blob: f5477bac1d4cc7b7c78b76b4e8ef760d0bae8a26 [file] [log] [blame]
// Copyright (c) 2015, 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 file.
// Note: This test relies on LF line endings in the source file.
// Test that JS printer callbacks occur when expected.
library js_ast.printer.callback_test;
import 'package:js_ast/js_ast.dart';
import 'package:test/test.dart';
enum TestMode {
INPUT,
NONE,
ENTER,
DELIMITER,
EXIT,
}
class TestCase {
final Map<TestMode, String> data;
/// Map from template names to the inserted values.
final Map<String, String> environment;
const TestCase(this.data, [this.environment = const {}]);
}
const List<TestCase> DATA = <TestCase>[
TestCase({
TestMode.NONE: """
function(a, b) {
return null;
}""",
TestMode.ENTER: """
@0function(@1a, @2b) @3{
@4return @5null;
}""",
TestMode.DELIMITER: """
function(a, b) {
return null@4;
@0}""",
TestMode.EXIT: """
function(a@1, b@2) {
return null@5;
@4}@3@0"""
}),
TestCase({
TestMode.NONE: """
function() {
if (true) {
foo1();
foo2();
} else if (false) {
bar1();
bar2();
}
while (false) {
baz3();
baz4();
}
}""",
TestMode.ENTER: """
@0function() @1{
@2if (@3true) @4{
@5@6@7foo1();
@8@9@10foo2();
} else @11if (@12false) @13{
@14@15@16bar1();
@17@18@19bar2();
}
@20while (@21false) @22{
@23@24@25baz3();
@26@27@28baz4();
}
}""",
TestMode.DELIMITER: """
function() {
if (true) {
foo1();
foo2();
} else if (false) {
bar1();
bar2();
}
while (false) {
baz3();
baz4();
}
@0}""",
TestMode.EXIT: """
function() {
if (true@3) {
foo1@7()@6;
@5 foo2@10()@9;
@8 }@4 else if (false@12) {
bar1@16()@15;
@14 bar2@19()@18;
@17 }@13
@11@2 while (false@21) {
baz3@25()@24;
@23 baz4@28()@27;
@26 }@22
@20}@1@0""",
}),
TestCase({
TestMode.NONE: """
function() {
function foo() {
}
}""",
TestMode.ENTER: """
@0function() @1{
@2@3function @4foo() @5{
}
}""",
TestMode.DELIMITER: """
function() {
function foo() {
@3}
@0}""",
TestMode.EXIT: """
function() {
function foo@4() {
}@5@3
@2}@1@0"""
}),
TestCase({
TestMode.INPUT: """
function() {
a['b'];
[1,, 2];
}""",
TestMode.NONE: """
function() {
a.b;
[1,, 2];
}""",
TestMode.ENTER: """
@0function() @1{
@2@3@4a.@5b;
@6@7[@81,@9, @102];
}""",
TestMode.DELIMITER: """
function() {
a.b;
[1,, 2];
@0}""",
TestMode.EXIT: """
function() {
a@4.b@5@3;
@2 [1@8,,@9 2@10]@7;
@6}@1@0""",
}),
TestCase({
TestMode.INPUT: "a.#nameTemplate = #nameTemplate",
TestMode.NONE: "a.nameValue = nameValue",
TestMode.ENTER: "@0@1@2a.@3nameValue = @3nameValue",
TestMode.DELIMITER: "a.nameValue = nameValue",
TestMode.EXIT: "a@2.nameValue@3@1 = nameValue@3@0",
}, {
'nameTemplate': 'nameValue'
}),
];
class FixedName extends Name {
@override
final String name;
@override
String get key => name;
FixedName(this.name);
}
void check(TestCase testCase) {
Map<TestMode, String> map = testCase.data;
String code = map[TestMode.INPUT];
if (code == null) {
// Input is the same as output.
code = map[TestMode.NONE];
}
JavaScriptPrintingOptions options = JavaScriptPrintingOptions();
Map arguments = {};
testCase.environment.forEach((String name, String value) {
arguments[name] = FixedName(value);
});
Node node = js.parseForeignJS(code).instantiate(arguments);
map.forEach((TestMode mode, String expectedOutput) {
if (mode == TestMode.INPUT) return;
Context context = Context(mode);
Printer(options, context).visit(node);
// TODO(johnniwinther): Remove `replaceAll(...)` when dart2js behaves as the
// VM on newline in multiline strings.
expect(context.getText(), equals(expectedOutput.replaceAll('\r\n', '\n')),
reason: "Unexpected output for $code in $mode");
});
}
class Context extends SimpleJavaScriptPrintingContext {
final TestMode mode;
final Map<Node, int> idMap = {};
final Map<int, List<String>> tagMap = {};
Context(this.mode);
int id(Node node) => idMap.putIfAbsent(node, () => idMap.length);
String tag(int value) => '@$value';
@override
void enterNode(Node node, int startPosition) {
int value = id(node);
if (mode == TestMode.ENTER) {
tagMap.putIfAbsent(startPosition, () => []).add(tag(value));
}
}
@override
void exitNode(
Node node, int startPosition, int endPosition, int delimiterPosition) {
int value = id(node);
if (mode == TestMode.DELIMITER && delimiterPosition != null) {
tagMap.putIfAbsent(delimiterPosition, () => []).add(tag(value));
} else if (mode == TestMode.EXIT) {
tagMap.putIfAbsent(endPosition, () => []).add(tag(value));
}
}
@override
String getText() {
String text = super.getText();
int offset = 0;
StringBuffer sb = StringBuffer();
for (int position in tagMap.keys.toList()..sort()) {
if (offset < position) {
sb.write(text.substring(offset, position));
}
tagMap[position].forEach((String tag) => sb.write(tag));
offset = position;
}
if (offset < text.length) {
sb.write(text.substring(offset));
}
return sb.toString();
}
}
void main() {
test('printer callback test', () {
DATA.forEach(check);
});
}