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 `--`
This commit is contained in:
parent
4fdaf7ae25
commit
4301731eb2
@ -100,6 +100,19 @@ class VMServiceFlutterDriver extends FlutterDriver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refreshes the isolate state periodically until the isolate reports as
|
||||||
|
// being runnable.
|
||||||
|
Future<vms.Isolate> waitForIsolateToBeRunnable(vms.IsolateRef ref) async {
|
||||||
|
while (true) {
|
||||||
|
final vms.Isolate isolate = await client.getIsolate(ref.id!);
|
||||||
|
if (isolate.pauseEvent!.kind == vms.EventKind.kNone) {
|
||||||
|
await Future<void>.delayed(_kPauseBetweenIsolateRefresh);
|
||||||
|
} else {
|
||||||
|
return isolate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final vms.IsolateRef isolateRef = (await _warnIfSlow<vms.IsolateRef?>(
|
final vms.IsolateRef isolateRef = (await _warnIfSlow<vms.IsolateRef?>(
|
||||||
future: waitForRootIsolate(),
|
future: waitForRootIsolate(),
|
||||||
timeout: kUnusuallyLongTimeout,
|
timeout: kUnusuallyLongTimeout,
|
||||||
@ -108,11 +121,13 @@ class VMServiceFlutterDriver extends FlutterDriver {
|
|||||||
: 'Isolate $isolateNumber is taking an unusually long time to start.',
|
: 'Isolate $isolateNumber is taking an unusually long time to start.',
|
||||||
))!;
|
))!;
|
||||||
_log('Isolate found with number: ${isolateRef.number}');
|
_log('Isolate found with number: ${isolateRef.number}');
|
||||||
vms.Isolate isolate = await client.getIsolate(isolateRef.id!);
|
final vms.Isolate isolate = await _warnIfSlow<vms.Isolate>(
|
||||||
|
future: waitForIsolateToBeRunnable(isolateRef),
|
||||||
if (isolate.pauseEvent!.kind == vms.EventKind.kNone) {
|
timeout: kUnusuallyLongTimeout,
|
||||||
isolate = await client.getIsolate(isolateRef.id!);
|
message: 'The isolate ${isolateRef.number} is taking unusually long time '
|
||||||
}
|
'to initialize. It still reports ${vms.EventKind.kNone} as pause '
|
||||||
|
'event which is incorrect.',
|
||||||
|
);
|
||||||
|
|
||||||
final VMServiceFlutterDriver driver = VMServiceFlutterDriver.connectedTo(
|
final VMServiceFlutterDriver driver = VMServiceFlutterDriver.connectedTo(
|
||||||
client,
|
client,
|
||||||
@ -201,7 +216,8 @@ class VMServiceFlutterDriver extends FlutterDriver {
|
|||||||
} else if (isolate.pauseEvent!.kind == vms.EventKind.kPauseExit ||
|
} else if (isolate.pauseEvent!.kind == vms.EventKind.kPauseExit ||
|
||||||
isolate.pauseEvent!.kind == vms.EventKind.kPauseBreakpoint ||
|
isolate.pauseEvent!.kind == vms.EventKind.kPauseBreakpoint ||
|
||||||
isolate.pauseEvent!.kind == vms.EventKind.kPauseException ||
|
isolate.pauseEvent!.kind == vms.EventKind.kPauseException ||
|
||||||
isolate.pauseEvent!.kind == vms.EventKind.kPauseInterrupted) {
|
isolate.pauseEvent!.kind == vms.EventKind.kPauseInterrupted ||
|
||||||
|
isolate.pauseEvent!.kind == vms.EventKind.kPausePostRequest) {
|
||||||
// If the isolate is paused for any other reason, assume the extension is
|
// If the isolate is paused for any other reason, assume the extension is
|
||||||
// already there.
|
// already there.
|
||||||
_log('Isolate is paused mid-flight.');
|
_log('Isolate is paused mid-flight.');
|
||||||
@ -583,6 +599,9 @@ Future<vms.VmService> _waitAndConnect(String url, Map<String, dynamic>? headers)
|
|||||||
/// the VM service.
|
/// the VM service.
|
||||||
const Duration _kPauseBetweenReconnectAttempts = Duration(seconds: 1);
|
const Duration _kPauseBetweenReconnectAttempts = Duration(seconds: 1);
|
||||||
|
|
||||||
|
/// The amount of time we wait prior to refreshing the isolate state.
|
||||||
|
const Duration _kPauseBetweenIsolateRefresh = Duration(milliseconds: 100);
|
||||||
|
|
||||||
// See `timeline_streams` in
|
// See `timeline_streams` in
|
||||||
// https://github.com/dart-lang/sdk/blob/main/runtime/vm/timeline.cc
|
// https://github.com/dart-lang/sdk/blob/main/runtime/vm/timeline.cc
|
||||||
List<String> _timelineStreamsToString(List<TimelineStream> streams) {
|
List<String> _timelineStreamsToString(List<TimelineStream> streams) {
|
||||||
|
@ -189,6 +189,33 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Refreshes isolate if it is not started for long time', () async {
|
||||||
|
fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kNone, timestamp: 0);
|
||||||
|
fakeClient.onGetIsolate = changeIsolateEventAfter(
|
||||||
|
5,
|
||||||
|
vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
|
||||||
|
expect(driver, isNotNull);
|
||||||
|
expect(
|
||||||
|
fakeClient.connectionLog,
|
||||||
|
<String>[
|
||||||
|
'getIsolate',
|
||||||
|
'getIsolate',
|
||||||
|
'getIsolate',
|
||||||
|
'getIsolate',
|
||||||
|
'getIsolate',
|
||||||
|
'setFlag pause_isolates_on_start false',
|
||||||
|
'resume',
|
||||||
|
'streamListen Isolate',
|
||||||
|
'getIsolate',
|
||||||
|
'onIsolateEvent',
|
||||||
|
'streamCancel Isolate',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('Connects to isolate number', () async {
|
test('Connects to isolate number', () async {
|
||||||
fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 0);
|
fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 0);
|
||||||
final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '', isolateNumber: int.parse(fakeIsolate.number!));
|
final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '', isolateNumber: int.parse(fakeIsolate.number!));
|
||||||
@ -246,6 +273,14 @@ void main() {
|
|||||||
expectLogContains('Isolate is paused mid-flight');
|
expectLogContains('Isolate is paused mid-flight');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('connects to isolate paused mid-flight after request', () async {
|
||||||
|
fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPausePostRequest, timestamp: 0);
|
||||||
|
|
||||||
|
final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
|
||||||
|
expect(driver, isNotNull);
|
||||||
|
expectLogContains('Isolate is paused mid-flight');
|
||||||
|
});
|
||||||
|
|
||||||
// This test simulates a situation when we believe that the isolate is
|
// This test simulates a situation when we believe that the isolate is
|
||||||
// currently paused, but something else (e.g. a debugger) resumes it before
|
// currently paused, but something else (e.g. a debugger) resumes it before
|
||||||
// we do. There's no need to fail as we should be able to drive the app
|
// we do. There's no need to fail as we should be able to drive the app
|
||||||
@ -1055,6 +1090,15 @@ vms.Response? makeFakeResponse(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Function(vms.Isolate) changeIsolateEventAfter(int gets, vms.Event nextEvent) {
|
||||||
|
return (vms.Isolate i) {
|
||||||
|
gets -= 1;
|
||||||
|
if (gets == 0) {
|
||||||
|
i.pauseEvent = nextEvent;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class FakeFlutterWebConnection extends Fake implements FlutterWebConnection {
|
class FakeFlutterWebConnection extends Fake implements FlutterWebConnection {
|
||||||
@override
|
@override
|
||||||
bool supportsTimelineAction = false;
|
bool supportsTimelineAction = false;
|
||||||
@ -1082,6 +1126,7 @@ class FakeVmService extends Fake implements vms.VmService {
|
|||||||
FakeVM? vm;
|
FakeVM? vm;
|
||||||
bool failOnSetFlag = false;
|
bool failOnSetFlag = false;
|
||||||
bool failOnResumeWith101 = false;
|
bool failOnResumeWith101 = false;
|
||||||
|
void Function(vms.Isolate)? onGetIsolate;
|
||||||
|
|
||||||
final List<String> connectionLog = <String>[];
|
final List<String> connectionLog = <String>[];
|
||||||
|
|
||||||
@ -1092,6 +1137,7 @@ class FakeVmService extends Fake implements vms.VmService {
|
|||||||
Future<vms.Isolate> getIsolate(String isolateId) async {
|
Future<vms.Isolate> getIsolate(String isolateId) async {
|
||||||
connectionLog.add('getIsolate');
|
connectionLog.add('getIsolate');
|
||||||
if (isolateId == vm!.isolate!.id) {
|
if (isolateId == vm!.isolate!.id) {
|
||||||
|
onGetIsolate?.call(vm!.isolate!);
|
||||||
return vm!.isolate!;
|
return vm!.isolate!;
|
||||||
}
|
}
|
||||||
throw UnimplementedError('getIsolate called with unrecognized $isolateId');
|
throw UnimplementedError('getIsolate called with unrecognized $isolateId');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user