A hand-rolled content-line model instead of ical4j: a raw (name, params, value) tree is what the round-trip requirement wants, and a typed model normalises away exactly what has to survive. lib-recur already does RRULE and java.time is native at minSdk 29, so the 2.2 MB of zone data and the registry shims buy nothing. Deviates from SYNC.md's library table — see SYNC-PLAN.md decision 4. - domain/ical: parser, serialiser, value codecs. No Android, no data types, so the floret-kit extraction stays a file move. - data/tasks/ical/VTodoMapper: VTODO <-> TaskEntity. Claims a property only when it can reproduce it exactly; everything else round-trips verbatim through TaskEntity.unknown_properties, which already existed at v1 — no migration needed, and BEGIN/END lines carry the nesting the plan thought needed a second table. - 19 fixtures as the specification, with the canonical comparison harness from SYNC.md: no property lost, modulo the enumerated allowlist. - ICalendarWriter now delegates folding and escaping rather than carrying its own copy. VALARM ownership settled: neither side writes the other's alarms. VALARMs round-trip in the residue, local reminders stay in task_alarms. The setAlarm collision was the provider's; the two stores are now disjoint.
3.2 KiB
3.2 KiB
VTODO fixture corpus
The mapper's specification, not its regression net. Every file here is a shape that was found in the wild and that a plausible implementation gets wrong. A clean, server-generated VTODO catches nothing and is deliberately absent.
RoundTripTest asserts every fixture survives parse → map → write → serialise
with no property lost, compared as a canonical multiset modulo the allowlist in
docs/SYNC-PLAN.md § chunk 1 (PRODID, DTSTAMP, LAST-MODIFIED, SEQUENCE,
VTIMEZONE bodies, fold positions, parameter quoting).
| File | What it is for |
|---|---|
nextcloud-shared-confidential.ics |
A CLASS:CONFIDENTIAL task as Nextcloud rewrites it on GET from a shared calendar — DUE, STATUS, COMPLETED, PERCENT-COMPLETE, PRIORITY and RELATED-TO already stripped, ETag left untouched. Re-PUTting this destroys the owner's task |
recurring-master-overrides.ics |
Master plus two RECURRENCE-ID overrides in one resource, sharing a UID |
valarm-unknown-nested.ics |
Unknown properties inside a VALARM — the case a flat property-per-row model cannot represent |
unknown-component.ics |
An entirely unknown sub-component |
unknown-tzid.ics |
A TZID no device tzdb knows, with its own VTIMEZONE |
uid-special-chars.ics |
A UID containing / and @ — the href sanitiser must not assume it is filename-safe |
rrule-due-no-dtstart.ics |
RRULE + DUE and no DTSTART: no well-defined RECURRENCE-ID, undefined in RFC 5545, ubiquitous in the wild |
moz-alarm-props.ics |
X-MOZ-LASTACK / X-MOZ-SNOOZE-TIME. Dropping them causes documented Thunderbird alarm storms |
apple-sort-order.ics |
X-APPLE-SORT-ORDER — preserve, never interpret |
floating-time.ics |
DTSTART with neither Z nor TZID. Readable, not reproducible: it must round-trip from the residue, not be rewritten as a guess |
completion-model-a.ics |
Completion as a RECURRENCE-ID override, master stays open — jtx Board, Thunderbird. The model we write |
completion-model-b.ics |
Master's DUE advanced in place, completion cleared — tasks.org, Evolution, Nextcloud in practice |
completion-model-c.ics |
STATUS:COMPLETED on the master, killing the series — Nextcloud Tasks ≤ 0.17. Always a bug; we read it, we never write it |
completion-model-d.ics |
The completed occurrence detached as a new task with a new UID — OpenTasks |
quoted-param-colon.ics |
A quoted parameter value containing a colon and a comma. The value separator is the first colon outside quotes |
mixed-value-types.ics |
DTSTART;VALUE=DATE with a timed DUE. One is_all_day flag cannot author both, so the odd one out must stay in the residue rather than be flattened to a date |
malformed-scalars.ics |
PRIORITY:11, PERCENT-COMPLETE:150, SEQUENCE:x, STATUS:X-DEFERRED, CLASS:X-INTERNAL — values no spec allows. Clamping or defaulting any of them is a silent rewrite of somebody's data |
two-timezones.ics |
DTSTART and DUE in different zones. One timezone column cannot author both |
folded-utf8.ics |
A line folded mid-emoji-adjacent, to prove folding counts octets and splits on character boundaries |