http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateGetEncodingTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateGetEncodingTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateGetEncodingTest.java new file mode 100644 index 0000000..4b5bf59 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateGetEncodingTest.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 static org.junit.Assert.*; + +import java.io.IOException; +import java.nio.charset.Charset; + +import org.apache.freemarker.core.templateresolver.ConditionalTemplateConfigurationFactory; +import org.apache.freemarker.core.templateresolver.FileNameGlobMatcher; +import org.apache.freemarker.core.templateresolver.impl.StrongCacheStorage; +import org.apache.freemarker.test.MonitoredTemplateLoader; +import org.junit.Test; + +public class TemplateGetEncodingTest { + + private static final Charset ISO_8859_2 = Charset.forName("ISO-8859-2"); + + @Test + public void test() throws IOException { + Configuration.Builder cfgB = new Configuration.Builder(Configuration.VERSION_3_0_0); + { + cfgB.setSourceEncoding(ISO_8859_2); + MonitoredTemplateLoader tl = new MonitoredTemplateLoader(); + tl.putBinaryTemplate("bin", "test"); + tl.putBinaryTemplate("bin-static", "<#test>"); + tl.putTextTemplate("text", "test"); + tl.putTextTemplate("text-static", "<#test>"); + TemplateConfiguration.Builder staticTextTCB = new TemplateConfiguration.Builder(); + staticTextTCB.setTemplateLanguage(TemplateLanguage.STATIC_TEXT); + cfgB.setTemplateConfigurations( + new ConditionalTemplateConfigurationFactory( + new FileNameGlobMatcher("*-static*"), staticTextTCB.build())); + cfgB.setTemplateLoader(tl); + cfgB.setCacheStorage(new StrongCacheStorage()); + } + + Configuration cfg = cfgB.build(); + assertEquals(ISO_8859_2, cfg.getTemplate("bin").getActualSourceEncoding()); + assertEquals(ISO_8859_2, cfg.getTemplate("bin-static").getActualSourceEncoding()); + assertNull(cfg.getTemplate("text").getActualSourceEncoding()); + assertNull(cfg.getTemplate("text-static").getActualSourceEncoding()); + assertNull(new Template(null, "test", cfg).getActualSourceEncoding()); + assertNull(Template.createPlainTextTemplate(null, "<#test>", cfg).getActualSourceEncoding()); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java new file mode 100644 index 0000000..f0e63a8 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java @@ -0,0 +1,669 @@ +/* + * 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.Configuration.ExtendableBuilder.TEMPLATE_LOOKUP_STRATEGY_KEY; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.Serializable; +import java.io.StringWriter; +import java.util.Locale; + +import org.apache.freemarker.core.templateresolver.TemplateLookupContext; +import org.apache.freemarker.core.templateresolver.TemplateLookupResult; +import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateLookupStrategy; +import org.apache.freemarker.test.MonitoredTemplateLoader; +import org.junit.Test; + +import com.google.common.collect.ImmutableList; + +public class TemplateLookupStrategyTest { + + @Test + public void testSetSetting() throws Exception { + assertSame( + DefaultTemplateLookupStrategy.INSTANCE, + new Configuration.Builder(Configuration.VERSION_3_0_0).build() + .getTemplateLookupStrategy()); + + assertTrue( + new Configuration.Builder(Configuration.VERSION_3_0_0) + .setting(TEMPLATE_LOOKUP_STRATEGY_KEY, MyTemplateLookupStrategy.class.getName() + "()") + .build() + .getTemplateLookupStrategy() instanceof MyTemplateLookupStrategy); + + assertSame( + DefaultTemplateLookupStrategy.INSTANCE, + new Configuration.Builder(Configuration.VERSION_3_0_0) + .setting(TEMPLATE_LOOKUP_STRATEGY_KEY, "dEfault") + .build() + .getTemplateLookupStrategy()); + } + + @Test + public void testCustomStrategy() throws IOException { + MonitoredTemplateLoader tl = new MonitoredTemplateLoader(); + tl.putTextTemplate("test.ftl", ""); + tl.putTextTemplate("aa/test.ftl", ""); + + Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0) + .templateLoader(tl) + .templateLookupStrategy(MyTemplateLookupStrategy.INSTANCE) + .build(); + + final Locale locale = new Locale("aa", "BB", "CC_DD"); + + try { + cfg.getTemplate("missing.ftl", locale); + fail(); + } catch (TemplateNotFoundException e) { + assertEquals("missing.ftl", e.getTemplateName()); + assertEquals(ImmutableList.of("aa/missing.ftl", "missing.ftl"), tl.getLoadNames()); + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Template t = cfg.getTemplate("test.ftl", locale); + assertEquals("test.ftl", t.getLookupName()); + assertEquals("aa/test.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals(ImmutableList.of("aa/test.ftl"), tl.getLoadNames()); + tl.clearEvents(); + cfg.clearTemplateCache(); + } + } + + @Test + public void testDefaultStrategy() throws IOException { + MonitoredTemplateLoader tl = new MonitoredTemplateLoader(); + tl.putTextTemplate("test.ftl", ""); + tl.putTextTemplate("test_aa.ftl", ""); + tl.putTextTemplate("test_aa_BB.ftl", ""); + tl.putTextTemplate("test_aa_BB_CC.ftl", ""); + tl.putTextTemplate("test_aa_BB_CC_DD.ftl", ""); + + try { + new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build() + .getTemplate("missing.ftl", new Locale("aa", "BB", "CC_DD")); + fail(); + } catch (TemplateNotFoundException e) { + assertEquals("missing.ftl", e.getTemplateName()); + assertEquals( + ImmutableList.of( + "missing_aa_BB_CC_DD.ftl", + "missing_aa_BB_CC.ftl", + "missing_aa_BB.ftl", + "missing_aa.ftl", + "missing.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + + try { + new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl) + .locale(new Locale("xx")).build() + .getTemplate("missing.ftl"); + fail(); + } catch (TemplateNotFoundException e) { + assertEquals("missing.ftl", e.getTemplateName()); + assertEquals( + ImmutableList.of("missing_xx.ftl", "missing.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + + try { + new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl) + .locale(new Locale("xx")) + .localizedLookup(false).build() + .getTemplate("missing.ftl"); + fail(); + } catch (TemplateNotFoundException e) { + assertEquals("missing.ftl", e.getTemplateName()); + assertEquals( + ImmutableList.of("missing.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + + try { + new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build() + .getTemplate("_a_b_.ftl", new Locale("xx", "yy")); + fail(); + } catch (TemplateNotFoundException e) { + assertEquals("_a_b_.ftl", e.getTemplateName()); + assertEquals( + ImmutableList.of("_a_b__xx_YY.ftl", "_a_b__xx.ftl", "_a_b_.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + + for (String templateName : new String[] { "test.ftl", "./test.ftl", "/test.ftl", "x/foo/../../test.ftl" }) { + { + final Locale locale = new Locale("aa", "BB", "CC_DD"); + final Template t = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build() + .getTemplate("test.ftl", locale); + assertEquals("test.ftl", t.getLookupName()); + assertEquals("test_aa_BB_CC_DD.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals(ImmutableList.of("test_aa_BB_CC_DD.ftl"), tl.getLoadNames()); + assertNull(t.getCustomLookupCondition()); + tl.clearEvents(); + } + + { + final Locale locale = new Locale("aa", "BB", "CC_XX"); + final Template t = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build() + .getTemplate(templateName, locale); + assertEquals("test.ftl", t.getLookupName()); + assertEquals("test_aa_BB_CC.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals(ImmutableList.of("test_aa_BB_CC_XX.ftl", "test_aa_BB_CC.ftl"), tl.getLoadNames()); + tl.clearEvents(); + } + + { + final Locale locale = new Locale("aa", "BB", "XX_XX"); + final Template t = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build() + .getTemplate(templateName, locale); + assertEquals("test.ftl", t.getLookupName()); + assertEquals("test_aa_BB.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals( + ImmutableList.of("test_aa_BB_XX_XX.ftl", "test_aa_BB_XX.ftl", "test_aa_BB.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + + { + final Locale locale = new Locale("aa", "BB", "XX_XX"); + final Template t = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl) + .localizedLookup(false).build() + .getTemplate(templateName, locale); + assertEquals("test.ftl", t.getLookupName()); + assertEquals("test.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals( + ImmutableList.of("test.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + + { + final Locale locale = new Locale("aa", "XX", "XX_XX"); + final Template t = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build() + .getTemplate(templateName, locale); + assertEquals("test.ftl", t.getLookupName()); + assertEquals("test_aa.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals( + ImmutableList.of("test_aa_XX_XX_XX.ftl", "test_aa_XX_XX.ftl", "test_aa_XX.ftl", "test_aa.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + + { + final Locale locale = new Locale("xx", "XX", "XX_XX"); + final Template t = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build() + .getTemplate(templateName, locale); + assertEquals("test.ftl", t.getLookupName()); + assertEquals("test.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals( + ImmutableList.of( + "test_xx_XX_XX_XX.ftl", "test_xx_XX_XX.ftl", "test_xx_XX.ftl", "test_xx.ftl", "test.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + + { + final Locale locale = new Locale("xx", "BB", "CC_DD"); + final Template t = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build() + .getTemplate(templateName, locale); + assertEquals("test.ftl", t.getLookupName()); + assertEquals("test.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals( + ImmutableList.of( + "test_xx_BB_CC_DD.ftl", "test_xx_BB_CC.ftl", "test_xx_BB.ftl", "test_xx.ftl", "test.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + } + } + } + + @Test + public void testAcquisition() throws IOException { + MonitoredTemplateLoader tl = new MonitoredTemplateLoader(); + tl.putTextTemplate("t.ftl", ""); + tl.putTextTemplate("sub/i.ftl", ""); + tl.putTextTemplate("x/sub/i.ftl", ""); + + Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build(); + + final Locale locale = new Locale("xx"); + + { + final Template t = cfg.getTemplate("/./moo/../x/y/*/sub/i.ftl", locale); + assertEquals("x/y/*/sub/i.ftl", t.getLookupName()); + assertEquals("x/sub/i.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals( + ImmutableList.of( + "x/y/sub/i_xx.ftl", "x/sub/i_xx.ftl", "sub/i_xx.ftl", + "x/y/sub/i.ftl", "x/sub/i.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Template t = cfg.getTemplate("a/b/*/./sub/i.ftl", locale); + assertEquals("a/b/*/sub/i.ftl", t.getLookupName()); + assertEquals("sub/i.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertNull(t.getCustomLookupCondition()); + assertEquals( + ImmutableList.of( + "a/b/sub/i_xx.ftl", "a/sub/i_xx.ftl", "sub/i_xx.ftl", + "a/b/sub/i.ftl", "a/sub/i.ftl", "sub/i.ftl"), + tl.getLoadNames()); + tl.clearEvents(); + cfg.clearTemplateCache(); + } + } + + @Test + public void testCustomLookupCondition() throws IOException, TemplateException { + MonitoredTemplateLoader tl = new MonitoredTemplateLoader(); + + final Configuration cfg; + final Configuration cfgNoLocLU; + { + Configuration.Builder cfgB = new Configuration.Builder(Configuration.VERSION_3_0_0) + .templateLoader(tl) + .templateLookupStrategy(new DomainTemplateLookupStrategy()); + cfg = cfgB.build(); + cfgNoLocLU = cfgB.localizedLookup(false).build(); + } + + final String iAtDefaultContent = "i at default"; + final String iXxAtDefaultContent = "i_xx at default"; + final String iAtBaazComContent = "i at baaz.com"; + final String iAtFooComContent = "i at foo.com"; + final String tAtDefaultWithoutIncludeContent = "t at default "; + final String tAtDefaultContent = toCanonicalFTL(tAtDefaultWithoutIncludeContent + "<#include 'i.ftl'>", cfg); + final String tAtBarComWithoutIncludeContent = "t at bar.com "; + final String tAtBarComContent = toCanonicalFTL(tAtBarComWithoutIncludeContent + "<#include 'i.ftl'>", cfg); + final String tAtFooComWithoutIncludeContent = "t at foo.com "; + final String tAtFooComContent = toCanonicalFTL(tAtFooComWithoutIncludeContent + "<#include 'i.ftl'>", cfg); + final String t2XxLocaleExpectedOutput = "i3_xx at foo.com"; + final String t2OtherLocaleExpectedOutput = "i3 at foo.com"; + + tl.putTextTemplate("@foo.com/t.ftl", tAtFooComContent); + tl.putTextTemplate("@bar.com/t.ftl", tAtBarComContent); + tl.putTextTemplate("@default/t.ftl", tAtDefaultContent); + tl.putTextTemplate("@foo.com/i.ftl", iAtFooComContent); + tl.putTextTemplate("@baaz.com/i.ftl", iAtBaazComContent); + tl.putTextTemplate("@default/i_xx.ftl", iXxAtDefaultContent); + tl.putTextTemplate("@default/i.ftl", iAtDefaultContent); + tl.putTextTemplate("@foo.com/t2.ftl", "<#import 'i2.ftl' as i2 />${proof}"); + tl.putTextTemplate("@default/i2.ftl", "<#import 'i3.ftl' as i3 />"); + tl.putTextTemplate("@foo.com/i3.ftl", "<#global proof = '" + t2OtherLocaleExpectedOutput + "'>"); + tl.putTextTemplate("@foo.com/i3_xx.ftl", "<#global proof = '" + t2XxLocaleExpectedOutput + "'>"); + + { + final Locale locale = new Locale("xx"); + final Domain domain = new Domain("foo.com"); + final Template t = cfg.getTemplate("t.ftl", locale, domain); + assertEquals("t.ftl", t.getLookupName()); + assertEquals("@foo.com/t.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertEquals(domain, t.getCustomLookupCondition()); + assertEquals(tAtFooComContent, t.toString()); + assertEquals( + ImmutableList.of("@foo.com/t_xx.ftl", "@foo.com/t.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + assertOutputEquals(tAtFooComWithoutIncludeContent + iAtFooComContent, t); + assertEquals( + ImmutableList.of("@foo.com/i_xx.ftl", "@foo.com/i.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Locale locale = new Locale("xx"); + final Domain domain = new Domain("bar.com"); + final Template t = cfg.getTemplate("t.ftl", locale, domain); + assertEquals("t.ftl", t.getLookupName()); + assertEquals("@bar.com/t.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertEquals(domain, t.getCustomLookupCondition()); + assertEquals(tAtBarComContent, t.toString()); + assertEquals( + ImmutableList.of("@bar.com/t_xx.ftl", "@bar.com/t.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + assertOutputEquals(tAtBarComWithoutIncludeContent + iXxAtDefaultContent, t); + assertEquals( + ImmutableList.of( + "@bar.com/i_xx.ftl", "@bar.com/i.ftl", + "@default/i_xx.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Locale locale = new Locale("xx", "YY"); + final Domain domain = new Domain("baaz.com"); + final Template t = cfg.getTemplate("t.ftl", locale, domain); + assertEquals("t.ftl", t.getLookupName()); + assertEquals("@default/t.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertEquals(domain, t.getCustomLookupCondition()); + assertEquals(tAtDefaultContent, t.toString()); + assertEquals( + ImmutableList.of( + "@baaz.com/t_xx_YY.ftl", "@baaz.com/t_xx.ftl", "@baaz.com/t.ftl", + "@default/t_xx_YY.ftl", "@default/t_xx.ftl", "@default/t.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + assertOutputEquals(tAtDefaultWithoutIncludeContent + iAtBaazComContent, t); + assertEquals( + ImmutableList.of("@baaz.com/i_xx_YY.ftl", "@baaz.com/i_xx.ftl", "@baaz.com/i.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Locale locale = new Locale("xx", "YY"); + final Domain domain = new Domain("nosuch.com"); + final Template t = cfg.getTemplate("i.ftl", locale, domain); + assertEquals("i.ftl", t.getLookupName()); + assertEquals("@default/i_xx.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertEquals(domain, t.getCustomLookupCondition()); + assertEquals(iXxAtDefaultContent, t.toString()); + assertEquals( + ImmutableList.of( + "@nosuch.com/i_xx_YY.ftl", "@nosuch.com/i_xx.ftl", "@nosuch.com/i.ftl", + "@default/i_xx_YY.ftl", "@default/i_xx.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Locale locale = new Locale("xx", "YY"); + final Domain domain = new Domain("nosuch.com"); + final Template t = cfgNoLocLU.getTemplate("i.ftl", locale, domain); + assertEquals("i.ftl", t.getLookupName()); + assertEquals("@default/i.ftl", t.getSourceName()); + assertEquals(locale, t.getLocale()); + assertEquals(domain, t.getCustomLookupCondition()); + assertEquals(iAtDefaultContent, t.toString()); + assertEquals( + ImmutableList.of("@nosuch.com/i.ftl", "@default/i.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfgNoLocLU.clearTemplateCache(); + } + + { + final Locale locale = new Locale("xx"); + final Domain domain = new Domain("foo.com"); + final Template t = cfg.getTemplate("t2.ftl", locale, domain); + assertOutputEquals(t2XxLocaleExpectedOutput, t); + assertEquals( + ImmutableList.of( + "@foo.com/t2_xx.ftl", "@foo.com/t2.ftl", + "@foo.com/i2_xx.ftl", "@foo.com/i2.ftl", "@default/i2_xx.ftl", "@default/i2.ftl", + "@foo.com/i3_xx.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Locale locale = new Locale("yy"); + final Domain domain = new Domain("foo.com"); + final Template t = cfg.getTemplate("t2.ftl", locale, domain); + assertOutputEquals(t2OtherLocaleExpectedOutput, t); + assertEquals( + ImmutableList.of( + "@foo.com/t2_yy.ftl", "@foo.com/t2.ftl", + "@foo.com/i2_yy.ftl", "@foo.com/i2.ftl", "@default/i2_yy.ftl", "@default/i2.ftl", + "@foo.com/i3_yy.ftl", "@foo.com/i3.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Locale locale = new Locale("xx"); + final Domain domain = new Domain("foo.com"); + final Template t = cfgNoLocLU.getTemplate("t2.ftl", locale, domain); + assertOutputEquals(t2OtherLocaleExpectedOutput, t); + assertEquals( + ImmutableList.of( + "@foo.com/t2.ftl", + "@foo.com/i2.ftl", "@default/i2.ftl", + "@foo.com/i3.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfgNoLocLU.clearTemplateCache(); + } + + { + final Locale locale = new Locale("xx"); + final Domain domain = new Domain("foo.com"); + cfg.getTemplate("i3.ftl", locale, domain); + assertEquals( + ImmutableList.of("@foo.com/i3_xx.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + final Locale locale = new Locale("xx"); + final Domain domain = new Domain("bar.com"); + try { + cfg.getTemplate("i3.ftl", locale, domain); + } catch (TemplateNotFoundException e) { + assertEquals("i3.ftl", e.getTemplateName()); + assertEquals(domain, e.getCustomLookupCondition()); + } + assertEquals( + ImmutableList.of( + "@bar.com/i3_xx.ftl", "@bar.com/i3.ftl", + "@default/i3_xx.ftl", "@default/i3.ftl"), + tl.getLoadNames()); + + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + } + + public static class Domain implements Serializable { + private final String name; + + public Domain(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Domain domain = (Domain) o; + + return name != null ? name.equals(domain.name) : domain.name == null; + } + + @Override + public int hashCode() { + return name != null ? name.hashCode() : 0; + } + } + + @Test + public void testNonparsed() throws IOException { + MonitoredTemplateLoader tl = new MonitoredTemplateLoader(); + tl.putTextTemplate("test.txt", ""); + tl.putTextTemplate("test_aa.txt", ""); + + Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build(); + + try { + cfg.getTemplate("missing.txt", new Locale("aa", "BB"), null, false); + fail(); + } catch (TemplateNotFoundException e) { + assertEquals("missing.txt", e.getTemplateName()); + assertEquals( + ImmutableList.of( + "missing_aa_BB.txt", + "missing_aa.txt", + "missing.txt"), + tl.getLoadNames()); + tl.clearEvents(); + cfg.clearTemplateCache(); + } + + { + Template t = cfg.getTemplate("test.txt", new Locale("aa", "BB"), null, false); + assertEquals("test.txt", t.getLookupName()); + assertEquals("test_aa.txt", t.getSourceName()); + assertEquals( + ImmutableList.of( + "test_aa_BB.txt", + "test_aa.txt"), + tl.getLoadNames()); + } + } + + @Test + public void testParseError() throws IOException { + MonitoredTemplateLoader tl = new MonitoredTemplateLoader(); + tl.putTextTemplate("test.ftl", ""); + tl.putTextTemplate("test_aa.ftl", "<#wrong>"); + + Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0).templateLoader(tl).build(); + + try { + cfg.getTemplate("test.ftl", new Locale("aa", "BB")); + fail(); + } catch (ParseException e) { + assertEquals("test_aa.ftl", e.getTemplateSourceName()); + assertEquals("test.ftl", e.getTemplateLookupName()); + } + } + + private String toCanonicalFTL(String ftl, Configuration cfg) throws IOException { + return new Template(null, ftl, cfg).toString(); + } + + private void assertOutputEquals(final String expectedContent, final Template t) throws TemplateException, + IOException { + StringWriter sw = new StringWriter(); + t.process(null, sw); + assertEquals(expectedContent, sw.toString()); + } + + public static class MyTemplateLookupStrategy extends TemplateLookupStrategy { + + public static final MyTemplateLookupStrategy INSTANCE = new MyTemplateLookupStrategy(); + + private MyTemplateLookupStrategy() { } + + @Override + public <R extends TemplateLookupResult> R lookup(TemplateLookupContext<R> ctx) throws IOException { + String lang = ctx.getTemplateLocale().getLanguage().toLowerCase(); + R lookupResult = ctx.lookupWithAcquisitionStrategy(lang + "/" + ctx.getTemplateName()); + if (lookupResult.isPositive()) { + return lookupResult; + } + + return ctx.lookupWithAcquisitionStrategy(ctx.getTemplateName()); + } + + } + + public static class DomainTemplateLookupStrategy extends TemplateLookupStrategy { + + public static final DomainTemplateLookupStrategy INSTANCE = new DomainTemplateLookupStrategy(); + + @Override + public <R extends TemplateLookupResult> R lookup(TemplateLookupContext<R> ctx) throws IOException { + Domain domain = (Domain) ctx.getCustomLookupCondition(); + if (domain == null) { + throw new NullPointerException("The domain wasn't specified"); + } + + final String templateName = ctx.getTemplateName(); + + // Disallow addressing the domain roots directly: + if (templateName.startsWith("@")) { + return ctx.createNegativeLookupResult(); + } + + R lookupResult = ctx.lookupWithLocalizedThenAcquisitionStrategy( + "@" + domain.name + "/" + templateName, + ctx.getTemplateLocale()); + if (lookupResult.isPositive()) { + return lookupResult; + } + + return ctx.lookupWithLocalizedThenAcquisitionStrategy("@default/" + templateName, ctx.getTemplateLocale()); + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateNameSpecialVariablesTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateNameSpecialVariablesTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateNameSpecialVariablesTest.java new file mode 100644 index 0000000..7c9a98f --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateNameSpecialVariablesTest.java @@ -0,0 +1,159 @@ +/* + * 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; + +public class TemplateNameSpecialVariablesTest extends TemplateTest { + + private static final String PRINT_ALL_FTL + = "ct=${.currentTemplateName!'-'}, mt=${.mainTemplateName!'-'}"; + + @Test + public void testMainTemplateName() throws IOException, TemplateException { + addTemplateNameTestTemplates(".mainTemplateName"); + assertOutputForNamed("main.ftl", + "In main: main.ftl\n" + + "In imp: main.ftl\n" + + "In main: main.ftl\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "In imp call imp:\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "After: main.ftl\n" + + "In main: main.ftl\n" + + "In inc: main.ftl\n" + + "In inc call imp:\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "In main: main.ftl\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "In inc call imp:\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "In main: main.ftl\n"); + } + + @Test + public void testCurrentTemplateName() throws IOException, TemplateException { + addTemplateNameTestTemplates(".currentTemplateName"); + assertOutputForNamed("main.ftl", + "In main: main.ftl\n" + + "In imp: imp.ftl\n" + + "In main: main.ftl\n" + + "imp.ftl\n" + + "{main.ftl}\n" + + "In imp call imp:\n" + + "imp.ftl\n" + + "{imp.ftl}\n" + + "After: imp.ftl\n" + + "In main: main.ftl\n" + + "In inc: inc.ftl\n" + + "In inc call imp:\n" + + "imp.ftl\n" + + "{inc.ftl}\n" + + "In main: main.ftl\n" + + "inc.ftl\n" + + "{main.ftl}\n" + + "In inc call imp:\n" + + "imp.ftl\n" + + "{inc.ftl}\n" + + "In main: main.ftl\n"); + } + + private void addTemplateNameTestTemplates(String specVar) { + addTemplate("main.ftl", + "In main: ${" + specVar + "}\n" + + "<#import 'imp.ftl' as i>" + + "In imp: ${inImp}\n" + + "In main: ${" + specVar + "}\n" + + "<@i.impM>${" + specVar + "}</@>\n" + + "<@i.impM2 />\n" + + "In main: ${" + specVar + "}\n" + + "<#include 'inc.ftl'>" + + "In main: ${" + specVar + "}\n" + + "<@incM>${" + specVar + "}</@>\n" + + "<@incM2 />\n" + + "In main: ${" + specVar + "}\n" + ); + addTemplate("imp.ftl", + "<#global inImp = " + specVar + ">" + + "<#macro impM>" + + "${" + specVar + "}\n" + + "{<#nested>}" + + "</#macro>" + + "<#macro impM2>" + + "In imp call imp:\n" + + "<@impM>${" + specVar + "}</@>\n" + + "After: ${" + specVar + "}" + + "</#macro>" + ); + addTemplate("inc.ftl", + "In inc: ${" + specVar + "}\n" + + "In inc call imp:\n" + + "<@i.impM>${" + specVar + "}</@>\n" + + "<#macro incM>" + + "${" + specVar + "}\n" + + "{<#nested>}" + + "</#macro>" + + "<#macro incM2>" + + "In inc call imp:\n" + + "<@i.impM>${" + specVar + "}</@>" + + "</#macro>" + ); + } + + @Test + public void testInAdhocTemplate() throws TemplateException, IOException { + addTemplate("inc.ftl", "Inc: " + PRINT_ALL_FTL); + + // In nameless templates, the deprecated .templateName is "", but the new variables are missing values. + assertOutput(new Template(null, PRINT_ALL_FTL + "; <#include 'inc.ftl'>", getConfiguration()), + "ct=-, mt=-; Inc: ct=inc.ftl, mt=-"); + + assertOutput(new Template("foo.ftl", PRINT_ALL_FTL + "; <#include 'inc.ftl'>", getConfiguration()), + "ct=foo.ftl, mt=foo.ftl; Inc: ct=inc.ftl, mt=foo.ftl"); + } + + @Test + public void testInInterpretTemplate() throws TemplateException, IOException { + addToDataModel("t", PRINT_ALL_FTL); + assertOutput(new Template("foo.ftl", PRINT_ALL_FTL + "; <@t?interpret />", getConfiguration()), + "ct=foo.ftl, mt=foo.ftl; " + + "ct=foo.ftl->anonymous_interpreted, mt=foo.ftl"); + assertOutput(new Template(null, PRINT_ALL_FTL + "; <@t?interpret />", getConfiguration()), + "ct=-, mt=-; " + + "ct=nameless_template->anonymous_interpreted, mt=-"); + assertOutput(new Template("foo.ftl", PRINT_ALL_FTL + "; <@[t,'bar']?interpret />", getConfiguration()), + "ct=foo.ftl, mt=foo.ftl; " + + "ct=foo.ftl->bar, mt=foo.ftl"); + } + + @Override + protected Configuration createDefaultConfiguration() throws Exception { + return new TestConfigurationBuilder().whitespaceStripping(false).build(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java new file mode 100644 index 0000000..66331ad --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateNotFoundMessageTest.java @@ -0,0 +1,207 @@ +/* + * 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.test.hamcerst.Matchers.*; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; + +import org.apache.freemarker.core.templateresolver.MalformedTemplateNameException; +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.TemplateLookupContext; +import org.apache.freemarker.core.templateresolver.TemplateLookupResult; +import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; +import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; +import org.junit.Test; + +public class TemplateNotFoundMessageTest { + + @Test + public void testFileTemplateLoader() throws IOException { + final File baseDir = new File(System.getProperty("user.home")); + final String errMsg = failWith(new FileTemplateLoader(baseDir)); + showErrorMessage(errMsg); + assertThat(errMsg, containsString(baseDir.toString())); + assertThat(errMsg, containsString("FileTemplateLoader")); + } + + @Test + public void testClassTemplateLoader() throws IOException { + final String errMsg = failWith(new ClassTemplateLoader(getClass(), "foo/bar")); + showErrorMessage(errMsg); + assertThat(errMsg, containsString("ClassTemplateLoader")); + assertThat(errMsg, containsString("foo/bar")); + } + + @Test + public void testStringTemplateLoader() throws IOException { + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("aaa", "A"); + tl.putTemplate("bbb", "B"); + tl.putTemplate("ccc", "C"); + final String errMsg = failWith(tl); + showErrorMessage(errMsg); + assertThat(errMsg, containsString("StringTemplateLoader")); + assertThat(errMsg, containsString("aaa")); + assertThat(errMsg, containsString("bbb")); + assertThat(errMsg, containsString("ccc")); + } + + @Test + public void testMultiTemplateLoader() throws IOException { + final String errMsg = failWith(new MultiTemplateLoader(new TemplateLoader[] { + new StringTemplateLoader(), + new ClassTemplateLoader(getClass(), "foo/bar") + })); + showErrorMessage(errMsg); + assertThat(errMsg, containsString("MultiTemplateLoader")); + assertThat(errMsg, containsString("StringTemplateLoader")); + assertThat(errMsg, containsString("ClassTemplateLoader")); + assertThat(errMsg, containsString("foo/bar")); + } + + @Test + public void testDefaultTemplateLoader() throws IOException { + String errMsg = failWith(null); + showErrorMessage(errMsg); + assertThat(errMsg, allOf(containsString("setTemplateLoader"), containsString("null"))); + } + + @Test + public void testOtherMessageDetails() throws IOException { + // Non-null TemplateLoader: + StringTemplateLoader emptyLoader = new StringTemplateLoader(); + { + String errMsg = failWith(emptyLoader, "../x"); + showErrorMessage(errMsg); + assertThat(errMsg, + allOf( + containsStringIgnoringCase("Malformed template name"), + containsStringIgnoringCase("root directory"))); + } + { + String errMsg = failWith(emptyLoader, "x\u0000y"); + showErrorMessage(errMsg); + assertThat(errMsg, + allOf( + containsStringIgnoringCase("Malformed template name"), + containsStringIgnoringCase("null character"))); + } + { + String errMsg = failWith(emptyLoader, "x\\y"); + showErrorMessage(errMsg); + assertThat(errMsg, + allOf(containsStringIgnoringCase("warning"), containsStringIgnoringCase("backslash"))); + } + { + String errMsg = failWith(emptyLoader, "x/./y"); + showErrorMessage(errMsg); + assertThat(errMsg, + allOf(containsStringIgnoringCase("normalized"), containsStringIgnoringCase("x/y"))); + } + { + String errMsg = failWith(emptyLoader, "/x/y"); + showErrorMessage(errMsg); + assertThat(errMsg, not(containsStringIgnoringCase("normalized"))); + } + { + String errMsg = failWith(emptyLoader, "x/y"); + showErrorMessage(errMsg); + assertThat(errMsg, not(containsStringIgnoringCase("normalized"))); + assertThat(errMsg, not(containsStringIgnoringCase("lookup strategy"))); + } + + Configuration.Builder cfgB = new Configuration.Builder(Configuration.VERSION_3_0_0) + .templateLoader(new StringTemplateLoader()) + .templateLookupStrategy( + new TemplateLookupStrategy() { + @Override + public TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException { + return ctx.lookupWithAcquisitionStrategy(ctx.getTemplateName()); + } + } + ); + { + String errMsg = failWith(emptyLoader, "x/y", + new TemplateLookupStrategy() { + @Override + public TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException { + return ctx.lookupWithAcquisitionStrategy(ctx.getTemplateName()); + } + } + ); + showErrorMessage(errMsg); + assertThat(errMsg, containsStringIgnoringCase("lookup strategy")); + } + + try { + cfgB.build().getTemplate("./missing", null, new DomainLookupCondition()); + fail(); + } catch (TemplateNotFoundException e) { + showErrorMessage(e.getMessage()); + assertThat(e.getMessage(), containsStringIgnoringCase("example.com")); + } + } + + private void showErrorMessage(String errMsg) { + // System.out.println(errMsg); + } + + private String failWith(TemplateLoader tl, String name, TemplateLookupStrategy templateLookupStrategy) { + try { + Configuration.Builder cfgB = new Configuration.Builder(Configuration.VERSION_3_0_0); + cfgB.setTemplateLoader(tl); + if (templateLookupStrategy != null) { + cfgB.setTemplateLookupStrategy(templateLookupStrategy); + } + cfgB.build().getTemplate(name); + fail(); + } catch (TemplateNotFoundException | MalformedTemplateNameException e) { + return e.getMessage(); + } catch (IOException e) { + fail("Unexpected exception: " + e); + } + return null; + } + + private String failWith(TemplateLoader tl, String name) { + return failWith(tl, name, null); + } + + private String failWith(TemplateLoader tl) { + return failWith(tl, "missing.ftl", null); + } + + @SuppressWarnings("serial") + private static final class DomainLookupCondition implements Serializable { + @Override + public String toString() { + return "example.com"; + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java new file mode 100644 index 0000000..a0174b3 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java @@ -0,0 +1,163 @@ +/* + * 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.util.Map; + +import org.apache.freemarker.core.ThreadInterruptionSupportTemplatePostProcessor.TemplateProcessingThreadInterruptedException; +import org.apache.freemarker.core.model.TemplateDirectiveBody; +import org.apache.freemarker.core.model.TemplateDirectiveModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.util._NullWriter; +import org.apache.freemarker.test.TestConfigurationBuilder; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TheadInterruptingSupportTest { + + private static final Logger LOG = LoggerFactory.getLogger(TheadInterruptingSupportTest.class); + + private static final int TEMPLATE_INTERRUPTION_TIMEOUT = 5000; + private final Configuration cfg = new TestConfigurationBuilder().build(); + + @Test + public void test() throws IOException, InterruptedException { + assertCanBeInterrupted("<#list 1.. as x></#list>"); + assertCanBeInterrupted("<#list 1.. as x>${x}</#list>"); + assertCanBeInterrupted("<#list 1.. as x>t${x}</#list>"); + assertCanBeInterrupted("<#list 1.. as x><#list 1.. as y>${y}</#list></#list>"); + assertCanBeInterrupted("<#list 1.. as x>${x}<#else>nope</#list>"); + assertCanBeInterrupted("<#list 1..>[<#items as x>${x}</#items>]<#else>nope</#list>"); + assertCanBeInterrupted("<@customLoopDirective />"); + assertCanBeInterrupted("<@customLoopDirective>x</@>"); + assertCanBeInterrupted("<@customLoopDirective><#if true>x</#if></@>"); + assertCanBeInterrupted("<#macro selfCalling><@sleepDirective/><@selfCalling /></#macro><@selfCalling />"); + assertCanBeInterrupted("<#function selfCalling><@sleepDirective/>${selfCalling()}</#function>${selfCalling()}"); + assertCanBeInterrupted("<#list 1.. as _><#attempt><@sleepDirective/><#recover>suppress</#attempt></#list>"); + assertCanBeInterrupted("<#attempt><#list 1.. as _></#list><#recover>suppress</#attempt>"); + } + + private void assertCanBeInterrupted(final String templateSourceCode) throws IOException, InterruptedException { + TemplateRunnerThread trt = new TemplateRunnerThread(templateSourceCode); + trt.start(); + synchronized (trt) { + while (!trt.isStarted()) { + trt.wait(); + } + } + Thread.sleep(50); // Just to ensure (hope...) that the template execution reaches "deep" enough + trt.interrupt(); + trt.join(TEMPLATE_INTERRUPTION_TIMEOUT); + assertTrue(trt.isTemplateProcessingInterrupted()); + } + + public class TemplateRunnerThread extends Thread { + + private final Template template; + private boolean started; + private boolean templateProcessingInterrupted; + + public TemplateRunnerThread(String templateSourceCode) throws IOException { + template = new Template(null, "<@startedDirective/>" + templateSourceCode, cfg); + _CoreAPI.addThreadInterruptedChecks(template); + } + + @Override + public void run() { + try { + template.process(this, _NullWriter.INSTANCE); + } catch (TemplateProcessingThreadInterruptedException e) { + //LOG.debug("Template processing interrupted", e); + synchronized (this) { + templateProcessingInterrupted = true; + } + } catch (Throwable e) { + LOG.error("Template processing failed", e); + } + } + + public synchronized boolean isTemplateProcessingInterrupted() { + return templateProcessingInterrupted; + } + + public synchronized boolean isStarted() { + return started; + } + + public TemplateDirectiveModel getStartedDirective() { + return new StartedDirective(); + } + + public TemplateDirectiveModel getCustomLoopDirective() { + return new CustomLoopDirective(); + } + + public TemplateDirectiveModel getSleepDirective() { + return new SleepDirective(); + } + + public class StartedDirective implements TemplateDirectiveModel { + + @Override + public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + throws TemplateException, IOException { + synchronized (TemplateRunnerThread.this) { + started = true; + TemplateRunnerThread.this.notifyAll(); + } + } + + } + + public class CustomLoopDirective implements TemplateDirectiveModel { + + @Override + public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + throws TemplateException, IOException { + // Deliberate infinite loop + while (true) { + body.render(_NullWriter.INSTANCE); + } + } + + } + + public class SleepDirective implements TemplateDirectiveModel { + + @Override + public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + throws TemplateException, IOException { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // Thread.sleep has reset the interrupted flag (because it has thrown InterruptedException). + Thread.currentThread().interrupt(); + } + } + + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java new file mode 100644 index 0000000..749d057 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java @@ -0,0 +1,105 @@ +/* + * 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.StringReader; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.freemarker.test.TemplateTest; +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +public class TypeErrorMessagesTest extends TemplateTest { + + static final Document doc; + static { + try { + DocumentBuilder docBuilder; + docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + doc = docBuilder.parse(new InputSource(new StringReader( + "<a><b>123</b><c a='true'>1</c><c a='false'>2</c></a>"))); + } catch (Exception e) { + throw new RuntimeException("Failed to build data-model", e); + } + } + + @Test + public void testNumericalBinaryOperator() { + assertErrorContains("${n - s}", "\"-\"", "right-hand", "number", "string"); + assertErrorContains("${s - n}", "\"-\"", "left-hand", "number", "string"); + } + + @Test + public void testGetterMistake() { + assertErrorContains("${bean.getX}", "${...}", + "number", "string", "method", "obj.getSomething", "obj.something"); + assertErrorContains("${1 * bean.getX}", "right-hand", + "number", "\\!string", "method", "obj.getSomething", "obj.something"); + assertErrorContains("<#if bean.isB></#if>", "condition", + "boolean", "method", "obj.isSomething", "obj.something"); + assertErrorContains("<#if bean.isB></#if>", "condition", + "boolean", "method", "obj.isSomething", "obj.something"); + assertErrorContains("${bean.voidM}", + "string", "method", "\\!()"); + assertErrorContains("${bean.intM}", + "string", "method", "obj.something()"); + assertErrorContains("${bean.intMP}", + "string", "method", "obj.something(params)"); + } + + @Test + public void testXMLTypeMismarches() throws Exception { + assertErrorContains("${doc.a.c}", + "used as string", "query result", "2", "multiple matches"); + assertErrorContains("${doc.a.c?boolean}", + "used as string", "query result", "2", "multiple matches"); + assertErrorContains("${doc.a.d}", + "used as string", "query result", "0", "no matches"); + assertErrorContains("${doc.a.d?boolean}", + "used as string", "query result", "0", "no matches"); + + assertErrorContains("${doc.a.c.@a}", + "used as string", "query result", "2", "multiple matches"); + assertErrorContains("${doc.a.d.@b}", + "used as string", "query result", "x", "no matches"); + + assertErrorContains("${doc.a.b * 2}", + "used as number", "text", "explicit conversion"); + assertErrorContains("<#if doc.a.b></#if>", + "used as number", "text", "explicit conversion"); + + assertErrorContains("${doc.a.d?nodeName}", + "used as node", "query result", "0", "no matches"); + assertErrorContains("${doc.a.c?nodeName}", + "used as node", "query result", "2", "multiple matches"); + } + + @Override + protected Object createDataModel() { + Map<String, Object> dataModel = createCommonTestValuesDataModel(); + dataModel.put("doc", doc); + return dataModel; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/UnclosedCommentTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/UnclosedCommentTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/UnclosedCommentTest.java new file mode 100644 index 0000000..99bf5d3 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/UnclosedCommentTest.java @@ -0,0 +1,41 @@ +/* + * 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 UnclosedCommentTest extends TemplateTest { + + @Test + public void test() throws IOException, TemplateException { + assertErrorContains("foo<#--", "end of file"); // Not too good... + assertErrorContains("foo<#-- ", "Unclosed", "<#--"); + assertErrorContains("foo<#--bar", "Unclosed", "<#--"); + assertErrorContains("foo\n<#--\n", "Unclosed", "<#--"); + assertErrorContains("foo<#noparse>", "end of file"); // Not too good... + assertErrorContains("foo<#noparse> ", "Unclosed", "#noparse"); + assertErrorContains("foo<#noparse>bar", "Unclosed", "#noparse"); + assertErrorContains("foo\n<#noparse>\n", "Unclosed", "#noparse"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/VersionTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/VersionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/VersionTest.java new file mode 100644 index 0000000..8c54df5 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/VersionTest.java @@ -0,0 +1,227 @@ +/* + * 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.sql.Date; + +import org.junit.Test; + +@SuppressWarnings("boxing") +public class VersionTest { + + @Test + public void testFromNumber() { + Version v = new Version(1, 2, 3); + assertEquals("1.2.3", v.toString()); + assertEquals(1002003, v.intValue()); + assertEquals(1, v.getMajor()); + assertEquals(2, v.getMinor()); + assertEquals(3, v.getMicro()); + assertNull(v.getExtraInfo()); + assertNull(v.isGAECompliant()); + assertNull(v.getBuildDate()); + } + + @Test + public void testFromNumber2() { + Version v = new Version(1, 2, 3, "beta8", Boolean.TRUE, new Date(5000)); + assertEquals("1.2.3-beta8", v.toString()); + assertEquals("beta8", v.getExtraInfo()); + assertTrue(v.isGAECompliant().booleanValue()); + assertEquals(new Date(5000), v.getBuildDate()); + } + + @Test + public void testFromNumber3() { + Version v = new Version(new Version(1, 2, 3).intValue()); + assertEquals(1, v.getMajor()); + assertEquals(2, v.getMinor()); + assertEquals(3, v.getMicro()); + } + + public void testFromNumberIncubating() { + Version v = new Version(2, 3, 24, "rc01-incubating", Boolean.FALSE, new Date(5000)); + assertEquals("2.3.24-rc01-incubating", v.toString()); + assertEquals("rc01-incubating", v.getExtraInfo()); + assertFalse(v.isGAECompliant().booleanValue()); + assertEquals(new Date(5000), v.getBuildDate()); + } + + @Test + public void testFromString() { + Version v = new Version("1.2.3-beta2"); + assertEquals("1.2.3-beta2", v.toString()); + assertEquals(1002003, v.intValue()); + assertEquals(1, v.getMajor()); + assertEquals(2, v.getMinor()); + assertEquals(3, v.getMicro()); + assertEquals("beta2", v.getExtraInfo()); + assertNull(v.isGAECompliant()); + assertNull(v.getBuildDate()); + } + + @Test + public void testFromString2() { + Version v = new Version("10.20.30", Boolean.TRUE, new Date(5000)); + assertEquals("10.20.30", v.toString()); + assertEquals(10020030, v.intValue()); + assertEquals(10, v.getMajor()); + assertEquals(20, v.getMinor()); + assertEquals(30, v.getMicro()); + assertNull(v.getExtraInfo()); + assertTrue(v.isGAECompliant().booleanValue()); + assertEquals(new Date(5000), v.getBuildDate()); + } + + @Test + public void testFromString3() { + Version v = new Version("01.002.0003-20130524"); + assertEquals("01.002.0003-20130524", v.toString()); + assertEquals(1, v.getMajor()); + assertEquals(2, v.getMinor()); + assertEquals(3, v.getMicro()); + assertEquals("20130524", v.getExtraInfo()); + + v = new Version("01.002.0003.4"); + assertEquals("01.002.0003.4", v.toString()); + assertEquals(1, v.getMajor()); + assertEquals(2, v.getMinor()); + assertEquals(3, v.getMicro()); + assertEquals("4", v.getExtraInfo()); + + v = new Version("1.2.3.FC"); + assertEquals("1.2.3.FC", v.toString()); + assertEquals("FC", v.getExtraInfo()); + + v = new Version("1.2.3mod"); + assertEquals("1.2.3mod", v.toString()); + assertEquals(1, v.getMajor()); + assertEquals(2, v.getMinor()); + assertEquals(3, v.getMicro()); + assertEquals("mod", v.getExtraInfo()); + + } + + @Test + public void testFromStringIncubating() { + Version v = new Version("2.3.24-rc01-incubating"); + assertEquals("2.3.24-rc01-incubating", v.toString()); + assertEquals(2, v.getMajor()); + assertEquals(3, v.getMinor()); + assertEquals(24, v.getMicro()); + assertEquals("rc01-incubating", v.getExtraInfo()); + } + + @Test + public void testHashAndEquals() { + Version v1 = new Version("1.2.3-beta2"); + Version v2 = new Version(1, 2, 3, "beta2", null, null); + assertEquals(v1, v2); + assertEquals(v1.hashCode(), v2.hashCode()); + + v2 = new Version("1.2.3-beta3"); + assertTrue(!v1.equals(v2)); + assertTrue(v1.hashCode() != v2.hashCode()); + + v2 = new Version(1, 2, 3, "beta2", true, null); + assertTrue(!v1.equals(v2)); + assertTrue(v1.hashCode() != v2.hashCode()); + + v2 = new Version(1, 2, 3, "beta2", null, new Date(5000)); + assertTrue(!v1.equals(v2)); + assertTrue(v1.hashCode() != v2.hashCode()); + + v2 = new Version("1.2.9-beta2"); + assertTrue(!v1.equals(v2)); + assertTrue(v1.hashCode() != v2.hashCode()); + + v2 = new Version("1.9.3-beta2"); + assertTrue(!v1.equals(v2)); + assertTrue(v1.hashCode() != v2.hashCode()); + + v2 = new Version("9.2.3-beta2"); + assertTrue(!v1.equals(v2)); + assertTrue(v1.hashCode() != v2.hashCode()); + + v2 = new Version("1.2.3"); + assertTrue(!v1.equals(v2)); + assertTrue(v1.hashCode() != v2.hashCode()); + } + + @Test + public void testShortForms() { + Version v = new Version("1.0.0-beta2"); + assertEquals(v, new Version("1.0-beta2")); + assertEquals(v, new Version("1-beta2")); + + v = new Version("1.0.0"); + assertEquals(v, new Version("1.0")); + assertEquals(v, new Version("1")); + } + + @Test + public void testMalformed() { + try { + new Version("1.2."); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + + try { + new Version("1.2.3."); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + + try { + new Version("1..3"); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + + try { + new Version(".2"); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + + try { + new Version("a"); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + + try { + new Version("-a"); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/WhitespaceStrippingTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/WhitespaceStrippingTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/WhitespaceStrippingTest.java new file mode 100644 index 0000000..8e5b52e --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/WhitespaceStrippingTest.java @@ -0,0 +1,63 @@ +/* + * 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; + +public class WhitespaceStrippingTest extends TemplateTest { + + @Test + public void testBasics() throws Exception { + assertOutput("<#assign x = 1>\n<#assign y = 2>\n${x}\n${y}", "1\n2", "\n\n1\n2"); + assertOutput(" <#assign x = 1> \n <#assign y = 2> \n${x}\n${y}", "1\n2", " \n \n1\n2"); + } + + @Test + public void testFTLHeader() throws Exception { + assertOutput("<#ftl>x", "x", "x"); + assertOutput(" <#ftl> x", " x", " x"); + assertOutput("\n<#ftl>\nx", "x", "x"); + assertOutput("\n<#ftl>\t \nx", "x", "x"); + assertOutput(" \n \n <#ftl> \n \n x", " \n x", " \n x"); + } + + @Test + public void testComment() throws Exception { + assertOutput(" a <#-- --> b ", " a b ", " a b "); + assertOutput(" a \n<#-- -->\n b ", " a \n b ", " a \n\n b "); + // These are wrong, but needed for 2.3.0 compatibility: + assertOutput(" a \n <#-- --> \n b ", " a \n b ", " a \n \n b "); + assertOutput(" a \n\t<#-- --> \n b ", " a \n\t b ", " a \n\t \n b "); + } + + private void assertOutput(String ftl, String expectedOutStripped, String expectedOutNonStripped) + throws IOException, TemplateException { + setConfiguration(new TestConfigurationBuilder().build()); + assertOutput(ftl, expectedOutStripped); + + setConfiguration(new TestConfigurationBuilder().whitespaceStripping(false).build()); + assertOutput(ftl, expectedOutNonStripped); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/XHTMLOutputFormatTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/XHTMLOutputFormatTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/XHTMLOutputFormatTest.java new file mode 100644 index 0000000..77951ed --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/XHTMLOutputFormatTest.java @@ -0,0 +1,59 @@ +/* + * 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.outputformat.impl.XHTMLOutputFormat.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.StringWriter; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.junit.Test; + +public class XHTMLOutputFormatTest { + + @Test + public void testOutputMO() throws TemplateModelException, IOException { + StringWriter out = new StringWriter(); + INSTANCE.output(INSTANCE.fromPlainTextByEscaping("a'b"), out); + assertEquals("a'b", out.toString()); + } + + @Test + public void testOutputString() throws TemplateModelException, IOException { + StringWriter out = new StringWriter(); + INSTANCE.output("a'b", out); + assertEquals("a'b", out.toString()); + } + + @Test + public void testEscaplePlainText() { + assertEquals("", INSTANCE.escapePlainText("")); + assertEquals("a", INSTANCE.escapePlainText("a")); + assertEquals("<a&b'c"d>", INSTANCE.escapePlainText("<a&b'c\"d>")); + assertEquals("<>", INSTANCE.escapePlainText("<>")); + } + + @Test + public void testGetMimeType() { + assertEquals("application/xhtml+xml", INSTANCE.getMimeType()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/XMLOutputFormatTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/XMLOutputFormatTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/XMLOutputFormatTest.java new file mode 100644 index 0000000..7e95e6d --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/XMLOutputFormatTest.java @@ -0,0 +1,59 @@ +/* + * 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.outputformat.impl.XMLOutputFormat.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.StringWriter; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.junit.Test; + +public class XMLOutputFormatTest { + + @Test + public void testOutputMO() throws TemplateModelException, IOException { + StringWriter out = new StringWriter(); + INSTANCE.output(INSTANCE.fromPlainTextByEscaping("a'b"), out); + assertEquals("a'b", out.toString()); + } + + @Test + public void testOutputString() throws TemplateModelException, IOException { + StringWriter out = new StringWriter(); + INSTANCE.output("a'b", out); + assertEquals("a'b", out.toString()); + } + + @Test + public void testEscaplePlainText() { + assertEquals("", INSTANCE.escapePlainText("")); + assertEquals("a", INSTANCE.escapePlainText("a")); + assertEquals("<a&b'c"d>", INSTANCE.escapePlainText("<a&b'c\"d>")); + assertEquals("<>", INSTANCE.escapePlainText("<>")); + } + + @Test + public void testGetMimeType() { + assertEquals("application/xml", INSTANCE.getMimeType()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java new file mode 100644 index 0000000..bdb9a56 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.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.model.impl; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.model.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateNumberModel; + +import junit.framework.TestCase; + +public abstract class AbstractParallelIntrospectionTest extends TestCase { + + private static final int NUM_THREADS = 8; + private static final int NUM_ENTITYES = 8; + private static final int NUM_MEMBERS = 8; + private static final int ITERATIONS = 20000; + private static final double CACHE_CLEARING_CHANCE = 0.01; + + private DefaultObjectWrapper ow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0) + .usePrivateCaches(true).build(); + + public AbstractParallelIntrospectionTest(String name) { + super(name); + } + + public void testReliability() { + testReliability(ITERATIONS); + } + + public void testReliability(int iterations) { + TestThread[] ts = new TestThread[NUM_THREADS]; + for (int i = 0; i < NUM_THREADS; i++) { + ts[i] = new TestThread(iterations); + ts[i].start(); + } + + for (int i = 0; i < NUM_THREADS; i++) { + try { + ts[i].join(); + if (ts[i].error != null) { + throw new AssertionError(ts[i].error); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + protected abstract TemplateHashModel getWrappedEntity(int objIdx) throws TemplateModelException; + + protected final DefaultObjectWrapper getObjectWrapper() { + return ow; + } + + private class TestThread extends Thread { + + private final int iterations; + + private Throwable error; + + private TestThread(int iterations) { + this.iterations = iterations; + } + + @Override + public void run() { + try { + for (int i = 0; i < iterations; i++) { + if (Math.random() < CACHE_CLEARING_CHANCE) { + ow.clearClassIntrospecitonCache(); + } + int objIdx = (int) (Math.random() * NUM_ENTITYES); + TemplateHashModel h = getWrappedEntity(objIdx); + int mIdx = (int) (Math.random() * NUM_MEMBERS); + testProperty(h, objIdx, mIdx); + testMethod(h, objIdx, mIdx); + } + } catch (Throwable e) { + error = e; + } + } + + private void testProperty(TemplateHashModel h, int objIdx, int mIdx) + throws TemplateModelException, AssertionError { + TemplateNumberModel pv = (TemplateNumberModel) h.get("p" + mIdx); + final int expected = objIdx * 1000 + mIdx; + final int got = pv.getAsNumber().intValue(); + if (got != expected) { + throw new AssertionError("Property assertation failed; " + + "expected " + expected + ", but got " + got); + } + } + + private void testMethod(TemplateHashModel h, int objIdx, int mIdx) + throws TemplateModelException, AssertionError { + TemplateMethodModel pv = (TemplateMethodModel) h.get("m" + mIdx); + final int expected = objIdx * 1000 + mIdx; + final int got = ((TemplateNumberModel) pv.exec(null)).getAsNumber().intValue(); + if (got != expected) { + throw new AssertionError("Method assertation failed; " + + "expected " + expected + ", but got " + got); + } + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AlphabeticalMethodSorter.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AlphabeticalMethodSorter.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AlphabeticalMethodSorter.java new file mode 100644 index 0000000..149ad0d --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AlphabeticalMethodSorter.java @@ -0,0 +1,45 @@ +/* + * 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.model.impl; + +import java.beans.MethodDescriptor; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +class AlphabeticalMethodSorter implements MethodSorter { + + private final boolean desc; + + public AlphabeticalMethodSorter(boolean desc) { + this.desc = desc; + } + + @Override + public void sortMethodDescriptors(List<MethodDescriptor> methodDescriptors) { + Collections.sort(methodDescriptors, new Comparator<MethodDescriptor>() { + public int compare(MethodDescriptor o1, MethodDescriptor o2) { + int res = o1.getMethod().toString().compareTo(o2.getMethod().toString()); + return desc ? -res : res; + } + }); + } + +} \ No newline at end of file
