| Index: packages/quiver/test/testing/equality/equality_test.dart
|
| diff --git a/packages/quiver/test/testing/equality/equality_test.dart b/packages/quiver/test/testing/equality/equality_test.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ef89ee782573e10a8c7e1b6d6ea1a206528df84e
|
| --- /dev/null
|
| +++ b/packages/quiver/test/testing/equality/equality_test.dart
|
| @@ -0,0 +1,358 @@
|
| +// Copyright 2014 Google Inc. All Rights Reserved.
|
| +//
|
| +// Licensed under the Apache License, Version 2.0 (the 'License');
|
| +// you may not use this file except in compliance with the License.
|
| +// You may obtain a copy of the License at
|
| +//
|
| +// http://www.apache.org/licenses/LICENSE-2.0
|
| +//
|
| +// Unless required by applicable law or agreed to in writing, software
|
| +// distributed under the License is distributed on an 'AS IS' BASIS,
|
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +// See the License for the specific language governing permissions and
|
| +// limitations under the License.
|
| +
|
| +library quiver.testing.util.equalstester;
|
| +
|
| +import 'package:quiver/testing/equality.dart';
|
| +import 'package:test/test.dart';
|
| +
|
| +main() {
|
| + group('expectEquals', () {
|
| + _ValidTestObject reference;
|
| + _ValidTestObject equalObject1;
|
| + _ValidTestObject equalObject2;
|
| + _ValidTestObject notEqualObject1;
|
| +
|
| + setUp(() {
|
| + reference = new _ValidTestObject(1, 2);
|
| + equalObject1 = new _ValidTestObject(1, 2);
|
| + equalObject2 = new _ValidTestObject(1, 2);
|
| + notEqualObject1 = new _ValidTestObject(0, 2);
|
| + });
|
| +
|
| + test('Test null reference yields error', () {
|
| + try {
|
| + expect(null, areEqualityGroups);
|
| + fail('Should fail with null reference');
|
| + } catch (e) {
|
| + expect(e.toString(), contains('Equality Group must not be null'));
|
| + }
|
| + });
|
| +
|
| + test('Test null group name yields error', () {
|
| + try {
|
| + expect({'null': [reference], null: [reference]}, areEqualityGroups);
|
| + fail('Should fail with null group name');
|
| + } catch (e) {
|
| + expect(e.toString(), contains('Group name must not be null'));
|
| + }
|
| + });
|
| +
|
| + test('Test null group yields error', () {
|
| + try {
|
| + expect({'bad group': null}, areEqualityGroups);
|
| + fail('Should fail with null group');
|
| + } catch (e) {
|
| + expect(e.toString(), contains('Group must not be null'));
|
| + }
|
| + });
|
| +
|
| + test('Test after adding multiple instances at once with a null', () {
|
| + try {
|
| + expect(
|
| + {'bad group': [reference, equalObject1, null]}, areEqualityGroups);
|
| + fail('Should fail with null group');
|
| + } catch (e) {
|
| + expect(e.toString(), contains("$reference [group 'bad group', item 1]"
|
| + " must be equal to null [group 'bad group', item 3]"));
|
| + }
|
| + });
|
| +
|
| + test('Test adding non-equal objects only in single group.', () {
|
| + try {
|
| + expect(
|
| + {'not equal': [equalObject1, notEqualObject1]}, areEqualityGroups);
|
| + fail("Should get not equal to equal object error");
|
| + } catch (e) {
|
| + expect(e.toString(), contains("$equalObject1 [group 'not equal', item"
|
| + " 1] must be equal to $notEqualObject1 [group 'not equal'"
|
| + ", item 2]"));
|
| + }
|
| + });
|
| +
|
| + test('Test with no equals or not equals objects. This checks'
|
| + ' proper handling of null, incompatible class and reflexive tests', () {
|
| + expect({'single object': [reference]}, areEqualityGroups);
|
| + });
|
| +
|
| + test('Test after populating equal objects. This checks proper'
|
| + ' handling of equality and verifies hashCode for valid objects', () {
|
| + expect({
|
| + 'all equal': [reference, equalObject1, equalObject2]
|
| + }, areEqualityGroups);
|
| + });
|
| +
|
| + test('Test proper handling of case where an object is not equal to itself',
|
| + () {
|
| + Object obj = new _NonReflexiveObject();
|
| + try {
|
| + expect({'non-reflexive': [obj]}, areEqualityGroups);
|
| + fail("Should get non-reflexive error");
|
| + } catch (e) {
|
| + expect(e.toString(), contains("$obj must be equal to itself"));
|
| + }
|
| + });
|
| +
|
| + test('Test proper handling of case where hashcode is not idempotent', () {
|
| + Object obj = new _InconsistentHashCodeObject(1, 2);
|
| + try {
|
| + expect({'non-reflexive': [obj]}, areEqualityGroups);
|
| + fail("Should get non-reflexive error");
|
| + } catch (e) {
|
| + expect(e.toString(), contains(
|
| + "the implementation of hashCode of $obj must be idempotent"));
|
| + }
|
| + });
|
| +
|
| + test('Test proper handling where an object incorrectly tests for an '
|
| + 'incompatible class', () {
|
| + Object obj = new _InvalidEqualsIncompatibleClassObject();
|
| + try {
|
| + expect({'equals method broken': [obj]}, areEqualityGroups);
|
| + fail("Should get equal to incompatible class error");
|
| + } catch (e) {
|
| + expect(e.toString(), contains("$obj must not be equal to an "
|
| + "arbitrary object of another class"));
|
| + }
|
| + });
|
| +
|
| + test('Test proper handling where an object is not equal to one the user '
|
| + 'has said should be equal', () {
|
| + try {
|
| + expect({'non-equal': [reference, notEqualObject1]}, areEqualityGroups);
|
| + fail("Should get not equal to equal object error");
|
| + } catch (e) {
|
| + expect(
|
| + e.toString(), contains("$reference [group 'non-equal', item 1]"));
|
| + expect(e.toString(),
|
| + contains("$notEqualObject1 [group 'non-equal', item 2]"));
|
| + }
|
| + });
|
| +
|
| + test('Test for an invalid hashCode method, i.e., one that returns '
|
| + 'different value for objects that are equal according to the equals '
|
| + 'method', () {
|
| + Object a = new _InvalidHashCodeObject(1, 2);
|
| + Object b = new _InvalidHashCodeObject(1, 2);
|
| + try {
|
| + expect({'invalid hashcode': [a, b]}, areEqualityGroups);
|
| + fail("Should get invalid hashCode error");
|
| + } catch (e) {
|
| + expect(e.toString(), contains("the hashCode (${a.hashCode}) of $a"
|
| + " [group 'invalid hashcode', item 1] must be equal to the"
|
| + " hashCode (${b.hashCode}) of $b"));
|
| + }
|
| + });
|
| +
|
| + test('Symmetry Broken', () {
|
| + try {
|
| + expect({
|
| + 'broken symmetry': [named('foo')..addPeers(['bar']), named('bar')]
|
| + }, areEqualityGroups);
|
| + fail("should fail because symmetry is broken");
|
| + } catch (e) {
|
| + expect(e.toString(), contains("bar [group 'broken symmetry', item 2] "
|
| + "must be equal to foo [group 'broken symmetry', item 1]"));
|
| + }
|
| + });
|
| +
|
| + test('Transitivity Broken In EqualityGroup', () {
|
| + try {
|
| + expect({
|
| + 'transitivity broken': [
|
| + named('foo')..addPeers(['bar', 'baz']),
|
| + named('bar')..addPeers(['foo']),
|
| + named('baz')..addPeers(['foo'])
|
| + ]
|
| + }, areEqualityGroups);
|
| + fail("should fail because transitivity is broken");
|
| + } catch (e) {
|
| + expect(e.toString(), contains("bar [group 'transitivity broken', "
|
| + "item 2] must be equal to baz [group 'transitivity "
|
| + "broken', item 3]"));
|
| + }
|
| + });
|
| +
|
| + test('Unequal Objects In EqualityGroup', () {
|
| + try {
|
| + expect({
|
| + 'unequal objects': [named('foo'), named('bar')]
|
| + }, areEqualityGroups);
|
| + fail('should fail because of unequal objects in the same equality '
|
| + 'group');
|
| + } catch (e) {
|
| + expect(e.toString(), contains("foo [group 'unequal objects', item 1] "
|
| + "must be equal to bar [group 'unequal objects', item 2]"));
|
| + }
|
| + });
|
| +
|
| + test('Transitivity Broken Across EqualityGroups', () {
|
| + try {
|
| + expect({
|
| + 'transitivity one': [
|
| + named('foo')..addPeers(['bar']),
|
| + named('bar')..addPeers(['foo', 'x'])
|
| + ],
|
| + 'transitivity two': [
|
| + named('baz')..addPeers(['x']),
|
| + named('x')..addPeers(['baz', 'bar'])
|
| + ]
|
| + }, areEqualityGroups);
|
| + fail('should fail because transitivity is broken');
|
| + } catch (e) {
|
| + expect(e.toString(), contains("bar [group 'transitivity one', item 2]"
|
| + " must not be equal to x [group 'transitivity two',"
|
| + " item 2]"));
|
| + }
|
| + });
|
| +
|
| + test('EqualityGroups', () {
|
| + expect({
|
| + 'valid groups one': [
|
| + named('foo').addPeers(['bar']),
|
| + named('bar').addPeers(['foo'])
|
| + ],
|
| + 'valid groups two': [named('baz'), named('baz')]
|
| + }, areEqualityGroups);
|
| + });
|
| + });
|
| +}
|
| +
|
| +/// Test class that violates reflexitivity. It is not equal to itself.
|
| +class _NonReflexiveObject {
|
| + @override
|
| + bool operator ==(Object o) => false;
|
| +
|
| + @override
|
| + int get hashCode => super.hashCode;
|
| +}
|
| +
|
| +/**
|
| + * Test class with valid equals and hashCode methods. Testers created
|
| + * with instances of this class should always pass.
|
| + */
|
| +class _ValidTestObject {
|
| + int aspect1;
|
| + int aspect2;
|
| +
|
| + _ValidTestObject(this.aspect1, this.aspect2);
|
| +
|
| + @override
|
| + bool operator ==(Object o) {
|
| + if (!(o is _ValidTestObject)) {
|
| + return false;
|
| + }
|
| + _ValidTestObject other = o as _ValidTestObject;
|
| + if (aspect1 != other.aspect1) {
|
| + return false;
|
| + }
|
| + if (aspect2 != other.aspect2) {
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + @override
|
| + int get hashCode {
|
| + int result = 17;
|
| + result = 37 * result + aspect1;
|
| + result = 37 * result + aspect2;
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +///Test class that returns true even if the test object is of the wrong class.
|
| +class _InvalidEqualsIncompatibleClassObject {
|
| + @override
|
| + bool operator ==(Object o) {
|
| + return true;
|
| + }
|
| +
|
| + @override
|
| + int get hashCode => 0;
|
| +}
|
| +
|
| +/// Test class with inconsistent hashCode method.
|
| +class _InconsistentHashCodeObject {
|
| + int _aspect1;
|
| + int _aspect2;
|
| + int _hashCode = 0;
|
| +
|
| + _InconsistentHashCodeObject(this._aspect1, this._aspect2);
|
| +
|
| + @override
|
| + int get hashCode => _hashCode++;
|
| +
|
| + @override
|
| + bool operator ==(Object o) {
|
| + if (!(o is _InconsistentHashCodeObject)) {
|
| + return false;
|
| + }
|
| + _InconsistentHashCodeObject other = o as _InconsistentHashCodeObject;
|
| + if (_aspect1 != other._aspect1) return false;
|
| + if (_aspect2 != other._aspect2) return false;
|
| + return true;
|
| + }
|
| +}
|
| +
|
| +/// Test class with invalid hashCode method.
|
| +class _InvalidHashCodeObject {
|
| + static int hashCodeSource = 0;
|
| + int _aspect1;
|
| + int _aspect2;
|
| + int _hashCode = hashCodeSource++;
|
| +
|
| + _InvalidHashCodeObject(this._aspect1, this._aspect2);
|
| +
|
| + @override
|
| + int get hashCode => _hashCode;
|
| +
|
| + @override
|
| + bool operator ==(Object o) {
|
| + if (!(o is _InvalidHashCodeObject)) {
|
| + return false;
|
| + }
|
| + _InvalidHashCodeObject other = o as _InvalidHashCodeObject;
|
| + if (_aspect1 != other._aspect1) return false;
|
| + if (_aspect2 != other._aspect2) return false;
|
| + return true;
|
| + }
|
| +}
|
| +
|
| +_NamedObject named(String name) => new _NamedObject(name);
|
| +
|
| +class _NamedObject {
|
| + final Set<String> peerNames = new Set();
|
| + final String name;
|
| +
|
| + _NamedObject(this.name);
|
| +
|
| + void addPeers(List<String> names) {
|
| + peerNames.addAll(names);
|
| + }
|
| +
|
| + @override
|
| + bool operator ==(Object obj) {
|
| + if (obj is _NamedObject) {
|
| + _NamedObject that = obj;
|
| + return name == that.name || peerNames.contains(that.name);
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + @override
|
| + int get hashCode => 0;
|
| +
|
| + @override String toString() => name;
|
| +}
|
|
|