Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.ichi2.testutils.BackupManagerTestUtilities
import com.ichi2.testutils.DbUtils
import com.ichi2.testutils.common.Flaky
import com.ichi2.testutils.common.OS
import com.ichi2.testutils.ext.addBasicNoteWithOp
import com.ichi2.testutils.ext.menu
import com.ichi2.testutils.grantWritePermissions
import com.ichi2.testutils.revokeWritePermissions
Expand Down
16 changes: 16 additions & 0 deletions AnkiDroid/src/test/java/com/ichi2/anki/RobolectricTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.ichi2.anki

import android.Manifest
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.Intent
Expand All @@ -32,15 +33,21 @@ import androidx.test.core.app.ApplicationProvider
import androidx.work.Configuration
import androidx.work.testing.SynchronousExecutor
import androidx.work.testing.WorkManagerTestInitHelper
import anki.collection.OpChanges
import com.ichi2.anki.CollectionManager.CollectionOpenFailure
import com.ichi2.anki.RobolectricTest.Companion.advanceRobolectricLooper
import com.ichi2.anki.RobolectricTest.Companion.advanceRobolectricLooperWithSleep
import com.ichi2.anki.common.annotations.UseContextParameter
import com.ichi2.anki.common.time.MockTime
import com.ichi2.anki.common.time.TimeManager
import com.ichi2.anki.dialogs.DialogHandler
import com.ichi2.anki.libanki.Card
import com.ichi2.anki.libanki.Collection
import com.ichi2.anki.libanki.Note
import com.ichi2.anki.libanki.NotetypeJson
import com.ichi2.anki.libanki.Storage
import com.ichi2.anki.observability.ChangeManager
import com.ichi2.anki.observability.undoableOp
import com.ichi2.anki.preferences.sharedPrefs
import com.ichi2.compat.customtabs.CustomTabActivityHelper
import com.ichi2.testutils.AndroidTest
Expand Down Expand Up @@ -476,6 +483,15 @@ open class RobolectricTest :
}
}

/** Helper method to update a note */
@SuppressLint("CheckResult")
@UseContextParameter("TestClass")
suspend fun Note.updateOp(block: Note.() -> Unit): Note =
this.also { note ->
block(note)
undoableOp<OpChanges> { col.updateNote(note) }
}

private fun maybeSetupBackend() {
try {
targetContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import com.ichi2.testutils.TestClass
import com.ichi2.testutils.createTransientDirectory
import com.ichi2.testutils.ensureNoOpsExecuted
import com.ichi2.testutils.ensureOpsExecuted
import com.ichi2.testutils.ext.reopenWithLanguage
import com.ichi2.testutils.mockIt
import kotlinx.coroutines.flow.first
import org.hamcrest.MatcherAssert.assertThat
Expand Down
37 changes: 0 additions & 37 deletions AnkiDroid/src/test/java/com/ichi2/testutils/TestClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
package com.ichi2.testutils

import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatDelegate
import anki.collection.OpChanges
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.ioDispatcher
import com.ichi2.anki.isCollectionEmpty
Expand All @@ -33,9 +31,7 @@ import com.ichi2.anki.libanki.NotetypeJson
import com.ichi2.anki.libanki.Notetypes
import com.ichi2.anki.libanki.QueueType
import com.ichi2.anki.libanki.exception.ConfirmModSchemaException
import com.ichi2.anki.observability.undoableOp
import com.ichi2.testutils.ext.addNote
import com.ichi2.utils.LanguageUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
Expand Down Expand Up @@ -108,17 +104,6 @@ interface TestClass {
return n
}

suspend fun addBasicNoteWithOp(
fields: List<String> = listOf("foo", "bar"),
noteType: NotetypeJson = col.notetypes.byName("Basic")!!,
): Note =
col.newNote(noteType).also { note ->
for ((i, field) in fields.withIndex()) {
note.setField(i, field)
}
undoableOp<OpChanges> { col.addNote(note, Consts.DEFAULT_DECK_ID) }
}

/**
* Create a new note type in the collection.
* @param name the name of the note type
Expand Down Expand Up @@ -207,20 +192,6 @@ interface TestClass {
addNotes(1)
}

/**
* Closes and reopens the backend using the provided [language], typically for
* [CollectionManager.TR] calls
*
* This does not set the [application locales][AppCompatDelegate.setApplicationLocales]
*
* @param language tag in the form: `de` or `zh-CN`
*/
suspend fun Collection.reopenWithLanguage(language: String) {
LanguageUtil.setDefaultBackendLanguages(language)
CollectionManager.discardBackend()
CollectionManager.getColUnsafe()
}

fun selectDefaultDeck() {
col.decks.select(Consts.DEFAULT_DECK_ID)
}
Expand Down Expand Up @@ -261,14 +232,6 @@ interface TestClass {
return this
}

/** Helper method to update a note */
@SuppressLint("CheckResult")
suspend fun Note.updateOp(block: Note.() -> Unit): Note =
this.also { note ->
block(note)
undoableOp<OpChanges> { col.updateNote(note) }
}

/** Helper method to all cards of a note */
fun Note.updateCards(update: Card.() -> Unit): Note {
cards().forEach { it.update(update) }
Expand Down
18 changes: 18 additions & 0 deletions AnkiDroid/src/test/java/com/ichi2/testutils/ext/Collection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,28 @@

package com.ichi2.testutils.ext

import androidx.appcompat.app.AppCompatDelegate
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.libanki.Collection
import com.ichi2.anki.libanki.Note
import com.ichi2.utils.LanguageUtil

fun Collection.addNote(note: Note): Int {
addNote(note, note.notetype.did)
return note.numberOfCards(this)
}

/**
* Closes and reopens the backend using the provided [language], typically for
* [CollectionManager.TR] calls
*
* This does not set the [application locales][AppCompatDelegate.setApplicationLocales]
*
* @param language tag in the form: `de` or `zh-CN`
*/
@Suppress("UnusedReceiverParameter")
suspend fun Collection.reopenWithLanguage(language: String) {
LanguageUtil.setDefaultBackendLanguages(language)
CollectionManager.discardBackend()
CollectionManager.getColUnsafe()
}
35 changes: 35 additions & 0 deletions AnkiDroid/src/test/java/com/ichi2/testutils/ext/TestClass.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2025 David Allison <davidallisongithub@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.ichi2.testutils.ext

import anki.collection.OpChanges
import com.ichi2.anki.libanki.Consts
import com.ichi2.anki.libanki.Note
import com.ichi2.anki.libanki.NotetypeJson
import com.ichi2.anki.observability.undoableOp
import com.ichi2.testutils.TestClass

suspend fun TestClass.addBasicNoteWithOp(
fields: List<String> = listOf("foo", "bar"),
noteType: NotetypeJson = col.notetypes.byName("Basic")!!,
): Note =
col.newNote(noteType).also { note ->
for ((i, field) in fields.withIndex()) {
note.setField(i, field)
}
undoableOp<OpChanges> { col.addNote(note, Consts.DEFAULT_DECK_ID) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 David Allison <davidallisongithub@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.ichi2.anki.common.annotations

/**
* Use when code should be converted to use context parameters (Kotlin 2.2.0)
*
* Context parameters are not yet supported by AnkiDroid
* https://github.com/JLLeitschuh/ktlint-gradle/issues/912
*
* @param toExtend the name of the class to extend
*/
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION,
AnnotationTarget.FIELD,
AnnotationTarget.PROPERTY,
)
@Repeatable
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class UseContextParameter(
val toExtend: String,
)