/* * 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())); } }