Back to the paper

Artifact

One Rule, Three Runtimes: Differential Testing of a Daily-Content Rotation Across Dart, Swift and Kotlin

# 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.