blob: 89fa1bc202cb47b2dabb3c8a7ac6455a812d6159 [file] [edit]
// Copyright (c) 2026, 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.
import 'dart:convert';
import 'dart:io';
import 'dart:io' as io;
import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
import 'package:compiler/src/deferred_load/program_split_constraints/parser.dart';
import 'package:dart2wasm/deferred_load/partition.dart';
import 'package:dart2wasm/deferred_loading.dart';
import 'package:dart2wasm/modules.dart';
import 'package:dart2wasm/util.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/binary/ast_from_binary.dart';
import 'package:kernel/core_types.dart';
import '../util.dart';
final sdkRootUri = Uri.base;
final sdkRoot = Directory.current.path;
const updateExpectations = bool.fromEnvironment('updateExpectations');
void main() async {
await runDart2wasmTests();
await runDart2jsCustomSplitTests();
}
Future runDart2wasmTests() async {
final outputDataDir = Directory(
'$sdkRoot/pkg/dart2wasm/test/deferred_loading/partition_tests/',
);
final tests = outputDataDir
.listSync()
.where((fse) => fse is File && fse.path.endsWith('.dart'))
.toList();
for (final mainFile in tests) {
final mainFilePath = mainFile.path;
final mainFileBasePath = mainFilePath.substring(
0,
mainFilePath.length - '.dart'.length,
);
// Generate a partitioning without constraints under
// `<testname>.default.txt`.
await testPartitionExpectation(
mainFilePath,
null,
'$mainFileBasePath.default.txt',
);
// If the `<testname>.constraints.json` exists generate a partitioining with
// constraints under `<testname>.constraints.txt`.
final jsonConstraintsFile = File('$mainFileBasePath.constraints.json');
if (jsonConstraintsFile.existsSync()) {
final constraintsJsonString = jsonConstraintsFile
.readAsStringSync()
.replaceAll(
'\${SDK_ROOT}',
Directory.current.absolute.uri.toString(),
);
final constraints = Parser().read(constraintsJsonString);
await testPartitionExpectation(
mainFilePath,
constraints,
'$mainFileBasePath.constraints.txt',
);
}
}
}
Future runDart2jsCustomSplitTests() async {
final outputDataDir = Directory(
'$sdkRoot/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split',
);
await forEachDart2JsCustomSplitTest((
testName,
testDir,
mainFile,
constraintsFile,
) async {
// Load constraints data.
final constraintsJsonString = replaceDart2JsTestUris(
File('$testDir/constraints.json').readAsStringSync(),
testDir,
);
final constraints = Parser().read(constraintsJsonString);
// Test with constraints.
await testPartitionExpectation(
mainFile,
constraints,
'${outputDataDir.path}/$testName.constraints.txt',
);
// Test without constraints.
await testPartitionExpectation(
mainFile,
null,
'${outputDataDir.path}/$testName.default.txt',
);
});
}
Future testPartitionExpectation(
String mainFile,
ConstraintData? constraints,
String expectationFilepath,
) async {
print('\nTesting $mainFile against $expectationFilepath');
await withTempDir((tempDir) async {
final outDill = '$tempDir/out.dill';
final result = await Process.run('/usr/bin/env', [
'bash',
'pkg/dart2wasm/tool/compile_benchmark',
'--enable-asserts',
'--compiler-asserts',
'--phases=cfe,tfa',
'--enable-deferred-loading',
'-o',
outDill,
mainFile,
]);
if (result.exitCode != 0) {
io.exitCode = 42;
print('Failed to compile $mainFile:');
print('stdout:\n${result.stdout}');
print('stderr:\n${result.stderr}');
return;
}
final (component, coreTypes) = readKernel(outDill);
final loadingMap = DeferredModuleLoadingMap.fromComponent(component);
component.accept(DeferredLoadingLowering(coreTypes, loadingMap));
final assertsEnabled = true;
final partition = partitionAppplication(
coreTypes,
component,
assertsEnabled,
loadingMap,
findWasmRoots(coreTypes, component),
constraints: constraints,
);
final actual = partition.toText(sdkRootUri, includeRoot: false);
final expectationFile = File(expectationFilepath);
if (!expectationFile.existsSync() ||
actual != expectationFile.readAsStringSync()) {
if (updateExpectations) {
print('Updating expectation file: $expectationFilepath');
expectationFile.parent.createSync(recursive: true);
expectationFile.writeAsStringSync(actual);
} else {
io.exitCode = 42;
if (!expectationFile.existsSync()) {
print('Expectation file is missing.');
} else {
final expectation = expectationFile.readAsStringSync();
print('Expectation file mismatch:.');
print('Expected:\n$expectation');
print('Actual:\n$actual');
}
}
}
});
}
Future forEachDart2JsCustomSplitTest(
Future Function(
String testName,
String testDir,
String mainFile,
String constraintsFile,
)
fun,
) async {
final dataDir = Directory('$sdkRoot/pkg/compiler/test/custom_split/data');
final testCases =
dataDir
.listSync()
.whereType<Directory>()
.map((d) => d.path.split(Platform.pathSeparator).last)
.toList()
..sort();
final outputDataDir = Directory(
'$sdkRoot/pkg/dart2wasm/test/deferred_loading/partition_test_data',
);
if (!outputDataDir.existsSync()) {
outputDataDir.createSync(recursive: true);
}
for (final testName in testCases) {
final testDir = sdkRootUri
.resolve('pkg/compiler/test/custom_split/data/$testName')
.toFilePath();
final constraintsFile = File('$testDir/constraints.json');
final mainFile = File('$testDir/main.dart');
await fun(testName, testDir, mainFile.path, constraintsFile.path);
}
}
/// The dart2js constraint data has `memory:*` uris in them, replace them with
/// actual file uris.
///
/// The `memory:sdk/tests/web/native/main.dart` is faking the actual main.
String replaceDart2JsTestUris(String constraintsJsonString, String testDir) {
Object updateJson(Object o) {
if (o is Map) {
o.updateAll((key, value) => updateJson(value));
return o;
}
if (o is List) {
for (int i = 0; i < o.length; ++i) {
o[i] = updateJson(o[i]);
}
return o;
}
if (o is String) {
final needle = 'memory:sdk/tests/web/native/';
if (o.startsWith(needle)) {
return sdkRootUri
.resolve('$testDir/${o.substring(needle.length)}')
.toString();
}
if (o.startsWith('memory:/')) {
return sdkRootUri.resolve(o.substring('memory:/'.length)).toString();
}
return o;
}
return o;
}
final data = json.decode(constraintsJsonString);
updateJson(data);
return json.encode(data);
}
(Component, CoreTypes) readKernel(String dillFilepath) {
final component = createEmptyComponent();
BinaryBuilderWithMetadata(
File(dillFilepath).readAsBytesSync(),
).readComponent(component);
return (component, CoreTypes(component));
}