Skip to content

Commit 922fb4b

Browse files
DoomsCodersanjaysargam
authored andcommitted
Add Instrumented tests for data-modifying methods
1 parent 37ccebb commit 922fb4b

2 files changed

Lines changed: 160 additions & 2 deletions

File tree

AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ContentProviderTest.kt

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ import android.database.Cursor
2424
import android.database.CursorWindow
2525
import android.net.Uri
2626
import anki.cards.FsrsMemoryState
27+
import anki.collection.OpChanges
2728
import anki.notetypes.StockNotetype
2829
import com.ichi2.anki.CollectionManager
2930
import com.ichi2.anki.FlashCardsContract
31+
import com.ichi2.anki.common.time.TimeManager
3032
import com.ichi2.anki.common.utils.annotation.KotlinCleanup
3133
import com.ichi2.anki.common.utils.emptyStringArray
3234
import com.ichi2.anki.libanki.Card
@@ -44,6 +46,7 @@ import com.ichi2.anki.libanki.backend.BackendUtils
4446
import com.ichi2.anki.libanki.exception.ConfirmModSchemaException
4547
import com.ichi2.anki.libanki.getStockNotetype
4648
import com.ichi2.anki.libanki.sched.Scheduler
49+
import com.ichi2.anki.observability.ChangeManager
4750
import com.ichi2.anki.provider.pureAnswer
4851
import com.ichi2.anki.testutil.DatabaseUtils.cursorFillWindow
4952
import com.ichi2.anki.testutil.GrantStoragePermission.storagePermission
@@ -70,6 +73,7 @@ import org.junit.Before
7073
import org.junit.Rule
7174
import org.junit.Test
7275
import timber.log.Timber
76+
import kotlin.test.assertFailsWith
7377
import kotlin.test.assertNotNull
7478
import kotlin.test.assertTrue
7579
import kotlin.test.junit.JUnitAsserter.assertNotNull
@@ -2462,6 +2466,160 @@ class ContentProviderTest : InstrumentedTest() {
24622466
return col
24632467
}
24642468

