diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..bd84223
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "src/test/resources/specs"]
+ path = src/test/resources/specs
+ url = git://github.com/mustache/spec.git
diff --git a/pom.xml b/pom.xml
index dbbe8e1..f56d303 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,12 @@
4.8.1
test
+
+ org.yaml
+ snakeyaml
+ 1.11
+ test
+
diff --git a/src/main/java/com/samskivert/mustache/Template.java b/src/main/java/com/samskivert/mustache/Template.java
index c5c4186..637335a 100644
--- a/src/main/java/com/samskivert/mustache/Template.java
+++ b/src/main/java/com/samskivert/mustache/Template.java
@@ -8,7 +8,6 @@ import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
diff --git a/src/test/java/com/samskivert/mustache/specs/Spec.java b/src/test/java/com/samskivert/mustache/specs/Spec.java
new file mode 100644
index 0000000..eb548fb
--- /dev/null
+++ b/src/test/java/com/samskivert/mustache/specs/Spec.java
@@ -0,0 +1,47 @@
+//
+// JMustache - A Java implementation of the Mustache templating language
+// http://github.com/samskivert/jmustache/blob/master/LICENSE
+
+package com.samskivert.mustache.specs;
+
+import java.util.Map;
+
+/**
+ * @author Yoryos Valotasios
+ */
+public class Spec
+{
+ private final Map map;
+ private final Map partials;
+
+ public Spec (Map map) {
+ this.map = map;
+ @SuppressWarnings("unchecked") Map partials =
+ (Map) map.get("partials");
+ this.partials = partials;
+ }
+
+ public String getName () {
+ return (String) map.get("name");
+ }
+
+ public String getDescription () {
+ return (String) map.get("descr");
+ }
+
+ public String getTemplate () {
+ return (String) map.get("template");
+ }
+
+ public String getExpectedOutput () {
+ return (String) map.get("expected");
+ }
+
+ public Object getData () {
+ return map.get("data");
+ }
+
+ public String getPartial (String name) {
+ return partials == null ? null : partials.get(name);
+ }
+}
diff --git a/src/test/java/com/samskivert/mustache/specs/SpecAwareTemplateLoader.java b/src/test/java/com/samskivert/mustache/specs/SpecAwareTemplateLoader.java
new file mode 100644
index 0000000..2e01f13
--- /dev/null
+++ b/src/test/java/com/samskivert/mustache/specs/SpecAwareTemplateLoader.java
@@ -0,0 +1,26 @@
+//
+// JMustache - A Java implementation of the Mustache templating language
+// http://github.com/samskivert/jmustache/blob/master/LICENSE
+
+package com.samskivert.mustache.specs;
+
+import java.io.Reader;
+import java.io.StringReader;
+
+import com.samskivert.mustache.Mustache;
+
+public class SpecAwareTemplateLoader implements Mustache.TemplateLoader
+{
+ private static final String EMPTY_STRING = "";
+ private Spec spec;
+
+ public void setSpec (Spec spec) {
+ this.spec = spec;
+ }
+
+ @Override public Reader getTemplate (String name) throws Exception {
+ if (spec == null) return new StringReader(EMPTY_STRING);
+ String partial = spec.getPartial(name);
+ return new StringReader(partial == null ? EMPTY_STRING : partial);
+ }
+}
diff --git a/src/test/java/com/samskivert/mustache/specs/SpecFrameworkTest.java b/src/test/java/com/samskivert/mustache/specs/SpecFrameworkTest.java
new file mode 100644
index 0000000..824eda7
--- /dev/null
+++ b/src/test/java/com/samskivert/mustache/specs/SpecFrameworkTest.java
@@ -0,0 +1,33 @@
+//
+// JMustache - A Java implementation of the Mustache templating language
+// http://github.com/samskivert/jmustache/blob/master/LICENSE
+
+package com.samskivert.mustache.specs;
+
+import java.lang.reflect.Method;
+import org.junit.runners.model.FrameworkMethod;
+
+/**
+ * @author Yoryos Valotasios
+ */
+public class SpecFrameworkTest extends FrameworkMethod
+{
+ private final String group;
+ private final Spec spec;
+
+ public SpecFrameworkTest (Method method, String group, Spec spec) {
+ super(method);
+ this.group = group;
+ this.spec = spec;
+ }
+
+ @Override
+ public Object invokeExplosively (Object target, Object... params) throws Throwable {
+ return super.invokeExplosively(target, spec);
+ }
+
+ @Override
+ public String getName () {
+ return group + ": " + spec.getName();
+ }
+}
diff --git a/src/test/java/com/samskivert/mustache/specs/SpecRunner.java b/src/test/java/com/samskivert/mustache/specs/SpecRunner.java
new file mode 100644
index 0000000..db60a8a
--- /dev/null
+++ b/src/test/java/com/samskivert/mustache/specs/SpecRunner.java
@@ -0,0 +1,91 @@
+//
+// JMustache - A Java implementation of the Mustache templating language
+// http://github.com/samskivert/jmustache/blob/master/LICENSE
+
+package com.samskivert.mustache.specs;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * @author Yoryos Valotasios
+ */
+public class SpecRunner extends BlockJUnit4ClassRunner
+{
+ private final Yaml yaml = new Yaml();
+ private final List tests;
+
+ public SpecRunner (Class> klass) throws InitializationError {
+ super(klass);
+ if (klass != SpecTest.class) throw new IllegalArgumentException(
+ SpecRunner.class.getSimpleName() + " should only be used with classes of type " +
+ SpecTest.class.getName());
+ this.tests = computeTests();
+ }
+
+ @Override
+ protected List computeTestMethods () {
+ return tests;
+ }
+
+ @Override
+ protected void validateInstanceMethods (List errors) {
+ validatePublicVoidNoArgMethods(After.class, false, errors);
+ validatePublicVoidNoArgMethods(Before.class, false, errors);
+ validateTestMethods(errors);
+ }
+
+ private List computeTests () throws InitializationError {
+ String[] groups = new String[] {
+ // "comments",
+ // "delimiters",
+ "interpolation",
+ // "inverted",
+ // "sections",
+ // "partials"
+ };
+ Method m = getTestClassMethod("test", Spec.class);
+ List tests = new ArrayList();
+ for (String name : groups) {
+ for (Map test: getTestsForGroup(name)) {
+ tests.add(new SpecFrameworkTest(m, name, new Spec(test)));
+ }
+ }
+ return tests;
+ }
+
+ private Method getTestClassMethod (String name, Class>... paramTypes)
+ throws InitializationError {
+ try {
+ return getTestClass().getJavaClass().getDeclaredMethod(name, paramTypes);
+ } catch (NoSuchMethodException ex) {
+ throw new InitializationError("Could not find " + name + " " +
+ getTestClass().getJavaClass());
+ } catch (SecurityException ex) {
+ throw new InitializationError("Could not find " + name + " " +
+ getTestClass().getJavaClass());
+ }
+ }
+
+ private Iterable