ankidroid / ankidroid/Anki-Android

[Bug] Unit Tests: java.lang.UnsatisfiedLinkError: 'byte[][] net.ankiweb.rsdroid.NativeMethods.openBackend(byte[])' when mixing `RunWith(AndroidJUnit4::class)`

Aperta
#14,796 8 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Bug Keep Open Tests
Lingua principale
Kotlin
Stelle
11.8k
Fork
2.9k
Merge medio
2g 3h
PR unite (30g)
171

Descrizione

A mix of `JvmTest` with and without the `AndroidJUnit4` annotation produces flaky tests

## Steps

Apply this patch & run tests on com.ichi2.anki.libanki

```patch
Subject: [PATCH] speed
---
Index: AnkiDroid/src/test/java/com/ichi2/libanki/FlagTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/FlagTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/FlagTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/FlagTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/FlagTest.kt (date 1700607248378)
@@ -21,7 +21,6 @@
import org.junit.Test
import org.junit.runner.RunWith

-@RunWith(AndroidJUnit4::class)
class FlagTest : JvmTest() {
/*****************
** Flags *
Index: AnkiDroid/src/test/java/com/ichi2/libanki/AbstractSchedTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/AbstractSchedTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/AbstractSchedTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/AbstractSchedTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/AbstractSchedTest.kt (date 1700607248355)
@@ -15,7 +15,6 @@
*/
package com.ichi2.libanki

-import androidx.test.ext.junit.runners.AndroidJUnit4
import com.ichi2.libanki.sched.Counts
import com.ichi2.testutils.JvmTest
import com.ichi2.utils.KotlinCleanup
@@ -23,7 +22,6 @@
import org.hamcrest.Matchers.*
import org.json.JSONArray
import org.junit.Test
-import org.junit.runner.RunWith
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

@@ -32,7 +30,6 @@
@KotlinCleanup("is -> equalTo")
@KotlinCleanup("reduce newlines in asserts")
@KotlinCleanup("improve increaseAndAssertNewCountsIs")
-@RunWith(AndroidJUnit4::class)
class AbstractSchedTest : JvmTest() {
@Test
fun ensureUndoCorrectCounts() {
Index: AnkiDroid/build.gradle
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/build.gradle b/AnkiDroid/build.gradle
--- a/AnkiDroid/build.gradle (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/build.gradle (date 1700607248350)
@@ -388,6 +388,7 @@
testImplementation("androidx.fragment:fragment-testing:$fragments_version")
// in a JvmTest we need org.json.JSONObject to not be mocked
testImplementation 'org.json:json:20220924'
+ testImplementation 'io.github.ivanshafran:shared-preferences-mock:1.2.4'
// May need a resolution strategy for support libs to our versions
androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version"
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espresso_version") {
Index: AnkiDroid/src/test/java/com/ichi2/testutils/JvmTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/testutils/JvmTest.kt b/AnkiDroid/src/test/java/com/ichi2/testutils/JvmTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/testutils/JvmTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/testutils/JvmTest.kt (date 1700607248414)
@@ -18,6 +18,10 @@

import android.annotation.SuppressLint
import androidx.annotation.CallSuper
+import androidx.core.content.edit
+import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder
+import com.ichi2.anki.AnkiDroidApp
+import com.ichi2.anki.CollectionHelper
import com.ichi2.anki.CollectionManager
import com.ichi2.libanki.ChangeManager
import com.ichi2.libanki.Collection
@@ -38,6 +42,7 @@
import org.junit.Before
import timber.log.Timber
import timber.log.Timber.Forest.plant
+import java.nio.file.Files

open class JvmTest {
private fun maybeSetupBackend() {
@@ -57,6 +62,12 @@
@Before
@CallSuper
open fun setUp() {
+ AnkiDroidApp.sharedPreferencesTestingOverride = SPMockBuilder().createSharedPreferences()
+ AnkiDroidApp.sharedPrefs().edit {
+ putString(CollectionHelper.PREF_COLLECTION_PATH, Files.createTempDirectory(
+ "AnkiDroid-JvmTest"
+ ).toFile().path)
+ }
TimeManager.resetWith(MockTime(2020, 7, 7, 7, 0, 0, 0, 10))

ChangeManager.clearSubscribers()
@@ -83,6 +94,7 @@
@After
@CallSuper
open fun tearDown() {
+ AnkiDroidApp.sharedPreferencesTestingOverride = null
try {
// If you don't tear down the database you'll get unexpected IllegalStateExceptions related to connections
col_?.close()
Index: AnkiDroid/src/test/java/com/ichi2/libanki/DecksTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/DecksTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/DecksTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/DecksTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/DecksTest.kt (date 1700607248370)
@@ -30,7 +30,6 @@
import kotlin.test.assertFalse
import kotlin.test.assertTrue

-@RunWith(AndroidJUnit4::class)
class DecksTest : JvmTest() {
@Test
fun test_remove() {
Index: AnkiDroid/src/test/java/com/ichi2/libanki/PythonExtensionsTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/PythonExtensionsTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/PythonExtensionsTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/PythonExtensionsTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/PythonExtensionsTest.kt (date 1700607248400)
@@ -27,7 +27,6 @@
import org.junit.Test
import org.junit.runner.RunWith

-@RunWith(AndroidJUnit4::class)
class PythonExtensionsTest {

@Test
Index: AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.kt (date 1700607248392)
@@ -37,7 +37,6 @@
return " data-cloze=\"${data}\""
}

-@RunWith(AndroidJUnit4::class)
@KotlinCleanup("improve kotlin code where possible")
class NotetypeTest : JvmTest() {
@Test
Index: AnkiDroid/src/test/java/com/ichi2/libanki/UtilsTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/UtilsTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/UtilsTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/UtilsTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/UtilsTest.kt (date 1700607248411)
@@ -23,7 +23,6 @@
import org.junit.runner.RunWith
import java.util.*

-@RunWith(AndroidJUnit4::class)
class UtilsTest {

@Test
Index: AnkiDroid/src/test/java/com/ichi2/libanki/FinderTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/FinderTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/FinderTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/FinderTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/FinderTest.kt (date 1700607248374)
@@ -36,7 +36,6 @@
import timber.log.Timber
import java.util.*

-@RunWith(AndroidJUnit4::class)
class FinderTest : JvmTest() {
@Test
@Config(qualifiers = "en")
Index: AnkiDroid/src/test/java/com/ichi2/libanki/MathJaxClozeTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/MathJaxClozeTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/MathJaxClozeTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/MathJaxClozeTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/MathJaxClozeTest.kt (date 1700607248382)
@@ -12,7 +12,6 @@
import org.junit.Test
import org.junit.runner.RunWith

-@RunWith(AndroidJUnit4::class)
@KotlinCleanup("removeFormattingFromMathjax was imported to stop bug in Kotlin: java.lang.NoSuchFieldError: INSTANCE")
@KotlinCleanup("add testing function returning c.models.byName(\"Cloze\")")
class MathJaxClozeTest : JvmTest() {
Index: AnkiDroid/src/test/java/com/ichi2/libanki/CardTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/CardTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/CardTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/CardTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/CardTest.kt (date 1700607248359)
@@ -31,7 +31,7 @@
import java.util.*
import kotlin.test.assertNotNull

-@RunWith(AndroidJUnit4::class)
+@RunWith(AndroidJUnit4::class) // nextDueTest: Short Date Format is different
class CardTest : JvmTest() {

@Test
Index: AnkiDroid/src/test/java/com/ichi2/libanki/TagsTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/TagsTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/TagsTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/TagsTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/TagsTest.kt (date 1700607248407)
@@ -22,7 +22,6 @@
import org.junit.Test
import org.junit.runner.RunWith

-@RunWith(AndroidJUnit4::class)
class TagsTest : JvmTest() {

@Test
Index: AnkiDroid/src/test/java/com/ichi2/libanki/StorageRustTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/StorageRustTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/StorageRustTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/StorageRustTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/StorageRustTest.kt (date 1700607248403)
@@ -24,7 +24,6 @@
import org.junit.runner.RunWith
import org.robolectric.annotation.Config

-@RunWith(AndroidJUnit4::class)
class StorageRustTest : JvmTest() {

@Test
Index: AnkiDroid/src/test/java/com/ichi2/libanki/ConfigTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/ConfigTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/ConfigTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/ConfigTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/ConfigTest.kt (date 1700607248367)
@@ -26,8 +26,6 @@
import org.junit.Test
import org.junit.jupiter.api.assertThrows
import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
class ConfigTest : JvmTest() {

@Test
Index: AnkiDroid/src/test/java/com/ichi2/libanki/CollectionTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/CollectionTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/CollectionTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/CollectionTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/CollectionTest.kt (date 1700607248363)
@@ -26,7 +26,7 @@
import org.junit.runner.RunWith
import java.util.*

-@RunWith(AndroidJUnit4::class)
+@RunWith(AndroidJUnit4::class) // test_timestamps: AnkiDroidApp.instance
class CollectionTest : JvmTest() {
@Test
fun editClozeGenerateCardsInSameDeck() {
Index: AnkiDroid/src/test/java/com/ichi2/libanki/NoteWithColTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/NoteWithColTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/NoteWithColTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/NoteWithColTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/NoteWithColTest.kt (date 1700607248395)
@@ -24,7 +24,6 @@
import org.junit.runner.RunWith
import org.robolectric.annotation.Config

-@RunWith(AndroidJUnit4::class)
class NoteWithColTest : JvmTest() {

@Test
Index: AnkiDroid/src/test/java/com/ichi2/libanki/MetaTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/MetaTest.kt b/AnkiDroid/src/test/java/com/ichi2/libanki/MetaTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/MetaTest.kt (revision 4ac94b14feb30004eb3d7f68cdb88f075b0fc459)
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/MetaTest.kt (date 1700607248387)
@@ -22,7 +22,6 @@
import org.junit.Test
import org.junit.runner.RunWith

-@RunWith(AndroidJUnit4::class)
class MetaTest : JvmTest() {
@Test
fun ensureDatabaseIsInMemory() {
```

