Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: lib/unittest/unittest.dart

Issue 10153005: unittest step 3 and 4: remove TestFramework.dart, make test.dart use (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « lib/unittest/html_print.dart ('k') | lib/unittest/vm_config.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 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 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 /** 5 /**
6 * A library for writing dart unit tests. 6 * A library for writing dart unit tests.
7 * 7 *
8 * ##Concepts## 8 * ##Concepts##
9 * 9 *
10 * * Tests: Tests are specified via the top-level function [test], they can be 10 * * Tests: Tests are specified via the top-level function [test], they can be
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 * }); 57 * });
58 * }); 58 * });
59 * group('group B', () { 59 * group('group B', () {
60 * test('this B.1', () { 60 * test('this B.1', () {
61 * int x = 2 + 3; 61 * int x = 2 + 3;
62 * expect(x).equals(5); 62 * expect(x).equals(5);
63 * }); 63 * });
64 * }); 64 * });
65 * } 65 * }
66 * 66 *
67 * Asynchronous tests: under the current API (soon to be deprecated): 67 * Asynchronous tests: if callbacks expect between 0 and 2 positional arguments.
68 * 68 *
69 * #import('path-to-dart/lib/unittest/unitest.dart'); 69 * #import('path-to-dart/lib/unittest/unitest.dart');
70 * #import('dart:dom'); 70 * #import('dart:dom');
71 * main() { 71 * main() {
72 * // use [asyncTest], indicate the expected number of callbacks: 72 * test('calllback is executed once', () {
73 * asyncTest('this is a test', 1, () { 73 * // wrap the callback of an asynchronous call with [expectAsync0] if
74 * window.setTimeout(() { 74 * // the callback takes 0 arguments...
75 * window.setTimeout(expectAsync0(() {
75 * int x = 2 + 3; 76 * int x = 2 + 3;
76 * expect(x).equals(5); 77 * expect(x).equals(5);
77 * // invoke [callbackDone] at the end of the callback. 78 * }), 0);
78 * callbackDone(); 79 * });
79 * }, 0); 80 *
81 * test('calllback is executed twice', () {
82 * var callback = expectAsync0(() {
83 * int x = 2 + 3;
84 * expect(x).equals(5);
85 * }, count: 2); // <-- we can indicate multiplicity to [expectAsync0]
86 * window.setTimeout(callback, 0);
87 * window.setTimeout(callback, 0);
80 * }); 88 * });
81 * } 89 * }
82 * 90 *
83 * We plan to replace this with a different API, one API we are considering is: 91 * Note: due to some language limitations we have to use different functions
92 * depending on the number of positional arguments of the callback. In the
93 * future, we plan to expose a single `expectAsync` function that can be used
94 * regardless of the number of positional arguments. This requires new langauge
95 * features or fixes to the current spec (e.g. see dartbug.com/2706).
96 *
97 * Meanwhile, we plan to add this alternative API for callbacks of more than 2
98 * arguments or that take named parameters. (this is not implemented yet,
99 * but will be coming here soon).
84 * 100 *
85 * #import('path-to-dart/lib/unittest/unitest.dart'); 101 * #import('path-to-dart/lib/unittest/unitest.dart');
86 * #import('dart:dom'); 102 * #import('dart:dom');
87 * main() { 103 * main() {
88 * test('this is a test', () { 104 * test('calllback is executed', () {
89 * // wrap the callback of an asynchronous call with [later] 105 * // indicate ahead of time that an async callback is expected.
90 * window.setTimeout(later(() { 106 * var async = startAsync();
91 * int x = 2 + 3; 107 * window.setTimeout(() {
92 * expect(x).equals(5); 108 * // Guard the body of the callback, so errors are propagated
109 * // correctly
110 * guardAsync(() {
111 * int x = 2 + 3;
112 * expect(x).equals(5);
113 * });
114 * // indicate that the asynchronous callback was invoked.
115 * async.complete();
93 * }), 0); 116 * }), 0);
94 * }); 117 * });
95 * } 118 *
96 */ 119 */
97 #library('unittest'); 120 #library('unittest');
98 121
99 #import('dart:isolate'); 122 #import('dart:isolate');
100 123
101 #source('config.dart'); 124 #source('config.dart');
102 #source('expectation.dart'); 125 #source('expectation.dart');
103 #source('test_case.dart'); 126 #source('test_case.dart');
104 127
105 /** [Configuration] used by the unittest library. */ 128 /** [Configuration] used by the unittest library. */
(...skipping 30 matching lines...) Expand all
136 final _RUNNING_TEST = 2; 159 final _RUNNING_TEST = 2;
137 160
138 /** 161 /**
139 * Whether an undetected error occurred while running the last test. These 162 * Whether an undetected error occurred while running the last test. These
140 * errors are commonly caused by DOM callbacks that were not guarded in a 163 * errors are commonly caused by DOM callbacks that were not guarded in a
141 * try-catch block. 164 * try-catch block.
142 */ 165 */
143 final _UNCAUGHT_ERROR = 3; 166 final _UNCAUGHT_ERROR = 3;
144 167
145 int _state = _UNINITIALIZED; 168 int _state = _UNINITIALIZED;
169 String _uncaughtErrorMessage = null;
146 170
147 final _PASS = 'pass'; 171 final _PASS = 'pass';
148 final _FAIL = 'fail'; 172 final _FAIL = 'fail';
149 final _ERROR = 'error'; 173 final _ERROR = 'error';
150 174
151 /** Creates an expectation for the given value. */ 175 /** Creates an expectation for the given value. */
152 Expectation expect(value) => new Expectation(value); 176 Expectation expect(value) => new Expectation(value);
153 177
154 /** Evaluates the given function and validates that it throws an exception. */ 178 /** Evaluates the given function and validates that it throws an exception. */
155 void expectThrow(function) { 179 void expectThrow(function) {
156 bool threw = false; 180 bool threw = false;
157 try { 181 try {
158 function(); 182 function();
159 } catch (var e) { 183 } catch (var e) {
160 threw = true; 184 threw = true;
161 } 185 }
162 Expect.equals(true, threw, 'Expected exception but none was thrown.'); 186 Expect.equals(true, threw, 'Expected exception but none was thrown.');
163 } 187 }
164 188
165 /** 189 /**
166 * Creates a new test case with the given description and body. The 190 * Creates a new test case with the given description and body. The
167 * description will include the descriptions of any surrounding group() 191 * description will include the descriptions of any surrounding group()
168 * calls. 192 * calls.
169 */ 193 */
170 void test(String spec, TestFunction body) { 194 void test(String spec, TestFunction body) {
171 _ensureInitialized(); 195 ensureInitialized();
172 196
173 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); 197 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0));
174 } 198 }
175 199
176 /** 200 /**
177 * Creates a new async test case with the given description and body. The 201 * Creates a new async test case with the given description and body. The
178 * description will include the descriptions of any surrounding group() 202 * description will include the descriptions of any surrounding group()
179 * calls. 203 * calls.
180 */ 204 */
181 // TODO(sigmund): deprecate this API 205 // TODO(sigmund): deprecate this API
182 void asyncTest(String spec, int callbacks, TestFunction body) { 206 void asyncTest(String spec, int callbacks, TestFunction body) {
183 _ensureInitialized(); 207 ensureInitialized();
184 208
185 final testCase = new TestCase( 209 final testCase = new TestCase(
186 _tests.length + 1, _fullSpec(spec), body, callbacks); 210 _tests.length + 1, _fullSpec(spec), body, callbacks);
187 _tests.add(testCase); 211 _tests.add(testCase);
188 212
189 if (callbacks < 1) { 213 if (callbacks < 1) {
190 testCase.error( 214 testCase.error(
191 'Async tests must wait for at least one callback ', ''); 215 'Async tests must wait for at least one callback ', '');
192 } 216 }
193 } 217 }
194 218
219 /** Sentinel value for [_SpreadArgsHelper]. */
220 class _Sentinel {
221 const _Sentinel();
222 }
223
224 // TODO(sigmund): make a singleton const field when frog supports passing those
225 // as default values to named arguments.
226 final _sentinel = const _Sentinel();
227
228 /** Simulates spread arguments using named arguments. */
229 // TODO(sigmund): remove this class and simply use a closure with named
230 // arguments inside [_expectAsync], once bug 282 is fixed or frog is replaced by
231 // dart2js.
232 class _SpreadArgsHelper {
233 Function callback;
234 int expectedCalls;
235 int calls = 0;
236 TestCase testCase;
237 _SpreadArgsHelper(this.callback, this.expectedCalls) {
238 Expect.isTrue(_currentTest < _tests.length);
239 testCase = _tests[_currentTest];
240 testCase.callbacks++;
241 }
242
243 invoke([arg0 = _sentinel, arg1 = _sentinel, arg2 = _sentinel,
244 arg3 = _sentinel, arg4 = _sentinel]) {
245 return _guard(() {
246 if (!_incrementCall()) {
247 return;
248 } else if (arg0 == _sentinel) {
249 return callback();
250 } else if (arg1 == _sentinel) {
251 return callback(arg0);
252 } else if (arg2 == _sentinel) {
253 return callback(arg0, arg1);
254 } else if (arg3 == _sentinel) {
255 return callback(arg0, arg1, arg2);
256 } else if (arg4 == _sentinel) {
257 return callback(arg0, arg1, arg2, arg3);
258 } else {
259 testCase.error(
260 'unittest lib does not support callbacks with more than 4 arguments',
261 '');
262 _state = _UNCAUGHT_ERROR;
263 }
264 }, () { if (calls == expectedCalls) callbackDone(); });
265 }
266
267 invoke0() {
268 return _guard(
269 () => _incrementCall() ? callback() : null,
270 () { if (calls == expectedCalls) callbackDone(); });
271 }
272
273 invoke1(arg1) {
274 return _guard(
275 () => _incrementCall() ? callback(arg1) : null,
276 () { if (calls == expectedCalls) callbackDone(); });
277 }
278
279 invoke2(arg1, arg2) {
280 return _guard(
281 () => _incrementCall() ? callback(arg1, arg2) : null,
282 () { if (calls == expectedCalls) callbackDone(); });
283 }
284
285 /** Returns false if we exceded the number of expected calls. */
286 bool _incrementCall() {
287 calls++;
288 if (calls > expectedCalls) {
289 testCase.error(
290 'Callback called more times than expected ($expectedCalls)',
291 '');
292 _state = _UNCAUGHT_ERROR;
293 return false;
294 }
295 return true;
296 }
297 }
298
195 /** 299 /**
196 * Indicate to the unittest framework that a 0-argument callback is expected. 300 * Indicate that [callback] is expected to be called a [count] number of times
197 * 301 * (by default 1). The unittest framework will wait for the callback to run the
198 * The framework will wait for the callback to run before it continues with the 302 * specified [count] times before it continues with the following test. Using
199 * following test. The callback must excute once and only once. Using [later] 303 * [_expectAsync] will also ensure that errors that occur within [callback] are
200 * will also ensure that errors that occur within the callback are tracked and 304 * tracked and reported. [callback] should take between 0 and 4 positional
201 * reported by the unittest framework. 305 * arguments (named arguments are not supported here).
202 */ 306 */
203 // TODO(sigmund): expose this functionality 307 Function _expectAsync(Function callback, [int count = 1]) {
204 Function _later0(Function callback) { 308 return new _SpreadArgsHelper(callback, count).invoke;
205 Expect.isTrue(_currentTest < _tests.length);
206 var testCase = _tests[_currentTest];
207 testCase.callbacks++;
208 return () {
209 _guard(() => callback(), callbackDone);
210 };
211 }
212
213 // TODO(sigmund): expose this functionality
214 /** Like [_later0] but expecting a callback with 1 argument. */
215 Function _later1(Function callback) {
216 Expect.isTrue(_currentTest < _tests.length);
217 var testCase = _tests[_currentTest];
218 testCase.callbacks++;
219 return (arg0) {
220 _guard(() => callback(arg0), callbackDone);
221 };
222 }
223
224 // TODO(sigmund): expose this functionality
225 /** Like [_later0] but expecting a callback with 2 arguments. */
226 Function _later2(Function callback) {
227 Expect.isTrue(_currentTest < _tests.length);
228 var testCase = _tests[_currentTest];
229 testCase.callbacks++;
230 return (arg0, arg1) {
231 _guard(() => callback(arg0, arg1), callbackDone);
232 };
233 } 309 }
234 310
235 /** 311 /**
312 * Indicate that [callback] is expected to be called a [count] number of times
313 * (by default 1). The unittest framework will wait for the callback to run the
314 * specified [count] times before it continues with the following test. Using
315 * [expectAsync0] will also ensure that errors that occur within [callback] are
316 * tracked and reported. [callback] should take 0 positional arguments (named
317 * arguments are not supported).
318 */
319 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
320 Function expectAsync0(Function callback, [int count = 1]) {
321 return new _SpreadArgsHelper(callback, count).invoke0;
322 }
323
324 /** Like [expectAsync0] but [callback] should take 1 positional argument. */
325 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
326 Function expectAsync1(Function callback, [int count = 1]) {
327 return new _SpreadArgsHelper(callback, count).invoke1;
328 }
329
330 /** Like [expectAsync0] but [callback] should take 1 positional argument. */
331 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
332 Function expectAsync2(Function callback, [int count = 1]) {
333 return new _SpreadArgsHelper(callback, count).invoke2;
334 }
335
336 /**
236 * Creates a new named group of tests. Calls to group() or test() within the 337 * Creates a new named group of tests. Calls to group() or test() within the
237 * body of the function passed to this will inherit this group's description. 338 * body of the function passed to this will inherit this group's description.
238 */ 339 */
239 void group(String description, void body()) { 340 void group(String description, void body()) {
240 _ensureInitialized(); 341 ensureInitialized();
241 342
242 // Concatenate the new group. 343 // Concatenate the new group.
243 final oldGroup = _currentGroup; 344 final oldGroup = _currentGroup;
244 if (_currentGroup != '') { 345 if (_currentGroup != '') {
245 // Add a space. 346 // Add a space.
246 _currentGroup = '$_currentGroup $description'; 347 _currentGroup = '$_currentGroup $description';
247 } else { 348 } else {
248 // The first group. 349 // The first group.
249 _currentGroup = description; 350 _currentGroup = description;
250 } 351 }
251 352
252 try { 353 try {
253 body(); 354 body();
254 } finally { 355 } finally {
255 // Now that the group is over, restore the previous one. 356 // Now that the group is over, restore the previous one.
256 _currentGroup = oldGroup; 357 _currentGroup = oldGroup;
257 } 358 }
258 } 359 }
259 360
260 /** Called by subclasses to indicate that an asynchronous test completed. */ 361 /** Called by subclasses to indicate that an asynchronous test completed. */
261 void callbackDone() { 362 void callbackDone() {
262 _callbacksCalled++; 363 _callbacksCalled++;
263 final testCase = _tests[_currentTest]; 364 if (_currentTest < _tests.length) {
264 if (_callbacksCalled > testCase.callbacks) { 365 final testCase = _tests[_currentTest];
265 final expected = testCase.callbacks; 366 if (_callbacksCalled > testCase.callbacks) {
266 testCase.error( 367 final expected = testCase.callbacks;
267 'More calls to callbackDone() than expected. ' 368 testCase.error(
268 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); 369 'More calls to callbackDone() than expected. '
269 _state = _UNCAUGHT_ERROR; 370 'Actual: ${_callbacksCalled}, expected: ${expected}', '');
270 } else if ((_callbacksCalled == testCase.callbacks) && 371 _state = _UNCAUGHT_ERROR;
271 (_state != _RUNNING_TEST)) { 372 } else if ((_callbacksCalled == testCase.callbacks) &&
272 if (testCase.result == null) testCase.pass(); 373 (_state != _RUNNING_TEST)) {
273 _currentTest++; 374 if (testCase.result == null) testCase.pass();
274 _testRunner(); 375 _currentTest++;
376 _testRunner();
377 }
275 } 378 }
276 } 379 }
277 380
278 void notifyError(String msg, String trace) { 381 /** Menchanism to notify that an error was caught outside of this library. */
382 void reportTestError(String msg, String trace) {
279 if (_currentTest < _tests.length) { 383 if (_currentTest < _tests.length) {
280 final testCase = _tests[_currentTest]; 384 final testCase = _tests[_currentTest];
281 testCase.error(msg, trace); 385 testCase.error(msg, trace);
282 _state = _UNCAUGHT_ERROR; 386 _state = _UNCAUGHT_ERROR;
283 if (testCase.callbacks > 0) { 387 if (testCase.callbacks > 0) {
284 _currentTest++; 388 _currentTest++;
285 _testRunner(); 389 _testRunner();
286 } 390 }
391 } else {
392 _uncaughtErrorMessage = "$msg: $trace";
287 } 393 }
288 } 394 }
289 395
290 /** Runs [callback] at the end of the event loop. */ 396 /** Runs [callback] at the end of the event loop. */
291 _defer(void callback()) { 397 _defer(void callback()) {
292 // Exploit isolate ports as a platform-independent mechanism to queue a 398 // Exploit isolate ports as a platform-independent mechanism to queue a
293 // message at the end of the event loop. 399 // message at the end of the event loop.
294 // TODO(sigmund): expose this functionality somewhere in our libraries. 400 // TODO(sigmund): expose this functionality somewhere in our libraries.
295 final port = new ReceivePort(); 401 final port = new ReceivePort();
296 port.receive((msg, reply) { 402 port.receive((msg, reply) {
(...skipping 11 matching lines...) Expand all
308 assert (_currentTest == 0); 414 assert (_currentTest == 0);
309 _testRunner(); 415 _testRunner();
310 }); 416 });
311 } 417 }
312 418
313 /** 419 /**
314 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, update 420 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, update
315 * the [_currentTest] status accordingly. 421 * the [_currentTest] status accordingly.
316 */ 422 */
317 _guard(tryBody, [finallyBody]) { 423 _guard(tryBody, [finallyBody]) {
318 final testCase = _tests[_currentTest];
319 try { 424 try {
320 tryBody(); 425 return tryBody();
321 } catch (ExpectException e, var trace) { 426 } catch (ExpectException e, var trace) {
322 if (_state != _UNCAUGHT_ERROR) { 427 if (_state != _UNCAUGHT_ERROR) {
323 //TODO(pquitslund) remove guard once dartc reliably propagates traces 428 _tests[_currentTest].fail(e.message, trace.toString());
324 testCase.fail(e.message, trace == null ? '' : trace.toString());
325 } 429 }
326 } catch (var e, var trace) { 430 } catch (var e, var trace) {
327 if (_state != _UNCAUGHT_ERROR) { 431 if (_state != _UNCAUGHT_ERROR) {
328 //TODO(pquitslund) remove guard once dartc reliably propagates traces 432 _tests[_currentTest].error('Caught $e', trace.toString());
329 testCase.error('Caught ${e}', trace == null ? '' : trace.toString());
330 } 433 }
331 } finally { 434 } finally {
332 _state = _READY; 435 _state = _READY;
333 if (finallyBody != null) finallyBody(); 436 if (finallyBody != null) finallyBody();
334 } 437 }
335 } 438 }
336 439
337 /** 440 /**
338 * Runs a batch of tests, yielding whenever an asynchronous test starts 441 * Runs a batch of tests, yielding whenever an asynchronous test starts
339 * running. Tests will resume executing when such asynchronous test calls 442 * running. Tests will resume executing when such asynchronous test calls
340 * [done] or if it fails with an exception. 443 * [done] or if it fails with an exception.
341 */ 444 */
342 _nextBatch() { 445 _nextBatch() {
343 while (_currentTest < _tests.length) { 446 while (_currentTest < _tests.length) {
344 final testCase = _tests[_currentTest]; 447 final testCase = _tests[_currentTest];
345
346 _guard(() { 448 _guard(() {
347 _callbacksCalled = 0; 449 _callbacksCalled = 0;
348 _state = _RUNNING_TEST; 450 _state = _RUNNING_TEST;
349 451
350 testCase.test(); 452 testCase.test();
351 453
352 if (_state != _UNCAUGHT_ERROR) { 454 if (_state != _UNCAUGHT_ERROR) {
353 if (testCase.callbacks == _callbacksCalled) { 455 if (testCase.callbacks == _callbacksCalled) {
354 testCase.pass(); 456 testCase.pass();
355 } 457 }
(...skipping 17 matching lines...) Expand all
373 int testsErrors_ = 0; 475 int testsErrors_ = 0;
374 476
375 for (TestCase t in _tests) { 477 for (TestCase t in _tests) {
376 switch (t.result) { 478 switch (t.result) {
377 case _PASS: testsPassed_++; break; 479 case _PASS: testsPassed_++; break;
378 case _FAIL: testsFailed_++; break; 480 case _FAIL: testsFailed_++; break;
379 case _ERROR: testsErrors_++; break; 481 case _ERROR: testsErrors_++; break;
380 } 482 }
381 } 483 }
382 484
383 _config.onDone(testsPassed_, testsFailed_, testsErrors_, _tests); 485 _config.onDone(testsPassed_, testsFailed_, testsErrors_, _tests,
486 _uncaughtErrorMessage);
384 } 487 }
385 488
386 String _fullSpec(String spec) { 489 String _fullSpec(String spec) {
387 if (spec === null) return '$_currentGroup'; 490 if (spec === null) return '$_currentGroup';
388 return _currentGroup != '' ? '$_currentGroup $spec' : spec; 491 return _currentGroup != '' ? '$_currentGroup $spec' : spec;
389 } 492 }
390 493
391 /** 494 /**
392 * Lazily initializes the test library if not already initialized. 495 * Lazily initializes the test library if not already initialized.
393 */ 496 */
394 _ensureInitialized() { 497 ensureInitialized() {
395 if (_state != _UNINITIALIZED) return; 498 if (_state != _UNINITIALIZED) return;
396 499
397 _tests = <TestCase>[]; 500 _tests = <TestCase>[];
398 _currentGroup = ''; 501 _currentGroup = '';
399 _state = _READY; 502 _state = _READY;
400 _testRunner = _nextBatch; 503 _testRunner = _nextBatch;
401 504
402 if (_config == null) { 505 if (_config == null) {
403 _config = new Configuration(); 506 _config = new Configuration();
404 } 507 }
405 _config.onInit(); 508 _config.onInit();
406 509
407 // Immediately queue the suite up. It will run after a timeout (i.e. after 510 // Immediately queue the suite up. It will run after a timeout (i.e. after
408 // main() has returned). 511 // main() has returned).
409 _defer(_runTests); 512 _defer(_runTests);
410 } 513 }
411 514
412 /** Signature for a test function. */ 515 /** Signature for a test function. */
413 typedef void TestFunction(); 516 typedef void TestFunction();
OLDNEW
« no previous file with comments | « lib/unittest/html_print.dart ('k') | lib/unittest/vm_config.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698