Unverified Commit 95217eb7 authored by Lau Ching Jun's avatar Lau Ching Jun Committed by GitHub

Add new hook for setupHotReload and after updating devFS is complete (#87532)

* Add new hook for setupHotReload and after updating devFS is complete

* Handle the case where updateDevFS failed
parent 788aea4b
...@@ -52,6 +52,18 @@ class HotRunnerConfig { ...@@ -52,6 +52,18 @@ class HotRunnerConfig {
Future<bool> setupHotRestart() async { Future<bool> setupHotRestart() async {
return true; return true;
} }
/// A hook for implementations to perform any necessary initialization prior
/// to a hot reload. Should return true if the hot restart should continue.
Future<bool> setupHotReload() async {
return true;
}
/// A hook for implementations to perform any necessary cleanup after the
/// devfs sync is complete. At this point the flutter_tools no longer needs to
/// access the source files and assets.
void updateDevFSComplete() {}
/// A hook for implementations to perform any necessary operations right /// A hook for implementations to perform any necessary operations right
/// before the runner is about to be shut down. /// before the runner is about to be shut down.
Future<void> runPreShutdownOperations() async { Future<void> runPreShutdownOperations() async {
...@@ -546,7 +558,12 @@ class HotRunner extends ResidentRunner { ...@@ -546,7 +558,12 @@ class HotRunner extends ResidentRunner {
String reason, String reason,
}) async { }) async {
final Stopwatch restartTimer = Stopwatch()..start(); final Stopwatch restartTimer = Stopwatch()..start();
final UpdateFSReport updatedDevFS = await _updateDevFS(fullRestart: true); UpdateFSReport updatedDevFS;
try {
updatedDevFS = await _updateDevFS(fullRestart: true);
} finally {
hotRunnerConfig.updateDevFSComplete();
}
if (!updatedDevFS.success) { if (!updatedDevFS.success) {
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
if (device.generator != null) { if (device.generator != null) {
...@@ -857,8 +874,16 @@ class HotRunner extends ResidentRunner { ...@@ -857,8 +874,16 @@ class HotRunner extends ResidentRunner {
} }
final Stopwatch reloadTimer = _stopwatchFactory.createStopwatch('reloadSources:reload')..start(); final Stopwatch reloadTimer = _stopwatchFactory.createStopwatch('reloadSources:reload')..start();
if (!(await hotRunnerConfig.setupHotReload())) {
return OperationResult(1, 'setupHotReload failed');
}
final Stopwatch devFSTimer = Stopwatch()..start(); final Stopwatch devFSTimer = Stopwatch()..start();
final UpdateFSReport updatedDevFS = await _updateDevFS(); UpdateFSReport updatedDevFS;
try {
updatedDevFS= await _updateDevFS();
} finally {
hotRunnerConfig.updateDevFSComplete();
}
// Record time it took to synchronize to DevFS. // Record time it took to synchronize to DevFS.
bool shouldReportReloadTime = true; bool shouldReportReloadTime = true;
_addBenchmarkData('hotReloadDevFSSyncMilliseconds', devFSTimer.elapsed.inMilliseconds); _addBenchmarkData('hotReloadDevFSSyncMilliseconds', devFSTimer.elapsed.inMilliseconds);
......
...@@ -157,37 +157,83 @@ void main() { ...@@ -157,37 +157,83 @@ void main() {
testUsage = TestUsage(); testUsage = TestUsage();
}); });
testUsingContext('setup function fails', () async { group('fails to setup', () {
fileSystem.file('.packages') TestHotRunnerConfig failingTestingConfig;
..createSync(recursive: true) setUp(() {
..writeAsStringSync('\n'); failingTestingConfig = TestHotRunnerConfig(
final FakeDevice device = FakeDevice(); successfulHotRestartSetup: false,
final List<FlutterDevice> devices = <FlutterDevice>[ successfulHotReloadSetup: false,
FlutterDevice(device, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = FakeDevFs(), );
]; });
final OperationResult result = await HotRunner(
devices, testUsingContext('setupHotRestart function fails', () async {
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug), fileSystem.file('.packages')
target: 'main.dart', ..createSync(recursive: true)
devtoolsHandler: createNoOpHandler, ..writeAsStringSync('\n');
).restart(fullRestart: true); final FakeDevice device = FakeDevice();
expect(result.isOk, false); final List<FlutterDevice> devices = <FlutterDevice>[
expect(result.message, 'setupHotRestart failed'); FlutterDevice(device, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = FakeDevFs(),
}, overrides: <Type, Generator>{ ];
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: false), final OperationResult result = await HotRunner(
Artifacts: () => Artifacts.test(), devices,
FileSystem: () => fileSystem, debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
Platform: () => FakePlatform(operatingSystem: 'linux'), target: 'main.dart',
ProcessManager: () => FakeProcessManager.any(), devtoolsHandler: createNoOpHandler,
).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(operatingSystem: 'linux'),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('setupHotReload function fails', () async {
fileSystem.file('.packages')
..createSync(recursive: true)
..writeAsStringSync('\n');
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,
String fastReassembleClassName,
) async => ReassembleResult(
<FlutterView, FlutterVmService>{null: null},
false,
true,
),
).restart(fullRestart: false);
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(operatingSystem: 'linux'),
ProcessManager: () => FakeProcessManager.any(),
});
}); });
group('shutdown hook tests', () { group('shutdown hook tests', () {
TestHotRunnerConfig shutdownTestingConfig; TestHotRunnerConfig shutdownTestingConfig;
setUp(() { setUp(() {
shutdownTestingConfig = TestHotRunnerConfig( shutdownTestingConfig = TestHotRunnerConfig();
successfulSetup: true,
);
}); });
testUsingContext('shutdown hook called after signal', () async { testUsingContext('shutdown hook called after signal', () async {
...@@ -235,156 +281,242 @@ void main() { ...@@ -235,156 +281,242 @@ void main() {
}); });
}); });
testUsingContext('correctly tracks time spent for analytics for hot restart', () async { group('successful hot restart', () {
final FakeDevice device = FakeDevice(); TestHotRunnerConfig testingConfig;
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device); setUp(() {
final List<FlutterDevice> devices = <FlutterDevice>[ testingConfig = TestHotRunnerConfig(
fakeFlutterDevice, 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.updateDevFSReport = UpdateFSReport( fakeFlutterDevice.updateDevFSReportCallback = () async => UpdateFSReport(
success: true, success: true,
invalidatedSourcesCount: 2, invalidatedSourcesCount: 2,
syncedBytes: 4, syncedBytes: 4,
scannedSourcesCount: 8, scannedSourcesCount: 8,
compileDuration: const Duration(seconds: 16), compileDuration: const Duration(seconds: 16),
transferDuration: const Duration(seconds: 32), transferDuration: const Duration(seconds: 32),
); );
final FakeStopwatchFactory fakeStopwatchFactory = FakeStopwatchFactory( final FakeStopwatchFactory fakeStopwatchFactory = FakeStopwatchFactory(
stopwatches: <String, Stopwatch>{ stopwatches: <String, Stopwatch>{
'fullRestartHelper': FakeStopwatch()..elapsed = const Duration(seconds: 64), 'fullRestartHelper': FakeStopwatch()..elapsed = const Duration(seconds: 64),
'updateDevFS': FakeStopwatch()..elapsed = const Duration(seconds: 128), 'updateDevFS': FakeStopwatch()..elapsed = const Duration(seconds: 128),
}, },
); );
(fakeFlutterDevice.devFS as FakeDevFs).baseUri = Uri.parse('file:///base_uri'); (fakeFlutterDevice.devFS as FakeDevFs).baseUri = Uri.parse('file:///base_uri');
final OperationResult result = await HotRunner( final OperationResult result = await HotRunner(
devices, devices,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
devtoolsHandler: createNoOpHandler, devtoolsHandler: createNoOpHandler,
stopwatchFactory: fakeStopwatchFactory, stopwatchFactory: fakeStopwatchFactory,
).restart(fullRestart: true); ).restart(fullRestart: true);
expect(result.isOk, true); expect(result.isOk, true);
expect(testUsage.events, <TestUsageEvent>[ expect(testUsage.events, <TestUsageEvent>[
const TestUsageEvent('hot', 'restart', parameters: CustomDimensions( const TestUsageEvent('hot', 'restart', parameters: CustomDimensions(
hotEventTargetPlatform: 'flutter-tester', hotEventTargetPlatform: 'flutter-tester',
hotEventSdkName: 'Tester', hotEventSdkName: 'Tester',
hotEventEmulator: false, hotEventEmulator: false,
hotEventFullRestart: true, hotEventFullRestart: true,
hotEventOverallTimeInMs: 64000, hotEventOverallTimeInMs: 64000,
hotEventSyncedBytes: 4, hotEventSyncedBytes: 4,
hotEventInvalidatedSourcesCount: 2, hotEventInvalidatedSourcesCount: 2,
hotEventTransferTimeInMs: 32000, hotEventTransferTimeInMs: 32000,
hotEventCompileTimeInMs: 16000, hotEventCompileTimeInMs: 16000,
hotEventFindInvalidatedTimeInMs: 128000, hotEventFindInvalidatedTimeInMs: 128000,
hotEventScannedSourcesCount: 8, hotEventScannedSourcesCount: 8,
)), )),
]); ]);
}, overrides: <Type, Generator>{ expect(testingConfig.updateDevFSCompleteCalled, true);
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), }, overrides: <Type, Generator>{
Artifacts: () => Artifacts.test(), HotRunnerConfig: () => testingConfig,
FileSystem: () => fileSystem, Artifacts: () => Artifacts.test(),
Platform: () => FakePlatform(operatingSystem: 'linux'), FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), Platform: () => FakePlatform(operatingSystem: 'linux'),
Usage: () => testUsage, ProcessManager: () => FakeProcessManager.any(),
Usage: () => testUsage,
});
}); });
testUsingContext('correctly tracks time spent for analytics for hot reload', () async { group('successful hot reload', () {
final FakeDevice device = FakeDevice(); TestHotRunnerConfig testingConfig;
final FakeFlutterDevice fakeFlutterDevice = FakeFlutterDevice(device); setUp(() {
final List<FlutterDevice> devices = <FlutterDevice>[ testingConfig = TestHotRunnerConfig(
fakeFlutterDevice, 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.updateDevFSReport = UpdateFSReport( fakeFlutterDevice.updateDevFSReportCallback = () async => UpdateFSReport(
success: true, success: true,
invalidatedSourcesCount: 6, invalidatedSourcesCount: 6,
syncedBytes: 8, syncedBytes: 8,
scannedSourcesCount: 16, scannedSourcesCount: 16,
compileDuration: const Duration(seconds: 16), compileDuration: const Duration(seconds: 16),
transferDuration: const Duration(seconds: 32), transferDuration: const Duration(seconds: 32),
); );
final FakeStopwatchFactory fakeStopwatchFactory = FakeStopwatchFactory( final FakeStopwatchFactory fakeStopwatchFactory = FakeStopwatchFactory(
stopwatches: <String, Stopwatch>{ stopwatches: <String, Stopwatch>{
'updateDevFS': FakeStopwatch()..elapsed = const Duration(seconds: 64), 'updateDevFS': FakeStopwatch()..elapsed = const Duration(seconds: 64),
'reloadSources:reload': FakeStopwatch()..elapsed = const Duration(seconds: 128), 'reloadSources:reload': FakeStopwatch()..elapsed = const Duration(seconds: 128),
'reloadSources:reassemble': FakeStopwatch()..elapsed = const Duration(seconds: 256), 'reloadSources:reassemble': FakeStopwatch()..elapsed = const Duration(seconds: 256),
'reloadSources:vm': FakeStopwatch()..elapsed = const Duration(seconds: 512), 'reloadSources:vm': FakeStopwatch()..elapsed = const Duration(seconds: 512),
}, },
); );
(fakeFlutterDevice.devFS as FakeDevFs).baseUri = Uri.parse('file:///base_uri'); (fakeFlutterDevice.devFS as FakeDevFs).baseUri = Uri.parse('file:///base_uri');
final OperationResult result = await HotRunner( final OperationResult result = await HotRunner(
devices, devices,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
devtoolsHandler: createNoOpHandler, devtoolsHandler: createNoOpHandler,
stopwatchFactory: fakeStopwatchFactory, stopwatchFactory: fakeStopwatchFactory,
reloadSourcesHelper: ( reloadSourcesHelper: (
HotRunner hotRunner, HotRunner hotRunner,
List<FlutterDevice> flutterDevices, List<FlutterDevice> flutterDevices,
bool pause, bool pause,
Map<String, dynamic> firstReloadDetails, Map<String, dynamic> firstReloadDetails,
String targetPlatform, String targetPlatform,
String sdkName, String sdkName,
bool emulator, bool emulator,
String reason, String reason,
) async { ) async {
firstReloadDetails['finalLibraryCount'] = 2; firstReloadDetails['finalLibraryCount'] = 2;
firstReloadDetails['receivedLibraryCount'] = 3; firstReloadDetails['receivedLibraryCount'] = 3;
firstReloadDetails['receivedClassesCount'] = 4; firstReloadDetails['receivedClassesCount'] = 4;
firstReloadDetails['receivedProceduresCount'] = 5; firstReloadDetails['receivedProceduresCount'] = 5;
return OperationResult.ok; return OperationResult.ok;
}, },
reassembleHelper: ( reassembleHelper: (
List<FlutterDevice> flutterDevices, List<FlutterDevice> flutterDevices,
Map<FlutterDevice, List<FlutterView>> viewCache, Map<FlutterDevice, List<FlutterView>> viewCache,
void Function(String message) onSlow, void Function(String message) onSlow,
String reloadMessage, String reloadMessage,
String fastReassembleClassName, String fastReassembleClassName,
) async => ReassembleResult( ) async => ReassembleResult(
<FlutterView, FlutterVmService>{null: null}, <FlutterView, FlutterVmService>{null: null},
false, false,
true, true,
), ),
).restart(fullRestart: false); ).restart(fullRestart: false);
expect(result.isOk, true); expect(result.isOk, true);
expect(testUsage.events, <TestUsageEvent>[ expect(testUsage.events, <TestUsageEvent>[
const TestUsageEvent('hot', 'reload', parameters: CustomDimensions( const TestUsageEvent('hot', 'reload', parameters: CustomDimensions(
hotEventFinalLibraryCount: 2, hotEventFinalLibraryCount: 2,
hotEventSyncedLibraryCount: 3, hotEventSyncedLibraryCount: 3,
hotEventSyncedClassesCount: 4, hotEventSyncedClassesCount: 4,
hotEventSyncedProceduresCount: 5, hotEventSyncedProceduresCount: 5,
hotEventSyncedBytes: 8, hotEventSyncedBytes: 8,
hotEventInvalidatedSourcesCount: 6, hotEventInvalidatedSourcesCount: 6,
hotEventTransferTimeInMs: 32000, hotEventTransferTimeInMs: 32000,
hotEventOverallTimeInMs: 128000, hotEventOverallTimeInMs: 128000,
hotEventTargetPlatform: 'flutter-tester', hotEventTargetPlatform: 'flutter-tester',
hotEventSdkName: 'Tester', hotEventSdkName: 'Tester',
hotEventEmulator: false, hotEventEmulator: false,
hotEventFullRestart: false, hotEventFullRestart: false,
fastReassemble: false, fastReassemble: false,
hotEventCompileTimeInMs: 16000, hotEventCompileTimeInMs: 16000,
hotEventFindInvalidatedTimeInMs: 64000, hotEventFindInvalidatedTimeInMs: 64000,
hotEventScannedSourcesCount: 16, hotEventScannedSourcesCount: 16,
hotEventReassembleTimeInMs: 256000, hotEventReassembleTimeInMs: 256000,
hotEventReloadVMTimeInMs: 512000, hotEventReloadVMTimeInMs: 512000,
)), )),
]); ]);
}, overrides: <Type, Generator>{ expect(testingConfig.updateDevFSCompleteCalled, true);
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), }, overrides: <Type, Generator>{
Artifacts: () => Artifacts.test(), HotRunnerConfig: () => testingConfig,
FileSystem: () => fileSystem, Artifacts: () => Artifacts.test(),
Platform: () => FakePlatform(operatingSystem: 'linux'), FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), Platform: () => FakePlatform(operatingSystem: 'linux'),
Usage: () => testUsage, ProcessManager: () => FakeProcessManager.any(),
Usage: () => testUsage,
});
});
group('hot restart that failed to sync dev fs', () {
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 'updateDevFS failed';
final HotRunner runner = HotRunner(
devices,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
await expectLater(runner.restart(fullRestart: true), throwsA('updateDevFS failed'));
expect(testingConfig.updateDevFSCompleteCalled, true);
}, overrides: <Type, Generator>{
HotRunnerConfig: () => testingConfig,
Artifacts: () => Artifacts.test(),
FileSystem: () => fileSystem,
Platform: () => FakePlatform(operatingSystem: 'linux'),
ProcessManager: () => FakeProcessManager.any(),
Usage: () => testUsage,
});
});
group('hot reload that failed to sync dev fs', () {
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 'updateDevFS failed';
final HotRunner runner = HotRunner(
devices,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
await expectLater(runner.restart(fullRestart: false), throwsA('updateDevFS failed'));
expect(testingConfig.updateDevFSCompleteCalled, true);
}, overrides: <Type, Generator>{
HotRunnerConfig: () => testingConfig,
Artifacts: () => Artifacts.test(),
FileSystem: () => fileSystem,
Platform: () => FakePlatform(operatingSystem: 'linux'),
ProcessManager: () => FakeProcessManager.any(),
Usage: () => testUsage,
});
}); });
}); });
...@@ -420,7 +552,7 @@ void main() { ...@@ -420,7 +552,7 @@ void main() {
); );
expect(exitCode, 2); expect(exitCode, 2);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), HotRunnerConfig: () => TestHotRunnerConfig(),
Artifacts: () => Artifacts.test(), Artifacts: () => Artifacts.test(),
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
Platform: () => FakePlatform(operatingSystem: 'linux'), Platform: () => FakePlatform(operatingSystem: 'linux'),
...@@ -519,7 +651,7 @@ class FakeFlutterDevice extends Fake implements FlutterDevice { ...@@ -519,7 +651,7 @@ class FakeFlutterDevice extends Fake implements FlutterDevice {
FakeFlutterDevice(this.device); FakeFlutterDevice(this.device);
bool stoppedEchoingDeviceLog = false; bool stoppedEchoingDeviceLog = false;
UpdateFSReport updateDevFSReport; Future<UpdateFSReport> Function() updateDevFSReportCallback;
@override @override
final FakeDevice device; final FakeDevice device;
...@@ -552,7 +684,7 @@ class FakeFlutterDevice extends Fake implements FlutterDevice { ...@@ -552,7 +684,7 @@ class FakeFlutterDevice extends Fake implements FlutterDevice {
@required String dillOutputPath, @required String dillOutputPath,
@required List<Uri> invalidatedFiles, @required List<Uri> invalidatedFiles,
@required PackageConfig packageConfig, @required PackageConfig packageConfig,
}) async => updateDevFSReport; }) => updateDevFSReportCallback();
} }
class TestFlutterDevice extends FlutterDevice { class TestFlutterDevice extends FlutterDevice {
...@@ -585,13 +717,27 @@ class TestFlutterDevice extends FlutterDevice { ...@@ -585,13 +717,27 @@ class TestFlutterDevice extends FlutterDevice {
} }
class TestHotRunnerConfig extends HotRunnerConfig { class TestHotRunnerConfig extends HotRunnerConfig {
TestHotRunnerConfig({@required this.successfulSetup}); TestHotRunnerConfig({this.successfulHotRestartSetup, this.successfulHotReloadSetup});
bool successfulSetup; bool successfulHotRestartSetup;
bool successfulHotReloadSetup;
bool shutdownHookCalled = false; bool shutdownHookCalled = false;
bool updateDevFSCompleteCalled = false;
@override @override
Future<bool> setupHotRestart() async { Future<bool> setupHotRestart() async {
return successfulSetup; assert(successfulHotRestartSetup != null, 'setupHotRestart is not expected to be called in this test.');
return successfulHotRestartSetup;
}
@override
Future<bool> setupHotReload() async {
assert(successfulHotReloadSetup != null, 'setupHotReload is not expected to be called in this test.');
return successfulHotReloadSetup;
}
@override
void updateDevFSComplete() {
updateDevFSCompleteCalled = true;
} }
@override @override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment