Skip to content

Commit 0e2c950

Browse files
authored
fix: cleanup .test_optimizer.dart on SIGINT (#1190)
1 parent 9ec06bd commit 0e2c950

File tree

2 files changed

+118
-3
lines changed

2 files changed

+118
-3
lines changed

lib/src/cli/flutter_cli.dart

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,59 @@ part of 'cli.dart';
22

33
const _testOptimizerFileName = '.test_optimizer.dart';
44

5+
/// This class facilitates overriding `ProcessSignal` related behavior.
6+
/// It should be extended by another class in client code with overrides
7+
/// that construct a custom implementation.
8+
@visibleForTesting
9+
abstract class ProcessSignalOverrides {
10+
static final _token = Object();
11+
StreamController<ProcessSignal>? _sigintStreamController;
12+
13+
/// Returns the current [ProcessSignalOverrides] instance.
14+
///
15+
/// This will return `null` if the current [Zone] does not contain
16+
/// any [ProcessSignalOverrides].
17+
///
18+
/// See also:
19+
/// * [ProcessSignalOverrides.runZoned] to provide [ProcessSignalOverrides]
20+
/// in a fresh [Zone].
21+
static ProcessSignalOverrides? get current {
22+
return Zone.current[_token] as ProcessSignalOverrides?;
23+
}
24+
25+
/// Runs [body] in a fresh [Zone] using the provided overrides.
26+
static R runZoned<R>(
27+
R Function() body, {
28+
Stream<ProcessSignal>? sigintStream,
29+
}) {
30+
final overrides = _ProcessSignalOverridesScope(sigintStream);
31+
return _asyncRunZoned(body, zoneValues: {_token: overrides});
32+
}
33+
34+
/// Provides a custom [Stream] of [ProcessSignal.sigint] events.
35+
Stream<ProcessSignal>? get sigintWatch;
36+
37+
/// Emits a [ProcessSignal.sigint] event on the [sigintWatch] stream.
38+
///
39+
/// If no custom [sigintWatch] stream is provided, this method does nothing.
40+
void addSIGINT() {
41+
_sigintStreamController?.add(ProcessSignal.sigint);
42+
}
43+
}
44+
45+
class _ProcessSignalOverridesScope extends ProcessSignalOverrides {
46+
_ProcessSignalOverridesScope(Stream<ProcessSignal>? mockSigintStream) {
47+
if (mockSigintStream != null) {
48+
_sigintStreamController = StreamController<ProcessSignal>();
49+
}
50+
}
51+
52+
@override
53+
Stream<ProcessSignal>? get sigintWatch {
54+
return _sigintStreamController?.stream;
55+
}
56+
}
57+
558
/// Thrown when `flutter pub get` is executed without a `pubspec.yaml`.
659
class PubspecNotFound implements Exception {}
760

@@ -211,9 +264,7 @@ class Flutter {
211264
stderr: stderr ?? noop,
212265
).whenComplete(() async {
213266
if (optimizePerformance) {
214-
File(p.join(cwd, 'test', _testOptimizerFileName))
215-
.delete()
216-
.ignore();
267+
await _cleanupOptimizerFile(cwd);
217268
}
218269

219270
if (collectCoverage) {
@@ -325,6 +376,8 @@ Future<int> _flutterTest({
325376
final groups = <int, TestGroup>{};
326377
final tests = <int, Test>{};
327378
final failedTestErrorMessages = <String, List<String>>{};
379+
final sigintWatch = ProcessSignalOverrides.current?.sigintWatch ??
380+
ProcessSignal.sigint.watch();
328381

329382
var successCount = 0;
330383
var skipCount = 0;
@@ -351,6 +404,15 @@ Future<int> _flutterTest({
351404
);
352405

353406
late final StreamSubscription<TestEvent> subscription;
407+
late final StreamSubscription<ProcessSignal> sigintWatchSubscription;
408+
409+
sigintWatchSubscription = sigintWatch.listen((_) async {
410+
await _cleanupOptimizerFile(cwd);
411+
await subscription.cancel();
412+
await sigintWatchSubscription.cancel();
413+
return completer.complete(ExitCode.success.code);
414+
});
415+
354416
subscription = testRunner(
355417
workingDirectory: cwd,
356418
arguments: [
@@ -483,6 +545,8 @@ Future<int> _flutterTest({
483545
if (event is ExitTestEvent) {
484546
if (completer.isCompleted) return;
485547
subscription.cancel();
548+
sigintWatchSubscription.cancel();
549+
486550
completer.complete(
487551
event.exitCode == ExitCode.success.code
488552
? ExitCode.success.code
@@ -506,6 +570,10 @@ String? _topGroupName(Test test, Map<int, TestGroup> groups) => test.groupIDs
506570
.map((groupID) => groups[groupID]?.name)
507571
.firstWhereOrNull((groupName) => groupName?.isNotEmpty ?? false);
508572

573+
Future<void> _cleanupOptimizerFile(String cwd) async => File(
574+
p.join(cwd, 'test', _testOptimizerFileName),
575+
).delete().ignore();
576+
509577
final int _lineLength = () {
510578
try {
511579
return stdout.terminalColumns;

test/src/cli/flutter_cli_test.dart

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,53 @@ void main() {
317317
stderrLogs = [];
318318
});
319319

320+
test('cleanup the .test_optimizer file when SIGINT is emitted', () async {
321+
final streamController = StreamController<ProcessSignal>();
322+
await ProcessSignalOverrides.runZoned(
323+
() async {
324+
final tempDirectory = Directory.systemTemp.createTempSync();
325+
addTearDown(() => tempDirectory.deleteSync(recursive: true));
326+
327+
final updatedVars = <String, dynamic>{
328+
'package-root': tempDirectory.path,
329+
'foo': 'bar',
330+
};
331+
332+
File(p.join(tempDirectory.path, 'pubspec.yaml')).createSync();
333+
Directory(p.join(tempDirectory.path, 'test')).createSync();
334+
when(
335+
() => hooks.preGen(
336+
vars: any(named: 'vars'),
337+
onVarsChanged: any(named: 'onVarsChanged'),
338+
workingDirectory: any(named: 'workingDirectory'),
339+
),
340+
).thenAnswer((invocation) async {
341+
(invocation.namedArguments[#onVarsChanged] as void Function(
342+
Map<String, dynamic> vars,
343+
))
344+
.call(updatedVars);
345+
});
346+
ProcessSignalOverrides.current?.addSIGINT();
347+
await Flutter.test(
348+
cwd: tempDirectory.path,
349+
optimizePerformance: true,
350+
stdout: stdoutLogs.add,
351+
stderr: stderrLogs.add,
352+
logger: logger,
353+
buildGenerator: generatorBuilder(),
354+
);
355+
final filePath = p.join(
356+
tempDirectory.path,
357+
'test',
358+
'.test_optimizer.dart',
359+
);
360+
final testOptimizerFile = File(filePath);
361+
expect(testOptimizerFile.existsSync(), isFalse);
362+
},
363+
sigintStream: streamController.stream,
364+
);
365+
});
366+
320367
test('throws when pubspec not found', () async {
321368
await expectLater(
322369
() => Flutter.test(cwd: Directory.systemTemp.path, logger: logger),

0 commit comments

Comments
 (0)