Improve test infrastructure

This commit is contained in:
Adam Gent
2023-11-19 13:14:28 -05:00
parent a1f5a8110c
commit 80882b0e5d
7 changed files with 212 additions and 63 deletions
@@ -4,16 +4,34 @@
package com.samskivert.mustache;
import static java.util.Map.entry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import com.samskivert.mustache.specs.SpecTest;
/**
* Vestige from when JMustache supported both GWT and JVM.
@@ -210,17 +228,25 @@ public abstract class SharedTests
fail();
} catch (UnsupportedOperationException uoe) {} // expected
}
Map<String,String> partials = new LinkedHashMap<>();
@SafeVarargs
protected final Mustache.TemplateLoader partials(Map.Entry<String, String> ... entries) {
Map<String,String> templates = new LinkedHashMap<>();
for (Entry<String, String> e : entries) {
templates.put(e.getKey(), e.getValue());
}
partials = templates;
return name -> new StringReader(templates.get(name));
}
@Test public void testPartial () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
if (name.equals("foo")) {
return new StringReader("inside:{{bar}}");
} else {
return new StringReader("nonfoo");
}
}
}), "foo inside:foo nonfoo foo", "{{bar}} {{>foo}} {{>baz}} {{bar}}", context("bar", "foo"));
test(Mustache.compiler().withLoader(
partials(entry("foo", "inside:{{bar}}"),
entry("baz", "nonfoo")))
, "foo inside:foo nonfoo foo", "{{bar}} {{>foo}} {{>baz}} {{bar}}", context("bar", "foo"));
}
@Test public void testPartialIndent () {
@@ -231,7 +257,7 @@ public abstract class SharedTests
}), "\\\n |\n <\n->\n |\n/\n", "\\\n {{>partial}}\n/\n", context("content", "<\n->"));
}
/* @Test */ public void testPartialBlankLines () {
@Ignore @Test public void testPartialBlankLines () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
return new StringReader("|\na\n\nb\n|\n");
@@ -239,7 +265,7 @@ public abstract class SharedTests
}), "\\\n\t|\n\ta\n\n\tb\n\t|\n/\n", "\\\n\t{{>partial}}\n/\n", context());
}
/* @Test */ public void testNestedPartialBlankLines () {
@Ignore @Test public void testNestedPartialBlankLines () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
if (name.equals("partial")) {
@@ -251,7 +277,7 @@ public abstract class SharedTests
}), "\\\n\t1\n\t\t2\n\t\ta\n\n\t\tb\n\t\t2\n\t1\n/\n", "\\\n\t{{>partial}}\n/\n", context());
}
/* @Test */ public void testNestedPartialBlankLinesCRLF () {
@Ignore @Test public void testNestedPartialBlankLinesCRLF () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
if (name.equals("partial")) {
@@ -263,16 +289,11 @@ public abstract class SharedTests
}), "\\\r\n\t1\r\n\t\t2\r\n\t\ta\r\n\r\n\t\tb\r\n\t\t2\r\n\t1\r\n/\r\n", "\\\r\n\t{{>partial}}\r\n/\r\n", context());
}
/* @Test */ public void testNestedPartialIndent () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
if (name.equals("partial")) {
return new StringReader("1\n {{>nest}}\n1\n");
} else {
return new StringReader("2\n{{{content}}}\n2\n");
}
}
}), "|\n 1\n 2\n <\n->\n 2\n 1\n|\n", "|\n {{>partial}}\n|\n", context("content", "<\n->"));
/* @Ignore */ @Test public void testNestedPartialIndent () {
Mustache.TemplateLoader loader = partials(entry("partial", "1\n {{>nest}}\n1\n"), entry("nest", "2\n{{{content}}}\n2\n"));
test(Mustache.compiler().withLoader(loader),
"|\n 1\n 2\n <\n->\n 2\n 1\n|\n",
"|\n {{>partial}}\n|\n", context("content", "<\n->"));
}
@Test public void testPartialIndentWithVariableAtTheStart () {
@@ -826,12 +847,40 @@ public abstract class SharedTests
test(Mustache.compiler().withDelims("<% %>"), "bar", "<%foo%>", context("foo", "bar"));
}
protected void test (Mustache.Compiler compiler, String expected, String template, Object ctx) {
check(expected, compiler.compile(template).execute(ctx));
protected String name;
@Rule public TestRule watcher = new TestWatcher() {
protected void starting(Description description) {
name = description.getDisplayName();
}
};
protected void test(Mustache.Compiler compiler, String expected, String template, Object ctx) {
String actual = compiler.compile(template).execute(ctx);
if (! Objects.equals(expected, actual)) {
System.out.println("");
System.out.println("----------------------------------------");
System.out.println("");
System.out.println("Failed: " + name);
System.out.println("Template : \"" + SpecTest.showWhitespace(template) + "\"");
if (! partials.isEmpty()) {
System.out.println("Partials : ");
for (Entry<String, String> e : partials.entrySet()) {
System.out.println("\t" + e.getKey() + ": \"" + SpecTest.showWhitespace(e.getValue()) + "\"");
}
}
System.out.println("Expected : \"" + SpecTest.showWhitespace(expected) + "\"");
System.out.println("Result : \"" + SpecTest.showWhitespace(actual) + "\"");
System.out.println("----------------------------------------");
System.out.println("");
}
check(expected, actual);
}
protected void check (String expected, String output) {
assertEquals(uncrlf(expected), uncrlf(output));
//assertEquals(uncrlf(expected), uncrlf(output));
assertEquals(SpecTest.showWhitespace(expected), SpecTest.showWhitespace(output));
}
protected void test (String expected, String template, Object ctx) {
@@ -0,0 +1,24 @@
package com.samskivert.mustache.specs;
import java.util.Collection;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class CustomSpecTest extends SpecTest {
public CustomSpecTest(Spec spec, String name) {
super(spec, name);
}
@Parameters(name = "{1}")
public static Collection<Object[]> data () {
String[] groups = new String[] {
"partials"
};
return SpecTest.data("/custom/specs/", groups);
}
}
@@ -0,0 +1,29 @@
package com.samskivert.mustache.specs;
import java.util.Collection;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class OfficialSpecTest extends SpecTest {
public OfficialSpecTest(Spec spec, String name) {
super(spec, name);
}
@Parameters(name = "{1}")
public static Collection<Object[]> data () {
String[] groups = new String[] {
"comments",
"delimiters",
"interpolation",
"inverted",
"sections",
"partials"
};
return SpecTest.data("/specs/specs/", groups);
}
}
@@ -5,6 +5,7 @@
package com.samskivert.mustache.specs;
import java.util.Map;
import java.util.function.Consumer;
/**
* @author Yoryos Valotasios
@@ -26,7 +27,7 @@ public class Spec
}
public String getDescription () {
return (String) map.get("descr");
return (String) map.get("desc");
}
public String getTemplate () {
@@ -44,4 +45,20 @@ public class Spec
public String getPartial (String name) {
return partials == null ? null : partials.get(name);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Consumer<String> value = s -> sb.append("\"").append(s).append("\"").append("\n");
Consumer<String> label = s -> sb.append("").append(s).append(": ");
label.accept("name");
value.accept(getName());
label.accept("desc");
value.accept(getDescription());
label.accept("template");
value.accept(getTemplate());
label.accept("expected");
value.accept(getExpectedOutput());
return sb.toString();
}
}
@@ -12,9 +12,10 @@ import com.samskivert.mustache.Mustache;
public class SpecAwareTemplateLoader implements Mustache.TemplateLoader
{
private static final String EMPTY_STRING = "";
private Spec spec;
private final Spec spec;
public void setSpec (Spec spec) {
public SpecAwareTemplateLoader(Spec spec) {
super();
this.spec = spec;
}
@@ -7,19 +7,14 @@ import java.util.Map;
import java.util.Objects;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
@RunWith(Parameterized.class)
public class SpecTest {
public abstract class SpecTest {
private static final Yaml yaml = new Yaml();
@@ -32,19 +27,11 @@ public class SpecTest {
this.name = name;
}
private static Mustache.Compiler compiler;
private static SpecAwareTemplateLoader loader;
@BeforeClass
public static void setUp () {
loader = new SpecAwareTemplateLoader();
compiler = Mustache.compiler().defaultValue("").withLoader(loader);
}
@Test
public void test () throws Exception {
//System.out.println("Testing: " + name);
loader.setSpec(spec);
SpecAwareTemplateLoader loader = new SpecAwareTemplateLoader(spec);
Mustache.Compiler compiler = Mustache.compiler().defaultValue("").withLoader(loader);
String tmpl = spec.getTemplate();
String desc = String.format("Template: '''%s'''\nData: '%s'\n",
uncrlf(tmpl), uncrlf(spec.getData().toString()));
@@ -75,7 +62,7 @@ public class SpecTest {
}
}
private static String showWhitespace (String s) {
public static String showWhitespace (String s) {
s = s.replace("\r\n", "\u240D");
s = s.replace('\t', '\u21E5');
s = s.replace("\n", "\u21B5\n");
@@ -88,20 +75,12 @@ public class SpecTest {
return (text == null) ? null : text.replace("\r", "\\r").replace("\n", "\\n");
}
@Parameters(name = "{1}")
public static Collection<Object[]> data () {
String[] groups = new String[] {
"comments",
"delimiters",
"interpolation",
"inverted",
"sections",
"partials"
};
public static Collection<Object[]> data (String specPath, String[] groups) {
List<Object[]> tuples = new ArrayList<>();
int i = 0;
for (String g : groups) {
Iterable<Spec> specs = getTestsForGroup(g);
Iterable<Spec> specs = getTestsForGroup(specPath, g);
for (Spec s : specs) {
Object[] tuple = new Object[] {s, g + "-" + s.getName() + "-" + i++};
tuples.add(tuple);
@@ -110,8 +89,13 @@ public class SpecTest {
return tuples;
}
private static Iterable<Spec> getTestsForGroup (String name) {
String ymlPath = "/specs/specs/" + name + ".yml";
private static Iterable<Spec> getTestsForGroup (String specPath, String name) {
//String ymlPath = "/specs/specs/" + name + ".yml";
String ymlPath = specPath + name + ".yml";
return getTestsFromYaml(name, ymlPath);
}
private static Iterable<Spec> getTestsFromYaml(String name, String ymlPath) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) yaml.load(SpecTest.class.getResourceAsStream(ymlPath));
@@ -0,0 +1,45 @@
overview: |
Partial tags are used to expand an external template into the current
template.
The tag's content MUST be a non-whitespace character sequence NOT containing
the current closing delimiter.
This tag's content names the partial to inject. Set Delimiter tags MUST NOT
affect the parsing of a partial. The partial MUST be rendered against the
context stack local to the tag. If the named partial cannot be found, the
empty string SHOULD be used instead, as in interpolations.
Partial tags SHOULD be treated as standalone when appropriate. If this tag
is used standalone, any whitespace preceding the tag should treated as
indentation, and prepended to each line of the partial before rendering.
tests:
- name: Nested Partial Indent
desc: "Nested partials should including the parent partials indent"
data: { content: "<\n->" }
template: "|\n {{>partial}}\n|\n"
partials:
partial: "1\n {{>nest}}\n1\n"
nest: "2\n{{{content}}}\n2\n"
expected: "|\n 1\n 2\n <\n->\n 2\n 1\n|\n"
- name: Standalone Indentation
desc: Each line of the partial should be indented before rendering.
data: { content: "<\n->" }
template: |
\
{{>partial}}
/
partials:
partial: |
|
{{{content}}}
|
expected: |
\
|
<
->
|
/