| // 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/ |
| |
| """); |
| } |
| } |
| } |