blob: ba925d19274290763d60733e6cf9f19976f49ff3 [file]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Helper functions for the hot reload test suite.
//
// The structure here reflects interfaces defined in DDC's
// `ddc_module_loader.js`.
import 'dart:convert';
import 'dart:js_interop';
import '../hot_reload_receipt.dart';
extension type _DartLoader(JSObject _) implements JSObject {
external _DDCLoader get loader;
}
extension type _DDCLoader(JSObject _) implements JSObject {
external JSPromise hotReload();
external void hotRestart();
external int get hotReloadGeneration;
external int get hotRestartGeneration;
external int intendedHotRestartGeneration;
}
extension type _DartDevEmbedder(JSObject _) implements JSObject {
external JSPromise hotReload(JSArray<JSString> files, JSArray<JSString> ids);
external JSPromise<JSArray<JSObject>> hotRestartBegin(
JSArray<JSObject> filesToRequest,
);
external void hotRestartEnd();
external JSNumber get hotReloadGeneration;
external JSNumber get hotRestartGeneration;
}
@JS('dartDevEmbedder')
external _DartDevEmbedder get _dartDevEmbedder;
@JS('\$injectedFilesAndLibrariesToReload')
external JSArray<JSArray<JSString>>? injectedFilesAndLibrariesToReload(
JSNumber requestedFileGeneration,
);
@JS('\$injectedReloadedSourcesHelper')
external JSArray<JSObject>? injectedReloadedSourcesHelper();
@JS('\$dartLoader')
external _DartLoader get _dartLoader;
final _ddcLoader = _dartLoader.loader;
int get hotRestartGeneration => _dartDevEmbedder.hotRestartGeneration.toDartInt;
Future<void> hotRestart() async {
_ddcLoader.intendedHotRestartGeneration++;
final filesToRequest = injectedReloadedSourcesHelper();
if (filesToRequest == null) {
throw Exception('Restart requested but no remaining generations found.');
}
await _dartDevEmbedder.hotRestartBegin(filesToRequest).toDart;
// Must return the receipt before re-running the main method.
final restartReceipt = HotReloadReceipt(
generation: _ddcLoader.intendedHotRestartGeneration,
status: Status.restarted,
);
print(
'${HotReloadReceipt.hotReloadReceiptTag}'
'${jsonEncode(restartReceipt.toJson())}',
);
_dartDevEmbedder.hotRestartEnd();
}
/// The reload generation of the currently running application.
int get hotReloadGeneration => _dartDevEmbedder.hotReloadGeneration.toDartInt;
/// The generation of reload requests by the test application.
///
/// This could differ from [hotReloadGeneration] when file generations get
/// rejected and the running application stays in the previous "application"
/// generation.
int _hotReloadFileGeneration = 0;
Future<void> hotReload({bool expectRejection = false}) async {
_hotReloadFileGeneration++;
final generationFileInfo = injectedFilesAndLibrariesToReload(
_hotReloadFileGeneration.toJS,
);
final HotReloadReceipt reloadStatus = expectRejection
? _rejectNextGeneration(generationFileInfo)
: await _reloadNextGeneration(generationFileInfo);
// Write reload receipt with a leading tag to be recognized by the reload
// suite runner and validated.
print(
'${HotReloadReceipt.hotReloadReceiptTag}'
'${jsonEncode(reloadStatus.toJson())}',
);
}
HotReloadReceipt _rejectNextGeneration(
JSArray<JSArray<JSString>>? generationFileInfo,
) {
if (generationFileInfo != null) {
throw Exception(
'Generation $_hotReloadFileGeneration was not rejected at compile '
'time. Verify the calls of `hotReload(expectRejection: true)` in the '
'test source match the rejected generation files.',
);
}
// This reload wasn't expected to find any files so this is OK.
// * The correct reason for rejection was already validated at compile time
// so nothing to check here.
// * Expected number and order of reloads are validated at the end of the test
// run.
return HotReloadReceipt(
generation: _hotReloadFileGeneration,
status: Status.rejected,
rejectionMessage: HotReloadReceipt.compileTimeErrorMessage,
);
}
Future<HotReloadReceipt> _reloadNextGeneration(
JSArray<JSArray<JSString>>? generationFileInfo,
) async {
if (generationFileInfo == null) {
throw Exception(
'No compiled files found for generation $_hotReloadFileGeneration. '
'Verify the calls of `hotReload()` in the test match the accepted '
'generation source files.',
);
}
await (_dartDevEmbedder.hotReload(
generationFileInfo[0],
generationFileInfo[1],
)).toDart;
return HotReloadReceipt(
generation: _hotReloadFileGeneration,
status: Status.accepted,
);
}