package com.connectsdk.service;
import com.connectsdk.core.SubtitleInfo;
import com.connectsdk.core.TestUtil;
import com.connectsdk.discovery.provider.ssdp.Service;
import com.connectsdk.service.config.ServiceConfig;
import com.connectsdk.service.config.ServiceDescription;
import com.connectsdk.service.upnp.DLNAHttpServer;
import junit.framework.Assert;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Created by oleksii.frolov on 1/13/2015.
*/
@RunWith(RobolectricTestRunner.class)
@Config(manifest=Config.NONE)
public class DLNAServiceTest {
private DLNAService service;
private DLNAHttpServer dlnaServer;
@Before
public void setUp() {
dlnaServer = Mockito.mock(DLNAHttpServer.class);
service = new DLNAService(Mockito.mock(ServiceDescription.class),
Mockito.mock(ServiceConfig.class), Robolectric.application, dlnaServer);
}
@Test
public void testParseData() {
String tag = "TrackDuration";
String response = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" +
" <SOAP-ENV:Body>\n" +
" <m:GetPositionInfoResponse xmlns:m=\"urn:schemas-upnp-org:service:AVTransport:1\">\n" +
" <Track xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui4\">1</Track>\n" +
" <TrackDuration xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">0:00:52</TrackDuration>\n" +
" <TrackMetaData xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\"><DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"><item id=\"1000\" parentID=\"0\" restricted=\"0\"><dc:title>Sintel Trailer</dc:title><dc:description>Blender Open Movie Project</dc:description><res protocolInfo=\"http-get:*:video/mp4:DLNA.ORG_OP=01\">http://ec2-54-201-108-205.us-west-2.compute.amazonaws.com/samples/media/video.mp4</res><upnp:albumArtURI>http://ec2-54-201-108-205.us-west-2.compute.amazonaws.com/samples/media/videoIcon.jpg</upnp:albumArtURI><upnp:class>object.item.videoItem</upnp:class></item></DIDL-Lite></TrackMetaData>\n" +
" <TrackURI xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">http://ec2-54-201-108-205.us-west-2.compute.amazonaws.com/samples/media/video.mp4</TrackURI>\n" +
" <RelTime xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">0:00:00</RelTime>\n" +
" <AbsTime xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">NOT_IMPLEMENTED</AbsTime>\n" +
" <RelCount xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"i4\">2147483647</RelCount>\n" +
" <AbsCount xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"i4\">2147483647</AbsCount>\n" +
" </m:GetPositionInfoResponse>\n" +
" </SOAP-ENV:Body>\n" +
"</SOAP-ENV:Envelope>";
String value = service.parseData(response, tag);
Assert.assertEquals("0:00:52", value);
}
@Test
public void testParseDataWithError() {
String tag = "errorCode";
String response = "<?xml version=\"1.0\"?>\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Client</faultcode><faultstring>UPnPError</faultstring><detail><u:UPnPError xmlns:u=\"urn:schemas-upnp-org:control-1-0\"><u:errorCode>402</u:errorCode><u:errorDescription>Invalid Args</u:errorDescription></u:UPnPError></detail></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>";
String value = service.parseData(response, tag);
Assert.assertEquals("402", value);
}
@Test
public void testParseData3Symbols() {
String tag = "errorCode";
String response = "<";
String value = null;
try {
value = service.parseData(response, tag);
} catch (Exception e) {
Assert.fail("exception thrown: " + e);
}
Assert.assertEquals("", value);
}
@Test
public void testGetMetadata() throws Exception {
String title = "<title>";
String description = "description";
String mime = "audio/mpeg";
String mediaURL = "http://host.com/media";
String iconURL = "http://host.com/icon";
String expectedXML = "<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " +
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:sec=\"http://www.sec.co.kr/\" " +
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\">" +
"<item id=\"1000\" parentID=\"0\" restricted=\"0\">" +
"<dc:title><title></dc:title>" +
"<dc:description>" + description + "</dc:description>" +
"<res protocolInfo=\"http-get:*:audio/mpeg:DLNA.ORG_OP=01\">" + mediaURL + "</res>" +
"<upnp:albumArtURI>" + iconURL + "</upnp:albumArtURI>" +
"<upnp:class>object.item.audioItem</upnp:class><" +
"/item></DIDL-Lite>";
String actualXML = service.getMetadata(mediaURL, null, mime, title, description, iconURL);
assertXMLEquals(expectedXML, actualXML);
}
@Test
public void testGetMessageXml() throws Exception {
String method = "GetPosition";
String serviceURN = "http://serviceurn/";
String expectedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
"<u:" + method + " xmlns:u=\"" + serviceURN + "\">" +
"<key>value</key>" +
"</u:" + method + ">" +
"</s:Body>" +
"</s:Envelope>";
Map<String, String> params = new HashMap<String, String>();
params.put("key", "value");
String actualXML = service.getMessageXml(serviceURN, method, null, params);
assertXMLEquals(expectedXML, actualXML);
}
@Test
public void testGetMessageXmlWithMetadataWithAllParametersExceptSubtitle() throws Exception {
String method = "SetAVTransportUri";
String serviceURN = "http://serviceurn/";
String expectedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
"<u:" + method + " xmlns:u=\"" + serviceURN + "\">" +
"<CurrentURIMetaData>" +
"<DIDL-Lite " +
"xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " +
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " +
"xmlns:sec=\"http://www.sec.co.kr/\" " +
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\">" +
"<item id=\"1000\" parentID=\"0\" restricted=\"0\">" +
"<dc:title>&\"title\"</dc:title>" +
"<dc:description>&</dc:description>" +
"<res protocolInfo=\"http-get:*:audio/mpeg:DLNA.ORG_OP=01\">http://url/t&t</res>" +
"<upnp:albumArtURI>http://host/image</upnp:albumArtURI>" +
"<upnp:class>object.item.audioItem</upnp:class>" +
"</item></DIDL-Lite>" +
"</CurrentURIMetaData>" +
"</u:" + method + "></s:Body></s:Envelope>";
String metadata = service.getMetadata("http://url/t&t", null, "audio/mpeg", "&\"title\"", "&", "http://host/image");
Map<String, String> params = new LinkedHashMap<String, String>();
params.put("CurrentURIMetaData", metadata);
String actualXML = service.getMessageXml(serviceURN, method, null, params);
assertXMLEquals(expectedXML, actualXML);
}
@Test
public void testGetMessageXmlWithMetadataWithAllParameters() throws Exception {
String method = "SetAVTransportUri";
String serviceURN = "http://serviceurn/";
String subtitleType = "text/vtt";
String subtitleSubType = "vtt";
SubtitleInfo subtitle = new SubtitleInfo.Builder("http://subtitleurl")
.setMimeType(subtitleType)
.setLabel("label")
.setLanguage("en")
.build();
String mediaUrl = "http://mediaurl/";
String mediaType = "audio/mp3";
String title = "&\"title";
String description = "description";
String iconUrl = "http://iconurl/";
String expectedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
"<u:" + method + " xmlns:u=\"" + serviceURN + "\">" +
"<CurrentURIMetaData>" +
"<DIDL-Lite " +
"xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " +
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " +
"xmlns:sec=\"http://www.sec.co.kr/\" " +
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\">" +
"<item id=\"1000\" parentID=\"0\" restricted=\"0\">" +
"<dc:title>&\"title</dc:title>" +
"<dc:description>description</dc:description>" +
"<res xmlns:pv=\"http://www.pv.com/pvns/\" " +
"protocolInfo=\"http-get:*:"+mediaType+":DLNA.ORG_OP=01\" " +
"pv:subtitleFileType=\""+subtitleSubType+"\" " +
"pv:subtitleFileUri=\""+subtitle.getUrl()+"\">"+mediaUrl+"</res>" +
"<upnp:albumArtURI>"+iconUrl+"</upnp:albumArtURI>" +
"<upnp:class>object.item.audioItem</upnp:class>" +
"<res protocolInfo=\"http-get:*:smi/caption\">"+subtitle.getUrl()+"</res>" +
"<res protocolInfo=\"http-get:*:"+subtitle.getMimeType()+":\">"+subtitle.getUrl()+"</res>" +
"<sec:CaptionInfoEx sec:type=\""+subtitleSubType+"\">"+subtitle.getUrl()+"</sec:CaptionInfoEx>" +
"<sec:CaptionInfo sec:type=\""+subtitleSubType+"\">"+subtitle.getUrl()+"</sec:CaptionInfo>" +
"</item></DIDL-Lite>" +
"</CurrentURIMetaData>" +
"</u:" + method + "></s:Body></s:Envelope>";
String metadata = service.getMetadata(mediaUrl, subtitle, mediaType, title, description, iconUrl);
Map<String, String> params = new LinkedHashMap<String, String>();
params.put("CurrentURIMetaData", metadata);
String actualXML = service.getMessageXml(serviceURN, method, null, params);
assertXMLEquals(expectedXML, actualXML);
}
@Test
public void testGetMessageXmlWithMetadataWithSubtitleUrl() throws Exception {
String method = "SetAVTransportUri";
String serviceURN = "http://serviceurn/";
String subtitleType = "text/srt";
String subtitleSubType = "srt";
SubtitleInfo subtitle = new SubtitleInfo.Builder("http://subtitleurl")
.build();
String mediaUrl = "http://mediaurl/";
String mediaType = "audio/mp3";
String title = "&\"title";
String description = "description";
String iconUrl = "http://iconurl/";
String expectedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
"<u:" + method + " xmlns:u=\"" + serviceURN + "\">" +
"<CurrentURIMetaData>" +
"<DIDL-Lite " +
"xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " +
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " +
"xmlns:sec=\"http://www.sec.co.kr/\" " +
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\">" +
"<item id=\"1000\" parentID=\"0\" restricted=\"0\">" +
"<dc:title>&\"title</dc:title>" +
"<dc:description>description</dc:description>" +
"<res xmlns:pv=\"http://www.pv.com/pvns/\" " +
"protocolInfo=\"http-get:*:"+mediaType+":DLNA.ORG_OP=01\" " +
"pv:subtitleFileType=\""+subtitleSubType+"\" " +
"pv:subtitleFileUri=\""+subtitle.getUrl()+"\">"+mediaUrl+"</res>" +
"<upnp:albumArtURI>"+iconUrl+"</upnp:albumArtURI>" +
"<upnp:class>object.item.audioItem</upnp:class>" +
"<res protocolInfo=\"http-get:*:smi/caption\">"+subtitle.getUrl()+"</res>" +
"<res protocolInfo=\"http-get:*:"+subtitleType+":\">"+subtitle.getUrl()+"</res>" +
"<sec:CaptionInfoEx sec:type=\""+subtitleSubType+"\">"+subtitle.getUrl()+"</sec:CaptionInfoEx>" +
"<sec:CaptionInfo sec:type=\""+subtitleSubType+"\">"+subtitle.getUrl()+"</sec:CaptionInfo>" +
"</item></DIDL-Lite>" +
"</CurrentURIMetaData>" +
"</u:" + method + "></s:Body></s:Envelope>";
String metadata = service.getMetadata(mediaUrl, subtitle, mediaType, title, description, iconUrl);
Map<String, String> params = new LinkedHashMap<String, String>();
params.put("CurrentURIMetaData", metadata);
String actualXML = service.getMessageXml(serviceURN, method, null, params);
assertXMLEquals(expectedXML, actualXML);
}
@Test
public void testGetMessageXmlWithMetadataWithRequiredParameters() throws Exception {
String method = "SetAVTransportUri";
String serviceURN = "http://serviceurn/";
String expectedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
"<u:" + method + " xmlns:u=\"" + serviceURN + "\">" +
"<CurrentURIMetaData>" +
"<DIDL-Lite " +
"xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " +
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " +
"xmlns:sec=\"http://www.sec.co.kr/\" " +
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\">" +
"<item id=\"1000\" parentID=\"0\" restricted=\"0\">" +
"<dc:title/>" +
"<dc:description/>" +
"<res protocolInfo=\"http-get:*:audio/mpeg:DLNA.ORG_OP=01\">http://url/t&t</res>" +
"<upnp:albumArtURI/>" +
"<upnp:class>object.item.audioItem</upnp:class>" +
"</item></DIDL-Lite>" +
"</CurrentURIMetaData>" +
"</u:" + method + "></s:Body></s:Envelope>";
String metadata = service.getMetadata("http://url/t&t", null, "audio/mpeg", null, null, null);
Map<String, String> params = new LinkedHashMap<String, String>();
params.put("CurrentURIMetaData", metadata);
String actualXML = service.getMessageXml(serviceURN, method, null, params);
assertXMLEquals(expectedXML, actualXML);
}
@Test
public void testUrlEncode() throws Exception {
String expected = "http://192.168.1.100:8000/ph&o't'o%20with%20symbols.jpg";
String urlStr = "http://192.168.1.100:8000/ph&o't'o with symbols.jpg";
Assert.assertEquals(expected, service.encodeURL(urlStr));
}
@Test
public void testUrlEncodeAlreadyEncoded() throws Exception {
String expected = "http://192.168.1.100:8000/ph&o't'o%20with%20symbols.jpg";
String urlStr = "http://192.168.1.100:8000/ph&o't'o%20with%20symbols.jpg";
Assert.assertEquals(expected, service.encodeURL(urlStr));
}
@Test
public void testNullUrlEncode() throws Exception {
Assert.assertEquals("", service.encodeURL(null));
}
@Test
public void testEmptyUrlEncode() throws Exception {
Assert.assertEquals("", service.encodeURL(""));
}
@Test
public void testServiceControlURL() {
DLNAService dlnaService = makeServiceWithControlURL("http://192.168.1.0/", "/controlURL");
Assert.assertEquals("http://192.168.1.0/controlURL", dlnaService.avTransportURL);
}
@Test
public void testServiceControlURLWithWrongBase() {
DLNAService dlnaService = makeServiceWithControlURL("http://192.168.1.0", "/controlURL");
Assert.assertEquals("http://192.168.1.0/controlURL", dlnaService.avTransportURL);
}
@Test
public void testServiceControlURLWithWrongControlURL() {
DLNAService dlnaService = makeServiceWithControlURL("http://192.168.1.0/", "controlURL");
Assert.assertEquals("http://192.168.1.0/controlURL", dlnaService.avTransportURL);
}
@Test
public void testServiceControlURLWithWrongBaseAndControlURL() {
DLNAService dlnaService = makeServiceWithControlURL("http://192.168.1.0", "controlURL");
Assert.assertEquals("http://192.168.1.0/controlURL", dlnaService.avTransportURL);
}
@Test
public void testInitialPairingType() {
Assert.assertEquals(DeviceService.PairingType.NONE, service.getPairingType());
}
@Test
public void testPairingTypeSetter() {
service.setPairingType(DeviceService.PairingType.PIN_CODE);
Assert.assertEquals(DeviceService.PairingType.NONE, service.getPairingType());
}
@Test
public void testTimeToLongNullValue() {
Assert.assertEquals(0L, service.convertStrTimeFormatToLong(null));
}
@Test
public void testTimeToLongWrongValue() {
Assert.assertEquals(0L, service.convertStrTimeFormatToLong("abc"));
}
@Test
public void testTimeToLongZeroValue() {
Assert.assertEquals(0L, service.convertStrTimeFormatToLong("00:00:00"));
}
@Test
public void testTimeToLong() {
Assert.assertEquals(10000L, service.convertStrTimeFormatToLong("00:00:10"));
}
@Test
public void testTimeToLong12Hours() {
Assert.assertEquals(43200000L, service.convertStrTimeFormatToLong("12:00:00"));
}
@Test
public void testTimeToLong20Hours() {
Assert.assertEquals(72000000L, service.convertStrTimeFormatToLong("20:00:00"));
}
@Test
public void testTimeToLongBigValue() {
Assert.assertEquals(432000000L, service.convertStrTimeFormatToLong("120:00:00"));
}
@Test
public void testStopDLNAServerOnDisconnect() {
service.disconnect();
TestUtil.runUtilBackgroundTasks();
Mockito.verify(dlnaServer).stop();
}
@Test
public void testTimeToLongWithMilliseconds() {
Assert.assertEquals(43200000L, service.convertStrTimeFormatToLong("12:00:00.777"));
}
@Test
public void testTimeToLongWithInvalidArguments() {
try {
Assert.assertEquals(0L, service.convertStrTimeFormatToLong("01.210"));
Assert.assertEquals(0L, service.convertStrTimeFormatToLong("00:01.210"));
Assert.assertEquals(0L, service.convertStrTimeFormatToLong("Not a number"));
} catch (Exception e) {
Assert.fail("convertStrTimeFormatToLong must not throw an exception");
}
}
@Test
public void testMakeControlURL() {
Assert.assertEquals("base/path", service.makeControlURL("base/", "path"));
}
@Test
public void testMakeControlURLWithNullBase() {
Assert.assertNull(service.makeControlURL(null, "path"));
}
@Test
public void testMakeControlURLWithNullPath() {
Assert.assertNull(service.makeControlURL("base", null));
}
private DLNAService makeServiceWithControlURL(String base, String controlURL) {
List<Service> services = new ArrayList<Service>();
Service service = new Service();
service.baseURL = base;
service.controlURL = controlURL;
service.serviceType = DLNAService.AV_TRANSPORT;
services.add(service);
ServiceDescription description = Mockito.mock(ServiceDescription.class);
Mockito.when(description.getServiceList()).thenReturn(services);
return new DLNAService(description, Mockito.mock(ServiceConfig.class), Robolectric.application, null);
}
private void assertXMLEquals(String expectedXML, String actualXML) throws SAXException, IOException {
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreAttributeOrder(true);
XMLUnit.setNormalize(true);
DetailedDiff diff = new DetailedDiff(XMLUnit.compareXML(expectedXML, actualXML));
List<?> allDifferences = diff.getAllDifferences();
Assert.assertEquals("XML differences found: " + diff.toString(), 0, allDifferences.size());
}
}