6
6
/// unittest/test API used by the language and core library.
7
7
///
8
8
/// Compared with `minitest.dart` , this library supports and expects
9
- /// asynchronous tests. It uses a `Zone` per test to collect errors in the
10
- /// correct test.
9
+ /// asynchronous tests. It uses a `Zone` per test to associate a test name with
10
+ /// the failure.
11
+ ///
11
12
/// It does not support `setUp` or `tearDown` methods,
12
13
/// and matchers are severely restricted.
13
14
///
@@ -42,17 +43,18 @@ void group(String name, body()) {
42
43
43
44
void test (String name, body ()) {
44
45
var oldName = _pushName (name);
45
- var test = new _Test (_currentName).. asyncWait ();
46
- var result =
47
- runZoned (body, zoneValues: {_testToken: test}, onError : test.fail );
46
+
47
+ asyncStart ();
48
+ var result = runZoned (body, zoneValues: {_testToken: _currentName} );
48
49
if (result is Future ) {
49
50
result.then ((_) {
50
- test. asyncDone ();
51
- }, onError : test.fail );
51
+ asyncEnd ();
52
+ });
52
53
} else {
53
54
// Ensure all tests get to be set up first.
54
- scheduleMicrotask (test.asyncDone );
55
+ scheduleMicrotask (asyncEnd );
55
56
}
57
+
56
58
_popName (oldName);
57
59
}
58
60
@@ -69,85 +71,84 @@ void expect(dynamic value, dynamic matcher, {String reason}) {
69
71
}
70
72
71
73
R Function () expectAsync0 <R >(R Function () f, {int count = 1 }) {
72
- var test = _currentTest.. asyncWait (count);
74
+ asyncStart (count);
73
75
return () {
74
76
var result = f ();
75
- test. asyncDone ();
77
+ asyncEnd ();
76
78
return result;
77
79
};
78
80
}
79
81
80
82
R Function (A ) expectAsync1 <R , A >(R Function (A ) f, {int count = 1 }) {
81
- var test = _currentTest.. asyncWait (count);
83
+ asyncStart (count);
82
84
return (A a) {
83
85
var result = f (a);
84
- test. asyncDone ();
86
+ asyncEnd ();
85
87
return result;
86
88
};
87
89
}
88
90
89
91
R Function (A , B ) expectAsync2 <R , A , B >(R Function (A , B ) f, {int count = 1 }) {
90
- var test = _currentTest.. asyncWait (count);
92
+ asyncStart (count);
91
93
return (A a, B b) {
92
94
var result = f (a, b);
93
- test. asyncDone ();
95
+ asyncEnd ();
94
96
return result;
95
97
};
96
98
}
97
99
98
100
dynamic expectAsync (Function f, {int count = 1 }) {
99
101
var f2 = f; // Avoid type-promoting f, we want dynamic invocations.
100
- var test = _currentTest;
101
102
if (f2 is Function (Null , Null , Null , Null , Null )) {
102
- test. asyncWait (count);
103
+ asyncStart (count);
103
104
return ([a, b, c, d, e]) {
104
105
var result = f (a, b, c, d, e);
105
- test. asyncDone ();
106
+ asyncEnd ();
106
107
return result;
107
108
};
108
109
}
109
110
if (f2 is Function (Null , Null , Null , Null )) {
110
- test. asyncWait (count);
111
+ asyncStart (count);
111
112
return ([a, b, c, d]) {
112
113
var result = f (a, b, c, d);
113
- test. asyncDone ();
114
+ asyncEnd ();
114
115
return result;
115
116
};
116
117
}
117
118
if (f2 is Function (Null , Null , Null )) {
118
- test. asyncWait (count);
119
+ asyncStart (count);
119
120
return ([a, b, c]) {
120
121
var result = f (a, b, c);
121
- test. asyncDone ();
122
+ asyncEnd ();
122
123
return result;
123
124
};
124
125
}
125
126
if (f2 is Function (Null , Null )) {
126
- test. asyncWait (count);
127
+ asyncStart (count);
127
128
return ([a, b]) {
128
129
var result = f (a, b);
129
- test. asyncDone ();
130
+ asyncEnd ();
130
131
return result;
131
132
};
132
133
}
133
134
if (f2 is Function (Null )) {
134
- test. asyncWait (count);
135
+ asyncStart (count);
135
136
return ([a]) {
136
137
var result = f (a);
137
- test. asyncDone ();
138
+ asyncEnd ();
138
139
return result;
139
140
};
140
141
}
141
142
if (f2 is Function ()) {
142
- test. asyncWait (count);
143
+ asyncStart (count);
143
144
return () {
144
145
var result = f ();
145
- test. asyncDone ();
146
+ asyncEnd ();
146
147
return result;
147
148
};
148
149
}
149
150
throw new UnsupportedError (
150
- "expectAsync only accepts up to five arguemnt functions" );
151
+ "expectAsync only accepts up to five argument functions" );
151
152
}
152
153
153
154
// Matchers
@@ -212,13 +213,13 @@ bool isStateError(dynamic o) {
212
213
213
214
void _checkThrow <T >(dynamic v, void onError (error)) {
214
215
if (v is Future ) {
215
- var test = _currentTest.. asyncWait ();
216
+ asyncStart ();
216
217
v.then ((_) {
217
218
Expect .fail ("Did not throw" );
218
219
}, onError: (e, s) {
219
220
if (e is ! T ) throw e;
220
221
if (onError != null ) onError (e);
221
- test. asyncDone ();
222
+ asyncEnd ();
222
223
});
223
224
return ;
224
225
}
@@ -251,20 +252,18 @@ Matcher throwsA(matcher) => (dynamic o) {
251
252
Matcher completion (matcher) => (dynamic o) {
252
253
Expect .type <Future >(o);
253
254
Future future = o;
254
- _currentTest. asyncWait ();
255
+ asyncStart ();
255
256
future.then ((value) {
256
257
expect (value, matcher);
257
- _currentTest. asyncDone ();
258
+ asyncEnd ();
258
259
});
259
260
};
260
261
261
262
void completes (dynamic o) {
262
263
Expect .type <Future >(o);
263
264
Future future = o;
264
- _currentTest.asyncWait ();
265
- future.then ((_) {
266
- _currentTest.asyncDone ();
267
- });
265
+ asyncStart ();
266
+ future.then (asyncSuccess);
268
267
}
269
268
270
269
void isMap (dynamic o) {
@@ -297,12 +296,21 @@ String fail(String message) {
297
296
Expect .fail ("$message " );
298
297
}
299
298
300
- // Internal helpers.
299
+ /// Key for zone value holding current test name.
300
+ final _testToken = Object ();
301
+
302
+ bool _initializedTestNameCallback = false ;
301
303
302
- // The current combined name of the nesting [group] or [test].
304
+ /// The current combined name of the nesting [group] or [test] .
303
305
String _currentName = null ;
304
306
305
307
String _pushName (String newName) {
308
+ // Look up the current test name from the zone created for the test.
309
+ if (! _initializedTestNameCallback) {
310
+ ExpectException .setTestNameCallback (() => Zone .current[_testToken]);
311
+ _initializedTestNameCallback = true ;
312
+ }
313
+
306
314
var oldName = _currentName;
307
315
if (oldName == null ) {
308
316
_currentName = newName;
@@ -315,73 +323,3 @@ String _pushName(String newName) {
315
323
void _popName (String oldName) {
316
324
_currentName = oldName;
317
325
}
318
-
319
- // Key for zone value holding current test object.
320
- final Object _testToken = new Object ();
321
-
322
- _Test get _currentTest =>
323
- Zone .current[_testToken] ?? (throw new StateError ("Not inside test!" ));
324
-
325
- class _Test {
326
- static int activeTests = 0 ;
327
- static int failedTests = 0 ;
328
-
329
- final String name;
330
- bool completed = false ;
331
- bool failed = false ;
332
- int asyncExpected = 0 ;
333
- _Test (this .name) {
334
- activeTests++ ;
335
- }
336
-
337
- void asyncWait ([int n = 1 ]) {
338
- if (completed) {
339
- print ("ERROR: $name : New operations started after completion."
340
- "${StackTrace .current }" );
341
- } else if (asyncExpected == 0 ) {
342
- asyncStart (); // Matched by asyncEnd in [_complete];
343
- }
344
- asyncExpected += n;
345
- }
346
-
347
- void asyncDone () {
348
- if (asyncExpected == 0 ) {
349
- print ("ERROR: $name : More asyncEnds than asyncStarts.\n "
350
- "${StackTrace .current }" );
351
- } else {
352
- asyncExpected-- ;
353
- if (asyncExpected == 0 && ! completed) {
354
- print ("SUCCESS: $name " );
355
- _complete ();
356
- }
357
- }
358
- }
359
-
360
- void fail (Object error, StackTrace stack) {
361
- if (! completed) {
362
- failed = true ;
363
- failedTests++ ;
364
- print ("FAILURE: $name : $error \n $stack " );
365
- _complete ();
366
- } else {
367
- if (! failed) {
368
- failed = true ;
369
- failedTests++ ;
370
- }
371
- print ("FAILURE: $name : (after completion) $error \n $stack " );
372
- }
373
- }
374
-
375
- void _complete () {
376
- assert (! completed);
377
- completed = true ;
378
- activeTests-- ;
379
- if (failedTests == 0 ) {
380
- asyncEnd ();
381
- } else if (activeTests == 0 ) {
382
- Zone .root.scheduleMicrotask (() {
383
- Expect .fail ("$failedTests tests failed" );
384
- });
385
- }
386
- }
387
- }
0 commit comments