diff --git a/packages/flame_test/lib/flame_test.dart b/packages/flame_test/lib/flame_test.dart index 124c1c935e5..c73d456576d 100644 --- a/packages/flame_test/lib/flame_test.dart +++ b/packages/flame_test/lib/flame_test.dart @@ -1,5 +1,6 @@ export 'src/close_to_aabb.dart' show closeToAabb; export 'src/close_to_vector.dart'; +export 'src/close_to_vector3.dart'; export 'src/debug_text_renderer.dart' show DebugTextRenderer; export 'src/expect_double.dart'; export 'src/fails_assert.dart'; diff --git a/packages/flame_test/lib/src/close_to_vector3.dart b/packages/flame_test/lib/src/close_to_vector3.dart new file mode 100644 index 00000000000..385d3765b42 --- /dev/null +++ b/packages/flame_test/lib/src/close_to_vector3.dart @@ -0,0 +1,45 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +/// Returns a matcher which checks if the argument is a 3d vector within +/// distance [epsilon] of [vector]. For example: +/// +/// ```dart +/// expect(scale, closeToVector3(Vector3(2, -2, 0))); +/// expect(position, closeToVector3(expectedPosition, 1e-10)); +/// ``` +Matcher closeToVector3(Vector3 vector, [double epsilon = 1e-15]) { + return _IsCloseTo(vector, epsilon); +} + +class _IsCloseTo extends Matcher { + const _IsCloseTo(this._value, this._epsilon); + + final Vector3 _value; + final double _epsilon; + + @override + bool matches(dynamic item, Map matchState) { + return (item is Vector3) && (item - _value).length <= _epsilon; + } + + @override + Description describe(Description description) { + final coords = '${_value.x}, ${_value.y}, ${_value.z}'; + return description.add('a Vector3 object within $_epsilon of ($coords)'); + } + + @override + Description describeMismatch( + dynamic item, + Description mismatchDescription, + Map matchState, + bool verbose, + ) { + if (item is! Vector3) { + return mismatchDescription.add('is not an instance of Vector3'); + } + final distance = (item - _value).length; + return mismatchDescription.add('is at distance $distance'); + } +} diff --git a/packages/flame_test/test/close_to_vector3_test.dart b/packages/flame_test/test/close_to_vector3_test.dart new file mode 100644 index 00000000000..b0245d50b29 --- /dev/null +++ b/packages/flame_test/test/close_to_vector3_test.dart @@ -0,0 +1,64 @@ +import 'package:flame_test/src/close_to_vector3.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +void main() { + group('closeToVector3', () { + test('matches normally', () { + expect(Vector3.zero(), closeToVector3(Vector3(0, 0, 0))); + expect(Vector3(-14, 99, -99), closeToVector3(Vector3(-14, 99, -99))); + expect(Vector3(1e-20, -1e-16, 0), closeToVector3(Vector3(0, 0, 0))); + + expect( + Vector3(1.0001, 2.0, -1.0001), + closeToVector3(Vector3(1, 2, -1), 0.01), + ); + expect(Vector3(9, 10, 11), closeToVector3(Vector3.all(10), 10)); + }); + + test('fails on type mismatch - double', () { + try { + expect(3.14, closeToVector3(Vector3.zero())); + } on TestFailure catch (e) { + expect( + e.message, + contains( + 'Expected: a Vector3 object within 1e-15 of (0.0, 0.0, 0.0)', + ), + ); + expect(e.message, contains('Actual: <3.14>')); + expect(e.message, contains('Which: is not an instance of Vector3')); + } + }); + + test('fails on type mismatch - vector2', () { + try { + expect(Vector2(1, 2), closeToVector3(Vector3.zero())); + } on TestFailure catch (e) { + expect( + e.message, + contains( + 'Expected: a Vector3 object within 1e-15 of (0.0, 0.0, 0.0)', + ), + ); + expect(e.message, contains('Actual: Vector2:<[1.0,2.0]>')); + expect(e.message, contains('Which: is not an instance of Vector3')); + } + }); + + test('fails on value mismatch', () { + try { + expect(Vector3(101, 217, 100), closeToVector3(Vector3(100, 220, 101))); + } on TestFailure catch (e) { + expect( + e.message, + contains( + 'Expected: a Vector3 object within 1e-15 of (100.0, 220.0, 101.0)', + ), + ); + expect(e.message, contains('Actual: Vector3:<[101.0,217.0,100.0]>')); + expect(e.message, contains('Which: is at distance 3.3166247903554')); + } + }); + }); +}