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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 22d053a200 ISIS-3049: value-semantics: do parser round-trip tests 
based on value-semantics' provided examples
22d053a200 is described below

commit 22d053a200df900c03f914dcaac4095f00f1b526
Author: Andi Huber <[email protected]>
AuthorDate: Thu Jul 14 08:15:26 2022 +0200

    ISIS-3049: value-semantics: do parser round-trip tests based on
    value-semantics' provided examples
    
    - allows for multiple example values to be tested per value-type
    
    - fixes all offset time example values to be independent of the current
    systems time-zone
---
 .../value/semantics/TemporalValueSemantics.java    | 25 +++++++-
 .../temporal/OffsetDateTimeValueSemantics.java     | 12 +++-
 .../temporal/OffsetTimeValueSemantics.java         | 12 +++-
 .../temporal/ZonedDateTimeValueSemantics.java      | 12 +++-
 .../isis/testdomain/value/ValueSemanticsTest.java  | 66 ++++++++++++----------
 .../model/valuetypes/ValueTypeExample.java         | 27 ++++++++-
 6 files changed, 114 insertions(+), 40 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/TemporalValueSemantics.java
 
b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/TemporalValueSemantics.java
index 98f9da39b9..09111e8513 100644
--- 
a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/TemporalValueSemantics.java
+++ 
b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/TemporalValueSemantics.java
@@ -113,6 +113,9 @@ extends
          * Yielding {@link TimeFormatPrecision#NANO_SECOND}.
          * <p>
          * Any missing temporal parts are filled up with zeros to meet the 
{@link TimeFormatPrecision}.
+         *
+         * @apiNote Supports various input forms as denoted by optional blocks 
(square brackets).
+         * The output format is inferred by removal of the square brackets 
(not their content).
          */
         @NotNull @NotEmpty
         private String timePatternNanoSecond = "HH[:mm[:ss][.SSSSSSSSS]]";
@@ -122,6 +125,9 @@ extends
          * Yielding {@link TimeFormatPrecision#MICRO_SECOND}.
          * <p>
          * Any missing temporal parts are filled up with zeros to meet the 
{@link TimeFormatPrecision}.
+         *
+         * @apiNote Supports various input forms as denoted by optional blocks 
(square brackets).
+         * The output format is inferred by removal of the square brackets 
(not their content).
          */
         @NotNull @NotEmpty
         private String timePatternMicroSecond = "HH[:mm[:ss][.SSSSSS]]";
@@ -131,6 +137,9 @@ extends
          * Yielding {@link TimeFormatPrecision#MILLI_SECOND}.
          * <p>
          * Any missing temporal parts are filled up with zeros to meet the 
{@link TimeFormatPrecision}.
+         *
+         * @apiNote Supports various input forms as denoted by optional blocks 
(square brackets).
+         * The output format is inferred by removal of the square brackets 
(not their content).
          */
         @NotNull @NotEmpty
         private String timePatternMilliSecond = "HH[:mm[:ss][.SSS]]";
@@ -140,6 +149,9 @@ extends
          * Yielding {@link TimeFormatPrecision#SECOND}.
          * <p>
          * Any missing temporal parts are filled up with zeros to meet the 
{@link TimeFormatPrecision}.
+         *
+         * @apiNote Supports various input forms as denoted by optional blocks 
(square brackets).
+         * The output format is inferred by removal of the square brackets 
(not their content).
          */
         @NotNull @NotEmpty
         private String timePatternSecond = "HH[:mm[:ss]]";
@@ -149,6 +161,9 @@ extends
          * Yielding {@link TimeFormatPrecision#MINUTE}.
          * <p>
          * Any missing temporal parts are filled up with zeros to meet the 
{@link TimeFormatPrecision}.
+         *
+         * @apiNote Supports various input forms as denoted by optional blocks 
(square brackets).
+         * The output format is inferred by removal of the square brackets 
(not their content).
          */
         @NotNull @NotEmpty
         private String timePatternMinute = "HH[:mm]";
@@ -158,6 +173,9 @@ extends
          * Yielding {@link TimeFormatPrecision#HOUR}.
          * <p>
          * Any missing temporal parts are filled up with zeros to meet the 
{@link TimeFormatPrecision}.
+         *
+         * @apiNote Supports various input forms as denoted by optional blocks 
(square brackets).
+         * The output format is inferred by removal of the square brackets 
(not their content).
          */
         @NotNull @NotEmpty
         private String timePatternHour = "HH";
@@ -177,15 +195,18 @@ extends
          *</pre>
          *
          * @apiNote Yet only tested with {@literal XXX}, as there needs to be 
a format correspondence with
-         * <i>momentJs</i> supported formats for the <i>tempus-dominus</i> 
date/time-picker to work
+         * <i>momentJs</i> for the <i>tempus-dominus</i> date/time-picker to 
work
          * (as used by the <i>Wicket Viewer</i>).
-         * {@link 
org.apache.isis.viewer.wicket.ui.components.scalars.datepicker._TimeFormatUtil} 
does the format conversion.
+         * {@link 
org.apache.isis.viewer.wicket.ui.components.scalars.datepicker._TimeFormatUtil}
+         * does the format conversion.
          */
         @NotNull @NotEmpty
         private String zonePatternForOutput = "XXX";
 
         /**
          * Support both forms for parsing, with or without colon.
+         * <p>
+         * (Order of optional blocks matter, eg. {@literal [X][XXX]} would not 
work.)
          * @see 
"https://stackoverflow.com/questions/34637626/java-datetimeformatter-for-time-zone-with-an-optional-colon-separator";
          */
         @NotNull @NotEmpty
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/OffsetDateTimeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/OffsetDateTimeValueSemantics.java
index fd220548cc..1ae6929f0f 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/OffsetDateTimeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/OffsetDateTimeValueSemantics.java
@@ -19,7 +19,9 @@
 package org.apache.isis.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
+import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
 
 import javax.inject.Named;
 
@@ -28,6 +30,8 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.schema.common.v2.ValueType;
 
+import lombok.val;
+
 @Component
 @Named("isis.val.OffsetDateTimeValueSemantics")
 //@Log4j2
@@ -63,9 +67,13 @@ extends TemporalValueSemanticsProvider<OffsetDateTime> {
 
     @Override
     public Can<OffsetDateTime> getExamples() {
+        // don't depend on current TimeZone.getDefault(),
+        // instead use an arbitrary mix of fixed time-zone offsets Z, +02:00 
and -02:00
+        val localNow = LocalDateTime.now();
         return Can.of(
-                OffsetDateTime.now(),
-                OffsetDateTime.now().plusDays(2).plusSeconds(15));
+                OffsetDateTime.of(localNow, ZoneOffset.UTC),
+                OffsetDateTime.of(localNow, ZoneOffset.ofHours(2)),
+                OffsetDateTime.of(localNow, 
ZoneOffset.ofHours(-2)).plusDays(2).plusSeconds(15));
     }
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/OffsetTimeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/OffsetTimeValueSemantics.java
index fcecaf31ac..799f879e6b 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/OffsetTimeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/OffsetTimeValueSemantics.java
@@ -19,7 +19,9 @@
 package org.apache.isis.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
+import java.time.LocalTime;
 import java.time.OffsetTime;
+import java.time.ZoneOffset;
 
 import javax.inject.Named;
 
@@ -28,6 +30,8 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.schema.common.v2.ValueType;
 
+import lombok.val;
+
 @Component
 @Named("isis.val.OffsetTimeValueSemantics")
 //@Log4j2
@@ -63,9 +67,13 @@ extends TemporalValueSemanticsProvider<OffsetTime> {
 
     @Override
     public Can<OffsetTime> getExamples() {
+        // don't depend on current TimeZone.getDefault(),
+        // instead use an arbitrary mix of fixed time-zone offsets Z, +02:00 
and -02:00
+        val localNow = LocalTime.now();
         return Can.of(
-                OffsetTime.now(),
-                OffsetTime.now().plusSeconds(15));
+                OffsetTime.of(localNow, ZoneOffset.UTC),
+                OffsetTime.of(localNow, ZoneOffset.ofHours(2)),
+                OffsetTime.of(localNow, 
ZoneOffset.ofHours(-2)).plusSeconds(15));
     }
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/ZonedDateTimeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/ZonedDateTimeValueSemantics.java
index 12ba84b43d..0aad0261af 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/ZonedDateTimeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/temporal/ZonedDateTimeValueSemantics.java
@@ -19,6 +19,8 @@
 package org.apache.isis.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 
 import javax.inject.Named;
@@ -28,6 +30,8 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.schema.common.v2.ValueType;
 
+import lombok.val;
+
 @Component
 @Named("isis.val.ZonedDateTimeValueSemantics")
 //@Log4j2
@@ -63,9 +67,13 @@ extends TemporalValueSemanticsProvider<ZonedDateTime> {
 
     @Override
     public Can<ZonedDateTime> getExamples() {
+        // don't depend on current TimeZone.getDefault(),
+        // instead use an arbitrary mix of fixed time-zone offsets Z, +02:00 
and -02:00
+        val localNow = LocalDateTime.now();
         return Can.of(
-                ZonedDateTime.now(),
-                ZonedDateTime.now().plusDays(2).plusSeconds(15));
+                ZonedDateTime.of(localNow, ZoneOffset.UTC),
+                ZonedDateTime.of(localNow, ZoneOffset.ofHours(2)),
+                ZonedDateTime.of(localNow, 
ZoneOffset.ofHours(-2)).plusDays(2).plusSeconds(15));
     }
 
 }
diff --git 
a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
 
b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
index 9dd971fdd4..d7ad02045c 100644
--- 
a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
+++ 
b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
@@ -22,9 +22,7 @@ import java.time.OffsetDateTime;
 import java.time.OffsetTime;
 import java.time.ZonedDateTime;
 import java.util.Locale;
-import java.util.Objects;
 import java.util.Set;
-import java.util.TimeZone;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -96,6 +94,7 @@ import lombok.val;
 @TestInstance(Lifecycle.PER_CLASS)
 class ValueSemanticsTest {
 
+    /* debug
     static class TestEnvironment {
 
         private Locale systemLocale;
@@ -124,7 +123,7 @@ class ValueSemanticsTest {
         }
 
     }
-
+     */
 
     @Test
     void fullTypeCoverage() {
@@ -160,7 +159,7 @@ class ValueSemanticsTest {
             final Class<T> valueType,
             final ValueTypeExample<T> example) {
 
-        val env = new TestEnvironment();
+        //val env = new TestEnvironment();
 
         assertNotNull(example);
 
@@ -234,43 +233,43 @@ class ValueSemanticsTest {
                     public void testParser(
                             final ValueSemanticsProvider.Context context,
                             final Parser<T> parser) {
+
                         // Parser round-trip test
-                        val stringified = 
parser.parseableTextRepresentation(context, example.getValue());
+                        for(val value : example.getExamples()) {
 
-                        if(valueType.equals(Password.class)) {
-                            val recoveredValue = 
(Password)parser.parseTextRepresentation(context, stringified);
-                            
assertTrue(recoveredValue.checkPassword(PlaceholderLiteral.SUPPRESSED.getLiteral()));
+                            val stringified = 
parser.parseableTextRepresentation(context, value);
 
-                        } else {
+                            if(valueType.equals(Password.class)) {
+                                val recoveredValue = 
(Password)parser.parseTextRepresentation(context, stringified);
+                                
assertTrue(recoveredValue.checkPassword(PlaceholderLiteral.SUPPRESSED.getLiteral()));
 
-                            //debug
-                            //System.err.printf("using %s trying to parse 
'%s'%n", valueType.getName(), stringified);
+                            } else {
 
-                            assertValueEqualsParsedValue(context, parser, 
stringified, "parser roundtrip failed");
+                                assertValueEqualsParsedValue(context, parser, 
value, stringified, "parser roundtrip failed");
 
+                                if(valueType.equals(OffsetDateTime.class)
+                                        || valueType.equals(OffsetTime.class)
+                                        || 
valueType.equals(ZonedDateTime.class)) {
 
-                            if(valueType.equals(OffsetDateTime.class)
-                                    || valueType.equals(OffsetTime.class)
-                                    || valueType.equals(ZonedDateTime.class)) {
+                                    if(stringified.endsWith("Z")) {
+                                        // skip format variations on UTC 
time-zone
+                                        //System.err.printf("DEBUG: skipping 
stringified: %s%n", stringified);
+                                        return;
+                                    }
 
-                                if(!stringified.contains("+")) {
-                                    //TODO debug yeah thats because time-zone 
might be zero on CI run .. 'Z', but why?
-                                    System.err.printf("DEBUG: invalid 
stringified: %s%n", stringified);
-                                    return;
-                                }
+                                    val with4digitZone = 
_Strings.substring(stringified, 0, -3) + "00";
+                                    val with2digitZone = 
_Strings.substring(stringified, 0, -3);
 
-                                val with4digitZone = 
_Strings.substring(stringified, 0, -6) + "+0200";
-                                val with2digitZone = 
_Strings.substring(stringified, 0, -6) + "+02";
+                                    // test alternative time-zone format with 
4 digits +-HHmm
+                                    assertValueEqualsParsedValue(context, 
parser, value, with4digitZone, "parser roundtrip failed "
+                                            + "(alternative time-zone format 
with 4 digits +-HHmm)");
 
-                                // test alternative time-zone format with 4 
digits +-HHmm
-                                assertValueEqualsParsedValue(context, parser, 
with4digitZone, "parser roundtrip failed "
-                                        + "(alternative time-zone format with 
4 digits +-HHmm)");
+                                    // test alternative time-zone format with 
2 digits +-HH
+                                    assertValueEqualsParsedValue(context, 
parser, value, with2digitZone, "parser roundtrip failed "
+                                            + "(alternative time-zone format 
with 2 digits +-HH)");
+                                }
 
-                                // test alternative time-zone format with 2 
digits +-HH
-                                assertValueEqualsParsedValue(context, parser, 
with2digitZone, "parser roundtrip failed "
-                                        + "(alternative time-zone format with 
2 digits +-HH)");
                             }
-
                         }
                     }
                     @Override
@@ -309,9 +308,14 @@ class ValueSemanticsTest {
                     private void assertValueEqualsParsedValue(
                             final ValueSemanticsProvider.Context context,
                             final Parser<T> parser,
+                            final T value,
                             final String textRepresentation,
                             final String failureMessage) {
 
+                        //debug
+                        System.err.printf("using %s trying to parse '%s' to 
value %s%n",
+                                valueType.getName(), textRepresentation, 
""+value);
+
                         val parsedValue = Try.call(()->
                             parser.parseTextRepresentation(context, 
textRepresentation));
 
@@ -320,14 +324,14 @@ class ValueSemanticsTest {
                         }
 
                         tester.assertValueEquals(
-                                example.getValue(),
+                                value,
                                 parsedValue.getValue().orElse(null),
                                 failureMessage);
                     }
 
                 });
 
-        env.cleanup();
+       // env.cleanup();
 
     }
 
diff --git 
a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExample.java
 
b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExample.java
index 47bf9d2984..12c79d17b3 100644
--- 
a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExample.java
+++ 
b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExample.java
@@ -37,6 +37,8 @@ import java.util.stream.Stream;
 
 import javax.inject.Named;
 
+import org.springframework.beans.factory.annotation.Autowired;
+
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.DomainObject;
@@ -54,6 +56,8 @@ import org.apache.isis.applib.value.LocalResourcePath;
 import org.apache.isis.applib.value.Markup;
 import org.apache.isis.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.isis.applib.value.Password;
+import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract;
+import org.apache.isis.commons.collections.Can;
 import 
org.apache.isis.core.metamodel.valuesemantics.ApplicationFeatureIdValueSemantics;
 import org.apache.isis.extensions.fullcalendar.applib.value.CalendarEvent;
 import 
org.apache.isis.extensions.fullcalendar.applib.value.CalendarEventSemantics;
@@ -80,8 +84,17 @@ public abstract class ValueTypeExample<T> {
         setValue(value);
     }
 
+    @Autowired(required = false) List<ValueSemanticsAbstract<T>> semanticsList;
+    @Programmatic
+    public Can<T> getExamples() {
+        return Can.ofCollection(semanticsList)
+        .getFirst()
+        .map(semantics->semantics.getExamples())
+        .orElseGet(()->Can.of(getValue(), getUpdateValue()));
+    }
+
     @Collection
-    public final List<T> getValues() {
+    public List<T> getValues() {
         return List.of(getValue(), getUpdateValue());
     }
 
@@ -285,6 +298,12 @@ public abstract class ValueTypeExample<T> {
         private Float value = -63.1f;
         @Getter
         private Float updateValue = 0.f;
+
+        //FIXME does not handle example Float.MIN_VALUE well
+        @Deprecated // remove override once fixed
+        @Override public Can<Float> getExamples() {
+            return Can.of(value, updateValue);
+        }
     }
 
     @Named("isis.testdomain.valuetypes.ValueTypeExampleDouble")
@@ -296,6 +315,12 @@ public abstract class ValueTypeExample<T> {
         private Double value = -63.1;
         @Getter
         private Double updateValue = 0.;
+
+        //FIXME does not handle example Double.MIN_VALUE well
+        @Deprecated // remove override once fixed
+        @Override public Can<Double> getExamples() {
+            return Can.of(value, updateValue);
+        }
     }
 
     @Named("isis.testdomain.valuetypes.ValueTypeExampleBigInteger")

Reply via email to