OLD | NEW |
---|---|
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 * This file is sourced from unittest_html, unittest_dom, and unittest_vm. | 6 * A library for writing dart unit tests. |
7 * These libraries shold also source 'config.dart' and should define a class | 7 * |
8 * called [PlatformConfiguration] that implements [Configuration]. | 8 * **Concepts** |
Bob Nystrom
2012/04/12 16:45:49
Use Markdown, one of:
## Concepts
## Concepts ##
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
Done.
| |
9 * | |
10 * * Tests: Tests are specified via the top-level function [test], they can be | |
11 * organized together using [group]. | |
12 * * Checks: Test expectations can be specified via [expect] (see methods in | |
13 * [Expectation]), [expectThrows], or using assertions with the [Expect] | |
14 * class. | |
15 * * Configuration: The framework can be adapted by calling [configure] with a | |
16 * configuration. Common configurations can be found in this package under: | |
17 * 'dom\_config.dart', 'html\_config.dart', and 'vm\_config.dart'. | |
18 * | |
19 * **Examples** | |
20 * | |
21 * A trivial test: | |
22 * #import('path-to-dart/lib/unittest/unitest.dart'); | |
Bob Nystrom
2012/04/12 16:45:49
Need blank line above this to separate out code bl
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
Ok, it didn't seem to need it when I created the p
Bob Nystrom
2012/04/12 18:01:37
Hmm, yeah I think dartdoc will allow it but most m
| |
23 * main() { | |
24 * test('this is a test', () { | |
25 * int x = 2 + 3; | |
26 * expect(x).equals(5); | |
27 * }); | |
28 * } | |
29 * | |
30 * Multiple tests: | |
31 * #import('path-to-dart/lib/unittest/unitest.dart'); | |
32 * main() { | |
33 * test('this is a test', () { | |
34 * int x = 2 + 3; | |
35 * expect(x).equals(5); | |
36 * }); | |
37 * test('this is another test', () { | |
38 * int x = 2 + 3; | |
39 * expect(x).equals(5); | |
40 * }); | |
41 * } | |
42 * | |
43 * Multiple tests, grouped by category: | |
44 * #import('path-to-dart/lib/unittest/unitest.dart'); | |
45 * main() { | |
46 * group('group A', () { | |
47 * test('test A.1', () { | |
48 * int x = 2 + 3; | |
49 * expect(x).equals(5); | |
50 * }); | |
51 * test('test A.2', () { | |
52 * int x = 2 + 3; | |
53 * expect(x).equals(5); | |
54 * }); | |
55 * }); | |
56 * group('group B', () { | |
57 * test('this B.1', () { | |
58 * int x = 2 + 3; | |
59 * expect(x).equals(5); | |
60 * }); | |
61 * }); | |
62 * } | |
63 * | |
64 * Asynchronous tests: under the current API (soon to be deprecated): | |
65 * #import('path-to-dart/lib/unittest/unitest.dart'); | |
66 * #import('dart:dom'); | |
67 * main() { | |
68 * // use [asyncTest], indicate the expected number of callbacks: | |
69 * asyncTest('this is a test', 1, () { | |
70 * window.setTimeout(() { | |
71 * int x = 2 + 3; | |
72 * expect(x).equals(5); | |
73 * // invoke [callbackDone] at the end of the callback. | |
74 * callbackDone(); | |
75 * }, 0); | |
76 * }); | |
77 * } | |
78 * | |
79 * We plan to replace this with a different API, one API we are considering is: | |
80 * #import('path-to-dart/lib/unittest/unitest.dart'); | |
81 * #import('dart:dom'); | |
82 * main() { | |
83 * test('this is a test', () { | |
84 * // wrap the callback of an asynchronous call with [later] | |
85 * window.setTimeout(later(() { | |
86 * int x = 2 + 3; | |
87 * expect(x).equals(5); | |
88 * }), 0); | |
89 * }); | |
90 * } | |
Bob Nystrom
2012/04/12 16:45:49
This documentation is excellent!
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
Thanks!
Emily Fortuna
2012/04/12 17:48:42
+1
| |
9 */ | 91 */ |
92 #library('unittest'); | |
93 | |
94 #import('dart:isolate'); | |
95 | |
96 #source('config.dart'); | |
97 #source('expectation.dart'); | |
98 #source('testcase.dart'); | |
Bob Nystrom
2012/04/12 16:45:49
"test_case"
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
Done.
| |
10 | 99 |
11 /** [Configuration] used by the unittest library. */ | 100 /** [Configuration] used by the unittest library. */ |
12 Configuration _config = null; | 101 Configuration _config = null; |
13 | 102 |
103 /** Set the [Configuration] used by the unittest library. */ | |
104 void configure(Configuration c) { | |
Bob Nystrom
2012/04/12 16:45:49
"c" -> "config".
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
Done.
| |
105 _config = c; | |
106 } | |
107 | |
14 /** | 108 /** |
15 * Description text of the current test group. If multiple groups are nested, | 109 * Description text of the current test group. If multiple groups are nested, |
16 * this will contain all of their text concatenated. | 110 * this will contain all of their text concatenated. |
17 */ | 111 */ |
18 String _currentGroup = ''; | 112 String _currentGroup = ''; |
19 | 113 |
20 /** Tests executed in this suite. */ | 114 /** Tests executed in this suite. */ |
21 List<TestCase> _tests; | 115 List<TestCase> _tests; |
22 | 116 |
23 /** | 117 /** |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
72 _ensureInitialized(); | 166 _ensureInitialized(); |
73 | 167 |
74 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); | 168 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); |
75 } | 169 } |
76 | 170 |
77 /** | 171 /** |
78 * Creates a new async test case with the given description and body. The | 172 * Creates a new async test case with the given description and body. The |
79 * description will include the descriptions of any surrounding group() | 173 * description will include the descriptions of any surrounding group() |
80 * calls. | 174 * calls. |
81 */ | 175 */ |
176 // TODO(sigmund): deprecate this API | |
82 void asyncTest(String spec, int callbacks, TestFunction body) { | 177 void asyncTest(String spec, int callbacks, TestFunction body) { |
83 _ensureInitialized(); | 178 _ensureInitialized(); |
84 | 179 |
85 final testCase = new TestCase( | 180 final testCase = new TestCase( |
86 _tests.length + 1, _fullSpec(spec), body, callbacks); | 181 _tests.length + 1, _fullSpec(spec), body, callbacks); |
87 _tests.add(testCase); | 182 _tests.add(testCase); |
88 | 183 |
89 if (callbacks < 1) { | 184 if (callbacks < 1) { |
90 testCase.error( | 185 testCase.error( |
91 'Async tests must wait for at least one callback ', ''); | 186 'Async tests must wait for at least one callback ', ''); |
92 } | 187 } |
93 } | 188 } |
94 | 189 |
190 class _SentinelType { | |
Bob Nystrom
2012/04/12 16:45:49
I'd just do "_Sentinel".
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
Done.
| |
191 const _SentinelType(); | |
192 } | |
193 | |
194 final _SentinelType _sentinel = const _SentinelType(); | |
Bob Nystrom
2012/04/12 16:45:49
Move this into the class and rename, like:
class
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
cool, done
Emily Fortuna
2012/04/12 17:48:42
+1
| |
195 | |
196 /** | |
197 * Indicate to the unittest framework that a callback is expected. [callback] | |
198 * can take any number of arguments between 0 and 4. | |
199 * | |
200 * The framework will wait for the callback to run before it continues with the | |
201 * following test. The callback must excute once and only once. Using [later] | |
202 * will also ensure that errors that occur within the callback are tracked and | |
203 * reported by the unittest framework. | |
204 */ | |
205 // TODO(sigmund): expose this functionality | |
206 Function _later(Function callback) { | |
Emily Fortuna
2012/04/12 17:48:42
Perhaps I missed it, but where is this function ca
Siggi Cherem (dart-lang)
2012/04/12 21:13:36
it is not yet (see note below about step 3 :-))
| |
207 Expect.isTrue(_currentTest < _tests.length); | |
208 var testCase = _tests[_currentTest]; | |
209 testCase.callbacks++; | |
210 // We simulate spread arguments using named arguments: | |
211 // Note: this works in the vm and dart2js, but not in frog. | |
212 return ([arg0 = _sentinel, arg1 = _sentinel, arg2 = _sentinel, | |
213 arg3 = _sentinel, arg4 = _sentinel]) { | |
214 _guard(testCase, () { | |
215 if (arg0 == _sentinel) { | |
216 callback(); | |
217 } else if (arg1 == _sentinel) { | |
218 callback(arg0); | |
219 } else if (arg3 == _sentinel) { | |
Emily Fortuna
2012/04/12 17:48:42
arg2
Siggi Cherem (dart-lang)
2012/04/12 21:13:36
nice catch
| |
220 callback(arg0, arg1); | |
221 } else if (arg3 == _sentinel) { | |
222 callback(arg0, arg1, arg2); | |
223 } else if (arg4 == _sentinel) { | |
224 callback(arg0, arg1, arg2, arg3); | |
225 } else { | |
226 testCase.error( | |
227 'unittest lib does not support callbacks with more than 4 arguments', | |
228 ''); | |
229 _state = _UNCAUGHT_ERROR; | |
230 } | |
231 }, callbackDone); | |
232 }; | |
233 } | |
234 | |
235 // TODO(sigmund): expose this functionality | |
236 Function _later0(Function callback) { | |
237 Expect.isTrue(_currentTest < _tests.length); | |
238 var testCase = _tests[_currentTest]; | |
239 testCase.callbacks++; | |
240 return () { | |
241 _guard(testCase, () => callback(), callbackDone); | |
242 }; | |
243 } | |
244 | |
245 // TODO(sigmund): expose this functionality | |
246 Function _later1(Function callback) { | |
247 Expect.isTrue(_currentTest < _tests.length); | |
248 var testCase = _tests[_currentTest]; | |
249 testCase.callbacks++; | |
250 return (arg0) { | |
251 _guard(testCase, () => callback(arg0), callbackDone); | |
252 }; | |
253 } | |
254 | |
255 // TODO(sigmund): expose this functionality | |
256 Function _later2(Function callback) { | |
Siggi Cherem (dart-lang)
2012/04/12 01:13:53
this is something I'll need for "step 3" (see emai
Bob Nystrom
2012/04/12 16:45:49
Nope, it's fine. Thanks for clarifying.
| |
257 Expect.isTrue(_currentTest < _tests.length); | |
258 var testCase = _tests[_currentTest]; | |
259 testCase.callbacks++; | |
260 return (arg0, arg1) { | |
261 _guard(testCase, () => callback(arg0, arg1), callbackDone); | |
262 }; | |
263 } | |
264 | |
95 /** | 265 /** |
96 * Creates a new named group of tests. Calls to group() or test() within the | 266 * 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. | 267 * body of the function passed to this will inherit this group's description. |
98 */ | 268 */ |
99 void group(String description, void body()) { | 269 void group(String description, void body()) { |
100 _ensureInitialized(); | 270 _ensureInitialized(); |
101 | 271 |
102 // Concatenate the new group. | 272 // Concatenate the new group. |
103 final oldGroup = _currentGroup; | 273 final oldGroup = _currentGroup; |
104 if (_currentGroup != '') { | 274 if (_currentGroup != '') { |
105 // Add a space. | 275 // Add a space. |
106 _currentGroup = '$_currentGroup $description'; | 276 _currentGroup = '$_currentGroup $description'; |
107 } else { | 277 } else { |
108 // The first group. | 278 // The first group. |
109 _currentGroup = description; | 279 _currentGroup = description; |
110 } | 280 } |
111 | 281 |
112 try { | 282 try { |
113 body(); | 283 body(); |
114 } finally { | 284 } finally { |
115 // Now that the group is over, restore the previous one. | 285 // Now that the group is over, restore the previous one. |
116 _currentGroup = oldGroup; | 286 _currentGroup = oldGroup; |
117 } | 287 } |
118 } | 288 } |
119 | 289 |
120 /** Called by subclasses to indicate that an asynchronous test completed. */ | 290 /** Called by subclasses to indicate that an asynchronous test completed. */ |
121 void callbackDone() { | 291 void callbackDone() { |
122 _callbacksCalled++; | 292 _callbacksCalled++; |
123 final testCase = _tests[_currentTest]; | 293 final testCase = _tests[_currentTest]; |
124 if (testCase.callbacks == 0) { | 294 if (_callbacksCalled > testCase.callbacks) { |
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; | 295 final expected = testCase.callbacks; |
130 testCase.error( | 296 testCase.error( |
131 'More calls to callbackDone() than expected. ' | 297 'More calls to callbackDone() than expected. ' |
132 + 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); | 298 + 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); |
133 _state = _UNCAUGHT_ERROR; | 299 _state = _UNCAUGHT_ERROR; |
134 } else if ((_callbacksCalled == testCase.callbacks) && | 300 } else if ((_callbacksCalled == testCase.callbacks) && |
135 (_state != _RUNNING_TEST)) { | 301 (_state != _RUNNING_TEST)) { |
136 testCase.pass(); | 302 if (testCase.result == null) testCase.pass(); |
137 _currentTest++; | 303 _currentTest++; |
138 _testRunner(); | 304 _testRunner(); |
139 } | 305 } |
140 } | 306 } |
141 | 307 |
308 void notifyError(String msg, String trace) { | |
309 if (_currentTest < _tests.length) { | |
310 final testCase = _tests[_currentTest]; | |
311 testCase.error(msg, trace); | |
312 _state = _UNCAUGHT_ERROR; | |
313 if (testCase.callbacks > 0) { | |
314 _currentTest++; | |
315 _testRunner(); | |
316 } | |
317 } | |
318 } | |
319 | |
142 /** Runs [callback] at the end of the event loop. */ | 320 /** Runs [callback] at the end of the event loop. */ |
143 _defer(void callback()) { | 321 _defer(void callback()) { |
144 // Exploit isolate ports as a platform-independent mechanism to queue a | 322 // Exploit isolate ports as a platform-independent mechanism to queue a |
145 // message at the end of the event loop. | 323 // message at the end of the event loop. |
146 // TODO(sigmund): expose this functionality somewhere in our libraries. | 324 // TODO(sigmund): expose this functionality somewhere in our libraries. |
147 final port = new ReceivePort(); | 325 final port = new ReceivePort(); |
148 port.receive((msg, reply) { | 326 port.receive((msg, reply) { |
149 callback(); | 327 callback(); |
150 port.close(); | 328 port.close(); |
151 }); | 329 }); |
152 port.toSendPort().send(null, null); | 330 port.toSendPort().send(null, null); |
153 } | 331 } |
154 | 332 |
155 /** Runs all queued tests, one at a time. */ | 333 /** Runs all queued tests, one at a time. */ |
156 _runTests() { | 334 _runTests() { |
157 _config.onStart(); | 335 _config.onStart(); |
158 | 336 |
159 _defer(() { | 337 _defer(() { |
160 assert (_currentTest == 0); | 338 assert (_currentTest == 0); |
161 _testRunner(); | 339 _testRunner(); |
162 }); | 340 }); |
163 } | 341 } |
164 | 342 |
165 /** Runs a single test. */ | 343 /** Runs a single test. */ |
166 _runTest(TestCase testCase) { | 344 _runTest(TestCase testCase) { |
Bob Nystrom
2012/04/12 16:45:49
I don't see this being called anywhere, can it be
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
It's actually called by [nextBatch] below. I decid
| |
167 try { | 345 _guard(testCase, () { |
168 _callbacksCalled = 0; | 346 _callbacksCalled = 0; |
169 _state = _RUNNING_TEST; | 347 _state = _RUNNING_TEST; |
170 | 348 |
171 testCase.test(); | 349 testCase.test(); |
172 | 350 |
173 if (_state != _UNCAUGHT_ERROR) { | 351 if (_state != _UNCAUGHT_ERROR) { |
174 if (testCase.callbacks == _callbacksCalled) { | 352 if (testCase.callbacks == _callbacksCalled) { |
175 testCase.pass(); | 353 testCase.pass(); |
176 } | 354 } |
177 } | 355 } |
356 }); | |
357 } | |
178 | 358 |
359 _guard(TestCase testCase, tryBody, [finallyBody]) { | |
Bob Nystrom
2012/04/12 16:45:49
Nice. In every callsite I see, testCase is always
Siggi Cherem (dart-lang)
2012/04/12 17:36:28
Done.
| |
360 try { | |
361 tryBody(); | |
179 } catch (ExpectException e, var trace) { | 362 } catch (ExpectException e, var trace) { |
180 if (_state != _UNCAUGHT_ERROR) { | 363 if (_state != _UNCAUGHT_ERROR) { |
181 //TODO(pquitslund) remove guard once dartc reliably propagates traces | 364 //TODO(pquitslund) remove guard once dartc reliably propagates traces |
182 testCase.fail(e.message, trace == null ? '' : trace.toString()); | 365 testCase.fail(e.message, trace == null ? '' : trace.toString()); |
183 } | 366 } |
184 } catch (var e, var trace) { | 367 } catch (var e, var trace) { |
185 if (_state != _UNCAUGHT_ERROR) { | 368 if (_state != _UNCAUGHT_ERROR) { |
186 //TODO(pquitslund) remove guard once dartc reliably propagates traces | 369 //TODO(pquitslund) remove guard once dartc reliably propagates traces |
187 testCase.error('Caught ${e}', trace == null ? '' : trace.toString()); | 370 testCase.error('Caught ${e}', trace == null ? '' : trace.toString()); |
188 } | 371 } |
189 } finally { | 372 } finally { |
190 _state = _READY; | 373 _state = _READY; |
374 if (finallyBody != null) finallyBody(); | |
191 } | 375 } |
192 } | 376 } |
193 | 377 |
194 /** | 378 /** |
195 * Runs a batch of tests, yielding whenever an asynchronous test starts | 379 * Runs a batch of tests, yielding whenever an asynchronous test starts |
196 * running. Tests will resume executing when such asynchronous test calls | 380 * running. Tests will resume executing when such asynchronous test calls |
197 * [done] or if it fails with an exception. | 381 * [done] or if it fails with an exception. |
198 */ | 382 */ |
199 _nextBatch() { | 383 _nextBatch() { |
200 while (_currentTest < _tests.length) { | 384 while (_currentTest < _tests.length) { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
239 */ | 423 */ |
240 _ensureInitialized() { | 424 _ensureInitialized() { |
241 if (_state != _UNINITIALIZED) return; | 425 if (_state != _UNINITIALIZED) return; |
242 | 426 |
243 _tests = <TestCase>[]; | 427 _tests = <TestCase>[]; |
244 _currentGroup = ''; | 428 _currentGroup = ''; |
245 _state = _READY; | 429 _state = _READY; |
246 _testRunner = _nextBatch; | 430 _testRunner = _nextBatch; |
247 | 431 |
248 if (_config == null) { | 432 if (_config == null) { |
249 // TODO(sigmund): make this [new Configuration], set configuration | 433 _config = new Configuration(); |
250 // for each platform in test.dart | |
251 _config = new PlatformConfiguration(); | |
252 } | 434 } |
253 _config.onInit(); | 435 _config.onInit(); |
254 | 436 |
255 // Immediately queue the suite up. It will run after a timeout (i.e. after | 437 // Immediately queue the suite up. It will run after a timeout (i.e. after |
256 // main() has returned). | 438 // main() has returned). |
257 _defer(_runTests); | 439 _defer(_runTests); |
258 } | 440 } |
259 | 441 |
260 /** | 442 /** Signature for a test function. */ |
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(); | 443 typedef void TestFunction(); |
OLD | NEW |