| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 // TODO(jmesserly): replace this with the real package:test. | |
| 6 // Not possible yet due to various bugs we still have. | |
| 7 library minitest; | |
| 8 | |
| 9 import 'dart:async'; | |
| 10 import 'dart:js'; | |
| 11 import 'package:matcher/matcher.dart'; | |
| 12 export 'package:matcher/matcher.dart'; | |
| 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 | |
| 121 void group(String name, void body()) => context.callMethod('suite', [name, body]
); | |
| 122 | |
| 123 void test(String name, body(), {String skip}) { | |
| 124 if (skip != null) { | |
| 125 print('SKIP $name: $skip'); | |
| 126 return; | |
| 127 } | |
| 128 JsObject result = context.callMethod('test', [name, (JsFunction done) { | |
| 129 _finishTest(f) { | |
| 130 if (f is Future) { | |
| 131 f.then(_finishTest); | |
| 132 } else { | |
| 133 done.apply([]); | |
| 134 } | |
| 135 } | |
| 136 _finishTest(body()); | |
| 137 }]); | |
| 138 result['async'] = 1; | |
| 139 } | |
| 140 | |
| 141 // TODO(jmesserly): everything below this was stolen from | |
| 142 // package:test/src/frontend/expect.dart | |
| 143 | |
| 144 /// An exception thrown when a test assertion fails. | |
| 145 class TestFailure { | |
| 146 final String message; | |
| 147 | |
| 148 TestFailure(this.message); | |
| 149 | |
| 150 String toString() => message; | |
| 151 } | |
| 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 | |
| 201 /// The type used for functions that can be used to build up error reports | |
| 202 /// upon failures in [expect]. | |
| 203 typedef String ErrorFormatter( | |
| 204 actual, Matcher matcher, String reason, Map matchState, bool verbose); | |
| 205 | |
| 206 /// Assert that [actual] matches [matcher]. | |
| 207 /// | |
| 208 /// This is the main assertion function. [reason] is optional and is typically | |
| 209 /// not supplied, as a reason is generated from [matcher]; if [reason] | |
| 210 /// is included it is appended to the reason generated by the matcher. | |
| 211 /// | |
| 212 /// [matcher] can be a value in which case it will be wrapped in an | |
| 213 /// [equals] matcher. | |
| 214 /// | |
| 215 /// If the assertion fails a [TestFailure] is thrown. | |
| 216 /// | |
| 217 /// In some cases extra diagnostic info can be produced on failure (for | |
| 218 /// example, stack traces on mismatched exceptions). To enable these, | |
| 219 /// [verbose] should be specified as `true`. | |
| 220 void expect(actual, matcher, | |
| 221 {String reason, bool verbose: false, ErrorFormatter formatter}) { | |
| 222 | |
| 223 matcher = wrapMatcher(matcher); | |
| 224 var matchState = {}; | |
| 225 try { | |
| 226 if (matcher.matches(actual, matchState)) return; | |
| 227 } catch (e, trace) { | |
| 228 if (reason == null) { | |
| 229 reason = '${(e is String) ? e : e.toString()} at $trace'; | |
| 230 } | |
| 231 } | |
| 232 if (formatter == null) formatter = _defaultFailFormatter; | |
| 233 fail(formatter(actual, matcher, reason, matchState, verbose)); | |
| 234 } | |
| 235 | |
| 236 /// Convenience method for throwing a new [TestFailure] with the provided | |
| 237 /// [message]. | |
| 238 void fail(String message) => throw new TestFailure(message); | |
| 239 | |
| 240 // The default error formatter. | |
| 241 String _defaultFailFormatter( | |
| 242 actual, Matcher matcher, String reason, Map matchState, bool verbose) { | |
| 243 var description = new StringDescription(); | |
| 244 description.add('Expected: ').addDescriptionOf(matcher).add('\n'); | |
| 245 description.add(' Actual: ').addDescriptionOf(actual).add('\n'); | |
| 246 | |
| 247 var mismatchDescription = new StringDescription(); | |
| 248 matcher.describeMismatch(actual, mismatchDescription, matchState, verbose); | |
| 249 | |
| 250 if (mismatchDescription.length > 0) { | |
| 251 description.add(' Which: ${mismatchDescription}\n'); | |
| 252 } | |
| 253 if (reason != null) description.add(reason).add('\n'); | |
| 254 return description.toString(); | |
| 255 } | |
| 256 | |
| 257 // from html_configuration | |
| 258 void useHtmlConfiguration([bool isLayoutTest = false]) { } | |
| OLD | NEW |