[flutter_tools] Call reassemble with DWDS 24.3.7 and update hot reload and restart analytics (#165006)
https://github.com/dart-lang/webdev/issues/2584 Reassemble was being called in DWDS in the injected client until v24.3.7. Flutter tools should now instead be the one to call the service extension. Now that it's in Flutter tools, we can also report how long it took. Similarly, we should update analytics on various things like, whether there was a reload rejection, how long the compile took, and more. Adds test to check that these analytics are being reported correctly. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
This commit is contained in:
parent
eef31a1c2b
commit
0bdb4d68b5
@ -927,7 +927,7 @@ class AppDomain extends Domain {
|
|||||||
final Map<String, Object?>? result = await device.vmService!.invokeFlutterExtensionRpcRaw(
|
final Map<String, Object?>? result = await device.vmService!.invokeFlutterExtensionRpcRaw(
|
||||||
methodName,
|
methodName,
|
||||||
args: params,
|
args: params,
|
||||||
isolateId: views.first.uiIsolate!.id!,
|
isolateId: views.first.uiIsolate!.id,
|
||||||
);
|
);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw DaemonException('method not available: $methodName');
|
throw DaemonException('method not available: $methodName');
|
||||||
|
@ -380,13 +380,15 @@ class UpdateFSReport {
|
|||||||
Duration compileDuration = Duration.zero,
|
Duration compileDuration = Duration.zero,
|
||||||
Duration transferDuration = Duration.zero,
|
Duration transferDuration = Duration.zero,
|
||||||
Duration findInvalidatedDuration = Duration.zero,
|
Duration findInvalidatedDuration = Duration.zero,
|
||||||
|
bool hotReloadRejected = false,
|
||||||
}) : _success = success,
|
}) : _success = success,
|
||||||
_invalidatedSourcesCount = invalidatedSourcesCount,
|
_invalidatedSourcesCount = invalidatedSourcesCount,
|
||||||
_syncedBytes = syncedBytes,
|
_syncedBytes = syncedBytes,
|
||||||
_scannedSourcesCount = scannedSourcesCount,
|
_scannedSourcesCount = scannedSourcesCount,
|
||||||
_compileDuration = compileDuration,
|
_compileDuration = compileDuration,
|
||||||
_transferDuration = transferDuration,
|
_transferDuration = transferDuration,
|
||||||
_findInvalidatedDuration = findInvalidatedDuration;
|
_findInvalidatedDuration = findInvalidatedDuration,
|
||||||
|
_hotReloadRejected = hotReloadRejected;
|
||||||
|
|
||||||
bool get success => _success;
|
bool get success => _success;
|
||||||
int get invalidatedSourcesCount => _invalidatedSourcesCount;
|
int get invalidatedSourcesCount => _invalidatedSourcesCount;
|
||||||
@ -396,6 +398,12 @@ class UpdateFSReport {
|
|||||||
Duration get transferDuration => _transferDuration;
|
Duration get transferDuration => _transferDuration;
|
||||||
Duration get findInvalidatedDuration => _findInvalidatedDuration;
|
Duration get findInvalidatedDuration => _findInvalidatedDuration;
|
||||||
|
|
||||||
|
/// Whether there was a hot reload rejection in this compile.
|
||||||
|
///
|
||||||
|
/// On the web, hot reload can be rejected during compile time instead of at
|
||||||
|
/// runtime.
|
||||||
|
bool get hotReloadRejected => _hotReloadRejected;
|
||||||
|
|
||||||
bool _success;
|
bool _success;
|
||||||
int _invalidatedSourcesCount;
|
int _invalidatedSourcesCount;
|
||||||
int _syncedBytes;
|
int _syncedBytes;
|
||||||
@ -403,6 +411,7 @@ class UpdateFSReport {
|
|||||||
Duration _compileDuration;
|
Duration _compileDuration;
|
||||||
Duration _transferDuration;
|
Duration _transferDuration;
|
||||||
Duration _findInvalidatedDuration;
|
Duration _findInvalidatedDuration;
|
||||||
|
bool _hotReloadRejected;
|
||||||
|
|
||||||
void incorporateResults(UpdateFSReport report) {
|
void incorporateResults(UpdateFSReport report) {
|
||||||
if (!report._success) {
|
if (!report._success) {
|
||||||
@ -414,6 +423,9 @@ class UpdateFSReport {
|
|||||||
_compileDuration += report._compileDuration;
|
_compileDuration += report._compileDuration;
|
||||||
_transferDuration += report._transferDuration;
|
_transferDuration += report._transferDuration;
|
||||||
_findInvalidatedDuration += report._findInvalidatedDuration;
|
_findInvalidatedDuration += report._findInvalidatedDuration;
|
||||||
|
if (report._hotReloadRejected) {
|
||||||
|
_hotReloadRejected = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1139,7 +1139,14 @@ class WebDevFS implements DevFS {
|
|||||||
recompileRestart: fullRestart,
|
recompileRestart: fullRestart,
|
||||||
);
|
);
|
||||||
if (compilerOutput == null || compilerOutput.errorCount > 0) {
|
if (compilerOutput == null || compilerOutput.errorCount > 0) {
|
||||||
return UpdateFSReport();
|
return UpdateFSReport(
|
||||||
|
// TODO(srujzs): We're currently reliant on compile error string parsing
|
||||||
|
// as hot reload rejections are sent to stderr just like other
|
||||||
|
// compilation errors. Ideally, we should have some shared parsing
|
||||||
|
// functionality, but that would require a shared package.
|
||||||
|
// See https://github.com/dart-lang/sdk/issues/60275.
|
||||||
|
hotReloadRejected: compilerOutput?.errorMessage?.contains('Hot reload rejected') ?? false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only update the last compiled time if we successfully compiled.
|
// Only update the last compiled time if we successfully compiled.
|
||||||
|
@ -432,19 +432,40 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
status = _logger.startProgress('Performing hot reload...', progressId: 'hot.reload');
|
status = _logger.startProgress('Performing hot reload...', progressId: 'hot.reload');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String targetPlatform = getNameForTargetPlatform(TargetPlatform.web_javascript);
|
||||||
|
final String sdkName = await device!.device!.sdkNameAndVersion;
|
||||||
|
|
||||||
|
late UpdateFSReport report;
|
||||||
if (debuggingOptions.buildInfo.isDebug && !debuggingOptions.webUseWasm) {
|
if (debuggingOptions.buildInfo.isDebug && !debuggingOptions.webUseWasm) {
|
||||||
await runSourceGenerators();
|
await runSourceGenerators();
|
||||||
// Don't reset the resident compiler for web, since the extra recompile is
|
// Don't reset the resident compiler for web, since the extra recompile is
|
||||||
// wasteful.
|
// wasteful.
|
||||||
final UpdateFSReport report = await _updateDevFS(
|
report = await _updateDevFS(fullRestart: fullRestart, resetCompiler: false);
|
||||||
fullRestart: fullRestart,
|
|
||||||
resetCompiler: false,
|
|
||||||
);
|
|
||||||
if (report.success) {
|
if (report.success) {
|
||||||
device!.generator!.accept();
|
device!.generator!.accept();
|
||||||
} else {
|
} else {
|
||||||
status.stop();
|
status.stop();
|
||||||
await device!.generator!.reject();
|
await device!.generator!.reject();
|
||||||
|
if (report.hotReloadRejected) {
|
||||||
|
// We cannot capture the reason why the reload was rejected as it may
|
||||||
|
// contain user information.
|
||||||
|
HotEvent(
|
||||||
|
'reload-reject',
|
||||||
|
targetPlatform: targetPlatform,
|
||||||
|
sdkName: sdkName,
|
||||||
|
emulator: false,
|
||||||
|
fullRestart: fullRestart,
|
||||||
|
).send();
|
||||||
|
_analytics.send(
|
||||||
|
Event.hotRunnerInfo(
|
||||||
|
label: 'reload-reject',
|
||||||
|
targetPlatform: targetPlatform,
|
||||||
|
sdkName: sdkName,
|
||||||
|
emulator: false,
|
||||||
|
fullRestart: fullRestart,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return OperationResult(1, 'Failed to recompile application.');
|
return OperationResult(1, 'Failed to recompile application.');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -469,6 +490,8 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
late Duration reloadDuration;
|
||||||
|
late Duration reassembleDuration;
|
||||||
try {
|
try {
|
||||||
if (!deviceIsDebuggable) {
|
if (!deviceIsDebuggable) {
|
||||||
_logger.printStatus('Recompile complete. Page requires refresh.');
|
_logger.printStatus('Recompile complete. Page requires refresh.');
|
||||||
@ -482,7 +505,9 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
} else {
|
} else {
|
||||||
// Isolates don't work on web. For lack of a better value, pass an
|
// Isolates don't work on web. For lack of a better value, pass an
|
||||||
// empty string for the isolate id.
|
// empty string for the isolate id.
|
||||||
|
final DateTime reloadStart = _systemClock.now();
|
||||||
final vmservice.ReloadReport report = await _vmService.service.reloadSources('');
|
final vmservice.ReloadReport report = await _vmService.service.reloadSources('');
|
||||||
|
reloadDuration = _systemClock.now().difference(reloadStart);
|
||||||
final ReloadReportContents contents = ReloadReportContents.fromReloadReport(report);
|
final ReloadReportContents contents = ReloadReportContents.fromReloadReport(report);
|
||||||
final bool success = contents.success ?? false;
|
final bool success = contents.success ?? false;
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@ -498,6 +523,21 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
}
|
}
|
||||||
return OperationResult(1, reloadFailedMessage);
|
return OperationResult(1, reloadFailedMessage);
|
||||||
}
|
}
|
||||||
|
String? failedReassemble;
|
||||||
|
final DateTime reassembleStart = _systemClock.now();
|
||||||
|
await _vmService
|
||||||
|
.flutterReassemble(isolateId: null)
|
||||||
|
.then(
|
||||||
|
(Object? o) => o,
|
||||||
|
onError: (Object error, StackTrace stackTrace) {
|
||||||
|
failedReassemble = 'Reassembling failed: $error\n$stackTrace';
|
||||||
|
_logger.printError(failedReassemble!);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
reassembleDuration = _systemClock.now().difference(reassembleStart);
|
||||||
|
if (failedReassemble != null) {
|
||||||
|
return OperationResult(1, failedReassemble!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// On non-debug builds, a hard refresh is required to ensure the
|
// On non-debug builds, a hard refresh is required to ensure the
|
||||||
@ -522,11 +562,6 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
|
|
||||||
// Don't track restart times for dart2js builds or web-server devices.
|
// Don't track restart times for dart2js builds or web-server devices.
|
||||||
if (debuggingOptions.buildInfo.isDebug && deviceIsDebuggable) {
|
if (debuggingOptions.buildInfo.isDebug && deviceIsDebuggable) {
|
||||||
// TODO(srujzs): There are a number of fields that the VM tracks in the
|
|
||||||
// analytics that we do not for both hot restart and reload. We should
|
|
||||||
// unify that.
|
|
||||||
final String targetPlatform = getNameForTargetPlatform(TargetPlatform.web_javascript);
|
|
||||||
final String sdkName = await device!.device!.sdkNameAndVersion;
|
|
||||||
if (fullRestart) {
|
if (fullRestart) {
|
||||||
_analytics.send(
|
_analytics.send(
|
||||||
Event.timing(
|
Event.timing(
|
||||||
@ -543,6 +578,12 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
fullRestart: true,
|
fullRestart: true,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
overallTimeInMs: elapsed.inMilliseconds,
|
overallTimeInMs: elapsed.inMilliseconds,
|
||||||
|
syncedBytes: report.syncedBytes,
|
||||||
|
invalidatedSourcesCount: report.invalidatedSourcesCount,
|
||||||
|
transferTimeInMs: report.transferDuration.inMilliseconds,
|
||||||
|
compileTimeInMs: report.compileDuration.inMilliseconds,
|
||||||
|
findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds,
|
||||||
|
scannedSourcesCount: report.scannedSourcesCount,
|
||||||
).send();
|
).send();
|
||||||
_analytics.send(
|
_analytics.send(
|
||||||
Event.hotRunnerInfo(
|
Event.hotRunnerInfo(
|
||||||
@ -553,6 +594,12 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
fullRestart: true,
|
fullRestart: true,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
overallTimeInMs: elapsed.inMilliseconds,
|
overallTimeInMs: elapsed.inMilliseconds,
|
||||||
|
syncedBytes: report.syncedBytes,
|
||||||
|
invalidatedSourcesCount: report.invalidatedSourcesCount,
|
||||||
|
transferTimeInMs: report.transferDuration.inMilliseconds,
|
||||||
|
compileTimeInMs: report.compileDuration.inMilliseconds,
|
||||||
|
findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds,
|
||||||
|
scannedSourcesCount: report.scannedSourcesCount,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -571,6 +618,14 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
fullRestart: false,
|
fullRestart: false,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
overallTimeInMs: elapsed.inMilliseconds,
|
overallTimeInMs: elapsed.inMilliseconds,
|
||||||
|
syncedBytes: report.syncedBytes,
|
||||||
|
invalidatedSourcesCount: report.invalidatedSourcesCount,
|
||||||
|
transferTimeInMs: report.transferDuration.inMilliseconds,
|
||||||
|
compileTimeInMs: report.compileDuration.inMilliseconds,
|
||||||
|
findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds,
|
||||||
|
scannedSourcesCount: report.scannedSourcesCount,
|
||||||
|
reassembleTimeInMs: reassembleDuration.inMilliseconds,
|
||||||
|
reloadVMTimeInMs: reloadDuration.inMilliseconds,
|
||||||
).send();
|
).send();
|
||||||
_analytics.send(
|
_analytics.send(
|
||||||
Event.hotRunnerInfo(
|
Event.hotRunnerInfo(
|
||||||
@ -581,6 +636,14 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
fullRestart: false,
|
fullRestart: false,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
overallTimeInMs: elapsed.inMilliseconds,
|
overallTimeInMs: elapsed.inMilliseconds,
|
||||||
|
syncedBytes: report.syncedBytes,
|
||||||
|
invalidatedSourcesCount: report.invalidatedSourcesCount,
|
||||||
|
transferTimeInMs: report.transferDuration.inMilliseconds,
|
||||||
|
compileTimeInMs: report.compileDuration.inMilliseconds,
|
||||||
|
findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds,
|
||||||
|
scannedSourcesCount: report.scannedSourcesCount,
|
||||||
|
reassembleTimeInMs: reassembleDuration.inMilliseconds,
|
||||||
|
reloadVMTimeInMs: reloadDuration.inMilliseconds,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,7 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
|
|||||||
await device.vmService!.invokeFlutterExtensionRpcRaw(
|
await device.vmService!.invokeFlutterExtensionRpcRaw(
|
||||||
method,
|
method,
|
||||||
args: params,
|
args: params,
|
||||||
isolateId: views.first.uiIsolate!.id!,
|
isolateId: views.first.uiIsolate!.id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1416,7 +1416,7 @@ Future<ReassembleResult> _defaultReassembleHelper(
|
|||||||
// If the tool identified a change in a single widget, do a fast instead
|
// If the tool identified a change in a single widget, do a fast instead
|
||||||
// of a full reassemble.
|
// of a full reassemble.
|
||||||
final Future<void> reassembleWork = device.vmService!.flutterReassemble(
|
final Future<void> reassembleWork = device.vmService!.flutterReassemble(
|
||||||
isolateId: view.uiIsolate!.id!,
|
isolateId: view.uiIsolate!.id,
|
||||||
);
|
);
|
||||||
reassembleFutures.add(
|
reassembleFutures.add(
|
||||||
reassembleWork.then(
|
reassembleWork.then(
|
||||||
|
@ -684,7 +684,7 @@ class FlutterVmService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, Object?>?> flutterReassemble({required String isolateId}) {
|
Future<Map<String, Object?>?> flutterReassemble({required String? isolateId}) {
|
||||||
return invokeFlutterExtensionRpcRaw('ext.flutter.reassemble', isolateId: isolateId);
|
return invokeFlutterExtensionRpcRaw('ext.flutter.reassemble', isolateId: isolateId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -803,12 +803,12 @@ class FlutterVmService {
|
|||||||
/// available, returns null.
|
/// available, returns null.
|
||||||
Future<Map<String, Object?>?> invokeFlutterExtensionRpcRaw(
|
Future<Map<String, Object?>?> invokeFlutterExtensionRpcRaw(
|
||||||
String method, {
|
String method, {
|
||||||
required String isolateId,
|
required String? isolateId,
|
||||||
Map<String, Object?>? args,
|
Map<String, Object?>? args,
|
||||||
}) async {
|
}) async {
|
||||||
final vm_service.Response? response = await _checkedCallServiceExtension(
|
final vm_service.Response? response = await _checkedCallServiceExtension(
|
||||||
method,
|
method,
|
||||||
args: <String, Object?>{'isolateId': isolateId, ...?args},
|
args: <String, Object?>{if (isolateId != null) 'isolateId': isolateId, ...?args},
|
||||||
);
|
);
|
||||||
return response?.json;
|
return response?.json;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ dependencies:
|
|||||||
archive: 3.6.1
|
archive: 3.6.1
|
||||||
args: 2.6.0
|
args: 2.6.0
|
||||||
dds: 5.0.0
|
dds: 5.0.0
|
||||||
dwds: 24.3.6
|
dwds: 24.3.7
|
||||||
code_builder: 4.10.1
|
code_builder: 4.10.1
|
||||||
completion: 1.0.1
|
completion: 1.0.1
|
||||||
coverage: 1.11.1
|
coverage: 1.11.1
|
||||||
@ -122,4 +122,4 @@ dartdoc:
|
|||||||
# Exclude this package from the hosted API docs.
|
# Exclude this package from the hosted API docs.
|
||||||
nodoc: true
|
nodoc: true
|
||||||
|
|
||||||
# PUBSPEC CHECKSUM: 5a6c
|
# PUBSPEC CHECKSUM: 776d
|
||||||
|
@ -260,6 +260,11 @@ class FakeWebDevice extends Fake implements Device {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> get sdkNameAndVersion async {
|
||||||
|
return 'Flutter Tools';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LaunchResult> startApp(
|
Future<LaunchResult> startApp(
|
||||||
ApplicationPackage? package, {
|
ApplicationPackage? package, {
|
||||||
|
@ -75,6 +75,11 @@ const List<VmServiceExpectation> kAttachExpectations = <VmServiceExpectation>[
|
|||||||
...kAttachIsolateExpectations,
|
...kAttachIsolateExpectations,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const List<String> kDdcLibraryBundleFlags = <String>[
|
||||||
|
'--dartdevc-module-format=ddc',
|
||||||
|
'--dartdevc-canary',
|
||||||
|
];
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
late FakeDebugConnection debugConnection;
|
late FakeDebugConnection debugConnection;
|
||||||
late FakeChromeDevice chromeDevice;
|
late FakeChromeDevice chromeDevice;
|
||||||
@ -725,13 +730,29 @@ name: my_app
|
|||||||
flutterDevice,
|
flutterDevice,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
systemClock: SystemClock.fixed(DateTime(2001)),
|
systemClock: SystemClock.fixed(DateTime(2001)),
|
||||||
|
debuggingOptions: DebuggingOptions.enabled(
|
||||||
|
const BuildInfo(
|
||||||
|
BuildMode.debug,
|
||||||
|
null,
|
||||||
|
trackWidgetCreation: true,
|
||||||
|
treeShakeIcons: false,
|
||||||
|
packageConfigPath: '.dart_tool/package_config.json',
|
||||||
|
// Hot reload only supported with these flags for now.
|
||||||
|
extraFrontEndOptions: kDdcLibraryBundleFlags,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
fakeVmServiceHost = FakeVmServiceHost(
|
fakeVmServiceHost = FakeVmServiceHost(
|
||||||
requests: <VmServiceExpectation>[
|
requests: <VmServiceExpectation>[
|
||||||
...kAttachExpectations,
|
...kAttachExpectations,
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kHotRestartServiceName,
|
method: kReloadSourcesServiceName,
|
||||||
jsonResponse: <String, Object>{'type': 'Success'},
|
args: <String, Object>{'isolateId': ''},
|
||||||
|
jsonResponse: <String, Object>{'type': 'ReloadReport', 'success': true},
|
||||||
|
),
|
||||||
|
const FakeVmServiceRequest(
|
||||||
|
method: 'ext.flutter.reassemble',
|
||||||
|
jsonResponse: <String, Object>{'type': 'ReloadReport', 'success': true},
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'streamListen',
|
method: 'streamListen',
|
||||||
@ -769,7 +790,7 @@ name: my_app
|
|||||||
|
|
||||||
final OperationResult result = await residentWebRunner.restart();
|
final OperationResult result = await residentWebRunner.restart();
|
||||||
|
|
||||||
expect(logger.statusText, contains('Restarted application in'));
|
expect(logger.statusText, contains('Reloaded application in'));
|
||||||
expect(result.code, 0);
|
expect(result.code, 0);
|
||||||
expect(webDevFS.mainUri.toString(), contains('entrypoint.dart'));
|
expect(webDevFS.mainUri.toString(), contains('entrypoint.dart'));
|
||||||
|
|
||||||
@ -777,24 +798,26 @@ name: my_app
|
|||||||
fakeAnalytics.sentEvents,
|
fakeAnalytics.sentEvents,
|
||||||
contains(
|
contains(
|
||||||
Event.hotRunnerInfo(
|
Event.hotRunnerInfo(
|
||||||
label: 'restart',
|
label: 'reload',
|
||||||
targetPlatform: 'web-javascript',
|
targetPlatform: 'web-javascript',
|
||||||
sdkName: '',
|
sdkName: '',
|
||||||
emulator: false,
|
emulator: false,
|
||||||
fullRestart: true,
|
fullRestart: false,
|
||||||
overallTimeInMs: 0,
|
overallTimeInMs: 0,
|
||||||
|
syncedBytes: 0,
|
||||||
|
invalidatedSourcesCount: 0,
|
||||||
|
transferTimeInMs: 0,
|
||||||
|
compileTimeInMs: 0,
|
||||||
|
findInvalidatedTimeInMs: 0,
|
||||||
|
scannedSourcesCount: 0,
|
||||||
|
reassembleTimeInMs: 0,
|
||||||
|
reloadVMTimeInMs: 0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
fakeAnalytics.sentEvents,
|
fakeAnalytics.sentEvents,
|
||||||
contains(
|
contains(Event.timing(workflow: 'hot', variableName: 'reload', elapsedMilliseconds: 0)),
|
||||||
Event.timing(
|
|
||||||
workflow: 'hot',
|
|
||||||
variableName: 'web-incremental-restart',
|
|
||||||
elapsedMilliseconds: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
overrides: <Type, Generator>{
|
overrides: <Type, Generator>{
|
||||||
@ -807,20 +830,31 @@ name: my_app
|
|||||||
);
|
);
|
||||||
|
|
||||||
testUsingContext(
|
testUsingContext(
|
||||||
'Can hot restart after attaching',
|
'Hot reload reject reports correct analytics',
|
||||||
() async {
|
() async {
|
||||||
final BufferLogger logger = BufferLogger.test();
|
final BufferLogger logger = BufferLogger.test();
|
||||||
final ResidentRunner residentWebRunner = setUpResidentRunner(
|
final ResidentRunner residentWebRunner = setUpResidentRunner(
|
||||||
flutterDevice,
|
flutterDevice,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
systemClock: SystemClock.fixed(DateTime(2001)),
|
systemClock: SystemClock.fixed(DateTime(2001)),
|
||||||
|
debuggingOptions: DebuggingOptions.enabled(
|
||||||
|
const BuildInfo(
|
||||||
|
BuildMode.debug,
|
||||||
|
null,
|
||||||
|
trackWidgetCreation: true,
|
||||||
|
treeShakeIcons: false,
|
||||||
|
packageConfigPath: '.dart_tool/package_config.json',
|
||||||
|
// Hot reload only supported with these flags for now.
|
||||||
|
extraFrontEndOptions: kDdcLibraryBundleFlags,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
fakeVmServiceHost = FakeVmServiceHost(
|
fakeVmServiceHost = FakeVmServiceHost(
|
||||||
requests: <VmServiceExpectation>[
|
requests: <VmServiceExpectation>[
|
||||||
...kAttachExpectations,
|
...kAttachExpectations,
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kHotRestartServiceName,
|
method: 'streamListen',
|
||||||
jsonResponse: <String, Object>{'type': 'Success'},
|
args: <String, Object>{'streamId': 'Isolate'},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -848,39 +882,25 @@ name: my_app
|
|||||||
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
||||||
Completer<DebugConnectionInfo>();
|
Completer<DebugConnectionInfo>();
|
||||||
unawaited(residentWebRunner.run(connectionInfoCompleter: connectionInfoCompleter));
|
unawaited(residentWebRunner.run(connectionInfoCompleter: connectionInfoCompleter));
|
||||||
await connectionInfoCompleter.future;
|
final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future;
|
||||||
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
|
|
||||||
|
|
||||||
// Ensure that generated entrypoint is generated correctly.
|
expect(debugConnectionInfo, isNotNull);
|
||||||
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_web' as ui_web;"));
|
|
||||||
expect(entrypointContents, contains('await ui_web.bootstrapEngine('));
|
|
||||||
|
|
||||||
expect(logger.statusText, contains('Restarted application in'));
|
webDevFS.report = UpdateFSReport(hotReloadRejected: true);
|
||||||
expect(result.code, 0);
|
final OperationResult result = await residentWebRunner.restart();
|
||||||
|
|
||||||
|
expect(result.code, 1);
|
||||||
|
expect(webDevFS.mainUri.toString(), contains('entrypoint.dart'));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
fakeAnalytics.sentEvents,
|
fakeAnalytics.sentEvents,
|
||||||
contains(
|
contains(
|
||||||
Event.hotRunnerInfo(
|
Event.hotRunnerInfo(
|
||||||
label: 'restart',
|
label: 'reload-reject',
|
||||||
targetPlatform: 'web-javascript',
|
targetPlatform: 'web-javascript',
|
||||||
sdkName: '',
|
sdkName: '',
|
||||||
emulator: false,
|
emulator: false,
|
||||||
fullRestart: true,
|
fullRestart: false,
|
||||||
overallTimeInMs: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
fakeAnalytics.sentEvents,
|
|
||||||
contains(
|
|
||||||
Event.timing(
|
|
||||||
workflow: 'hot',
|
|
||||||
variableName: 'web-incremental-restart',
|
|
||||||
elapsedMilliseconds: 0,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -894,6 +914,120 @@ name: my_app
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Hot restart is available with and without the DDC library bundle format.
|
||||||
|
// Test one extra config where `fullRestart` is false without the DDC library
|
||||||
|
// bundle format - we should do a hot restart in this case because hot reload
|
||||||
|
// is not available.
|
||||||
|
for (final (List<String> flags, bool fullRestart) in <(List<String>, bool)>[
|
||||||
|
(kDdcLibraryBundleFlags, true),
|
||||||
|
(<String>[], true),
|
||||||
|
(<String>[], false),
|
||||||
|
]) {
|
||||||
|
testUsingContext(
|
||||||
|
'Can hot restart after attaching with flags: $flags fullRestart: $fullRestart',
|
||||||
|
() async {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
final ResidentRunner residentWebRunner = setUpResidentRunner(
|
||||||
|
flutterDevice,
|
||||||
|
logger: logger,
|
||||||
|
systemClock: SystemClock.fixed(DateTime(2001)),
|
||||||
|
debuggingOptions: DebuggingOptions.enabled(
|
||||||
|
BuildInfo(
|
||||||
|
BuildMode.debug,
|
||||||
|
null,
|
||||||
|
trackWidgetCreation: true,
|
||||||
|
treeShakeIcons: false,
|
||||||
|
packageConfigPath: '.dart_tool/package_config.json',
|
||||||
|
extraFrontEndOptions: flags,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
fakeVmServiceHost = FakeVmServiceHost(
|
||||||
|
requests: <VmServiceExpectation>[
|
||||||
|
...kAttachExpectations,
|
||||||
|
const FakeVmServiceRequest(
|
||||||
|
method: kHotRestartServiceName,
|
||||||
|
jsonResponse: <String, Object>{'type': 'Success'},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
setupMocks();
|
||||||
|
final TestChromiumLauncher chromiumLauncher = TestChromiumLauncher();
|
||||||
|
final FakeProcess process = FakeProcess();
|
||||||
|
final Chromium chrome = Chromium(
|
||||||
|
1,
|
||||||
|
chromeConnection,
|
||||||
|
chromiumLauncher: chromiumLauncher,
|
||||||
|
process: process,
|
||||||
|
logger: logger,
|
||||||
|
);
|
||||||
|
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: fullRestart);
|
||||||
|
|
||||||
|
// 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_web' as ui_web;"));
|
||||||
|
expect(entrypointContents, contains('await ui_web.bootstrapEngine('));
|
||||||
|
|
||||||
|
expect(logger.statusText, contains('Restarted application in'));
|
||||||
|
expect(result.code, 0);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
fakeAnalytics.sentEvents,
|
||||||
|
contains(
|
||||||
|
Event.hotRunnerInfo(
|
||||||
|
label: 'restart',
|
||||||
|
targetPlatform: 'web-javascript',
|
||||||
|
sdkName: '',
|
||||||
|
emulator: false,
|
||||||
|
fullRestart: true,
|
||||||
|
overallTimeInMs: 0,
|
||||||
|
syncedBytes: 0,
|
||||||
|
invalidatedSourcesCount: 0,
|
||||||
|
transferTimeInMs: 0,
|
||||||
|
compileTimeInMs: 0,
|
||||||
|
findInvalidatedTimeInMs: 0,
|
||||||
|
scannedSourcesCount: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
fakeAnalytics.sentEvents,
|
||||||
|
contains(
|
||||||
|
Event.timing(
|
||||||
|
workflow: 'hot',
|
||||||
|
variableName: 'web-incremental-restart',
|
||||||
|
elapsedMilliseconds: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
Analytics: () => fakeAnalytics,
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
FeatureFlags: enableExplicitPackageDependencies,
|
||||||
|
Pub: FakePubWithPrimedDeps.new,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
testUsingContext(
|
testUsingContext(
|
||||||
'Can hot restart after attaching with web-server device',
|
'Can hot restart after attaching with web-server device',
|
||||||
() async {
|
() async {
|
||||||
@ -1130,7 +1264,8 @@ name: my_app
|
|||||||
trackWidgetCreation: true,
|
trackWidgetCreation: true,
|
||||||
treeShakeIcons: false,
|
treeShakeIcons: false,
|
||||||
packageConfigPath: '.dart_tool/package_config.json',
|
packageConfigPath: '.dart_tool/package_config.json',
|
||||||
extraFrontEndOptions: <String>['--dartdevc-module-format=ddc', '--dartdevc-canary'],
|
// Hot reload only supported with these flags for now.
|
||||||
|
extraFrontEndOptions: kDdcLibraryBundleFlags,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user