Skip to content

Commit 3eab4eb

Browse files
committed
Adapt tests
1 parent 8da9161 commit 3eab4eb

File tree

4 files changed

+54
-27
lines changed

4 files changed

+54
-27
lines changed

lib/src/androidTest/kotlin/at/bitfire/synctools/storage/calendar/AndroidCalendarTest.kt

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import android.accounts.Account
1010
import android.content.ContentProviderClient
1111
import android.content.ContentValues
1212
import android.content.Entity
13-
import android.os.Build
1413
import android.provider.CalendarContract
1514
import android.provider.CalendarContract.Events
1615
import android.provider.CalendarContract.Reminders
@@ -43,7 +42,8 @@ class AndroidCalendarTest {
4342

4443
@Before
4544
fun prepare() {
46-
client = InstrumentationRegistry.getInstrumentation().targetContext.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!!
45+
val context = InstrumentationRegistry.getInstrumentation().targetContext
46+
client = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!!
4747

4848
// make sure there are no colors for testAccount
4949
provider = AndroidCalendarProvider(testAccount, client)
@@ -283,7 +283,7 @@ class AndroidCalendarTest {
283283
}
284284

285285

286-
// event instances
286+
// event instances (we always test numDirectInstances + numInstances together)
287287

288288
@Test
289289
fun testNumInstances_SingleInstance() {
@@ -293,6 +293,7 @@ class AndroidCalendarTest {
293293
Events.DTEND to now + 3600000,
294294
Events.TITLE to "Event with 1 instance"
295295
)))
296+
assertEquals(1, calendar.numDirectInstances(id))
296297
assertEquals(1, calendar.numInstances(id))
297298
}
298299

@@ -305,6 +306,7 @@ class AndroidCalendarTest {
305306
Events.TITLE to "Event with 5 instances",
306307
Events.RRULE to "FREQ=DAILY;COUNT=5"
307308
)))
309+
assertEquals(5, calendar.numDirectInstances(id))
308310
assertEquals(5, calendar.numInstances(id))
309311
}
310312

@@ -317,6 +319,7 @@ class AndroidCalendarTest {
317319
Events.TITLE to "Event without end",
318320
Events.RRULE to "FREQ=DAILY"
319321
)))
322+
assertNull(calendar.numDirectInstances(id))
320323
assertNull(calendar.numInstances(id))
321324
}
322325

@@ -330,10 +333,13 @@ class AndroidCalendarTest {
330333
Events.RRULE to "FREQ=YEARLY;UNTIL=20740119T010203Z"
331334
)))
332335

333-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
336+
if (AndroidCalendarProvider.supportsYear2074) {
337+
assertEquals(52, calendar.numDirectInstances(id))
334338
assertEquals(52, calendar.numInstances(id))
335-
else // year 2074 is not supported by Android <11 Calendar Storage
339+
} else {
340+
assertNull(calendar.numDirectInstances(id))
336341
assertNull(calendar.numInstances(id))
342+
}
337343
}
338344

339345
@Test
@@ -346,11 +352,11 @@ class AndroidCalendarTest {
346352
Events.RRULE to "FREQ=DAILY;UNTIL=20240120T010203Z"
347353
)))
348354
assertEquals(
349-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
350-
365 * 2 // Android <9: does not include UNTIL (incorrect!)
355+
if (AndroidCalendarProvider.instancesIncludeUntil)
356+
365 * 2 + 1 // Android 9: includes UNTIL (correct)
351357
else
352-
365 * 2 + 1, // Android 9: includes UNTIL (correct)
353-
calendar.numInstances(id)
358+
365 * 2, // Android <9: does not include UNTIL (incorrect!)
359+
calendar.numDirectInstances(id)
354360
)
355361
}
356362

@@ -364,16 +370,12 @@ class AndroidCalendarTest {
364370
Events.RRULE to "FREQ=DAILY;COUNT=5",
365371
Events.EXDATE to "20220121T010203Z"
366372
)))
373+
assertEquals(4, calendar.numDirectInstances(id))
367374
assertEquals(4, calendar.numInstances(id))
368375
}
369376

