Skip to content

Commit

Permalink
tests: ensure Widget Config initialized properly
Browse files Browse the repository at this point in the history
The following classes required a non-empty collection:
* `CardAnalysisWidgetConfig`
* `DeckPickerWidgetConfig`

Previously this was not handled in tests, and instead a
second call to `initializeUIComponents` was made

To fix this:

* define `initTask` and check for completion in tests

Prep for fixing flaky tests due to async collection access:
Issue 17010
  • Loading branch information
david-allison committed Sep 18, 2024
1 parent 3fd1c69 commit 5bf4326
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import android.view.View
import android.widget.Button
import androidx.activity.OnBackPressedCallback
import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
Expand All @@ -46,6 +47,7 @@ import com.ichi2.anki.snackbar.SnackbarBuilder
import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.widget.WidgetConfigScreenAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
Expand All @@ -67,6 +69,10 @@ class CardAnalysisWidgetConfig : AnkiActivity(), DeckSelectionListener, BaseSnac
private lateinit var onBackPressedCallback: OnBackPressedCallback
private val EXTRA_SELECTED_DECK_IDS = "card_analysis_widget_selected_deck_ids"

/** Tracks coroutine running [initializeUIComponents]: must be run on a non-empty collection */
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
internal lateinit var initTask: Job

override fun onCreate(savedInstanceState: Bundle?) {
if (showedActivityFailedScreen(savedInstanceState)) {
return
Expand Down Expand Up @@ -94,7 +100,7 @@ class CardAnalysisWidgetConfig : AnkiActivity(), DeckSelectionListener, BaseSnac
}

// Check if the collection is empty before proceeding and if the collection is empty, show a toast instead of the configuration view.
lifecycleScope.launch {
this.initTask = lifecycleScope.launch {
if (isCollectionEmpty()) {
Timber.w("Closing: Collection is empty")
showThemedToast(
Expand Down Expand Up @@ -126,7 +132,7 @@ class CardAnalysisWidgetConfig : AnkiActivity(), DeckSelectionListener, BaseSnac
showSnackbar(getString(messageResId))
}

fun initializeUIComponents() {
private fun initializeUIComponents() {
deckAdapter = WidgetConfigScreenAdapter { deck, _ ->
deckAdapter.removeDeck(deck.deckId)
showSnackbar(R.string.deck_removed_from_widget)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.activity.OnBackPressedCallback
import androidx.annotation.VisibleForTesting
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
Expand All @@ -47,6 +48,7 @@ import com.ichi2.anki.snackbar.SnackbarBuilder
import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.widget.WidgetConfigScreenAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
Expand All @@ -71,6 +73,10 @@ class DeckPickerWidgetConfig : AnkiActivity(), DeckSelectionListener, BaseSnackb
private var isAdapterObserverRegistered = false
private lateinit var onBackPressedCallback: OnBackPressedCallback

/** Tracks coroutine running [initializeUIComponents]: must be run on a non-empty collection */
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
internal lateinit var initTask: Job

override fun onCreate(savedInstanceState: Bundle?) {
if (showedActivityFailedScreen(savedInstanceState)) {
return
Expand Down Expand Up @@ -98,7 +104,7 @@ class DeckPickerWidgetConfig : AnkiActivity(), DeckSelectionListener, BaseSnackb
}

// Check if the collection is empty before proceeding and if the collection is empty, show a toast instead of the configuration view.
lifecycleScope.launch {
this.initTask = lifecycleScope.launch {
if (isCollectionEmpty()) {
Timber.w("Closing: Collection is empty")
showThemedToast(
Expand All @@ -125,7 +131,7 @@ class DeckPickerWidgetConfig : AnkiActivity(), DeckSelectionListener, BaseSnackb
showSnackbar(getString(messageResId))
}

fun initializeUIComponents() {
private fun initializeUIComponents() {
deckAdapter = WidgetConfigScreenAdapter { deck, position ->
deckAdapter.removeDeck(deck.deckId)
showSnackbar(R.string.deck_removed_from_widget)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.ichi2.anki.RobolectricTest
import com.ichi2.anki.dialogs.DeckSelectionDialog
import com.ichi2.widget.cardanalysis.CardAnalysisWidgetConfig
import com.ichi2.widget.cardanalysis.CardAnalysisWidgetPreferences
import kotlinx.coroutines.runBlocking
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Before
Expand All @@ -47,14 +48,16 @@ class CardAnalysisWidgetConfigTest : RobolectricTest() {
@Before
override fun setUp() {
super.setUp()
ensureNonEmptyCollection()

val intent = Intent(targetContext, CardAnalysisWidgetConfig::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 1)
}

activity = startActivityNormallyOpenCollectionWithIntent(CardAnalysisWidgetConfig::class.java, intent)

// Ensure deckAdapter is initialized
activity.initializeUIComponents()
runBlocking { activity.initTask.join() }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.ichi2.anki.RobolectricTest
import com.ichi2.anki.dialogs.DeckSelectionDialog
import com.ichi2.widget.deckpicker.DeckPickerWidgetConfig
import com.ichi2.widget.deckpicker.DeckPickerWidgetPreferences
import kotlinx.coroutines.runBlocking
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Before
Expand All @@ -47,14 +48,16 @@ class DeckPickerWidgetConfigTest : RobolectricTest() {
@Before
override fun setUp() {
super.setUp()
ensureNonEmptyCollection()

val intent = Intent(targetContext, DeckPickerWidgetConfig::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 1)
}

activity = startActivityNormallyOpenCollectionWithIntent(DeckPickerWidgetConfig::class.java, intent)

// Ensure deckAdapter is initialized
activity.initializeUIComponents()
runBlocking { activity.initTask.join() }
}

/**
Expand Down
6 changes: 6 additions & 0 deletions AnkiDroid/src/test/java/com/ichi2/testutils/TestClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.ichi2.testutils

import com.ichi2.anki.CollectionManager
import com.ichi2.anki.ioDispatcher
import com.ichi2.anki.isCollectionEmpty
import com.ichi2.libanki.Card
import com.ichi2.libanki.Collection
import com.ichi2.libanki.Consts
Expand Down Expand Up @@ -144,6 +145,11 @@ interface TestClass {
}
}

/** Ensures [isCollectionEmpty] returns `false` */
fun ensureNonEmptyCollection() {
addNotes(1)
}

fun selectDefaultDeck() {
col.decks.select(Consts.DEFAULT_DECK_ID)
}
Expand Down

0 comments on commit 5bf4326

Please sign in to comment.