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

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

Issue 10226008: Revert "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, 7 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: if callbacks expect between 0 and 2 positional arguments. 67 * Asynchronous tests: under the current API (soon to be deprecated):
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 * test('calllback is executed once', () { 72 * // use [asyncTest], indicate the expected number of callbacks:
73 * // wrap the callback of an asynchronous call with [expectAsync0] if 73 * asyncTest('this is a test', 1, () {
74 * // the callback takes 0 arguments... 74 * window.setTimeout(() {
75 * window.setTimeout(expectAsync0(() { 75 * int x = 2 + 3;
76 * expect(x).equals(5);
77 * // invoke [callbackDone] at the end of the callback.
78 * callbackDone();
79 * }, 0);
80 * });
81 * }
82 *
83 * We plan to replace this with a different API, one API we are considering is:
84 *
85 * #import('path-to-dart/lib/unittest/unitest.dart');
86 * #import('dart:dom');
87 * main() {
88 * test('this is a test', () {
89 * // wrap the callback of an asynchronous call with [later]
90 * window.setTimeout(later(() {
76 * int x = 2 + 3; 91 * int x = 2 + 3;
77 * expect(x).equals(5); 92 * expect(x).equals(5);
78 * }), 0); 93 * }), 0);
79 * }); 94 * });
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);
88 * });
89 * } 95 * }
90 *
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).
100 *
101 * #import('path-to-dart/lib/unittest/unitest.dart');
102 * #import('dart:dom');
103 * main() {
104 * test('calllback is executed', () {
105 * // indicate ahead of time that an async callback is expected.
106 * var async = startAsync();
107 * window.setTimeout(() {
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();
116 * }), 0);
117 * });
118 *
119 */ 96 */
120 #library('unittest'); 97 #library('unittest');
121 98
122 #import('dart:isolate'); 99 #import('dart:isolate');
123 100
124 #source('config.dart'); 101 #source('config.dart');
125 #source('expectation.dart'); 102 #source('expectation.dart');
126 #source('test_case.dart'); 103 #source('test_case.dart');
127 104
128 /** [Configuration] used by the unittest library. */ 105 /** [Configuration] used by the unittest library. */
(...skipping 30 matching lines...) Expand all
159 final _RUNNING_TEST = 2; 136 final _RUNNING_TEST = 2;
160 137
161 /** 138 /**
162 * Whether an undetected error occurred while running the last test. These 139 * Whether an undetected error occurred while running the last test. These
163 * errors are commonly caused by DOM callbacks that were not guarded in a 140 * errors are commonly caused by DOM callbacks that were not guarded in a
164 * try-catch block. 141 * try-catch block.
165 */ 142 */
166 final _UNCAUGHT_ERROR = 3; 143 final _UNCAUGHT_ERROR = 3;
167 144
168 int _state = _UNINITIALIZED; 145 int _state = _UNINITIALIZED;
169 String _uncaughtErrorMessage = null;
170 146
171 final _PASS = 'pass'; 147 final _PASS = 'pass';
172 final _FAIL = 'fail'; 148 final _FAIL = 'fail';
173 final _ERROR = 'error'; 149 final _ERROR = 'error';
174 150
175 /** Creates an expectation for the given value. */ 151 /** Creates an expectation for the given value. */
176 Expectation expect(value) => new Expectation(value); 152 Expectation expect(value) => new Expectation(value);
177 153
178 /** Evaluates the given function and validates that it throws an exception. */ 154 /** Evaluates the given function and validates that it throws an exception. */
179 void expectThrow(function) { 155 void expectThrow(function) {
180 bool threw = false; 156 bool threw = false;
181 try { 157 try {
182 function(); 158 function();
183 } catch (var e) { 159 } catch (var e) {
184 threw = true; 160 threw = true;
185 } 161 }
186 Expect.equals(true, threw, 'Expected exception but none was thrown.'); 162 Expect.equals(true, threw, 'Expected exception but none was thrown.');
187 } 163 }
188 164
189 /** 165 /**
190 * Creates a new test case with the given description and body. The 166 * Creates a new test case with the given description and body. The
191 * description will include the descriptions of any surrounding group() 167 * description will include the descriptions of any surrounding group()
192 * calls. 168 * calls.
193 */ 169 */
194 void test(String spec, TestFunction body) { 170 void test(String spec, TestFunction body) {
195 ensureInitialized(); 171 _ensureInitialized();
196 172
197 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); 173 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0));
198 } 174 }
199 175
200 /** 176 /**
201 * Creates a new async test case with the given description and body. The 177 * Creates a new async test case with the given description and body. The
202 * description will include the descriptions of any surrounding group() 178 * description will include the descriptions of any surrounding group()
203 * calls. 179 * calls.
204 */ 180 */
205 // TODO(sigmund): deprecate this API 181 // TODO(sigmund): deprecate this API
206 void asyncTest(String spec, int callbacks, TestFunction body) { 182 void asyncTest(String spec, int callbacks, TestFunction body) {
207 ensureInitialized(); 183 _ensureInitialized();
208 184
209 final testCase = new TestCase( 185 final testCase = new TestCase(
210 _tests.length + 1, _fullSpec(spec), body, callbacks); 186 _tests.length + 1, _fullSpec(spec), body, callbacks);
211 _tests.add(testCase); 187 _tests.add(testCase);
212 188
213 if (callbacks < 1) { 189 if (callbacks < 1) {
214 testCase.error( 190 testCase.error(
215 'Async tests must wait for at least one callback ', ''); 191 'Async tests must wait for at least one callback ', '');
216 } 192 }
217 } 193 }
218 194
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
299 /** 195 /**
300 * Indicate that [callback] is expected to be called a [count] number of times 196 * Indicate to the unittest framework that a 0-argument callback is expected.
301 * (by default 1). The unittest framework will wait for the callback to run the 197 *
302 * specified [count] times before it continues with the following test. Using 198 * The framework will wait for the callback to run before it continues with the
303 * [_expectAsync] will also ensure that errors that occur within [callback] are 199 * following test. The callback must excute once and only once. Using [later]
304 * tracked and reported. [callback] should take between 0 and 4 positional 200 * will also ensure that errors that occur within the callback are tracked and
305 * arguments (named arguments are not supported here). 201 * reported by the unittest framework.
306 */ 202 */
307 Function _expectAsync(Function callback, [int count = 1]) { 203 // TODO(sigmund): expose this functionality
308 return new _SpreadArgsHelper(callback, count).invoke; 204 Function _later0(Function callback) {
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 };
309 } 233 }
310 234
311 /** 235 /**
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 /**
337 * Creates a new named group of tests. Calls to group() or test() within the 236 * Creates a new named group of tests. Calls to group() or test() within the
338 * body of the function passed to this will inherit this group's description. 237 * body of the function passed to this will inherit this group's description.
339 */ 238 */
340 void group(String description, void body()) { 239 void group(String description, void body()) {
341 ensureInitialized(); 240 _ensureInitialized();
342 241
343 // Concatenate the new group. 242 // Concatenate the new group.
344 final oldGroup = _currentGroup; 243 final oldGroup = _currentGroup;
345 if (_currentGroup != '') { 244 if (_currentGroup != '') {
346 // Add a space. 245 // Add a space.
347 _currentGroup = '$_currentGroup $description'; 246 _currentGroup = '$_currentGroup $description';
348 } else { 247 } else {
349 // The first group. 248 // The first group.
350 _currentGroup = description; 249 _currentGroup = description;
351 } 250 }
352 251
353 try { 252 try {
354 body(); 253 body();
355 } finally { 254 } finally {
356 // Now that the group is over, restore the previous one. 255 // Now that the group is over, restore the previous one.
357 _currentGroup = oldGroup; 256 _currentGroup = oldGroup;
358 } 257 }
359 } 258 }
360 259
361 /** Called by subclasses to indicate that an asynchronous test completed. */ 260 /** Called by subclasses to indicate that an asynchronous test completed. */
362 void callbackDone() { 261 void callbackDone() {
363 _callbacksCalled++; 262 _callbacksCalled++;
364 if (_currentTest < _tests.length) { 263 final testCase = _tests[_currentTest];
365 final testCase = _tests[_currentTest]; 264 if (_callbacksCalled > testCase.callbacks) {
366 if (_callbacksCalled > testCase.callbacks) { 265 final expected = testCase.callbacks;
367 final expected = testCase.callbacks; 266 testCase.error(
368 testCase.error( 267 'More calls to callbackDone() than expected. '
369 'More calls to callbackDone() than expected. ' 268 'Actual: ${_callbacksCalled}, expected: ${expected}', '');
370 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); 269 _state = _UNCAUGHT_ERROR;
371 _state = _UNCAUGHT_ERROR; 270 } else if ((_callbacksCalled == testCase.callbacks) &&
372 } else if ((_callbacksCalled == testCase.callbacks) && 271 (_state != _RUNNING_TEST)) {
373 (_state != _RUNNING_TEST)) { 272 if (testCase.result == null) testCase.pass();
374 if (testCase.result == null) testCase.pass(); 273 _currentTest++;
375 _currentTest++; 274 _testRunner();
376 _testRunner();
377 }
378 } 275 }
379 } 276 }
380 277
381 /** Menchanism to notify that an error was caught outside of this library. */ 278 void notifyError(String msg, String trace) {
382 void reportTestError(String msg, String trace) {
383 if (_currentTest < _tests.length) { 279 if (_currentTest < _tests.length) {
384 final testCase = _tests[_currentTest]; 280 final testCase = _tests[_currentTest];
385 testCase.error(msg, trace); 281 testCase.error(msg, trace);
386 _state = _UNCAUGHT_ERROR; 282 _state = _UNCAUGHT_ERROR;
387 if (testCase.callbacks > 0) { 283 if (testCase.callbacks > 0) {
388 _currentTest++; 284 _currentTest++;
389 _testRunner(); 285 _testRunner();
390 } 286 }
391 } else {
392 _uncaughtErrorMessage = "$msg: $trace";
393 } 287 }
394 } 288 }
395 289
396 /** Runs [callback] at the end of the event loop. */ 290 /** Runs [callback] at the end of the event loop. */
397 _defer(void callback()) { 291 _defer(void callback()) {
398 // Exploit isolate ports as a platform-independent mechanism to queue a 292 // Exploit isolate ports as a platform-independent mechanism to queue a
399 // message at the end of the event loop. 293 // message at the end of the event loop.
400 // TODO(sigmund): expose this functionality somewhere in our libraries. 294 // TODO(sigmund): expose this functionality somewhere in our libraries.
401 final port = new ReceivePort(); 295 final port = new ReceivePort();
402 port.receive((msg, reply) { 296 port.receive((msg, reply) {
(...skipping 11 matching lines...) Expand all
414 assert (_currentTest == 0); 308 assert (_currentTest == 0);
415 _testRunner(); 309 _testRunner();
416 }); 310 });
417 } 311 }
418 312
419 /** 313 /**
420 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, update 314 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, update
421 * the [_currentTest] status accordingly. 315 * the [_currentTest] status accordingly.
422 */ 316 */
423 _guard(tryBody, [finallyBody]) { 317 _guard(tryBody, [finallyBody]) {
318 final testCase = _tests[_currentTest];
424 try { 319 try {
425 return tryBody(); 320 tryBody();
426 } catch (ExpectException e, var trace) { 321 } catch (ExpectException e, var trace) {
427 if (_state != _UNCAUGHT_ERROR) { 322 if (_state != _UNCAUGHT_ERROR) {
428 _tests[_currentTest].fail(e.message, trace.toString()); 323 //TODO(pquitslund) remove guard once dartc reliably propagates traces
324 testCase.fail(e.message, trace == null ? '' : trace.toString());
429 } 325 }
430 } catch (var e, var trace) { 326 } catch (var e, var trace) {
431 if (_state != _UNCAUGHT_ERROR) { 327 if (_state != _UNCAUGHT_ERROR) {
432 _tests[_currentTest].error('Caught $e', trace.toString()); 328 //TODO(pquitslund) remove guard once dartc reliably propagates traces
329 testCase.error('Caught ${e}', trace == null ? '' : trace.toString());
433 } 330 }
434 } finally { 331 } finally {
435 _state = _READY; 332 _state = _READY;
436 if (finallyBody != null) finallyBody(); 333 if (finallyBody != null) finallyBody();
437 } 334 }
438 } 335 }
439 336
440 /** 337 /**
441 * Runs a batch of tests, yielding whenever an asynchronous test starts 338 * Runs a batch of tests, yielding whenever an asynchronous test starts
442 * running. Tests will resume executing when such asynchronous test calls 339 * running. Tests will resume executing when such asynchronous test calls
443 * [done] or if it fails with an exception. 340 * [done] or if it fails with an exception.
444 */ 341 */
445 _nextBatch() { 342 _nextBatch() {
446 while (_currentTest < _tests.length) { 343 while (_currentTest < _tests.length) {
447 final testCase = _tests[_currentTest]; 344 final testCase = _tests[_currentTest];
345
448 _guard(() { 346 _guard(() {
449 _callbacksCalled = 0; 347 _callbacksCalled = 0;
450 _state = _RUNNING_TEST; 348 _state = _RUNNING_TEST;
451 349
452 testCase.test(); 350 testCase.test();
453 351
454 if (_state != _UNCAUGHT_ERROR) { 352 if (_state != _UNCAUGHT_ERROR) {
455 if (testCase.callbacks == _callbacksCalled) { 353 if (testCase.callbacks == _callbacksCalled) {
456 testCase.pass(); 354 testCase.pass();
457 } 355 }
(...skipping 17 matching lines...) Expand all
475 int testsErrors_ = 0; 373 int testsErrors_ = 0;
476 374
477 for (TestCase t in _tests) { 375 for (TestCase t in _tests) {
478 switch (t.result) { 376 switch (t.result) {
479 case _PASS: testsPassed_++; break; 377 case _PASS: testsPassed_++; break;
480 case _FAIL: testsFailed_++; break; 378 case _FAIL: testsFailed_++; break;
481 case _ERROR: testsErrors_++; break; 379 case _ERROR: testsErrors_++; break;
482 } 380 }
483 } 381 }
484 382
485 _config.onDone(testsPassed_, testsFailed_, testsErrors_, _tests, 383 _config.onDone(testsPassed_, testsFailed_, testsErrors_, _tests);
486 _uncaughtErrorMessage);
487 } 384 }
488 385
489 String _fullSpec(String spec) { 386 String _fullSpec(String spec) {
490 if (spec === null) return '$_currentGroup'; 387 if (spec === null) return '$_currentGroup';
491 return _currentGroup != '' ? '$_currentGroup $spec' : spec; 388 return _currentGroup != '' ? '$_currentGroup $spec' : spec;
492 } 389 }
493 390
494 /** 391 /**
495 * Lazily initializes the test library if not already initialized. 392 * Lazily initializes the test library if not already initialized.
496 */ 393 */
497 ensureInitialized() { 394 _ensureInitialized() {
498 if (_state != _UNINITIALIZED) return; 395 if (_state != _UNINITIALIZED) return;
499 396
500 _tests = <TestCase>[]; 397 _tests = <TestCase>[];
501 _currentGroup = ''; 398 _currentGroup = '';
502 _state = _READY; 399 _state = _READY;
503 _testRunner = _nextBatch; 400 _testRunner = _nextBatch;
504 401
505 if (_config == null) { 402 if (_config == null) {
506 _config = new Configuration(); 403 _config = new Configuration();
507 } 404 }
508 _config.onInit(); 405 _config.onInit();
509 406
510 // Immediately queue the suite up. It will run after a timeout (i.e. after 407 // Immediately queue the suite up. It will run after a timeout (i.e. after
511 // main() has returned). 408 // main() has returned).
512 _defer(_runTests); 409 _defer(_runTests);
513 } 410 }
514 411
515 /** Signature for a test function. */ 412 /** Signature for a test function. */
516 typedef void TestFunction(); 413 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