mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

* Migrate packages/flutter_tools/test/general.shard, part 2/2 * Fix analysis * Fix tests * Fix analysis * Apply suggestions from code review Co-authored-by: Christopher Fujino <fujino@google.com> Co-authored-by: Christopher Fujino <fujino@google.com>
1607 lines
52 KiB
Dart
1607 lines
52 KiB
Dart
// 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 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:dwds/dwds.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/application_package.dart';
|
|
import 'package:flutter_tools/src/asset.dart';
|
|
import 'package:flutter_tools/src/base/dds.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:flutter_tools/src/base/time.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/build_system/targets/shader_compiler.dart';
|
|
import 'package:flutter_tools/src/compile.dart';
|
|
import 'package:flutter_tools/src/devfs.dart';
|
|
import 'package:flutter_tools/src/device.dart';
|
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
|
import 'package:flutter_tools/src/isolated/devfs_web.dart';
|
|
import 'package:flutter_tools/src/isolated/resident_web_runner.dart';
|
|
import 'package:flutter_tools/src/project.dart';
|
|
import 'package:flutter_tools/src/reporting/reporting.dart';
|
|
import 'package:flutter_tools/src/resident_devtools_handler.dart';
|
|
import 'package:flutter_tools/src/resident_runner.dart';
|
|
import 'package:flutter_tools/src/vmservice.dart';
|
|
import 'package:flutter_tools/src/web/chrome.dart';
|
|
import 'package:flutter_tools/src/web/web_device.dart';
|
|
import 'package:package_config/package_config.dart';
|
|
import 'package:package_config/package_config_types.dart';
|
|
import 'package:test/fake.dart';
|
|
import 'package:vm_service/vm_service.dart' as vm_service;
|
|
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
|
|
|
|
import '../src/common.dart';
|
|
import '../src/context.dart';
|
|
import '../src/fake_vm_services.dart';
|
|
|
|
const List<VmServiceExpectation> kAttachLogExpectations =
|
|
<VmServiceExpectation>[
|
|
FakeVmServiceRequest(
|
|
method: 'streamListen',
|
|
args: <String, Object>{
|
|
'streamId': 'Stdout',
|
|
},
|
|
),
|
|
FakeVmServiceRequest(
|
|
method: 'streamListen',
|
|
args: <String, Object>{
|
|
'streamId': 'Stderr',
|
|
},
|
|
),
|
|
];
|
|
|
|
const List<VmServiceExpectation> kAttachIsolateExpectations =
|
|
<VmServiceExpectation>[
|
|
FakeVmServiceRequest(method: 'streamListen', args: <String, Object>{
|
|
'streamId': 'Isolate',
|
|
}),
|
|
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
|
|
'service': 'reloadSources',
|
|
'alias': 'Flutter Tools',
|
|
}),
|
|
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
|
|
'service': 'flutterVersion',
|
|
'alias': 'Flutter Tools',
|
|
}),
|
|
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
|
|
'service': 'flutterMemoryInfo',
|
|
'alias': 'Flutter Tools',
|
|
}),
|
|
FakeVmServiceRequest(
|
|
method: 'streamListen',
|
|
args: <String, Object>{
|
|
'streamId': 'Extension',
|
|
},
|
|
),
|
|
];
|
|
|
|
const List<VmServiceExpectation> kAttachExpectations = <VmServiceExpectation>[
|
|
...kAttachLogExpectations,
|
|
...kAttachIsolateExpectations,
|
|
];
|
|
|
|
void main() {
|
|
late FakeDebugConnection debugConnection;
|
|
late FakeChromeDevice chromeDevice;
|
|
late FakeAppConnection appConnection;
|
|
late FakeFlutterDevice flutterDevice;
|
|
late FakeWebDevFS webDevFS;
|
|
late FakeResidentCompiler residentCompiler;
|
|
late FakeChromeConnection chromeConnection;
|
|
late FakeChromeTab chromeTab;
|
|
late FakeWebServerDevice webServerDevice;
|
|
late FakeDevice mockDevice;
|
|
late FakeVmServiceHost fakeVmServiceHost;
|
|
late FileSystem fileSystem;
|
|
late ProcessManager processManager;
|
|
late TestUsage testUsage;
|
|
|
|
setUp(() {
|
|
testUsage = TestUsage();
|
|
fileSystem = MemoryFileSystem.test();
|
|
processManager = FakeProcessManager.any();
|
|
debugConnection = FakeDebugConnection();
|
|
mockDevice = FakeDevice();
|
|
appConnection = FakeAppConnection();
|
|
webDevFS = FakeWebDevFS();
|
|
residentCompiler = FakeResidentCompiler();
|
|
chromeDevice = FakeChromeDevice();
|
|
chromeConnection = FakeChromeConnection();
|
|
chromeTab = FakeChromeTab('index.html');
|
|
webServerDevice = FakeWebServerDevice();
|
|
flutterDevice = FakeFlutterDevice()
|
|
.._devFS = webDevFS
|
|
..device = mockDevice
|
|
..generator = residentCompiler;
|
|
fileSystem.file('.packages').writeAsStringSync('\n');
|
|
});
|
|
|
|
void setupMocks() {
|
|
fileSystem.file('pubspec.yaml').createSync();
|
|
fileSystem.file('lib/main.dart').createSync(recursive: true);
|
|
fileSystem.file('web/index.html').createSync(recursive: true);
|
|
webDevFS.report = UpdateFSReport(success: true);
|
|
debugConnection.fakeVmServiceHost = () => fakeVmServiceHost;
|
|
webDevFS.result = ConnectionResult(
|
|
appConnection,
|
|
debugConnection,
|
|
debugConnection.vmService,
|
|
);
|
|
debugConnection.uri = 'ws://127.0.0.1/abcd/';
|
|
chromeConnection.tabs.add(chromeTab);
|
|
}
|
|
|
|
testUsingContext(
|
|
'runner with web server device does not support debugging without --start-paused',
|
|
() {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
flutterDevice.device = WebServerDevice(
|
|
logger: BufferLogger.test(),
|
|
);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
final ResidentRunner profileResidentWebRunner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
ipv6: true,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
|
|
expect(profileResidentWebRunner.debuggingEnabled, false);
|
|
|
|
flutterDevice.device = chromeDevice;
|
|
|
|
expect(residentWebRunner.debuggingEnabled, true);
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext(
|
|
'runner with web server device supports debugging with --start-paused',
|
|
() {
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
setupMocks();
|
|
flutterDevice.device = WebServerDevice(
|
|
logger: BufferLogger.test(),
|
|
);
|
|
final ResidentRunner profileResidentWebRunner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions:
|
|
DebuggingOptions.enabled(BuildInfo.debug, startPaused: true),
|
|
ipv6: true,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
|
|
expect(profileResidentWebRunner.uri, webDevFS.baseUri);
|
|
expect(profileResidentWebRunner.debuggingEnabled, true);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
testUsingContext('profile does not supportsServiceProtocol', () {
|
|
final ResidentRunner residentWebRunner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
ipv6: true,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
flutterDevice.device = chromeDevice;
|
|
final ResidentRunner profileResidentWebRunner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile),
|
|
ipv6: true,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
|
|
expect(profileResidentWebRunner.supportsServiceProtocol, false);
|
|
expect(residentWebRunner.supportsServiceProtocol, true);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Can successfully run and connect to vmservice', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, logger: logger);
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
final DebugConnectionInfo debugConnectionInfo =
|
|
await connectionInfoCompleter.future;
|
|
|
|
expect(appConnection.ranMain, true);
|
|
expect(logger.statusText,
|
|
contains('Debug service listening on ws://127.0.0.1/abcd/'));
|
|
expect(debugConnectionInfo.wsUri.toString(), 'ws://127.0.0.1/abcd/');
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('WebRunner copies compiled app.dill to cache during startup',
|
|
() async {
|
|
final DebuggingOptions debuggingOptions = DebuggingOptions.enabled(
|
|
const BuildInfo(BuildMode.debug, null, treeShakeIcons: false),
|
|
);
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, debuggingOptions: debuggingOptions);
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
|
|
residentWebRunner.artifactDirectory
|
|
.childFile('app.dill')
|
|
.writeAsStringSync('ABC');
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
expect(
|
|
await fileSystem
|
|
.file(fileSystem.path.join('build', 'cache.dill'))
|
|
.readAsString(),
|
|
'ABC');
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext(
|
|
'WebRunner copies compiled app.dill to cache during startup with track-widget-creation',
|
|
() async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
|
|
residentWebRunner.artifactDirectory
|
|
.childFile('app.dill')
|
|
.writeAsStringSync('ABC');
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
expect(
|
|
await fileSystem
|
|
.file(fileSystem.path.join('build', 'cache.dill.track.dill'))
|
|
.readAsString(),
|
|
'ABC');
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/60613
|
|
testUsingContext(
|
|
'ResidentWebRunner calls appFailedToStart if initial compilation fails',
|
|
() async {
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fileSystem
|
|
.file(globals.fs.path.join('lib', 'main.dart'))
|
|
.createSync(recursive: true);
|
|
webDevFS.report = UpdateFSReport();
|
|
|
|
expect(await residentWebRunner.run(), 1);
|
|
// Completing this future ensures that the daemon can exit correctly.
|
|
expect(await residentWebRunner.waitForAppToFinish(), 1);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext(
|
|
'Can successfully run without an index.html including status warning',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
fileSystem.file(fileSystem.path.join('web', 'index.html')).deleteSync();
|
|
final ResidentWebRunner residentWebRunner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
ipv6: true,
|
|
stayResident: false,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
|
|
expect(await residentWebRunner.run(), 0);
|
|
expect(logger.statusText,
|
|
contains('This application is not configured to build on the web'));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Can successfully run and disconnect with --no-resident',
|
|
() async {
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
final ResidentRunner residentWebRunner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
ipv6: true,
|
|
stayResident: false,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
|
|
expect(await residentWebRunner.run(), 0);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Listens to stdout and stderr streams before running main',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, logger: logger);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachLogExpectations,
|
|
FakeVmServiceStreamResponse(
|
|
streamId: 'Stdout',
|
|
event: vm_service.Event(
|
|
timestamp: 0,
|
|
kind: vm_service.EventStreams.kStdout,
|
|
bytes: base64.encode(utf8.encode('THIS MESSAGE IS IMPORTANT'))),
|
|
),
|
|
FakeVmServiceStreamResponse(
|
|
streamId: 'Stderr',
|
|
event: vm_service.Event(
|
|
timestamp: 0,
|
|
kind: vm_service.EventStreams.kStderr,
|
|
bytes: base64.encode(utf8.encode('SO IS THIS'))),
|
|
),
|
|
...kAttachIsolateExpectations,
|
|
]);
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
expect(logger.statusText, contains('THIS MESSAGE IS IMPORTANT'));
|
|
expect(logger.statusText, contains('SO IS THIS'));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Listens to extension events with structured errors',
|
|
() async {
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, logger: testLogger);
|
|
final Map<String, String> extensionData = <String, String>{
|
|
'test': 'data',
|
|
'renderedErrorText': 'error text',
|
|
};
|
|
final Map<String, String> emptyExtensionData = <String, String>{
|
|
'test': 'data',
|
|
'renderedErrorText': '',
|
|
};
|
|
final Map<String, String> nonStructuredErrorData = <String, String>{
|
|
'other': 'other stuff',
|
|
};
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachExpectations,
|
|
FakeVmServiceStreamResponse(
|
|
streamId: 'Extension',
|
|
event: vm_service.Event(
|
|
timestamp: 0,
|
|
extensionKind: 'Flutter.Error',
|
|
extensionData: vm_service.ExtensionData.parse(extensionData),
|
|
kind: vm_service.EventStreams.kExtension,
|
|
),
|
|
),
|
|
// Empty error text should not break anything.
|
|
FakeVmServiceStreamResponse(
|
|
streamId: 'Extension',
|
|
event: vm_service.Event(
|
|
timestamp: 0,
|
|
extensionKind: 'Flutter.Error',
|
|
extensionData: vm_service.ExtensionData.parse(emptyExtensionData),
|
|
kind: vm_service.EventStreams.kExtension,
|
|
),
|
|
),
|
|
// This is not Flutter.Error kind data, so it should not be logged.
|
|
FakeVmServiceStreamResponse(
|
|
streamId: 'Extension',
|
|
event: vm_service.Event(
|
|
timestamp: 0,
|
|
extensionKind: 'Other',
|
|
extensionData: vm_service.ExtensionData.parse(nonStructuredErrorData),
|
|
kind: vm_service.EventStreams.kExtension,
|
|
),
|
|
),
|
|
]);
|
|
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
await null;
|
|
|
|
expect(testLogger.statusText, contains('\nerror text'));
|
|
expect(testLogger.statusText, isNot(contains('other stuff')));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Does not run main with --start-paused', () async {
|
|
final ResidentRunner residentWebRunner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions:
|
|
DebuggingOptions.enabled(BuildInfo.debug, startPaused: true),
|
|
ipv6: true,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
expect(appConnection.ranMain, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Can hot reload after attaching', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(
|
|
flutterDevice,
|
|
logger: logger,
|
|
systemClock: SystemClock.fixed(DateTime(2001)),
|
|
);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachExpectations,
|
|
const FakeVmServiceRequest(
|
|
method: 'hotRestart',
|
|
jsonResponse: <String, Object>{
|
|
'type': 'Success',
|
|
}),
|
|
const FakeVmServiceRequest(
|
|
method: 'streamListen',
|
|
args: <String, Object>{
|
|
'streamId': 'Isolate',
|
|
},
|
|
),
|
|
]);
|
|
setupMocks();
|
|
final TestChromiumLauncher chromiumLauncher = TestChromiumLauncher();
|
|
final Chromium chrome =
|
|
Chromium(1, chromeConnection, chromiumLauncher: chromiumLauncher);
|
|
chromiumLauncher.setInstance(chrome);
|
|
|
|
flutterDevice.device = GoogleChromeDevice(
|
|
fileSystem: fileSystem,
|
|
chromiumLauncher: chromiumLauncher,
|
|
logger: BufferLogger.test(),
|
|
platform: FakePlatform(),
|
|
processManager: FakeProcessManager.any(),
|
|
);
|
|
webDevFS.report = UpdateFSReport(success: true);
|
|
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
final DebugConnectionInfo debugConnectionInfo =
|
|
await connectionInfoCompleter.future;
|
|
|
|
expect(debugConnectionInfo, isNotNull);
|
|
|
|
final OperationResult result = await residentWebRunner.restart();
|
|
|
|
expect(logger.statusText, contains('Restarted application in'));
|
|
expect(result.code, 0);
|
|
expect(webDevFS.mainUri.toString(), contains('entrypoint.dart'));
|
|
|
|
// ensure that analytics are sent.
|
|
expect(testUsage.events, <TestUsageEvent>[
|
|
TestUsageEvent('hot', 'restart',
|
|
parameters: CustomDimensions.fromMap(<String, String>{
|
|
'cd27': 'web-javascript',
|
|
'cd28': '',
|
|
'cd29': 'false',
|
|
'cd30': 'true',
|
|
'cd13': '0',
|
|
'cd48': 'false'
|
|
})),
|
|
]);
|
|
expect(testUsage.timings, const <TestTimingEvent>[
|
|
TestTimingEvent('hot', 'web-incremental-restart', Duration.zero),
|
|
]);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Can hot restart after attaching', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(
|
|
flutterDevice,
|
|
logger: logger,
|
|
systemClock: SystemClock.fixed(DateTime(2001)),
|
|
);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachExpectations,
|
|
const FakeVmServiceRequest(
|
|
method: 'hotRestart',
|
|
jsonResponse: <String, Object>{
|
|
'type': 'Success',
|
|
}),
|
|
]);
|
|
setupMocks();
|
|
final TestChromiumLauncher chromiumLauncher = TestChromiumLauncher();
|
|
final Chromium chrome =
|
|
Chromium(1, chromeConnection, chromiumLauncher: chromiumLauncher);
|
|
chromiumLauncher.setInstance(chrome);
|
|
|
|
flutterDevice.device = GoogleChromeDevice(
|
|
fileSystem: fileSystem,
|
|
chromiumLauncher: chromiumLauncher,
|
|
logger: BufferLogger.test(),
|
|
platform: FakePlatform(),
|
|
processManager: FakeProcessManager.any(),
|
|
);
|
|
webDevFS.report = UpdateFSReport(success: true);
|
|
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
final OperationResult result =
|
|
await residentWebRunner.restart(fullRestart: true);
|
|
|
|
// Ensure that generated entrypoint is generated correctly.
|
|
expect(webDevFS.mainUri, isNotNull);
|
|
final String entrypointContents =
|
|
fileSystem.file(webDevFS.mainUri).readAsStringSync();
|
|
expect(entrypointContents, contains('// Flutter web bootstrap script'));
|
|
expect(entrypointContents, contains("import 'dart:ui' as ui;"));
|
|
expect(entrypointContents, contains('await ui.webOnlyWarmupEngine('));
|
|
|
|
expect(logger.statusText, contains('Restarted application in'));
|
|
expect(result.code, 0);
|
|
|
|
// ensure that analytics are sent.
|
|
expect(testUsage.events, <TestUsageEvent>[
|
|
TestUsageEvent('hot', 'restart',
|
|
parameters: CustomDimensions.fromMap(<String, String>{
|
|
'cd27': 'web-javascript',
|
|
'cd28': '',
|
|
'cd29': 'false',
|
|
'cd30': 'true',
|
|
'cd13': '0',
|
|
'cd48': 'false'
|
|
})),
|
|
]);
|
|
expect(testUsage.timings, const <TestTimingEvent>[
|
|
TestTimingEvent('hot', 'web-incremental-restart', Duration.zero),
|
|
]);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Can hot restart after attaching with web-server device',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(
|
|
flutterDevice,
|
|
logger: logger,
|
|
systemClock: SystemClock.fixed(DateTime(2001)),
|
|
);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations);
|
|
setupMocks();
|
|
flutterDevice.device = webServerDevice;
|
|
webDevFS.report = UpdateFSReport(success: true);
|
|
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
final OperationResult result =
|
|
await residentWebRunner.restart(fullRestart: true);
|
|
|
|
expect(logger.statusText, contains('Restarted application in'));
|
|
expect(result.code, 0);
|
|
|
|
// web-server device does not send restart analytics
|
|
expect(testUsage.events, isEmpty);
|
|
expect(testUsage.timings, isEmpty);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('web resident runner is debuggable', () {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
|
|
expect(residentWebRunner.debuggingEnabled, true);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Exits when initial compile fails', () async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
setupMocks();
|
|
webDevFS.report = UpdateFSReport();
|
|
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
|
|
expect(await residentWebRunner.run(), 1);
|
|
expect(testUsage.events, isEmpty);
|
|
expect(testUsage.timings, isEmpty);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext(
|
|
'Faithfully displays stdout messages with leading/trailing spaces',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, logger: logger);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachLogExpectations,
|
|
FakeVmServiceStreamResponse(
|
|
streamId: 'Stdout',
|
|
event: vm_service.Event(
|
|
timestamp: 0,
|
|
kind: vm_service.EventStreams.kStdout,
|
|
bytes: base64.encode(
|
|
utf8.encode(
|
|
' This is a message with 4 leading and trailing spaces '),
|
|
),
|
|
),
|
|
),
|
|
...kAttachIsolateExpectations,
|
|
]);
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
expect(
|
|
logger.statusText,
|
|
contains(
|
|
' This is a message with 4 leading and trailing spaces '));
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Fails on compilation errors in hot restart', () async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
webDevFS.report = UpdateFSReport();
|
|
|
|
final OperationResult result =
|
|
await residentWebRunner.restart(fullRestart: true);
|
|
|
|
expect(result.code, 1);
|
|
expect(result.message, contains('Failed to recompile application.'));
|
|
expect(testUsage.events, isEmpty);
|
|
expect(testUsage.timings, isEmpty);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext(
|
|
'Fails non-fatally on vmservice response error for hot restart',
|
|
() async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachExpectations,
|
|
const FakeVmServiceRequest(
|
|
method: 'hotRestart',
|
|
jsonResponse: <String, Object>{
|
|
'type': 'Failed',
|
|
},
|
|
),
|
|
]);
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
final OperationResult result = await residentWebRunner.restart();
|
|
|
|
expect(result.code, 0);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Fails fatally on Vm Service error response', () async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachExpectations,
|
|
const FakeVmServiceRequest(
|
|
method: 'hotRestart',
|
|
// Failed response,
|
|
errorCode: RPCErrorCodes.kInternalError,
|
|
),
|
|
]);
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
final OperationResult result = await residentWebRunner.restart();
|
|
|
|
expect(result.code, 1);
|
|
expect(result.message, contains(RPCErrorCodes.kInternalError.toString()));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('printHelp without details shows hot restart help message',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, logger: logger);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
residentWebRunner.printHelp(details: false);
|
|
|
|
expect(logger.statusText, contains('To hot restart changes'));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('cleanup of resources is safe to call multiple times',
|
|
() async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
mockDevice.dds = DartDevelopmentService();
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachExpectations,
|
|
]);
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
await residentWebRunner.exit();
|
|
await residentWebRunner.exit();
|
|
|
|
expect(debugConnection.didClose, false);
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('cleans up Chrome if tab is closed', () async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachExpectations,
|
|
]);
|
|
setupMocks();
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
final Future<int?> result = residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
);
|
|
await connectionInfoCompleter.future;
|
|
debugConnection.completer.complete();
|
|
|
|
await result;
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Prints target and device name on run', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, logger: logger);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachExpectations,
|
|
]);
|
|
setupMocks();
|
|
mockDevice.name = 'Chromez';
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(residentWebRunner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
expect(
|
|
logger.statusText,
|
|
contains(
|
|
'Launching ${fileSystem.path.join('lib', 'main.dart')} on '
|
|
'Chromez in debug mode',
|
|
));
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Sends launched app.webLaunchUrl event for Chrome device',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
|
...kAttachLogExpectations,
|
|
...kAttachIsolateExpectations,
|
|
]);
|
|
setupMocks();
|
|
final FakeChromeConnection chromeConnection = FakeChromeConnection();
|
|
final TestChromiumLauncher chromiumLauncher = TestChromiumLauncher();
|
|
final Chromium chrome =
|
|
Chromium(1, chromeConnection, chromiumLauncher: chromiumLauncher);
|
|
chromiumLauncher.setInstance(chrome);
|
|
|
|
flutterDevice.device = GoogleChromeDevice(
|
|
fileSystem: fileSystem,
|
|
chromiumLauncher: chromiumLauncher,
|
|
logger: logger,
|
|
platform: FakePlatform(),
|
|
processManager: FakeProcessManager.any(),
|
|
);
|
|
webDevFS.baseUri = Uri.parse('http://localhost:8765/app/');
|
|
|
|
final FakeChromeTab chromeTab = FakeChromeTab('index.html');
|
|
chromeConnection.tabs.add(chromeTab);
|
|
|
|
final ResidentWebRunner runner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
ipv6: true,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(runner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
// Ensure we got the URL and that it was already launched.
|
|
expect(
|
|
logger.eventText,
|
|
contains(json.encode(
|
|
<String, Object>{
|
|
'name': 'app.webLaunchUrl',
|
|
'args': <String, Object>{
|
|
'url': 'http://localhost:8765/app/',
|
|
'launched': true,
|
|
},
|
|
},
|
|
)));
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext(
|
|
'Sends unlaunched app.webLaunchUrl event for Web Server device',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
setupMocks();
|
|
flutterDevice.device = WebServerDevice(
|
|
logger: logger,
|
|
);
|
|
webDevFS.baseUri = Uri.parse('http://localhost:8765/app/');
|
|
|
|
final ResidentWebRunner runner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
ipv6: true,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
|
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
|
Completer<DebugConnectionInfo>();
|
|
unawaited(runner.run(
|
|
connectionInfoCompleter: connectionInfoCompleter,
|
|
));
|
|
await connectionInfoCompleter.future;
|
|
|
|
// Ensure we got the URL and that it was not already launched.
|
|
expect(
|
|
logger.eventText,
|
|
contains(json.encode(
|
|
<String, Object>{
|
|
'name': 'app.webLaunchUrl',
|
|
'args': <String, Object>{
|
|
'url': 'http://localhost:8765/app/',
|
|
'launched': false,
|
|
},
|
|
},
|
|
)));
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('ResidentWebRunner generates files when l10n.yaml exists', () async {
|
|
fakeVmServiceHost =
|
|
FakeVmServiceHost(requests: kAttachExpectations.toList());
|
|
setupMocks();
|
|
final ResidentRunner residentWebRunner = ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
ipv6: true,
|
|
stayResident: false,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
usage: globals.flutterUsage,
|
|
systemClock: globals.systemClock,
|
|
);
|
|
|
|
// Create necessary files.
|
|
globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
|
.createSync(recursive: true);
|
|
globals.fs.file(globals.fs.path.join('lib', 'l10n', 'app_en.arb'))
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('''
|
|
{
|
|
"helloWorld": "Hello, World!",
|
|
"@helloWorld": {
|
|
"description": "Sample description"
|
|
}
|
|
}''');
|
|
globals.fs.file('l10n.yaml').createSync();
|
|
globals.fs.file('pubspec.yaml').writeAsStringSync('''
|
|
flutter:
|
|
generate: true
|
|
''');
|
|
globals.fs.directory('.dart_tool')
|
|
.childFile('package_config.json')
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('''
|
|
{
|
|
"configVersion": 2,
|
|
"packages": [
|
|
{
|
|
"name": "path_provider_linux",
|
|
"rootUri": "../../../path_provider_linux",
|
|
"packageUri": "lib/",
|
|
"languageVersion": "2.12"
|
|
}
|
|
]
|
|
}
|
|
''');
|
|
expect(await residentWebRunner.run(), 0);
|
|
final File generatedLocalizationsFile = globals.fs.directory('.dart_tool')
|
|
.childDirectory('flutter_gen')
|
|
.childDirectory('gen_l10n')
|
|
.childFile('app_localizations.dart');
|
|
expect(generatedLocalizationsFile.existsSync(), isTrue);
|
|
// Completing this future ensures that the daemon can exit correctly.
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
// While this file should be ignored on web, generating it here will cause a
|
|
// perf regression in hot restart.
|
|
testUsingContext('Does not generate dart_plugin_registrant.dart', () async {
|
|
// Create necessary files for [DartPluginRegistrantTarget]
|
|
final File packageConfig =
|
|
globals.fs.directory('.dart_tool').childFile('package_config.json');
|
|
packageConfig.createSync(recursive: true);
|
|
packageConfig.writeAsStringSync('''
|
|
{
|
|
"configVersion": 2,
|
|
"packages": [
|
|
{
|
|
"name": "path_provider_linux",
|
|
"rootUri": "../../../path_provider_linux",
|
|
"packageUri": "lib/",
|
|
"languageVersion": "2.12"
|
|
}
|
|
]
|
|
}
|
|
''');
|
|
// Start with a dart_plugin_registrant.dart file.
|
|
globals.fs
|
|
.directory('.dart_tool')
|
|
.childDirectory('flutter_build')
|
|
.childFile('dart_plugin_registrant.dart')
|
|
.createSync(recursive: true);
|
|
|
|
final FlutterProject project =
|
|
FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
|
|
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
await residentWebRunner.runSourceGenerators();
|
|
|
|
// dart_plugin_registrant.dart should be untouched, indicating that its
|
|
// generation didn't run. If it had run, the file would have been removed as
|
|
// there are no plugins in the project.
|
|
expect(project.dartPluginRegistrant.existsSync(), true);
|
|
expect(project.dartPluginRegistrant.readAsStringSync(), '');
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Successfully turns WebSocketException into ToolExit',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, logger: logger);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
setupMocks();
|
|
webDevFS.exception = const WebSocketException();
|
|
|
|
await expectLater(residentWebRunner.run, throwsToolExit());
|
|
expect(logger.errorText, contains('WebSocketException'));
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Successfully turns AppConnectionException into ToolExit',
|
|
() async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
setupMocks();
|
|
webDevFS.exception = AppConnectionException('');
|
|
|
|
await expectLater(residentWebRunner.run, throwsToolExit());
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Successfully turns ChromeDebugError into ToolExit',
|
|
() async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
setupMocks();
|
|
|
|
webDevFS.exception = ChromeDebugException(<String, Object?>{
|
|
'text': 'error',
|
|
});
|
|
|
|
await expectLater(residentWebRunner.run, throwsToolExit());
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Rethrows unknown Exception type from dwds', () async {
|
|
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
setupMocks();
|
|
webDevFS.exception = Exception();
|
|
|
|
await expectLater(residentWebRunner.run, throwsException);
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('Rethrows unknown Error type from dwds tooling', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final ResidentRunner residentWebRunner =
|
|
setUpResidentRunner(flutterDevice, logger: logger);
|
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
|
setupMocks();
|
|
webDevFS.exception = StateError('');
|
|
|
|
await expectLater(residentWebRunner.run, throwsStateError);
|
|
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fileSystem,
|
|
ProcessManager: () => processManager,
|
|
});
|
|
}
|
|
|
|
ResidentRunner setUpResidentRunner(
|
|
FlutterDevice flutterDevice, {
|
|
Logger? logger,
|
|
SystemClock? systemClock,
|
|
DebuggingOptions? debuggingOptions,
|
|
}) {
|
|
return ResidentWebRunner(
|
|
flutterDevice,
|
|
flutterProject:
|
|
FlutterProject.fromDirectoryTest(globals.fs.currentDirectory),
|
|
debuggingOptions:
|
|
debuggingOptions ?? DebuggingOptions.enabled(BuildInfo.debug),
|
|
ipv6: true,
|
|
usage: globals.flutterUsage,
|
|
systemClock: systemClock ?? SystemClock.fixed(DateTime.now()),
|
|
fileSystem: globals.fs,
|
|
logger: logger ?? BufferLogger.test(),
|
|
devtoolsHandler: createNoOpHandler,
|
|
);
|
|
}
|
|
|
|
// 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
|
|
class FakeWebServerDevice extends FakeDevice implements WebServerDevice {}
|
|
|
|
// 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
|
|
class FakeDevice extends Fake implements Device {
|
|
@override
|
|
String name = 'FakeDevice';
|
|
|
|
int count = 0;
|
|
|
|
@override
|
|
Future<String> get sdkNameAndVersion async => 'SDK Name and Version';
|
|
|
|
@override
|
|
late DartDevelopmentService dds;
|
|
|
|
@override
|
|
Future<LaunchResult> startApp(
|
|
covariant ApplicationPackage? package, {
|
|
String? mainPath,
|
|
String? route,
|
|
DebuggingOptions? debuggingOptions,
|
|
Map<String, dynamic>? platformArgs,
|
|
bool prebuiltApplication = false,
|
|
bool ipv6 = false,
|
|
String? userIdentifier,
|
|
}) async {
|
|
return LaunchResult.succeeded();
|
|
}
|
|
|
|
@override
|
|
Future<bool> stopApp(
|
|
covariant ApplicationPackage? app, {
|
|
String? userIdentifier,
|
|
}) async {
|
|
if (count > 0) {
|
|
throw StateError('stopApp called more than once.');
|
|
}
|
|
count += 1;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class FakeDebugConnection extends Fake implements DebugConnection {
|
|
late FakeVmServiceHost Function() fakeVmServiceHost;
|
|
|
|
@override
|
|
vm_service.VmService get vmService =>
|
|
fakeVmServiceHost.call().vmService.service;
|
|
|
|
@override
|
|
late String uri;
|
|
|
|
final Completer<void> completer = Completer<void>();
|
|
bool didClose = false;
|
|
|
|
@override
|
|
Future<void> get onDone => completer.future;
|
|
|
|
@override
|
|
Future<void> close() async {
|
|
didClose = true;
|
|
}
|
|
}
|
|
|
|
class FakeAppConnection extends Fake implements AppConnection {
|
|
bool ranMain = false;
|
|
|
|
@override
|
|
void runMain() {
|
|
ranMain = true;
|
|
}
|
|
}
|
|
|
|
// 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
|
|
class FakeChromeDevice extends Fake implements ChromiumDevice {}
|
|
|
|
class FakeWipDebugger extends Fake implements WipDebugger {}
|
|
|
|
class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|
@override
|
|
Future<CompilerOutput> recompile(
|
|
Uri mainUri,
|
|
List<Uri>? invalidatedFiles, {
|
|
required String outputPath,
|
|
required PackageConfig packageConfig,
|
|
required FileSystem fs,
|
|
String? projectRootPath,
|
|
bool suppressErrors = false,
|
|
bool checkDartPluginRegistry = false,
|
|
File? dartPluginRegistrant,
|
|
}) async {
|
|
return const CompilerOutput('foo.dill', 0, <Uri>[]);
|
|
}
|
|
|
|
@override
|
|
void accept() {}
|
|
|
|
@override
|
|
void reset() {}
|
|
|
|
@override
|
|
Future<CompilerOutput> reject() async {
|
|
return const CompilerOutput('foo.dill', 0, <Uri>[]);
|
|
}
|
|
|
|
@override
|
|
void addFileSystemRoot(String root) {}
|
|
}
|
|
|
|
class FakeWebDevFS extends Fake implements WebDevFS {
|
|
Object? exception;
|
|
ConnectionResult? result;
|
|
late UpdateFSReport report;
|
|
|
|
Uri? mainUri;
|
|
|
|
@override
|
|
List<Uri> sources = <Uri>[];
|
|
|
|
@override
|
|
Uri baseUri = Uri.parse('http://localhost:12345');
|
|
|
|
@override
|
|
DateTime? lastCompiled = DateTime.now();
|
|
|
|
@override
|
|
PackageConfig? lastPackageConfig = PackageConfig.empty;
|
|
|
|
@override
|
|
Future<Uri> create() async {
|
|
return baseUri;
|
|
}
|
|
|
|
@override
|
|
Future<UpdateFSReport> update({
|
|
required Uri mainUri,
|
|
required ResidentCompiler generator,
|
|
required bool trackWidgetCreation,
|
|
required String pathToReload,
|
|
required List<Uri> invalidatedFiles,
|
|
required PackageConfig packageConfig,
|
|
required String dillOutputPath,
|
|
required DevelopmentShaderCompiler shaderCompiler,
|
|
DevFSWriter? devFSWriter,
|
|
String? target,
|
|
AssetBundle? bundle,
|
|
DateTime? firstBuildTime,
|
|
bool bundleFirstUpload = false,
|
|
bool fullRestart = false,
|
|
String? projectRootPath,
|
|
File? dartPluginRegistrant,
|
|
}) async {
|
|
this.mainUri = mainUri;
|
|
return report;
|
|
}
|
|
|
|
@override
|
|
Future<ConnectionResult?> connect(bool useDebugExtension, {VmServiceFactory vmServiceFactory = createVmServiceDelegate}) async {
|
|
if (exception != null) {
|
|
assert(exception is Exception || exception is Error);
|
|
// ignore: only_throw_errors, exception is either Error or Exception here.
|
|
throw exception!;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class FakeChromeConnection extends Fake implements ChromeConnection {
|
|
final List<ChromeTab> tabs = <ChromeTab>[];
|
|
|
|
@override
|
|
Future<ChromeTab> getTab(bool Function(ChromeTab tab) accept,
|
|
{Duration? retryFor}) async {
|
|
return tabs.firstWhere(accept);
|
|
}
|
|
|
|
@override
|
|
Future<List<ChromeTab>> getTabs({Duration? retryFor}) async {
|
|
return tabs;
|
|
}
|
|
}
|
|
|
|
class FakeChromeTab extends Fake implements ChromeTab {
|
|
FakeChromeTab(this.url);
|
|
|
|
@override
|
|
final String url;
|
|
final FakeWipConnection connection = FakeWipConnection();
|
|
|
|
@override
|
|
Future<WipConnection> connect({Function? onError}) async {
|
|
return connection;
|
|
}
|
|
}
|
|
|
|
class FakeWipConnection extends Fake implements WipConnection {
|
|
@override
|
|
final WipDebugger debugger = FakeWipDebugger();
|
|
}
|
|
|
|
/// A test implementation of the [ChromiumLauncher] that launches a fixed instance.
|
|
class TestChromiumLauncher implements ChromiumLauncher {
|
|
TestChromiumLauncher();
|
|
|
|
bool _hasInstance = false;
|
|
void setInstance(Chromium chromium) {
|
|
_hasInstance = true;
|
|
currentCompleter.complete(chromium);
|
|
}
|
|
|
|
@override
|
|
Completer<Chromium> currentCompleter = Completer<Chromium>();
|
|
|
|
@override
|
|
bool canFindExecutable() {
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
Future<Chromium> get connectedInstance => currentCompleter.future;
|
|
|
|
@override
|
|
String findExecutable() {
|
|
return 'chrome';
|
|
}
|
|
|
|
@override
|
|
bool get hasChromeInstance => _hasInstance;
|
|
|
|
@override
|
|
Future<Chromium> launch(
|
|
String url, {
|
|
bool headless = false,
|
|
int? debugPort,
|
|
bool skipCheck = false,
|
|
Directory? cacheDir,
|
|
List<String> webBrowserFlags = const <String>[],
|
|
}) async {
|
|
return currentCompleter.future;
|
|
}
|
|
|
|
@override
|
|
Future<Chromium> connect(Chromium chrome, bool skipCheck) {
|
|
return currentCompleter.future;
|
|
}
|
|
}
|
|
|
|
class FakeFlutterDevice extends Fake implements FlutterDevice {
|
|
Uri? testUri;
|
|
UpdateFSReport report = UpdateFSReport(
|
|
success: true,
|
|
invalidatedSourcesCount: 1,
|
|
);
|
|
Exception? reportError;
|
|
|
|
@override
|
|
ResidentCompiler? generator;
|
|
|
|
@override
|
|
Stream<Uri?> get observatoryUris => Stream<Uri?>.value(testUri);
|
|
|
|
@override
|
|
DevelopmentShaderCompiler get developmentShaderCompiler => const FakeShaderCompiler();
|
|
|
|
@override
|
|
FlutterVmService? vmService;
|
|
|
|
DevFS? _devFS;
|
|
|
|
@override
|
|
DevFS? get devFS => _devFS;
|
|
|
|
@override
|
|
set devFS(DevFS? value) {}
|
|
|
|
@override
|
|
Device? device;
|
|
|
|
@override
|
|
Future<void> stopEchoingDeviceLog() async {}
|
|
|
|
@override
|
|
Future<void> initLogReader() async {}
|
|
|
|
@override
|
|
Future<Uri?> setupDevFS(String fsName, Directory rootDirectory) async {
|
|
return testUri;
|
|
}
|
|
|
|
@override
|
|
Future<void> exitApps(
|
|
{Duration timeoutDelay = const Duration(seconds: 10)}) async {}
|
|
|
|
@override
|
|
Future<void> connect({
|
|
ReloadSources? reloadSources,
|
|
Restart? restart,
|
|
CompileExpression? compileExpression,
|
|
GetSkSLMethod? getSkSLMethod,
|
|
PrintStructuredErrorLogMethod? printStructuredErrorLogMethod,
|
|
int? hostVmServicePort,
|
|
int? ddsPort,
|
|
bool disableServiceAuthCodes = false,
|
|
bool enableDds = true,
|
|
bool cacheStartupProfile = false,
|
|
required bool allowExistingDdsInstance,
|
|
bool? ipv6 = false,
|
|
}) async {}
|
|
|
|
@override
|
|
Future<UpdateFSReport> updateDevFS({
|
|
Uri? mainUri,
|
|
String? target,
|
|
AssetBundle? bundle,
|
|
DateTime? firstBuildTime,
|
|
bool bundleFirstUpload = false,
|
|
bool bundleDirty = false,
|
|
bool fullRestart = false,
|
|
String? projectRootPath,
|
|
String? pathToReload,
|
|
String? dillOutputPath,
|
|
List<Uri>? invalidatedFiles,
|
|
PackageConfig? packageConfig,
|
|
File? dartPluginRegistrant,
|
|
}) async {
|
|
if (reportError != null) {
|
|
throw reportError!;
|
|
}
|
|
return report;
|
|
}
|
|
|
|
@override
|
|
Future<void> updateReloadStatus(bool wasReloadSuccessful) async {}
|
|
}
|
|
|
|
class FakeShaderCompiler implements DevelopmentShaderCompiler {
|
|
const FakeShaderCompiler();
|
|
|
|
@override
|
|
void configureCompiler(TargetPlatform? platform, { required bool enableImpeller }) { }
|
|
|
|
@override
|
|
Future<DevFSContent> recompileShader(DevFSContent inputShader) {
|
|
throw UnimplementedError();
|
|
}
|
|
}
|