| OLD | NEW |
| 1 // Copyright 2014 Google Inc. All Rights Reserved. | 1 // Copyright 2014 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
| 6 // | 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // | 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. | 13 // limitations under the License. |
| 14 | 14 |
| 15 part of quiver.testing.equality; | 15 part of quiver.testing.equality; |
| 16 | 16 |
| 17 /** | 17 /// Matcher for == and hashCode methods of a class. |
| 18 * Matcher for == and hashCode methods of a class. | 18 /// |
| 19 * | 19 /// To use, invoke areEqualityGroups with a list of equality groups where each |
| 20 * To use, invoke areEqualityGroups with a list of equality groups where each | 20 /// group contains objects that are supposed to be equal to each other, and |
| 21 * group contains objects that are supposed to be equal to each other, and | 21 /// objects of different groups are expected to be unequal. For example: |
| 22 * objects of different groups are expected to be unequal. For example: | 22 /// |
| 23 * | 23 /// expect({ |
| 24 * expect({ | 24 /// 'hello': ["hello", "h" + "ello"], |
| 25 * 'hello': ["hello", "h" + "ello"], | 25 /// 'world': ["world", "wor" + "ld"], |
| 26 * 'world': ["world", "wor" + "ld"], | 26 /// 'three': [2, 1 + 1] |
| 27 * 'three': [2, 1 + 1] | 27 /// }, areEqualityGroups); |
| 28 * }, areEqualityGroups); | 28 /// |
| 29 * | 29 /// This tests that: |
| 30 * This tests that: | 30 /// |
| 31 * | 31 /// * comparing each object against itself returns true |
| 32 * * comparing each object against itself returns true | 32 /// * comparing each object against an instance of an incompatible class |
| 33 * * comparing each object against an instance of an incompatible class | 33 /// returns false |
| 34 * returns false | 34 /// * comparing each pair of objects within the same equality group returns |
| 35 * * comparing each pair of objects within the same equality group returns | 35 /// true |
| 36 * true | 36 /// * comparing each pair of objects from different equality groups returns |
| 37 * * comparing each pair of objects from different equality groups returns | 37 /// false |
| 38 * false | 38 /// * the hash codes of any two equal objects are equal |
| 39 * * the hash codes of any two equal objects are equal | 39 /// * equals implementation is idempotent |
| 40 * * equals implementation is idempotent | 40 /// |
| 41 * | 41 /// The format of the Map passed to expect is such that the map keys are used in |
| 42 * The format of the Map passed to expect is such that the map keys are used in | 42 /// error messages to identify the group described by the map value. |
| 43 * error messages to identify the group described by the map value. | 43 /// |
| 44 * | 44 /// When a test fails, the error message labels the objects involved in |
| 45 * When a test fails, the error message labels the objects involved in | 45 /// the failed comparison as follows: |
| 46 * the failed comparison as follows: | 46 /// |
| 47 * | 47 /// "`[group x, item j]`" refers to the ith item in the xth equality group, |
| 48 * "`[group x, item j]`" refers to the ith item in the xth equality group, | 48 /// where both equality groups and the items within equality groups are |
| 49 * where both equality groups and the items within equality groups are | 49 /// numbered starting from 1. When either a constructor argument or an |
| 50 * numbered starting from 1. When either a constructor argument or an | 50 /// equal object is provided, that becomes group 1. |
| 51 * equal object is provided, that becomes group 1. | |
| 52 * | |
| 53 */ | |
| 54 const Matcher areEqualityGroups = const _EqualityGroupMatcher(); | 51 const Matcher areEqualityGroups = const _EqualityGroupMatcher(); |
| 55 | 52 |
| 56 const _repetitions = 3; | 53 const _repetitions = 3; |
| 57 | 54 |
| 58 class _EqualityGroupMatcher extends Matcher { | 55 class _EqualityGroupMatcher extends Matcher { |
| 59 static const failureReason = 'failureReason'; | 56 static const failureReason = 'failureReason'; |
| 60 const _EqualityGroupMatcher(); | 57 const _EqualityGroupMatcher(); |
| 61 | 58 |
| 62 @override | 59 @override |
| 63 Description describe(Description description) => | 60 Description describe(Description description) => |
| 64 description.add('to be equality groups'); | 61 description.add('to be equality groups'); |
| 65 | 62 |
| 66 @override | 63 @override |
| 67 bool matches(Map<String, List> item, Map matchState) { | 64 bool matches(item, Map matchState) { |
| 68 try { | 65 try { |
| 69 _verifyEqualityGroups(item, matchState); | 66 _verifyEqualityGroups(item as Map<String, List>, matchState); |
| 70 return true; | 67 return true; |
| 71 } on MatchError catch (e) { | 68 } on MatchError catch (e) { |
| 72 matchState[failureReason] = e.toString(); | 69 matchState[failureReason] = e.toString(); |
| 73 return false; | 70 return false; |
| 74 } | 71 } |
| 75 } | 72 } |
| 76 | 73 |
| 77 Description describeMismatch(item, Description mismatchDescription, | 74 Description describeMismatch(item, Description mismatchDescription, |
| 78 Map matchState, bool verbose) => | 75 Map matchState, bool verbose) => |
| 79 mismatchDescription.add(" ${matchState[failureReason]}"); | 76 mismatchDescription.add(" ${matchState[failureReason]}"); |
| 80 | 77 |
| 81 void _verifyEqualityGroups(Map<String, List> equalityGroups, Map matchState) { | 78 void _verifyEqualityGroups(Map<String, List> equalityGroups, Map matchState) { |
| 82 if (equalityGroups == null) { | 79 if (equalityGroups == null) { |
| 83 throw new MatchError('Equality Group must not be null'); | 80 throw new MatchError('Equality Group must not be null'); |
| 84 } | 81 } |
| 85 var equalityGroupsCopy = {}; | 82 final equalityGroupsCopy = <String, List>{}; |
| 86 equalityGroups.forEach((String groupName, List group) { | 83 equalityGroups.forEach((String groupName, List group) { |
| 87 if (groupName == null) { | 84 if (groupName == null) { |
| 88 throw new MatchError('Group name must not be null'); | 85 throw new MatchError('Group name must not be null'); |
| 89 } | 86 } |
| 90 if (group == null) { | 87 if (group == null) { |
| 91 throw new MatchError('Group must not be null'); | 88 throw new MatchError('Group must not be null'); |
| 92 } | 89 } |
| 93 equalityGroupsCopy[groupName] = new List.from(group); | 90 equalityGroupsCopy[groupName] = new List.from(group); |
| 94 }); | 91 }); |
| 95 | 92 |
| 96 // Run the test multiple times to ensure deterministic equals | 93 // Run the test multiple times to ensure deterministic equals |
| 97 for (var run in range(_repetitions)) { | 94 for (var i = 0; i < _repetitions; i++) { |
| 98 _checkBasicIdentity(equalityGroupsCopy, matchState); | 95 _checkBasicIdentity(equalityGroupsCopy, matchState); |
| 99 _checkGroupBasedEquality(equalityGroupsCopy); | 96 _checkGroupBasedEquality(equalityGroupsCopy); |
| 100 } | 97 } |
| 101 } | 98 } |
| 102 | 99 |
| 103 void _checkBasicIdentity(Map<String, List> equalityGroups, Map matchState) { | 100 void _checkBasicIdentity(Map<String, List> equalityGroups, Map matchState) { |
| 104 var flattened = equalityGroups.values.expand((group) => group); | 101 var flattened = equalityGroups.values.expand((group) => group); |
| 105 for (var item in flattened) { | 102 for (var item in flattened) { |
| 106 if (item == _NotAnInstance.equalToNothing) { | 103 if (item == _NotAnInstance.equalToNothing) { |
| 107 throw new MatchError( | 104 throw new MatchError( |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 } | 203 } |
| 207 | 204 |
| 208 class MatchError extends Error { | 205 class MatchError extends Error { |
| 209 final message; | 206 final message; |
| 210 | 207 |
| 211 /// The [message] describes the match error. | 208 /// The [message] describes the match error. |
| 212 MatchError([this.message]); | 209 MatchError([this.message]); |
| 213 | 210 |
| 214 String toString() => message; | 211 String toString() => message; |
| 215 } | 212 } |
| OLD | NEW |