diff options
Diffstat (limited to 'src/test/java/com/google/escapevelocity/TemplateTest.java')
-rw-r--r-- | src/test/java/com/google/escapevelocity/TemplateTest.java | 226 |
1 files changed, 209 insertions, 17 deletions
diff --git a/src/test/java/com/google/escapevelocity/TemplateTest.java b/src/test/java/com/google/escapevelocity/TemplateTest.java index bd769d6..0437734 100644 --- a/src/test/java/com/google/escapevelocity/TemplateTest.java +++ b/src/test/java/com/google/escapevelocity/TemplateTest.java @@ -14,24 +14,32 @@ package com.google.escapevelocity; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.truth.Expect; +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; +import java.util.function.Supplier; +import org.apache.commons.collections.ExtendedProperties; import org.apache.velocity.VelocityContext; +import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.RuntimeInstance; import org.apache.velocity.runtime.log.NullLogChute; import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.runtime.resource.Resource; +import org.apache.velocity.runtime.resource.loader.ResourceLoader; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -52,18 +60,21 @@ public class TemplateTest { private RuntimeInstance velocityRuntimeInstance; @Before - public void setUp() { - velocityRuntimeInstance = new RuntimeInstance(); + public void initVelocityRuntimeInstance() { + velocityRuntimeInstance = newVelocityRuntimeInstance(); + velocityRuntimeInstance.init(); + } + + private RuntimeInstance newVelocityRuntimeInstance() { + RuntimeInstance runtimeInstance = new RuntimeInstance(); // Ensure that $undefinedvar will produce an exception rather than outputting $undefinedvar. - velocityRuntimeInstance.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, "true"); - velocityRuntimeInstance.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, - new NullLogChute()); + runtimeInstance.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, "true"); // Disable any logging that Velocity might otherwise see fit to do. - velocityRuntimeInstance.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new NullLogChute()); - - velocityRuntimeInstance.init(); + runtimeInstance.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, new NullLogChute()); + runtimeInstance.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new NullLogChute()); + return runtimeInstance; } private void compare(String template) { @@ -71,7 +82,7 @@ public class TemplateTest { } private void compare(String template, Map<String, ?> vars) { - compare(template, Suppliers.ofInstance(vars)); + compare(template, () -> vars); } /** @@ -89,11 +100,13 @@ public class TemplateTest { try { escapeVelocityRendered = Template.parseFrom(new StringReader(template)).evaluate(escapeVelocityVars); - } catch (IOException e) { - throw new AssertionError(e); + } catch (Exception e) { + throw new AssertionError( + "EscapeVelocity failed, but Velocity succeeded and returned: <" + velocityRendered + ">", + e); } - String failure = "from velocity: <" + velocityRendered + ">\n" - + "from escape velocity: <" + escapeVelocityRendered + ">\n"; + String failure = "from Velocity: <" + velocityRendered + ">\n" + + "from EscapeVelocity: <" + escapeVelocityRendered + ">\n"; expect.withMessage(failure).that(escapeVelocityRendered).isEqualTo(velocityRendered); } @@ -124,17 +137,58 @@ public class TemplateTest { } @Test - public void comment() { + public void lineComment() { compare("line 1 ##\n line 2"); } @Test + public void blockComment() { + compare("line 1 #* blah\n line 2 * #\n line 3 *# \n line 4"); + compare("foo #*# bar *# baz"); + compare("foo #* one *# #* two *# #* three *#"); + compare("foo #** bar *# #* baz **#"); + } + + @Test + public void ignoreHashIfNotDirectiveOrComment() { + compare("# if is not a directive because of the space"); + compare("#<foo>"); + compare("# <foo>"); + compare("${foo}#${bar}", ImmutableMap.of("foo", "xxx", "bar", "yyy")); + } + + @Test + public void blockQuote() { + compare("#[[]]#"); + compare("x#[[]]#y"); + compare("#[[$notAReference #notADirective]]#"); + compare("#[[ [[ ]] ]# ]]#"); + compare("#[ foo"); + compare("x\n #[[foo\nbar\nbaz]]#y"); + } + + @Test public void substituteNoBraces() { compare(" $x ", ImmutableMap.of("x", 1729)); compare(" ! $x ! ", ImmutableMap.of("x", 1729)); } @Test + public void dollarWithoutId() { + compare(" $? "); + compare(" $$ "); + compare(" $. "); + compare(" $[ "); + } + + @Test + public void doubleDollar() { + // The first $ is plain text and the second one starts a reference. + compare(" $$foo ", ImmutableMap.of("foo", true)); + compare(" $${foo} ", ImmutableMap.of("foo", true)); + } + + @Test public void substituteWithBraces() { compare("a${x}\nb", ImmutableMap.of("x", "1729")); } @@ -150,6 +204,18 @@ public class TemplateTest { } @Test + public void substituteNotPropertyId() { + compare("$foo.!", ImmutableMap.of("foo", false)); + } + + /* TODO(emcmanus): make this work. + @Test + public void substituteNotPropertyId() { + compare("$foo.!", ImmutableMap.of("foo", false)); + } + */ + + @Test public void substituteNestedProperty() { compare("\n$t.name.empty\n", ImmutableMap.of("t", Thread.currentThread())); } @@ -206,6 +272,10 @@ public class TemplateTest { compare("<AZaz-foo_bar23>", ImmutableMap.of("AZaz-foo_bar23", "(P)")); } + /** + * A public class with a public {@code get} method that has one argument. That means instances can + * be used like {@code $indexable["foo"]}. + */ public static class Indexable { public String get(String y) { return "[" + y + "]"; @@ -319,7 +389,7 @@ public class TemplateTest { /** * Tests the surprising definition of equality mentioned in - * {@link ExpressionNode.EqualsExpressionNode}. + * {@link ExpressionNode.BinaryExpressionNode}. */ @Test public void funkyEquals() { @@ -452,6 +522,7 @@ public class TemplateTest { compare("x #set($x = 0) #set($x = 0) #set($x = 0) y"); compare("x ## comment\n #set($x = 0) y"); + compare("x #* comment *# #set($x = 0) y"); } @Test @@ -633,6 +704,14 @@ public class TemplateTest { } @Test + public void badBraceReference() throws IOException { + String template = "line 1\nline 2\nbar${foo.!}baz"; + thrown.expect(ParseException.class); + thrown.expectMessage("Expected }, on line 3, at text starting: .!}baz"); + Template.parseFrom(new StringReader(template)); + } + + @Test public void undefinedMacro() throws IOException { String template = "#oops()"; thrown.expect(ParseException.class); @@ -650,4 +729,117 @@ public class TemplateTest { Template.parseFrom(new StringReader(template)); } + @Test + public void unclosedBlockQuote() throws IOException { + String template = "foo\nbar #[[\nblah\nblah"; + thrown.expect(ParseException.class); + thrown.expectMessage("Unterminated #[[ - did not see matching ]]#, on line 2"); + Template.parseFrom(new StringReader(template)); + } + + @Test + public void unclosedBlockComment() throws IOException { + String template = "foo\nbar #*\nblah\nblah"; + thrown.expect(ParseException.class); + thrown.expectMessage("Unterminated #* - did not see matching *#, on line 2"); + Template.parseFrom(new StringReader(template)); + } + + /** + * A Velocity ResourceLoader that looks resources up in a map. This allows us to test directives + * that read "resources", for example {@code #parse}, without needing to make separate files to + * put them in. + */ + private static final class MapResourceLoader extends ResourceLoader { + private final ImmutableMap<String, String> resourceMap; + + MapResourceLoader(ImmutableMap<String, String> resourceMap) { + this.resourceMap = resourceMap; + } + + @Override + public void init(ExtendedProperties configuration) { + } + + @Override + public InputStream getResourceStream(String source) { + String resource = resourceMap.get(source); + if (resource == null) { + throw new ResourceNotFoundException(source); + } + return new ByteArrayInputStream(resource.getBytes(StandardCharsets.ISO_8859_1)); + } + + @Override + public boolean isSourceModified(Resource resource) { + return false; + } + + @Override + public long getLastModified(Resource resource) { + return 0; + } + }; + + private String renderWithResources( + String templateResourceName, + ImmutableMap<String, String> resourceMap, + ImmutableMap<String, String> vars) { + MapResourceLoader mapResourceLoader = new MapResourceLoader(resourceMap); + RuntimeInstance runtimeInstance = newVelocityRuntimeInstance(); + runtimeInstance.setProperty("resource.loader", "map"); + runtimeInstance.setProperty("map.resource.loader.instance", mapResourceLoader); + runtimeInstance.init(); + org.apache.velocity.Template velocityTemplate = + runtimeInstance.getTemplate(templateResourceName); + StringWriter velocityWriter = new StringWriter(); + VelocityContext velocityContext = new VelocityContext(new TreeMap<>(vars)); + velocityTemplate.merge(velocityContext, velocityWriter); + return velocityWriter.toString(); + } + + @Test + public void parseDirective() throws IOException { + // If outer.vm does #parse("nested.vm"), then we should be able to #set a variable in + // nested.vm and use it in outer.vm, and we should be able to define a #macro in nested.vm + // and call it in outer.vm. + ImmutableMap<String, String> resources = ImmutableMap.of( + "outer.vm", + "first line\n" + + "#parse (\"nested.vm\")\n" + + "<#decorate (\"left\" \"right\")>\n" + + "$baz skidoo\n" + + "last line\n", + "nested.vm", + "nested template first line\n" + + "[#if ($foo == $bar) equal #else not equal #end]\n" + + "#macro (decorate $a $b) < $a | $b > #end\n" + + "#set ($baz = 23)\n" + + "nested template last line\n"); + + ImmutableMap<String, String> vars = ImmutableMap.of("foo", "foovalue", "bar", "barvalue"); + + String velocityResult = renderWithResources("outer.vm", resources, vars); + + Template.ResourceOpener resourceOpener = resourceName -> { + String resource = resources.get(resourceName); + if (resource == null) { + throw new FileNotFoundException(resourceName); + } + return new StringReader(resource); + }; + Template template = Template.parseFrom("outer.vm", resourceOpener); + + String result = template.evaluate(vars); + assertThat(result).isEqualTo(velocityResult); + + ImmutableMap<String, String> badVars = ImmutableMap.of("foo", "foovalue"); + try { + template.evaluate(badVars); + fail(); + } catch (EvaluationException e) { + assertThat(e).hasMessageThat().isEqualTo( + "In expression on line 2 of nested.vm: Undefined reference $bar"); + } + } } |