Independent test helpers (#957)

* Independent test helpers

* Relative test helper path

* Fix imports

* Fix pubspec description

* Less tested lines, since the moved lines were in tests

* Move to canvas_test
This commit is contained in:
Lukas Klingsbo
2021-09-16 13:24:45 +02:00
committed by GitHub
parent 3240d26b67
commit 2a7af3730b
47 changed files with 106 additions and 553 deletions

View File

@ -1 +1 @@
59.7
59.4

View File

@ -1,13 +0,0 @@
/// Defines how MockCanvas#matches behaves.
enum AssertionMode {
/// Actual and expected must match exactly in nature and order.
///
/// This is implemented by MockCanvas#matchExactly.
matchExactly,
/// All elements on expect must match to at least one element on actual,
/// regardless of order.
///
/// This is implemented by MockCanvas#containsAnyOrder.
containsAnyOrder,
}

View File

@ -1,23 +0,0 @@
import 'dart:ui';
import 'command.dart';
/// canvas.clipRect()
class ClipRectCommand extends CanvasCommand {
ClipRectCommand(this.clipRect, this.clipOp, this.doAntiAlias);
final Rect clipRect;
final ClipOp clipOp;
final bool doAntiAlias;
@override
bool equals(ClipRectCommand other) {
return eq(clipRect, other.clipRect) &&
clipOp == other.clipOp &&
doAntiAlias == other.doAntiAlias;
}
@override
String toString() {
return 'clipRect(${repr(clipRect)}, clipOp=$clipOp, doAntiAlias=$doAntiAlias)';
}
}

View File

@ -1,107 +0,0 @@
import 'dart:ui';
import '../mock_canvas.dart';
/// This class encapsulates a single command that was issued to a [MockCanvas].
/// Most methods of [MockCanvas] will use a dedicated class derived from
/// [CanvasCommand] to store all the arguments and then match them against
/// the expected values.
///
/// Each subclass is expected to implement two methods:
/// - `equals()`, which compares the current object against another instance
/// of the same class; and
/// - `toString()`, which is used when printing error messages in case of a
/// mismatch.
///
/// Use helper function `eq()` to implement the first method, and `repr()` to
/// implement the second.
abstract class CanvasCommand {
double tolerance = 1e-10;
/// Return true if this command is equal to [other], up to the
/// given absolute [tolerance]. The argument [other] is guaranteed
/// to have the same type as the current command.
bool equals(covariant CanvasCommand other);
/// Helper function to check the equality of any two objects.
bool eq(dynamic a, dynamic b) {
if (a == null || b == null) {
return true;
}
if (a is num && b is num) {
return (a - b).abs() < tolerance;
}
if (a is Offset && b is Offset) {
return eq(a.dx, b.dx) && eq(a.dy, b.dy);
}
if (a is List && b is List) {
return a.length == b.length &&
Iterable<int>.generate(a.length).every((i) => eq(a[i], b[i]));
}
if (a is Rect && b is Rect) {
return eq(_rectAsList(a), _rectAsList(b));
}
if (a is RRect && b is RRect) {
return eq(_rrectAsList(a), _rrectAsList(b));
}
if (a is Paint && b is Paint) {
return eq(_paintAsList(a), _paintAsList(b));
}
return a == b;
}
/// Helper function to generate string representations of various
/// components of a command.
String repr(dynamic a) {
if (a is num) {
// A more compact stringification for a floating-point [a]: it avoids
// printing ".0" at the end of round floating numbers.
return (a == a.toInt()) ? a.toInt().toString() : a.toString();
}
if (a is Offset) {
return 'Offset(${repr(a.dx)}, ${repr(a.dy)})';
}
if (a is List) {
return a.map(repr).join(', ');
}
if (a is Rect) {
return 'Rect(${repr(_rectAsList(a))})';
}
if (a is RRect) {
return 'RRect(${repr(_rrectAsList(a))})';
}
if (a is Paint) {
return 'Paint(${repr(_paintAsList(a))})';
}
return a.toString();
}
List<double> _rectAsList(Rect rect) {
return [rect.left, rect.top, rect.right, rect.bottom];
}
List<double> _rrectAsList(RRect rect) {
return [
rect.left,
rect.top,
rect.right,
rect.bottom,
rect.tlRadiusX,
rect.tlRadiusY,
rect.trRadiusX,
rect.trRadiusY,
rect.blRadiusX,
rect.blRadiusY,
rect.brRadiusX,
rect.brRadiusY,
];
}
List _paintAsList(Paint paint) {
return <dynamic>[
paint.color,
paint.blendMode,
paint.style,
paint.strokeWidth,
];
}
}

View File

@ -1,20 +0,0 @@
import 'dart:ui';
import 'command.dart';
/// canvas.drawImage()
class ImageCommand extends CanvasCommand {
final Offset offset;
final Paint? paint;
ImageCommand(this.offset, this.paint);
@override
bool equals(ImageCommand other) {
return eq(offset, other.offset) && eq(paint, other.paint);
}
@override
String toString() {
return 'drawImage(image, ${repr(offset)}, ${repr(paint)})';
}
}

View File

@ -1,20 +0,0 @@
import 'dart:ui';
import 'command.dart';
/// canvas.drawLine()
class LineCommand extends CanvasCommand {
LineCommand(this.p1, this.p2, this.paint);
final Offset p1;
final Offset p2;
final Paint? paint;
@override
bool equals(LineCommand other) {
return eq(p1, other.p1) && eq(p2, other.p2) && eq(paint, other.paint);
}
@override
String toString() {
return 'drawLine(${repr(p1)}, ${repr(p2)}, ${repr(paint)})';
}
}

View File

@ -1,16 +0,0 @@
import 'dart:ui';
import 'command.dart';
/// canvas.drawParagraph()
class ParagraphCommand extends CanvasCommand {
ParagraphCommand(this.offset);
final Offset offset;
@override
bool equals(ParagraphCommand other) => eq(offset, other.offset);
@override
String toString() {
return 'drawParagraph(${repr(offset)})';
}
}

View File

@ -1,19 +0,0 @@
import 'dart:ui';
import 'command.dart';
/// canvas.drawRect()
class RectCommand extends CanvasCommand {
RectCommand(this.rect, this.paint);
final Rect rect;
final Paint? paint;
@override
bool equals(RectCommand other) {
return eq(rect, other.rect) && eq(paint, other.paint);
}
@override
String toString() {
return 'drawRect(${repr(rect)}, ${repr(paint)})';
}
}

View File

@ -1,19 +0,0 @@
import 'dart:ui';
import 'command.dart';
/// canvas.drawRRect()
class RRectCommand extends CanvasCommand {
RRectCommand(this.rrect, this.paint);
final RRect rrect;
final Paint? paint;
@override
bool equals(RRectCommand other) {
return eq(rrect, other.rrect) && eq(paint, other.paint);
}
@override
String toString() {
return 'drawRRect(${repr(rrect)}, ${repr(paint)})';
}
}

View File

@ -1,31 +0,0 @@
import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart';
import 'command.dart';
/// This canvas command describes either a single or multiple method calls
/// `canvas.translate()`, `canvas.rotate()`, `canvas.scale()`, or
/// `canvas.transform()`.
class TransformCommand extends CanvasCommand {
TransformCommand() : _transform = Matrix4.identity();
final Matrix4 _transform;
void transform(Float64List matrix) {
_transform.multiply(Matrix4.fromFloat64List(matrix));
}
void translate(double dx, double dy) => _transform.translate(dx, dy);
void rotate(double angle) => _transform.rotateZ(angle);
void scale(double sx, double sy) => _transform.scale(sx, sy, 1);
@override
bool equals(TransformCommand other) {
return eq(_transform.storage, other._transform.storage);
}
@override
String toString() {
final content = _transform.storage.map(repr).join(', ');
return 'transform($content)';
}
}

View File

@ -1,248 +0,0 @@
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter_test/flutter_test.dart';
import 'package:test/fake.dart';
import 'assertion_mode.dart';
import 'canvas_commands/cliprect_command.dart';
import 'canvas_commands/command.dart';
import 'canvas_commands/image_command.dart';
import 'canvas_commands/line_command.dart';
import 'canvas_commands/paragraph_command.dart';
import 'canvas_commands/rect_command.dart';
import 'canvas_commands/rrect_command.dart';
import 'canvas_commands/transform_command.dart';
/// [MockCanvas] is a utility class for writing tests. It supports the same API
/// as the regular [Canvas] class from dart:ui (in theory; any missing commands
/// can be added as the need arises). In addition, this class is also a
/// [Matcher], allowing it to be used in `expect()` calls:
/// ```dart
/// final canvas = MockCanvas();
/// // ... draw something on the canvas
/// // then check that the commands issued were the ones that you'd expect:
/// expect(
/// canvas,
/// MockCanvas()
/// ..translate(10, 10)
/// ..drawRect(const Rect.fromLTWH(0, 0, 100, 100)),
/// );
/// ```
///
/// Two mock canvases will match only if they have the same number of commands,
/// and if each pair of corresponding commands matches.
///
/// Multiple transform commands (`translate()`, `scale()`, `rotate()` and
/// `transform()`) that are issued in a row are always joined into a single
/// combined transform. Thus, for example, calling `translate(10, 10)` and
/// then `translate(30, -10)` will match a single call `translate(40, 0)`.
///
/// Some commands can be partially specified. For example, in `drawLine()` and
/// `drawRect()` the `paint` argument is optional. If provided, it will be
/// checked against the actual Paint used, but if omitted, the match will still
/// succeed.
///
/// Commands that involve numeric components (i.e. coordinates, dimensions,
/// etc) will be matched approximately, with the default absolute tolerance of
/// 1e-10.
class MockCanvas extends Fake implements Canvas, Matcher {
MockCanvas({this.mode = AssertionMode.matchExactly})
: _commands = [],
_saveCount = 0;
final AssertionMode mode;
final List<CanvasCommand> _commands;
int _saveCount;
/// The absolute tolerance used when comparing numeric quantities for
/// equality. Two numeric variables `x` and `y` are considered equal if they
/// are within the distance [tolerance] from each other.
///
/// When comparing two [MockCanvas] objects, the largest of their respective
/// [tolerance]s is used.
double tolerance = 1e-10;
//#region Matcher API
@override
bool matches(covariant MockCanvas other, Map matchState) {
switch (mode) {
case AssertionMode.matchExactly:
return matchExactly(other, matchState);
case AssertionMode.containsAnyOrder:
return containsAnyOrder(other, matchState);
}
}
@override
Description describe(Description description) {
description.add('Canvas$_commands');
return description;
}
@override
Description describeMismatch(
dynamic item,
Description mismatchDescription,
Map matchState,
bool verbose,
) {
return mismatchDescription.add(matchState['description'] as String);
}
bool matchExactly(covariant MockCanvas other, Map matchState) {
if (_saveCount != 0) {
return _fail('Canvas finished with saveCount=$_saveCount', matchState);
}
final n1 = _commands.length;
final n2 = other._commands.length;
if (n1 != n2) {
return _fail(
'Canvas contains $n1 commands, but $n2 expected',
matchState,
);
}
final useTolerance = max(tolerance, other.tolerance);
for (var i = 0; i < n1; i++) {
final cmd1 = _commands[i];
final cmd2 = other._commands[i];
if (cmd1.runtimeType != cmd2.runtimeType) {
return _fail(
'Mismatched canvas commands at index $i: the actual '
'command is ${cmd1.runtimeType}, while the expected '
'was ${cmd2.runtimeType}',
matchState,
);
}
cmd1.tolerance = useTolerance;
if (!cmd1.equals(cmd2)) {
return _fail(
'Mismatched canvas commands at index $i: the actual '
'command is ${cmd1.toString()}, while the expected '
'was ${cmd2.toString()}',
matchState,
);
}
}
return true;
}
bool containsAnyOrder(covariant MockCanvas other, Map matchState) {
if (_saveCount != 0) {
return _fail('Canvas finished with saveCount=$_saveCount', matchState);
}
final useTolerance = max(tolerance, other.tolerance);
final remainingActualCommands = other._commands.toList();
for (final expectedCommand in _commands) {
final idx = remainingActualCommands.indexWhere((cmd) {
if (expectedCommand.runtimeType != cmd.runtimeType) {
return false;
}
expectedCommand.tolerance = cmd.tolerance = useTolerance;
return expectedCommand.equals(cmd);
});
if (idx == -1) {
return _fail(
'Expected canvas command not found: $expectedCommand. '
'Actual commands: $_commands',
matchState,
);
} else {
remainingActualCommands.removeAt(idx);
}
}
return true;
}
//#endregion
//#region Canvas API
@override
void transform(Float64List matrix4) {
_lastTransform.transform(matrix4);
}
@override
void translate(double dx, double dy) {
_lastTransform.translate(dx, dy);
}
@override
void rotate(double angle) {
_lastTransform.rotate(angle);
}
@override
void scale(double sx, [double? sy]) {
sy ??= sx;
_lastTransform.scale(sx, sy);
}
@override
void clipRect(
Rect rect, {
ClipOp clipOp = ClipOp.intersect,
bool doAntiAlias = true,
}) {
_commands.add(ClipRectCommand(rect, clipOp, doAntiAlias));
}
@override
void drawRect(Rect rect, [Paint? paint]) {
_commands.add(RectCommand(rect, paint));
}
@override
void drawRRect(RRect rrect, [Paint? paint]) {
_commands.add(RRectCommand(rrect, paint));
}
@override
void drawLine(Offset p1, Offset p2, [Paint? paint]) {
_commands.add(LineCommand(p1, p2, paint));
}
@override
void drawParagraph(Paragraph? paragraph, Offset offset) {
_commands.add(ParagraphCommand(offset));
}
@override
void drawImage(Image? image, Offset offset, [Paint? paint]) {
// don't compare the actual images as that would be slow, brittle and hard to test
_commands.add(ImageCommand(offset, paint));
}
@override
int getSaveCount() => _saveCount;
@override
void restore() => _saveCount--;
@override
void save() => _saveCount++;
//#endregion
//#region Private helpers
TransformCommand get _lastTransform {
if (_commands.isNotEmpty && _commands.last is TransformCommand) {
return _commands.last as TransformCommand;
}
final transform2d = TransformCommand();
_commands.add(transform2d);
return transform2d;
}
bool _fail(String reason, Map state) {
state['description'] = reason;
return false;
}
//#endregion
}

View File

@ -1,6 +0,0 @@
export 'src/test_helpers/assertion_mode.dart';
export 'src/test_helpers/expect_double.dart';
export 'src/test_helpers/expect_vector2.dart';
export 'src/test_helpers/mock_canvas.dart';
export 'src/test_helpers/mock_gesture_events.dart';
export 'src/test_helpers/mock_image.dart';

View File

@ -17,6 +17,9 @@ dev_dependencies:
dart_code_metrics: ^4.1.0
dartdoc: ^0.42.0
mocktail: ^0.1.4
canvas_test: ^0.1.1
flame_test:
path: ../flame_test
environment:
sdk: ">=2.12.0 <3.0.0"

View File

@ -1,8 +1,9 @@
import 'dart:ui';
import 'package:canvas_test/canvas_test.dart';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
class MyGame extends FlameGame with HasTappableComponents {

View File

@ -2,10 +2,10 @@ import 'dart:math' as math;
import 'dart:math';
import 'dart:ui';
import 'package:canvas_test/canvas_test.dart';
import 'package:flame/components.dart';
import 'package:flame/geometry.dart';
import 'package:flame/src/test_helpers/mock_canvas.dart';
import 'package:flame/src/test_helpers/random_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
class MyComponent extends Component {}

View File

@ -1,6 +1,6 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
void main() async {

View File

@ -1,6 +1,6 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
enum AnimationState {

View File

@ -1,5 +1,5 @@
import 'package:flame/components.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
void main() async {

View File

@ -1,5 +1,5 @@
import 'package:flame/components.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
enum SpriteState {

View File

@ -1,9 +1,9 @@
import 'dart:ui';
import 'package:canvas_test/canvas_test.dart';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/palette.dart';
import 'package:flame/test.dart';
import 'package:test/test.dart';
void main() {

View File

@ -2,7 +2,7 @@ import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
import 'package:flame/src/test_helpers/random_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'effect_test_utils.dart';

View File

@ -3,7 +3,7 @@ import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
class Callback {

View File

@ -1,7 +1,7 @@
import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame/src/test_helpers/random_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'effect_test_utils.dart';

View File

@ -1,7 +1,7 @@
import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame/src/test_helpers/random_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'effect_test_utils.dart';

View File

@ -1,7 +1,7 @@
import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame/src/test_helpers/random_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'effect_test_utils.dart';

View File

@ -1,7 +1,7 @@
import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame/src/test_helpers/random_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'effect_test_utils.dart';

View File

@ -1,7 +1,7 @@
import 'dart:math';
import 'package:flame/effects.dart';
import 'package:flame/src/test_helpers/random_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'effect_test_utils.dart';

View File

@ -1,5 +1,5 @@
import 'package:canvas_test/canvas_test.dart';
import 'package:flame/extensions.dart';
import 'package:flame/test.dart';
import 'package:test/test.dart';
void main() {

View File

@ -1,5 +1,5 @@
import 'package:flame/extensions.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
void main() {

View File

@ -1,7 +1,7 @@
import 'dart:math' as math;
import 'package:flame/extensions.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
void main() {

View File

@ -3,7 +3,7 @@ import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/src/game/game_render_box.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

View File

@ -1,9 +1,9 @@
import 'dart:ui' show Paint;
import 'package:canvas_test/canvas_test.dart';
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/test.dart';
import 'package:test/test.dart';
class TestComponent extends PositionComponent {

View File

@ -1,7 +1,7 @@
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:test/test.dart';
void main() {

View File

@ -1,7 +1,7 @@
name: flame_audio
description: Audio support for the Flame game engine. This containst all audio related code will live in the future, using the audioplayers package.
version: 1.0.0-rc.1
homepage: https://github.com/flame-engine/flame_audio
homepage: https://github.com/flame-engine/flame/tree/main/packages/flame_audio
publish_to: 'none'
environment:

View File

@ -1,7 +1,7 @@
name: flame_forge2d
description: Forge2D (Box2D) support for the Flame game engine. This uses the forge2d package and provides wrappers and components to be used inside Flame.
version: 0.8.1-releasecandidate.13
homepage: https://github.com/flame-engine/flame_forge2d
homepage: https://github.com/flame-engine/flame/tree/main/packages/flame_forge2d
publish_to: 'none'

View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 4cc385b4b84ac2f816d939a49ea1f328c4e0b48e
channel: beta
project_type: package

View File

@ -0,0 +1,4 @@
# CHANGELOG
## 0.1.0
- Initial release containing classes to help testing classes using `Flame`

View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2021 Blue Fire
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,4 @@
# Flame test helpers
This package contains classes that helps with testing applications using Flame,
and it also helps testing parts of Flame itself.

View File

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,5 @@
export 'src/expect_double.dart';
export 'src/expect_vector2.dart';
export 'src/mock_gesture_events.dart';
export 'src/mock_image.dart';
export 'src/random_test.dart';

View File

@ -1,7 +1,6 @@
import 'package:flame/extensions.dart';
import 'package:test/test.dart';
import '../../extensions.dart';
void expectVector2(
Vector2 actual,
Vector2 expected, {

View File

@ -1,9 +1,8 @@
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/input.dart';
import 'package:flutter/gestures.dart';
import '../../extensions.dart';
import '../../game.dart';
import '../../input.dart';
TapDownInfo createTapDownEvent(
Game game, {
Offset? globalPosition,

View File

@ -1,7 +1,7 @@
import 'dart:typed_data';
import 'dart:ui';
import '../../flame.dart';
import 'package:flame/flame.dart';
Future<Image> generateImage() {
final data = Uint8List(4);

View File

@ -0,0 +1,24 @@
name: flame_test
description: A package with classes to help testing applications using Flame
version: 0.1.0
homepage: https://github.com/flame-engine/flame/tree/main/packages/flame_test
publish_to: 'none'
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.17.0"
dependencies:
flame:
path: ../flame
flutter:
sdk: flutter
flutter_test:
sdk: flutter
dart_code_metrics: ^4.1.0
dartdoc: ^0.42.0
test: ^1.17.10
vector_math: '>=2.1.0 <3.0.0'
dev_dependencies:
flutter_lints: ^1.0.0