runner.dart 7.32 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7
import 'package:meta/meta.dart';
8 9 10 11 12

import '../artifacts.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
13
import '../device.dart';
14
import '../globals.dart' as globals;
15
import '../project.dart';
16
import '../web/chrome.dart';
17
import '../web/memory_fs.dart';
18
import 'flutter_platform.dart' as loader;
19
import 'flutter_web_platform.dart';
20
import 'test_wrapper.dart';
21
import 'watcher.dart';
22
import 'web_test_compiler.dart';
23

24 25 26
/// A class that abstracts launching the test process from the test runner.
abstract class FlutterTestRunner {
  const factory FlutterTestRunner() = _FlutterTestRunnerImpl;
27

28 29 30 31
  /// Runs tests using package:test and the Flutter engine.
  Future<int> runTests(
    TestWrapper testWrapper,
    List<String> testFiles, {
32
    @required DebuggingOptions debuggingOptions,
33 34
    List<String> names = const <String>[],
    List<String> plainNames = const <String>[],
35 36
    String tags,
    String excludeTags,
37 38 39 40 41 42 43 44 45 46 47 48 49
    bool enableObservatory = false,
    bool ipv6 = false,
    bool machine = false,
    String precompiledDillPath,
    Map<String, String> precompiledDillFiles,
    bool updateGoldens = false,
    TestWatcher watcher,
    @required int concurrency,
    bool buildTestAssets = false,
    FlutterProject flutterProject,
    String icudtlPath,
    Directory coverageDirectory,
    bool web = false,
50
    String randomSeed,
51 52
    String reporter,
    String timeout,
53
    bool runSkipped = false,
54 55
    int shardIndex,
    int totalShards,
56 57
    Device integrationTestDevice,
    String integrationTestUserIdentifier,
58 59 60 61 62 63 64 65 66 67
  });
}

class _FlutterTestRunnerImpl implements FlutterTestRunner {
  const _FlutterTestRunnerImpl();

