integration_test_device.dart 4.64 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
// 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';
13
import '../globals.dart' as globals;
14 15 16 17 18 19 20 21 22
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({
23 24 25 26
    required this.id,
    required this.device,
    required this.debuggingOptions,
    required this.userIdentifier,
27 28 29 30 31
  });

  final int id;
  final Device device;
  final DebuggingOptions debuggingOptions;
32
  final String? userIdentifier;
33

34
  ApplicationPackage? _applicationPackage;
35 36 37 38 39
  final Completer<void> _finished = Completer<void>();
  final Completer<Uri> _gotProcessObservatoryUri = Completer<Uri>();

  /// Starts the device.
  ///
40
  /// [entrypointPath] must be a path to an un-compiled source file.
41 42 43
  @override
  Future<StreamChannel<String>> start(String entrypointPath) async {
    final TargetPlatform targetPlatform = await device.targetPlatform;
44
    _applicationPackage = await ApplicationPackageFactory.instance?.getPackageForPlatform(
45 46 47
      targetPlatform,
      buildInfo: debuggingOptions.buildInfo,
    );
48 49 50
    if (_applicationPackage == null) {
      throw TestDeviceException('No application found for $targetPlatform.', StackTrace.current);
    }
51 52

    final LaunchResult launchResult = await device.startApp(
53
      _applicationPackage,
54 55 56 57 58 59 60 61
      mainPath: entrypointPath,
      platformArgs: <String, dynamic>{},
      debuggingOptions: debuggingOptions,
      userIdentifier: userIdentifier,
    );
    if (!launchResult.started) {
      throw TestDeviceException('Unable to start the app on the device.', StackTrace.current);
    }
62 63
    final Uri? observatoryUri = launchResult.observatoryUri;
    if (observatoryUri == null) {
64 65 66 67 68 69
      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.

70
    _gotProcessObservatoryUri.complete(observatoryUri);
71 72

    globals.printTrace('test $id: Connecting to vm service');
73
    final FlutterVmService vmService = await connectToVmService(observatoryUri, logger: globals.logger).timeout(
74 75 76 77 78
      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');
79 80 81
    final vm_service.IsolateRef isolateRef = await vmService.findExtensionIsolate(
      kIntegrationTestMethod,
    );
82 83 84 85

    await vmService.service.streamListen(vm_service.EventStreams.kExtension);
    final Stream<String> remoteMessages = vmService.service.onExtensionEvent
        .where((vm_service.Event e) => e.extensionKind == kIntegrationTestExtension)
86
        .map((vm_service.Event e) => e.extensionData!.data[kIntegrationTestData] as String);
87 88 89 90 91 92 93 94 95 96 97 98 99

    final StreamChannelController<String> controller = StreamChannelController<String>();

    controller.local.stream.listen((String event) {
      vmService.service.callServiceExtension(
        kIntegrationTestMethod,
        isolateId: isolateRef.id,
        args: <String, String>{
          kIntegrationTestData: event,
        },
      );
    });

100 101 102 103 104 105 106 107
    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(),
    ));

108 109 110 111 112 113 114 115
    return controller.foreign;
  }

  @override
  Future<Uri> get observatoryUri => _gotProcessObservatoryUri.future;

  @override
  Future<void> kill() async {
116 117 118 119 120 121 122 123
    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.');
      }
124 125 126 127 128 129 130 131 132
    }

    await device.dispose();
    _finished.complete();
  }

  @override
  Future<void> get finished => _finished.future;
}