Skip to content

Commit b4f23a3

Browse files
[SoundCloud] Refactor SoundcloudStreamExtractorTest to use test cases; fix failing tests
1 parent 6dbef5f commit b4f23a3

11 files changed

Lines changed: 508 additions & 87 deletions

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ allprojects {
3131
jsr305Version = "3.0.2"
3232
junitVersion = "5.12.1"
3333
checkstyleVersion = "10.4"
34+
immutablesVersion = "2.10.1"
3435
}
3536
}
3637

extractor/build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,12 @@ dependencies {
4646

4747
testImplementation "com.squareup.okhttp3:okhttp:4.12.0"
4848
testImplementation 'com.google.code.gson:gson:2.12.1'
49+
testImplementation "org.immutables:value:$immutablesVersion"
50+
testAnnotationProcessor "org.immutables:value:$immutablesVersion"
51+
compileOnly "org.immutables:value:$immutablesVersion"
52+
4953
}
54+
55+
repositories {
56+
mavenCentral()
57+
}

extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,12 @@ public OffsetDateTime offsetDateTime() {
6969
public boolean isApproximation() {
7070
return isApproximation;
7171
}
72+
73+
@Override
74+
public String toString() {
75+
return "DateWrapper{"
76+
+ "offsetDateTime=" + offsetDateTime
77+
+ ", isApproximation=" + isApproximation
78+
+ '}';
79+
}
7280
}

extractor/src/test/java/org/schabi/newpipe/extractor/ExtractorAsserts.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.Collections;
99
import java.util.List;
1010
import java.util.Set;
11+
import java.util.regex.Pattern;
1112
import java.util.stream.Collectors;
1213

1314
import javax.annotation.Nonnull;
@@ -168,6 +169,13 @@ public static void assertContains(
168169
"'" + shouldBeContained + "' should be contained inside '" + container + "'");
169170
}
170171

