From ffab960f19b17d9b1bb890523edf04348cb61f1c Mon Sep 17 00:00:00 2001 From: Lasse Reichstein Holst Nielsen Date: Thu, 11 Jan 2018 17:07:10 +0100 Subject: [PATCH] Add Expect.notIdentical and Expect.allDistinct to package:expect. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I8d3c92a4723f74b40b8c0c968340d1dd1175d748 Reviewed-on: https://dart-review.googlesource.com/34061 Reviewed-by: Peter von der Ahé --- pkg/expect/lib/expect.dart | 52 ++++++++++++++++- pkg/expect/test/distinct_test.dart | 91 ++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 pkg/expect/test/distinct_test.dart diff --git a/pkg/expect/lib/expect.dart b/pkg/expect/lib/expect.dart index 020e40989ce..c04b1ced440 100644 --- a/pkg/expect/lib/expect.dart +++ b/pkg/expect/lib/expect.dart @@ -178,6 +178,55 @@ class Expect { "fails."); } + /** + * Checks whether the expected and actual values are *not* identical + * (using `identical`). + */ + static void notIdentical(var unexpected, var actual, [String reason = null]) { + if (!_identical(unexpected, actual)) return; + String msg = _getMessage(reason); + _fail("Expect.notIdentical(expected and actual: <$actual>$msg) fails."); + } + + /** + * Checks that no two [objects] are `identical`. + */ + static void allDistinct(List objects, [String reason = null]) { + String msg = _getMessage(reason); + var equivalences = new List>(objects.length); + bool hasEquivalence = false; + for (int i = 0; i < objects.length; i++) { + if (equivalences[i] != null) continue; + var o = objects[i]; + for (int j = i + 1; j < objects.length; j++) { + if (equivalences[j] != null) continue; + if (_identical(o, objects[j])) { + equivalences[j] = (equivalences[i] ??= [i])..add(j); + hasEquivalence = true; + } + } + } + if (!hasEquivalence) return; + var buffer = new StringBuffer("Expect.allDistinct(["); + var separator = ""; + for (int i = 0; i < objects.length; i++) { + buffer.write(separator); + separator = ","; + var equivalence = equivalences[i]; + if (equivalence == null) { + buffer.write('_'); + } else { + int first = equivalence[0]; + buffer..write('#')..write(first); + if (first == i) { + buffer..write('=')..write(objects[i]); + } + } + } + buffer..write("]")..write(msg)..write(")"); + _fail(buffer.toString()); + } + // Unconditional failure. static void fail(String msg) { _fail("Expect.fail('$msg')"); @@ -514,15 +563,16 @@ class Expect { } } +/// Used in [Expect] because [Expect.identical] shadows the real [identical]. bool _identical(a, b) => identical(a, b); typedef bool _CheckExceptionFn(exception); typedef _Nullary(); // Expect.throws argument must be this type. class ExpectException implements Exception { + final String message; ExpectException(this.message); String toString() => message; - String message; } /// Annotation class for testing of dart2js. Use this as metadata on method diff --git a/pkg/expect/test/distinct_test.dart b/pkg/expect/test/distinct_test.dart new file mode 100644 index 00000000000..5750782c6da --- /dev/null +++ b/pkg/expect/test/distinct_test.dart @@ -0,0 +1,91 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. 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:expect/expect.dart"; + +main() { + var o1 = new Object(); + var o2 = new Object(); + var o3 = new Object(); + var c1 = new C(0); + var c2 = new C(0); + + // Successful checks. + Expect.notIdentical(o1, o2, "msg"); + + Expect.equals(c1, c2); + Expect.notIdentical(c1, c2, "msg"); + + Expect.notIdentical([1], [1], "msg"); + + Expect.allDistinct([], "msg"); + Expect.allDistinct([o1], "msg"); + Expect.allDistinct([ + o1, + o2, + c1, + c2, + [1] + ], "msg"); + Expect.allDistinct(new List.generate(100, (_) => new Object())); + + fails((msg) { + Expect.notIdentical(o1, o1, msg); + }); + + fails((msg) { + var list = [1]; + Expect.notIdentical(list, list, msg); + }); + + fails((msg) { + Expect.allDistinct([o1, o1], msg); + }); + fails((msg) { + Expect.allDistinct([o1, o1, o1, o1], msg); + }); + fails((msg) { + Expect.allDistinct([o1, o1, o2, o3], msg); + }); + fails((msg) { + Expect.allDistinct([o1, o2, o1, o3], msg); + }); + fails((msg) { + Expect.allDistinct([o1, o2, o3, o1], msg); + }); + fails((msg) { + Expect.allDistinct([o1, o2, o2, o3], msg); + }); + fails((msg) { + Expect.allDistinct([o1, o2, o3, o2], msg); + }); + fails((msg) { + Expect.allDistinct([o1, o2, o3, o3], msg); + }); + fails((msg) { + var list = new List.generate(100, (_) => new Object()); + list.add(list[0]); + Expect.allDistinct(list, msg); + }); +} + +class C { + final x; + const C(this.x); + int get hashCode => x.hashCode; + bool operator ==(Object other) => other is C && x == other.x; +} + +int _ctr = 0; +fails(test(msg)) { + var msg = "__#${_ctr++}#__"; // "Unique" name. + try { + test(msg); + throw "Did not throw!"; + } on ExpectException catch (e) { + if (e.message.indexOf(msg) < 0) { + throw "Failure did not contain message: \"$msg\" not in ${e.message}"; + } + } +}