package com.github.mustachejava;
import com.github.mustachejava.codes.IterableCode;
import com.github.mustachejava.codes.PartialCode;
import com.github.mustachejava.functions.CommentFunction;
import com.github.mustachejava.reflect.ReflectionObjectHandler;
import com.github.mustachejava.reflect.SimpleObjectHandler;
import com.github.mustachejava.resolver.DefaultResolver;
import com.github.mustachejava.util.CapturingMustacheVisitor;
import com.github.mustachejavabenchmarks.JsonCapturer;
import com.github.mustachejavabenchmarks.JsonInterpreterTest;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import junit.framework.TestCase;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.MappingJsonFactory;
import org.junit.Assert;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Tests for the compiler.
* <p/>
* User: sam
* Date: May 3, 2010
* Time: 10:23:54 AM
*/
public class InterpreterTest extends TestCase {
protected File root;
public void testSimple() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = createMustacheFactory();
Mustache m = c.compile("simple.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String name = "Chris";
int value = 10000;
int taxed_value() {
return (int) (this.value - (this.value * 0.4));
}
boolean in_ca = true;
});
assertEquals(getContents(root, "simple.txt"), sw.toString());
}
private static class LocalizedMustacheResolver extends DefaultResolver {
private final Locale locale;
LocalizedMustacheResolver(File root, Locale locale) {
super(root);
this.locale = locale;
}
@Override
public Reader getReader(String resourceName) {
// Build resource name with locale suffix
int index = resourceName.lastIndexOf('.');
String newResourceName;
if (index == -1) {
newResourceName = resourceName;
} else {
newResourceName = resourceName.substring(0, index) + "_" + locale.toLanguageTag() + resourceName.substring(index);
}
// First look with locale
Reader reader = super.getReader(newResourceName);
if(reader == null) {
// Fallback to non-localized resourceName
reader = super.getReader(resourceName);
}
return reader;
}
}
public void testSimpleI18N() throws MustacheException, IOException, ExecutionException, InterruptedException {
{
MustacheFactory c = new DefaultMustacheFactory(new LocalizedMustacheResolver(root, Locale.KOREAN));
Mustache m = c.compile("simple.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String name = "Chris";
int value = 10000;
int taxed_value() {
return (int) (this.value - (this.value * 0.4));
}
boolean in_ca = true;
});
assertEquals(getContents(root, "simple_ko.txt"), sw.toString());
}
{
MustacheFactory c = new DefaultMustacheFactory(new LocalizedMustacheResolver(root, Locale.JAPANESE));
Mustache m = c.compile("simple.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String name = "Chris";
int value = 10000;
int taxed_value() {
return (int) (this.value - (this.value * 0.4));
}
boolean in_ca = true;
});
assertEquals(getContents(root, "simple.txt"), sw.toString());
}
}
public void testRootCheck() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = createMustacheFactory();
try {
Mustache m = c.compile("../../../pom.xml");
fail("Should have failed to compile");
} catch (MustacheException e) {
// Success
}
}
public void testSimpleFiltered() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = new DefaultMustacheFactory(root) {
/**
* Override this method to apply any filtering to text that will appear
* verbatim in the output template.
*
*
*
* @param appended
* @param startOfLine
* @return
*/
@Override
public String filterText(String appended, boolean startOfLine) {
// Remove duplicate spaces, leading spaces and trailing spaces
if (startOfLine) {
appended = appended.replaceAll("^[\t ]+", "");
}
return appended
.replaceAll("[ \t]+", " ")
.replaceAll("[ \n\t]*\n[ \n\t]*", "\n");
}
};
Mustache m = c.compile("simplefiltered.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String name = "Chris";
int value = 10000;
int taxed_value() {
return (int) (this.value - (this.value * 0.4));
}
boolean in_ca = true;
});
assertEquals(getContents(root, "simplefiltered.txt"), sw.toString());
}
public void testTypedSimple() throws MustacheException, IOException, ExecutionException, InterruptedException {
final Object scope = new Object() {
String name = "Chris";
int value = 10000;
class MyObject {
int taxed_value() {
return (int) (value - (value * 0.4));
}
String fred = "";
}
MyObject in_ca = new MyObject();
boolean test = false;
};
DefaultMustacheFactory c = new DefaultMustacheFactory(root);
c.setObjectHandler(new TypeCheckingHandler());
Mustache m = c.compile("simple.html");
StringWriter sw = new StringWriter();
m.execute(sw, scope.getClass()).flush();
assertEquals(getContents(root, "simpletyped.txt"), sw.toString());
}
protected DefaultMustacheFactory createMustacheFactory() {
return new DefaultMustacheFactory(root);
}
public void testRecurision() throws IOException {
MustacheFactory c = createMustacheFactory();
Mustache m = c.compile("recursion.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Object value = new Object() {
boolean value = false;
};
});
assertEquals(getContents(root, "recursion.txt"), sw.toString());
}
public void testRecursionWithInheritance() throws IOException {
MustacheFactory c = createMustacheFactory();
Mustache m = c.compile("recursion_with_inheritance.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Object value = new Object() {
boolean value = false;
};
});
assertEquals(getContents(root, "recursion.txt"), sw.toString());
}
public void testPartialRecursionWithInheritance() throws IOException {
MustacheFactory c = createMustacheFactory();
Mustache m = c.compile("recursive_partial_inheritance.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Object test = new Object() {
boolean test = false;
};
});
assertEquals(getContents(root, "recursive_partial_inheritance.txt"), sw.toString());
}
public void testSimplePragma() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = createMustacheFactory();
Mustache m = c.compile("simplepragma.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String name = "Chris";
int value = 10000;
int taxed_value() {
return (int) (this.value - (this.value * 0.4));
}
boolean in_ca = true;
});
assertEquals(getContents(root, "simple.txt"), sw.toString());
}
private class OkGenerator {
public boolean isItOk() {
return true;
}
}
public void testNestedAccessWithSimpleObjectHandler() throws IOException {
assertEquals(getOutput(false), getOutput(true));
}
private String getOutput(final boolean setObjectHandler) {
final DefaultMustacheFactory mustacheFactory = new DefaultMustacheFactory();
if (setObjectHandler) {
mustacheFactory.setObjectHandler(new SimpleObjectHandler());
}
final Mustache defaultMustache = mustacheFactory.compile(new StringReader("{{#okGenerator.isItOk}}{{okGenerator.isItOk}}{{/okGenerator.isItOk}}"), "Test template");
final Map<String, Object> params = new HashMap<String, Object>();
params.put("okGenerator", new OkGenerator());
final Writer writer = new StringWriter();
defaultMustache.execute(writer, params);
return writer.toString();
}
public void testClosingReader() {
final AtomicBoolean closed = new AtomicBoolean();
StringReader reader = new StringReader("{{test") {
@Override
public void close() {
closed.set(true);
}
};
MustacheFactory mf = new DefaultMustacheFactory();
try {
mf.compile(reader, "test");
fail("Should have thrown an exception");
} catch (MustacheException me) {
// The reader should be closed now
assertEquals(true, closed.get());
}
}
public void testMultipleWrappers() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = createMustacheFactory();
Mustache m = c.compile("simple.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String name = "Chris";
int value = 10000;
Object o = new Object() {
int taxed_value() {
return (int) (value - (value * 0.4));
}
String fred = "test";
};
Object in_ca = Arrays.asList(
o, new Object() {
int taxed_value = (int) (value - (value * 0.2));
},
o
);
});
assertEquals(getContents(root, "simplerewrap.txt"), sw.toString());
}
public void testNestedLatchesIterable() throws IOException {
DefaultMustacheFactory c = createMustacheFactory();
c.setExecutorService(Executors.newCachedThreadPool());
Mustache m = c.compile("latchedtestiterable.html");
StringWriter sw = new StringWriter();
final StringBuffer sb = new StringBuffer();
final CountDownLatch cdl1 = new CountDownLatch(1);
final CountDownLatch cdl2 = new CountDownLatch(1);
m.execute(sw, new Object() {
Iterable list = Arrays.asList(
new Callable<Object>() {
@Override
public Object call() throws Exception {
cdl1.await();
sb.append("How");
return "How";
}
},
new Callable<Object>() {
@Override
public Object call() throws Exception {
cdl2.await();
sb.append("are");
cdl1.countDown();
return "are";
}
},
new Callable<Object>() {
@Override
public Object call() throws Exception {
sb.append("you?");
cdl2.countDown();
return "you?";
}
}
);
}).close();
assertEquals(getContents(root, "latchedtest.txt"), sw.toString());
assertEquals("you?areHow", sb.toString());
}
public void testConcurrency() throws IOException {
DefaultMustacheFactory c = createMustacheFactory();
c.setExecutorService(Executors.newCachedThreadPool());
Mustache m = c.compile(new StringReader("{{a}} {{#caps}}{{b}}{{/caps}} {{c}}"), "concurrency");
StringWriter sw = new StringWriter();
long start = System.currentTimeMillis();
Writer execute = m.execute(sw, new Object() {
Callable<Object> a = new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(300);
return "How";
}
};
Callable<Object> b = new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(200);
return "are";
}
};
Callable<Object> c = new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(100);
return "you?";
}
};
Callable<Function> caps = new Callable<Function>() {
@Override
public Function call() throws Exception {
return new Function() {
@Override
public Object apply(Object o) {
return o.toString().toUpperCase();
}
};
}
};
});
execute.close();
assertTrue("Time < 600ms", System.currentTimeMillis() - start < 600);
assertEquals("How ARE you?", sw.toString());
}
public void testNestedLatches() throws IOException {
DefaultMustacheFactory c = createMustacheFactory();
c.setExecutorService(Executors.newCachedThreadPool());
Mustache m = c.compile("latchedtest.html");
StringWriter sw = new StringWriter();
Writer execute = m.execute(sw, new Object() {
Callable<Object> nest = new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(300);
return "How";
}
};
Callable<Object> nested = new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(200);
return "are";
}
};
Callable<Object> nestest = new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(100);
return "you?";
}
};
});
execute.close();
assertEquals("<outer>\n<inner>How</inner>\n<inner>are</inner>\n<inner>you?</inner>\n</outer>\n", sw.toString());
}
public void testBrokenSimple() throws MustacheException, IOException, ExecutionException, InterruptedException {
try {
MustacheFactory c = init();
Mustache m = c.compile("brokensimple.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String name = "Chris";
int value = 10000;
int taxed_value() {
return (int) (this.value - (this.value * 0.4));
}
boolean in_ca = true;
});
fail("Should have failed: " + sw.toString());
} catch (Exception e) {
// success
}
}
public void testIsNotEmpty() throws IOException {
MustacheFactory c = createMustacheFactory();
Mustache m = c.compile("isempty.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
List people = Arrays.asList("Test");
});
assertEquals(getContents(root, "isempty.txt"), sw.toString());
}
public void testNumber0IsFalse() throws IOException {
DefaultMustacheFactory c = createMustacheFactory();
c.setObjectHandler(new ReflectionObjectHandler() {
@Override
public Writer falsey(Iteration iteration, Writer writer, Object object, Object[] scopes) {
if (object instanceof Number) {
if (((Number) object).intValue() == 0) {
return iteration.next(writer, object, scopes);
}
}
return super.falsey(iteration, writer, object, scopes);
}
@Override
public Writer iterate(Iteration iteration, Writer writer, Object object, Object[] scopes) {
if (object instanceof Number) {
if (((Number) object).intValue() == 0) {
return writer;
}
}
return super.iterate(iteration, writer, object, scopes);
}
});
StringWriter sw = new StringWriter();
Mustache m = c.compile(new StringReader("{{#zero}}zero{{/zero}}{{#one}}one{{/one}}{{^zero}}zero{{/zero}}{{^one}}one{{/one}}"), "zeroone");
m.execute(sw, new Object() {
int zero = 0;
int one = 1;
}).close();
assertEquals("onezero", sw.toString());
}
public void testSecurity() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = init();
Mustache m = c.compile("security.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String name = "Chris";
int value = 10000;
int taxed_value() {
return (int) (this.value - (this.value * 0.4));
}
boolean in_ca = true;
// Should not be accessible
private String test = "Test";
});
assertEquals(getContents(root, "security.txt"), sw.toString());
}
public void testIdentitySimple() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = init();
Mustache m = c.compile("simple.html");
StringWriter sw = new StringWriter();
m.identity(sw);
assertEquals(getContents(root, "simple.html").replaceAll("\\s+", ""), sw.toString().replaceAll(
"\\s+", ""));
}
public void testProperties() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = init();
Mustache m = c.compile("simple.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String getName() {
return "Chris";
}
int getValue() {
return 10000;
}
int taxed_value() {
return (int) (this.getValue() - (this.getValue() * 0.4));
}
boolean isIn_ca() {
return true;
}
});
assertEquals(getContents(root, "simple.txt"), sw.toString());
}
public void testSimpleWithMap() throws MustacheException, IOException, ExecutionException, InterruptedException {
MustacheFactory c = init();
Mustache m = c.compile("simple.html");
StringWriter sw = new StringWriter();
m.execute(sw, new HashMap<String, Object>() {{
put("name", "Chris");
put("value", 10000);
put("taxed_value", 6000);
put("in_ca", true);
}});
assertEquals(getContents(root, "simple.txt"), sw.toString());
}
public void testPartialWithTF() throws MustacheException, IOException {
MustacheFactory c = init();
Mustache m = c.compile("partialintemplatefunction.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
public TemplateFunction i() {
return new TemplateFunction() {
@Override
public String apply(String s) {
return s;
}
};
}
});
assertEquals("This is not interesting.", sw.toString());
}
public void testFunctions() throws IOException {
MustacheFactory c = init();
Mustache m = c.compile(new StringReader("{{#f}}{{foo}}{{/f}}"), "test");
{
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Function f = new Function<String, String>() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
};
String foo = "bar";
}).flush();
assertEquals("BAR", sw.toString());
}
{
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Function f = new TemplateFunction() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
};
String foo = "bar";
String FOO = "baz";
}).flush();
assertEquals("baz", sw.toString());
}
}
public void testComplex() throws MustacheException, IOException {
StringWriter json = new StringWriter();
MappingJsonFactory jf = new MappingJsonFactory();
final JsonGenerator jg = jf.createJsonGenerator(json);
jg.writeStartObject();
final JsonCapturer captured = new JsonCapturer(jg);
MustacheFactory c = new DefaultMustacheFactory(root) {
@Override
public MustacheVisitor createMustacheVisitor() {
return new CapturingMustacheVisitor(this, captured);
}
};
Mustache m = c.compile("complex.html");
StringWriter sw = new StringWriter();
m.execute(sw, new ComplexObject());
jg.writeEndObject();
jg.flush();
assertEquals(getContents(root, "complex.txt"), sw.toString());
JsonNode jsonNode = jf.createJsonParser(json.toString()).readValueAsTree();
Object o = JsonInterpreterTest.toObject(jsonNode);
sw = new StringWriter();
m = init().compile("complex.html");
m.execute(sw, o);
assertEquals(getContents(root, "complex.txt"), sw.toString());
}
public void testComplexParallel() throws MustacheException, IOException {
MustacheFactory c = initParallel();
Mustache m = c.compile("complex.html");
StringWriter sw = new StringWriter();
m.execute(sw, new ParallelComplexObject()).close();
assertEquals(getContents(root, "complex.txt"), sw.toString());
}
public void testSerialCallable() throws MustacheException, IOException {
MustacheFactory c = init();
Mustache m = c.compile("complex.html");
StringWriter sw = new StringWriter();
m.execute(sw, new ParallelComplexObject());
assertEquals(getContents(root, "complex.txt"), sw.toString());
}
/*
@SuppressWarnings("serial")
public void testCurrentElementInArray() throws IOException, MustacheException {
MustacheBuilder c = init();
Mustache m = c.parseFile("simple_array.html");
StringWriter sw = new StringWriter();
FutureWriter writer = new FutureWriter(sw);
m.execute(writer, new Scope(new HashMap<String, Object>() {
{
put("list", Arrays.asList(1,2,3));
}
}));
writer.flush();
assertEquals(getContents(root, "simple_array.txt"), sw.toString());
sw = new StringWriter();
writer = new FutureWriter(sw);
m.execute(writer, new Scope(new HashMap<String, Object>() {
{
put("list", Arrays.asList(null,null));
}
}));
writer.flush();
assertEquals("\n\n", sw.toString());
}
*/
public void testReadme() throws MustacheException, IOException {
MustacheFactory c = init();
Mustache m = c.compile("items.html");
StringWriter sw = new StringWriter();
long start = System.currentTimeMillis();
m.execute(sw, new Context());
long diff = System.currentTimeMillis() - start;
assertEquals(getContents(root, "items.txt"), sw.toString());
}
public void testReadmeSerial() throws MustacheException, IOException {
MustacheFactory c = init();
Mustache m = c.compile("items2.html");
StringWriter sw = new StringWriter();
long start = System.currentTimeMillis();
m.execute(sw, new Context());
long diff = System.currentTimeMillis() - start;
assertEquals(getContents(root, "items.txt"), sw.toString());
assertTrue("Should be a little bit more than 4 seconds: " + diff, diff > 3999 && diff < 6000);
}
public void testReadmeParallel() throws MustacheException, IOException {
MustacheFactory c = initParallel();
Mustache m = c.compile("items2.html");
StringWriter sw = new StringWriter();
long start = System.currentTimeMillis();
m.execute(sw, new Context()).close();
long diff = System.currentTimeMillis() - start;
assertEquals(getContents(root, "items.txt"), sw.toString());
assertTrue("Should be a little bit more than 1 second: " + diff, diff > 999 && diff < 2000);
}
static class Context {
List<Item> items() {
return Arrays.asList(
new Item("Item 1", "$19.99",
Arrays.asList(new Feature("New!"), new Feature("Awesome!"))),
new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly.")))
);
}
static class Item {
Item(String name, String price, List<Feature> features) {
this.name = name;
this.price = price;
this.features = features;
}
String name, price;
List<Feature> features;
}
static class Feature {
Feature(String description) {
this.description = description;
}
String description;
Callable<String> desc() throws InterruptedException {
return new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return description;
}
};
}
}
}
public void testDeferred() throws IOException {
DefaultMustacheFactory mf = new DeferringMustacheFactory(root);
mf.setExecutorService(Executors.newCachedThreadPool());
Object context = new Object() {
String title = "Deferred";
Object deferred = new DeferringMustacheFactory.DeferredCallable();
Object deferredpartial = DeferringMustacheFactory.DEFERRED;
};
Mustache m = mf.compile("deferred.html");
StringWriter sw = new StringWriter();
m.execute(sw, context).close();
assertEquals(getContents(root, "deferred.txt"), sw.toString());
}
public void testMultipleCallsWithDifferentScopes() throws IOException {
String template = "Value: {{value}}";
Mustache mustache = new DefaultMustacheFactory().compile(new StringReader(
template), "test");
// scope object doesn't have a 'value' property, lookup will fail
mustache.execute(new StringWriter(), new Object());
// scope object has a 'value' property, lookup shouldn't fail
StringWriter sw = new StringWriter();
mustache.execute(sw, new Object() {
String value = "something";
});
assertEquals("Value: something", sw.toString());
}
public void testMultipleCallsWithDifferentMapScopes() throws IOException {
String template = "Value: {{value}}";
Mustache mustache = new DefaultMustacheFactory().compile(new StringReader(
template), "test");
Map<String, String> emptyMap = new HashMap<String, String>();
Map<String, String> map = new HashMap<String, String>();
map.put("value", "something");
// map doesn't have an entry for 'value', lookup will fail
mustache.execute(new StringWriter(), emptyMap);
// map has an entry for 'value', lookup shouldn't fail
StringWriter sw = new StringWriter();
mustache.execute(sw, map);
assertEquals("Value: something", sw.toString());
}
public void testRelativePathsSameDir() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache compile = mf.compile("relative/paths.html");
StringWriter sw = new StringWriter();
compile.execute(sw, null).close();
assertEquals(getContents(root, "relative/paths.txt"), sw.toString());
}
public void testRelativePathsRootDir() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache compile = mf.compile("relative/rootpath.html");
StringWriter sw = new StringWriter();
compile.execute(sw, null).close();
assertEquals(getContents(root, "relative/paths.txt"), sw.toString());
}
public void testPathsWithExtension() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache compile = mf.compile("relative/extension.html");
StringWriter sw = new StringWriter();
compile.execute(sw, null).close();
assertEquals(getContents(root, "relative/paths.txt"), sw.toString());
}
public void testRelativePathsTemplateFunction() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache compile = mf.compile("relative/functionpaths.html");
StringWriter sw = new StringWriter();
compile.execute(sw, new Object() {
Function i = new TemplateFunction() {
@Override
public String apply(String s) {
return s;
}
};
}).close();
assertEquals(getContents(root, "relative/paths.txt"), sw.toString());
}
public void testRelativePathFail() throws IOException {
MustacheFactory mf = createMustacheFactory();
try {
Mustache compile = mf.compile("relative/pathfail.html");
fail("Should have failed to compile");
} catch (MustacheException e) {
// Success
}
}
public void testIterator() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#values}}{{.}}{{/values}}{{^values}}Test2{{/values}}"), "testIterator");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Iterator values() {
return Arrays.asList(1, 2, 3).iterator();
}
}).close();
assertEquals("123", sw.toString());
}
public void testObjectArray() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#values}}{{.}}{{/values}}{{^values}}Test2{{/values}}"), "testObjectArray");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Integer[] values = new Integer[]{1, 2, 3};
}).close();
assertEquals("123", sw.toString());
}
public void testBaseArray() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#values}}{{.}}{{/values}}{{^values}}Test2{{/values}}"), "testBaseArray");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
int[] values = new int[]{1, 2, 3};
}).close();
assertEquals("123", sw.toString());
}
public void testEmptyString() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#values}}Test1{{/values}}{{^values}}Test2{{/values}}"), "testEmptyString");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String values = "";
}).close();
assertEquals("Test2", sw.toString());
}
public void testPrivate() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#values}}Test1{{/values}}{{^values}}Test2{{/values}}"), "testPrivate");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
private String values = "value";
private String values() {
return "value";
}
}).close();
// Values ignored as if it didn't exist at all
assertEquals("Test2", sw.toString());
}
public void testSingleCurly() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{value } }}"), "testSingleCurly");
StringWriter sw = new StringWriter();
m.execute(sw, new HashMap() {{
put("value }", "test");
}}).close();
// Values ignored as if it didn't exist at all
assertEquals("test", sw.toString());
}
public void testPragma() throws IOException {
final AtomicBoolean found = new AtomicBoolean();
DefaultMustacheFactory mf = new DefaultMustacheFactory() {
@Override
public MustacheVisitor createMustacheVisitor() {
DefaultMustacheVisitor visitor = new DefaultMustacheVisitor(this);
visitor.addPragmaHandler("pragma", new PragmaHandler() {
@Override
public Code handle(TemplateContext tc, String pragma, String args) {
if (pragma.equals("pragma") && args.equals("1 2 3")) {
found.set(true);
}
return null;
}
});
return visitor;
}
};
Mustache m = mf.compile(new StringReader("Pragma: {{% pragma 1 2 3 }}"), "testPragma");
StringWriter sw = new StringWriter();
m.execute(sw, null).close();
// Values ignored as if it didn't exist at all
assertEquals("Pragma: ", sw.toString());
assertTrue(found.get());
}
public void testNotIterableCallable() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{^value}}test{{/value}}"), "testNotIterableCallable");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Callable value = new Callable() {
@Override
public Object call() throws Exception {
return null;
}
};
}).close();
// Values ignored as if it didn't exist at all
assertEquals("test", sw.toString());
}
public static class AccessTrackingMap extends HashMap<String, String> {
Set<Object> accessed = new HashSet<Object>();
@Override
public String get(Object key) {
accessed.add(key);
return super.get(key);
}
public void check() {
Set<String> keyset = new HashSet<String>(keySet());
keyset.removeAll(accessed);
if (!keyset.isEmpty()) {
throw new MustacheException("All keys in the map were not accessed");
}
}
}
public void testAccessTracker() throws IOException {
{
Map<String, String> accessTrackingMap = createBaseMap();
DefaultMustacheFactory mf = createMustacheFactory();
Mustache test = mf.compile(new StringReader("{{first}} {{last}}"), "test");
StringWriter sw = new StringWriter();
test.execute(sw, accessTrackingMap).close();
assertEquals("Sam Pullara", sw.toString());
}
{
AccessTrackingMap accessTrackingMap = createBaseMap();
accessTrackingMap.put("notused", "shouldcauseanerror");
DefaultMustacheFactory mf = createMustacheFactory();
Mustache test = mf.compile(new StringReader("{{first}} {{last}}"), "test");
StringWriter sw = new StringWriter();
test.execute(sw, accessTrackingMap).close();
assertEquals("Sam Pullara", sw.toString());
try {
accessTrackingMap.check();
fail("Should have thrown an exception");
} catch (MustacheException me) {
// Succcess
}
}
}
private AccessTrackingMap createBaseMap() {
AccessTrackingMap accessTrackingMap = new AccessTrackingMap();
accessTrackingMap.put("first", "Sam");
accessTrackingMap.put("last", "Pullara");
return accessTrackingMap;
}
public void testMismatch() {
try {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#value}}"), "testMismatch");
fail("Not mismatched");
} catch (MustacheException e) {
// Success
try {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#value}}{{/values}}"), "testMismatch");
fail("Not mismatched");
} catch (MustacheException e2) {
// Success
}
}
}
public void testInvalidDelimiters() {
try {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{=toolong}}"), "testInvalidDelimiters");
fail("Not invalid");
} catch (MustacheException e) {
// Success
}
}
public void testTemplateFunction() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#i}}{{{test}}}{{f}}{{/i}}" +
"{{#comment}}comment{{/comment}}"), "testTemplateFunction");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
Function i = new TemplateFunction() {
@Override
public String apply(String s) {
return s.replace("test", "test2");
}
};
String test2 = "test";
Function f = new Function() {
@Override
public Object apply(Object o) {
return null;
}
};
CommentFunction comment = new CommentFunction();
}).close();
// Values ignored as if it didn't exist at all
assertEquals("test", sw.toString());
}
static class SuperClass {
String values = "value";
}
public void testSuperField() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache m = mf.compile(new StringReader("{{#values}}Test1{{/values}}{{^values}}Test2{{/values}}"), "testIterator");
StringWriter sw = new StringWriter();
m.execute(sw, new SuperClass() {
}).close();
// Values ignored as if it didn't exist at all
assertEquals("Test1", sw.toString());
}
public void testRelativePathsDotDotDir() throws IOException {
MustacheFactory mf = createMustacheFactory();
Mustache compile = mf.compile("relative/dotdot.html");
StringWriter sw = new StringWriter();
compile.execute(sw, null).close();
assertEquals(getContents(root, "uninterestingpartial.html"), sw.toString());
}
public void testRelativePathsDotDotDirOverride() throws IOException {
MustacheFactory mf = new DefaultMustacheFactory(root) {
@Override
public String resolvePartialPath(String dir, String name, String extension) {
return name + extension;
}
};
Mustache compile = mf.compile("relative/nonrelative.html");
StringWriter sw = new StringWriter();
compile.execute(sw, null).close();
assertEquals(getContents(root, "nonrelative.html"), sw.toString());
}
public void testOverrideExtension() throws IOException {
MustacheFactory mf = new DefaultMustacheFactory(root) {
@Override
public MustacheVisitor createMustacheVisitor() {
return new DefaultMustacheVisitor(this) {
@Override
public void partial(TemplateContext tc, String variable) {
TemplateContext partialTC = new TemplateContext("{{", "}}", tc.file(), tc.line(), tc.startOfLine());
list.add(new PartialCode(partialTC, df, variable) {
@Override
protected String partialName() {
return name;
}
});
}
};
}
};
StringWriter sw = new StringWriter();
mf.compile("overrideextension.html").execute(sw, null).close();
assertEquals("not interesting.", sw.toString());
}
public void testEmptyMustache() {
try {
new DefaultMustacheFactory().compile(new StringReader("{{}}"), "test");
fail("Didn't throw an exception");
} catch (MustacheException e) {
assertTrue(e.getMessage().startsWith("Empty mustache"));
}
}
public void testMustacheNotFoundException() {
String nonExistingMustache = "404";
try {
new DefaultMustacheFactory().compile(nonExistingMustache);
fail("Didn't throw an exception");
} catch (MustacheNotFoundException e) {
assertEquals(nonExistingMustache, e.getName());
}
}
public void testImplicitIteratorNoScope() throws IOException {
Mustache test = new DefaultMustacheFactory().compile(new StringReader("{{.}}"), "test");
StringWriter sw = new StringWriter();
test.execute(sw, null).close();
assertEquals("", sw.toString());
StringWriter sw2 = new StringWriter();
test.execute(sw2, new Object[0]).close();
assertEquals("", sw2.toString());
}
public void testImplicitIteratorWithScope() throws IOException {
Mustache test = new DefaultMustacheFactory().compile(new StringReader("{{#test}}{{.}}{{/test}}"), "test");
StringWriter sw = new StringWriter();
test.execute(sw, new Object() {
List<String> test = Arrays.asList("a", "b", "c");
}).close();
assertEquals("abc", sw.toString());
}
public void testCR() {
Mustache m = new DefaultMustacheFactory().compile(new StringReader("{{test}}\r\n{{test}}\r\n"), "test");
StringWriter sw = new StringWriter();
m.execute(sw, new Object() {
String test = "fred";
});
assertEquals("fred\r\nfred\r\n", sw.toString());
}
public void testOutputDelimiters() {
String template = "{{=## ##=}}{{##={{ }}=####";
Mustache mustache = new DefaultMustacheFactory().compile(new StringReader(template), "test");
StringWriter sw = new StringWriter();
mustache.execute(sw, new Object[0]);
assertEquals("{{##", sw.toString());
}
public void testLimitedDepthRecursion() {
try {
MustacheFactory c = init();
Mustache m = c.compile("infiniteparent.html");
StringWriter sw = new StringWriter();
m.execute(sw, new Context());
fail("Should have failed");
} catch (StackOverflowError soe) {
fail("Should not have overflowed the stack");
} catch (MustacheException e) {
assertEquals("Maximum partial recursion limit reached: 100", e.getMessage());
}
}
public void testMalformedTag() {
try {
String template = "\n{{$value}}\n{/value}}";
Mustache mustache = new DefaultMustacheFactory().compile(new StringReader(template), "test");
StringWriter sw = new StringWriter();
mustache.execute(sw, new Object[0]);
fail("Should have failed to compile");
} catch (MustacheException e) {
assertEquals("Failed to close 'value' tag at line 2", e.getMessage());
}
}
public void testTemplateFunctionWithData() {
String template = "{{#parse}}\n" +
"{{replaceMe}}\n" +
"{{/parse}}";
Mustache mustache = new DefaultMustacheFactory().compile(new StringReader(template), "test");
StringWriter sw = new StringWriter();
mustache.execute(sw, new Object() {
public TemplateFunction parse() {
return new TemplateFunction() {
@Override
public String apply(String s){
return "blablabla {{anotherVar}}, blablabla {{yetAnotherVar}}";
}
};
}
String anotherVar = "banana";
String yetAnotherVar = "apple";
});
assertEquals("blablabla banana, blablabla apple", sw.toString());
}
public void testTemplateFunctionWithImplicitParams() {
String template = "{{#parse}}\n" +
"{{replaceMe}}\n" +
"{{/parse}}";
DefaultMustacheFactory mf = new DefaultMustacheFactory() {
public MustacheVisitor createMustacheVisitor() {
return new DefaultMustacheVisitor(this) {
public void iterable(final TemplateContext templateContext, String variable, Mustache mustache) {
list.add(new IterableCode(templateContext, df, mustache, variable) {
Binding binding = oh.createBinding("params", templateContext, this);
protected Writer handleFunction(Writer writer, Function function, Object[] scopes) {
return super.handleFunction(writer, function, addScope(scopes, binding.get(scopes)));
}
});
}
};
}
};
Mustache mustache = mf.compile(new StringReader(template), "test");
StringWriter sw = new StringWriter();
mustache.execute(sw, new Object() {
public TemplateFunction parse() {
return new TemplateFunction() {
@Override
public String apply(String s){
return "blablabla {{anotherVar}}, blablabla {{yetAnotherVar}}";
}
};
}
Map<String, Object> params = ImmutableMap.<String, Object>builder()
.put("anotherVar", "banana")
.put("yetAnotherVar", "apple")
.build();
});
assertEquals("blablabla banana, blablabla apple", sw.toString());
}
public void testPropertyWithDot() throws IOException {
DefaultMustacheFactory mustacheFactory = new DefaultMustacheFactory();
Reader reader = new StringReader("value=${some.value}");
Mustache mustache = mustacheFactory.compile(reader, "maven", "${", "}");
Map<String, String> properties = new HashMap<String, String>();
properties.put("some.value", "some.value");
StringWriter writer = new StringWriter();
mustache.execute(writer, new Object[]{properties}).close();
Assert.assertEquals("value=some.value", writer.toString());
}
private MustacheFactory init() {
return createMustacheFactory();
}
private DefaultMustacheFactory initParallel() {
DefaultMustacheFactory cf = createMustacheFactory();
cf.setExecutorService(Executors.newCachedThreadPool());
return cf;
}
protected String getContents(File root, String file) throws IOException {
BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(new File(root, file)), "UTF-8"));
StringWriter capture = new StringWriter();
char[] buffer = new char[8192];
int read;
while ((read = br.read(buffer)) != -1) {
capture.write(buffer, 0, read);
}
return capture.toString();
}
protected void setUp() throws Exception {
super.setUp();
File file = new File("src/test/resources");
root = new File(file, "simple.html").exists() ? file : new File("../src/test/resources");
}
@Test
public void testMap() throws IOException {
ArrayList<Map<String, String>> fn = new ArrayList<Map<String, String>>();
Map<String, String> map1 = new HashMap<String, String>();
map1.put("name", "firstName");
map1.put("last", "lastName");
fn.add(map1);
Map<String, String> map2 = new HashMap<String, String>();
map2.put("name", "firstName 1");
map2.put("last", "lastName 1");
fn.add(map2);
Map map = new HashMap();
map.put("names", fn);
Writer writer = new OutputStreamWriter(System.out);
MustacheFactory mf = new DefaultMustacheFactory();
Mustache mustache = mf.compile(new StringReader("{{#names}}<h2>First Name : {{name}}</h2> <h2>Last Name : {{last}}</h2>{{/names}}"), "example");
mustache.execute(writer, map);
writer.flush();
}
}