/* * Copyright 2009 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.soytree; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; import com.google.template.soy.SoyFileSetParserBuilder; import com.google.template.soy.base.internal.IdGenerator; import com.google.template.soy.base.internal.IncrementingIdGenerator; import com.google.template.soy.base.internal.SoyFileKind; import com.google.template.soy.basetree.SyntaxVersion; import com.google.template.soy.error.ErrorReporter; import com.google.template.soy.error.ExplodingErrorReporter; import com.google.template.soy.exprtree.AbstractExprNodeVisitor; import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.ExprNode.ParentExprNode; import com.google.template.soy.exprtree.VarDefn; import com.google.template.soy.exprtree.VarRefNode; import com.google.template.soy.soyparse.SoyFileParser; import com.google.template.soy.soytree.SoyNode.StandaloneNode; import com.google.template.soy.soytree.defn.LocalVar; import com.google.template.soy.types.SoyTypeRegistry; import java.io.StringReader; import java.util.List; import junit.framework.TestCase; /** * Unit tests for SoyTreeUtils. * */ public final class SoyTreeUtilsTest extends TestCase { // ----------------------------------------------------------------------------------------------- // Tests for executing an ExprNode visitor on all expressions in a Soy tree. public void testVisitAllExprs() { String testFileContent = "{namespace boo autoescape=\"deprecated-noncontextual\"}\n" + "\n" + "/** @param items */\n" + "{template .foo}\n" + " {length($items) + 5}\n" // 5 nodes + " {foreach $item in $items}\n" // 2 nodes + " {$item.goo}\n" // 3 nodes + " {/foreach}\n" + "{/template}\n"; ErrorReporter boom = ExplodingErrorReporter.get(); SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(testFileContent) .errorReporter(boom) .parse() .fileSet(); CountingVisitor countingVisitor = new CountingVisitor(); SoyTreeUtils.execOnAllV2Exprs(soyTree, countingVisitor); CountingVisitor.Counts counts = countingVisitor.getCounts(); assertEquals(3, counts.numExecs); assertEquals(10, counts.numVisitedNodes); } /** * Visitor that counts the number of times {@code exec()} is called and the total number of nodes * visited over all of those calls. * * <p> Helper class for {@code testVisitAllExprs()}. */ private static class CountingVisitor extends AbstractExprNodeVisitor<Void> { public static class Counts { public int numExecs; public int numVisitedNodes; } private final Counts counts = new Counts(); public Counts getCounts() { return counts; } @Override public Void exec(ExprNode node) { counts.numExecs++; return super.exec(node); } @Override protected void visitExprNode(ExprNode node) { counts.numVisitedNodes++; if (node instanceof ParentExprNode) { visitChildren((ParentExprNode) node); } } } // ----------------------------------------------------------------------------------------------- // Tests for cloning. Adapted from Mike Samuel's CloneVisitorTest. private static final String SOY_SOURCE_FOR_TESTING_CLONING = Joiner.on('\n').join( "{namespace ns autoescape=\"deprecated-noncontextual\"}", "/** example for cloning. */", "{template .ex1 private=\"true\"}", " {@param a : ?}", " {@param b : ?}", " {@param c : ?}", " {@param v : ?}", " {@param x : ?}", " {@param start : ?}", " {@param end : ?}", " {@param cond0 : ?}", " {@param cond1 : ?}", " {@param items : ?}", " {@param world : ?}", " {@param foo : ?}", " Hello, World!", " {lb}{call foo data=\"all\"}{param x: $x /}{/call}{rb}", " {$x |escapeHtml}", " {if $cond0}", " {$a}", " {elseif $cond1}", " {print $b}", " {else}", " {$c}", " {/if}", " {switch $v}", " {case 0}", " Zero", " {default}", " Some", " {/switch}", " {literal}", " {interpreted literally/}", " {/literal}", " <style>{css $foo} {lb}color: red{rb}</style>", " {msg desc=\"test\"}<h1 class=\"howdy\">Hello, {$world}!</h1>{/msg}", " <ol>", " {foreach $item in $items}", " <li>{$item}</li>", " {ifempty}", " <li><i>Nothing to see here!", " {/foreach}", " {for $i in range($start, $end)}", " <li value={$i}>foo</li>", " {/for}", " </ol>", " {let $local : 'foo' /}", " {$local}", "{/template}"); public final void testClone() throws Exception { SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(SOY_SOURCE_FOR_TESTING_CLONING) .declaredSyntaxVersion(SyntaxVersion.V2_4) .parse() .fileSet(); SoyFileSetNode clone = SoyTreeUtils.cloneNode(soyTree); assertEquals(1, clone.numChildren()); assertEquals(clone.getChild(0).toSourceString(), soyTree.getChild(0).toSourceString()); // All the localvarnodes, there is one of each type ForNode forNode = Iterables.getOnlyElement(SoyTreeUtils.getAllNodesOfType(clone, ForNode.class)); ForeachNonemptyNode foreachNonemptyNode = Iterables.getOnlyElement(SoyTreeUtils.getAllNodesOfType(clone, ForeachNonemptyNode.class)); LetValueNode letValueNode = Iterables.getOnlyElement(SoyTreeUtils.getAllNodesOfType(clone, LetValueNode.class)); for (VarRefNode varRef : SoyTreeUtils.getAllNodesOfType(clone, VarRefNode.class)) { VarDefn defn = varRef.getDefnDecl(); LocalVar local; switch (varRef.getName()) { case "local": local = (LocalVar) defn; assertSame(letValueNode, local.declaringNode()); assertSame(letValueNode.getVar(), local); break; case "item": local = (LocalVar) defn; assertSame(foreachNonemptyNode, local.declaringNode()); assertSame(foreachNonemptyNode.getVar(), defn); break; case "i": local = (LocalVar) defn; assertSame(forNode, local.declaringNode()); assertSame(forNode.getVar(), defn); break; } } } public final void testCloneWithNewIds() throws Exception { IdGenerator nodeIdGen = new IncrementingIdGenerator(); SoyFileSetNode soyTree = new SoyFileSetNode(nodeIdGen.genId(), nodeIdGen); SoyFileNode soyFile = new SoyFileParser( new SoyTypeRegistry(), nodeIdGen, new StringReader(SOY_SOURCE_FOR_TESTING_CLONING), SoyFileKind.SRC, "test.soy", ExplodingErrorReporter.get()) .parseSoyFile(); soyTree.addChild(soyFile); SoyFileSetNode clone = SoyTreeUtils.cloneWithNewIds(soyTree, nodeIdGen); assertEquals(1, clone.numChildren()); assertFalse(clone.getId() == soyTree.getId()); assertEquals(clone.getChild(0).toSourceString(), soyFile.toSourceString()); } public final void testCloneListWithNewIds() throws Exception { IdGenerator nodeIdGen = new IncrementingIdGenerator(); SoyFileSetNode soyTree = new SoyFileSetNode(nodeIdGen.genId(), nodeIdGen); SoyFileNode soyFile = new SoyFileParser( new SoyTypeRegistry(), nodeIdGen, new StringReader(SOY_SOURCE_FOR_TESTING_CLONING), SoyFileKind.SRC, "test.soy", ExplodingErrorReporter.get()) .parseSoyFile(); soyTree.addChild(soyFile); TemplateNode template = soyFile.getChild(0); int numChildren = template.numChildren(); List<StandaloneNode> clones = SoyTreeUtils.cloneListWithNewIds(template.getChildren(), nodeIdGen); assertThat(clones).hasSize(numChildren); for (int i = 0; i < numChildren; i++) { StandaloneNode clone = clones.get(i); StandaloneNode child = template.getChild(i); assertFalse(clone.getId() == child.getId()); assertEquals(child.toSourceString(), clone.toSourceString()); } } public final void testMsgHtmlTagNode() throws Exception { IdGenerator nodeIdGen = new IncrementingIdGenerator(); SoyFileSetNode soyTree = new SoyFileSetNode(nodeIdGen.genId(), nodeIdGen); ErrorReporter boom = ExplodingErrorReporter.get(); SoyFileNode soyFile = new SoyFileParser( new SoyTypeRegistry(), nodeIdGen, new StringReader(SOY_SOURCE_FOR_TESTING_CLONING), SoyFileKind.SRC, "test.soy", boom) .parseSoyFile(); soyTree.addChild(soyFile); List<MsgHtmlTagNode> msgHtmlTagNodes = SoyTreeUtils.getAllNodesOfType(soyFile, MsgHtmlTagNode.class); for (MsgHtmlTagNode origMsgHtmlTagNode : msgHtmlTagNodes) { MsgHtmlTagNode clonedMsgHtmlTagNode = SoyTreeUtils.cloneNode(origMsgHtmlTagNode); assertEquals(clonedMsgHtmlTagNode.numChildren(), origMsgHtmlTagNode.numChildren()); assertEquals(clonedMsgHtmlTagNode.getId(), origMsgHtmlTagNode.getId()); assertEquals(clonedMsgHtmlTagNode.getFullTagText(), origMsgHtmlTagNode.getFullTagText()); assertEquals(clonedMsgHtmlTagNode.getLcTagName(), origMsgHtmlTagNode.getLcTagName()); assertEquals( clonedMsgHtmlTagNode.getSyntaxVersionUpperBound(), origMsgHtmlTagNode.getSyntaxVersionUpperBound()); } } }