[tiler] Unittests
fx run-host-tests tiler_unittests
Change-Id: Ie3f52e65751ab3f935d18eee77d3ab079ceceecf
diff --git a/lib/tiler/BUILD.gn b/lib/tiler/BUILD.gn
index d834a12..5d28ccd 100644
--- a/lib/tiler/BUILD.gn
+++ b/lib/tiler/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/dart/dart_library.gni")
+import("//topaz/runtime/dart/flutter_test.gni")
dart_library("tiler") {
package_name = "tiler"
@@ -15,3 +16,15 @@
"//third_party/dart-pkg/git/flutter/packages/flutter",
]
}
+
+flutter_test("tiler_unittests") {
+ sources = [
+ "tiler_test.dart",
+ ]
+
+ deps = [
+ ":tiler",
+ "//third_party/dart-pkg/git/flutter/packages/flutter_test",
+ ]
+}
+
diff --git a/lib/tiler/lib/src/tile.dart b/lib/tiler/lib/src/tile.dart
index db89c43..37fa7a9 100644
--- a/lib/tiler/lib/src/tile.dart
+++ b/lib/tiler/lib/src/tile.dart
@@ -210,6 +210,7 @@
void notify() => notifyListeners();
@override
- String toString() =>
- type == TileType.content ? '$type($flex)' : '$type $tiles';
+ String toString() => type == TileType.content
+ ? '($content)'
+ : type == TileType.row ? 'row $tiles' : 'column $tiles';
}
diff --git a/lib/tiler/lib/src/tiler.dart b/lib/tiler/lib/src/tiler.dart
index 258a4ec..96c803e 100644
--- a/lib/tiler/lib/src/tiler.dart
+++ b/lib/tiler/lib/src/tiler.dart
@@ -77,14 +77,13 @@
TileModel split({
@required TileModel tile,
Object content,
- AxisDirection direction,
+ AxisDirection direction = AxisDirection.right,
double flex = 0.5,
}) {
assert(tile != null);
assert(flex > 0 && flex < 1);
final parent = tile.parent;
- int index = parent?.tiles?.indexOf(tile) ?? 0;
final newTile = TileModel(
type: TileType.content,
@@ -92,48 +91,32 @@
flex: flex,
);
- // If we are splitting in the same axis as the parent, just add the
- // tile to the parent's tiles.
- if (parent?.type == TileType.row && _isVertical(direction) ||
- parent?.type == TileType.column && _isHorizontal(direction)) {
- parent.tiles.insert(
- axisDirectionIsReversed(direction) ? index : index + 1,
- newTile,
- );
- newTile.parent = parent;
+ final newParent = TileModel(
+ type: _isHorizontal(direction) ? TileType.column : TileType.row,
+ tiles: axisDirectionIsReversed(direction)
+ ? [newTile, tile]
+ : [tile, newTile],
+ );
- double oldFlex = tile.flex;
- newTile.flex = oldFlex - oldFlex * flex;
- tile.flex = oldFlex - newTile.flex;
+ // If parent is null, tile should be the root tile.
+ if (parent == null) {
+ assert(tile == root);
+ root = newParent;
} else {
- // Create a new parent that holds the tile being split and the new tile.
- final newParent = TileModel(
- type: _isHorizontal(direction) ? TileType.column : TileType.row,
- tiles: axisDirectionIsReversed(direction)
- ? [newTile, tile]
- : [tile, newTile],
- );
-
- if (parent == null) {
- // If parent is null, tile should be the root tile.
- assert(tile == root);
- root = newParent;
- } else {
- parent.tiles.remove(tile);
-
- // Copy existing flex and resize offsets from tile.
- newParent.copy(tile);
- tile.reset();
-
- newParent.parent = parent;
- parent.tiles.insert(index, newParent);
- }
- newTile.parent = newParent;
- tile
- ..parent = newParent
- ..flex = 1 - flex;
+ int index = parent?.tiles?.indexOf(tile) ?? 0;
+ parent?.tiles?.remove(tile);
+ // Copy existing flex and resize offsets from tile.
+ newParent.copy(tile);
+ tile.reset();
+ newParent.parent = parent;
+ parent.tiles.insert(index, newParent);
}
+ newTile.parent = newParent;
+ tile
+ ..parent = newParent
+ ..flex = 1 - flex;
+
notifyListeners();
return newTile;
@@ -155,10 +138,8 @@
if (root == null) {
root = tile;
- } else if (nearTile == null) {
- tile.parent = root;
- root.tiles.add(tile);
} else {
+ nearTile ??= root;
_insert(nearTile, tile, direction);
}
@@ -194,17 +175,22 @@
if (parent.tiles.isEmpty) {
// Remove empty parent.
_remove(parent);
- } else if (parent.tiles.length == 1 && parent.parent != null) {
+ } else if (parent.tiles.length == 1) {
// For parent with only one child, move the child to it's grand parent.
// and remove the parent.
final grandParent = parent.parent;
- final index = grandParent.tiles.indexOf(parent);
- var child = parent.tiles.first;
- parent.tiles.remove(child);
- grandParent.tiles.remove(parent);
+ if (grandParent != null) {
+ final index = grandParent.tiles.indexOf(parent);
+ var child = parent.tiles.first;
+ parent.tiles.remove(child);
+ grandParent.tiles.remove(parent);
- child.parent = grandParent;
- grandParent.tiles.insert(index, child);
+ child.parent = grandParent;
+ grandParent.tiles.insert(index, child);
+ } else {
+ assert(parent == root);
+ root = parent.tiles.first..parent = null;
+ }
}
tile.parent = null;
}
diff --git a/lib/tiler/test/tiler_test.dart b/lib/tiler/test/tiler_test.dart
new file mode 100644
index 0000000..3d37418
--- /dev/null
+++ b/lib/tiler/test/tiler_test.dart
@@ -0,0 +1,307 @@
+// Copyright 2019 The Fuchsia 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 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:tiler/tiler.dart';
+
+void main() {
+ group('init', () {
+ test('should create an instance of tiler', () {
+ expect(
+ Tiler(
+ model: null,
+ chromeBuilder: null,
+ sizerBuilder: null,
+ ),
+ isNotNull);
+ });
+ test('should create an instance of TilerModel with null root', () {
+ final tiler = TilerModel();
+ expect(tiler, isNotNull);
+ expect(tiler.root, isNull);
+ });
+ test('should create an instance of TilerModel with content tile', () {
+ final tiler = TilerModel()..add(content: 'x');
+ expect(tiler.root.type, TileType.content);
+ expect(tiler.root.content, 'x');
+ });
+ test('should allow initializing with empty row or column', () {
+ final tiler = TilerModel(root: TileModel(type: TileType.row));
+ expect(tiler.root.type, TileType.row);
+ expect(tiler.root.tiles.isEmpty, true);
+ });
+ });
+
+ group('add tile', () {
+ test('should allow specifying nearTile = null with non-null root', () {
+ final tiler =
+ TilerModel(root: TileModel(type: TileType.content, content: 'root'))
+ ..add(content: 'new');
+ expect(tiler.root.type, TileType.column);
+ expect(tiler.root.tiles.length, 2);
+ expect(tiler.root.tiles.first.content, 'root');
+ expect(tiler.root.tiles.last.content, 'new');
+ });
+ test('should allow specifying nearTile = null with null root', () {
+ final tiler = TilerModel()..add(content: 'new');
+ expect(tiler.root.type, TileType.content);
+ expect(tiler.root.tiles.length, 0);
+ expect(tiler.root.content, 'new');
+ });
+ test('should add to the right of given tile in a column', () {
+ final tiler = TilerModel(
+ root: TileModel(type: TileType.column, tiles: [
+ TileModel(
+ type: TileType.content,
+ content: 'near',
+ ),
+ ]),
+ );
+ final nearTile = tiler.root.tiles.first;
+ tiler.add(
+ nearTile: nearTile,
+ direction: AxisDirection.right,
+ content: 'right',
+ );
+
+ expect(tiler.root.tiles.first.content, 'near');
+ expect(tiler.root.tiles.last.content, 'right');
+ });
+ test('should add to the left of given tile in a column', () {
+ final tiler = TilerModel(
+ root: TileModel(type: TileType.column, tiles: [
+ TileModel(
+ type: TileType.content,
+ content: 'near',
+ ),
+ ]),
+ );
+ final nearTile = tiler.root.tiles.first;
+ tiler.add(
+ nearTile: nearTile,
+ direction: AxisDirection.left,
+ content: 'left',
+ );
+
+ expect(tiler.root.tiles.last.content, 'near');
+ expect(tiler.root.tiles.first.content, 'left');
+ });
+ test('should add to the top of given tile in a column', () {
+ final tiler = TilerModel(
+ root: TileModel(type: TileType.column, tiles: [
+ TileModel(
+ type: TileType.content,
+ content: 'near',
+ ),
+ ]),
+ );
+ final nearTile = tiler.root.tiles.first;
+ tiler.add(
+ nearTile: nearTile,
+ direction: AxisDirection.up,
+ content: 'up',
+ );
+
+ // Expected layout:
+ // TileType.row [
+ // TileType.content('up'),
+ // TileType.column [
+ // TileType.content('near')
+ // ]
+ // ]
+ expect(tiler.root.tiles.last.tiles.first.content, 'near');
+ expect(tiler.root.tiles.first.content, 'up');
+ });
+ test('should add to the bottom of given tile in a column', () {
+ final tiler = TilerModel(
+ root: TileModel(type: TileType.column, tiles: [
+ TileModel(
+ type: TileType.content,
+ content: 'near',
+ ),
+ ]),
+ );
+ final nearTile = tiler.root.tiles.first;
+ tiler.add(
+ nearTile: nearTile,
+ direction: AxisDirection.down,
+ content: 'down',
+ );
+
+ // Expected layout:
+ // TileType.row [
+ // TileType.column [
+ // TileType.content('near')
+ // ],
+ // TileType.content('down')
+ // ]
+ expect(tiler.root.tiles.first.tiles.first.content, 'near');
+ expect(tiler.root.tiles.last.content, 'down');
+ });
+ });
+
+ group('split tile', () {
+ test('should not allow splitting a null tile', () {
+ expect(() => TilerModel()..split(tile: null), throwsAssertionError);
+ });
+ test('should reparent a tile being split', () {
+ final tiler = TilerModel(
+ root: TileModel(
+ type: TileType.content,
+ content: 'root',
+ ));
+ tiler.split(tile: tiler.root, content: 'new');
+ expect(tiler.root.type, TileType.column);
+ expect(tiler.root.tiles.length, 2);
+ expect(tiler.root.tiles.first.content, 'root');
+ expect(tiler.root.tiles.last.content, 'new');
+ });
+ test('should reparent tile if split in same direction', () {
+ final tiler = TilerModel(
+ root: TileModel(
+ type: TileType.column,
+ tiles: [
+ TileModel(
+ type: TileType.content,
+ content: 'first',
+ )
+ ],
+ ),
+ );
+ tiler.split(
+ tile: tiler.root.tiles.first,
+ content: 'new',
+ direction: AxisDirection.right,
+ );
+ // Expected layout:
+ // column [column [(first), (new)]]
+ expect(tiler.root.type, TileType.column);
+ expect(tiler.root.tiles.length, 1);
+ expect(tiler.root.tiles.first.type, TileType.column);
+ expect(tiler.root.tiles.first.tiles.length, 2);
+ expect(tiler.root.tiles.first.tiles.first.content, 'first');
+ expect(tiler.root.tiles.first.tiles.last.content, 'new');
+ });
+ test('should reparent tile if split in cross direction(up)', () {
+ final tiler = TilerModel(
+ root: TileModel(
+ type: TileType.column,
+ tiles: [
+ TileModel(
+ type: TileType.content,
+ content: 'first',
+ )
+ ],
+ ),
+ );
+ tiler.split(
+ tile: tiler.root.tiles.first,
+ content: 'new',
+ direction: AxisDirection.up,
+ );
+ // Expected layout:
+ // column [row [content(new), content(first)]]
+ expect(tiler.root.type, TileType.column);
+ expect(tiler.root.tiles.length, 1);
+ expect(tiler.root.tiles.first.type, TileType.row);
+ expect(tiler.root.tiles.first.tiles.length, 2);
+ expect(tiler.root.tiles.first.tiles.first.content, 'new');
+ expect(tiler.root.tiles.first.tiles.last.content, 'first');
+ });
+ test('should reparent tile if split in cross direction(down)', () {
+ final tiler = TilerModel(
+ root: TileModel(
+ type: TileType.column,
+ tiles: [
+ TileModel(
+ type: TileType.content,
+ content: 'first',
+ )
+ ],
+ ),
+ );
+ tiler.split(
+ tile: tiler.root.tiles.first,
+ content: 'new',
+ direction: AxisDirection.down,
+ );
+ // Expected layout:
+ // column [row [content(first), content(new)]]
+ expect(tiler.root.type, TileType.column);
+ expect(tiler.root.tiles.length, 1);
+ expect(tiler.root.tiles.first.type, TileType.row);
+ expect(tiler.root.tiles.first.tiles.length, 2);
+ expect(tiler.root.tiles.first.tiles.first.content, 'first');
+ expect(tiler.root.tiles.first.tiles.last.content, 'new');
+ });
+ });
+
+ group('remove tile', () {
+ test('should not allow removing a null tile', () {
+ expect(() => TilerModel()..remove(null), throwsAssertionError);
+ });
+ test('should allow removing the root', () {
+ final tiler = TilerModel(
+ root: TileModel(
+ type: TileType.content,
+ content: 'root',
+ ));
+ tiler.remove(tiler.root);
+ expect(tiler.root, isNull);
+ });
+ test('should remove empty parent, including root', () {
+ final tiler = TilerModel(
+ root: TileModel(
+ type: TileType.row,
+ tiles: [
+ TileModel(type: TileType.content),
+ ],
+ ),
+ );
+
+ tiler.remove(tiler.root.tiles.first);
+ expect(tiler.root, isNull);
+ });
+ test('should set root to last remaining tile', () {
+ final tiler = TilerModel(
+ root: TileModel(
+ type: TileType.row,
+ tiles: [
+ TileModel(type: TileType.content, content: 'first'),
+ TileModel(type: TileType.content, content: 'second'),
+ ],
+ ),
+ );
+
+ tiler.remove(tiler.root.tiles.last);
+ expect(tiler.root.type, TileType.content);
+ expect(tiler.root.content, 'first');
+ });
+ test('should remove parent if left with one tile', () {
+ final tiler = TilerModel(
+ root: TileModel(
+ type: TileType.row,
+ tiles: [
+ TileModel(type: TileType.content, content: 'first'),
+ TileModel(
+ type: TileType.row,
+ tiles: [
+ TileModel(type: TileType.content, content: 'second'),
+ TileModel(type: TileType.content, content: 'third'),
+ ],
+ ),
+ ],
+ ),
+ );
+
+ tiler.remove(tiler.root.tiles.last.tiles.last);
+ expect(tiler.root.type, TileType.row);
+ expect(tiler.root.tiles.length, 2);
+ expect(tiler.root.tiles.first.content, 'first');
+ expect(tiler.root.tiles.last.content, 'second');
+ });
+ });
+}
diff --git a/packages/tests/BUILD.gn b/packages/tests/BUILD.gn
index 4d39c45..f623cb5 100644
--- a/packages/tests/BUILD.gn
+++ b/packages/tests/BUILD.gn
@@ -30,6 +30,7 @@
"//topaz/lib/setui/settings/common:lib_setui_settings_common_test($host_toolchain)",
"//topaz/lib/setui/settings/service:lib_setui_service_test($host_toolchain)",
"//topaz/lib/setui/settings/testing:lib_setui_settings_testing_test($host_toolchain)",
+ "//topaz/lib/tiler:tiler_unittests($host_toolchain)",
"//topaz/public/dart/composition_delegate:composition_delegate_tests($host_toolchain)",
"//topaz/public/dart/fuchsia_inspect:fuchsia_inspect_package_unittests($host_toolchain)",
"//topaz/public/dart/fuchsia_logger:fuchsia_logger_package_unittests($host_toolchain)",