flutter_flutter/packages/flutter/test/widgets/scroll_simulation_test.dart
Michael Goderbauer 5491c8c146
Auto-format Framework (#160545)
This auto-formats all *.dart files in the repository outside of the
`engine` subdirectory and enforces that these files stay formatted with
a presubmit check.

**Reviewers:** Please carefully review all the commits except for the
one titled "formatted". The "formatted" commit was auto-generated by
running `dev/tools/format.sh -a -f`. The other commits were hand-crafted
to prepare the repo for the formatting change. I recommend reviewing the
commits one-by-one via the "Commits" tab and avoiding Github's "Files
changed" tab as it will likely slow down your browser because of the
size of this PR.

---------

Co-authored-by: Kate Lovett <katelovett@google.com>
Co-authored-by: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com>
2024-12-19 20:06:21 +00:00

185 lines
6.8 KiB
Dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('ClampingScrollSimulation has a stable initial conditions', () {
void checkInitialConditions(double position, double velocity) {
final ClampingScrollSimulation simulation = ClampingScrollSimulation(
position: position,
velocity: velocity,
);
expect(simulation.x(0.0), moreOrLessEquals(position));
expect(simulation.dx(0.0), moreOrLessEquals(velocity));
}
checkInitialConditions(51.0, 2866.91537);
checkInitialConditions(584.0, 2617.294734);
checkInitialConditions(345.0, 1982.785934);
checkInitialConditions(0.0, 1831.366634);
checkInitialConditions(-156.2, 1541.57665);
checkInitialConditions(4.0, 1139.250439);
checkInitialConditions(4534.0, 1073.553798);
checkInitialConditions(75.0, 614.2093);
checkInitialConditions(5469.0, 182.114534);
});
test('ClampingScrollSimulation only decelerates, never speeds up', () {
// Regression test for https://github.com/flutter/flutter/issues/113424
final ClampingScrollSimulation simulation = ClampingScrollSimulation(
position: 0,
velocity: 8000.0,
);
double time = 0.0;
double velocity = simulation.dx(time);
while (!simulation.isDone(time)) {
expect(time, lessThan(3.0));
time += 1 / 60;
final double nextVelocity = simulation.dx(time);
expect(nextVelocity, lessThanOrEqualTo(velocity));
velocity = nextVelocity;
}
});
test(
'ClampingScrollSimulation reaches a smooth stop: velocity is continuous and goes to zero',
() {
// Regression test for https://github.com/flutter/flutter/issues/113424
const double initialVelocity = 8000.0;
const double maxDeceleration = 5130.0; // -acceleration(initialVelocity), from formula below
final ClampingScrollSimulation simulation = ClampingScrollSimulation(
position: 0,
velocity: initialVelocity,
);
double time = 0.0;
double velocity = simulation.dx(time);
const double delta = 1 / 60;
do {
expect(time, lessThan(3.0));
time += delta;
final double nextVelocity = simulation.dx(time);
expect((nextVelocity - velocity).abs(), lessThan(delta * maxDeceleration));
velocity = nextVelocity;
} while (!simulation.isDone(time));
expect(velocity, moreOrLessEquals(0.0));
},
);
test('ClampingScrollSimulation is ballistic', () {
// Regression test for https://github.com/flutter/flutter/issues/120338
const double delta = 1 / 90;
final ClampingScrollSimulation undisturbed = ClampingScrollSimulation(
position: 0,
velocity: 8000.0,
);
double time = 0.0;
ClampingScrollSimulation restarted = undisturbed;
final List<double> xsRestarted = <double>[];
final List<double> xsUndisturbed = <double>[];
final List<double> dxsRestarted = <double>[];
final List<double> dxsUndisturbed = <double>[];
do {
expect(time, lessThan(4.0));
time += delta;
restarted = ClampingScrollSimulation(
position: restarted.x(delta),
velocity: restarted.dx(delta),
);
xsRestarted.add(restarted.x(0));
xsUndisturbed.add(undisturbed.x(time));
dxsRestarted.add(restarted.dx(0));
dxsUndisturbed.add(undisturbed.dx(time));
} while (!restarted.isDone(0) || !undisturbed.isDone(time));
// Compare the headline number first: the total distances traveled.
// This way, if the test fails, it shows the big final difference
// instead of the tiny difference that's in the very first frame.
expect(xsRestarted.last, moreOrLessEquals(xsUndisturbed.last));
// The whole trajectories along the way should match too.
for (int i = 0; i < xsRestarted.length; i++) {
expect(xsRestarted[i], moreOrLessEquals(xsUndisturbed[i]));
expect(dxsRestarted[i], moreOrLessEquals(dxsUndisturbed[i]));
}
});
test('ClampingScrollSimulation satisfies a physical acceleration formula', () {
// Different regression test for https://github.com/flutter/flutter/issues/120338
//
// This one provides a formula for the particle's acceleration as a function
// of its velocity, and checks that it behaves according to that formula.
// The point isn't that it's this specific formula, but just that there's
// some formula which depends only on velocity, not time, so that the
// physical metaphor makes sense.
// Copied from the implementation.
final double kDecelerationRate = math.log(0.78) / math.log(0.9);
// Same as the referenceVelocity in _flingDuration.
const double referenceVelocity = .015 * 9.80665 * 39.37 * 160.0 * 0.84 / 0.35;
// The value of _duration when velocity == referenceVelocity.
final double referenceDuration = kDecelerationRate * 0.35;
// The rate of deceleration when dx(time) == referenceVelocity.
final double referenceDeceleration =
(kDecelerationRate - 1) * referenceVelocity / referenceDuration;
double acceleration(double velocity) {
return -velocity.sign *
referenceDeceleration *
math.pow(
velocity.abs() / referenceVelocity,
(kDecelerationRate - 2) / (kDecelerationRate - 1),
);
}
double jerk(double velocity) {
return referenceVelocity /
referenceDuration /
referenceDuration *
(kDecelerationRate - 1) *
(kDecelerationRate - 2) *
math.pow(
velocity.abs() / referenceVelocity,
(kDecelerationRate - 3) / (kDecelerationRate - 1),
);
}
void checkAcceleration(double position, double velocity) {
final ClampingScrollSimulation simulation = ClampingScrollSimulation(
position: position,
velocity: velocity,
);
double time = 0.0;
const double delta = 1 / 60;
for (; time < 2.0; time += delta) {
final double difference = simulation.dx(time + delta) - simulation.dx(time);
final double predictedDifference = delta * acceleration(simulation.dx(time + delta / 2));
final double maxThirdDerivative = jerk(simulation.dx(time + delta));
expect(
(difference - predictedDifference).abs(),
lessThan(maxThirdDerivative * math.pow(delta, 2) / 2),
);
}
}
checkAcceleration(51.0, 2866.91537);
checkAcceleration(584.0, 2617.294734);
checkAcceleration(345.0, 1982.785934);
checkAcceleration(0.0, 1831.366634);
checkAcceleration(-156.2, 1541.57665);
checkAcceleration(4.0, 1139.250439);
checkAcceleration(4534.0, 1073.553798);
checkAcceleration(75.0, 614.2093);
checkAcceleration(5469.0, 182.114534);
});
}