blob: a2e735569872ae5472f2d7058ca632f5d788449e [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.md file.
library fasta.test.assert_locations_test;
import 'package:async_helper/async_helper.dart' show asyncTest;
import 'package:expect/expect.dart' show Expect;
import 'package:kernel/ast.dart'
show Component, RecursiveVisitor, Procedure, AssertStatement;
import "package:front_end/src/api_prototype/compiler_options.dart"
show CompilerOptions;
import 'package:front_end/src/testing/compiler_common.dart' show compileScript;
import 'package:front_end/src/fasta/fasta_codes.dart' show LocatedMessage;
import 'package:front_end/src/fasta/severity.dart' show Severity;
/// Span of the condition expression in the assert statement.
class ConditionSpan {
final startOffset;
final endOffset;
ConditionSpan(this.startOffset, this.endOffset);
}
/// Generated test with multiple functions each containing a single
/// assertion statement inside. [spans] specifies the mapping
/// between function name and [ConditionSpan] for an [AssertionStatement]
/// inside that function.
class Test {
final String source;
final Map<String, ConditionSpan> spans;
Test(this.source, this.spans);
}
Test generateTest() {
final sb = new StringBuffer();
final spans = new Map<String, ConditionSpan>();
// Create a test case for assert statement with the given [condition] and
// [message]. Argument list would contain a trailing comma if [trailingComma]
// is [true].
// [additionalOffset] specifies an offset in characters from the opening
// parenthesis of the assert statement to the first character of the
// condition.
void makeAssertWithMessage(String condition,
{String message, bool trailingComma: false, int additionalOffset: 0}) {
final name = 'testCase${spans.length}';
sb.writeln('void $name(x) {');
sb.write('assert(');
final startOffset = sb.length + additionalOffset;
sb.write(condition);
final endOffset = sb.length;
if (message != null) {
sb.write(', "${message}"');
}
if (trailingComma) {
sb.write(',');
}
sb.writeln(');');
sb.writeln('}');
spans[name] = new ConditionSpan(startOffset, endOffset);
}
// Create test cases for various variants of the assert statement with
// the given condition.
void makeAssert(String condition, {int additionalOffset: 0}) {
makeAssertWithMessage(condition, additionalOffset: additionalOffset);
makeAssertWithMessage(condition,
trailingComma: true, additionalOffset: additionalOffset);
makeAssertWithMessage(condition,
message: 'message message', additionalOffset: additionalOffset);
makeAssertWithMessage(condition,
message: 'message message',
trailingComma: true,
additionalOffset: additionalOffset);
}
// Create all test cases.
makeAssert('''
(
x != null
)''', additionalOffset: 3);
makeAssert('''(x != null)''');
makeAssert('''x''');
makeAssert('''((x))''');
makeAssert('''!x''');
makeAssert('''((!x))''');
makeAssert('''x.method("a", "b")''');
// Add a dummy main to avoid compilation errors.
sb.writeln('''
void main() {}
''');
return new Test(sb.toString(), spans);
}
/// Visitor that verifies that all [AssertStatement]s in the Kernel AST
/// have expected spans for their conditions.
class VerifyingVisitor extends RecursiveVisitor<Null> {
final Test test;
/// Set of names of verified [Procedure]s.
final Set<String> verified = new Set<String>();
/// When [AssertStatement] is reached it is checked against this
/// span.
ConditionSpan expectedSpan;
VerifyingVisitor(this.test);
@override
visitProcedure(Procedure node) {
expectedSpan = test.spans[node.name.name];
if (expectedSpan != null) {
super.visitProcedure(node);
verified.add(node.name.name);
expectedSpan = null;
}
}
@override
visitAssertStatement(AssertStatement node) {
Expect.equals(expectedSpan.startOffset, node.conditionStartOffset);
Expect.equals(expectedSpan.endOffset, node.conditionEndOffset);
}
}
void main() {
asyncTest(() async {
Test test = generateTest();
CompilerOptions options = new CompilerOptions()
..onProblem = (LocatedMessage message, Severity severity,
String formatted, int line, int column) {
Expect.fail("Unexpected error: $formatted");
}
..strongMode = true;
Component p = await compileScript(test.source,
options: options, fileName: 'synthetic-test.dart');
Expect.isNotNull(p);
VerifyingVisitor visitor = new VerifyingVisitor(test);
p.mainMethod.enclosingLibrary.accept(visitor);
Expect.setEquals(test.spans.keys, visitor.verified);
});
}