From 093e274a1e1acc7dd6a96ea7603cd7207c2b1d09 Mon Sep 17 00:00:00 2001 From: Ralf Ueberfuhr Date: Fri, 6 Sep 2024 15:02:29 +0200 Subject: [PATCH] Refactor Continuous Testing RPC service and UI - merge observers for test state and results - replace direct rendering of results and state by custom API object (DTO) - optimize size and structure of JSON message resolves #43067 --- .../dev/testing/TestClassResult.java | 25 +- .../deployment/dev/testing/TestResult.java | 27 ++- .../dev/testing/TestRunResults.java | 25 +- .../results/TestClassResultInterface.java | 16 ++ .../testing/results/TestResultInterface.java | 34 +++ .../results/TestRunResultsInterface.java | 31 +++ .../menu/ContinuousTestingProcessor.java | 3 +- .../dev-ui/qwc/qwc-continuous-testing.js | 214 ++++++++--------- .../ContinuousTestingJsonRPCService.java | 102 +++++--- .../ContinuousTestingJsonRPCState.java | 217 ++++++++++++++++++ 10 files changed, 522 insertions(+), 172 deletions(-) create mode 100644 core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestClassResultInterface.java create mode 100644 core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestResultInterface.java create mode 100644 core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestRunResultsInterface.java create mode 100644 extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/continuoustesting/ContinuousTestingJsonRPCState.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestClassResult.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestClassResult.java index da4df8a928f7d3..3025d52a6164bb 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestClassResult.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestClassResult.java @@ -3,7 +3,10 @@ import java.util.ArrayList; import java.util.List; -public class TestClassResult implements Comparable { +import io.quarkus.dev.testing.results.TestClassResultInterface; +import io.quarkus.dev.testing.results.TestResultInterface; + +public class TestClassResult implements TestClassResultInterface { final String className; final List passing; final List failing; @@ -11,7 +14,11 @@ public class TestClassResult implements Comparable { final long latestRunId; final long time; - public TestClassResult(String className, List passing, List failing, List skipped, + public TestClassResult( + String className, + List passing, + List failing, + List skipped, long time) { this.className = className; this.passing = passing; @@ -19,15 +26,16 @@ public TestClassResult(String className, List passing, List getResults() { List ret = new ArrayList<>(); ret.addAll(passing); @@ -64,4 +68,5 @@ public List getResults() { ret.addAll(skipped); return ret; } + } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestResult.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestResult.java index 4f5025391a0b3a..f5d9994f790542 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestResult.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestResult.java @@ -7,7 +7,9 @@ import org.junit.platform.engine.TestExecutionResult; import org.junit.platform.engine.UniqueId; -public class TestResult { +import io.quarkus.dev.testing.results.TestResultInterface; + +public class TestResult implements TestResultInterface { final String displayName; final String testClass; @@ -49,18 +51,22 @@ public TestExecutionResult getTestExecutionResult() { return testExecutionResult; } + @Override public List getLogOutput() { return logOutput; } + @Override public String getDisplayName() { return displayName; } + @Override public String getTestClass() { return testClass; } + @Override public List getTags() { return tags; } @@ -69,23 +75,42 @@ public UniqueId getUniqueId() { return uniqueId; } + @Override public boolean isTest() { return test; } + @Override + public String getId() { + return uniqueId.toString(); + } + + @Override public long getRunId() { return runId; } + @Override public long getTime() { return time; } + @Override public List getProblems() { return problems; } + @Override public boolean isReportable() { return reportable; } + + @Override + public State getState() { + return switch (testExecutionResult.getStatus()) { + case FAILED -> State.FAILED; + case ABORTED -> State.SKIPPED; + case SUCCESSFUL -> State.PASSED; + }; + } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunResults.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunResults.java index e4a96544f492f6..fea7f082621d0f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunResults.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunResults.java @@ -7,8 +7,10 @@ import java.util.Map; import io.quarkus.deployment.dev.ClassScanResult; +import io.quarkus.dev.testing.results.TestResultInterface; +import io.quarkus.dev.testing.results.TestRunResultsInterface; -public class TestRunResults { +public class TestRunResults implements TestRunResultsInterface { /** * The run id @@ -28,7 +30,7 @@ public class TestRunResults { private final long started; private final long completed; - private final Map results; + private final Map results; private final Map currentFailing = new HashMap<>(); private final Map historicFailing = new HashMap<>(); private final Map currentPassing = new HashMap<>(); @@ -58,9 +60,9 @@ public TestRunResults(long id, ClassScanResult trigger, boolean full, long start long currentFailedCount = 0; long currentSkippedCount = 0; for (Map.Entry i : results.entrySet()) { - passedCount += i.getValue().getPassing().stream().filter(TestResult::isTest).count(); - failedCount += i.getValue().getFailing().stream().filter(TestResult::isTest).count(); - skippedCount += i.getValue().getSkipped().stream().filter(TestResult::isTest).count(); + passedCount += i.getValue().getPassing().stream().filter(TestResultInterface::isTest).count(); + failedCount += i.getValue().getFailing().stream().filter(TestResultInterface::isTest).count(); + skippedCount += i.getValue().getSkipped().stream().filter(TestResultInterface::isTest).count(); currentPassedCount += i.getValue().getPassing().stream().filter(s -> s.isTest() && s.getRunId() == id).count(); currentFailedCount += i.getValue().getFailing().stream().filter(s -> s.isTest() && s.getRunId() == id).count(); currentSkippedCount += i.getValue().getSkipped().stream().filter(s -> s.isTest() && s.getRunId() == id).count(); @@ -98,6 +100,7 @@ public TestRunResults(long id, ClassScanResult trigger, boolean full, long start this.currentSkippedCount = currentSkippedCount; } + @Override public long getId() { return id; } @@ -110,6 +113,7 @@ public boolean isFull() { return full; } + @Override public Map getResults() { return Collections.unmodifiableMap(results); } @@ -130,14 +134,17 @@ public Map getHistoricPassing() { return historicPassing; } + @Override public long getStartedTime() { return started; } + @Override public long getCompletedTime() { return completed; } + @Override public long getTotalTime() { return completed - started; } @@ -154,34 +161,42 @@ public List getSkipped() { return skipped; } + @Override public long getPassedCount() { return passedCount; } + @Override public long getFailedCount() { return failedCount; } + @Override public long getSkippedCount() { return skippedCount; } + @Override public long getCurrentPassedCount() { return currentPassedCount; } + @Override public long getCurrentFailedCount() { return currentFailedCount; } + @Override public long getCurrentSkippedCount() { return currentSkippedCount; } + @Override public long getTotalCount() { return getPassedCount() + getFailedCount() + getSkippedCount(); } + @Override public long getCurrentTotalCount() { return getCurrentPassedCount() + getCurrentFailedCount() + getCurrentSkippedCount(); } diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestClassResultInterface.java b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestClassResultInterface.java new file mode 100644 index 00000000000000..ec482003a62457 --- /dev/null +++ b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestClassResultInterface.java @@ -0,0 +1,16 @@ +package io.quarkus.dev.testing.results; + +import java.util.List; + +public interface TestClassResultInterface extends Comparable { + + String getClassName(); + + List getResults(); + + @Override + default int compareTo(TestClassResultInterface o) { + return getClassName().compareTo(o.getClassName()); + } + +} diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestResultInterface.java b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestResultInterface.java new file mode 100644 index 00000000000000..8509d68fa219bc --- /dev/null +++ b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestResultInterface.java @@ -0,0 +1,34 @@ +package io.quarkus.dev.testing.results; + +import java.util.List; + +public interface TestResultInterface { + List getLogOutput(); + + String getDisplayName(); + + String getTestClass(); + + List getTags(); + + boolean isTest(); + + String getId(); + + long getRunId(); + + long getTime(); + + List getProblems(); + + boolean isReportable(); + + State getState(); + + enum State { + PASSED, + FAILED, + SKIPPED + } + +} diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestRunResultsInterface.java b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestRunResultsInterface.java new file mode 100644 index 00000000000000..816703d9df2bb8 --- /dev/null +++ b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/results/TestRunResultsInterface.java @@ -0,0 +1,31 @@ +package io.quarkus.dev.testing.results; + +import java.util.Map; + +public interface TestRunResultsInterface { + long getId(); + + Map getResults(); + + long getStartedTime(); + + long getCompletedTime(); + + long getTotalTime(); + + long getPassedCount(); + + long getFailedCount(); + + long getSkippedCount(); + + long getCurrentPassedCount(); + + long getCurrentFailedCount(); + + long getCurrentSkippedCount(); + + long getTotalCount(); + + long getCurrentTotalCount(); +} diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ContinuousTestingProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ContinuousTestingProcessor.java index 69eaa535b3961f..f1a7e1c0531b1c 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ContinuousTestingProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ContinuousTestingProcessor.java @@ -16,6 +16,7 @@ import io.quarkus.deployment.dev.testing.TestRunResults; import io.quarkus.deployment.dev.testing.TestSupport; import io.quarkus.dev.spi.DevModeType; +import io.quarkus.dev.testing.results.TestRunResultsInterface; import io.quarkus.devui.deployment.InternalPageBuildItem; import io.quarkus.devui.runtime.continuoustesting.ContinuousTestingJsonRPCService; import io.quarkus.devui.runtime.continuoustesting.ContinuousTestingRecorder; @@ -237,7 +238,7 @@ private void registerGetStatusMethod(LaunchModeBuildItem launchModeBuildItem, Bu } private void registerGetResultsMethod(LaunchModeBuildItem launchModeBuildItem, BuildTimeActionBuildItem actions) { - actions.addAction("getResults", ignored -> { + actions. addAction("getResults", ignored -> { try { Optional ts = TestSupport.instance(); if (testsDisabled(launchModeBuildItem, ts)) { diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-continuous-testing.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-continuous-testing.js index e255967f68e0ea..9e4a4508b7931c 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-continuous-testing.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-continuous-testing.js @@ -78,7 +78,6 @@ export class QwcContinuousTesting extends QwcHotReloadElement { static properties = { _state: {state: true}, - _results: {state: true}, _busy: {state: true}, _detailsOpenedItem: {state: true, type: Array}, _displayTags: {state: true, type: Boolean}, @@ -93,6 +92,45 @@ export class QwcContinuousTesting extends QwcHotReloadElement { this._displayTags = true; } + set _tests(value) { + this._state = value; + this._state?.result?.passed?.forEach(item => item.style = "successful"); + this._state?.result?.failed?.forEach(item => item.style = "failed"); + this._state?.result?.skipped?.forEach(item => item.style = "aborted"); + } + + get _tests() { + return this._state; + } + + get _testsReceived() { + return this._tests != null; + } + + get _testsEnabled() { + return this._tests?.config?.enabled ?? false; + } + + get _testsCurrentlyRunning() { + return this._tests?.inProgress ?? false; + } + + get _testResult() { + return this._tests?.result; + } + + get _hasTestResult() { + return (this._testResult?.counts?.total ?? 0) > 0 + } + + get _hasFailedTestResult() { + return (this._testResult?.counts?.failed ?? 0) > 0; + } + + get _hasTestResultWithTags() { + return (this._testResult?.tags?.length ?? 0) > 0; + } + connectedCallback() { super.connectedCallback(); this._lastKnownState(); @@ -105,11 +143,8 @@ export class QwcContinuousTesting extends QwcHotReloadElement { } _createObservers(){ - this._streamStateObserver = this.jsonRpc.streamTestState().onNext(jsonRpcResponse => { - this._state = JSON.parse(jsonRpcResponse.result); - }); - this._streamResultsObserver = this.jsonRpc.streamTestResults().onNext(jsonRpcResponse => { - this._results = jsonRpcResponse.result; + this._streamStateObserver = this.jsonRpc.streamState().onNext(jsonRpcResponse => { + this._tests = jsonRpcResponse.result; }); } @@ -120,13 +155,8 @@ export class QwcContinuousTesting extends QwcHotReloadElement { _lastKnownState(){ // Get last known - this.jsonRpc.lastKnownState().then(jsonRpcResponse => { - this._state = JSON.parse(jsonRpcResponse.result); - }); - this.jsonRpc.lastKnownResults().then(jsonRpcResponse => { - if(jsonRpcResponse.result){ - this._results = jsonRpcResponse.result; - } + this.jsonRpc.currentState().then(jsonRpcResponse => { + this._tests = jsonRpcResponse.result; }); } @@ -137,77 +167,36 @@ export class QwcContinuousTesting extends QwcHotReloadElement { } render() { - let results = this._prepareResultsToRender(); return html` - ${this._renderMenuBar(results)} - ${this._renderResults(results)} + ${this._renderMenuBar()} + ${this._renderResults()} ${this._renderBarChart()} `; } - _prepareResultsToRender() { - if(this._state && this._state.running && this._results && this._results.results) { - let itemsByState = { - passing: [], - failing: [], - skipped: [], - } - Object - .values(this._results.results) - .forEach(item => { - itemsByState.passing.push(...item.passing.filter( obj => obj.test === true )); - itemsByState.failing.push(...item.failing.filter( obj => obj.test === true )); - itemsByState.skipped.push(...item.skipped.filter( obj => obj.test === true )); - }); - let items = itemsByState.failing.concat( - itemsByState.passing, - itemsByState.skipped - ); - let hasTags = items.find( item => item.tags && item.tags.length > 0 ); - return { - items: items, - meta: { - hasTags: hasTags, - failing: itemsByState.failing.length, - passing: itemsByState.passing.length, - skipped: itemsByState.skipped.length, - }, - } - } else { - return { - items: [], - meta: { - hasTags: false, - failing: 0, - passing: 0, - skipped: 0, - }, - }; - } - } - - _renderMenuBar(results){ - if(this._state){ - return html`