From ed40dd3577b48f2c55a30f8a287c76e9949d4212 Mon Sep 17 00:00:00 2001 From: Viktor Lidholt Date: Fri, 29 Apr 2016 14:46:09 -0700 Subject: [PATCH] Initial set of tests for flutter sprites (#3643) * Initial set of tests for flutter sprites --- .../flutter_sprites/lib/src/sprite_box.dart | 14 +- .../flutter_sprites/test/action_test.dart | 213 ++++++++++++++++++ .../flutter_sprites/test/constraint_test.dart | 82 +++++++ packages/flutter_sprites/test/node_test.dart | 205 +++++++++++++++++ 4 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 packages/flutter_sprites/test/action_test.dart create mode 100644 packages/flutter_sprites/test/constraint_test.dart create mode 100644 packages/flutter_sprites/test/node_test.dart diff --git a/packages/flutter_sprites/lib/src/sprite_box.dart b/packages/flutter_sprites/lib/src/sprite_box.dart index 11ae783fe4..52dc15c101 100644 --- a/packages/flutter_sprites/lib/src/sprite_box.dart +++ b/packages/flutter_sprites/lib/src/sprite_box.dart @@ -63,6 +63,12 @@ class SpriteBox extends RenderBox { _scheduleTick(); } + @override + void detach() { + super.detach(); + _unscheduleTick(); + } + // Member variables // Root node for drawing @@ -366,8 +372,14 @@ class SpriteBox extends RenderBox { // Updates + int _frameCallbackId; + void _scheduleTick() { - SchedulerBinding.instance.scheduleFrameCallback(_tick); + _frameCallbackId = SchedulerBinding.instance.scheduleFrameCallback(_tick); + } + + void _unscheduleTick() { + SchedulerBinding.instance.cancelFrameCallbackWithId(_frameCallbackId); } void _tick(Duration timeStamp) { diff --git a/packages/flutter_sprites/test/action_test.dart b/packages/flutter_sprites/test/action_test.dart new file mode 100644 index 0000000000..7b54270ec3 --- /dev/null +++ b/packages/flutter_sprites/test/action_test.dart @@ -0,0 +1,213 @@ +// Copyright 2015 The Chromium 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:ui'; + +import 'package:flutter_sprites/flutter_sprites.dart'; +import 'package:test/test.dart'; + +const double epsilon = 0.01; + +void main() { + test("Actions - ActionTween", () { + // Tween doubles. + double doubleValue; + ActionTween tween = new ActionTween((double a) => doubleValue = a, 0.0, 10.0, 60.0); + + tween.update(0.0); + expect(doubleValue, closeTo(0.0, epsilon)); + + tween.update(0.1); + expect(doubleValue, closeTo(1.0, epsilon)); + + tween.update(0.5); + expect(doubleValue, closeTo(5.0, epsilon)); + + tween.update(1.0); + expect(doubleValue, closeTo(10.0, epsilon)); + + tween.update(1.5); + expect(doubleValue, closeTo(15.0, epsilon)); + + tween.update(-0.5); + expect(doubleValue, closeTo(-5.0, epsilon)); + + // Tween Points. + Point pointValue; + tween = new ActionTween((Point a) => pointValue = a, Point.origin, new Point(10.0, 20.0), 60.0); + + tween.update(0.0); + expect(pointValue.x, closeTo(0.0, epsilon)); + expect(pointValue.y, closeTo(0.0, epsilon)); + + tween.update(0.1); + expect(pointValue.x, closeTo(1.0, epsilon)); + expect(pointValue.y, closeTo(2.0, epsilon)); + + tween.update(0.5); + expect(pointValue.x, closeTo(5.0, epsilon)); + expect(pointValue.y, closeTo(10.0, epsilon)); + + tween.update(1.0); + expect(pointValue.x, closeTo(10.0, epsilon)); + expect(pointValue.y, closeTo(20.0, epsilon)); + + tween.update(1.5); + expect(pointValue.x, closeTo(15.0, epsilon)); + expect(pointValue.y, closeTo(30.0, epsilon)); + + tween.update(-0.5); + expect(pointValue.x, closeTo(-5.0, epsilon)); + expect(pointValue.y, closeTo(-10.0, epsilon)); + + // Tween Colors. + Color colorValue; + tween = new ActionTween((Color a) => colorValue = a, const Color(0xff000000), const Color(0xffffffff), 60.0); + + tween.update(0.0); + expect(colorValue, equals(const Color(0xff000000))); + + tween.update(0.5); + expect(colorValue, equals(const Color(0xff7f7f7f))); + + tween.update(1.0); + expect(colorValue, equals(const Color(0xffffffff))); + + tween.update(-0.5); + expect(colorValue, equals(const Color(0xff000000))); + + tween.update(1.5); + expect(colorValue, equals(const Color(0xffffffff))); + + // Tween Size. + Size sizeValue; + tween = new ActionTween((Size a) => sizeValue = a, Size.zero, const Size(200.0, 100.0), 60.0); + + tween.update(0.0); + expect(sizeValue, equals(Size.zero)); + + tween.update(1.0); + expect(sizeValue, equals(const Size(200.0, 100.0))); + + tween.update(0.5); + expect(sizeValue.width, closeTo(100.0, epsilon)); + expect(sizeValue.height, closeTo(50.0, epsilon)); + + // Tween Rect. + Rect rectValue; + tween = new ActionTween( + (Rect a) => rectValue = a, + new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0), + new Rect.fromLTWH(100.0, 100.0, 200.0, 200.0), + 60.0 + ); + + tween.update(0.0); + expect(rectValue, equals(new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0))); + + tween.update(1.0); + expect(rectValue, equals(new Rect.fromLTWH(100.0, 100.0, 200.0, 200.0))); + + tween.update(0.5); + expect(rectValue.left, closeTo(50.0, epsilon)); + expect(rectValue.top, closeTo(50.0, epsilon)); + expect(rectValue.width, closeTo(150.0, epsilon)); + expect(rectValue.height, closeTo(150.0, epsilon)); + }); + + test("Actions - ActionRepeat", () { + double doubleValue; + ActionTween tween = new ActionTween((double a) => doubleValue = a, 0.0, 1.0, 60.0); + + ActionRepeat repeat2x = new ActionRepeat(tween, 2); + expect(repeat2x.duration, closeTo(120.0, epsilon)); + + repeat2x.update(0.0); + expect(doubleValue, closeTo(0.0, epsilon)); + + repeat2x.update(0.25); + expect(doubleValue, closeTo(0.5, epsilon)); + + repeat2x.update(0.75); + expect(doubleValue, closeTo(0.5, epsilon)); + + repeat2x.update(1.0); + expect(doubleValue, closeTo(1.0, epsilon)); + + ActionRepeat repeat4x = new ActionRepeat(tween, 4); + expect(repeat4x.duration, closeTo(240.0, epsilon)); + + repeat4x.update(0.0); + expect(doubleValue, closeTo(0.0, epsilon)); + + repeat4x.update(0.125); + expect(doubleValue, closeTo(0.5, epsilon)); + + repeat4x.update(0.875); + expect(doubleValue, closeTo(0.5, epsilon)); + + repeat4x.update(1.0); + expect(doubleValue, closeTo(1.0, epsilon)); + }); + + test("Actions - ActionGroup", () { + double value0; + double value1; + + ActionTween tween0 = new ActionTween((double a) => value0 = a, 0.0, 1.0, 10.0); + ActionTween tween1 = new ActionTween((double a) => value1 = a, 0.0, 1.0, 20.0); + + ActionGroup group = new ActionGroup([tween0, tween1]); + expect(group.duration, closeTo(20.0, epsilon)); + + group.update(0.0); + expect(value0, closeTo(0.0, epsilon)); + expect(value1, closeTo(0.0, epsilon)); + + group.update(0.5); + expect(value0, closeTo(1.0, epsilon)); + expect(value1, closeTo(0.5, epsilon)); + + group.update(1.0); + expect(value0, closeTo(1.0, epsilon)); + expect(value1, closeTo(1.0, epsilon)); + }); + + test("Actions - ActionSequence", () { + double doubleValue; + + ActionTween tween0 = new ActionTween((double a) => doubleValue = a, 0.0, 1.0, 4.0); + ActionTween tween1 = new ActionTween((double a) => doubleValue = a, 1.0, 0.0, 12.0); + + ActionSequence sequence = new ActionSequence([tween0, tween1]); + expect(sequence.duration, closeTo(16.0, epsilon)); + + sequence.update(0.0); + expect(doubleValue, closeTo(0.0, epsilon)); + + sequence.update(0.125); + expect(doubleValue, closeTo(0.5, epsilon)); + + sequence.update(0.25); + expect(doubleValue, closeTo(1.0, epsilon)); + + sequence.update(1.0); + expect(doubleValue, closeTo(0.0, epsilon)); + }); + + test("Actions - stepping", () { + double doubleValue; + + ActionTween tween = new ActionTween((double a) => doubleValue = a, 0.0, 1.0, 60.0); + + tween.step(0.0); + expect(doubleValue, closeTo(0.0, epsilon)); + + tween.step(30.0); + expect(doubleValue, closeTo(0.5, epsilon)); + + tween.step(30.0); + expect(doubleValue, closeTo(1.0, epsilon)); + }); +} diff --git a/packages/flutter_sprites/test/constraint_test.dart b/packages/flutter_sprites/test/constraint_test.dart new file mode 100644 index 0000000000..f4ffca5c79 --- /dev/null +++ b/packages/flutter_sprites/test/constraint_test.dart @@ -0,0 +1,82 @@ +// Copyright 2015 The Chromium 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:ui'; + +import 'package:flutter_sprites/flutter_sprites.dart'; +import 'package:test/test.dart'; + +const double epsilon = 0.01; + +void main() { + test("Constraints - ConstraintPositionToNode", () { + Node parent = new Node(); + + Node node0 = new Node(); + Node node1 = new Node(); + + parent.addChild(node0); + parent.addChild(node1); + + node1.constraints = [(new ConstraintPositionToNode(node0))]; + + node0.position = const Point(100.0, 50.0); + node1.applyConstraints(0.1); + + expect(node1.position.x, closeTo(100.0, epsilon)); + expect(node1.position.y, closeTo(50.0, epsilon)); + }); + + test("Constraints - ConstraintRotationToNode", () { + Node parent = new Node(); + + Node node0 = new Node(); + Node node1 = new Node()..position = const Point(0.0, 100.0); + + parent.addChild(node0); + parent.addChild(node1); + + node1.constraints = [(new ConstraintRotationToNode(node0))]; + + node1.applyConstraints(0.1); + + expect(node1.rotation, closeTo(-90.0, epsilon)); + }); + + test("Constraints - ConstraintRotationToNodeRotation", () { + Node parent = new Node(); + + Node node0 = new Node(); + Node node1 = new Node(); + + parent.addChild(node0); + parent.addChild(node1); + + node1.constraints = [(new ConstraintRotationToNodeRotation(node0, baseRotation: 10.0))]; + + node0.rotation = 90.0; + node1.applyConstraints(0.1); + + expect(node1.rotation, closeTo(100.0, epsilon)); + }); + + test("Constraints - ConstraintRotationToMovement", () { + Node parent = new Node(); + + Node node0 = new Node(); + + parent.addChild(node0); + + Constraint constraint = new ConstraintRotationToMovement(); + node0.constraints = [constraint]; + + node0.position = const Point(0.0, 0.0); + constraint.preUpdate(node0, 0.1); + + node0.position = const Point(0.0, 100.0); + node0.applyConstraints(0.1); + + expect(node0.rotation, closeTo(90.0, epsilon)); + }); +} diff --git a/packages/flutter_sprites/test/node_test.dart b/packages/flutter_sprites/test/node_test.dart new file mode 100644 index 0000000000..9ccf92e538 --- /dev/null +++ b/packages/flutter_sprites/test/node_test.dart @@ -0,0 +1,205 @@ +// Copyright 2015 The Chromium 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:ui'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_sprites/flutter_sprites.dart'; +import 'package:test/test.dart'; + +void main() { + test("Node - adding and removing children", () { + // Create root node. + NodeWithSize rootNode = new NodeWithSize(const Size(1024.0, 1024.0)); + + expect(rootNode.spriteBox, isNull); + expect(rootNode.children.length, equals(0)); + + // Create children. + Node child0 = new Node(); + Node child1 = new Node(); + + expect(child0.parent, isNull); + expect(child1.parent, isNull); + expect(child0.spriteBox, isNull); + expect(child1.spriteBox, isNull); + + // Create sprite box. + SpriteBox spriteBox = new SpriteBox(rootNode); + expect(rootNode.spriteBox, equals(spriteBox)); + + // Add children. + rootNode.addChild(child0); + rootNode.addChild(child1); + + expect(child0, isIn(rootNode.children)); + expect(child1, isIn(rootNode.children)); + expect(rootNode.children.length, equals(2)); + expect(child0.parent, equals(rootNode)); + expect(child1.parent, equals(rootNode)); + expect(child0.spriteBox, equals(spriteBox)); + expect(child1.spriteBox, equals(spriteBox)); + + // Remove one of the children. + rootNode.removeChild(child0); + + expect(child1, isIn(rootNode.children)); + expect(child1.parent, equals(rootNode)); + expect(rootNode.children.length, equals(1)); + expect(child0.parent, isNull); + expect(child0.spriteBox, isNull); + + // Add a child back in. + rootNode.addChild(child0); + expect(child0, isIn(rootNode.children)); + expect(child1, isIn(rootNode.children)); + expect(rootNode.children.length, equals(2)); + expect(child0.parent, equals(rootNode)); + expect(child1.parent, equals(rootNode)); + expect(child0.spriteBox, equals(spriteBox)); + expect(child1.spriteBox, equals(spriteBox)); + + // Remove all children. + rootNode.removeAllChildren(); + expect(rootNode.children.length, equals(0)); + expect(child0.parent, isNull); + expect(child1.parent, isNull); + expect(child0.spriteBox, isNull); + expect(child1.spriteBox, isNull); + }); + + testWidgets("Node - transformations", (WidgetTester tester) { + const double epsilon = 0.01; + + NodeWithSize rootNode = new NodeWithSize(const Size(1024.0, 1024.0)); + tester.pumpWidget(new SpriteWidget(rootNode)); + + // Translations and transformations adding up correctly. + Node child0 = new Node(); + child0.position = const Point(100.0, 0.0); + rootNode.addChild(child0); + + Node child1 = new Node(); + child1.position = const Point(200.0, 0.0); + child0.addChild(child1); + + Point rootPoint = rootNode.convertPointFromNode(Point.origin, child1); + expect(rootPoint.x, closeTo(300.0, epsilon)); + expect(rootPoint.y, closeTo(0.0, epsilon)); + + // Rotations. + Node rotatedChild = new Node(); + rotatedChild.rotation = 90.0; + rootNode.addChild(rotatedChild); + + rootPoint = rootNode.convertPointFromNode(const Point(1.0, 0.0), rotatedChild); + expect(rootPoint.x, closeTo(0.0, epsilon)); + expect(rootPoint.y, closeTo(1.0, epsilon)); + + // Scale. + Node scaledChild = new Node(); + scaledChild.scale = 2.0; + rootNode.addChild(scaledChild); + + rootPoint = rootNode.convertPointFromNode(const Point(1.0, 1.0), scaledChild); + expect(rootPoint.x, closeTo(2.0, epsilon)); + expect(rootPoint.y, closeTo(2.0, epsilon)); + + // Scale x-axis only. + Node scaledXChild = new Node(); + scaledXChild.scaleX = 2.0; + rootNode.addChild(scaledXChild); + + rootPoint = rootNode.convertPointFromNode(const Point(1.0, 1.0), scaledXChild); + expect(rootPoint.x, closeTo(2.0, epsilon)); + expect(rootPoint.y, closeTo(1.0, epsilon)); + + // Scale y-axis only. + Node scaledYChild = new Node(); + scaledYChild.scaleY = 2.0; + rootNode.addChild(scaledYChild); + + rootPoint = rootNode.convertPointFromNode(const Point(1.0, 1.0), scaledYChild); + expect(rootPoint.x, closeTo(1.0, epsilon)); + expect(rootPoint.y, closeTo(2.0, epsilon)); + + // Skew x-axis. + Node skewedXChild = new Node(); + skewedXChild.skewX = 45.0; + rootNode.addChild(skewedXChild); + + rootPoint = rootNode.convertPointFromNode(const Point(1.0, 1.0), skewedXChild); + expect(rootPoint.x, closeTo(1.0, epsilon)); + expect(rootPoint.y, closeTo(2.0, epsilon)); + + // Skew y-axis. + Node skewedYChild = new Node(); + skewedYChild.skewY = 45.0; + rootNode.addChild(skewedYChild); + + rootPoint = rootNode.convertPointFromNode(const Point(1.0, 1.0), skewedYChild); + expect(rootPoint.x, closeTo(2.0, epsilon)); + expect(rootPoint.y, closeTo(1.0, epsilon)); + }); + + test("Node - zOrder", () { + // Ensure zOrder takes president over order added. + { + Node rootNode = new Node(); + + Node node0 = new Node(); + Node node1 = new Node(); + Node node2 = new Node()..zPosition = 1.0; + Node node3 = new Node()..zPosition = 1.0; + + rootNode.addChild(node0); + rootNode.addChild(node2); + rootNode.addChild(node1); + rootNode.addChild(node3); + + expect(rootNode.children[0], equals(node0)); + expect(rootNode.children[1], equals(node1)); + expect(rootNode.children[2], equals(node2)); + expect(rootNode.children[3], equals(node3)); + } + + // Test negative zOrder. + { + Node rootNode = new Node(); + + Node node0 = new Node()..zPosition = -1.0;; + Node node1 = new Node(); + Node node2 = new Node()..zPosition = 1.0; + + rootNode.addChild(node2); + rootNode.addChild(node1); + rootNode.addChild(node0); + + expect(rootNode.children[0], equals(node0)); + expect(rootNode.children[1], equals(node1)); + expect(rootNode.children[2], equals(node2)); + } + }); + + test("Node - isPointInside", () { + Node node = new Node(); + + expect(node.isPointInside(Point.origin), equals(false)); + + NodeWithSize nodeWithSize = new NodeWithSize(const Size(10.0, 10.0)); + nodeWithSize.pivot = Point.origin; + + expect(nodeWithSize.isPointInside(const Point(1.0, 1.0)), isTrue); + expect(nodeWithSize.isPointInside(const Point(9.0, 9.0)), isTrue); + expect(nodeWithSize.isPointInside(const Point(11.0, 1.0)), isFalse); + expect(nodeWithSize.isPointInside(const Point(-1.0, -1.0)), isFalse); + + nodeWithSize.pivot = const Point(0.5, 0.5); + + expect(nodeWithSize.isPointInside(const Point(1.0, 1.0)), isTrue); + expect(nodeWithSize.isPointInside(const Point(9.0, 9.0)), isFalse); + expect(nodeWithSize.isPointInside(const Point(11.0, 1.0)), isFalse); + expect(nodeWithSize.isPointInside(const Point(-1.0, -1.0)), isTrue); + }); +}