Files
sdk/pkg/front_end/test/fasta/incremental_test.dart
T
Sigmund Cherem 9b44fc39c8 Deprecate chaseDependencies option.
The option is not used by any client at this time and removing it simplifies the
next stack of changes I'll be making on how we compose file systems.

I also don't believe we will necessarily add this back as an option, but instead
it might be that clients requriing hermeticity will provide a custom file system
that helps with that.

Change-Id: I401efb042920d234382b6c041b13d40ffae5c908
Reviewed-on: https://dart-review.googlesource.com/56462
Commit-Queue: Peter von der Ahé <ahe@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
2018-05-24 04:57:01 +00:00

240 lines
7.5 KiB
Dart

// Copyright (c) 2017, 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.test.incremental_test;
import "dart:async" show Future;
import "dart:convert" show JsonEncoder;
import "dart:io" show File;
import "package:kernel/ast.dart" show Component;
import "package:testing/testing.dart"
show Chain, ChainContext, Result, Step, TestDescription, runMe;
import "package:yaml/yaml.dart" show YamlMap, loadYamlNode;
import "package:front_end/src/api_prototype/front_end.dart"
show CompilationMessage, CompilerOptions, Severity;
import "package:front_end/src/api_prototype/incremental_kernel_generator.dart"
show IncrementalKernelGenerator;
import "package:front_end/src/api_prototype/memory_file_system.dart"
show MemoryFileSystem;
import 'package:front_end/src/compute_platform_binaries_location.dart'
show computePlatformBinariesLocation;
import 'package:front_end/src/external_state_snapshot.dart'
show ExternalStateSnapshot;
import 'package:front_end/src/base/processed_options.dart'
show ProcessedOptions;
import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
import 'package:front_end/src/fasta/incremental_compiler.dart'
show IncrementalCompiler;
import "incremental_expectations.dart"
show IncrementalExpectation, extractJsonExpectations;
import "incremental_source_files.dart" show expandDiff, expandUpdates;
const JsonEncoder json = const JsonEncoder.withIndent(" ");
final Uri base = Uri.parse("org-dartlang-test:///");
final Uri entryPoint = base.resolve("main.dart");
class Context extends ChainContext {
final CompilerContext compilerContext;
final ExternalStateSnapshot snapshot;
final List<CompilationMessage> errors;
final List<Step> steps = const <Step>[
const ReadTest(),
const RunCompilations(),
];
final IncrementalKernelGenerator compiler;
Context(this.compilerContext, this.snapshot, this.errors)
: compiler = new IncrementalCompiler(compilerContext);
ProcessedOptions get options => compilerContext.options;
MemoryFileSystem get fileSystem => options.fileSystem;
T runInContext<T>(T action(CompilerContext c)) {
return compilerContext.runInContext<T>(action);
}
void reset() {
errors.clear();
snapshot.restore();
}
List<CompilationMessage> takeErrors() {
List<CompilationMessage> result = new List<CompilationMessage>.from(errors);
errors.clear();
return result;
}
}
class ReadTest extends Step<TestDescription, TestCase, Context> {
const ReadTest();
String get name => "read test";
Future<Result<TestCase>> run(
TestDescription description, Context context) async {
context.reset();
Uri uri = description.uri;
String contents = await new File.fromUri(uri).readAsString();
Map<String, List<String>> sources = <String, List<String>>{};
List<IncrementalExpectation> expectations;
bool firstPatch = true;
YamlMap map = loadYamlNode(contents, sourceUrl: uri);
map.forEach((_fileName, _contents) {
String fileName = _fileName; // Strong mode hurray!
String contents = _contents; // Strong mode hurray!
if (fileName.endsWith(".patch")) {
fileName = fileName.substring(0, fileName.length - ".patch".length);
if (firstPatch) {
expectations = extractJsonExpectations(contents);
}
sources[fileName] = expandUpdates(expandDiff(contents));
firstPatch = false;
} else {
sources[fileName] = <String>[contents];
}
});
final TestCase test = new TestCase(description, sources, expectations);
return test.validate(this);
}
}
class RunCompilations extends Step<TestCase, TestCase, Context> {
const RunCompilations();
String get name => "run compilations";
Future<Result<TestCase>> run(TestCase test, Context context) async {
for (int edits = 0;; edits++) {
bool foundSources = false;
test.sources.forEach((String name, List<String> sources) {
if (edits < sources.length) {
String source = sources[edits];
Uri uri = base.resolve(name);
context.fileSystem.entityForUri(uri).writeAsStringSync(source);
foundSources = true;
context.compiler.invalidate(uri);
if (edits == 0) {
print("==> $uri <==");
} else {
print("==> $uri (edit #$edits) <==");
}
print(source.trimRight());
}
});
if (!foundSources) {
return edits == 0 ? fail(test, "No sources found") : pass(test);
}
var compiler = context.compiler;
Component component = await compiler.computeDelta(entryPoint: entryPoint);
List<CompilationMessage> errors = context.takeErrors();
if (test.expectations[edits].hasCompileTimeError) {
if (errors.isEmpty) {
return fail(test, "Compile-time error expected, but none reported");
}
} else if (errors.isNotEmpty) {
return fail(
test, "Unexpected compile-time errors:\n ${errors.join('\n ')}");
} else if (component.libraries.length < 1) {
return fail(test, "The compiler detected no changes");
}
}
}
}
class TestCase {
final TestDescription description;
final Map<String, List<String>> sources;
final List<IncrementalExpectation> expectations;
TestCase(this.description, this.sources, this.expectations);
String toString() {
return "TestCase(${json.convert(sources)}, ${json.convert(expectations)})";
}
Result<TestCase> validate(Step<dynamic, TestCase, ChainContext> step) {
print(this);
if (sources == null) {
return step.fail(this, "No sources.");
}
if (expectations == null || expectations.isEmpty) {
return step.fail(this, "No expectations.");
}
for (String name in sources.keys) {
List<String> versions = sources[name];
if (versions.length != 1 && versions.length != expectations.length) {
return step.fail(
this,
"Found ${versions.length} versions of $name,"
" but expected 1 or ${expectations.length}.");
}
}
return step.pass(this);
}
}
Future<Context> createContext(
Chain suite, Map<String, String> environment) async {
/// The custom URI used to locate the dill file in the MemoryFileSystem.
final Uri sdkSummary = base.resolve("vm_platform.dill");
/// The actual location of the dill file.
final Uri sdkSummaryFile =
computePlatformBinariesLocation().resolve("vm_platform.dill");
final MemoryFileSystem fs = new MemoryFileSystem(base);
fs
.entityForUri(sdkSummary)
.writeAsBytesSync(await new File.fromUri(sdkSummaryFile).readAsBytes());
final List<CompilationMessage> errors = <CompilationMessage>[];
final CompilerOptions optionBuilder = new CompilerOptions()
..strongMode = false
..reportMessages = true
..verbose = true
..fileSystem = fs
..sdkSummary = sdkSummary
..onError = (CompilationMessage message) {
if (message.severity != Severity.nit &&
message.severity != Severity.warning) {
errors.add(message);
}
};
final ProcessedOptions options =
new ProcessedOptions(optionBuilder, [entryPoint]);
final ExternalStateSnapshot snapshot =
new ExternalStateSnapshot(await options.loadSdkSummary(null));
return new Context(new CompilerContext(options), snapshot, errors);
}
main([List<String> arguments = const []]) =>
runMe(arguments, createContext, "../../testing.json");