Artifact
One Rule, Three Runtimes: Differential Testing of a Daily-Content Rotation Across Dart, Swift and Kotlin
- README.md
- analyse.py
- build_paper.py
- dart/harness.dart
- dart/pubspec.yaml
- dart/widget_rotation.dart
- evidence.json
- gen_latent.py
- gen_vectors.py
- kotlin/Rotation.kt
- make_contract_tree.sh
- make_figures.py
- mutant_results.py
- mutants.py
- probes/CalProbe.java
- probes/dart_nuuk.dart
- probes/dart_tzcheck.dart
- probes/swift_probe.swift
- results/cost.json
- results/latent/latent_kotlin_t0.json
- results/latent/latent_kotlin_t1.json
- results/latent/latent_kotlin_t2.json
- results/latent/latent_swift_t0.json
- results/latent/latent_swift_t1.json
- results/latent/latent_swift_t2.json
- results/mutants.json
- results/raw.tar.gz
- results/summary.json
- run_all.sh
- run_mutants.sh
- source-hashes/NOTE.txt
- source-hashes/SHA256SUMS
- swift/Rotation.swift
- vectors/latent_in.json
- vectors/scenarios.json.gz
# One Rule, Three Runtimes: artifact
Reproduction package for the technical report *One Rule, Three Runtimes:
Differential Testing of a Daily-Content Rotation Across Dart, Swift and Kotlin*
(Yousof Selim, 2 November 2026).
The study runs copies of Adelante's widget-rotation rule in Dart, Swift and
Kotlin side by side and compares the pool index each one shows. The rule comes
from Adelante commit `af1aea2` (1.0.0 build 21). The only app code included is
the copied rotation functions inside the three harnesses. `source-hashes/SHA256SUMS`
lists the SHA-256 of every source file they were copied from.
## Contents
| Path | What it is |
| --- | --- |
| `gen_vectors.py` | Seeded golden-vector generator (seed 20261102): 7,658 publications, 29 zones, 550,420 query instants. Proposes inputs only. |
| `gen_latent.py` | Nine hand-built payloads the current app never publishes (Table 4). |
| `dart/harness.dart`, `dart/widget_rotation.dart` | Dart publisher and the two Dart answers (app reconciliation, `currentPoolIndex`). `widget_rotation.dart` is a verbatim copy. |
| `swift/Rotation.swift` | Swift rule, `adelanteTimeline` and payload decoding (verbatim ranges marked). |
| `kotlin/Rotation.kt` | Kotlin rule, alarm chain and catalog-backed preferences (verbatim ranges marked). |
| `run_all.sh` | Runs Dart, then Swift, then Kotlin for every zone, each process with `TZ=<zone>`. |
| `analyse.py` | Oracle, agreement rates, mechanical root-cause classes, `out/summary.json`. |
| `mutants.py`, `run_mutants.sh`, `mutant_results.py` | 14 single-line mutants. Each runs the 22 contract tests and the differential harness (six zones). |
| `make_contract_tree.sh` | Rebuilds the scratch copy of the files the 22 contract tests read, from an Adelante checkout. |
| `make_figures.py`, `build_paper.py` | SVG figures and the report JSON, built only from the result files. |
| `probes/` | Single-call probes of the library behaviour behind each divergence (Foundation, `java.util.Calendar`, Dart `DateTime`). |
| `vectors/scenarios.json.gz` | The golden vectors used for the reported run. |
| `vectors/latent_in.json` | The nine latent payloads. |
| `results/summary.json` | Agreement rates, class counts, examples, per-zone counts and per-runtime timings. |
| `results/mutants.json` | Mutant table: contract-test pass/fail counts and the number of answers changed. |
| `results/cost.json` | CPU time per runtime (user+sys, summed over 29 zone processes). |
| `results/raw.tar.gz` | Raw per-zone answers: `dart_<zone>.json` (payloads, queries, two Dart answers), `swift_<zone>.json` and `kotlin_<zone>.json` (instant and displayed answers). |
| `results/latent/` | Swift and Kotlin answers for the latent payloads at widget tiers 0, 1 and 2. |
## Toolchain used for the reported run
- macOS 27.0.1 (Darwin 27.0.0), Apple M1 Pro; Xcode 27.1
- Dart 3.12.1 (Flutter 3.44.1 stable)
- Swift 6.4 (swiftlang-6.4.0.34.1); Foundation time-zone data 2026e
- Kotlin 2.4.21 (`brew install kotlin`) on OpenJDK 17.0.19; JVM tzdb 2026a
- `org.json` 20180813 jar (from the Gradle cache) for the Kotlin harness
- Python 3.14.2 with system tzdata 2026e (`/usr/share/zoneinfo`)
Adelante's Android build uses the Kotlin Gradle plugin 2.3.20. The Kotlin harness
ran on a desktop JVM, not Android's runtime (see Threats to validity).
## Commands
Run everything from one working directory that holds this artifact's files, with
an `out/` folder:
```sh
mkdir -p out
python3 gen_vectors.py out/scenarios.json # or: gunzip -c vectors/scenarios.json.gz > out/scenarios.json
# build the three harnesses
(cd dart && dart compile exe harness.dart -o harness)
(cd swift && swiftc -O -parse-as-library Rotation.swift -o rotation)
(cd kotlin && cp <path>/json-20180813.jar . && \
kotlinc Rotation.kt -cp json-20180813.jar -include-runtime -d rotation.jar)
# one zone by hand
TZ=America/New_York dart/harness out/scenarios.json out/dart_America_New_York.json
TZ=America/New_York swift/rotation out/dart_America_New_York.json out/swift_America_New_York.json
TZ=America/New_York java -cp kotlin/rotation.jar:kotlin/json-20180813.jar RotationKt \
out/dart_America_New_York.json out/kotlin_America_New_York.json
# all 29 zones, then the analysis
./run_all.sh "$PWD"
python3 analyse.py "$PWD" # writes out/summary.json
# latent payloads (third argument = widget tier 0/1/2)
python3 gen_latent.py out/latent_in.json
TZ=UTC swift/rotation out/latent_in.json out/latent_swift_t2.json 2
TZ=UTC java -cp kotlin/rotation.jar:kotlin/json-20180813.jar RotationKt out/latent_in.json out/latent_kotlin_t2.json 2
# contract tests and mutants (needs an Adelante checkout at af1aea2)
./make_contract_tree.sh <adelante-checkout> contract_tree
(cd contract_tree && flutter pub get --offline && flutter test \
test/widget_parity_test.dart test/android_widget_remote_views_test.dart test/ios_widget_source_test.dart)
./run_mutants.sh "$PWD" && python3 mutant_results.py "$PWD"
```
`run_all.sh` uses `/usr/bin/time -p` and writes `out/time_<runtime>_<zone>.txt`.
Every harness writes the zone it actually ran in (`probeTzName`, `tzCurrent`,
`tzDefault`), and `analyse.py` asserts that it matches the requested zone.
## Mechanical changes to the copied code
- Swift: SwiftUI and WidgetKit types removed. `Timeline<AdelanteEntry>` replaced by
`HarnessTimeline` with the same entries and refresh date. `AdelanteQuoteEntry`
keeps only `id` and `tier`.
- Kotlin: `android.content.SharedPreferences` replaced by a three-member interface,
backed by the catalog JSON exactly as `WidgetConfigurations.resolve` overrides it.
`scheduledIndex` and `nextChange` made non-private.
- Dart: the app's reconciliation is the arithmetic extracted from `providers.dart`
(`_selectQuote` / `_selectTodayQuote`), not the Riverpod provider. The functions in
`WidgetConfig` and `elapsedWidgetRefreshPeriods` are verbatim.
## Notes
- `build_paper.py` generated the first draft of the report JSON from the result
files. The published report was later revised by hand; every measured number in it is
still taken from `results/`.
- The Swift copied ranges were checked line by line against the Adelante source.
That check needs the private source, so it cannot be rerun from this artifact;
the ranges are marked `VERBATIM` with their source line numbers and the source
files' SHA-256 hashes are in `source-hashes/SHA256SUMS`.
- Wall-clock times in `summary.json` are not meaningful. The machine was shared,
with load averages of 68 to 112 during the run. Use the CPU times in `cost.json`.
- Displayed-quote answers are idealised: WidgetKit reloads exactly at `refreshAt`,
and Android's alarm window fires at its start.