Skip to content

Commit 032fe6f

Browse files
committed
Move InitCalendarProviderRule
1 parent 6b1899a commit 032fe6f

File tree

9 files changed

+146
-11
lines changed

9 files changed

+146
-11
lines changed

lib/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
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+
17
plugins {
28
alias(libs.plugins.android.library)
39
alias(libs.plugins.kotlin.android)
@@ -117,6 +123,9 @@ dependencies {
117123
exclude(group = "org.freemarker")
118124
}
119125

126+
// synctools.test package also provide test rules
127+
implementation(libs.androidx.test.rules)
128+
120129
// instrumented tests
121130
androidTestImplementation(libs.androidx.test.rules)
122131
androidTestImplementation(libs.androidx.test.runner)

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*/
66
package at.bitfire.ical4android
77

8-
import android.Manifest
98
import android.accounts.Account
109
import android.content.ContentProviderClient
1110
import android.content.ContentUris
@@ -22,12 +21,12 @@ import android.provider.CalendarContract.ExtendedProperties
2221
import android.provider.CalendarContract.Reminders
2322
import androidx.core.content.contentValuesOf
2423
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
25-
import androidx.test.rule.GrantPermissionRule
2624
import at.bitfire.ical4android.impl.TestCalendar
2725
import at.bitfire.ical4android.util.AndroidTimeUtils
2826
import at.bitfire.ical4android.util.DateUtils
2927
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
3028
import at.bitfire.ical4android.util.MiscUtils.closeCompat
29+
import at.bitfire.synctools.test.InitCalendarProviderRule
3130
import net.fortuna.ical4j.model.Date
3231
import net.fortuna.ical4j.model.DateList
3332
import net.fortuna.ical4j.model.DateTime
@@ -59,6 +58,7 @@ import org.junit.Before
5958
import org.junit.BeforeClass
6059
import org.junit.ClassRule
6160
import org.junit.Test
61+
import org.junit.rules.TestRule
6262
import java.net.URI
6363
import java.time.Duration
6464
import java.time.Period
@@ -72,10 +72,7 @@ class AndroidEventTest {
7272

7373
@JvmField
7474
@ClassRule
75-
val permissionRule = GrantPermissionRule.grant(
76-
Manifest.permission.READ_CALENDAR,
77-
Manifest.permission.WRITE_CALENDAR
78-
)
75+
val initCalendarProviderRule: TestRule = InitCalendarProviderRule.getInstance()
7976

8077
lateinit var provider: ContentProviderClient
8178

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
package at.bitfire.ical4android
88

99
import androidx.test.platform.app.InstrumentationRegistry
10-
import at.bitfire.synctools.GrantPermissionOrSkipRule
10+
import at.bitfire.synctools.test.GrantPermissionOrSkipRule
1111
import org.junit.After
1212
import org.junit.Assert.assertNotNull
1313
import org.junit.Before

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import androidx.test.platform.app.InstrumentationRegistry
1313
import at.bitfire.ical4android.impl.TestJtxCollection
1414
import at.bitfire.ical4android.impl.testProdId
1515
import at.bitfire.ical4android.util.MiscUtils.closeCompat
16-
import at.bitfire.synctools.GrantPermissionOrSkipRule
16+
import at.bitfire.synctools.test.GrantPermissionOrSkipRule
1717
import at.techbee.jtx.JtxContract
1818
import at.techbee.jtx.JtxContract.asSyncAdapter
1919
import junit.framework.TestCase.assertEquals

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import androidx.test.platform.app.InstrumentationRegistry
1616
import at.bitfire.ical4android.impl.TestJtxCollection
1717
import at.bitfire.ical4android.impl.testProdId
1818
import at.bitfire.ical4android.util.MiscUtils.closeCompat
19-
import at.bitfire.synctools.GrantPermissionOrSkipRule
19+
import at.bitfire.synctools.test.GrantPermissionOrSkipRule
2020
import at.techbee.jtx.JtxContract
2121
import at.techbee.jtx.JtxContract.JtxICalObject
2222
import at.techbee.jtx.JtxContract.JtxICalObject.Component

lib/src/androidTest/kotlin/at/bitfire/synctools/storage/JtxBatchOperationTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import at.bitfire.ical4android.JtxICalObject
1616
import at.bitfire.ical4android.TaskProvider
1717
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
1818
import at.bitfire.ical4android.util.MiscUtils.closeCompat
19-
import at.bitfire.synctools.GrantPermissionOrSkipRule
19+
import at.bitfire.synctools.test.GrantPermissionOrSkipRule
2020
import at.techbee.jtx.JtxContract
2121
import io.mockk.mockk
2222
import org.junit.After

lib/src/main/kotlin/at/bitfire/ical4android/AndroidCalendar.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,11 @@ open class AndroidCalendar(
263263

264264
}
265265

266+
267+
// default factory (will be removed as soon as AndroidCalendar is not open anymore)
268+
object Factory : AndroidCalendarFactory<AndroidCalendar> {
269+
override fun newInstance(account: Account, provider: ContentProviderClient, id: Long) =
270+
AndroidCalendar(account, provider, id)
271+
}
272+
266273
}

lib/src/androidTest/kotlin/at/bitfire/synctools/GrantPermissionOrSkipRule.kt renamed to lib/src/main/kotlin/at/bitfire/synctools/test/GrantPermissionOrSkipRule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: GPL-3.0-or-later
55
*/
66

7-
package at.bitfire.synctools
7+
package at.bitfire.synctools.test
88

99
import androidx.test.rule.GrantPermissionRule
1010
import org.junit.Assume
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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.test
8+
9+
import android.Manifest
10+
import android.accounts.Account
11+
import android.content.ContentProviderClient
12+
import android.content.ContentUris
13+
import android.content.ContentValues
14+
import android.os.Build
15+
import android.provider.CalendarContract
16+
import androidx.test.platform.app.InstrumentationRegistry
17+
import androidx.test.rule.GrantPermissionRule
18+
import at.bitfire.ical4android.AndroidCalendar
19+
import at.bitfire.ical4android.AndroidEvent
20+
import at.bitfire.ical4android.Event
21+
import net.fortuna.ical4j.model.property.DtStart
22+
import net.fortuna.ical4j.model.property.RRule
23+
import org.junit.Assert
24+
import org.junit.rules.ExternalResource
25+
import org.junit.rules.RuleChain
26+
import java.util.logging.Logger
27+
28+
/**
29+
* JUnit ClassRule which initializes the AOSP CalendarProvider.
30+
*
31+
* It seems that the calendar provider unfortunately forgets the very first requests when it is used the very first time,
32+
* maybe by some wrongly synchronized database initialization. So things like querying the instances
33+
* fails in this case.
34+
*
35+
* So this rule is needed to allow tests which need the calendar provider to succeed even when the calendar provider
36+
* is used the very first time (especially in CI tests / a fresh emulator).
37+
*/
38+
class InitCalendarProviderRule private constructor() : ExternalResource() {
39+
40+
companion object {
41+
42+
private var isInitialized = false
43+
private val logger = Logger.getLogger(InitCalendarProviderRule::javaClass.name)
44+
45+
fun getInstance(): RuleChain = RuleChain
46+
.outerRule(GrantPermissionRule.grant(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR))
47+
.around(InitCalendarProviderRule())
48+
49+
}
50+
51+
override fun before() {
52+
if (!isInitialized) {
53+
logger.info("Initializing calendar provider")
54+
if (Build.VERSION.SDK_INT < 31)
55+
logger.warning("Calendar provider initialization may or may not work. See InitCalendarProviderRule")
56+
57+
val context = InstrumentationRegistry.getInstrumentation().targetContext
58+
val client = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)
59+
Assert.assertNotNull("Couldn't acquire calendar provider", client)
60+
61+
client!!.use {
62+
initCalendarProvider(client)
63+
isInitialized = true
64+
}
65+
}
66+
}
67+
68+
private fun initCalendarProvider(provider: ContentProviderClient) {
69+
val account = Account("LocalCalendarTest", CalendarContract.ACCOUNT_TYPE_LOCAL)
70+
71+
// Sometimes, the calendar provider returns an ID for the created calendar, but then fails to find it.
72+
var calendarOrNull: AndroidCalendar? = null
73+
for (i in 0..50) {
74+
calendarOrNull = createAndVerifyCalendar(account, provider)
75+
if (calendarOrNull != null)
76+
break
77+
else
78+
Thread.sleep(100)
79+
}
80+
val calendar = calendarOrNull ?: throw IllegalStateException("Couldn't create calendar")
81+
82+
try {
83+
// single event init
84+
val normalEvent = Event().apply {
85+
dtStart = DtStart("20220120T010203Z")
86+
summary = "Event with 1 instance"
87+
}
88+
val normalLocalEvent = AndroidEvent(calendar, normalEvent, null, null, null, 0)
89+
normalLocalEvent.add()
90+
AndroidEvent.Companion.numInstances(provider, account, normalLocalEvent.id!!)
91+
92+
// recurring event init
93+
val recurringEvent = Event().apply {
94+
dtStart = DtStart("20220120T010203Z")
95+
summary = "Event over 22 years"
96+
rRules.add(RRule("FREQ=YEARLY;UNTIL=20740119T010203Z")) // year needs to be >2074 (not supported by Android <11 Calendar Storage)
97+
}
98+
val localRecurringEvent = AndroidEvent(calendar, recurringEvent, null, null, null, 0)
99+
localRecurringEvent.add()
100+
AndroidEvent.Companion.numInstances(provider, account, localRecurringEvent.id!!)
101+
} finally {
102+
calendar.delete()
103+
}
104+
}
105+
106+
private fun createAndVerifyCalendar(account: Account, provider: ContentProviderClient): AndroidCalendar? {
107+
val uri = AndroidCalendar.Companion.create(account, provider, ContentValues())
108+
109+
return try {
110+
AndroidCalendar.Companion.findByID(
111+
account,
112+
provider,
113+
AndroidCalendar.Factory,
114+
ContentUris.parseId(uri)
115+
)
116+
} catch (e: Exception) {
117+
logger.warning("Couldn't find calendar after creation: $e")
118+
null
119+
}
120+
}
121+
122+
}

0 commit comments

Comments
 (0)