Index: client/tests/client/dom/utils.dart |
diff --git a/client/tests/client/dom/utils.dart b/client/tests/client/dom/utils.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7072f0c71473b3ade392429d8f3cd03cad0eb487 |
--- /dev/null |
+++ b/client/tests/client/dom/utils.dart |
@@ -0,0 +1,61 @@ |
+#library('TestUtils'); |
+ |
+/** |
+ * Verifies that [actual] has the same graph structure as [expected]. |
+ * Detects cycles and DAG structure in Maps and Lists. |
+ */ |
+verifyGraph(expected, actual) { |
+ var eItems = []; |
+ var aItems = []; |
+ |
+ message(path, reason) => path == '' |
+ ? reason |
+ : reason == null ? "path: $path" : "path: $path, $reason"; |
+ |
+ walk(path, expected, actual) { |
+ if (expected is String || expected is num || expected == null) { |
+ Expect.equals(expected, actual, message(path, 'not equal')); |
+ return; |
+ } |
+ |
+ // Cycle or DAG? |
+ for (int i = 0; i < eItems.length; i++) { |
+ if (expected === eItems[i]) { |
+ Expect.identical(aItems[i], actual, message(path, 'back or side edge')); |
+ return; |
+ } |
+ } |
+ eItems.add(expected); |
+ aItems.add(actual); |
+ |
+ if (expected is List) { |
+ Expect.isTrue(actual is List, message(path, '$actual is List')); |
+ Expect.equals(expected.length, actual.length, |
+ message(path, 'different list lengths')); |
+ for (var i = 0; i < expected.length; i++) { |
+ walk('$path[$i]', expected[i], actual[i]); |
+ } |
+ return; |
+ } |
+ |
+ if (expected is Map) { |
+ Expect.isTrue(actual is Map, message(path, '$actual is Map')); |
+ for (var key in expected.getKeys()) { |
+ if (!actual.containsKey(key)) { |
+ Expect.fail(message(path, 'missing key "$key"')); |
+ } |
+ walk('$path["$key"]', expected[key], actual[key]); |
+ } |
+ for (var key in actual.getKeys()) { |
+ if (!expected.containsKey(key)) { |
+ Expect.fail(message(path, 'extra key "$key"')); |
+ } |
+ } |
+ return; |
+ } |
+ |
+ Expect.fail('Unhandled type: $expected'); |
+ } |
+ |
+ walk('', expected, actual); |
+} |