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