blob: ad9383991bf1166892ddb5e49eae131f843f3ff9 [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.
// @dart = 2.8
import 'dart:async';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/isolated/devfs_web.dart';
import 'package:flutter_tools/src/isolated/resident_web_runner.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/web/chrome.dart';
import 'package:flutter_tools/src/web/web_device.dart';
import 'package:mockito/mockito.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/fakes.dart';
import '../src/test_build_system.dart';
void main() {
ResidentWebRunner residentWebRunner;
MockFlutterDevice mockFlutterDevice;
MockWebDevFS mockWebDevFS;
setUp(() {
mockWebDevFS = MockWebDevFS();
final MockWebDevice mockWebDevice = MockWebDevice();
mockFlutterDevice = MockFlutterDevice();
when(mockFlutterDevice.device).thenReturn(mockWebDevice);
when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS);
when(mockWebDevFS.sources).thenReturn(<Uri>[]);
});
void _setupMocks() {
globals.fs.file('.packages').writeAsStringSync('\n');
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
globals.fs.file(globals.fs.path.join('web', 'index.html')).createSync(recursive: true);
final FlutterProject project = FlutterProject.fromDirectoryTest(globals.fs.currentDirectory);
residentWebRunner = ResidentWebRunner(
mockFlutterDevice,
flutterProject: project,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
ipv6: true,
stayResident: true,
urlTunneller: null,
fileSystem: globals.fs,
logger: globals.logger,
systemClock: globals.systemClock,
usage: globals.flutterUsage,
);
}
testUsingContext('Can successfully run and connect without vmservice', () async {
_setupMocks();
final FakeStatusLogger fakeStatusLogger = globals.logger as FakeStatusLogger;
final MockStatus mockStatus = MockStatus();
fakeStatusLogger.status = mockStatus;
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future;
expect(debugConnectionInfo.wsUri, null);
verify(mockStatus.stop()).called(1);
}, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
Logger: () => FakeStatusLogger(BufferLogger.test()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
// Regression test for https://github.com/flutter/flutter/issues/60613
testUsingContext('ResidentWebRunner calls appFailedToStart if initial compilation fails', () async {
_setupMocks();
expect(() async => residentWebRunner.run(), throwsToolExit());
expect(await residentWebRunner.waitForAppToFinish(), 1);
}, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.all(BuildResult(success: false)),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
// Regression test for https://github.com/flutter/flutter/issues/60613
testUsingContext('ResidentWebRunner calls appFailedToStart if error is thrown during startup', () async {
_setupMocks();
expect(() async => residentWebRunner.run(), throwsA(isA<Exception>()));
expect(await residentWebRunner.waitForAppToFinish(), 1);
}, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.error(Exception('foo')),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Can full restart after attaching', () async {
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
expect(result.code, 0);
}, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Fails on compilation errors in hot restart', () async {
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
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.'));
}, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.list(<BuildResult>[
BuildResult(success: true),
BuildResult(success: false),
]),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Correctly performs a full refresh on attached chrome device.', () async {
_setupMocks();
final MockChromeDevice chromeDevice = MockChromeDevice();
final MockChrome chrome = MockChrome();
final MockChromeConnection mockChromeConnection = MockChromeConnection();
final MockChromeTab mockChromeTab = MockChromeTab();
final MockWipConnection mockWipConnection = MockWipConnection();
final MockChromiumLauncher chromiumLauncher = MockChromiumLauncher();
when(mockChromeConnection.getTab(any)).thenAnswer((Invocation invocation) async {
return mockChromeTab;
});
when(mockChromeTab.connect()).thenAnswer((Invocation invocation) async {
return mockWipConnection;
});
when(chromiumLauncher.connectedInstance).thenAnswer((Invocation invocation) async {
return chrome;
});
when(chrome.chromeConnection).thenReturn(mockChromeConnection);
when(chromeDevice.chromeLauncher).thenReturn(chromiumLauncher);
when(mockFlutterDevice.device).thenReturn(chromeDevice);
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
expect(result.code, 0);
verify(mockWipConnection.sendCommand('Page.reload', <String, Object>{
'ignoreCache': true,
})).called(1);
}, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
}
class MockWebDevFS extends Mock implements WebDevFS {}
class MockWebDevice extends Mock implements Device {}
class MockStatus extends Mock implements Status {}
class MockFlutterDevice extends Mock implements FlutterDevice {}
class MockChromeDevice extends Mock implements ChromiumDevice {}
class MockChrome extends Mock implements Chromium {}
class MockChromeConnection extends Mock implements ChromeConnection {}
class MockChromeTab extends Mock implements ChromeTab {}
class MockWipConnection extends Mock implements WipConnection {}
class MockChromiumLauncher extends Mock implements ChromiumLauncher {}