http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpretAndEvalTemplateNameTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpretAndEvalTemplateNameTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpretAndEvalTemplateNameTest.java new file mode 100644 index 0000000..ff5897f --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpretAndEvalTemplateNameTest.java @@ -0,0 +1,70 @@ +/* + * 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.core.templateresolver.impl.StringTemplateLoader; +import org.apache.freemarker.test.TemplateTest; +import org.apache.freemarker.test.TestConfigurationBuilder; +import org.junit.Test; + +/** + * Test template names returned by special variables and relative path resolution in {@code ?interpret}-ed and + * {@code ?eval}-ed parts. + */ +public class InterpretAndEvalTemplateNameTest extends TemplateTest { + + @Test + public void testInterpret() throws IOException, TemplateException { + for (String getTemplateNames : new String[] { + "c=${.current_template_name}, m=${.main_template_name}", + "c=${\".current_template_name\"?eval}, m=${\".main_template_name\"?eval}" + }) { + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate( + "main.ftl", + getTemplateNames + " " + + "{<#include 'sub/t.ftl'>}"); + tl.putTemplate( + "sub/t.ftl", + getTemplateNames + " " + + "i{<@r'" + getTemplateNames + " {<#include \"a.ftl\">'?interpret />}} " + + "i{<@[r'" + getTemplateNames + " {<#include \"a.ftl\">','named_interpreted']?interpret />}}"); + tl.putTemplate("sub/a.ftl", "In sub/a.ftl, " + getTemplateNames); + tl.putTemplate("a.ftl", "In a.ftl"); + + setConfiguration(new TestConfigurationBuilder().templateLoader(tl).build()); + + assertOutputForNamed("main.ftl", + "c=main.ftl, m=main.ftl " + + "{" + + "c=sub/t.ftl, m=main.ftl " + + "i{c=sub/t.ftl->anonymous_interpreted, m=main.ftl {In sub/a.ftl, c=sub/a.ftl, m=main.ftl}} " + + "i{c=sub/t.ftl->named_interpreted, m=main.ftl {In sub/a.ftl, c=sub/a.ftl, m=main.ftl}}" + + "}"); + + assertOutputForNamed("sub/t.ftl", + "c=sub/t.ftl, m=sub/t.ftl " + + "i{c=sub/t.ftl->anonymous_interpreted, m=sub/t.ftl {In sub/a.ftl, c=sub/a.ftl, m=sub/t.ftl}} " + + "i{c=sub/t.ftl->named_interpreted, m=sub/t.ftl {In sub/a.ftl, c=sub/a.ftl, m=sub/t.ftl}}"); + } + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpretSettingInheritanceTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpretSettingInheritanceTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpretSettingInheritanceTest.java new file mode 100644 index 0000000..2d061d7 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpretSettingInheritanceTest.java @@ -0,0 +1,104 @@ +/* + * 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.apache.freemarker.test.TestConfigurationBuilder; +import org.junit.Test; + +/** + * The {@code interpret} built-in must not consider the settings or established auto-detected syntax of the surrounding + * template. It can only depend on the {@link Configuration}. + */ +public class InterpretSettingInheritanceTest extends TemplateTest { + + private static final String FTL_A_S_A = "<#ftl><@'[#if true]s[/#if]<#if true>a</#if>'?interpret />"; + private static final String FTL_A_A_S = "<#ftl><@'<#if true>a</#if>[#if true]s[/#if]'?interpret />"; + private static final String FTL_S_S_A = "[#ftl][@'[#if true]s[/#if]<#if true>a</#if>'?interpret /]"; + private static final String FTL_S_A_S = "[#ftl][@'<#if true>a</#if>[#if true]s[/#if]'?interpret /]"; + private static final String OUT_S_A_WHEN_SYNTAX_IS_S = "s<#if true>a</#if>"; + private static final String OUT_S_A_WHEN_SYNTAX_IS_A = "[#if true]s[/#if]a"; + private static final String OUT_A_S_WHEN_SYNTAX_IS_A = "a[#if true]s[/#if]"; + private static final String OUT_A_S_WHEN_SYNTAX_IS_S = "<#if true>a</#if>s"; + + @Test + public void tagSyntaxTest() throws IOException, TemplateException { + setConfiguration(new TestConfigurationBuilder() + .tagSyntax(ParsingConfiguration.ANGLE_BRACKET_TAG_SYNTAX) + .build()); + assertOutput(FTL_S_A_S, OUT_A_S_WHEN_SYNTAX_IS_A); + assertOutput(FTL_S_S_A, OUT_S_A_WHEN_SYNTAX_IS_A); + assertOutput(FTL_A_A_S, OUT_A_S_WHEN_SYNTAX_IS_A); + assertOutput(FTL_A_S_A, OUT_S_A_WHEN_SYNTAX_IS_A); + + setConfiguration(new TestConfigurationBuilder() + .tagSyntax(ParsingConfiguration.SQUARE_BRACKET_TAG_SYNTAX) + .build()); + assertOutput(FTL_S_A_S, OUT_A_S_WHEN_SYNTAX_IS_S); + assertOutput(FTL_S_S_A, OUT_S_A_WHEN_SYNTAX_IS_S); + assertOutput(FTL_A_A_S, OUT_A_S_WHEN_SYNTAX_IS_S); + assertOutput(FTL_A_S_A, OUT_S_A_WHEN_SYNTAX_IS_S); + + setConfiguration(new TestConfigurationBuilder() + .tagSyntax(ParsingConfiguration.AUTO_DETECT_TAG_SYNTAX) + .build()); + assertOutput(FTL_S_A_S, OUT_A_S_WHEN_SYNTAX_IS_A); + assertOutput(FTL_S_S_A, OUT_S_A_WHEN_SYNTAX_IS_S); + assertOutput(FTL_A_A_S, OUT_A_S_WHEN_SYNTAX_IS_A); + assertOutput(FTL_A_S_A, OUT_S_A_WHEN_SYNTAX_IS_S); + assertOutput("<@'[#ftl]x'?interpret />[#if true]y[/#if]", "x[#if true]y[/#if]"); + } + + @Test + public void whitespaceStrippingTest() throws IOException, TemplateException { + Configuration cfg = getConfiguration(); + + setConfiguration(new TestConfigurationBuilder() + .whitespaceStripping(true) + .build()); + assertOutput("<#assign x = 1>\nX<@'<#assign x = 1>\\nY'?interpret />", "XY"); + assertOutput("<#ftl stripWhitespace=false><#assign x = 1>\nX<@'<#assign x = 1>\\nY'?interpret />", "\nXY"); + assertOutput("<#assign x = 1>\nX<@'<#ftl stripWhitespace=false><#assign x = 1>\\nY'?interpret />", "X\nY"); + + setConfiguration(new TestConfigurationBuilder() + .whitespaceStripping(false) + .build()); + assertOutput("<#assign x = 1>\nX<@'<#assign x = 1>\\nY'?interpret />", "\nX\nY"); + assertOutput("<#ftl stripWhitespace=true><#assign x = 1>\nX<@'<#assign x = 1>\\nY'?interpret />", "X\nY"); + assertOutput("<#assign x = 1>\nX<@'<#ftl stripWhitespace=true><#assign x = 1>\\nY'?interpret />", "\nXY"); + } + + @Test + public void evalTest() throws IOException, TemplateException { + setConfiguration(new TestConfigurationBuilder() + .tagSyntax(ParsingConfiguration.ANGLE_BRACKET_TAG_SYNTAX) + .build()); + assertOutput("<@'\"[#if true]s[/#if]<#if true>a</#if>\"?interpret'?eval />", OUT_S_A_WHEN_SYNTAX_IS_A); + assertOutput("[#ftl][@'\"[#if true]s[/#if]<#if true>a</#if>\"?interpret'?eval /]", OUT_S_A_WHEN_SYNTAX_IS_A); + + setConfiguration(new TestConfigurationBuilder() + .tagSyntax(ParsingConfiguration.SQUARE_BRACKET_TAG_SYNTAX) + .build()); + assertOutput("[@'\"[#if true]s[/#if]<#if true>a</#if>\"?interpret'?eval /]", OUT_S_A_WHEN_SYNTAX_IS_S); + assertOutput("<#ftl><@'\"[#if true]s[/#if]<#if true>a</#if>\"?interpret'?eval />", OUT_S_A_WHEN_SYNTAX_IS_S); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java new file mode 100644 index 0000000..08fcee2 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java @@ -0,0 +1,64 @@ +/* + * 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.util.Arrays; +import java.util.Iterator; + +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; +import org.apache.freemarker.test.TemplateTest; +import org.junit.Test; + +public class IteratorIssuesTest extends TemplateTest { + + private static final DefaultObjectWrapper OW = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build(); + + private static final String FTL_HAS_CONTENT_AND_LIST + = "<#if it?hasContent><#list it as i>${i}</#list><#else>empty</#if>"; + private static final String OUT_HAS_CONTENT_AND_LIST_ABC = "abc"; + private static final String OUT_HAS_CONTENT_AND_LIST_EMPTY = "empty"; + + private static final String FTL_LIST_AND_HAS_CONTENT + = "<#list it as i>${i}${it?hasContent?then('+', '-')}</#list>"; + private static final String OUT_LIST_AND_HAS_CONTENT_BW_GOOD = "a+b+c-"; + + @Test + public void testHasContentAndList() throws Exception { + addToDataModel("it", OW.wrap(getAbcIt())); + assertOutput(FTL_HAS_CONTENT_AND_LIST, OUT_HAS_CONTENT_AND_LIST_ABC); + + addToDataModel("it", OW.wrap(getEmptyIt())); + assertOutput(FTL_HAS_CONTENT_AND_LIST, OUT_HAS_CONTENT_AND_LIST_EMPTY); + } + + @Test + public void testListAndHasContent() throws Exception { + addToDataModel("it", OW.wrap(getAbcIt())); + assertErrorContains(FTL_LIST_AND_HAS_CONTENT, "can be listed only once"); + } + + private Iterator getAbcIt() { + return Arrays.asList(new String[] { "a", "b", "c" }).iterator(); + } + + private Iterator getEmptyIt() { + return Arrays.asList(new String[] { }).iterator(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/JavaCCExceptionAsEOFFixTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/JavaCCExceptionAsEOFFixTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/JavaCCExceptionAsEOFFixTest.java new file mode 100644 index 0000000..0fa3f79 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/JavaCCExceptionAsEOFFixTest.java @@ -0,0 +1,126 @@ +/* + * 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.io.Reader; + +import org.apache.freemarker.test.TestConfigurationBuilder; +import org.junit.Assert; +import org.junit.Test; + +/** + * JavaCC suppresses exceptions thrown by the Reader, silently treating them as EOF. To be precise, JavaCC 3.2 only does + * that with {@link IOException}-s, while JavaCC 6 does that for all {@link Exception}-s. This tests FreeMarker's + * workaround for this problem. + */ +public class JavaCCExceptionAsEOFFixTest { + + public static class FailingReader extends Reader { + + private static final String CONTENT = "abc"; + + private final Throwable exceptionToThrow; + private int readSoFar; + + protected FailingReader(Throwable exceptionToThrow) { + this.exceptionToThrow = exceptionToThrow; + } + + @Override + public int read() throws IOException { + if (readSoFar == CONTENT.length()) { + if (exceptionToThrow != null) { + throwException(); + } else { + return -1; + } + } + return CONTENT.charAt(readSoFar++); + } + + private void throwException() throws IOException { + if (exceptionToThrow instanceof IOException) { + throw (IOException) exceptionToThrow; + } + if (exceptionToThrow instanceof RuntimeException) { + throw (RuntimeException) exceptionToThrow; + } + if (exceptionToThrow instanceof Error) { + throw (Error) exceptionToThrow; + } + Assert.fail(); + } + + @Override + public void close() throws IOException { + // nop + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + for (int i = 0; i < len; i++) { + int c = read(); + if (c == -1) return i == 0 ? -1 : i; + cbuf[off + i] = (char) c; + } + return len; + } + + } + + @Test + public void testIOException() throws IOException { + try { + new Template(null, new FailingReader(new IOException("test")), new TestConfigurationBuilder().build()); + fail(); + } catch (IOException e) { + assertEquals("test", e.getMessage()); + } + } + + @Test + public void testRuntimeException() throws IOException { + try { + new Template(null, new FailingReader(new NullPointerException("test")), new TestConfigurationBuilder().build()); + fail(); + } catch (NullPointerException e) { + assertEquals("test", e.getMessage()); + } + } + + @Test + public void testError() throws IOException { + try { + new Template(null, new FailingReader(new OutOfMemoryError("test")), new TestConfigurationBuilder().build()); + fail(); + } catch (OutOfMemoryError e) { + assertEquals("test", e.getMessage()); + } + } + + @Test + public void testNoException() throws IOException { + Template t = new Template(null, new FailingReader(null), new TestConfigurationBuilder().build()); + assertEquals("abc", t.toString()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java new file mode 100644 index 0000000..05bac4f --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java @@ -0,0 +1,130 @@ +/* + * 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.core.model.impl.DefaultObjectWrapper; +import org.apache.freemarker.test.TemplateTest; +import org.apache.freemarker.test.templatesuite.models.Listables; +import org.junit.Test; + +import com.google.common.collect.ImmutableMap; + +public class ListErrorsTest extends TemplateTest { + + @Test + public void testValid() throws IOException, TemplateException { + assertOutput("<#list 1..2 as x><#list 3..4>${x}:<#items as x>${x}</#items></#list>;</#list>", "1:34;2:34;"); + assertOutput("<#list [] as x>${x}<#else><#list 1..2 as x>${x}<#sep>, </#list></#list>", "1, 2"); + assertOutput("<#macro m>[<#nested 3>]</#macro>" + + "<#list 1..2 as x>" + + "${x}@${x?index}" + + "<@m ; x>" + + "${x}," + + "<#list 4..4 as x>${x}@${x?index}</#list>" + + "</@>" + + "${x}@${x?index}; " + + "</#list>", + "1@0[3,4@0]1@0; 2@1[3,4@0]2@1; "); + } + + @Test + public void testInvalidItemsParseTime() throws IOException, TemplateException { + assertErrorContains("<#items as x>${x}</#items>", + "#items", "must be inside", "#list"); + assertErrorContains("<#list xs><#macro m><#items as x></#items></#macro></#list>", + "#items", "must be inside", "#list"); + assertErrorContains("<#list xs as x><#items as x>${x}</#items></#list>", + "#list", "must not have", "#items", "as loopVar"); + assertErrorContains("<#list xs><#list xs as x><#items as x>${x}</#items></#list></#list>", + "#list", "must not have", "#items", "as loopVar"); + assertErrorContains("<#list xs></#list>", + "#list", "must have", "#items", "as loopVar"); + } + + @Test + public void testInvalidSepParseTime() throws IOException, TemplateException { + assertErrorContains("<#sep>, </#sep>", + "#sep", "must be inside", "#list"); + assertErrorContains("<#sep>, ", + "#sep", "must be inside", "#list"); + assertErrorContains("<#list xs as x><#else><#sep>, </#list>", + "#sep", "must be inside", "#list"); + assertErrorContains("<#list xs as x><#macro m><#sep>, </#macro></#list>", + "#sep", "must be inside", "#list"); + } + + @Test + public void testInvalidItemsRuntime() throws IOException, TemplateException { + assertErrorContains("<#list 1..1><#items as x></#items><#items as x></#items></#list>", + "#items", "already entered earlier"); + assertErrorContains("<#list 1..1><#items as x><#items as y>${x}/${y}</#items></#items></#list>", + "#items", "Can't nest #items into each other"); + } + + @Test + public void testInvalidLoopVarBuiltinLHO() { + assertErrorContains("<#list foos>${foo?index}</#list>", + "?index", "foo", "no loop variable"); + assertErrorContains("<#list foos as foo></#list>${foo?index}", + "?index", "foo" , "no loop variable"); + assertErrorContains("<#list foos as foo><#macro m>${foo?index}</#macro></#list>", + "?index", "foo" , "no loop variable"); + assertErrorContains("<#list foos as foo><#function f>${foo?index}</#function></#list>", + "?index", "foo" , "no loop variable"); + assertErrorContains("<#list xs as x>${foo?index}</#list>", + "?index", "foo" , "no loop variable"); + assertErrorContains("<#list foos as foo><@m; foo>${foo?index}</@></#list>", + "?index", "foo" , "user defined directive"); + assertErrorContains( + "<#list foos as foo><@m; foo><@m; foo>${foo?index}</@></@></#list>", + "?index", "foo" , "user defined directive"); + assertErrorContains( + "<#list foos as foo><@m; foo>" + + "<#list foos as foo><@m; foo>${foo?index}</@></#list>" + + "</@></#list>", + "?index", "foo" , "user defined directive"); + } + + @Test + public void testKeyValueSameName() { + assertErrorContains("<#list {} as foo, foo></#list>", + "key", "value", "both" , "foo"); + } + + @Test + public void testCollectionVersusHash() { + assertErrorContains("<#list {} as i></#list>", + "as k, v"); + assertErrorContains("<#list [] as k, v></#list>", + "only one loop variable"); + } + + @Test + public void testNonEx2NonStringKey() throws IOException, TemplateException { + addToDataModel("m", new Listables.NonEx2MapAdapter(ImmutableMap.of("k1", "v1", 2, "v2"), + new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build())); + assertOutput("<#list m?keys as k>${k};</#list>", "k1;2;"); + assertErrorContains("<#list m as k, v></#list>", + "string", "number", ".TemplateHashModelEx2"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/MiscErrorMessagesTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/MiscErrorMessagesTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MiscErrorMessagesTest.java new file mode 100644 index 0000000..1903e05 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MiscErrorMessagesTest.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat; +import org.apache.freemarker.test.TemplateTest; +import org.apache.freemarker.test.TestConfigurationBuilder; +import org.junit.Test; + +public class MiscErrorMessagesTest extends TemplateTest { + + @Test + public void stringIndexOutOfBounds() { + assertErrorContains("${'foo'[10]}", "length", "3", "10", "String index out of"); + } + + @Test + public void wrongTemplateNameFormat() { + setConfiguration(new TestConfigurationBuilder().templateNameFormat(DefaultTemplateNameFormat.INSTANCE).build()); + + assertErrorContains("<#include 'foo:/bar:baaz'>", "Malformed template name", "':'"); + assertErrorContains("<#include '../baaz'>", "Malformed template name", "root"); + assertErrorContains("<#include '\u0000'>", "Malformed template name", "\\u0000"); + } + + @Test + public void numericalKeyHint() { + assertErrorContains("${{}[10]}", "[]", "?api"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java new file mode 100644 index 0000000..5fcee97 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java @@ -0,0 +1,104 @@ +/* + * 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.io.StringWriter; +import java.util.List; + +import org.apache.freemarker.core.Environment.Namespace; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; +import org.apache.freemarker.core.util._NullWriter; +import org.apache.freemarker.test.TestConfigurationBuilder; +import org.junit.Test; + +/** + * These are things that users shouldn't do, but we shouldn't break backward compatibility without knowing about it. + */ +public class MistakenlyPublicImportAPIsTest { + + @Test + public void testImportCopying() throws IOException, TemplateException { + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("imp1", "<#macro m>1</#macro>"); + tl.putTemplate("imp2", "<#assign x = 2><#macro m>${x}</#macro>"); + + Configuration cfg = new TestConfigurationBuilder().templateLoader(tl).build(); + + Template t1 = new Template(null, "<#import 'imp1' as i1><#import 'imp2' as i2>", cfg); + List<ASTDirImport> imports = t1.getImports(); + assertEquals(2, imports.size()); + + { + Template t2 = new Template(null, "<@i1.m/><@i2.m/>", cfg); + for (ASTDirImport libLoad : imports) { + t2.addImport(libLoad); + } + + try { + t2.process(null, _NullWriter.INSTANCE); + fail(); + } catch (InvalidReferenceException e) { + // Apparenly, it has never worked like this... + assertEquals("i1", e.getBlamedExpressionString()); + } + } + + // It works this way, though it has nothing to do with the problematic API-s: + Environment env = t1.createProcessingEnvironment(null, _NullWriter.INSTANCE); + env.process(); + TemplateModel i1 = env.getVariable("i1"); + assertThat(i1, instanceOf(Namespace.class)); + TemplateModel i2 = env.getVariable("i2"); + assertThat(i2, instanceOf(Namespace.class)); + + { + Template t2 = new Template(null, "<@i1.m/>", cfg); + + StringWriter sw = new StringWriter(); + env = t2.createProcessingEnvironment(null, sw); + env.setVariable("i1", i1); + + env.process(); + assertEquals("1", sw.toString()); + } + + { + Template t2 = new Template(null, "<@i2.m/>", cfg); + + StringWriter sw = new StringWriter(); + env = t2.createProcessingEnvironment(null, sw); + env.setVariable("i2", i2); + + try { + env.process(); + assertEquals("2", sw.toString()); + } catch (NullPointerException e) { + // Expected on 2.3.x, because it won't find the namespace for the macro + // [2.4] Fix this "bug" + } + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicMacroAPIsTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicMacroAPIsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicMacroAPIsTest.java new file mode 100644 index 0000000..9c87e61 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicMacroAPIsTest.java @@ -0,0 +1,88 @@ +/* + * 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.io.StringWriter; +import java.util.Map; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.util._NullWriter; +import org.apache.freemarker.test.TestConfigurationBuilder; +import org.junit.Test; + +/** + * These are things that users shouldn't do, but we shouldn't break backward compatibility without knowing about it. + */ +public class MistakenlyPublicMacroAPIsTest { + + private final Configuration cfg = new TestConfigurationBuilder().build(); + + /** + * Getting the macros from one template, and adding them to another. + */ + @Test + public void testMacroCopyingExploit() throws IOException, TemplateException { + Template tMacros = new Template(null, "<#macro m1>1</#macro><#macro m2>2</#macro>", cfg); + Map<String, ASTDirMacro> macros = tMacros.getMacros(); + + Template t = new Template(null, + "<@m1/><@m2/><@m3/>" + + "<#macro m1>1b</#macro><#macro m3>3b</#macro> " + + "<@m1/><@m2/><@m3/>", cfg); + t.addMacro(macros.get("m1")); + t.addMacro(macros.get("m2")); + + assertEquals("123b 1b23b", getTemplateOutput(t)); + } + + @Test + public void testMacroCopyingExploitAndNamespaces() throws IOException, TemplateException { + Template tMacros = new Template(null, "<#assign x = 0><#macro m1>${x}</#macro>", cfg); + Template t = new Template(null, "<#assign x = 1><@m1/>", cfg); + t.addMacro((ASTDirMacro) tMacros.getMacros().get("m1")); + + assertEquals("1", getTemplateOutput(t)); + } + + @Test + public void testMacroCopyingFromFTLVariable() throws IOException, TemplateException { + Template tMacros = new Template(null, "<#assign x = 0><#macro m1>${x}</#macro>", cfg); + Environment env = tMacros.createProcessingEnvironment(null, _NullWriter.INSTANCE); + env.process(); + TemplateModel m1 = env.getVariable("m1"); + assertThat(m1, instanceOf(ASTDirMacro.class)); + + Template t = new Template(null, "<#assign x = 1><@m1/>", cfg); + t.addMacro((ASTDirMacro) m1); + + assertEquals("1", getTemplateOutput(t)); + } + + private String getTemplateOutput(Template t) throws TemplateException, IOException { + StringWriter sw = new StringWriter(); + t.process(null, sw); + return sw.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/NewBiObjectWrapperRestrictionTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/NewBiObjectWrapperRestrictionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/NewBiObjectWrapperRestrictionTest.java new file mode 100644 index 0000000..506101d --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/NewBiObjectWrapperRestrictionTest.java @@ -0,0 +1,117 @@ +/* + * 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.Arrays; +import java.util.Collection; +import java.util.Map; + +import org.apache.freemarker.core.model.ObjectWrapper; +import org.apache.freemarker.core.model.TemplateBooleanModel; +import org.apache.freemarker.core.model.TemplateDateModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelAdapter; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; +import org.apache.freemarker.core.model.impl.SimpleDate; +import org.apache.freemarker.core.model.impl.SimpleHash; +import org.apache.freemarker.core.model.impl.SimpleNumber; +import org.apache.freemarker.core.model.impl.SimpleScalar; +import org.apache.freemarker.core.model.impl.SimpleSequence; +import org.apache.freemarker.test.TemplateTest; +import org.apache.freemarker.test.TestConfigurationBuilder; +import org.junit.Test; + +public class NewBiObjectWrapperRestrictionTest extends TemplateTest { + + @Override + protected Configuration createDefaultConfiguration() throws Exception { + return new TestConfigurationBuilder().objectWrapper(new EntirelyCustomObjectWrapper()).build(); + } + + @Test + public void testPositive() throws IOException, TemplateException { + assertOutput( + "${'org.apache.freemarker.test.templatesuite.models.NewTestModel'?new()}", + "default constructor"); + } + + @Test + public void testNegative() { + assertErrorContains( + "${'org.apache.freemarker.test.templatesuite.models.NewTestModel'?new('s')}", + "only supports 0 argument"); + } + + /** + * An object wrapper that doesn't extend {@link DefaultObjectWrapper}. + */ + public static class EntirelyCustomObjectWrapper implements ObjectWrapper { + + @Override + public TemplateModel wrap(Object obj) throws TemplateModelException { + if (obj == null) { + return null; + } + + if (obj instanceof TemplateModel) { + return (TemplateModel) obj; + } + if (obj instanceof TemplateModelAdapter) { + return ((TemplateModelAdapter) obj).getTemplateModel(); + } + + if (obj instanceof String) { + return new SimpleScalar((String) obj); + } + if (obj instanceof Number) { + return new SimpleNumber((Number) obj); + } + if (obj instanceof Boolean) { + return obj.equals(Boolean.TRUE) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; + } + if (obj instanceof java.util.Date) { + if (obj instanceof java.sql.Date) { + return new SimpleDate((java.sql.Date) obj); + } + if (obj instanceof java.sql.Time) { + return new SimpleDate((java.sql.Time) obj); + } + if (obj instanceof java.sql.Timestamp) { + return new SimpleDate((java.sql.Timestamp) obj); + } + return new SimpleDate((java.util.Date) obj, TemplateDateModel.UNKNOWN); + } + + if (obj.getClass().isArray()) { + obj = Arrays.asList((Object[]) obj); + } + if (obj instanceof Collection) { + return new SimpleSequence((Collection<?>) obj, this); + } + if (obj instanceof Map) { + return new SimpleHash((Map<?, ?>) obj, this); + } + + return null; + } + } +}
