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

import 'dart:async';

7
import 'package:file/memory.dart';
8
import 'package:flutter_tools/src/application_package.dart';
9
import 'package:flutter_tools/src/base/file_system.dart';
10
import 'package:flutter_tools/src/base/logger.dart';
11
import 'package:flutter_tools/src/base/time.dart';
12
import 'package:flutter_tools/src/build_info.dart';
13
import 'package:flutter_tools/src/build_system/build_system.dart';
14
import 'package:flutter_tools/src/devfs.dart';
15
import 'package:flutter_tools/src/device.dart';
16 17
import 'package:flutter_tools/src/isolated/devfs_web.dart';
import 'package:flutter_tools/src/isolated/resident_web_runner.dart';
18
import 'package:flutter_tools/src/project.dart';
19
import 'package:flutter_tools/src/reporting/reporting.dart';
20
import 'package:flutter_tools/src/resident_runner.dart';
21 22
import 'package:flutter_tools/src/vmservice.dart';
import 'package:test/fake.dart';
23 24

import '../src/common.dart';
25
import '../src/context.dart';
26
import '../src/test_build_system.dart';
27 28

void main() {
29 30 31
  late FakeFlutterDevice mockFlutterDevice;
  late FakeWebDevFS mockWebDevFS;
  late FileSystem fileSystem;
32 33

  setUp(() {
34 35 36 37 38 39 40 41 42 43
    fileSystem = MemoryFileSystem.test();
    mockWebDevFS = FakeWebDevFS();
    final FakeWebDevice mockWebDevice = FakeWebDevice();
    mockFlutterDevice = FakeFlutterDevice(mockWebDevice);
    mockFlutterDevice._devFS = mockWebDevFS;

    fileSystem.file('.packages').writeAsStringSync('\n');
    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
    fileSystem.file(fileSystem.path.join('web', 'index.html')).createSync(recursive: true);
44 45
  });

46 47 48
  testUsingContext('Can successfully run and connect without vmservice', () async {
    final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
    final ResidentWebRunner residentWebRunner = ResidentWebRunner(
49 50 51 52
      mockFlutterDevice,
      flutterProject: project,
      debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
      ipv6: true,
53 54 55 56
      fileSystem: fileSystem,
      logger: BufferLogger.test(),
      systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
      usage: TestUsage(),
57
    );
58 59 60 61 62 63 64 65

    final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
    unawaited(residentWebRunner.run(
      connectionInfoCompleter: connectionInfoCompleter,
    ));
    final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future;

    expect(debugConnectionInfo.wsUri, null);
66
  }, overrides: <Type, Generator>{
67
    BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
68
    FileSystem: () => fileSystem,
69 70
    ProcessManager: () => FakeProcessManager.any(),
  });
71

72
  // Regression test for https://github.com/flutter/flutter/issues/60613
73
  testUsingContext('ResidentWebRunner calls appFailedToStart if initial compilation fails', () async {
74 75 76 77 78 79 80 81 82 83 84
    final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
    final ResidentWebRunner residentWebRunner = ResidentWebRunner(
      mockFlutterDevice,
      flutterProject: project,
      debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
      ipv6: true,
      fileSystem: fileSystem,
      logger: BufferLogger.test(),
      systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
      usage: TestUsage(),
    );
85

86
    expect(() => residentWebRunner.run(), throwsToolExit());
87 88
    expect(await residentWebRunner.waitForAppToFinish(), 1);
  }, overrides: <Type, Generator>{
89
    BuildSystem: () => TestBuildSystem.all(BuildResult(success: false)),
90
    FileSystem: () => fileSystem,
91 92
    ProcessManager: () => FakeProcessManager.any(),
  });
93 94

  // Regression test for https://github.com/flutter/flutter/issues/60613
95
  testUsingContext('ResidentWebRunner calls appFailedToStart if error is thrown during startup', () async {
96 97 98 99 100 101 102 103 104 105 106
    final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
    final ResidentWebRunner residentWebRunner = ResidentWebRunner(
      mockFlutterDevice,
      flutterProject: project,
      debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
      ipv6: true,
      fileSystem: fileSystem,
      logger: BufferLogger.test(),
      systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
      usage: TestUsage(),
    );
107

108
    expect(() async => residentWebRunner.run(), throwsException);
109 110
    expect(await residentWebRunner.waitForAppToFinish(), 1);
  }, overrides: <Type, Generator>{
111
    BuildSystem: () => TestBuildSystem.error(Exception('foo')),
112
    FileSystem: () => fileSystem,
113 114
    ProcessManager: () => FakeProcessManager.any(),
  });
115

116
  testUsingContext('Can full restart after attaching', () async {
117 118 119 120 121 122 123 124 125 126 127
    final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
    final ResidentWebRunner residentWebRunner = ResidentWebRunner(
      mockFlutterDevice,
      flutterProject: project,
      debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
      ipv6: true,
      fileSystem: fileSystem,
      logger: BufferLogger.test(),
      systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
      usage: TestUsage(),
    );
128
    final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
129
    unawaited(residentWebRunner.run(
130 131 132 133 134 135
      connectionInfoCompleter: connectionInfoCompleter,
    ));
    await connectionInfoCompleter.future;
    final OperationResult result = await residentWebRunner.restart(fullRestart: true);

    expect(result.code, 0);
136
  }, overrides: <Type, Generator>{
137
    BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
138
    FileSystem: () => fileSystem,
139 140
    ProcessManager: () => FakeProcessManager.any(),
  });
141

142
  testUsingContext('Fails on compilation errors in hot restart', () async {
143 144 145 146 147 148 149 150 151 152 153
    final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
    final ResidentWebRunner residentWebRunner = ResidentWebRunner(
      mockFlutterDevice,
      flutterProject: project,
      debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
      ipv6: true,
      fileSystem: fileSystem,
      logger: BufferLogger.test(),
      systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
      usage: TestUsage(),
    );
154
    final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
155
    unawaited(residentWebRunner.run(
156 157 158 159 160 161 162
      connectionInfoCompleter: connectionInfoCompleter,
    ));
    await connectionInfoCompleter.future;
    final OperationResult result = await residentWebRunner.restart(fullRestart: true);

    expect(result.code, 1);
    expect(result.message, contains('Failed to recompile application.'));
163
  }, overrides: <Type, Generator>{
164 165 166 167
    BuildSystem: () => TestBuildSystem.list(<BuildResult>[
      BuildResult(success: true),
      BuildResult(success: false),
    ]),
168
    FileSystem: () => fileSystem,
169 170
    ProcessManager: () => FakeProcessManager.any(),
  });
171
}
172

