@@ -112,6 +112,16 @@ class Declarer {
112
112
/// `null` .
113
113
final Set <String >? _seenNames;
114
114
115
+ /// Whether this declarer is running in a standalone test executation.
116
+ ///
117
+ /// The full test runner awaits asynchronous `main` declarations, and so
118
+ /// asynchronous work can be performed in between calls to `group` , and `test`
119
+ /// etc. When running as a standalone file tests are run synchronously
120
+ /// following the first call to declare a test, so all tests must be declared
121
+ /// synchronously starting at that point. Track whether we are running in this
122
+ /// more limited mode to customize the error message for tests declared late.
123
+ final bool _isStandalone;
124
+
115
125
/// Creates a new declarer for the root group.
116
126
///
117
127
/// This is the implicit group that exists outside of any calls to `group()` .
@@ -139,16 +149,19 @@ class Declarer {
139
149
String ? fullTestName,
140
150
// TODO: Change the default https://github.com/dart-lang/test/issues/1571
141
151
bool allowDuplicateTestNames = true ,
152
+ bool isStandalone = false ,
142
153
}) : this ._(
143
- null ,
144
- null ,
145
- metadata ?? Metadata (),
146
- platformVariables ?? const UnmodifiableSetView .empty (),
147
- collectTraces,
148
- null ,
149
- noRetry,
150
- fullTestName,
151
- allowDuplicateTestNames ? null : < String > {});
154
+ null ,
155
+ null ,
156
+ metadata ?? Metadata (),
157
+ platformVariables ?? const UnmodifiableSetView .empty (),
158
+ collectTraces,
159
+ null ,
160
+ noRetry,
161
+ fullTestName,
162
+ allowDuplicateTestNames ? null : < String > {},
163
+ isStandalone,
164
+ );
152
165
153
166
Declarer ._(
154
167
this ._parent,
@@ -160,6 +173,7 @@ class Declarer {
160
173
this ._noRetry,
161
174
this ._fullTestName,
162
175
this ._seenNames,
176
+ this ._isStandalone,
163
177
);
164
178
165
179
/// Runs [body] with this declarer as [Declarer.current] .
@@ -252,15 +266,17 @@ class Declarer {
252
266
var trace = _collectTraces ? Trace .current (2 ) : null ;
253
267
254
268
var declarer = Declarer ._(
255
- this ,
256
- fullTestPrefix,
257
- metadata,
258
- _platformVariables,
259
- _collectTraces,
260
- trace,
261
- _noRetry,
262
- _fullTestName,
263
- _seenNames);
269
+ this ,
270
+ fullTestPrefix,
271
+ metadata,
272
+ _platformVariables,
273
+ _collectTraces,
274
+ trace,
275
+ _noRetry,
276
+ _fullTestName,
277
+ _seenNames,
278
+ _isStandalone,
279
+ );
264
280
declarer.declare (() {
265
281
// Cast to dynamic to avoid the analyzer complaining about us using the
266
282
// result of a void method.
@@ -340,7 +356,20 @@ class Declarer {
340
356
/// [name] should be the name of the method being called.
341
357
void _checkNotBuilt (String name) {
342
358
if (! _built) return ;
343
- throw StateError ("Can't call $name () once tests have begun running." );
359
+ final restrictionMessage = _isStandalone
360
+ ? 'When running a test as an executable directly '
361
+ '(not as a suite by the test runner), '
362
+ 'tests must be declared in a synchronous block.\n '
363
+ 'If async work is required before any tests are run '
364
+ 'use a `setUpAll` callback.\n '
365
+ 'If async work cannot be avoided before declaring tests, '
366
+ 'all async events must be complete before declaring the first test.'
367
+ : 'If async work is required before any tests are run '
368
+ 'use a `setUpAll` callback.\n '
369
+ 'If async work cannot be avoided before declaring tests it must '
370
+ 'all be awaited within the Future returned from `main`.' ;
371
+ throw StateError ("Can't call $name () once tests have begun running.\n "
372
+ '$restrictionMessage ' );
344
373
}
345
374
346
375
/// Run the set-up functions for this and any parent groups.
0 commit comments