Prepare for matching expectations
Change-Id: I0c6dd4c64e20883b79b35d87b899c451f6b11149
Reviewed-on: https://dart-review.googlesource.com/c/84024
Commit-Queue: Peter von der Ahé <ahe@google.com>
Commit-Queue: Kevin Millikin <kmillikin@google.com>
Auto-Submit: Peter von der Ahé <ahe@google.com>
Reviewed-by: Kevin Millikin <kmillikin@google.com>
diff --git a/pkg/front_end/test/fasta/type_promotion_look_ahead_test.dart b/pkg/front_end/test/fasta/type_promotion_look_ahead_test.dart
index 2173108..0e0e024 100644
--- a/pkg/front_end/test/fasta/type_promotion_look_ahead_test.dart
+++ b/pkg/front_end/test/fasta/type_promotion_look_ahead_test.dart
@@ -2,6 +2,10 @@
// 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:convert' show jsonDecode;
+
+import 'dart:io' show File, IOSink;
+
import 'package:front_end/src/base/processed_options.dart'
show ProcessedOptions;
@@ -24,6 +28,9 @@
TypePromotionState,
UnspecifiedDeclaration;
+import 'package:front_end/src/fasta/testing/kernel_chain.dart'
+ show openWrite, runDiff;
+
import 'package:front_end/src/fasta/testing/scanner_chain.dart'
show Read, Scan, ScannedFile;
@@ -31,6 +38,19 @@
import 'package:testing/testing.dart';
+const String EXPECTATIONS = '''
+[
+ {
+ "name": "ExpectationFileMismatch",
+ "group": "Fail"
+ },
+ {
+ "name": "ExpectationFileMissing",
+ "group": "Fail"
+ }
+]
+''';
+
Future<ChainContext> createContext(
Chain suite, Map<String, String> environment) async {
CompilerContext context =
@@ -40,45 +60,68 @@
new Future<CompilerContext>.value(context),
errorOnMissingInput: false);
context.disableColors();
- return new TypePromotionLookAheadContext(context);
+ return new TypePromotionLookAheadContext(
+ context, environment["updateExpectations"] == "true");
}
class TypePromotionLookAheadContext extends ChainContext {
final CompilerContext context;
+
final List<Step> steps = const <Step>[
const Read(),
const Scan(),
- const TypePromotionLookAheadStep()
+ const TypePromotionLookAheadStep(),
+ const CheckTypePromotionResult(),
];
- TypePromotionLookAheadContext(this.context);
+ final bool updateExpectations;
+
+ final ExpectationSet expectationSet =
+ new ExpectationSet.fromJsonList(jsonDecode(EXPECTATIONS));
+
+ TypePromotionLookAheadContext(this.context, this.updateExpectations);
+
+ Expectation get expectationFileMismatch =>
+ expectationSet["ExpectationFileMismatch"];
+
+ Expectation get expectationFileMissing =>
+ expectationSet["ExpectationFileMissing"];
}
-class TypePromotionLookAheadStep
- extends Step<ScannedFile, Null, TypePromotionLookAheadContext> {
+class TypePromotionLookAheadStep extends Step<ScannedFile, TypePromotionResult,
+ TypePromotionLookAheadContext> {
const TypePromotionLookAheadStep();
String get name => "Type Promotion Look Ahead";
- Future<Result<Null>> run(
+ Future<Result<TypePromotionResult>> run(
ScannedFile file, TypePromotionLookAheadContext context) async {
return context.context
- .runInContext<Result<Null>>((CompilerContext c) async {
- c.uriToSource[file.file.uri] =
- new Source(file.result.lineStarts, file.file.bytes);
- Parser parser = new Parser(new TestListener(file.file.uri));
+ .runInContext<Result<TypePromotionResult>>((CompilerContext c) async {
+ Uri uri = file.file.uri;
+ c.uriToSource[uri] = new Source(file.result.lineStarts, file.file.bytes);
+ StringBuffer buffer = new StringBuffer();
+ Parser parser = new Parser(new TestListener(uri, buffer));
try {
parser.parseUnit(file.result.tokens);
} finally {
- c.uriToSource.remove(file.file.uri);
+ c.uriToSource.remove(uri);
}
- return pass(null);
+ return pass(new TypePromotionResult(uri, "$buffer"));
});
}
}
class TestState extends TypePromotionState {
- TestState(Uri uri) : super(uri);
+ final StringBuffer buffer;
+
+ TestState(Uri uri, this.buffer) : super(uri);
+
+ void note(String message, Token token) {
+ buffer.writeln(CompilerContext.current.format(
+ debugMessage(message, uri, token.charOffset, token.lexeme.length),
+ Severity.context));
+ }
@override
void checkEmpty(Token token) {
@@ -103,13 +146,16 @@
@override
void registerWrite(UnspecifiedDeclaration declaration, Token token) {
- trace("Write to ${declaration.name}", token);
+ // TODO(ahe): Call `note` instead to create expectations.
+ trace("Write to ${declaration.name}@${declaration.charOffset}", token);
}
@override
void registerPromotionCandidate(
UnspecifiedDeclaration declaration, Token token) {
- trace("Possible promotion of ${declaration.name}", token);
+ // TODO(ahe): Call `note` instead to create expectations.
+ trace("Possible promotion of ${declaration.name}@${declaration.charOffset}",
+ token);
}
@override
@@ -142,7 +188,8 @@
}
class TestListener extends TypePromotionLookAheadListener {
- TestListener(Uri uri) : super(new TestState(uri));
+ TestListener(Uri uri, StringBuffer buffer)
+ : super(new TestState(uri, buffer));
@override
void debugEvent(String name, Token token) {
@@ -168,5 +215,65 @@
String toString() => "<<$name@$charOffset>>";
}
+class TypePromotionResult {
+ final Uri uri;
+
+ final String trace;
+
+ const TypePromotionResult(this.uri, this.trace);
+}
+
+class CheckTypePromotionResult
+ extends Step<TypePromotionResult, Null, TypePromotionLookAheadContext> {
+ const CheckTypePromotionResult();
+
+ String get name => "Check Type Promotion Result";
+
+ Future<Result<Null>> run(
+ TypePromotionResult result, TypePromotionLookAheadContext context) async {
+ Uri uri = result.uri;
+ String actual = result.trace.trim();
+ if (actual.isNotEmpty) {
+ actual += "\n";
+ }
+ File expectedFile = new File("${uri.toFilePath()}.type_promotion.expect");
+ if (await expectedFile.exists()) {
+ String expected = await expectedFile.readAsString();
+ if (expected != actual) {
+ if (context.updateExpectations) {
+ return updateExpectationFile(expectedFile.uri, actual);
+ }
+ String diff = await runDiff(expectedFile.uri, actual);
+ return new Result<Null>(null, context.expectationFileMismatch,
+ "$uri doesn't match ${expectedFile.uri}\n$diff", null);
+ }
+ return pass(null);
+ } else {
+ if (actual.isEmpty) return pass(null);
+ if (context.updateExpectations) {
+ return updateExpectationFile(expectedFile.uri, actual);
+ }
+ return new Result<Null>(
+ null,
+ context.expectationFileMissing,
+ """
+Please create file ${expectedFile.path} with this content:
+$actual""",
+ null);
+ }
+ }
+
+ Future<Result<Null>> updateExpectationFile(Uri uri, String actual) async {
+ if (actual.isEmpty) {
+ await new File.fromUri(uri).delete();
+ } else {
+ await openWrite(uri, (IOSink sink) {
+ sink.write(actual);
+ });
+ }
+ return pass(null);
+ }
+}
+
main([List<String> arguments = const []]) =>
runMe(arguments, createContext, "../../testing.json");