/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Lists;
import com.google.javascript.jscomp.PhaseOptimizer.Loop;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import junit.framework.TestCase;
import java.util.List;
import java.util.Random;
/**
* Tests for {@link PhaseOptimizer}.
* @author nicksantos@google.com (Nick Santos)
*/
public class PhaseOptimizerTest extends TestCase {
private final List<String> passesRun = Lists.newArrayList();
private PhaseOptimizer optimizer;
private Compiler compiler;
private PerformanceTracker tracker;
@Override
public void setUp() {
passesRun.clear();
compiler = new Compiler();
compiler.initCompilerOptionsIfTesting();
tracker = new PerformanceTracker(new Node(Token.BLOCK), false);
optimizer = new PhaseOptimizer(compiler, tracker);
}
public void testOneRun() {
addOneTimePass("x");
assertPasses("x");
}
public void testTwoRuns() {
addOneTimePass("x");
optimizer.process(null, null);
try {
optimizer.process(null, null);
fail();
} catch (IllegalStateException e) {
assertEquals(
"One-time passes cannot be run multiple times: x", e.getMessage());
}
}
public void testLoop1() {
Loop loop = optimizer.addFixedPointLoop();
addLoopedPass(loop, "x", 0);
assertPasses("x");
}
public void testLoop2() {
Loop loop = optimizer.addFixedPointLoop();
addLoopedPass(loop, "x", 3);
assertPasses("x", "x", "x", "x");
}
public void testLoop3() {
Loop loop = optimizer.addFixedPointLoop();
addLoopedPass(loop, "x", 3);
addLoopedPass(loop, "y", 1);
assertPasses("x", "y", "x", "y", "x", "y", "x", "y");
}
public void testNotInfiniteLoop() {
Loop loop = optimizer.addFixedPointLoop();
addLoopedPass(loop, "x", PhaseOptimizer.MAX_LOOPS);
optimizer.process(null, null);
assertEquals("There should be no errors.", 0, compiler.getErrorCount());
}
public void testInfiniteLoop() {
Loop loop = optimizer.addFixedPointLoop();
addLoopedPass(loop, "x", PhaseOptimizer.MAX_LOOPS + 1);
try {
optimizer.process(null, null);
fail("Expected RuntimeException");
} catch (RuntimeException e) {
assertTrue(e.getMessage().contains(PhaseOptimizer.OPTIMIZE_LOOP_ERROR));
}
}
public void testCombined() {
addOneTimePass("a");
Loop loop = optimizer.addFixedPointLoop();
addLoopedPass(loop, "x", 3);
addLoopedPass(loop, "y", 1);
addOneTimePass("z");
assertPasses("a", "x", "y", "x", "y", "x", "y", "x", "y", "z");
}
public void testSanityCheck() {
Loop loop = optimizer.addFixedPointLoop();
addLoopedPass(loop, "x", 1);
addOneTimePass("z");
optimizer.setSanityCheck(
createPassFactory("sanity", createPass("sanity", 0), false));
assertPasses("x", "sanity", "x", "sanity", "z", "sanity");
}
public void testConsumption1() {
optimizer.consume(
Lists.newArrayList(
createPassFactory("a", 0, true),
createPassFactory("b", 1, false),
createPassFactory("c", 2, false),
createPassFactory("d", 1, false),
createPassFactory("e", 1, true),
createPassFactory("f", 0, true)));
assertPasses("a", "b", "c", "d", "b", "c", "d", "b", "c", "d", "e", "f");
}
public void testConsumption2() {
optimizer.consume(
Lists.newArrayList(
createPassFactory("a", 2, false),
createPassFactory("b", 1, true),
createPassFactory("c", 1, false)));
assertPasses("a", "a", "a", "b", "c", "c");
}
public void testConsumption3() {
optimizer.consume(
Lists.newArrayList(
createPassFactory("a", 2, true),
createPassFactory("b", 0, false),
createPassFactory("c", 0, false)));
assertPasses("a", "b", "c");
}
public void testDuplicateLoop() {
Loop loop = optimizer.addFixedPointLoop();
addLoopedPass(loop, "x", 1);
try {
addLoopedPass(loop, "x", 1);
fail("Expected exception");
} catch (IllegalArgumentException e) {}
}
public void testPassOrdering() {
Loop loop = optimizer.addFixedPointLoop();
List<String> optimalOrder = Lists.newArrayList(
PhaseOptimizer.OPTIMAL_ORDER);
Random random = new Random();
while (optimalOrder.size() > 0) {
addLoopedPass(
loop, optimalOrder.remove(random.nextInt(optimalOrder.size())), 0);
}
optimizer.process(null, null);
assertEquals(PhaseOptimizer.OPTIMAL_ORDER, passesRun);
}
public void assertPasses(String ... names) {
optimizer.process(null, null);
assertEquals(Lists.newArrayList(names), passesRun);
}
private void addOneTimePass(String name) {
optimizer.addOneTimePass(
createPassFactory(name, 0, true));
}
private void addLoopedPass(Loop loop, String name, int numChanges) {
loop.addLoopedPass(
createPassFactory(name, numChanges, false));
}
private PassFactory createPassFactory(
String name, int numChanges, boolean isOneTime) {
return createPassFactory(name, createPass(name, numChanges), isOneTime);
}
private PassFactory createPassFactory(
String name, final CompilerPass pass, boolean isOneTime) {
return new PassFactory(name, isOneTime) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return pass;
}
};
}
private CompilerPass createPass(final String name, int numChanges) {
final int[] numChangesClosure = new int[] {numChanges};
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
passesRun.add(name);
if (numChangesClosure[0] > 0) {
compiler.reportCodeChange();
numChangesClosure[0] = numChangesClosure[0] - 1;
}
}
};
}
}