2019-11-27 23:04:02 +00:00
|
|
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
2016-12-13 21:43:19 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
2018-07-06 22:18:57 +00:00
|
|
|
import 'dart:async';
|
|
|
|
|
2020-06-24 17:23:59 +00:00
|
|
|
import 'package:file/memory.dart';
|
|
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
2020-04-28 00:41:42 +00:00
|
|
|
import 'package:vm_service/vm_service.dart' as vm_service;
|
2018-08-31 22:40:00 +00:00
|
|
|
import 'package:flutter_tools/src/artifacts.dart';
|
2019-08-02 23:02:02 +00:00
|
|
|
import 'package:flutter_tools/src/base/io.dart';
|
2019-04-16 04:02:20 +00:00
|
|
|
import 'package:flutter_tools/src/build_info.dart';
|
2019-08-02 23:02:02 +00:00
|
|
|
import 'package:flutter_tools/src/compile.dart';
|
2018-11-09 18:33:22 +00:00
|
|
|
import 'package:flutter_tools/src/devfs.dart';
|
2018-07-06 22:18:57 +00:00
|
|
|
import 'package:flutter_tools/src/device.dart';
|
|
|
|
import 'package:flutter_tools/src/resident_runner.dart';
|
2017-01-28 22:26:49 +00:00
|
|
|
import 'package:flutter_tools/src/run_hot.dart';
|
2019-08-02 23:02:02 +00:00
|
|
|
import 'package:flutter_tools/src/vmservice.dart';
|
2018-07-06 22:18:57 +00:00
|
|
|
import 'package:meta/meta.dart';
|
|
|
|
import 'package:mockito/mockito.dart';
|
2016-12-13 21:43:19 +00:00
|
|
|
|
2019-07-13 18:51:44 +00:00
|
|
|
import '../src/common.dart';
|
|
|
|
import '../src/context.dart';
|
|
|
|
import '../src/mocks.dart';
|
2016-12-13 21:43:19 +00:00
|
|
|
|
2020-05-05 19:09:51 +00:00
|
|
|
final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate(
|
|
|
|
id: '1',
|
|
|
|
pauseEvent: vm_service.Event(
|
|
|
|
kind: vm_service.EventKind.kResume,
|
|
|
|
timestamp: 0
|
|
|
|
),
|
|
|
|
breakpoints: <vm_service.Breakpoint>[],
|
|
|
|
exceptionPauseMode: null,
|
|
|
|
libraries: <vm_service.LibraryRef>[],
|
|
|
|
livePorts: 0,
|
|
|
|
name: 'test',
|
|
|
|
number: '1',
|
|
|
|
pauseOnExit: false,
|
|
|
|
runnable: true,
|
|
|
|
startTime: 0,
|
|
|
|
);
|
|
|
|
|
|
|
|
final FlutterView fakeFlutterView = FlutterView(
|
|
|
|
id: 'a',
|
|
|
|
uiIsolate: fakeUnpausedIsolate,
|
|
|
|
);
|
|
|
|
|
|
|
|
final FakeVmServiceRequest listViews = FakeVmServiceRequest(
|
|
|
|
method: kListViewsMethod,
|
|
|
|
jsonResponse: <String, Object>{
|
|
|
|
'views': <Object>[
|
|
|
|
fakeFlutterView.toJson(),
|
|
|
|
],
|
|
|
|
},
|
|
|
|
);
|
2018-02-02 22:27:29 +00:00
|
|
|
void main() {
|
2016-12-13 21:43:19 +00:00
|
|
|
group('validateReloadReport', () {
|
|
|
|
testUsingContext('invalid', () async {
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{}), false);
|
2018-06-05 02:12:55 +00:00
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': false,
|
|
|
|
'details': <String, dynamic>{},
|
|
|
|
}), false);
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': false,
|
|
|
|
'details': <String, dynamic>{
|
|
|
|
'notices': <Map<String, dynamic>>[
|
|
|
|
],
|
|
|
|
},
|
|
|
|
}), false);
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': false,
|
|
|
|
'details': <String, dynamic>{
|
|
|
|
'notices': <String, dynamic>{
|
|
|
|
'message': 'error',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}), false);
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': false,
|
|
|
|
'details': <String, dynamic>{
|
|
|
|
'notices': <Map<String, dynamic>>[],
|
|
|
|
},
|
|
|
|
}), false);
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': false,
|
|
|
|
'details': <String, dynamic>{
|
|
|
|
'notices': <Map<String, dynamic>>[
|
2019-03-20 22:23:31 +00:00
|
|
|
<String, dynamic>{'message': false},
|
2018-06-05 02:12:55 +00:00
|
|
|
],
|
|
|
|
},
|
|
|
|
}), false);
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': false,
|
|
|
|
'details': <String, dynamic>{
|
|
|
|
'notices': <Map<String, dynamic>>[
|
2019-03-20 22:23:31 +00:00
|
|
|
<String, dynamic>{'message': <String>['error']},
|
2018-06-05 02:12:55 +00:00
|
|
|
],
|
|
|
|
},
|
|
|
|
}), false);
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': false,
|
|
|
|
'details': <String, dynamic>{
|
|
|
|
'notices': <Map<String, dynamic>>[
|
2019-03-20 22:23:31 +00:00
|
|
|
<String, dynamic>{'message': 'error'},
|
|
|
|
<String, dynamic>{'message': <String>['error']},
|
2018-06-05 02:12:55 +00:00
|
|
|
],
|
|
|
|
},
|
|
|
|
}), false);
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': false,
|
|
|
|
'details': <String, dynamic>{
|
|
|
|
'notices': <Map<String, dynamic>>[
|
2019-03-20 22:23:31 +00:00
|
|
|
<String, dynamic>{'message': 'error'},
|
2018-06-05 02:12:55 +00:00
|
|
|
],
|
|
|
|
},
|
|
|
|
}), false);
|
|
|
|
expect(HotRunner.validateReloadReport(<String, dynamic>{
|
|
|
|
'type': 'ReloadReport',
|
|
|
|
'success': true,
|
|
|
|
}), true);
|
2016-12-13 21:43:19 +00:00
|
|
|
});
|
|
|
|
});
|
2018-07-06 22:18:57 +00:00
|
|
|
|
|
|
|
group('hotRestart', () {
|
2018-09-12 06:29:29 +00:00
|
|
|
final MockResidentCompiler residentCompiler = MockResidentCompiler();
|
2018-11-09 18:33:22 +00:00
|
|
|
final MockDevFs mockDevFs = MockDevFs();
|
2020-06-24 17:23:59 +00:00
|
|
|
FileSystem fileSystem;
|
2018-07-06 22:18:57 +00:00
|
|
|
|
2018-11-09 18:33:22 +00:00
|
|
|
when(mockDevFs.update(
|
2020-04-20 21:02:49 +00:00
|
|
|
mainUri: anyNamed('mainUri'),
|
2018-11-09 18:33:22 +00:00
|
|
|
target: anyNamed('target'),
|
|
|
|
bundle: anyNamed('bundle'),
|
|
|
|
firstBuildTime: anyNamed('firstBuildTime'),
|
|
|
|
bundleFirstUpload: anyNamed('bundleFirstUpload'),
|
|
|
|
generator: anyNamed('generator'),
|
|
|
|
fullRestart: anyNamed('fullRestart'),
|
|
|
|
dillOutputPath: anyNamed('dillOutputPath'),
|
|
|
|
trackWidgetCreation: anyNamed('trackWidgetCreation'),
|
|
|
|
projectRootPath: anyNamed('projectRootPath'),
|
|
|
|
pathToReload: anyNamed('pathToReload'),
|
2019-03-15 22:02:45 +00:00
|
|
|
invalidatedFiles: anyNamed('invalidatedFiles'),
|
2020-04-20 21:02:49 +00:00
|
|
|
packageConfig: anyNamed('packageConfig'),
|
2018-12-27 17:53:24 +00:00
|
|
|
)).thenAnswer((Invocation _) => Future<UpdateFSReport>.value(
|
|
|
|
UpdateFSReport(success: true, syncedBytes: 1000, invalidatedSourcesCount: 1)));
|
2019-03-06 19:05:16 +00:00
|
|
|
when(mockDevFs.assetPathsToEvict).thenReturn(<String>{});
|
2018-11-09 18:33:22 +00:00
|
|
|
when(mockDevFs.baseUri).thenReturn(Uri.file('test'));
|
2019-03-21 04:58:15 +00:00
|
|
|
when(mockDevFs.sources).thenReturn(<Uri>[Uri.file('test')]);
|
|
|
|
when(mockDevFs.lastCompiled).thenReturn(DateTime.now());
|
2018-11-09 18:33:22 +00:00
|
|
|
|
2018-07-06 22:18:57 +00:00
|
|
|
setUp(() {
|
2020-06-24 17:23:59 +00:00
|
|
|
fileSystem = MemoryFileSystem.test();
|
2018-07-06 22:18:57 +00:00
|
|
|
});
|
|
|
|
|
2018-11-09 18:33:22 +00:00
|
|
|
testUsingContext('Does not hot restart when device does not support it', () async {
|
2020-06-25 19:52:14 +00:00
|
|
|
fileSystem.file('.packages')
|
2020-06-24 17:23:59 +00:00
|
|
|
..createSync(recursive: true)
|
|
|
|
..writeAsStringSync('\n');
|
2018-11-09 18:33:22 +00:00
|
|
|
// Setup mocks
|
|
|
|
final MockDevice mockDevice = MockDevice();
|
|
|
|
when(mockDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockDevice.supportsHotRestart).thenReturn(false);
|
2019-07-29 14:24:02 +00:00
|
|
|
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
|
2018-11-09 18:33:22 +00:00
|
|
|
// Trigger hot restart.
|
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
2020-03-06 22:53:36 +00:00
|
|
|
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs,
|
2018-11-09 18:33:22 +00:00
|
|
|
];
|
2020-06-25 19:52:14 +00:00
|
|
|
final OperationResult result = await HotRunner(
|
|
|
|
devices,
|
|
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
|
|
).restart(fullRestart: true);
|
2018-11-09 18:33:22 +00:00
|
|
|
// Expect hot restart failed.
|
2018-10-05 20:48:41 +00:00
|
|
|
expect(result.isOk, false);
|
2018-11-09 18:33:22 +00:00
|
|
|
expect(result.message, 'hotRestart not supported');
|
2018-07-06 22:18:57 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
2019-03-15 22:02:45 +00:00
|
|
|
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
|
2020-06-24 17:23:59 +00:00
|
|
|
Artifacts: () => Artifacts.test(),
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => FakePlatform(operatingSystem: 'linux'),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2018-11-09 18:33:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('Does not hot restart when one of many devices does not support it', () async {
|
2020-06-25 19:52:14 +00:00
|
|
|
fileSystem.file('.packages')
|
2020-06-24 17:23:59 +00:00
|
|
|
..createSync(recursive: true)
|
|
|
|
..writeAsStringSync('\n');
|
2018-11-09 18:33:22 +00:00
|
|
|
// Setup mocks
|
|
|
|
final MockDevice mockDevice = MockDevice();
|
|
|
|
final MockDevice mockHotDevice = MockDevice();
|
|
|
|
when(mockDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockDevice.supportsHotRestart).thenReturn(false);
|
|
|
|
when(mockHotDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockHotDevice.supportsHotRestart).thenReturn(true);
|
|
|
|
// Trigger hot restart.
|
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
2020-03-06 22:53:36 +00:00
|
|
|
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs,
|
|
|
|
FlutterDevice(mockHotDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs,
|
2018-11-09 18:33:22 +00:00
|
|
|
];
|
2020-06-25 19:52:14 +00:00
|
|
|
final OperationResult result = await HotRunner(
|
|
|
|
devices,
|
|
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug)
|
|
|
|
).restart(fullRestart: true);
|
2018-11-09 18:33:22 +00:00
|
|
|
// Expect hot restart failed.
|
|
|
|
expect(result.isOk, false);
|
|
|
|
expect(result.message, 'hotRestart not supported');
|
|
|
|
}, overrides: <Type, Generator>{
|
2019-03-15 22:02:45 +00:00
|
|
|
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
|
2020-06-24 17:23:59 +00:00
|
|
|
Artifacts: () => Artifacts.test(),
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => FakePlatform(operatingSystem: 'linux'),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2018-11-09 18:33:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('Does hot restarts when all devices support it', () async {
|
2020-04-28 00:41:42 +00:00
|
|
|
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
2020-05-05 19:09:51 +00:00
|
|
|
listViews,
|
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: 'getIsolate',
|
|
|
|
args: <String, Object>{
|
|
|
|
'isolateId': fakeUnpausedIsolate.id,
|
|
|
|
},
|
|
|
|
jsonResponse: fakeUnpausedIsolate.toJson(),
|
2020-04-28 00:41:42 +00:00
|
|
|
),
|
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: 'getVM',
|
|
|
|
jsonResponse: vm_service.VM.parse(<String, Object>{}).toJson()
|
|
|
|
),
|
2020-05-05 19:09:51 +00:00
|
|
|
listViews,
|
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: 'getIsolate',
|
|
|
|
args: <String, Object>{
|
|
|
|
'isolateId': fakeUnpausedIsolate.id,
|
|
|
|
},
|
|
|
|
jsonResponse: fakeUnpausedIsolate.toJson(),
|
|
|
|
),
|
2020-04-28 00:41:42 +00:00
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: 'getVM',
|
|
|
|
jsonResponse: vm_service.VM.parse(<String, Object>{}).toJson()
|
|
|
|
),
|
2020-05-05 19:09:51 +00:00
|
|
|
listViews,
|
|
|
|
listViews,
|
2020-04-28 00:41:42 +00:00
|
|
|
const FakeVmServiceRequest(
|
2020-05-05 19:09:51 +00:00
|
|
|
method: 'streamListen',
|
|
|
|
args: <String, Object>{
|
|
|
|
'streamId': 'Isolate',
|
2020-04-28 00:41:42 +00:00
|
|
|
}
|
|
|
|
),
|
|
|
|
const FakeVmServiceRequest(
|
2020-05-05 19:09:51 +00:00
|
|
|
method: 'streamListen',
|
|
|
|
args: <String, Object>{
|
|
|
|
'streamId': 'Isolate',
|
|
|
|
}
|
|
|
|
),
|
|
|
|
FakeVmServiceStreamResponse(
|
|
|
|
streamId: 'Isolate',
|
|
|
|
event: vm_service.Event(
|
|
|
|
timestamp: 0,
|
|
|
|
kind: vm_service.EventKind.kIsolateRunnable,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
FakeVmServiceStreamResponse(
|
|
|
|
streamId: 'Isolate',
|
|
|
|
event: vm_service.Event(
|
|
|
|
timestamp: 0,
|
|
|
|
kind: vm_service.EventKind.kIsolateRunnable,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: kRunInViewMethod,
|
|
|
|
args: <String, Object>{
|
|
|
|
'viewId': fakeFlutterView.id,
|
|
|
|
'mainScript': 'lib/main.dart.dill',
|
|
|
|
'assetDirectory': 'build/flutter_assets',
|
|
|
|
}
|
|
|
|
),
|
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: kRunInViewMethod,
|
|
|
|
args: <String, Object>{
|
|
|
|
'viewId': fakeFlutterView.id,
|
|
|
|
'mainScript': 'lib/main.dart.dill',
|
|
|
|
'assetDirectory': 'build/flutter_assets',
|
2020-04-28 00:41:42 +00:00
|
|
|
}
|
|
|
|
),
|
|
|
|
]);
|
2018-11-09 18:33:22 +00:00
|
|
|
// Setup mocks
|
|
|
|
final MockDevice mockDevice = MockDevice();
|
|
|
|
final MockDevice mockHotDevice = MockDevice();
|
|
|
|
when(mockDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockDevice.supportsHotRestart).thenReturn(true);
|
|
|
|
when(mockHotDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockHotDevice.supportsHotRestart).thenReturn(true);
|
|
|
|
// Trigger a restart.
|
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
2020-04-28 00:41:42 +00:00
|
|
|
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)
|
|
|
|
..vmService = fakeVmServiceHost.vmService
|
|
|
|
..devFS = mockDevFs,
|
|
|
|
FlutterDevice(mockHotDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)
|
|
|
|
..vmService = fakeVmServiceHost.vmService
|
|
|
|
..devFS = mockDevFs,
|
2018-11-09 18:33:22 +00:00
|
|
|
];
|
2020-06-25 19:52:14 +00:00
|
|
|
final HotRunner hotRunner = HotRunner(
|
|
|
|
devices,
|
|
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
|
|
);
|
2020-02-23 00:49:03 +00:00
|
|
|
final OperationResult result = await hotRunner.restart(fullRestart: true);
|
2018-11-09 18:33:22 +00:00
|
|
|
// Expect hot restart was successful.
|
2020-02-23 00:49:03 +00:00
|
|
|
expect(hotRunner.uri, mockDevFs.baseUri);
|
2018-11-09 18:33:22 +00:00
|
|
|
expect(result.isOk, true);
|
|
|
|
expect(result.message, isNot('hotRestart not supported'));
|
|
|
|
}, overrides: <Type, Generator>{
|
2019-03-15 22:02:45 +00:00
|
|
|
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
|
2020-06-24 17:23:59 +00:00
|
|
|
Artifacts: () => Artifacts.test(),
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => FakePlatform(operatingSystem: 'linux'),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2018-07-06 22:18:57 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('setup function fails', () async {
|
2020-06-25 19:52:14 +00:00
|
|
|
fileSystem.file('.packages')
|
2020-06-24 17:23:59 +00:00
|
|
|
..createSync(recursive: true)
|
|
|
|
..writeAsStringSync('\n');
|
2018-11-09 18:33:22 +00:00
|
|
|
final MockDevice mockDevice = MockDevice();
|
|
|
|
when(mockDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockDevice.supportsHotRestart).thenReturn(true);
|
2019-07-29 14:24:02 +00:00
|
|
|
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
|
2018-11-09 18:33:22 +00:00
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
2020-03-06 22:53:36 +00:00
|
|
|
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug),
|
2018-11-09 18:33:22 +00:00
|
|
|
];
|
2020-06-25 19:52:14 +00:00
|
|
|
final OperationResult result = await HotRunner(
|
|
|
|
devices,
|
|
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
|
|
).restart(fullRestart: true);
|
2018-10-05 20:48:41 +00:00
|
|
|
expect(result.isOk, false);
|
|
|
|
expect(result.message, 'setupHotRestart failed');
|
2018-07-06 22:18:57 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
2018-09-12 06:29:29 +00:00
|
|
|
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: false),
|
2020-06-24 17:23:59 +00:00
|
|
|
Artifacts: () => Artifacts.test(),
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => FakePlatform(operatingSystem: 'linux'),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2018-07-06 22:18:57 +00:00
|
|
|
});
|
2018-11-09 18:33:22 +00:00
|
|
|
|
|
|
|
testUsingContext('hot restart supported', () async {
|
2020-06-25 19:52:14 +00:00
|
|
|
fileSystem.file('.packages')
|
2020-06-24 17:23:59 +00:00
|
|
|
..createSync(recursive: true)
|
|
|
|
..writeAsStringSync('\n');
|
2018-11-09 18:33:22 +00:00
|
|
|
// Setup mocks
|
2020-04-28 00:41:42 +00:00
|
|
|
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
2020-05-05 19:09:51 +00:00
|
|
|
listViews,
|
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: 'getIsolate',
|
|
|
|
args: <String, Object>{
|
|
|
|
'isolateId': fakeUnpausedIsolate.id,
|
|
|
|
},
|
|
|
|
jsonResponse: fakeUnpausedIsolate.toJson(),
|
2020-04-28 00:41:42 +00:00
|
|
|
),
|
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: 'getVM',
|
2020-05-05 19:09:51 +00:00
|
|
|
jsonResponse: vm_service.VM.parse(<String, Object>{}).toJson(),
|
2020-04-28 00:41:42 +00:00
|
|
|
),
|
2020-05-05 19:09:51 +00:00
|
|
|
listViews,
|
2020-04-28 00:41:42 +00:00
|
|
|
const FakeVmServiceRequest(
|
2020-05-05 19:09:51 +00:00
|
|
|
method: 'streamListen',
|
|
|
|
args: <String, Object>{
|
|
|
|
'streamId': 'Isolate',
|
2020-04-28 00:41:42 +00:00
|
|
|
}
|
|
|
|
),
|
2020-05-05 19:09:51 +00:00
|
|
|
FakeVmServiceRequest(
|
|
|
|
method: kRunInViewMethod,
|
|
|
|
args: <String, Object>{
|
|
|
|
'viewId': fakeFlutterView.id,
|
|
|
|
'mainScript': 'lib/main.dart.dill',
|
|
|
|
'assetDirectory': 'build/flutter_assets',
|
|
|
|
}
|
|
|
|
),
|
|
|
|
FakeVmServiceStreamResponse(
|
|
|
|
streamId: 'Isolate',
|
|
|
|
event: vm_service.Event(
|
|
|
|
timestamp: 0,
|
|
|
|
kind: vm_service.EventKind.kIsolateRunnable,
|
|
|
|
)
|
|
|
|
),
|
2020-04-28 00:41:42 +00:00
|
|
|
]);
|
2018-11-09 18:33:22 +00:00
|
|
|
final MockDevice mockDevice = MockDevice();
|
|
|
|
when(mockDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockDevice.supportsHotRestart).thenReturn(true);
|
2019-07-29 14:24:02 +00:00
|
|
|
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
|
2018-11-09 18:33:22 +00:00
|
|
|
// Trigger hot restart.
|
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
2020-04-28 00:41:42 +00:00
|
|
|
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)
|
|
|
|
..vmService = fakeVmServiceHost.vmService
|
|
|
|
..devFS = mockDevFs,
|
2018-11-09 18:33:22 +00:00
|
|
|
];
|
2020-06-25 19:52:14 +00:00
|
|
|
final HotRunner hotRunner = HotRunner(
|
|
|
|
devices,
|
|
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug),
|
|
|
|
);
|
2020-02-23 00:49:03 +00:00
|
|
|
final OperationResult result = await hotRunner.restart(fullRestart: true);
|
2018-11-09 18:33:22 +00:00
|
|
|
// Expect hot restart successful.
|
2020-02-23 00:49:03 +00:00
|
|
|
expect(hotRunner.uri, mockDevFs.baseUri);
|
2018-11-09 18:33:22 +00:00
|
|
|
expect(result.isOk, true);
|
|
|
|
expect(result.message, isNot('setupHotRestart failed'));
|
|
|
|
}, overrides: <Type, Generator>{
|
2019-03-15 22:02:45 +00:00
|
|
|
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
|
2020-06-24 17:23:59 +00:00
|
|
|
Artifacts: () => Artifacts.test(),
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => FakePlatform(operatingSystem: 'linux'),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2018-11-09 18:33:22 +00:00
|
|
|
});
|
2019-01-17 16:49:02 +00:00
|
|
|
|
|
|
|
group('shutdown hook tests', () {
|
|
|
|
TestHotRunnerConfig shutdownTestingConfig;
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
shutdownTestingConfig = TestHotRunnerConfig(
|
|
|
|
successfulSetup: true,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('shutdown hook called after signal', () async {
|
2020-06-25 19:52:14 +00:00
|
|
|
fileSystem.file('.packages')
|
2020-06-24 17:23:59 +00:00
|
|
|
..createSync(recursive: true)
|
|
|
|
..writeAsStringSync('\n');
|
2019-01-17 16:49:02 +00:00
|
|
|
final MockDevice mockDevice = MockDevice();
|
|
|
|
when(mockDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockDevice.supportsHotRestart).thenReturn(true);
|
2019-06-06 18:16:19 +00:00
|
|
|
when(mockDevice.supportsFlutterExit).thenReturn(false);
|
2019-01-17 16:49:02 +00:00
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
2020-03-06 22:53:36 +00:00
|
|
|
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug),
|
2019-01-17 16:49:02 +00:00
|
|
|
];
|
2020-06-25 19:52:14 +00:00
|
|
|
await HotRunner(
|
|
|
|
devices,
|
|
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug)
|
|
|
|
).cleanupAfterSignal();
|
2019-01-17 16:49:02 +00:00
|
|
|
expect(shutdownTestingConfig.shutdownHookCalled, true);
|
2019-03-22 23:02:21 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
2019-01-17 16:49:02 +00:00
|
|
|
HotRunnerConfig: () => shutdownTestingConfig,
|
2020-06-24 17:23:59 +00:00
|
|
|
Artifacts: () => Artifacts.test(),
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => FakePlatform(operatingSystem: 'linux'),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2019-01-17 16:49:02 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('shutdown hook called after app stop', () async {
|
2020-06-25 19:52:14 +00:00
|
|
|
fileSystem.file('.packages')
|
2020-06-24 17:23:59 +00:00
|
|
|
..createSync(recursive: true)
|
|
|
|
..writeAsStringSync('\n');
|
2019-01-17 16:49:02 +00:00
|
|
|
final MockDevice mockDevice = MockDevice();
|
|
|
|
when(mockDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockDevice.supportsHotRestart).thenReturn(true);
|
2019-06-06 18:16:19 +00:00
|
|
|
when(mockDevice.supportsFlutterExit).thenReturn(false);
|
2019-01-17 16:49:02 +00:00
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
2020-03-06 22:53:36 +00:00
|
|
|
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug),
|
2019-01-17 16:49:02 +00:00
|
|
|
];
|
2020-06-25 19:52:14 +00:00
|
|
|
await HotRunner(
|
|
|
|
devices,
|
|
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug)
|
|
|
|
).preExit();
|
2019-01-17 16:49:02 +00:00
|
|
|
expect(shutdownTestingConfig.shutdownHookCalled, true);
|
2019-03-22 23:02:21 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
2019-01-17 16:49:02 +00:00
|
|
|
HotRunnerConfig: () => shutdownTestingConfig,
|
2020-06-24 17:23:59 +00:00
|
|
|
Artifacts: () => Artifacts.test(),
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => FakePlatform(operatingSystem: 'linux'),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2019-01-17 16:49:02 +00:00
|
|
|
});
|
|
|
|
});
|
2018-07-06 22:18:57 +00:00
|
|
|
});
|
2019-08-02 23:02:02 +00:00
|
|
|
|
|
|
|
group('hot attach', () {
|
2020-06-24 17:23:59 +00:00
|
|
|
FileSystem fileSystem;
|
2019-08-02 23:02:02 +00:00
|
|
|
|
|
|
|
setUp(() {
|
2020-06-24 17:23:59 +00:00
|
|
|
fileSystem = MemoryFileSystem.test();
|
2019-08-02 23:02:02 +00:00
|
|
|
});
|
|
|
|
|
2020-06-03 20:27:47 +00:00
|
|
|
testUsingContext('Exits with code 2 when when HttpException is thrown '
|
|
|
|
'during VM service connection', () async {
|
2020-06-25 19:52:14 +00:00
|
|
|
fileSystem.file('.packages')
|
2020-06-24 17:23:59 +00:00
|
|
|
..createSync(recursive: true)
|
|
|
|
..writeAsStringSync('\n');
|
|
|
|
|
2020-06-03 20:27:47 +00:00
|
|
|
final MockResidentCompiler residentCompiler = MockResidentCompiler();
|
2019-08-02 23:02:02 +00:00
|
|
|
final MockDevice mockDevice = MockDevice();
|
|
|
|
when(mockDevice.supportsHotReload).thenReturn(true);
|
|
|
|
when(mockDevice.supportsHotRestart).thenReturn(false);
|
|
|
|
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
|
|
|
|
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation _) async => 'Android 10');
|
|
|
|
|
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
|
|
|
TestFlutterDevice(
|
|
|
|
device: mockDevice,
|
|
|
|
generator: residentCompiler,
|
|
|
|
exception: const HttpException('Connection closed before full header was received, '
|
2019-09-24 06:06:09 +00:00
|
|
|
'uri = http://127.0.0.1:63394/5ZmLv8A59xY=/ws'),
|
2019-08-02 23:02:02 +00:00
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
final int exitCode = await HotRunner(devices,
|
|
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
|
|
).attach();
|
|
|
|
expect(exitCode, 2);
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
|
2020-06-24 17:23:59 +00:00
|
|
|
Artifacts: () => Artifacts.test(),
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => FakePlatform(operatingSystem: 'linux'),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2019-08-02 23:02:02 +00:00
|
|
|
});
|
|
|
|
});
|
2019-10-15 19:50:44 +00:00
|
|
|
|
|
|
|
group('hot cleanupAtFinish()', () {
|
|
|
|
MockFlutterDevice mockFlutterDeviceFactory(Device device) {
|
|
|
|
final MockFlutterDevice mockFlutterDevice = MockFlutterDevice();
|
|
|
|
when(mockFlutterDevice.stopEchoingDeviceLog()).thenAnswer((Invocation invocation) => Future<void>.value(null));
|
|
|
|
when(mockFlutterDevice.device).thenReturn(device);
|
|
|
|
return mockFlutterDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
testUsingContext('disposes each device', () async {
|
|
|
|
final MockDevice mockDevice1 = MockDevice();
|
|
|
|
final MockDevice mockDevice2 = MockDevice();
|
|
|
|
final MockFlutterDevice mockFlutterDevice1 = mockFlutterDeviceFactory(mockDevice1);
|
|
|
|
final MockFlutterDevice mockFlutterDevice2 = mockFlutterDeviceFactory(mockDevice2);
|
|
|
|
|
|
|
|
final List<FlutterDevice> devices = <FlutterDevice>[
|
|
|
|
mockFlutterDevice1,
|
|
|
|
mockFlutterDevice2,
|
|
|
|
];
|
|
|
|
|
|
|
|
await HotRunner(devices,
|
|
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
|
|
).cleanupAtFinish();
|
|
|
|
|
|
|
|
verify(mockDevice1.dispose());
|
|
|
|
verify(mockFlutterDevice1.stopEchoingDeviceLog());
|
|
|
|
verify(mockDevice2.dispose());
|
|
|
|
verify(mockFlutterDevice2.stopEchoingDeviceLog());
|
|
|
|
});
|
|
|
|
});
|
2018-07-06 22:18:57 +00:00
|
|
|
}
|
|
|
|
|
2018-11-09 18:33:22 +00:00
|
|
|
class MockDevFs extends Mock implements DevFS {}
|
|
|
|
|
2018-07-06 22:18:57 +00:00
|
|
|
class MockDevice extends Mock implements Device {
|
|
|
|
MockDevice() {
|
|
|
|
when(isSupported()).thenReturn(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-15 19:50:44 +00:00
|
|
|
class MockFlutterDevice extends Mock implements FlutterDevice {}
|
|
|
|
|
2019-08-02 23:02:02 +00:00
|
|
|
class TestFlutterDevice extends FlutterDevice {
|
|
|
|
TestFlutterDevice({
|
|
|
|
@required Device device,
|
|
|
|
@required this.exception,
|
2019-09-24 06:06:09 +00:00
|
|
|
@required ResidentCompiler generator,
|
2019-08-02 23:02:02 +00:00
|
|
|
}) : assert(exception != null),
|
2020-03-06 22:53:36 +00:00
|
|
|
super(device, buildInfo: BuildInfo.debug, generator: generator);
|
2019-08-02 23:02:02 +00:00
|
|
|
|
|
|
|
/// The exception to throw when the connect method is called.
|
|
|
|
final Exception exception;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> connect({
|
|
|
|
ReloadSources reloadSources,
|
|
|
|
Restart restart,
|
|
|
|
CompileExpression compileExpression,
|
2019-12-10 05:31:34 +00:00
|
|
|
ReloadMethod reloadMethod,
|
2020-05-27 17:10:41 +00:00
|
|
|
GetSkSLMethod getSkSLMethod,
|
2020-06-09 18:18:03 +00:00
|
|
|
PrintStructuredErrorLogMethod printStructuredErrorLogMethod,
|
2020-07-16 18:38:17 +00:00
|
|
|
bool disableDds = false,
|
|
|
|
bool ipv6 = false,
|
2019-08-02 23:02:02 +00:00
|
|
|
}) async {
|
|
|
|
throw exception;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-06 22:18:57 +00:00
|
|
|
class TestHotRunnerConfig extends HotRunnerConfig {
|
2019-03-15 22:02:45 +00:00
|
|
|
TestHotRunnerConfig({@required this.successfulSetup});
|
2018-10-04 05:28:07 +00:00
|
|
|
bool successfulSetup;
|
2019-01-17 16:49:02 +00:00
|
|
|
bool shutdownHookCalled = false;
|
2018-10-04 05:28:07 +00:00
|
|
|
|
2018-07-06 22:18:57 +00:00
|
|
|
@override
|
|
|
|
Future<bool> setupHotRestart() async {
|
|
|
|
return successfulSetup;
|
|
|
|
}
|
2019-01-17 16:49:02 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> runPreShutdownOperations() async {
|
|
|
|
shutdownHookCalled = true;
|
|
|
|
}
|
2016-12-13 21:43:19 +00:00
|
|
|
}
|