You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/05/15 21:24:05 UTC
[49/51] [abbrv] [partial] incubator-freemarker git commit:
Restructured project so that freemarker-test-utils depends on freemarker-core
(and hence can provide common classes for testing templates,
and can use utility classes defined in the core). As a c
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java
new file mode 100644
index 0000000..57e40fa
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.core;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import org.apache.freemarker.test.TestConfigurationBuilder;
+import org.junit.Test;
+
+public class ActualNamingConvetionTest {
+
+ @Test
+ public void testUndetectable() throws IOException {
+ final String ftl = "<#if true>${x?size}</#if>";
+ assertEquals(getActualNamingConvention(ftl,
+ ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION), ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION);
+ assertEquals(getActualNamingConvention(ftl,
+ ParsingConfiguration.LEGACY_NAMING_CONVENTION), ParsingConfiguration.LEGACY_NAMING_CONVENTION);
+ assertEquals(getActualNamingConvention(ftl,
+ ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION), ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION);
+ }
+
+ @Test
+ public void testLegacyDetected() throws IOException {
+ final String ftl = "${x?upper_case}";
+ assertEquals(getActualNamingConvention(ftl,
+ ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION), ParsingConfiguration.LEGACY_NAMING_CONVENTION);
+ assertEquals(getActualNamingConvention(ftl,
+ ParsingConfiguration.LEGACY_NAMING_CONVENTION), ParsingConfiguration.LEGACY_NAMING_CONVENTION);
+ }
+
+ @Test
+ public void testCamelCaseDetected() throws IOException {
+ final String ftl = "${x?upperCase}";
+ assertEquals(getActualNamingConvention(ftl,
+ ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION), ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION);
+ assertEquals(getActualNamingConvention(ftl,
+ ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION), ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION);
+ }
+
+ private int getActualNamingConvention(String ftl, int namingConvention) throws IOException {
+ return new Template(null, ftl,
+ new TestConfigurationBuilder().namingConvention(namingConvention).build())
+ .getActualNamingConvention();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java
new file mode 100644
index 0000000..88f0646
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.core;
+
+import static org.apache.freemarker.core.ParsingConfiguration.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import org.apache.freemarker.test.TestConfigurationBuilder;
+import org.junit.Test;
+
+public class ActualTagSyntaxTest {
+
+ @Test
+ public void testWithFtlHeader() throws IOException {
+ testWithFtlHeader(AUTO_DETECT_TAG_SYNTAX);
+ testWithFtlHeader(ANGLE_BRACKET_TAG_SYNTAX);
+ testWithFtlHeader(SQUARE_BRACKET_TAG_SYNTAX);
+ }
+
+ private void testWithFtlHeader(int cfgTagSyntax) throws IOException {
+ assertEquals(getActualTagSyntax("[#ftl]foo", cfgTagSyntax), SQUARE_BRACKET_TAG_SYNTAX);
+ assertEquals(getActualTagSyntax("<#ftl>foo", cfgTagSyntax), ANGLE_BRACKET_TAG_SYNTAX);
+ }
+
+ @Test
+ public void testUndecidable() throws IOException {
+ assertEquals(getActualTagSyntax("foo", AUTO_DETECT_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+ assertEquals(getActualTagSyntax("foo", ANGLE_BRACKET_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+ assertEquals(getActualTagSyntax("foo", SQUARE_BRACKET_TAG_SYNTAX), SQUARE_BRACKET_TAG_SYNTAX);
+ }
+
+ @Test
+ public void testDecidableWithoutFtlHeader() throws IOException {
+ assertEquals(getActualTagSyntax("foo<#if true></#if>", AUTO_DETECT_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+ assertEquals(getActualTagSyntax("foo<#if true></#if>", ANGLE_BRACKET_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+ assertEquals(getActualTagSyntax("foo<#if true></#if>", SQUARE_BRACKET_TAG_SYNTAX), SQUARE_BRACKET_TAG_SYNTAX);
+
+ assertEquals(getActualTagSyntax("foo[#if true][/#if]", AUTO_DETECT_TAG_SYNTAX), SQUARE_BRACKET_TAG_SYNTAX);
+ assertEquals(getActualTagSyntax("foo[#if true][/#if]", ANGLE_BRACKET_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+ assertEquals(getActualTagSyntax("foo[#if true][/#if]", SQUARE_BRACKET_TAG_SYNTAX), SQUARE_BRACKET_TAG_SYNTAX);
+ }
+
+ private int getActualTagSyntax(String ftl, int cfgTagSyntax) throws IOException {
+ return new Template(
+ null, ftl,
+ new TestConfigurationBuilder().tagSyntax(cfgTagSyntax).build()).getActualTagSyntax();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java
new file mode 100644
index 0000000..61ba02b
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.core;
+
+import java.io.IOException;
+
+import org.apache.freemarker.test.TemplateTest;
+import org.junit.Test;
+
+public class BreakPlacementTest extends TemplateTest {
+
+ private static final String BREAK_NESTING_ERROR_MESSAGE_PART = "<#break> must be nested";
+
+ @Test
+ public void testValidPlacements() throws IOException, TemplateException {
+ assertOutput("<#assign x = 1><#switch x><#case 1>one<#break><#case 2>two</#switch>", "one");
+ assertOutput("<#list 1..2 as x>${x}<#break></#list>", "1");
+ assertOutput("<#list 1..2>[<#items as x>${x}<#break></#items>]</#list>", "[1]");
+ assertOutput("<#list 1..2 as x>${x}<#list 1..3>B<#break>E<#items as y></#items></#list>E</#list>.", "1B.");
+ assertOutput("<#list 1..2 as x>${x}<#list 3..4 as x>${x}<#break></#list>;</#list>", "13;23;");
+ assertOutput("<#list [1..2, 3..4, [], 5..6] as xs>[<#list xs as x>${x}<#else><#break></#list>]</#list>.",
+ "[12][34][.");
+ assertOutput("<#list [1..2, 3..4, [], 5..6] as xs>"
+ + "<#list xs>[<#items as x>${x}</#items>]<#else><#break></#list>"
+ + "</#list>.",
+ "[12][34].");
+ }
+
+ @Test
+ public void testInvalidPlacements() throws IOException, TemplateException {
+ assertErrorContains("<#break>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#list 1..2 as x>${x}</#list><#break>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#if false><#break></#if>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#list xs><#break></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#list 1..2 as x>${x}<#else><#break></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#list 1..2 as x>${x}<#macro m><#break></#macro></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/CamelCaseTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
new file mode 100644
index 0000000..95572ad
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
@@ -0,0 +1,486 @@
+/*
+ * 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.core;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
+import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
+import org.apache.freemarker.core.util._StringUtil;
+import org.apache.freemarker.test.TemplateTest;
+import org.apache.freemarker.test.TestConfigurationBuilder;
+import org.junit.Test;
+
+public class CamelCaseTest extends TemplateTest {
+
+ @Test
+ public void camelCaseSpecialVars() throws IOException, TemplateException {
+ setConfiguration(new TestConfigurationBuilder()
+ .outputEncoding(StandardCharsets.UTF_8)
+ .urlEscapingCharset(StandardCharsets.ISO_8859_1)
+ .locale(Locale.GERMANY)
+ .build());
+ assertOutput("${.dataModel?isHash?c}", "true");
+ assertOutput("${.data_model?is_hash?c}", "true");
+ assertOutput("${.localeObject.toString()}", "de_DE");
+ assertOutput("${.locale_object.toString()}", "de_DE");
+ assertOutput("${.templateName!'null'}", "null");
+ assertOutput("${.template_name!'null'}", "null");
+ assertOutput("${.currentTemplateName!'null'}", "null");
+ assertOutput("${.current_template_name!'null'}", "null");
+ assertOutput("${.mainTemplateName!'null'}", "null");
+ assertOutput("${.main_template_name!'null'}", "null");
+ assertOutput("${.outputEncoding}", StandardCharsets.UTF_8.name());
+ assertOutput("${.output_encoding}", StandardCharsets.UTF_8.name());
+ assertOutput("${.outputFormat}", UndefinedOutputFormat.INSTANCE.getName());
+ assertOutput("${.output_format}", UndefinedOutputFormat.INSTANCE.getName());
+ assertOutput("${.urlEscapingCharset}", StandardCharsets.ISO_8859_1.name());
+ assertOutput("${.url_escaping_charset}", StandardCharsets.ISO_8859_1.name());
+ assertOutput("${.currentNode!'-'}", "-");
+ assertOutput("${.current_node!'-'}", "-");
+ }
+
+ @Test
+ public void camelCaseSpecialVarsInErrorMessage() throws IOException, TemplateException {
+ assertErrorContains("${.fooBar}", "dataModel", "\\!data_model");
+ assertErrorContains("${.foo_bar}", "data_model", "\\!dataModel");
+ // [2.4] If camel case will be the recommended style, then this need to be inverted:
+ assertErrorContains("${.foo}", "data_model", "\\!dataModel");
+
+ assertErrorContains("<#if x><#elseIf y></#if>${.foo}", "dataModel", "\\!data_model");
+ assertErrorContains("<#if x><#elseif y></#if>${.foo}", "data_model", "\\!dataModel");
+
+ setConfigurationToCamelCaseNamingConvention();
+ assertErrorContains("${.foo}", "dataModel", "\\!data_model");
+
+ setConfigurationToLegacyCaseNamingConvention();
+ assertErrorContains("${.foo}", "data_model", "\\!dataModel");
+ }
+
+ @Test
+ public void camelCaseSettingNames() throws IOException, TemplateException {
+ assertOutput("<#setting booleanFormat='Y,N'>${true} <#setting booleanFormat='+,-'>${true}", "Y +");
+ assertOutput("<#setting boolean_format='Y,N'>${true} <#setting boolean_format='+,-'>${true}", "Y +");
+
+ // Still works inside ?interpret
+ assertOutput("<@r\"<#setting booleanFormat='Y,N'>${true}\"?interpret />", "Y");
+ }
+
+ @Test
+ public void camelCaseFtlHeaderParameters() throws IOException, TemplateException {
+ assertOutput(
+ "<#ftl "
+ + "stripWhitespace=false "
+ + "stripText=true "
+ + "outputFormat='" + HTMLOutputFormat.INSTANCE.getName() + "' "
+ + "autoEsc=true "
+ + "nsPrefixes={} "
+ + ">\nx\n<#if true>\n${.outputFormat}\n</#if>\n",
+ "\nHTML\n");
+
+ assertOutput(
+ "<#ftl "
+ + "strip_whitespace=false "
+ + "strip_text=true "
+ + "output_format='" + HTMLOutputFormat.INSTANCE.getName() + "' "
+ + "auto_esc=true "
+ + "ns_prefixes={} "
+ + ">\nx\n<#if true>\n${.output_format}\n</#if>\n",
+ "\nHTML\n");
+
+ assertErrorContains("<#ftl strip_text=true xmlns={}>", "ns_prefixes", "\\!nsPrefixes");
+ assertErrorContains("<#ftl stripText=true xmlns={}>", "nsPrefixes");
+
+ assertErrorContains("<#ftl stripWhitespace=true strip_text=true>", "naming convention");
+ assertErrorContains("<#ftl strip_whitespace=true stripText=true>", "naming convention");
+ assertErrorContains("<#ftl stripWhitespace=true>${.foo_bar}", "naming convention");
+ assertErrorContains("<#ftl strip_whitespace=true>${.fooBar}", "naming convention");
+
+ setConfiguration(new TestConfigurationBuilder()
+ .namingConvention(ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION)
+ .outputEncoding(StandardCharsets.UTF_8)
+ .build());
+ assertErrorContains("<#ftl strip_whitespace=true>", "naming convention");
+ assertOutput("<#ftl stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
+
+ setConfiguration(new TestConfigurationBuilder()
+ .namingConvention(ParsingConfiguration.LEGACY_NAMING_CONVENTION)
+ .outputEncoding(StandardCharsets.UTF_8)
+ .build());
+ assertErrorContains("<#ftl stripWhitespace=true>", "naming convention");
+ assertOutput("<#ftl strip_whitespace=true>${.output_encoding}", StandardCharsets.UTF_8.name());
+
+ setConfiguration(new TestConfigurationBuilder()
+ .namingConvention(ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION)
+ .outputEncoding(StandardCharsets.UTF_8)
+ .build());
+ assertOutput("<#ftl stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
+ assertOutput("<#ftl encoding='iso-8859-1' stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
+ assertOutput("<#ftl stripWhitespace=true encoding='iso-8859-1'>${.outputEncoding}", StandardCharsets.UTF_8.name());
+ assertOutput("<#ftl encoding='iso-8859-1' strip_whitespace=true>${.output_encoding}", StandardCharsets.UTF_8.name());
+ assertOutput("<#ftl strip_whitespace=true encoding='iso-8859-1'>${.output_encoding}", StandardCharsets.UTF_8.name());
+ }
+
+ @Test
+ public void camelCaseSettingNamesInErrorMessages() throws IOException, TemplateException {
+ assertErrorContains("<#setting fooBar=1>", "booleanFormat", "\\!boolean_format");
+ assertErrorContains("<#setting foo_bar=1>", "boolean_format", "\\!booleanFormat");
+ // [2.4] If camel case will be the recommended style, then this need to be inverted:
+ assertErrorContains("<#setting foo=1>", "boolean_format", "\\!booleanFormat");
+
+ assertErrorContains("<#if x><#elseIf y></#if><#setting foo=1>", "booleanFormat", "\\!boolean_format");
+ assertErrorContains("<#if x><#elseif y></#if><#setting foo=1>", "boolean_format", "\\!booleanFormat");
+
+ setConfigurationToCamelCaseNamingConvention();
+ assertErrorContains("<#setting foo=1>", "booleanFormat", "\\!boolean_format");
+
+ setConfigurationToLegacyCaseNamingConvention();
+ assertErrorContains("<#setting foo=1>", "boolean_format", "\\!booleanFormat");
+ }
+
+ @Test
+ public void camelCaseIncludeParameters() throws IOException, TemplateException {
+ assertOutput("<#ftl stripWhitespace=true>[<#include 'noSuchTemplate' ignoreMissing=true>]", "[]");
+ assertOutput("<#ftl strip_whitespace=true>[<#include 'noSuchTemplate' ignore_missing=true>]", "[]");
+ assertErrorContains("<#ftl stripWhitespace=true>[<#include 'noSuchTemplate' ignore_missing=true>]",
+ "naming convention", "ignore_missing");
+ assertErrorContains("<#ftl strip_whitespace=true>[<#include 'noSuchTemplate' ignoreMissing=true>]",
+ "naming convention", "ignoreMissing");
+ }
+
+ @Test
+ public void specialVarsHasBothNamingStyle() throws IOException, TemplateException {
+ assertContainsBothNamingStyles(
+ new HashSet(Arrays.asList(ASTExpBuiltInVariable.SPEC_VAR_NAMES)),
+ new NamePairAssertion() { @Override
+ public void assertPair(String name1, String name2) { } });
+ }
+
+ @Test
+ public void camelCaseBuiltIns() throws IOException, TemplateException {
+ assertOutput("${'x'?upperCase}", "X");
+ assertOutput("${'x'?upper_case}", "X");
+ }
+
+ @Test
+ public void stringLiteralInterpolation() throws IOException, TemplateException {
+ assertEquals(ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION, getConfiguration().getNamingConvention());
+ addToDataModel("x", "x");
+
+ assertOutput("${'-${x?upperCase}-'} ${x?upperCase}", "-X- X");
+ assertOutput("${x?upperCase} ${'-${x?upperCase}-'}", "X -X-");
+ assertOutput("${'-${x?upper_case}-'} ${x?upper_case}", "-X- X");
+ assertOutput("${x?upper_case} ${'-${x?upper_case}-'}", "X -X-");
+
+ assertErrorContains("${'-${x?upper_case}-'} ${x?upperCase}",
+ "naming convention", "legacy", "upperCase", "detection", "9");
+ assertErrorContains("${x?upper_case} ${'-${x?upperCase}-'}",
+ "naming convention", "legacy", "upperCase", "detection", "5");
+ assertErrorContains("${'-${x?upperCase}-'} ${x?upper_case}",
+ "naming convention", "camel", "upper_case");
+ assertErrorContains("${x?upperCase} ${'-${x?upper_case}-'}",
+ "naming convention", "camel", "upper_case");
+
+ setConfigurationToCamelCaseNamingConvention();
+ assertOutput("${'-${x?upperCase}-'} ${x?upperCase}", "-X- X");
+ assertErrorContains("${'-${x?upper_case}-'}",
+ "naming convention", "camel", "upper_case", "\\!detection");
+
+ setConfigurationToLegacyCaseNamingConvention();
+ assertOutput("${'-${x?upper_case}-'} ${x?upper_case}", "-X- X");
+ assertErrorContains("${'-${x?upperCase}-'}",
+ "naming convention", "legacy", "upperCase", "\\!detection");
+ }
+
+ @Test
+ public void evalAndInterpret() throws IOException, TemplateException {
+ assertEquals(ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION, getConfiguration().getNamingConvention());
+ // The naming convention detected doesn't affect the enclosing template's naming convention.
+ // - ?eval:
+ assertOutput("${\"'x'?upperCase\"?eval}${'x'?upper_case}", "XX");
+ assertOutput("${\"'x'?upper_case\"?eval}${'x'?upperCase}", "XX");
+ assertOutput("${'x'?upperCase}${\"'x'?upper_case\"?eval}", "XX");
+ assertErrorContains("${\"'x'\n?upperCase\n?is_string\"?eval}",
+ "naming convention", "camel", "upperCase", "is_string", "line 2", "line 3");
+ // - ?interpret:
+ assertOutput("<@r\"${'x'?upperCase}\"?interpret />${'x'?upper_case}", "XX");
+ assertOutput("<@r\"${'x'?upper_case}\"?interpret />${'x'?upperCase}", "XX");
+ assertOutput("${'x'?upper_case}<@r\"${'x'?upperCase}\"?interpret />", "XX");
+ assertErrorContains("<@r\"${'x'\n?upperCase\n?is_string}\"?interpret />",
+ "naming convention", "camel", "upperCase", "is_string", "line 2", "line 3");
+
+ // Will be inherited by ?eval-ed/?interpreted fragments:
+ setConfigurationToCamelCaseNamingConvention();
+ // - ?eval:
+ assertErrorContains("${\"'x'?upper_case\"?eval}", "naming convention", "camel", "upper_case");
+ assertOutput("${\"'x'?upperCase\"?eval}", "X");
+ // - ?interpret:
+ assertErrorContains("<@r\"${'x'?upper_case}\"?interpret />", "naming convention", "camel", "upper_case");
+ assertOutput("<@r\"${'x'?upperCase}\"?interpret />", "X");
+
+ // Again, will be inherited by ?eval-ed/?interpreted fragments:
+ setConfigurationToLegacyCaseNamingConvention();
+ // - ?eval:
+ assertErrorContains("${\"'x'?upperCase\"?eval}", "naming convention", "legacy", "upperCase");
+ assertOutput("${\"'x'?upper_case\"?eval}", "X");
+ // - ?interpret:
+ assertErrorContains("<@r\"${'x'?upperCase}\"?interpret />", "naming convention", "legacy", "upperCase");
+ assertOutput("<@r\"${'x'?upper_case}\"?interpret />", "X");
+ }
+
+ private void setConfigurationToLegacyCaseNamingConvention() {
+ setConfiguration(new TestConfigurationBuilder()
+ .namingConvention(ParsingConfiguration.LEGACY_NAMING_CONVENTION)
+ .build());
+ }
+
+ @Test
+ public void camelCaseBuiltInErrorMessage() throws IOException, TemplateException {
+ assertErrorContains("${'x'?upperCasw}", "upperCase", "\\!upper_case");
+ assertErrorContains("${'x'?upper_casw}", "upper_case", "\\!upperCase");
+ // [2.4] If camel case will be the recommended style, then this need to be inverted:
+ assertErrorContains("${'x'?foo}", "upper_case", "\\!upperCase");
+
+ assertErrorContains("<#if x><#elseIf y></#if> ${'x'?foo}", "upperCase", "\\!upper_case");
+ assertErrorContains("<#if x><#elseif y></#if>${'x'?foo}", "upper_case", "\\!upperCase");
+
+ setConfigurationToCamelCaseNamingConvention();
+ assertErrorContains("${'x'?foo}", "upperCase", "\\!upper_case");
+ setConfigurationToLegacyCaseNamingConvention();
+ assertErrorContains("${'x'?foo}", "upper_case", "\\!upperCase");
+ }
+
+ private void setConfigurationToCamelCaseNamingConvention() {
+ setConfiguration(new TestConfigurationBuilder()
+ .namingConvention(ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION)
+ .build());
+ }
+
+ @Test
+ public void builtInsHasBothNamingStyle() throws IOException, TemplateException {
+ assertContainsBothNamingStyles(getConfiguration().getSupportedBuiltInNames(), new NamePairAssertion() {
+
+ @Override
+ public void assertPair(String name1, String name2) {
+ ASTExpBuiltIn bi1 = ASTExpBuiltIn.BUILT_INS_BY_NAME.get(name1);
+ ASTExpBuiltIn bi2 = ASTExpBuiltIn.BUILT_INS_BY_NAME.get(name2);
+ assertTrue("\"" + name1 + "\" and \"" + name2 + "\" doesn't belong to the same BI object.",
+ bi1 == bi2);
+ }
+
+ });
+ }
+
+ private void assertContainsBothNamingStyles(Set<String> names, NamePairAssertion namePairAssertion) {
+ Set<String> underscoredNamesWithCamelCasePair = new HashSet<>();
+ for (String name : names) {
+ if (_StringUtil.getIdentifierNamingConvention(name) == ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION) {
+ String underscoredName = correctIsoBIExceptions(_StringUtil.camelCaseToUnderscored(name));
+ assertTrue(
+ "Missing underscored variation \"" + underscoredName + "\" for \"" + name + "\".",
+ names.contains(underscoredName));
+ assertTrue(underscoredNamesWithCamelCasePair.add(underscoredName));
+
+ namePairAssertion.assertPair(name, underscoredName);
+ }
+ }
+ for (String name : names) {
+ if (_StringUtil.getIdentifierNamingConvention(name) == ParsingConfiguration.LEGACY_NAMING_CONVENTION) {
+ assertTrue("Missing camel case variation for \"" + name + "\".",
+ underscoredNamesWithCamelCasePair.contains(name));
+ }
+ }
+ }
+
+ private String correctIsoBIExceptions(String underscoredName) {
+ return underscoredName.replace("_n_z", "_nz").replace("_f_z", "_fz");
+ }
+
+ @Test
+ public void camelCaseDirectives() throws IOException, TemplateException {
+ camelCaseDirectives(false);
+ setConfiguration(new TestConfigurationBuilder()
+ .tagSyntax(ParsingConfiguration.AUTO_DETECT_TAG_SYNTAX)
+ .build());
+ camelCaseDirectives(true);
+ }
+
+ private void camelCaseDirectives(boolean squared) throws IOException, TemplateException {
+ assertOutput(
+ squared("<#list 1..4 as x><#if x == 1>one <#elseIf x == 2>two <#elseIf x == 3>three "
+ + "<#else>more</#if></#list>", squared),
+ "one two three more");
+ assertOutput(
+ squared("<#list 1..4 as x><#if x == 1>one <#elseif x == 2>two <#elseif x == 3>three "
+ + "<#else>more</#if></#list>", squared),
+ "one two three more");
+
+ assertOutput(
+ squared("<#escape x as x?upperCase>${'a'}<#noEscape>${'b'}</#noEscape></#escape>", squared),
+ "Ab");
+ assertOutput(
+ squared("<#escape x as x?upper_case>${'a'}<#noescape>${'b'}</#noescape></#escape>", squared),
+ "Ab");
+
+ assertOutput(
+ squared("<#noParse></#noparse></#noParse>", squared),
+ squared("</#noparse>", squared));
+ assertOutput(
+ squared("<#noparse></#noParse></#noparse>", squared),
+ squared("</#noParse>", squared));
+ }
+
+ private String squared(String ftl, boolean squared) {
+ return squared ? ftl.replace('<', '[').replace('>', ']') : ftl;
+ }
+
+ @Test
+ public void explicitNamingConvention() throws IOException, TemplateException {
+ explicitNamingConvention(false);
+ explicitNamingConvention(true);
+ }
+
+ private void explicitNamingConvention(boolean squared) throws IOException, TemplateException {
+ int tagSyntax = squared ? ParsingConfiguration.AUTO_DETECT_TAG_SYNTAX
+ : ParsingConfiguration.ANGLE_BRACKET_TAG_SYNTAX;
+ setConfiguration(new TestConfigurationBuilder()
+ .tagSyntax(tagSyntax)
+ .namingConvention(ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION)
+ .build());
+
+ assertErrorContains(
+ squared("<#if true>t<#elseif false>f</#if>", squared),
+ "naming convention", "camel", "#elseif");
+ assertOutput(
+ squared("<#if true>t<#elseIf false>f</#if>", squared),
+ "t");
+
+ assertErrorContains(
+ squared("<#noparse>${x}</#noparse>", squared),
+ "naming convention", "camel", "#noparse");
+ assertOutput(
+ squared("<#noParse>${x}</#noParse>", squared),
+ "${x}");
+
+ assertErrorContains(
+ squared("<#escape x as -x><#noescape>${1}</#noescape></#escape>", squared),
+ "naming convention", "camel", "#noescape");
+ assertOutput(
+ squared("<#escape x as -x><#noEscape>${1}</#noEscape></#escape>", squared),
+ "1");
+
+ // ---
+
+ setConfiguration(new TestConfigurationBuilder()
+ .tagSyntax(tagSyntax)
+ .namingConvention(ParsingConfiguration.LEGACY_NAMING_CONVENTION)
+ .build());
+
+ assertErrorContains(
+ squared("<#if true>t<#elseIf false>f</#if>", squared),
+ "naming convention", "legacy", "#elseIf");
+ assertOutput(
+ squared("<#if true>t<#elseif false>f</#if>", squared),
+ "t");
+
+ assertErrorContains(
+ squared("<#noParse>${x}</#noParse>", squared),
+ "naming convention", "legacy", "#noParse");
+ assertOutput(
+ squared("<#noparse>${x}</#noparse>", squared),
+ "${x}");
+
+ assertErrorContains(
+ squared("<#escape x as -x><#noEscape>${1}</#noEscape></#escape>", squared),
+ "naming convention", "legacy", "#noEscape");
+ assertOutput(
+ squared("<#escape x as -x><#noescape>${1}</#noescape></#escape>", squared),
+ "1");
+ }
+
+ @Test
+ public void inconsistentAutoDetectedNamingConvention() {
+ assertErrorContains(
+ "<#if x><#elseIf y><#elseif z></#if>",
+ "naming convention", "camel");
+ assertErrorContains(
+ "<#if x><#elseif y><#elseIf z></#if>",
+ "naming convention", "legacy");
+ assertErrorContains(
+ "<#if x><#elseIf y></#if><#noparse></#noparse>",
+ "naming convention", "camel");
+ assertErrorContains(
+ "<#if x><#elseif y></#if><#noParse></#noParse>",
+ "naming convention", "legacy");
+ assertErrorContains(
+ "<#if x><#elseif y><#elseIf z></#if>",
+ "naming convention", "legacy");
+ assertErrorContains(
+ "<#escape x as x + 1><#noEscape></#noescape></#escape>",
+ "naming convention", "camel");
+ assertErrorContains(
+ "<#escape x as x + 1><#noEscape></#noEscape><#noescape></#noescape></#escape>",
+ "naming convention", "camel");
+ assertErrorContains(
+ "<#escape x as x + 1><#noescape></#noEscape></#escape>",
+ "naming convention", "legacy");
+ assertErrorContains(
+ "<#escape x as x + 1><#noescape></#noescape><#noEscape></#noEscape></#escape>",
+ "naming convention", "legacy");
+
+ assertErrorContains("${x?upperCase?is_string}",
+ "naming convention", "camel", "upperCase", "is_string");
+ assertErrorContains("${x?upper_case?isString}",
+ "naming convention", "legacy", "upper_case", "isString");
+
+ assertErrorContains("<#setting outputEncoding='utf-8'>${x?is_string}",
+ "naming convention", "camel", "outputEncoding", "is_string");
+ assertErrorContains("<#setting output_encoding='utf-8'>${x?isString}",
+ "naming convention", "legacy", "output_encoding", "isString");
+
+ assertErrorContains("${x?isString}<#setting output_encoding='utf-8'>",
+ "naming convention", "camel", "isString", "output_encoding");
+ assertErrorContains("${x?is_string}<#setting outputEncoding='utf-8'>",
+ "naming convention", "legacy", "is_string", "outputEncoding");
+
+ assertErrorContains("${.outputEncoding}${x?is_string}",
+ "naming convention", "camel", "outputEncoding", "is_string");
+ assertErrorContains("${.output_encoding}${x?isString}",
+ "naming convention", "legacy", "output_encoding", "isString");
+
+ assertErrorContains("${x?upperCase}<#noparse></#noparse>",
+ "naming convention", "camel", "upperCase", "noparse");
+ assertErrorContains("${x?upper_case}<#noParse></#noParse>",
+ "naming convention", "legacy", "upper_case", "noParse");
+ }
+
+ private interface NamePairAssertion {
+
+ void assertPair(String name1, String name2);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java
new file mode 100644
index 0000000..fd1a5a5
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.core;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader;
+import org.apache.freemarker.test.CopyrightCommentRemoverTemplateLoader;
+import org.apache.freemarker.test.TestConfigurationBuilder;
+import org.apache.freemarker.test.FileTestCase;
+
+public class CanonicalFormTest extends FileTestCase {
+
+ public CanonicalFormTest(String name) {
+ super(name);
+ }
+
+ public void testMacrosCanonicalForm() throws Exception {
+ assertCanonicalFormOf("cano-macros.ftl");
+ }
+
+ public void testIdentifierEscapingCanonicalForm() throws Exception {
+ assertCanonicalFormOf("cano-identifier-escaping.ftl");
+ }
+
+ public void testAssignmentCanonicalForm() throws Exception {
+ assertCanonicalFormOf("cano-assignments.ftl");
+ }
+
+ public void testBuiltInCanonicalForm() throws Exception {
+ assertCanonicalFormOf("cano-builtins.ftl");
+ }
+
+ public void testStringLiteralInterpolationCanonicalForm() throws Exception {
+ assertCanonicalFormOf("cano-strlitinterpolation.ftl");
+ }
+
+ private void assertCanonicalFormOf(String ftlFileName)
+ throws IOException {
+ Configuration cfg = new TestConfigurationBuilder()
+ .templateLoader(
+ new CopyrightCommentRemoverTemplateLoader(
+ new ClassTemplateLoader(CanonicalFormTest.class, "")))
+ .build();
+ StringWriter sw = new StringWriter();
+ cfg.getTemplate(ftlFileName).dump(sw);
+ assertExpectedFileEqualsString(ftlFileName + ".out", sw.toString());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java
new file mode 100644
index 0000000..91d3749
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.core;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Date;
+
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.impl.SimpleDate;
+import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
+import org.apache.freemarker.core.userpkg.HTMLISOTemplateDateFormatFactory;
+import org.apache.freemarker.core.userpkg.PrintfGTemplateNumberFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
+import org.apache.freemarker.test.TemplateTest;
+import org.apache.freemarker.test.TestConfigurationBuilder;
+import org.junit.Before;
+import org.junit.Test;
+
+@SuppressWarnings("boxing")
+public class CoercionToTextualTest extends TemplateTest {
+
+ /** 2015-09-06T12:00:00Z */
+ private static long T = 1441540800000L;
+ private static TemplateDateModel TM = new SimpleDate(new Date(T), TemplateDateModel.DATETIME);
+
+ @Test
+ public void testBasicStringBuiltins() throws IOException, TemplateException {
+ assertOutput("${s?upperCase}", "ABC");
+ assertOutput("${n?string?lowerCase}", "1.50e+03");
+ assertErrorContains("${n?lowerCase}", "convert", "string", "markup", "text/html");
+ assertOutput("${dt?string?lowerCase}", "2015-09-06t12:00:00z");
+ assertErrorContains("${dt?lowerCase}", "convert", "string", "markup", "text/html");
+ assertOutput("${b?upperCase}", "Y");
+ assertErrorContains("${m?upperCase}", "convertible to string", "HTMLOutputModel");
+ }
+
+ @Test
+ public void testEscBuiltin() throws IOException, TemplateException {
+ setConfiguration(createDefaultConfigurationBuilder()
+ .outputFormat(HTMLOutputFormat.INSTANCE)
+ .autoEscapingPolicy(ParsingConfiguration.DISABLE_AUTO_ESCAPING_POLICY)
+ .booleanFormat("<y>,<n>")
+ .build());
+ assertOutput("${'a<b'?esc}", "a<b");
+ assertOutput("${n?string?esc}", "1.50E+03");
+ assertOutput("${n?esc}", "1.50*10<sup>3</sup>");
+ assertOutput("${dt?string?esc}", "2015-09-06T12:00:00Z");
+ assertOutput("${dt?esc}", "2015-09-06<span class='T'>T</span>12:00:00Z");
+ assertOutput("${b?esc}", "<y>");
+ assertOutput("${m?esc}", "<p>M</p>");
+ }
+
+ @Test
+ public void testStringOverloadedBuiltIns() throws IOException, TemplateException {
+ assertOutput("${s?contains('b')}", "y");
+ assertOutput("${n?string?contains('E')}", "y");
+ assertErrorContains("${n?contains('E')}", "convert", "string", "markup", "text/html");
+ assertErrorContains("${n?indexOf('E')}", "convert", "string", "markup", "text/html");
+ assertOutput("${dt?string?contains('0')}", "y");
+ assertErrorContains("${dt?contains('0')}", "convert", "string", "markup", "text/html");
+ assertErrorContains("${m?contains('0')}", "convertible to string", "HTMLOutputModel");
+ assertErrorContains("${m?indexOf('0')}", "convertible to string", "HTMLOutputModel");
+ }
+
+ @Test
+ public void testMarkupStringBuiltIns() throws IOException, TemplateException {
+ assertErrorContains("${n?string?markupString}", "Expected", "markup", "string");
+ assertErrorContains("${n?markupString}", "Expected", "markup", "number");
+ assertErrorContains("${dt?markupString}", "Expected", "markup", "date");
+ }
+
+ @Test
+ public void testSimpleInterpolation() throws IOException, TemplateException {
+ assertOutput("${s}", "abc");
+ assertOutput("${n?string}", "1.50E+03");
+ assertOutput("${n}", "1.50*10<sup>3</sup>");
+ assertOutput("${dt?string}", "2015-09-06T12:00:00Z");
+ assertOutput("${dt}", "2015-09-06<span class='T'>T</span>12:00:00Z");
+ assertOutput("${b}", "y");
+ assertOutput("${m}", "<p>M</p>");
+ }
+
+ @Test
+ public void testConcatenation() throws IOException, TemplateException {
+ assertOutput("${s + '&'}", "abc&");
+ assertOutput("${n?string + '&'}", "1.50E+03&");
+ assertOutput("${n + '&'}", "1.50*10<sup>3</sup>&");
+ assertOutput("${dt?string + '&'}", "2015-09-06T12:00:00Z&");
+ assertOutput("${dt + '&'}", "2015-09-06<span class='T'>T</span>12:00:00Z&");
+ assertOutput("${b + '&'}", "y&");
+ assertOutput("${m + '&'}", "<p>M</p>&");
+ }
+
+ @Test
+ public void testConcatenation2() throws IOException, TemplateException {
+ assertOutput("${'&' + s}", "&abc");
+ assertOutput("${'&' + n?string}", "&1.50E+03");
+ assertOutput("${'&' + n}", "&1.50*10<sup>3</sup>");
+ assertOutput("${'&' + dt?string}", "&2015-09-06T12:00:00Z");
+ assertOutput("${'&' + dt}", "&2015-09-06<span class='T'>T</span>12:00:00Z");
+ assertOutput("${'&' + b}", "&y");
+ assertOutput("${'&' + m}", "&<p>M</p>");
+ }
+
+ @Override
+ protected Configuration createDefaultConfiguration() throws Exception {
+ return createDefaultConfigurationBuilder().build();
+ }
+
+ private TestConfigurationBuilder createDefaultConfigurationBuilder() {
+ return new TestConfigurationBuilder()
+ .customNumberFormats(Collections.<String, TemplateNumberFormatFactory>singletonMap(
+ "G", PrintfGTemplateNumberFormatFactory.INSTANCE))
+ .customDateFormats(Collections.<String, TemplateDateFormatFactory>singletonMap(
+ "HI", HTMLISOTemplateDateFormatFactory.INSTANCE))
+ .numberFormat("@G 3")
+ .dateTimeFormat("@HI")
+ .booleanFormat("y,n");
+ }
+
+ @Before
+ public void setup() throws TemplateModelException {
+ addToDataModel("s", "abc");
+ addToDataModel("n", 1500);
+ addToDataModel("dt", TM);
+ addToDataModel("b", Boolean.TRUE);
+ addToDataModel("m", HTMLOutputFormat.INSTANCE.fromMarkup("<p>M</p>"));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurableTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurableTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurableTest.java
new file mode 100644
index 0000000..ebcc465
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurableTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.core;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.freemarker.core.util._StringUtil;
+import org.junit.Test;
+
+public class ConfigurableTest {
+
+ @Test
+ public void testGetSettingNamesAreSorted() throws Exception {
+ MutableProcessingConfiguration cfgable = createConfigurable();
+ for (boolean camelCase : new boolean[] { false, true }) {
+ Collection<String> names = cfgable.getSettingNames(camelCase);
+ String prevName = null;
+ for (String name : names) {
+ if (prevName != null) {
+ assertThat(name, greaterThan(prevName));
+ }
+ prevName = name;
+ }
+ }
+ }
+
+ @Test
+ public void testStaticFieldKeysCoverAllGetSettingNames() throws Exception {
+ MutableProcessingConfiguration cfgable = createConfigurable();
+ Collection<String> names = cfgable.getSettingNames(false);
+ for (String name : names) {
+ assertTrue("No field was found for " + name, keyFieldExists(name));
+ }
+ }
+
+ @Test
+ public void testGetSettingNamesCoversAllStaticKeyFields() throws Exception {
+ MutableProcessingConfiguration cfgable = createConfigurable();
+ Collection<String> names = cfgable.getSettingNames(false);
+
+ for (Field f : MutableProcessingConfiguration.class.getFields()) {
+ if (f.getName().endsWith("_KEY")) {
+ final Object name = f.get(null);
+ assertTrue("Missing setting name: " + name, names.contains(name));
+ }
+ }
+ }
+
+ @Test
+ public void testKeyStaticFieldsHasAllVariationsAndCorrectFormat() throws IllegalArgumentException, IllegalAccessException {
+ ConfigurableTest.testKeyStaticFieldsHasAllVariationsAndCorrectFormat(MutableProcessingConfiguration.class);
+ }
+
+ @Test
+ public void testGetSettingNamesNameConventionsContainTheSame() throws Exception {
+ MutableProcessingConfiguration cfgable = createConfigurable();
+ ConfigurableTest.testGetSettingNamesNameConventionsContainTheSame(
+ new ArrayList<>(cfgable.getSettingNames(false)),
+ new ArrayList<>(cfgable.getSettingNames(true)));
+ }
+
+ public static void testKeyStaticFieldsHasAllVariationsAndCorrectFormat(
+ Class<? extends MutableProcessingConfiguration> confClass) throws IllegalArgumentException, IllegalAccessException {
+ // For all _KEY fields there must be a _KEY_CAMEL_CASE and a _KEY_SNAKE_CASE field.
+ // Their content must not contradict the expected naming convention.
+ // They _KEY filed value must be deducable from the field name
+ // The _KEY value must be the same as _KEY_SNAKE_CASE field.
+ // The _KEY_CAMEL_CASE converted to snake case must give the value of the _KEY_SNAKE_CASE.
+ for (Field field : confClass.getFields()) {
+ String fieldName = field.getName();
+ if (fieldName.endsWith("_KEY")) {
+ String keyFieldValue = (String) field.get(null);
+ assertNotEquals(ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION,
+ _StringUtil.getIdentifierNamingConvention(keyFieldValue));
+ assertEquals(fieldName.substring(0, fieldName.length() - 4).toLowerCase(), keyFieldValue);
+
+ try {
+ String keySCFieldValue = (String) confClass.getField(fieldName + "_SNAKE_CASE").get(null);
+ assertEquals(keyFieldValue, keySCFieldValue);
+ } catch (NoSuchFieldException e) {
+ fail("Missing ..._SNAKE_CASE field for " + fieldName);
+ }
+
+ try {
+ String keyCCFieldValue = (String) confClass.getField(fieldName + "_CAMEL_CASE").get(null);
+ assertNotEquals(ParsingConfiguration.LEGACY_NAMING_CONVENTION,
+ _StringUtil.getIdentifierNamingConvention(keyCCFieldValue));
+ assertEquals(keyFieldValue, _StringUtil.camelCaseToUnderscored(keyCCFieldValue));
+ } catch (NoSuchFieldException e) {
+ fail("Missing ..._CAMEL_CASE field for " + fieldName);
+ }
+ }
+ }
+
+ // For each _KEY_SNAKE_CASE field there must be a _KEY field.
+ for (Field field : confClass.getFields()) {
+ String fieldName = field.getName();
+ if (fieldName.endsWith("_KEY_SNAKE_CASE")) {
+ try {
+ confClass.getField(fieldName.substring(0, fieldName.length() - 11)).get(null);
+ } catch (NoSuchFieldException e) {
+ fail("Missing ..._KEY field for " + fieldName);
+ }
+ }
+ }
+
+ // For each _KEY_CAMEL_CASE field there must be a _KEY field.
+ for (Field field : confClass.getFields()) {
+ String fieldName = field.getName();
+ if (fieldName.endsWith("_KEY_CAMEL_CASE")) {
+ try {
+ confClass.getField(fieldName.substring(0, fieldName.length() - 11)).get(null);
+ } catch (NoSuchFieldException e) {
+ fail("Missing ..._KEY field for " + fieldName);
+ }
+ }
+ }
+ }
+
+ public static void testGetSettingNamesNameConventionsContainTheSame(List<String> namesSCList, List<String> namesCCList) {
+ Set<String> namesSC = new HashSet<>(namesSCList);
+ assertEquals(namesSCList.size(), namesSC.size());
+
+ Set<String> namesCC = new HashSet<>(namesCCList);
+ assertEquals(namesCCList.size(), namesCC.size());
+
+ assertEquals(namesSC.size(), namesCC.size());
+
+ for (String nameCC : namesCC) {
+ final String nameSC = _StringUtil.camelCaseToUnderscored(nameCC);
+ if (!namesSC.contains(nameSC)) {
+ fail("\"" + nameCC + "\" misses corresponding snake case name, \"" + nameSC + "\".");
+ }
+ }
+ }
+
+ private MutableProcessingConfiguration createConfigurable() throws IOException {
+ return new TemplateConfiguration.Builder();
+ }
+
+ private boolean keyFieldExists(String name) throws Exception {
+ try {
+ MutableProcessingConfiguration.class.getField(name.toUpperCase() + "_KEY");
+ } catch (NoSuchFieldException e) {
+ return false;
+ }
+ return true;
+ }
+
+}