172+
public static void assertMatches(final Pattern pattern, final String input) {
173+
assertNotNull(pattern, "pattern is null");
174+
assertNotNull(input, "input is null");
175+
assertTrue(pattern.matcher(input).find(),
176+
"Pattern '" + pattern + "' not found in input '" + input + "'");
177+
}
178+
171179
public static void assertTabsContain(@Nonnull final List<ListLinkHandler> tabs,
172180
@Nonnull final String... expectedTabs) {
173181
final Set<String> tabSet = tabs.stream()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.schabi.newpipe.extractor;
2+
3+
import org.immutables.value.Value;
4+
5+
import java.lang.annotation.ElementType;
6+
import java.lang.annotation.Target;
7+
8+
// CHECKSTYLE:OFF
9+
10+
/**
11+
* Custom style for generated Immutables.
12+
* See <a href="https://immutables.github.io/style.html">Style</a>.
13+
* <p>
14+
* - Abstract types start with 'I' (e.g., IExample).<p>
15+
* - Concrete immutable types do not have a prefix (e.g., Example).<p>
16+
* - Getters are prefixed with 'get', 'is', or no prefix.<p>
17+
* - <a href="https://immutables.github.io/immutable.html#strict-builder">Strict builder pattern is enforced.</a><p>
18+
*/
19+
// CHECKSTYLE:ON
20+
@Target({ElementType.PACKAGE, ElementType.TYPE})
21+
@Value.Style(
22+
get = {"get*", "is*", "*"}, // Methods matching these prefixes will be used as getters.
23+
// Methods matching these patterns can NOT be used as setters.
24+
typeAbstract = {"I*"}, // Abstract types start with I
25+
typeImmutable = "*", // Generated concrete Immutable types will not have the I prefix
26+
visibility = Value.Style.ImplementationVisibility.PUBLIC,
27+
strictBuilder = true,
28+
defaultAsDefault = true, // https://immutables.github.io/immutable.html#default-attributes
29+
jdkOnly = true
30+
)
31+
public @interface ImmutableStyle { }
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.schabi.newpipe.extractor.services;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.schabi.newpipe.extractor.ExtractorAsserts;
5+
import org.schabi.newpipe.extractor.MediaFormat;
6+
import org.schabi.newpipe.extractor.services.testcases.SoundcloudStreamExtractorTestCase;
7+
import org.schabi.newpipe.extractor.stream.AudioStream;
8+
import org.schabi.newpipe.extractor.stream.DeliveryMethod;
9+
10+
import java.util.List;
11+
import java.util.regex.Pattern;
12+
13+
import static org.junit.jupiter.api.Assertions.*;
14+
15+
public abstract class ParameterisedDefaultSoundcloudStreamExtractorTest
16+
extends ParameterisedDefaultStreamExtractorTest<SoundcloudStreamExtractorTestCase> {
17+
protected ParameterisedDefaultSoundcloudStreamExtractorTest(SoundcloudStreamExtractorTestCase testCase) {
18+
super(testCase);
19+
}
20+
21+
final Pattern mp3CdnUrlPattern = Pattern.compile("-media\\.sndcdn\\.com/[a-zA-Z0-9]{12}\\.128\\.mp3");
22+
23+
@Override
24+
@Test
25+
public void testAudioStreams() throws Exception {
26+
super.testAudioStreams();
27+
final List<AudioStream> audioStreams = extractor.getAudioStreams();
28+
assertEquals(3, audioStreams.size()); // 2 MP3 streams (1 progressive, 1 HLS) and 1 OPUS
29+
audioStreams.forEach(audioStream -> {
30+
final DeliveryMethod deliveryMethod = audioStream.getDeliveryMethod();
31+
final String mediaUrl = audioStream.getContent();
32+
if (audioStream.getFormat() == MediaFormat.OPUS) {
33+
assertSame(DeliveryMethod.HLS, deliveryMethod,
34+
"Wrong delivery method for stream " + audioStream.getId() + ": "
35+
+ deliveryMethod);
36+
// Assert it's an OPUS 64 kbps media playlist URL which comes from an HLS
37+
// SoundCloud CDN
38+
ExtractorAsserts.assertContains("-hls-opus-media.sndcdn.com", mediaUrl);
39+
ExtractorAsserts.assertContains(".64.opus", mediaUrl);
40+
} else if (audioStream.getFormat() == MediaFormat.MP3) {
41+
if (deliveryMethod == DeliveryMethod.PROGRESSIVE_HTTP) {
42+
// Assert it's a MP3 128 kbps media URL which comes from a progressive
43+
// SoundCloud CDN
44+
ExtractorAsserts.assertMatches(mp3CdnUrlPattern, mediaUrl);
45+
} else if (deliveryMethod == DeliveryMethod.HLS) {
46+
// Assert it's a MP3 128 kbps media HLS playlist URL which comes from an HLS
47+
// SoundCloud CDN
48+
ExtractorAsserts.assertContains("-hls-media.sndcdn.com", mediaUrl);
49+
ExtractorAsserts.assertContains(".128.mp3", mediaUrl);
50+
} else {
51+
fail("Wrong delivery method for stream " + audioStream.getId() + ": "
52+
+ deliveryMethod);
53+
}
54+
}
55+
});
56+
}
57+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.schabi.newpipe.extractor.services;
2+
3+
import org.junit.jupiter.api.BeforeAll;
4+
import org.junit.jupiter.api.TestInstance;
5+
import org.schabi.newpipe.downloader.DownloaderTestImpl;
6+
import org.schabi.newpipe.extractor.MetaInfo;
7+
import org.schabi.newpipe.extractor.NewPipe;
8+
import org.schabi.newpipe.extractor.StreamingService;
9+
import org.schabi.newpipe.extractor.services.testcases.DefaultStreamExtractorTestCase;
10+
import org.schabi.newpipe.extractor.stream.StreamExtractor;
11+
import org.schabi.newpipe.extractor.stream.StreamType;
12+
13+
import javax.annotation.Nullable;
14+
import java.util.List;
15+
import java.util.Locale;
16+
17+
/**
18+
* Test for {@link StreamExtractor}
19+
*/
20+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
21+
public abstract class ParameterisedDefaultStreamExtractorTest<TTestCase extends DefaultStreamExtractorTestCase> extends DefaultStreamExtractorTest {
22+
protected TTestCase testCase;
23+
protected StreamExtractor extractor;
24+
25+
protected ParameterisedDefaultStreamExtractorTest(TTestCase testCase)
26+
{
27+
this.testCase = testCase;
28+
}
29+
30+
@BeforeAll
31+
public void setUp() throws Exception {
32+
if (extractor != null) {
33+
throw new IllegalStateException("extractor already initialized before BeforeAll");
34+
}
35+
NewPipe.init(DownloaderTestImpl.getInstance());
36+
extractor = testCase.service().getStreamExtractor(testCase.url());
37+
extractor.fetchPage();
38+
}
39+
40+
///
41+
/// DefaultExtractorTest overrides
42+
///
43+
44+
@Override public StreamExtractor extractor() throws Exception { return extractor; }
45+
46+
@Override public StreamingService expectedService() throws Exception { return testCase.service(); }
47+
@Override public String expectedName() throws Exception { return testCase.name(); }
48+
@Override public String expectedId() throws Exception { return testCase.id(); }
49+
@Override public String expectedUrlContains() throws Exception { return testCase.urlContains(); }
50+
@Override public String expectedOriginalUrlContains() throws Exception { return testCase.originalUrlContains(); }
51+
52+
///
53+
/// DefaultStreamExtractorTest overrides
54+
///
55+
@Override public StreamType expectedStreamType() { return testCase.streamType(); }
56+
@Override public String expectedUploaderName() { return testCase.uploaderName(); }
57+
@Override public String expectedUploaderUrl() { return testCase.uploaderUrl(); }
58+
@Override public boolean expectedUploaderVerified() { return testCase.uploaderVerified(); }
59+
@Override public long expectedUploaderSubscriberCountAtLeast() { return testCase.uploaderSubscriberCountAtLeast(); }
60+
@Override public String expectedSubChannelName() { return testCase.subChannelName(); }
61+
@Override public String expectedSubChannelUrl() { return testCase.subChannelUrl(); }
62+
@Override public boolean expectedDescriptionIsEmpty() { return testCase.descriptionIsEmpty(); }
63+
@Override public List<String> expectedDescriptionContains() { return testCase.descriptionContains(); }
64+
@Override public long expectedLength() { return testCase.length(); }
65+
@Override public long expectedTimestamp() { return testCase.timestamp(); }
66+
@Override public long expectedViewCountAtLeast() { return testCase.viewCountAtLeast(); }
67+
@Override @Nullable public String expectedUploadDate() { return testCase.uploadDate(); }
68+
@Override @Nullable public String expectedTextualUploadDate() { return testCase.textualUploadDate(); }
69+
@Override public long expectedLikeCountAtLeast() { return testCase.likeCountAtLeast(); }
70+
@Override public long expectedDislikeCountAtLeast() { return testCase.dislikeCountAtLeast(); }
71+
@Override public boolean expectedHasRelatedItems() { return testCase.hasRelatedItems(); }
72+
@Override public int expectedAgeLimit() { return testCase.ageLimit(); }
73+
@Override @Nullable public String expectedErrorMessage() { return testCase.errorMessage(); }
74+
@Override public boolean expectedHasVideoStreams() { return testCase.hasVideoStreams(); }
75+
@Override public boolean expectedHasAudioStreams() { return testCase.hasAudioStreams(); }
76+
@Override public boolean expectedHasSubtitles() { return testCase.hasSubtitles(); }
77+
@Override @Nullable public String expectedDashMpdUrlContains() { return testCase.dashMpdUrlContains(); }
78+
@Override public boolean expectedHasFrames() { return testCase.hasFrames(); }
79+
@Override public String expectedHost() { return testCase.host(); }
80+
@Override public StreamExtractor.Privacy expectedPrivacy() { return testCase.privacy(); }
81+
@Override public String expectedCategory() { return testCase.category(); }
82+
@Override public String expectedLicence() { return testCase.licence(); }
83+
@Override public Locale expectedLanguageInfo() { return testCase.languageInfo(); }
84+
@Override public List<String> expectedTags() { return testCase.tags(); }
85+
@Override public String expectedSupportInfo() { return testCase.supportInfo(); }
86+
@Override public int expectedStreamSegmentsCount() { return testCase.streamSegmentsCount(); }
87+
@Override public List<MetaInfo> expectedMetaInfo() { return testCase.metaInfo(); }
88+
}

0 commit comments

Comments
 (0)