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

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

Issue 10037027: unittest step2: bye bye to multiple entrypoints for unittest (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/test_case.dart » ('j') | 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 * 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();
OLDNEW
« no previous file with comments | « lib/unittest/html_print.dart ('k') | lib/unittest/test_case.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698