Complete Phase 1: accumulator, exports, and full parity coverage

T05 — ride_accumulator.dart with the cross-batch anchor intact (12 tests).
T06 — ride_export.dart, GPX 1.1 and GeoJSON (15 tests), parsed with real
parsers rather than substring matching.
T07 — parity harness extended to cover export byte output.

GPX and GeoJSON are byte-identical across Kotlin and Dart: same length, same
FNV hash, including the escaped hostile name and all %.7f/%.1f formatting.

Two harness bugs found and fixed while building it. String.hashCode is not
comparable across Java and Dart, so text comparison used FNV-1a instead. And a
missing jar let a failed Kotlin compile pass as a green run -- run.sh now checks
for the artifact and exits non-zero, the same failure mode as the v2 sweep that
hid a compile error behind /dev/null.

Phase 1 done: 79 tests passing, analyze clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 01:08:30 -05:00
parent 4af4e3411a
commit 398157b1f5
12 changed files with 918 additions and 6 deletions

View File

@@ -1,8 +1,10 @@
// ignore_for_file: avoid_print -- this is a command-line harness; stdout is the output.
// Dart side of the cross-language parity harness (T07).
// Must stay fixture-for-fixture identical to tool/parity/main.kt.
import 'dart:math' as math;
import 'package:rippr/src/domain/models.dart';
import 'package:rippr/src/export/ride_export.dart';
import 'package:rippr/src/geo/geo.dart';
import 'package:rippr/src/stats/ride_statistics.dart';
@@ -18,6 +20,17 @@ class Lcg {
}
}
/// FNV-1a 32-bit. Dart's String.hashCode differs from Java's, so it cannot be used to
/// compare text across languages -- this can.
int fnv1a(String s) {
var h = 2166136261;
for (final c in s.codeUnits) {
h ^= c;
h = (h * 16777619) & 0xFFFFFFFF;
}
return h;
}
TrackPoint p(int seg, int ts, double lat, double lon, double sp, double alt, int id) =>
TrackPoint(id: id, tripId: 1, segmentId: seg, timestamp: ts,
latitude: lat, longitude: lon, speedKmh: sp, altitudeM: alt);
@@ -61,4 +74,29 @@ void main() {
final hist = speedHistogram(run, bucketKmh: 10);
out('hist_buckets', hist.length);
out('hist_first_millis', hist.first.millis);
// --- export byte output -------------------------------------------------
const etrip = Trip(
id: 1,
startedAt: 1700000000000,
endedAt: 1700000600000,
state: TripState.completed,
distanceM: 12345.6,
maxSpeedKmh: 118.4);
const esegs = [
Segment(id: 1, tripId: 1, startedAt: 0, endedAt: 1),
Segment(id: 2, tripId: 1, startedAt: 2, endedAt: 3),
];
final epts = <TrackPoint>[
for (final sg in esegs)
for (var i = 0; i < 5; i++)
p(sg.id, 1700000000000 + i * 1000, 51.0447 + i * 0.0001, -114.0719, 72.0,
1045.0, i + 1),
];
final g = gpx(etrip, esegs, epts, "Sam & Dave's <ride>");
final j = geoJson(etrip, esegs, epts, "Sam & Dave's <ride>");
out('gpx_len', g.length);
out('gpx_fnv', fnv1a(g));
out('geojson_len', j.length);
out('geojson_fnv', fnv1a(j));
}