Skip to content

Commit

Permalink
fix: hashCode should be the same for equal objects (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel authored May 19, 2021
1 parent d7f5053 commit 0616ad9
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 2.0.1

- fix: `hashCode` should be the same for equal objects (`Map` fix)

# 2.0.0

- **BREAKING**: opt into null safety
Expand Down
10 changes: 7 additions & 3 deletions lib/src/equatable_utils.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';

Expand Down Expand Up @@ -39,9 +41,11 @@ bool _isEquatable(dynamic object) {
/// https://en.wikipedia.org/wiki/Jenkins_hash_function
int _combine(int hash, dynamic object) {
if (object is Map) {
object.forEach((dynamic key, dynamic value) {
hash = hash ^ _combine(hash, <dynamic>[key, value]);
});
SplayTreeMap<dynamic, dynamic>.of(object).forEach(
(dynamic key, dynamic value) {
hash = hash ^ _combine(hash, <dynamic>[key, value]);
},
);
return hash;
}
if (object is Iterable) {
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: equatable
description: A Dart package that helps to implement value based equality without needing to explicitly override == and hashCode.
version: 2.0.0
version: 2.0.1
repository: https://github.com/felangel/equatable
issue_tracker: https://github.com/felangel/equatable/issues
homepage: https://github.com/felangel/equatable
Expand Down
46 changes: 46 additions & 0 deletions test/equatable_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,52 @@ void main() {
});
});

group('Simple Equatable (map)', () {
test('should correct toString', () {
final instance = SimpleEquatable(<String, dynamic>{});
expect(instance.toString(), 'SimpleEquatable<Map<String, dynamic>>({})');
});

test('should return true when instance is the same', () {
final instance = SimpleEquatable({'a': 1, 'b': 2, 'c': 3});
expect(instance == instance, true);
});

test('should return correct hashCode', () {
final instance = SimpleEquatable({'a': 1, 'b': 2, 'c': 3});
expect(
instance.hashCode,
instance.runtimeType.hashCode ^ mapPropsToHashCode(instance.props),
);
});

test('should have same hashCode when values are equal', () {
final instanceA = SimpleEquatable({'a': 1, 'b': 2, 'c': 3});
final instanceB = SimpleEquatable({'b': 2, 'a': 1, 'c': 3});
expect(instanceA == instanceB, true);
expect(instanceA.hashCode, instanceB.hashCode);
});

test('should return true when instances are different', () {
final instanceA = SimpleEquatable({'a': 1, 'b': 2, 'c': 3});
final instanceB = SimpleEquatable({'a': 1, 'b': 2, 'c': 3});
expect(instanceA == instanceB, true);
expect(instanceA.hashCode == instanceB.hashCode, true);
});

test('should return false when compared to non-equatable', () {
final instanceA = SimpleEquatable({'a': 1, 'b': 2, 'c': 3});
final instanceB = NonEquatable();
expect(instanceA == instanceB, false);
});

test('should return false when values are different', () {
final instanceA = SimpleEquatable({'a': 1, 'b': 2, 'c': 3});
final instanceB = SimpleEquatable({'a': 1, 'b': 2, 'c': 4});
expect(instanceA == instanceB, false);
});
});

group('Simple Equatable (Equatable)', () {
test('should correct toString', () {
final instance = SimpleEquatable(EquatableData(
Expand Down

0 comments on commit 0616ad9

Please sign in to comment.