From e51067177edf0ad4034c330f69c2dc7c92a4df15 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Mon, 14 Aug 2023 23:05:38 +0200 Subject: [PATCH] Add tests for new methods retrieving MediaFormats Fix failing tests --- .../newpipe/util/StreamItemAdapterTest.kt | 159 ++++++++++++++++++ .../newpipe/util/StreamItemAdapter.java | 14 +- 2 files changed, 169 insertions(+), 4 deletions(-) diff --git a/app/src/androidTest/java/org/schabi/newpipe/util/StreamItemAdapterTest.kt b/app/src/androidTest/java/org/schabi/newpipe/util/StreamItemAdapterTest.kt index c67564232..13c27aec9 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/util/StreamItemAdapterTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/util/StreamItemAdapterTest.kt @@ -12,15 +12,21 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import androidx.test.internal.runner.junit4.statement.UiThreadStatement import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.schabi.newpipe.R import org.schabi.newpipe.extractor.MediaFormat +import org.schabi.newpipe.extractor.downloader.Response import org.schabi.newpipe.extractor.stream.AudioStream import org.schabi.newpipe.extractor.stream.Stream import org.schabi.newpipe.extractor.stream.SubtitlesStream import org.schabi.newpipe.extractor.stream.VideoStream +import org.schabi.newpipe.util.StreamItemAdapter.StreamInfoWrapper @MediumTest @RunWith(AndroidJUnit4::class) @@ -123,6 +129,101 @@ class StreamItemAdapterTest { } } + @Test + fun retrieveMediaFormatFromFileTypeHeaders() { + val streams = getIncompleteAudioStreams(5) + val wrapper = StreamInfoWrapper(streams, context) + val retrieveMediaFormat = { stream: AudioStream, response: Response -> + StreamInfoWrapper.retrieveMediaFormatFromFileTypeHeaders(stream, wrapper, response) + } + val helper = AssertionHelper(streams, wrapper, retrieveMediaFormat) + + helper.assertInvalidResponse(getResponse(mapOf(Pair("content-length", "mp3"))), 0) + helper.assertInvalidResponse(getResponse(mapOf(Pair("file-type", "mp0"))), 1) + + helper.assertValidResponse(getResponse(mapOf(Pair("x-amz-meta-file-type", "aiff"))), 2, MediaFormat.AIFF) + helper.assertValidResponse(getResponse(mapOf(Pair("file-type", "mp3"))), 3, MediaFormat.MP3) + } + + @Test + fun retrieveMediaFormatFromContentDispositionHeader() { + val streams = getIncompleteAudioStreams(11) + val wrapper = StreamInfoWrapper(streams, context) + val retrieveMediaFormat = { stream: AudioStream, response: Response -> + StreamInfoWrapper.retrieveMediaFormatFromContentDispositionHeader(stream, wrapper, response) + } + val helper = AssertionHelper(streams, wrapper, retrieveMediaFormat) + + helper.assertInvalidResponse(getResponse(mapOf(Pair("content-length", "mp3"))), 0) + helper.assertInvalidResponse( + getResponse(mapOf(Pair("Content-Disposition", "filename=\"train.png\""))), 1 + ) + helper.assertInvalidResponse( + getResponse(mapOf(Pair("Content-Disposition", "form-data; name=\"data.csv\""))), 2 + ) + helper.assertInvalidResponse( + getResponse(mapOf(Pair("Content-Disposition", "form-data; filename=\"data.csv\""))), 3 + ) + helper.assertInvalidResponse( + getResponse(mapOf(Pair("Content-Disposition", "form-data; name=\"fieldName\"; filename*=\"filename.jpg\""))), 4 + ) + + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Disposition", "filename=\"train.ogg\""))), + 5, MediaFormat.OGG + ) + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Disposition", "some-form-data; filename=\"audio.flac\""))), + 6, MediaFormat.FLAC + ) + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Disposition", "form-data; name=\"audio.aiff\"; filename=\"audio.aiff\""))), + 7, MediaFormat.AIFF + ) + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Disposition", "form-data; name=\"alien?\"; filename*=UTF-8''%CE%B1%CE%BB%CE%B9%CF%B5%CE%BD.m4a"))), + 8, MediaFormat.M4A + ) + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Disposition", "form-data; name=\"audio.mp3\"; filename=\"audio.opus\"; filename*=UTF-8''alien.opus"))), + 9, MediaFormat.OPUS + ) + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Disposition", "form-data; name=\"audio.mp3\"; filename=\"audio.opus\"; filename*=\"UTF-8''alien.opus\""))), + 10, MediaFormat.OPUS + ) + } + + @Test + fun retrieveMediaFormatFromContentTypeHeader() { + val streams = getIncompleteAudioStreams(10) + val wrapper = StreamInfoWrapper(streams, context) + val retrieveMediaFormat = { stream: AudioStream, response: Response -> + StreamInfoWrapper.retrieveMediaFormatFromContentTypeHeader(stream, wrapper, response) + } + val helper = AssertionHelper(streams, wrapper, retrieveMediaFormat) + + helper.assertInvalidResponse(getResponse(mapOf(Pair("content-length", "984501"))), 0) + helper.assertInvalidResponse(getResponse(mapOf(Pair("Content-Type", "audio/xyz"))), 1) + helper.assertInvalidResponse(getResponse(mapOf(Pair("Content-Type", "mp3"))), 2) + helper.assertInvalidResponse(getResponse(mapOf(Pair("Content-Type", "mp3"))), 3) + helper.assertInvalidResponse(getResponse(mapOf(Pair("Content-Type", "audio/mpeg"))), 4) + helper.assertInvalidResponse(getResponse(mapOf(Pair("Content-Type", "audio/aif"))), 5) + + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Type", "audio/flac"))), 6, MediaFormat.FLAC + ) + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Type", "audio/wav"))), 7, MediaFormat.WAV + ) + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Type", "audio/opus"))), 8, MediaFormat.OPUS + ) + helper.assertValidResponse( + getResponse(mapOf(Pair("Content-Type", "audio/aiff"))), 9, MediaFormat.AIFF + ) + } + /** * @return a list of video streams, in which their video only property mirrors the provided * [videoOnly] vararg. @@ -161,6 +262,19 @@ class StreamItemAdapterTest { } ) + private fun getIncompleteAudioStreams(size: Int): List { + val list = ArrayList(size) + for (i in 1..size) { + list.add( + AudioStream.Builder() + .setId(Stream.ID_UNKNOWN) + .setContent("https://example.com/$i", true) + .build() + ) + } + return list + } + /** * Checks whether the item at [position] in the [spinner] has the correct icon visibility when * it is shown in normal mode (selected) and in dropdown mode (user is choosing one of a list). @@ -203,4 +317,49 @@ class StreamItemAdapterTest { put(index, secondaryStreamHelper) } } + + private fun getResponse(headers: Map): Response { + val listHeaders = HashMap>() + headers.forEach { entry -> + listHeaders[entry.key] = listOf(entry.value) + } + return Response(200, null, listHeaders, "", "") + } + + /** + * Helper class for assertion related to extractions of [MediaFormat]s. + */ + class AssertionHelper( + private val streams: List, + private val wrapper: StreamInfoWrapper, + private val retrieveMediaFormat: (stream: T, response: Response) -> Boolean + ) { + + /** + * Assert that an invalid response does not result in wrongly extracted [MediaFormat]. + */ + fun assertInvalidResponse( + response: Response, + index: Int + ) { + assertFalse( + "invalid header returns valid value", retrieveMediaFormat(streams[index], response) + ) + assertNull("Media format extracted although stated otherwise", wrapper.getFormat(index)) + } + + /** + * Assert that a valid response results in correctly extracted and handled [MediaFormat]. + */ + fun assertValidResponse( + response: Response, + index: Int, + format: MediaFormat + ) { + assertTrue( + "header was not recognized", retrieveMediaFormat(streams[index], response) + ) + assertEquals("Wrong media format extracted", format, wrapper.getFormat(index)) + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java index 04cd737b6..691b39403 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java @@ -13,6 +13,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.collection.SparseArrayCompat; import org.schabi.newpipe.DownloaderImpl; @@ -298,7 +299,8 @@ public class StreamItemAdapter extends BaseA * @param response the response of the head request for the given stream * @return {@code true} if the media format could be retrieved; {@code false} otherwise */ - private static boolean retrieveMediaFormat( + @VisibleForTesting + public static boolean retrieveMediaFormat( @NonNull final X stream, @NonNull final StreamInfoWrapper streamsWrapper, @NonNull final Response response) { @@ -308,7 +310,8 @@ public class StreamItemAdapter extends BaseA || retrieveMediaFormatFromContentTypeHeader(stream, streamsWrapper, response); } - private static boolean retrieveMediaFormatFromFileTypeHeaders( + @VisibleForTesting + public static boolean retrieveMediaFormatFromFileTypeHeaders( @NonNull final X stream, @NonNull final StreamInfoWrapper streamsWrapper, @NonNull final Response response) { @@ -342,6 +345,7 @@ public class StreamItemAdapter extends BaseA * otherwise {@code false} * @param */ + @VisibleForTesting public static boolean retrieveMediaFormatFromContentDispositionHeader( @NonNull final X stream, @NonNull final StreamInfoWrapper streamsWrapper, @@ -391,7 +395,8 @@ public class StreamItemAdapter extends BaseA return false; } - private static boolean retrieveMediaFormatFromContentTypeHeader( + @VisibleForTesting + public static boolean retrieveMediaFormatFromContentTypeHeader( @NonNull final X stream, @NonNull final StreamInfoWrapper streamsWrapper, @NonNull final Response response) { @@ -416,7 +421,8 @@ public class StreamItemAdapter extends BaseA public void resetInfo() { Arrays.fill(streamSizes, SIZE_UNSET); for (int i = 0; i < streamsList.size(); i++) { - streamFormats[i] = streamsList.get(i).getFormat(); + streamFormats[i] = streamsList.get(i) == null // test for invalid streams + ? null : streamsList.get(i).getFormat(); } }