mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

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>
628 lines
20 KiB
Dart
628 lines
20 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';
|
|
|
|
import 'package:collection/collection.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:vitool/vitool.dart';
|
|
|
|
void main() {
|
|
test('parsePixels', () {
|
|
expect(parsePixels('23px'), 23);
|
|
expect(parsePixels('9px'), 9);
|
|
expect(() {
|
|
parsePixels('9pt');
|
|
}, throwsArgumentError);
|
|
});
|
|
|
|
test('parsePoints', () {
|
|
expect(parsePoints('1.0, 2.0'), const <Point<double>>[Point<double>(1.0, 2.0)]);
|
|
expect(parsePoints('12.0, 34.0 5.0, 6.6'), const <Point<double>>[
|
|
Point<double>(12.0, 34.0),
|
|
Point<double>(5.0, 6.6),
|
|
]);
|
|
expect(parsePoints('12.0 34.0 5.0 6.6'), const <Point<double>>[
|
|
Point<double>(12.0, 34.0),
|
|
Point<double>(5.0, 6.6),
|
|
]);
|
|
});
|
|
|
|
group('parseSvg', () {
|
|
test('empty SVGs', () {
|
|
interpretSvg(testAsset('empty_svg_1_48x48.svg'));
|
|
interpretSvg(testAsset('empty_svg_2_100x50.svg'));
|
|
});
|
|
|
|
test('illegal SVGs', () {
|
|
expect(() {
|
|
interpretSvg(testAsset('illegal_svg_multiple_roots.svg'));
|
|
}, throwsA(anything));
|
|
});
|
|
|
|
test('SVG size', () {
|
|
expect(
|
|
interpretSvg(testAsset('empty_svg_1_48x48.svg')).size,
|
|
const Point<double>(48.0, 48.0),
|
|
);
|
|
|
|
expect(
|
|
interpretSvg(testAsset('empty_svg_2_100x50.svg')).size,
|
|
const Point<double>(100.0, 50.0),
|
|
);
|
|
});
|
|
|
|
test('horizontal bar', () {
|
|
final FrameData frameData = interpretSvg(testAsset('horizontal_bar.svg'));
|
|
expect(frameData.paths, <SvgPath>[
|
|
const SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 19.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 19.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 29.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(0.0, 29.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
]),
|
|
]);
|
|
});
|
|
|
|
test('leading space path command', () {
|
|
interpretSvg(testAsset('leading_space_path_command.svg'));
|
|
});
|
|
|
|
test('SVG illegal path', () {
|
|
expect(() {
|
|
interpretSvg(testAsset('illegal_path.svg'));
|
|
}, throwsA(anything));
|
|
});
|
|
|
|
test('SVG group', () {
|
|
final FrameData frameData = interpretSvg(testAsset('bars_group.svg'));
|
|
expect(frameData.paths, const <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 19.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 19.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 29.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(0.0, 29.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
]),
|
|
SvgPath('path_2', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 34.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 34.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 44.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(0.0, 44.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
]),
|
|
]);
|
|
});
|
|
|
|
test('SVG group translate', () {
|
|
final FrameData frameData = interpretSvg(testAsset('bar_group_translate.svg'));
|
|
expect(frameData.paths, const <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 34.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 34.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 44.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(0.0, 44.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
]),
|
|
]);
|
|
});
|
|
|
|
test('SVG group scale', () {
|
|
final FrameData frameData = interpretSvg(testAsset('bar_group_scale.svg'));
|
|
expect(frameData.paths, const <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 9.5)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(24.0, 9.5)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(24.0, 14.5)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(0.0, 14.5)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
]),
|
|
]);
|
|
});
|
|
|
|
test('SVG group rotate scale', () {
|
|
final FrameData frameData = interpretSvg(testAsset('bar_group_rotate_scale.svg'));
|
|
expect(frameData.paths, const <PathMatcher>[
|
|
PathMatcher(
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(29.0, 0.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(29.0, 48.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(19.0, 48.0)]),
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(19.0, 0.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
]),
|
|
margin: precisionErrorTolerance,
|
|
),
|
|
]);
|
|
});
|
|
|
|
test('SVG illegal transform', () {
|
|
expect(() {
|
|
interpretSvg(testAsset('illegal_transform.svg'));
|
|
}, throwsA(anything));
|
|
});
|
|
|
|
test('SVG group opacity', () {
|
|
final FrameData frameData = interpretSvg(testAsset('bar_group_opacity.svg'));
|
|
expect(frameData.paths, const <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 19.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 19.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 29.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(0.0, 29.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
], opacity: 0.5),
|
|
]);
|
|
});
|
|
|
|
test('horizontal bar relative', () {
|
|
// This asset uses the relative 'l' command instead of 'L'.
|
|
final FrameData frameData = interpretSvg(testAsset('horizontal_bar_relative.svg'));
|
|
expect(frameData.paths, const <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 19.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 19.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(48.0, 29.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(0.0, 29.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
]),
|
|
]);
|
|
});
|
|
|
|
test('close in middle of path', () {
|
|
// This asset uses the relative 'l' command instead of 'L'.
|
|
final FrameData frameData = interpretSvg(testAsset('close_path_in_middle.svg'));
|
|
expect(frameData.paths, const <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(50.0, 50.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(60.0, 50.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(60.0, 60.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(50.0, 40.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(40.0, 40.0)]),
|
|
SvgPathCommand('Z', <Point<double>>[]),
|
|
]),
|
|
]);
|
|
});
|
|
});
|
|
|
|
group('create PathAnimation', () {
|
|
test('single path', () {
|
|
const List<FrameData> frameData = <FrameData>[
|
|
FrameData(Point<double>(10.0, 10.0), <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 0.0)]),
|
|
SvgPathCommand('L', <Point<double>>[Point<double>(10.0, 10.0)]),
|
|
]),
|
|
]),
|
|
];
|
|
expect(
|
|
PathAnimation.fromFrameData(frameData, 0),
|
|
const PathAnimationMatcher(
|
|
PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(0.0, 0.0)],
|
|
]),
|
|
PathCommandAnimation('L', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(10.0, 10.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[1.0],
|
|
),
|
|
),
|
|
);
|
|
});
|
|
|
|
test('multiple paths', () {
|
|
const List<FrameData> frameData = <FrameData>[
|
|
FrameData(Point<double>(10.0, 10.0), <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 0.0)]),
|
|
]),
|
|
SvgPath('path_2', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(5.0, 6.0)]),
|
|
]),
|
|
]),
|
|
];
|
|
expect(
|
|
PathAnimation.fromFrameData(frameData, 0),
|
|
const PathAnimationMatcher(
|
|
PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(0.0, 0.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[1.0],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
PathAnimation.fromFrameData(frameData, 1),
|
|
const PathAnimationMatcher(
|
|
PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(5.0, 6.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[1.0],
|
|
),
|
|
),
|
|
);
|
|
});
|
|
|
|
test('multiple frames', () {
|
|
const List<FrameData> frameData = <FrameData>[
|
|
FrameData(Point<double>(10.0, 10.0), <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 0.0)]),
|
|
], opacity: 0.5),
|
|
]),
|
|
FrameData(Point<double>(10.0, 10.0), <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(10.0, 10.0)]),
|
|
]),
|
|
]),
|
|
];
|
|
expect(
|
|
PathAnimation.fromFrameData(frameData, 0),
|
|
const PathAnimationMatcher(
|
|
PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(0.0, 0.0), Point<double>(10.0, 10.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[0.5, 1.0],
|
|
),
|
|
),
|
|
);
|
|
});
|
|
});
|
|
|
|
group('create Animation', () {
|
|
test('multiple paths', () {
|
|
const List<FrameData> frameData = <FrameData>[
|
|
FrameData(Point<double>(10.0, 10.0), <SvgPath>[
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(0.0, 0.0)]),
|
|
]),
|
|
SvgPath('path_1', <SvgPathCommand>[
|
|
SvgPathCommand('M', <Point<double>>[Point<double>(5.0, 6.0)]),
|
|
]),
|
|
]),
|
|
];
|
|
final Animation animation = Animation.fromFrameData(frameData);
|
|
expect(
|
|
animation.paths[0],
|
|
const PathAnimationMatcher(
|
|
PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(0.0, 0.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[1.0],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
animation.paths[1],
|
|
const PathAnimationMatcher(
|
|
PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(5.0, 6.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[1.0],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(animation.size, const Point<double>(10.0, 10.0));
|
|
});
|
|
});
|
|
|
|
group('toDart', () {
|
|
test('_PathMoveTo', () {
|
|
const PathCommandAnimation command = PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(1.0, 2.0), Point<double>(3.0, 4.0)],
|
|
]);
|
|
|
|
expect(
|
|
command.toDart(),
|
|
' const _PathMoveTo(\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(1.0, 2.0),\n'
|
|
' const Offset(3.0, 4.0),\n'
|
|
' ],\n'
|
|
' ),\n',
|
|
);
|
|
});
|
|
|
|
test('_PathLineTo', () {
|
|
const PathCommandAnimation command = PathCommandAnimation('L', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(1.0, 2.0), Point<double>(3.0, 4.0)],
|
|
]);
|
|
|
|
expect(
|
|
command.toDart(),
|
|
' const _PathLineTo(\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(1.0, 2.0),\n'
|
|
' const Offset(3.0, 4.0),\n'
|
|
' ],\n'
|
|
' ),\n',
|
|
);
|
|
});
|
|
|
|
test('_PathCubicTo', () {
|
|
const PathCommandAnimation command = PathCommandAnimation('C', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(16.0, 24.0), Point<double>(16.0, 10.0)],
|
|
<Point<double>>[Point<double>(16.0, 25.0), Point<double>(16.0, 11.0)],
|
|
<Point<double>>[Point<double>(40.0, 40.0), Point<double>(40.0, 40.0)],
|
|
]);
|
|
|
|
expect(
|
|
command.toDart(),
|
|
' const _PathCubicTo(\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(16.0, 24.0),\n'
|
|
' const Offset(16.0, 10.0),\n'
|
|
' ],\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(16.0, 25.0),\n'
|
|
' const Offset(16.0, 11.0),\n'
|
|
' ],\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(40.0, 40.0),\n'
|
|
' const Offset(40.0, 40.0),\n'
|
|
' ],\n'
|
|
' ),\n',
|
|
);
|
|
});
|
|
|
|
test('_PathClose', () {
|
|
const PathCommandAnimation command = PathCommandAnimation('Z', <List<Point<double>>>[]);
|
|
|
|
expect(
|
|
command.toDart(),
|
|
' const _PathClose(\n'
|
|
' ),\n',
|
|
);
|
|
});
|
|
|
|
test('Unsupported path command', () {
|
|
const PathCommandAnimation command = PathCommandAnimation('h', <List<Point<double>>>[]);
|
|
|
|
expect(() {
|
|
command.toDart();
|
|
}, throwsA(anything));
|
|
});
|
|
|
|
test('_PathFrames', () {
|
|
const PathAnimation pathAnimation = PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(0.0, 0.0), Point<double>(10.0, 10.0)],
|
|
]),
|
|
PathCommandAnimation('L', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(48.0, 10.0), Point<double>(0.0, 0.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[0.5, 1.0],
|
|
);
|
|
|
|
expect(
|
|
pathAnimation.toDart(),
|
|
' const _PathFrames(\n'
|
|
' opacities: const <double>[\n'
|
|
' 0.5,\n'
|
|
' 1.0,\n'
|
|
' ],\n'
|
|
' commands: const <_PathCommand>[\n'
|
|
' const _PathMoveTo(\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(0.0, 0.0),\n'
|
|
' const Offset(10.0, 10.0),\n'
|
|
' ],\n'
|
|
' ),\n'
|
|
' const _PathLineTo(\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(48.0, 10.0),\n'
|
|
' const Offset(0.0, 0.0),\n'
|
|
' ],\n'
|
|
' ),\n'
|
|
' ],\n'
|
|
' ),\n',
|
|
);
|
|
});
|
|
|
|
test('Animation', () {
|
|
const Animation animation = Animation(Point<double>(48.0, 48.0), <PathAnimation>[
|
|
PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(0.0, 0.0), Point<double>(10.0, 10.0)],
|
|
]),
|
|
PathCommandAnimation('L', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(48.0, 10.0), Point<double>(0.0, 0.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[0.5, 1.0],
|
|
),
|
|
|
|
PathAnimation(
|
|
<PathCommandAnimation>[
|
|
PathCommandAnimation('M', <List<Point<double>>>[
|
|
<Point<double>>[Point<double>(0.0, 0.0), Point<double>(10.0, 10.0)],
|
|
]),
|
|
],
|
|
opacities: <double>[0.5, 1.0],
|
|
),
|
|
]);
|
|
|
|
expect(
|
|
animation.toDart('_AnimatedIconData', r'_$data1'),
|
|
'const _AnimatedIconData _\$data1 = const _AnimatedIconData(\n'
|
|
' const Size(48.0, 48.0),\n'
|
|
' const <_PathFrames>[\n'
|
|
' const _PathFrames(\n'
|
|
' opacities: const <double>[\n'
|
|
' 0.5,\n'
|
|
' 1.0,\n'
|
|
' ],\n'
|
|
' commands: const <_PathCommand>[\n'
|
|
' const _PathMoveTo(\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(0.0, 0.0),\n'
|
|
' const Offset(10.0, 10.0),\n'
|
|
' ],\n'
|
|
' ),\n'
|
|
' const _PathLineTo(\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(48.0, 10.0),\n'
|
|
' const Offset(0.0, 0.0),\n'
|
|
' ],\n'
|
|
' ),\n'
|
|
' ],\n'
|
|
' ),\n'
|
|
' const _PathFrames(\n'
|
|
' opacities: const <double>[\n'
|
|
' 0.5,\n'
|
|
' 1.0,\n'
|
|
' ],\n'
|
|
' commands: const <_PathCommand>[\n'
|
|
' const _PathMoveTo(\n'
|
|
' const <Offset>[\n'
|
|
' const Offset(0.0, 0.0),\n'
|
|
' const Offset(10.0, 10.0),\n'
|
|
' ],\n'
|
|
' ),\n'
|
|
' ],\n'
|
|
' ),\n'
|
|
' ],\n'
|
|
');',
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Matches all path commands' points within an error margin.
|
|
class PathMatcher extends Matcher {
|
|
const PathMatcher(this.actual, {this.margin = 0.0});
|
|
|
|
final SvgPath actual;
|
|
final double margin;
|
|
|
|
@override
|
|
Description describe(Description description) => description.add('$actual (±$margin)');
|
|
|
|
@override
|
|
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
|
|
if (item == null) {
|
|
return item == actual;
|
|
}
|
|
|
|
if (item.runtimeType != actual.runtimeType) {
|
|
return false;
|
|
}
|
|
|
|
final SvgPath other = item as SvgPath;
|
|
if (other.id != actual.id || other.opacity != actual.opacity) {
|
|
return false;
|
|
}
|
|
|
|
if (other.commands.length != actual.commands.length) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < other.commands.length; i += 1) {
|
|
if (!commandsMatch(actual.commands[i], other.commands[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool commandsMatch(SvgPathCommand actual, SvgPathCommand other) {
|
|
if (other.points.length != actual.points.length) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < other.points.length; i += 1) {
|
|
if ((other.points[i].x - actual.points[i].x).abs() > margin) {
|
|
return false;
|
|
}
|
|
if ((other.points[i].y - actual.points[i].y).abs() > margin) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class PathAnimationMatcher extends Matcher {
|
|
const PathAnimationMatcher(this.expected);
|
|
|
|
final PathAnimation expected;
|
|
|
|
@override
|
|
Description describe(Description description) => description.add('$expected');
|
|
|
|
@override
|
|
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
|
|
if (item == null) {
|
|
return item == expected;
|
|
}
|
|
|
|
if (item.runtimeType != expected.runtimeType) {
|
|
return false;
|
|
}
|
|
|
|
final PathAnimation other = item as PathAnimation;
|
|
|
|
if (!const ListEquality<double>().equals(other.opacities, expected.opacities)) {
|
|
return false;
|
|
}
|
|
|
|
if (other.commands.length != expected.commands.length) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < other.commands.length; i += 1) {
|
|
if (!commandsMatch(expected.commands[i], other.commands[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool commandsMatch(PathCommandAnimation expected, PathCommandAnimation other) {
|
|
if (other.points.length != expected.points.length) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < other.points.length; i += 1) {
|
|
if (!const ListEquality<Point<double>>().equals(other.points[i], expected.points[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
String testAsset(String name) {
|
|
return path.join('test_assets', name);
|
|
}
|