freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [01/19] incubator-freemarker git commit: Moving TemplateTestSuite to freemarker-test-utils, so that it can be used from multiple modules. It had to be generalized for this a bit, as now it has a CoreTemplateTestSuite subclass in freemarker-core-test.
Date Tue, 16 May 2017 16:08:59 GMT
Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 bb9acc9da -> 4b75ea930


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/4b75ea93/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/templates/xmlns4.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/templates/xmlns4.ftl
b/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/templates/xmlns4.ftl
deleted file mode 100644
index e97bfc0..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/templates/xmlns4.ftl
+++ /dev/null
@@ -1,70 +0,0 @@
-<#ftl ns_prefixes = {"x" : "http://x", "y" : "http://y"}>
-<#--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing,
-  software distributed under the License is distributed on an
-  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-  KIND, either express or implied.  See the License for the
-  specific language governing permissions and limitations
-  under the License.
--->
-<#recurse doc >
-
-<#macro book>
-  <html>
-    <head>
-      <title><#recurse .node["x:title"]></title>
-    </head>
-    <body>
-      <h1><#recurse .node["x:title"]></h1>
-      <#recurse>
-    </body>
-  </html>
-</#macro>
-
-<#macro chapter>
-  <h2><#recurse .node["y:title"]></h2>
-  <#recurse>
-</#macro>
-
-<#macro 'x:chapter'>
-  <h2><#recurse .node["y:title"]></h2>
-  <#recurse>
-</#macro>
-
-<#macro para>
-  <p><#recurse>
-</#macro>
-
-<#macro 'x:para'>
-  <p><#recurse>
-</#macro>
-
-<#macro 'y:para'>
-  <p><#recurse>
-</#macro>
-
-<#macro "x:title">
-  <#--
-    We have handled this element imperatively,
-    so we do nothing here.
-  -->
-</#macro>
-
-<#macro "y:title">
-  <#--
-    We have handled this element imperatively,
-    so we do nothing here.
-  -->
-</#macro>
-
-<#macro @text>${.node?html}</#macro>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/4b75ea93/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/templates/xmlns5.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/templates/xmlns5.ftl
b/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/templates/xmlns5.ftl
deleted file mode 100644
index edc3b4a..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/templates/xmlns5.ftl
+++ /dev/null
@@ -1,28 +0,0 @@
-<#ftl ns_prefixes = {"D": "http://y.com", "xx" : "http://x.com"}>
-<#--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing,
-  software distributed under the License is distributed on an
-  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-  KIND, either express or implied.  See the License for the
-  specific language governing permissions and limitations
-  under the License.
--->
-<#assign r = doc["N:root"]>
-${r["N:t1"][0]?default('-')} = No NS
-${r["xx:t2"][0]?default('-')} = x NS
-${r["t3"][0]?default('-')} = y NS
-${r["xx:t4"][0]?default('-')} = x NS
-${r["//t1"][0]?default('-')} = No NS
-${r["//t2"][0]?default('-')} = -
-${r["//t3"][0]?default('-')} = -
-${r["//t4"][0]?default('-')} = -

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/4b75ea93/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml
b/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml
deleted file mode 100644
index 2cfe290..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml
+++ /dev/null
@@ -1,211 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing,
-  software distributed under the License is distributed on an
-  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-  KIND, either express or implied.  See the License for the
-  specific language governing permissions and limitations
-  under the License.
--->
-
-<!DOCTYPE testCases [
-  <!ELEMENT testCases (setting?, testCase*)>
-  <!ELEMENT testCase (setting?)>
-     <!ATTLIST testCase 
-               name CDATA #REQUIRED
-               template CDATA #IMPLIED
-               expected CDATA #IMPLIED
-               noOutput CDATA #IMPLIED
-     >
-     <!-- The default of `template` is "${name?keep_before('[#endTN]')}.ftl" -->
-     <!-- The default of `expected` is "${name}.txt" -->
-     <!-- The default of `noOutput` is false -->
-     
- <!ELEMENT setting EMPTY>
-     <!ATTLIST setting 
-            auto_import CDATA #IMPLIED
-            source_encoding CDATA #IMPLIED
-            locale CDATA #IMPLIED
-            object_wrapper CDATA #IMPLIED
-            output_encoding CDATA #IMPLIED
-            output_dir CDATA #IMPLIED
-            new_builtin_class_resolver CDATA #IMPLIED
-            url_escaping_charset CDATA #IMPLIED
-            incompatible_improvements CDATA #IMPLIED
-            time_zone CDATA #IMPLIED
-            api_builtin_enabled CDATA #IMPLIED
-      >
-]>
-<!--
-Note that for the incompatible_improvements setting you can specify a list of versions, for
example:
-<setting incompatible_improvements="min, 3.0.5, max" /> 
--->
-
-<testCases>
-   <setting source_encoding="UTF-8" output_encoding="UTF-8" />
-   
-   <testCase name="api-builtins" noOutput="true">
-      <setting api_builtin_enabled="true" />
-   </testCase>
-   <testCase name="arithmetic" />
-   <testCase name="assignments" noOutput="true" />
-   <testCase name="boolean" />
-   <testCase name="charset-in-header" />
-   <testCase name="comment" />
-   <testCase name="comparisons" />
-   <testCase name="compress" />
-   <testCase name="then-builtin" noOutput="true" />
-   <testCase name="dateformat-java" />
-   <testCase name="dateformat-iso-like" noOutput="true" />
-   <testCase name="dateformat-iso-bi" noOutput="true" />
-   <testCase name="dateparsing" noOutput="true" />
-   <testCase name="default"/>
-   <testCase name="default-object-wrapper">
-      <setting api_builtin_enabled="true" />
-   </testCase>
-   <testCase name="default-xmlns" />
-   <testCase name="encoding-builtins" />
-   <testCase name="escapes" />
-   <testCase name="hashliteral" />
-   <testCase name="identifier-non-ascii" />
-   <testCase name="identifier-escaping" />
-   <testCase name="import">
-      <setting auto_import="import_lib.ftl as my"/>
-   </testCase>
-   <testCase name="include" />
-   <testCase name="include2">
-      <setting source_encoding="utf-8" />
-   </testCase>
-   <testCase name="interpret"/>
-   <testCase name="iterators"/>
-   <testCase name="lastcharacter"/>
-   <testCase name="list[#endTN]-simpleTemplateModels" expected="list.txt">
-      <setting object_wrapper="org.apache.freemarker.test.templatesuite.models.SimpleMapAndCollectionObjectWrapper(3.0.0)"
/>
-   </testCase>
-   <testCase name="list[#endTN]-collectionAdapter" expected="list.txt">
-      <setting object_wrapper="DefaultObjectWrapper(3.0.0)" />
-   </testCase>
-   <testCase name="list2[#endTN]-simpleTemplateModels" expected="list2.txt">
-      <setting object_wrapper="org.apache.freemarker.test.templatesuite.models.SimpleMapAndCollectionObjectWrapper(3.0.0)"
/>
-   </testCase>
-   <testCase name="list2[#endTN]-collectionAdapter" expected="list2.txt">
-      <setting object_wrapper="DefaultObjectWrapper(3.0.0)" />
-   </testCase>
-   <testCase name="list3[#endTN]-simpleTemplateModels" expected="list3.txt">
-      <setting object_wrapper="org.apache.freemarker.test.templatesuite.models.SimpleMapAndCollectionObjectWrapper(3.0.0)"
/>
-   </testCase>
-   <testCase name="list3[#endTN]-collectionAdapter" expected="list3.txt">
-      <setting object_wrapper="DefaultObjectWrapper(3.0.0)" />
-   </testCase>
-   <testCase name="list-bis" />
-   <testCase name="list-bis[#endTN]-collectionAdapter" expected="list-bis.txt">
-      <setting object_wrapper="DefaultObjectWrapper(3.0.0)" />
-   </testCase>
-   <testCase name="listhash" />
-   <testCase name="listhashliteral" />
-   <testCase name="listliteral" />
-   <testCase name="localization" >
-      <setting locale="en_AU"/>
-   </testCase>
-   <testCase name="loopvariable" />
-   <testCase name="macros"/>
-   <testCase name="macros2"/>
-   <testCase name="macros-return"/>
-   <testCase name="multimodels"/>
-   <testCase name="nested" />
-   <testCase name="newlines1" />
-   <testCase name="newlines2" />
-   <testCase name="noparse" />
-   <testCase name="number-format" />
-   <testCase name="number-literal" >
-      <setting locale="fr_FR"/>
-   </testCase>
-   <testCase name="numerical-cast" />
-   <testCase name="output-encoding1"/>
-   <testCase name="output-encoding2">
-       <setting output_encoding="UTF-16"/>
-   </testCase>
-   <testCase name="output-encoding3">
-       <setting output_encoding="ISO-8859-1" url_escaping_charset="UTF-16" />
-   </testCase>
-   <testCase name="precedence"/>
-   <testCase name="range" noOutput="true" />
-   <testCase name="recover" />
-   <testCase name="root" />
-   <testCase name="setting" noOutput="true" />
-   <testCase name="sequence-builtins[#endTN]-with-SimpleTemplateModel" expected="sequence-builtins.txt">
-      <setting object_wrapper="org.apache.freemarker.test.templatesuite.models.SimpleMapAndCollectionObjectWrapper(3.0.0)"
/>
-   </testCase>
-   <testCase name="sequence-builtins[#endTN]-with-DefaultObjectWrapper" expected="sequence-builtins.txt">
-      <setting object_wrapper="default"/> 
-   </testCase>
-   <testCase name="sequence-builtins[#endTN]-with-DefaultObjectWrapper-collAdapters" expected="sequence-builtins.txt">
-      <setting object_wrapper="DefaultObjectWrapper(3.0.0)"/>
-   </testCase>
-   <testCase name="simplehash-char-key" noOutput="true" />
-   <testCase name="existence-operators" noOutput="true" />
-   <testCase name="string-builtins1" />
-   <testCase name="string-builtins2" />
-   <testCase name="string-builtins3" noOutput="true" />
-   <testCase name="string-builtins-regexps" />
-   <testCase name="string-builtins-regexps-matches" />
-   <testCase name="stringbimethods" />
-   <testCase name="stringliteral"/>
-   <testCase name="if" />
-   <testCase name="switch" />
-   <testCase name="switch-builtin" noOutput="true" />
-   <testCase name="transforms"/>
-   <testCase name="type-builtins" />
-   <testCase name="date-type-builtins" noOutput="true" />
-   <testCase name="url" noOutput="true" />
-   <testCase name="var-layers"/>
-   <testCase name="variables"/>
-   <testCase name="whitespace-trim"/>
-   <testCase name="wstrip-in-header"/>
-   <testCase name="xml-fragment" />
-   <testCase name="xmlns1" />
-   <testCase name="xmlns2" template="xmlns1.ftl"  expected="xmlns1.txt" />
-   <testCase name="xmlns3" />
-   <testCase name="xmlns4" />
-   <testCase name="xmlns5" />
-   <testCase name="xml-ns_prefix-scope" template="xml-ns_prefix-scope-main.ftl" />
-   <testCase name="hashconcat"/>
-   <testCase name="new-defaultresolver" />
-   <testCase name="new-unrestricted" template="new-defaultresolver.ftl" expected="new-defaultresolver.txt">
-      <setting new_builtin_class_resolver="unrestricted"/>
-   </testCase>   
-   <testCase name="new-allowsnothing" template="new-defaultresolver.ftl">
-      <setting new_builtin_class_resolver="allows_nothing"/>
-   </testCase>   
-   <testCase name="new-optin">
-      <setting new_builtin_class_resolver="
-      		allowed_classes: org.apache.freemarker.test.templatesuite.models.NewTestModel,
-      		trusted_templates: subdir/new-optin.ftl, subdir/subsub/*"
-      />
-   </testCase>   
-   <testCase name="specialvars">
-      <setting locale="en_US" output_encoding="utf-8" url_escaping_charset="iso-8859-1"/>
-   </testCase>   
-   <testCase name="number-to-date" />
-   <testCase name="varargs" />
-   <testCase name="boolean-formatting"  />
-   <testCase name="number-math-builtins" noOutput="true" />
-   <testCase name="string-builtin-coercion" noOutput="true" />
-   
-   <testCase name="overloaded-methods[#endTN]-inc-dow" noOutput="true">
-      <setting object_wrapper="org.apache.freemarker.core.model.impl.DefaultObjectWrapperInc"/>
-   </testCase>
-   <testCase name="overloaded-methods[#endTN]-desc-dow" noOutput="true">
-      <setting object_wrapper="org.apache.freemarker.core.model.impl.DefaultObjectWrapperDesc"/>
-   </testCase>
-</testCases>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/4b75ea93/freemarker-core/build.gradle
----------------------------------------------------------------------
diff --git a/freemarker-core/build.gradle b/freemarker-core/build.gradle
index 3419439..39af628 100644
--- a/freemarker-core/build.gradle
+++ b/freemarker-core/build.gradle
@@ -39,14 +39,6 @@ dependencies {
         // xml-apis is part of Java SE since version 1.4:
         exclude group: "xml-apis", module: "xml-apis"
     }
-
-    testRuntime "jaxen:jaxen:1.0-FCS"
-    testRuntime "saxpath:saxpath:1.0-FCS"
-    testRuntime("xalan:xalan:2.7.0") {
-        // xml-apis is part of Java SE since version 1.4:
-        exclude group: "xml-apis", module: "xml-apis"
-    }
-
 }
 
 compileJavacc {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/4b75ea93/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestCase.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestCase.java
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestCase.java
new file mode 100644
index 0000000..a8a0c55
--- /dev/null
+++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestCase.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.test;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.ConfigurationException;
+import org.apache.freemarker.core.Template;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader;
+import org.apache.freemarker.core.util._NullArgumentException;
+import org.apache.freemarker.core.util._NullWriter;
+import org.apache.freemarker.core.util._StringUtil;
+import org.apache.freemarker.test.templateutil.AssertDirective;
+import org.apache.freemarker.test.templateutil.AssertEqualsDirective;
+import org.apache.freemarker.test.templateutil.AssertFailsDirective;
+import org.apache.freemarker.test.templateutil.NoOutputDirective;
+import org.junit.Ignore;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * Instances of this are created and called by {@link TemplateTestSuite}. (It's on "Ignore"
so that Eclipse doesn't try
+ * to run this alone.) 
+ */
+@Ignore
+class TemplateTestCase extends FileTestCase {
+    
+    // Name of variables exposed to all test FTL-s:
+    private static final String ICI_INT_VALUE_VAR_NAME = "iciIntValue";
+    private static final String TEST_NAME_VAR_NAME = "testName";
+    private static final String NO_OUTPUT_VAR_NAME = "noOutput";
+    private static final String ASSERT_FAILS_VAR_NAME = "assertFails";
+    private static final String ASSERT_EQUALS_VAR_NAME = "assertEquals";
+    private static final String ASSERT_VAR_NAME = "assert";
+
+    private final TemplateTestSuite testSuite;
+
+    private final String simpleTestName;
+    private final String templateName;
+    private final String expectedFileName;
+    private final boolean noOutput;
+    
+    private final Configuration.ExtendableBuilder<?> confB;
+    private final HashMap<String, Object> dataModel = new HashMap<>();
+    
+    TemplateTestCase(
+            TemplateTestSuite testSuite,
+            String testName, String simpleTestName, String templateName, String expectedFileName,
boolean noOutput,
+            Version incompatibleImprovements) {
+        super(testName);
+        _NullArgumentException.check("testName", testName);
+
+        _NullArgumentException.check("testSuite", testSuite);
+        this.testSuite = testSuite;
+        
+        _NullArgumentException.check("simpleTestName", simpleTestName);
+        this.simpleTestName = simpleTestName;
+        
+        _NullArgumentException.check("templateName", templateName);
+        this.templateName = templateName;
+        
+        _NullArgumentException.check("expectedFileName", expectedFileName);
+        this.expectedFileName = expectedFileName;
+        
+        this.noOutput = noOutput;
+
+        confB = new TestConfigurationBuilder(incompatibleImprovements);
+    }
+    
+    void setSetting(String param, String value) throws IOException {
+        if ("auto_import".equals(param)) {
+            StringTokenizer st = new StringTokenizer(value);
+            if (!st.hasMoreTokens()) fail("Expecting libname");
+            String libname = st.nextToken();
+            if (!st.hasMoreTokens()) fail("Expecting 'as <alias>' in autoimport");
+            String as = st.nextToken();
+            if (!as.equals("as")) fail("Expecting 'as <alias>' in autoimport");
+            if (!st.hasMoreTokens()) fail("Expecting alias after 'as' in autoimport");
+            String alias = st.nextToken();
+            confB.addAutoImport(alias, libname);
+        } else if ("source_encoding".equals(param)) {
+            confB.setSourceEncoding(Charset.forName(value));
+        // INCOMPATIBLE_IMPROVEMENTS is a list here, and was already set in the constructor.
+        } else if (!Configuration.ExtendableBuilder.INCOMPATIBLE_IMPROVEMENTS_KEY.equals(param))
{
+            try {
+                confB.setSetting(param, value);
+            } catch (ConfigurationException e) {
+                throw new RuntimeException(
+                        "Failed to set setting " +
+                        _StringUtil.jQuote(param) + " to " +
+                        _StringUtil.jQuote(value) + "; see cause exception.",
+                        e);
+            }
+        }
+    }
+    
+    /*
+     * This method just contains all the code to seed the data model 
+     * ported over from the individual classes. This seems ugly and unnecessary.
+     * We really might as well just expose pretty much 
+     * the same tree to all our tests. (JR)
+     */
+    @Override
+    @SuppressWarnings("boxing")
+    protected void setUp() throws Exception {
+        confB.setTemplateLoader(
+                new CopyrightCommentRemoverTemplateLoader(
+                        new ClassTemplateLoader(testSuite.getClass(), "templates")));
+        
+        dataModel.put(ASSERT_VAR_NAME, AssertDirective.INSTANCE);
+        dataModel.put(ASSERT_EQUALS_VAR_NAME, AssertEqualsDirective.INSTANCE);
+        dataModel.put(ASSERT_FAILS_VAR_NAME, AssertFailsDirective.INSTANCE);
+        dataModel.put(NO_OUTPUT_VAR_NAME, NoOutputDirective.INSTANCE);
+
+        dataModel.put(TEST_NAME_VAR_NAME, simpleTestName);
+        dataModel.put(ICI_INT_VALUE_VAR_NAME, confB.getIncompatibleImprovements().intValue());
+
+        testSuite.setUpTestCase(simpleTestName, dataModel, confB);
+    }
+    
+    @Override
+    protected void runTest() throws IOException, ConfigurationException {
+        Template template;
+        try {
+            template = confB.build().getTemplate(templateName);
+        } catch (IOException e) {
+            throw new AssertionFailedError(
+                    "Could not load template " + _StringUtil.jQuote(templateName) + ":\n"
+ getStackTrace(e));
+        }
+
+        testSuite.validateTemplate(template);
+
+        StringWriter out = noOutput ? null : new StringWriter();
+        try {
+            template.process(dataModel, out != null ? out : _NullWriter.INSTANCE);
+        } catch (TemplateException e) {
+            throw new AssertionFailedError("Template " + _StringUtil.jQuote(templateName)
+ " has stopped with error:\n"
+                        + getStackTrace(e));
+        }
+        
+        if (out != null) {
+            assertExpectedFileEqualsString(expectedFileName, out.toString());
+        }
+    }
+
+    private String getStackTrace(Throwable e) {
+        StringWriter sw = new StringWriter();
+        e.printStackTrace(new PrintWriter(sw));
+        return sw.toString();
+    }
+
+    @Override
+    protected String getExpectedContentFileDirectoryResourcePath() throws IOException {
+        return joinResourcePaths(super.getExpectedContentFileDirectoryResourcePath(), "expected");
+    }
+
+    @Override
+    protected Charset getTestResourceDefaultCharset() {
+        return confB.getOutputEncoding() != null ? confB.getOutputEncoding() : StandardCharsets.UTF_8;
+    }
+
+    @Override
+    protected Class getTestResourcesBaseClass() {
+        return testSuite.getClass();
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/4b75ea93/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java
new file mode 100644
index 0000000..c9b4e4c
--- /dev/null
+++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.Template;
+import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core.util._StringUtil;
+import org.apache.freemarker.dom.NodeModel;
+import org.junit.Test;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import junit.framework.TestSuite;
+
+/**
+ * Abstract superclass for JUnit test suites where the test cases are defined in
+ * {@code <suiteClassPackage>/testcases.xml}, and process templates and compare their
output with the expected output.
+ * It's important to add this static method like <code>public static TestSuite suite()
{ return new
+ * SomeTemplateTestSuite(); }</code> to concrete subclasses, or else it won't be detected
and run by build tools and
+ * IDE-s.
+ * <p>
+ * If you only want to run certain tests, you can specify a regular expression for the test
name in the
+ * {@link #TEST_FILTER_PROPERTY_NAME} system property.
+ * <p>
+ * To add a test-case, go to the resource directory that corresponds to the package of the
{@link TemplateTestSuite}
+ * subclass and inside that directory:</p>
+ * <ol>
+ * <li>Add a new <tt>testcase</tt> element to <tt>testcases.xml</tt></li>
+ * <li>Add a template to <tt>templates/</tt> with fits the <tt>testcase</tt>
added to the XML (by default it's the test
+ * case name + ".ftl")</li>
+ * <li>Add the expected output to <tt>references/</tt> with fits the <tt>testcase</tt>
added to the XML (by default
+ * it's the test name + ".txt")</li>
+ * <li>If you want to add items to the data-model or change the {@link Configuration},
modify the
+ * {@link #setUpTestCase(String, Map, Configuration.ExtendableBuilder)}} method in the {@link
TemplateTestSuite}
+ * subclass.</li>
+ * </ol>
+ */
+public abstract class TemplateTestSuite extends TestSuite {
+    
+    private static final String ELEM_TEST_CASE = "testCase";
+
+    private static final String ELEM_SETTING = "setting";
+
+    private static final String ATTR_NO_OUTPUT = "noOutput";
+
+    private static final String ATTR_EXPECTED = "expected";
+
+    private static final String ATTR_TEMPLATE = "template";
+
+    private static final String END_TEMPLATE_NAME_MARK = "[#endTN]";
+
+    public static final String CONFIGURATION_XML_FILE_NAME = "testcases.xml";
+
+    /**
+     * When setting this system property, only the tests whose name matches the
+     * given regular expression will be executed.
+     */
+    public static final String TEST_FILTER_PROPERTY_NAME = "freemareker.templateTestSuite.testFilter";
+    
+    /**
+     * Comma separated list of "incompatible improvements" versions to run the test cases
with.
+     */
+    public static final String INCOMPATIBLE_IMPROVEMENTS_PROPERTY_NAME
+            = "freemareker.templateTestSuite.incompatibleImprovements";
+    
+    private final Map<String, String> testSuiteSettings = new LinkedHashMap<>();
+
+    private final ArrayList<Version> testSuiteIcis;
+
+    private final Pattern testCaseNameFilter;
+
+    public TemplateTestSuite() {
+        try {
+            NodeModel.useJaxenXPathSupport();
+
+            String filterStr = System.getProperty(TEST_FILTER_PROPERTY_NAME);
+            testCaseNameFilter = filterStr != null ? Pattern.compile(filterStr) : null;
+            if (testCaseNameFilter != null) {
+                System.out.println(
+                        "Note: " + TEST_FILTER_PROPERTY_NAME + " is " + _StringUtil.jQuote(testCaseNameFilter));
+            }
+
+            testSuiteIcis = new ArrayList<>();
+            String testedIcIsStr = System.getProperty(INCOMPATIBLE_IMPROVEMENTS_PROPERTY_NAME);
+            if (testedIcIsStr != null) {
+                for (String iciStr : testedIcIsStr.split(",")) {
+                    iciStr = iciStr.trim();
+                    if (iciStr.length() != 0) {
+                        testSuiteIcis.add(new Version(iciStr));
+                    }
+                }
+            }
+            if (testSuiteIcis.isEmpty()) {
+                testSuiteIcis.add(getMinIcIVersion());
+                testSuiteIcis.add(getMaxIcIVersion());
+            }
+
+            java.net.URL url = getClass().getResource(CONFIGURATION_XML_FILE_NAME);
+            if (url == null) {
+                throw new IOException("Resource not found: "
+                        + getClass().getName() + ", " + CONFIGURATION_XML_FILE_NAME);
+            }
+            processConfigXML(url.toURI());
+        } catch (Exception e) {
+            throw new IllegalStateException("Failed to initialize test suite", e);
+        }
+    }
+
+    /**
+     * At least with Gradle 3.5 TestSuite-s aren't run even if we explicitly include the
class, unless we have a
+     * test method in them (which won't be run).
+     */
+    @Test
+    public final void gradleTestPluginWorkaround() {
+        // Does nothing
+    }
+
+    protected abstract void setUpTestCase(String simpleTestName, Map<String, Object>
dataModel,
+            Configuration.ExtendableBuilder<?> confB) throws Exception;
+
+    /**
+     * Read the test case configurations file and build up the test suite.
+     */
+    private void processConfigXML(URI uri) throws Exception {
+        Element testCasesElem = loadXMLFromURL(uri);
+        
+        NodeList children = testCasesElem.getChildNodes();
+        for (int childIdx = 0; childIdx < children.getLength(); childIdx++) {
+            Node n = children.item(childIdx);
+            if (n.getNodeType() == Node.ELEMENT_NODE) {
+                final String nodeName = n.getNodeName();
+                if (nodeName.equals(ELEM_SETTING)) {
+                    NamedNodeMap attrs = n.getAttributes();
+                    for (int attrIdx = 0; attrIdx < attrs.getLength(); attrIdx++) {
+                        Attr attr = (Attr) attrs.item(attrIdx);
+                        testSuiteSettings.put(attr.getName(), attr.getValue());
+                    }
+                } else if (nodeName.equals(ELEM_TEST_CASE)) {
+                    for (TemplateTestCase testCase : createTestCasesFromElement((Element)
n)) {
+                        addTest(testCase);
+                    }
+                }
+            }
+        }
+    }
+
+    private Element loadXMLFromURL(URI uri) throws ParserConfigurationException, SAXException,
IOException {
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        // dbf.setValidating(true);
+        DocumentBuilder db = dbf.newDocumentBuilder();
+        Document d = db.parse(uri.toString());
+        return d.getDocumentElement();
+    }
+    
+    String getTextInElement(Element e) {
+        StringBuilder buf = new StringBuilder();
+        NodeList children = e.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node n = children.item(i);
+            short type = n.getNodeType();
+            if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) {
+                buf.append(n.getNodeValue());
+            }
+        }
+        return buf.toString();
+    }
+    
+    /**
+     * Returns the list of test cases generated from the {@link #ELEM_TEST_CASE} element.
+     * There can be multiple generated test cases because of "incompatible improvements"
variations, or none because
+     * of the {@code nameFilter}.
+     */
+    private List<TemplateTestCase> createTestCasesFromElement(Element testCaseElem)
+            throws Exception {
+        final String caseName = _StringUtil.emptyToNull(testCaseElem.getAttribute("name"));
+        if (caseName == null) throw new Exception("Invalid XML: the \"name\" attribute is
mandatory.");
+        
+        if (testCaseNameFilter != null
+                && !testCaseNameFilter.matcher(caseName).matches()) {
+            return Collections.emptyList();
+        }
+        
+        final String templateName;
+        final String expectedFileName;
+        {
+            final String beforeEndTN;
+            final String afterEndTN;
+            {
+                int tBNameSep = caseName.indexOf(END_TEMPLATE_NAME_MARK);
+                beforeEndTN = tBNameSep == -1 ? caseName : caseName.substring(0, tBNameSep);
+                afterEndTN = tBNameSep == -1
+                        ? "" : caseName.substring(tBNameSep + END_TEMPLATE_NAME_MARK.length());
+            }
+            
+            {
+                String s = _StringUtil.emptyToNull(testCaseElem.getAttribute(ATTR_TEMPLATE));
+                templateName = s != null ? s : beforeEndTN + ".ftl";
+            }
+    
+            {
+                String s = _StringUtil.emptyToNull(testCaseElem.getAttribute(ATTR_EXPECTED));
+                expectedFileName = s != null ? s : beforeEndTN + afterEndTN + ".txt";
+            }
+        }
+        
+        final boolean noOutput;
+        {
+            String s = _StringUtil.emptyToNull(testCaseElem.getAttribute(ATTR_NO_OUTPUT));
+            noOutput = s != null && _StringUtil.getYesNo(s);
+        }
+
+        final Map<String, String> testCaseSettings = getCaseFMSettings(testCaseElem);
+        
+        final List<Version> icisToTest;
+        {
+            final String testCaseIcis = testCaseSettings.get(Configuration.ExtendableBuilder.INCOMPATIBLE_IMPROVEMENTS_KEY);
+                    
+            icisToTest = testCaseIcis != null ? parseVersionList(testCaseIcis) : testSuiteIcis;
+            if (icisToTest.isEmpty()) {
+                throw new Exception("The incompatible_improvement list was empty");
+            }
+        }
+
+        List<TemplateTestCase> result = new ArrayList<>();
+        for (Version iciToTest : icisToTest) {
+            TemplateTestCase testCase = new TemplateTestCase(
+                    this,
+                    caseName + "(ici=" + iciToTest + ")", caseName,
+                    templateName, expectedFileName, noOutput, iciToTest);
+            for (Map.Entry<String, String> setting : testSuiteSettings.entrySet())
{
+                testCase.setSetting(setting.getKey(), setting.getValue());
+            }
+            for (Map.Entry<String, String> setting : testCaseSettings.entrySet()) {
+                testCase.setSetting(setting.getKey(), setting.getValue());
+            }
+            
+            result.add(testCase);
+        }
+        
+        return result;
+    }
+
+    private List<Version> parseVersionList(String versionsStr) {
+        List<Version> versions = new ArrayList<>();
+        for (String versionStr : versionsStr.split(",")) {
+            versionStr = versionStr.trim();
+            if (versionStr.length() != 0) {
+                final Version v;
+                if ("min".equals(versionStr)) {
+                    v = getMinIcIVersion();
+                } else if ("max".equals(versionStr)) {
+                    v = getMaxIcIVersion();
+                } else {
+                    v = new Version(versionStr);
+                }
+                if (!versions.contains(v)) {
+                    versions.add(v);
+                }
+            }
+        }
+        return versions;
+    }
+
+    private Version getMaxIcIVersion() {
+        Version v = Configuration.getVersion();
+        // Remove nightly, RC and such:
+        return new Version(v.getMajor(), v.getMinor(), v.getMicro());
+    }
+
+    private Version getMinIcIVersion() {
+        return Configuration.VERSION_3_0_0;
+    }
+
+    private Map<String, String> getCaseFMSettings(Element e) {
+        final Map<String, String> caseFMSettings;
+        caseFMSettings = new LinkedHashMap<>();
+        NodeList settingElems = e.getElementsByTagName(ELEM_SETTING);
+        for (int elemIdx = 0; elemIdx < settingElems.getLength(); elemIdx++) {
+            NamedNodeMap attrs = settingElems.item(elemIdx).getAttributes();
+            for (int attrIdx = 0; attrIdx < attrs.getLength(); attrIdx++) {
+                Attr attr = (Attr) attrs.item(attrIdx);
+
+                final String settingName = attr.getName();
+                caseFMSettings.put(settingName, attr.getValue());
+            }
+        }
+        return caseFMSettings;
+    }
+
+    /**
+     * Override this if you want to check something in the main {@link Template} before running
the test.
+     */
+    protected void validateTemplate(Template template) {
+        // Does nothing by default
+    }
+}



Mime
View raw message