aboutsummaryrefslogtreecommitdiff
path: root/src/test/java/com/google/escapevelocity/TemplateTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/google/escapevelocity/TemplateTest.java')
-rw-r--r--src/test/java/com/google/escapevelocity/TemplateTest.java169
1 files changed, 138 insertions, 31 deletions
diff --git a/src/test/java/com/google/escapevelocity/TemplateTest.java b/src/test/java/com/google/escapevelocity/TemplateTest.java
index 04bad8e..0503125 100644
--- a/src/test/java/com/google/escapevelocity/TemplateTest.java
+++ b/src/test/java/com/google/escapevelocity/TemplateTest.java
@@ -16,10 +16,12 @@
package com.google.escapevelocity;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.truth.Expect;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
@@ -27,6 +29,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
+import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
@@ -36,6 +39,7 @@ 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.exception.VelocityException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.RuntimeInstance;
import org.apache.velocity.runtime.log.NullLogChute;
@@ -45,7 +49,6 @@ import org.apache.velocity.runtime.resource.loader.ResourceLoader;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -57,7 +60,6 @@ import org.junit.runners.JUnit4;
public class TemplateTest {
@Rule public TestName testName = new TestName();
@Rule public Expect expect = Expect.create();
- @Rule public ExpectedException thrown = ExpectedException.none();
private RuntimeInstance velocityRuntimeInstance;
@@ -128,6 +130,31 @@ public class TemplateTest {
return writer.toString();
}
+ private void expectParseException(
+ String template,
+ String expectedMessageSubstring) {
+ Exception velocityException = null;
+ try {
+ SimpleNode parsedTemplate =
+ velocityRuntimeInstance.parse(new StringReader(template), testName.getMethodName());
+ VelocityContext velocityContext = new VelocityContext(new TreeMap<>());
+ velocityRuntimeInstance.render(
+ velocityContext, new StringWriter(), parsedTemplate.getTemplateName(), parsedTemplate);
+ fail("Velocity did not throw an exception for this template");
+ } catch (org.apache.velocity.runtime.parser.ParseException | VelocityException expected) {
+ velocityException = expected;
+ }
+ try {
+ Template.parseFrom(new StringReader(template));
+ fail("Velocity generated an exception, but EscapeVelocity did not: " + velocityException);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ } catch (ParseException expected) {
+ assertWithMessage("Got expected exception, but message did not match")
+ .that(expected).hasMessageThat().contains(expectedMessageSubstring);
+ }
+ }
+
@Test
public void empty() {
compare("");
@@ -210,13 +237,6 @@ public class TemplateTest {
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()));
@@ -228,23 +248,64 @@ public class TemplateTest {
}
@Test
+ public void substituteMethodNoArgsSyntheticOverride() {
+ compare("<$c.isEmpty()>", ImmutableMap.of("c", ImmutableSetMultimap.of()));
+ }
+
+ @Test
public void substituteMethodOneArg() {
compare("<$list.get(0)>", ImmutableMap.of("list", ImmutableList.of("foo")));
}
@Test
+ public void substituteMethodOneNullArg() {
+ // This should evaluate map.containsKey(map.get("absent")), which is map.containsKey(null).
+ compare("<$map.containsKey($map.get(\"absent\"))>", ImmutableMap.of("map", ImmutableMap.of()));
+ }
+
+ @Test
public void substituteMethodTwoArgs() {
compare("\n$s.indexOf(\"bar\", 2)\n", ImmutableMap.of("s", "barbarbar"));
}
@Test
- public void substituteMethodNoSynthetic() {
+ public void substituteMethodSyntheticOverloads() {
// If we aren't careful, we'll see both the inherited `Set<K> keySet()` from Map
// and the overridden `ImmutableSet<K> keySet()` in ImmutableMap.
compare("$map.keySet()", ImmutableMap.of("map", ImmutableMap.of("foo", "bar")));
}
@Test
+ public void substituteStaticMethod() {
+ compare("$Integer.toHexString(23)", ImmutableMap.of("Integer", Integer.class));
+ }
+
+ @Test
+ public void substituteStaticMethodAsInstanceMethod() {
+ compare("$i.toHexString(23)", ImmutableMap.of("i", 0));
+ }
+
+ @Test
+ public void substituteClassMethod() {
+ // This is Class.getName().
+ compare("$Integer.getName()", ImmutableMap.of("Integer", Integer.class));
+ }
+
+ /** See {@link #substituteClassOrInstanceMethod}. */
+ public static class GetName {
+ public static String getName() {
+ return "Noddy";
+ }
+ }
+
+ @Test
+ public void substituteClassOrInstanceMethod() {
+ // If the method exists as both an instance method on Class and a static method on the named
+ // class, it's the instance method that wins, so this is still Class.getName().
+ compare("$GetName.getName()", ImmutableMap.of("GetName", GetName.class));
+ }
+
+ @Test
public void substituteIndexNoBraces() {
compare("<$map[\"x\"]>", ImmutableMap.of("map", ImmutableMap.of("x", "y")));
}
@@ -254,6 +315,14 @@ public class TemplateTest {
compare("<${map[\"x\"]}>", ImmutableMap.of("map", ImmutableMap.of("x", "y")));
}
+ // Velocity allows you to write $map.foo instead of $map["foo"].
+ @Test
+ public void substituteMapProperty() {
+ compare("$map.foo", ImmutableMap.of("map", ImmutableMap.of("foo", "bar")));
+ // $map.empty is always equivalent to $map["empty"], never Map.isEmpty().
+ compare("$map.empty", ImmutableMap.of("map", ImmutableMap.of("empty", "foo")));
+ }
+
@Test
public void substituteIndexThenProperty() {
compare("<$map[2].name>", ImmutableMap.of("map", ImmutableMap.of(2, getClass())));
@@ -291,6 +360,29 @@ public class TemplateTest {
}
@Test
+ public void substituteInString() {
+ String template =
+ "#foreach ($a in $list)"
+ + "#set ($s = \"THING_${foreach.index}\")"
+ + "$s,$s;"
+ + "#end";
+ compare(template, ImmutableMap.of("list", ImmutableList.of(1, 2, 3)));
+ compare("#set ($s = \"$x\") <$s>", ImmutableMap.of("x", "fred"));
+ compare("#set ($s = \"==$x$y\") <$s>", ImmutableMap.of("x", "fred", "y", "jim"));
+ compare("#set ($s = \"$x$y==\") <$s>", ImmutableMap.of("x", "fred", "y", "jim"));
+ }
+
+ @Test
+ public void stringOperationsOnSubstitution() {
+ compare("#set ($s = \"a${b}c\") $s.length()", ImmutableMap.of("b", 23));
+ }
+
+ @Test
+ public void singleQuoteNoSubstitution() {
+ compare("#set ($s = 'a${b}c') x${s}y", ImmutableMap.of("b", 23));
+ }
+
+ @Test
public void simpleSet() {
compare("$x#set ($x = 17)#set ($y = 23) ($x, $y)", ImmutableMap.of("x", 1));
}
@@ -506,6 +598,18 @@ public class TemplateTest {
}
@Test
+ public void forEachIndex() {
+ String template =
+ "#foreach ($x in $list)"
+ + "[$foreach.index]"
+ + "#foreach ($y in $list)"
+ + "($foreach.index)==$x.$y=="
+ + "#end"
+ + "#end";
+ compare(template, ImmutableMap.of("list", ImmutableList.of("blim", "blam", "blum")));
+ }
+
+ @Test
public void setSpacing() {
// The spacing in the output from #set is eccentric.
compare("x#set ($x = 0)x");
@@ -550,6 +654,18 @@ public class TemplateTest {
compare(template, ImmutableMap.of("x", "tiddly"));
}
+ @Test
+ public void macroWithCommaSeparatedArgs() {
+ String template =
+ "$x\n"
+ + "#macro (m, $x, $y)\n"
+ + " #if ($x < $y) less #else greater #end\n"
+ + "#end\n"
+ + "#m(17 23) #m(23 17) #m(17 17)\n"
+ + "$x";
+ compare(template, ImmutableMap.of("x", "tiddly"));
+ }
+
/**
* Tests defining a macro inside a conditional. This proves that macros are not evaluated in the
* main control flow, but rather are extracted at parse time. It also tests what happens if there
@@ -706,45 +822,36 @@ public class TemplateTest {
}
@Test
- public void badBraceReference() throws IOException {
+ public void badBraceReference() {
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));
+ expectParseException(template, "Expected }, on line 3, at text starting: .!}baz");
}
@Test
- public void undefinedMacro() throws IOException {
+ public void undefinedMacro() {
String template = "#oops()";
- thrown.expect(ParseException.class);
- thrown.expectMessage("#oops is neither a standard directive nor a macro that has been defined");
- Template.parseFrom(new StringReader(template));
+ expectParseException(
+ template,
+ "#oops is neither a standard directive nor a macro that has been defined");
}
@Test
- public void macroArgumentMismatch() throws IOException {
+ public void macroArgumentMismatch() {
String template =
"#macro (twoArgs $a $b) $a $b #end\n"
+ "#twoArgs(23)\n";
- thrown.expect(ParseException.class);
- thrown.expectMessage("Wrong number of arguments to #twoArgs: expected 2, got 1");
- Template.parseFrom(new StringReader(template));
+ expectParseException(template, "Wrong number of arguments to #twoArgs: expected 2, got 1");
}
@Test
- public void unclosedBlockQuote() throws IOException {
+ public void unclosedBlockQuote() {
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));
+ expectParseException(template, "Unterminated #[[ - did not see matching ]]#, on line 2");
}
@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));
+ public void unclosedBlockComment() {
+ compare("foo\nbar #*\nblah\nblah");
}
/**