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.model;
17  
18  import org.fourthline.cling.UpnpService;
19  import org.fourthline.cling.binding.xml.DeviceDescriptorBinder;
20  import org.fourthline.cling.binding.xml.UDA10DeviceDescriptorBinderImpl;
21  import org.fourthline.cling.mock.MockUpnpService;
22  import org.fourthline.cling.model.meta.Device;
23  import org.fourthline.cling.model.meta.RemoteDevice;
24  import org.fourthline.cling.model.meta.RemoteDeviceIdentity;
25  import org.fourthline.cling.model.meta.StateVariable;
26  import org.fourthline.cling.model.meta.StateVariableAllowedValueRange;
27  import org.fourthline.cling.model.meta.StateVariableTypeDetails;
28  import org.fourthline.cling.model.resource.Resource;
29  import org.fourthline.cling.model.resource.ServiceEventCallbackResource;
30  import org.fourthline.cling.model.types.DLNADoc;
31  import org.fourthline.cling.model.types.Datatype;
32  import org.fourthline.cling.model.types.DeviceType;
33  import org.fourthline.cling.model.types.IntegerDatatype;
34  import org.fourthline.cling.model.types.ServiceId;
35  import org.fourthline.cling.model.types.ServiceType;
36  import org.fourthline.cling.model.types.UDADeviceType;
37  import org.fourthline.cling.model.types.UDAServiceId;
38  import org.fourthline.cling.model.types.UDN;
39  import org.fourthline.cling.test.data.SampleData;
40  import org.fourthline.cling.test.data.SampleDeviceRoot;
41  import org.testng.annotations.Test;
42  
43  import java.net.URI;
44  
45  import static org.testng.Assert.*;
46  
47  public class IncompatibilityTest {
48  
49      @Test
50      public void validateMSFTServiceType() {
51          // TODO: UPNP VIOLATION: Microsoft violates the spec and sends periods in domain names instead of hyphens!
52          ServiceType serviceType = ServiceType.valueOf("urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1");
53          assertEquals(serviceType.getNamespace(), "microsoft.com");
54          assertEquals(serviceType.getType(), "X_MS_MediaReceiverRegistrar");
55          assertEquals(serviceType.getVersion(), 1);
56      }
57  
58      // TODO: UPNP VIOLATION: Azureus sends a URN as the service ID suffix. This doesn't violate the spec but it's unusual...
59      @Test
60      public void validateAzureusServiceId() {
61          ServiceId serviceId = ServiceId.valueOf("urn:upnp-org:serviceId:urn:schemas-upnp-org:service:ConnectionManager");
62          assertEquals(serviceId.getNamespace(), "upnp-org");
63          assertEquals(serviceId.getId(), "urn:schemas-upnp-org:service:ConnectionManager");
64      }
65  
66      // TODO: UPNP VIOLATION: PS Audio Bridge has invalid service IDs
67      @Test
68      public void validatePSAudioBridgeServiceId() {
69          ServiceId serviceId = ServiceId.valueOf("urn:foo:ThisSegmentShouldBeNamed'service':baz");
70          assertEquals(serviceId.getNamespace(), "foo");
71          assertEquals(serviceId.getId(), "baz");
72      }
73  
74      // TODO: UPNP VIOLATION: Some devices send spaces in URNs
75      @Test
76      public void validateSpacesInServiceType() {
77          String st = "urn:schemas-upnp-org:service: WANDSLLinkConfig:1";
78          ServiceType serviceType = ServiceType.valueOf(st);
79          assertEquals(serviceType.getNamespace(), "schemas-upnp-org");
80          assertEquals(serviceType.getType(), "WANDSLLinkConfig");
81          assertEquals(serviceType.getVersion(), 1);
82      }
83  
84  
85      @Test
86      public void validateIntelServiceId() {
87          // The Intel UPnP tools NetworkLight sends a valid but weird identifier with a dot
88          ServiceId serviceId = ServiceId.valueOf("urn:upnp-org:serviceId:DimmingService.0001");
89          assertEquals(serviceId.getNamespace(), "upnp-org");
90          assertEquals(serviceId.getId(), "DimmingService.0001");
91  
92          // TODO: UPNP VIOLATION: The Intel UPnP tools MediaRenderer sends an invalid identifier, we need to deal
93          serviceId = ServiceId.valueOf("urn:schemas-upnp-org:service:AVTransport");
94          assertEquals(serviceId.getNamespace(), "upnp-org");
95          assertEquals(serviceId.getId(), "AVTransport");
96      }
97  
98      @Test
99      public void readColonRelativePaths() throws Exception {
100         // Funny URI paths for services, breaks the java.net.URI parser so we deal with this special, see UDA10DeviceDescriptorBinderImpl
101         String descriptor =
102                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
103                         "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n" +
104                         "   <specVersion>\n" +
105                         "      <major>1</major>\n" +
106                         "      <minor>0</minor>\n" +
107                         "   </specVersion>\n" +
108                         "   <device>\n" +
109                         "      <deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType>\n" +
110                         "      <presentationURL>/</presentationURL>\n" +
111                         "      <friendlyName>Network Light (CB-1CE8FF0B14FA)</friendlyName>\n" +
112                         "      <manufacturer>OpenSource</manufacturer>\n" +
113                         "      <manufacturerURL>http://www.sourceforge.org</manufacturerURL>\n" +
114                         "      <modelDescription>Software Emulated Light Bulb</modelDescription>\n" +
115                         "      <modelName>Network Light Bulb</modelName>\n" +
116                         "      <modelNumber>XPC-L1</modelNumber>\n" +
117                         "      <modelURL>http://www.sourceforge.org/</modelURL>\n" +
118                         "      <UDN>uuid:872843be-9fb4-4eb4-8250-0b629c047a27</UDN>\n" +
119                         "      <iconList>\n" +
120                         "         <icon>\n" +
121                         "            <mimetype>image/png</mimetype>\n" +
122                         "            <width>32</width>\n" +
123                         "            <height>32</height>\n" +
124                         "            <depth>32</depth>\n" +
125                         "            <url>/icon.png</url>\n" +
126                         "         </icon>\n" +
127                         "         <icon>\n" +
128                         "            <mimetype>image/jpg</mimetype>\n" +
129                         "            <width>32</width>\n" +
130                         "            <height>32</height>\n" +
131                         "            <depth>32</depth>\n" +
132                         "            <url>/icon.jpg</url>\n" +
133                         "         </icon>\n" +
134                         "      </iconList>\n" +
135                         "      <serviceList>\n" +
136                         "         <service>\n" +
137                         "            <serviceType>urn:schemas-upnp-org:service:DimmingService:1</serviceType>\n" +
138                         "            <serviceId>urn:upnp-org:serviceId:DimmingService.0001</serviceId>\n" +
139                         "            <SCPDURL>_urn:upnp-org:serviceId:DimmingService.0001_scpd.xml</SCPDURL>\n" +
140                         "            <controlURL>_urn:upnp-org:serviceId:DimmingService.0001_control</controlURL>\n" +
141                         "            <eventSubURL>_urn:upnp-org:serviceId:DimmingService.0001_event</eventSubURL>\n" +
142                         "         </service>\n" +
143                         "         <service>\n" +
144                         "            <serviceType>urn:schemas-upnp-org:service:SwitchPower:1</serviceType>\n" +
145                         "            <serviceId>urn:upnp-org:serviceId:SwitchPower.0001</serviceId>\n" +
146                         "            <SCPDURL>_urn:upnp-org:serviceId:SwitchPower.0001_scpd.xml</SCPDURL>\n" +
147                         "            <controlURL>_urn:upnp-org:serviceId:SwitchPower.0001_control</controlURL>\n" +
148                         "            <eventSubURL>_urn:upnp-org:serviceId:SwitchPower.0001_event</eventSubURL>\n" +
149                         "         </service>\n" +
150                         "      </serviceList>\n" +
151                         "   </device>\n" +
152                         "</root>";
153 
154         DeviceDescriptorBinder binder = new UDA10DeviceDescriptorBinderImpl();
155         RemoteDevice device = new RemoteDevice(SampleData.createRemoteDeviceIdentity());
156         device = binder.describe(device, descriptor);
157         assertEquals(device.findServices().length, 2);
158     }
159 
160     // TODO: UPNP VIOLATION: Roku Soundbridge cuts off callback URI path after 100 characters.
161     @Test
162     public void validateCallbackURILength() throws Exception {
163         UpnpService upnpService = new MockUpnpService();
164         Device dev = SampleData.createRemoteDevice(
165                 new RemoteDeviceIdentity(
166                 UDN.uniqueSystemIdentifier("I'mARokuSoundbridge"),
167                 1800,
168                 SampleDeviceRoot.getDeviceDescriptorURL(),
169                 null,
170                 SampleData.getLocalBaseAddress()
171         ));
172         Resource[] resources = upnpService.getConfiguration().getNamespace().getResources(dev);
173         boolean test = false;
174         for (Resource resource : resources) {
175             if (!(resource instanceof ServiceEventCallbackResource)) {
176                 continue;
177             }
178             if (resource.getPathQuery().toString().length() < 100) test = true;
179         }
180         assertTrue(test);
181     }
182 
183     // TODO: UPNP VIOLATION: Some devices use non-integer service/device type versions
184     @Test
185     public void parseUDADeviceTypeFractions() {
186         UDADeviceType deviceType = (UDADeviceType) DeviceType.valueOf("urn:schemas-upnp-org:device:MyDeviceType:1.0");
187         assertEquals(deviceType.getType(), "MyDeviceType");
188         assertEquals(deviceType.getVersion(), 1);
189         deviceType = (UDADeviceType) DeviceType.valueOf("urn:schemas-upnp-org:device:MyDeviceType:2.5");
190         assertEquals(deviceType.getType(), "MyDeviceType");
191         assertEquals(deviceType.getVersion(), 2);
192     }
193 
194     // TODO: UPNP VIOLATION: Of course, adding more rules makes more devices compatible! DLNA genuises ftw!
195     @Test
196     public void parseInvalidDLNADoc() {
197         DLNADoc doc = DLNADoc.valueOf("DMS 1.50"); // No hyphen
198         assertEquals(doc.getDevClass(), "DMS");
199         assertEquals(doc.getVersion(), DLNADoc.Version.V1_5.toString());
200         assertEquals(doc.toString(), "DMS-1.50");
201     }
202 
203     // TODO: UPNP VIOLATION: DirecTV HR23/700 High Definition DVR Receiver has invalid default value for statevar
204     @Test
205     public void invalidStateVarDefaultValue() {
206         StateVariable stateVariable = new StateVariable(
207                 "Test",
208                 new StateVariableTypeDetails(
209                         Datatype.Builtin.STRING.getDatatype(),
210                         "A",
211                         new String[] {"B", "C"},
212                         null
213                 )
214         );
215 
216         boolean foundA = false;
217         for (String s : stateVariable.getTypeDetails().getAllowedValues()) {
218             if (s.equals("A")) foundA = true;
219         }
220         assertEquals(foundA, true);
221         assertEquals(stateVariable.getTypeDetails().getAllowedValues().length, 3);
222         assertEquals(stateVariable.validate().size(), 0);
223     }
224 
225     // TODO: UPNP VIOLATION: Onkyo NR-TX808 has a bug in RenderingControl service, switching maximum/minimum value range
226     @Test
227     public void switchedMinimumMaximumValueRange() {
228         StateVariable stateVariable = new StateVariable(
229                 "Test",
230                 new StateVariableTypeDetails(
231                         Datatype.Builtin.I2.getDatatype(),
232                         null,
233                         null,
234                         new StateVariableAllowedValueRange(100, 0)
235                 )
236         );
237 
238         assertEquals(stateVariable.validate().size(), 0);
239         assertEquals(stateVariable.getTypeDetails().getAllowedValueRange().getMinimum(), 0);
240         assertEquals(stateVariable.getTypeDetails().getAllowedValueRange().getMaximum(), 100);
241     }
242 
243     // TODO: UPNP VIOLATION: Some renderers (like PacketVideo TMM Player) send
244     // RelCount and AbsCount as "NOT_IMPLEMENTED" in GetPositionInfoResponse action.
245     @Test
246     public void invalidIntegerValue() {
247         assertEquals(new IntegerDatatype(1).valueOf("NOT_IMPLEMENTED").intValue(), Byte.MAX_VALUE);
248         assertEquals(new IntegerDatatype(2).valueOf("NOT_IMPLEMENTED").intValue(), Short.MAX_VALUE);
249         assertEquals(new IntegerDatatype(4).valueOf("NOT_IMPLEMENTED").intValue(), Integer.MAX_VALUE);
250     }
251 
252     @Test
253     public void parseBrokenServiceType() {
254         ServiceType serviceType = ServiceType.valueOf("urn:schemas-upnp-org:serviceId:Foo:1");
255         assertEquals(serviceType.getNamespace(), "schemas-upnp-org");
256         assertEquals(serviceType.getType(), "Foo");
257         assertEquals(serviceType.getVersion(), 1);
258     }
259 
260     @Test
261     public void parseBrokenServiceId() {
262         ServiceId serviceId = ServiceId.valueOf("urn:my-domain-namespace:service:MyService123");
263         assertEquals(serviceId.getNamespace(), "my-domain-namespace");
264         assertEquals(serviceId.getId(), "MyService123");
265         assertEquals(serviceId.toString(), "urn:my-domain-namespace:serviceId:MyService123");
266     }
267 
268     @Test
269     public void parseServiceNameAsServiceId() {
270         UDAServiceId serviceId = UDAServiceId.valueOf("ContentDirectory");
271         assertEquals(serviceId.getNamespace(), UDAServiceId.DEFAULT_NAMESPACE);
272         assertEquals(serviceId.getId(), "ContentDirectory");
273         assertEquals(serviceId.toString(), "urn:" + UDAServiceId.DEFAULT_NAMESPACE + ":serviceId:ContentDirectory");
274 
275         serviceId = UDAServiceId.valueOf("ConnectionManager");
276         assertEquals(serviceId.getNamespace(), UDAServiceId.DEFAULT_NAMESPACE);
277         assertEquals(serviceId.getId(), "ConnectionManager");
278         assertEquals(serviceId.toString(), "urn:" + UDAServiceId.DEFAULT_NAMESPACE + ":serviceId:ConnectionManager");
279 
280         serviceId = UDAServiceId.valueOf("RenderingControl");
281         assertEquals(serviceId.getNamespace(), UDAServiceId.DEFAULT_NAMESPACE);
282         assertEquals(serviceId.getId(), "RenderingControl");
283         assertEquals(serviceId.toString(), "urn:" + UDAServiceId.DEFAULT_NAMESPACE + ":serviceId:RenderingControl");
284 
285         serviceId = UDAServiceId.valueOf("AVTransport");
286         assertEquals(serviceId.getNamespace(), UDAServiceId.DEFAULT_NAMESPACE);
287         assertEquals(serviceId.getId(), "AVTransport");
288         assertEquals(serviceId.toString(), "urn:" + UDAServiceId.DEFAULT_NAMESPACE + ":serviceId:AVTransport");
289     }
290 
291     // TODO: UPNP VIOLATION: EyeTV Netstream uses colons in device type token
292     @Test
293     public void parseEyeTVDeviceType() {
294         DeviceType deviceType= DeviceType.valueOf("urn:schemas-microsoft-com:device:pbda:tuner:1");
295         assertEquals(deviceType.getNamespace(), "schemas-microsoft-com");
296         assertEquals(deviceType.getType(), "pbda-tuner");
297         assertEquals(deviceType.getVersion(), 1);
298     }
299 
300     // TODO: UPNP VIOLATION: EyeTV Netstream uses colons in service type token
301     @Test
302     public void parseEyeTVServiceType() {
303         ServiceType serviceType= ServiceType.valueOf("urn:schemas-microsoft-com:service:pbda:tuner:1");
304         assertEquals(serviceType.getNamespace(), "schemas-microsoft-com");
305         assertEquals(serviceType.getType(), "pbda-tuner");
306         assertEquals(serviceType.getVersion(), 1);
307     }
308 
309     // TODO: UPNP VIOLATION: Ceyton InfiniTV uses colons in service type token and 'serviceId' instead of 'service'
310     @Test
311     public void parseCeytonInfiniTVServiceType() {
312         ServiceType serviceType= ServiceType.valueOf("urn:schemas-opencable-com:serviceId:dri2:debug:1");
313         assertEquals(serviceType.getNamespace(), "schemas-opencable-com");
314         assertEquals(serviceType.getType(), "dri2-debug");
315         assertEquals(serviceType.getVersion(), 1);
316     }
317 
318     // TODO: UPNP VIOLATION: Handle garbage sent by Eyecon Android app
319     @Test
320     public void parseEyeconServiceId() {
321         ServiceId serviceId = ServiceId.valueOf("urn:upnp-orgerviceId:urnchemas-upnp-orgervice:Foo");
322         assertEquals(serviceId.getNamespace(), UDAServiceId.DEFAULT_NAMESPACE);
323         assertEquals(serviceId.getId(), "Foo");
324         assertEquals(serviceId.toString(), "urn:" + UDAServiceId.DEFAULT_NAMESPACE + ":serviceId:Foo");
325     }
326 
327     // TODO: UPNP VIOLATION: Escient doesn't provide any device type token
328     @Test
329     public void parseEscientDeviceType() {
330         DeviceType deviceType= DeviceType.valueOf("urn:schemas-upnp-org:device::1");
331         assertEquals(deviceType.getNamespace(), "schemas-upnp-org");
332         assertEquals(deviceType.getType(), DeviceType.UNKNOWN);
333         assertEquals(deviceType.getVersion(), 1);
334     }
335 
336     // TODO: UPNP VIOLATION: Kodak Media Server doesn't provide any service ID token
337     @Test
338     public void parseKodakServiceId() {
339         ServiceId serviceId = ServiceId.valueOf("urn:upnp-org:serviceId:");
340         assertEquals(serviceId.getNamespace(), "upnp-org");
341         assertEquals(serviceId.getId(), ServiceId.UNKNOWN);
342         assertEquals(serviceId.toString(), "urn:upnp-org:serviceId:" + ServiceId.UNKNOWN);
343     }
344 
345 }