PageRenderTime 45ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/closure-compiler/test/com/google/javascript/jscomp/CompilerTestCase.java

https://bitbucket.org/simpler/squeeze
Java | 1060 lines | 507 code | 123 blank | 430 comment | 71 complexity | 5a5503fdfcde4e23e94e2ab6d5721f6d MD5 | raw file
Possible License(s): LGPL-2.0, Apache-2.0, GPL-2.0
  1. /*
  2. * Copyright 2006 The Closure Compiler Authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.google.javascript.jscomp;
  17. import com.google.common.base.Joiner;
  18. import com.google.common.base.Preconditions;
  19. import com.google.common.base.Predicates;
  20. import com.google.common.collect.Lists;
  21. import com.google.javascript.jscomp.CodeChangeHandler.RecentChange;
  22. import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
  23. import com.google.javascript.rhino.Node;
  24. import com.google.javascript.rhino.testing.BaseJSTypeTestCase;
  25. import junit.framework.TestCase;
  26. import java.io.IOException;
  27. import java.util.List;
  28. /**
  29. * <p>Base class for testing JS compiler classes that change
  30. * the node tree of a compiled JS input.</p>
  31. *
  32. * <p>Pulls in shared functionality from different test cases. Also supports
  33. * node tree comparison for input and output (instead of string comparison),
  34. * which makes it easier to write tests b/c you don't have to get the syntax
  35. * exactly correct to the spacing.</p>
  36. *
  37. */
  38. public abstract class CompilerTestCase extends TestCase {
  39. /** Externs for the test */
  40. private final JSSourceFile[] externsInputs;
  41. /** Whether to compare input and output as trees instead of strings */
  42. private final boolean compareAsTree;
  43. /** Whether to parse type info from JSDoc comments */
  44. protected boolean parseTypeInfo;
  45. /** Whether we check warnings without source information. */
  46. private boolean allowSourcelessWarnings = false;
  47. /** True iff type checking pass runs before pass being tested. */
  48. private boolean typeCheckEnabled = false;
  49. /** Error level reported by type checker. */
  50. private CheckLevel typeCheckLevel;
  51. /** Whether the Normalize pass runs before pass being tested. */
  52. private boolean normalizeEnabled = false;
  53. /** Whether the expected js strings should be normalized. */
  54. private boolean normalizeExpected = false;
  55. /** Whether to check that all line number information is preserved. */
  56. private boolean checkLineNumbers = true;
  57. /**
  58. * An expected symbol table error. Only useful for testing the
  59. * symbol table error-handling.
  60. */
  61. private DiagnosticType expectedSymbolTableError = null;
  62. /**
  63. * Whether the MarkNoSideEffectsCalls pass runs before the pass being tested
  64. */
  65. private boolean markNoSideEffects = false;
  66. /** The most recently used Compiler instance. */
  67. private Compiler lastCompiler;
  68. /**
  69. * Whether to acceptES5 source.
  70. */
  71. private boolean acceptES5 = true;
  72. /**
  73. * Whether externs changes should be allowed for this pass.
  74. */
  75. private boolean allowExternsChanges = false;
  76. /**
  77. * Whether the AST should be validated.
  78. */
  79. private boolean astValidationEnabled = true;
  80. private String filename = "testcode";
  81. /**
  82. * Constructs a test.
  83. *
  84. * @param externs Externs JS as a string
  85. * @param compareAsTree True to compare output & expected as a node tree.
  86. * 99% of the time you want to compare as a tree. There are a few
  87. * special cases where you don't, like if you want to test the code
  88. * printing of "unnatural" syntax trees. For example,
  89. *
  90. * <pre>
  91. * IF
  92. * IF
  93. * STATEMENT
  94. * ELSE
  95. * STATEMENT
  96. * </pre>
  97. */
  98. protected CompilerTestCase(String externs, boolean compareAsTree) {
  99. this.externsInputs = new JSSourceFile[] {
  100. JSSourceFile.fromCode("externs", externs)
  101. };
  102. this.compareAsTree = compareAsTree;
  103. this.parseTypeInfo = false;
  104. }
  105. /**
  106. * Constructs a test. Uses AST comparison.
  107. * @param externs Externs JS as a string
  108. */
  109. protected CompilerTestCase(String externs) {
  110. this(externs, true);
  111. }
  112. /**
  113. * Constructs a test. Uses AST comparison and no externs.
  114. */
  115. protected CompilerTestCase() {
  116. this("", true);
  117. }
  118. /**
  119. * Gets the compiler pass instance to use for a test.
  120. *
  121. * @param compiler The compiler
  122. * @return The pass to test
  123. */
  124. protected abstract CompilerPass getProcessor(Compiler compiler);
  125. /**
  126. * Gets the compiler options to use for this test. Use getProcessor to
  127. * determine what passes should be run.
  128. */
  129. protected CompilerOptions getOptions() {
  130. return getOptions(new CompilerOptions());
  131. }
  132. /**
  133. * Gets the compiler options to use for this test. Use getProcessor to
  134. * determine what passes should be run.
  135. */
  136. protected CompilerOptions getOptions(CompilerOptions options) {
  137. if (this.acceptES5) {
  138. options.setLanguageIn(LanguageMode.ECMASCRIPT5);
  139. }
  140. // This doesn't affect whether checkSymbols is run--it just affects
  141. // whether variable warnings are filtered.
  142. options.checkSymbols = true;
  143. options.setWarningLevel(
  144. DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING);
  145. options.setCodingConvention(getCodingConvention());
  146. return options;
  147. }
  148. protected CodingConvention getCodingConvention() {
  149. return new GoogleCodingConvention();
  150. }
  151. public void setFilename(String filename) {
  152. this.filename = filename;
  153. }
  154. /**
  155. * Returns the number of times the pass should be run before results are
  156. * verified.
  157. */
  158. protected int getNumRepetitions() {
  159. // Since most compiler passes should be idempotent, we run each pass twice
  160. // by default.
  161. return 2;
  162. }
  163. /** Expect warnings without source information. */
  164. void allowSourcelessWarnings() {
  165. allowSourcelessWarnings = true;
  166. }
  167. /** The most recently used JSComp instance. */
  168. Compiler getLastCompiler() {
  169. return lastCompiler;
  170. }
  171. /**
  172. * Whether to allow ECMASCRIPT5 source parsing.
  173. */
  174. protected void enableEcmaScript5(boolean acceptES5) {
  175. this.acceptES5 = acceptES5;
  176. }
  177. /**
  178. * Whether to allow externs changes.
  179. */
  180. protected void allowExternsChanges(boolean allowExternsChanges) {
  181. this.allowExternsChanges = allowExternsChanges;
  182. }
  183. /**
  184. * Perform type checking before running the test pass. This will check
  185. * for type errors and annotate nodes with type information.
  186. *
  187. * @param level the level of severity to report for type errors
  188. *
  189. * @see TypeCheck
  190. */
  191. public void enableTypeCheck(CheckLevel level) {
  192. typeCheckEnabled = true;
  193. typeCheckLevel = level;
  194. }
  195. /**
  196. * Check to make sure that line numbers were preserved.
  197. */
  198. public void enableLineNumberCheck(boolean newVal) {
  199. checkLineNumbers = newVal;
  200. }
  201. /**
  202. * Do not run type checking before running the test pass.
  203. *
  204. * @see TypeCheck
  205. */
  206. void disableTypeCheck() {
  207. typeCheckEnabled = false;
  208. }
  209. /**
  210. * Perform AST normalization before running the test pass, and anti-normalize
  211. * after running it.
  212. *
  213. * @see Normalize
  214. */
  215. protected void enableNormalize() {
  216. enableNormalize(true);
  217. }
  218. /**
  219. * Perform AST normalization before running the test pass, and anti-normalize
  220. * after running it.
  221. *
  222. * @param normalizeExpected Whether to perform normalization on the
  223. * expected js result.
  224. * @see Normalize
  225. */
  226. protected void enableNormalize(boolean normalizeExpected) {
  227. normalizeEnabled = true;
  228. this.normalizeExpected = normalizeExpected;
  229. }
  230. /**
  231. * Don't perform AST normalization before running the test pass.
  232. * @see Normalize
  233. */
  234. protected void disableNormalize() {
  235. normalizeEnabled = false;
  236. }
  237. /**
  238. * Run the MarkSideEffectCalls pass before running the test pass.
  239. *
  240. * @see MarkNoSideEffectCalls
  241. */
  242. void enableMarkNoSideEffects() {
  243. markNoSideEffects = true;
  244. }
  245. /**
  246. * Whether to allow Validate the AST after each run of the pass.
  247. */
  248. protected void enableAstValidation(boolean validate) {
  249. astValidationEnabled = validate;
  250. }
  251. /** Returns a newly created TypeCheck. */
  252. private static TypeCheck createTypeCheck(Compiler compiler,
  253. CheckLevel level) {
  254. ReverseAbstractInterpreter rai =
  255. new SemanticReverseAbstractInterpreter(compiler.getCodingConvention(),
  256. compiler.getTypeRegistry());
  257. return new TypeCheck(compiler, rai, compiler.getTypeRegistry(),
  258. level, CheckLevel.OFF);
  259. }
  260. /**
  261. * Verifies that the compiler pass's JS output matches the expected output.
  262. *
  263. * @param js Input
  264. * @param expected Expected JS output
  265. */
  266. public void test(String js, String expected) {
  267. test(js, expected, (DiagnosticType) null);
  268. }
  269. /**
  270. * Verifies that the compiler pass's JS output matches the expected output,
  271. * or that an expected error is encountered.
  272. *
  273. * @param js Input
  274. * @param expected Expected output, or null if an error is expected
  275. * @param error Expected error, or null if no error is expected
  276. */
  277. public void test(String js, String expected, DiagnosticType error) {
  278. test(js, expected, error, null);
  279. }
  280. /**
  281. * Verifies that the compiler pass's JS output matches the expected output,
  282. * or that an expected error is encountered.
  283. *
  284. * @param js Input
  285. * @param expected Expected output, or null if an error is expected
  286. * @param error Expected error, or null if no error is expected
  287. * @param warning Expected warning, or null if no warning is expected
  288. * @param description The content of the error expected
  289. */
  290. public void test(String js, String expected, DiagnosticType error,
  291. DiagnosticType warning, String description) {
  292. test(externsInputs, js, expected, error, warning, description);
  293. }
  294. /**
  295. * Verifies that the compiler pass's JS output matches the expected output
  296. * and (optionally) that an expected warning is issued. Or, if an error is
  297. * expected, this method just verifies that the error is encountered.
  298. *
  299. * @param js Input
  300. * @param expected Expected output, or null if an error is expected
  301. * @param error Expected error, or null if no error is expected
  302. * @param warning Expected warning, or null if no warning is expected
  303. */
  304. public void test(String js, String expected,
  305. DiagnosticType error, DiagnosticType warning) {
  306. test(externsInputs, js, expected, error, warning, null);
  307. }
  308. /**
  309. * Verifies that the compiler pass's JS output matches the expected output
  310. * and (optionally) that an expected warning is issued. Or, if an error is
  311. * expected, this method just verifies that the error is encountered.
  312. *
  313. * @param externs Externs input
  314. * @param js Input
  315. * @param expected Expected output, or null if an error is expected
  316. * @param error Expected error, or null if no error is expected
  317. * @param warning Expected warning, or null if no warning is expected
  318. */
  319. public void test(String externs, String js, String expected,
  320. DiagnosticType error, DiagnosticType warning) {
  321. test(externs, js, expected, error, warning, null);
  322. }
  323. /**
  324. * Verifies that the compiler pass's JS output matches the expected output
  325. * and (optionally) that an expected warning is issued. Or, if an error is
  326. * expected, this method just verifies that the error is encountered.
  327. *
  328. * @param externs Externs input
  329. * @param js Input
  330. * @param expected Expected output, or null if an error is expected
  331. * @param error Expected error, or null if no error is expected
  332. * @param warning Expected warning, or null if no warning is expected
  333. * @param description The description of the expected warning,
  334. * or null if no warning is expected or if the warning's description
  335. * should not be examined
  336. */
  337. public void test(String externs, String js, String expected,
  338. DiagnosticType error, DiagnosticType warning,
  339. String description) {
  340. JSSourceFile[] externsInputs = new JSSourceFile[]{
  341. JSSourceFile.fromCode("externs", externs)
  342. };
  343. test(externsInputs, js, expected, error, warning, description);
  344. }
  345. /**
  346. * Verifies that the compiler pass's JS output matches the expected output
  347. * and (optionally) that an expected warning is issued. Or, if an error is
  348. * expected, this method just verifies that the error is encountered.
  349. *
  350. * @param externs Externs inputs
  351. * @param js Input
  352. * @param expected Expected output, or null if an error is expected
  353. * @param error Expected error, or null if no error is expected
  354. * @param warning Expected warning, or null if no warning is expected
  355. * @param description The description of the expected warning,
  356. * or null if no warning is expected or if the warning's description
  357. * should not be examined
  358. */
  359. public void test(JSSourceFile[] externs, String js, String expected,
  360. DiagnosticType error,
  361. DiagnosticType warning, String description) {
  362. Compiler compiler = createCompiler();
  363. lastCompiler = compiler;
  364. CompilerOptions options = getOptions();
  365. if (this.acceptES5) {
  366. options.setLanguageIn(LanguageMode.ECMASCRIPT5);
  367. }
  368. // Note that in this context, turning on the checkTypes option won't
  369. // actually cause the type check to run.
  370. options.checkTypes = parseTypeInfo;
  371. compiler.init(externs, new JSSourceFile[] {
  372. JSSourceFile.fromCode(filename, js) }, options);
  373. BaseJSTypeTestCase.addNativeProperties(compiler.getTypeRegistry());
  374. test(compiler, new String[] { expected }, error, warning, description);
  375. }
  376. /**
  377. * Verifies that the compiler pass's JS output matches the expected output.
  378. *
  379. * @param js Inputs
  380. * @param expected Expected JS output
  381. */
  382. public void test(String[] js, String[] expected) {
  383. test(js, expected, null);
  384. }
  385. /**
  386. * Verifies that the compiler pass's JS output matches the expected output,
  387. * or that an expected error is encountered.
  388. *
  389. * @param js Inputs
  390. * @param expected Expected JS output
  391. * @param error Expected error, or null if no error is expected
  392. */
  393. public void test(String[] js, String[] expected, DiagnosticType error) {
  394. test(js, expected, error, null);
  395. }
  396. /**
  397. * Verifies that the compiler pass's JS output matches the expected output
  398. * and (optionally) that an expected warning is issued. Or, if an error is
  399. * expected, this method just verifies that the error is encountered.
  400. *
  401. * @param js Inputs
  402. * @param expected Expected JS output
  403. * @param error Expected error, or null if no error is expected
  404. * @param warning Expected warning, or null if no warning is expected
  405. */
  406. public void test(String[] js, String[] expected, DiagnosticType error,
  407. DiagnosticType warning) {
  408. test(js, expected, error, warning, null);
  409. }
  410. /**
  411. * Verifies that the compiler pass's JS output matches the expected output
  412. * and (optionally) that an expected warning is issued. Or, if an error is
  413. * expected, this method just verifies that the error is encountered.
  414. *
  415. * @param js Inputs
  416. * @param expected Expected JS output
  417. * @param error Expected error, or null if no error is expected
  418. * @param warning Expected warning, or null if no warning is expected
  419. * @param description The description of the expected warning,
  420. * or null if no warning is expected or if the warning's description
  421. * should not be examined
  422. */
  423. public void test(String[] js, String[] expected, DiagnosticType error,
  424. DiagnosticType warning, String description) {
  425. Compiler compiler = createCompiler();
  426. lastCompiler = compiler;
  427. JSSourceFile[] inputs = new JSSourceFile[js.length];
  428. for (int i = 0; i < js.length; i++) {
  429. inputs[i] = JSSourceFile.fromCode("input" + i, js[i]);
  430. }
  431. compiler.init(externsInputs, inputs, getOptions());
  432. test(compiler, expected, error, warning, description);
  433. }
  434. /**
  435. * Verifies that the compiler pass's JS output matches the expected output.
  436. *
  437. * @param modules Module inputs
  438. * @param expected Expected JS outputs (one per module)
  439. */
  440. public void test(JSModule[] modules, String[] expected) {
  441. test(modules, expected, null);
  442. }
  443. /**
  444. * Verifies that the compiler pass's JS output matches the expected output,
  445. * or that an expected error is encountered.
  446. *
  447. * @param modules Module inputs
  448. * @param expected Expected JS outputs (one per module)
  449. * @param error Expected error, or null if no error is expected
  450. */
  451. public void test(JSModule[] modules, String[] expected,
  452. DiagnosticType error) {
  453. test(modules, expected, error, null);
  454. }
  455. /**
  456. * Verifies that the compiler pass's JS output matches the expected output
  457. * and (optionally) that an expected warning is issued. Or, if an error is
  458. * expected, this method just verifies that the error is encountered.
  459. *
  460. * @param modules Module inputs
  461. * @param expected Expected JS outputs (one per module)
  462. * @param error Expected error, or null if no error is expected
  463. * @param warning Expected warning, or null if no warning is expected
  464. */
  465. public void test(JSModule[] modules, String[] expected,
  466. DiagnosticType error, DiagnosticType warning) {
  467. Compiler compiler = createCompiler();
  468. lastCompiler = compiler;
  469. compiler.init(externsInputs, modules, getOptions());
  470. test(compiler, expected, error, warning);
  471. }
  472. /**
  473. * Verifies that the compiler pass's JS output is the same as its input.
  474. *
  475. * @param js Input and output
  476. */
  477. public void testSame(String js) {
  478. test(js, js);
  479. }
  480. /**
  481. * Verifies that the compiler pass's JS output is the same as its input
  482. * and (optionally) that an expected warning is issued.
  483. *
  484. * @param js Input and output
  485. * @param warning Expected warning, or null if no warning is expected
  486. */
  487. public void testSame(String js, DiagnosticType warning) {
  488. test(js, js, null, warning);
  489. }
  490. /**
  491. * Verifies that the compiler pass's JS output is the same as its input
  492. * and (optionally) that an expected warning is issued.
  493. *
  494. * @param js Input and output
  495. * @param diag Expected error or warning, or null if none is expected
  496. * @param error true if diag is an error, false if it is a warning
  497. */
  498. public void testSame(String js, DiagnosticType diag, boolean error) {
  499. if (error) {
  500. test(js, js, diag);
  501. } else {
  502. test(js, js, null, diag);
  503. }
  504. }
  505. /**
  506. * Verifies that the compiler pass's JS output is the same as its input
  507. * and (optionally) that an expected warning is issued.
  508. *
  509. * @param externs Externs input
  510. * @param js Input and output
  511. * @param warning Expected warning, or null if no warning is expected
  512. */
  513. public void testSame(String externs, String js, DiagnosticType warning) {
  514. testSame(externs, js, warning, null);
  515. }
  516. /**
  517. * Verifies that the compiler pass's JS output is the same as its input
  518. * and (optionally) that an expected warning is issued.
  519. *
  520. * @param externs Externs input
  521. * @param js Input and output
  522. * @param diag Expected error or warning, or null if none is expected
  523. * @param error true if diag is an error, false if it is a warning
  524. */
  525. public void testSame(
  526. String externs, String js, DiagnosticType diag, boolean error) {
  527. if (error) {
  528. test(externs, js, js, diag, null);
  529. } else {
  530. test(externs, js, js, null, diag);
  531. }
  532. }
  533. /**
  534. * Verifies that the compiler pass's JS output is the same as its input
  535. * and (optionally) that an expected warning and description is issued.
  536. *
  537. * @param externs Externs input
  538. * @param js Input and output
  539. * @param warning Expected warning, or null if no warning is expected
  540. * @param description The description of the expected warning,
  541. * or null if no warning is expected or if the warning's description
  542. * should not be examined
  543. */
  544. public void testSame(String externs, String js, DiagnosticType warning,
  545. String description) {
  546. JSSourceFile[] externsInputs = new JSSourceFile[]{
  547. JSSourceFile.fromCode("externs", externs)
  548. };
  549. test(externsInputs, js, js, null, warning, description);
  550. }
  551. /**
  552. * Verifies that the compiler pass's JS output is the same as its input.
  553. *
  554. * @param js Inputs and outputs
  555. */
  556. public void testSame(String[] js) {
  557. test(js, js);
  558. }
  559. /**
  560. * Verifies that the compiler pass's JS output is the same as its input,
  561. * and emits the given error.
  562. *
  563. * @param js Inputs and outputs
  564. * @param error Expected error, or null if no error is expected
  565. */
  566. public void testSame(String[] js, DiagnosticType error) {
  567. test(js, js, error);
  568. }
  569. /**
  570. * Verifies that the compiler pass's JS output is the same as its input,
  571. * and emits the given error and warning.
  572. *
  573. * @param js Inputs and outputs
  574. * @param error Expected error, or null if no error is expected
  575. * @param warning Expected warning, or null if no warning is expected
  576. */
  577. public void testSame(
  578. String[] js, DiagnosticType error, DiagnosticType warning) {
  579. test(js, js, error, warning);
  580. }
  581. /**
  582. * Verifies that the compiler pass's JS output is the same as the input.
  583. *
  584. * @param modules Module inputs
  585. */
  586. public void testSame(JSModule[] modules) {
  587. testSame(modules, null);
  588. }
  589. /**
  590. * Verifies that the compiler pass's JS output is the same as the input.
  591. *
  592. * @param modules Module inputs
  593. * @param warning A warning, or null for no expected warning.
  594. */
  595. public void testSame(JSModule[] modules, DiagnosticType warning) {
  596. try {
  597. String[] expected = new String[modules.length];
  598. for (int i = 0; i < modules.length; i++) {
  599. expected[i] = "";
  600. for (CompilerInput input : modules[i].getInputs()) {
  601. expected[i] += input.getSourceFile().getCode();
  602. }
  603. }
  604. test(modules, expected, null, warning);
  605. } catch (IOException e) {
  606. throw new RuntimeException(e);
  607. }
  608. }
  609. /**
  610. * Verifies that the compiler pass's JS output matches the expected output
  611. * and (optionally) that an expected warning is issued. Or, if an error is
  612. * expected, this method just verifies that the error is encountered.
  613. *
  614. * @param compiler A compiler that has been initialized via
  615. * {@link Compiler#init}
  616. * @param expected Expected output, or null if an error is expected
  617. * @param error Expected error, or null if no error is expected
  618. * @param warning Expected warning, or null if no warning is expected
  619. */
  620. protected void test(Compiler compiler, String[] expected,
  621. DiagnosticType error, DiagnosticType warning) {
  622. test(compiler, expected, error, warning, null);
  623. }
  624. /**
  625. * Verifies that the compiler pass's JS output matches the expected output
  626. * and (optionally) that an expected warning is issued. Or, if an error is
  627. * expected, this method just verifies that the error is encountered.
  628. *
  629. * @param compiler A compiler that has been initialized via
  630. * {@link Compiler#init}
  631. * @param expected Expected output, or null if an error is expected
  632. * @param error Expected error, or null if no error is expected
  633. * @param warning Expected warning, or null if no warning is expected
  634. * @param description The description of the expected warning,
  635. * or null if no warning is expected or if the warning's description
  636. * should not be examined
  637. */
  638. private void test(Compiler compiler, String[] expected,
  639. DiagnosticType error, DiagnosticType warning,
  640. String description) {
  641. RecentChange recentChange = new RecentChange();
  642. compiler.addChangeHandler(recentChange);
  643. Node root = compiler.parseInputs();
  644. assertTrue("Unexpected parse error(s): " +
  645. Joiner.on("\n").join(compiler.getErrors()), root != null);
  646. if (astValidationEnabled) {
  647. (new AstValidator()).validateRoot(root);
  648. }
  649. Node externsRoot = root.getFirstChild();
  650. Node mainRoot = root.getLastChild();
  651. // Save the tree for later comparison.
  652. Node rootClone = root.cloneTree();
  653. Node externsRootClone = rootClone.getFirstChild();
  654. Node mainRootClone = rootClone.getLastChild();
  655. int numRepetitions = getNumRepetitions();
  656. ErrorManager[] errorManagers = new ErrorManager[numRepetitions];
  657. int aggregateWarningCount = 0;
  658. List<JSError> aggregateWarnings = Lists.newArrayList();
  659. boolean hasCodeChanged = false;
  660. assertFalse("Code should not change before processing",
  661. recentChange.hasCodeChanged());
  662. for (int i = 0; i < numRepetitions; ++i) {
  663. if (compiler.getErrorCount() == 0) {
  664. errorManagers[i] = new BlackHoleErrorManager(compiler);
  665. // Only run the type checking pass once, if asked.
  666. // Running it twice can cause unpredictable behavior because duplicate
  667. // objects for the same type are created, and the type system
  668. // uses reference equality to compare many types.
  669. if (typeCheckEnabled && i == 0) {
  670. TypeCheck check = createTypeCheck(compiler, typeCheckLevel);
  671. check.processForTesting(externsRoot, mainRoot);
  672. }
  673. // Only run the normalize pass once, if asked.
  674. if (normalizeEnabled && i == 0) {
  675. normalizeActualCode(compiler, externsRoot, mainRoot);
  676. }
  677. if (markNoSideEffects && i == 0) {
  678. MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler);
  679. mark.process(externsRoot, mainRoot);
  680. }
  681. recentChange.reset();
  682. getProcessor(compiler).process(externsRoot, mainRoot);
  683. if (astValidationEnabled) {
  684. (new AstValidator()).validateRoot(root);
  685. }
  686. if (checkLineNumbers) {
  687. (new LineNumberCheck(compiler)).process(externsRoot, mainRoot);
  688. }
  689. hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged();
  690. aggregateWarningCount += errorManagers[i].getWarningCount();
  691. aggregateWarnings.addAll(Lists.newArrayList(compiler.getWarnings()));
  692. if (normalizeEnabled) {
  693. boolean verifyDeclaredConstants = true;
  694. new Normalize.VerifyConstants(compiler, verifyDeclaredConstants)
  695. .process(externsRoot, mainRoot);
  696. }
  697. }
  698. }
  699. if (error == null) {
  700. assertEquals(
  701. "Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()),
  702. 0, compiler.getErrorCount());
  703. // Verify the symbol table.
  704. ErrorManager symbolTableErrorManager =
  705. new BlackHoleErrorManager(compiler);
  706. Node expectedRoot = parseExpectedJs(expected);
  707. expectedRoot.detachFromParent();
  708. JSError[] stErrors = symbolTableErrorManager.getErrors();
  709. if (expectedSymbolTableError != null) {
  710. assertEquals("There should be one error.", 1, stErrors.length);
  711. assertEquals(expectedSymbolTableError, stErrors[0].getType());
  712. } else {
  713. assertEquals("Unexpected symbol table error(s): " +
  714. Joiner.on("\n").join(stErrors),
  715. 0, stErrors.length);
  716. }
  717. if (warning == null) {
  718. assertEquals(
  719. "Unexpected warning(s): " + Joiner.on("\n").join(aggregateWarnings),
  720. 0, aggregateWarningCount);
  721. } else {
  722. assertEquals("There should be one warning, repeated " + numRepetitions +
  723. " time(s).", numRepetitions, aggregateWarningCount);
  724. for (int i = 0; i < numRepetitions; ++i) {
  725. JSError[] warnings = errorManagers[i].getWarnings();
  726. JSError actual = warnings[0];
  727. assertEquals(warning, actual.getType());
  728. // Make sure that source information is always provided.
  729. if (!allowSourcelessWarnings) {
  730. assertTrue("Missing source file name in warning",
  731. actual.sourceName != null && !actual.sourceName.isEmpty());
  732. assertTrue("Missing line number in warning",
  733. -1 != actual.lineNumber);
  734. assertTrue("Missing char number in warning",
  735. -1 != actual.getCharno());
  736. }
  737. if (description != null) {
  738. assertEquals(description, actual.description);
  739. }
  740. }
  741. }
  742. if (normalizeEnabled) {
  743. normalizeActualCode(compiler, externsRootClone, mainRootClone);
  744. }
  745. boolean codeChange = !mainRootClone.isEquivalentTo(mainRoot);
  746. boolean externsChange = !externsRootClone.isEquivalentTo(externsRoot);
  747. // Generally, externs should not be change by the compiler passes.
  748. if (externsChange && !allowExternsChanges) {
  749. String explanation = externsRootClone.checkTreeEquals(externsRoot);
  750. fail("Unexpected changes to externs" +
  751. "\nExpected: " + compiler.toSource(externsRootClone) +
  752. "\nResult: " + compiler.toSource(externsRoot) +
  753. "\n" + explanation);
  754. }
  755. if (!codeChange && !externsChange) {
  756. assertFalse(
  757. "compiler.reportCodeChange() was called " +
  758. "even though nothing changed",
  759. hasCodeChanged);
  760. } else {
  761. assertTrue("compiler.reportCodeChange() should have been called",
  762. hasCodeChanged);
  763. }
  764. if (compareAsTree) {
  765. String explanation = expectedRoot.checkTreeEquals(mainRoot);
  766. assertNull("\nExpected: " + compiler.toSource(expectedRoot) +
  767. "\nResult: " + compiler.toSource(mainRoot) +
  768. "\n" + explanation, explanation);
  769. } else if (expected != null) {
  770. assertEquals(
  771. Joiner.on("").join(expected), compiler.toSource(mainRoot));
  772. }
  773. // Verify normalization is not invalidated.
  774. Node normalizeCheckRootClone = root.cloneTree();
  775. Node normalizeCheckExternsRootClone = root.getFirstChild();
  776. Node normalizeCheckMainRootClone = root.getLastChild();
  777. new PrepareAst(compiler).process(
  778. normalizeCheckExternsRootClone, normalizeCheckMainRootClone);
  779. String explanation =
  780. normalizeCheckMainRootClone.checkTreeEquals(mainRoot);
  781. assertNull("Node structure normalization invalidated.\nExpected: " +
  782. compiler.toSource(normalizeCheckMainRootClone) +
  783. "\nResult: " + compiler.toSource(mainRoot) +
  784. "\n" + explanation, explanation);
  785. // TODO(johnlenz): enable this for most test cases.
  786. // Currently, this invalidates test for while-loops, for-loop
  787. // initializers, and other naming. However, a set of code
  788. // (FoldConstants, etc) runs before the Normalize pass, so this can't be
  789. // force on everywhere.
  790. if (normalizeEnabled) {
  791. new Normalize(compiler, true).process(
  792. normalizeCheckExternsRootClone, normalizeCheckMainRootClone);
  793. explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot);
  794. assertNull("Normalization invalidated.\nExpected: " +
  795. compiler.toSource(normalizeCheckMainRootClone) +
  796. "\nResult: " + compiler.toSource(mainRoot) +
  797. "\n" + explanation, explanation);
  798. }
  799. } else {
  800. String errors = "";
  801. for (JSError actualError : compiler.getErrors()) {
  802. errors += actualError.description + "\n";
  803. }
  804. assertEquals("There should be one error. " + errors,
  805. 1, compiler.getErrorCount());
  806. assertEquals(errors, error, compiler.getErrors()[0].getType());
  807. if (warning != null) {
  808. String warnings = "";
  809. for (JSError actualError : compiler.getWarnings()) {
  810. warnings += actualError.description + "\n";
  811. }
  812. assertEquals("There should be one warning. " + warnings,
  813. 1, compiler.getWarningCount());
  814. assertEquals(warnings, warning, compiler.getWarnings()[0].getType());
  815. }
  816. }
  817. }
  818. private void normalizeActualCode(
  819. Compiler compiler, Node externsRoot, Node mainRoot) {
  820. Normalize normalize = new Normalize(compiler, false);
  821. normalize.process(externsRoot, mainRoot);
  822. }
  823. /**
  824. * Parses expected js inputs and returns the root of the parse tree.
  825. */
  826. protected Node parseExpectedJs(String[] expected) {
  827. Compiler compiler = createCompiler();
  828. JSSourceFile[] inputs = new JSSourceFile[expected.length];
  829. for (int i = 0; i < expected.length; i++) {
  830. inputs[i] = JSSourceFile.fromCode("expected" + i, expected[i]);
  831. }
  832. compiler.init(externsInputs, inputs, getOptions());
  833. Node root = compiler.parseInputs();
  834. assertTrue("Unexpected parse error(s): " +
  835. Joiner.on("\n").join(compiler.getErrors()), root != null);
  836. Node externsRoot = root.getFirstChild();
  837. Node mainRoot = externsRoot.getNext();
  838. // Only run the normalize pass, if asked.
  839. if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) {
  840. Normalize normalize = new Normalize(compiler, false);
  841. normalize.process(externsRoot, mainRoot);
  842. }
  843. return mainRoot;
  844. }
  845. protected Node parseExpectedJs(String expected) {
  846. return parseExpectedJs(new String[] {expected});
  847. }
  848. /**
  849. * Generates a list of modules from a list of inputs, such that each module
  850. * depends on the module before it.
  851. */
  852. static JSModule[] createModuleChain(String... inputs) {
  853. JSModule[] modules = createModules(inputs);
  854. for (int i = 1; i < modules.length; i++) {
  855. modules[i].addDependency(modules[i - 1]);
  856. }
  857. return modules;
  858. }
  859. /**
  860. * Generates a list of modules from a list of inputs, such that each module
  861. * depends on the first module.
  862. */
  863. static JSModule[] createModuleStar(String... inputs) {
  864. JSModule[] modules = createModules(inputs);
  865. for (int i = 1; i < modules.length; i++) {
  866. modules[i].addDependency(modules[0]);
  867. }
  868. return modules;
  869. }
  870. /**
  871. * Generates a list of modules from a list of inputs, such that modules
  872. * form a bush formation. In a bush formation, module 2 depends
  873. * on module 1, and all other modules depend on module 2.
  874. */
  875. static JSModule[] createModuleBush(String ... inputs) {
  876. Preconditions.checkState(inputs.length > 2);
  877. JSModule[] modules = createModules(inputs);
  878. for (int i = 1; i < modules.length; i++) {
  879. modules[i].addDependency(modules[i == 1 ? 0 : 1]);
  880. }
  881. return modules;
  882. }
  883. /**
  884. * Generates a list of modules from a list of inputs, such that modules
  885. * form a tree formation. In a tree formation, module N depends on
  886. * module `floor(N/2)`, So the modules form a balanced binary tree.
  887. */
  888. static JSModule[] createModuleTree(String ... inputs) {
  889. JSModule[] modules = createModules(inputs);
  890. for (int i = 1; i < modules.length; i++) {
  891. modules[i].addDependency(modules[(i - 1) / 2]);
  892. }
  893. return modules;
  894. }
  895. /**
  896. * Generates a list of modules from a list of inputs. Does not generate any
  897. * dependencies between the modules.
  898. */
  899. static JSModule[] createModules(String... inputs) {
  900. JSModule[] modules = new JSModule[inputs.length];
  901. for (int i = 0; i < inputs.length; i++) {
  902. JSModule module = modules[i] = new JSModule("m" + i);
  903. module.add(JSSourceFile.fromCode("i" + i, inputs[i]));
  904. }
  905. return modules;
  906. }
  907. private static class BlackHoleErrorManager extends BasicErrorManager {
  908. private BlackHoleErrorManager(Compiler compiler) {
  909. compiler.setErrorManager(this);
  910. }
  911. @Override
  912. public void println(CheckLevel level, JSError error) {}
  913. @Override
  914. public void printSummary() {}
  915. }
  916. Compiler createCompiler() {
  917. Compiler compiler = new Compiler();
  918. return compiler;
  919. }
  920. protected void setExpectedSymbolTableError(DiagnosticType type) {
  921. this.expectedSymbolTableError = type;
  922. }
  923. /** Finds the first matching qualified name node in post-traversal order. */
  924. protected final Node findQualifiedNameNode(final String name, Node root) {
  925. final List<Node> matches = Lists.newArrayList();
  926. NodeUtil.visitPostOrder(root,
  927. new NodeUtil.Visitor() {
  928. @Override public void visit(Node n) {
  929. if (name.equals(n.getQualifiedName())) {
  930. matches.add(n);
  931. }
  932. }
  933. },
  934. Predicates.<Node>alwaysTrue());
  935. return matches.get(0);
  936. }
  937. }