diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/TestModules.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/TestModules.kt index 6c99a4f9c..7913da040 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/TestModules.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/TestModules.kt @@ -1,32 +1,13 @@ package at.bitfire.davdroid -import android.content.Context -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.push.PushRegistrationWorkerManager import at.bitfire.davdroid.repository.DavCollectionRepository import at.bitfire.davdroid.startup.StartupPlugin import at.bitfire.davdroid.startup.TasksAppWatcher import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import dagger.hilt.testing.TestInstallIn import dagger.multibindings.Multibinds -import javax.inject.Provider -import javax.inject.Qualifier - -/*@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class TargetContext - -@Module -@InstallIn(SingletonComponent::class) -internal object TargetContextModule { - @Provides @TargetContext - fun targetContext(): Provider = object: Provider { - override fun get() = InstrumentationRegistry.getInstrumentation().targetContext - } -}*/ // remove PushRegistrationWorkerModule from Android tests @Module diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalCalendarTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalCalendarTest.kt index f875d1213..9fc942b0a 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalCalendarTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalCalendarTest.kt @@ -119,7 +119,7 @@ class LocalCalendarTest { } @Test - // Flaky, Needs single or rec init of CalendarProvider (InitCalendarProviderRule) + // Needs InitCalendarProviderRule fun testDeleteDirtyEventsWithoutInstances_Recurring_Instances() { val event = Event().apply { dtStart = DtStart("20220120T010203Z") diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalEventTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalEventTest.kt index 01a3ac645..4b15c3add 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalEventTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalEventTest.kt @@ -12,7 +12,6 @@ import android.os.Build import android.provider.CalendarContract import android.provider.CalendarContract.ACCOUNT_TYPE_LOCAL import android.provider.CalendarContract.Events -import androidx.datastore.dataStore import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.InitCalendarProviderRule import at.bitfire.ical4android.AndroidCalendar @@ -41,29 +40,6 @@ import java.util.UUID class LocalEventTest { - companion object { - - @JvmField - @ClassRule - val initCalendarProviderRule: TestRule = InitCalendarProviderRule.getInstance() - - private lateinit var provider: ContentProviderClient - - @BeforeClass - @JvmStatic - fun setUpClass() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - provider = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!! - } - - @AfterClass - @JvmStatic - fun tearDownClass() { - provider.closeCompat() - } - - } - private val account = Account("LocalCalendarTest", ACCOUNT_TYPE_LOCAL) private lateinit var calendar: LocalCalendar @@ -482,4 +458,28 @@ class LocalEventTest { } } -} + + companion object { + + @JvmField + @ClassRule + val initCalendarProviderRule: TestRule = InitCalendarProviderRule.getInstance() + + private lateinit var provider: ContentProviderClient + + @BeforeClass + @JvmStatic + fun setUpClass() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + provider = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!! + } + + @AfterClass + @JvmStatic + fun tearDownClass() { + provider.closeCompat() + } + + } + +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt index 25f90b2ac..8f07d48c9 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt @@ -62,7 +62,8 @@ class LocalGroupTest { @After fun tearDown() { - + addressBookGroupsAsCategories.remove() + addressBookGroupsAsVCards.remove() } @@ -270,9 +271,8 @@ class LocalGroupTest { @BeforeClass @JvmStatic fun connect() { - val context = InstrumentationRegistry.getInstrumentation().targetContext + val context = InstrumentationRegistry.getInstrumentation().context provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! - assertNotNull(provider) } @AfterClass diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalTestAddressBook.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalTestAddressBook.kt index a1a3c31d2..0c501b898 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalTestAddressBook.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalTestAddressBook.kt @@ -119,6 +119,11 @@ class LocalTestAddressBook @AssistedInject constructor( val counter = AtomicInteger() + /** + * Creates a [at.bitfire.davdroid.resource.LocalTestAddressBook]. + * + * Make sure to delete it with [at.bitfire.davdroid.resource.LocalTestAddressBook.remove] or [removeAll] after use. + */ fun create(context: Context, account: Account, provider: ContentProviderClient, groupMethod: GroupMethod = GroupMethod.GROUP_VCARDS): LocalTestAddressBook { // create new address book account val addressBookAccount = Account("Test Address Book ${counter.incrementAndGet()}", context.getString(R.string.account_type_address_book)) @@ -131,6 +136,12 @@ class LocalTestAddressBook @AssistedInject constructor( return factory.create(account, addressBookAccount, provider, groupMethod) } + fun removeAll(context: Context) { + val accountManager = AccountManager.get(context) + for (account in accountManager.getAccountsByType(context.getString(R.string.account_type_address_book))) + accountManager.removeAccountExplicitly(account) + } + } } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/CachedGroupMembershipHandlerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/CachedGroupMembershipHandlerTest.kt index 236e066b4..18429a5c2 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/CachedGroupMembershipHandlerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/CachedGroupMembershipHandlerTest.kt @@ -32,6 +32,38 @@ import javax.inject.Inject @HiltAndroidTest class CachedGroupMembershipHandlerTest { + @Inject + @ApplicationContext + lateinit var context: Context + + @get:Rule + val hiltRule = HiltAndroidRule(this) + + val account = Account("Test Account", "Test Account Type") + + @Before + fun inject() { + hiltRule.inject() + } + + + @Test + fun testMembership() { + val addressBook = LocalTestAddressBook.create(context, account, provider, GroupMethod.GROUP_VCARDS) + try { + val contact = Contact() + val localContact = LocalContact(addressBook, contact, null, null, 0) + CachedGroupMembershipHandler(localContact).handle(ContentValues().apply { + put(CachedGroupMembership.GROUP_ID, 123456) + put(CachedGroupMembership.RAW_CONTACT_ID, 789) + }, contact) + assertArrayEquals(arrayOf(123456L), localContact.cachedGroupMemberships.toArray()) + } finally { + addressBook.remove() + } + } + + companion object { @JvmField @@ -55,33 +87,4 @@ class CachedGroupMembershipHandlerTest { } - - @Inject - @ApplicationContext - lateinit var context: Context - - @get:Rule - val hiltRule = HiltAndroidRule(this) - - val account = Account("Test Account", "Test Account Type") - - @Before - fun inject() { - hiltRule.inject() - } - - - @Test - fun testMembership() { - val addressBook = LocalTestAddressBook.create(context, account, provider, GroupMethod.GROUP_VCARDS) - - val contact = Contact() - val localContact = LocalContact(addressBook, contact, null, null, 0) - CachedGroupMembershipHandler(localContact).handle(ContentValues().apply { - put(CachedGroupMembership.GROUP_ID, 123456) - put(CachedGroupMembership.RAW_CONTACT_ID, 789) - }, contact) - assertArrayEquals(arrayOf(123456L), localContact.cachedGroupMemberships.toArray()) - } - } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipBuilderTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipBuilderTest.kt index 2499e8ec7..4868bd0d7 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipBuilderTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipBuilderTest.kt @@ -52,10 +52,14 @@ class GroupMembershipBuilderTest { categories += "TEST GROUP" } val addressBookGroupsAsCategories = LocalTestAddressBook.create(context, account, provider, GroupMethod.CATEGORIES) - GroupMembershipBuilder(Uri.EMPTY, null, contact, addressBookGroupsAsCategories, false).build().also { result -> - assertEquals(1, result.size) - assertEquals(GroupMembership.CONTENT_ITEM_TYPE, result[0].values[GroupMembership.MIMETYPE]) - assertEquals(addressBookGroupsAsCategories.findOrCreateGroup("TEST GROUP"), result[0].values[GroupMembership.GROUP_ROW_ID]) + try { + GroupMembershipBuilder(Uri.EMPTY, null, contact, addressBookGroupsAsCategories, false).build().also { result -> + assertEquals(1, result.size) + assertEquals(GroupMembership.CONTENT_ITEM_TYPE, result[0].values[GroupMembership.MIMETYPE]) + assertEquals(addressBookGroupsAsCategories.findOrCreateGroup("TEST GROUP"), result[0].values[GroupMembership.GROUP_ROW_ID]) + } + } finally { + addressBookGroupsAsCategories.remove() } } @@ -65,9 +69,13 @@ class GroupMembershipBuilderTest { categories += "TEST GROUP" } val addressBookGroupsAsVCards = LocalTestAddressBook.create(context, account, provider, GroupMethod.GROUP_VCARDS) - GroupMembershipBuilder(Uri.EMPTY, null, contact, addressBookGroupsAsVCards, false).build().also { result -> - // group membership is constructed during post-processing - assertEquals(0, result.size) + try { + GroupMembershipBuilder(Uri.EMPTY, null, contact, addressBookGroupsAsVCards, false).build().also { result -> + // group membership is constructed during post-processing + assertEquals(0, result.size) + } + } finally { + addressBookGroupsAsVCards.remove() } } @@ -83,7 +91,7 @@ class GroupMembershipBuilderTest { @BeforeClass @JvmStatic fun connect() { - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + val context: Context = InstrumentationRegistry.getInstrumentation().context provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipHandlerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipHandlerTest.kt index 749a18c5c..c463e4452 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipHandlerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipHandlerTest.kt @@ -34,30 +34,6 @@ import javax.inject.Inject @HiltAndroidTest class GroupMembershipHandlerTest { - companion object { - - @JvmField - @ClassRule - val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!! - - private lateinit var provider: ContentProviderClient - - @BeforeClass - @JvmStatic - fun connect() { - val context: Context = InstrumentationRegistry.getInstrumentation().context - provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! - Assert.assertNotNull(provider) - } - - @AfterClass - @JvmStatic - fun disconnect() { - provider.close() - } - - } - @Inject @ApplicationContext lateinit var context: Context @@ -109,4 +85,29 @@ class GroupMembershipHandlerTest { } } + + companion object { + + @JvmField + @ClassRule + val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!! + + private lateinit var provider: ContentProviderClient + + @BeforeClass + @JvmStatic + fun connect() { + val context: Context = InstrumentationRegistry.getInstrumentation().context + provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! + Assert.assertNotNull(provider) + } + + @AfterClass + @JvmStatic + fun disconnect() { + provider.close() + } + + } + } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/AccountsCleanupWorkerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/AccountsCleanupWorkerTest.kt index 590914c4d..bdd80ff68 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/AccountsCleanupWorkerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/AccountsCleanupWorkerTest.kt @@ -11,12 +11,11 @@ import at.bitfire.davdroid.TestUtils import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.resource.LocalAddressBook +import at.bitfire.davdroid.resource.LocalTestAddressBook import at.bitfire.davdroid.settings.SettingsManager import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest -import io.mockk.every -import io.mockk.mockkObject import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -36,8 +35,7 @@ class AccountsCleanupWorkerTest { @Inject lateinit var accountsCleanupWorkerFactory: AccountsCleanupWorker.Factory - @Inject - @ApplicationContext + @Inject @ApplicationContext lateinit var context: Context @Inject @@ -59,15 +57,14 @@ class AccountsCleanupWorkerTest { hiltRule.inject() TestUtils.setUpWorkManager(context, workerFactory) - service = createTestService(Service.TYPE_CARDDAV) - - // Prepare test account accountManager = AccountManager.get(context) + service = createTestService() + addressBookAccountType = context.getString(R.string.account_type_address_book) - addressBookAccount = Account( - "Fancy address book account", - addressBookAccountType - ) + addressBookAccount = Account("Fancy address book account", addressBookAccountType) + + // Make sure there are no address books + LocalTestAddressBook.removeAll(context) } @After @@ -95,27 +92,24 @@ class AccountsCleanupWorkerTest { @Test fun testCleanUpServices_oneAccount() { - val account = Account("test", "test") - val accountManager = AccountManager.get(context) - mockkObject(accountManager) - every { accountManager.getAccountsByType(context.getString(R.string.account_type)) } returns arrayOf(account) - - // Insert services, one that reference the existing account and one that references an invalid account - db.serviceDao().insertOrReplace(Service(id = 1, accountName = account.name, type = Service.TYPE_CALDAV, principal = null)) - assertNotNull(db.serviceDao().get(1)) + TestAccount.provide { existingAccount -> + // Insert services, one that reference the existing account and one that references an invalid account + db.serviceDao().insertOrReplace(Service(id = 1, accountName = existingAccount.name, type = Service.TYPE_CALDAV, principal = null)) + assertNotNull(db.serviceDao().get(1)) - db.serviceDao().insertOrReplace(Service(id = 2, accountName = "not existing", type = Service.TYPE_CARDDAV, principal = null)) - assertNotNull(db.serviceDao().get(2)) + db.serviceDao().insertOrReplace(Service(id = 2, accountName = "not existing", type = Service.TYPE_CARDDAV, principal = null)) + assertNotNull(db.serviceDao().get(2)) - // Create worker and run the method - val worker = TestListenableWorkerBuilder(context) - .setWorkerFactory(workerFactory) - .build() - worker.cleanUpServices() + // Create worker and run the method + val worker = TestListenableWorkerBuilder(context) + .setWorkerFactory(workerFactory) + .build() + worker.cleanUpServices() - // Verify that one service is deleted and the other one is kept - assertNotNull(db.serviceDao().get(1)) - assertNull(db.serviceDao().get(2)) + // Verify that one service is deleted and the other one is kept + assertNotNull(db.serviceDao().get(1)) + assertNull(db.serviceDao().get(2)) + } } @@ -123,9 +117,7 @@ class AccountsCleanupWorkerTest { fun testCleanUpAddressBooks_deletesAddressBookWithoutAccount() { // Create address book account without corresponding account assertTrue(accountManager.addAccountExplicitly(addressBookAccount, null, null)) - - val addressBookAccounts = accountManager.getAccountsByType(addressBookAccountType) - assertEquals(addressBookAccount, addressBookAccounts.firstOrNull()) + assertEquals(listOf(addressBookAccount), accountManager.getAccountsByType(addressBookAccountType).toList()) // Create worker and run the method val worker = TestListenableWorkerBuilder(context) @@ -139,16 +131,14 @@ class AccountsCleanupWorkerTest { @Test fun testCleanUpAddressBooks_keepsAddressBookWithAccount() { - TestAccount.provide { account -> + TestAccount.provide { existingAccount -> // Create address book account _with_ corresponding account and verify val userData = Bundle(2).apply { - putString(LocalAddressBook.USER_DATA_ACCOUNT_NAME, account.name) - putString(LocalAddressBook.USER_DATA_ACCOUNT_TYPE, account.type) + putString(LocalAddressBook.USER_DATA_ACCOUNT_NAME, existingAccount.name) + putString(LocalAddressBook.USER_DATA_ACCOUNT_TYPE, existingAccount.type) } assertTrue(accountManager.addAccountExplicitly(addressBookAccount, null, userData)) - - val addressBookAccounts = accountManager.getAccountsByType(addressBookAccountType) - assertEquals(addressBookAccount, addressBookAccounts.firstOrNull()) + assertEquals(listOf(addressBookAccount), accountManager.getAccountsByType(addressBookAccountType).toList()) // Create worker and run the method val worker = TestListenableWorkerBuilder(context) @@ -157,15 +147,15 @@ class AccountsCleanupWorkerTest { worker.cleanUpAddressBooks() // Verify account was _not_ deleted - assertEquals(addressBookAccount, addressBookAccounts.firstOrNull()) + assertEquals(listOf(addressBookAccount), accountManager.getAccountsByType(addressBookAccountType).toList()) } } // helpers - private fun createTestService(serviceType: String): Service { - val service = Service(id=0, accountName="test", type=serviceType, principal = null) + private fun createTestService(): Service { + val service = Service(id=0, accountName="test", type=Service.TYPE_CARDDAV, principal = null) val serviceId = db.serviceDao().insertOrReplace(service) return db.serviceDao().get(serviceId)!! } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/SystemAccountUtilsTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/SystemAccountUtilsTest.kt index c3ef133e1..b05b36d27 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/SystemAccountUtilsTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/SystemAccountUtilsTest.kt @@ -32,11 +32,6 @@ class SystemAccountUtilsTest { @Inject lateinit var settingsManager: SettingsManager - val account = Account( - "AccountUtilsTest", - context.getString(R.string.account_type) - ) - @Before fun setUp() { hiltRule.inject() @@ -49,6 +44,7 @@ class SystemAccountUtilsTest { userData.putString("int", "1") userData.putString("string", "abc/\"-") + val account = Account("AccountUtilsTest", context.getString(R.string.account_type)) val manager = AccountManager.get(context) try { assertTrue(SystemAccountUtils.createAccount(context, account, userData)) diff --git a/app/src/main/res/xml/sync_calendars.xml b/app/src/main/res/xml/sync_calendars.xml index 7b01f2054..d047b5d56 100644 --- a/app/src/main/res/xml/sync_calendars.xml +++ b/app/src/main/res/xml/sync_calendars.xml @@ -1,6 +1,6 @@ \ No newline at end of file + android:supportsUploading="true" + android:allowParallelSyncs="true" /> \ No newline at end of file diff --git a/app/src/main/res/xml/sync_contacts.xml b/app/src/main/res/xml/sync_contacts.xml index 87b67ec1c..23a5173a1 100644 --- a/app/src/main/res/xml/sync_contacts.xml +++ b/app/src/main/res/xml/sync_contacts.xml @@ -1,6 +1,6 @@ \ No newline at end of file + android:supportsUploading="true" + android:allowParallelSyncs="true" /> \ No newline at end of file diff --git a/app/src/main/res/xml/sync_notes.xml b/app/src/main/res/xml/sync_notes.xml index 1db9a29d0..d571b982f 100644 --- a/app/src/main/res/xml/sync_notes.xml +++ b/app/src/main/res/xml/sync_notes.xml @@ -1,6 +1,6 @@ \ No newline at end of file + android:supportsUploading="true" + android:allowParallelSyncs="true" /> \ No newline at end of file diff --git a/app/src/main/res/xml/sync_opentasks.xml b/app/src/main/res/xml/sync_opentasks.xml index dfa5c7984..6e82cdf2c 100644 --- a/app/src/main/res/xml/sync_opentasks.xml +++ b/app/src/main/res/xml/sync_opentasks.xml @@ -1,6 +1,6 @@ \ No newline at end of file + android:supportsUploading="true" + android:allowParallelSyncs="true" /> \ No newline at end of file diff --git a/app/src/main/res/xml/sync_tasks_org.xml b/app/src/main/res/xml/sync_tasks_org.xml index ecd712be5..f93254975 100644 --- a/app/src/main/res/xml/sync_tasks_org.xml +++ b/app/src/main/res/xml/sync_tasks_org.xml @@ -1,6 +1,6 @@ \ No newline at end of file + android:supportsUploading="true" + android:allowParallelSyncs="true" /> \ No newline at end of file