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

Side by Side Diff: client/testing/unittest/unittestsuite.dart

Issue 8413058: Add command-line non-client support to unit test lib. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rename a couple of methods. Created 9 years, 1 month 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 | « client/testing/unittest/unittest_vm.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 * Description text of the current test group. If multiple groups are nested,
7 * this will contain all of their text concatenated.
8 */
9 String _currentGroup = '';
10
11 /** Tests executed in this suite. */
12 List<TestCase> _tests;
13
14 /** Whether this is run within dartium layout tests. */
15 bool _isLayoutTest = false;
16
17 /** Current test being executed. */
18 int _currentTest = 0;
19
20 /** Total number of callbacks that have been executed in the current test. */
21 int _callbacksCalled = 0;
22
23 // TODO(rnystrom): Get rid of this if we get canonical closures for methods.
24 EventListener _onErrorClosure;
25
26 final _UNINITIALIZED = 0;
27 final _READY = 1;
28 final _RUNNING_TEST = 2;
29
30 /**
31 * Whether an undetected error occurred while running the last test. These
32 * errors are commonly caused by DOM callbacks that were not guarded in a
33 * try-catch block.
34 */
35 final _UNCAUGHT_ERROR = 3;
36
37 int _state = _UNINITIALIZED;
38
39 /** Creates an expectation for the given value. */
40 Expectation expect(value) => new Expectation(value);
41
42 /** Evaluates the given function and validates that it throws an exception. */
43 void expectThrow(function) {
44 bool threw = false;
45 try {
46 function();
47 } catch (var e) {
48 threw = true;
49 }
50 Expect.equals(true, threw, 'Expected exception but none was thrown.');
51 }
52
53 /**
54 * Creates a new test case with the given description and body. The
55 * description will include the descriptions of any surrounding group()
56 * calls.
57 */
58 void test(String spec, TestFunction body) {
59 _ensureInitialized();
60
61 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0));
62 }
63
64 /**
65 * Creates a new async test case with the given description and body. The
66 * description will include the descriptions of any surrounding group()
67 * calls.
68 */
69 void asyncTest(String spec, int callbacks, TestFunction body) {
70 _ensureInitialized();
71
72 final testCase = new TestCase(
73 _tests.length + 1, _fullSpec(spec), body, callbacks);
74 _tests.add(testCase);
75
76 if (callbacks < 1) {
77 testCase.recordError(
78 'Async tests must wait for at least one callback ', '');
79 }
80 }
81
82 /**
83 * Creates a new named group of tests. Calls to group() or test() within the
84 * body of the function passed to this will inherit this group's description.
85 */
86 void group(String description, void body()) {
87 _ensureInitialized();
88
89 // Concatenate the new group.
90 final oldGroup = _currentGroup;
91 if (_currentGroup != '') {
92 // Add a space.
93 _currentGroup = '$_currentGroup $description';
94 } else {
95 // The first group.
96 _currentGroup = description;
97 }
98
99 try {
100 body();
101 } finally {
102 // Now that the group is over, restore the previous one.
103 _currentGroup = oldGroup;
104 }
105 }
106
107 /** Called by subclasses to indicate that an asynchronous test completed. */
108 void callbackDone() {
109 _callbacksCalled++;
110 final testCase = _tests[_currentTest];
111 if (testCase.callbacks == 0) {
112 testCase.recordError(
113 "Can't call callbackDone() on a synchronous test", '');
114 _state = _UNCAUGHT_ERROR;
115 } else if (_callbacksCalled > testCase.callbacks) {
116 final expected = testCase.callbacks;
117 testCase.recordError(
118 'More calls to callbackDone() than expected. '
119 + 'Actual: ${_callbacksCalled}, expected: ${expected}', '');
120 _state = _UNCAUGHT_ERROR;
121 } else if ((_callbacksCalled == testCase.callbacks) &&
122 (_state != _RUNNING_TEST)) {
123 testCase.recordSuccess();
124 _currentTest++;
125 _nextBatch();
126 }
127 }
128
129 void forLayoutTests() {
130 _isLayoutTest = true;
131 }
132
133 /** Runs all queued tests, one at a time. */
134 _runTests() {
135 window.postMessage('unittest-suite-start', '*');
136 window.setTimeout(() {
137 assert (_currentTest == 0);
138
139 // Listen for uncaught errors.
140 window.onerror = _onErrorClosure;
141 _nextBatch();
142 }, 0);
143 }
144
145 /** Runs a single test. */
146 _runTest(TestCase testCase) {
147 try {
148 // TODO(sigmund): remove this declaration once dartc supports trapping error
149 // traces.
150 var trace = '';
151 _callbacksCalled = 0;
152 _state = _RUNNING_TEST;
153
154 testCase.test();
155
156 if (_state != _UNCAUGHT_ERROR) {
157 if (testCase.callbacks == _callbacksCalled) {
158 testCase.recordSuccess();
159 }
160 }
161 } catch (ExpectException e, var trace) {
162 if (_state != _UNCAUGHT_ERROR) {
163 testCase.recordFail(e.message, trace.toString());
164 }
165 } catch (var e, var trace) {
166 if (_state != _UNCAUGHT_ERROR) {
167 testCase.recordError('Caught ${e}', trace.toString());
168 }
169 } finally {
170 _state = _READY;
171 }
172 }
173
174 /**
175 * Runs a batch of tests, yielding whenever an asynchronous test starts
176 * running. Tests will resume executing when such asynchronous test calls
177 * [done] or if it fails with an exception.
178 */
179 _nextBatch() {
180 while (_currentTest < _tests.length) {
181 final testCase = _tests[_currentTest];
182
183 _runTest(testCase);
184
185 if (!testCase.isComplete && testCase.callbacks > 0) return;
186
187 _currentTest++;
188 }
189
190 _completeTests();
191 }
192
193 /** Publish results on the page and notify controller. */
194 _completeTests() {
195 window.onerror = null;
196
197 _state = _UNINITIALIZED;
198
199 int testsFailed = 0;
200 int testsErrors = 0;
201 int testsPassed = 0;
202
203 for (TestCase t in _tests) {
204 if (t.success) testsPassed++;
205 if (t.fail) testsFailed++;
206 if (t.error) testsErrors++;
207 }
208
209 if (_isLayoutTest && testsPassed == _tests.length) {
210 document.body.innerHTML = "PASS";
211 } else {
212 var newBody = new StringBuffer();
213 newBody.add("<table class='unittest-table'><tbody>");
214 newBody.add(testsPassed == _tests.length
215 ? "<tr><td colspan='3' class='unittest-pass'>PASS</td></tr>"
216 : "<tr><td colspan='3' class='unittest-fail'>FAIL</td></tr>");
217
218 for (final test in _tests) {
219 newBody.add(test.message);
220 }
221
222 if (testsPassed == _tests.length) {
223 newBody.add("<tr><td colspan='3' class='unittest-pass'>All "
224 + testsPassed + " tests passed</td></tr>");
225 } else {
226 newBody.add("""
227 <tr><td colspan='3'>Total
228 <span class='unittest-pass'>${testsPassed} passed</span>,
229 <span class='unittest-fail'>${testsFailed} failed</span>
230 <span class='unittest-error'>${testsErrors} errors</span>
231 </td></tr>""");
232 }
233 newBody.add("</tbody></table>");
234 document.body.innerHTML = newBody.toString();
235 }
236
237 window.postMessage('unittest-suite-done', '*');
238 }
239
240 void _onError(e) {
241 if (_currentTest < _tests.length) {
242 final testCase = _tests[_currentTest];
243 // TODO(vsm): figure out how to expose the stack trace here
244 // Currently e.message works in dartium, but not in dartc.
245 testCase.recordError('(DOM callback has errors) Caught ${e}', '');
246 _state = _UNCAUGHT_ERROR;
247 if (testCase.callbacks > 0) {
248 _currentTest++;
249 _nextBatch();
250 }
251 }
252 }
253
254 String _fullSpec(String spec) {
255 if (spec === null) return '$_currentGroup';
256 return _currentGroup != '' ? '$_currentGroup $spec' : spec;
257 }
258
259 /**
260 * Lazily initializes the test library if not already initialized.
261 */
262 _ensureInitialized() {
263 if (_state != _UNINITIALIZED) return;
264
265 _tests = <TestCase>[];
266 _onErrorClosure = (e) { _onError(e); };
267
268 // Immediately queue the suite up. It will run after a timeout (i.e. after
269 // main() has returned).
270 listener() {
271 _currentGroup = '';
272 _runTests();
273 };
274
275 window.setTimeout(listener, 0);
276
277 _state = _READY;
278 }
279
280 /**
281 * Wraps an value and provides an "==" operator that can be used to verify that
282 * the value matches a given expectation.
283 */
284 class Expectation {
285 final _value;
286
287 Expectation(this._value);
288
289 /** Asserts that the value is equivalent to [expected]. */
290 void equals(expected) {
291 Expect.equals(expected, _value);
292 }
293
294 /**
295 * Asserts that the difference between [expected] and the value is within
296 * [tolerance]. If no tolerance is given, it is assumed to be the value 4
297 * significant digits smaller than the expected value.
298 */
299 void approxEquals(num expected,
300 [num tolerance = null, String reason = null]) {
301 Expect.approxEquals(expected, _value, tolerance: tolerance, reason: reason);
302 }
303
304 /** Asserts that the value is [null]. */
305 void isNull() {
306 Expect.equals(null, _value);
307 }
308
309 /** Asserts that the value is not [null]. */
310 void isNotNull() {
311 Expect.notEquals(null, _value);
312 }
313
314 /** Asserts that the value is [true]. */
315 void isTrue() {
316 Expect.equals(true, _value);
317 }
318
319 /** Asserts that the value is [false]. */
320 void isFalse() {
321 Expect.equals(false, _value);
322 }
323
324 /** Asserts that the value has the same elements as [expected]. */
325 void equalsCollection(Collection expected) {
326 Expect.listEquals(expected, _value);
327 }
328
329 /**
330 * Checks that every element of [expected] is also in [actual], and that
331 * every element of [actual] is also in [expected].
332 */
333 void equalsSet(Iterable expected) {
334 Expect.setEquals(expected, _value);
335 }
336 }
337
338 /** Summarizes information about a single test case. */
339 class TestCase {
340 /** Identifier for this test. */
341 final id;
342
343 /** A description of what the test is specifying. */
344 final String description;
345
346 /** The body of the test case. */
347 final TestFunction test;
348
349 /** Total number of callbacks to wait for before the test completes. */
350 int callbacks;
351
352 /** Whether this test case was succesful. */
353 bool success;
354
355 /** Whether an Expect call failed in this test. */
356 bool fail;
357
358 /** Whether this test case had a runtime error. */
359 bool error;
360
361 /** Messages to display at the end of the test run. */
362 String message;
363
364 TestCase(this.id, this.description, this.test, this.callbacks)
365 : success = false,
366 fail = false,
367 error = false {
368 message = '''<tr>
369 <td>${id}</td>
370 <td class="unittest-error">NO STATUS</td>
371 <td>Test did not complete</td>
372 </tr>''';
373 }
374
375 bool get isComplete() => success || fail || error;
376
377 void recordSuccess() {
378 _setMessage('pass', '', null);
379 success = true;
380 }
381
382 void recordError(String msg, String stackTrace) {
383 _setMessage('error', msg, stackTrace);
384 error = true;
385 }
386
387 void recordFail(String msg, String stackTrace) {
388 _setMessage('fail', msg, stackTrace);
389 fail = true;
390 }
391
392 void _setMessage(String type, String msg, String stackTrace) {
393 message =
394 '''
395 <tr>
396 <td>${id}</td>
397 <td class="unittest-$type">${type.toUpperCase()}</td>
398 <td>Expectation: $description. $msg</td>
399 </tr>
400 ''';
401
402 if (stackTrace != null) {
403 message +=
404 '<tr><td></td><td colspan="2"><pre>${stackTrace}</pre></td></tr>';
405 }
406 }
407 }
408
409 typedef void TestFunction();
OLDNEW
« no previous file with comments | « client/testing/unittest/unittest_vm.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698