flutter/dev/devicelab/test/runner_test.dart
Ben Konyi 2c84e63ba7
[ Cocoon ] Wait for task results to be received by the task runner before shutting down the task process (#156002)
Prior to this fix, `_TaskRunner.run` would immediately cleanup the
keep-alive port once the task completed, which would result in the
isolate shutting down as soon as the task result was returned from
`ext.cocoonRunTask` callback in the form of a
`ServiceExtensionResponse`. Since the service extension response is
processed by the service isolate, it was possible for the VM to start
shutting down before the service isolate could send the task result data
back to the task runner.

This change introduces a new service extension,
`ext.cocoonTaskResultReceived`, that the task runner invokes after it
receives the task result from `ext.cocoonRunTask`, notifying the task
process that it can close the keep-alive port and shutdown.

Fixes https://github.com/flutter/flutter/issues/155475
2024-10-02 17:01:24 -04:00

74 lines
2.4 KiB
Dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_devicelab/framework/runner.dart';
import 'package:vm_service/vm_service.dart';
import 'common.dart';
void main() {
final Map<String, String> isolateParams = <String, String>{
'runFlutterConfig': 'false',
'timeoutInMinutes': '1',
};
late List<String> printLog;
void print(String s) => printLog.add(s);
group('run.dart script', () {
test('Reruns - Test passes the first time.', () async {
printLog = <String>[];
await runTasks(
<String>['smoke_test_success'],
isolateParams: isolateParams,
print: print,
logs: printLog,
);
expect(printLog.length, 2);
expect(printLog[0], 'Test passed on first attempt.');
expect(printLog[1], 'flaky: false');
});
test('Reruns - Test fails all reruns.', () async {
printLog = <String>[];
await runTasks(
<String>['smoke_test_failure'],
isolateParams: isolateParams,
print: print,
logs: printLog,
);
expect(printLog.length, 2);
expect(printLog[0], 'Consistently failed across all 3 executions.');
expect(printLog[1], 'flaky: false');
});
test('Ensures task results are received before task process shuts down.', () async {
// Regression test for https://github.com/flutter/flutter/issues/155475
//
// Runs multiple concurrent instances of a short-lived task in an effort to
// trigger the race between the VM service processing the response from
// ext.cocoonRunTask and the VM shutting down, which will throw a RPCError
// with a "Service connection disposed" message.
//
// Obviously this isn't foolproof, but this test becoming flaky or failing
// consistently should signal that we're encountering a shutdown race
// somewhere.
const int runs = 30;
try {
await Future.wait(
<Future<void>>[
for (int i = 0; i < runs; ++i)
runTasks(
<String>['smoke_test_success'],
isolateParams: isolateParams,
),
],
eagerError: true,
);
} on RPCError catch (e) {
fail('Unexpected RPCError: $e');
}
}, timeout: const Timeout.factor(2));
});
}