2469+
@Test
2470+
fun testInsertNotifiesUI() {
2471+
val counter = TestSubscriber()
2472+
ChangeManager.subscribe(counter)
2473+
try {
2474+
val mid = noteTypeId
2475+
val noteType = col.notetypes.get(mid)!!
2476+
val fieldCount = noteType.fields.length()
2477+
val emptyFields = Array(fieldCount) { "" }.joinToString(separator = "\u001f")
2478+
2479+
val values =
2480+
ContentValues().apply {
2481+
put(FlashCardsContract.Note.MID, mid)
2482+
put(FlashCardsContract.Note.FLDS, emptyFields)
2483+
}
2484+
2485+
contentResolver.insert(FlashCardsContract.Note.CONTENT_URI, values)
2486+
assertNotificationReceived(counter)
2487+
} finally {
2488+
ChangeManager.unsubscribe(counter)
2489+
}
2490+
}
2491+
2492+
@Test
2493+
fun testUpdateNotifiesUI() {
2494+
val noteId = createdNotes.first().lastPathSegment!!.toLong()
2495+
val uri = Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI, noteId.toString())
2496+
val values =
2497+
ContentValues().apply {
2498+
put(FlashCardsContract.Note.TAGS, "new_tag")
2499+
}
2500+
val counter = TestSubscriber()
2501+
ChangeManager.subscribe(counter)
2502+
try {
2503+
contentResolver.update(uri, values, null, null)
2504+
assertNotificationReceived(counter)
2505+
} finally {
2506+
ChangeManager.unsubscribe(counter)
2507+
}
2508+
}
2509+
2510+
@Test
2511+
fun testUpdateNonExistentNoteDoesNotNotifyUI() {
2512+
val counter = TestSubscriber()
2513+
ChangeManager.subscribe(counter)
2514+
try {
2515+
val values =
2516+
ContentValues().apply {
2517+
put(FlashCardsContract.Note.TAGS, "new_tag")
2518+
}
2519+
val uri = Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI, "999999")
2520+
2521+
assertFailsWith<Exception> {
2522+
contentResolver.update(uri, values, null, null)
2523+
}
2524+
2525+
Thread.sleep(1000)
2526+
assertEquals("UI should not be notified if update is failed", 0, counter.count)
2527+
} finally {
2528+
ChangeManager.unsubscribe(counter)
2529+
}
2530+
}
2531+
2532+
@Test
2533+
fun testDeleteNotifiesUI() {
2534+
val noteId = createdNotes.first().lastPathSegment!!.toLong()
2535+
val uri = Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI, noteId.toString())
2536+
val counter = TestSubscriber()
2537+
ChangeManager.subscribe(counter)
2538+
try {
2539+
contentResolver.delete(uri, null, null)
2540+
assertNotificationReceived(counter)
2541+
} finally {
2542+
ChangeManager.unsubscribe(counter)
2543+
}
2544+
}
2545+
2546+
@Test
2547+
fun testDeleteNonExistentNoteDoesNotNotifyUI() {
2548+
val counter = TestSubscriber()
2549+
ChangeManager.subscribe(counter)
2550+
try {
2551+
val uri = Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI, "999999")
2552+
val deletedCount = contentResolver.delete(uri, null, null)
2553+
assertEquals("It should return 0 for non-existent note", 0, deletedCount)
2554+
2555+
Thread.sleep(1000)
2556+
assertEquals("UI should not be notify if nothing was deleted", 0, counter.count)
2557+
} finally {
2558+
ChangeManager.unsubscribe(counter)
2559+
}
2560+
}
2561+
2562+
@Test
2563+
fun testBulkInsertNotifiesUI() {
2564+
val counter = TestSubscriber()
2565+
ChangeManager.subscribe(counter)
2566+
try {
2567+
val mid = noteTypeId
2568+
val noteType = col.notetypes.get(mid)!!
2569+
val fieldCount = noteType.fields.length()
2570+
val emptyFields = Array(fieldCount) { "" }.joinToString(separator = "\u001f")
2571+
2572+
val values =
2573+
arrayOf(
2574+
ContentValues().apply {
2575+
put(FlashCardsContract.Note.MID, mid)
2576+
put(FlashCardsContract.Note.FLDS, emptyFields)
2577+
},
2578+
)
2579+
2580+
contentResolver.bulkInsert(FlashCardsContract.Note.CONTENT_URI, values)
2581+
assertNotificationReceived(counter)
2582+
} finally {
2583+
ChangeManager.unsubscribe(counter)
2584+
}
2585+
}
2586+
2587+
@Test
2588+
fun testBulkInsertEmptyListDoesNotNotifyUI() {
2589+
val counter = TestSubscriber()
2590+
ChangeManager.subscribe(counter)
2591+
try {
2592+
contentResolver.bulkInsert(FlashCardsContract.Note.CONTENT_URI, emptyArray())
2593+
2594+
Thread.sleep(1000)
2595+
assertEquals("UI should not be notified for empty bulk insert", 0, counter.count)
2596+
} finally {
2597+
ChangeManager.unsubscribe(counter)
2598+
}
2599+
}
2600+
2601+
// TODO: PERF: use TestChangeSubscriber once we've moved to testFixtures
2602+
private class TestSubscriber : ChangeManager.Subscriber {
2603+
var count = 0
2604+
2605+
override fun opExecuted(
2606+
changes: OpChanges,
2607+
handler: Any?,
2608+
) {
2609+
count++
2610+
}
2611+
}
2612+
2613+
private fun assertNotificationReceived(subscriber: TestSubscriber) {
2614+
val timeout = 5000L
2615+
val startTime = TimeManager.time.intTimeMS()
2616+
while (subscriber.count == 0 && TimeManager.time.intTimeMS() - startTime < timeout) {
2617+
Thread.sleep(100)
2618+
}
2619+
2620+
assertTrue("UI should be notified of the change", subscriber.count > 0)
2621+
}
2622+
24652623
private val contentResolver: ContentResolver
24662624
get() = testContext.contentResolver
24672625

AnkiDroid/src/test/java/com/ichi2/anki/observability/ChangeManagerTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ class ChangeManagerPublishTest : JvmTest() {
121121
val counter1 = subscriberChangeCounter()
122122
val counter2 = subscriberChangeCounter()
123123

124-
ChangeManager.publish(ChangeManager.ALL)
124+
ChangeManager.publishAllValuesChanged()
125125

126-
assertThat("not notified synchronously by publish", counter1.hasChanges, equalTo(false))
126+
assertThat("not notified synchronously by publishAllValuesChanged", counter1.hasChanges, equalTo(false))
127127

128128
advanceUntilIdle()
129129

0 commit comments

Comments
 (0)