Skip to content

Commit 4301731

Browse files
Make Flutter Driver actively wait for runnable isolate (#113969)
* Test the case when main Isolate is in `None` state for long time * Wait for isolate to become runnable * Handle `PausePostRequest` as a normal "paused" event * Use `-= 1` instead of `--`
1 parent 4fdaf7a commit 4301731

File tree

2 files changed

+71
-6
lines changed

2 files changed

+71
-6
lines changed

packages/flutter_driver/lib/src/driver/vmservice_driver.dart

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,19 @@ class VMServiceFlutterDriver extends FlutterDriver {
100100
}
101101
}
102102

103+
// Refreshes the isolate state periodically until the isolate reports as
104+
// being runnable.
105+
Future<vms.Isolate> waitForIsolateToBeRunnable(vms.IsolateRef ref) async {
106+
while (true) {
107+
final vms.Isolate isolate = await client.getIsolate(ref.id!);
108+
if (isolate.pauseEvent!.kind == vms.EventKind.kNone) {
109+
await Future<void>.delayed(_kPauseBetweenIsolateRefresh);
110+
} else {
111+
return isolate;
112+
}
113+
}
114+
}
115+
103116
final vms.IsolateRef isolateRef = (await _warnIfSlow<vms.IsolateRef?>(
104117
future: waitForRootIsolate(),
105118
timeout: kUnusuallyLongTimeout,
@@ -108,11 +121,13 @@ class VMServiceFlutterDriver extends FlutterDriver {
108121
: 'Isolate $isolateNumber is taking an unusually long time to start.',
109122
))!;
110123
_log('Isolate found with number: ${isolateRef.number}');
111-
vms.Isolate isolate = await client.getIsolate(isolateRef.id!);
112-
113-
if (isolate.pauseEvent!.kind == vms.EventKind.kNone) {
114-
isolate = await client.getIsolate(isolateRef.id!);
115-
}
124+
final vms.Isolate isolate = await _warnIfSlow<vms.Isolate>(
125+
future: waitForIsolateToBeRunnable(isolateRef),
126+
timeout: kUnusuallyLongTimeout,
127+
message: 'The isolate ${isolateRef.number} is taking unusually long time '
128+
'to initialize. It still reports ${vms.EventKind.kNone} as pause '
129+
'event which is incorrect.',
130+
);
116131

117132
final VMServiceFlutterDriver driver = VMServiceFlutterDriver.connectedTo(
118133
client,
@@ -201,7 +216,8 @@ class VMServiceFlutterDriver extends FlutterDriver {
201216
} else if (isolate.pauseEvent!.kind == vms.EventKind.kPauseExit ||
202217
isolate.pauseEvent!.kind == vms.EventKind.kPauseBreakpoint ||
203218
isolate.pauseEvent!.kind == vms.EventKind.kPauseException ||
204-
isolate.pauseEvent!.kind == vms.EventKind.kPauseInterrupted) {
219+
isolate.pauseEvent!.kind == vms.EventKind.kPauseInterrupted ||
220+
isolate.pauseEvent!.kind == vms.EventKind.kPausePostRequest) {
205221
// If the isolate is paused for any other reason, assume the extension is
206222
// already there.
207223
_log('Isolate is paused mid-flight.');
@@ -583,6 +599,9 @@ Future<vms.VmService> _waitAndConnect(String url, Map<String, dynamic>? headers)
583599
/// the VM service.
584600
const Duration _kPauseBetweenReconnectAttempts = Duration(seconds: 1);
585601

602+
/// The amount of time we wait prior to refreshing the isolate state.
603+
const Duration _kPauseBetweenIsolateRefresh = Duration(milliseconds: 100);
604+
586605
// See `timeline_streams` in
587606
// https://github.com/dart-lang/sdk/blob/main/runtime/vm/timeline.cc
588607
List<String> _timelineStreamsToString(List<TimelineStream> streams) {

packages/flutter_driver/test/src/real_tests/flutter_driver_test.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,33 @@ void main() {
189189
);
190190
});
191191

192+
test('Refreshes isolate if it is not started for long time', () async {
193+
fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kNone, timestamp: 0);
194+
fakeClient.onGetIsolate = changeIsolateEventAfter(
195+
5,
196+
vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 1),
197+
);
198+
199+
final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
200+
expect(driver, isNotNull);
201+
expect(
202+
fakeClient.connectionLog,
203+
<String>[
204+
'getIsolate',
205+
'getIsolate',
206+
'getIsolate',
207+
'getIsolate',
208+
'getIsolate',
209+
'setFlag pause_isolates_on_start false',
210+
'resume',
211+
'streamListen Isolate',
212+
'getIsolate',
213+
'onIsolateEvent',
214+
'streamCancel Isolate',
215+
],
216+
);
217+
});
218+
192219
test('Connects to isolate number', () async {
193220
fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 0);
194221
final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '', isolateNumber: int.parse(fakeIsolate.number!));
@@ -246,6 +273,14 @@ void main() {
246273
expectLogContains('Isolate is paused mid-flight');
247274
});
248275

276+
test('connects to isolate paused mid-flight after request', () async {
277+
fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPausePostRequest, timestamp: 0);
278+
279+
final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
280+
expect(driver, isNotNull);
281+
expectLogContains('Isolate is paused mid-flight');
282+
});
283+
249284
// This test simulates a situation when we believe that the isolate is
250285
// currently paused, but something else (e.g. a debugger) resumes it before
251286
// we do. There's no need to fail as we should be able to drive the app
@@ -1055,6 +1090,15 @@ vms.Response? makeFakeResponse(
10551090
});
10561091
}
10571092

1093+
void Function(vms.Isolate) changeIsolateEventAfter(int gets, vms.Event nextEvent) {
1094+
return (vms.Isolate i) {
1095+
gets -= 1;
1096+
if (gets == 0) {
1097+
i.pauseEvent = nextEvent;
1098+
}
1099+
};
1100+
}
1101+
10581102
class FakeFlutterWebConnection extends Fake implements FlutterWebConnection {
10591103
@override
10601104
bool supportsTimelineAction = false;
@@ -1082,6 +1126,7 @@ class FakeVmService extends Fake implements vms.VmService {
10821126
FakeVM? vm;
10831127
bool failOnSetFlag = false;
10841128
bool failOnResumeWith101 = false;
1129+
void Function(vms.Isolate)? onGetIsolate;
10851130

10861131
final List<String> connectionLog = <String>[];
10871132

@@ -1092,6 +1137,7 @@ class FakeVmService extends Fake implements vms.VmService {
10921137
Future<vms.Isolate> getIsolate(String isolateId) async {
10931138
connectionLog.add('getIsolate');
10941139
if (isolateId == vm!.isolate!.id) {
1140+
onGetIsolate?.call(vm!.isolate!);
10951141
return vm!.isolate!;
10961142
}
10971143
throw UnimplementedError('getIsolate called with unrecognized $isolateId');

0 commit comments

Comments
 (0)