Add tests for 11.3 migration

pull/1309/head
Alex Baker 5 years ago
parent 20f74bec33
commit 137a27432a

@ -0,0 +1,243 @@
@file:Suppress("ClassName")
package com.todoroo.astrid.service
import android.content.ContentProviderResult
import at.bitfire.ical4android.BatchOperation
import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules
import kotlinx.coroutines.runBlocking
import org.dmfs.tasks.contract.TaskContract
import org.dmfs.tasks.contract.TaskContract.CALLER_IS_SYNCADAPTER
import org.dmfs.tasks.contract.TaskContract.TaskLists
import org.junit.Test
import org.tasks.SuspendFreeze.Companion.freezeAt
import org.tasks.TestUtilities.assertEquals
import org.tasks.TestUtilities.fromString
import org.tasks.data.*
import org.tasks.injection.InjectingTestCase
import org.tasks.injection.ProductionModule
import org.tasks.makers.CaldavTaskMaker.CALENDAR
import org.tasks.makers.CaldavTaskMaker.REMOTE_ID
import org.tasks.makers.CaldavTaskMaker.TASK
import org.tasks.makers.CaldavTaskMaker.VTODO
import org.tasks.makers.CaldavTaskMaker.newCaldavTask
import org.tasks.makers.TaskMaker.DUE_DATE
import org.tasks.makers.TaskMaker.HIDE_TYPE
import org.tasks.makers.TaskMaker.MODIFICATION_TIME
import org.tasks.makers.TaskMaker.newTask
import org.tasks.time.DateTime
import javax.inject.Inject
@UninstallModules(ProductionModule::class)
@HiltAndroidTest
class Upgrade_11_3_Test : InjectingTestCase() {
@Inject lateinit var taskDao: TaskDao
@Inject lateinit var caldavDao: CaldavDao
@Inject lateinit var openTaskDao: OpenTaskDao
@Inject lateinit var upgrader: Upgrade_11_3
@Test
fun applyRemoteiCalendarStartDate() = runBlocking {
val taskId = taskDao.insert(newTask())
caldavDao.insert(newCaldavTask(with(TASK, taskId), with(VTODO, VTODO_WITH_START_DATE)))
upgrader.applyiCalendarStartDates()
assertEquals(DateTime(2021, 1, 21), taskDao.fetch(taskId)?.hideUntil)
}
@Test
fun ignoreRemoteiCalendarStartDate() = runBlocking {
val taskId = taskDao.insert(newTask(
with(DUE_DATE, DateTime(2021, 1, 20)),
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE)
))
caldavDao.insert(newCaldavTask(with(TASK, taskId), with(VTODO, VTODO_WITH_START_DATE)))
upgrader.applyiCalendarStartDates()
assertEquals(DateTime(2021, 1, 20), taskDao.fetch(taskId)?.hideUntil)
}
@Test
fun touchTaskWithLocaliCalendarStartDate() = runBlocking {
val upgradeTime = DateTime(2021, 1, 21, 11, 47, 32, 450)
val taskId = taskDao.insert(newTask(
with(DUE_DATE, DateTime(2021, 1, 20)),
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE),
with(MODIFICATION_TIME, DateTime(2021, 1, 21, 9, 50, 4, 348))
))
caldavDao.insert(newCaldavTask(with(TASK, taskId), with(VTODO, VTODO_WITH_START_DATE)))
freezeAt(upgradeTime) {
upgrader.applyiCalendarStartDates()
}
assertEquals(upgradeTime, taskDao.fetch(taskId)?.modificationDate)
}
@Test
fun dontTouchWhenNoiCalendarStartDate() = runBlocking {
val modificationTime = DateTime(2021, 1, 21, 9, 50, 4, 348)
val taskId = taskDao.insert(newTask(with(MODIFICATION_TIME, modificationTime)))
caldavDao.insert(newCaldavTask(with(TASK, taskId), with(VTODO, VTODO_NO_START_DATE)))
upgrader.applyiCalendarStartDates()
assertEquals(modificationTime, taskDao.fetch(taskId)?.modificationDate)
}
@Test
fun applyRemoteOpenTaskStartDate() = runBlocking {
val (listId, list) = insertList()
applyOperation(
MyAndroidTask(fromString(VTODO_WITH_START_DATE))
.toBuilder(openTaskDao.tasks, true)
.withValue(TaskContract.TaskColumns.LIST_ID, listId)
)
val taskId = taskDao.insert(newTask())
caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid),
with(REMOTE_ID, "4586964443060640060"),
with(TASK, taskId)
))
upgrader.applyOpenTaskStartDates()
assertEquals(DateTime(2021, 1, 21), taskDao.fetch(taskId)?.hideUntil)
}
@Test
fun ignoreRemoteOpenTaskStartDate() = runBlocking {
val (listId, list) = insertList()
applyOperation(
MyAndroidTask(fromString(VTODO_WITH_START_DATE))
.toBuilder(openTaskDao.tasks, true)
.withValue(TaskContract.TaskColumns.LIST_ID, listId)
)
val taskId = taskDao.insert(newTask(
with(DUE_DATE, DateTime(2021, 1, 20)),
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE)
))
caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid),
with(REMOTE_ID, "4586964443060640060"),
with(TASK, taskId)
))
upgrader.applyOpenTaskStartDates()
assertEquals(DateTime(2021, 1, 20), taskDao.fetch(taskId)?.hideUntil)
}
@Test
fun touchWithOpenTaskStartDate() = runBlocking {
val upgradeTime = DateTime(2021, 1, 21, 11, 47, 32, 450)
val (listId, list) = insertList()
applyOperation(
MyAndroidTask(fromString(VTODO_WITH_START_DATE))
.toBuilder(openTaskDao.tasks, true)
.withValue(TaskContract.TaskColumns.LIST_ID, listId)
)
val taskId = taskDao.insert(newTask(
with(DUE_DATE, DateTime(2021, 1, 20)),
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE),
with(MODIFICATION_TIME, DateTime(2021, 1, 21, 9, 50, 4, 348))
))
caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid),
with(REMOTE_ID, "4586964443060640060"),
with(TASK, taskId)
))
freezeAt(upgradeTime) {
upgrader.applyOpenTaskStartDates()
}
assertEquals(upgradeTime, taskDao.fetch(taskId)?.modificationDate)
}
@Test
fun dontTouchNoOpenTaskStartDate() = runBlocking {
val modificationTime = DateTime(2021, 1, 21, 9, 50, 4, 348)
val (listId, list) = insertList()
applyOperation(
MyAndroidTask(fromString(VTODO_NO_START_DATE))
.toBuilder(openTaskDao.tasks, true)
.withValue(TaskContract.TaskColumns.LIST_ID, listId)
)
val taskId = taskDao.insert(newTask(with(MODIFICATION_TIME, modificationTime)))
caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid),
with(REMOTE_ID, "4586964443060640060"),
with(TASK, taskId)
))
upgrader.applyOpenTaskStartDates()
assertEquals(modificationTime, taskDao.fetch(taskId)?.modificationDate)
}
private suspend fun insertList(
type: String = OpenTaskDao.ACCOUNT_TYPE_DAVx5,
account: String = "account"
): Pair<String, CaldavCalendar> {
val url = UUIDHelper.newUUID()
val uri = openTaskDao.taskLists.buildUpon()
.appendQueryParameter(CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(TaskLists.ACCOUNT_NAME, account)
.appendQueryParameter(TaskLists.ACCOUNT_TYPE, type)
.build()
val result = applyOperation(
BatchOperation.CpoBuilder.newInsert(uri)
.withValue(TaskContract.CommonSyncColumns._SYNC_ID, url)
.withValue(TaskLists.SYNC_ENABLED, "1")
)
return Pair(result.uri!!.lastPathSegment!!, CaldavCalendar().apply {
uuid = UUIDHelper.newUUID()
this.account = "$type:$account"
this.url = url
caldavDao.insert(this)
})
}
private fun applyOperation(build: BatchOperation.CpoBuilder): ContentProviderResult =
context.contentResolver.applyBatch(openTaskDao.authority, arrayListOf(build.build()))[0]
companion object {
val VTODO_WITH_START_DATE = """
BEGIN:VCALENDAR
VERSION:2.0
PRODID:+//IDN tasks.org//android-110301//EN
BEGIN:VTODO
DTSTAMP:20210121T153032Z
UID:4586964443060640060
CREATED:20210121T153000Z
LAST-MODIFIED:20210121T153029Z
SUMMARY:Test
PRIORITY:9
X-APPLE-SORT-ORDER:-27
DTSTART;VALUE=DATE:20210121
END:VTODO
END:VCALENDAR
""".trimIndent()
val VTODO_NO_START_DATE = """
BEGIN:VCALENDAR
VERSION:2.0
PRODID:+//IDN tasks.org//android-110301//EN
BEGIN:VTODO
DTSTAMP:20210121T153032Z
UID:4586964443060640060
CREATED:20210121T153000Z
LAST-MODIFIED:20210121T153029Z
SUMMARY:Test
PRIORITY:9
X-APPLE-SORT-ORDER:-27
END:VTODO
END:VCALENDAR
""".trimIndent()
}
}

