d43d9df1a7
Change-Id: If92f55296dfe83b64165a2bd07eaefb7d137198c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/497341 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Nate Biggs <natebiggs@google.com>
187 lines
5.2 KiB
Dart
187 lines
5.2 KiB
Dart
// 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.
|
|
|
|
/// Entrypoint to run dynamic module tests.
|
|
library;
|
|
|
|
import 'dart:io';
|
|
|
|
import 'package:args/args.dart';
|
|
|
|
import 'vm.dart';
|
|
import 'ddc.dart';
|
|
import 'load.dart';
|
|
import 'model.dart';
|
|
import 'target.dart';
|
|
import 'util.dart';
|
|
|
|
void main(List<String> args) async {
|
|
final suiteRoot = repoRoot.resolve('pkg/dynamic_modules/test/data/');
|
|
var tests = loadAllTests(suiteRoot);
|
|
final parser = ArgParser()
|
|
..addFlag('help', help: 'Help message', negatable: false, abbr: 'h')
|
|
..addOption(
|
|
'test',
|
|
help: 'Run a single test, rather than the entire suite',
|
|
allowed: tests.map((t) => t.name).toList(),
|
|
abbr: 't',
|
|
)
|
|
..addOption(
|
|
'runtime',
|
|
help: 'Which runtime to use to run the test configuration',
|
|
allowed: Target.values.map((v) => v.name),
|
|
defaultsTo: Target.ddc.name,
|
|
abbr: 'r',
|
|
)
|
|
..addOption(
|
|
'configuration',
|
|
help: 'Configuration to use for reporting test results',
|
|
abbr: 'n',
|
|
)
|
|
..addOption(
|
|
'output-directory',
|
|
help: 'location where to emit the json-l result and log files',
|
|
)
|
|
..addFlag(
|
|
'verbose',
|
|
help: 'Show a lot of information',
|
|
negatable: false,
|
|
abbr: 'v',
|
|
);
|
|
final options = parser.parse(args);
|
|
if (options['help'] as bool) {
|
|
print(parser.usage);
|
|
return;
|
|
}
|
|
|
|
var singleTest = options['test'] as String?;
|
|
if (singleTest != null) {
|
|
tests = [...tests.where((t) => t.name == singleTest)];
|
|
}
|
|
|
|
final logger = Logger(options['verbose'] as bool);
|
|
|
|
final target = Target.values.firstWhere((v) => v.name == options['runtime']);
|
|
late TargetExecutor executor;
|
|
try {
|
|
executor = switch (target) {
|
|
Target.ddc => DdcExecutor(logger),
|
|
Target.aot => VmExecutor(logger, mode: VmMode.aot),
|
|
Target.jit => VmExecutor(logger, mode: VmMode.jit),
|
|
};
|
|
|
|
final results = <DynamicModuleTestResult>[];
|
|
for (final t in tests) {
|
|
final testResult = await _runSingleTest(t, executor);
|
|
if (testResult.status != Status.pass) {
|
|
logger.error(testResult.details);
|
|
}
|
|
results.add(testResult);
|
|
}
|
|
final result = _reportResults(
|
|
results,
|
|
writeLog: singleTest == null,
|
|
configuration: options['configuration'],
|
|
logDir: options['output-directory'],
|
|
);
|
|
if (result != 0) {
|
|
exitCode = result;
|
|
}
|
|
} finally {
|
|
executor.suiteComplete();
|
|
}
|
|
}
|
|
|
|
/// Takes the steps to build the artifacts needed by a test and then execute it
|
|
/// on the target environment.
|
|
Future<DynamicModuleTestResult> _runSingleTest(
|
|
DynamicModuleTest test,
|
|
TargetExecutor target,
|
|
) async {
|
|
var timer = Stopwatch()..start();
|
|
try {
|
|
await target.compileApplication(test);
|
|
for (var name in test.dynamicModules.keys) {
|
|
await target.compileDynamicModule(test, name);
|
|
}
|
|
} catch (e, st) {
|
|
return DynamicModuleTestResult.compileError(test, '$e\n$st', timer.elapsed);
|
|
}
|
|
|
|
try {
|
|
await target.executeApplication(test);
|
|
} catch (e, st) {
|
|
return DynamicModuleTestResult.runtimeError(test, '$e\n$st', timer.elapsed);
|
|
}
|
|
|
|
return DynamicModuleTestResult.pass(test, timer.elapsed);
|
|
}
|
|
|
|
/// Generates a report of the test results in the JSON format
|
|
/// that is expected by our testing infrastructure.
|
|
int _reportResults(
|
|
List<DynamicModuleTestResult> results, {
|
|
required bool writeLog,
|
|
String? configuration,
|
|
String? logDir,
|
|
}) {
|
|
bool fail = false;
|
|
print('Test results:');
|
|
for (var result in results) {
|
|
print(' ${result.name}: ${result.status}');
|
|
if (result.status != Status.pass) fail = true;
|
|
}
|
|
if (fail) print('Error: some tests failed');
|
|
|
|
if (writeLog) {
|
|
if (logDir == null) {
|
|
print('Error: no output directory provided, logs won\'t be emitted.');
|
|
return 1;
|
|
}
|
|
if (configuration == null) {
|
|
print('Error: no configuration name provided, logs won\'t be emitted.');
|
|
return 1;
|
|
}
|
|
|
|
// Ensure the directory URI ends with a path separator.
|
|
var dirUri = Directory(logDir).uri;
|
|
File.fromUri(dirUri.resolve('results.json')).writeAsStringSync(
|
|
results.map((r) => '${r.toRecordJson(configuration)}\n').join(),
|
|
flush: true,
|
|
);
|
|
File.fromUri(dirUri.resolve('logs.json')).writeAsStringSync(
|
|
results
|
|
.where((r) => r.status != Status.pass)
|
|
.map((r) => '${r.toLogJson(configuration)}\n')
|
|
.join(),
|
|
flush: true,
|
|
);
|
|
|
|
print('Success: log files emitted under $dirUri');
|
|
} else if (fail) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// Placeholder until we implement all executors.
|
|
class UnimplementedExecutor implements TargetExecutor {
|
|
UnimplementedExecutor(Logger logger);
|
|
|
|
@override
|
|
Future<void> suiteComplete() async => throw UnimplementedError();
|
|
|
|
@override
|
|
Future compileApplication(DynamicModuleTest test) async =>
|
|
throw UnimplementedError();
|
|
|
|
@override
|
|
Future compileDynamicModule(DynamicModuleTest test, String name) async =>
|
|
throw UnimplementedError();
|
|
|
|
@override
|
|
Future executeApplication(DynamicModuleTest test) async =>
|
|
throw UnimplementedError();
|
|
}
|