blob: ed5b3f54d2d75c565c361920b039b820d2ce2ed2 [file] [log] [blame]
// Copyright (c) 2018, 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:async';
import 'dart:io';
import 'package:front_end/src/api_unstable/vm.dart'
show
CompilerOptions,
DiagnosticMessage,
computePlatformBinariesLocation,
kernelForModule,
kernelForProgram,
parseExperimentalArguments,
parseExperimentalFlags;
import 'package:kernel/ast.dart';
import 'package:kernel/text/ast_to_text.dart' show Printer;
import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
import 'package:kernel/target/targets.dart';
import 'package:test/test.dart';
import 'package:vm/target/vm.dart' show VmTarget;
/// Environment define to update expectation files on failures.
const kUpdateExpectations = 'updateExpectations';
/// Environment define to dump actual results alongside expectations.
const kDumpActualResult = 'dump.actual.result';
class TestingVmTarget extends VmTarget {
TestingVmTarget(TargetFlags flags) : super(flags);
@override
bool enableSuperMixins = false;
}
Future<Component> compileTestCaseToKernelProgram(Uri sourceUri,
{Target? target,
bool enableSuperMixins = false,
List<String>? experimentalFlags,
Map<String, String>? environmentDefines,
Uri? packagesFileUri,
List<Uri>? linkedDependencies}) async {
Directory? tempDirectory;
try {
final platformKernel =
computePlatformBinariesLocation().resolve('vm_platform_strong.dill');
target ??= new TestingVmTarget(new TargetFlags())
..enableSuperMixins = enableSuperMixins;
environmentDefines ??= <String, String>{};
final options = new CompilerOptions()
..target = target
..additionalDills = <Uri>[platformKernel]
..environmentDefines = environmentDefines
..packagesFileUri = packagesFileUri
..explicitExperimentalFlags =
parseExperimentalFlags(parseExperimentalArguments(experimentalFlags),
onError: (String message) {
throw message;
})
..onDiagnostic = (DiagnosticMessage message) {
fail("Compilation error: ${message.plainTextFormatted.join('\n')}");
};
if (linkedDependencies != null) {
final Component component =
(await kernelForModule(linkedDependencies, options)).component!;
tempDirectory = await Directory.systemTemp.createTemp();
Uri uri = tempDirectory.uri.resolve("generated.dill");
File generated = new File.fromUri(uri);
IOSink sink = generated.openWrite();
try {
new BinaryPrinter(sink).writeComponentFile(component);
} finally {
await sink.close();
}
options..additionalDills = <Uri>[platformKernel, uri];
}
final Component component =
(await kernelForProgram(sourceUri, options))!.component!;
// Make sure the library name is the same and does not depend on the order
// of test cases.
component.mainMethod!.enclosingLibrary.name = '#lib';
return component;
} finally {
await tempDirectory?.delete(recursive: true);
}
}
String kernelLibraryToString(Library library) {
final StringBuffer buffer = new StringBuffer();
final printer = new Printer(buffer, showMetadata: true);
printer.writeLibraryFile(library);
printer.writeConstantTable(library.enclosingComponent!);
return buffer
.toString()
.replaceAll(library.importUri.toString(), library.name!);
}
String kernelComponentToString(Component component) {
final StringBuffer buffer = new StringBuffer();
new Printer(buffer, showMetadata: true).writeComponentFile(component);
final mainLibrary = component.mainMethod!.enclosingLibrary;
return buffer
.toString()
.replaceAll(mainLibrary.importUri.toString(), mainLibrary.name!);
}
class DevNullSink<T> extends Sink<T> {
@override
void add(T data) {}
@override
void close() {}
}
void ensureKernelCanBeSerializedToBinary(Component component) {
new BinaryPrinter(new DevNullSink<List<int>>()).writeComponentFile(component);
}
class Difference {
final int line;
final String actual;
final String expected;
Difference(this.line, this.actual, this.expected);
}
Difference findFirstDifference(String actual, String expected) {
final actualLines = actual.split('\n');
final expectedLines = expected.split('\n');
int i = 0;
for (; i < actualLines.length && i < expectedLines.length; ++i) {
if (actualLines[i] != expectedLines[i]) {
return new Difference(i + 1, actualLines[i], expectedLines[i]);
}
}
return new Difference(
i + 1,
i < actualLines.length ? actualLines[i] : '<END>',
i < expectedLines.length ? expectedLines[i] : '<END>');
}
void compareResultWithExpectationsFile(Uri source, String actual) {
final expectFile = new File(source.toFilePath() + '.expect');
final expected = expectFile.existsSync() ? expectFile.readAsStringSync() : '';
if (actual != expected) {
if (bool.fromEnvironment(kUpdateExpectations)) {
expectFile.writeAsStringSync(actual);
print(" Updated $expectFile");
} else {
if (bool.fromEnvironment(kDumpActualResult)) {
new File(source.toFilePath() + '.actual').writeAsStringSync(actual);
}
Difference diff = findFirstDifference(actual, expected);
fail("""
Result is different for the test case $source
The first difference is at line ${diff.line}.
Actual: ${diff.actual}
Expected: ${diff.expected}
This failure can be caused by changes in the front-end if it starts generating
different kernel AST for the same Dart programs.
In order to re-generate expectations run tests with -D$kUpdateExpectations=true VM option:
tools/test.py -m release --vm-options -D$kUpdateExpectations=true pkg/vm/
In order to dump actual results into .actual files run tests with -D$kDumpActualResult=true VM option:
tools/test.py -m release --vm-options -D$kDumpActualResult=true pkg/vm/
""");
}
}
}