flutter_attach_test.dart 5.23 KB
Newer Older
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
// Copyright (c) 2018 The Chromium 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 'dart:convert';
import 'dart:io';

import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';

Future<void> testReload(Process process, { Future<void> Function() onListening }) async {
  section('Testing hot reload, restart and quit');
  final Completer<Null> listening = new Completer<Null>();
  final Completer<Null> ready = new Completer<Null>();
  final Completer<Null> reloaded = new Completer<Null>();
  final Completer<Null> restarted = new Completer<Null>();
  final Completer<Null> finished = new Completer<Null>();
  final List<String> stdout = <String>[];
  final List<String> stderr = <String>[];

  if (onListening == null)
    listening.complete();

  int exitCode;
  process.stdout
      .transform(utf8.decoder)
      .transform(const LineSplitter())
      .listen((String line) {
    print('attach:stdout: $line');
    stdout.add(line);
    if (line.contains('Listening') && onListening != null) {
      listening.complete(onListening());
    }
    if (line.contains('To quit, press "q".'))
      ready.complete();
    if (line.contains('Reloaded '))
      reloaded.complete();
    if (line.contains('Restarted app in '))
      restarted.complete();
    if (line.contains('Application finished'))
      finished.complete();
  });
  process.stderr
      .transform(utf8.decoder)
      .transform(const LineSplitter())
      .listen((String line) {
    print('run:stderr: $line');
    stdout.add(line);
  });

  process.exitCode.then((int processExitCode) { exitCode = processExitCode; });

  Future<dynamic> eventOrExit(Future<Null> event) {
    return Future.any<dynamic>(<Future<dynamic>>[ event, process.exitCode ]);
  }

  await eventOrExit(listening.future);
  await eventOrExit(ready.future);

  if (exitCode != null)
    throw 'Failed to attach to test app; command unexpected exited, with exit code $exitCode.';

  process.stdin.write('r');
  process.stdin.flush();
  await eventOrExit(reloaded.future);
  process.stdin.write('R');
  process.stdin.flush();
  await eventOrExit(restarted.future);
  process.stdin.write('q');
  process.stdin.flush();
  await eventOrExit(finished.future);

  await process.exitCode;

  if (stderr.isNotEmpty)
    throw 'flutter attach had output on standard error.';

  if (exitCode != 0)
    throw 'exit code was not 0';
}

void main() {
  const String kAppId = 'com.yourcompany.integration_ui';
  const String kActivityId = '$kAppId/com.yourcompany.integration_ui.MainActivity';

  task(() async {
    final AndroidDevice device = await devices.workingDevice;
    await device.unlock();
    final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui'));
    await inDirectory(appDir, () async {
      section('Build: starting...');
      final String buildStdout = await eval(
          path.join(flutterDirectory.path, 'bin', 'flutter'),
          <String>['--suppress-analytics', 'build', 'apk', '--debug', 'lib/main.dart'],
      );
      final String lastLine = buildStdout.split('\n').last;
      final RegExp builtRegExp = new RegExp(r'Built (.+)( \(|\.$)');
      final String apkPath = builtRegExp.firstMatch(lastLine)[1];

      section('Installing $apkPath');

105
      await device.adb(<String>['install', '-r', apkPath]);
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

      try {
        section('Launching attach.');
        Process attachProcess = await startProcess(
          path.join(flutterDirectory.path, 'bin', 'flutter'),
          <String>['--suppress-analytics', 'attach', '-d', device.deviceId],
          isBot: false, // we just want to test the output, not have any debugging info
        );

        await testReload(attachProcess, onListening: () async {
          section('Launching app.');
          await device.shellExec('am', <String>['start', '-n', kActivityId]);
        });

        final String currentTime = (await device.shellEval('date', <String>['"+%F %R:%S.000"'])).trim();
        print('Start time on device: $currentTime');
        section('Launching app');
        await device.shellExec('am', <String>['start', '-n', kActivityId]);

        final String observatoryLine = await device.adb(<String>['logcat', '-e', 'Observatory listening on http:', '-m', '1', '-T', currentTime]);
        print('Found observatory line: $observatoryLine');
        final String observatoryPort = new RegExp(r'Observatory listening on http://.*:([0-9]+)').firstMatch(observatoryLine)[1];
        print('Extracted observatory port: $observatoryPort');

        section('Launching attach with given port.');
        attachProcess = await startProcess(
          path.join(flutterDirectory.path, 'bin', 'flutter'),
          <String>['--suppress-analytics', 'attach', '--debug-port', observatoryPort, '-d', device.deviceId],
          isBot: false, // we just want to test the output, not have any debugging info
        );
        await testReload(attachProcess);

      } finally {
        section('Uninstalling');
        await device.adb(<String>['uninstall', kAppId]);
      }
    });
    return new TaskResult.success(null);
  });
}