370377
@Test
371378
fun testNumInstances_RecurringWithExceptions_MatchingOrigInstanceTime() {
372-
// TODO: Fails when
373-
// 1) Force-stop calendar storage
374-
// 2) Clear calendar storage
375-
// 3) Run this this class
376-
377379
val syncId = "recurring-with-exceptions"
378380
val id = calendar.addEvent(Entity(contentValuesOf(
379381
Events.CALENDAR_ID to calendar.id,
@@ -389,7 +391,7 @@ class AndroidCalendarTest {
389391
Events.ORIGINAL_INSTANCE_TIME to 1642640523000 + 2*86400000,
390392
Events.DTSTART to 1642640523000 + 2*86400000 + 3600000, // one hour later
391393
Events.DTEND to 1642640523000 + 2*86400000 + 2*3600000,
392-
Events.TITLE to "Exception on 3rd day",
394+
Events.TITLE to "Exception on 3rd day"
393395
)))
394396
calendar.addEvent(Entity(contentValuesOf(
395397
Events.CALENDAR_ID to calendar.id,
@@ -398,8 +400,10 @@ class AndroidCalendarTest {
398400
Events.DTSTART to 1642640523000 + 4*86400000 + 3600000, // one hour later
399401
Events.DTEND to 1642640523000 + 4*86400000 + 2*3600000,
400402
Events.TITLE to "Exception on 5th day",
403+
Events.STATUS to Events.STATUS_CANCELED
401404
)))
402-
assertEquals(5 - 2, calendar.numInstances(id))
405+
assertEquals(5 - 2, calendar.numDirectInstances(id))
406+
assertEquals(5 - /* one cancelled */ 1, calendar.numInstances(id))
403407
}
404408

405409
@Test
@@ -419,17 +423,18 @@ class AndroidCalendarTest {
419423
Events.ORIGINAL_INSTANCE_TIME to 1642640523000 + 2*86400000,
420424
Events.DTSTART to 1642640523000 + 2*86400000 + 3600000, // one hour later
421425
Events.DURATION to "PT1H",
422-
Events.TITLE to "Exception on 3rd day",
426+
Events.TITLE to "Exception on 3rd day"
423427
)))
424428
calendar.addEvent(Entity(contentValuesOf(
425429
Events.CALENDAR_ID to calendar.id,
426430
Events.ORIGINAL_SYNC_ID to syncId,
427431
Events.ORIGINAL_INSTANCE_TIME to 1642640523000 + 4*86400000 + 100, // doesn't match original instance time!
428432
Events.DTSTART to 1642640523000 + 4*86400000 + 3600000, // one hour later
429433
Events.DURATION to "PT1H",
430-
Events.TITLE to "Exception on 5th day (wrong instance time)",
434+
Events.TITLE to "Exception on 5th day (wrong instance time)"
431435
)))
432-
assertEquals(5 - 1, calendar.numInstances(id))
436+
assertEquals(5 - 1, calendar.numDirectInstances(id))
437+
assertEquals(5 + /* one extra outside the recurrence */ 1, calendar.numInstances(id))
433438
}
434439

435440
}

lib/src/main/kotlin/at/bitfire/synctools/storage/calendar/AndroidCalendar.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ class AndroidCalendar(
357357
*
358358
* @throws LocalStorageException when the content provider returns an error
359359
*/
360-
fun numInstances(eventId: Long): Int? {
360+
fun numDirectInstances(eventId: Long): Int? {
361361
// query event to get first and last instance
362362
var first: Long? = null
363363
var last: Long? = null
@@ -393,11 +393,21 @@ class AndroidCalendar(
393393
return numInstances
394394
}
395395

396-
fun numInstancesIncludingExceptions(eventId: Long): Int? {
397-
val numDirectInstances = numInstances(eventId) ?: return null
396+
fun numInstances(eventId: Long): Int? {
397+
val numDirectInstances = numDirectInstances(eventId) ?: return null
398398

399399
// add instances generated by exceptions
400-
return numDirectInstances
400+
var numExInstances = 0
401+
iterateEventRows(
402+
arrayOf(Events._ID),
403+
"${Events.ORIGINAL_ID}=?", arrayOf(eventId.toString())
404+
) { exception ->
405+
val exceptionId = exception.getAsLong(Events._ID)
406+
// an exception can have 0 instances (if cancelled) or 1 instance (but it can't be recurring)
407+
numExInstances += numDirectInstances(exceptionId) ?: 0
408+
}
409+
410+
return numDirectInstances + numExInstances
401411
}
402412

403413

lib/src/main/kotlin/at/bitfire/synctools/storage/calendar/AndroidCalendarProvider.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import android.accounts.Account
1010
import android.content.ContentProviderClient
1111
import android.content.ContentUris
1212
import android.content.ContentValues
13+
import android.os.Build
1314
import android.os.RemoteException
1415
import android.provider.CalendarContract
1516
import android.provider.CalendarContract.Calendars
@@ -315,6 +316,18 @@ class AndroidCalendarProvider(
315316
*/
316317
const val COLUMN_CALENDAR_SYNC_STATE = Calendars.CAL_SYNC1
317318

319+
/**
320+
* When calculating instances of recurring events with an `RRULE:...;UNTIL=xxx`,
321+
* old calendar provider versions don't include the instance exactly at UNTIL
322+
* as they should.
323+
*/
324+
val instancesIncludeUntil = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
325+
326+
/**
327+
* Not all calendar provider versions support recurring events up to the year 2074.
328+
*/
329+
val supportsYear2074 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
330+
318331
}
319332

320333
}

lib/src/main/kotlin/at/bitfire/synctools/test/InitCalendarProviderRule.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import android.Manifest
1010
import android.accounts.Account
1111
import android.content.ContentProviderClient
1212
import android.content.Entity
13-
import android.os.Build
1413
import android.provider.CalendarContract
1514
import android.provider.CalendarContract.Calendars
1615
import android.provider.CalendarContract.Events
@@ -124,10 +123,10 @@ class InitCalendarProviderRule private constructor() : TestRule {
124123
Events.DTEND to 1642640523000 + 4*86400000 + 2*3600000,
125124
Events.TITLE to "Exception on 5th day",
126125
)))
127-
calendar.numInstances(id) == 3
126+
calendar.numDirectInstances(id) == 3
128127
}
129128

130-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) // year 2074 is not supported by Android <11 Calendar Storage
129+
if (AndroidCalendarProvider.supportsYear2074)
131130
tryUntilTrue {
132131
val id = calendar.addEvent(Entity(contentValuesOf(
133132
Events.CALENDAR_ID to calendar.id,
@@ -136,7 +135,7 @@ class InitCalendarProviderRule private constructor() : TestRule {
136135
Events.TITLE to "Event until 2074",
137136
Events.RRULE to "FREQ=YEARLY;UNTIL=20740119T010203Z"
138137
)))
139-
calendar.numInstances(id) == 52
138+
calendar.numDirectInstances(id) == 52
140139
}
141140
} finally {
142141
calendar.delete()

0 commit comments

Comments
 (0)