blob: 6bde11f98e5a5338d8feac0673ba74a84928140a [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.js;
import com.google.gwt.core.ext.soyc.Range;
import com.google.gwt.dev.jjs.JsSourceMap;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import junit.framework.TestCase;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.List;
/**
* Verifies that the generator returns sourcemaps whose keys are the correct ranges of
* JavaScript. (Doesn't check that they point to the right place.)
*/
public class JsReportGenerationVisitorTest extends TestCase {
boolean compact = false;
// If true, list all the JavaScript ranges where a Java method might be inlined.
boolean includeInlinedRanges = false;
JsProgram program;
public void testEmpty() throws Exception {
program = parseJs("");
checkMappings();
}
public void testAssignmentDraft() throws Exception {
program = parseJs("x = 1");
checkMappings(
"x = 1;\n"
);
}
public void testAssignmentCompact() throws Exception {
compact = true;
program = parseJs("x = 1");
checkMappings(
"x=1;"
);
}
public void testAssignmentCompactInlined() throws Exception {
compact = true;
includeInlinedRanges = true;
program = parseJs("x = 1");
checkMappings(
"x=1;",
"x",
"1"
);
}
public void testTwoStatementsDraft() throws Exception {
program = parseJs("x = 1; y = 2");
checkMappings(
"x = 1;\ny = 2;\n",
"x = 1;\n",
"y = 2;\n"
);
}
public void testTwoStatementsCompactInlined() throws Exception {
compact = true;
includeInlinedRanges = true;
program = parseJs("x = 1; y = 2");
checkMappings(
"x=1;y=2;",
"x=1;",
"x",
"1",
"y=2;",
"y",
"2"
);
}
public void testIfStatementDraft() throws Exception {
program = parseJs("if(true) { x=1 } else { y=2 }");
checkMappings(
"if (true) {\n x = 1;\n}\n else {\n y = 2;\n}\n",
" x = 1;\n",
" y = 2;\n"
);
}
public void testIfStatementCompactInlined() throws Exception {
includeInlinedRanges = true;
compact = true;
program = parseJs("if(true) { x=1 } else { y=2 }");
checkMappings(
"if(true){x=1}else{y=2}",
"x=1",
"x",
"1",
"y=2",
"y",
"2"
);
}
public void testFunctionDraft() throws Exception {
program = parseJs("function f() { return 42; }");
checkMappings(
"function f(){\n return 42;\n}\n\n",
"function f(){\n return 42;\n}\n",
" return 42;\n"
);
}
public void testFunctionCompactInlined() throws Exception {
compact = true;
includeInlinedRanges = true;
program = parseJs("function f() { return 42; }");
checkMappings("function f(){return 42}\n",
"function f(){return 42}",
"return 42",
"42"
);
}
public void testTryStatementDraft() throws Exception {
program = parseJs("try { 123 } catch (e) { 456 } finally { 789 }");
checkMappings(
"try {\n 123;\n}\n catch (e) {\n 456;\n}\n finally {\n 789;\n}\n",
" 123;\n",
" 456;\n",
" 789;\n"
);
}
public void testTryStatementCompactInlined() throws Exception {
compact = true;
includeInlinedRanges = true;
program = parseJs("try{ 123 } catch (e) { 456 } finally { 789 }");
checkMappings(
"try{123}catch(e){456}finally{789}",
"123",
"456",
"789"
);
}
public void testDoWhileDraft() throws Exception {
program = parseJs("do { something() } while (x>1)");
checkMappings(
"do {\n something();\n}\n while (x > 1);\n",
" something();\n",
"x > 1"
);
}
public void testForStatementDraft() throws Exception {
program = parseJs("for (var i = 0; i < 10; i++) { something() }");
checkMappings(
"for (var i = 0; i < 10; i++) {\n something();\n}\n",
"var i = 0;", // a separate range because it's a statement TODO: remove?
" something();\n"
);
}
public void testForInStatementDraft() throws Exception {
program = parseJs("for (var x in someIterable) { something() }");
checkMappings(
"for (var x in someIterable) {\n something();\n}\n",
" something();\n"
);
}
public void testSwitchStatementDraft() throws Exception {
program = parseJs("switch (abc) { case c1: s1; break; case c2: s2; default: s3 }");
checkMappings(
"switch (abc) {\n case c1:\n s1;\n break;\n case c2:\n s2;\n default:s3;\n}\n",
" s1;",
" break;",
" s2;",
"s3;"
);
}
public void testSwitchStatementCompactInlined() throws Exception {
compact = true;
includeInlinedRanges = true;
program = parseJs("switch (abc) { case c1: s1; break; case c2: s2; default: s3 }");
checkMappings(
"switch(abc){case c1:s1;break;case c2:s2;default:s3;}",
"abc",
"c1",
"s1;",
"break;",
"c2",
"s2;",
"s3;"
);
}
public void testWhileStatementDraft() throws Exception {
program = parseJs("while (a>2) { a--; }");
checkMappings(
"while (a > 2) {\n a--;\n}\n",
" a--;\n"
);
}
public void testWhileStatementCompactInlined() throws Exception {
compact = true;
includeInlinedRanges = true;
program = parseJs("while (a>2) { a--; }");
checkMappings(
"while(a>2){a--}",
"a>2",
"a",
"2",
"a--",
"a"
);
}
private JsProgram parseJs(String js) throws IOException, JsParserException {
JsProgram program = new JsProgram();
SourceInfo info = SourceOrigin.create(0, js.length(), 123, "test.js");
List<JsStatement> statements = JsParser.parse(info, program.getScope(),
new StringReader(js));
program.getGlobalBlock().getStatements().addAll(statements);
return program;
}
private void checkMappings(String ...expectedLines)
throws IOException, JsParserException {
DefaultTextOutput text = new DefaultTextOutput(compact);
JsReportGenerationVisitor generator = new JsReportGenerationVisitor(text,
JavaToJavaScriptMap.EMPTY, false) {
@Override
boolean surroundsInJavaSource(SourceInfo parent, SourceInfo child) {
// The Rhino-based JavaScript parser doesn't provide character ranges
// in SourceInfo. Therefore we can't test this method directly
// and have to mock it out.
return !includeInlinedRanges;
}
};
generator.accept(program);
String actual = dumpMappings(text.toString(), generator.getSourceInfoMap());
StringBuilder expected = new StringBuilder();
expected.append("Mappings:\n");
for (String line : expectedLines) {
expected.append(escape(line));
expected.append("\n");
}
assertEquals(expected.toString(), actual);
}
private String dumpMappings(String javascript, JsSourceMap mappings) {
List<Range> ranges = Lists.newArrayList(mappings.getRanges());
Collections.sort(ranges, Range.DEPENDENCY_ORDER_COMPARATOR);
StringBuilder out = new StringBuilder();
out.append("Mappings:\n");
for (Range r : ranges) {
String js = javascript.substring(r.getStart(), r.getEnd());
out.append(escape(js));
out.append("\n");
}
return out.toString();
}
/**
* Escape newlines in a readable way so that each range is on one line for comparison.
*/
private String escape(String js) {
return js.replace("\\n", "\\\\n").replace("\n", "\\n");
}
}