From cdb7a1ca0f7423086c1cc0ab4bec3ac9cc698df1 Mon Sep 17 00:00:00 2001 From: Marco Bungart Date: Sat, 7 Sep 2024 01:35:50 +0200 Subject: [PATCH] #4: Fix progress bar and console reporting. The RemoteTestResultReporter is unsuitable for a "live update" of test state. Switching to a test listener, however, brings the desired behaviour. To get the missing time logging in the console logging, corresponding files and mappings have to be added. There is still an open issue with the logging reporter; in the container, the PERFORMANCE shows "0ms", although each job shows its execution time. --- .github/workflows/.env | 2 + .github/workflows/build.yml | 20 +++ citrus-remote-sample/pom.xml | 2 +- .../remote/sample/test/http/GetTextIT.java | 18 ++- .../remote/CitrusRemoteApplication.java | 142 ++++++------------ .../citrusframework/remote/job/RunJob.java | 56 ++++--- .../remote/listener/RemoteTestListener.java | 127 ++++++++++++++++ .../remote/model/RemoteResult.java | 43 +++++- .../reporter/RemoteTestResultReporter.java | 61 -------- 9 files changed, 289 insertions(+), 182 deletions(-) create mode 100644 citrus-remote-server/src/main/java/org/citrusframework/remote/listener/RemoteTestListener.java delete mode 100644 citrus-remote-server/src/main/java/org/citrusframework/remote/reporter/RemoteTestResultReporter.java diff --git a/.github/workflows/.env b/.github/workflows/.env index 848c74a..0c76aa3 100644 --- a/.github/workflows/.env +++ b/.github/workflows/.env @@ -1,3 +1,5 @@ +CITRUS_REMOTE_LOG_ARTIFACT=citrus-remote-logs + JAR_ARTIFACT_NAME=jars JAVA_DISTRIBUTION=temurin diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2d0c07b..fc750df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,6 +37,7 @@ on: - 'LICENSE' - 'NOTICE' env: + CITRUS_REMOTE_LOGS_ARTIFACT: will-be-read-from-env-file JAVA_DISTRIBUTION: will-be-read-from-env-file JAVA_VERSION: will-be-read-from-env-file JAR_ARTIFACT_NAME: will-be-read-from-env-file @@ -80,6 +81,16 @@ jobs: --no-transfer-progress \ verify + - name: Print Citrus remote Logs + if: ${{ always() }} + run: | + if [[ -f citrus-remote-sample/target/container-logs/citrus.log ]] + then + cat citrus-remote-sample/target/container-logs/citrus.log + else + echo "(log file 'citrus-remote-sample/target/container-logs/citrus.log' not found)" + fi + - name: Upload JARs uses: actions/upload-artifact@v4 if: ${{ always() }} @@ -101,6 +112,15 @@ jobs: **/target/citrus-remote/junitreports/TEST*.xml retention-days: 2 + - name: Upload Citrus remote log + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + if-no-files-found: error + name: ${{ env.CITRUS_REMOTE_LOGS_ARTIFACT }} + path: citrus-remote-sample/target/container-logs/citrus.log + retention-days: 2 + - name: Get PR number id: get-pr-number if: ${{ always() && github.event_name == 'pull_request' }} diff --git a/citrus-remote-sample/pom.xml b/citrus-remote-sample/pom.xml index e629162..8c332c9 100644 --- a/citrus-remote-sample/pom.xml +++ b/citrus-remote-sample/pom.xml @@ -77,7 +77,7 @@ testng true - 15000 + 1000 ${citrus.remote.report.directory} diff --git a/citrus-remote-sample/src/test/java/org/citrusframework/remote/sample/test/http/GetTextIT.java b/citrus-remote-sample/src/test/java/org/citrusframework/remote/sample/test/http/GetTextIT.java index bd20803..5a2f42e 100644 --- a/citrus-remote-sample/src/test/java/org/citrusframework/remote/sample/test/http/GetTextIT.java +++ b/citrus-remote-sample/src/test/java/org/citrusframework/remote/sample/test/http/GetTextIT.java @@ -6,17 +6,27 @@ import org.citrusframework.testng.spring.TestNGCitrusSpringSupport; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.testng.annotations.DataProvider; import org.testng.annotations.Optional; import org.testng.annotations.Test; +import static org.citrusframework.actions.SleepAction.Builder.sleep; import static org.citrusframework.container.Async.Builder.async; import static org.citrusframework.http.actions.HttpActionBuilder.http; public class GetTextIT extends TestNGCitrusSpringSupport { - @Test + @DataProvider + public Object[][] body() { + return new Object[][]{ + {null, "foo"}, + {null, "bar"}, + {null, "citrus:randomString(10, MIXED, true)"}}; + } + + @Test(dataProvider = "body") @CitrusTest - public void test(@Optional @CitrusResource TestCaseRunner runner) { - runner.variable("body", "citrus:randomString(10, MIXED, true)"); + public void test(@Optional @CitrusResource TestCaseRunner runner, String body) { + runner.variable("body", body); runner.given(async().actions( http().server("httpServer") .receive() @@ -44,5 +54,7 @@ public void test(@Optional @CitrusResource TestCaseRunner runner) { .message() .contentType(MediaType.TEXT_PLAIN_VALUE) .body("${body}")); + + runner.$(sleep().seconds(2)); } } diff --git a/citrus-remote-server/src/main/java/org/citrusframework/remote/CitrusRemoteApplication.java b/citrus-remote-server/src/main/java/org/citrusframework/remote/CitrusRemoteApplication.java index d0749b5..ba0fb82 100644 --- a/citrus-remote-server/src/main/java/org/citrusframework/remote/CitrusRemoteApplication.java +++ b/citrus-remote-server/src/main/java/org/citrusframework/remote/CitrusRemoteApplication.java @@ -23,6 +23,7 @@ import io.vertx.core.MultiMap; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RequestBody; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.BodyHandler; @@ -32,10 +33,9 @@ import org.citrusframework.TestClass; import org.citrusframework.main.CitrusAppConfiguration; import org.citrusframework.main.TestRunConfiguration; -import org.citrusframework.remote.controller.RunController; import org.citrusframework.remote.job.RunJob; import org.citrusframework.remote.model.RemoteResult; -import org.citrusframework.remote.reporter.RemoteTestResultReporter; +import org.citrusframework.remote.listener.RemoteTestListener; import org.citrusframework.remote.transformer.JsonRequestTransformer; import org.citrusframework.remote.transformer.JsonResponseTransformer; import org.citrusframework.report.JUnitReporter; @@ -48,7 +48,6 @@ import java.net.URLDecoder; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -84,8 +83,8 @@ public class CitrusRemoteApplication extends AbstractVerticle { private Future> remoteResultFuture; /** Latest test reports */ - private final RemoteTestResultReporter remoteTestResultReporter = - new RemoteTestResultReporter(); + private final RemoteTestListener remoteTestListener = + new RemoteTestListener(); /** Router customizations */ private final List> routerCustomizations; @@ -111,16 +110,13 @@ public CitrusRemoteApplication( @Override public void start() { CitrusInstanceManager.mode(CitrusInstanceStrategy.SINGLETON); - CitrusInstanceManager.addInstanceProcessor(citrus -> - citrus.addTestReporter(remoteTestResultReporter)); + CitrusInstanceManager + .addInstanceProcessor(citrus -> citrus.addTestListener(remoteTestListener)); Router router = Router.router(getVertx()); router.route().handler(BodyHandler.create()); router.route().handler(ctx -> { - logger.info( - "{} {}", - ctx.request().method(), - ctx.request().uri()); + logger.info("{} {}", ctx.request().method(), ctx.request().uri()); ctx.next(); }); addHealthEndpoint(router); @@ -140,7 +136,8 @@ public void start() { private static void addHealthEndpoint(Router router) { router.get("/health") .handler(wrapThrowingHandler(ctx -> - ctx.response().putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) + ctx.response() + .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) .end("{ \"status\": \"UP\" }"))); } @@ -178,17 +175,17 @@ private void addResultsEndpoints(Router router) { response.end(responseTransformer.render(results))) .onFailure(throwable -> response .setStatusCode(HttpResponseStatus.PARTIAL_CONTENT.code()) - .end(responseTransformer.render(Collections.emptyList()))); + .end(responseTransformer + .render(remoteTestListener.toRemoteResults()))); } else { - final List results = new ArrayList<>(); - remoteTestResultReporter.getLatestResults().doWithResults(result -> - results.add(RemoteResult.fromTestResult(result))); + final List results = remoteTestListener.toRemoteResults(); + logger.info("results = {}", results.size()); response.end(responseTransformer.render(results)); } })); router.get("/results") - .handler(ctx -> ctx.response().end( - responseTransformer.render(remoteTestResultReporter.getTestReport()))); + .handler(ctx -> ctx.response() + .end(responseTransformer.render(remoteTestListener.generateTestReport()))); router.get("/results/files") .handler(wrapThrowingHandler(ctx -> { File junitReportsFolder = new File(getJUnitReportsFolder()); @@ -239,31 +236,37 @@ private void addResultsEndpoints(Router router) { private void addRunEndpoints(Router router) { router.get("/run") - .handler(wrapThrowingHandler(ctx -> { - TestRunConfiguration runConfiguration = constructRunConfig(ctx); - runTestsAsync(runConfiguration, ctx.response()); - })); + .handler(wrapThrowingHandler(ctx -> + runTestsAsync(constructRunConfig(ctx.request().params()), ctx.response()))); + router.post("/run") + .handler(wrapThrowingHandler(ctx -> + runTestsAsync(constructRunConfig(ctx.body()), ctx.response()))); router.put("/run") .handler(wrapThrowingHandler(ctx -> { - remoteResultFuture = Future.fromCompletionStage(CompletableFuture.supplyAsync( - constructTestRun(ctx)::call, - executorService)); + remoteTestListener.reset(); + remoteResultFuture = startTestAsync(constructRunConfig(ctx.body())); ctx.response().end(""); })); - router.post("/run") - .handler(wrapThrowingHandler(ctx -> { - HttpServerResponse response = ctx.response(); - TestRunConfiguration runConfiguration = requestTransformer.read( - ctx.body().asString(), - TestRunConfiguration.class); - runTestsAsync(runConfiguration, response); - })); } - private TestRunConfiguration constructRunConfig(RoutingContext ctx) + public static Handler wrapThrowingHandler( + ThrowingHandler handler) { + return ctx -> { + try { + handler.handle(ctx); + } catch (Exception e) { + ctx.response() + .setStatusCode(HttpResponseStatus.INTERNAL_SERVER_ERROR.code()) + .end(e.getMessage()); + } + }; + } + + + + private TestRunConfiguration constructRunConfig(MultiMap queryParams) throws UnsupportedEncodingException { TestRunConfiguration runConfiguration = new TestRunConfiguration(); - MultiMap queryParams = ctx.request().params(); if (queryParams.contains("engine")) { String engine = queryParams.get("engine"); runConfiguration.setEngine(URLDecoder.decode(engine, ENCODING)); @@ -291,12 +294,16 @@ private TestRunConfiguration constructRunConfig(RoutingContext ctx) return runConfiguration; } + private TestRunConfiguration constructRunConfig(RequestBody body) { + return requestTransformer.read(body.asString(), TestRunConfiguration.class); + } + private void runTestsAsync( TestRunConfiguration runConfiguration, HttpServerResponse response) { Future .fromCompletionStage(CompletableFuture.supplyAsync( - () -> runTests(runConfiguration), + new RunJob(configuration, runConfiguration, remoteTestListener), executorService)) .onSuccess(results -> response.end(responseTransformer.render(results))) @@ -305,16 +312,10 @@ private void runTestsAsync( .end(error.getMessage())); } - private RunJob constructTestRun(RoutingContext ctx) { - TestRunConfiguration config = requestTransformer.read( - ctx.body().asString(), - TestRunConfiguration.class); - return new RunJob(config) { - @Override - public List run(TestRunConfiguration runConfiguration) { - return runTests(runConfiguration); - } - }; + private Future> startTestAsync(TestRunConfiguration testRunConfiguration) { + return Future.fromCompletionStage(CompletableFuture.supplyAsync( + new RunJob(configuration, testRunConfiguration, remoteTestListener), + executorService)); } private void addConfigEndpoints(Router router) { @@ -330,61 +331,18 @@ private void addConfigEndpoints(Router router) { CitrusAppConfiguration.class)))); } - public static Handler wrapThrowingHandler( - ThrowingHandler handler) { - return ctx -> { - try { - handler.handle(ctx); - } catch (Exception e) { - ctx.response().setStatusCode(HttpResponseStatus.INTERNAL_SERVER_ERROR.code()) - .end(e.getMessage()); - } - }; - } - - /** - * Construct run controller and execute with given configuration. - * @param runConfiguration - * @return remote results - */ - private List runTests(TestRunConfiguration runConfiguration) { - RunController runController = new RunController(configuration); - - runController.setEngine(runConfiguration.getEngine()); - runController.setIncludes(runConfiguration.getIncludes()); - - if (!runConfiguration.getDefaultProperties().isEmpty()) { - runController.addDefaultProperties(runConfiguration.getDefaultProperties()); - } - - if (runConfiguration.getPackages().isEmpty() && runConfiguration.getTestSources().isEmpty()) { - runController.runAll(); - } - - if (!runConfiguration.getPackages().isEmpty()) { - runController.runPackages(runConfiguration.getPackages()); - } - - if (!runConfiguration.getTestSources().isEmpty()) { - runController.runClasses(runConfiguration.getTestSources()); - } - - List results = new ArrayList<>(); - remoteTestResultReporter.getLatestResults().doWithResults(result -> results.add(RemoteResult.fromTestResult(result))); - return results; - } - /** * Find reports folder based in unit testing framework present on classpath. * @return */ private String getJUnitReportsFolder() { - if (isPresent("org.testng.annotations.Test")) { return "test-output" + File.separator + "junitreports"; } else if (isPresent("org.junit.Test")) { JUnitReporter jUnitReporter = new JUnitReporter(); - return jUnitReporter.getReportDirectory() + File.separator + jUnitReporter.getOutputDirectory(); + return jUnitReporter.getReportDirectory() + + File.separator + + jUnitReporter.getOutputDirectory(); } else { return new LoggingReporter().getReportDirectory(); } diff --git a/citrus-remote-server/src/main/java/org/citrusframework/remote/job/RunJob.java b/citrus-remote-server/src/main/java/org/citrusframework/remote/job/RunJob.java index d43acbd..f204716 100644 --- a/citrus-remote-server/src/main/java/org/citrusframework/remote/job/RunJob.java +++ b/citrus-remote-server/src/main/java/org/citrusframework/remote/job/RunJob.java @@ -17,36 +17,52 @@ package org.citrusframework.remote.job; import org.citrusframework.main.TestRunConfiguration; +import org.citrusframework.remote.CitrusRemoteConfiguration; +import org.citrusframework.remote.controller.RunController; import org.citrusframework.remote.model.RemoteResult; +import org.citrusframework.remote.listener.RemoteTestListener; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Callable; +import java.util.function.Supplier; /** * @author Christoph Deppisch * @since 2.7.4 */ -public abstract class RunJob implements Callable> { +public record RunJob ( + CitrusRemoteConfiguration configuration, + TestRunConfiguration runConfiguration, + RemoteTestListener remoteTestListener) + implements Supplier> { - private final TestRunConfiguration runConfiguration; + @Override + public List get() { + RunController runController = new RunController(configuration); - /** - * Default constructor using run configuration. - * @param runConfiguration - */ - protected RunJob(TestRunConfiguration runConfiguration) { - this.runConfiguration = runConfiguration; - } + runController.setEngine(runConfiguration.getEngine()); + runController.setIncludes(runConfiguration.getIncludes()); - @Override - public List call() { - return run(runConfiguration); - } + if (!runConfiguration.getDefaultProperties().isEmpty()) { + runController.addDefaultProperties(runConfiguration.getDefaultProperties()); + } - /** - * Subclasses must implement this method for executing the tests based on given configuration. - * @param runConfiguration - * @return - */ - protected abstract List run(TestRunConfiguration runConfiguration); + if (runConfiguration.getPackages().isEmpty() && + runConfiguration.getTestSources().isEmpty()) { + runController.runAll(); + } + + if (!runConfiguration.getPackages().isEmpty()) { + runController.runPackages(runConfiguration.getPackages()); + } + + if (!runConfiguration.getTestSources().isEmpty()) { + runController.runClasses(runConfiguration.getTestSources()); + } + + List results = new ArrayList<>(); + remoteTestListener.getResults() + .doWithResults(result -> results.add(RemoteResult.fromTestResult(result))); + return results; + } } diff --git a/citrus-remote-server/src/main/java/org/citrusframework/remote/listener/RemoteTestListener.java b/citrus-remote-server/src/main/java/org/citrusframework/remote/listener/RemoteTestListener.java new file mode 100644 index 0000000..62e42e7 --- /dev/null +++ b/citrus-remote-server/src/main/java/org/citrusframework/remote/listener/RemoteTestListener.java @@ -0,0 +1,127 @@ +/* + * Copyright 2006-2018 the original author or authors. + * + * 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 org.citrusframework.remote.listener; + +import org.citrusframework.TestCase; +import org.citrusframework.TestResult; +import org.citrusframework.remote.model.RemoteResult; +import org.citrusframework.report.OutputStreamReporter; +import org.citrusframework.report.TestListener; +import org.citrusframework.report.TestResults; + +import java.io.StringWriter; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Christoph Deppisch + * @since 2.7.4 + */ +public class RemoteTestListener implements TestListener { + + /** Latest test results */ + private TestResults results = new TestResults(); + + private final Map startTimes = new HashMap<>(); + + /** + * Generate a test report from the current {@link #results}. + * @return + */ + public String generateTestReport() { + StringWriter reportWriter = new StringWriter(); + OutputStreamReporter reporter = new OutputStreamReporter(reportWriter); + reporter.generate(results); + return reportWriter.toString(); + } + + /** + * Obtains the latestResults. + * @return + */ + public TestResults getResults() { + return results; + } + + @Override + public void onTestStart(TestCase test) { + startTimes.put(ClassAndName.of(test), System.currentTimeMillis()); + } + + @Override + public void onTestFinish(TestCase test) { + // NOOP + } + + @Override + public void onTestSuccess(TestCase test) { + Duration consumed = Duration.ofMillis( + System.currentTimeMillis() - startTimes.get(ClassAndName.of(test))); + results + .addResult(TestResult.success( + test.getName(), + test.getTestClass().getCanonicalName(), + test.getVariableDefinitions()) + .withDuration(consumed)); + } + + @Override + public void onTestFailure(TestCase test, Throwable cause) { + Duration consumed = Duration.ofMillis( + System.currentTimeMillis() - startTimes.get(ClassAndName.of(test))); + results + .addResult(TestResult.failed( + test.getName(), + test.getTestClass().getCanonicalName(), + cause, + test.getVariableDefinitions()) + .withDuration(consumed)); + } + + @Override + public void onTestSkipped(TestCase test) { + Duration consumed = Duration.ofMillis( + System.currentTimeMillis() - startTimes.get(ClassAndName.of(test))); + results + .addResult(TestResult.skipped( + test.getName(), + test.getTestClass().getCanonicalName(), + test.getVariableDefinitions()) + .withDuration(consumed)); + } + + public List toRemoteResults() { + return getResults().asList().stream() + .map(RemoteResult::fromTestResult) + .toList(); + } + + public void reset() { + results = new TestResults(); + startTimes.clear(); + } + + private record ClassAndName(Class clazz, String name) { + static ClassAndName of(TestCase testCase) { + return new ClassAndName( + testCase.getTestClass(), + testCase.getName()); + } + } +} diff --git a/citrus-remote-server/src/main/java/org/citrusframework/remote/model/RemoteResult.java b/citrus-remote-server/src/main/java/org/citrusframework/remote/model/RemoteResult.java index 6d26271..3d1fc19 100644 --- a/citrus-remote-server/src/main/java/org/citrusframework/remote/model/RemoteResult.java +++ b/citrus-remote-server/src/main/java/org/citrusframework/remote/model/RemoteResult.java @@ -21,6 +21,7 @@ import java.io.PrintWriter; import java.io.StringWriter; +import java.time.Duration; import java.util.Optional; /** @@ -35,6 +36,9 @@ public class RemoteResult { /** Fully qualified test class name */ private String testClass; + /** Duration of the test run */ + private Long durationMillis; + /** Failure cause */ private String cause; @@ -57,6 +61,10 @@ public static RemoteResult fromTestResult(TestResult testResult) { RemoteResult remoteResult = new RemoteResult(); remoteResult.setTestName(testResult.getTestName()); remoteResult.setTestClass(testResult.getClassName()); + remoteResult.setDurationMillis(Optional.of(testResult) + .map(TestResult::getDuration) + .orElse(Duration.ZERO) + .toMillis()); remoteResult.setSuccess(testResult.isSuccess()); remoteResult.setFailed(testResult.isFailed()); remoteResult.setSkipped(testResult.isSkipped()); @@ -80,17 +88,24 @@ public static RemoteResult fromTestResult(TestResult testResult) { * @return */ public static TestResult toTestResult(RemoteResult remoteResult) { + TestResult result; if (remoteResult.isSuccess()) { - return TestResult.success(remoteResult.getTestName(), remoteResult.getTestClass()); + result = TestResult.success(remoteResult.getTestName(), remoteResult.getTestClass()); } else if (remoteResult.isSkipped()) { - return TestResult.skipped(remoteResult.getTestName(), remoteResult.getTestClass()); + result = TestResult.skipped(remoteResult.getTestName(), remoteResult.getTestClass()); } else if (remoteResult.isFailed()) { // TODO: Check if this is fine, failure stack, failure type are never used in the new Citrus version - return TestResult.failed(remoteResult.getTestName(), remoteResult.getTestClass(), remoteResult.getErrorMessage()) - .withFailureType(remoteResult.getCause()); + result = TestResult + .failed( + remoteResult.getTestName(), + remoteResult.getTestClass(), + remoteResult.getErrorMessage()) + .withFailureType(remoteResult.getCause()); } else { - throw new CitrusRuntimeException("Unexpected test result state " + remoteResult.getTestName()); + throw new CitrusRuntimeException( + "Unexpected test result state " + remoteResult.getTestName()); } + return result.withDuration(Duration.ofMillis(remoteResult.getDurationMillis())); } /** @@ -129,6 +144,24 @@ public void setTestClass(String testClass) { this.testClass = testClass; } + /** + * Gets the durationInMillis. + * + * @return + */ + public long getDurationMillis() { + return durationMillis; + } + + /** + * Sets the durationInMillis. + * + * @param durationMillis + */ + public void setDurationMillis(long durationMillis) { + this.durationMillis = durationMillis; + } + /** * Gets the cause. * diff --git a/citrus-remote-server/src/main/java/org/citrusframework/remote/reporter/RemoteTestResultReporter.java b/citrus-remote-server/src/main/java/org/citrusframework/remote/reporter/RemoteTestResultReporter.java deleted file mode 100644 index e91a7e8..0000000 --- a/citrus-remote-server/src/main/java/org/citrusframework/remote/reporter/RemoteTestResultReporter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2006-2018 the original author or authors. - * - * 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 org.citrusframework.remote.reporter; - -import org.citrusframework.report.AbstractTestReporter; -import org.citrusframework.report.OutputStreamReporter; -import org.citrusframework.report.TestResults; - -import java.io.StringWriter; - -/** - * @author Christoph Deppisch - * @since 2.7.4 - */ -public class RemoteTestResultReporter extends AbstractTestReporter { - - /** Test report */ - private String testReport; - - /** Latest test results */ - private TestResults latestResults = new TestResults(); - - @Override - public void generate(TestResults testResults) { - this.latestResults = testResults; - StringWriter results = new StringWriter(); - OutputStreamReporter reporter = new OutputStreamReporter(results); - reporter.generate(testResults); - this.testReport = results.toString(); - } - - /** - * Gets the latest. - * @return - */ - public String getTestReport() { - return testReport; - } - - /** - * Obtains the latestResults. - * @return - */ - public TestResults getLatestResults() { - return latestResults; - } -}