/* * Copyright 2015 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.sharedpasses.opti; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.template.soy.SoyFileSetParser.ParseResult; import com.google.template.soy.SoyFileSetParserBuilder; import com.google.template.soy.SoyModule; import com.google.template.soy.base.SourceLocation; import com.google.template.soy.error.ExplodingErrorReporter; import com.google.template.soy.soytree.ForNode; import com.google.template.soy.soytree.MsgFallbackGroupNode; import com.google.template.soy.soytree.MsgNode; import com.google.template.soy.soytree.MsgPlaceholderNode; import com.google.template.soy.soytree.RawTextNode; import com.google.template.soy.soytree.SoyFileSetNode; import com.google.template.soy.soytree.SoyNode.StandaloneNode; import com.google.template.soy.soytree.TemplateNode; import com.google.template.soy.soytree.TemplateRegistry; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author Kai Huang */ @RunWith(JUnit4.class) public class SimplifyVisitorTest { @Test public void testCombineConsecutiveRawTextNodes() throws Exception { String soyCode = "{@param boo : ?}\n" + "blah{$boo}blah" + "{for $i in range(5)}" + " blah{$boo}blah" + "{/for}"; SoyFileSetNode soyTree = SoyFileSetParserBuilder.forTemplateContents(soyCode).parse().fileSet(); TemplateNode template = soyTree.getChild(0).getChild(0); ForNode forNode = (ForNode) template.getChild(3); forNode.addChild(new RawTextNode(0, "bleh", SourceLocation.UNKNOWN)); forNode.addChild(new RawTextNode(0, "bluh", SourceLocation.UNKNOWN)); template.addChild(0, new RawTextNode(0, "bleh", SourceLocation.UNKNOWN)); template.addChild(0, new RawTextNode(0, "bluh", SourceLocation.UNKNOWN)); assertEquals(6, template.numChildren()); assertEquals(5, forNode.numChildren()); SimplifyVisitor simplifyVisitor = INJECTOR.getInstance(SimplifyVisitor.class); simplifyVisitor.simplify(soyTree, new TemplateRegistry(soyTree, ExplodingErrorReporter.get())); assertEquals(4, template.numChildren()); assertEquals(3, forNode.numChildren()); assertEquals("bluhblehblah", ((RawTextNode) template.getChild(0)).getRawText()); assertEquals("blahblehbluh", ((RawTextNode) forNode.getChild(2)).getRawText()); } @Test public void testMsgBlockNodeChildrenAreNotReplaced() throws Exception { String soyFileContent = "{namespace boo autoescape=\"deprecated-noncontextual\"}\n" + "\n" + "{template .foo}\n" + "\n" + " {msg desc=\"\"}\n" + " blah\n" + " {'blah'}\n" + " blah\n" + " {call .aaa /}\n" + " blah\n" + " <div class=\"{call .aaa /}\">\n" + " </div>\n" + " blah\n" + " {/msg}\n" + "{/template}\n" + "\n" + "/***/\n" + "{template .aaa}\n" + " blah\n" + "{/template}"; MsgNode msgNode = ((MsgFallbackGroupNode) simplifySoyFiles(soyFileContent).getChild(0).getChild(0).getChild(0)) .getChild(0); assertEquals(8, msgNode.numChildren()); // The MsgPlaceholderNode children are not replaced. assertTrue(msgNode.getChild(1) instanceof MsgPlaceholderNode); assertTrue(msgNode.getChild(3) instanceof MsgPlaceholderNode); assertTrue(msgNode.getChild(5) instanceof MsgPlaceholderNode); assertTrue(msgNode.getChild(6) instanceof MsgPlaceholderNode); // But the contents within the MsgPlaceholderNode children can be replaced. assertTrue(((MsgPlaceholderNode) msgNode.getChild(1)).getChild(0) instanceof RawTextNode); } @Test public void testSimplifyPrintNode() throws Exception { String soyCode; soyCode = "{'foo'}"; assertEquals("foo", simplifySoyCode(soyCode).get(0).toSourceString()); soyCode = "{'<b>&</b>' |escapeHtml}"; assertEquals("<b>&</b>", simplifySoyCode(soyCode).get(0).toSourceString()); soyCode = "{'<b>&</b>' |escapeHtml |insertWordBreaks:5}"; assertEquals("<b>&<<wbr>/b>", simplifySoyCode(soyCode).get(0).toSourceString()); // Doesn't simplify PrintNode with non-constant expression (but expression is simplified). soyCode = "{@param boo : ?}\n" + "{1 + 3 + $boo}"; assertEquals("{4 + $boo}", simplifySoyCode(soyCode).get(0).toSourceString()); // formatNum is not annotated as a SoyPurePrintDirective, so it should not be simplified. soyCode = "{5 |formatNum}"; assertEquals("{5 |formatNum}", simplifySoyCode(soyCode).get(0).toSourceString()); // Doesn't simplify PrintNode with non-constant directive arg. soyCode = "{@param boo : ?}\n" + "{'0123456789' |insertWordBreaks:$boo}"; assertEquals( "{'0123456789' |insertWordBreaks:$boo}", simplifySoyCode(soyCode).get(0).toSourceString()); } @Test public void testSimplifyIfNode() throws Exception { String soyCode; soyCode = "{if not false}\n" + " 111\n" + "{/if}\n" + "{if true and false}\n" + " 222\n" + "{/if}\n"; assertEquals("111", simplifySoyCode(soyCode).get(0).toSourceString()); soyCode = "{if ''}\n" + " 111\n" + "{elseif not 1}\n" + " 222\n" + "{else}\n" + " 333\n" + "{/if}\n"; assertEquals("333", simplifySoyCode(soyCode).get(0).toSourceString()); soyCode = "{if false}\n" + " 111\n" + "{elseif true}\n" + " 222\n" + "{else}\n" + " 333\n" + "{/if}\n"; assertEquals("222", simplifySoyCode(soyCode).get(0).toSourceString()); soyCode = "{@param boo : ?}\n" + "{if false}\n" + " 111\n" + "{elseif $boo}\n" + " 222\n" + "{elseif true}\n" + " 333\n" + "{else}\n" + " 444\n" + "{/if}\n"; assertEquals("{if $boo}222{else}333{/if}", simplifySoyCode(soyCode).get(0).toSourceString()); soyCode = "{@param boo : ?}\n" + "{if 0}\n" + " 111\n" + "{elseif 1}\n" + " {if true}\n" + " {if $boo}\n" + " 222\n" + " {elseif ''}\n" + " 333\n" + " {elseif 'blah'}\n" + " 444\n" + " {else}\n" + " 555\n" + " {/if}\n" + " {else}\n" + " 666\n" + " {/if}\n" + "{else}\n" + " 777\n" + "{/if}\n"; assertEquals("{if $boo}222{else}444{/if}", simplifySoyCode(soyCode).get(0).toSourceString()); } @Test public void testSimplifySwitchNode() throws Exception { String soyCode; soyCode = "{@param boo : ?}\n" + "{switch 1 + 2}\n" + " {case 1}111\n" + " {case 2, 3}222333\n" + " {case $boo}444\n" + " {default}goo\n" + "{/switch}\n"; assertEquals("222333", simplifySoyCode(soyCode).get(0).toSourceString()); soyCode = "{switch 1 + 2}\n" + " {case 1}111\n" + " {case 2}222\n" + " {default}333\n" + "{/switch}\n"; assertEquals("333", simplifySoyCode(soyCode).get(0).toSourceString()); soyCode = "{@param boo : ?}\n" + "{switch 1 + 2}\n" + " {case $boo}111\n" + " {case 2}222\n" + " {case 3}333\n" + " {default}444\n" + "{/switch}\n"; assertEquals( "{switch 3}{case $boo}111{default}333{/switch}", simplifySoyCode(soyCode).get(0).toSourceString()); } // ----------------------------------------------------------------------------------------------- // Helpers. private static final Injector INJECTOR = Guice.createInjector(new SoyModule()); private static List<StandaloneNode> simplifySoyCode(String soyCode) throws Exception { ParseResult parse = SoyFileSetParserBuilder.forTemplateContents(soyCode).parse(); SimplifyVisitor simplifyVisitor = INJECTOR.getInstance(SimplifyVisitor.class); simplifyVisitor.simplify(parse.fileSet(), parse.registry()); return parse.fileSet().getChild(0).getChild(0).getChildren(); } private static SoyFileSetNode simplifySoyFiles(String... soyFileContents) throws Exception { ParseResult parse = SoyFileSetParserBuilder.forFileContents(soyFileContents).parse(); SimplifyVisitor simplifyVisitor = INJECTOR.getInstance(SimplifyVisitor.class); simplifyVisitor.simplify(parse.fileSet(), parse.registry()); return parse.fileSet(); } }