Refresh the Flutter snapshot: v3 tickets V3-04 through V3-16, plus a fresh installable APK
V3-04 through V3-07, V3-10, V3-16 shipped complete; V3-11/V3-12/V3-14 shipped code-complete pending device/account verification; V3-08/V3-09 deferred behind a new V3-17 (self-hosted OSRM investigation). 316 tests passing, up from 221. The APK is a fresh release build (debug-signed, no release signing config exists yet) with two build fixes applied: core library desugaring enabled for flutter_local_notifications, and sentry_flutter bumped to 9.27.0 (8.14.2's bundled Kotlin plugin was incompatible with this project's Kotlin 2.4.0 toolchain).
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:rippr/src/domain/activity_profile.dart';
|
||||
import 'package:rippr/src/domain/models.dart';
|
||||
import 'package:rippr/src/stats/ride_statistics.dart';
|
||||
|
||||
@@ -299,4 +300,57 @@ void main() {
|
||||
expect(elevationProfile([point(ts: 0)]).length, 1);
|
||||
});
|
||||
});
|
||||
|
||||
group('activity profile changes real output (V3-01)', () {
|
||||
test('a slow walk reads as stationary under the motorcycle floor, but not under '
|
||||
"walking's own", () {
|
||||
// 20 fixes a second apart, each ~0.7 km/h -- real walking pace, comfortably under
|
||||
// the motorcycle noise floor (1.5) but above the walking one (0.3). This is the
|
||||
// whole point of activity profiles: without one, a recorded walk would show
|
||||
// 00:00:00 moving time despite every fix showing genuine movement.
|
||||
final points = List.generate(
|
||||
20,
|
||||
(i) => point(ts: i * 1000, lat: 51.0 + i * 0.0000065, speed: 0.7),
|
||||
);
|
||||
|
||||
final asMotorcycle =
|
||||
computeSummary(points, profile: ActivityProfile.motorcycle);
|
||||
final asWalking = computeSummary(points, profile: ActivityProfile.walking);
|
||||
|
||||
expect(asMotorcycle.movingMillis, 0,
|
||||
reason: 'the motorcycle floor must reject real walking speed as noise');
|
||||
expect(asWalking.movingMillis, greaterThan(0),
|
||||
reason: "the walking profile must recognise its own pace as movement");
|
||||
});
|
||||
|
||||
test('elevation window size changes what counts as a real climb', () {
|
||||
// A short, shallow rise across only a handful of samples. A wider averaging
|
||||
// window (running/walking) smooths it away as noise; the narrower motorcycle
|
||||
// window banks more of it as real.
|
||||
final points = List.generate(
|
||||
10,
|
||||
(i) => point(ts: i * 1000, alt: 1000.0 + i * 0.5),
|
||||
);
|
||||
|
||||
final motorcycle =
|
||||
computeSummary(points, profile: ActivityProfile.motorcycle).elevationGainM;
|
||||
final running =
|
||||
computeSummary(points, profile: ActivityProfile.running).elevationGainM;
|
||||
|
||||
expect(running, lessThanOrEqualTo(motorcycle),
|
||||
reason: "running's wider smoothing window must not report MORE gain than "
|
||||
"motorcycle's narrower one on the same climb");
|
||||
});
|
||||
|
||||
test('defaults to the motorcycle profile when none is given', () {
|
||||
final points = List.generate(
|
||||
5,
|
||||
(i) => point(ts: i * 1000, speed: 40.0),
|
||||
);
|
||||
expect(
|
||||
computeSummary(points),
|
||||
computeSummary(points, profile: ActivityProfile.motorcycle),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user