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

This flag has been enabled by default for quite some time in `master`, and in the current `stable`. This is the first of many PRs to get rid of the flag and the deprecated code it is guarding.
721 lines
26 KiB
Dart
721 lines
26 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 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/artifacts.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/dart/pub.dart';
|
|
import 'package:flutter_tools/src/devfs.dart';
|
|
import 'package:flutter_tools/src/device.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/run_hot.dart';
|
|
import 'package:flutter_tools/src/vmservice.dart';
|
|
import 'package:unified_analytics/unified_analytics.dart';
|
|
import 'package:vm_service/vm_service.dart' as vm_service;
|
|
|
|
import '../src/common.dart';
|
|
import '../src/context.dart';
|
|
import '../src/fakes.dart';
|
|
import '../src/package_config.dart';
|
|
import '../src/throwing_pub.dart';
|
|
import 'hot_shared.dart';
|
|
|
|
void main() {
|
|
group('validateReloadReport', () {
|
|
testUsingContext('invalid', () async {
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': false,
|
|
'details': <String, dynamic>{},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': false,
|
|
'details': <String, dynamic>{'notices': <Map<String, dynamic>>[]},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': false,
|
|
'details': <String, dynamic>{
|
|
'notices': <String, dynamic>{'message': 'error'},
|
|
},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': false,
|
|
'details': <String, dynamic>{'notices': <Map<String, dynamic>>[]},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': false,
|
|
'details': <String, dynamic>{
|
|
'notices': <Map<String, dynamic>>[
|
|
<String, dynamic>{'message': false},
|
|
],
|
|
},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': false,
|
|
'details': <String, dynamic>{
|
|
'notices': <Map<String, dynamic>>[
|
|
<String, dynamic>{
|
|
'message': <String>['error'],
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': false,
|
|
'details': <String, dynamic>{
|
|
'notices': <Map<String, dynamic>>[
|
|
<String, dynamic>{'message': 'error'},
|
|
<String, dynamic>{
|
|
'message': <String>['error'],
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': false,
|
|
'details': <String, dynamic>{
|
|
'notices': <Map<String, dynamic>>[
|
|
<String, dynamic>{'message': 'error'},
|
|
],
|
|
},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
expect(
|
|
HotRunner.validateReloadReport(
|
|
vm_service.ReloadReport.parse(<String, dynamic>{'type': 'ReloadReport', 'success': true}),
|
|
),
|
|
true,
|
|
);
|
|
});
|
|
|
|
testWithoutContext('ReasonForCancelling toString has a hint for specific errors', () {
|
|
final ReasonForCancelling reasonForCancelling = ReasonForCancelling(
|
|
message: 'Const class cannot remove fields',
|
|
);
|
|
|
|
expect(reasonForCancelling.toString(), contains('Try performing a hot restart instead.'));
|
|
});
|
|
});
|
|
|
|
group('hotRestart', () {
|
|
final FakeResidentCompiler residentCompiler = FakeResidentCompiler();
|
|
late MemoryFileSystem fileSystem;
|
|
late TestUsage testUsage;
|
|
late FakeAnalytics fakeAnalytics;
|
|
|
|
setUp(() {
|
|
fileSystem = MemoryFileSystem.test();
|
|
testUsage = TestUsage();
|
|
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
|
fs: fileSystem,
|
|
fakeFlutterVersion: FakeFlutterVersion(),
|
|
);
|
|
});
|
|
|
|
group('fails to setup', () {
|
|
late TestHotRunnerConfig failingTestingConfig;
|
|
setUp(() {
|
|
failingTestingConfig = TestHotRunnerConfig(
|
|
successfulHotRestartSetup: false,
|
|
successfulHotReloadSetup: false,
|
|
);
|
|
});
|
|
|
|
testUsingContext(
|
|
'setupHotRestart function fails',
|
|
() async {
|
|
fileSystem.file('pubspec.yaml').writeAsStringSync('''
|
|
name: my_app
|
|
''');
|
|
writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'my_app');
|
|
final FakeDevice device = FakeDevice();
|
|
final List<FlutterDevice> devices = <FlutterDevice>[FakeFlutterDevice(device)];
|
|
final OperationResult result = await HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
devtoolsHandler: createNoOpHandler,
|
|
analytics: fakeAnalytics,
|
|
).restart(fullRestart: true);
|
|
expect(result.isOk, false);
|
|
expect(result.message, 'setupHotRestart failed');
|
|
expect(failingTestingConfig.updateDevFSCompleteCalled, false);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => failingTestingConfig,
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Pub: ThrowingPub.new,
|
|
},
|
|
);
|
|
|
|
testUsingContext(
|
|
'setupHotReload function fails',
|
|
() async {
|
|
fileSystem.file('pubspec.yaml').writeAsStringSync('''
|
|
name: my_app
|
|
''');
|
|
writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'my_app');
|
|
final FakeDevice device = FakeDevice();
|
|
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device);
|
|
final List<FlutterDevice> devices = <FlutterDevice>[fakeFlutterDevice];
|
|
final OperationResult result =
|
|
await HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
devtoolsHandler: createNoOpHandler,
|
|
reassembleHelper:
|
|
(
|
|
List<FlutterDevice?> flutterDevices,
|
|
Map<FlutterDevice?, List<FlutterView>> viewCache,
|
|
void Function(String message)? onSlow,
|
|
String reloadMessage,
|
|
) async => ReassembleResult(
|
|
<FlutterView?, FlutterVmService?>{null: null},
|
|
false,
|
|
true,
|
|
),
|
|
analytics: fakeAnalytics,
|
|
).restart();
|
|
expect(result.isOk, false);
|
|
expect(result.message, 'setupHotReload failed');
|
|
expect(failingTestingConfig.updateDevFSCompleteCalled, false);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => failingTestingConfig,
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Pub: ThrowingPub.new,
|
|
},
|
|
);
|
|
});
|
|
|
|
group('shutdown hook tests', () {
|
|
late TestHotRunnerConfig shutdownTestingConfig;
|
|
|
|
setUp(() {
|
|
shutdownTestingConfig = TestHotRunnerConfig();
|
|
});
|
|
|
|
testUsingContext(
|
|
'shutdown hook called after signal',
|
|
() async {
|
|
writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'my_app');
|
|
final FakeDevice device = FakeDevice();
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
|
FlutterDevice(
|
|
device,
|
|
generator: residentCompiler,
|
|
buildInfo: BuildInfo.debug,
|
|
developmentShaderCompiler: const FakeShaderCompiler(),
|
|
),
|
|
];
|
|
await HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
analytics: fakeAnalytics,
|
|
).cleanupAfterSignal();
|
|
expect(shutdownTestingConfig.shutdownHookCalled, true);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => shutdownTestingConfig,
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
},
|
|
);
|
|
|
|
testUsingContext(
|
|
'shutdown hook called after app stop',
|
|
() async {
|
|
writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'my_app');
|
|
final FakeDevice device = FakeDevice();
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
|
FlutterDevice(
|
|
device,
|
|
generator: residentCompiler,
|
|
buildInfo: BuildInfo.debug,
|
|
developmentShaderCompiler: const FakeShaderCompiler(),
|
|
),
|
|
];
|
|
await HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
analytics: fakeAnalytics,
|
|
).preExit();
|
|
expect(shutdownTestingConfig.shutdownHookCalled, true);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => shutdownTestingConfig,
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
},
|
|
);
|
|
});
|
|
|
|
group('successful hot restart', () {
|
|
late TestHotRunnerConfig testingConfig;
|
|
setUp(() {
|
|
testingConfig = TestHotRunnerConfig(successfulHotRestartSetup: true);
|
|
});
|
|
testUsingContext(
|
|
'correctly tracks time spent for analytics for hot restart',
|
|
() async {
|
|
final FakeDevice device = FakeDevice();
|
|
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device);
|
|
final List<FlutterDevice> devices = <FlutterDevice>[fakeFlutterDevice];
|
|
|
|
fakeFlutterDevice.updateDevFSReportCallback =
|
|
() async => UpdateFSReport(
|
|
success: true,
|
|
invalidatedSourcesCount: 2,
|
|
syncedBytes: 4,
|
|
scannedSourcesCount: 8,
|
|
compileDuration: const Duration(seconds: 16),
|
|
transferDuration: const Duration(seconds: 32),
|
|
);
|
|
|
|
final FakeStopwatchFactory fakeStopwatchFactory = FakeStopwatchFactory(
|
|
stopwatches: <String, Stopwatch>{
|
|
'fullRestartHelper': FakeStopwatch()..elapsed = const Duration(seconds: 64),
|
|
'updateDevFS': FakeStopwatch()..elapsed = const Duration(seconds: 128),
|
|
},
|
|
);
|
|
|
|
(fakeFlutterDevice.devFS! as FakeDevFs).baseUri = Uri.parse('file:///base_uri');
|
|
|
|
final OperationResult result = await HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
devtoolsHandler: createNoOpHandler,
|
|
stopwatchFactory: fakeStopwatchFactory,
|
|
analytics: fakeAnalytics,
|
|
).restart(fullRestart: true);
|
|
|
|
expect(result.isOk, true);
|
|
expect(testUsage.events, <TestUsageEvent>[
|
|
const TestUsageEvent(
|
|
'hot',
|
|
'restart',
|
|
parameters: CustomDimensions(
|
|
hotEventTargetPlatform: 'flutter-tester',
|
|
hotEventSdkName: 'Tester',
|
|
hotEventEmulator: false,
|
|
hotEventFullRestart: true,
|
|
hotEventOverallTimeInMs: 64000,
|
|
hotEventSyncedBytes: 4,
|
|
hotEventInvalidatedSourcesCount: 2,
|
|
hotEventTransferTimeInMs: 32000,
|
|
hotEventCompileTimeInMs: 16000,
|
|
hotEventFindInvalidatedTimeInMs: 128000,
|
|
hotEventScannedSourcesCount: 8,
|
|
),
|
|
),
|
|
]);
|
|
|
|
expect(
|
|
fakeAnalytics.sentEvents,
|
|
contains(
|
|
Event.hotRunnerInfo(
|
|
label: 'restart',
|
|
targetPlatform: 'flutter-tester',
|
|
sdkName: 'Tester',
|
|
emulator: false,
|
|
fullRestart: true,
|
|
syncedBytes: 4,
|
|
invalidatedSourcesCount: 2,
|
|
transferTimeInMs: 32000,
|
|
overallTimeInMs: 64000,
|
|
compileTimeInMs: 16000,
|
|
findInvalidatedTimeInMs: 128000,
|
|
scannedSourcesCount: 8,
|
|
),
|
|
),
|
|
);
|
|
expect(testingConfig.updateDevFSCompleteCalled, true);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => testingConfig,
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Usage: () => testUsage,
|
|
},
|
|
);
|
|
});
|
|
|
|
group('successful hot reload', () {
|
|
late TestHotRunnerConfig testingConfig;
|
|
setUp(() {
|
|
testingConfig = TestHotRunnerConfig(successfulHotReloadSetup: true);
|
|
});
|
|
testUsingContext(
|
|
'correctly tracks time spent for analytics for hot reload',
|
|
() async {
|
|
final FakeDevice device = FakeDevice();
|
|
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device);
|
|
final List<FlutterDevice> devices = <FlutterDevice>[fakeFlutterDevice];
|
|
|
|
fakeFlutterDevice.updateDevFSReportCallback =
|
|
() async => UpdateFSReport(
|
|
success: true,
|
|
invalidatedSourcesCount: 6,
|
|
syncedBytes: 8,
|
|
scannedSourcesCount: 16,
|
|
compileDuration: const Duration(seconds: 16),
|
|
transferDuration: const Duration(seconds: 32),
|
|
);
|
|
|
|
final FakeStopwatchFactory fakeStopwatchFactory = FakeStopwatchFactory(
|
|
stopwatches: <String, Stopwatch>{
|
|
'updateDevFS': FakeStopwatch()..elapsed = const Duration(seconds: 64),
|
|
'reloadSources:reload': FakeStopwatch()..elapsed = const Duration(seconds: 128),
|
|
'reloadSources:reassemble': FakeStopwatch()..elapsed = const Duration(seconds: 256),
|
|
'reloadSources:vm': FakeStopwatch()..elapsed = const Duration(seconds: 512),
|
|
},
|
|
);
|
|
|
|
(fakeFlutterDevice.devFS! as FakeDevFs).baseUri = Uri.parse('file:///base_uri');
|
|
|
|
final OperationResult result =
|
|
await HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
devtoolsHandler: createNoOpHandler,
|
|
stopwatchFactory: fakeStopwatchFactory,
|
|
analytics: fakeAnalytics,
|
|
reloadSourcesHelper: (
|
|
HotRunner hotRunner,
|
|
List<FlutterDevice?> flutterDevices,
|
|
bool? pause,
|
|
Map<String, dynamic> firstReloadDetails,
|
|
String? targetPlatform,
|
|
String? sdkName,
|
|
bool? emulator,
|
|
String? reason,
|
|
Analytics? analytics,
|
|
) async {
|
|
firstReloadDetails['finalLibraryCount'] = 2;
|
|
firstReloadDetails['receivedLibraryCount'] = 3;
|
|
firstReloadDetails['receivedClassesCount'] = 4;
|
|
firstReloadDetails['receivedProceduresCount'] = 5;
|
|
return OperationResult.ok;
|
|
},
|
|
reassembleHelper:
|
|
(
|
|
List<FlutterDevice?> flutterDevices,
|
|
Map<FlutterDevice?, List<FlutterView>> viewCache,
|
|
void Function(String message)? onSlow,
|
|
String reloadMessage,
|
|
) async => ReassembleResult(
|
|
<FlutterView?, FlutterVmService?>{null: null},
|
|
false,
|
|
true,
|
|
),
|
|
).restart();
|
|
|
|
expect(result.isOk, true);
|
|
expect(testUsage.events, <TestUsageEvent>[
|
|
const TestUsageEvent(
|
|
'hot',
|
|
'reload',
|
|
parameters: CustomDimensions(
|
|
hotEventFinalLibraryCount: 2,
|
|
hotEventSyncedLibraryCount: 3,
|
|
hotEventSyncedClassesCount: 4,
|
|
hotEventSyncedProceduresCount: 5,
|
|
hotEventSyncedBytes: 8,
|
|
hotEventInvalidatedSourcesCount: 6,
|
|
hotEventTransferTimeInMs: 32000,
|
|
hotEventOverallTimeInMs: 128000,
|
|
hotEventTargetPlatform: 'flutter-tester',
|
|
hotEventSdkName: 'Tester',
|
|
hotEventEmulator: false,
|
|
hotEventFullRestart: false,
|
|
hotEventCompileTimeInMs: 16000,
|
|
hotEventFindInvalidatedTimeInMs: 64000,
|
|
hotEventScannedSourcesCount: 16,
|
|
hotEventReassembleTimeInMs: 256000,
|
|
hotEventReloadVMTimeInMs: 512000,
|
|
),
|
|
),
|
|
]);
|
|
expect(
|
|
fakeAnalytics.sentEvents,
|
|
contains(
|
|
Event.hotRunnerInfo(
|
|
label: 'reload',
|
|
targetPlatform: 'flutter-tester',
|
|
sdkName: 'Tester',
|
|
emulator: false,
|
|
fullRestart: false,
|
|
finalLibraryCount: 2,
|
|
syncedLibraryCount: 3,
|
|
syncedClassesCount: 4,
|
|
syncedProceduresCount: 5,
|
|
syncedBytes: 8,
|
|
invalidatedSourcesCount: 6,
|
|
transferTimeInMs: 32000,
|
|
overallTimeInMs: 128000,
|
|
compileTimeInMs: 16000,
|
|
findInvalidatedTimeInMs: 64000,
|
|
scannedSourcesCount: 16,
|
|
reassembleTimeInMs: 256000,
|
|
reloadVMTimeInMs: 512000,
|
|
),
|
|
),
|
|
);
|
|
expect(testingConfig.updateDevFSCompleteCalled, true);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => testingConfig,
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Usage: () => testUsage,
|
|
},
|
|
);
|
|
});
|
|
|
|
group('hot restart that failed to sync dev fs', () {
|
|
late TestHotRunnerConfig testingConfig;
|
|
setUp(() {
|
|
testingConfig = TestHotRunnerConfig(successfulHotRestartSetup: true);
|
|
});
|
|
testUsingContext(
|
|
'still calls the devfs complete callback',
|
|
() async {
|
|
final FakeDevice device = FakeDevice();
|
|
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device);
|
|
final List<FlutterDevice> devices = <FlutterDevice>[fakeFlutterDevice];
|
|
fakeFlutterDevice.updateDevFSReportCallback =
|
|
() async => throw Exception('updateDevFS failed');
|
|
|
|
final HotRunner runner = HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
devtoolsHandler: createNoOpHandler,
|
|
analytics: fakeAnalytics,
|
|
);
|
|
|
|
await expectLater(
|
|
runner.restart(fullRestart: true),
|
|
throwsA(
|
|
isA<Exception>().having(
|
|
(Exception e) => e.toString(),
|
|
'message',
|
|
'Exception: updateDevFS failed',
|
|
),
|
|
),
|
|
);
|
|
expect(testingConfig.updateDevFSCompleteCalled, true);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => testingConfig,
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Usage: () => testUsage,
|
|
},
|
|
);
|
|
});
|
|
|
|
group('hot reload that failed to sync dev fs', () {
|
|
late TestHotRunnerConfig testingConfig;
|
|
setUp(() {
|
|
testingConfig = TestHotRunnerConfig(successfulHotReloadSetup: true);
|
|
});
|
|
testUsingContext(
|
|
'still calls the devfs complete callback',
|
|
() async {
|
|
final FakeDevice device = FakeDevice();
|
|
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device);
|
|
final List<FlutterDevice> devices = <FlutterDevice>[fakeFlutterDevice];
|
|
fakeFlutterDevice.updateDevFSReportCallback =
|
|
() async => throw Exception('updateDevFS failed');
|
|
|
|
final HotRunner runner = HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
devtoolsHandler: createNoOpHandler,
|
|
analytics: fakeAnalytics,
|
|
);
|
|
|
|
await expectLater(
|
|
runner.restart(),
|
|
throwsA(
|
|
isA<Exception>().having(
|
|
(Exception e) => e.toString(),
|
|
'message',
|
|
'Exception: updateDevFS failed',
|
|
),
|
|
),
|
|
);
|
|
expect(testingConfig.updateDevFSCompleteCalled, true);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => testingConfig,
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Usage: () => testUsage,
|
|
},
|
|
);
|
|
});
|
|
});
|
|
|
|
group('hot attach', () {
|
|
late MemoryFileSystem fileSystem;
|
|
late FakeAnalytics fakeAnalytics;
|
|
|
|
setUp(() {
|
|
fileSystem = MemoryFileSystem.test();
|
|
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
|
fs: fileSystem,
|
|
fakeFlutterVersion: FakeFlutterVersion(),
|
|
);
|
|
});
|
|
|
|
testUsingContext(
|
|
'Exits with code 2 when HttpException is thrown '
|
|
'during VM service connection',
|
|
() async {
|
|
writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'my_app');
|
|
|
|
final FakeResidentCompiler residentCompiler = FakeResidentCompiler();
|
|
final FakeDevice device = FakeDevice();
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
|
TestFlutterDevice(
|
|
device: device,
|
|
generator: residentCompiler,
|
|
exception: const HttpException(
|
|
'Connection closed before full header was received, '
|
|
'uri = http://127.0.0.1:63394/5ZmLv8A59xY=/ws',
|
|
),
|
|
),
|
|
];
|
|
|
|
final int exitCode = await HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
analytics: fakeAnalytics,
|
|
).attach(needsFullRestart: false);
|
|
expect(exitCode, 2);
|
|
},
|
|
overrides: <Type, Generator>{
|
|
HotRunnerConfig: () => TestHotRunnerConfig(),
|
|
Artifacts: () => Artifacts.test(),
|
|
FileSystem: () => fileSystem,
|
|
Platform: () => FakePlatform(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
},
|
|
);
|
|
});
|
|
|
|
group('hot cleanupAtFinish()', () {
|
|
late MemoryFileSystem fileSystem;
|
|
late FakeAnalytics fakeAnalytics;
|
|
|
|
setUp(() {
|
|
fileSystem = MemoryFileSystem.test();
|
|
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
|
fs: fileSystem,
|
|
fakeFlutterVersion: FakeFlutterVersion(),
|
|
);
|
|
});
|
|
|
|
testUsingContext('disposes each device', () async {
|
|
final FakeDevice device1 = FakeDevice();
|
|
final FakeDevice device2 = FakeDevice();
|
|
final FakeFlutterDevice flutterDevice1 = FakeFlutterDevice(device1);
|
|
final FakeFlutterDevice flutterDevice2 = FakeFlutterDevice(device2);
|
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[flutterDevice1, flutterDevice2];
|
|
|
|
await HotRunner(
|
|
devices,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
target: 'main.dart',
|
|
analytics: fakeAnalytics,
|
|
).cleanupAtFinish();
|
|
|
|
expect(device1.disposed, true);
|
|
expect(device2.disposed, true);
|
|
|
|
expect(flutterDevice1.stoppedEchoingDeviceLog, true);
|
|
expect(flutterDevice2.stoppedEchoingDeviceLog, true);
|
|
});
|
|
});
|
|
}
|