You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2021/11/06 23:29:39 UTC

[freemarker] branch FREEMARKER-35 updated: [FREEMARKER-35] Extended customTemporalFormat test coverage.

This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch FREEMARKER-35
in repository https://gitbox.apache.org/repos/asf/freemarker.git


The following commit(s) were added to refs/heads/FREEMARKER-35 by this push:
     new 3b24b1b  [FREEMARKER-35] Extended customTemporalFormat test coverage.
3b24b1b is described below

commit 3b24b1be33ecb31fb815efeb7ca3a593b27231a9
Author: ddekany <dd...@apache.org>
AuthorDate: Sun Nov 7 00:29:08 2021 +0100

    [FREEMARKER-35] Extended customTemporalFormat test coverage.
---
 .../freemarker/template/utility/ClassUtil.java     |   7 +-
 .../freemarker/core/CoercionToTextualTest.java     |  31 ++++--
 ...dTZSensitiveTemplateTemporalFormatFactory.java} |   7 +-
 .../freemarker/core/TemplateConfigurationTest.java |  13 +++
 .../java/freemarker/core/TemporalFormatTest2.java  |   2 +-
 .../freemarker/template/ConfigurationTest.java     | 113 ++++++++++++++++++++-
 6 files changed, 160 insertions(+), 13 deletions(-)

diff --git a/src/main/java/freemarker/template/utility/ClassUtil.java b/src/main/java/freemarker/template/utility/ClassUtil.java
index 7fc4046..3fa4aa4 100644
--- a/src/main/java/freemarker/template/utility/ClassUtil.java
+++ b/src/main/java/freemarker/template/utility/ClassUtil.java
@@ -62,6 +62,7 @@ import freemarker.template.TemplateNodeModelEx;
 import freemarker.template.TemplateNumberModel;
 import freemarker.template.TemplateScalarModel;
 import freemarker.template.TemplateSequenceModel;
+import freemarker.template.TemplateTemporalModel;
 import freemarker.template.TemplateTransformModel;
 
 /**
@@ -274,7 +275,11 @@ public class ClassUtil {
         if (TemplateDateModel.class.isAssignableFrom(cl)) {
             appendTypeName(sb, typeNamesAppended, "date_or_time_or_datetime");
         }
-        
+
+        if (TemplateTemporalModel.class.isAssignableFrom(cl)) {
+            appendTypeName(sb, typeNamesAppended, "temporal");
+        }
+
         if (TemplateBooleanModel.class.isAssignableFrom(cl)) {
             appendTypeName(sb, typeNamesAppended, "boolean");
         }
diff --git a/src/test/java/freemarker/core/CoercionToTextualTest.java b/src/test/java/freemarker/core/CoercionToTextualTest.java
index 3525c50..efa015a 100644
--- a/src/test/java/freemarker/core/CoercionToTextualTest.java
+++ b/src/test/java/freemarker/core/CoercionToTextualTest.java
@@ -19,6 +19,7 @@
 package freemarker.core;
 
 import java.io.IOException;
+import java.time.Instant;
 import java.util.Collections;
 import java.util.Date;
 
@@ -27,25 +28,25 @@ import org.junit.Test;
 
 import freemarker.template.Configuration;
 import freemarker.template.SimpleDate;
+import freemarker.template.SimpleTemporal;
 import freemarker.template.TemplateDateModel;
 import freemarker.template.TemplateException;
 import freemarker.template.TemplateModelException;
+import freemarker.template.utility.DateUtil;
 import freemarker.test.TemplateTest;
 
 @SuppressWarnings("boxing")
 public class CoercionToTextualTest extends TemplateTest {
-    
-    /** 2015-09-06T12:00:00Z */
-    private static long T = 1441540800000L;
-    private static TemplateDateModel TM = new SimpleDate(new Date(T), TemplateDateModel.DATETIME);
-    
+
     @Test
     public void testBasicStringBuiltins() throws IOException, TemplateException {
         assertOutput("${s?upperCase}", "ABC");
         assertOutput("${n?string?lowerCase}", "1.50e+03");
         assertErrorContains("${n?lowerCase}", "convert", "string", "markup", "text/html");
         assertOutput("${dt?string?lowerCase}", "2015-09-06t12:00:00z");
+        assertOutput("${ti?string?lowerCase}", "2015-09-06t12:00:00z");
         assertErrorContains("${dt?lowerCase}", "convert", "string", "markup", "text/html");
+        assertErrorContains("${ti?lowerCase}", "convert", "string", "markup", "text/html");
         assertOutput("${b?upperCase}", "Y");
         assertErrorContains("${m?upperCase}", "convertible to string", "HTMLOutputModel");
     }