## Result

Flaky tests sometimes failing with

```
'byte[][] net.ankiweb.rsdroid.NativeMethods.openBackend(byte[])'
java.lang.UnsatisfiedLinkError: 'byte[][] net.ankiweb.rsdroid.NativeMethods.openBackend(byte[])'
at net.ankiweb.rsdroid.NativeMethods.openBackend(Native Method)
at net.ankiweb.rsdroid.Backend.(Backend.kt:81)
```

This is not a failure to call `RustBackendLoader.ensureSetup()`, if `ensureSetup()` is forced to be called twice, it fails `Native Library /private/var/folders/ym/nqynp93d4j74dpzw20sq4wq00000gn/T/librsdroid-b7f0b01dd98e063d7f3e09f8a0a0979aafe9dc0c.dylib already loaded in another classloader`

### Classloaders

There are two classloaders in play:

* `jdk.internal.loader.ClassLoaders$AppClassLoader@14899482` (`currentThread on JvmTest/ClassLoader.getSystemClassLoader()`)
* `org.robolectric.internal.bytecode.SandboxClassLoader` (`currentThread on @RunWith(AndroidJUnit4::class)`)

Inside a JvmTest, if you use the non-default (`SandboxClassLoader`) to load `RustBackendLoader`:
* First test passes, second test fails with `java.lang.UnsatisfiedLinkError: 'byte[][] ...`

Inside an AndroidJUnitTest: if you use the non-default `AppClassLoader`:
* First test fails with `java.lang.UnsatisfiedLinkError: 'byte[][] ...`

###### Debug info
HEAD is 4ac94b14feb30004eb3d7f68cdb88f075b0fc459

###### Research
- [x] I have read the [support page](https://ankidroid.org/docs/help.html) and am reporting a bug or enhancement request specific to AnkiDroid
- [x] I have checked the [manual](https://ankidroid.org/docs/manual.html) and the [FAQ](https://github.com/ankidroid/Anki-Android/wiki/FAQ) and could not find a solution to my issue
- [x] I have searched for similar existing issues here and on the user forum
- [x] (Optional) I have confirmed the issue is not resolved in the latest alpha release ([instructions](https://docs.ankidroid.org/manual.html#betaTesting))

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.