1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// 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.
// @dart = 2.8
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:stream_channel/stream_channel.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import '../application_package.dart';
import '../base/common.dart';
import '../build_info.dart';
import '../device.dart';
import '../globals.dart' as globals;
import '../vmservice.dart';
import 'test_device.dart';
const String kIntegrationTestExtension = 'Flutter.IntegrationTest';
const String kIntegrationTestData = 'data';
const String kIntegrationTestMethod = 'ext.flutter.integrationTest';
class IntegrationTestTestDevice implements TestDevice {
IntegrationTestTestDevice({
@required this.id,
@required this.device,
@required this.debuggingOptions,
@required this.userIdentifier,
});
final int id;
final Device device;
final DebuggingOptions debuggingOptions;
final String userIdentifier;
ApplicationPackage _applicationPackage;
final Completer<void> _finished = Completer<void>();
final Completer<Uri> _gotProcessObservatoryUri = Completer<Uri>();
/// Starts the device.
///
/// [entrypointPath] must be a path to an un-compiled source file.
@override
Future<StreamChannel<String>> start(String entrypointPath) async {
final TargetPlatform targetPlatform = await device.targetPlatform;
_applicationPackage = await ApplicationPackageFactory.instance.getPackageForPlatform(
targetPlatform,
buildInfo: debuggingOptions.buildInfo,
);
final LaunchResult launchResult = await device.startApp(
_applicationPackage,
mainPath: entrypointPath,
platformArgs: <String, dynamic>{},
debuggingOptions: debuggingOptions,
userIdentifier: userIdentifier,
);
if (!launchResult.started) {
throw TestDeviceException('Unable to start the app on the device.', StackTrace.current);
}
if (launchResult.observatoryUri == null) {
throw TestDeviceException('Observatory is not available on the test device.', StackTrace.current);
}
// No need to set up the log reader because the logs are captured and
// streamed to the package:test_core runner.
_gotProcessObservatoryUri.complete(launchResult.observatoryUri);
globals.printTrace('test $id: Connecting to vm service');
final FlutterVmService vmService = await connectToVmService(launchResult.observatoryUri, logger: globals.logger).timeout(
const Duration(seconds: 5),
onTimeout: () => throw TimeoutException('Connecting to the VM Service timed out.'),
);
globals.printTrace('test $id: Finding the correct isolate with the integration test service extension');
final vm_service.IsolateRef isolateRef = await vmService.findExtensionIsolate(
kIntegrationTestMethod,
);
await vmService.service.streamListen(vm_service.EventStreams.kExtension);
final Stream<String> remoteMessages = vmService.service.onExtensionEvent
.where((vm_service.Event e) => e.extensionKind == kIntegrationTestExtension)
.map((vm_service.Event e) => e.extensionData.data[kIntegrationTestData] as String);
final StreamChannelController<String> controller = StreamChannelController<String>();
controller.local.stream.listen((String event) {
vmService.service.callServiceExtension(
kIntegrationTestMethod,
isolateId: isolateRef.id,
args: <String, String>{
kIntegrationTestData: event,
},
);
});
remoteMessages.listen(
(String s) => controller.local.sink.add(s),
onError: (Object error, StackTrace stack) => controller.local.sink.addError(error, stack),
);
unawaited(vmService.service.onDone.whenComplete(
() => controller.local.sink.close(),
));
return controller.foreign;
}
@override
Future<Uri> get observatoryUri => _gotProcessObservatoryUri.future;
@override
Future<void> kill() async {
if (!await device.stopApp(_applicationPackage, userIdentifier: userIdentifier)) {
globals.printTrace('Could not stop the Integration Test app.');
}
if (!await device.uninstallApp(_applicationPackage, userIdentifier: userIdentifier)) {
globals.printTrace('Could not uninstall the Integration Test app.');
}
await device.dispose();
_finished.complete();
}
@override
Future<void> get finished => _finished.future;
}