@@ -62,6 +63,8 @@ public class CoercionToTextualTest extends TemplateTest {
         assertOutput("${n?esc}", "1.50*10<sup>3</sup>");
         assertOutput("${dt?string?esc}", "2015-09-06T12:00:00Z");
         assertOutput("${dt?esc}", "2015-09-06<span class='T'>T</span>12:00:00Z");
+        assertOutput("${ti?string?esc}", "2015-09-06T12:00:00Z");
+        assertOutput("${ti?esc}", "2015-09-06<span class='T'>T</span>12:00:00Z");
         assertOutput("${b?esc}", "&lt;y&gt;");
         assertOutput("${m?esc}", "<p>M</p>");
     }
@@ -74,6 +77,8 @@ public class CoercionToTextualTest extends TemplateTest {
         assertErrorContains("${n?indexOf('E')}", "convert", "string", "markup", "text/html");
         assertOutput("${dt?string?contains('0')}", "y");
         assertErrorContains("${dt?contains('0')}", "convert", "string", "markup", "text/html");
+        assertOutput("${ti?string?contains('0')}", "y");
+        assertErrorContains("${ti?contains('0')}", "convert", "string", "markup", "text/html");
         assertErrorContains("${m?contains('0')}", "convertible to string", "HTMLOutputModel");
         assertErrorContains("${m?indexOf('0')}", "convertible to string", "HTMLOutputModel");
     }
@@ -83,6 +88,7 @@ public class CoercionToTextualTest extends TemplateTest {
         assertErrorContains("${n?string?markupString}", "Expected", "markup", "string");
         assertErrorContains("${n?markupString}", "Expected", "markup", "number");
         assertErrorContains("${dt?markupString}", "Expected", "markup", "date");
+        assertErrorContains("${ti?markupString}", "Expected", "markup", "temporal");
     }
     
     @Test
@@ -92,6 +98,8 @@ public class CoercionToTextualTest extends TemplateTest {
         assertOutput("${n}", "1.50*10<sup>3</sup>");
         assertOutput("${dt?string}", "2015-09-06T12:00:00Z");
         assertOutput("${dt}", "2015-09-06<span class='T'>T</span>12:00:00Z");
+        assertOutput("${ti?string}", "2015-09-06T12:00:00Z");
+        assertOutput("${ti}", "2015-09-06<span class='T'>T</span>12:00:00Z");
         assertOutput("${b}", "y");
         assertOutput("${m}", "<p>M</p>");
     }
@@ -103,6 +111,8 @@ public class CoercionToTextualTest extends TemplateTest {
         assertOutput("${n + '&'}", "1.50*10<sup>3</sup>&amp;");
         assertOutput("${dt?string + '&'}", "2015-09-06T12:00:00Z&");
         assertOutput("${dt + '&'}", "2015-09-06<span class='T'>T</span>12:00:00Z&amp;");
+        assertOutput("${ti?string + '&'}", "2015-09-06T12:00:00Z&");
+        assertOutput("${ti + '&'}", "2015-09-06<span class='T'>T</span>12:00:00Z&amp;");
         assertOutput("${b + '&'}", "y&");
         assertOutput("${m + '&'}", "<p>M</p>&amp;");
     }
