fromInputStream(@Nonnull final InputStream contentInputStream)
+ throws ExtractionException {
+ throw new UnsupportedOperationException("Service " + service.getServiceInfo().getName()
+ + " doesn't support extracting from an InputStream");
}
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/DonationLinkHelper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/DonationLinkHelper.java
index 817595ad..eb7f35b1 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/DonationLinkHelper.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/DonationLinkHelper.java
@@ -15,16 +15,14 @@ public class DonationLinkHelper {
AMAZON,
}
- public static DonationService getDonatoinServiceByLink(String link) throws MalformedURLException {
+ public static DonationService getDonationServiceByLink(String link) throws MalformedURLException {
URL url = new URL(fixLink(link));
switch (url.getHost()) {
case "www.patreon.com":
- return DonationService.PATREON;
case "patreon.com":
return DonationService.PATREON;
- case "paypal.me":
- return DonationService.PAYPAL;
case "www.paypal.me":
+ case "paypal.me":
return DonationService.PAYPAL;
default:
return DonationService.NO_DONATION;
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java
index 288e401c..95920270 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java
@@ -181,14 +181,39 @@ public class Utils {
return s;
}
- public static String getBaseUrl(String url) throws ParsingException {
- URL uri;
+ public static String getBaseUrl(final String url) throws ParsingException {
try {
- uri = stringToURL(url);
- } catch (MalformedURLException e) {
+ final URL uri = stringToURL(url);
+ return uri.getProtocol() + "://" + uri.getAuthority();
+ } catch (final MalformedURLException e) {
+ final String message = e.getMessage();
+ if (message.startsWith("unknown protocol: ")) {
+ // return just the protocol (e.g. vnd.youtube)
+ return message.substring("unknown protocol: ".length());
+ }
+
throw new ParsingException("Malformed url: " + url, e);
}
- return uri.getProtocol() + "://" + uri.getAuthority();
+ }
+
+ /**
+ * If the provided url is a Google search redirect, then the actual url is extracted from the
+ * {@code url=} query value and returned, otherwise the original url is returned.
+ * @param url the url which can possibly be a Google search redirect
+ * @return an url with no Google search redirects
+ */
+ public static String followGoogleRedirectIfNeeded(final String url) {
+ // if the url is a redirect from a Google search, extract the actual url
+ try {
+ final URL decoded = Utils.stringToURL(url);
+ if (decoded.getHost().contains("google") && decoded.getPath().equals("/url")) {
+ return URLDecoder.decode(Parser.matchGroup1("&url=([^&]+)(?:&|$)", url), "UTF-8");
+ }
+ } catch (final Exception ignored) {
+ }
+
+ // url is not a google search redirect
+ return url;
}
public static boolean isNullOrEmpty(final String str) {
diff --git a/extractor/src/test/java/org/schabi/newpipe/FileUtils.java b/extractor/src/test/java/org/schabi/newpipe/FileUtils.java
new file mode 100644
index 00000000..5c55a2fb
--- /dev/null
+++ b/extractor/src/test/java/org/schabi/newpipe/FileUtils.java
@@ -0,0 +1,94 @@
+package org.schabi.newpipe;
+
+import com.grack.nanojson.JsonArray;
+import com.grack.nanojson.JsonObject;
+import com.grack.nanojson.JsonWriter;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * Util class to write file to disk
+ *
+ * Can be used to debug and test, for example writing a service's JSON response
+ * (especially useful if the response provided by the service is not documented)
+ */
+public class FileUtils {
+
+ public static void createFile(String path, JsonObject content) throws IOException {
+ createFile(path, jsonObjToString(content));
+ }
+
+ public static void createFile(String path, JsonArray array) throws IOException {
+ createFile(path, jsonArrayToString(array));
+ }
+
+ /**
+ * Create a file given a path and its content. Create subdirectories if needed
+ *
+ * @param path the path to write the file, including the filename (and its extension)
+ * @param content the content to write
+ * @throws IOException
+ */
+ public static void createFile(final String path, final String content) throws IOException {
+ final String[] dirs = path.split("/");
+ if (dirs.length > 1) {
+ String pathWithoutFileName = path.replace(dirs[dirs.length - 1], "");
+ if (!Files.exists(Paths.get(pathWithoutFileName))) { //create dirs if they don't exist
+ if (!new File(pathWithoutFileName).mkdirs()) {
+ throw new IOException("An error occurred while creating directories");
+ }
+ }
+ }
+ writeFile(path, content);
+ }
+
+ /**
+ * Write a file to disk
+ *
+ * @param filename the file name (and its extension if wanted)
+ * @param content the content to write
+ * @throws IOException
+ */
+ private static void writeFile(final String filename, final String content) throws IOException {
+ final BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
+ writer.write(content);
+ writer.flush();
+ writer.close();
+ }
+
+ /**
+ * Resolves the test resource file based on its filename. Looks in
+ * {@code extractor/src/test/resources/} and {@code src/test/resources/}
+ * @param filename the resource filename
+ * @return the resource file
+ */
+ public static File resolveTestResource(final String filename) {
+ final File file = new File("extractor/src/test/resources/" + filename);
+ if (file.exists()) {
+ return file;
+ } else {
+ return new File("src/test/resources/" + filename);
+ }
+ }
+
+ /**
+ * Convert a JSON object to String
+ * toString() does not produce a valid JSON string
+ */
+ public static String jsonObjToString(JsonObject object) {
+ return JsonWriter.string(object);
+ }
+
+ /**
+ * Convert a JSON array to String
+ * toString() does not produce a valid JSON string
+ */
+ public static String jsonArrayToString(JsonArray array) {
+ return JsonWriter.string(array);
+ }
+}
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/ExtractorAsserts.java b/extractor/src/test/java/org/schabi/newpipe/extractor/ExtractorAsserts.java
index 1006f7b3..69e10b0e 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/ExtractorAsserts.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/ExtractorAsserts.java
@@ -1,12 +1,19 @@
package org.schabi.newpipe.extractor;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
public class ExtractorAsserts {
public static void assertEmptyErrors(String message, List errors) {
@@ -56,4 +63,22 @@ public class ExtractorAsserts {
assertTrue(message, stringToCheck.isEmpty());
}
}
+
+ public static void assertAtLeast(long expected, long actual) {
+ assertTrue(actual + " is not at least " + expected, actual >= expected);
+ }
+
+ // this assumes that sorting a and b in-place is not an issue, so it's only intended for tests
+ public static void assertEqualsOrderIndependent(List expected, List actual) {
+ if (expected == null) {
+ assertNull(actual);
+ return;
+ } else {
+ assertNotNull(actual);
+ }
+
+ Collections.sort(expected);
+ Collections.sort(actual);
+ assertEquals(expected, actual);
+ }
}
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/NewPipeTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/NewPipeTest.java
index bdad6cb6..5dbc4317 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/NewPipeTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/NewPipeTest.java
@@ -6,6 +6,7 @@ import java.util.HashSet;
import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.NewPipe.getServiceByUrl;
+import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
public class NewPipeTest {
@@ -39,8 +40,10 @@ public class NewPipeTest {
assertEquals(getServiceByUrl("https://www.youtube.com/watch?v=_r6CgaFNAGg"), YouTube);
assertEquals(getServiceByUrl("https://www.youtube.com/channel/UCi2bIyFtz-JdI-ou8kaqsqg"), YouTube);
assertEquals(getServiceByUrl("https://www.youtube.com/playlist?list=PLRqwX-V7Uu6ZiZxtDDRCi6uhfTH4FilpH"), YouTube);
+ assertEquals(getServiceByUrl("https://www.google.it/url?sa=t&rct=j&q=&esrc=s&cd=&cad=rja&uact=8&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DHu80uDzh8RY&source=video"), YouTube);
- assertNotEquals(getServiceByUrl("https://soundcloud.com/pegboardnerds"), YouTube);
+ assertEquals(getServiceByUrl("https://soundcloud.com/pegboardnerds"), SoundCloud);
+ assertEquals(getServiceByUrl("https://www.google.com/url?sa=t&url=https%3A%2F%2Fsoundcloud.com%2Fciaoproduction&rct=j&q=&esrc=s&source=web&cd="), SoundCloud);
}
@Test
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/BaseStreamExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/BaseStreamExtractorTest.java
new file mode 100644
index 00000000..40356ceb
--- /dev/null
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/BaseStreamExtractorTest.java
@@ -0,0 +1,35 @@
+package org.schabi.newpipe.extractor.services;
+
+public interface BaseStreamExtractorTest extends BaseExtractorTest {
+ void testStreamType() throws Exception;
+ void testUploaderName() throws Exception;
+ void testUploaderUrl() throws Exception;
+ void testUploaderAvatarUrl() throws Exception;
+ void testSubChannelName() throws Exception;
+ void testSubChannelUrl() throws Exception;
+ void testSubChannelAvatarUrl() throws Exception;
+ void testThumbnailUrl() throws Exception;
+ void testDescription() throws Exception;
+ void testLength() throws Exception;
+ void testTimestamp() throws Exception;
+ void testViewCount() throws Exception;
+ void testUploadDate() throws Exception;
+ void testTextualUploadDate() throws Exception;
+ void testLikeCount() throws Exception;
+ void testDislikeCount() throws Exception;
+ void testRelatedStreams() throws Exception;
+ void testAgeLimit() throws Exception;
+ void testErrorMessage() throws Exception;
+ void testAudioStreams() throws Exception;
+ void testVideoStreams() throws Exception;
+ void testSubtitles() throws Exception;
+ void testGetDashMpdUrl() throws Exception;
+ void testFrames() throws Exception;
+ void testHost() throws Exception;
+ void testPrivacy() throws Exception;
+ void testCategory() throws Exception;
+ void testLicence() throws Exception;
+ void testLanguageInfo() throws Exception;
+ void testTags() throws Exception;
+ void testSupportInfo() throws Exception;
+}
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java
new file mode 100644
index 00000000..837ce603
--- /dev/null
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java
@@ -0,0 +1,382 @@
+package org.schabi.newpipe.extractor.services;
+
+import org.junit.Test;
+import org.schabi.newpipe.extractor.MediaFormat;
+import org.schabi.newpipe.extractor.localization.DateWrapper;
+import org.schabi.newpipe.extractor.stream.AudioStream;
+import org.schabi.newpipe.extractor.stream.Description;
+import org.schabi.newpipe.extractor.stream.Frameset;
+import org.schabi.newpipe.extractor.stream.StreamExtractor;
+import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
+import org.schabi.newpipe.extractor.stream.StreamType;
+import org.schabi.newpipe.extractor.stream.SubtitlesStream;
+import org.schabi.newpipe.extractor.stream.VideoStream;
+
+import javax.annotation.Nullable;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.schabi.newpipe.extractor.ExtractorAsserts.assertAtLeast;
+import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEqualsOrderIndependent;
+import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl;
+import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsValidUrl;
+import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestListOfItems;
+
+/**
+ * Test for {@link StreamExtractor}
+ */
+public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest
+ implements BaseStreamExtractorTest {
+
+ public abstract StreamType expectedStreamType();
+ public abstract String expectedUploaderName();
+ public abstract String expectedUploaderUrl();
+ public String expectedSubChannelName() { return ""; } // default: there is no subchannel
+ public String expectedSubChannelUrl() { return ""; } // default: there is no subchannel
+ public abstract List expectedDescriptionContains(); // e.g. for full links
+ public abstract long expectedLength();
+ public long expectedTimestamp() { return 0; } // default: there is no timestamp
+ public abstract long expectedViewCountAtLeast();
+ @Nullable public abstract String expectedUploadDate(); // format: "yyyy-MM-dd HH:mm:ss.SSS"
+ @Nullable public abstract String expectedTextualUploadDate();
+ public abstract long expectedLikeCountAtLeast(); // return -1 if ratings are disabled
+ public abstract long expectedDislikeCountAtLeast(); // return -1 if ratings are disabled
+ public boolean expectedHasRelatedStreams() { return true; } // default: there are related videos
+ public int expectedAgeLimit() { return StreamExtractor.NO_AGE_LIMIT; } // default: no limit
+ @Nullable public String expectedErrorMessage() { return null; } // default: no error message
+ public boolean expectedHasVideoStreams() { return true; } // default: there are video streams
+ public boolean expectedHasAudioStreams() { return true; } // default: there are audio streams
+ public boolean expectedHasSubtitles() { return true; } // default: there are subtitles streams
+ @Nullable public String expectedDashMpdUrlContains() { return null; } // default: no dash mpd
+ public boolean expectedHasFrames() { return true; } // default: there are frames
+ public String expectedHost() { return ""; } // default: no host for centralized platforms
+ public String expectedPrivacy() { return ""; } // default: no privacy policy available
+ public String expectedCategory() { return ""; } // default: no category
+ public String expectedLicence() { return ""; } // default: no licence
+ public Locale expectedLanguageInfo() { return null; } // default: no language info available
+ public List expectedTags() { return Collections.emptyList(); } // default: no tags
+ public String expectedSupportInfo() { return ""; } // default: no support info available
+
+ @Test
+ @Override
+ public void testStreamType() throws Exception {
+ assertEquals(expectedStreamType(), extractor().getStreamType());
+ }
+
+ @Test
+ @Override
+ public void testUploaderName() throws Exception {
+ assertEquals(expectedUploaderName(), extractor().getUploaderName());
+ }
+
+ @Test
+ @Override
+ public void testUploaderUrl() throws Exception {
+ final String uploaderUrl = extractor().getUploaderUrl();
+ assertIsSecureUrl(uploaderUrl);
+ assertEquals(expectedUploaderUrl(), uploaderUrl);
+ }
+
+ @Test
+ @Override
+ public void testUploaderAvatarUrl() throws Exception {
+ assertIsSecureUrl(extractor().getUploaderAvatarUrl());
+ }
+
+ @Test
+ @Override
+ public void testSubChannelName() throws Exception {
+ assertEquals(expectedSubChannelName(), extractor().getSubChannelName());
+ }
+
+ @Test
+ @Override
+ public void testSubChannelUrl() throws Exception {
+ final String subChannelUrl = extractor().getSubChannelUrl();
+ assertEquals(expectedSubChannelUrl(), subChannelUrl);
+
+ if (!expectedSubChannelUrl().isEmpty()) {
+ // this stream has a subchannel
+ assertIsSecureUrl(subChannelUrl);
+ }
+ }
+
+ @Test
+ @Override
+ public void testSubChannelAvatarUrl() throws Exception {
+ if (expectedSubChannelName().isEmpty() && expectedSubChannelUrl().isEmpty()) {
+ // this stream has no subchannel
+ assertEquals("", extractor().getSubChannelAvatarUrl());
+ } else {
+ // this stream has a subchannel
+ assertIsSecureUrl(extractor().getSubChannelAvatarUrl());
+ }
+ }
+
+ @Test
+ @Override
+ public void testThumbnailUrl() throws Exception {
+ assertIsSecureUrl(extractor().getThumbnailUrl());
+ }
+
+ @Test
+ @Override
+ public void testDescription() throws Exception {
+ final Description description = extractor().getDescription();
+ assertNotNull(description);
+ assertFalse("description is empty", description.getContent().isEmpty());
+
+ for (final String s : expectedDescriptionContains()) {
+ assertThat(description.getContent(), containsString(s));
+ }
+ }
+
+ @Test
+ @Override
+ public void testLength() throws Exception {
+ assertEquals(expectedLength(), extractor().getLength());
+ }
+
+ @Test
+ @Override
+ public void testTimestamp() throws Exception {
+ assertEquals(expectedTimestamp(), extractor().getTimeStamp());
+ }
+
+ @Test
+ @Override
+ public void testViewCount() throws Exception {
+ assertAtLeast(expectedViewCountAtLeast(), extractor().getViewCount());
+ }
+
+ @Test
+ @Override
+ public void testUploadDate() throws Exception {
+ final DateWrapper dateWrapper = extractor().getUploadDate();
+
+ if (expectedUploadDate() == null) {
+ assertNull(dateWrapper);
+ } else {
+ assertNotNull(dateWrapper);
+
+ final LocalDateTime expectedDateTime = LocalDateTime.parse(expectedUploadDate(),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
+ final LocalDateTime actualDateTime = dateWrapper.offsetDateTime().toLocalDateTime();
+
+ assertEquals(expectedDateTime, actualDateTime);
+ }
+ }
+
+ @Test
+ @Override
+ public void testTextualUploadDate() throws Exception {
+ assertEquals(expectedTextualUploadDate(), extractor().getTextualUploadDate());
+ }
+
+ @Test
+ @Override
+ public void testLikeCount() throws Exception {
+ if (expectedLikeCountAtLeast() == -1) {
+ assertEquals(-1, extractor().getLikeCount());
+ } else {
+ assertAtLeast(expectedLikeCountAtLeast(), extractor().getLikeCount());
+ }
+ }
+
+ @Test
+ @Override
+ public void testDislikeCount() throws Exception {
+ if (expectedDislikeCountAtLeast() == -1) {
+ assertEquals(-1, extractor().getDislikeCount());
+ } else {
+ assertAtLeast(expectedDislikeCountAtLeast(), extractor().getDislikeCount());
+ }
+ }
+
+ @Test
+ @Override
+ public void testRelatedStreams() throws Exception {
+ final StreamInfoItemsCollector relatedStreams = extractor().getRelatedStreams();
+
+ if (expectedHasRelatedStreams()) {
+ assertNotNull(relatedStreams);
+ defaultTestListOfItems(extractor().getService(), relatedStreams.getItems(),
+ relatedStreams.getErrors());
+ } else {
+ assertNull(relatedStreams);
+ }
+ }
+
+ @Test
+ @Override
+ public void testAgeLimit() throws Exception {
+ assertEquals(expectedAgeLimit(), extractor().getAgeLimit());
+ }
+
+ @Test
+ @Override
+ public void testErrorMessage() throws Exception {
+ assertEquals(expectedErrorMessage(), extractor().getErrorMessage());
+ }
+
+ @Test
+ @Override
+ public void testVideoStreams() throws Exception {
+ final List videoStreams = extractor().getVideoStreams();
+ final List videoOnlyStreams = extractor().getVideoOnlyStreams();
+ assertNotNull(videoStreams);
+ assertNotNull(videoOnlyStreams);
+ videoStreams.addAll(videoOnlyStreams);
+
+ if (expectedHasVideoStreams()) {
+ assertFalse(videoStreams.isEmpty());
+
+ for (final VideoStream stream : videoStreams) {
+ assertIsSecureUrl(stream.getUrl());
+ assertFalse(stream.getResolution().isEmpty());
+
+ final int formatId = stream.getFormatId();
+ // see MediaFormat: video stream formats range from 0 to 0x100
+ assertTrue("format id does not fit a video stream: " + formatId,
+ 0 <= formatId && formatId < 0x100);
+ }
+ } else {
+ assertTrue(videoStreams.isEmpty());
+ }
+ }
+
+ @Test
+ @Override
+ public void testAudioStreams() throws Exception {
+ final List audioStreams = extractor().getAudioStreams();
+ assertNotNull(audioStreams);
+
+ if (expectedHasAudioStreams()) {
+ assertFalse(audioStreams.isEmpty());
+
+ for (final AudioStream stream : audioStreams) {
+ assertIsSecureUrl(stream.getUrl());
+
+ final int formatId = stream.getFormatId();
+ // see MediaFormat: video stream formats range from 0x100 to 0x1000
+ assertTrue("format id does not fit an audio stream: " + formatId,
+ 0x100 <= formatId && formatId < 0x1000);
+ }
+ } else {
+ assertTrue(audioStreams.isEmpty());
+ }
+ }
+
+ @Test
+ @Override
+ public void testSubtitles() throws Exception {
+ final List subtitles = extractor().getSubtitlesDefault();
+ assertNotNull(subtitles);
+
+ if (expectedHasSubtitles()) {
+ assertFalse(subtitles.isEmpty());
+
+ for (final SubtitlesStream stream : subtitles) {
+ assertIsSecureUrl(stream.getUrl());
+
+ final int formatId = stream.getFormatId();
+ // see MediaFormat: video stream formats range from 0x1000 to 0x10000
+ assertTrue("format id does not fit a subtitles stream: " + formatId,
+ 0x1000 <= formatId && formatId < 0x10000);
+ }
+ } else {
+ assertTrue(subtitles.isEmpty());
+
+ final MediaFormat[] formats = {MediaFormat.VTT, MediaFormat.TTML, MediaFormat.SRT,
+ MediaFormat.TRANSCRIPT1, MediaFormat.TRANSCRIPT2, MediaFormat.TRANSCRIPT3};
+ for (final MediaFormat format : formats) {
+ final List formatSubtitles = extractor().getSubtitles(format);
+ assertNotNull(formatSubtitles);
+ assertTrue(formatSubtitles.isEmpty());
+ }
+ }
+ }
+
+ @Override
+ public void testGetDashMpdUrl() throws Exception {
+ final String dashMpdUrl = extractor().getDashMpdUrl();
+ if (expectedDashMpdUrlContains() == null) {
+ assertNotNull(dashMpdUrl);
+ assertTrue(dashMpdUrl.isEmpty());
+ } else {
+ assertIsSecureUrl(dashMpdUrl);
+ assertThat(extractor().getDashMpdUrl(), containsString(expectedDashMpdUrlContains()));
+ }
+ }
+
+ @Test
+ @Override
+ public void testFrames() throws Exception {
+ final List