protected void testExternChanges(String extern, String input, String expectedExtern) { Compiler compiler = createCompiler(); CompilerOptions options = getOptions(); compiler.init( ImmutableList.of(SourceFile.fromCode("extern", extern)), ImmutableList.of(SourceFile.fromCode("input", input)), options); compiler.parseInputs(); assertFalse(compiler.hasErrors()); Node externsAndJs = compiler.getRoot(); Node root = externsAndJs.getLastChild(); Node externs = externsAndJs.getFirstChild(); Node expected = compiler.parseTestCode(expectedExtern); assertFalse(compiler.hasErrors()); (getProcessor(compiler)).process(externs, root); String externsCode = compiler.toSource(externs); String expectedCode = compiler.toSource(expected); assertEquals(expectedCode, externsCode); }
public void testPackagePrivateAccessForProperties3() { testSame( ImmutableList.of( SourceFile.fromCode( "foo/bar.js", "/** @constructor */ function Foo() {}" + "/** @package */ Foo.prototype.bar = function() {}; (new Foo).bar();"), SourceFile.fromCode("foo/baz.js", "Foo.prototype.baz = function() { this.bar(); };"))); }
public void testSortInputs() throws Exception { SourceFile a = SourceFile.fromCode("a.js", "require('b');require('c')"); SourceFile b = SourceFile.fromCode("b.js", "require('d')"); SourceFile c = SourceFile.fromCode("c.js", "require('d')"); SourceFile d = SourceFile.fromCode("d.js", "1;"); assertSortedInputs(ImmutableList.of(d, b, c, a), ImmutableList.of(a, b, c, d)); assertSortedInputs(ImmutableList.of(d, b, c, a), ImmutableList.of(d, b, c, a)); assertSortedInputs(ImmutableList.of(d, c, b, a), ImmutableList.of(d, c, b, a)); assertSortedInputs(ImmutableList.of(d, b, c, a), ImmutableList.of(d, a, b, c)); }
public void testNoPackagePrivateAccessForProperties3() { test( ImmutableList.of( SourceFile.fromCode( "foo/bar.js", "/** @constructor */ function Foo() {} " + "/** @package */ Foo.prototype.bar = function() {};"), SourceFile.fromCode( "baz/quux.js", "/** @constructor */ function OtherFoo() { (new Foo).bar(); }")), null, BAD_PACKAGE_PROPERTY_ACCESS); }
public void testNoPackagePrivateAccessForProperties5() { test( ImmutableList.of( SourceFile.fromCode( "foo/bar.js", "/** @constructor */ function Foo() {} " + "/** @package */ Foo.prototype.bar = function() {};"), SourceFile.fromCode( "baz/quux.js", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {};" + "SubFoo.prototype.baz = function() { this.bar(); }")), null, BAD_PACKAGE_PROPERTY_ACCESS); }
/** Parses expected JS inputs and returns the root of the parse tree. */ protected Node parseExpectedJs(String[] expected) { List<SourceFile> inputs = Lists.newArrayList(); for (int i = 0; i < expected.length; i++) { inputs.add(SourceFile.fromCode("expected" + i, expected[i])); } return parseExpectedJs(inputs); }
private void splitFiles(String[] input) { Compiler compiler = new Compiler(); List<SourceFile> files = Lists.newArrayList(); for (int i = 0; i < input.length; i++) { files.add(SourceFile.fromCode("file" + i, input[i])); } compiler.init(ImmutableList.<SourceFile>of(), files, new CompilerOptions()); compiler.parse(); Node original = compiler.getRoot(); Node root = original.cloneTree(); AstParallelizer parallelizer = AstParallelizer.createNewFileLevelAstParallelizer(root); List<Node> forest = parallelizer.split(); assertEquals(input.length, forest.size()); int i = 0; for (Node n : forest) { Node tree = compiler.parseTestCode(input[i++]); assertEquals(compiler.toSource(tree), compiler.toSource(n)); } parallelizer.join(); assertTrue(original.isEquivalentTo(root)); }
public void testNoPackagePrivateAccessForProperties6() { // Overriding a private property with a non-package-private property // in a different file causes problems. test( ImmutableList.of( SourceFile.fromCode( "foo/bar.js", "/** @constructor */ function Foo() {} " + "/** @package */ Foo.prototype.bar = function() {};"), SourceFile.fromCode( "baz/quux.js", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {};" + "SubFoo.prototype.bar = function() {};")), null, BAD_PACKAGE_PROPERTY_ACCESS); }
public void testModuleName() { setFilename("foo/bar"); testModules( "var name = require('other');", "goog.provide('module$foo$bar'); var module$foo$bar = {};" + "goog.require('module$other');" + "var name$$module$foo$bar = module$other;"); ProcessEs6ModulesTest.testModules( this, ImmutableList.of( SourceFile.fromCode("foo/name.js", ""), SourceFile.fromCode("foo/bar.js", "var name = require('./name');")), "goog.provide('module$foo$bar');" + "var module$foo$bar = {};" + "goog.require('module$foo$name');" + "var name$$module$foo$bar = module$foo$name;"); }
/** * Generates a list of modules from a list of inputs. Does not generate any dependencies between * the modules. */ static JSModule[] createModules(String... inputs) { JSModule[] modules = new JSModule[inputs.length]; for (int i = 0; i < inputs.length; i++) { JSModule module = modules[i] = new JSModule("m" + i); module.add(SourceFile.fromCode("i" + i, inputs[i])); } return modules; }
public void testNoPackagePrivateAccessForProperties7() { // It's OK to override a package-private property with a // non-package-private property in the same file, but you'll get // yelled at when you try to use it. test( ImmutableList.of( SourceFile.fromCode( "foo/bar.js", "/** @constructor */ function Foo() {} " + "/** @package */ Foo.prototype.bar = function() {};" + "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {};" + "SubFoo.prototype.bar = function() {};"), SourceFile.fromCode( "baz/quux.js", "SubFoo.prototype.baz = function() { this.bar(); }")), null, BAD_PACKAGE_PROPERTY_ACCESS); }
/** * Verifies that the compiler pass's JS output is the same as its input and (optionally) that an * expected warning and description is issued. * * @param externs Externs input * @param js Input and output * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, or null if no warning is expected * or if the warning's description should not be examined */ public void testSame( String externs, String js, DiagnosticType type, String description, boolean error) { List<SourceFile> externsInputs = ImmutableList.of(SourceFile.fromCode("externs", externs)); if (error) { test(externsInputs, js, js, type, null, description); } else { test(externsInputs, js, js, null, type, description); } }
public void testFileNotOnOnlyApplyToRegexpIsNotChecked() { configuration = "requirement: {\n" + " type: BANNED_NAME\n" + " value: 'eval'\n" + " error_message: 'eval is not allowed'\n" + " only_apply_to_regexp: 'test.js$'\n " + "}"; testSame(ImmutableList.of(SourceFile.fromCode("bar.js", "eval()"))); }
protected void testExternChanges(String extern, String input, String expectedExtern) { Compiler compiler = createCompiler(); CompilerOptions options = getOptions(); compiler.init( ImmutableList.of(SourceFile.fromCode("extern", extern)), ImmutableList.of(SourceFile.fromCode("input", input)), options); compiler.parseInputs(); assertFalse(compiler.hasErrors()); Node externsAndJs = compiler.getRoot(); Node root = externsAndJs.getLastChild(); Node externs = externsAndJs.getFirstChild(); Node expected = compiler.parseTestCode(expectedExtern); assertFalse(compiler.hasErrors()); (getProcessor(compiler)).process(externs, root); if (compareAsTree) { // Expected output parsed without implied block. Preconditions.checkState(externs.isBlock()); Preconditions.checkState(compareJsDoc); Preconditions.checkState( externs.hasOneChild(), "Compare as tree only works when output has a single script."); externs = externs.getFirstChild(); String explanation = expected.checkTreeEqualsIncludingJsDoc(externs); assertNull( "\nExpected: " + compiler.toSource(expected) + "\nResult: " + compiler.toSource(externs) + "\n" + explanation, explanation); } else { String externsCode = compiler.toSource(externs); String expectedCode = compiler.toSource(expected); assertEquals(expectedCode, externsCode); } }
/** * Verifies that the compiler pass's JS output matches the expected output and (optionally) that * an expected warning is issued. Or, if an error is expected, this method just verifies that the * error is encountered. * * @param js Inputs * @param expected Expected JS output * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, or null if no warning is expected * or if the warning's description should not be examined */ public void test( String[] js, String[] expected, DiagnosticType error, DiagnosticType warning, String description) { List<SourceFile> inputs = Lists.newArrayList(); for (int i = 0; i < js.length; i++) { inputs.add(SourceFile.fromCode("input" + i, js[i])); } test(inputs, expected, error, warning, description); }
/** * Verifies that the compiler pass's JS output matches the expected output and (optionally) that * an expected warning is issued. Or, if an error is expected, this method just verifies that the * error is encountered. * * @param externs Externs input * @param js Input * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, or null if no warning is expected * or if the warning's description should not be examined */ public void test( String externs, String js, String expected, DiagnosticType error, DiagnosticType warning, String description) { SourceFile externsFile = SourceFile.fromCode("externs", externs); externsFile.setIsExtern(true); List<SourceFile> externsInputs = ImmutableList.of(externsFile); test(externsInputs, js, expected, error, warning, description); }
public void testWithoutExports() { setFilename("test"); testModules( "var name = require('other');" + "name()", "goog.provide('module$test');" + "var module$test = {};" + "goog.require('module$other');" + "var name$$module$test = module$other;" + "name$$module$test();"); setFilename("test/sub"); ProcessEs6ModulesTest.testModules( this, ImmutableList.of( SourceFile.fromCode("mod/name.js", ""), SourceFile.fromCode( "test/sub.js", "var name = require('mod/name');" + "(function() { name(); })();")), "goog.provide('module$test$sub');" + "var module$test$sub = {};" + "goog.require('module$mod$name');" + "var name$$module$test$sub = module$mod$name;" + "(function() { name$$module$test$sub(); })();"); }
public void testPackagePrivateAccessForProperties5() { test( ImmutableList.of( SourceFile.fromCode( "foo/bar.js", "/** @constructor */\n" + "function Parent () {\n" + " /** @package */\n" + " this.prop = 'foo';\n" + "};"), SourceFile.fromCode( "baz/quux.js", "/**\n" + " * @constructor\n" + " * @extends {Parent}\n" + " */\n" + "function Child() {\n" + " this.prop = 'asdf';\n" + "}\n" + "Child.prototype = new Parent();")), null, BAD_PACKAGE_PROPERTY_ACCESS); }
/** * Verifies that the compiler pass's JS output matches the expected output and (optionally) that * an expected warning is issued. Or, if an error is expected, this method just verifies that the * error is encountered. * * @param externs Externs inputs * @param js Input * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, or null if no warning is expected * or if the warning's description should not be examined */ public void test( List<SourceFile> externs, String js, String expected, DiagnosticType error, DiagnosticType warning, String description) { test( externs, ImmutableList.of(SourceFile.fromCode(filename, js)), expected, error, warning, description); }
public void testSpecifyingWhitelistAndOnlyApplyToIsRuntimeError() { configuration = "requirement: {\n" + " type: BANNED_NAME\n" + " value: 'eval'\n" + " error_message: 'eval is not allowed'\n" + " whitelist: 'blah'\n" + " only_apply_to_regexp: 'test.js$'\n " + "}"; try { testSame(ImmutableList.of(SourceFile.fromCode("bar.js", "eval()"))); fail("expected IllegalArgumentException"); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class); } }
public void testFileOnOnlyApplyToIsChecked() { configuration = "requirement: {\n" + " type: BANNED_NAME\n" + " value: 'eval'\n" + " error_message: 'eval is not allowed'\n" + " only_apply_to: 'foo.js'\n " + "}"; ImmutableList<SourceFile> input = ImmutableList.of(SourceFile.fromCode("foo.js", "eval()")); test( input, input, null, CheckConformance.CONFORMANCE_VIOLATION, "Violation: eval is not allowed"); }
/** * Verifies that the compiler pass's JS output matches the expected output and (optionally) that * an expected warning is issued. Or, if an error is expected, this method just verified that the * error is encountered. * * @param compiler A compiler that has been initialized via {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ private void test( Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning, String description) { if (expected == null) { test(compiler, (List<SourceFile>) null, error, warning, description); } else { List<SourceFile> inputs = Lists.newArrayList(); for (int i = 0; i < expected.length; i++) { inputs.add(SourceFile.fromCode("expected" + i, expected[i])); } test(compiler, inputs, error, warning, description); } }
public void testSyntheticExterns() { externs = ImmutableList.of(SourceFile.fromCode("externs", "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("--warning_level=VERBOSE"); test( "var theirVar = {}; var myVar = {}; var myVar = {};", SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); }
private Node parse(String[] original) { String[] argStrings = args.toArray(new String[] {}); CommandLineRunner runner = new CommandLineRunner(argStrings); Compiler compiler = runner.createCompiler(); List<SourceFile> inputs = Lists.newArrayList(); 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 assertSortedInputs(List<SourceFile> expected, List<SourceFile> shuffled) throws Exception { Compiler compiler = new Compiler(System.err); compiler.initCompilerOptionsIfTesting(); compiler.getOptions().setProcessCommonJSModules(true); compiler .getOptions() .dependencyOptions .setEntryPoints(ImmutableList.of(ES6ModuleLoader.toModuleName(URI.create("a")))); compiler.compile( ImmutableList.of(SourceFile.fromCode("externs.js", "")), shuffled, compiler.getOptions()); List<SourceFile> result = new ArrayList<>(); for (JSModule m : compiler.getModuleGraph().getAllModules()) { for (CompilerInput i : m.getInputs()) { result.add(i.getSourceFile()); } } assertEquals(expected, result); }
private String printHelper(String js, boolean runProcessor) { Compiler compiler = createCompiler(); CompilerOptions options = getOptions(); compiler.init( ImmutableList.<SourceFile>of(), ImmutableList.of(SourceFile.fromCode("testcode", js)), options); Node root = compiler.parseInputs(); assertNotNull( "Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()) + "\nEXPR: " + js, root); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); if (runProcessor) { getProcessor(compiler).process(externsRoot, mainRoot); } return compiler.toSource(mainRoot); }
private Compiler compile(String[] original) { CommandLineRunner runner = createCommandLineRunner(original); assertTrue(new String(errReader.toByteArray()), runner.shouldRunCompiler()); Supplier<List<SourceFile>> inputsSupplier = null; Supplier<List<JSModule>> modulesSupplier = null; if (useModules == ModulePattern.NONE) { List<SourceFile> inputs = Lists.newArrayList(); 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( Lists.<JSModule>newArrayList(CompilerTestCase.createModuleStar(original))); } else if (useModules == ModulePattern.CHAIN) { modulesSupplier = Suppliers.<List<JSModule>>ofInstance( Lists.<JSModule>newArrayList(CompilerTestCase.createModuleChain(original))); } else { throw new IllegalArgumentException("Unknown module type: " + useModules); } runner.enableTestMode( Suppliers.<List<SourceFile>>ofInstance(externs), inputsSupplier, modulesSupplier, new Function<Integer, Boolean>() { @Override public Boolean apply(Integer code) { return exitCodes.add(code); } }); runner.run(); lastCompiler = runner.getCompiler(); lastCommandLineRunner = runner; return lastCompiler; }
/** * Tests for {@link CommandLineRunner}. * * @author [email protected] (Nick Santos) */ public class CommandLineRunnerTest extends TestCase { 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 List<String> args = Lists.newArrayList(); /** Externs for the test */ private final List<SourceFile> DEFAULT_EXTERNS = ImmutableList.of( SourceFile.fromCode( "externs", "var arguments;" + "/**\n" + " * @constructor\n" + " * @param {...*} var_args\n" + " * @nosideeffects\n" + " * @throws {Error}\n" + " */\n" + "function Function(var_args) {}\n" + "/**\n" + " * @param {...*} var_args\n" + " * @return {*}\n" + " */\n" + "Function.prototype.call = function(var_args) {};" + "/**\n" + " * @constructor\n" + " * @param {...*} var_args\n" + " * @return {!Array}\n" + " */\n" + "function Array(var_args) {}" + "/**\n" + " * @param {*=} opt_begin\n" + " * @param {*=} opt_end\n" + " * @return {!Array}\n" + " * @this {Object}\n" + " */\n" + "Array.prototype.slice = function(opt_begin, opt_end) {};" + "/** @constructor */ function Window() {}\n" + "/** @type {string} */ Window.prototype.name;\n" + "/** @type {Window} */ var window;" + "/** @constructor */ function Element() {}" + "Element.prototype.offsetWidth;" + "/** @nosideeffects */ function noSideEffects() {}\n" + "/** @param {...*} x */ function alert(x) {}\n")); private List<SourceFile> externs; @Override public void setUp() throws Exception { super.setUp(); externs = DEFAULT_EXTERNS; filenames = Maps.newHashMap(); lastCompiler = null; lastArg = null; outReader = new ByteArrayOutputStream(); errReader = new ByteArrayOutputStream(); useStringComparison = false; useModules = ModulePattern.NONE; args.clear(); exitCodes = Lists.newArrayList(); } @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() {}"); } 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 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 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"); 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"); test( "/** @constructor */ function F() { this.a = 0; }" + "F.prototype.inc = function() { this.a++; return 10; };" + "F.prototype.bar = function() { " + " var c = 3; var val = inc(); this.a += val + c;" + "};" + "window['f'] = new F();" + "window['f']['bar'] = window['f'].bar;", "function a(){ this.a = 0; }" + "a.prototype.b = function(){ var b=inc(); this.a += b + 3; };" + "window.f = new a;" + "window.f.bar = window.f.b"); } public void testTypedAdvanced() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); args.add("--use_types_for_optimization"); 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 testTypeCheckingOnWithVerbose() { args.add("--warning_level=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.TYPE_PARSE_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("--warning_level=VERBOSE"); test("x = 3;", VarCheck.UNDEFINED_VAR_ERROR); test("var y; var y;", SyntacticScopeCreator.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); assertTrue(lastCompiler.hasHaltingErrors()); } 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 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';"); } ////////////////////////////////////////////////////////////////////////////// // Integration tests public void testIssue70a() { test("function foo({}) {}", RhinoErrorReporter.PARSE_ERROR); } public void testIssue70b() { test("function foo([]) {}", RhinoErrorReporter.PARSE_ERROR); } public void testIssue81() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); 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("--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"); 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"); assertFalse(createCommandLineRunner(new String[] {"function f() {}"}).shouldRunCompiler()); } public void testExternsLifting1() throws Exception { String code = "/** @externs */ function f() {}"; test(new String[] {code}, new String[] {}); assertEquals(2, lastCompiler.getExternsForTesting().size()); CompilerInput extern = lastCompiler.getExternsForTesting().get(1); assertNull(extern.getModule()); assertTrue(extern.isExtern()); assertEquals(code, extern.getCode()); assertEquals(1, lastCompiler.getInputsForTesting().size()); CompilerInput input = lastCompiler.getInputsForTesting().get(0); assertNotNull(input.getModule()); assertFalse(input.isExtern()); assertEquals("", input.getCode()); } 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("--manage_closure_dependencies=true"); 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("--manage_closure_dependencies=true"); 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');" }, JSModule.CIRCULAR_DEPENDENCY_ERROR); } public void testSourceSortingCircularDeps2() { args.add("--manage_closure_dependencies=true"); 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');" }, JSModule.CIRCULAR_DEPENDENCY_ERROR); } public void testSourcePruningOn1() { args.add("--manage_closure_dependencies=true"); test( new String[] { "goog.require('beer');", "goog.provide('beer');", "goog.provide('scotch'); var x = 3;" }, new String[] {"var beer = {};", ""}); } public void testSourcePruningOn2() { args.add("--closure_entry_point=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("--closure_entry_point=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("--closure_entry_point=scotch"); args.add("--closure_entry_point=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("--closure_entry_point=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("--closure_entry_point=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("--manage_closure_dependencies=true"); test( new String[] { "var COMPILED = false;", }, new String[] { "var COMPILED = !1;", }); } public void testSourcePruningOn8() { args.add("--only_closure_dependencies"); args.add("--closure_entry_point=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("--only_closure_dependencies"); args.add("--closure_entry_point=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("--manage_closure_dependencies"); 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("--manage_closure_dependencies=true"); 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.TYPE_PARSE_ERROR); } public void testOnlyClosureDependenciesEmptyEntryPoints() throws Exception { // Prevents this from trying to load externs.zip args.add("--use_only_custom_externs=true"); args.add("--only_closure_dependencies=true"); try { CommandLineRunner runner = createCommandLineRunner(new String[0]); runner.doRun(); fail("Expected FlagUsageException"); } catch (FlagUsageException e) { assertTrue(e.getMessage(), e.getMessage().contains("only_closure_dependencies")); } } public void testOnlyClosureDependenciesOneEntryPoint() throws Exception { args.add("--only_closure_dependencies=true"); args.add("--closure_entry_point=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;"); assertEquals( "/path/to/out.js.map", lastCommandLineRunner.expandSourceMapPath(lastCompiler.getOptions(), null)); } 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;"}); assertEquals( "foo.map", lastCommandLineRunner.expandSourceMapPath(lastCompiler.getOptions(), null)); } 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;"}); assertEquals( "foo_m0.js.map", lastCommandLineRunner.expandSourceMapPath( lastCompiler.getOptions(), lastCompiler.getModuleGraph().getRootModule())); } public void testSourceMapFormat1() { args.add("--js_output_file"); args.add("/path/to/out.js"); testSame("var x = 3;"); assertEquals(SourceMap.Format.DEFAULT, lastCompiler.getOptions().sourceMapFormat); } public void testSourceMapFormat2() { args.add("--js_output_file"); args.add("/path/to/out.js"); args.add("--source_map_format=V3"); testSame("var x = 3;"); assertEquals(SourceMap.Format.V3, lastCompiler.getOptions().sourceMapFormat); } 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()); assertEquals("var x=3; // m0.js\n", builder.toString()); } public void testCharSetExpansion() { testSame(""); assertEquals("US-ASCII", lastCompiler.getOptions().outputCharset); args.add("--charset=UTF-8"); testSame(""); assertEquals("UTF-8", lastCompiler.getOptions().outputCharset); } 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); assertEquals( "{m0}\n" + "i0\n" + "\n" + "{m1:m0}\n" + "i1\n" + "\n" + "{m2:m1}\n" + "i2\n" + "\n" + "{m3:m2}\n" + "i3\n", builder.toString()); } 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); assertEquals( "{m0}\n" + "i0\n" + "\n" + "{m1:m0}\n" + "i1\n" + "\n" + "{m2:m0}\n" + "i2\n" + "\n" + "{m3:m0}\n" + "i3\n", builder.toString()); } 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); assertTrue(builder.toString().indexOf("transitive-dependencies") != -1); } public void testVersionFlag() { args.add("--version"); testSame(""); assertEquals( 0, new String(errReader.toByteArray()) .indexOf("Closure Compiler (http://code.google.com/closure/compiler)\n" + "Version: ")); } public void testVersionFlag2() { lastArg = "--version"; testSame(""); assertEquals( 0, new String(errReader.toByteArray()) .indexOf("Closure Compiler (http://code.google.com/closure/compiler)\n" + "Version: ")); } public void testPrintAstFlag() { args.add("--print_ast=true"); testSame(""); assertEquals( "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"BLOCK\"];\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", new String(outReader.toByteArray())); } public void testSyntheticExterns() { externs = ImmutableList.of(SourceFile.fromCode("externs", "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("--warning_level=VERBOSE"); test( "var theirVar = {}; var myVar = {}; var myVar = {};", SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); } public void testGoogAssertStripping() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); 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 testGenerateExports() { args.add("--generate_exports=true"); test( "/** @export */ foo.prototype.x = function() {};", "foo.prototype.x=function(){};" + "goog.exportSymbol(\"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;"}); assertEquals(2, compiler.getErrors().length); } public void testES3ByDefault() { test("var x = f.function", "var x = f['function']", RhinoErrorReporter.INVALID_ES3_PROP_NAME); } public void testES5ChecksByDefault() { testSame("var x = 3; delete x;"); } 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"); 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"); Compiler compiler = compile(new String[] {"var x = f.function"}); String outputSource = compiler.toSource(); assertEquals("'use strict'", outputSource.substring(0, 12)); } public void testES5StrictUseStrictMultipleInputs() { args.add("--language_in=ECMASCRIPT5_STRICT"); Compiler compiler = compile(new String[] {"var x = f.function", "var y = f.function", "var z = f.function"}); String outputSource = compiler.toSource(); assertEquals("'use strict'", outputSource.substring(0, 12)); assertEquals(outputSource.substring(13).indexOf("'use strict'"), -1); } public void testWithKeywordDefault() { test("var x = {}; with (x) {}", ControlStructureCheck.USE_OF_WITH); } public void testWithKeywordWithEs5ChecksOff() { args.add("--jscomp_off=es5Strict"); testSame("var x = {}; with (x) {}"); } public void testNoSrCFilesWithManifest() throws IOException { args.add("--use_only_custom_externs=true"); args.add("--output_manifest=test.MF"); CommandLineRunner runner = createCommandLineRunner(new String[0]); String expectedMessage = ""; try { runner.doRun(); } catch (FlagUsageException e) { expectedMessage = e.getMessage(); } assertEquals( expectedMessage, "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})", "exports = {test: 1}"); } public void testProcessCJS() { useStringComparison = true; args.add("--process_common_js_modules"); args.add("--common_js_entry_module=foo/bar"); setFilename(0, "foo/bar.js"); String expected = "var module$foo$bar={test:1};"; test("exports.test = 1", expected); assertEquals(expected + "\n", outReader.toString()); } public void testProcessCJSWithModuleOutput() { useStringComparison = true; args.add("--process_common_js_modules"); args.add("--common_js_entry_module=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. assertEquals("", outReader.toString()); } public void testFormattingSingleQuote() { testSame("var x = '';"); assertEquals("var x=\"\";", lastCompiler.toSource()); args.add("--formatting=SINGLE_QUOTES"); testSame("var x = '';"); assertEquals("var x='';", lastCompiler.toSource()); } public void testTransformAMDAndProcessCJS() { useStringComparison = true; args.add("--transform_amd_modules"); args.add("--process_common_js_modules"); args.add("--common_js_entry_module=foo/bar"); setFilename(0, "foo/bar.js"); test("define({foo: 1})", "var module$foo$bar={},module$foo$bar={foo:1};"); } public void testModuleJSON() { useStringComparison = true; args.add("--transform_amd_modules"); args.add("--process_common_js_modules"); args.add("--common_js_entry_module=foo/bar"); args.add("--output_module_dependencies=test.json"); setFilename(0, "foo/bar.js"); test("define({foo: 1})", "var module$foo$bar={},module$foo$bar={foo:1};"); } public void testOutputSameAsInput() { args.add("--js_output_file=" + getFilename(0)); test("", AbstractCommandLineRunner.OUTPUT_SAME_AS_INPUT_ERROR); } /* 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) { 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 { assertEquals(1, compiler.getWarnings().length); assertEquals(warning, compiler.getWarnings()[0].getType()); } Node root = compiler.getRoot().getLastChild(); if (useStringComparison) { assertEquals(Joiner.on("").join(compiled), compiler.toSource()); } else { Node expectedRoot = parse(compiled); String explanation = expectedRoot.checkTreeEquals(root); assertNull( "\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(root) + "\n" + explanation, explanation); } } /** 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 " + "Errors: \n" + Joiner.on("\n").join(compiler.getErrors()) + "Warnings: \n" + Joiner.on("\n").join(compiler.getWarnings()), 1, compiler.getErrors().length + compiler.getWarnings().length); assertTrue(exitCodes.size() > 0); int lastExitCode = exitCodes.get(exitCodes.size() - 1); if (compiler.getErrors().length > 0) { assertEquals(1, compiler.getErrors().length); assertEquals(warning, compiler.getErrors()[0].getType()); assertEquals(1, lastExitCode); } else { assertEquals(1, compiler.getWarnings().length); assertEquals(warning, compiler.getWarnings()[0].getType()); assertEquals(0, lastExitCode); } } 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 Compiler compile(String[] original) { CommandLineRunner runner = createCommandLineRunner(original); assertTrue(new String(errReader.toByteArray()), runner.shouldRunCompiler()); Supplier<List<SourceFile>> inputsSupplier = null; Supplier<List<JSModule>> modulesSupplier = null; if (useModules == ModulePattern.NONE) { List<SourceFile> inputs = Lists.newArrayList(); 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( Lists.<JSModule>newArrayList(CompilerTestCase.createModuleStar(original))); } else if (useModules == ModulePattern.CHAIN) { modulesSupplier = Suppliers.<List<JSModule>>ofInstance( Lists.<JSModule>newArrayList(CompilerTestCase.createModuleChain(original))); } else { throw new IllegalArgumentException("Unknown module type: " + useModules); } runner.enableTestMode( Suppliers.<List<SourceFile>>ofInstance(externs), inputsSupplier, modulesSupplier, new Function<Integer, Boolean>() { @Override public Boolean apply(Integer code) { return exitCodes.add(code); } }); 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 = Lists.newArrayList(); 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; } return filenames.get(i); } }
private final void parseAndTypeCheck(String externs, String js) { setUp(); final CompilerOptions options = compiler.getOptions(); options.setClosurePass(true); options.setNewTypeInference(true); options.setWarningLevel(DiagnosticGroups.NEW_CHECK_TYPES_ALL_CHECKS, CheckLevel.WARNING); compiler.init( ImmutableList.of(SourceFile.fromCode("[externs]", externs)), ImmutableList.of(SourceFile.fromCode("[testcode]", js)), options); Node externsRoot = IR.block(); externsRoot.setIsSyntheticBlock(true); externsRoot.addChildToFront(compiler.getInput(new InputId("[externs]")).getAstRoot(compiler)); Node astRoot = IR.block(); astRoot.setIsSyntheticBlock(true); astRoot.addChildToFront(compiler.getInput(new InputId("[testcode]")).getAstRoot(compiler)); assertEquals( "parsing error: " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); assertEquals( "parsing warning: " + Joiner.on(", ").join(compiler.getWarnings()), 0, compiler.getWarningCount()); // Create common parent of externs and ast; needed by Es6RewriteBlockScopedDeclaration. Node block = IR.block(externsRoot, astRoot); block.setIsSyntheticBlock(true); // Run ASTValidator (new AstValidator(compiler)).validateRoot(block); DeclaredGlobalExternsOnWindow rewriteExterns = new DeclaredGlobalExternsOnWindow(compiler); passes.add(makePassFactory("globalExternsOnWindow", rewriteExterns)); ProcessClosurePrimitives closurePass = new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR, false); passes.add(makePassFactory("ProcessClosurePrimitives", closurePass)); if (options.getLanguageIn() == CompilerOptions.LanguageMode.ECMASCRIPT6_TYPED) { passes.add(makePassFactory("convertEs6TypedToEs6", new Es6TypedToEs6Converter(compiler))); } if (options.getLanguageIn().isEs6OrHigher()) { passes.add( makePassFactory( "Es6RenameVariablesInParamLists", new Es6RenameVariablesInParamLists(compiler))); passes.add( makePassFactory( "Es6SplitVariableDeclarations", new Es6SplitVariableDeclarations(compiler))); passes.add(makePassFactory("es6ConvertSuper", new Es6ConvertSuper(compiler))); passes.add(makePassFactory("convertEs6", new Es6ToEs3Converter(compiler))); passes.add( makePassFactory( "Es6RewriteBlockScopedDeclaration", new Es6RewriteBlockScopedDeclaration(compiler))); passes.add(makePassFactory("rewriteGenerators", new Es6RewriteGenerators(compiler))); passes.add(makePassFactory("Es6RuntimeLibrary", new InjectEs6RuntimeLibrary(compiler))); passes.add( makePassFactory("Es6StaticInheritance", new Es6ToEs3ClassSideInheritance(compiler))); } passes.add(makePassFactory("GlobalTypeInfo", compiler.getSymbolTable())); passes.add(makePassFactory("NewTypeInference", new NewTypeInference(compiler))); PhaseOptimizer phaseopt = new PhaseOptimizer(compiler, null, null); phaseopt.consume(passes); phaseopt.process(externsRoot, astRoot); }
/** * Constructs a test. * * @param externs Externs JS as a string * @param compareAsTree True to compare output & expected as a node tree. 99% of the time you want * to compare as a tree. There are a few special cases where you don't, like if you want to * test the code printing of "unnatural" syntax trees. For example, * <pre> * IF * IF * STATEMENT * ELSE * STATEMENT * </pre> */ protected CompilerTestCase(String externs, boolean compareAsTree) { this.externsInputs = ImmutableList.of(SourceFile.fromCode("externs", externs)); this.compareAsTree = compareAsTree; this.parseTypeInfo = false; this.compareJsDoc = true; }