blob: cdf1dbd70befee17c1759c5a33aa80c6051228d7 [file] [log] [blame]
// 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 'dart:async';
import 'package:stream_channel/stream_channel.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import '../application_package.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 {
required this.device,
required this.debuggingOptions,
required this.userIdentifier,
required this.compileExpression,
final int id;
final Device device;
final DebuggingOptions debuggingOptions;
final String? userIdentifier;
final CompileExpression? compileExpression;
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.
Future<StreamChannel<String>> start(String entrypointPath) async {
final TargetPlatform targetPlatform = await device.targetPlatform;
_applicationPackage = await ApplicationPackageFactory.instance?.getPackageForPlatform(
buildInfo: debuggingOptions.buildInfo,
if (_applicationPackage == null) {
throw TestDeviceException('No application found for $targetPlatform.', StackTrace.current);
final LaunchResult launchResult = await device.startApp(
mainPath: entrypointPath,
platformArgs: <String, dynamic>{},
debuggingOptions: debuggingOptions,
userIdentifier: userIdentifier,
if (!launchResult.started) {
throw TestDeviceException('Unable to start the app on the device.', StackTrace.current);
final Uri? observatoryUri = launchResult.observatoryUri;
if (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.
globals.printTrace('test $id: Connecting to vm service');
final FlutterVmService vmService = await connectToVmService(
logger: globals.logger,
compileExpression: compileExpression,
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(
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>(); event) {
args: <String, String>{
kIntegrationTestData: event,
(String s) => controller.local.sink.add(s),
onError: (Object error, StackTrace stack) => controller.local.sink.addError(error, stack),
() => controller.local.sink.close(),
return controller.foreign;
Future<Uri> get observatoryUri => _gotProcessObservatoryUri.future;
Future<void> kill() async {
final ApplicationPackage? applicationPackage = _applicationPackage;
if (applicationPackage != null) {
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();
Future<void> get finished => _finished.future;