| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011, 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 /** | |
| 6 * This file is sourced from unittest_html, unittest_dom, and unittest_vm. | |
| 7 * These libraries shold also source 'config.dart' and should define a class | |
| 8 * called [PlatformConfiguration] that implements [Configuration]. | |
| 9 */ | |
| 10 | |
| 11 /** [Configuration] used by the unittest library. */ | |
| 12 Configuration _config = null; | |
| 13 | |
| 14 /** | |
| 15 * Description text of the current test group. If multiple groups are nested, | |
| 16 * this will contain all of their text concatenated. | |
| 17 */ | |
| 18 String _currentGroup = ''; | |
| 19 | |
| 20 /** Tests executed in this suite. */ | |
| 21 List<TestCase> _tests; | |
| 22 | |
| 23 /** | |
| 24 * Callback used to run tests. Entrypoints can replace this with their own | |
| 25 * if they want. | |
| 26 */ | |
| 27 Function _testRunner; | |
| 28 | |
| 29 /** Current test being executed. */ | |
| 30 int _currentTest = 0; | |
| 31 | |
| 32 /** Total number of callbacks that have been executed in the current test. */ | |
| 33 int _callbacksCalled = 0; | |
| 34 | |
| 35 final _UNINITIALIZED = 0; | |
| 36 final _READY = 1; | |
| 37 final _RUNNING_TEST = 2; | |
| 38 | |
| 39 /** | |
| 40 * Whether an undetected error occurred while running the last test. These | |
| 41 * errors are commonly caused by DOM callbacks that were not guarded in a | |
| 42 * try-catch block. | |
| 43 */ | |
| 44 final _UNCAUGHT_ERROR = 3; | |
| 45 | |
| 46 int _state = _UNINITIALIZED; | |
| 47 | |
| 48 final _PASS = 'pass'; | |
| 49 final _FAIL = 'fail'; | |
| 50 final _ERROR = 'error'; | |
| 51 | |
| 52 /** Creates an expectation for the given value. */ | |
| 53 Expectation expect(value) => new Expectation(value); | |
| 54 | |
| 55 /** Evaluates the given function and validates that it throws an exception. */ | |
| 56 void expectThrow(function) { | |
| 57 bool threw = false; | |
| 58 try { | |
| 59 function(); | |
| 60 } catch (var e) { | |
| 61 threw = true; | |
| 62 } | |
| 63 Expect.equals(true, threw, 'Expected exception but none was thrown.'); | |
| 64 } | |
| 65 | |
| 66 /** | |
| 67 * Creates a new test case with the given description and body. The | |
| 68 * description will include the descriptions of any surrounding group() | |
| 69 * calls. | |
| 70 */ | |
| 71 void test(String spec, TestFunction body) { | |
| 72 _ensureInitialized(); | |
| 73 | |
| 74 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); | |
| 75 } | |
| 76 | |
| 77 /** | |
| 78 * Creates a new async test case with the given description and body. The | |
| 79 * description will include the descriptions of any surrounding group() | |
| 80 * calls. | |
| 81 */ | |
| 82 void asyncTest(String spec, int callbacks, TestFunction body) { | |
| 83 _ensureInitialized(); | |
| 84 | |
| 85 final testCase = new TestCase( | |
| 86 _tests.length + 1, _fullSpec(spec), body, callbacks); | |
| 87 _tests.add(testCase); | |
| 88 | |
| 89 if (callbacks < 1) { | |
| 90 testCase.error( | |
| 91 'Async tests must wait for at least one callback ', ''); | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 /** | |
| 96 * Creates a new named group of tests. Calls to group() or test() within the | |
| 97 * body of the function passed to this will inherit this group's description. | |
| 98 */ | |
| 99 void group(String description, void body()) { | |
| 100 _ensureInitialized(); | |
| 101 | |
| 102 // Concatenate the new group. | |
| 103 final oldGroup = _currentGroup; | |
| 104 if (_currentGroup != '') { | |
| 105 // Add a space. | |
| 106 _currentGroup = '$_currentGroup $description'; | |
| 107 } else { | |
| 108 // The first group. | |
| 109 _currentGroup = description; | |
| 110 } | |
| 111 | |
| 112 try { | |
| 113 body(); | |
| 114 } finally { | |
| 115 // Now that the group is over, restore the previous one. | |
| 116 _currentGroup = oldGroup; | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 /** Called by subclasses to indicate that an asynchronous test completed. */ | |
| 121 void callbackDone() { | |
| 122 _callbacksCalled++; | |
| 123 final testCase = _tests[_currentTest]; | |
| 124 if (testCase.callbacks == 0) { | |
| 125 testCase.error( | |
| 126 "Can't call callbackDone() on a synchronous test", ''); | |
| 127 _state = _UNCAUGHT_ERROR; | |
| 128 } else if (_callbacksCalled > testCase.callbacks) { | |
| 129 final expected = testCase.callbacks; | |
| 130 testCase.error( | |
| 131 'More calls to callbackDone() than expected. ' | |
| 132 + 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); | |
| 133 _state = _UNCAUGHT_ERROR; | |
| 134 } else if ((_callbacksCalled == testCase.callbacks) && | |
| 135 (_state != _RUNNING_TEST)) { | |
| 136 testCase.pass(); | |
| 137 _currentTest++; | |
| 138 _testRunner(); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 /** Runs [callback] at the end of the event loop. */ | |
| 143 _defer(void callback()) { | |
| 144 // Exploit isolate ports as a platform-independent mechanism to queue a | |
| 145 // message at the end of the event loop. | |
| 146 // TODO(sigmund): expose this functionality somewhere in our libraries. | |
| 147 final port = new ReceivePort(); | |
| 148 port.receive((msg, reply) { | |
| 149 callback(); | |
| 150 port.close(); | |
| 151 }); | |
| 152 port.toSendPort().send(null, null); | |
| 153 } | |
| 154 | |
| 155 /** Runs all queued tests, one at a time. */ | |
| 156 _runTests() { | |
| 157 _config.onStart(); | |
| 158 | |
| 159 _defer(() { | |
| 160 assert (_currentTest == 0); | |
| 161 _testRunner(); | |
| 162 }); | |
| 163 } | |
| 164 | |
| 165 /** Runs a single test. */ | |
| 166 _runTest(TestCase testCase) { | |
| 167 try { | |
| 168 _callbacksCalled = 0; | |
| 169 _state = _RUNNING_TEST; | |
| 170 | |
| 171 testCase.test(); | |
| 172 | |
| 173 if (_state != _UNCAUGHT_ERROR) { | |
| 174 if (testCase.callbacks == _callbacksCalled) { | |
| 175 testCase.pass(); | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 } catch (ExpectException e, var trace) { | |
| 180 if (_state != _UNCAUGHT_ERROR) { | |
| 181 //TODO(pquitslund) remove guard once dartc reliably propagates traces | |
| 182 testCase.fail(e.message, trace == null ? '' : trace.toString()); | |
| 183 } | |
| 184 } catch (var e, var trace) { | |
| 185 if (_state != _UNCAUGHT_ERROR) { | |
| 186 //TODO(pquitslund) remove guard once dartc reliably propagates traces | |
| 187 testCase.error('Caught ${e}', trace == null ? '' : trace.toString()); | |
| 188 } | |
| 189 } finally { | |
| 190 _state = _READY; | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 /** | |
| 195 * Runs a batch of tests, yielding whenever an asynchronous test starts | |
| 196 * running. Tests will resume executing when such asynchronous test calls | |
| 197 * [done] or if it fails with an exception. | |
| 198 */ | |
| 199 _nextBatch() { | |
| 200 while (_currentTest < _tests.length) { | |
| 201 final testCase = _tests[_currentTest]; | |
| 202 | |
| 203 _runTest(testCase); | |
| 204 | |
| 205 if (!testCase.isComplete && testCase.callbacks > 0) return; | |
| 206 | |
| 207 _currentTest++; | |
| 208 } | |
| 209 | |
| 210 _completeTests(); | |
| 211 } | |
| 212 | |
| 213 /** Publish results on the page and notify controller. */ | |
| 214 _completeTests() { | |
| 215 _state = _UNINITIALIZED; | |
| 216 | |
| 217 int testsPassed_ = 0; | |
| 218 int testsFailed_ = 0; | |
| 219 int testsErrors_ = 0; | |
| 220 | |
| 221 for (TestCase t in _tests) { | |
| 222 switch (t.result) { | |
| 223 case _PASS: testsPassed_++; break; | |
| 224 case _FAIL: testsFailed_++; break; | |
| 225 case _ERROR: testsErrors_++; break; | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 _config.onDone(testsPassed_, testsFailed_, testsErrors_, _tests); | |
| 230 } | |
| 231 | |
| 232 String _fullSpec(String spec) { | |
| 233 if (spec === null) return '$_currentGroup'; | |
| 234 return _currentGroup != '' ? '$_currentGroup $spec' : spec; | |
| 235 } | |
| 236 | |
| 237 /** | |
| 238 * Lazily initializes the test library if not already initialized. | |
| 239 */ | |
| 240 _ensureInitialized() { | |
| 241 if (_state != _UNINITIALIZED) return; | |
| 242 | |
| 243 _tests = <TestCase>[]; | |
| 244 _currentGroup = ''; | |
| 245 _state = _READY; | |
| 246 _testRunner = _nextBatch; | |
| 247 | |
| 248 if (_config == null) { | |
| 249 // TODO(sigmund): make this [new Configuration], set configuration | |
| 250 // for each platform in test.dart | |
| 251 _config = new PlatformConfiguration(); | |
| 252 } | |
| 253 _config.onInit(); | |
| 254 | |
| 255 // Immediately queue the suite up. It will run after a timeout (i.e. after | |
| 256 // main() has returned). | |
| 257 _defer(_runTests); | |
| 258 } | |
| 259 | |
| 260 /** | |
| 261 * Wraps an value and provides an "==" operator that can be used to verify that | |
| 262 * the value matches a given expectation. | |
| 263 */ | |
| 264 class Expectation { | |
| 265 final _value; | |
| 266 | |
| 267 Expectation(this._value); | |
| 268 | |
| 269 /** Asserts that the value is equivalent to [expected]. */ | |
| 270 void equals(expected) { | |
| 271 // Use the type-specialized versions when appropriate to give better | |
| 272 // error messages. | |
| 273 if (_value is String && expected is String) { | |
| 274 Expect.stringEquals(expected, _value); | |
| 275 } else if (_value is Map && expected is Map) { | |
| 276 Expect.mapEquals(expected, _value); | |
| 277 } else if (_value is Set && expected is Set) { | |
| 278 Expect.setEquals(expected, _value); | |
| 279 } else { | |
| 280 Expect.equals(expected, _value); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 /** | |
| 285 * Asserts that the difference between [expected] and the value is within | |
| 286 * [tolerance]. If no tolerance is given, it is assumed to be the value 4 | |
| 287 * significant digits smaller than the expected value. | |
| 288 */ | |
| 289 void approxEquals(num expected, | |
| 290 [num tolerance = null, String reason = null]) { | |
| 291 Expect.approxEquals(expected, _value, tolerance: tolerance, reason: reason); | |
| 292 } | |
| 293 | |
| 294 /** Asserts that the value is [null]. */ | |
| 295 void isNull() { | |
| 296 Expect.equals(null, _value); | |
| 297 } | |
| 298 | |
| 299 /** Asserts that the value is not [null]. */ | |
| 300 void isNotNull() { | |
| 301 Expect.notEquals(null, _value); | |
| 302 } | |
| 303 | |
| 304 /** Asserts that the value is [true]. */ | |
| 305 void isTrue() { | |
| 306 Expect.equals(true, _value); | |
| 307 } | |
| 308 | |
| 309 /** Asserts that the value is [false]. */ | |
| 310 void isFalse() { | |
| 311 Expect.equals(false, _value); | |
| 312 } | |
| 313 | |
| 314 /** Asserts that the value has the same elements as [expected]. */ | |
| 315 void equalsCollection(Collection expected) { | |
| 316 Expect.listEquals(expected, _value); | |
| 317 } | |
| 318 | |
| 319 /** | |
| 320 * Checks that every element of [expected] is also in [actual], and that | |
| 321 * every element of [actual] is also in [expected]. | |
| 322 */ | |
| 323 void equalsSet(Iterable expected) { | |
| 324 Expect.setEquals(expected, _value); | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 /** Summarizes information about a single test case. */ | |
| 329 class TestCase { | |
| 330 /** Identifier for this test. */ | |
| 331 final id; | |
| 332 | |
| 333 /** A description of what the test is specifying. */ | |
| 334 final String description; | |
| 335 | |
| 336 /** The body of the test case. */ | |
| 337 final TestFunction test; | |
| 338 | |
| 339 /** Total number of callbacks to wait for before the test completes. */ | |
| 340 int callbacks; | |
| 341 | |
| 342 /** Error or failure message. */ | |
| 343 String message = ''; | |
| 344 | |
| 345 /** | |
| 346 * One of [_PASS], [_FAIL], or [_ERROR] or [null] if the test hasn't run yet. | |
| 347 */ | |
| 348 String result; | |
| 349 | |
| 350 /** Stack trace associated with this test, or null if it succeeded. */ | |
| 351 String stackTrace; | |
| 352 | |
| 353 Date startTime; | |
| 354 | |
| 355 Duration runningTime; | |
| 356 | |
| 357 TestCase(this.id, this.description, this.test, this.callbacks); | |
| 358 | |
| 359 bool get isComplete() => result != null; | |
| 360 | |
| 361 void pass() { | |
| 362 result = _PASS; | |
| 363 } | |
| 364 | |
| 365 void fail(String message_, String stackTrace_) { | |
| 366 result = _FAIL; | |
| 367 this.message = message_; | |
| 368 this.stackTrace = stackTrace_; | |
| 369 } | |
| 370 | |
| 371 void error(String message_, String stackTrace_) { | |
| 372 result = _ERROR; | |
| 373 this.message = message_; | |
| 374 this.stackTrace = stackTrace_; | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 typedef void TestFunction(); | |
| OLD | NEW |