Skip to content

Commit

Permalink
Merge pull request #18 from turing85/feature/fix-reporting
Browse files Browse the repository at this point in the history
#4 Fix progress bar and reporting
  • Loading branch information
christophd authored Sep 11, 2024
2 parents d4a4229 + 1ac0822 commit 278e5da
Show file tree
Hide file tree
Showing 11 changed files with 302 additions and 190 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
CITRUS_REMOTE_LOGS_ARTIFACT=citrus-remote-logs

JAR_ARTIFACT_NAME=jars

JAVA_DISTRIBUTION=temurin
Expand Down
34 changes: 29 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,16 @@ on:
- 'LICENSE'
- 'NOTICE'
env:
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
TEST_REPORTS_ARTIFACT_NAME: will-be-read-from-env-file
PR_NUMBER_ARTIFACT_NAME: will-be-read-from-env-file
CITRUS_REMOTE_LOGS_ARTIFACT: will be read from .env file

JAR_ARTIFACT_NAME: will be read from .env file

JAVA_DISTRIBUTION: will be read from .env file
JAVA_VERSION: will be read from .env file

PR_NUMBER_ARTIFACT_NAME: will be read from .env file

TEST_REPORTS_ARTIFACT_NAME: will be read from .env file

permissions:
actions: write
Expand Down Expand Up @@ -80,6 +85,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() }}
Expand All @@ -101,6 +116,15 @@ jobs:
**/target/citrus-remote/junitreports/TEST*.xml
retention-days: 2

- name: Upload Citrus remote logs
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' }}
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/pr-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ permissions:
pull-requests: write

env:
TEST_REPORTS_ARTIFACT_NAME: will-be-read-from-env-file
PR_NUMBER_ARTIFACT_NAME: will-be-read-from-env-file

TEST_REPORTS_ARTIFACT_NAME: will-be-read-from-env-file
TEST_REPORTS_NAME: will-be-read-from-env-file

jobs:
Expand Down
2 changes: 1 addition & 1 deletion citrus-remote-sample/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
<run>
<engine>testng</engine>
<async>true</async>
<pollingInterval>15000</pollingInterval>
<pollingInterval>1000</pollingInterval>
</run>
<report>
<directory>${citrus.remote.report.directory}</directory>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static void main(String... args) {
.forLogger(LogManager.getLogger("system.out"))
.buildPrintStream());
CitrusRemoteServer.entrypoint(
args, List.of(CustomEntrypoint::getLogHandler, CustomEntrypoint::rorateLogHandler));
args, List.of(CustomEntrypoint::getLogHandler, CustomEntrypoint::rotateLogHandler));
}

private static void getLogHandler(Router router) {
Expand Down Expand Up @@ -61,7 +61,7 @@ private static void getLogHandler(Router router) {
}));
}

private static void rorateLogHandler(Router router) {
private static void rotateLogHandler(Router router) {
router.delete("/citrus-logs")
.handler(CitrusRemoteApplication.wrapThrowingHandler(ctx -> {
HttpServerResponse response = ctx.response();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -44,5 +54,7 @@ public void test(@Optional @CitrusResource TestCaseRunner runner) {
.message()
.contentType(MediaType.TEXT_PLAIN_VALUE)
.body("${body}"));

runner.$(sleep().seconds(2));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -84,8 +83,8 @@ public class CitrusRemoteApplication extends AbstractVerticle {
private Future<List<RemoteResult>> remoteResultFuture;

/** Latest test reports */
private final RemoteTestResultReporter remoteTestResultReporter =
new RemoteTestResultReporter();
private final RemoteTestListener remoteTestListener =
new RemoteTestListener();

/** Router customizations */
private final List<Consumer<Router>> routerCustomizations;
Expand All @@ -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);
Expand All @@ -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\" }")));
}

Expand Down Expand 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<RemoteResult> results = new ArrayList<>();
remoteTestResultReporter.getLatestResults().doWithResults(result ->
results.add(RemoteResult.fromTestResult(result)));
final List<RemoteResult> 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());
Expand Down Expand Up @@ -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 = startTestsAsync(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<RoutingContext> wrapThrowingHandler(
ThrowingHandler<RoutingContext> 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));
Expand Down Expand Up @@ -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)))
Expand All @@ -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<RemoteResult> run(TestRunConfiguration runConfiguration) {
return runTests(runConfiguration);
}
};
private Future<List<RemoteResult>> startTestsAsync(TestRunConfiguration testRunConfiguration) {
return Future.fromCompletionStage(CompletableFuture.supplyAsync(
new RunJob(configuration, testRunConfiguration, remoteTestListener),
executorService));
}

private void addConfigEndpoints(Router router) {
Expand All @@ -330,61 +331,18 @@ private void addConfigEndpoints(Router router) {
CitrusAppConfiguration.class))));
}

public static Handler<RoutingContext> wrapThrowingHandler(
ThrowingHandler<RoutingContext> 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<RemoteResult> 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<RemoteResult> 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();
}
Expand Down
Loading

0 comments on commit 278e5da

Please sign in to comment.