|
| 1 | +// Copyright (c) 2017, Google Inc. Please see the AUTHORS file for details. |
| 2 | +// All rights reserved. Use of this source code is governed by a BSD-style |
| 3 | +// license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +import 'package:built_collection/built_collection.dart'; |
| 6 | +import 'package:built_value/built_value.dart'; |
| 7 | +import 'package:test/test.dart'; |
| 8 | + |
| 9 | +/// Returns a matcher that matches if the value is structurally equal to |
| 10 | +/// [expected]. |
| 11 | +/// |
| 12 | +/// Improves on a simple equality test by offering a detailed mismatch message. |
| 13 | +Matcher equalsBuilt(Built expected) => new _BuiltValueMatcher(expected); |
| 14 | + |
| 15 | +/// Matcher for [Built] instances. |
| 16 | +/// |
| 17 | +/// Converts instances to maps using [_CapturingToStringHelper] then compares |
| 18 | +/// using [equals], which does deep comparison on maps. |
| 19 | +class _BuiltValueMatcher implements Matcher { |
| 20 | + final Built _expected; |
| 21 | + final Matcher _delegate; |
| 22 | + |
| 23 | + _BuiltValueMatcher(this._expected) : _delegate = equals(_toMap(_expected)); |
| 24 | + |
| 25 | + @override |
| 26 | + Description describe(Description description) => |
| 27 | + _delegate.describe(description); |
| 28 | + |
| 29 | + @override |
| 30 | + Description describeMismatch(dynamic item, Description mismatchDescription, |
| 31 | + Map matchState, bool verbose) { |
| 32 | + if (_expected.runtimeType != item.runtimeType) |
| 33 | + return mismatchDescription.add('is the wrong type'); |
| 34 | + |
| 35 | + return _delegate.describeMismatch( |
| 36 | + _toMap(item), mismatchDescription, matchState, verbose); |
| 37 | + } |
| 38 | + |
| 39 | + @override |
| 40 | + bool matches(dynamic item, Map matchState) { |
| 41 | + if (_expected.runtimeType != item.runtimeType) return false; |
| 42 | + |
| 43 | + return _delegate.matches(_toMap(item), matchState); |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +/// Converts a Built to a map. |
| 48 | +Map<String, Object> _toMap(Object built) { |
| 49 | + // Save the current newBuiltValueToStringHelper so we can restore it on |
| 50 | + // return. |
| 51 | + final previousNewBuiltValueToStringHelper = newBuiltValueToStringHelper; |
| 52 | + |
| 53 | + // Create a ToStringHelper that will capture values instead of converting |
| 54 | + // them to String. |
| 55 | + final capturingToStringHelper = new _CapturingToStringHelper(); |
| 56 | + newBuiltValueToStringHelper = (String className) { |
| 57 | + // Store the class name in the map, so we check types as well as fields |
| 58 | + // and values. |
| 59 | + capturingToStringHelper.map[r'$class'] = className; |
| 60 | + return capturingToStringHelper; |
| 61 | + }; |
| 62 | + |
| 63 | + // Call toString() on the value to do capture. |
| 64 | + built.toString(); |
| 65 | + |
| 66 | + // Reset to what it was on method entry. |
| 67 | + newBuiltValueToStringHelper = previousNewBuiltValueToStringHelper; |
| 68 | + |
| 69 | + return capturingToStringHelper.map; |
| 70 | +} |
| 71 | + |
| 72 | +/// Captures values in a Map instead of converting to a String. |
| 73 | +class _CapturingToStringHelper implements BuiltValueToStringHelper { |
| 74 | + final Map<String, Object> map = <String, Object>{}; |
| 75 | + |
| 76 | + @override |
| 77 | + void add(String field, Object value) { |
| 78 | + if (value is Built) { |
| 79 | + map[field] = _toMap(value); |
| 80 | + } else if (value is BuiltMap) { |
| 81 | + map[field] = value.asMap(); |
| 82 | + } else { |
| 83 | + map[field] = value; |
| 84 | + } |
| 85 | + } |
| 86 | +} |
0 commit comments