  @override
  Future<int> runTests(
    TestWrapper testWrapper,
    List<String> testFiles, {
68
    @required DebuggingOptions debuggingOptions,
69 70
    List<String> names = const <String>[],
    List<String> plainNames = const <String>[],
71 72
    String tags,
    String excludeTags,
73 74 75 76 77 78 79 80 81 82 83 84 85
    bool enableObservatory = false,
    bool ipv6 = false,
    bool machine = false,
    String precompiledDillPath,
    Map<String, String> precompiledDillFiles,
    bool updateGoldens = false,
    TestWatcher watcher,
    @required int concurrency,
    bool buildTestAssets = false,
    FlutterProject flutterProject,
    String icudtlPath,
    Directory coverageDirectory,
    bool web = false,
86
    String randomSeed,
87 88
    String reporter,
    String timeout,
89
    bool runSkipped = false,
90 91
    int shardIndex,
    int totalShards,
92 93
    Device integrationTestDevice,
    String integrationTestUserIdentifier,
94 95 96 97 98 99 100 101
  }) async {
    // Configure package:test to use the Flutter engine for child processes.
    final String shellPath = globals.artifacts.getArtifactPath(Artifact.flutterTester);

    // Compute the command-line arguments for package:test.
    final List<String> testArgs = <String>[
      if (!globals.terminal.supportsColor)
        '--no-color',
102
      if (debuggingOptions.startPaused)
103 104 105 106
        '--pause-after-load',
      if (machine)
        ...<String>['-r', 'json']
      else
107 108 109
        ...<String>['-r', reporter ?? 'compact'],
      if (timeout != null)
        ...<String>['--timeout', timeout],
110 111 112 113 114
      '--concurrency=$concurrency',
      for (final String name in names)
        ...<String>['--name', name],
      for (final String plainName in plainNames)
        ...<String>['--plain-name', plainName],
115 116
      if (randomSeed != null)
        '--test-randomize-ordering-seed=$randomSeed',
117 118 119 120
      if (tags != null)
        ...<String>['--tags', tags],
      if (excludeTags != null)
        ...<String>['--exclude-tags', excludeTags],
121 122
      if (runSkipped)
        '--run-skipped',
123 124 125 126
      if (totalShards != null)
        '--total-shards=$totalShards',
      if (shardIndex != null)
        '--shard-index=$shardIndex',
127 128 129 130 131 132 133
    ];
    if (web) {
      final String tempBuildDir = globals.fs.systemTempDirectory
        .createTempSync('flutter_test.')
        .absolute
        .uri
        .toFilePath();
134 135 136 137 138 139 140 141
      final WebMemoryFS result = await WebTestCompiler(
        logger: globals.logger,
        fileSystem: globals.fs,
        platform: globals.platform,
        artifacts: globals.artifacts,
        processManager: globals.processManager,
        config: globals.config,
      ).initialize(
142 143 144
        projectDirectory: flutterProject.directory,
        testOutputDir: tempBuildDir,
        testFiles: testFiles,
145
        buildInfo: debuggingOptions.buildInfo,
146
      );
147
      if (result == null) {
148 149 150 151 152 153 154 155 156
        throwToolExit('Failed to compile tests');
      }
      testArgs
        ..add('--platform=chrome')
        ..add('--')
        ..addAll(testFiles);
      testWrapper.registerPlatformPlugin(
        <Runtime>[Runtime.chrome],
        () {
157 158
          // TODO(jonahwilliams): refactor this into a factory that handles
          // providing dependencies.
159 160 161 162 163
          return FlutterWebPlatform.start(
            flutterProject.directory.path,
            updateGoldens: updateGoldens,
            shellPath: shellPath,
            flutterProject: flutterProject,
164 165 166
            pauseAfterLoad: debuggingOptions.startPaused,
            nullAssertions: debuggingOptions.nullAssertions,
            buildInfo: debuggingOptions.buildInfo,
167
            webMemoryFS: result,
168 169 170 171 172 173 174 175 176 177 178
            logger: globals.logger,
            fileSystem: globals.fs,
            artifacts: globals.artifacts,
            chromiumLauncher: ChromiumLauncher(
              fileSystem: globals.fs,
              platform: globals.platform,
              processManager: globals.processManager,
              operatingSystemUtils: globals.os,
              browserFinder: findChromeExecutable,
              logger: globals.logger,
            ),
179 180 181 182 183
          );
        },
      );
      await testWrapper.main(testArgs);
      return exitCode;
184
    }
185

186 187 188 189 190 191 192
    if (integrationTestDevice != null) {
      // Without this, some async exceptions which are caught will surface when
      // debugging tests.
      // TODO(jiahaog): Remove this once https://github.com/dart-lang/stack_trace/issues/106 is fixed.
      testArgs.add('--no-chain-stack-traces');
    }

193 194 195
    testArgs
      ..add('--')
      ..addAll(testFiles);
196 197 198 199 200 201 202

    final InternetAddressType serverType =
        ipv6 ? InternetAddressType.IPv6 : InternetAddressType.IPv4;

    final loader.FlutterPlatform platform = loader.installHook(
      testWrapper: testWrapper,
      shellPath: shellPath,
203
      debuggingOptions: debuggingOptions,
204 205 206 207 208 209 210 211 212 213 214
      watcher: watcher,
      enableObservatory: enableObservatory,
      machine: machine,
      serverType: serverType,
      precompiledDillPath: precompiledDillPath,
      precompiledDillFiles: precompiledDillFiles,
      updateGoldens: updateGoldens,
      buildTestAssets: buildTestAssets,
      projectRootDirectory: globals.fs.currentDirectory.uri,
      flutterProject: flutterProject,
      icudtlPath: icudtlPath,
215 216
      integrationTestDevice: integrationTestDevice,
      integrationTestUserIdentifier: integrationTestUserIdentifier,
217
    );
218

219 220 221
    try {
      globals.printTrace('running test package with arguments: $testArgs');
      await testWrapper.main(testArgs);
222

223 224
      // test.main() sets dart:io's exitCode global.
      globals.printTrace('test package returned with exit code $exitCode');
225

226 227 228 229
      return exitCode;
    } finally {
      await platform.close();
    }
230 231
  }
}