blob: 5a99dddfb6bdb35f42b09152a378c565ffc8dd99 [file] [log] [blame]
/*
* Copyright 2008 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.dev.jjs.SourceOrigin;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsStringLiteral;
import com.google.gwt.dev.js.ast.JsVisitor;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.TextOutput;
import junit.framework.TestCase;
import java.io.StringReader;
import java.util.List;
public class JsToStringGenerationVisitorAccuracyTest extends TestCase {
public void testAdditionPositive() throws Exception {
// x plus positive 3
doTest("x + +3");
}
public void testArithmetic() throws Exception {
doTest("a + (b * (c - d)) / (e / f) % x");
}
public void testArrayDeclarationArrayAccess() throws Exception {
doTest("[1,2,3,4][2]");
}
public void testArrayLiteralParentheses() throws Exception {
doTest("var x = [a, (b, c), d]");
}
public void testBinaryBinaryUnary() throws Exception {
// there needs to be a space between the subtraction and negation
doTest("var x = a - (-b / c)");
doTest("var x = a - (-b * c)");
doTest("var x = a - (-b % c)");
}
public void testBinaryConditionalUnary() throws Exception {
// the subtraction operator has to be separated from the negation
doTest("var x = a - (-b ? c : d)");
}
public void testComplexConstruction() throws Exception {
doTest("(new (new (a(({a : 'b', c : 'd'}),[1,2,3,x,y,z]))())())()");
}
public void testConditionalInvocation() throws Exception {
doTest("(flag?f:g)()");
}
public void testConditionals() throws Exception {
doTest("(a?b:c)?d:e");
doTest("a?b:c?d:e");
doTest("a?b?c:d:e?f:g");
}
public void testConstructionInvocation() throws Exception {
doTest("(new a())()");
}
public void testDecrement() throws Exception {
doTest("(x--)-(-(--y))");
}
public void testEmptyStatements() throws Exception {
doTest("function f() {if (x);}");
doTest("function f() {while (x);}");
doTest("function f() {label:;}");
doTest("function f() {for (i=0;i<n;i++);}");
doTest("function f() {for (var x in s);}");
}
public void testFunctionDeclarationInvocation() throws Exception {
doTest("(function () {})()");
}
public void testInvocationConstruction() throws Exception {
doTest("new ((a.b.c()).d.e)(1,2,3)");
}
public void testNestedConstruction() throws Exception {
doTest("new (new (new MyClass()))");
}
public void testNumberLiteralNameRef() throws Exception {
doTest("(42).nameRef");
}
public void testObjectDeclarationArrayAccess() throws Exception {
doTest("({ a : 'b'})['a']");
}
public void testObjectDeclarationMemberAccess() throws Exception {
doTest("({ a : 'b'}).a");
}
public void testObjectLiteral() throws Exception {
// declaring an object requires parentheses
doTest("({ 'property' : 'value'})");
}
public void testObjectLiteralConditional() throws Exception {
doTest("var x = {a : ((b(), c) ? d : e)}");
}
public void testObjectLiteralDeclaration() throws Exception {
// quotes are necessary around some property variables
doTest("var x = {'abc\\'' : 'value'}");
doTest("var x = {\"a.1\" : 'value'}");
doTest("var x = {\"\\'\\\"\" : 'value'}");
}
public void testObjectLiteralParentheses() throws Exception {
doTest("var x = {a : (c, d), b : 3}");
}
public void testUnaryOperations() throws Exception {
// spaces or parentheses are necessary to separate negation and decrement
doTest("var x = -(-(--y))");
// + prefix not stripped when operand is not literal number
doTest("var x = +y", "var x = +y");
// + prefix stripped when operand is literal number
doTest("var x = +42", "var x = 42");
}
public void testEscapes() {
// Use short octal escapesĀ at the end of the string or when the next
// character is a non-digit
doTestEscapes("\u0000", "'\\0'");
doTestEscapes("a\u0000", "'a\\0'");
doTestEscapes("\u0000a", "'\\0a'");
doTestEscapes("a\u0000a", "'a\\0a'");
// Ensure hex escapes are used where octal is not possible due to a
// following digit
doTestEscapes("\u00006", "'\\x006'");
doTestEscapes("\u00006\u0000", "'\\x006\\0'");
// Single-digit octal escapes or special cases (\b,\t,\n\,f\,\r)
// for characters from 0 to 15
doTestEscapes("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007",
"'\\0\\1\\2\\3\\4\\5\\6\\7'");
doTestEscapes("\u0008\u0009\n\u000b\u000c\r\u000e\u000f",
"'\\b\\t\\n\\13\\f\\r\\16\\17'");
// Use two-digit octal escapes for characters from 16 to 31
doTestEscapes("\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017",
"'\\20\\21\\22\\23\\24\\25\\26\\27'");
doTestEscapes("\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f",
"'\\30\\31\\32\\33\\34\\35\\36\\37'");
// Use two-digit hex escapes for characters up to 0xff
doTestEscapes("\u007f\u00ab", "'\\x7F\\xAB'");
// Use four-digit unicode escapes for characters from 0x100 up
doTestEscapes("\u0100\u117f\u2345", "'\\u0100\\u117F\\u2345'");
}
private void doTest(String js) throws Exception {
List<JsStatement> expected = JsParser.parse(SourceOrigin.UNKNOWN,
new JsProgram().getScope(), new StringReader(js));
List<JsStatement> actual = parse(expected, true);
ComparingVisitor.exec(expected, actual);
actual = parse(expected, false);
ComparingVisitor.exec(expected, actual);
}
private void doTest(String js, String expectedJs) throws Exception {
List<JsStatement> actual = JsParser.parse(SourceOrigin.UNKNOWN,
new JsProgram().getScope(), new StringReader(js));
List<JsStatement> expected = JsParser.parse(SourceOrigin.UNKNOWN,
new JsProgram().getScope(), new StringReader(expectedJs));
ComparingVisitor.exec(expected, actual);
}
private void doTestEscapes(String value, String expected) {
String actual = new JsStringLiteral(SourceOrigin.UNKNOWN, value).toString();
assertEquals(expected, actual);
}
private List<JsStatement> parse(List<JsStatement> expected, boolean compact)
throws Exception {
TextOutput text = new DefaultTextOutput(compact);
JsVisitor generator = new JsSourceGenerationVisitor(text);
generator.acceptList(expected);
return JsParser.parse(SourceOrigin.UNKNOWN, new JsProgram().getScope(),
new StringReader(text.toString()));
}
}