Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 55d01f28 authored by Fahim Salam Chowdhury's avatar Fahim Salam Chowdhury 👽
Browse files

Merge commit '78e4cbe0' into 634-update_to_v4_2_2_ose

parents 032d0c29 78e4cbe0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -35,5 +35,5 @@ jobs:
        uses: softprops/action-gh-release@v0.1.14
        with:
          prerelease: ${{ contains(github.ref_name, '-alpha') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-rc') }}
          files: app/build/outputs/apk/standard/release/*.apk
          files: app/build/outputs/apk/ose/release/*.apk
          fail_on_unmatched_files: true
+2 −2
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ android {

    flavorDimensions "distribution"
    productFlavors {
        standard {
        ose {
            versionNameSuffix "-ose"
        }
    }
@@ -149,7 +149,7 @@ dependencies {
    implementation "com.squareup.okhttp3:okhttp:${versions.okhttp}"
    implementation "com.squareup.okhttp3:okhttp-brotli:${versions.okhttp}"
    implementation "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}"
    implementation 'commons-io:commons-io:2.11.0'
    implementation 'commons-io:commons-io:2.8.0' // don't update until API level 26 (Android 8) is the minimum API (DAVx5#130)
    //noinspection GradleDependency - dnsjava 3+ needs Java 8/Android 7
    implementation 'dnsjava:dnsjava:2.1.9'
    //noinspection GradleDependency
+58 −37
Original line number Diff line number Diff line
@@ -4,11 +4,15 @@

package at.bitfire.davdroid

import android.Manifest
import android.accounts.Account
import android.content.ContentUris
import android.content.ContentValues
import android.os.Build
import android.provider.CalendarContract
import androidx.annotation.RequiresApi
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.LocalCalendar
import at.bitfire.davdroid.resource.LocalEvent
@@ -16,29 +20,55 @@ import at.bitfire.ical4android.AndroidCalendar
import at.bitfire.ical4android.Event
import net.fortuna.ical4j.model.property.DtStart
import net.fortuna.ical4j.model.property.RRule
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

/**
 * JUnit ClassRule which initializes the AOSP CalendarProvider
 * Needed for some "flaky" tests which would otherwise only succeed on second run
 * JUnit ClassRule which initializes the AOSP CalendarProvider.
 * Needed for some "flaky" tests which would otherwise only succeed on second run.
 *
 * Currently tested on development machine (Ryzen) with Android 12 images (with/without Google Play).
 * Calendar provider behaves quite randomly, so it may or may not work. If you (the reader
 * if this comment) can find out on how to initialize the calendar provider so that the
 * tests are reliably run after `adb shell pm clear com.android.providers.calendar`,
 * please let us know!
 *
 * If you run tests manually, just make sure to ignore the first run after the calendar
 * provider has been accessed the first time.
 */
class InitCalendarProviderRule : TestRule {
class InitCalendarProviderRule private constructor(): TestRule {

    companion object {
        private val account = Account("LocalCalendarTest", CalendarContract.ACCOUNT_TYPE_LOCAL)
        private val context = InstrumentationRegistry.getInstrumentation().targetContext
        private val provider = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!!
        private val uri = AndroidCalendar.create(account, provider, ContentValues())
        private val calendar = AndroidCalendar.findByID(account, provider, LocalCalendar.Factory, ContentUris.parseId(uri))
        fun getInstance() = RuleChain
            .outerRule(GrantPermissionRule.grant(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR))
            .around(InitCalendarProviderRule())
    }

    override fun apply(base: Statement, description: Description): Statement {
        Logger.log.info("Before test: ${description.displayName}")
        Logger.log.info("Initializing calendar provider before running ${description.displayName}")
        return InitCalendarProviderStatement(base)
    }


        Logger.log.info("Initializing CalendarProvider (InitCalendarProviderRule)")
    class InitCalendarProviderStatement(val base: Statement): Statement() {

        override fun evaluate() {
            if (Build.VERSION.SDK_INT < 31)
                Logger.log.warning("Calendar provider initialization may or may not work. See InitCalendarProviderRule")
            initCalendarProvider()

            base.evaluate()
        }

        private fun initCalendarProvider() {
            val account = Account("LocalCalendarTest", CalendarContract.ACCOUNT_TYPE_LOCAL)
            val context = InstrumentationRegistry.getInstrumentation().targetContext
            val provider = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!!
            val uri = AndroidCalendar.create(account, provider, ContentValues())
            val calendar = AndroidCalendar.findByID(account, provider, LocalCalendar.Factory, ContentUris.parseId(uri))
            try {
                // single event init
                val normalEvent = Event().apply {
                    dtStart = DtStart("20220120T010203Z")
@@ -57,19 +87,10 @@ class InitCalendarProviderRule : TestRule {
                val localRecurringEvent = LocalEvent(calendar, recurringEvent, null, null, null, 0)
                localRecurringEvent.add()
                LocalEvent.numInstances(provider, account, localRecurringEvent.id!!)

        // Run test
        Logger.log.info("Evaluating test..")
        return try {
            object : Statement() {
                @Throws(Throwable::class)
                override fun evaluate() {
                    base.evaluate()
                }
            }
            } finally {
            Logger.log.info("After test: $description")
                calendar.delete()
            }
        }
    }

}
 No newline at end of file
+2 −6
Original line number Diff line number Diff line
@@ -31,12 +31,8 @@ class LocalCalendarTest {

    companion object {
        @JvmField
        @ClassRule(order = 0)
        val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR)!!

        @JvmField
        @ClassRule(order = 1)
        val initCalendarProviderRule: TestRule = InitCalendarProviderRule()
        @ClassRule
        val initCalendarProviderRule: TestRule = InitCalendarProviderRule.getInstance()

        private lateinit var provider: ContentProviderClient

+95 −13
Original line number Diff line number Diff line
@@ -6,7 +6,9 @@ package at.bitfire.davdroid.resource

import android.Manifest
import android.accounts.Account
import android.content.*
import android.content.ContentProviderClient
import android.content.ContentUris
import android.content.ContentValues
import android.os.Build
import android.provider.CalendarContract
import android.provider.CalendarContract.ACCOUNT_TYPE_LOCAL
@@ -25,17 +27,15 @@ import net.fortuna.ical4j.model.property.*
import org.junit.*
import org.junit.Assert.*
import org.junit.rules.TestRule
import java.util.*

class LocalEventTest {

    companion object {
        @JvmField
        @ClassRule(order = 0)
        val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR)!!

        @JvmField
        @ClassRule(order = 1)
        val initCalendarProviderRule: TestRule = InitCalendarProviderRule()
        @ClassRule
        val initCalendarProviderRule: TestRule = InitCalendarProviderRule.getInstance()

        private val account = Account("LocalCalendarTest", ACCOUNT_TYPE_LOCAL)

@@ -107,7 +107,7 @@ class LocalEventTest {
    }

    @Test
    // Flaky, Needs rec event init of CalendarProvider
    // flaky, needs InitCalendarProviderRule
    fun testNumDirectInstances_Recurring_LateEnd() {
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
@@ -124,7 +124,7 @@ class LocalEventTest {
    }

    @Test
    // Flaky, Needs rec event init of CalendarProvider
    // flaky, needs InitCalendarProviderRule
    fun testNumDirectInstances_Recurring_ManyInstances() {
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
@@ -141,7 +141,7 @@ class LocalEventTest {
    }

    @Test
    // Flaky, Needs single event init of CalendarProvider
    // flaky, needs InitCalendarProviderRule
    fun testNumDirectInstances_RecurringWithExdate() {
        val event = Event().apply {
            dtStart = DtStart(Date("20220120T010203Z"))
@@ -180,7 +180,7 @@ class LocalEventTest {


    @Test
    // Flaky, Needs single or rec event init of CalendarProvider
    // flaky, needs InitCalendarProviderRule
    fun testNumInstances_SingleInstance() {
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
@@ -219,7 +219,7 @@ class LocalEventTest {
    }

    @Test
    // Flaky, Needs rec event init of CalendarProvider
    // flaky, needs InitCalendarProviderRule
    fun testNumInstances_Recurring_LateEnd() {
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
@@ -236,7 +236,7 @@ class LocalEventTest {
    }

    @Test
    // Flaky, Needs rec event init of CalendarProvider
    // flaky, needs InitCalendarProviderRule
    fun testNumInstances_Recurring_ManyInstances() {
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
@@ -306,6 +306,89 @@ class LocalEventTest {
    }


    @Test
    fun testPrepareForUpload_NoUid() {
        // create event
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
            summary = "Event without uid"
        }
        val localEvent = LocalEvent(calendar, event, null, null, null, 0)
        localEvent.add()    // save it to calendar storage

        // prepare for upload - this should generate a new random uuid, returned as filename
        val fileNameWithSuffix = localEvent.prepareForUpload()
        val fileName = fileNameWithSuffix.removeSuffix(".ics")

        // throws an exception if fileName is not an UUID
        UUID.fromString(fileName)

        // UID in calendar storage should be the same as file name
        provider.query(
            ContentUris.withAppendedId(Events.CONTENT_URI, localEvent.id!!).asSyncAdapter(account),
            arrayOf(Events.UID_2445), null, null, null
        )!!.use { cursor ->
            cursor.moveToFirst()
            assertEquals(fileName, cursor.getString(0))
        }
    }

    @Test
    fun testPrepareForUpload_NormalUid() {
        // create event
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
            summary = "Event with normal uid"
            uid = "some-event@hostname.tld"     // old UID format, UUID would be new format
        }
        val localEvent = LocalEvent(calendar, event, null, null, null, 0)
        localEvent.add() // save it to calendar storage

        // prepare for upload - this should use the UID for the file name
        val fileNameWithSuffix = localEvent.prepareForUpload()
        val fileName = fileNameWithSuffix.removeSuffix(".ics")

        assertEquals(event.uid, fileName)

        // UID in calendar storage should still be set, too
        provider.query(
            ContentUris.withAppendedId(Events.CONTENT_URI, localEvent.id!!).asSyncAdapter(account),
            arrayOf(Events.UID_2445), null, null, null
        )!!.use { cursor ->
            cursor.moveToFirst()
            assertEquals(fileName, cursor.getString(0))
        }
    }

    @Test
    fun testPrepareForUpload_UidHasDangerousChars() {
        // create event
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
            summary = "Event with funny uid"
            uid = "https://www.example.com/events/asdfewfe-cxyb-ewrws-sadfrwerxyvser-asdfxye-"
        }
        val localEvent = LocalEvent(calendar, event, null, null, null, 0)
        localEvent.add() // save it to calendar storage

        // prepare for upload - this should generate a new random uuid, returned as filename
        val fileNameWithSuffix = localEvent.prepareForUpload()
        val fileName = fileNameWithSuffix.removeSuffix(".ics")

        // throws an exception if fileName is not an UUID
        UUID.fromString(fileName)

        // UID in calendar storage shouldn't have been changed
        provider.query(
            ContentUris.withAppendedId(Events.CONTENT_URI, localEvent.id!!).asSyncAdapter(account),
            arrayOf(Events.UID_2445), null, null, null
        )!!.use { cursor ->
            cursor.moveToFirst()
            assertEquals(event.uid, cursor.getString(0))
        }
    }


    @Test
    fun testDeleteDirtyEventsWithoutInstances_NoInstances_Exdate() {
        // TODO
@@ -360,7 +443,6 @@ class LocalEventTest {
    }

    @Test
    // Flaky, Needs single event init OR rec event init of CalendarProvider
    fun testDeleteDirtyEventsWithoutInstances_Recurring_Instances() {
        val event = Event().apply {
            dtStart = DtStart("20220120T010203Z")
Loading