Skip to content

Commit 07c3917

Browse files
committed
Add some documentation to tests
1 parent fda498e commit 07c3917

3 files changed

Lines changed: 44 additions & 12 deletions

File tree

app/src/androidTest/java/org/schabi/newpipe/InstrumentedTestUtil.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import androidx.preference.PreferenceManager
1010
import androidx.test.platform.app.InstrumentationRegistry
1111
import org.junit.Assert.fail
1212

13+
/**
14+
* Use this instead of passing contexts around in instrumented tests.
15+
*/
1316
val ctx: Context
1417
get() = InstrumentationRegistry.getInstrumentation().targetContext
1518

@@ -52,6 +55,9 @@ fun SemanticsNodeInteractionsProvider.onNodeWithContentDescription(
5255
return this.onNodeWithContentDescription(ctx.getString(text), substring, ignoreCase, useUnmergedTree)
5356
}
5457

58+
/**
59+
* Asserts that [value] is in the range [[l], [r]] (both extremes included).
60+
*/
5561
fun <T : Comparable<T>> assertInRange(l: T, r: T, value: T) {
5662
if (l > r) {
5763
fail("Invalid range passed to `assertInRange`: [$l, $r]")
@@ -61,6 +67,9 @@ fun <T : Comparable<T>> assertInRange(l: T, r: T, value: T) {
6167
}
6268
}
6369

70+
/**
71+
* Asserts that [value] is NOT in the range [[l], [r]] (both extremes included).
72+
*/
6473
fun <T : Comparable<T>> assertNotInRange(l: T, r: T, value: T) {
6574
if (l > r) {
6675
fail("Invalid range passed to `assertInRange`: [$l, $r]")

app/src/androidTest/java/org/schabi/newpipe/ui/components/menu/LongPressMenuTest.kt

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,24 @@ class LongPressMenuTest {
8080
RuleChain.emptyRuleChain()
8181
}
8282

83+
/**
84+
* Utility to build a [LongPressable] with dummy data for testing.
85+
*/
8386
private fun getLongPressable(
8487
title: String = "title",
8588
url: String? = "https://example.com",
8689
thumbnailUrl: String? = "android.resource://${ctx.packageName}/${R.drawable.placeholder_thumbnail_video}",
8790
uploader: String? = "uploader",
88-
uploaderUrl: String? = "https://example.com",
8991
viewCount: Long? = 42,
9092
streamType: StreamType? = StreamType.VIDEO_STREAM,
9193
uploadDate: Either<String, OffsetDateTime>? = Either.left("2026"),
9294
decoration: LongPressable.Decoration? = LongPressable.Decoration.Duration(9478)
93-
) = LongPressable(title, url, thumbnailUrl, uploader, uploaderUrl, viewCount, streamType, uploadDate, decoration)
95+
) = LongPressable(title, url, thumbnailUrl, uploader, viewCount, streamType, uploadDate, decoration)
9496

97+
/**
98+
* Sets up the [LongPressMenu] in the [composeRule] Compose content for running tests. Handles
99+
* setting dialog settings via shared preferences, and closing the dialog when it is dismissed.
100+
*/
95101
private fun setLongPressMenu(
96102
longPressable: LongPressable = getLongPressable(),
97103
longPressActions: List<LongPressAction> = LongPressAction.Type.entries.map { it.buildAction { } },
@@ -114,8 +120,10 @@ class LongPressMenuTest {
114120
}
115121
}
116122

117-
// the three tests below all call this function to ensure that the editor button is shown
118-
// independently of the long press menu contents
123+
/**
124+
* The three tests below all call this function to ensure that the editor button is shown
125+
* independently of the long press menu contents.
126+
*/
119127
private fun assertEditorIsEnteredAndExitedProperly() {
120128
composeRule.onNodeWithContentDescription(R.string.long_press_menu_enabled_actions_description)
121129
.assertDoesNotExist()
@@ -256,13 +264,16 @@ class LongPressMenuTest {
256264

257265
@Test
258266
fun testHeaderUploadDate1() {
267+
// here the upload date is an unparsed String we have to use as-is
268+
// (e.g. the extractor could not parse it)
259269
setLongPressMenu(getLongPressable(uploadDate = Either.left("abcd")))
260270
composeRule.onNodeWithText("abcd", substring = true)
261271
.assertIsDisplayed()
262272
}
263273

264274
@Test
265275
fun testHeaderUploadDate2() {
276+
// here the upload date is a proper OffsetDateTime that can be formatted properly
266277
val date = OffsetDateTime.now()
267278
.minus(2, ChronoUnit.HOURS)
268279
.minus(50, ChronoUnit.MILLIS)
@@ -382,7 +393,7 @@ class LongPressMenuTest {
382393
}
383394

384395
@Test
385-
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
396+
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N) // setDisplaySize not available on API < 24
386397
fun testHeaderSpansAllWidthIfSmallScreen() {
387398
onDevice().setDisplaySize(
388399
widthSizeClass = WidthSizeClass.COMPACT,
@@ -397,12 +408,13 @@ class LongPressMenuTest {
397408
val header = composeRule.onNodeWithTag("LongPressMenuHeader")
398409
.fetchSemanticsNode()
399410
.boundsInRoot
411+
// checks that the header is roughly as large as the row that contains it
400412
assertInRange(row.left, row.left + 24.dp.value, header.left)
401413
assertInRange(row.right - 24.dp.value, row.right, header.right)
402414
}
403415

404416
@Test
405-
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
417+
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N) // setDisplaySize not available on API < 24
406418
fun testHeaderIsNotFullWidthIfLargeScreen() {
407419
onDevice().setDisplaySize(
408420
widthSizeClass = WidthSizeClass.EXPANDED,
@@ -417,20 +429,25 @@ class LongPressMenuTest {
417429
val header = composeRule.onNodeWithTag("LongPressMenuHeader")
418430
.fetchSemanticsNode()
419431
.boundsInRoot
432+
// checks that the header is definitely smaller than the row that contains it
420433
assertInRange(row.left, row.left + 24.dp.value, header.left)
421-
assertNotInRange(row.right - 24.dp.value, row.right, header.right)
434+
assertNotInRange(row.right - 24.dp.value, Float.MAX_VALUE, header.right)
422435
}
423436

424-
// the tests below all call this function to test, under different conditions, that the shown
425-
// actions are the intersection between the available and the enabled actions
437+
/**
438+
* The tests below all call this function to test, under different conditions, that the shown
439+
* actions are the intersection between the available and the enabled actions.
440+
*/
426441
fun assertOnlyAndAllArrangedActionsDisplayed(
427442
availableActions: List<LongPressAction.Type>,
428443
actionArrangement: List<LongPressAction.Type>,
429444
expectedShownActions: List<LongPressAction.Type>
430445
) {
431446
setLongPressMenu(
432447
longPressActions = availableActions.map { it.buildAction {} },
433-
isHeaderEnabled = ((availableActions.size + actionArrangement.size) % 2) == 0,
448+
// whether the header is enabled or not shouldn't influence the result, so enable it
449+
// at random (but still deterministically)
450+
isHeaderEnabled = ((expectedShownActions + availableActions).sumOf { it.id } % 2) == 0,
434451
actionArrangement = actionArrangement
435452
)
436453
for (type in LongPressAction.Type.entries) {
@@ -551,7 +568,7 @@ class LongPressMenuTest {
551568
}
552569

553570
@Test
554-
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
571+
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N) // setDisplaySize not available on API < 24
555572
fun testAllActionsOnSmallScreenAreScrollable() {
556573
onDevice().setDisplaySize(
557574
widthSizeClass = WidthSizeClass.COMPACT,
@@ -620,6 +637,9 @@ class LongPressMenuTest {
620637
longPressActions = listOf(BackgroundShuffled.buildAction { delay(500) })
621638
)
622639

640+
// test that the loading circle appears while the action is being performed; note that there
641+
// is no way to test that the long press menu contents disappear, because in the current
642+
// implementation they just become hidden below the loading circle (with touches suppressed)
623643
composeRule.onNode(SemanticsMatcher.keyIsDefined(ProgressBarRangeInfo))
624644
.assertDoesNotExist()
625645
composeRule.onNodeWithText(BackgroundShuffled.label)
@@ -643,6 +663,8 @@ class LongPressMenuTest {
643663
)
644664
)
645665

666+
// make sure that a snackbar is shown after the dialog gets dismissed,
667+
// see https://stackoverflow.com/a/33245290
646668
onView(withId(com.google.android.material.R.id.snackbar_text))
647669
.check(doesNotExist())
648670
composeRule.onNodeWithText(BackgroundShuffled.label)

app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistDialog.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ protected void setStreamEntities(final List<StreamEntity> streamEntities) {
135135
*
136136
* @param context context used for accessing the database
137137
* @param streamEntities used for crating the dialog
138-
* @return the {@link Maybe} to subscribe to to obtain the correct {@link PlaylistDialog}
138+
* @return the {@link Maybe} to subscribe to to obtain the correct {@link PlaylistDialog}; the
139+
* function inside the subscribe() will be called on the main thread
139140
*/
140141
public static Maybe<PlaylistDialog> createCorrespondingDialog(
141142
final Context context,

0 commit comments

Comments
 (0)