Files
jmustache/src/test/java/com/samskivert/mustache/MustacheTest.java
T
Michael Bayne b3c54669cc Make MustacheCustomContext a Mustache inner interface.
This matches the style of all the other extension points.
2018-08-21 17:20:33 -07:00

279 lines
9.9 KiB
Java

//
// JMustache - A Java implementation of the Mustache templating language
// http://github.com/samskivert/jmustache/blob/master/LICENSE
package com.samskivert.mustache;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Mustache tests that can only be run on the JVM. Most tests should go in BaseMustacheTest so
* that they can be run on GWT and the JVM to ensure they work in both places.
*/
public class MustacheTest extends SharedTests
{
@Test public void testFieldVariable () {
test("bar", "{{foo}}", new Object() {
String foo = "bar";
});
}
@Test public void testMethodVariable () {
test("bar", "{{foo}}", new Object() {
String foo () { return "bar"; }
});
}
@Test public void testPropertyVariable () {
test("bar", "{{foo}}", new Object() {
String getFoo () { return "bar"; }
});
}
@Test public void testCustomContext() {
test("bar", "{{foo}}", new Mustache.CustomContext() {
@Override
public Object get(String name) {
return "foo".equals(name) ? "bar" : null;
}
});
}
public interface HasDefault {
default String getFoo () { return "bar"; }
}
public interface Interloper extends HasDefault {
default String getFoo () { return "bang"; }
}
@Test public void testDefaultMethodVariable () {
test("bar", "{{foo}}", new HasDefault() {
});
test("bang", "{{foo}}", new Interloper() {
});
test("bong", "{{foo}}", new Interloper() {
public String getFoo () { return "bong"; }
});
}
@Test public void testBooleanPropertyVariable () {
test("true", "{{foo}}", new Object() {
Boolean isFoo () { return true; }
});
}
@Test public void testPrimitiveBooleanPropertyVariable () {
test("false", "{{foo}}", new Object() {
boolean isFoo () { return false; }
});
}
@Test public void testSkipVoidReturn () {
test("bar", "{{foo}}", new Object() {
void foo () {}
String getFoo () { return "bar"; }
});
}
@Test public void testCallSiteReuse () {
Template tmpl = Mustache.compiler().compile("{{foo}}");
Object ctx = new Object() {
String getFoo () { return "bar"; }
};
for (int ii = 0; ii < 50; ii++) {
check("bar", tmpl.execute(ctx));
}
}
@Test public void testCallSiteChange () {
Template tmpl = Mustache.compiler().compile("{{foo}}");
check("bar", tmpl.execute(new Object() {
String getFoo () { return "bar"; }
}));
check("bar", tmpl.execute(new Object() {
String foo = "bar";
}));
}
@Test public void testSectionWithNonFalseyZero () {
test(Mustache.compiler(), "test", "{{#foo}}test{{/foo}}", new Object() {
Long foo = 0L;
});
}
@Test public void testSectionWithFalseyZero () {
test(Mustache.compiler().zeroIsFalse(true), "",
"{{#intv}}intv{{/intv}}" +
"{{#longv}}longv{{/longv}}" +
"{{#floatv}}floatv{{/floatv}}" +
"{{#doublev}}doublev{{/doublev}}" +
"{{#longm}}longm{{/longm}}" +
"{{#intm}}intm{{/intm}}" +
"{{#floatm}}floatm{{/floatm}}" +
"{{#doublem}}doublem{{/doublem}}",
new Object() {
Integer intv = 0;
Long longv = 0L;
Float floatv = 0f;
Double doublev = 0d;
int intm () { return 0; }
long longm () { return 0l; }
float floatm () { return 0f; }
double doublem () { return 0d; }
});
}
@Test public void testOptionalSupportingCollector () {
Mustache.Compiler comp = Mustache.compiler().withCollector(new DefaultCollector() {
public Iterator<?> toIterator (final Object value) {
if (value instanceof Optional<?>) {
Optional<?> opt = (Optional<?>) value;
return opt.isPresent() ? Collections.singleton(opt.get()).iterator() :
Collections.emptyList().iterator();
} else return super.toIterator(value);
}
});
test(comp, "test", "{{#foo}}{{.}}{{/foo}}", context("foo", Optional.of("test")));
test(comp, "", "{{#foo}}{{.}}{{/foo}}", context("foo", Optional.empty()));
test(comp, "", "{{^foo}}{{.}}{{/foo}}", context("foo", Optional.of("test")));
test(comp, "test", "{{^foo}}test{{/foo}}", context("foo", Optional.empty()));
}
@Test public void testCompoundVariable () {
test("hello", "{{foo.bar.baz}}", new Object() {
Object foo () {
return new Object() {
Object bar = new Object() {
String baz = "hello";
};
};
}
});
}
@Test public void testNullComponentInCompoundVariable () {
try {
test(Mustache.compiler(), "unused", "{{foo.bar.baz}}", new Object() {
Object foo = new Object() {
Object bar = null;
};
});
fail();
} catch (MustacheException me) {} // expected
}
@Test public void testMissingComponentInCompoundVariable () {
try {
test(Mustache.compiler(), "unused", "{{foo.bar.baz}}", new Object() {
Object foo = new Object(); // no bar
});
fail();
} catch (MustacheException me) {} // expected
}
@Test public void testNullComponentInCompoundVariableWithDefault () {
test(Mustache.compiler().nullValue("null"), "null", "{{foo.bar.baz}}", new Object() {
Object foo = null;
});
test(Mustache.compiler().nullValue("null"), "null", "{{foo.bar.baz}}", new Object() {
Object foo = new Object() {
Object bar = null;
};
});
}
@Test public void testMissingComponentInCompoundVariableWithDefault () {
test(Mustache.compiler().defaultValue("?"), "?", "{{foo.bar.baz}}", new Object() {
// no foo, no bar
});
test(Mustache.compiler().defaultValue("?"), "?", "{{foo.bar.baz}}", new Object() {
Object foo = new Object(); // no bar
});
}
@Test public void testCompoundVariableAsPlain () {
// if a compound variable is found without decomposition, we use that first
test("wholekey", "{{foo.bar}}", context(
"foo.bar", "wholekey",
"foo", new Object() { String bar = "hello"; }));
}
@Test public void testShadowedContextWithNull () {
Mustache.Compiler comp = Mustache.compiler().nullValue("(null)");
String tmpl = "{{foo}}{{#inner}}{{foo}}{{/inner}}", expect = "outer(null)";
test(comp, expect, tmpl, new Object() {
public String foo = "outer";
public Object inner = new Object() {
// this foo should shadow the outer foo even though it's null
public String foo = null;
};
});
// same as above, but with maps instead of objects
test(comp, expect, tmpl, context("foo", "outer", "inner", context("foo", null)));
}
@Test public void testContextPokingLambda () {
Mustache.Compiler c = Mustache.compiler();
class Foo { public int foo = 1; }
final Template lfoo = c.compile("{{foo}}");
check("1", lfoo.execute(new Foo()));
class Bar { public String bar = "one"; }
final Template lbar = c.compile("{{bar}}");
check("one", lbar.execute(new Bar()));
test(c, "1oneone1one!", "{{#events}}{{#renderEvent}}{{/renderEvent}}{{/events}}", context(
"renderEvent", new Mustache.Lambda() {
public void execute (Template.Fragment frag, Writer out) throws IOException {
Object ctx = frag.context();
if (ctx instanceof Foo) lfoo.execute(ctx, out);
else if (ctx instanceof Bar) lbar.execute(ctx, out);
else out.write("!");
}
},
"events", Arrays.asList(
new Foo(), new Bar(), new Bar(), new Foo(), new Bar(), "wtf?")));
}
@Test public void testCustomFormatter () {
Mustache.Formatter fmt = new Mustache.Formatter() {
public String format (Object value) {
if (value instanceof Date) return _fmt.format((Date)value);
else return String.valueOf(value);
}
protected SimpleDateFormat _fmt = new SimpleDateFormat("yyyy/MM/dd"); {
_fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
}
};
check("Date: 2014/01/08", Mustache.compiler().withFormatter(fmt).
compile("{{msg}}: {{today}}").execute(new Object() {
String msg = "Date";
Date today = new Date(1389208567874L);
}));
}
@Test public void testMapEntriesPlusReflectSection () {
Map<String,String> data = new HashMap<String,String>();
data.put("k1", "v1");
data.put("k2", "v2");
// 'key' and 'value' here rely on reflection so we can't test this in GWT
test("k1v1k2v2", "{{#map.entrySet}}{{key}}{{value}}{{/map.entrySet}}",
context("map", data));
}
}