@ -6,11 +6,18 @@ import com.todoroo.astrid.data.Task
import org.tasks.caldav.CaldavConverter import org.tasks.caldav.CaldavConverter
import org.tasks.data.CaldavTask import org.tasks.data.CaldavTask
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTime
import java.io.StringReader import java.io.StringReader
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
object TestUtilities { object TestUtilities {
fun assertEquals(expected: Long, actual: DateTime) =
org.junit.Assert.assertEquals(expected, actual.millis)
fun assertEquals(expected: DateTime, actual: Long?) =
org.junit.Assert.assertEquals(expected.millis, actual)
fun newPreferences(context: Context): Preferences { fun newPreferences(context: Context): Preferences {
return Preferences(context, "test_preferences") return Preferences(context, "test_preferences")
} }
@ -39,7 +46,7 @@ object TestUtilities {
return String(Files.readAllBytes(paths), Charsets.UTF_8) return String(Files.readAllBytes(paths), Charsets.UTF_8)
} }
private fun fromString(task: String): at.bitfire.ical4android.Task = fun fromString(task: String): at.bitfire.ical4android.Task =
tasksFromReader(StringReader(task)) tasksFromReader(StringReader(task))
.takeIf { it.size == 1 } .takeIf { it.size == 1 }
?.first() ?.first()

@ -12,11 +12,13 @@ object CaldavTaskMaker {
val TASK: Property<CaldavTask, Long> = newProperty() val TASK: Property<CaldavTask, Long> = newProperty()
val REMOTE_ID: Property<CaldavTask, String?> = newProperty() val REMOTE_ID: Property<CaldavTask, String?> = newProperty()
val REMOTE_PARENT: Property<CaldavTask, String?> = newProperty() val REMOTE_PARENT: Property<CaldavTask, String?> = newProperty()
val VTODO: Property<CaldavTask, String?> = newProperty()
private val instantiator = Instantiator<CaldavTask> { private val instantiator = Instantiator<CaldavTask> {
val task = CaldavTask(it.valueOf(TASK, 1L), it.valueOf(CALENDAR, "calendar")) val task = CaldavTask(it.valueOf(TASK, 1L), it.valueOf(CALENDAR, "calendar"))
task.remoteId = it.valueOf(REMOTE_ID, task.remoteId) task.remoteId = it.valueOf(REMOTE_ID, task.remoteId)
task.remoteParent = it.valueOf(REMOTE_PARENT, null as String?) task.remoteParent = it.valueOf(REMOTE_PARENT, null as String?)
task.vtodo = it.valueOf(VTODO, null as String?)
task task
} }

@ -21,6 +21,7 @@ object TaskMaker {
val RANDOM_REMINDER_PERIOD: Property<Task, Long> = newProperty() val RANDOM_REMINDER_PERIOD: Property<Task, Long> = newProperty()
val HIDE_TYPE: Property<Task, Int> = newProperty() val HIDE_TYPE: Property<Task, Int> = newProperty()
val REMINDERS: Property<Task, Int> = newProperty() val REMINDERS: Property<Task, Int> = newProperty()
val MODIFICATION_TIME: Property<Task, DateTime> = newProperty()
val CREATION_TIME: Property<Task, DateTime> = newProperty() val CREATION_TIME: Property<Task, DateTime> = newProperty()
val COMPLETION_TIME: Property<Task, DateTime> = newProperty() val COMPLETION_TIME: Property<Task, DateTime> = newProperty()
val DELETION_TIME: Property<Task, DateTime?> = newProperty() val DELETION_TIME: Property<Task, DateTime?> = newProperty()
@ -89,7 +90,7 @@ object TaskMaker {
task.uuid = lookup.valueOf(UUID, NO_UUID) task.uuid = lookup.valueOf(UUID, NO_UUID)
val creationTime = lookup.valueOf(CREATION_TIME, DateTimeUtils.newDateTime()) val creationTime = lookup.valueOf(CREATION_TIME, DateTimeUtils.newDateTime())
task.creationDate = creationTime.millis task.creationDate = creationTime.millis
task.modificationDate = creationTime.millis task.modificationDate = lookup.valueOf(MODIFICATION_TIME, creationTime).millis
task.parent = lookup.valueOf(PARENT, 0L) task.parent = lookup.valueOf(PARENT, 0L)
task task
} }

@ -0,0 +1,55 @@
@file:Suppress("ClassName")
package com.todoroo.astrid.service
import org.tasks.caldav.iCalendar
import org.tasks.caldav.iCalendar.Companion.apply
import org.tasks.data.OpenTaskDao
import org.tasks.data.TaskDao
import org.tasks.data.UpgraderDao
import javax.inject.Inject
class Upgrade_11_3 @Inject constructor(
private val upgraderDao: UpgraderDao,
private val openTaskDao: OpenTaskDao,
private val taskDao: TaskDao
) {
internal suspend fun applyiCalendarStartDates() {
val (hasStartDate, noStartDate) =
upgraderDao.tasksWithVtodos().partition { it.startDate > 0 }
for (task in noStartDate) {
task.vtodo?.let { iCalendar.fromVtodo(it) }?.dtStart?.let {
it.apply(task.task)
upgraderDao.setStartDate(task.id, task.startDate)
}
}
hasStartDate
.map { it.id }
.let { taskDao.touch(it) }
}
internal suspend fun applyOpenTaskStartDates() {
openTaskDao.getLists().forEach { list ->
val (hasStartDate, noStartDate) =
upgraderDao
.getOpenTasksForList(list.account!!, list.url!!)
.partition { it.startDate > 0 }
for (task in noStartDate) {
openTaskDao
.getTask(list.id, task.remoteId!!)
?.dtStart
?.let {
it.apply(task.task)
upgraderDao.setStartDate(task.id, task.startDate)
}
}
hasStartDate
.map { it.id }
.let { taskDao.touch(it) }
}
}
companion object {
const val VERSION = 110300
}
}

@ -7,12 +7,12 @@ import com.google.common.collect.ListMultimap
import com.google.common.collect.Multimaps import com.google.common.collect.Multimaps
import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import dagger.Lazy
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.tasks.R import org.tasks.R
import org.tasks.Strings.isNullOrEmpty import org.tasks.Strings.isNullOrEmpty
import org.tasks.caldav.iCalendar import org.tasks.caldav.iCalendar
import org.tasks.caldav.iCalendar.Companion.apply
import org.tasks.caldav.iCalendar.Companion.fromVtodo import org.tasks.caldav.iCalendar.Companion.fromVtodo
import org.tasks.caldav.iCalendar.Companion.getParent import org.tasks.caldav.iCalendar.Companion.getParent
import org.tasks.caldav.iCalendar.Companion.order import org.tasks.caldav.iCalendar.Companion.order
@ -43,7 +43,7 @@ class Upgrader @Inject constructor(
private val widgetManager: AppWidgetManager, private val widgetManager: AppWidgetManager,
private val taskMover: TaskMover, private val taskMover: TaskMover,
private val upgraderDao: UpgraderDao, private val upgraderDao: UpgraderDao,
private val openTaskDao: OpenTaskDao) { private val upgrade_11_3: Lazy<Upgrade_11_3>) {
fun upgrade(from: Int, to: Int) { fun upgrade(from: Int, to: Int) {
if (from > 0) { if (from > 0) {
@ -72,10 +72,12 @@ class Upgrader @Inject constructor(
.filter { it.getSql().trim() == "WHERE" } .filter { it.getSql().trim() == "WHERE" }
.forEach { filterDao.delete(it) } .forEach { filterDao.delete(it) }
} }
run(from, V11_3) { run(from, Upgrade_11_3.VERSION) {
with(upgrade_11_3.get()) {
applyiCalendarStartDates() applyiCalendarStartDates()
applyOpenTaskStartDates() applyOpenTaskStartDates()
} }
}
preferences.setBoolean(R.string.p_just_updated, true) preferences.setBoolean(R.string.p_just_updated, true)
} }
preferences.setCurrentVersion(to) preferences.setCurrentVersion(to)
@ -122,39 +124,6 @@ class Upgrader @Inject constructor(
return getAndroidColor(context, index) return getAndroidColor(context, index)
} }
private suspend fun applyiCalendarStartDates() {
val (hasStartDate, noStartDate) = upgraderDao.tasksWithVtodos().partition { it.startDate > 0 }
for (task in noStartDate) {
task.vtodo?.let { fromVtodo(it) }?.dtStart?.let {
it.apply(task.task)
upgraderDao.setStartDate(task.id, task.startDate)
}
}
hasStartDate
.map { it.id }
.let { taskDao.touch(it) }
}
private suspend fun applyOpenTaskStartDates() {
openTaskDao.getLists().forEach { list ->
val (hasStartDate, noStartDate) =
upgraderDao
.getOpenTasksForList(list.account!!, list.url!!)
.partition { it.startDate > 0 }
for (task in noStartDate) {
openTaskDao
.getTask(list.id, task.remoteId!!)
?.dtStart
?.let {
it.apply(task.task)
upgraderDao.setStartDate(task.id, task.startDate)
}
}
hasStartDate
.map { it.id }
.let { taskDao.touch(it) }
}
}
private suspend fun applyCaldavOrder() { private suspend fun applyCaldavOrder() {
for (task in upgraderDao.tasksWithVtodos().map(CaldavTaskContainer::caldavTask)) { for (task in upgraderDao.tasksWithVtodos().map(CaldavTaskContainer::caldavTask)) {
@ -357,7 +326,6 @@ class Upgrader @Inject constructor(
const val V9_7 = 90700 const val V9_7 = 90700
const val V9_7_3 = 90704 const val V9_7_3 = 90704
const val V10_0_2 = 100012 const val V10_0_2 = 100012
const val V11_3 = 110300
@JvmStatic @JvmStatic
fun getAndroidColor(context: Context, index: Int): Int { fun getAndroidColor(context: Context, index: Int): Int {

@ -6,6 +6,7 @@ import android.content.ContentProviderOperation.newInsert
import android.content.ContentValues import android.content.ContentValues
import android.content.Context import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.net.Uri
import at.bitfire.ical4android.BatchOperation import at.bitfire.ical4android.BatchOperation
import at.bitfire.ical4android.Task import at.bitfire.ical4android.Task
import at.bitfire.ical4android.UnknownProperty import at.bitfire.ical4android.UnknownProperty
@ -28,7 +29,8 @@ class OpenTaskDao @Inject constructor(
) { ) {
private val cr = context.contentResolver private val cr = context.contentResolver
val authority = context.getString(R.string.opentasks_authority) val authority = context.getString(R.string.opentasks_authority)
val tasks = Tasks.getContentUri(authority) val tasks: Uri = Tasks.getContentUri(authority)
val taskLists: Uri = TaskLists.getContentUri(authority)
private val properties = Properties.getContentUri(authority) private val properties = Properties.getContentUri(authority)
suspend fun newAccounts(): List<String> = getListsByAccount().newAccounts(caldavDao) suspend fun newAccounts(): List<String> = getListsByAccount().newAccounts(caldavDao)
@ -39,7 +41,7 @@ class OpenTaskDao @Inject constructor(
suspend fun getLists(): List<CaldavCalendar> = withContext(Dispatchers.IO) { suspend fun getLists(): List<CaldavCalendar> = withContext(Dispatchers.IO) {
val calendars = ArrayList<CaldavCalendar>() val calendars = ArrayList<CaldavCalendar>()
cr.query( cr.query(
TaskLists.getContentUri(authority), taskLists,
null, null,
"${TaskListColumns.SYNC_ENABLED}=1 AND ($SUPPORTED_TYPE_FILTER)", "${TaskListColumns.SYNC_ENABLED}=1 AND ($SUPPORTED_TYPE_FILTER)",
null, null,
@ -192,10 +194,7 @@ class OpenTaskDao @Inject constructor(
suspend fun getTask(listId: Long, uid: String): Task? = withContext(Dispatchers.IO) { suspend fun getTask(listId: Long, uid: String): Task? = withContext(Dispatchers.IO) {
cr.query( cr.query(
Tasks.getContentUri(authority) tasks.buildUpon().appendQueryParameter(LOAD_PROPERTIES, "1").build(),
.buildUpon()
.appendQueryParameter(LOAD_PROPERTIES, "1")
.build(),
null, null,
"${Tasks.LIST_ID} = $listId AND ${Tasks._UID} = '$uid'", "${Tasks.LIST_ID} = $listId AND ${Tasks._UID} = '$uid'",
null, null,
@ -210,7 +209,7 @@ class OpenTaskDao @Inject constructor(
companion object { companion object {
private const val OPENTASK_BATCH_LIMIT = 499 private const val OPENTASK_BATCH_LIMIT = 499
private const val ACCOUNT_TYPE_DAVx5 = "bitfire.at.davdroid" const val ACCOUNT_TYPE_DAVx5 = "bitfire.at.davdroid"
private const val ACCOUNT_TYPE_ETESYNC = "com.etesync.syncadapter" private const val ACCOUNT_TYPE_ETESYNC = "com.etesync.syncadapter"
private const val ACCOUNT_TYPE_DECSYNC = "org.decsync.tasks" private const val ACCOUNT_TYPE_DECSYNC = "org.decsync.tasks"
val SUPPORTED_TYPES = setOf( val SUPPORTED_TYPES = setOf(

Loading…
Cancel
Save