Skip to content

Commit 2a45226

Browse files
sunkuprfc2822
andauthored
Add unified BatchOperation to synctools package (#14)
* Add generic BatchOperation to synctools * Replace vcard4android.BatchOperation with generic synctools.BatchOperation * Replace ical4android.BatchOperation with generic synctools.BatchOperation * Move BatchOperationTest to synctools package and rename methods * Create tests for max operations per yield point * Increase max operations per yield point for contacts provider to 499 * Don't expect exception for calendar provider with high number of operations * Alter kdoc * Add task provider tests * Use GrantPermissionOrSkipRule to skip task provider tests in CI * Fix test names and add tests for expected failure * [WIP] Update BatchOperation constructor to accept maxOperationsPerYieldPoint * Move BatchOperation to storage package and add specific subclasses + tests for different providers * Add `+=` operator as shortcut * Converter classes: require specific BatchOperation; BatchOperation: replace `enqueue` by `+=` operator --------- Co-authored-by: Ricki Hirner <[email protected]>
1 parent c37ecc2 commit 2a45226

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+646
-655
lines changed

lib/src/androidTest/kotlin/at/bitfire/ical4android/AttendeeMappingsTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package at.bitfire.ical4android
99
import android.content.ContentValues
1010
import android.net.Uri
1111
import android.provider.CalendarContract.Attendees
12+
import at.bitfire.synctools.storage.BatchOperation
1213
import net.fortuna.ical4j.model.Parameter
1314
import net.fortuna.ical4j.model.parameter.CuType
1415
import net.fortuna.ical4j.model.parameter.Role

lib/src/androidTest/kotlin/at/bitfire/ical4android/BatchOperationTest.kt

Lines changed: 0 additions & 135 deletions
This file was deleted.

lib/src/androidTest/kotlin/at/bitfire/ical4android/DmfsTaskTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import android.net.Uri
1414
import at.bitfire.ical4android.impl.TestTask
1515
import at.bitfire.ical4android.impl.TestTaskList
1616
import at.bitfire.ical4android.util.DateUtils
17-
import at.bitfire.synctools.LocalStorageException
17+
import at.bitfire.synctools.storage.LocalStorageException
1818
import net.fortuna.ical4j.model.Date
1919
import net.fortuna.ical4j.model.DateList
2020
import net.fortuna.ical4j.model.DateTime

lib/src/androidTest/kotlin/at/bitfire/ical4android/impl/TestEvent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import android.provider.CalendarContract.Events
1111
import at.bitfire.ical4android.AndroidCalendar
1212
import at.bitfire.ical4android.AndroidEvent
1313
import at.bitfire.ical4android.AndroidEventFactory
14-
import at.bitfire.ical4android.BatchOperation
1514
import at.bitfire.ical4android.Event
15+
import at.bitfire.synctools.storage.BatchOperation
1616
import java.util.UUID
1717

1818
class TestEvent: AndroidEvent {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.storage
8+
9+
import android.content.ContentProviderClient
10+
import android.os.TransactionTooLargeException
11+
import androidx.core.net.toUri
12+
import io.mockk.every
13+
import io.mockk.mockk
14+
import io.mockk.verify
15+
import org.junit.Test
16+
17+
class BatchOperationTest {
18+
19+
@Test
20+
fun testSplitLargeTransaction() {
21+
val provider = mockk<ContentProviderClient>(relaxed = true)
22+
23+
val maxSize = 100
24+
every { provider.applyBatch(match { it.size > maxSize }) } throws TransactionTooLargeException()
25+
26+
val batch = BatchOperation(provider, null)
27+
repeat(4*maxSize) {
28+
batch += BatchOperation.CpoBuilder.newInsert("test://".toUri())
29+
}
30+
batch.commit()
31+
32+
// one too large batch (with 400 operations) +
33+
// then two still too large batches with 200 operations each +
34+
// then four batches with 100 operations each
35+
verify(exactly = 7) { provider.applyBatch(any()) }
36+
}
37+
38+
@Test(expected = LocalStorageException::class)
39+
fun testSplitLargeTransaction_OneTooBigRow() {
40+
val provider = mockk<ContentProviderClient>()
41+
42+
every { provider.applyBatch(any()) } throws TransactionTooLargeException()
43+
44+
val batch = BatchOperation(provider, null)
45+
batch += BatchOperation.CpoBuilder.newInsert("test://".toUri())
46+
batch.commit()
47+
}
48+
49+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.storage
8+
9+
import android.Manifest
10+
import android.accounts.Account
11+
import android.content.ContentProviderClient
12+
import android.provider.CalendarContract
13+
import android.provider.CalendarContract.Events
14+
import androidx.test.platform.app.InstrumentationRegistry
15+
import androidx.test.rule.GrantPermissionRule
16+
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
17+
import at.bitfire.ical4android.util.MiscUtils.closeCompat
18+
import org.junit.After
19+
import org.junit.Before
20+
import org.junit.Rule
21+
import org.junit.Test
22+
23+
class CalendarBatchOperationTest {
24+
25+
@get:Rule
26+
val permissionRule = GrantPermissionRule.grant(
27+
Manifest.permission.READ_CALENDAR,
28+
Manifest.permission.WRITE_CALENDAR
29+
)
30+
31+
private val testAccount = Account(javaClass.name, CalendarContract.ACCOUNT_TYPE_LOCAL)
32+
33+
lateinit var provider: ContentProviderClient
34+
35+
@Before
36+
fun setUp() {
37+
provider = InstrumentationRegistry.getInstrumentation().targetContext.contentResolver
38+
.acquireContentProviderClient(CalendarContract.AUTHORITY)!!
39+
}
40+
41+
@After
42+
fun tearDown() {
43+
// delete all events in test account
44+
provider.delete(
45+
Events.CONTENT_URI,
46+
"${Events.ACCOUNT_TYPE}=? AND ${Events.ACCOUNT_NAME}=?",
47+
arrayOf(testAccount.type, testAccount.name)
48+
)
49+
provider.closeCompat()
50+
}
51+
52+
53+
@Test
54+
fun testCalendarProvider_OperationsPerYieldPoint_501() {
55+
val batch = CalendarBatchOperation(provider)
56+
57+
// 501 operations should succeed with CalendarBatchOperation
58+
repeat(501) { idx ->
59+
batch += BatchOperation.CpoBuilder.newInsert(Events.CONTENT_URI.asSyncAdapter(testAccount))
60+
.withValue(Events.TITLE, "Event $idx")
61+
}
62+
batch.commit()
63+
}
64+
65+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.storage
8+
9+
import android.Manifest
10+
import android.accounts.Account
11+
import android.content.ContentProviderClient
12+
import android.provider.ContactsContract
13+
import androidx.test.platform.app.InstrumentationRegistry
14+
import androidx.test.rule.GrantPermissionRule
15+
import at.bitfire.ical4android.util.MiscUtils.closeCompat
16+
import org.junit.After
17+
import org.junit.Before
18+
import org.junit.Rule
19+
import org.junit.Test
20+
21+
class ContactsBatchOperationTest {
22+
23+
@get:Rule
24+
val permissionRule = GrantPermissionRule.grant(
25+
Manifest.permission.READ_CONTACTS,
26+
Manifest.permission.WRITE_CONTACTS
27+
)
28+
29+
private val testAccount = Account(javaClass.name, javaClass.packageName)
30+
31+
lateinit var provider: ContentProviderClient
32+
33+
@Before
34+
fun setUp() {
35+
provider = InstrumentationRegistry.getInstrumentation().targetContext.contentResolver
36+
.acquireContentProviderClient(ContactsContract.AUTHORITY)!!
37+
}
38+
39+
@After
40+
fun tearDown() {
41+
// delete all contacts in test account
42+
provider.delete(
43+
ContactsContract.RawContacts.CONTENT_URI,
44+
"${ContactsContract.RawContacts.ACCOUNT_TYPE}=? AND ${ContactsContract.RawContacts.ACCOUNT_NAME}=?",
45+
arrayOf(testAccount.type, testAccount.name)
46+
)
47+
provider.closeCompat()
48+
}
49+
50+
51+
@Test(expected = LocalStorageException::class)
52+
fun testContactsProvider_OperationsPerYieldPoint_500_WithoutMax() {
53+
val batch = BatchOperation(provider, maxOperationsPerYieldPoint = null)
54+
55+
// 500 operations should fail with BatchOperation(maxOperationsPerYieldPoint = null) (max. 499)
56+
repeat(500) { idx ->
57+
batch += BatchOperation.CpoBuilder.newInsert(ContactsContract.RawContacts.CONTENT_URI)
58+
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, testAccount.type)
59+
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, testAccount.name)
60+
}
61+
batch.commit()
62+
}
63+
64+
@Test
65+
fun testContactsProvider_OperationsPerYieldPoint_501() {
66+
val batch = ContactsBatchOperation(provider)
67+
68+
// 501 operations should succeed with ContactsBatchOperation
69+
repeat(501) { idx ->
70+
batch += BatchOperation.CpoBuilder.newInsert(ContactsContract.RawContacts.CONTENT_URI)
71+
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, testAccount.type)
72+
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, testAccount.name)
73+
}
74+
batch.commit()
75+
}
76+
77+
}

0 commit comments

Comments
 (0)