Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit dc9fb7b6 authored by Ricki Hirner's avatar Ricki Hirner Committed by GitHub
Browse files

`AndroidSyncFrameworkTest`: allow optional states (#1625)

* Update sync state verification to allow optional states

* Refactor AndroidSyncFrameworkTest to use stateEquals for comparison
parent 44b52f65
Loading
Loading
Loading
Loading
+51 −17
Original line number Diff line number Diff line
@@ -16,12 +16,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import io.mockk.junit4.MockKRule
import junit.framework.AssertionFailedError
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.junit.After
import org.junit.AfterClass
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Rule
@@ -86,10 +86,10 @@ class AndroidSyncFrameworkTest {
    @SdkSuppress(maxSdkVersion = 33)
    @Test
    fun testVerifySyncAlwaysPending_correctBehaviour_android13() {
        assertSyncStates(
        verifySyncStates(
            listOf(
                State(pending = false, active = false),                 // no sync pending or active
                State(pending = true, active = false),      // sync becomes pending
                State(pending = true, active = false, optional = true), // sync becomes pending
                State(pending = true, active = true),                   // ... and pending and active at the same time
                State(pending = false, active = true),                  // ... and then only active
                State(pending = false, active = false)                  // sync finished
@@ -104,10 +104,10 @@ class AndroidSyncFrameworkTest {
    @SdkSuppress(minSdkVersion = 34 /*, maxSdkVersion = 36 */)
    @Test
    fun testVerifySyncAlwaysPending_wrongBehaviour_android14() {
        assertSyncStates(
        verifySyncStates(
            listOf(
                State(pending = false, active = false),                 // no sync pending or active
                State(pending = true, active = false),      // sync becomes pending
                State(pending = true, active = false, optional = true), // sync becomes pending
                State(pending = true, active = true),                   // ... and pending and active at the same time
                State(pending = true, active = false)                   // ... and finishes, but stays pending
            )
@@ -128,7 +128,7 @@ class AndroidSyncFrameworkTest {
    /**
     * Verifies that the given expected states match the recorded states.
     */
    private fun assertSyncStates(expectedStates: List<State>) = runBlocking {
    private fun verifySyncStates(expectedStates: List<State>) = runBlocking {
        // We use runBlocking for these tests because it uses the default dispatcher
        // which does not auto-advance virtual time and we need real system time to
        // test the sync framework behavior.
@@ -143,16 +143,46 @@ class AndroidSyncFrameworkTest {
            while (recordedStates.size < expectedStates.size) {
                // verify already known states
                if (recordedStates.isNotEmpty())
                    assertEquals(expectedStates.subList(0, recordedStates.size), recordedStates)
                    assertStatesEqual(expectedStates.subList(0, recordedStates.size), recordedStates)

                delay(500) // avoid busy-waiting
            }

            assertEquals(expectedStates, recordedStates)
            assertStatesEqual(expectedStates, recordedStates)
        }
    }

    /**
     * Asserts whether [actualStates] and [expectedStates] are the same, under the condition
     * that expected states with the [State.optional] flag can be skipped.
     */
    private fun assertStatesEqual(expectedStates: List<State>, actualStates: List<State>) {
        fun fail() {
            throw AssertionFailedError("Expected states=$expectedStates, actual=$actualStates")
        }

        // iterate through entries
        val expectedIterator = expectedStates.iterator()
        for (actual in actualStates) {
            if (!expectedIterator.hasNext())
                fail()
            var expected = expectedIterator.next()

            // skip optional expected entries if they don't match the actual entry
            while (!actual.stateEquals(expected) && expected.optional) {
                if (!expectedIterator.hasNext())
                    fail()
                expected = expectedIterator.next()
            }

            if (!actual.stateEquals(expected))
                fail()
        }
    }


    // SyncStatusObserver implementation and data class

    fun onStatusChanged(which: Int) {
        val state = State(
            pending = ContentResolver.isSyncPending(account, authority),
@@ -168,8 +198,12 @@ class AndroidSyncFrameworkTest {

    data class State(
        val pending: Boolean,
        val active: Boolean
    )
        val active: Boolean,
        val optional: Boolean = false
    ) {
        fun stateEquals(other: State) =
            pending == other.pending && active == other.active
    }


    companion object {