/*
* Copyright 2012 Google Inc.
*
* 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.template.soy.passes;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableMap;
import com.google.template.soy.SoyFileSetParserBuilder;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.ExplodingErrorReporter;
import com.google.template.soy.passes.FindIjParamsVisitor.IjParamsInfo;
import com.google.template.soy.soytree.SoyFileSetNode;
import com.google.template.soy.soytree.TemplateNode;
import com.google.template.soy.soytree.TemplateRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Unit tests for FindIjParamsVisitor.
*
*/
@RunWith(JUnit4.class)
public final class FindIjParamsVisitorTest {
private static final ErrorReporter FAIL = ExplodingErrorReporter.get();
@Test
public void testSimple() {
// aaa -> {bbb, ccc}, bbb -> ddd.
String fileContent =
""
+ "{namespace ns autoescape=\"deprecated-noncontextual\"}\n"
+ "\n"
+ "/***/\n"
+ "{template .aaa}\n"
+ " {call .bbb /} {$ij.boo} {call .ccc /} {$ij.foo}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .bbb}\n"
+ " {$ij.boo} {$ij.goo} {call .ddd /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ccc}\n"
+ " {$ij.boo} {$ij.moo + $ij.woo}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ddd}\n"
+ " {$ij.boo} {$ij.moo} {round($ij.zoo)}\n"
+ "{/template}\n";
SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(fileContent).parse().fileSet();
TemplateRegistry templateRegistry = new TemplateRegistry(soyTree, FAIL);
TemplateNode aaa = soyTree.getChild(0).getChild(0);
TemplateNode bbb = soyTree.getChild(0).getChild(1);
TemplateNode ccc = soyTree.getChild(0).getChild(2);
TemplateNode ddd = soyTree.getChild(0).getChild(3);
// Test with exec(aaa).
// Exercises: processCalleeHelper case 5 with incorporateCalleeVisitInfo case 1 (aaa -> bbb).
FindIjParamsVisitor visitor = new FindIjParamsVisitor(templateRegistry);
visitor.exec(aaa);
assertThat(visitor.exec(ddd).ijParamToCalleesMultimap).hasSize(3);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap).hasSize(3);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap).hasSize(5);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap).hasSize(10);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap.keySet()).hasSize(6);
// Test with exec(bbb) then exec(aaa).
// Exercises: processCalleeHelper case 1 (aaa -> bbb).
visitor = new FindIjParamsVisitor(templateRegistry);
visitor.exec(bbb);
assertThat(visitor.exec(ddd).ijParamToCalleesMultimap).hasSize(3);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap).hasSize(5);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap.keySet()).hasSize(4);
visitor.exec(aaa);
assertThat(visitor.exec(ddd).ijParamToCalleesMultimap).hasSize(3);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap).hasSize(3);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap).hasSize(5);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap).hasSize(10);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap.keySet()).hasSize(6);
}
@Test
public void testTwoPathsToSameTemplate() {
// aaa -> {bbb, ccc}, ccc -> bbb.
String fileContent =
""
+ "{namespace ns autoescape=\"deprecated-noncontextual\"}\n"
+ "\n"
+ "/***/\n"
+ "{template .aaa}\n"
+ " {call .bbb /} {$ij.boo} {call .ccc /} {$ij.foo}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .bbb}\n"
+ " {$ij.boo} {$ij.goo}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ccc}\n"
+ " {$ij.boo} {$ij.moo + $ij.woo} {call .bbb /}\n"
+ "{/template}\n";
SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(fileContent).parse().fileSet();
TemplateRegistry templateRegistry = new TemplateRegistry(soyTree, FAIL);
TemplateNode aaa = soyTree.getChild(0).getChild(0);
TemplateNode bbb = soyTree.getChild(0).getChild(1);
TemplateNode ccc = soyTree.getChild(0).getChild(2);
// Test with exec(aaa).
// Exercises: processCalleeHelper case 4 with incorporateCalleeVisitInfo case 1 (ccc -> bbb).
FindIjParamsVisitor visitor = new FindIjParamsVisitor(templateRegistry);
visitor.exec(aaa);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap).hasSize(2);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap).hasSize(5);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap).hasSize(7);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap.keySet()).hasSize(5);
}
@Test
public void testSimpleRecursion() {
// Tests direct recursion (cycle of 1) and indirect recursion with a cycle of 2.
// aaa -> bbb, bbb -> {bbb, ccc}, ccc -> bbb.
String fileContent =
""
+ "{namespace ns autoescape=\"deprecated-noncontextual\"}\n"
+ "\n"
+ "/***/\n"
+ "{template .aaa}\n"
+ " {call .bbb /} {$ij.boo} {$ij.foo}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .bbb}\n"
+ " {$ij.boo} {$ij.goo} {call .bbb /} {call .ccc /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ccc}\n"
+ " {$ij.boo} {call .bbb /} {$ij.moo}\n"
+ "{/template}\n";
SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(fileContent).parse().fileSet();
TemplateRegistry templateRegistry = new TemplateRegistry(soyTree, FAIL);
TemplateNode aaa = soyTree.getChild(0).getChild(0);
TemplateNode bbb = soyTree.getChild(0).getChild(1);
TemplateNode ccc = soyTree.getChild(0).getChild(2);
// Test with exec(aaa).
// Exercises: processCalleeHelper case 2 (bbb -> bbb).
// Exercises: processCalleeHelper case 3 (ccc -> bbb).
// Exercises: processCalleeHelper case 5 with incorporateCalleeVisitInfo case 2 (bbb -> ccc).
FindIjParamsVisitor visitor = new FindIjParamsVisitor(templateRegistry);
visitor.exec(aaa);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap).hasSize(4);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap.keySet()).hasSize(3);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap).hasSize(4);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap.keySet()).hasSize(3);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap).hasSize(6);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap.keySet()).hasSize(4);
}
@Test
public void testLargerRecursiveCycle() {
// Tests indirect recursion with a cycle of 3.
// aaa -> bbb, bbb -> ccc, ccc -> aaa.
String fileContent =
""
+ "{namespace ns autoescape=\"deprecated-noncontextual\"}\n"
+ "\n"
+ "/***/\n"
+ "{template .aaa}\n"
+ " {$ij.foo} {$ij.boo} {call .bbb /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .bbb}\n"
+ " {$ij.goo} {call .ccc /} {$ij.boo}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ccc}\n"
+ " {call .aaa /} {$ij.moo} {$ij.boo}\n"
+ "{/template}\n";
SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(fileContent).parse().fileSet();
TemplateRegistry templateRegistry = new TemplateRegistry(soyTree, FAIL);
TemplateNode aaa = soyTree.getChild(0).getChild(0);
TemplateNode bbb = soyTree.getChild(0).getChild(1);
TemplateNode ccc = soyTree.getChild(0).getChild(2);
// Test with exec(aaa).
// Exercises: processCalleeHelper case 3 (ccc-> aaa).
// Exercises: processCalleeHelper case 5 with incorporateCalleeVisitInfo case 3 (bbb -> ccc).
// Exercises: processCalleeHelper case 5 with incorporateCalleeVisitInfo case 2 (aaa -> bbb).
FindIjParamsVisitor visitor = new FindIjParamsVisitor(templateRegistry);
visitor.exec(aaa);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap).hasSize(6);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap).hasSize(6);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap).hasSize(6);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap.keySet()).hasSize(4);
}
@Test
public void testTwoPathsToSameRecursiveCycle() {
// aaa -> {bbb, ccc}, bbb -> ddd, ccc -> ddd, ddd -> bbb.
String fileContent =
""
+ "{namespace ns autoescape=\"deprecated-noncontextual\"}\n"
+ "\n"
+ "/***/\n"
+ "{template .aaa}\n"
+ " {$ij.boo} {$ij.foo} {call .bbb /} {call .ccc /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .bbb}\n"
+ " {$ij.boo} {$ij.goo} {call .ddd /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ccc}\n"
+ " {$ij.boo} {$ij.moo} {call .ddd /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ddd}\n"
+ " {$ij.boo} {$ij.too} {call .bbb /}\n"
+ "{/template}\n";
SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(fileContent).parse().fileSet();
TemplateRegistry templateRegistry = new TemplateRegistry(soyTree, FAIL);
TemplateNode aaa = soyTree.getChild(0).getChild(0);
TemplateNode bbb = soyTree.getChild(0).getChild(1);
TemplateNode ccc = soyTree.getChild(0).getChild(2);
TemplateNode ddd = soyTree.getChild(0).getChild(3);
// Test with exec(aaa).
// Exercises: processCalleeHelper case 4 with incorporateCalleeVisitInfo case 4 (ccc -> ddd).
FindIjParamsVisitor visitor = new FindIjParamsVisitor(templateRegistry);
visitor.exec(aaa);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap).hasSize(4);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap.keySet()).hasSize(3);
assertThat(visitor.exec(ddd).ijParamToCalleesMultimap).hasSize(4);
assertThat(visitor.exec(ddd).ijParamToCalleesMultimap.keySet()).hasSize(3);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap).hasSize(6);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap).hasSize(8);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap.keySet()).hasSize(5);
}
@Test
public void testSmallerRecursiveCycleInLargerRecursiveCycle() {
// aaa -> {bbb, ccc}, bbb -> aaa, ccc -> bbb.
String fileContent =
""
+ "{namespace ns autoescape=\"deprecated-noncontextual\"}\n"
+ "\n"
+ "/***/\n"
+ "{template .aaa}\n"
+ " {$ij.foo} {$ij.boo} {call .bbb /} {call .ccc /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .bbb}\n"
+ " {$ij.goo} {$ij.boo} {call .aaa /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ccc}\n"
+ " {$ij.moo} {$ij.boo} {call .bbb /}\n"
+ "{/template}\n";
SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(fileContent).parse().fileSet();
TemplateRegistry templateRegistry = new TemplateRegistry(soyTree, FAIL);
TemplateNode aaa = soyTree.getChild(0).getChild(0);
TemplateNode bbb = soyTree.getChild(0).getChild(1);
TemplateNode ccc = soyTree.getChild(0).getChild(2);
// Test with exec(aaa).
// Exercises: processCalleeHelper case 4 with incorporateCalleeVisitInfo case 3 (ccc -> bbb).
FindIjParamsVisitor visitor = new FindIjParamsVisitor(templateRegistry);
visitor.exec(aaa);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap).hasSize(6);
assertThat(visitor.exec(ccc).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap).hasSize(6);
assertThat(visitor.exec(bbb).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap).hasSize(6);
assertThat(visitor.exec(aaa).ijParamToCalleesMultimap.keySet()).hasSize(4);
}
@Test
public void testExecForAllTemplates() {
// aaa -> {bbb, ccc}, bbb -> ddd.
String fileContent =
""
+ "{namespace ns autoescape=\"deprecated-noncontextual\"}\n"
+ "\n"
+ "/***/\n"
+ "{template .bbb}\n"
+ " {$ij.boo} {$ij.goo} {call .ddd /}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .aaa}\n"
+ " {call .bbb /} {$ij.boo} {call .ccc /} {$ij.foo}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ccc}\n"
+ " {$ij.boo} {$ij.moo + $ij.woo}\n"
+ "{/template}\n"
+ "\n"
+ "/***/\n"
+ "{template .ddd}\n"
+ " {$ij.boo} {$ij.moo} {round($ij.zoo)}\n"
+ "{/template}\n";
SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(fileContent).parse().fileSet();
TemplateRegistry templateRegistry = new TemplateRegistry(soyTree, FAIL);
TemplateNode bbb = soyTree.getChild(0).getChild(0);
TemplateNode aaa = soyTree.getChild(0).getChild(1);
TemplateNode ccc = soyTree.getChild(0).getChild(2);
TemplateNode ddd = soyTree.getChild(0).getChild(3);
ImmutableMap<TemplateNode, IjParamsInfo> templateToIjParamsInfoMap =
new FindIjParamsVisitor(templateRegistry).execOnAllTemplates(soyTree);
assertThat(templateToIjParamsInfoMap).hasSize(4);
assertThat(templateToIjParamsInfoMap.get(ddd).ijParamToCalleesMultimap).hasSize(3);
assertThat(templateToIjParamsInfoMap.get(ccc).ijParamToCalleesMultimap).hasSize(3);
assertThat(templateToIjParamsInfoMap.get(bbb).ijParamToCalleesMultimap).hasSize(5);
assertThat(templateToIjParamsInfoMap.get(bbb).ijParamToCalleesMultimap.keySet()).hasSize(4);
assertThat(templateToIjParamsInfoMap.get(aaa).ijParamToCalleesMultimap).hasSize(10);
assertThat(templateToIjParamsInfoMap.get(aaa).ijParamToCalleesMultimap.keySet()).hasSize(6);
}
}