/* * Copyright 2017 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import junit.framework.TestCase; public final class ChangeVerifierTest extends TestCase { public void testCorrectValidationOfScriptWithChangeAfterFunction() { Node script = parse("function A() {} if (0) { A(); }"); Preconditions.checkState(script.isScript()); Compiler compiler = new Compiler(); compiler.incrementChangeStamp(); ChangeVerifier verifier = new ChangeVerifier(compiler).snapshot(script); // Here we make a change in that doesn't change the script node // child count. getCallNode(script).detach(); // Mark the script as changed compiler.incrementChangeStamp(); script.setChangeTime(compiler.getChangeStamp()); // will throw if no change is detected. verifier.checkRecordedChanges("test1", script); } public void testChangeToScriptNotReported() { Node script = parse("function A() {} if (0) { A(); }"); Preconditions.checkState(script.isScript()); Compiler compiler = new Compiler(); compiler.incrementChangeStamp(); ChangeVerifier verifier = new ChangeVerifier(compiler).snapshot(script); // no change verifier.checkRecordedChanges("test1", script); // add a statement, but don't report the change. script.addChildToBack(IR.exprResult(IR.nullNode())); try { verifier.checkRecordedChanges("test2", script); fail("exception expected"); } catch (IllegalStateException e) { // TODO(johnlenz): use this when we upgrade Trush: // assertThat(e).hasMessageThat().contains("change scope not marked as changed"); assertThat(e.getMessage()).contains("changed scope not marked as changed"); } } public static void testChangeVerification() { Compiler compiler = new Compiler(); Node mainScript = IR.script(); Node main = IR.root(mainScript); ChangeVerifier verifier = new ChangeVerifier(compiler).snapshot(main); verifier.checkRecordedChanges(main); mainScript.addChildToFront( IR.function(IR.name("A"), IR.paramList(), IR.block())); compiler.incrementChangeStamp(); mainScript.setChangeTime(compiler.getChangeStamp()); try { verifier.checkRecordedChanges(main); fail("method should throw"); } catch (IllegalStateException e) { // ensure that e was thrown from the right code-path // especially important if it's something as frequent // as an IllegalArgumentException, etc. assertThat(e).hasMessageThat().startsWith("new scope not explicitly marked as changed:"); } } private static Node parse(String js) { Compiler compiler = new Compiler(); compiler.initCompilerOptionsIfTesting(); Node n = compiler.parseTestCode(js); assertThat(compiler.getErrors()).isEmpty(); return n; } private static Node getCallNode(Node n) { if (n.isCall()) { return n; } for (Node c : n.children()) { Node result = getCallNode(c); if (result != null) { return result; } } return null; } }