Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 1C22C200C67 for ; Mon, 15 May 2017 23:23:48 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 1AA55160BA9; Mon, 15 May 2017 21:23:48 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 41CC3160BD0 for ; Mon, 15 May 2017 23:23:46 +0200 (CEST) Received: (qmail 77463 invoked by uid 500); 15 May 2017 21:23:45 -0000 Mailing-List: contact notifications-help@freemarker.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@freemarker.incubator.apache.org Delivered-To: mailing list notifications@freemarker.incubator.apache.org Received: (qmail 77454 invoked by uid 99); 15 May 2017 21:23:45 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 15 May 2017 21:23:45 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 029DD1A058C for ; Mon, 15 May 2017 21:23:45 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.222 X-Spam-Level: X-Spam-Status: No, score=-4.222 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001] autolearn=disabled Received: from mx1-lw-us.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id pyXsJVXv_Igx for ; Mon, 15 May 2017 21:23:31 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-us.apache.org (ASF Mail Server at mx1-lw-us.apache.org) with SMTP id 021A762433 for ; Mon, 15 May 2017 21:23:20 +0000 (UTC) Received: (qmail 76007 invoked by uid 99); 15 May 2017 21:23:19 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 15 May 2017 21:23:19 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 25DE6E01BC; Mon, 15 May 2017 21:23:19 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: ddekany@apache.org To: notifications@freemarker.incubator.apache.org Date: Mon, 15 May 2017 21:24:05 -0000 Message-Id: <38d3738ca0b8483aa3a63ed4d854a1d3@git.apache.org> In-Reply-To: <4bfc87c4667d4f69a855365b7369a594@git.apache.org> References: <4bfc87c4667d4f69a855365b7369a594@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [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 archived-at: Mon, 15 May 2017 21:23:48 -0000 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}"; + 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>", AUTO_DETECT_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX); + assertEquals(getActualTagSyntax("foo<#if true>", ANGLE_BRACKET_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX); + assertEquals(getActualTagSyntax("foo<#if true>", 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", "one"); + assertOutput("<#list 1..2 as x>${x}<#break>", "1"); + assertOutput("<#list 1..2>[<#items as x>${x}<#break>]", "[1]"); + assertOutput("<#list 1..2 as x>${x}<#list 1..3>B<#break>E<#items as y>E.", "1B."); + assertOutput("<#list 1..2 as x>${x}<#list 3..4 as x>${x}<#break>;", "13;23;"); + assertOutput("<#list [1..2, 3..4, [], 5..6] as xs>[<#list xs as x>${x}<#else><#break>].", + "[12][34][."); + assertOutput("<#list [1..2, 3..4, [], 5..6] as xs>" + + "<#list xs>[<#items as x>${x}]<#else><#break>" + + ".", + "[12][34]."); + } + + @Test + public void testInvalidPlacements() throws IOException, TemplateException { + assertErrorContains("<#break>", BREAK_NESTING_ERROR_MESSAGE_PART); + assertErrorContains("<#list 1..2 as x>${x}<#break>", BREAK_NESTING_ERROR_MESSAGE_PART); + assertErrorContains("<#if false><#break>", BREAK_NESTING_ERROR_MESSAGE_PART); + assertErrorContains("<#list xs><#break>", BREAK_NESTING_ERROR_MESSAGE_PART); + assertErrorContains("<#list 1..2 as x>${x}<#else><#break>", BREAK_NESTING_ERROR_MESSAGE_PART); + assertErrorContains("<#list 1..2 as x>${x}<#macro m><#break>", 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>${.foo}", "dataModel", "\\!data_model"); + assertErrorContains("<#if x><#elseif y>${.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\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\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><#setting foo=1>", "booleanFormat", "\\!boolean_format"); + assertErrorContains("<#if x><#elseif y><#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> ${'x'?foo}", "upperCase", "\\!upper_case"); + assertErrorContains("<#if x><#elseif y>${'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 names, NamePairAssertion namePairAssertion) { + Set 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", 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", squared), + "one two three more"); + + assertOutput( + squared("<#escape x as x?upperCase>${'a'}<#noEscape>${'b'}", squared), + "Ab"); + assertOutput( + squared("<#escape x as x?upper_case>${'a'}<#noescape>${'b'}", squared), + "Ab"); + + assertOutput( + squared("<#noParse>", squared), + squared("", squared)); + assertOutput( + squared("<#noparse>", squared), + squared("", 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", squared), + "naming convention", "camel", "#elseif"); + assertOutput( + squared("<#if true>t<#elseIf false>f", squared), + "t"); + + assertErrorContains( + squared("<#noparse>${x}", squared), + "naming convention", "camel", "#noparse"); + assertOutput( + squared("<#noParse>${x}", squared), + "${x}"); + + assertErrorContains( + squared("<#escape x as -x><#noescape>${1}", squared), + "naming convention", "camel", "#noescape"); + assertOutput( + squared("<#escape x as -x><#noEscape>${1}", squared), + "1"); + + // --- + + setConfiguration(new TestConfigurationBuilder() + .tagSyntax(tagSyntax) + .namingConvention(ParsingConfiguration.LEGACY_NAMING_CONVENTION) + .build()); + + assertErrorContains( + squared("<#if true>t<#elseIf false>f", squared), + "naming convention", "legacy", "#elseIf"); + assertOutput( + squared("<#if true>t<#elseif false>f", squared), + "t"); + + assertErrorContains( + squared("<#noParse>${x}", squared), + "naming convention", "legacy", "#noParse"); + assertOutput( + squared("<#noparse>${x}", squared), + "${x}"); + + assertErrorContains( + squared("<#escape x as -x><#noEscape>${1}", squared), + "naming convention", "legacy", "#noEscape"); + assertOutput( + squared("<#escape x as -x><#noescape>${1}", squared), + "1"); + } + + @Test + public void inconsistentAutoDetectedNamingConvention() { + assertErrorContains( + "<#if x><#elseIf y><#elseif z>", + "naming convention", "camel"); + assertErrorContains( + "<#if x><#elseif y><#elseIf z>", + "naming convention", "legacy"); + assertErrorContains( + "<#if x><#elseIf y><#noparse>", + "naming convention", "camel"); + assertErrorContains( + "<#if x><#elseif y><#noParse>", + "naming convention", "legacy"); + assertErrorContains( + "<#if x><#elseif y><#elseIf z>", + "naming convention", "legacy"); + assertErrorContains( + "<#escape x as x + 1><#noEscape>", + "naming convention", "camel"); + assertErrorContains( + "<#escape x as x + 1><#noEscape><#noescape>", + "naming convention", "camel"); + assertErrorContains( + "<#escape x as x + 1><#noescape>", + "naming convention", "legacy"); + assertErrorContains( + "<#escape x as x + 1><#noescape><#noEscape>", + "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>", + "naming convention", "camel", "upperCase", "noparse"); + assertErrorContains("${x?upper_case}<#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(",") + .build()); + assertOutput("${'a3"); + assertOutput("${dt?string?esc}", "2015-09-06T12:00:00Z"); + assertOutput("${dt?esc}", "2015-09-06T12:00:00Z"); + assertOutput("${b?esc}", "<y>"); + assertOutput("${m?esc}", "

M

"); + } + + @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*103"); + assertOutput("${dt?string}", "2015-09-06T12:00:00Z"); + assertOutput("${dt}", "2015-09-06T12:00:00Z"); + assertOutput("${b}", "y"); + assertOutput("${m}", "

M

"); + } + + @Test + public void testConcatenation() throws IOException, TemplateException { + assertOutput("${s + '&'}", "abc&"); + assertOutput("${n?string + '&'}", "1.50E+03&"); + assertOutput("${n + '&'}", "1.50*103&"); + assertOutput("${dt?string + '&'}", "2015-09-06T12:00:00Z&"); + assertOutput("${dt + '&'}", "2015-09-06T12:00:00Z&"); + assertOutput("${b + '&'}", "y&"); + assertOutput("${m + '&'}", "

M

&"); + } + + @Test + public void testConcatenation2() throws IOException, TemplateException { + assertOutput("${'&' + s}", "&abc"); + assertOutput("${'&' + n?string}", "&1.50E+03"); + assertOutput("${'&' + n}", "&1.50*103"); + assertOutput("${'&' + dt?string}", "&2015-09-06T12:00:00Z"); + assertOutput("${'&' + dt}", "&2015-09-06T12:00:00Z"); + assertOutput("${'&' + b}", "&y"); + assertOutput("${'&' + m}", "&

M

"); + } + + @Override + protected Configuration createDefaultConfiguration() throws Exception { + return createDefaultConfigurationBuilder().build(); + } + + private TestConfigurationBuilder createDefaultConfigurationBuilder() { + return new TestConfigurationBuilder() + .customNumberFormats(Collections.singletonMap( + "G", PrintfGTemplateNumberFormatFactory.INSTANCE)) + .customDateFormats(Collections.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("

M

")); + } + +} 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 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 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 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 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 namesSCList, List namesCCList) { + Set namesSC = new HashSet<>(namesSCList); + assertEquals(namesSCList.size(), namesSC.size()); + + Set 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; + } + +}