// 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. // @dart = 2.9 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, DiagnosticMessage; import 'package:front_end/src/testing/compiler_common.dart' show compileScript; /// 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 spans; Test(this.source, this.spans); } Test generateTest() { final sb = new StringBuffer(); final spans = new Map(); // 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 { final Test test; /// Set of names of verified [Procedure]s. final Set verified = new Set(); /// When [AssertStatement] is reached it is checked against this /// span. ConditionSpan expectedSpan; VerifyingVisitor(this.test); @override visitProcedure(Procedure node) { expectedSpan = test.spans[node.name.text]; if (expectedSpan != null) { super.visitProcedure(node); verified.add(node.name.text); 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() ..onDiagnostic = (DiagnosticMessage message) { Expect.fail( "Unexpected message: ${message.plainTextFormatted.join('\n')}"); }; Component p = (await compileScript(test.source, options: options, fileName: 'synthetic-test.dart')) ?.component; Expect.isNotNull(p); VerifyingVisitor visitor = new VerifyingVisitor(test); p.mainMethod.enclosingLibrary.accept(visitor); Expect.setEquals(test.spans.keys, visitor.verified); }); }