blob: 75998988a74281d673f72b8c44dc7374aab440bb [file] [log] [blame]
// Copyright (c) 2019, 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 'package:_fe_analyzer_shared/src/messages/severity.dart'
show CfeSeverity;
import 'package:_fe_analyzer_shared/src/testing/id.dart'
show ActualData, DataRegistry, Id;
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/target/targets.dart';
import '../api_prototype/compiler_options.dart'
show CompilerOptions, CfeDiagnosticMessage;
import '../api_prototype/experimental_flags.dart'
show AllowedExperimentalFlags, ExperimentalFlag;
import '../api_prototype/terminal_color_support.dart'
show printDiagnosticMessage;
import '../base/common.dart';
import '../base/messages.dart' show FormattedMessage;
import '../kernel_generator_impl.dart' show InternalCompilerResult;
import 'compiler_common.dart' show compileScript, toTestUri;
import 'id_extractor.dart' show DataExtractor;
import 'kernel_id_testing.dart';
export '../base/compiler_context.dart' show CompilerContext;
export '../base/messages.dart' show FormattedMessage;
export '../kernel_generator_impl.dart' show InternalCompilerResult;
/// Test configuration used for testing CFE in its default state.
const CfeTestConfig defaultCfeConfig = const CfeTestConfig(cfeMarker, 'cfe');
class CfeTestConfig extends TestConfig {
final Map<ExperimentalFlag, bool> explicitExperimentalFlags;
final AllowedExperimentalFlags? allowedExperimentalFlags;
final Uri? librariesSpecificationUri;
final Uri? packageConfigUri;
// TODO(johnniwinther): Tailor support to redefine selected platform
// classes/members only.
final bool compileSdk;
final TestTargetFlags targetFlags;
const CfeTestConfig(
super.marker,
super.name, {
this.explicitExperimentalFlags = const {},
this.allowedExperimentalFlags,
this.librariesSpecificationUri,
this.packageConfigUri,
this.compileSdk = false,
this.targetFlags = const TestTargetFlags(),
});
/// Called before running test on [testData].
///
/// This allows tests to customize the [options] based on the [testData].
///
/// A custom object can be returned. This is passed to data computer.
dynamic customizeCompilerOptions(
CompilerOptions options,
TestData testData,
) => null;
/// Called after running test on [testData] with the resulting
/// [testResultData].
void onCompilationResult(
MarkerOptions markerOptions,
TestData testData,
CfeTestResultData testResultData,
) {}
}
abstract class CfeDataComputer<T>
extends
DataComputer<
T,
CfeTestConfig,
InternalCompilerResult,
CfeTestResultData
> {
const CfeDataComputer();
}
/// Auxiliary data from running a test.
class CfeTestResultData
extends TestResultData<CfeTestConfig, InternalCompilerResult> {
/// CustomData is passed from [CfeTestConfig.customizeCompilerOptions].
final dynamic customData;
CfeTestResultData(super.config, this.customData, super.compilerResult);
@override
Component get component => compilerResult.component!;
}
mixin CfeDataRegistryMixin<T> implements DataRegistry<T> {
InternalCompilerResult get compilerResult;
@override
void report(Uri uri, int offset, String message) {
printMessageInLocation(
compilerResult.component!.uriToSource,
uri,
offset,
message,
);
}
@override
void fail(String message) {
onFailure(message);
}
}
class CfeDataRegistry<T> with DataRegistry<T>, CfeDataRegistryMixin<T> {
@override
final InternalCompilerResult compilerResult;
@override
final Map<Id, ActualData<T>> actualMap;
CfeDataRegistry(this.compilerResult, this.actualMap);
}
abstract class CfeDataExtractor<T> extends DataExtractor<T>
with CfeDataRegistryMixin<T> {
@override
final InternalCompilerResult compilerResult;
CfeDataExtractor(this.compilerResult, Map<Id, ActualData<T>> actualMap)
: super(actualMap);
}
/// Create the testing URI used for [fileName] in annotated tests.
Uri createUriForFileName(String fileName) => toTestUri(fileName);
void onFailure(String message) => throw new StateError(message);
/// Creates a test runner for [dataComputer] on [testedConfigs].
RunTestFunction<T> runTestFor<T>(
CfeDataComputer<T> dataComputer,
List<CfeTestConfig> testedConfigs,
) {
retainDataForTesting = true;
return (
MarkerOptions markerOptions,
TestData testData, {
required bool testAfterFailures,
required bool verbose,
required bool succinct,
required bool printCode,
Map<String, List<String>>? skipMap,
required Uri nullUri,
}) {
return runTest(
markerOptions,
testData,
dataComputer,
testedConfigs,
testAfterFailures: testAfterFailures,
verbose: verbose,
succinct: succinct,
printCode: printCode,
onFailure: onFailure,
skipMap: skipMap,
nullUri: nullUri,
);
};
}
/// Runs [dataComputer] on [testData] for all [testedConfigs].
///
/// Returns `true` if an error was encountered.
Future<Map<String, TestResult<T>>> runTest<T>(
MarkerOptions markerOptions,
TestData testData,
CfeDataComputer<T> dataComputer,
List<CfeTestConfig> testedConfigs, {
required bool testAfterFailures,
required bool verbose,
required bool succinct,
required bool printCode,
bool forUserLibrariesOnly = true,
Iterable<Id> globalIds = const <Id>[],
required void onFailure(String message),
Map<String, List<String>>? skipMap,
required Uri nullUri,
}) async {
for (CfeTestConfig config in testedConfigs) {
if (!testData.expectedMaps.containsKey(config.marker)) {
throw new ArgumentError(
"Unexpected test marker '${config.marker}'. "
"Supported markers: ${testData.expectedMaps.keys}.",
);
}
}
Map<String, TestResult<T>> results = {};
for (CfeTestConfig config in testedConfigs) {
if (skipForConfig(testData.name, config.marker, skipMap)) {
continue;
}
results[config.marker] = await runTestForConfig(
markerOptions,
testData,
dataComputer,
config,
fatalErrors: !testAfterFailures,
onFailure: onFailure,
verbose: verbose,
succinct: succinct,
printCode: printCode,
nullUri: nullUri,
);
}
return results;
}
/// Runs [dataComputer] on [testData] for [config].
///
/// Returns `true` if an error was encountered.
Future<TestResult<T>> runTestForConfig<T>(
MarkerOptions markerOptions,
TestData testData,
CfeDataComputer<T> dataComputer,
CfeTestConfig config, {
required bool fatalErrors,
required bool verbose,
required bool succinct,
required bool printCode,
bool forUserLibrariesOnly = true,
Iterable<Id> globalIds = const <Id>[],
required void onFailure(String message),
required Uri nullUri,
}) async {
CompilerOptions options = new CompilerOptions();
List<FormattedMessage> errors = [];
options.onDiagnostic = (CfeDiagnosticMessage message) {
if (message is FormattedMessage && message.severity == CfeSeverity.error) {
errors.add(message);
}
if (!succinct) printDiagnosticMessage(message, print);
};
options.debugDump = printCode;
options.target = new TestTargetWrapper(
new NoneTarget(config.targetFlags),
config.targetFlags,
);
options.explicitExperimentalFlags.addAll(config.explicitExperimentalFlags);
options.allowedExperimentalFlagsForTesting = config.allowedExperimentalFlags;
if (config.librariesSpecificationUri != null) {
Set<Uri> testFiles = testData.memorySourceFiles.keys
.map(createUriForFileName)
.toSet();
if (testFiles.contains(config.librariesSpecificationUri)) {
options.librariesSpecificationUri = config.librariesSpecificationUri;
options.compileSdk = config.compileSdk;
}
}
options.packagesFileUri = config.packageConfigUri;
dynamic customData = config.customizeCompilerOptions(options, testData);
InternalCompilerResult compilerResult =
await compileScript(
testData.memorySourceFiles,
options: options,
retainDataForTesting: true,
requireMain: false,
)
as InternalCompilerResult;
CfeTestResultData testResultData = new CfeTestResultData(
config,
customData,
compilerResult,
);
config.onCompilationResult(markerOptions, testData, testResultData);
return processCompiledResult(
markerOptions,
testData,
dataComputer,
testResultData,
errors,
fatalErrors: fatalErrors,
verbose: verbose,
succinct: succinct,
onFailure: onFailure,
nullUri: nullUri,
);
}