OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // TODO(jmesserly): replace this with the real package:test. | 5 // TODO(jmesserly): replace this with the real package:test. |
6 // Not possible yet due to various bugs we still have. | 6 // Not possible yet due to various bugs we still have. |
7 library minitest; | 7 library minitest; |
8 | 8 |
9 import 'dart:async'; | 9 import 'dart:async'; |
10 import 'dart:js'; | 10 import 'dart:js'; |
11 import 'package:matcher/matcher.dart'; | 11 import 'package:matcher/matcher.dart'; |
12 export 'package:matcher/matcher.dart'; | 12 export 'package:matcher/matcher.dart'; |
13 | 13 |
| 14 // from matcher/throws_matcher.dart |
| 15 |
| 16 Function _wrapAsync = (Function f, [id]) => f; |
| 17 |
| 18 /// This can be used to match two kinds of objects: |
| 19 /// |
| 20 /// * A [Function] that throws an exception when called. The function cannot |
| 21 /// take any arguments. If you want to test that a function expecting |
| 22 /// arguments throws, wrap it in another zero-argument function that calls |
| 23 /// the one you want to test. |
| 24 /// |
| 25 /// * A [Future] that completes with an exception. Note that this creates an |
| 26 /// asynchronous expectation. The call to `expect()` that includes this will |
| 27 /// return immediately and execution will continue. Later, when the future |
| 28 /// completes, the actual expectation will run. |
| 29 const Matcher throws = const Throws(); |
| 30 |
| 31 /// This can be used to match two kinds of objects: |
| 32 /// |
| 33 /// * A [Function] that throws an exception when called. The function cannot |
| 34 /// take any arguments. If you want to test that a function expecting |
| 35 /// arguments throws, wrap it in another zero-argument function that calls |
| 36 /// the one you want to test. |
| 37 /// |
| 38 /// * A [Future] that completes with an exception. Note that this creates an |
| 39 /// asynchronous expectation. The call to `expect()` that includes this will |
| 40 /// return immediately and execution will continue. Later, when the future |
| 41 /// completes, the actual expectation will run. |
| 42 /// |
| 43 /// In both cases, when an exception is thrown, this will test that the exceptio
n |
| 44 /// object matches [matcher]. If [matcher] is not an instance of [Matcher], it |
| 45 /// will implicitly be treated as `equals(matcher)`. |
| 46 Matcher throwsA(matcher) => new Throws(wrapMatcher(matcher)); |
| 47 |
| 48 class Throws extends Matcher { |
| 49 final Matcher _matcher; |
| 50 |
| 51 const Throws([Matcher matcher]) : this._matcher = matcher; |
| 52 |
| 53 bool matches(item, Map matchState) { |
| 54 if (item is! Function && item is! Future) return false; |
| 55 if (item is Future) { |
| 56 var done = _wrapAsync((fn) => fn()); |
| 57 |
| 58 // Queue up an asynchronous expectation that validates when the future |
| 59 // completes. |
| 60 item.then((value) { |
| 61 done(() { |
| 62 fail("Expected future to fail, but succeeded with '$value'."); |
| 63 }); |
| 64 }, onError: (error, trace) { |
| 65 done(() { |
| 66 if (_matcher == null) return; |
| 67 var reason; |
| 68 if (trace != null) { |
| 69 var stackTrace = trace.toString(); |
| 70 stackTrace = " ${stackTrace.replaceAll("\n", "\n ")}"; |
| 71 reason = "Actual exception trace:\n$stackTrace"; |
| 72 } |
| 73 expect(error, _matcher, reason: reason); |
| 74 }); |
| 75 }); |
| 76 // It hasn't failed yet. |
| 77 return true; |
| 78 } |
| 79 |
| 80 try { |
| 81 item(); |
| 82 return false; |
| 83 } catch (e, s) { |
| 84 if (_matcher == null || _matcher.matches(e, matchState)) { |
| 85 return true; |
| 86 } else { |
| 87 addStateInfo(matchState, {'exception': e, 'stack': s}); |
| 88 return false; |
| 89 } |
| 90 } |
| 91 } |
| 92 |
| 93 Description describe(Description description) { |
| 94 if (_matcher == null) { |
| 95 return description.add("throws"); |
| 96 } else { |
| 97 return description.add('throws ').addDescriptionOf(_matcher); |
| 98 } |
| 99 } |
| 100 |
| 101 Description describeMismatch( |
| 102 item, Description mismatchDescription, Map matchState, bool verbose) { |
| 103 if (item is! Function && item is! Future) { |
| 104 return mismatchDescription.add('is not a Function or Future'); |
| 105 } else if (_matcher == null || matchState['exception'] == null) { |
| 106 return mismatchDescription.add('did not throw'); |
| 107 } else { |
| 108 mismatchDescription |
| 109 .add('threw ') |
| 110 .addDescriptionOf(matchState['exception']); |
| 111 if (verbose) { |
| 112 mismatchDescription.add(' at ').add(matchState['stack'].toString()); |
| 113 } |
| 114 return mismatchDescription; |
| 115 } |
| 116 } |
| 117 } |
| 118 |
| 119 // End of matcher/throws_matcher.dart |
| 120 |
14 void group(String name, void body()) => context.callMethod('suite', [name, body]
); | 121 void group(String name, void body()) => context.callMethod('suite', [name, body]
); |
15 | 122 |
16 void test(String name, body(), {String skip}) { | 123 void test(String name, body(), {String skip}) { |
17 if (skip != null) { | 124 if (skip != null) { |
18 print('SKIP $name: $skip'); | 125 print('SKIP $name: $skip'); |
19 return; | 126 return; |
20 } | 127 } |
21 JsObject result = context.callMethod('test', [name, (JsFunction done) { | 128 JsObject result = context.callMethod('test', [name, (JsFunction done) { |
22 _finishTest(f) { | 129 _finishTest(f) { |
23 if (f is Future) { | 130 if (f is Future) { |
24 f.then(_finishTest); | 131 f.then(_finishTest); |
25 } else { | 132 } else { |
26 done.apply([]); | 133 done.apply([]); |
27 } | 134 } |
28 } | 135 } |
29 _finishTest(body()); | 136 _finishTest(body()); |
30 }]); | 137 }]); |
31 result['async'] = 1; | 138 result['async'] = 1; |
32 } | 139 } |
33 | 140 |
34 | |
35 // TODO(jmesserly): everything below this was stolen from | 141 // TODO(jmesserly): everything below this was stolen from |
36 // package:test/src/frontend/expect.dart | 142 // package:test/src/frontend/expect.dart |
37 | 143 |
38 /// An exception thrown when a test assertion fails. | 144 /// An exception thrown when a test assertion fails. |
39 class TestFailure { | 145 class TestFailure { |
40 final String message; | 146 final String message; |
41 | 147 |
42 TestFailure(this.message); | 148 TestFailure(this.message); |
43 | 149 |
44 String toString() => message; | 150 String toString() => message; |
45 } | 151 } |
46 | 152 |
| 153 /// An individual unit test. |
| 154 abstract class TestCase { |
| 155 /// A unique numeric identifier for this test case. |
| 156 int get id; |
| 157 |
| 158 /// A description of what the test is specifying. |
| 159 String get description; |
| 160 |
| 161 /// The error or failure message for the tests. |
| 162 /// |
| 163 /// Initially an empty string. |
| 164 String get message; |
| 165 |
| 166 /// The result of the test case. |
| 167 /// |
| 168 /// If the test case has is completed, this will be one of [PASS], [FAIL], or |
| 169 /// [ERROR]. Otherwise, it will be `null`. |
| 170 String get result; |
| 171 |
| 172 /// Returns whether this test case passed. |
| 173 bool get passed; |
| 174 |
| 175 /// The stack trace for the error that caused this test case to fail, or |
| 176 /// `null` if it succeeded. |
| 177 StackTrace get stackTrace; |
| 178 |
| 179 /// The name of the group within which this test is running. |
| 180 String get currentGroup; |
| 181 |
| 182 /// The time the test case started running. |
| 183 /// |
| 184 /// `null` if the test hasn't yet begun running. |
| 185 DateTime get startTime; |
| 186 |
| 187 /// The amount of time the test case took. |
| 188 /// |
| 189 /// `null` if the test hasn't finished running. |
| 190 Duration get runningTime; |
| 191 |
| 192 /// Whether this test is enabled. |
| 193 /// |
| 194 /// Disabled tests won't be run. |
| 195 bool get enabled; |
| 196 |
| 197 /// Whether this test case has finished running. |
| 198 bool get isComplete => !enabled || result != null; |
| 199 } |
| 200 |
47 /// The type used for functions that can be used to build up error reports | 201 /// The type used for functions that can be used to build up error reports |
48 /// upon failures in [expect]. | 202 /// upon failures in [expect]. |
49 typedef String ErrorFormatter( | 203 typedef String ErrorFormatter( |
50 actual, Matcher matcher, String reason, Map matchState, bool verbose); | 204 actual, Matcher matcher, String reason, Map matchState, bool verbose); |
51 | 205 |
52 /// Assert that [actual] matches [matcher]. | 206 /// Assert that [actual] matches [matcher]. |
53 /// | 207 /// |
54 /// This is the main assertion function. [reason] is optional and is typically | 208 /// This is the main assertion function. [reason] is optional and is typically |
55 /// not supplied, as a reason is generated from [matcher]; if [reason] | 209 /// not supplied, as a reason is generated from [matcher]; if [reason] |
56 /// is included it is appended to the reason generated by the matcher. | 210 /// is included it is appended to the reason generated by the matcher. |
(...skipping 35 matching lines...) Loading... |
92 | 246 |
93 var mismatchDescription = new StringDescription(); | 247 var mismatchDescription = new StringDescription(); |
94 matcher.describeMismatch(actual, mismatchDescription, matchState, verbose); | 248 matcher.describeMismatch(actual, mismatchDescription, matchState, verbose); |
95 | 249 |
96 if (mismatchDescription.length > 0) { | 250 if (mismatchDescription.length > 0) { |
97 description.add(' Which: ${mismatchDescription}\n'); | 251 description.add(' Which: ${mismatchDescription}\n'); |
98 } | 252 } |
99 if (reason != null) description.add(reason).add('\n'); | 253 if (reason != null) description.add(reason).add('\n'); |
100 return description.toString(); | 254 return description.toString(); |
101 } | 255 } |
| 256 |
| 257 // from html_configuration |
| 258 void useHtmlConfiguration([bool isLayoutTest = false]) { } |
OLD | NEW |