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

Unverified Commit f95d63c4 authored by Arnau Mora's avatar Arnau Mora Committed by GitHub
Browse files

Handle period durations of days with T prefix (#86)



* Fixed invalid day offset parsing (closes #77)

Signed-off-by: default avatarArnau Mora <arnyminer.z@gmail.com>

* Strengthened tests for `FixInvalidDayOffsetPreprocessor`

Signed-off-by: default avatarArnau Mora <arnyminer.z@gmail.com>

* Optimized imports

Signed-off-by: default avatarArnau Mora <arnyminer.z@gmail.com>

* Use mockk to mock objects; ICalPreprocessorTest: test stream processor application

* Added ICS test

Signed-off-by: default avatarArnau Mora <arnyminer.z@gmail.com>

* Removed ICS test

Signed-off-by: default avatarArnau Mora <arnyminer.z@gmail.com>

* Added multiple durations test

Signed-off-by: default avatarArnau Mora <arnyminer.z@gmail.com>

* Rename methods and add clarifying comments

---------

Signed-off-by: default avatarArnau Mora <arnyminer.z@gmail.com>
Co-authored-by: default avatarRicki Hirner <hirner@bitfire.at>
Co-authored-by: default avatarSunik Kupfer <kupfer@bitfire.at>
parent 8c52fc84
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -95,8 +95,9 @@ dependencies {
    implementation 'org.slf4j:slf4j-jdk14:2.0.3'
    implementation 'androidx.core:core-ktx:1.9.0'

    androidTestImplementation 'androidx.test:core:1.4.0'
    androidTestImplementation 'androidx.test:runner:1.4.0'
    androidTestImplementation 'androidx.test:rules:1.4.0'
    androidTestImplementation 'androidx.test:core:1.5.0'
    androidTestImplementation 'androidx.test:runner:1.5.2'
    androidTestImplementation 'androidx.test:rules:1.5.0'
    androidTestImplementation 'io.mockk:mockk-android:1.13.4'
    testImplementation 'junit:junit:4.13.2'
}
 No newline at end of file
+16 −98
Original line number Diff line number Diff line
@@ -4,119 +4,37 @@

package at.bitfire.ical4android

import at.bitfire.ical4android.validation.FixInvalidDayOffsetPreprocessor
import at.bitfire.ical4android.validation.FixInvalidUtcOffsetPreprocessor
import at.bitfire.ical4android.validation.ICalPreprocessor
import io.mockk.mockkObject
import io.mockk.verify
import java.io.InputStreamReader
import java.io.StringReader
import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.model.Component
import net.fortuna.ical4j.model.component.VEvent
import org.apache.commons.io.IOUtils
import org.junit.Assert.assertEquals
import org.junit.Test
import java.io.InputStreamReader
import java.io.StringReader
import java.time.Duration

class ICalPreprocessorTest {

    @Test
    fun testFixInvalidUtcOffset() {
        val invalid = "BEGIN:VEVENT" +
                "SUMMARY:Test" +
                "DTSTART;TZID=Test:19970714T133000" +
                "END:VEVENT" +
                "BEGIN:VTIMEZONE\n" +
                "TZID:Test\n" +
                "BEGIN:DAYLIGHT\n" +
                "DTSTART:19670430T020000\n" +
                "TZOFFSETFROM:-5730\n" +
                "TZOFFSETTO:+1920\n" +
                "TZNAME:EDT\n" +
                "END:DAYLIGHT\n" +
                "BEGIN:STANDARD\n" +
                "DTSTART:19671029T020000\n" +
                "TZOFFSETFROM:-0400\n" +
                "TZOFFSETTO:-0500\n" +
                "TZNAME:EST" +
                "END:STANDARD\n" +
                "END:VTIMEZONE"
        val valid = "BEGIN:VEVENT" +
                "SUMMARY:Test" +
                "DTSTART;TZID=Test:19970714T133000" +
                "END:VEVENT" +
                "BEGIN:VTIMEZONE\n" +
                "TZID:Test\n" +
                "BEGIN:DAYLIGHT\n" +
                "DTSTART:19670430T020000\n" +
                "TZOFFSETFROM:-005730\n" +
                "TZOFFSETTO:+001920\n" +
                "TZNAME:EDT\n" +
                "END:DAYLIGHT\n" +
                "BEGIN:STANDARD\n" +
                "DTSTART:19671029T020000\n" +
                "TZOFFSETFROM:-0400\n" +
                "TZOFFSETTO:-0500\n" +
                "TZNAME:EST" +
                "END:STANDARD\n" +
                "END:VTIMEZONE"
        ICalPreprocessor.preprocessStream(StringReader(invalid)).let { result ->
            assertEquals(valid, IOUtils.toString(result))
        }
        ICalPreprocessor.preprocessStream(StringReader(valid)).let { result ->
            assertEquals(valid, IOUtils.toString(result))
        }
    }
    fun testPreprocessStream_appliesStreamProcessors() {
        mockkObject(FixInvalidDayOffsetPreprocessor, FixInvalidUtcOffsetPreprocessor) {
            ICalPreprocessor.preprocessStream(StringReader(""))

    @Test
    fun testFixInvalidDuration() {
        val invalid = "BEGIN:VEVENT\n" +
                "LAST-MODIFIED:20230108T011226Z\n" +
                "DTSTAMP:20230108T011226Z\n" +
                "X-ECAL-SCHEDULE:63b0e38979739f000d5c1724\n" +
                "DTSTART:20230101T015100Z\n" +
                "DTEND:20230101T020600Z\n" +
                "SUMMARY:This is a test event\n" +
                "TRANSP:TRANSPARENT\n" +
                "SEQUENCE:0\n" +
                "UID:63b0e389453c5d000e1161ae\n" +
                "PRIORITY:5\n" +
                "X-MICROSOFT-CDO-IMPORTANCE:1\n" +
                "CLASS:PUBLIC\n" +
                "DESCRIPTION:Example description\n" +
                "BEGIN:VALARM\n" +
                "TRIGGER:-PT2D\n" +
                "ACTION:DISPLAY\n" +
                "DESCRIPTION:Reminder\n" +
                "END:VALARM\n" +
                "END:VEVENT"
        val valid = "BEGIN:VEVENT\n" +
                "LAST-MODIFIED:20230108T011226Z\n" +
                "DTSTAMP:20230108T011226Z\n" +
                "X-ECAL-SCHEDULE:63b0e38979739f000d5c1724\n" +
                "DTSTART:20230101T015100Z\n" +
                "DTEND:20230101T020600Z\n" +
                "SUMMARY:This is a test event\n" +
                "TRANSP:TRANSPARENT\n" +
                "SEQUENCE:0\n" +
                "UID:63b0e389453c5d000e1161ae\n" +
                "PRIORITY:5\n" +
                "X-MICROSOFT-CDO-IMPORTANCE:1\n" +
                "CLASS:PUBLIC\n" +
                "DESCRIPTION:Example description\n" +
                "BEGIN:VALARM\n" +
                "TRIGGER:-P2D\n" +
                "ACTION:DISPLAY\n" +
                "DESCRIPTION:Reminder\n" +
                "END:VALARM\n" +
                "END:VEVENT"
        ICalPreprocessor.preprocessStream(StringReader(invalid)).let { result ->
            assertEquals(valid, IOUtils.toString(result))
            // verify that the required stream processors have been called
            verify {
                FixInvalidDayOffsetPreprocessor.preprocess(any())
                FixInvalidUtcOffsetPreprocessor.preprocess(any())
            }
        ICalPreprocessor.preprocessStream(StringReader(valid)).let { result ->
            assertEquals(valid, IOUtils.toString(result))
        }
    }


    @Test
    fun testMsTimeZones() {
    fun testPreprocessCalendar_MsTimeZones() {
        javaClass.classLoader!!.getResourceAsStream("events/outlook1.ics").use { stream ->
            val reader = InputStreamReader(stream, Charsets.UTF_8)
            val calendar = CalendarBuilder().build(reader)
+8 −5
Original line number Diff line number Diff line
@@ -21,13 +21,16 @@ object FixInvalidDayOffsetPreprocessor : StreamPreprocessor() {
    override fun fixString(original: String): String {
        var s: String = original

        // Find all matches for the expression
        val found = regexpForProblem().find(s) ?: return s
        for (match in found.groupValues) {
            val fixed = match
        // Find all instances matching the defined expression
        val found = regexpForProblem().findAll(s)

        // ..and repair them
        for (match in found) {
            val matchStr = match.value
            val fixed = matchStr
                .replace("PT", "P")
                .replace("DT", "D")
            s = s.replace(match, fixed)
            s = s.replace(matchStr, fixed)
        }
        return s
    }
+39 −11
Original line number Diff line number Diff line
@@ -4,15 +4,26 @@

package at.bitfire.ical4android.validation

import org.junit.Assert.*
import org.junit.Test
import java.time.Duration
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class FixInvalidDayOffsetPreprocessorTest {

    private fun fixStringAndAssert(expected: String, string: String) {
        val fixed = FixInvalidDayOffsetPreprocessor.fixString(string)
        Duration.parse(fixed.substring(fixed.indexOf(':') + 1))
    private fun fixAndAssert(expected: String, testValue: String) {

        // Fix the duration string
        val fixed = FixInvalidDayOffsetPreprocessor.fixString(testValue)

        // Test the duration can now be parsed
        for (line in fixed.split('\n')) {
            val duration = line.substring(line.indexOf(':') + 1)
            Duration.parse(duration)
        }

        // Assert
        assertEquals(expected, fixed)
    }

@@ -26,17 +37,34 @@ class FixInvalidDayOffsetPreprocessorTest {

    @Test
    fun test_FixString_DayOffsetFrom_Invalid() {
        fixStringAndAssert("DURATION:-P1D", "DURATION:-PT1D")
        fixStringAndAssert("TRIGGER:-P2D", "TRIGGER:-PT2D")
        fixAndAssert("DURATION:-P1D", "DURATION:-PT1D")
        fixAndAssert("TRIGGER:-P2D", "TRIGGER:-PT2D")

        fixStringAndAssert("DURATION:-P1D", "DURATION:-P1DT")
        fixStringAndAssert("TRIGGER:-P2D", "TRIGGER:-P2DT")
        fixAndAssert("DURATION:-P1D", "DURATION:-P1DT")
        fixAndAssert("TRIGGER:-P2D", "TRIGGER:-P2DT")
    }

    @Test
    fun test_FixString_DayOffsetFrom_Valid() {
        fixStringAndAssert("DURATION:-PT12H", "DURATION:-PT12H")
        fixStringAndAssert("TRIGGER:-PT12H", "TRIGGER:-PT12H")
        fixAndAssert("DURATION:-PT12H", "DURATION:-PT12H")
        fixAndAssert("TRIGGER:-PT12H", "TRIGGER:-PT12H")
    }

    @Test
    fun test_FixString_DayOffsetFromMultiple_Invalid() {
        fixAndAssert("DURATION:-P1D\nTRIGGER:-P2D", "DURATION:-PT1D\nTRIGGER:-PT2D")
        fixAndAssert("DURATION:-P1D\nTRIGGER:-P2D", "DURATION:-P1DT\nTRIGGER:-P2DT")
    }

    @Test
    fun test_FixString_DayOffsetFromMultiple_Valid() {
        fixAndAssert("DURATION:-PT12H\nTRIGGER:-PT12H", "DURATION:-PT12H\nTRIGGER:-PT12H")
    }

    @Test
    fun test_FixString_DayOffsetFromMultiple_Mixed() {
        fixAndAssert("DURATION:-P1D\nDURATION:-PT12H\nTRIGGER:-P2D", "DURATION:-PT1D\nDURATION:-PT12H\nTRIGGER:-PT2D")
        fixAndAssert("DURATION:-P1D\nDURATION:-PT12H\nTRIGGER:-P2D", "DURATION:-P1DT\nDURATION:-PT12H\nTRIGGER:-P2DT")
    }

    @Test