From 2ea05ca0fb9306d29c6477e59fc68a99faa9dfa8 Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Mon, 4 Jul 2022 08:08:04 +0200 Subject: [PATCH 01/10] TestKit backend: add full support for temporal types --- .../org/testkit/backend/TestkitState.java | 3 +- .../TestkitRequestProcessorHandler.java | 5 +- .../backend/messages/TestkitModule.java | 31 ++- .../requests/SessionReadTransaction.java | 9 +- .../requests/SessionWriteTransaction.java | 9 +- .../messages/requests/StartSubTest.java | 203 ++++++++++++++++++ .../backend/messages/requests/StartTest.java | 39 +++- .../messages/requests/TestkitRequest.java | 3 +- .../TestkitCypherDateDeserializer.java | 49 +++++ .../TestkitCypherDateTimeDeserializer.java | 77 +++---- .../TestkitCypherDurationDeserializer.java | 49 +++++ .../TestkitCypherParamDeserializer.java | 7 +- .../TestkitCypherTimeDeserializer.java | 50 +++++ .../deserializer/TestkitCypherTypeMapper.java | 72 +++++++ .../deserializer/TestkitListDeserializer.java | 7 +- .../deserializer/types/CypherDateTime.java | 71 ++++++ .../deserializer/types/CypherTime.java | 47 ++++ .../deserializer/types/CypherType.java | 25 +++ .../messages/responses/RunSubTests.java | 29 +++ .../responses/serializer/GenUtils.java | 35 ++- .../TestkitDateTimeValueSerializer.java | 54 +++++ .../TestkitDateValueSerializer.java | 42 ++++ .../TestkitDurationValueSerializer.java | 50 +++++ .../TestkitLocalDateTimeValueSerializer.java | 44 ++++ .../TestkitLocalTimeValueSerializer.java | 42 ++++ .../TestkitTimeValueSerializer.java | 44 ++++ .../serializer/TestkitValueSerializer.java | 2 +- 27 files changed, 1011 insertions(+), 87 deletions(-) create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateDeserializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDurationDeserializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTimeDeserializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTypeMapper.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherDateTime.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherTime.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherType.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RunSubTests.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDateTimeValueSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDateValueSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDurationValueSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitLocalDateTimeValueSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitLocalTimeValueSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitTimeValueSerializer.java diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java index 9f445fc3d2..4ad0e758ee 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java @@ -40,7 +40,6 @@ import neo4j.org.testkit.backend.holder.TransactionHolder; import neo4j.org.testkit.backend.messages.requests.TestkitCallbackResult; import neo4j.org.testkit.backend.messages.responses.TestkitResponse; -import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.internal.cluster.RoutingTableRegistry; import reactor.core.publisher.Mono; @@ -69,7 +68,7 @@ public class TestkitState { private final Map transactionIdToReactiveTransactionHolder = new HashMap<>(); @Getter - private final Map errors = new HashMap<>(); + private final Map errors = new HashMap<>(); private final AtomicInteger idGenerator = new AtomicInteger(0); diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java index bc816856cf..a78ad6d6f0 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java @@ -21,6 +21,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import java.time.zone.ZoneRulesException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; @@ -120,8 +121,10 @@ private TestkitResponse createErrorResponse(Throwable throwable) { .build(); } else if (isConnectionPoolClosedException(throwable) || throwable instanceof UntrustedServerException - || throwable instanceof NoSuchRecordException) { + || throwable instanceof NoSuchRecordException + || throwable instanceof ZoneRulesException) { String id = testkitState.newId(); + testkitState.getErrors().put(id, (Exception) throwable); return DriverError.builder() .data(DriverError.DriverErrorBody.builder() .id(id) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java index 399d17ec22..26b1880ba5 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java @@ -19,33 +19,60 @@ package neo4j.org.testkit.backend.messages; import com.fasterxml.jackson.databind.module.SimpleModule; -import java.time.ZonedDateTime; +import java.time.LocalDate; import java.util.List; +import neo4j.org.testkit.backend.messages.requests.deserializer.TestkitCypherDateDeserializer; import neo4j.org.testkit.backend.messages.requests.deserializer.TestkitCypherDateTimeDeserializer; +import neo4j.org.testkit.backend.messages.requests.deserializer.TestkitCypherDurationDeserializer; +import neo4j.org.testkit.backend.messages.requests.deserializer.TestkitCypherTimeDeserializer; import neo4j.org.testkit.backend.messages.requests.deserializer.TestkitListDeserializer; +import neo4j.org.testkit.backend.messages.requests.deserializer.types.CypherDateTime; +import neo4j.org.testkit.backend.messages.requests.deserializer.types.CypherTime; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitDateTimeValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitDateValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitDurationValueSerializer; import neo4j.org.testkit.backend.messages.responses.serializer.TestkitListValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitLocalDateTimeValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitLocalTimeValueSerializer; import neo4j.org.testkit.backend.messages.responses.serializer.TestkitMapValueSerializer; import neo4j.org.testkit.backend.messages.responses.serializer.TestkitNodeValueSerializer; import neo4j.org.testkit.backend.messages.responses.serializer.TestkitPathValueSerializer; import neo4j.org.testkit.backend.messages.responses.serializer.TestkitRecordSerializer; import neo4j.org.testkit.backend.messages.responses.serializer.TestkitRelationshipValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitTimeValueSerializer; import neo4j.org.testkit.backend.messages.responses.serializer.TestkitValueSerializer; import org.neo4j.driver.Record; import org.neo4j.driver.Value; +import org.neo4j.driver.internal.value.DateTimeValue; +import org.neo4j.driver.internal.value.DateValue; +import org.neo4j.driver.internal.value.DurationValue; import org.neo4j.driver.internal.value.ListValue; +import org.neo4j.driver.internal.value.LocalDateTimeValue; +import org.neo4j.driver.internal.value.LocalTimeValue; import org.neo4j.driver.internal.value.MapValue; import org.neo4j.driver.internal.value.NodeValue; import org.neo4j.driver.internal.value.PathValue; import org.neo4j.driver.internal.value.RelationshipValue; +import org.neo4j.driver.internal.value.TimeValue; +import org.neo4j.driver.types.IsoDuration; public class TestkitModule extends SimpleModule { public TestkitModule() { this.addDeserializer(List.class, new TestkitListDeserializer()); - this.addDeserializer(ZonedDateTime.class, new TestkitCypherDateTimeDeserializer()); + this.addDeserializer(CypherDateTime.class, new TestkitCypherDateTimeDeserializer()); + this.addDeserializer(CypherTime.class, new TestkitCypherTimeDeserializer()); + this.addDeserializer(IsoDuration.class, new TestkitCypherDurationDeserializer()); + this.addDeserializer(LocalDate.class, new TestkitCypherDateDeserializer()); this.addSerializer(Value.class, new TestkitValueSerializer()); this.addSerializer(NodeValue.class, new TestkitNodeValueSerializer()); this.addSerializer(ListValue.class, new TestkitListValueSerializer()); + this.addSerializer(DateTimeValue.class, new TestkitDateTimeValueSerializer()); + this.addSerializer(DateValue.class, new TestkitDateValueSerializer()); + this.addSerializer(DurationValue.class, new TestkitDurationValueSerializer()); + this.addSerializer(LocalDateTimeValue.class, new TestkitLocalDateTimeValueSerializer()); + this.addSerializer(LocalTimeValue.class, new TestkitLocalTimeValueSerializer()); + this.addSerializer(TimeValue.class, new TestkitTimeValueSerializer()); this.addSerializer(Record.class, new TestkitRecordSerializer()); this.addSerializer(MapValue.class, new TestkitMapValueSerializer()); this.addSerializer(PathValue.class, new TestkitPathValueSerializer()); diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java index 9a5aaa3b0c..0203edf84a 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java @@ -23,7 +23,6 @@ import java.util.concurrent.ExecutionException; import lombok.Getter; import lombok.Setter; -import neo4j.org.testkit.backend.FrontendError; import neo4j.org.testkit.backend.ReactiveTransactionContextAdapter; import neo4j.org.testkit.backend.TestkitState; import neo4j.org.testkit.backend.holder.AsyncTransactionHolder; @@ -38,7 +37,6 @@ import org.neo4j.driver.TransactionWork; import org.neo4j.driver.async.AsyncSession; import org.neo4j.driver.async.AsyncTransactionWork; -import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.reactive.ReactiveTransactionCallback; import org.neo4j.driver.reactive.RxTransactionWork; import org.reactivestreams.Publisher; @@ -129,11 +127,8 @@ private TransactionWork handle(TestkitState testkitState, SessionHolder se if (workThrowable instanceof ExecutionException) { workThrowable = workThrowable.getCause(); } - if (workThrowable instanceof Neo4jException) { - throw (Neo4jException) workThrowable; - } - if (workThrowable instanceof FrontendError) { - throw (FrontendError) workThrowable; + if (workThrowable instanceof RuntimeException) { + throw (RuntimeException) workThrowable; } throw new RuntimeException("Unexpected exception occurred in transaction work function", workThrowable); } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionWriteTransaction.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionWriteTransaction.java index 6a7f898c4a..50803689fc 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionWriteTransaction.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionWriteTransaction.java @@ -24,7 +24,6 @@ import java.util.concurrent.ExecutionException; import lombok.Getter; import lombok.Setter; -import neo4j.org.testkit.backend.FrontendError; import neo4j.org.testkit.backend.ReactiveTransactionContextAdapter; import neo4j.org.testkit.backend.TestkitState; import neo4j.org.testkit.backend.holder.AsyncTransactionHolder; @@ -39,7 +38,6 @@ import org.neo4j.driver.TransactionWork; import org.neo4j.driver.async.AsyncSession; import org.neo4j.driver.async.AsyncTransactionWork; -import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.reactive.ReactiveTransactionCallback; import org.neo4j.driver.reactive.RxTransactionWork; import org.reactivestreams.Publisher; @@ -130,11 +128,8 @@ private TransactionWork handle(TestkitState testkitState, SessionHolder se if (workThrowable instanceof ExecutionException) { workThrowable = workThrowable.getCause(); } - if (workThrowable instanceof Neo4jException) { - throw (Neo4jException) workThrowable; - } - if (workThrowable instanceof FrontendError) { - throw (FrontendError) workThrowable; + if (workThrowable instanceof RuntimeException) { + throw (RuntimeException) workThrowable; } throw new RuntimeException("Unexpected exception occurred in transaction work function", workThrowable); } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java new file mode 100644 index 0000000000..9236f46490 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.requests; + +import java.time.DateTimeException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import lombok.Getter; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.RunTest; +import neo4j.org.testkit.backend.messages.responses.SkipTest; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; +import reactor.core.publisher.Mono; + +@Setter +@Getter +public class StartSubTest implements TestkitRequest { + interface SkipDeciderInterface { + SkipDecision check(Map params); + } + + public static class SkipDecision { + private final boolean skipped; + private final String reason; + + public SkipDecision(boolean skipped, String reason) { + this.skipped = skipped; + this.reason = reason; + } + + public boolean isSkipped() { + return skipped; + } + + public String getReason() { + return reason; + } + + static SkipDecision ofNonSkipped() { + return new SkipDecision(false, null); + } + + static SkipDecision ofSkipped(String reason) { + return new SkipDecision(true, reason); + } + } + + private static final Map COMMON_SKIP_PATTERN_TO_CHECK = new HashMap<>(); + private static final Map ASYNC_SKIP_PATTERN_TO_CHECK = new HashMap<>(); + private static final Map REACTIVE_LEGACY_SKIP_PATTERN_TO_CHECK = new HashMap<>(); + private static final Map REACTIVE_SKIP_PATTERN_TO_CHECK = new HashMap<>(); + + private static SkipDecision checkTzIdSupported(Map params) { + String tzId = (String) params.get("tz_id"); + try { + ZoneId.of(tzId); + return SkipDecision.ofNonSkipped(); + } catch (DateTimeException e) { + return SkipDecision.ofSkipped("Timezone not supported: " + tzId); + } + } + + private static SkipDecision checkDateTimeSupported(Map params) { + @SuppressWarnings("unchecked") + HashMap dt_param = (HashMap) params.get("dt"); + if (dt_param == null) { + throw new RuntimeException("params expected to contain 'dt'"); + } + @SuppressWarnings("unchecked") + HashMap data = (HashMap) dt_param.get("data"); + if (data == null) { + throw new RuntimeException("param 'dt' expected to contain 'data'"); + } + Integer year = (Integer) data.get("year"); + Integer month = (Integer) data.get("month"); + Integer day = (Integer) data.get("day"); + Integer hour = (Integer) data.get("hour"); + Integer minute = (Integer) data.get("minute"); + Integer second = (Integer) data.get("second"); + Integer nano = (Integer) data.get("nanosecond"); + Integer utcOffset = (Integer) data.get("utc_offset_s"); + String tzId = (String) data.get("timezone_id"); + try { + ZonedDateTime dt = ZonedDateTime.of(year, month, day, hour, minute, second, nano, ZoneId.of(tzId)); + if (dt.getOffset().getTotalSeconds() != utcOffset) { + throw new DateTimeException(String.format( + "Unmatched UTC offset. TestKit expected %d, local zone db yielded %d", + utcOffset, dt.getOffset().getTotalSeconds())); + } + return SkipDecision.ofNonSkipped(); + } catch (DateTimeException e) { + return SkipDecision.ofSkipped("DateTime not supported: " + e.getMessage()); + } + } + + static { + COMMON_SKIP_PATTERN_TO_CHECK.put( + "neo4j\\.datatypes\\.test_temporal_types\\.TestDataTypes\\.test_should_echo_all_timezone_ids", + StartSubTest::checkDateTimeSupported); + COMMON_SKIP_PATTERN_TO_CHECK.put( + "neo4j\\.datatypes\\.test_temporal_types\\.TestDataTypes\\.test_date_time_cypher_created_tz_id", + StartSubTest::checkTzIdSupported); + + ASYNC_SKIP_PATTERN_TO_CHECK.putAll(COMMON_SKIP_PATTERN_TO_CHECK); + + REACTIVE_LEGACY_SKIP_PATTERN_TO_CHECK.putAll(COMMON_SKIP_PATTERN_TO_CHECK); + + REACTIVE_SKIP_PATTERN_TO_CHECK.putAll(COMMON_SKIP_PATTERN_TO_CHECK); + } + + private StartSubTestBody data; + + public static boolean decidePerSubTest(String testName) { + return skipPatternMatches(testName, COMMON_SKIP_PATTERN_TO_CHECK); + } + + public static boolean decidePerSubTestAsync(String testName) { + return skipPatternMatches(testName, ASYNC_SKIP_PATTERN_TO_CHECK); + } + + public static boolean decidePerSubTestReactiveLegacy(String testName) { + return skipPatternMatches(testName, REACTIVE_LEGACY_SKIP_PATTERN_TO_CHECK); + } + + public static boolean decidePerSubTestReactive(String testName) { + return skipPatternMatches(testName, REACTIVE_SKIP_PATTERN_TO_CHECK); + } + + private static boolean skipPatternMatches( + String testName, Map skipPatternToFunction) { + return skipPatternToFunction.entrySet().stream().anyMatch(entry -> testName.matches(entry.getKey())); + } + + @Override + public TestkitResponse process(TestkitState testkitState) { + return createResponse(COMMON_SKIP_PATTERN_TO_CHECK); + } + + @Override + public CompletionStage processAsync(TestkitState testkitState) { + TestkitResponse testkitResponse = createResponse(ASYNC_SKIP_PATTERN_TO_CHECK); + return CompletableFuture.completedFuture(testkitResponse); + } + + @Override + public Mono processRx(TestkitState testkitState) { + TestkitResponse testkitResponse = createResponse(REACTIVE_LEGACY_SKIP_PATTERN_TO_CHECK); + return Mono.fromCompletionStage(CompletableFuture.completedFuture(testkitResponse)); + } + + @Override + public Mono processReactive(TestkitState testkitState) { + TestkitResponse testkitResponse = createResponse(REACTIVE_SKIP_PATTERN_TO_CHECK); + return Mono.fromCompletionStage(CompletableFuture.completedFuture(testkitResponse)); + } + + private TestkitResponse createResponse(Map skipPatternToCheck) { + System.out.println(data.getTestName()); + return (TestkitResponse) skipPatternToCheck.entrySet().stream() + .filter(entry -> data.getTestName().matches(entry.getKey())) + .findFirst() + .map(entry -> { + SkipDecision decision = entry.getValue().check(data.getSubtestArguments()); + if (decision.isSkipped()) { + return SkipTest.builder() + .data(SkipTest.SkipTestBody.builder() + .reason(decision.getReason()) + .build()) + .build(); + } + return RunTest.builder().build(); + }) + .orElse(RunTest.builder().build()); + } + + @Setter + @Getter + public static class StartSubTestBody { + private String testName; + private Map subtestArguments; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java index 00cec49b60..44704434eb 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java @@ -25,6 +25,7 @@ import lombok.Getter; import lombok.Setter; import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.RunSubTests; import neo4j.org.testkit.backend.messages.responses.RunTest; import neo4j.org.testkit.backend.messages.responses.SkipTest; import neo4j.org.testkit.backend.messages.responses.TestkitResponse; @@ -81,9 +82,9 @@ public class StartTest implements TestkitRequest { "^.*\\.TestOptimizations\\.test_uses_implicit_default_arguments_multi_query$", skipMessage); COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.TestOptimizations\\.test_uses_implicit_default_arguments_multi_query_nested$", skipMessage); - skipMessage = "Additional type support is needed"; COMMON_SKIP_PATTERN_TO_REASON.put( - "^neo4j\\.datatypes\\.test_temporal_types\\.TestDataTypes\\..*$", skipMessage); + "^.*\\.test_unknown_then_known_zoned_date_time(_patched)?$", + "Unknown zone names make the driver close the connection."); ASYNC_SKIP_PATTERN_TO_REASON.putAll(COMMON_SKIP_PATTERN_TO_REASON); @@ -150,28 +151,50 @@ public class StartTest implements TestkitRequest { @Override public TestkitResponse process(TestkitState testkitState) { - return createResponse(COMMON_SKIP_PATTERN_TO_REASON); + TestkitResponse testkitResponse = createSkipResponse(COMMON_SKIP_PATTERN_TO_REASON); + if (testkitResponse != null) { + return testkitResponse; + } + if (StartSubTest.decidePerSubTest(data.getTestName())) { + return RunSubTests.builder().build(); + } + return RunTest.builder().build(); } @Override public CompletionStage processAsync(TestkitState testkitState) { - TestkitResponse testkitResponse = createResponse(ASYNC_SKIP_PATTERN_TO_REASON); + TestkitResponse testkitResponse = createSkipResponse(ASYNC_SKIP_PATTERN_TO_REASON); + if (testkitResponse == null && StartSubTest.decidePerSubTestAsync(data.getTestName())) { + testkitResponse = RunSubTests.builder().build(); + } else { + testkitResponse = RunTest.builder().build(); + } return CompletableFuture.completedFuture(testkitResponse); } @Override public Mono processRx(TestkitState testkitState) { - TestkitResponse testkitResponse = createResponse(REACTIVE_LEGACY_SKIP_PATTERN_TO_REASON); + TestkitResponse testkitResponse = createSkipResponse(REACTIVE_LEGACY_SKIP_PATTERN_TO_REASON); + if (testkitResponse == null && StartSubTest.decidePerSubTestReactiveLegacy(data.getTestName())) { + testkitResponse = RunSubTests.builder().build(); + } else { + testkitResponse = RunTest.builder().build(); + } return Mono.fromCompletionStage(CompletableFuture.completedFuture(testkitResponse)); } @Override public Mono processReactive(TestkitState testkitState) { - TestkitResponse testkitResponse = createResponse(REACTIVE_SKIP_PATTERN_TO_REASON); + TestkitResponse testkitResponse = createSkipResponse(REACTIVE_SKIP_PATTERN_TO_REASON); + if (testkitResponse == null && StartSubTest.decidePerSubTestReactive(data.getTestName())) { + testkitResponse = RunSubTests.builder().build(); + } else { + testkitResponse = RunTest.builder().build(); + } return Mono.fromCompletionStage(CompletableFuture.completedFuture(testkitResponse)); } - private TestkitResponse createResponse(Map skipPatternToReason) { + private TestkitResponse createSkipResponse(Map skipPatternToReason) { System.out.println(data.getTestName()); return skipPatternToReason.entrySet().stream() .filter(entry -> data.getTestName().matches(entry.getKey())) @@ -181,7 +204,7 @@ private TestkitResponse createResponse(Map skipPatternToReason) .reason(entry.getValue()) .build()) .build()) - .orElseGet(() -> RunTest.builder().build()); + .orElse(null); } @Setter diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java index 37d45b89c6..b9009dbd5d 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java @@ -41,7 +41,8 @@ @JsonSubTypes.Type(GetRoutingTable.class), @JsonSubTypes.Type(TransactionClose.class), @JsonSubTypes.Type(ResultList.class), @JsonSubTypes.Type(GetConnectionPoolMetrics.class), @JsonSubTypes.Type(ResultPeek.class), @JsonSubTypes.Type(CheckDriverIsEncrypted.class), - @JsonSubTypes.Type(CypherTypeField.class), @JsonSubTypes.Type(ResultSingle.class) + @JsonSubTypes.Type(CypherTypeField.class), @JsonSubTypes.Type(ResultSingle.class), + @JsonSubTypes.Type(StartSubTest.class) }) public interface TestkitRequest { TestkitResponse process(TestkitState testkitState); diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateDeserializer.java new file mode 100644 index 0000000000..43a39e535a --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateDeserializer.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.requests.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import java.io.IOException; +import java.time.LocalDate; +import java.util.Date; + +public class TestkitCypherDateDeserializer extends StdDeserializer { + + private final TestkitCypherTypeMapper mapper; + + private static final class CypherDateData { + Integer year; + Integer month; + Integer day; + } + + public TestkitCypherDateDeserializer() { + super(Date.class); + mapper = new TestkitCypherTypeMapper(); + } + + public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + CypherDateData data = mapper.mapData(p, ctxt, new CypherDateData()); + return LocalDate.of(data.year, data.month, data.day); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateTimeDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateTimeDeserializer.java index b1d24d4b53..97bc1cf6df 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateTimeDeserializer.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateTimeDeserializer.java @@ -18,67 +18,22 @@ */ package neo4j.org.testkit.backend.messages.requests.deserializer; -import com.fasterxml.jackson.core.JacksonException; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; -import java.lang.reflect.Field; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; +import neo4j.org.testkit.backend.messages.requests.deserializer.types.CypherDateTime; -public class TestkitCypherDateTimeDeserializer extends StdDeserializer { - public TestkitCypherDateTimeDeserializer() { - super(ZonedDateTime.class); - } - - @Override - public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { - - CypherDateTime dateTime = new CypherDateTime(); - - JsonToken token = p.currentToken(); - while (token == JsonToken.FIELD_NAME - || token == JsonToken.VALUE_NUMBER_INT - || token == JsonToken.VALUE_STRING) { - if (token == JsonToken.VALUE_NUMBER_INT) { - String field = p.getCurrentName(); - int value = p.getValueAsInt(); - setField(dateTime, field, value); - } else if (token == JsonToken.VALUE_STRING) { - String field = p.getCurrentName(); - String value = p.getValueAsString(); - setField(dateTime, field, value); - } - token = p.nextToken(); - } +public class TestkitCypherDateTimeDeserializer extends StdDeserializer { + private final TestkitCypherTypeMapper mapper; - ZoneId zoneId = dateTime.timezone_id != null - ? ZoneId.of(dateTime.timezone_id) - : ZoneOffset.ofTotalSeconds(dateTime.utc_offset_s); - return ZonedDateTime.of( - dateTime.year, - dateTime.month, - dateTime.day, - dateTime.hour, - dateTime.minute, - dateTime.second, - dateTime.nanosecond, - zoneId); - } - - private void setField(CypherDateTime dateTime, String fieldName, Object value) { - try { - Field field = CypherDateTime.class.getDeclaredField(fieldName); - field.set(dateTime, value); - } catch (NoSuchFieldException | IllegalAccessException e) { - // ignored - } + public TestkitCypherDateTimeDeserializer() { + super(CypherDateTime.class); + mapper = new TestkitCypherTypeMapper(); } - private static class CypherDateTime { + private static final class CypherDateTimeData { Integer year; Integer month; Integer day; @@ -89,4 +44,20 @@ private static class CypherDateTime { Integer utc_offset_s; String timezone_id; } + + @Override + public CypherDateTime deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + CypherDateTimeData data = mapper.mapData(p, ctxt, new CypherDateTimeData()); + return new CypherDateTime( + data.year, + data.month, + data.day, + data.hour, + data.minute, + data.second, + data.nanosecond, + data.timezone_id, + data.utc_offset_s); + } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDurationDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDurationDeserializer.java new file mode 100644 index 0000000000..472d172b18 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDurationDeserializer.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.requests.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import java.io.IOException; +import org.neo4j.driver.internal.InternalIsoDuration; +import org.neo4j.driver.types.IsoDuration; + +public class TestkitCypherDurationDeserializer extends StdDeserializer { + private final TestkitCypherTypeMapper mapper; + + public TestkitCypherDurationDeserializer() { + super(IsoDuration.class); + mapper = new TestkitCypherTypeMapper(); + } + + private static final class CypherDurationData { + Long months; + Long days; + Long seconds; + Integer nanoseconds; + } + + public IsoDuration deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + CypherDurationData data = mapper.mapData(p, ctxt, new CypherDurationData()); + return new InternalIsoDuration(data.months, data.days, data.seconds, data.nanoseconds); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherParamDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherParamDeserializer.java index 929c67ba30..b5740d0db0 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherParamDeserializer.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherParamDeserializer.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import neo4j.org.testkit.backend.messages.requests.deserializer.types.CypherType; public class TestkitCypherParamDeserializer extends StdDeserializer> { public TestkitCypherParamDeserializer() { @@ -77,7 +78,11 @@ public Map deserialize(JsonParser p, DeserializationContext ctxt { result.put(key, deserialize(p, ctxt)); } else { - result.put(key, p.readValueAs(mapValueType)); + Object obj = p.readValueAs(mapValueType); + if (obj instanceof CypherType) { + obj = ((CypherType) obj).asValue(); + } + result.put(key, obj); } } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTimeDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTimeDeserializer.java new file mode 100644 index 0000000000..b66d012f45 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTimeDeserializer.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.requests.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import java.io.IOException; +import neo4j.org.testkit.backend.messages.requests.deserializer.types.CypherTime; + +public class TestkitCypherTimeDeserializer extends StdDeserializer { + private final TestkitCypherTypeMapper mapper; + + public TestkitCypherTimeDeserializer() { + super(CypherTime.class); + mapper = new TestkitCypherTypeMapper(); + } + + private static final class CypherTimeData { + Integer hour; + Integer minute; + Integer second; + Integer nanosecond; + Integer utc_offset_s; + } + + @Override + public CypherTime deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + CypherTimeData data = mapper.mapData(p, ctxt, new CypherTimeData()); + return new CypherTime(data.hour, data.minute, data.second, data.nanosecond, data.utc_offset_s); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTypeMapper.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTypeMapper.java new file mode 100644 index 0000000000..cb38f06c89 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTypeMapper.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.requests.deserializer; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import java.io.IOException; +import java.lang.reflect.Field; + +public class TestkitCypherTypeMapper { + public TestkitCypherTypeMapper() {} + + public T mapData(JsonParser p, DeserializationContext ctxt, T data) throws IOException, JacksonException { + JsonToken token = p.currentToken(); + while (token == JsonToken.FIELD_NAME + || token == JsonToken.VALUE_NUMBER_INT + || token == JsonToken.VALUE_STRING) { + if (token == JsonToken.VALUE_NUMBER_INT) { + String field = p.getCurrentName(); + if (fieldIsType(data, field, Long.class)) { + setField(data, field, p.getLongValue()); + } else if (fieldIsType(data, field, Integer.class)) { + setField(data, field, p.getIntValue()); + } else { + throw new RuntimeException("Unhandled field type: " + field); + } + } else if (token == JsonToken.VALUE_STRING) { + String field = p.getCurrentName(); + String value = p.getValueAsString(); + setField(data, field, value); + } + token = p.nextToken(); + } + return data; + } + + private boolean fieldIsType(Object data, String field, Class type) { + try { + Field f = data.getClass().getDeclaredField(field); + return f.getType().equals(type); + } catch (NoSuchFieldException e) { + return false; + } + } + + private void setField(Object data, String fieldName, Object value) { + try { + Field field = data.getClass().getDeclaredField(fieldName); + field.set(data, value); + } catch (NoSuchFieldException | IllegalAccessException e) { + // ignored + } + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitListDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitListDeserializer.java index 034fd24116..e5dacda40b 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitListDeserializer.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitListDeserializer.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import neo4j.org.testkit.backend.messages.requests.deserializer.types.CypherType; public class TestkitListDeserializer extends StdDeserializer> { private final TestkitCypherParamDeserializer mapDeserializer; @@ -80,7 +81,11 @@ public List deserialize(JsonParser p, DeserializationContext ctxt) throws IOE { result.add(mapDeserializer.deserialize(p, ctxt)); } else { - result.add(p.readValueAs(mapValueType)); + Object obj = p.readValueAs(mapValueType); + if (obj instanceof CypherType) { + obj = ((CypherType) obj).asValue(); + } + result.add(obj); } } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherDateTime.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherDateTime.java new file mode 100644 index 0000000000..5888cbc1be --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherDateTime.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.requests.deserializer.types; + +import java.time.DateTimeException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.value.DateTimeValue; +import org.neo4j.driver.internal.value.LocalDateTimeValue; + +public class CypherDateTime implements CypherType { + private final int year, month, day, hour, minute, second, nano; + private final String zoneId; + private final Integer offset; + + public CypherDateTime( + int year, + int month, + int day, + int hour, + int minute, + int second, + int nanosecond, + String zoneId, + Integer offset) { + this.year = year; + this.month = month; + this.day = day; + this.hour = hour; + this.minute = minute; + this.second = second; + this.nano = nanosecond; + this.zoneId = zoneId; + this.offset = offset; + } + + @Override + public Value asValue() { + if (offset == null) { + return new LocalDateTimeValue(LocalDateTime.of(year, month, day, hour, minute, second, nano)); + } + if (zoneId == null) { + ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(offset); + return new DateTimeValue(ZonedDateTime.of(year, month, day, hour, minute, second, nano, zoneOffset)); + } + try { + return new DateTimeValue(ZonedDateTime.of(year, month, day, hour, minute, second, nano, ZoneId.of(zoneId))); + } catch (DateTimeException e) { + return null; + } + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherTime.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherTime.java new file mode 100644 index 0000000000..92ac1a690a --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherTime.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.requests.deserializer.types; + +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.value.LocalTimeValue; +import org.neo4j.driver.internal.value.TimeValue; + +public class CypherTime implements CypherType { + private final int hour, minute, second, nano; + private final Integer offset; + + public CypherTime(int hour, int minute, int second, int nanosecond, Integer offset) { + this.hour = hour; + this.minute = minute; + this.second = second; + this.nano = nanosecond; + this.offset = offset; + } + + @Override + public Value asValue() { + if (offset == null) { + return new LocalTimeValue(LocalTime.of(hour, minute, second, nano)); + } + return new TimeValue(OffsetTime.of(hour, minute, second, nano, ZoneOffset.ofTotalSeconds(offset))); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherType.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherType.java new file mode 100644 index 0000000000..b50bddf7f3 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherType.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.requests.deserializer.types; + +import org.neo4j.driver.Value; + +public interface CypherType { + public Value asValue(); +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RunSubTests.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RunSubTests.java new file mode 100644 index 0000000000..70dc30db02 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RunSubTests.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; + +@Builder +public class RunSubTests implements TestkitResponse { + @Override + public String testkitName() { + return "RunSubTests"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java index c783c3efa0..dd0c7d1515 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java @@ -20,11 +20,14 @@ import com.fasterxml.jackson.core.JsonGenerator; import java.io.IOException; -import java.time.ZonedDateTime; +import java.time.LocalDate; import java.util.List; import java.util.Map; import lombok.AccessLevel; import lombok.AllArgsConstructor; +import neo4j.org.testkit.backend.messages.requests.deserializer.types.CypherDateTime; +import neo4j.org.testkit.backend.messages.requests.deserializer.types.CypherTime; +import org.neo4j.driver.types.IsoDuration; @AllArgsConstructor(access = AccessLevel.PRIVATE) public final class GenUtils { @@ -55,12 +58,32 @@ public static void cypherObject(JsonGenerator gen, String name, RunnableWith }); } + public static void writeDate(JsonGenerator gen, int year, int month, int day) throws IOException { + gen.writeFieldName("year"); + gen.writeNumber(year); + gen.writeFieldName("month"); + gen.writeNumber(month); + gen.writeFieldName("day"); + gen.writeNumber(day); + } + + public static void writeTime(JsonGenerator gen, int hour, int minute, int second, int nano) throws IOException { + gen.writeFieldName("hour"); + gen.writeNumber(hour); + gen.writeFieldName("minute"); + gen.writeNumber(minute); + gen.writeFieldName("second"); + gen.writeNumber(second); + gen.writeFieldName("nanosecond"); + gen.writeNumber(nano); + } + public static Class cypherTypeToJavaType(String typeString) { switch (typeString) { case "CypherBool": return Boolean.class; case "CypherInt": - return Integer.class; + return Long.class; case "CypherFloat": return Double.class; case "CypherString": @@ -70,7 +93,13 @@ public static Class cypherTypeToJavaType(String typeString) { case "CypherMap": return Map.class; case "CypherDateTime": - return ZonedDateTime.class; + return CypherDateTime.class; + case "CypherTime": + return CypherTime.class; + case "CypherDate": + return LocalDate.class; + case "CypherDuration": + return IsoDuration.class; case "CypherNull": return null; default: diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDateTimeValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDateTimeValueSerializer.java new file mode 100644 index 0000000000..c40fef3fd5 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDateTimeValueSerializer.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import org.neo4j.driver.internal.value.DateTimeValue; + +public class TestkitDateTimeValueSerializer extends StdSerializer { + public TestkitDateTimeValueSerializer() { + super(DateTimeValue.class); + } + + @Override + public void serialize(DateTimeValue timeValue, JsonGenerator gen, SerializerProvider provider) throws IOException { + cypherObject(gen, "CypherDateTime", () -> { + ZonedDateTime dateTime = timeValue.asZonedDateTime(); + GenUtils.writeDate(gen, dateTime.getYear(), dateTime.getMonthValue(), dateTime.getDayOfMonth()); + GenUtils.writeTime(gen, dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano()); + ZoneOffset offset = dateTime.getOffset(); + gen.writeFieldName("utc_offset_s"); + gen.writeNumber(offset.getTotalSeconds()); + ZoneId zoneId = dateTime.getZone(); + if (zoneId != offset) { + // not fixed offset + gen.writeFieldName("timezone_id"); + gen.writeString(zoneId.toString()); + } + }); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDateValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDateValueSerializer.java new file mode 100644 index 0000000000..32c9f297fc --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDateValueSerializer.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.time.LocalDate; +import org.neo4j.driver.internal.value.DateValue; + +public class TestkitDateValueSerializer extends StdSerializer { + public TestkitDateValueSerializer() { + super(DateValue.class); + } + + @Override + public void serialize(DateValue dateValue, JsonGenerator gen, SerializerProvider provider) throws IOException { + cypherObject(gen, "CypherDate", () -> { + LocalDate date = dateValue.asLocalDate(); + GenUtils.writeDate(gen, date.getYear(), date.getMonthValue(), date.getDayOfMonth()); + }); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDurationValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDurationValueSerializer.java new file mode 100644 index 0000000000..23df07f2a4 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitDurationValueSerializer.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import org.neo4j.driver.internal.value.DurationValue; +import org.neo4j.driver.types.IsoDuration; + +public class TestkitDurationValueSerializer extends StdSerializer { + public TestkitDurationValueSerializer() { + super(DurationValue.class); + } + + @Override + public void serialize(DurationValue durationValue, JsonGenerator gen, SerializerProvider provider) + throws IOException { + cypherObject(gen, "CypherDuration", () -> { + IsoDuration duration = durationValue.asIsoDuration(); + gen.writeFieldName("months"); + gen.writeNumber(duration.months()); + gen.writeFieldName("days"); + gen.writeNumber(duration.days()); + gen.writeFieldName("seconds"); + gen.writeNumber(duration.seconds()); + gen.writeFieldName("nanoseconds"); + gen.writeNumber(duration.nanoseconds()); + }); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitLocalDateTimeValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitLocalDateTimeValueSerializer.java new file mode 100644 index 0000000000..edf52f8292 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitLocalDateTimeValueSerializer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.time.LocalDateTime; +import org.neo4j.driver.internal.value.LocalDateTimeValue; + +public class TestkitLocalDateTimeValueSerializer extends StdSerializer { + public TestkitLocalDateTimeValueSerializer() { + super(LocalDateTimeValue.class); + } + + @Override + public void serialize(LocalDateTimeValue timeValue, JsonGenerator gen, SerializerProvider provider) + throws IOException { + cypherObject(gen, "CypherDateTime", () -> { + LocalDateTime dateTime = timeValue.asLocalDateTime(); + GenUtils.writeDate(gen, dateTime.getYear(), dateTime.getMonthValue(), dateTime.getDayOfMonth()); + GenUtils.writeTime(gen, dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano()); + }); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitLocalTimeValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitLocalTimeValueSerializer.java new file mode 100644 index 0000000000..84a6e9f4a7 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitLocalTimeValueSerializer.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.time.LocalTime; +import org.neo4j.driver.internal.value.LocalTimeValue; + +public class TestkitLocalTimeValueSerializer extends StdSerializer { + public TestkitLocalTimeValueSerializer() { + super(LocalTimeValue.class); + } + + @Override + public void serialize(LocalTimeValue timeValue, JsonGenerator gen, SerializerProvider provider) throws IOException { + cypherObject(gen, "CypherTime", () -> { + LocalTime time = timeValue.asLocalTime(); + GenUtils.writeTime(gen, time.getHour(), time.getMinute(), time.getSecond(), time.getNano()); + }); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitTimeValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitTimeValueSerializer.java new file mode 100644 index 0000000000..46fe0d21c0 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitTimeValueSerializer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.time.OffsetTime; +import org.neo4j.driver.internal.value.TimeValue; + +public class TestkitTimeValueSerializer extends StdSerializer { + public TestkitTimeValueSerializer() { + super(TimeValue.class); + } + + @Override + public void serialize(TimeValue timeValue, JsonGenerator gen, SerializerProvider provider) throws IOException { + cypherObject(gen, "CypherTime", () -> { + OffsetTime time = timeValue.asOffsetTime(); + GenUtils.writeTime(gen, time.getHour(), time.getMinute(), time.getSecond(), time.getNano()); + gen.writeFieldName("utc_offset_s"); + gen.writeNumber(time.getOffset().getTotalSeconds()); + }); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java index a3ada4d893..91d8633d39 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java @@ -39,7 +39,7 @@ public void serialize(Value value, JsonGenerator gen, SerializerProvider provide } else if (InternalTypeSystem.TYPE_SYSTEM.NULL().isTypeOf(value)) { cypherObject(gen, "CypherNull", () -> gen.writeNullField("value")); } else if (InternalTypeSystem.TYPE_SYSTEM.INTEGER().isTypeOf(value)) { - cypherObject(gen, "CypherInt", value.asInt()); + cypherObject(gen, "CypherInt", value.asLong()); } else if (InternalTypeSystem.TYPE_SYSTEM.FLOAT().isTypeOf(value)) { cypherObject(gen, "CypherFloat", value.asDouble()); } else if (InternalTypeSystem.TYPE_SYSTEM.STRING().isTypeOf(value)) { From f975a36fe7fd4f0cc90a081325d3919c5b134d15 Mon Sep 17 00:00:00 2001 From: Robsdedude Date: Tue, 5 Jul 2022 17:07:21 +0200 Subject: [PATCH 02/10] Remove debug print Co-authored-by: injectives <11927660+injectives@users.noreply.github.com> --- .../neo4j/org/testkit/backend/messages/requests/StartTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java index 44704434eb..606c2e7499 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java @@ -195,7 +195,6 @@ public Mono processReactive(TestkitState testkitState) { } private TestkitResponse createSkipResponse(Map skipPatternToReason) { - System.out.println(data.getTestName()); return skipPatternToReason.entrySet().stream() .filter(entry -> data.getTestName().matches(entry.getKey())) .findFirst() From a1786cfd56c8c7787af0f290539c80273fc362f0 Mon Sep 17 00:00:00 2001 From: Dmitriy Tverdiakov Date: Tue, 5 Jul 2022 17:16:58 +0200 Subject: [PATCH 03/10] Code style: Mono.just Signed-off-by: Rouven Bauer --- .../org/testkit/backend/messages/requests/StartSubTest.java | 4 ++-- .../org/testkit/backend/messages/requests/StartTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java index 9236f46490..7232efe93e 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java @@ -166,13 +166,13 @@ public CompletionStage processAsync(TestkitState testkitState) @Override public Mono processRx(TestkitState testkitState) { TestkitResponse testkitResponse = createResponse(REACTIVE_LEGACY_SKIP_PATTERN_TO_CHECK); - return Mono.fromCompletionStage(CompletableFuture.completedFuture(testkitResponse)); + return Mono.just(testkitResponse); } @Override public Mono processReactive(TestkitState testkitState) { TestkitResponse testkitResponse = createResponse(REACTIVE_SKIP_PATTERN_TO_CHECK); - return Mono.fromCompletionStage(CompletableFuture.completedFuture(testkitResponse)); + return Mono.just(testkitResponse); } private TestkitResponse createResponse(Map skipPatternToCheck) { diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java index 606c2e7499..71286416d7 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java @@ -180,7 +180,7 @@ public Mono processRx(TestkitState testkitState) { } else { testkitResponse = RunTest.builder().build(); } - return Mono.fromCompletionStage(CompletableFuture.completedFuture(testkitResponse)); + return Mono.just(testkitResponse); } @Override @@ -191,7 +191,7 @@ public Mono processReactive(TestkitState testkitState) { } else { testkitResponse = RunTest.builder().build(); } - return Mono.fromCompletionStage(CompletableFuture.completedFuture(testkitResponse)); + return Mono.just(testkitResponse); } private TestkitResponse createSkipResponse(Map skipPatternToReason) { From b37794137eacb72b6046c203d72060f1ad68e2e9 Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Tue, 5 Jul 2022 17:31:20 +0200 Subject: [PATCH 04/10] Code style: use Optional for `createSkipResponse` Signed-off-by: Dmitriy Tverdiakov --- .../backend/messages/requests/StartTest.java | 50 ++++++++----------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java index 71286416d7..794c14c566 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import lombok.Getter; @@ -151,59 +152,48 @@ public class StartTest implements TestkitRequest { @Override public TestkitResponse process(TestkitState testkitState) { - TestkitResponse testkitResponse = createSkipResponse(COMMON_SKIP_PATTERN_TO_REASON); - if (testkitResponse != null) { - return testkitResponse; - } - if (StartSubTest.decidePerSubTest(data.getTestName())) { - return RunSubTests.builder().build(); - } - return RunTest.builder().build(); + return createSkipResponse(COMMON_SKIP_PATTERN_TO_REASON) + .orElseGet(() -> StartSubTest.decidePerSubTestReactive(data.getTestName()) + ? RunSubTests.builder().build() + : RunTest.builder().build()); } @Override public CompletionStage processAsync(TestkitState testkitState) { - TestkitResponse testkitResponse = createSkipResponse(ASYNC_SKIP_PATTERN_TO_REASON); - if (testkitResponse == null && StartSubTest.decidePerSubTestAsync(data.getTestName())) { - testkitResponse = RunSubTests.builder().build(); - } else { - testkitResponse = RunTest.builder().build(); - } + TestkitResponse testkitResponse = createSkipResponse(ASYNC_SKIP_PATTERN_TO_REASON) + .orElseGet(() -> StartSubTest.decidePerSubTestReactive(data.getTestName()) + ? RunSubTests.builder().build() + : RunTest.builder().build()); return CompletableFuture.completedFuture(testkitResponse); } @Override public Mono processRx(TestkitState testkitState) { - TestkitResponse testkitResponse = createSkipResponse(REACTIVE_LEGACY_SKIP_PATTERN_TO_REASON); - if (testkitResponse == null && StartSubTest.decidePerSubTestReactiveLegacy(data.getTestName())) { - testkitResponse = RunSubTests.builder().build(); - } else { - testkitResponse = RunTest.builder().build(); - } + TestkitResponse testkitResponse = createSkipResponse(REACTIVE_LEGACY_SKIP_PATTERN_TO_REASON) + .orElseGet(() -> StartSubTest.decidePerSubTestReactive(data.getTestName()) + ? RunSubTests.builder().build() + : RunTest.builder().build()); return Mono.just(testkitResponse); } @Override public Mono processReactive(TestkitState testkitState) { - TestkitResponse testkitResponse = createSkipResponse(REACTIVE_SKIP_PATTERN_TO_REASON); - if (testkitResponse == null && StartSubTest.decidePerSubTestReactive(data.getTestName())) { - testkitResponse = RunSubTests.builder().build(); - } else { - testkitResponse = RunTest.builder().build(); - } + TestkitResponse testkitResponse = createSkipResponse(REACTIVE_SKIP_PATTERN_TO_REASON) + .orElseGet(() -> StartSubTest.decidePerSubTestReactive(data.getTestName()) + ? RunSubTests.builder().build() + : RunTest.builder().build()); return Mono.just(testkitResponse); } - private TestkitResponse createSkipResponse(Map skipPatternToReason) { + private Optional createSkipResponse(Map skipPatternToReason) { return skipPatternToReason.entrySet().stream() .filter(entry -> data.getTestName().matches(entry.getKey())) .findFirst() - .map(entry -> (TestkitResponse) SkipTest.builder() + .map(entry -> SkipTest.builder() .data(SkipTest.SkipTestBody.builder() .reason(entry.getValue()) .build()) - .build()) - .orElse(null); + .build()); } @Setter From 1bb3294b0154d3b6bdaa84e503d320898940082b Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Tue, 5 Jul 2022 17:52:51 +0200 Subject: [PATCH 05/10] Invert logic and throw sooner on parsing temporal TestKit types --- .../deserializer/types/CypherDateTime.java | 21 +++++++++++-------- .../deserializer/types/CypherTime.java | 6 +++--- .../deserializer/types/CypherType.java | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherDateTime.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherDateTime.java index 5888cbc1be..6706ad79ed 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherDateTime.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherDateTime.java @@ -18,7 +18,6 @@ */ package neo4j.org.testkit.backend.messages.requests.deserializer.types; -import java.time.DateTimeException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; @@ -55,17 +54,21 @@ public CypherDateTime( @Override public Value asValue() { - if (offset == null) { - return new LocalDateTimeValue(LocalDateTime.of(year, month, day, hour, minute, second, nano)); + if (zoneId != null) { + ZonedDateTime dateTime = ZonedDateTime.of(year, month, day, hour, minute, second, nano, ZoneId.of(zoneId)); + if (dateTime.getOffset().getTotalSeconds() != offset) { + throw new RuntimeException(String.format( + "TestKit's and driver's tz info diverge. " + + "TestKit assumes %ds offset for %s while the driver assumes %ds for %s.", + offset, zoneId, dateTime.getOffset().getTotalSeconds(), dateTime)); + } + return new DateTimeValue(dateTime); } - if (zoneId == null) { + + if (offset != null) { ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(offset); return new DateTimeValue(ZonedDateTime.of(year, month, day, hour, minute, second, nano, zoneOffset)); } - try { - return new DateTimeValue(ZonedDateTime.of(year, month, day, hour, minute, second, nano, ZoneId.of(zoneId))); - } catch (DateTimeException e) { - return null; - } + return new LocalDateTimeValue(LocalDateTime.of(year, month, day, hour, minute, second, nano)); } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherTime.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherTime.java index 92ac1a690a..34f6156d76 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherTime.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherTime.java @@ -39,9 +39,9 @@ public CypherTime(int hour, int minute, int second, int nanosecond, Integer offs @Override public Value asValue() { - if (offset == null) { - return new LocalTimeValue(LocalTime.of(hour, minute, second, nano)); + if (offset != null) { + return new TimeValue(OffsetTime.of(hour, minute, second, nano, ZoneOffset.ofTotalSeconds(offset))); } - return new TimeValue(OffsetTime.of(hour, minute, second, nano, ZoneOffset.ofTotalSeconds(offset))); + return new LocalTimeValue(LocalTime.of(hour, minute, second, nano)); } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherType.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherType.java index b50bddf7f3..208de86b7e 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherType.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/types/CypherType.java @@ -21,5 +21,5 @@ import org.neo4j.driver.Value; public interface CypherType { - public Value asValue(); + Value asValue(); } From c76699e5bbb32f5fd11287741a13748da861ccbf Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Tue, 5 Jul 2022 17:55:58 +0200 Subject: [PATCH 06/10] Be more strict when parsing temporal TestKit types --- .../requests/deserializer/TestkitCypherTypeMapper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTypeMapper.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTypeMapper.java index cb38f06c89..ee97479391 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTypeMapper.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTypeMapper.java @@ -66,7 +66,9 @@ private void setField(Object data, String fieldName, Object value) { Field field = data.getClass().getDeclaredField(fieldName); field.set(data, value); } catch (NoSuchFieldException | IllegalAccessException e) { - // ignored + throw new RuntimeException(String.format( + "Received unexpected TestKit data field %s while parsing into %s", + fieldName, data.getClass().getName())); } } } From b57efc93edf8b4b1c8f85e17a667fbe78fee2d8d Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Tue, 5 Jul 2022 18:42:24 +0200 Subject: [PATCH 07/10] Move inner classes to bottom of their parent class' body --- .../TestkitCypherDateDeserializer.java | 12 +++++----- .../TestkitCypherDateTimeDeserializer.java | 24 +++++++++---------- .../TestkitCypherDurationDeserializer.java | 12 +++++----- .../TestkitCypherTimeDeserializer.java | 14 +++++------ 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateDeserializer.java index 43a39e535a..7d4247f7ef 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateDeserializer.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateDeserializer.java @@ -30,12 +30,6 @@ public class TestkitCypherDateDeserializer extends StdDeserializer { private final TestkitCypherTypeMapper mapper; - private static final class CypherDateData { - Integer year; - Integer month; - Integer day; - } - public TestkitCypherDateDeserializer() { super(Date.class); mapper = new TestkitCypherTypeMapper(); @@ -46,4 +40,10 @@ public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) CypherDateData data = mapper.mapData(p, ctxt, new CypherDateData()); return LocalDate.of(data.year, data.month, data.day); } + + private static final class CypherDateData { + Integer year; + Integer month; + Integer day; + } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateTimeDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateTimeDeserializer.java index 97bc1cf6df..97e59e7210 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateTimeDeserializer.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDateTimeDeserializer.java @@ -33,18 +33,6 @@ public TestkitCypherDateTimeDeserializer() { mapper = new TestkitCypherTypeMapper(); } - private static final class CypherDateTimeData { - Integer year; - Integer month; - Integer day; - Integer hour; - Integer minute; - Integer second; - Integer nanosecond; - Integer utc_offset_s; - String timezone_id; - } - @Override public CypherDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { @@ -60,4 +48,16 @@ public CypherDateTime deserialize(JsonParser p, DeserializationContext ctxt) data.timezone_id, data.utc_offset_s); } + + private static final class CypherDateTimeData { + Integer year; + Integer month; + Integer day; + Integer hour; + Integer minute; + Integer second; + Integer nanosecond; + Integer utc_offset_s; + String timezone_id; + } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDurationDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDurationDeserializer.java index 472d172b18..b1d7fb49bf 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDurationDeserializer.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherDurationDeserializer.java @@ -34,16 +34,16 @@ public TestkitCypherDurationDeserializer() { mapper = new TestkitCypherTypeMapper(); } + public IsoDuration deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + CypherDurationData data = mapper.mapData(p, ctxt, new CypherDurationData()); + return new InternalIsoDuration(data.months, data.days, data.seconds, data.nanoseconds); + } + private static final class CypherDurationData { Long months; Long days; Long seconds; Integer nanoseconds; } - - public IsoDuration deserialize(JsonParser p, DeserializationContext ctxt) - throws IOException, JsonProcessingException { - CypherDurationData data = mapper.mapData(p, ctxt, new CypherDurationData()); - return new InternalIsoDuration(data.months, data.days, data.seconds, data.nanoseconds); - } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTimeDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTimeDeserializer.java index b66d012f45..1717568b5c 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTimeDeserializer.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/deserializer/TestkitCypherTimeDeserializer.java @@ -33,6 +33,13 @@ public TestkitCypherTimeDeserializer() { mapper = new TestkitCypherTypeMapper(); } + @Override + public CypherTime deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + CypherTimeData data = mapper.mapData(p, ctxt, new CypherTimeData()); + return new CypherTime(data.hour, data.minute, data.second, data.nanosecond, data.utc_offset_s); + } + private static final class CypherTimeData { Integer hour; Integer minute; @@ -40,11 +47,4 @@ private static final class CypherTimeData { Integer nanosecond; Integer utc_offset_s; } - - @Override - public CypherTime deserialize(JsonParser p, DeserializationContext ctxt) - throws IOException, JsonProcessingException { - CypherTimeData data = mapper.mapData(p, ctxt, new CypherTimeData()); - return new CypherTime(data.hour, data.minute, data.second, data.nanosecond, data.utc_offset_s); - } } From 1ce7a0b5f3d29a3efee6a89cf406cad33241fe8d Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Tue, 5 Jul 2022 18:43:11 +0200 Subject: [PATCH 08/10] Make backend handle (de-)serialization Exceptions gracefully --- .../TestkitRequestResponseMapperHandler.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java index 9afc93e308..19d01d9627 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java @@ -18,7 +18,6 @@ */ package neo4j.org.testkit.backend.channel.handler; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.channel.ChannelDuplexHandler; @@ -26,20 +25,17 @@ import io.netty.channel.ChannelPromise; import neo4j.org.testkit.backend.messages.TestkitModule; import neo4j.org.testkit.backend.messages.requests.TestkitRequest; +import neo4j.org.testkit.backend.messages.responses.BackendError; import neo4j.org.testkit.backend.messages.responses.TestkitResponse; public class TestkitRequestResponseMapperHandler extends ChannelDuplexHandler { private final ObjectMapper objectMapper = newObjectMapper(); @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String testkitMessage = (String) msg; TestkitRequest testkitRequest; - try { - testkitRequest = objectMapper.readValue(testkitMessage, TestkitRequest.class); - } catch (JsonProcessingException e) { - throw new RuntimeException("Failed to deserialize Testkit message", e); - } + testkitRequest = objectMapper.readValue(testkitMessage, TestkitRequest.class); ctx.fireChannelRead(testkitRequest); } @@ -50,6 +46,16 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) ctx.writeAndFlush(responseStr, promise); } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + TestkitResponse response = BackendError.builder() + .data(BackendError.BackendErrorBody.builder() + .msg(cause.toString()) + .build()) + .build(); + ctx.writeAndFlush(objectMapper.writeValueAsString(response)); + } + public static ObjectMapper newObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); TestkitModule testkitModule = new TestkitModule(); From ce83ef55bef3731d6f21643bcfa8a2904cbe8e76 Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Wed, 6 Jul 2022 08:19:55 +0200 Subject: [PATCH 09/10] Move `createErrorResponse` to the right handler in the pipeline --- .../handler/TestkitRequestProcessorHandler.java | 7 ++++++- .../handler/TestkitRequestResponseMapperHandler.java | 11 ----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java index a78ad6d6f0..ffd6a73bb5 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java @@ -87,7 +87,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } }); } catch (Throwable throwable) { - ctx.writeAndFlush(createErrorResponse(throwable)); + exceptionCaught(ctx, throwable); } }); } @@ -103,6 +103,11 @@ private static CompletionStage wrapSyncRequest( return result; } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + ctx.writeAndFlush(createErrorResponse(cause)); + } + private TestkitResponse createErrorResponse(Throwable throwable) { if (throwable instanceof CompletionException) { throwable = throwable.getCause(); diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java index 19d01d9627..321feba4ce 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java @@ -25,7 +25,6 @@ import io.netty.channel.ChannelPromise; import neo4j.org.testkit.backend.messages.TestkitModule; import neo4j.org.testkit.backend.messages.requests.TestkitRequest; -import neo4j.org.testkit.backend.messages.responses.BackendError; import neo4j.org.testkit.backend.messages.responses.TestkitResponse; public class TestkitRequestResponseMapperHandler extends ChannelDuplexHandler { @@ -46,16 +45,6 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) ctx.writeAndFlush(responseStr, promise); } - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - TestkitResponse response = BackendError.builder() - .data(BackendError.BackendErrorBody.builder() - .msg(cause.toString()) - .build()) - .build(); - ctx.writeAndFlush(objectMapper.writeValueAsString(response)); - } - public static ObjectMapper newObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); TestkitModule testkitModule = new TestkitModule(); From 424449b3af77cd47fc5f71c16a56d1e61b02e9c7 Mon Sep 17 00:00:00 2001 From: Rouven Bauer Date: Wed, 6 Jul 2022 08:22:11 +0200 Subject: [PATCH 10/10] Remove leftover debug print --- .../org/testkit/backend/messages/requests/StartSubTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java index 7232efe93e..86544c2994 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartSubTest.java @@ -176,8 +176,7 @@ public Mono processReactive(TestkitState testkitState) { } private TestkitResponse createResponse(Map skipPatternToCheck) { - System.out.println(data.getTestName()); - return (TestkitResponse) skipPatternToCheck.entrySet().stream() + return skipPatternToCheck.entrySet().stream() .filter(entry -> data.getTestName().matches(entry.getKey())) .findFirst() .map(entry -> {