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")