blob: 0230a6ece934469634258566b43ac93358be8cb7 [file] [log] [blame]
// Copyright (c) 2016, 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.md file.
library fasta.testing.suite;
import 'dart:async' show Future;
import 'dart:io' show File;
import 'dart:convert' show JSON;
import 'package:front_end/physical_file_system.dart';
import 'package:front_end/src/fasta/testing/validating_instrumentation.dart'
show ValidatingInstrumentation;
import 'package:front_end/src/fasta/testing/patched_sdk_location.dart';
import 'package:kernel/ast.dart' show Program;
import 'package:testing/testing.dart'
show
Chain,
ChainContext,
ExpectationSet,
Result,
Step,
TestDescription,
StdioProcess;
import 'package:front_end/src/fasta/errors.dart' show InputError;
import 'package:front_end/src/fasta/testing/kernel_chain.dart'
show MatchExpectation, Print, Verify, WriteDill;
import 'package:front_end/src/fasta/ticker.dart' show Ticker;
import 'package:front_end/src/fasta/translate_uri.dart' show TranslateUri;
import 'package:analyzer/src/fasta/analyzer_target.dart' show AnalyzerTarget;
import 'package:front_end/src/fasta/kernel/kernel_target.dart'
show KernelTarget;
import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget;
import 'package:kernel/kernel.dart' show loadProgramFromBytes;
export 'package:testing/testing.dart' show Chain, runMe;
const String STRONG_MODE = " strong mode ";
const String ENABLE_FULL_COMPILE = " full compile ";
const String AST_KIND_INDEX = " AST kind index ";
const String EXPECTATIONS = '''
[
{
"name": "VerificationError",
"group": "Fail"
}
]
''';
String shortenAstKindName(AstKind astKind, bool strongMode) {
switch (astKind) {
case AstKind.Analyzer:
return strongMode ? "dartk-strong" : "dartk";
case AstKind.Kernel:
return strongMode ? "strong" : "direct";
}
throw "Unknown AST kind: $astKind";
}
enum AstKind {
Analyzer,
Kernel,
}
class FastaContext extends ChainContext {
final TranslateUri uriTranslator;
final List<Step> steps;
final Uri vm;
Uri sdk;
Uri platformUri;
Uri outlineUri;
List<int> outlineBytes;
final ExpectationSet expectationSet =
new ExpectationSet.fromJsonList(JSON.decode(EXPECTATIONS));
FastaContext(
this.vm,
bool strongMode,
bool updateExpectations,
bool updateComments,
bool skipVm,
this.uriTranslator,
bool fullCompile,
AstKind astKind)
: steps = <Step>[
new Outline(fullCompile, astKind, strongMode,
updateComments: updateComments),
const Print(),
new Verify(fullCompile),
new MatchExpectation(
fullCompile
? ".${shortenAstKindName(astKind, strongMode)}.expect"
: ".outline.expect",
updateExpectations: updateExpectations)
] {
if (fullCompile && !skipVm) {
steps.add(const WriteDill());
steps.add(const Run());
}
}
Future ensurePlatformUris() async {
if (sdk == null) {
sdk = await computePatchedSdk();
platformUri = sdk.resolve('platform.dill');
outlineUri = sdk.resolve('outline.dill');
}
}
Future<Program> loadPlatformOutline() async {
if (outlineBytes == null) {
await ensurePlatformUris();
outlineBytes = new File.fromUri(outlineUri).readAsBytesSync();
}
// Note: we rebuild the platform outline on every test because the compiler
// currently mutates the in-memory representation of the program without
// cloning it.
// TODO(sigmund): investigate alternatives to this approach.
return loadProgramFromBytes(outlineBytes);
}
static Future<FastaContext> create(
Chain suite, Map<String, String> environment) async {
Uri sdk = await computePatchedSdk();
Uri vm = computeDartVm(sdk);
Uri packages = Uri.base.resolve(".packages");
TranslateUri uriTranslator =
await TranslateUri.parse(PhysicalFileSystem.instance, packages);
bool strongMode = environment.containsKey(STRONG_MODE);
bool updateExpectations = environment["updateExpectations"] == "true";
bool updateComments = environment["updateComments"] == "true";
bool skipVm = environment["skipVm"] == "true";
String astKindString = environment[AST_KIND_INDEX];
AstKind astKind =
astKindString == null ? null : AstKind.values[int.parse(astKindString)];
return new FastaContext(
vm,
strongMode,
updateExpectations,
updateComments,
skipVm,
uriTranslator,
environment.containsKey(ENABLE_FULL_COMPILE),
astKind);
}
}
class Run extends Step<Uri, int, FastaContext> {
const Run();
String get name => "run";
bool get isAsync => true;
bool get isRuntime => true;
Future<Result<int>> run(Uri uri, FastaContext context) async {
File generated = new File.fromUri(uri);
StdioProcess process;
try {
await context.ensurePlatformUris();
var platformDill = context.platformUri.toFilePath();
var args = ['--platform=$platformDill', generated.path, "Hello, World!"];
process = await StdioProcess.run(context.vm.toFilePath(), args);
print(process.output);
} finally {
generated.parent.delete(recursive: true);
}
return process.toResult();
}
}
class Outline extends Step<TestDescription, Program, FastaContext> {
final bool fullCompile;
final AstKind astKind;
final bool strongMode;
const Outline(this.fullCompile, this.astKind, this.strongMode,
{this.updateComments: false});
final bool updateComments;
String get name {
return fullCompile ? "${astKind} compile" : "outline";
}
bool get isCompiler => fullCompile;
Future<Result<Program>> run(
TestDescription description, FastaContext context) async {
Program platformOutline = await context.loadPlatformOutline();
Ticker ticker = new Ticker();
DillTarget dillTarget = new DillTarget(ticker, context.uriTranslator, "vm");
platformOutline.unbindCanonicalNames();
dillTarget.loader.appendLibraries(platformOutline);
KernelTarget sourceTarget = astKind == AstKind.Analyzer
? new AnalyzerTarget(dillTarget, context.uriTranslator, strongMode)
: new KernelTarget(PhysicalFileSystem.instance, dillTarget,
context.uriTranslator, strongMode);
Program p;
try {
sourceTarget.read(description.uri);
await dillTarget.buildOutlines();
ValidatingInstrumentation instrumentation;
if (strongMode) {
instrumentation = new ValidatingInstrumentation();
await instrumentation.loadExpectations(description.uri);
sourceTarget.loader.instrumentation = instrumentation;
}
p = await sourceTarget.buildOutlines();
if (fullCompile) {
p = await sourceTarget.buildProgram(trimDependencies: true);
instrumentation?.finish();
if (instrumentation != null && instrumentation.hasProblems) {
if (updateComments) {
await instrumentation.fixSource(description.uri);
} else {
return fail(null, instrumentation.problemsAsString);
}
}
}
} on InputError catch (e, s) {
return fail(null, e.error, s);
}
return pass(p);
}
}