173 174 175
class FakeWebDevFS extends Fake implements WebDevFS {
  @override
  List<Uri> get sources => <Uri>[];
176

177 178 179 180 181 182
  @override
  Future<Uri> create() async {
    return Uri.base;
  }
}

183 184 185
// Unfortunately Device, despite not being immutable, has an `operator ==`.
// Until we fix that, we have to also ignore related lints here.
// ignore: avoid_implementing_value_types
186 187 188 189 190 191
class FakeWebDevice extends Fake implements Device {
  @override
  String get name => 'web';

  @override
  Future<bool> stopApp(
192
    ApplicationPackage? app, {
193
    String? userIdentifier,
194 195 196 197 198 199
  }) async {
    return true;
  }

  @override
  Future<LaunchResult> startApp(
200
    ApplicationPackage? package, {
201 202 203 204
    String? mainPath,
    String? route,
    DebuggingOptions? debuggingOptions,
    Map<String, dynamic>? platformArgs,
205 206
    bool prebuiltApplication = false,
    bool ipv6 = false,
207
    String? userIdentifier,
208 209 210
  }) async {
    return LaunchResult.succeeded();
  }
211 212
}

213 214 215 216 217 218 219
class FakeFlutterDevice extends Fake implements FlutterDevice {
  FakeFlutterDevice(this.device);

  @override
  final FakeWebDevice device;


220
  DevFS? _devFS;
221 222

  @override
223
  DevFS? get devFS => _devFS;
224 225

  @override
226
  set devFS(DevFS? value) { }
227 228

  @override
229
  FlutterVmService? vmService;
230
}