diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookTest.kt index 2971ad9f7..7de546919 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookTest.kt @@ -6,16 +6,13 @@ package at.bitfire.davdroid.resource import android.Manifest import android.accounts.Account -import android.accounts.AccountManager import android.content.ContentProviderClient import android.content.ContentUris import android.content.Context import android.provider.ContactsContract import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule -import at.bitfire.davdroid.R import at.bitfire.vcard4android.Contact -import at.bitfire.vcard4android.GroupMethod import at.bitfire.vcard4android.LabeledProperty import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule @@ -25,7 +22,7 @@ import org.junit.After import org.junit.AfterClass import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse -import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.BeforeClass import org.junit.ClassRule @@ -37,17 +34,13 @@ import javax.inject.Inject @HiltAndroidTest class LocalAddressBookTest { - @get:Rule - val hiltRule = HiltAndroidRule(this) - - @Inject - lateinit var addressbookFactory: LocalTestAddressBook.Factory - @Inject @ApplicationContext lateinit var context: Context - val targetContext by lazy { InstrumentationRegistry.getInstrumentation().targetContext } - val account by lazy { Account("Test Account", targetContext.getString(R.string.account_type)) } + @get:Rule + val hiltRule = HiltAndroidRule(this) + + val account = Account("Test Account", "Test Account Type") lateinit var addressBook: LocalTestAddressBook @@ -55,15 +48,13 @@ class LocalAddressBookTest { fun setUp() { hiltRule.inject() - addressBook = addressbookFactory.create(account, provider, GroupMethod.CATEGORIES) - LocalTestAddressBook.createAccount(context) + addressBook = LocalTestAddressBook.create(context, account, provider) } @After fun tearDown() { // remove address book - val accountManager = AccountManager.get(context) - accountManager.removeAccountExplicitly(addressBook.addressBookAccount) + addressBook.remove() } @@ -88,7 +79,7 @@ class LocalAddressBookTest { // rename address book val newName = "New Name" addressBook.renameAccount(newName) - assertEquals(Account(newName, LocalTestAddressBook.ACCOUNT.type), addressBook.addressBookAccount) + assertEquals(newName, addressBook.addressBookAccount.name) // check whether contact is still here (including data rows) and not dirty val result = addressBook.findContactById(id) @@ -116,8 +107,8 @@ class LocalAddressBookTest { // rename address book val newName = "New Name" - addressBook.renameAccount(newName) - assertEquals(Account(newName, LocalTestAddressBook.ACCOUNT.type), addressBook.addressBookAccount) + assertTrue(addressBook.renameAccount(newName)) + assertEquals(newName, addressBook.addressBookAccount.name) // check whether group is still here and not dirty val result = addressBook.findGroupById(id) @@ -127,6 +118,7 @@ class LocalAddressBookTest { assertEquals("Test Group", group.displayName) } + companion object { @JvmField @@ -138,9 +130,8 @@ class LocalAddressBookTest { @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/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 d2da3dba7..8f07d48c9 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt @@ -21,6 +21,7 @@ import at.bitfire.vcard4android.GroupMethod import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.After import org.junit.AfterClass import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -37,39 +38,12 @@ import javax.inject.Inject @HiltAndroidTest class LocalGroupTest { - 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 = InstrumentationRegistry.getInstrumentation().targetContext - provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! - assertNotNull(provider) - } - - @AfterClass - @JvmStatic - fun disconnect() { - provider.close() - } - } + @Inject @ApplicationContext + lateinit var context: Context @get:Rule val hiltRule = HiltAndroidRule(this) - @Inject - @ApplicationContext - lateinit var context: Context - - @Inject - lateinit var addressbookFactory: LocalTestAddressBook.Factory - val account = Account("Test Account", "Test Account Type") private lateinit var addressBookGroupsAsCategories: LocalTestAddressBook private lateinit var addressBookGroupsAsVCards: LocalTestAddressBook @@ -78,14 +52,20 @@ class LocalGroupTest { fun setup() { hiltRule.inject() - addressBookGroupsAsCategories = addressbookFactory.create(account, provider, GroupMethod.CATEGORIES) - addressBookGroupsAsVCards = addressbookFactory.create(account, provider, GroupMethod.GROUP_VCARDS) + addressBookGroupsAsCategories = LocalTestAddressBook.create(context, account, provider, GroupMethod.CATEGORIES) + addressBookGroupsAsVCards = LocalTestAddressBook.create(context, account, provider, GroupMethod.GROUP_VCARDS) // clear contacts addressBookGroupsAsCategories.clear() addressBookGroupsAsVCards.clear() } + @After + fun tearDown() { + addressBookGroupsAsCategories.remove() + addressBookGroupsAsVCards.remove() + } + @Test fun testApplyPendingMemberships_addPendingMembership() { @@ -279,4 +259,27 @@ class LocalGroupTest { add() } + + 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 = InstrumentationRegistry.getInstrumentation().context + provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! + } + + @AfterClass + @JvmStatic + fun disconnect() { + provider.close() + } + } + } \ No newline at end of file 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 9f934f5b1..1cebec3dc 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalTestAddressBook.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalTestAddressBook.kt @@ -10,7 +10,6 @@ import android.content.ContentProviderClient import android.content.ContentUris import android.content.Context import android.provider.ContactsContract -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.R import at.bitfire.davdroid.repository.AccountRepository import at.bitfire.davdroid.repository.DavCollectionRepository @@ -21,26 +20,31 @@ import at.bitfire.vcard4android.GroupMethod import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import dagger.hilt.InstallIn +import dagger.hilt.android.EntryPointAccessors import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import org.junit.Assert.assertTrue import java.io.FileNotFoundException import java.util.Optional +import java.util.concurrent.atomic.AtomicInteger import java.util.logging.Logger class LocalTestAddressBook @AssistedInject constructor( @Assisted account: Account, + @Assisted("addressBook") addressBookAccount: Account, @Assisted provider: ContentProviderClient, @Assisted override val groupMethod: GroupMethod, accountRepository: AccountRepository, accountSettingsFactory: AccountSettings.Factory, collectionRepository: DavCollectionRepository, - @ApplicationContext context: Context, + @ApplicationContext private val context: Context, logger: Logger, serviceRepository: DavServiceRepository, syncFramework: SyncFrameworkIntegration ): LocalAddressBook( account = account, - _addressBookAccount = ACCOUNT, + _addressBookAccount = addressBookAccount, provider = provider, accountRepository = accountRepository, accountSettingsFactory = accountSettingsFactory, @@ -54,7 +58,7 @@ class LocalTestAddressBook @AssistedInject constructor( @AssistedFactory interface Factory { - fun create(account: Account, provider: ContentProviderClient, groupMethod: GroupMethod): LocalTestAddressBook + fun create(account: Account, @Assisted("addressBook") addressBookAccount: Account, provider: ContentProviderClient, groupMethod: GroupMethod): LocalTestAddressBook } override var readOnly: Boolean @@ -102,15 +106,43 @@ class LocalTestAddressBook @AssistedInject constructor( throw FileNotFoundException() } + fun remove() { + val accountManager = AccountManager.get(context) + assertTrue(accountManager.removeAccountExplicitly(addressBookAccount)) + } + companion object { - val targetContext by lazy { InstrumentationRegistry.getInstrumentation().targetContext } - val ACCOUNT = Account("LocalTestAddressBook", targetContext.getString(R.string.account_type_address_book)) + @dagger.hilt.EntryPoint + @InstallIn(SingletonComponent::class) + interface EntryPoint { + fun localTestAddressBookFactory(): Factory + } + + 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)) + val accountManager = AccountManager.get(context) + assertTrue(accountManager.addAccountExplicitly(addressBookAccount, null, null)) + + // return address book with this account + val entryPoint = EntryPointAccessors.fromApplication(context) + val factory = entryPoint.localTestAddressBookFactory() + return factory.create(account, addressBookAccount, provider, groupMethod) + } - fun createAccount(context: Context) { - val am = AccountManager.get(context) - assertTrue("Couldn't create account for local test address-book", am.addAccountExplicitly(ACCOUNT, null, null)) + 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) } } 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 88b8479d5..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,36 +87,4 @@ class CachedGroupMembershipHandlerTest { } - - @Inject - lateinit var addressbookFactory: LocalTestAddressBook.Factory - - @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 = addressbookFactory.create(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 f41aafe77..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 @@ -31,41 +31,15 @@ import javax.inject.Inject @HiltAndroidTest class GroupMembershipBuilderTest { - 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().targetContext - provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! - } - - @AfterClass - @JvmStatic - fun disconnect() { - provider.close() - } - - } + @Inject @ApplicationContext + lateinit var context: Context @get:Rule val hiltRule = HiltAndroidRule(this) - @Inject - lateinit var addressbookFactory: LocalTestAddressBook.Factory - - @Inject - @ApplicationContext - lateinit var context: Context - val account = Account("Test Account", "Test Account Type") + @Before fun inject() { hiltRule.inject() @@ -77,11 +51,15 @@ class GroupMembershipBuilderTest { val contact = Contact().apply { categories += "TEST GROUP" } - val addressBookGroupsAsCategories = addressbookFactory.create(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]) + val addressBookGroupsAsCategories = LocalTestAddressBook.create(context, account, provider, GroupMethod.CATEGORIES) + 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() } } @@ -90,11 +68,39 @@ class GroupMembershipBuilderTest { val contact = Contact().apply { categories += "TEST GROUP" } - val addressBookGroupsAsVCards = addressbookFactory.create(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) + val addressBookGroupsAsVCards = LocalTestAddressBook.create(context, account, provider, GroupMethod.GROUP_VCARDS) + 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() + } + } + + + 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)!! + } + + @AfterClass + @JvmStatic + fun disconnect() { + provider.close() } + } } \ No newline at end of file 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 fff590aa5..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,6 +34,58 @@ import javax.inject.Inject @HiltAndroidTest class GroupMembershipHandlerTest { + @Inject @ApplicationContext + lateinit var context: Context + + @get:Rule + var hiltRule = HiltAndroidRule(this) + + val account = Account("Test Account", "Test Account Type") + + @Before + fun inject() { + hiltRule.inject() + } + + + @Test + fun testMembership_GroupsAsCategories() { + val addressBookGroupsAsCategories = LocalTestAddressBook.create(context, account, provider, GroupMethod.CATEGORIES) + try { + val addressBookGroupsAsCategoriesGroup = addressBookGroupsAsCategories.findOrCreateGroup("TEST GROUP") + + val contact = Contact() + val localContact = LocalContact(addressBookGroupsAsCategories, contact, null, null, 0) + GroupMembershipHandler(localContact).handle(ContentValues().apply { + put(CachedGroupMembership.GROUP_ID, addressBookGroupsAsCategoriesGroup) + put(CachedGroupMembership.RAW_CONTACT_ID, -1) + }, contact) + assertArrayEquals(arrayOf(addressBookGroupsAsCategoriesGroup), localContact.groupMemberships.toArray()) + assertArrayEquals(arrayOf("TEST GROUP"), contact.categories.toArray()) + } finally { + addressBookGroupsAsCategories.remove() + } + } + + + @Test + fun testMembership_GroupsAsVCards() { + val addressBookGroupsAsVCards = LocalTestAddressBook.create(context, account, provider, GroupMethod.GROUP_VCARDS) + try { + val contact = Contact() + val localContact = LocalContact(addressBookGroupsAsVCards, contact, null, null, 0) + GroupMembershipHandler(localContact).handle(ContentValues().apply { + put(CachedGroupMembership.GROUP_ID, 12345) // because the group name is not queried and put into CATEGORIES, the group doesn't have to really exist + put(CachedGroupMembership.RAW_CONTACT_ID, -1) + }, contact) + assertArrayEquals(arrayOf(12345L), localContact.groupMemberships.toArray()) + assertTrue(contact.categories.isEmpty()) + } finally { + addressBookGroupsAsVCards.remove() + } + } + + companion object { @JvmField @@ -58,51 +110,4 @@ class GroupMembershipHandlerTest { } - @Inject - lateinit var addressbookFactory: LocalTestAddressBook.Factory - - @Inject @ApplicationContext - lateinit var context: Context - - @get:Rule - var hiltRule = HiltAndroidRule(this) - - val account = Account("Test Account", "Test Account Type") - - @Before - fun inject() { - hiltRule.inject() - } - - - @Test - fun testMembership_GroupsAsCategories() { - val addressBookGroupsAsCategories = addressbookFactory.create(account, provider, GroupMethod.CATEGORIES) - val addressBookGroupsAsCategoriesGroup = addressBookGroupsAsCategories.findOrCreateGroup("TEST GROUP") - - val contact = Contact() - val localContact = LocalContact(addressBookGroupsAsCategories, contact, null, null, 0) - GroupMembershipHandler(localContact).handle(ContentValues().apply { - put(CachedGroupMembership.GROUP_ID, addressBookGroupsAsCategoriesGroup) - put(CachedGroupMembership.RAW_CONTACT_ID, -1) - }, contact) - assertArrayEquals(arrayOf(addressBookGroupsAsCategoriesGroup), localContact.groupMemberships.toArray()) - assertArrayEquals(arrayOf("TEST GROUP"), contact.categories.toArray()) - } - - - @Test - fun testMembership_GroupsAsVCards() { - val addressBookGroupsAsVCards = addressbookFactory.create(account, provider, GroupMethod.GROUP_VCARDS) - - val contact = Contact() - val localContact = LocalContact(addressBookGroupsAsVCards, contact, null, null, 0) - GroupMembershipHandler(localContact).handle(ContentValues().apply { - put(CachedGroupMembership.GROUP_ID, 12345) // because the group name is not queried and put into CATEGORIES, the group doesn't have to really exist - put(CachedGroupMembership.RAW_CONTACT_ID, -1) - }, contact) - assertArrayEquals(arrayOf(12345L), localContact.groupMemberships.toArray()) - assertTrue(contact.categories.isEmpty()) - } - } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/settings/AccountSettingsTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/settings/AccountSettingsTest.kt index a9d98b686..67eb29a8a 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/settings/AccountSettingsTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/settings/AccountSettingsTest.kt @@ -7,7 +7,7 @@ package at.bitfire.davdroid.settings import android.accounts.AccountManager import android.content.Context import at.bitfire.davdroid.TestUtils -import at.bitfire.davdroid.sync.account.TestAccountAuthenticator +import at.bitfire.davdroid.sync.account.TestAccount import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest @@ -39,7 +39,7 @@ class AccountSettingsTest { @Test(expected = IllegalArgumentException::class) fun testUpdate_MissingMigrations() { - TestAccountAuthenticator.provide(version = 1) { account -> + TestAccount.provide(version = 1) { account -> // will run AccountSettings.update accountSettingsFactory.create(account, abortOnMissingMigration = true) } @@ -47,7 +47,7 @@ class AccountSettingsTest { @Test fun testUpdate_RunAllMigrations() { - TestAccountAuthenticator.provide(version = 6) { account -> + TestAccount.provide(version = 6) { account -> // will run AccountSettings.update accountSettingsFactory.create(account, abortOnMissingMigration = true) diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration17Test.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration17Test.kt index 2132ac4c0..c3b773970 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration17Test.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration17Test.kt @@ -13,7 +13,7 @@ import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.resource.LocalAddressBook -import at.bitfire.davdroid.sync.account.TestAccountAuthenticator +import at.bitfire.davdroid.sync.account.TestAccount import at.bitfire.davdroid.sync.account.setAndVerifyUserData import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule @@ -54,7 +54,7 @@ class AccountSettingsMigration17Test { @Test fun testMigrate_OldAddressBook_CollectionInDB() { - TestAccountAuthenticator.provide(version = 16) { account -> + TestAccount.provide(version = 16) { account -> val accountManager = AccountManager.get(context) val addressBookAccountType = context.getString(R.string.account_type_address_book) var addressBookAccount = Account("Address Book", addressBookAccountType) diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncAdapterServicesTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncAdapterServicesTest.kt index faa77050d..9bf5055f7 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncAdapterServicesTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncAdapterServicesTest.kt @@ -17,7 +17,7 @@ import at.bitfire.davdroid.repository.AccountRepository import at.bitfire.davdroid.repository.DavCollectionRepository import at.bitfire.davdroid.repository.DavServiceRepository import at.bitfire.davdroid.settings.AccountSettings -import at.bitfire.davdroid.sync.account.TestAccountAuthenticator +import at.bitfire.davdroid.sync.account.TestAccount import at.bitfire.davdroid.sync.worker.SyncWorkerManager import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule @@ -30,7 +30,6 @@ import io.mockk.mockk import io.mockk.mockkObject import io.mockk.mockkStatic import io.mockk.unmockkAll -import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow @@ -44,6 +43,7 @@ import org.junit.Test import org.junit.rules.Timeout import java.util.logging.Logger import javax.inject.Inject +import kotlin.coroutines.cancellation.CancellationException @HiltAndroidTest class SyncAdapterServicesTest { @@ -88,12 +88,12 @@ class SyncAdapterServicesTest { hiltRule.inject() TestUtils.setUpWorkManager(context, workerFactory) - account = TestAccountAuthenticator.create() + account = TestAccount.create() } @After fun tearDown() { - TestAccountAuthenticator.remove(account) + TestAccount.remove(account) unmockkAll() } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncManagerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncManagerTest.kt index 219baba6e..d9d9e8174 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncManagerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncManagerTest.kt @@ -19,7 +19,7 @@ import at.bitfire.davdroid.db.SyncState import at.bitfire.davdroid.network.HttpClient import at.bitfire.davdroid.repository.DavSyncStatsRepository import at.bitfire.davdroid.settings.AccountSettings -import at.bitfire.davdroid.sync.account.TestAccountAuthenticator +import at.bitfire.davdroid.sync.account.TestAccount import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -78,14 +78,14 @@ class SyncManagerTest { hiltRule.inject() TestUtils.setUpWorkManager(context, workerFactory) - account = TestAccountAuthenticator.create() + account = TestAccount.create() server.start() } @After fun tearDown() { - TestAccountAuthenticator.remove(account) + TestAccount.remove(account) // clear annoying syncError notifications NotificationManagerCompat.from(context).cancelAll() 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 931cea3f3..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 @@ -44,10 +44,7 @@ class SystemAccountUtilsTest { userData.putString("int", "1") userData.putString("string", "abc/\"-") - val account = Account( - "AccountUtilsTest", - context.getString(R.string.account_type) - ) + 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/androidTest/kotlin/at/bitfire/davdroid/sync/account/TestAccount.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/TestAccount.kt new file mode 100644 index 000000000..58fe2d2c9 --- /dev/null +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/account/TestAccount.kt @@ -0,0 +1,53 @@ +/*************************************************************************************************** + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + **************************************************************************************************/ +package at.bitfire.davdroid.sync.account + +import android.accounts.Account +import android.accounts.AccountManager +import androidx.test.platform.app.InstrumentationRegistry +import at.bitfire.davdroid.R +import at.bitfire.davdroid.settings.AccountSettings +import org.junit.Assert.assertTrue + +object TestAccount { + + private val targetContext by lazy { InstrumentationRegistry.getInstrumentation().targetContext } + + /** + * Creates a test account, usually in the `Before` setUp of a test. + * + * Remove it with [remove]. + */ + fun create(version: Int = AccountSettings.CURRENT_VERSION): Account { + val accountType = targetContext.getString(R.string.account_type) + val account = Account("Test Account", accountType) + + val initialData = AccountSettings.initialUserData(null) + initialData.putString(AccountSettings.KEY_SETTINGS_VERSION, version.toString()) + assertTrue(SystemAccountUtils.createAccount(targetContext, account, initialData)) + + return account + } + + /** + * Removes a test account, usually in the `@After` tearDown of a test. + */ + fun remove(account: Account) { + val am = AccountManager.get(targetContext) + assertTrue(am.removeAccountExplicitly(account)) + } + + /** + * Convenience method to create a test account and remove it after executing the block. + */ + fun provide(version: Int = AccountSettings.CURRENT_VERSION, block: (Account) -> Unit) { + val account = create(version) + try { + block(account) + } finally { + remove(account) + } + } + +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorkerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorkerTest.kt index 2966ec514..cb135848d 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorkerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorkerTest.kt @@ -14,7 +14,7 @@ import androidx.work.testing.TestListenableWorkerBuilder import androidx.work.workDataOf import at.bitfire.davdroid.TestUtils import at.bitfire.davdroid.sync.SyncDataType -import at.bitfire.davdroid.sync.account.TestAccountAuthenticator +import at.bitfire.davdroid.sync.account.TestAccount import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest @@ -47,12 +47,12 @@ class PeriodicSyncWorkerTest { hiltRule.inject() TestUtils.setUpWorkManager(context) - account = TestAccountAuthenticator.create() + account = TestAccount.create() } @After fun tearDown() { - TestAccountAuthenticator.remove(account) + TestAccount.remove(account) } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/SyncWorkerManagerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/SyncWorkerManagerTest.kt index b28864a39..c7f8be84c 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/SyncWorkerManagerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/SyncWorkerManagerTest.kt @@ -10,7 +10,7 @@ import androidx.hilt.work.HiltWorkerFactory import at.bitfire.davdroid.TestUtils import at.bitfire.davdroid.TestUtils.workScheduledOrRunning import at.bitfire.davdroid.sync.SyncDataType -import at.bitfire.davdroid.sync.account.TestAccountAuthenticator +import at.bitfire.davdroid.sync.account.TestAccount import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest @@ -46,12 +46,12 @@ class SyncWorkerManagerTest { hiltRule.inject() TestUtils.setUpWorkManager(context, workerFactory) - account = TestAccountAuthenticator.create() + account = TestAccount.create() } @After fun tearDown() { - TestAccountAuthenticator.remove(account) + TestAccount.remove(account) } diff --git a/app/src/androidTest/res/values/strings.xml b/app/src/androidTest/res/values/strings.xml index 8daf014d1..75c07e1a5 100644 --- a/app/src/androidTest/res/values/strings.xml +++ b/app/src/androidTest/res/values/strings.xml @@ -10,6 +10,5 @@ Davx5Test - at.bitfire.davdroid.test \ No newline at end of file diff --git a/app/src/main/kotlin/at/bitfire/davdroid/db/Collection.kt b/app/src/main/kotlin/at/bitfire/davdroid/db/Collection.kt index e79668823..1fbc0269f 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/db/Collection.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/db/Collection.kt @@ -4,6 +4,7 @@ package at.bitfire.davdroid.db +import androidx.annotation.StringDef import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey @@ -29,6 +30,14 @@ import at.bitfire.ical4android.util.DateUtils import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +@Retention(AnnotationRetention.SOURCE) +@StringDef( + Collection.TYPE_ADDRESSBOOK, + Collection.TYPE_CALENDAR, + Collection.TYPE_WEBCAL +) +annotation class CollectionType + @Entity(tableName = "collection", foreignKeys = [ ForeignKey(entity = Service::class, parentColumns = arrayOf("id"), childColumns = arrayOf("serviceId"), onDelete = ForeignKey.CASCADE), @@ -67,7 +76,8 @@ data class Collection( /** * Type of service. CalDAV or CardDAV */ - var type: String, + @CollectionType + val type: String, /** * Address where this collection lives - with trailing slash diff --git a/app/src/main/res/xml/sync_calendars.xml b/app/src/main/res/xml/sync_calendars.xml index f1bfe60a6..d047b5d56 100644 --- a/app/src/main/res/xml/sync_calendars.xml +++ b/app/src/main/res/xml/sync_calendars.xml @@ -1,7 +1,6 @@ + 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 094973199..23a5173a1 100644 --- a/app/src/main/res/xml/sync_contacts.xml +++ b/app/src/main/res/xml/sync_contacts.xml @@ -1,7 +1,6 @@ \ No newline at end of file + 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 21d538796..d571b982f 100644 --- a/app/src/main/res/xml/sync_notes.xml +++ b/app/src/main/res/xml/sync_notes.xml @@ -1,7 +1,6 @@ \ No newline at end of file + 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 c1beb0fad..6e82cdf2c 100644 --- a/app/src/main/res/xml/sync_opentasks.xml +++ b/app/src/main/res/xml/sync_opentasks.xml @@ -1,7 +1,6 @@ \ No newline at end of file + 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 b77b8434f..f93254975 100644 --- a/app/src/main/res/xml/sync_tasks_org.xml +++ b/app/src/main/res/xml/sync_tasks_org.xml @@ -1,7 +1,6 @@ \ No newline at end of file + android:allowParallelSyncs="true" /> \ No newline at end of file