/*
* Copyright 2009 The Closure Compiler Authors.
*
* 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.javascript.jscomp;
import static com.google.common.truth.Truth.assertThat;
import static com.google.javascript.jscomp.testing.JSErrorSubject.assertError;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;
import com.google.javascript.jscomp.AbstractCommandLineRunner.FlagEntry;
import com.google.javascript.jscomp.AbstractCommandLineRunner.FlagUsageException;
import com.google.javascript.jscomp.AbstractCommandLineRunner.JsSourceType;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.SourceMap.LocationMapping;
import com.google.javascript.rhino.Node;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import junit.framework.TestCase;
/**
* Tests for {@link CommandLineRunner}.
*
* @author nicksantos@google.com (Nick Santos)
*/
public final class CommandLineRunnerTest extends TestCase {
private static final Joiner LINE_JOINER = Joiner.on('\n');
private Compiler lastCompiler = null;
private CommandLineRunner lastCommandLineRunner = null;
private List<Integer> exitCodes = null;
private ByteArrayOutputStream outReader = null;
private ByteArrayOutputStream errReader = null;
private Map<Integer, String> filenames;
// If set, this will be appended to the end of the args list.
// For testing args parsing.
private String lastArg = null;
// If set to true, uses comparison by string instead of by AST.
private boolean useStringComparison = false;
private ModulePattern useModules = ModulePattern.NONE;
private enum ModulePattern {
NONE,
CHAIN,
STAR
}
private final List<String> args = new ArrayList<>();
/** Externs for the test */
private static final ImmutableList<SourceFile> DEFAULT_EXTERNS = ImmutableList.of(
SourceFile.fromCode("externs", Joiner.on('\n').join(
"var arguments;",
"/**",
" * @constructor",
" * @param {...*} var_args",
" * @nosideeffects",
" * @throws {Error}",
" */",
"function Function(var_args) {}",
"/**",
" * @param {...*} var_args",
" * @return {*}",
" */",
"Function.prototype.call = function(var_args) {};",
"/**",
" * @constructor",
" * @param {...*} var_args",
" * @return {!Array}",
" */",
"function Array(var_args) {}",
"/**",
" * @param {*=} opt_begin",
" * @param {*=} opt_end",
" * @return {!Array}",
" * @this {Object}",
" */",
"Array.prototype.slice = function(opt_begin, opt_end) {};",
"/** @constructor */ function Window() {}",
"/** @type {string} */ Window.prototype.name;",
"/** @type {Window} */ var window;",
"/** @constructor */ function Element() {}",
"Element.prototype.offsetWidth;",
"/** @nosideeffects */ function noSideEffects() {}",
"/** @param {...*} x */ function alert(x) {}",
"function Symbol() {}")));
private List<SourceFile> externs;
@Override
public void setUp() throws Exception {
super.setUp();
externs = DEFAULT_EXTERNS;
filenames = new HashMap<>();
lastCompiler = null;
lastArg = null;
outReader = new ByteArrayOutputStream();
errReader = new ByteArrayOutputStream();
useStringComparison = false;
useModules = ModulePattern.NONE;
args.clear();
exitCodes = new ArrayList<>();
}
@Override
public void tearDown() throws Exception {
super.tearDown();
}
public void testUnknownAnnotation() {
args.add("--warning_level=VERBOSE");
test("/** @unknownTag */ function f() {}",
RhinoErrorReporter.BAD_JSDOC_ANNOTATION);
args.add("--extra_annotation_name=unknownTag");
testSame("/** @unknownTag */ function f() {}");
}
// See b/26884264
public void testForOfTypecheck() throws IOException {
args.add("--jscomp_error=checkTypes");
args.add("--language_in=ES6_STRICT");
args.add("--language_out=ES3");
externs = AbstractCommandLineRunner.getBuiltinExterns(CompilerOptions.Environment.BROWSER);
test(
Joiner.on('\n').join(
"class Cat {meow() {}}",
"class Dog {}",
"",
"/** @type {!Array<!Dog>} */",
"var dogs = [];",
"",
"for (var dog of dogs) {",
" dog.meow();", // type error
"}"),
TypeCheck.INEXISTENT_PROPERTY);
}
public void testWarningGuardOrdering1() {
args.add("--jscomp_error=globalThis");
args.add("--jscomp_off=globalThis");
testSame("function f() { this.a = 3; }");
}
public void testWarningGuardOrdering2() {
args.add("--jscomp_off=globalThis");
args.add("--jscomp_error=globalThis");
test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testWarningGuardOrdering3() {
args.add("--jscomp_warning=globalThis");
args.add("--jscomp_off=globalThis");
testSame("function f() { this.a = 3; }");
}
public void testWarningGuardOrdering4() {
args.add("--jscomp_off=globalThis");
args.add("--jscomp_warning=globalThis");
test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testWarningGuardWildcard1() {
args.add("--jscomp_warning=*");
test("/** @public */function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testWarningGuardWildcardOrdering() {
args.add("--jscomp_warning=*");
args.add("--jscomp_off=globalThis");
testSame("/** @public */function f() { this.a = 3; }");
}
public void testWarningGuardHideWarningsFor1() {
args.add("--jscomp_warning=globalThis");
args.add("--hide_warnings_for=foo/bar");
setFilename(0, "foo/bar.baz");
testSame("function f() { this.a = 3; }");
}
public void testWarningGuardHideWarningsFor2() {
args.add("--jscomp_warning=globalThis");
args.add("--hide_warnings_for=bar/baz");
setFilename(0, "foo/bar.baz");
test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testSimpleModeLeavesUnusedParams() {
args.add("--compilation_level=SIMPLE_OPTIMIZATIONS");
testSame("window.f = function(a) {};");
}
public void testAdvancedModeRemovesUnusedParams() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
test("window.f = function(a) {};", "window.a = function() {};");
}
public void testCheckGlobalThisOffByDefault() {
testSame("function f() { this.a = 3; }");
}
public void testCheckGlobalThisOnWithAdvancedMode() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testCheckGlobalThisOnWithAdvanced() {
args.add("-O=ADVANCED");
test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testCheckGlobalThisOnWithErrorFlag() {
args.add("--jscomp_error=globalThis");
test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testCheckGlobalThisOff() {
args.add("--warning_level=VERBOSE");
args.add("--jscomp_off=globalThis");
testSame("function f() { this.a = 3; }");
}
public void testTypeCheckingOffByDefault() {
test("function f(x) { return x; } f();",
"function f(a) { return a; } f();");
}
public void testReflectedMethods() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--jscomp_off=checkVars");
test(
"/** @constructor */" +
"function Foo() {}" +
"Foo.prototype.handle = function(x, y) { alert(y); };" +
"var x = goog.reflect.object(Foo, {handle: 1});" +
"for (var i in x) { x[i].call(x); }" +
"window['Foo'] = Foo;",
"function a() {}" +
"a.prototype.a = function(e, d) { alert(d); };" +
"var b = goog.c.b(a, {a: 1}),c;" +
"for (c in b) { b[c].call(b); }" +
"window.Foo = a;");
}
public void testInlineVariables() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--jscomp_off=checkVars");
// Verify local var "val" in method "bar" is not inlined over the "inc"
// method call (which has side-effects) but "c" is inlined (which can't be
// modified by the call).
test(
"/** @constructor */ function F() { this.a = 0; }" +
"F.prototype.inc = function() { this.a++; return 10; };" +
"F.prototype.bar = function() { " +
" var c = 3; var val = this.inc(); this.a += val + c;" +
"};" +
"window['f'] = new F();" +
"window['f']['inc'] = window['f'].inc;" +
"window['f']['bar'] = window['f'].bar;" +
"use(window['f'].a)",
"function a(){ this.a = 0; }" +
"a.prototype.b = function(){ this.a++; return 10; };" +
"a.prototype.c = function(){ var b=this.b(); this.a += b + 3; };" +
"window.f = new a;" +
"window.f.inc = window.f.b;" +
"window.f.bar = window.f.c;" +
"use(window.f.a);");
}
public void testTypedAdvanced() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--jscomp_warning=checkTypes");
test(
"/** @constructor */\n" +
"function Foo() {}\n" +
"Foo.prototype.handle1 = function(x, y) { alert(y); };\n" +
"/** @constructor */\n" +
"function Bar() {}\n" +
"Bar.prototype.handle1 = function(x, y) {};\n" +
"new Foo().handle1(1, 2);\n" +
"new Bar().handle1(1, 2);\n",
"alert(2)");
}
public void testTypedDisabledAdvanced() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--use_types_for_optimization=false");
test(
"/** @constructor */\n"
+ "function Foo() {}\n"
+"Foo.prototype.handle1 = function(x, y) { alert(y); };\n"
+ "/** @constructor */\n"
+ "function Bar() {}\n"
+ "Bar.prototype.handle1 = function(x, y) {};\n"
+ "new Foo().handle1(1, 2);\n"
+ "new Bar().handle1(1, 2);\n",
"function a() {}\n"
+ "a.prototype.a = function(d, c) { alert(c); };\n"
+ "function b() {}\n"
+ "b.prototype.a = function() {};\n"
+ "(new a).a(1, 2);\n"
+ "(new b).a(1, 2);");
}
public void testTypeCheckingOnWithVerbose() {
args.add("--warning_level=VERBOSE");
test("function f(x) { return x; } f();", TypeCheck.WRONG_ARGUMENT_COUNT);
}
public void testTypeCheckingOnWithWVerbose() {
args.add("-W=VERBOSE");
test("function f(x) { return x; } f();", TypeCheck.WRONG_ARGUMENT_COUNT);
}
public void testTypeParsingOffByDefault() {
testSame("/** @return {number */ function f(a) { return a; }");
}
public void testTypeParsingOnWithVerbose() {
args.add("--warning_level=VERBOSE");
test("/** @return {number */ function f(a) { return a; }",
RhinoErrorReporter.TYPE_PARSE_ERROR);
test("/** @return {n} */ function f(a) { return a; }",
RhinoErrorReporter.UNRECOGNIZED_TYPE_ERROR);
}
public void testTypeCheckOverride1() {
args.add("--warning_level=VERBOSE");
args.add("--jscomp_off=checkTypes");
testSame("var x = x || {}; x.f = function() {}; x.f(3);");
}
public void testTypeCheckOverride2() {
args.add("--warning_level=DEFAULT");
testSame("var x = x || {}; x.f = function() {}; x.f(3);");
args.add("--jscomp_warning=checkTypes");
test("var x = x || {}; x.f = function() {}; x.f(3);",
TypeCheck.WRONG_ARGUMENT_COUNT);
}
public void testCheckSymbolsOffForDefault() {
args.add("--warning_level=DEFAULT");
test("x = 3; var y; var y;", "x=3; var y;");
}
public void testCheckSymbolsOnForVerbose() {
args.add("--jscomp_error=checkVars");
args.add("--warning_level=VERBOSE");
test("x = 3;", VarCheck.UNDEFINED_VAR_ERROR);
test("var y; var y;", VarCheck.VAR_MULTIPLY_DECLARED_ERROR);
}
public void testCheckSymbolsOverrideForVerbose() {
args.add("--warning_level=VERBOSE");
args.add("--jscomp_off=undefinedVars");
testSame("x = 3;");
}
public void testCheckSymbolsOverrideForQuiet() {
args.add("--warning_level=QUIET");
args.add("--jscomp_error=undefinedVars");
test("x = 3;", VarCheck.UNDEFINED_VAR_ERROR);
}
public void testCheckUndefinedProperties1() {
args.add("--warning_level=VERBOSE");
args.add("--jscomp_error=missingProperties");
test("var x = {}; var y = x.bar;", TypeCheck.INEXISTENT_PROPERTY);
}
public void testCheckUndefinedProperties2() {
args.add("--warning_level=VERBOSE");
args.add("--jscomp_off=missingProperties");
test("var x = {}; var y = x.bar;", CheckGlobalNames.UNDEFINED_NAME_WARNING);
}
public void testCheckUndefinedProperties3() {
args.add("--warning_level=VERBOSE");
test("function f() {var x = {}; var y = x.bar;}",
TypeCheck.INEXISTENT_PROPERTY);
}
public void testDuplicateParams() {
test("function f(a, a) {}", RhinoErrorReporter.DUPLICATE_PARAM);
assertThat(lastCompiler.hasHaltingErrors()).isTrue();
}
public void testDefineFlag() {
args.add("--define=FOO");
args.add("--define=\"BAR=5\"");
args.add("--D"); args.add("CCC");
args.add("-D"); args.add("DDD");
test("/** @define {boolean} */ var FOO = false;" +
"/** @define {number} */ var BAR = 3;" +
"/** @define {boolean} */ var CCC = false;" +
"/** @define {boolean} */ var DDD = false;",
"var FOO = !0, BAR = 5, CCC = !0, DDD = !0;");
}
public void testDefineFlag2() {
args.add("--define=FOO='x\"'");
test("/** @define {string} */ var FOO = \"a\";",
"var FOO = \"x\\\"\";");
}
public void testDefineFlag3() {
args.add("--define=FOO=\"x'\"");
test("/** @define {string} */ var FOO = \"a\";",
"var FOO = \"x'\";");
}
public void testScriptStrictModeNoWarning() {
test("'use strict';", "");
test("'no use strict';", CheckSideEffects.USELESS_CODE_ERROR);
}
public void testFunctionStrictModeNoWarning() {
test("function f() {'use strict';}", "function f() {}");
test("function f() {'no use strict';}",
CheckSideEffects.USELESS_CODE_ERROR);
}
public void testQuietMode() {
args.add("--warning_level=DEFAULT");
test("/** @const \n * @const */ var x;",
RhinoErrorReporter.PARSE_ERROR);
args.add("--warning_level=QUIET");
testSame("/** @const \n * @const */ var x;");
}
public void testProcessClosurePrimitives() {
test("var goog = {}; goog.provide('goog.dom');",
"var goog = {dom:{}};");
args.add("--process_closure_primitives=false");
testSame("var goog = {}; goog.provide('goog.dom');");
}
public void testGetMsgWiring() throws Exception {
test("var goog = {}; goog.getMsg = function(x) { return x; };" +
"/** @desc A real foo. */ var MSG_FOO = goog.getMsg('foo');",
"var goog={getMsg:function(a){return a}}, " +
"MSG_FOO=goog.getMsg('foo');");
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
test("var goog = {}; goog.getMsg = function(x) { return x; };" +
"/** @desc A real foo. */ var MSG_FOO = goog.getMsg('foo');" +
"window['foo'] = MSG_FOO;",
"window.foo = 'foo';");
}
public void testGetMsgWiringNoWarnings() throws Exception {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
test("/** @desc A bad foo. */ var MSG_FOO = 1;", "");
}
public void testCssNameWiring() throws Exception {
test("var goog = {}; goog.getCssName = function() {};" +
"goog.setCssNameMapping = function() {};" +
"goog.setCssNameMapping({'goog': 'a', 'button': 'b'});" +
"var a = goog.getCssName('goog-button');" +
"var b = goog.getCssName('css-button');" +
"var c = goog.getCssName('goog-menu');" +
"var d = goog.getCssName('css-menu');",
"var goog = { getCssName: function() {}," +
" setCssNameMapping: function() {} }," +
" a = 'a-b'," +
" b = 'css-b'," +
" c = 'a-menu'," +
" d = 'css-menu';");
}
public void testIssue70a() {
args.add("--language_in=ECMASCRIPT5");
test("function foo({}) {}", RhinoErrorReporter.ES6_FEATURE);
}
public void testIssue70b() {
args.add("--language_in=ECMASCRIPT5");
test("function foo([]) {}", RhinoErrorReporter.ES6_FEATURE);
}
public void testIssue81() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--jscomp_off=checkVars");
useStringComparison = true;
test("eval('1'); var x = eval; x('2');",
"eval(\"1\");(0,eval)(\"2\");");
}
public void testIssue115() {
args.add("--compilation_level=SIMPLE_OPTIMIZATIONS");
args.add("--jscomp_off=es5Strict");
args.add("--strict_mode_input=false");
args.add("--warning_level=VERBOSE");
test("function f() { " +
" var arguments = Array.prototype.slice.call(arguments, 0);" +
" return arguments[0]; " +
"}",
"function f() { " +
" arguments = Array.prototype.slice.call(arguments, 0);" +
" return arguments[0]; " +
"}");
}
public void testIssue297() {
args.add("--compilation_level=SIMPLE_OPTIMIZATIONS");
test("function f(p) {" +
" var x;" +
" return ((x=p.id) && (x=parseInt(x.substr(1)))) && x>0;" +
"}",
"function f(b) {" +
" var a;" +
" return ((a=b.id) && (a=parseInt(a.substr(1)))) && 0<a;" +
"}");
}
public void testHiddenSideEffect() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--jscomp_off=checkVars");
test("element.offsetWidth;",
"element.offsetWidth", CheckSideEffects.USELESS_CODE_ERROR);
}
public void testIssue504() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
test("void function() { alert('hi'); }();",
"alert('hi');void 0", CheckSideEffects.USELESS_CODE_ERROR);
}
public void testIssue601() {
args.add("--compilation_level=WHITESPACE_ONLY");
test("function f() { return '\\v' == 'v'; } window['f'] = f;",
"function f(){return'\\v'=='v'}window['f']=f");
}
public void testIssue601b() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
test("function f() { return '\\v' == 'v'; } window['f'] = f;",
"window.f=function(){return'\\v'=='v'}");
}
public void testIssue601c() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
test("function f() { return '\\u000B' == 'v'; } window['f'] = f;",
"window.f=function(){return'\\u000B'=='v'}");
}
public void testIssue846() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
testSame(
"try { new Function('this is an error'); } catch(a) { alert('x'); }");
}
public void testSideEffectIntegration() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
test("/** @constructor */" +
"var Foo = function() {};" +
"Foo.prototype.blah = function() {" +
" Foo.bar_(this)" +
"};" +
"Foo.bar_ = function(f) {" +
" f.x = 5;" +
"};" +
"var y = new Foo();" +
"Foo.bar_({});" +
// We used to strip this too
// due to bad side-effect propagation.
"y.blah();" +
"alert(y);",
"var a = new function(){}; a.a = 5; alert(a);");
}
public void testDebugFlag1() {
args.add("--compilation_level=SIMPLE_OPTIMIZATIONS");
args.add("--debug=false");
test("function foo(a) {}",
"function foo(a) {}");
}
public void testDebugFlag2() {
args.add("--compilation_level=SIMPLE_OPTIMIZATIONS");
args.add("--debug=true");
test("function foo(a) {alert(a)}",
"function foo($a$$) {alert($a$$)}");
}
public void testDebugFlag3() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--warning_level=QUIET");
args.add("--debug=false");
test("function Foo() {}" +
"Foo.x = 1;" +
"function f() {throw new Foo().x;} f();",
"throw (new function() {}).a;");
}
public void testDebugFlag4() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--warning_level=QUIET");
args.add("--debug=true");
test("function Foo() {}" +
"Foo.x = 1;" +
"function f() {throw new Foo().x;} f();",
"throw (new function Foo() {}).$x$;");
}
public void testBooleanFlag1() {
args.add("--compilation_level=SIMPLE_OPTIMIZATIONS");
args.add("--debug");
test("function foo(a) {alert(a)}",
"function foo($a$$) {alert($a$$)}");
}
public void testBooleanFlag2() {
args.add("--debug");
args.add("--compilation_level=SIMPLE_OPTIMIZATIONS");
test("function foo(a) {alert(a)}",
"function foo($a$$) {alert($a$$)}");
}
public void testHelpFlag() {
args.add("--help");
CommandLineRunner runner =
createCommandLineRunner(new String[] {"function f() {}"});
assertThat(runner.shouldRunCompiler()).isFalse();
assertThat(runner.hasErrors()).isFalse();
String output = new String(outReader.toByteArray(), UTF_8);
assertThat(output).contains(" --help ");
assertThat(output).contains(" --version ");
}
public void testHoistedFunction1() {
args.add("--jscomp_off=es5Strict");
args.add("-W=VERBOSE");
test("if (true) { f(); function f() {} }",
VariableReferenceCheck.EARLY_REFERENCE);
}
public void testHoistedFunction2() {
test("if (window) { f(); function f() {} }",
"if (window) { var f = function() {}; f(); }");
}
public void testExternsLifting1() throws Exception{
String code = "/** @externs */ function f() {}";
test(new String[] {code},
new String[] {});
assertThat(lastCompiler.getExternsForTesting()).hasSize(2);
CompilerInput extern = lastCompiler.getExternsForTesting().get(1);
assertThat(extern.getModule()).isNull();
assertThat(extern.isExtern()).isTrue();
assertThat(extern.getCode()).isEqualTo(code);
assertThat(lastCompiler.getInputsForTesting()).hasSize(1);
CompilerInput input = lastCompiler.getInputsForTesting().get(0);
assertThat(input.getModule()).isNotNull();
assertThat(input.isExtern()).isFalse();
assertThat(input.getCode()).isEmpty();
}
public void testExternsLifting2() {
args.add("--warning_level=VERBOSE");
test(new String[] {"/** @externs */ function f() {}", "f(3);"},
new String[] {"f(3);"},
TypeCheck.WRONG_ARGUMENT_COUNT);
}
public void testSourceSortingOff() {
args.add("--compilation_level=WHITESPACE_ONLY");
testSame(
new String[] {
"goog.require('beer');",
"goog.provide('beer');"
});
}
public void testSourceSortingOn() {
test(new String[] {
"goog.require('beer');",
"goog.provide('beer');"
},
new String[] {
"var beer = {};",
""
});
}
public void testSourceSortingOn2() {
test(new String[] {
"goog.provide('a');",
"goog.require('a');\n" +
"var COMPILED = false;",
},
new String[] {
"var a={};",
"var COMPILED=!1"
});
}
public void testSourceSortingOn3() {
args.add("--dependency_mode=LOOSE");
args.add("--language_in=ECMASCRIPT5");
test(new String[] {
"goog.addDependency('sym', [], []);\nvar x = 3;",
"var COMPILED = false;",
},
new String[] {
"var COMPILED = !1;",
"var x = 3;"
});
}
public void testSourceSortingCircularDeps1() {
args.add("--dependency_mode=LOOSE");
args.add("--language_in=ECMASCRIPT5");
test(new String[] {
"goog.provide('gin'); goog.require('tonic'); var gin = {};",
"goog.provide('tonic'); goog.require('gin'); var tonic = {};",
"goog.require('gin'); goog.require('tonic');"
},
ProcessClosurePrimitives.LATE_PROVIDE_ERROR);
}
public void testSourceSortingCircularDeps2() {
args.add("--dependency_mode=LOOSE");
args.add("--language_in=ECMASCRIPT5");
test(new String[] {
"goog.provide('roses.lime.juice');",
"goog.provide('gin'); goog.require('tonic'); var gin = {};",
"goog.provide('tonic'); goog.require('gin'); var tonic = {};",
"goog.require('gin'); goog.require('tonic');",
"goog.provide('gimlet'); goog.require('gin'); goog.require('roses.lime.juice');"
},
ProcessClosurePrimitives.LATE_PROVIDE_ERROR);
}
public void testSourcePruningOn1() {
args.add("--dependency_mode=LOOSE");
args.add("--language_in=ECMASCRIPT5");
test(new String[] {
"goog.require('beer');",
"goog.provide('beer');",
"goog.provide('scotch'); var x = 3;"
},
new String[] {
"var beer = {};",
""
});
}
public void testSourcePruningOn2() {
args.add("--entry_point=goog:guinness");
test(new String[] {
"goog.provide('guinness');\ngoog.require('beer');",
"goog.provide('beer');",
"goog.provide('scotch'); var x = 3;"
},
new String[] {
"var beer = {};",
"var guinness = {};"
});
}
public void testSourcePruningOn3() {
args.add("--entry_point=goog:scotch");
test(new String[] {
"goog.provide('guinness');\ngoog.require('beer');",
"goog.provide('beer');",
"goog.provide('scotch'); var x = 3;"
},
new String[] {
"var scotch = {}, x = 3;",
});
}
public void testSourcePruningOn4() {
args.add("--entry_point=goog:scotch");
args.add("--entry_point=goog:beer");
test(new String[] {
"goog.provide('guinness');\ngoog.require('beer');",
"goog.provide('beer');",
"goog.provide('scotch'); var x = 3;"
},
new String[] {
"var beer = {};",
"var scotch = {}, x = 3;",
});
}
public void testSourcePruningOn5() {
args.add("--entry_point=goog:shiraz");
test(new String[] {
"goog.provide('guinness');\ngoog.require('beer');",
"goog.provide('beer');",
"goog.provide('scotch'); var x = 3;"
},
Compiler.MISSING_ENTRY_ERROR);
}
public void testSourcePruningOn6() {
args.add("--entry_point=goog:scotch");
test(new String[] {
"goog.require('beer');",
"goog.provide('beer');",
"goog.provide('scotch'); var x = 3;"
},
new String[] {
"var beer = {};",
"",
"var scotch = {}, x = 3;",
});
}
public void testSourcePruningOn7() {
args.add("--dependency_mode=LOOSE");
test(new String[] {
"var COMPILED = false;",
},
new String[] {
"var COMPILED = !1;",
});
}
public void testSourcePruningOn8() {
args.add("--dependency_mode=STRICT");
args.add("--entry_point=goog:scotch");
args.add("--warning_level=VERBOSE");
test(new String[] {
"/** @externs */\n" +
"var externVar;",
"goog.provide('scotch'); var x = externVar;"
},
new String[] {
"var scotch = {}, x = externVar;",
});
}
public void testModuleEntryPoint() throws Exception {
useModules = ModulePattern.STAR;
args.add("--dependency_mode=STRICT");
args.add("--entry_point=goog:m1:a");
test(
new String[] {
"goog.provide('a');",
"goog.provide('b');"
},
// Check that 'b' was stripped out, and 'a' was moved to the second
// module (m1).
new String[] {
"",
"var a = {};"
});
}
public void testNoCompile() {
args.add("--warning_level=VERBOSE");
test(new String[] {
"/** @nocompile */\n" +
"goog.provide('x');\n" +
"var dupeVar;",
"var dupeVar;"
},
new String[] {
"var dupeVar;"
});
}
public void testDependencySortingWhitespaceMode() {
args.add("--dependency_mode=LOOSE");
args.add("--compilation_level=WHITESPACE_ONLY");
test(new String[] {
"goog.require('beer');",
"goog.provide('beer');\ngoog.require('hops');",
"goog.provide('hops');",
},
new String[] {
"goog.provide('hops');",
"goog.provide('beer');\ngoog.require('hops');",
"goog.require('beer');"
});
}
public void testForwardDeclareDroppedTypes() {
args.add("--dependency_mode=LOOSE");
args.add("--warning_level=VERBOSE");
test(
new String[] {
"goog.require('beer');",
"goog.provide('beer'); /** @param {Scotch} x */ function f(x) {}",
"goog.provide('Scotch'); var x = 3;"
},
new String[] {"var beer = {}; function f(a) {}", ""});
test(new String[] {
"goog.require('beer');",
"goog.provide('beer'); /** @param {Scotch} x */ function f(x) {}"
},
new String[] {
"var beer = {}; function f(a) {}",
""
},
RhinoErrorReporter.UNRECOGNIZED_TYPE_ERROR);
}
public void testOnlyClosureDependenciesEmptyEntryPoints() throws Exception {
// Prevents this from trying to load externs.zip
args.add("--env=CUSTOM");
args.add("--dependency_mode=STRICT");
try {
CommandLineRunner runner = createCommandLineRunner(new String[0]);
runner.doRun();
fail("Expected FlagUsageException");
} catch (FlagUsageException e) {
assertTrue(e.getMessage(), e.getMessage().contains("dependency_mode=STRICT"));
}
}
public void testOnlyClosureDependenciesOneEntryPoint() throws Exception {
args.add("--dependency_mode=STRICT");
args.add("--entry_point=goog:beer");
test(new String[] {
"goog.require('beer'); var beerRequired = 1;",
"goog.provide('beer');\ngoog.require('hops');\nvar beerProvided = 1;",
"goog.provide('hops'); var hopsProvided = 1;",
"goog.provide('scotch'); var scotchProvided = 1;",
"goog.require('scotch');\nvar includeFileWithoutProvides = 1;",
"/** This is base.js */\nvar COMPILED = false;",
},
new String[] {
"var COMPILED = !1;",
"var hops = {}, hopsProvided = 1;",
"var beer = {}, beerProvided = 1;"
});
}
public void testSourceMapExpansion1() {
args.add("--js_output_file");
args.add("/path/to/out.js");
args.add("--create_source_map=%outname%.map");
testSame("var x = 3;");
assertThat(lastCommandLineRunner.expandSourceMapPath(lastCompiler.getOptions(), null))
.isEqualTo("/path/to/out.js.map");
}
public void testSourceMapExpansion2() {
useModules = ModulePattern.CHAIN;
args.add("--create_source_map=%outname%.map");
args.add("--module_output_path_prefix=foo");
testSame(new String[] {"var x = 3;", "var y = 5;"});
assertThat(lastCommandLineRunner.expandSourceMapPath(lastCompiler.getOptions(), null))
.isEqualTo("foo.map");
}
public void testSourceMapExpansion3() {
useModules = ModulePattern.CHAIN;
args.add("--create_source_map=%outname%.map");
args.add("--module_output_path_prefix=foo_");
testSame(new String[] {"var x = 3;", "var y = 5;"});
assertThat(
lastCommandLineRunner.expandSourceMapPath(
lastCompiler.getOptions(), lastCompiler.getModuleGraph().getRootModule()))
.isEqualTo("foo_m0.js.map");
}
public void testInvalidSourceMapPattern() {
useModules = ModulePattern.CHAIN;
args.add("--create_source_map=out.map");
args.add("--module_output_path_prefix=foo_");
test(
new String[] {"var x = 3;", "var y = 5;"},
AbstractCommandLineRunner.INVALID_MODULE_SOURCEMAP_PATTERN);
}
public void testSourceMapFormat1() {
args.add("--js_output_file");
args.add("/path/to/out.js");
testSame("var x = 3;");
assertThat(lastCompiler.getOptions().sourceMapFormat).isEqualTo(SourceMap.Format.DEFAULT);
}
public void testSourceMapFormat2() {
args.add("--js_output_file");
args.add("/path/to/out.js");
args.add("--source_map_format=V3");
testSame("var x = 3;");
assertThat(lastCompiler.getOptions().sourceMapFormat).isEqualTo(SourceMap.Format.V3);
}
public void testSourceMapLocationsTranslations1() {
args.add("--js_output_file");
args.add("/path/to/out.js");
args.add("--create_source_map=%outname%.map");
args.add("--source_map_location_mapping=foo/|http://bar");
testSame("var x = 3;");
List<LocationMapping> mappings = lastCompiler.getOptions()
.sourceMapLocationMappings;
assertThat(ImmutableSet.copyOf(mappings).toString())
.isEqualTo(ImmutableSet.of(new LocationMapping("foo/", "http://bar")).toString());
}
public void testSourceMapLocationsTranslations2() {
args.add("--js_output_file");
args.add("/path/to/out.js");
args.add("--create_source_map=%outname%.map");
args.add("--source_map_location_mapping=foo/|http://bar");
args.add("--source_map_location_mapping=xxx/|http://yyy");
testSame("var x = 3;");
List<LocationMapping> mappings = lastCompiler.getOptions()
.sourceMapLocationMappings;
assertThat(ImmutableSet.copyOf(mappings).toString())
.isEqualTo(
ImmutableSet.of(
new LocationMapping("foo/", "http://bar"),
new LocationMapping("xxx/", "http://yyy"))
.toString());
}
public void testSourceMapLocationsTranslations3() {
// Prevents this from trying to load externs.zip
args.add("--env=CUSTOM");
args.add("--js_output_file");
args.add("/path/to/out.js");
args.add("--create_source_map=%outname%.map");
args.add("--source_map_location_mapping=foo/");
CommandLineRunner runner = createCommandLineRunner(new String[0]);
assertThat(runner.shouldRunCompiler()).isFalse();
assertThat(new String(errReader.toByteArray(), UTF_8))
.contains("Bad value for --source_map_location_mapping");
}
public void testInputOneZip() throws IOException {
LinkedHashMap<String, String> zip1Contents = new LinkedHashMap<>();
zip1Contents.put("run.js", "console.log(\"Hello World\");");
FlagEntry<JsSourceType> zipFile1 = createZipFile(zip1Contents);
compileFiles("console.log(\"Hello World\");", zipFile1);
}
public void testInputMultipleZips() throws IOException {
LinkedHashMap<String, String> zip1Contents = new LinkedHashMap<>();
zip1Contents.put("run.js", "console.log(\"Hello World\");");
FlagEntry<JsSourceType> zipFile1 = createZipFile(zip1Contents);
LinkedHashMap<String, String> zip2Contents = new LinkedHashMap<>();
zip2Contents.put("run1.js", "window.alert(\"Hi Browser\");");
FlagEntry<JsSourceType> zipFile2 = createZipFile(zip2Contents);
compileFiles(
"console.log(\"Hello World\");window.alert(\"Hi Browser\");", zipFile1, zipFile2);
}
public void testInputMultipleDuplicateZips() throws IOException {
args.add("--jscomp_error=duplicateZipContents");
FlagEntry<JsSourceType> zipFile1 =
createZipFile(ImmutableMap.of("run.js", "console.log(\"Hello World\");"));
FlagEntry<JsSourceType> zipFile2 =
createZipFile(ImmutableMap.of("run.js", "console.log(\"Hello World\");"));
compileFilesError(
SourceFile.DUPLICATE_ZIP_CONTENTS, zipFile1, zipFile2);
}
public void testInputMultipleConflictingZips() throws IOException {
FlagEntry<JsSourceType> zipFile1 =
createZipFile(ImmutableMap.of("run.js", "console.log(\"Hello World\");"));
FlagEntry<JsSourceType> zipFile2 =
createZipFile(ImmutableMap.of("run.js", "window.alert(\"Hi Browser\");"));
compileFilesError(
AbstractCommandLineRunner.CONFLICTING_DUPLICATE_ZIP_CONTENTS, zipFile1, zipFile2);
}
public void testInputMultipleContents() throws IOException {
LinkedHashMap<String, String> zip1Contents = new LinkedHashMap<>();
zip1Contents.put("a.js", "console.log(\"File A\");");
zip1Contents.put("b.js", "console.log(\"File B\");");
zip1Contents.put("c.js", "console.log(\"File C\");");
FlagEntry<JsSourceType> zipFile1 = createZipFile(zip1Contents);
compileFiles(
"console.log(\"File A\");console.log(\"File B\");console.log(\"File C\");", zipFile1);
}
public void testInputMultipleFiles() throws IOException {
LinkedHashMap<String, String> zip1Contents = new LinkedHashMap<>();
zip1Contents.put("run.js", "console.log(\"Hello World\");");
FlagEntry<JsSourceType> zipFile1 = createZipFile(zip1Contents);
FlagEntry<JsSourceType> jsFile1 = createJsFile("testjsfile", "var a;");
LinkedHashMap<String, String> zip2Contents = new LinkedHashMap<>();
zip2Contents.put("run1.js", "window.alert(\"Hi Browser\");");
FlagEntry<JsSourceType> zipFile2 = createZipFile(zip2Contents);
compileFiles(
"console.log(\"Hello World\");var a;window.alert(\"Hi Browser\");",
zipFile1, jsFile1, zipFile2);
}
public void testInputMultipleJsFilesWithOneJsFlag() throws IOException {
// Test that file order is preserved with --js test3.js test2.js test1.js
FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
FlagEntry<JsSourceType> jsFile3 = createJsFile("test3", "var c;");
compileJsFiles("var c;var b;var a;", jsFile3, jsFile2, jsFile1);
}
public void testGlobJs1() throws IOException {
FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
// Move test2 to the same directory as test1, also make the filename of test2
// lexicographically larger than test1
assertTrue(new File(jsFile2.getValue()).renameTo(new File(
new File(jsFile1.getValue()).getParentFile() + File.separator + "utest2.js")));
String glob = new File(jsFile1.getValue()).getParent() + File.separator + "**.js";
compileFiles(
"var a;var b;", new FlagEntry<>(JsSourceType.JS, glob));
}
public void testGlobJs2() throws IOException {
FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
assertTrue(new File(jsFile2.getValue()).renameTo(new File(
new File(jsFile1.getValue()).getParentFile() + File.separator + "utest2.js")));
String glob = new File(jsFile1.getValue()).getParent() + File.separator + "*test*.js";
compileFiles(
"var a;var b;", new FlagEntry<>(JsSourceType.JS, glob));
}
public void testGlobJs3() throws IOException {
FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
assertTrue(new File(jsFile2.getValue()).renameTo(new File(
new File(jsFile1.getValue()).getParentFile() + File.separator + "test2.js")));
// Make sure test2.js is excluded from the inputs when the exclusion
// comes after the inclusion
String glob1 = new File(jsFile1.getValue()).getParent() + File.separator + "**.js";
String glob2 = "!" + new File(jsFile1.getValue()).getParent() + File.separator + "**test2.js";
compileFiles(
"var a;", new FlagEntry<>(JsSourceType.JS, glob1),
new FlagEntry<>(JsSourceType.JS, glob2));
}
public void testGlobJs4() throws IOException {
FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
assertTrue(new File(jsFile2.getValue()).renameTo(new File(
new File(jsFile1.getValue()).getParentFile() + File.separator + "test2.js")));
// Make sure test2.js is excluded from the inputs when the exclusion
// comes before the inclusion
String glob1 = "!" + new File(jsFile1.getValue()).getParent() + File.separator + "**test2.js";
String glob2 = new File(jsFile1.getValue()).getParent() + File.separator + "**.js";
compileFiles(
"var a;", new FlagEntry<>(JsSourceType.JS, glob1),
new FlagEntry<>(JsSourceType.JS, glob2));
}
public void testGlobJs5() throws IOException {
FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
File temp1 = Files.createTempDir();
File temp2 = Files.createTempDir();
File jscompTempDir = new File(jsFile1.getValue()).getParentFile();
File newTemp1 = new File(jscompTempDir + File.separator + "temp1");
File newTemp2 = new File(jscompTempDir + File.separator + "temp2");
assertTrue(temp1.renameTo(newTemp1));
assertTrue(temp2.renameTo(newTemp2));
new File(jsFile1.getValue()).renameTo(new File(newTemp1 + File.separator + "test1.js"));
new File(jsFile2.getValue()).renameTo(new File(newTemp2 + File.separator + "test2.js"));
// Test multiple segments with glob patterns, like /foo/bar/**/*.js
String glob = jscompTempDir + File.separator + "**" + File.separator + "*.js";
compileFiles(
"var a;var b;", new FlagEntry<>(JsSourceType.JS, glob));
}
// TODO(tbreisacher): Re-enable this test when we drop Ant.
public void disabled_testGlobJs6() throws IOException {
FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
File ignoredJs = new File("." + File.separator + "ignored.js");
if (ignoredJs.isDirectory()) {
for (File f : ignoredJs.listFiles()) {
f.delete();
}
}
ignoredJs.delete();
assertTrue(new File(jsFile2.getValue()).renameTo(ignoredJs));
// Make sure patterns like "!**\./ignored**.js" work
String glob1 = "!**\\." + File.separator + "ignored**.js";
String glob2 = new File(jsFile1.getValue()).getParent() + File.separator + "**.js";
compileFiles(
"var a;", new FlagEntry<>(JsSourceType.JS, glob1),
new FlagEntry<>(JsSourceType.JS, glob2));
ignoredJs.delete();
}
// TODO(tbreisacher): Re-enable this test when we drop Ant.
public void disabled_testGlobJs7() throws IOException {
FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
File takenJs = new File("." + File.separator + "globTestTaken.js");
File ignoredJs = new File("." + File.separator + "globTestIgnored.js");
if (takenJs.isDirectory()) {
for (File f : takenJs.listFiles()) {
f.delete();
}
}
takenJs.delete();
if (ignoredJs.isDirectory()) {
for (File f : ignoredJs.listFiles()) {
f.delete();
}
}
ignoredJs.delete();
assertTrue(new File(jsFile1.getValue()).renameTo(takenJs));
assertTrue(new File(jsFile2.getValue()).renameTo(ignoredJs));
// Make sure that relative paths like "!**ignored.js" work with absolute paths.
String glob1 = takenJs.getParentFile().getAbsolutePath() + File.separator + "**Taken.js";
String glob2 = "!**Ignored.js";
compileFiles(
"var a;", new FlagEntry<>(JsSourceType.JS, glob1),
new FlagEntry<>(JsSourceType.JS, glob2));
takenJs.delete();
ignoredJs.delete();
}
public void testSourceMapInputs() throws Exception {
args.add("--js_output_file");
args.add("/path/to/out.js");
args.add("--source_map_input=input1|input1.sourcemap");
args.add("--source_map_input=input2|input2.sourcemap");
testSame("var x = 3;");
Map<String, SourceMapInput> inputMaps = lastCompiler.getOptions()
.inputSourceMaps;
assertThat(inputMaps).hasSize(2);
assertThat(inputMaps.get("input1").getOriginalPath())
.isEqualTo("input1.sourcemap");
assertThat(inputMaps.get("input2").getOriginalPath())
.isEqualTo("input2.sourcemap");
}
public void testModuleWrapperBaseNameExpansion() throws Exception {
useModules = ModulePattern.CHAIN;
args.add("--module_wrapper=m0:%s // %basename%");
testSame(new String[] {
"var x = 3;",
"var y = 4;"
});
StringBuilder builder = new StringBuilder();
lastCommandLineRunner.writeModuleOutput(
builder,
lastCompiler.getModuleGraph().getRootModule());
assertThat(builder.toString()).isEqualTo("var x=3; // m0.js\n");
}
public void testCharSetExpansion() {
testSame("");
assertThat(lastCompiler.getOptions().outputCharset).isEqualTo(US_ASCII);
args.add("--charset=UTF-8");
testSame("");
assertThat(lastCompiler.getOptions().outputCharset).isEqualTo(UTF_8);
}
public void testChainModuleManifest() throws Exception {
useModules = ModulePattern.CHAIN;
testSame(new String[] {"var x = 3;", "var y = 5;", "var z = 7;", "var a = 9;"});
StringBuilder builder = new StringBuilder();
lastCommandLineRunner.printModuleGraphManifestOrBundleTo(
lastCompiler.getModuleGraph(), builder, true);
assertThat(builder.toString())
.isEqualTo(Joiner.on('\n').join(
"{m0}",
"i0.js",
"",
"{m1:m0}",
"i1.js",
"",
"{m2:m1}",
"i2.js",
"",
"{m3:m2}",
"i3.js",
""));
}
public void testStarModuleManifest() throws Exception {
useModules = ModulePattern.STAR;
testSame(new String[] {
"var x = 3;", "var y = 5;", "var z = 7;", "var a = 9;"});
StringBuilder builder = new StringBuilder();
lastCommandLineRunner.printModuleGraphManifestOrBundleTo(
lastCompiler.getModuleGraph(), builder, true);
assertThat(builder.toString())
.isEqualTo(Joiner.on('\n').join(
"{m0}",
"i0.js",
"",
"{m1:m0}",
"i1.js",
"",
"{m2:m0}",
"i2.js",
"",
"{m3:m0}",
"i3.js",
""));
}
public void testOutputModuleGraphJson() throws Exception {
useModules = ModulePattern.STAR;
testSame(new String[] {"var x = 3;", "var y = 5;", "var z = 7;", "var a = 9;"});
StringBuilder builder = new StringBuilder();
lastCommandLineRunner.printModuleGraphJsonTo(builder);
assertThat(builder.toString()).contains("transitive-dependencies");
}
public void testVersionFlag() {
args.add("--version");
CommandLineRunner runner =
createCommandLineRunner(new String[] {"function f() {}"});
assertThat(runner.shouldRunCompiler()).isFalse();
assertThat(runner.hasErrors()).isFalse();
assertThat(
new String(outReader.toByteArray(), UTF_8)
.indexOf("Closure Compiler (http://github.com/google/closure-compiler)\n"
+ "Version: ")).isEqualTo(0);
}
public void testVersionFlag2() {
lastArg = "--version";
CommandLineRunner runner =
createCommandLineRunner(new String[] {"function f() {}"});
assertThat(runner.shouldRunCompiler()).isFalse();
assertThat(runner.hasErrors()).isFalse();
assertThat(new String(outReader.toByteArray(), UTF_8))
.startsWith("Closure Compiler (http://github.com/google/closure-compiler)\nVersion: ");
}
public void testPrintAstFlag() {
args.add("--print_ast=true");
testSame("");
assertThat(new String(outReader.toByteArray(), UTF_8))
.isEqualTo("digraph AST {\n"
+ " node [color=lightblue2, style=filled];\n"
+ " node0 [label=\"ROOT\"];\n"
+ " node1 [label=\"SCRIPT\"];\n"
+ " node0 -> node1 [weight=1];\n"
+ " node1 -> RETURN [label=\"UNCOND\", "
+ "fontcolor=\"red\", weight=0.01, color=\"red\"];\n"
+ " node0 -> RETURN [label=\"SYN_BLOCK\", "
+ "fontcolor=\"red\", weight=0.01, color=\"red\"];\n"
+ " node0 -> node1 [label=\"UNCOND\", "
+ "fontcolor=\"red\", weight=0.01, color=\"red\"];\n"
+ "}\n\n");
}
public void testSyntheticExterns() {
externs = ImmutableList.of(
SourceFile.fromCode("externs", "function Symbol() {}; myVar.property;"));
test("var theirVar = {}; var myVar = {}; var yourVar = {};",
VarCheck.UNDEFINED_EXTERN_VAR_ERROR);
args.add("--jscomp_off=externsValidation");
args.add("--warning_level=VERBOSE");
test("var theirVar = {}; var myVar = {}; var yourVar = {};",
"var theirVar={},myVar={},yourVar={};");
args.add("--jscomp_off=externsValidation");
args.add("--jscomp_error=checkVars");
args.add("--warning_level=VERBOSE");
test(
"var theirVar = {}; var myVar = {}; var myVar = {};", VarCheck.VAR_MULTIPLY_DECLARED_ERROR);
}
public void testGoogAssertStripping() {
args.add("--compilation_level=ADVANCED_OPTIMIZATIONS");
args.add("--jscomp_off=checkVars");
test("goog.asserts.assert(false)", "");
args.add("--debug");
test("goog.asserts.assert(false)", "goog.$asserts$.$assert$(!1)");
}
public void testMissingReturnCheckOnWithVerbose() {
args.add("--warning_level=VERBOSE");
test("/** @return {number} */ function f() {f()} f();",
CheckMissingReturn.MISSING_RETURN_STATEMENT);
}
public void testChecksOnlySkipsOptimizations() {
args.add("--checks_only");
test("var foo = 1 + 1;",
"var foo = 1 + 1;");
}
public void testChecksOnlyWithParseError() {
args.add("--compilation_level=WHITESPACE_ONLY");
args.add("--checks_only");
test("val foo = 1;",
RhinoErrorReporter.PARSE_ERROR);
}
public void testChecksOnlyWithWarning() {
args.add("--checks_only");
args.add("--warning_level=VERBOSE");
test("/** @deprecated */function foo() {}; foo();",
CheckAccessControls.DEPRECATED_NAME);
}
public void testGenerateExports() {
args.add("--generate_exports=true");
test("var goog; /** @export */ foo.prototype.x = function() {};",
"var goog; foo.prototype.x=function(){};" +
"goog.exportProperty(foo.prototype,\"x\",foo.prototype.x);");
}
public void testDepreciationWithVerbose() {
args.add("--warning_level=VERBOSE");
test("/** @deprecated */ function f() {}; f()",
CheckAccessControls.DEPRECATED_NAME);
}
public void testTwoParseErrors() {
// If parse errors are reported in different files, make
// sure all of them are reported.
Compiler compiler = compile(new String[] {
"var a b;",
"var b c;"
});
assertThat(compiler.getErrors()).hasLength(2);
}
public void testES3() {
args.add("--language_in=ECMASCRIPT3");
args.add("--language_out=ECMASCRIPT3");
useStringComparison = true;
test(
"var x = f.function",
"var x=f[\"function\"];",
RhinoErrorReporter.INVALID_ES3_PROP_NAME);
}
public void testES6TranspiledByDefault() {
test("var x = class {};", "var x = function() {};");
}
public void testES5ChecksByDefault() {
test("var x = 3; delete x;", StrictModeCheck.DELETE_VARIABLE);
}
public void testES5ChecksInVerbose() {
args.add("--warning_level=VERBOSE");
test("function f(x) { delete x; }", StrictModeCheck.DELETE_VARIABLE);
}
public void testES5() {
args.add("--language_in=ECMASCRIPT5");
test("var x = f.function", "var x = f.function");
test("var let", "var let");
}
public void testES5Strict() {
args.add("--language_in=ECMASCRIPT5_STRICT");
args.add("--language_out=ECMASCRIPT5_STRICT");
test("var x = f.function", "'use strict';var x = f.function");
test("var let", RhinoErrorReporter.PARSE_ERROR);
test("function f(x) { delete x; }", StrictModeCheck.DELETE_VARIABLE);
}
public void testES5StrictUseStrict() {
args.add("--language_in=ECMASCRIPT5_STRICT");
args.add("--language_out=ECMASCRIPT5_STRICT");
Compiler compiler = compile(new String[] {"var x = f.function"});
String outputSource = compiler.toSource();
assertThat(outputSource).startsWith("'use strict'");
}
public void testES5StrictUseStrictMultipleInputs() {
args.add("--language_in=ECMASCRIPT5_STRICT");
args.add("--language_out=ECMASCRIPT5_STRICT");
Compiler compiler = compile(new String[] {"var x = f.function",
"var y = f.function", "var z = f.function"});
String outputSource = compiler.toSource();
assertThat(outputSource).startsWith("'use strict'");
assertThat(outputSource.substring(13)).doesNotContain("'use strict'");
}
public void testWithKeywordWithEs5ChecksOff() {
args.add("--jscomp_off=es5Strict");
args.add("--strict_mode_input=false");
testSame("var x = {}; with (x) {}");
}
public void testIsolationMode() {
args.add("--isolation_mode=IIFE");
testSame("window.x = \"123\";");
String output = new String(outReader.toByteArray(), UTF_8);
assertThat(output).isEqualTo(
"(function(){window.x=\"123\";}).call(this);\n");
}
public void testNoSrCFilesWithManifest() throws IOException {
args.add("--env=CUSTOM");
args.add("--output_manifest=test.MF");
CommandLineRunner runner = createCommandLineRunner(new String[0]);
try {
runner.doRun();
fail("Expected flag usage exception");
} catch (FlagUsageException e) {
assertThat(e)
.hasMessage(
"Bad --js flag. Manifest files cannot be generated when the input is from stdin.");
}
}
public void testTransformAMD() {
args.add("--transform_amd_modules");
test("define({test: 1})", "module.exports = {test: 1}");
}
public void testProcessCJS() {
useStringComparison = true;
args.add("--process_common_js_modules");
args.add("--entry_point=foo/bar");
setFilename(0, "foo/bar.js");
String expected = "var module$foo$bar={test:1};";
test("exports.test = 1", expected);
assertThat(outReader.toString()).isEqualTo(expected + "\n");
}
public void testProcessCJSWithModuleOutput() {
args.add("--process_common_js_modules");
args.add("--entry_point=foo/bar");
args.add("--module=auto");
setFilename(0, "foo/bar.js");
test("exports.test = 1",
"var module$foo$bar={test:1};");
// With modules=auto no direct output is created.
assertThat(outReader.toString()).isEmpty();
}
/**
* closure requires mixed with cjs, raised in
* https://github.com/google/closure-compiler/pull/630
* https://gist.github.com/sayrer/c4c4ce0c1748573f863e
*/
public void testProcessCJSWithClosureRequires() {
args.add("--process_common_js_modules");
args.add("--entry_point=app");
args.add("--dependency_mode=STRICT");
setFilename(0, "base.js");
setFilename(1, "array.js");
setFilename(2, "Baz.js");
setFilename(3, "app.js");
test(
new String[] {
LINE_JOINER.join(
"/** @provideGoog */",
"/** @const */ var goog = goog || {};",
"var COMPILED = false;",
"goog.provide = function (arg) {};",
"goog.require = function (arg) {};"),
"goog.provide('goog.array');",
LINE_JOINER.join(
"goog.require('goog.array');",
"function Baz() {}",
"Baz.prototype = {",
" baz: function() {",
" return goog.array.last(['asdf','asd','baz']);",
" },",
" bar: function () {",
" return 4 + 4;",
" }",
"};",
"module.exports = Baz;"),
LINE_JOINER.join(
"var Baz = require('./Baz');",
"var baz = new Baz();",
"console.log(baz.baz());",
"console.log(baz.bar());")
},
new String[] {
LINE_JOINER.join(
"var goog=goog||{},COMPILED=!1;",
"goog.provide=function(a){};goog.require=function(a){};"),
"goog.array={};",
LINE_JOINER.join(
"var module$Baz = function (){};",
"module$Baz.prototype={",
" baz:function(){return goog.array.last(['asdf','asd','baz'])},",
" bar:function(){return 8}",
"};"),
LINE_JOINER.join(
"var Baz = module$Baz,",
" baz = new module$Baz();",
"console.log(baz.baz());",
"console.log(baz.bar());")
});
}
/**
* closure requires mixed with cjs, raised in
* https://github.com/google/closure-compiler/pull/630
* https://gist.github.com/sayrer/c4c4ce0c1748573f863e
*/
public void testProcessCJSWithClosureRequires2() {
args.add("--process_common_js_modules");
args.add("--dependency_mode=LOOSE");
args.add("--entry_point=app");
setFilename(0, "base.js");
setFilename(1, "array.js");
setFilename(2, "Baz.js");
setFilename(3, "app.js");
test(
new String[] {
LINE_JOINER.join(
"/** @provideGoog */",
"/** @const */ var goog = goog || {};",
"var COMPILED = false;",
"goog.provide = function (arg) {};",
"goog.require = function (arg) {};"),
"goog.provide('goog.array');",
LINE_JOINER.join(
"goog.require('goog.array');",
"function Baz() {}",
"Baz.prototype = {",
" baz: function() {",
" return goog.array.last(['asdf','asd','baz']);",
" },",
" bar: function () {",
" return 4 + 4;",
" }",
"};",
"module.exports = Baz;"),
LINE_JOINER.join(
"var Baz = require('./Baz');",
"var baz = new Baz();",
"console.log(baz.baz());",
"console.log(baz.bar());")
},
new String[] {
LINE_JOINER.join(
"var goog=goog||{},COMPILED=!1;",
"goog.provide=function(a){};goog.require=function(a){};"),
"goog.array={};",
LINE_JOINER.join(
"var module$Baz = function (){};",
"module$Baz.prototype={",
" baz:function(){return goog.array.last([\"asdf\",\"asd\",\"baz\"])},",
" bar:function(){return 8}",
"};"),
LINE_JOINER.join(
"var Baz = module$Baz,",
" baz = new module$Baz();",
"console.log(baz.baz());",
"console.log(baz.bar());")
});
}
public void testProcessCJSWithES6Export() {
args.add("--process_common_js_modules");
args.add("--entry_point=app");
args.add("--dependency_mode=STRICT");
args.add("--language_in=ECMASCRIPT6");
setFilename(0, "foo.js");
setFilename(1, "app.js");
test(
new String[] {
LINE_JOINER.join("export default class Foo {", " bar() { console.log('bar'); }", "}"),
LINE_JOINER.join(
"var FooBar = require('./foo');",
"var baz = new FooBar.default();",
"console.log(baz.bar());")
},
new String[] {
LINE_JOINER.join(
"var module$foo={},",
"Foo$$module$foo=function(){};",
"Foo$$module$foo.prototype.bar=function(){console.log(\"bar\")};",
"module$foo.default=Foo$$module$foo;"),
LINE_JOINER.join(
"var FooBar = module$foo,",
" baz = new module$foo.default();",
"console.log(baz.bar());")
});
}
public void testES6ImportOfCJS() {
args.add("--process_common_js_modules");
args.add("--entry_point=app");
args.add("--dependency_mode=STRICT");
args.add("--language_in=ECMASCRIPT6");
setFilename(0, "foo.js");
setFilename(1, "app.js");
test(
new String[] {
LINE_JOINER.join(
"/** @constructor */ function Foo () {}",
"Foo.prototype.bar = function() { console.log('bar'); };",
"module.exports = Foo;"),
LINE_JOINER.join(
"import * as FooBar from './foo';",
"var baz = new FooBar();",
"console.log(baz.bar());")
},
new String[] {
LINE_JOINER.join(
"/** @constructor */ var module$foo = function(){};",
"module$foo.prototype.bar=function(){console.log(\"bar\")};"),
LINE_JOINER.join(
"var baz$$module$app = new module$foo();", "console.log(baz$$module$app.bar());")
});
}
public void testES6ImportOfFileWithoutImportsOrExports() {
args.add("--dependency_mode=NONE");
args.add("--language_in=ECMASCRIPT6");
setFilename(0, "foo.js");
setFilename(1, "app.js");
test(
new String[] {
CompilerTestCase.LINE_JOINER.join("function foo() { alert('foo'); }", "foo();"),
"import './foo';"
},
new String[] {
CompilerTestCase.LINE_JOINER.join(
"/** @const */ var module$foo={};",
"function foo$$module$foo(){ alert('foo'); }",
"foo$$module$foo();"),
"'use strict';"
});
}
public void testCommonJSRequireOfFileWithoutExports() {
args.add("--process_common_js_modules");
args.add("--dependency_mode=NONE");
args.add("--language_in=ECMASCRIPT6");
setFilename(0, "foo.js");
setFilename(1, "app.js");
test(
new String[] {
CompilerTestCase.LINE_JOINER.join("function foo() { alert('foo'); }", "foo();"),
"require('./foo');"
},
new String[] {
CompilerTestCase.LINE_JOINER.join(
"/** @const */ var module$foo={};",
"function foo$$module$foo(){ alert('foo'); }",
"foo$$module$foo();"),
CompilerTestCase.LINE_JOINER.join("'use strict';", "")
});
}
public void testFormattingSingleQuote() {
testSame("var x = '';");
assertThat(lastCompiler.toSource()).isEqualTo("var x=\"\";");
args.add("--formatting=SINGLE_QUOTES");
testSame("var x = '';");
assertThat(lastCompiler.toSource()).isEqualTo("var x='';");
}
public void testTransformAMDAndProcessCJS() {
args.add("--transform_amd_modules");
args.add("--process_common_js_modules");
args.add("--entry_point=foo/bar");
setFilename(0, "foo/bar.js");
test("define({foo: 1})",
"var module$foo$bar={foo:1};");
}
public void testModuleJSON() {
args.add("--transform_amd_modules");
args.add("--process_common_js_modules");
args.add("--entry_point=foo/bar");
args.add("--output_module_dependencies=test.json");
setFilename(0, "foo/bar.js");
test("define({foo: 1})",
"var module$foo$bar={foo:1};");
}
public void testOutputSameAsInput() {
args.add("--js_output_file=" + getFilename(0));
test("", AbstractCommandLineRunner.OUTPUT_SAME_AS_INPUT_ERROR);
}
public void testOutputWrapperFlag() {
// if the output wrapper flag is specified without a valid output marker,
// ensure that the compiler displays an error and exits.
// See github issue 123
args.add("--output_wrapper=output");
CommandLineRunner runner =
createCommandLineRunner(new String[] {"function f() {}"});
assertThat(runner.shouldRunCompiler()).isFalse();
assertThat(runner.hasErrors()).isTrue();
}
public void testJsonStreamInputFlag() {
String inputString = "[{\"src\": \"alert('foo');\", \"path\":\"foo.js\"}]";
args.add("--json_streams=IN");
CommandLineRunner runner =
new CommandLineRunner(
args.toArray(new String[] {}),
new ByteArrayInputStream(inputString.getBytes(UTF_8)),
new PrintStream(outReader),
new PrintStream(errReader));
lastCompiler = runner.getCompiler();
try {
runner.doRun();
} catch (IOException e) {
e.printStackTrace();
fail("Unexpected exception " + e);
}
String output = runner.getCompiler().toSource();
assertThat(output).isEqualTo("alert(\"foo\");");
}
public void testJsonStreamOutputFlag() {
String inputString = "alert('foo');";
args.add("--json_streams=OUT");
CommandLineRunner runner =
new CommandLineRunner(
args.toArray(new String[] {}),
new ByteArrayInputStream(inputString.getBytes(UTF_8)),
new PrintStream(outReader),
new PrintStream(errReader));
lastCompiler = runner.getCompiler();
try {
runner.doRun();
} catch (IOException e) {
e.printStackTrace();
fail("Unexpected exception " + e);
}
String output = new String(outReader.toByteArray(), UTF_8);
assertThat(output).isEqualTo("[{\"src\":\"alert(\\\"foo\\\");\\n\","
+ "\"path\":\"compiled.js\",\"source_map\":\"{\\n\\\"version\\\":3,"
+ "\\n\\\"file\\\":\\\"compiled.js\\\",\\n\\\"lineCount\\\":1,"
+ "\\n\\\"mappings\\\":\\\"AAAAA,KAAA,CAAM,KAAN;\\\","
+ "\\n\\\"sources\\\":[\\\"stdin\\\"],"
+ "\\n\\\"names\\\":[\\\"alert\\\"]\\n}\\n\"}]");
}
public void testJsonStreamBothFlag() {
String inputString = "[{\"src\": \"alert('foo');\", \"path\":\"foo.js\"}]";
args.add("--json_streams=BOTH");
args.add("--js_output_file=bar.js");
CommandLineRunner runner =
new CommandLineRunner(
args.toArray(new String[] {}),
new ByteArrayInputStream(inputString.getBytes(UTF_8)),
new PrintStream(outReader),
new PrintStream(errReader));
lastCompiler = runner.getCompiler();
try {
runner.doRun();
} catch (IOException e) {
e.printStackTrace();
fail("Unexpected exception " + e);
}
String output = new String(outReader.toByteArray(), UTF_8);
assertThat(output).isEqualTo("[{\"src\":\"alert(\\\"foo\\\");\\n\","
+ "\"path\":\"bar.js\",\"source_map\":\"{\\n\\\"version\\\":3,"
+ "\\n\\\"file\\\":\\\"bar.js\\\",\\n\\\"lineCount\\\":1,"
+ "\\n\\\"mappings\\\":\\\"AAAAA,KAAA,CAAM,KAAN;\\\","
+ "\\n\\\"sources\\\":[\\\"foo.js\\\"],"
+ "\\n\\\"names\\\":[\\\"alert\\\"]\\n}\\n\"}]");
}
public void testJsonStreamSourceMap() {
String inputSourceMap =
"{\n"
+ "\"version\":3,\n"
+ "\"file\":\"one.out.js\",\n"
+ "\"lineCount\":1,\n"
+ "\"mappings\":\"AAAAA,QAASA,IAAG,CAACC,CAAD,CAAI,CACdC,"
+ "OAAAF,IAAA,CAAYC,CAAZ,CADc,CAGhBD,GAAA,CAAI,QAAJ;\",\n"
+ "\"sources\":[\"one.js\"],\n"
+ "\"names\":[\"log\",\"a\",\"console\"]\n"
+ "}";
inputSourceMap = inputSourceMap.replace("\"", "\\\"");
String inputString =
"[{"
+ "\"src\": \"function log(a){console.log(a)}log(\\\"one.js\\\");\", "
+ "\"path\":\"one.out.js\", "
+ "\"sourceMap\": \""
+ inputSourceMap
+ "\" }]";
args.add("--json_streams=BOTH");
args.add("--js_output_file=bar.js");
args.add("--apply_input_source_maps");
CommandLineRunner runner =
new CommandLineRunner(
args.toArray(new String[] {}),
new ByteArrayInputStream(inputString.getBytes(UTF_8)),
new PrintStream(outReader),
new PrintStream(errReader));
lastCompiler = runner.getCompiler();
try {
runner.doRun();
} catch (IOException e) {
e.printStackTrace();
fail("Unexpected exception " + e);
}
String output = new String(outReader.toByteArray(), UTF_8);
assertThat(output)
.isEqualTo(
"[{"
+ "\"src\":\"function log(a){console.log(a)}log(\\\"one.js\\\");\\n\","
+ "\"path\":\"bar.js\","
+ "\"source_map\":\"{\\n\\\"version\\\":3,\\n\\\"file\\\":\\\"bar.js\\\",\\n"
+ "\\\"lineCount\\\":1,\\n\\\"mappings\\\":\\\"AAAAA,QAASA,IAAGC,CAACC,CAADD,CAAIA,"
+ "CACdE,OAAAA,IAAAA,CAAYD,CAAZC,CADcF,CAGhBD,GAAAA,CAAIC,QAAJD;\\\",\\n"
+ "\\\"sources\\\":[\\\"one.js\\\"],\\n\\\"names\\\":[\\\"log\\\",\\\"\\\","
+ "\\\"a\\\",\\\"console\\\"]\\n}\\n\"}]");
}
public void testOutputModuleNaming() {
String inputString = "[{\"src\": \"alert('foo');\", \"path\":\"foo.js\"}]";
args.add("--json_streams=BOTH");
args.add("--module=foo--bar.baz:1");
CommandLineRunner runner =
new CommandLineRunner(
args.toArray(new String[] {}),
new ByteArrayInputStream(inputString.getBytes(UTF_8)),
new PrintStream(outReader),
new PrintStream(errReader));
lastCompiler = runner.getCompiler();
try {
runner.doRun();
} catch (IOException e) {
e.printStackTrace();
fail("Unexpected exception " + e);
}
String output = new String(outReader.toByteArray(), UTF_8);
assertThat(output).isEqualTo("[{\"src\":\"alert(\\\"foo\\\");\\n\","
+ "\"path\":\"./foo--bar.baz.js\",\"source_map\":\"{\\n\\\"version\\\":3,"
+ "\\n\\\"file\\\":\\\"./foo--bar.baz.js\\\",\\n\\\"lineCount\\\":1,"
+ "\\n\\\"mappings\\\":\\\"AAAAA,KAAA,CAAM,KAAN;\\\","
+ "\\n\\\"sources\\\":[\\\"foo.js\\\"],"
+ "\\n\\\"names\\\":[\\\"alert\\\"]\\n}\\n\"}]");
}
public void testAssumeFunctionWrapper() {
args.add("--compilation_level=SIMPLE_OPTIMIZATIONS");
args.add("--assume_function_wrapper");
// remove used vars enabled
test(
"var someName = function(a) {};",
"");
// function inlining enabled.
test(
"var someName = function() {return 'hi'};alert(someName())",
"alert('hi')");
// renaming enabled, and collapse anonymous functions enabled
test(
"var someName = function() {return 'hi'};alert(someName);alert(someName)",
"function a() {return 'hi'}alert(a);alert(a)");
}
/* Helper functions */
private void testSame(String original) {
testSame(new String[] { original });
}
private void testSame(String[] original) {
test(original, original);
}
private void test(String original, String compiled) {
test(new String[] { original }, new String[] { compiled });
}
/**
* Asserts that when compiling with the given compiler options,
* {@code original} is transformed into {@code compiled}.
*/
private void test(String[] original, String[] compiled) {
test(original, compiled, null);
}
/**
* Asserts that when compiling with the given compiler options,
* {@code original} is transformed into {@code compiled}.
* If {@code warning} is non-null, we will also check if the given
* warning type was emitted.
*/
private void test(String[] original, String[] compiled, DiagnosticType warning) {
exitCodes.clear();
Compiler compiler = compile(original);
if (warning == null) {
assertEquals("Expected no warnings or errors\n" +
"Errors: \n" + Joiner.on("\n").join(compiler.getErrors()) +
"Warnings: \n" + Joiner.on("\n").join(compiler.getWarnings()),
0, compiler.getErrors().length + compiler.getWarnings().length);
} else {
assertThat(compiler.getWarnings()).hasLength(1);
assertThat(compiler.getWarnings()[0].getType()).isEqualTo(warning);
}
Node root = compiler.getRoot().getLastChild();
if (useStringComparison) {
assertThat(compiler.toSource()).isEqualTo(Joiner.on("").join(compiled));
} else {
Node expectedRoot = parse(compiled);
String explanation = expectedRoot.checkTreeEquals(root);
assertNull("\nExpected: " + compiler.toSource(expectedRoot) +
"\nResult: " + compiler.toSource(root) +
"\n" + explanation, explanation);
}
assertThat(exitCodes).containsExactly(0);
}
/**
* Asserts that when compiling, there is an error or warning.
*/
private void test(String original, DiagnosticType warning) {
test(new String[] { original }, warning);
}
private void test(String original, String expected, DiagnosticType warning) {
test(new String[] { original }, new String[] { expected }, warning);
}
/**
* Asserts that when compiling, there is an error or warning.
*/
private void test(String[] original, DiagnosticType warning) {
Compiler compiler = compile(original);
assertEquals("Expected exactly one warning or error " +
"\nErrors: \n" + Joiner.on("\n").join(compiler.getErrors()) +
"\nWarnings: \n" + Joiner.on("\n").join(compiler.getWarnings()),
1, compiler.getErrors().length + compiler.getWarnings().length);
assertThat(exitCodes).isNotEmpty();
int lastExitCode = Iterables.getLast(exitCodes);
if (compiler.getErrors().length > 0) {
assertThat(compiler.getErrors()).hasLength(1);
assertThat(compiler.getErrors()[0].getType()).isEqualTo(warning);
assertThat(lastExitCode).isEqualTo(1);
} else {
assertThat(compiler.getWarnings()).hasLength(1);
assertThat(compiler.getWarnings()[0].getType()).isEqualTo(warning);
assertThat(lastExitCode).isEqualTo(0);
}
}
private CommandLineRunner createCommandLineRunner(String[] original) {
for (int i = 0; i < original.length; i++) {
args.add("--js");
args.add("/path/to/input" + i + ".js");
if (useModules == ModulePattern.CHAIN) {
args.add("--module");
args.add("m" + i + ":1" + (i > 0 ? (":m" + (i - 1)) : ""));
} else if (useModules == ModulePattern.STAR) {
args.add("--module");
args.add("m" + i + ":1" + (i > 0 ? ":m0" : ""));
}
}
if (lastArg != null) {
args.add(lastArg);
}
String[] argStrings = args.toArray(new String[] {});
return new CommandLineRunner(
argStrings,
new PrintStream(outReader),
new PrintStream(errReader));
}
private static FlagEntry<JsSourceType> createZipFile(Map<String, String> entryContentsByName)
throws IOException {
File tempZipFile = File.createTempFile("testdata", ".js.zip",
java.nio.file.Files.createTempDirectory("jscomp").toFile());
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(tempZipFile))) {
for (Entry<String, String> entry : entryContentsByName.entrySet()) {
zipOutputStream.putNextEntry(new ZipEntry(entry.getKey()));
zipOutputStream.write(entry.getValue().getBytes(java.nio.charset.StandardCharsets.UTF_8));
}
}
return new FlagEntry<>(JsSourceType.JS_ZIP, tempZipFile.getAbsolutePath());
}
private static FlagEntry<JsSourceType> createJsFile(String filename, String fileContent)
throws IOException {
File tempJsFile = File.createTempFile(filename, ".js",
java.nio.file.Files.createTempDirectory("jscomp").toFile());
try (FileOutputStream fileOutputStream = new FileOutputStream(tempJsFile)) {
fileOutputStream.write(fileContent.getBytes(java.nio.charset.StandardCharsets.UTF_8));
}
return new FlagEntry<>(JsSourceType.JS, tempJsFile.getAbsolutePath());
}
/**
* Helper for compiling from zip and js files and checking output string.
* @param expectedOutput string representation of expected output.
* @param entries entries of flags for zip and js files containing source to compile.
*/
@SafeVarargs
private final void compileFiles(String expectedOutput, FlagEntry<JsSourceType>... entries) {
setupFlags(entries);
compileArgs(expectedOutput, null);
}
@SafeVarargs
private final void compileFilesError(
DiagnosticType expectedError, FlagEntry<JsSourceType>... entries) {
setupFlags(entries);
compileArgs("", expectedError);
}
@SafeVarargs
private final void setupFlags(FlagEntry<JsSourceType>... entries) {
for (FlagEntry<JsSourceType> entry : entries) {
args.add("--" + entry.getFlag().flagName + "=" + entry.getValue());
}
}
/**
* Helper for compiling js files and checking output string, using a single --js flag.
*
* @param expectedOutput string representation of expected output.
* @param entries entries of flags for js files containing source to compile.
*/
@SafeVarargs
private final void compileJsFiles(String expectedOutput, FlagEntry<JsSourceType>... entries) {
args.add("--js");
for (FlagEntry<JsSourceType> entry : entries) {
args.add(entry.getValue());
}
compileArgs(expectedOutput, null);
}
private void compileArgs(String expectedOutput, DiagnosticType expectedError) {
String[] argStrings = args.toArray(new String[] {});
CommandLineRunner runner =
new CommandLineRunner(argStrings, new PrintStream(outReader), new PrintStream(errReader));
lastCompiler = runner.getCompiler();
try {
runner.doRun();
} catch (IOException e) {
e.printStackTrace();
fail("Unexpected exception " + e);
}
Compiler compiler = runner.getCompiler();
String output = compiler.toSource();
if (expectedError == null) {
assertThat(compiler.getErrors()).isEmpty();
assertThat(compiler.getWarnings()).isEmpty();
assertThat(output).isEqualTo(expectedOutput);
} else {
assertThat(compiler.getErrors()).hasLength(1);
assertError(compiler.getErrors()[0]).hasType(expectedError);
}
}
private Compiler compile(String[] original) {
CommandLineRunner runner = createCommandLineRunner(original);
if (!runner.shouldRunCompiler()) {
assertThat(runner.hasErrors()).isTrue();
fail(new String(errReader.toByteArray(), UTF_8));
}
Supplier<List<SourceFile>> inputsSupplier = null;
Supplier<List<JSModule>> modulesSupplier = null;
if (useModules == ModulePattern.NONE) {
List<SourceFile> inputs = new ArrayList<>();
for (int i = 0; i < original.length; i++) {
inputs.add(SourceFile.fromCode(getFilename(i), original[i]));
}
inputsSupplier = Suppliers.ofInstance(inputs);
} else if (useModules == ModulePattern.STAR) {
modulesSupplier = Suppliers.<List<JSModule>>ofInstance(
ImmutableList.copyOf(
CompilerTestCase.createModuleStar(original)));
} else if (useModules == ModulePattern.CHAIN) {
modulesSupplier = Suppliers.<List<JSModule>>ofInstance(
ImmutableList.copyOf(
CompilerTestCase.createModuleChain(original)));
} else {
throw new IllegalArgumentException("Unknown module type: " + useModules);
}
runner.enableTestMode(
Suppliers.ofInstance(externs),
inputsSupplier,
modulesSupplier,
new Function<Integer, Void>() {
@Override
public Void apply(Integer code) {
exitCodes.add(code);
return null;
}
});
runner.run();
lastCompiler = runner.getCompiler();
lastCommandLineRunner = runner;
return lastCompiler;
}
private Node parse(String[] original) {
String[] argStrings = args.toArray(new String[] {});
CommandLineRunner runner = new CommandLineRunner(argStrings);
Compiler compiler = runner.createCompiler();
List<SourceFile> inputs = new ArrayList<>();
for (int i = 0; i < original.length; i++) {
inputs.add(SourceFile.fromCode(getFilename(i), original[i]));
}
CompilerOptions options = new CompilerOptions();
// ECMASCRIPT5 is the most forgiving.
options.setLanguageIn(LanguageMode.ECMASCRIPT5);
compiler.init(externs, inputs, options);
Node all = compiler.parseInputs();
Preconditions.checkState(compiler.getErrorCount() == 0);
Preconditions.checkNotNull(all);
Node n = all.getLastChild();
return n;
}
private void setFilename(int i, String filename) {
this.filenames.put(i, filename);
}
private String getFilename(int i) {
if (filenames.isEmpty()) {
return "input" + i;
}
String name = filenames.get(i);
Preconditions.checkState(name != null && !name.isEmpty());
return name;
}
}