OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /// The mirror matchers library provides some additional matchers that | |
6 /// make use of `dart:mirrors`. | |
7 library matcher.mirror_matchers; | |
8 | |
9 import 'dart:mirrors'; | |
10 | |
11 import 'matcher.dart'; | |
12 | |
13 /// Returns a matcher that checks if a class instance has a property | |
14 /// with name [name], and optionally, if that property in turn satisfies | |
15 /// a [matcher]. | |
16 Matcher hasProperty(String name, [matcher]) => | |
17 new _HasProperty(name, matcher == null ? null : wrapMatcher(matcher)); | |
18 | |
19 class _HasProperty extends Matcher { | |
20 final String _name; | |
21 final Matcher _matcher; | |
22 | |
23 const _HasProperty(this._name, [this._matcher]); | |
24 | |
25 bool matches(item, Map matchState) { | |
26 var mirror = reflect(item); | |
27 var classMirror = mirror.type; | |
28 var symbol = new Symbol(_name); | |
29 var candidate = classMirror.declarations[symbol]; | |
30 if (candidate == null) { | |
31 addStateInfo(matchState, {'reason': 'has no property named "$_name"'}); | |
32 return false; | |
33 } | |
34 bool isInstanceField = candidate is VariableMirror && !candidate.isStatic; | |
35 bool isInstanceGetter = | |
36 candidate is MethodMirror && candidate.isGetter && !candidate.isStatic; | |
37 if (!(isInstanceField || isInstanceGetter)) { | |
38 addStateInfo(matchState, {'reason': | |
39 'has a member named "$_name", but it is not an instance property'}); | |
40 return false; | |
41 } | |
42 if (_matcher == null) return true; | |
43 var result = mirror.getField(symbol); | |
44 var resultMatches = _matcher.matches(result.reflectee, matchState); | |
45 if (!resultMatches) { | |
46 addStateInfo(matchState, {'value': result.reflectee}); | |
47 } | |
48 return resultMatches; | |
49 } | |
50 | |
51 Description describe(Description description) { | |
52 description.add('has property "$_name"'); | |
53 if (_matcher != null) { | |
54 description.add(' which matches ').addDescriptionOf(_matcher); | |
55 } | |
56 return description; | |
57 } | |
58 | |
59 Description describeMismatch(item, Description mismatchDescription, | |
60 Map matchState, bool verbose) { | |
61 var reason = matchState == null ? null : matchState['reason']; | |
62 if (reason != null) { | |
63 mismatchDescription.add(reason); | |
64 } else { | |
65 mismatchDescription.add('has property "$_name" with value '). | |
66 addDescriptionOf(matchState['value']); | |
67 var innerDescription = new StringDescription(); | |
68 _matcher.describeMismatch(matchState['value'], innerDescription, | |
69 matchState['state'], verbose); | |
70 if (innerDescription.length > 0) { | |
71 mismatchDescription.add(' which ').add(innerDescription.toString()); | |
72 } | |
73 } | |
74 return mismatchDescription; | |
75 } | |
76 } | |
OLD | NEW |