@@ -114,6 +124,8 @@ public class CoercionToTextualTest extends TemplateTest {
         assertOutput("${'&' + n}", "&amp;1.50*10<sup>3</sup>");
         assertOutput("${'&' + dt?string}", "&2015-09-06T12:00:00Z");
         assertOutput("${'&' + dt}", "&amp;2015-09-06<span class='T'>T</span>12:00:00Z");
+        assertOutput("${'&' + ti?string}", "&2015-09-06T12:00:00Z");
+        assertOutput("${'&' + ti}", "&amp;2015-09-06<span class='T'>T</span>12:00:00Z");
         assertOutput("${'&' + b}", "&y");
         assertOutput("${'&' + m}", "&amp;<p>M</p>");
     }
@@ -123,13 +135,18 @@ public class CoercionToTextualTest extends TemplateTest {
         Configuration cfg = getConfiguration();
         cfg.setCustomNumberFormats(Collections.singletonMap("G", PrintfGTemplateNumberFormatFactory.INSTANCE));
         cfg.setCustomDateFormats(Collections.singletonMap("HI", HTMLISOTemplateDateFormatFactory.INSTANCE));
+        cfg.setCustomTemporalFormats(Collections.singletonMap("HI", HTMLISOTemplateTemporalFormatFactory.INSTANCE));
         cfg.setNumberFormat("@G 3");
         cfg.setDateTimeFormat("@HI");
+        cfg.setInstantFormat("@HI");
         cfg.setBooleanFormat("y,n");
-        
+        cfg.setTimeZone(DateUtil.UTC);
+
         addToDataModel("s", "abc");
         addToDataModel("n", 1500);
-        addToDataModel("dt", TM);
+        long epochMillis = 1441540800000L; // 2015-09-06T12:00:00Z
+        addToDataModel("dt", new SimpleDate(new Date(epochMillis), TemplateDateModel.DATETIME));
+        addToDataModel("ti", new SimpleTemporal(Instant.ofEpochMilli(epochMillis)));
         addToDataModel("b", Boolean.TRUE);
         addToDataModel("m", HTMLOutputFormat.INSTANCE.fromMarkup("<p>M</p>"));
     }
diff --git a/src/test/java/freemarker/core/LocAndTZSensitiveTemporalFormatFactory.java b/src/test/java/freemarker/core/LocAndTZSensitiveTemplateTemporalFormatFactory.java
similarity index 90%
rename from src/test/java/freemarker/core/LocAndTZSensitiveTemporalFormatFactory.java
rename to src/test/java/freemarker/core/LocAndTZSensitiveTemplateTemporalFormatFactory.java
index 4b7b46a..446f3c3 100644
--- a/src/test/java/freemarker/core/LocAndTZSensitiveTemporalFormatFactory.java
+++ b/src/test/java/freemarker/core/LocAndTZSensitiveTemplateTemporalFormatFactory.java
@@ -26,11 +26,12 @@ import java.util.TimeZone;
 import freemarker.template.TemplateModelException;
 import freemarker.template.TemplateTemporalModel;
 
