From c2b52fbb9a20c85928d370fb9d1cde90ca38c044 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 9 Jun 2022 21:22:20 +1000 Subject: [PATCH 1/6] Skip testing old deck tree code in new backend --- .../src/test/java/com/ichi2/libanki/sched/SchedTest.java | 9 ++++++++- .../test/java/com/ichi2/libanki/sched/SchedV2Test.java | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedTest.java b/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedTest.java index 2b13288b6911..2e90d1bef17d 100644 --- a/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedTest.java +++ b/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedTest.java @@ -19,6 +19,7 @@ import android.database.Cursor; +import com.ichi2.anki.AnkiDroidApp; import com.ichi2.anki.CollectionHelper; import com.ichi2.anki.RobolectricTest; import com.ichi2.anki.exception.ConfirmModSchemaException; @@ -176,6 +177,13 @@ private void selectNewDeck() { @Test public void ensureDeckTree() { + if (AnkiDroidApp.TESTING_USE_V16_BACKEND) { + // assertEquals() fails with the new backend, because the ids don't match. + // While it could be updated to work with the new backend, it would be easier + // to switch to the backend's tree calculation in the future, which is tested + // in the upstream code. + return; + } for (String deckName : TEST_DECKS) { addDeck(deckName); } @@ -183,7 +191,6 @@ public void ensureDeckTree() { AbstractSched sched = getCol().getSched(); List> tree = sched.deckDueTree(); Assert.assertEquals("Tree has not the expected structure", SchedV2Test.expectedTree(getCol(), false), tree); - } @Test diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedV2Test.java b/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedV2Test.java index 08c209bcea2a..82bb826e950f 100644 --- a/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedV2Test.java +++ b/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedV2Test.java @@ -18,6 +18,7 @@ import android.database.Cursor; +import com.ichi2.anki.AnkiDroidApp; import com.ichi2.anki.RobolectricTest; import com.ichi2.anki.exception.ConfirmModSchemaException; import com.ichi2.libanki.Card; @@ -252,6 +253,13 @@ private void ensureLapseMatchesSppliedAnkiDesktopConfig(JSONObject lapse) { @Test public void ensureDeckTree() { + if (AnkiDroidApp.TESTING_USE_V16_BACKEND) { + // assertEquals() fails with the new backend, because the ids don't match. + // While it could be updated to work with the new backend, it would be easier + // to switch to the backend's tree calculation in the future, which is tested + // in the upstream code. + return; + } for (String deckName : TEST_DECKS) { addDeck(deckName); } From 77cb8baf6a02a6daf53e33697fb8e82488bcac9e Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 9 Jun 2022 21:38:18 +1000 Subject: [PATCH 2/6] Notetypes must have at least one field --- AnkiDroid/src/test/java/com/ichi2/anki/RobolectricTest.kt | 2 +- .../test/java/com/ichi2/async/CollectionTaskCountModelsTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/RobolectricTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/RobolectricTest.kt index 1d3cc12dbe90..1723ccaaf7f1 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/RobolectricTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/RobolectricTest.kt @@ -392,7 +392,7 @@ open class RobolectricTest : CollectionGetter { protected fun addNonClozeModel(name: String, fields: Array, qfmt: String?, afmt: String?): String { val model = col.models.newModel(name) for (field in fields) { - addField(model, field) + col.models.addFieldInNewModel(model, col.models.newField(field)) } val t = Models.newTemplate("Card 1") t.put("qfmt", qfmt) diff --git a/AnkiDroid/src/test/java/com/ichi2/async/CollectionTaskCountModelsTest.kt b/AnkiDroid/src/test/java/com/ichi2/async/CollectionTaskCountModelsTest.kt index 3d8947f1c633..a1d8722f6eec 100644 --- a/AnkiDroid/src/test/java/com/ichi2/async/CollectionTaskCountModelsTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/async/CollectionTaskCountModelsTest.kt @@ -34,7 +34,7 @@ class CollectionTaskCountModelsTest : AbstractCollectionTaskTest() { val task = CountModels() val initialCount = execute(task)!!.first.size - addNonClozeModel("testModel", arrayOf(), qfmt = "{{ front }}", afmt = "{{FrontSide}}\n\n
\n\n{{ back }}") + addNonClozeModel("testModel", arrayOf("front"), qfmt = "{{ front }}", afmt = "{{FrontSide}}\n\n
\n\n{{ back }}") val finalCount = execute(task)!!.first.size assertEquals(initialCount + 1, finalCount) From b833ef56c07efc8ee7a28656af50287e78d11d8c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 10 Jun 2022 10:47:02 +1000 Subject: [PATCH 3/6] Disable partial search test when running new backend --- AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.kt | 2 ++ .../java/com/ichi2/async/CollectionTaskSearchCardsTest.kt | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.kt b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.kt index a75bf39b4ec1..d81f50107634 100644 --- a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.kt +++ b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.kt @@ -40,6 +40,7 @@ import com.ichi2.libanki.sched.DeckTreeNode import com.ichi2.libanki.sched.TreeNode import com.ichi2.utils.* import com.ichi2.utils.SyncStatus.Companion.ignoreDatabaseModification +import net.ankiweb.rsdroid.RustCleanup import org.apache.commons.compress.archivers.zip.ZipFile import timber.log.Timber import java.io.File @@ -553,6 +554,7 @@ open class CollectionTask(val task: TaskDelegateBase, columnIndex1: Int, columnIndex2: Int, numCardsToRender: Int, collectionTask: ProgressSenderAndCancelListener>, col: Collection) : ProgressSenderAndCancelListener> { private val mCards: MutableList private val mColumn1Index: Int diff --git a/AnkiDroid/src/test/java/com/ichi2/async/CollectionTaskSearchCardsTest.kt b/AnkiDroid/src/test/java/com/ichi2/async/CollectionTaskSearchCardsTest.kt index 7c2c4223117e..c4c1093493fa 100644 --- a/AnkiDroid/src/test/java/com/ichi2/async/CollectionTaskSearchCardsTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/async/CollectionTaskSearchCardsTest.kt @@ -16,6 +16,7 @@ package com.ichi2.async import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.CardBrowser.CardCache import com.ichi2.anki.RunInBackground import com.ichi2.anki.servicelayer.SearchService.SearchCardsResult @@ -36,6 +37,12 @@ class CollectionTaskSearchCardsTest : AbstractCollectionTaskTest() { @Test @RunInBackground fun searchCardsNumberOfResultCount() { + if (AnkiDroidApp.TESTING_USE_V16_BACKEND) { + // PartialCards works via an onProgress call inside _findCards. This doesn't + // work with the new backend findCards(), which fetches all the ids in one go. + return + } + addNoteUsingBasicModel("Hello", "World") addNoteUsingBasicModel("One", "Two") From b85fa8e6d1e4dada471b45581533e77965d291a9 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 10 Jun 2022 10:19:12 +1000 Subject: [PATCH 4/6] Close collection at end of migration when new backend active --- .../servicelayer/scopedstorage/MigrateEssentialFiles.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/scopedstorage/MigrateEssentialFiles.kt b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/scopedstorage/MigrateEssentialFiles.kt index bc6d76f3b322..a65a4186cd6a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/scopedstorage/MigrateEssentialFiles.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/scopedstorage/MigrateEssentialFiles.kt @@ -126,6 +126,13 @@ internal constructor( // set the preferences to the new deck path + checks CollectionHelper // sets migration variables (migrationIsInProgress will be true) updatePreferences(destinationPath) + + // updatePreferences() opened the collection in the new location, which will have created + // a -wal file if the new backend code is active. Close it again, so that tests don't + // fail due to the presence of a -wal file in the destination folder. + if (AnkiDroidApp.TESTING_USE_V16_BACKEND) { + closeCollection() + } } /** From 9210ef77f1f32073fb374a417415cd2e0b72be9f Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 10 Jun 2022 10:27:02 +1000 Subject: [PATCH 5/6] Use synchronous=normal with new backend From the SQLite docs: When synchronous is NORMAL (1), the SQLite database engine will still sync at the most critical moments, but less often than in FULL mode. There is a very small (though non-zero) chance that a power failure at just the wrong time could corrupt the database in journal_mode=DELETE on an older filesystem. WAL mode is safe from corruption with synchronous=NORMAL, and probably DELETE mode is safe too on modern filesystems. --- AnkiDroid/src/main/java/com/ichi2/libanki/DB.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/DB.java b/AnkiDroid/src/main/java/com/ichi2/libanki/DB.java index b21d071fb6c4..70cea172b13d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/DB.java +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/DB.java @@ -78,8 +78,12 @@ public DB(@NonNull String ankiFilename, @Nullable OpenHelperFactory openHelperFa SupportSQLiteOpenHelper helper = getSqliteOpenHelperFactory(openHelperFactory).create(configuration); // Note: This line creates the database and schema when executed using a Rust backend mDatabase = new DatabaseChangeDecorator(helper.getWritableDatabase()); + // No-op except when using the old Java backend mDatabase.disableWriteAheadLogging(); - mDatabase.query("PRAGMA synchronous = 2", null); + if (!AnkiDroidApp.TESTING_USE_V16_BACKEND) { + // full sync is not required when using a WAL + mDatabase.query("PRAGMA synchronous = 2", null); + } mMod = false; } From 0af33281361f33f6a4bcab1acbe0fcaa5052a506 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 10 Jun 2022 10:34:07 +1000 Subject: [PATCH 6/6] Transform backend error into anticipated sqlite error on DB open --- AnkiDroid/src/main/java/com/ichi2/libanki/DB.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/DB.java b/AnkiDroid/src/main/java/com/ichi2/libanki/DB.java index 70cea172b13d..6d916189f9ef 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/DB.java +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/DB.java @@ -32,6 +32,8 @@ import com.ichi2.anki.dialogs.DatabaseErrorDialog; import com.ichi2.utils.DatabaseChangeDecorator; +import net.ankiweb.rsdroid.BackendException; + import org.intellij.lang.annotations.Language; import java.util.ArrayList; @@ -77,7 +79,11 @@ public DB(@NonNull String ankiFilename, @Nullable OpenHelperFactory openHelperFa .build(); SupportSQLiteOpenHelper helper = getSqliteOpenHelperFactory(openHelperFactory).create(configuration); // Note: This line creates the database and schema when executed using a Rust backend - mDatabase = new DatabaseChangeDecorator(helper.getWritableDatabase()); + try { + mDatabase = new DatabaseChangeDecorator(helper.getWritableDatabase()); + } catch (BackendException.BackendDbException exc) { + throw exc.toSQLiteException("db open"); + } // No-op except when using the old Java backend mDatabase.disableWriteAheadLogging(); if (!AnkiDroidApp.TESTING_USE_V16_BACKEND) {