fd3cb424e2
* Adds support to emit log records for test outcomes and failure logs * Adds steps to the test_matrix Change-Id: Ibabf0410a0304aae446387a0d3ca147488f56df3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/383929 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Sigmund Cherem <sigmund@google.com>
164 lines
5.0 KiB
Dart
164 lines
5.0 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 'aot.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 => AotExecutor(logger),
|
|
Target.dart2wasm => UnimplementedExecutor(logger),
|
|
};
|
|
|
|
final results = <DynamicModuleTestResult>[];
|
|
for (final t in tests) {
|
|
results.add(await _runSingleTest(t, executor));
|
|
}
|
|
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();
|
|
}
|