-public class LocAndTZSensitiveTemporalFormatFactory extends TemplateTemporalFormatFactory {
+public class LocAndTZSensitiveTemplateTemporalFormatFactory extends TemplateTemporalFormatFactory {
 
-    public static final LocAndTZSensitiveTemporalFormatFactory INSTANCE = new LocAndTZSensitiveTemporalFormatFactory();
+    public static final LocAndTZSensitiveTemplateTemporalFormatFactory
+            INSTANCE = new LocAndTZSensitiveTemplateTemporalFormatFactory();
     
-    private LocAndTZSensitiveTemporalFormatFactory() {
+    private LocAndTZSensitiveTemplateTemporalFormatFactory() {
         // Defined to decrease visibility
     }
     
diff --git a/src/test/java/freemarker/core/TemplateConfigurationTest.java b/src/test/java/freemarker/core/TemplateConfigurationTest.java
index c5aa08b..dc1dba1 100644
--- a/src/test/java/freemarker/core/TemplateConfigurationTest.java
+++ b/src/test/java/freemarker/core/TemplateConfigurationTest.java
@@ -343,6 +343,9 @@ public class TemplateConfigurationTest {
         tc1.setCustomDateFormats(ImmutableMap.of(
                 "epoch", EpochMillisTemplateDateFormatFactory.INSTANCE,
                 "x", LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE));
+        tc1.setCustomTemporalFormats(ImmutableMap.of(
+                "epoch", EpochMillisTemplateTemporalFormatFactory.INSTANCE,
+                "x", LocAndTZSensitiveTemplateTemporalFormatFactory.INSTANCE));
         tc1.setCustomNumberFormats(ImmutableMap.of(
                 "hex", HexTemplateNumberFormatFactory.INSTANCE,
                 "x", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE));
@@ -352,6 +355,9 @@ public class TemplateConfigurationTest {
         tc2.setCustomDateFormats(ImmutableMap.of(
                 "loc", LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE,
                 "x", EpochMillisDivTemplateDateFormatFactory.INSTANCE));
+        tc2.setCustomTemporalFormats(ImmutableMap.of(
+                "loc", LocAndTZSensitiveTemplateTemporalFormatFactory.INSTANCE,
+                "x", EpochMillisDivTemplateTemporalFormatFactory.INSTANCE));
         tc2.setCustomNumberFormats(ImmutableMap.of(
                 "loc", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE,
                 "x", BaseNTemplateNumberFormatFactory.INSTANCE));
@@ -364,6 +370,11 @@ public class TemplateConfigurationTest {
         assertEquals(LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("loc"));
         assertEquals(EpochMillisDivTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("x"));
         
+        Map<String, ? extends TemplateTemporalFormatFactory> mergedCustomTemporalFormats = tc1.getCustomTemporalFormats();
+        assertEquals(EpochMillisTemplateTemporalFormatFactory.INSTANCE, mergedCustomTemporalFormats.get("epoch"));
+        assertEquals(LocAndTZSensitiveTemplateTemporalFormatFactory.INSTANCE, mergedCustomTemporalFormats.get("loc"));
+        assertEquals(EpochMillisDivTemplateTemporalFormatFactory.INSTANCE, mergedCustomTemporalFormats.get("x"));
+        
         Map<String, ? extends TemplateNumberFormatFactory> mergedCustomNumberFormats = tc1.getCustomNumberFormats();
         assertEquals(HexTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("hex"));
         assertEquals(LocaleSensitiveTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("loc"));
@@ -377,12 +388,14 @@ public class TemplateConfigurationTest {
         // Empty map merging optimization:
         tc1.merge(new TemplateConfiguration());
         assertSame(mergedCustomDateFormats, tc1.getCustomDateFormats());
+        assertSame(mergedCustomTemporalFormats, tc1.getCustomTemporalFormats());
         assertSame(mergedCustomNumberFormats, tc1.getCustomNumberFormats());
         
         // Empty map merging optimization:
         TemplateConfiguration tc3 = new TemplateConfiguration();
         tc3.merge(tc1);
         assertSame(mergedCustomDateFormats, tc3.getCustomDateFormats());
+        assertSame(mergedCustomTemporalFormats, tc3.getCustomTemporalFormats());
         assertSame(mergedCustomNumberFormats, tc3.getCustomNumberFormats());
     }
     
diff --git a/src/test/java/freemarker/core/TemporalFormatTest2.java b/src/test/java/freemarker/core/TemporalFormatTest2.java
index 215eb7c..c519c68 100644
--- a/src/test/java/freemarker/core/TemporalFormatTest2.java
+++ b/src/test/java/freemarker/core/TemporalFormatTest2.java
@@ -46,7 +46,7 @@ public class TemporalFormatTest2 extends TemplateTest {
 
         cfg.setCustomTemporalFormats(ImmutableMap.of(
                 "epoch", EpochMillisTemplateTemporalFormatFactory.INSTANCE,
-                "loc", LocAndTZSensitiveTemporalFormatFactory.INSTANCE,
+                "loc", LocAndTZSensitiveTemplateTemporalFormatFactory.INSTANCE,
                 "div", EpochMillisDivTemplateTemporalFormatFactory.INSTANCE,
                 "htmlIso", HTMLISOTemplateTemporalFormatFactory.INSTANCE));
     }
diff --git a/src/test/java/freemarker/template/ConfigurationTest.java b/src/test/java/freemarker/template/ConfigurationTest.java
index 9bf0609..a377922 100644
--- a/src/test/java/freemarker/template/ConfigurationTest.java
+++ b/src/test/java/freemarker/template/ConfigurationTest.java
@@ -64,7 +64,9 @@ import freemarker.core.DefaultTruncateBuiltinAlgorithm;
 import freemarker.core.DummyOutputFormat;
 import freemarker.core.Environment;
 import freemarker.core.EpochMillisDivTemplateDateFormatFactory;
+import freemarker.core.EpochMillisDivTemplateTemporalFormatFactory;
 import freemarker.core.EpochMillisTemplateDateFormatFactory;
+import freemarker.core.EpochMillisTemplateTemporalFormatFactory;
 import freemarker.core.HTMLOutputFormat;
 import freemarker.core.HexTemplateNumberFormatFactory;
 import freemarker.core.MarkupOutputFormat;
@@ -75,6 +77,7 @@ import freemarker.core.RTFOutputFormat;
 import freemarker.core.TemplateClassResolver;
 import freemarker.core.TemplateDateFormatFactory;
 import freemarker.core.TemplateNumberFormatFactory;
+import freemarker.core.TemplateTemporalFormatFactory;
 import freemarker.core.UndefinedOutputFormat;
 import freemarker.core.UnregisteredOutputFormatException;
 import freemarker.core.XHTMLOutputFormat;
@@ -1566,6 +1569,78 @@ public class ConfigurationTest extends TestCase {
                     containsString(TemplateDateFormatFactory.class.getName())));
         }
     }
+    @SuppressFBWarnings(value="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS", justification="We test failures")
+    @Test
+    public void testSetCustomTemporalFormat() throws Exception {
+        Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
+        
+        try {
+            cfg.setCustomTemporalFormats(null);
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), containsString("null"));
+        }
+        
+        try {
+            cfg.setCustomTemporalFormats(Collections.singletonMap("", EpochMillisTemplateTemporalFormatFactory.INSTANCE));
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), containsString("0 length"));
+        }
+
+        try {
+            cfg.setCustomTemporalFormats(Collections.singletonMap("a_b", EpochMillisTemplateTemporalFormatFactory.INSTANCE));
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), containsString("a_b"));
+        }
+
+        try {
+            cfg.setCustomTemporalFormats(Collections.singletonMap("a b", EpochMillisTemplateTemporalFormatFactory.INSTANCE));
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), containsString("a b"));
+        }
+        
+        try {
+            cfg.setCustomTemporalFormats(ImmutableMap.of(
+                    "a", EpochMillisTemplateTemporalFormatFactory.INSTANCE,
+                    "@wrong", EpochMillisTemplateTemporalFormatFactory.INSTANCE));
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), containsString("@wrong"));
+        }
+        
+        cfg.setSetting(Configurable.CUSTOM_TEMPORAL_FORMATS_KEY_CAMEL_CASE,
+                "{ 'epoch': " + EpochMillisTemplateTemporalFormatFactory.class.getName() + "() }");
+        assertEquals(
+                Collections.singletonMap("epoch", EpochMillisTemplateTemporalFormatFactory.INSTANCE),
+                cfg.getCustomTemporalFormats());
+        
+        cfg.setSetting(Configurable.CUSTOM_TEMPORAL_FORMATS_KEY_SNAKE_CASE,
+                "{ "
+                + "'epoch': " + EpochMillisTemplateTemporalFormatFactory.class.getName() + "(), "
+                + "'epochDiv': " + EpochMillisDivTemplateTemporalFormatFactory.class.getName() + "()"
+                + " }");
+        assertEquals(
+                ImmutableMap.of(
+                        "epoch", EpochMillisTemplateTemporalFormatFactory.INSTANCE,
+                        "epochDiv", EpochMillisDivTemplateTemporalFormatFactory.INSTANCE),
+                cfg.getCustomTemporalFormats());
+        
+        cfg.setSetting(Configurable.CUSTOM_TEMPORAL_FORMATS_KEY, "{}");
+        assertEquals(Collections.emptyMap(), cfg.getCustomTemporalFormats());
+        
+        try {
+            cfg.setSetting(Configurable.CUSTOM_TEMPORAL_FORMATS_KEY_CAMEL_CASE,
+                    "{ 'x': " + HexTemplateNumberFormatFactory.class.getName() + "() }");
+            fail();
+        } catch (TemplateException e) {
+            assertThat(e.getCause().getMessage(), allOf(
+                    containsString(HexTemplateNumberFormatFactory.class.getName()),
+                    containsString(TemplateTemporalFormatFactory.class.getName())));
+        }
+    }
     
     @Test
     public void testHasCustomFormats() throws IOException, TemplateException {
@@ -1605,7 +1680,43 @@ public class ConfigurationTest extends TestCase {
         assertFalse(cfg.hasCustomFormats());
         assertFalse(t.hasCustomFormats());
         assertFalse(env.hasCustomFormats());
-        
+
+        // Same with temporal formats:
+
+        assertFalse(cfg.hasCustomFormats());
+        assertFalse(t.hasCustomFormats());
+        assertFalse(env.hasCustomFormats());
+
+        env.setCustomTemporalFormats(Collections.singletonMap("f", EpochMillisTemplateTemporalFormatFactory.INSTANCE));
+        assertFalse(t.hasCustomFormats());
+        assertTrue(env.hasCustomFormats());
+        t.setCustomTemporalFormats(Collections.singletonMap("f", EpochMillisTemplateTemporalFormatFactory.INSTANCE));
+        assertFalse(cfg.hasCustomFormats());
+        assertTrue(t.hasCustomFormats());
+        cfg.setCustomTemporalFormats(Collections.singletonMap("f", EpochMillisTemplateTemporalFormatFactory.INSTANCE));
+        assertTrue(cfg.hasCustomFormats());
+        assertTrue(t.hasCustomFormats());
+        assertTrue(env.hasCustomFormats());
+
+        cfg.setCustomTemporalFormats(Collections.emptyMap());
+        t.setCustomTemporalFormats(Collections.emptyMap());
+        env.setCustomTemporalFormats(Collections.emptyMap());
+        assertFalse(cfg.hasCustomFormats());
+        assertFalse(t.hasCustomFormats());
+        assertFalse(env.hasCustomFormats());
+
+        cfg.setCustomTemporalFormats(Collections.singletonMap("f", EpochMillisTemplateTemporalFormatFactory.INSTANCE));
+        assertTrue(cfg.hasCustomFormats());
+        assertTrue(t.hasCustomFormats());
+        assertTrue(env.hasCustomFormats());
+
+        cfg.setCustomTemporalFormats(Collections.emptyMap());
+        t.setCustomTemporalFormats(Collections.emptyMap());
+        env.setCustomTemporalFormats(Collections.emptyMap());
+        assertFalse(cfg.hasCustomFormats());
+        assertFalse(t.hasCustomFormats());
+        assertFalse(env.hasCustomFormats());
+
         // Same with number formats:
         
         env.setCustomNumberFormats(Collections.singletonMap("f", HexTemplateNumberFormatFactory.INSTANCE));