/** * 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) { 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); } }
/** Parses expected JS inputs and returns the root of the parse tree. */ protected Node parseExpectedJs(List<SourceFile> inputs) { Compiler compiler = createCompiler(); compiler.init(externsInputs, inputs, getOptions()); Node root = compiler.parseInputs(); assertTrue( "Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); // Only run the normalize pass, if asked. if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } return mainRoot; }
/** Parses expected JS inputs and returns the root of the parse tree. */ protected Node parseExpectedJs(List<SourceFile> inputs) { Compiler compiler = createCompiler(); compiler.init(externsInputs, inputs, getOptions()); Node root = compiler.parseInputs(); assertNotNull("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); // Only run the normalize pass, if asked. if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } if (closurePassEnabled && closurePassEnabledForExpected && !compiler.hasErrors()) { new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR, false).process(null, mainRoot); } return mainRoot; }
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); }
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); }
/** * 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 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 * @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 */ private void test( Compiler compiler, List<SourceFile> expected, DiagnosticType error, DiagnosticType warning, String description) { RecentChange recentChange = new RecentChange(); compiler.addChangeHandler(recentChange); Node root = compiler.parseInputs(); assertNotNull("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root); if (!expectParseWarningsThisTest) { assertEquals( "Unexpected parse warnings(s): " + Joiner.on("\n").join(compiler.getWarnings()), 0, compiler.getWarnings().length); } if (astValidationEnabled) { (new AstValidator(compiler)).validateRoot(root); } Node externsRoot = root.getFirstChild(); Node mainRoot = root.getLastChild(); // Save the tree for later comparison. Node rootClone = root.cloneTree(); Node externsRootClone = rootClone.getFirstChild(); Node mainRootClone = rootClone.getLastChild(); Map<Node, Node> mtoc = NodeUtil.mapMainToClone(mainRoot, mainRootClone); int numRepetitions = getNumRepetitions(); ErrorManager[] errorManagers = new ErrorManager[numRepetitions]; int aggregateWarningCount = 0; List<JSError> aggregateWarnings = Lists.newArrayList(); boolean hasCodeChanged = false; assertFalse("Code should not change before processing", recentChange.hasCodeChanged()); for (int i = 0; i < numRepetitions; ++i) { if (compiler.getErrorCount() == 0) { errorManagers[i] = new BlackHoleErrorManager(compiler); // Only run process closure primitives once, if asked. if (closurePassEnabled && i == 0) { recentChange.reset(); new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR, false) .process(null, mainRoot); hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); } // Only run the type checking pass once, if asked. // Running it twice can cause unpredictable behavior because duplicate // objects for the same type are created, and the type system // uses reference equality to compare many types. if (!runTypeCheckAfterProcessing && typeCheckEnabled && i == 0) { TypeCheck check = createTypeCheck(compiler, typeCheckLevel); check.processForTesting(externsRoot, mainRoot); } // Only run the normalize pass once, if asked. if (normalizeEnabled && i == 0) { normalizeActualCode(compiler, externsRoot, mainRoot); } if (enableInferConsts && i == 0) { new InferConsts(compiler).process(externsRoot, mainRoot); } if (computeSideEffects && i == 0) { PureFunctionIdentifier.Driver mark = new PureFunctionIdentifier.Driver(compiler, null, false); mark.process(externsRoot, mainRoot); } if (markNoSideEffects && i == 0) { MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); mark.process(externsRoot, mainRoot); } if (gatherExternPropertiesEnabled && i == 0) { (new GatherExternProperties(compiler)).process(externsRoot, mainRoot); } recentChange.reset(); getProcessor(compiler).process(externsRoot, mainRoot); if (astValidationEnabled) { (new AstValidator(compiler)).validateRoot(root); } if (checkLineNumbers) { (new LineNumberCheck(compiler)).process(externsRoot, mainRoot); } if (runTypeCheckAfterProcessing && typeCheckEnabled && i == 0) { TypeCheck check = createTypeCheck(compiler, typeCheckLevel); check.processForTesting(externsRoot, mainRoot); } hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); aggregateWarningCount += errorManagers[i].getWarningCount(); Collections.addAll(aggregateWarnings, compiler.getWarnings()); if (normalizeEnabled) { boolean verifyDeclaredConstants = true; new Normalize.VerifyConstants(compiler, verifyDeclaredConstants) .process(externsRoot, mainRoot); } } } if (error == null) { assertEquals( "Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()), 0, compiler.getErrorCount()); // Verify the symbol table. ErrorManager symbolTableErrorManager = new BlackHoleErrorManager(compiler); Node expectedRoot = null; if (expected != null) { expectedRoot = parseExpectedJs(expected); expectedRoot.detachFromParent(); } JSError[] stErrors = symbolTableErrorManager.getErrors(); if (expectedSymbolTableError != null) { assertEquals("There should be one error.", 1, stErrors.length); assertEquals(expectedSymbolTableError, stErrors[0].getType()); } else { assertEquals( "Unexpected symbol table error(s): " + Joiner.on("\n").join(stErrors), 0, stErrors.length); } if (warning == null) { assertEquals( "Unexpected warning(s): " + Joiner.on("\n").join(aggregateWarnings), 0, aggregateWarningCount); } else { assertEquals( "There should be one warning, repeated " + numRepetitions + " time(s). Warnings: " + aggregateWarnings, numRepetitions, aggregateWarningCount); for (int i = 0; i < numRepetitions; ++i) { JSError[] warnings = errorManagers[i].getWarnings(); JSError actual = warnings[0]; assertEquals(warning, actual.getType()); // Make sure that source information is always provided. if (!allowSourcelessWarnings) { assertTrue( "Missing source file name in warning", actual.sourceName != null && !actual.sourceName.isEmpty()); assertTrue("Missing line number in warning", -1 != actual.lineNumber); assertTrue("Missing char number in warning", -1 != actual.getCharno()); } if (description != null) { assertEquals(description, actual.description); } } } // If we ran normalize on the AST, we must also run normalize on the // clone before checking for changes. if (normalizeEnabled) { normalizeActualCode(compiler, externsRootClone, mainRootClone); } boolean codeChange = !mainRootClone.isEquivalentTo(mainRoot); boolean externsChange = !externsRootClone.isEquivalentTo(externsRoot); // Generally, externs should not be changed by the compiler passes. if (externsChange && !allowExternsChanges) { String explanation = externsRootClone.checkTreeEquals(externsRoot); fail( "Unexpected changes to externs" + "\nExpected: " + compiler.toSource(externsRootClone) + "\nResult: " + compiler.toSource(externsRoot) + "\n" + explanation); } if (!codeChange && !externsChange) { assertFalse( "compiler.reportCodeChange() was called " + "even though nothing changed", hasCodeChanged); } else { assertTrue( "compiler.reportCodeChange() should have been called." + "\nOriginal: " + mainRootClone.toStringTree() + "\nNew: " + mainRoot.toStringTree(), hasCodeChanged); } // Check correctness of the changed-scopes-only traversal NodeUtil.verifyScopeChanges(mtoc, mainRoot, false, compiler); if (expected != null) { if (compareAsTree) { String explanation; if (compareJsDoc) { explanation = expectedRoot.checkTreeEqualsIncludingJsDoc(mainRoot); } else { explanation = expectedRoot.checkTreeEquals(mainRoot); } assertNull( "\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } else if (expected != null) { String[] expectedSources = new String[expected.size()]; for (int i = 0; i < expected.size(); ++i) { try { expectedSources[i] = expected.get(i).getCode(); } catch (IOException e) { throw new RuntimeException("failed to get source code", e); } } assertEquals(Joiner.on("").join(expectedSources), compiler.toSource(mainRoot)); } } // Verify normalization is not invalidated. Node normalizeCheckRootClone = root.cloneTree(); Node normalizeCheckExternsRootClone = normalizeCheckRootClone.getFirstChild(); Node normalizeCheckMainRootClone = normalizeCheckRootClone.getLastChild(); new PrepareAst(compiler).process(normalizeCheckExternsRootClone, normalizeCheckMainRootClone); String explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull( "Node structure normalization invalidated." + "\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); // TODO(johnlenz): enable this for most test cases. // Currently, this invalidates test for while-loops, for-loop // initializers, and other naming. However, a set of code // (Closure primitive rewrites, etc) runs before the Normalize pass, // so this can't be force on everywhere. if (normalizeEnabled) { new Normalize(compiler, true) .process(normalizeCheckExternsRootClone, normalizeCheckMainRootClone); explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull( "Normalization invalidated." + "\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } } else { String errors = ""; for (JSError actualError : compiler.getErrors()) { errors += actualError.description + "\n"; } assertEquals("There should be one error. " + errors, 1, compiler.getErrorCount()); assertEquals(errors, error, compiler.getErrors()[0].getType()); if (warning != null) { String warnings = ""; for (JSError actualError : compiler.getWarnings()) { warnings += actualError.description + "\n"; } assertEquals("There should be one warning. " + warnings, 1, compiler.getWarningCount()); assertEquals(warnings, warning, compiler.getWarnings()[0].getType()); } } }