blob: e984a824f31a3ab180f20e478b52e75d63bcd18f [file] [log] [blame]
// 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 hotRestart();
external JSNumber get hotReloadGeneration;
external JSNumber get hotRestartGeneration;
}
@JS('dartDevEmbedder')
external _DartDevEmbedder get _dartDevEmbedder;
@JS('\$injectedFilesAndLibrariesToReload')
external JSArray<JSArray<JSString>>? injectedFilesAndLibrariesToReload(
JSNumber requestedFileGeneration);
@JS('\$dartLoader')
external _DartLoader get _dartLoader;
final _ddcLoader = _dartLoader.loader;
int get hotRestartGeneration => _dartDevEmbedder.hotRestartGeneration.toDartInt;
Future<void> hotRestart() async {
_ddcLoader.intendedHotRestartGeneration++;
final restartReceipt = HotReloadReceipt(
generation: _ddcLoader.intendedHotRestartGeneration,
status: Status.restarted,
);
print('${HotReloadReceipt.hotReloadReceiptTag}'
'${jsonEncode(restartReceipt.toJson())}');
await _dartDevEmbedder.hotRestart().toDart;
}
/// 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,
);
}