View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.test.gena;
17  
18  import org.fourthline.cling.UpnpService;
19  import org.fourthline.cling.binding.xml.ServiceDescriptorBinder;
20  import org.fourthline.cling.binding.xml.UDA10ServiceDescriptorBinderImpl;
21  import org.fourthline.cling.mock.MockUpnpService;
22  import org.fourthline.cling.mock.MockUpnpServiceConfiguration;
23  import org.fourthline.cling.model.UnsupportedDataException;
24  import org.fourthline.cling.model.gena.CancelReason;
25  import org.fourthline.cling.model.gena.RemoteGENASubscription;
26  import org.fourthline.cling.model.message.StreamRequestMessage;
27  import org.fourthline.cling.model.message.UpnpMessage.BodyType;
28  import org.fourthline.cling.model.message.UpnpResponse;
29  import org.fourthline.cling.model.message.gena.IncomingEventRequestMessage;
30  import org.fourthline.cling.model.message.gena.OutgoingEventRequestMessage;
31  import org.fourthline.cling.model.meta.RemoteService;
32  import org.fourthline.cling.model.state.StateVariableValue;
33  import org.fourthline.cling.model.types.UnsignedIntegerFourBytes;
34  import org.fourthline.cling.test.data.SampleData;
35  import org.fourthline.cling.transport.impl.PullGENAEventProcessorImpl;
36  import org.fourthline.cling.transport.impl.RecoveringGENAEventProcessorImpl;
37  import org.fourthline.cling.transport.spi.GENAEventProcessor;
38  import org.seamless.util.io.IO;
39  import org.seamless.xml.XmlPullParserUtils;
40  import org.testng.annotations.DataProvider;
41  import org.testng.annotations.Test;
42  import org.xmlpull.v1.XmlPullParser;
43  
44  import java.util.ArrayList;
45  import java.util.HashMap;
46  import java.util.Map;
47  
48  import static org.testng.Assert.assertFalse;
49  import static org.testng.Assert.assertTrue;
50  
51  public class InvalidEventXMLProcessingTest {
52  
53      @DataProvider(name = "invalidXMLFile")
54      public String[][] getInvalidXMLFile() throws Exception {
55          return new String[][]{
56              {"/invalidxml/event/invalid_root_element.xml"},
57          };
58      }
59  
60      @DataProvider(name = "invalidRecoverableXMLFile")
61      public String[][] getInvalidRecoverableXMLFile() throws Exception {
62          return new String[][]{
63              {"/invalidxml/event/truncated.xml"},
64              {"/invalidxml/event/orange_liveradio.xml"},
65          };
66      }
67  
68      // TODO: Shouldn't these be failures of the LastChangeParser?
69      // The GENA parser does the right thing for most of them, no?
70      @DataProvider(name = "invalidUnrecoverableXMLFile")
71      public String[][] getInvalidUnrecoverableXMLFile() throws Exception {
72          return new String[][]{
73              {"/invalidxml/event/unrecoverable/denon_avr4306.xml"},
74              {"/invalidxml/event/unrecoverable/philips_np2900.xml"},
75              {"/invalidxml/event/unrecoverable/philips_sla5220.xml"},
76              {"/invalidxml/event/unrecoverable/terratec_noxon2.xml"},
77              {"/invalidxml/event/unrecoverable/marantz_mcr603.xml"},
78              {"/invalidxml/event/unrecoverable/teac_wap4500.xml"},
79              {"/invalidxml/event/unrecoverable/technisat_digi_hd8+.xml"},
80          };
81      }
82  
83      /* ############################## TEST FAILURE ############################ */
84  
85      @Test(dataProvider = "invalidXMLFile", expectedExceptions = UnsupportedDataException.class)
86      public void readDefaultFailure(String invalidXMLFile) throws Exception {
87          // This should always fail!
88          read(invalidXMLFile,new MockUpnpService());
89      }
90  
91      @Test(dataProvider = "invalidRecoverableXMLFile", expectedExceptions = UnsupportedDataException.class)
92      public void readRecoverableFailure(String invalidXMLFile) throws Exception {
93          // This should always fail!
94          read(invalidXMLFile,new MockUpnpService());
95      }
96  
97      @Test(dataProvider = "invalidUnrecoverableXMLFile", expectedExceptions = Exception.class)
98      public void readRecoveringFailure(String invalidXMLFile) throws Exception {
99          // This should always fail!
100         read(
101             invalidXMLFile,
102             new MockUpnpService(new MockUpnpServiceConfiguration() {
103                 @Override
104                 public GENAEventProcessor getGenaEventProcessor() {
105                     return new RecoveringGENAEventProcessorImpl();
106                 }
107             })
108         );
109     }
110 
111     /* ############################## TEST SUCCESS ############################ */
112 
113     @Test(dataProvider = "invalidXMLFile")
114     public void readPull(String invalidXMLFile) throws Exception {
115         read(
116             invalidXMLFile,
117             new MockUpnpService(new MockUpnpServiceConfiguration() {
118                 @Override
119                 public GENAEventProcessor getGenaEventProcessor() {
120                     return new PullGENAEventProcessorImpl();
121                 }
122             })
123         );
124     }
125 
126     @Test(dataProvider = "invalidRecoverableXMLFile")
127     public void readRecovering(String invalidXMLFile) throws Exception {
128         read(
129             invalidXMLFile,
130             new MockUpnpService(new MockUpnpServiceConfiguration() {
131                 @Override
132                 public GENAEventProcessor getGenaEventProcessor() {
133                     return new RecoveringGENAEventProcessorImpl();
134                 }
135             })
136         );
137     }
138 
139     protected void read(String invalidXMLFile, UpnpService upnpService) throws Exception {
140         ServiceDescriptorBinder binder = new UDA10ServiceDescriptorBinderImpl();
141         RemoteService service = SampleData.createUndescribedRemoteService();
142         service = binder.describe(service, IO.readLines(
143             getClass().getResourceAsStream("/descriptors/service/uda10_avtransport.xml"))
144         );
145 
146         RemoteGENASubscription subscription = new RemoteGENASubscription(service, 1800) {
147             public void failed(UpnpResponse responseStatus) {
148             }
149 
150             public void ended(CancelReason reason, UpnpResponse responseStatus) {
151             }
152 
153             public void eventsMissed(int numberOfMissedEvents) {
154             }
155 
156             public void established() {
157             }
158 
159             public void eventReceived() {
160             }
161 
162             public void invalidMessage(UnsupportedDataException ex) {
163             }
164         };
165         subscription.receive(new UnsignedIntegerFourBytes(0), new ArrayList<StateVariableValue>());
166 
167         OutgoingEventRequestMessage outgoingCall =
168             new OutgoingEventRequestMessage(subscription, SampleData.getLocalBaseURL());
169 
170         upnpService.getConfiguration().getGenaEventProcessor().writeBody(outgoingCall);
171 
172         StreamRequestMessage incomingStream = new StreamRequestMessage(outgoingCall);
173 
174         IncomingEventRequestMessage message = new IncomingEventRequestMessage(incomingStream, service);
175         message.setBody(BodyType.STRING, IO.readLines(getClass().getResourceAsStream(invalidXMLFile)));
176 
177         upnpService.getConfiguration().getGenaEventProcessor().readBody(message);
178 
179         // All of the messages must have a LastChange state variable, and we should be able to parse
180         // the XML value of that state variable
181         boolean found = false;
182         for (StateVariableValue stateVariableValue : message.getStateVariableValues()) {
183             if (stateVariableValue.getStateVariable().getName().equals("LastChange")
184                 && stateVariableValue.getValue() != null) {
185                 found = true;
186                 String lastChange = (String) stateVariableValue.getValue();
187                 Map<String, Object> lastChangeValues = parseLastChangeXML(lastChange);
188                 assertFalse(lastChangeValues.isEmpty());
189                 break;
190             }
191         }
192 
193         assertTrue(found);
194     }
195 
196     protected Map<String, Object> parseLastChangeXML(String lastChange) throws Exception {
197         // All we do here is trying to parse some XML looking for any element with a 'val' attribute
198         Map<String, Object> values = new HashMap<>();
199         XmlPullParser xpp = XmlPullParserUtils.createParser(lastChange);
200         xpp.nextTag();
201         int event;
202         while ((event = xpp.next()) != XmlPullParser.END_DOCUMENT) {
203             if (event != XmlPullParser.START_TAG) continue;
204             String tag = xpp.getName();
205             String value = xpp.getAttributeValue(null, "val");
206             if (value == null)
207                 continue;
208             values.put(tag, value);
209         }
210         return values;
211     }
212 }