You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/11/22 15:12:08 UTC

[isis] branch master updated: ISIS-2877: testing value-types full circle now (WIP)

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 a05ddca  ISIS-2877: testing value-types full circle now (WIP)
a05ddca is described below

commit a05ddca47ce2f65fbaf0b506a026e610b0481034
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Nov 22 16:11:54 2021 +0100

    ISIS-2877: testing value-types full circle now (WIP)
---
 .../commons/internal/functions/_Functions.java     |  61 ++++++---
 .../isis/testdomain/value/ValueSemanticsTest.java  | 100 +++++++++++---
 .../testdomain/value/ValueSemanticsTester.java     | 143 +++++++++++++++++++++
 .../model/valuetypes/ValueTypeExample.java         |   5 +-
 ...xampleUuid.java => ValueTypeExampleString.java} |  13 +-
 .../model/valuetypes/ValueTypeExampleUuid.java     |   3 +
 6 files changed, 288 insertions(+), 37 deletions(-)

diff --git a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
index 531c8b1..62ed1c2 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.commons.internal.functions;
 
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -54,7 +55,7 @@ public final class _Functions {
      * and incremented after each function call.
      * @param indexed
      */
-    public static <T, R> Function<T, R> indexedZeroBase(IndexedFunction<T, R> indexed){
+    public static <T, R> Function<T, R> indexedZeroBase(final IndexedFunction<T, R> indexed){
         return new _Functions_IndexedZeroBase<T, R>(indexed);
     }
 
@@ -72,15 +73,15 @@ public final class _Functions {
 
         R apply(T t) throws Exception;
 
-        default <U extends RuntimeException> Function<T, R> toUnchecked(Function<Exception, U> toUncheckedException) {
+        default <U extends RuntimeException> Function<T, R> toUnchecked(final Function<Exception, U> toUncheckedException) {
             return uncheckedFunction(this, toUncheckedException);
         }
 
     }
 
     public static <T, R, U extends RuntimeException> Function<T, R> uncheckedFunction(
-            CheckedFunction<T, R> checkedFunction,
-            Function<Exception, U> toUncheckedException) {
+            final CheckedFunction<T, R> checkedFunction,
+            final Function<Exception, U> toUncheckedException) {
         return t->{
             try {
                 return checkedFunction.apply(t);
@@ -102,15 +103,15 @@ public final class _Functions {
 
         void run() throws Exception;
 
-        default <U extends RuntimeException> Runnable toUnchecked(Function<Exception, U> toUncheckedException) {
+        default <U extends RuntimeException> Runnable toUnchecked(final Function<Exception, U> toUncheckedException) {
             return uncheckedRunnable(this, toUncheckedException);
         }
 
     }
 
     public static <U extends RuntimeException> Runnable uncheckedRunnable(
-            CheckedRunnable checkedRunnable,
-            Function<Exception, U> toUncheckedException) {
+            final CheckedRunnable checkedRunnable,
+            final Function<Exception, U> toUncheckedException) {
         return ()->{
             try {
                 checkedRunnable.run();
@@ -120,7 +121,7 @@ public final class _Functions {
         };
     }
 
-    public static  Runnable uncheckedRunnable(CheckedRunnable checkedRunnable) {
+    public static  Runnable uncheckedRunnable(final CheckedRunnable checkedRunnable) {
         return ()->{
             try {
                 checkedRunnable.run();
@@ -143,15 +144,15 @@ public final class _Functions {
 
         void accept(T t) throws Exception;
 
-        default <U extends RuntimeException> Consumer<T> toUnchecked(Function<Exception, U> toUncheckedException) {
+        default <U extends RuntimeException> Consumer<T> toUnchecked(final Function<Exception, U> toUncheckedException) {
             return uncheckedConsumer(this, toUncheckedException);
         }
 
     }
 
     public static <T, U extends RuntimeException> Consumer<T> uncheckedConsumer(
-            CheckedConsumer<T> checkedConsumer,
-            Function<Exception, U> toUncheckedException) {
+            final CheckedConsumer<T> checkedConsumer,
+            final Function<Exception, U> toUncheckedException) {
         return t->{
             try {
                 checkedConsumer.accept(t);
@@ -161,6 +162,36 @@ public final class _Functions {
         };
     }
 
+    /**
+    *
+    * Similar to {@link BiConsumer}, but allows checked exceptions to be thrown.
+    *
+    * @param <T>
+    */
+   @FunctionalInterface
+   public interface CheckedBiConsumer<T, U> {
+
+       void accept(T t, U u) throws Exception;
+
+       default <V extends RuntimeException> BiConsumer<T, U> toUnchecked(
+               final Function<Exception, V> toUncheckedException) {
+           return uncheckedBiConsumer(this, toUncheckedException);
+       }
+
+   }
+
+   public static <T, U, V extends RuntimeException> BiConsumer<T, U> uncheckedBiConsumer(
+           final CheckedBiConsumer<T, U> checkedBiConsumer,
+           final Function<Exception, V> toUncheckedException) {
+       return (t, u)->{
+           try {
+               checkedBiConsumer.accept(t, u);
+           } catch (Exception e) {
+               throw toUncheckedException.apply(e);
+           }
+       };
+   }
+
     // -- CHECKED EXCEPTION ADAPTERS (SUPPLIER)
 
     /**
@@ -174,15 +205,15 @@ public final class _Functions {
 
         T get() throws Exception;
 
-        default <U extends RuntimeException> Supplier<T> toUnchecked(Function<Exception, U> toUncheckedException) {
+        default <U extends RuntimeException> Supplier<T> toUnchecked(final Function<Exception, U> toUncheckedException) {
             return uncheckedSupplier(this, toUncheckedException);
         }
 
     }
 
     public static <T, U extends RuntimeException> Supplier<T> uncheckedSupplier(
-            CheckedSupplier<T> checkedSupplier,
-            Function<Exception, U> toUncheckedException) {
+            final CheckedSupplier<T> checkedSupplier,
+            final Function<Exception, U> toUncheckedException) {
         return ()->{
             try {
                 return checkedSupplier.get();
@@ -193,7 +224,7 @@ public final class _Functions {
     }
 
     public static <T, U extends RuntimeException> Supplier<T> uncheckedSupplier(
-            CheckedSupplier<T> checkedSupplier) {
+            final CheckedSupplier<T> checkedSupplier) {
         return ()->{
             try {
                 return checkedSupplier.get();
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 d7849ae..13a0c1d 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
@@ -18,6 +18,9 @@
  */
 package org.apache.isis.testdomain.value;
 
+import java.io.Serializable;
+import java.util.Locale;
+import java.util.UUID;
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
@@ -30,10 +33,20 @@ import org.junit.jupiter.params.provider.MethodSource;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.inject.ServiceInjector;
+import org.apache.isis.applib.util.schema.CommonDtoUtils;
+import org.apache.isis.commons.internal.base._Refs;
+import org.apache.isis.commons.internal.resources._Xml;
+import org.apache.isis.commons.internal.resources._Xml.WriteOptions;
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.schema.cmd.v2.PropertyDto;
+import org.apache.isis.schema.common.v2.ValueWithTypeDto;
 import org.apache.isis.testdomain.conf.Configuration_headless;
 import org.apache.isis.testdomain.model.valuetypes.Configuration_usingValueTypes;
 import org.apache.isis.testdomain.model.valuetypes.ValueTypeExample;
@@ -56,35 +69,92 @@ import lombok.val;
 @TestInstance(Lifecycle.PER_CLASS)
 class ValueSemanticsTest {
 
-    //@Inject ValueSemanticsRegistry valueSemanticsRegistry;
-    @Inject ValueTypeExampleService valueTypeExampleProvider;
-    @Inject SpecificationLoader specLoader;
-
     @ParameterizedTest(name = "{index} {0}")
     @MethodSource("provideValueTypeExamples")
-    void valueTypes(
+    <T extends Serializable> void valueTypes(
             final String name,
-            final ValueTypeExample<?> example) {
+            final Class<T> valueType,
+            final ValueTypeExample<T> example) {
         assertNotNull(example);
-        System.err.printf("%s%n", example);
 
-        val exampleSpec = specLoader.specForTypeElseFail(example.getClass());
+        val tester = serviceInjector.injectServicesInto(
+                new ValueSemanticsTester<T>(example.getValueType(), example));
+
+        tester.propertyInteraction("value",
+                interactionContext(),
+                managedProp->example.getUpdateValue(),
+                (context, codec)->{
+
+                    // CoderDecoder round-trip test
+                    val serialized = codec.toEncodedString(example.getValue());
+                    assertEquals(example.getValue(), codec.fromEncodedString(serialized));
+
+                },
+                (context, parser)->{
+
+                    // Parser round-trip test
+                    val stringified = parser.parseableTextRepresentation(context, example.getValue());
+                    assertEquals(example.getValue(), parser.parseTextRepresentation(context, stringified));
+
+                },
+                (context, renderer)->{
+
+                },
+                command->{
+
+                    val propertyDto = (PropertyDto)command.getCommandDto().getMember();
+                    val newValueRecordedDto = propertyDto.getNewValue();
+
+                    val newValueRecorded = CommonDtoUtils.getValue(newValueRecordedDto);
 
-        val act = exampleSpec.getActionElseFail("updateValue");
-        val prop = exampleSpec.getPropertyElseFail("value");
-        val coll = exampleSpec.getCollectionElseFail("values");
+                    // TODO skip tests, because some value-types are not represented by the schema yet
+                    if(valueType.equals(UUID.class)) {
+                        return;
+                    }
 
-        assertNotNull(act);
-        assertNotNull(prop);
-        assertNotNull(coll);
+                    assertEquals(example.getUpdateValue(), newValueRecorded);
+
+//                    //debug
+//                    System.err.printf("Value %s %s%n", name,
+//                            valueToXml(newValueRecordedDto));
+//
+//                    //debug
+//                    System.err.printf("CommandDto %s %s%n", name,
+//                            CommandDtoUtils.toXml(
+//                                    command.getCommandDto()));
+                });
+
+    }
+
+    // -- HELPER
+
+    // eg.. <ValueWithTypeDto type="string"><com:string>anotherString</com:string></ValueWithTypeDto>
+    private static String valueToXml(final ValueWithTypeDto valueWithTypeDto) {
+        val xmlResult = _Xml.writeXml(valueWithTypeDto,
+                WriteOptions.builder().allowMissingRootElement(true).useContextCache(true).build());
+        val rawXml = xmlResult.presentElseFail();
+        val xmlRef = _Refs.stringRef(rawXml);
+        xmlRef.cutAtIndexOf("<ValueWithTypeDto");
+        return xmlRef.cutAtLastIndexOf("</ValueWithTypeDto>")
+                .replace(" null=\"false\" xmlns:com=\"http://isis.apache.org/schema/common\" xmlns:cmd=\"http://isis.apache.org/schema/cmd\"", "")
+                + "</ValueWithTypeDto>";
 
     }
 
+    private InteractionContext interactionContext() {
+        return InteractionContext.builder().locale(Locale.ENGLISH).build();
+    }
+
     // -- DEPENDENCIES
 
+    @Inject ValueTypeExampleService valueTypeExampleProvider;
+    @Inject SpecificationLoader specLoader;
+    @Inject InteractionService interactionService;
+    @Inject ServiceInjector serviceInjector;
+
     Stream<Arguments> provideValueTypeExamples() {
         return valueTypeExampleProvider.streamExamples()
-                .map(x->Arguments.of(x.getValueType().getSimpleName(), x));
+                .map(x->Arguments.of(x.getValueType().getSimpleName(), x.getValueType(), x));
     }
 
 }
diff --git a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java
new file mode 100644
index 0000000..0f62028
--- /dev/null
+++ b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java
@@ -0,0 +1,143 @@
+/*
+ *  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.isis.testdomain.value;
+
+import java.io.Serializable;
+import java.util.function.Function;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.value.semantics.EncoderDecoder;
+import org.apache.isis.applib.value.semantics.Parser;
+import org.apache.isis.applib.value.semantics.Renderer;
+import org.apache.isis.applib.value.semantics.ValueSemanticsProvider;
+import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.commons.internal.functions._Functions.CheckedBiConsumer;
+import org.apache.isis.commons.internal.functions._Functions.CheckedConsumer;
+import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty;
+import org.apache.isis.core.metamodel.interactions.managed.PropertyInteraction;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+
+import lombok.NonNull;
+import lombok.SneakyThrows;
+import lombok.val;
+
+public class ValueSemanticsTester<T extends Serializable> {
+
+    @Inject InteractionService interactionService;
+    @Inject SpecificationLoader specLoader;
+
+    private final Class<T> valueType;
+    private final Object domainObject;
+
+    public ValueSemanticsTester(final Class<T> valueType, final Object domainObject) {
+        this.valueType = valueType;
+        this.domainObject = domainObject;
+    }
+
+    public void actionInteraction(
+            final @NonNull String actionId) {
+        val objSpec = specLoader.specForTypeElseFail(domainObject.getClass());
+        val act = objSpec.getActionElseFail(actionId);
+    }
+
+    @SneakyThrows
+    public void propertyInteraction(
+            final @NonNull String propertyId,
+            final @NonNull InteractionContext interactionContext,
+            final @NonNull Function<ManagedProperty, Object> newProperyValueProvider,
+            final @NonNull CheckedBiConsumer<ValueSemanticsProvider.Context, EncoderDecoder<T>> codecCallback,
+            final @NonNull CheckedBiConsumer<ValueSemanticsProvider.Context, Parser<T>> parserCallback,
+            final @NonNull CheckedBiConsumer<ValueSemanticsProvider.Context, Renderer<T>> renderCallback,
+            final @NonNull CheckedConsumer<Command> commandCallback) {
+
+        val objSpec = specLoader.specForTypeElseFail(domainObject.getClass());
+        val prop = objSpec.getPropertyElseFail(propertyId);
+
+        val context = valueFacet(prop)
+                .createValueSemanticsContext(prop);
+
+        codecCallback.accept(context, codec(prop));
+        parserCallback.accept(context, parser(prop));
+        renderCallback.accept(context, renderer(prop));
+
+        interactionService.run(interactionContext, ()->{
+
+            val command = interactionService.currentInteractionElseFail().getCommand();
+
+            val propInteraction = PropertyInteraction
+                    .wrap(ManagedProperty.of(ManagedObject.of(objSpec, domainObject), prop, Where.OBJECT_FORMS));
+
+            propInteraction.modifyProperty(managedProp->
+                ManagedObject.of(managedProp.getElementType(), newProperyValueProvider.apply(managedProp)));
+
+            commandCallback.accept(command);
+        });
+    }
+
+    public void collectionInteraction(
+            final @NonNull String collectionId,
+            final @NonNull InteractionContext interactionContext) {
+        val objSpec = specLoader.specForTypeElseFail(domainObject.getClass());
+        val coll = objSpec.getCollectionElseFail(collectionId);
+    }
+
+    // -- HELPER
+
+    private ValueFacet<T> valueFacet(
+            final ObjectFeature feature) {
+
+        val valueFacet = feature.getElementType()
+                .lookupFacet(ValueFacet.class)
+                .orElseThrow(()->_Exceptions.noSuchElement(
+                        "Value type Property or Parameter %s is missing a ValueFacet",
+                        feature.getFeatureIdentifier()));
+
+        return _Casts.uncheckedCast(valueFacet);
+    }
+
+    private EncoderDecoder<T> codec(
+            final ObjectFeature feature) {
+        val valueFacet = valueFacet(feature);
+        return valueFacet.selectDefaultEncoderDecoder()
+                .orElseThrow(()->_Exceptions.noSuchElement());
+    }
+
+    private Parser<T> parser(
+            final ObjectFeature feature) {
+        val valueFacet = valueFacet(feature);
+        return valueFacet.selectParserForFeatureElseFallback(feature);
+    }
+
+    private Renderer<T> renderer(
+            final ObjectFeature feature) {
+        val valueFacet = valueFacet(feature);
+        return valueFacet.selectDefaultRenderer()
+                .orElseThrow(()->_Exceptions.noSuchElement());
+    }
+
+}
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 f6717ab..eda768e 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
@@ -31,6 +31,9 @@ public abstract class ValueTypeExample<T> {
     public abstract T getValue();
     public abstract void setValue(T value);
 
+    @Programmatic
+    public abstract T getUpdateValue();
+
     @Action
     public final void updateValue(final T value) {
         setValue(value);
@@ -38,7 +41,7 @@ public abstract class ValueTypeExample<T> {
 
     @Collection
     public final List<T> getValues() {
-        return List.of(getValue(), getValue());
+        return List.of(getValue(), getUpdateValue());
     }
 
     @SuppressWarnings("unchecked")
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleUuid.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleString.java
similarity index 86%
copy from regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleUuid.java
copy to regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleString.java
index c7dafae..c537837 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleUuid.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleString.java
@@ -18,8 +18,6 @@
  */
 package org.apache.isis.testdomain.model.valuetypes;
 
-import java.util.UUID;
-
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.Property;
@@ -28,12 +26,15 @@ import lombok.Getter;
 import lombok.Setter;
 
 @DomainObject(
-        logicalTypeName = "isis.testdomain.valuetypes.ValueTypeExampleUuid",
+        logicalTypeName = "isis.testdomain.valuetypes.ValueTypeExampleString",
         nature = Nature.BEAN)
-public class ValueTypeExampleUuid
-extends ValueTypeExample<UUID> {
+public class ValueTypeExampleString
+extends ValueTypeExample<String> {
 
     @Property @Getter @Setter
-    private UUID value = UUID.randomUUID();
+    private String value = "aString";
+
+    @Getter
+    private String updateValue = "anotherString";
 
 }
\ No newline at end of file
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleUuid.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleUuid.java
index c7dafae..8e760df 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleUuid.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/valuetypes/ValueTypeExampleUuid.java
@@ -36,4 +36,7 @@ extends ValueTypeExample<UUID> {
     @Property @Getter @Setter
     private UUID value = UUID.randomUUID();
 
+    @Getter
+    private UUID updateValue = UUID.randomUUID();
+
 }
\ No newline at end of file