Skip to content

Commit 6009f7c

Browse files
committed
#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.
1 parent d4a4229 commit 6009f7c

File tree

10 files changed

+300
-188
lines changed

10 files changed

+300
-188
lines changed

.github/workflows/.env

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
CITRUS_REMOTE_LOGS_ARTIFACT=citrus-remote-logs
2+
13
JAR_ARTIFACT_NAME=jars
24

35
JAVA_DISTRIBUTION=temurin

.github/workflows/build.yml

+29-5
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,16 @@ on:
3737
- 'LICENSE'
3838
- 'NOTICE'
3939
env:
40-
JAVA_DISTRIBUTION: will-be-read-from-env-file
41-
JAVA_VERSION: will-be-read-from-env-file
42-
JAR_ARTIFACT_NAME: will-be-read-from-env-file
43-
TEST_REPORTS_ARTIFACT_NAME: will-be-read-from-env-file
44-
PR_NUMBER_ARTIFACT_NAME: will-be-read-from-env-file
40+
CITRUS_REMOTE_LOGS_ARTIFACT: will be read from .env file
41+
42+
JAR_ARTIFACT_NAME: will be read from .env file
43+
44+
JAVA_DISTRIBUTION: will be read from .env file
45+
JAVA_VERSION: will be read from .env file
46+
47+
PR_NUMBER_ARTIFACT_NAME: will be read from .env file
48+
49+
TEST_REPORTS_ARTIFACT_NAME: will be read from .env file
4550

4651
permissions:
4752
actions: write
@@ -80,6 +85,16 @@ jobs:
8085
--no-transfer-progress \
8186
verify
8287
88+
- name: Print Citrus remote Logs
89+
if: ${{ always() }}
90+
run: |
91+
if [[ -f citrus-remote-sample/target/container-logs/citrus.log ]]
92+
then
93+
cat citrus-remote-sample/target/container-logs/citrus.log
94+
else
95+
echo "(log file 'citrus-remote-sample/target/container-logs/citrus.log' not found)"
96+
fi
97+
8398
- name: Upload JARs
8499
uses: actions/upload-artifact@v4
85100
if: ${{ always() }}
@@ -101,6 +116,15 @@ jobs:
101116
**/target/citrus-remote/junitreports/TEST*.xml
102117
retention-days: 2
103118

119+
- name: Upload Citrus remote logs
120+
uses: actions/upload-artifact@v4
121+
if: ${{ always() }}
122+
with:
123+
if-no-files-found: error
124+
name: ${{ env.CITRUS_REMOTE_LOGS_ARTIFACT }}
125+
path: citrus-remote-sample/target/container-logs/citrus.log
126+
retention-days: 2
127+
104128
- name: Get PR number
105129
id: get-pr-number
106130
if: ${{ always() && github.event_name == 'pull_request' }}

.github/workflows/pr-report.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ permissions:
3030
pull-requests: write
3131

3232
env:
33-
TEST_REPORTS_ARTIFACT_NAME: will-be-read-from-env-file
3433
PR_NUMBER_ARTIFACT_NAME: will-be-read-from-env-file
34+
35+
TEST_REPORTS_ARTIFACT_NAME: will-be-read-from-env-file
3536
TEST_REPORTS_NAME: will-be-read-from-env-file
3637

3738
jobs:

citrus-remote-sample/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
<run>
9595
<engine>testng</engine>
9696
<async>true</async>
97-
<pollingInterval>15000</pollingInterval>
97+
<pollingInterval>1000</pollingInterval>
9898
</run>
9999
<report>
100100
<directory>${citrus.remote.report.directory}</directory>

citrus-remote-sample/src/test/java/org/citrusframework/remote/sample/test/http/GetTextIT.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,27 @@
66
import org.citrusframework.testng.spring.TestNGCitrusSpringSupport;
77
import org.springframework.http.HttpStatus;
88
import org.springframework.http.MediaType;
9+
import org.testng.annotations.DataProvider;
910
import org.testng.annotations.Optional;
1011
import org.testng.annotations.Test;
1112

13+
import static org.citrusframework.actions.SleepAction.Builder.sleep;
1214
import static org.citrusframework.container.Async.Builder.async;
1315
import static org.citrusframework.http.actions.HttpActionBuilder.http;
1416

1517
public class GetTextIT extends TestNGCitrusSpringSupport {
16-
@Test
18+
@DataProvider
19+
public Object[][] body() {
20+
return new Object[][]{
21+
{null, "foo"},
22+
{null, "bar"},
23+
{null, "citrus:randomString(10, MIXED, true)"}};
24+
}
25+
26+
@Test(dataProvider = "body")
1727
@CitrusTest
18-
public void test(@Optional @CitrusResource TestCaseRunner runner) {
19-
runner.variable("body", "citrus:randomString(10, MIXED, true)");
28+
public void test(@Optional @CitrusResource TestCaseRunner runner, String body) {
29+
runner.variable("body", body);
2030
runner.given(async().actions(
2131
http().server("httpServer")
2232
.receive()
@@ -44,5 +54,7 @@ public void test(@Optional @CitrusResource TestCaseRunner runner) {
4454
.message()
4555
.contentType(MediaType.TEXT_PLAIN_VALUE)
4656
.body("${body}"));
57+
58+
runner.$(sleep().seconds(2));
4759
}
4860
}

citrus-remote-server/src/main/java/org/citrusframework/remote/CitrusRemoteApplication.java

+50-92
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.vertx.core.MultiMap;
2424
import io.vertx.core.http.HttpHeaders;
2525
import io.vertx.core.http.HttpServerResponse;
26+
import io.vertx.ext.web.RequestBody;
2627
import io.vertx.ext.web.Router;
2728
import io.vertx.ext.web.RoutingContext;
2829
import io.vertx.ext.web.handler.BodyHandler;
@@ -32,10 +33,9 @@
3233
import org.citrusframework.TestClass;
3334
import org.citrusframework.main.CitrusAppConfiguration;
3435
import org.citrusframework.main.TestRunConfiguration;
35-
import org.citrusframework.remote.controller.RunController;
3636
import org.citrusframework.remote.job.RunJob;
3737
import org.citrusframework.remote.model.RemoteResult;
38-
import org.citrusframework.remote.reporter.RemoteTestResultReporter;
38+
import org.citrusframework.remote.listener.RemoteTestListener;
3939
import org.citrusframework.remote.transformer.JsonRequestTransformer;
4040
import org.citrusframework.remote.transformer.JsonResponseTransformer;
4141
import org.citrusframework.report.JUnitReporter;
@@ -48,7 +48,6 @@
4848
import java.net.URLDecoder;
4949
import java.nio.file.Files;
5050
import java.nio.file.Path;
51-
import java.util.ArrayList;
5251
import java.util.Collections;
5352
import java.util.List;
5453
import java.util.Optional;
@@ -84,8 +83,8 @@ public class CitrusRemoteApplication extends AbstractVerticle {
8483
private Future<List<RemoteResult>> remoteResultFuture;
8584

8685
/** Latest test reports */
87-
private final RemoteTestResultReporter remoteTestResultReporter =
88-
new RemoteTestResultReporter();
86+
private final RemoteTestListener remoteTestListener =
87+
new RemoteTestListener();
8988

9089
/** Router customizations */
9190
private final List<Consumer<Router>> routerCustomizations;
@@ -111,16 +110,13 @@ public CitrusRemoteApplication(
111110
@Override
112111
public void start() {
113112
CitrusInstanceManager.mode(CitrusInstanceStrategy.SINGLETON);
114-
CitrusInstanceManager.addInstanceProcessor(citrus ->
115-
citrus.addTestReporter(remoteTestResultReporter));
113+
CitrusInstanceManager
114+
.addInstanceProcessor(citrus -> citrus.addTestListener(remoteTestListener));
116115

117116
Router router = Router.router(getVertx());
118117
router.route().handler(BodyHandler.create());
119118
router.route().handler(ctx -> {
120-
logger.info(
121-
"{} {}",
122-
ctx.request().method(),
123-
ctx.request().uri());
119+
logger.info("{} {}", ctx.request().method(), ctx.request().uri());
124120
ctx.next();
125121
});
126122
addHealthEndpoint(router);
@@ -140,7 +136,8 @@ public void start() {
140136
private static void addHealthEndpoint(Router router) {
141137
router.get("/health")
142138
.handler(wrapThrowingHandler(ctx ->
143-
ctx.response().putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
139+
ctx.response()
140+
.putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
144141
.end("{ \"status\": \"UP\" }")));
145142
}
146143

@@ -178,17 +175,17 @@ private void addResultsEndpoints(Router router) {
178175
response.end(responseTransformer.render(results)))
179176
.onFailure(throwable -> response
180177
.setStatusCode(HttpResponseStatus.PARTIAL_CONTENT.code())
181-
.end(responseTransformer.render(Collections.emptyList())));
178+
.end(responseTransformer
179+
.render(remoteTestListener.toRemoteResults())));
182180
} else {
183-
final List<RemoteResult> results = new ArrayList<>();
184-
remoteTestResultReporter.getLatestResults().doWithResults(result ->
185-
results.add(RemoteResult.fromTestResult(result)));
181+
final List<RemoteResult> results = remoteTestListener.toRemoteResults();
182+
logger.info("results = {}", results.size());
186183
response.end(responseTransformer.render(results));
187184
}
188185
}));
189186
router.get("/results")
190-
.handler(ctx -> ctx.response().end(
191-
responseTransformer.render(remoteTestResultReporter.getTestReport())));
187+
.handler(ctx -> ctx.response()
188+
.end(responseTransformer.render(remoteTestListener.generateTestReport())));
192189
router.get("/results/files")
193190
.handler(wrapThrowingHandler(ctx -> {
194191
File junitReportsFolder = new File(getJUnitReportsFolder());
@@ -239,31 +236,37 @@ private void addResultsEndpoints(Router router) {
239236

240237
private void addRunEndpoints(Router router) {
241238
router.get("/run")
242-
.handler(wrapThrowingHandler(ctx -> {
243-
TestRunConfiguration runConfiguration = constructRunConfig(ctx);
244-
runTestsAsync(runConfiguration, ctx.response());
245-
}));
239+
.handler(wrapThrowingHandler(ctx ->
240+
runTestsAsync(constructRunConfig(ctx.request().params()), ctx.response())));
241+
router.post("/run")
242+
.handler(wrapThrowingHandler(ctx ->
243+
runTestsAsync(constructRunConfig(ctx.body()), ctx.response())));
246244
router.put("/run")
247245
.handler(wrapThrowingHandler(ctx -> {
248-
remoteResultFuture = Future.fromCompletionStage(CompletableFuture.supplyAsync(
249-
constructTestRun(ctx)::call,
250-
executorService));
246+
remoteTestListener.reset();
247+
remoteResultFuture = startTestsAsync(constructRunConfig(ctx.body()));
251248
ctx.response().end("");
252249
}));
253-
router.post("/run")
254-
.handler(wrapThrowingHandler(ctx -> {
255-
HttpServerResponse response = ctx.response();
256-
TestRunConfiguration runConfiguration = requestTransformer.read(
257-
ctx.body().asString(),
258-
TestRunConfiguration.class);
259-
runTestsAsync(runConfiguration, response);
260-
}));
261250
}
262251

263-
private TestRunConfiguration constructRunConfig(RoutingContext ctx)
252+
public static Handler<RoutingContext> wrapThrowingHandler(
253+
ThrowingHandler<RoutingContext> handler) {
254+
return ctx -> {
255+
try {
256+
handler.handle(ctx);
257+
} catch (Exception e) {
258+
ctx.response()
259+
.setStatusCode(HttpResponseStatus.INTERNAL_SERVER_ERROR.code())
260+
.end(e.getMessage());
261+
}
262+
};
263+
}
264+
265+
266+
267+
private TestRunConfiguration constructRunConfig(MultiMap queryParams)
264268
throws UnsupportedEncodingException {
265269
TestRunConfiguration runConfiguration = new TestRunConfiguration();
266-
MultiMap queryParams = ctx.request().params();
267270
if (queryParams.contains("engine")) {
268271
String engine = queryParams.get("engine");
269272
runConfiguration.setEngine(URLDecoder.decode(engine, ENCODING));
@@ -291,12 +294,16 @@ private TestRunConfiguration constructRunConfig(RoutingContext ctx)
291294
return runConfiguration;
292295
}
293296

297+
private TestRunConfiguration constructRunConfig(RequestBody body) {
298+
return requestTransformer.read(body.asString(), TestRunConfiguration.class);
299+
}
300+
294301
private void runTestsAsync(
295302
TestRunConfiguration runConfiguration,
296303
HttpServerResponse response) {
297304
Future
298305
.fromCompletionStage(CompletableFuture.supplyAsync(
299-
() -> runTests(runConfiguration),
306+
new RunJob(configuration, runConfiguration, remoteTestListener),
300307
executorService))
301308
.onSuccess(results ->
302309
response.end(responseTransformer.render(results)))
@@ -305,16 +312,10 @@ private void runTestsAsync(
305312
.end(error.getMessage()));
306313
}
307314

308-
private RunJob constructTestRun(RoutingContext ctx) {
309-
TestRunConfiguration config = requestTransformer.read(
310-
ctx.body().asString(),
311-
TestRunConfiguration.class);
312-
return new RunJob(config) {
313-
@Override
314-
public List<RemoteResult> run(TestRunConfiguration runConfiguration) {
315-
return runTests(runConfiguration);
316-
}
317-
};
315+
private Future<List<RemoteResult>> startTestsAsync(TestRunConfiguration testRunConfiguration) {
316+
return Future.fromCompletionStage(CompletableFuture.supplyAsync(
317+
new RunJob(configuration, testRunConfiguration, remoteTestListener),
318+
executorService));
318319
}
319320

320321
private void addConfigEndpoints(Router router) {
@@ -330,61 +331,18 @@ private void addConfigEndpoints(Router router) {
330331
CitrusAppConfiguration.class))));
331332
}
332333

333-
public static Handler<RoutingContext> wrapThrowingHandler(
334-
ThrowingHandler<RoutingContext> handler) {
335-
return ctx -> {
336-
try {
337-
handler.handle(ctx);
338-
} catch (Exception e) {
339-
ctx.response().setStatusCode(HttpResponseStatus.INTERNAL_SERVER_ERROR.code())
340-
.end(e.getMessage());
341-
}
342-
};
343-
}
344-
345-
/**
346-
* Construct run controller and execute with given configuration.
347-
* @param runConfiguration
348-
* @return remote results
349-
*/
350-
private List<RemoteResult> runTests(TestRunConfiguration runConfiguration) {
351-
RunController runController = new RunController(configuration);
352-
353-
runController.setEngine(runConfiguration.getEngine());
354-
runController.setIncludes(runConfiguration.getIncludes());
355-
356-
if (!runConfiguration.getDefaultProperties().isEmpty()) {
357-
runController.addDefaultProperties(runConfiguration.getDefaultProperties());
358-
}
359-
360-
if (runConfiguration.getPackages().isEmpty() && runConfiguration.getTestSources().isEmpty()) {
361-
runController.runAll();
362-
}
363-
364-
if (!runConfiguration.getPackages().isEmpty()) {
365-
runController.runPackages(runConfiguration.getPackages());
366-
}
367-
368-
if (!runConfiguration.getTestSources().isEmpty()) {
369-
runController.runClasses(runConfiguration.getTestSources());
370-
}
371-
372-
List<RemoteResult> results = new ArrayList<>();
373-
remoteTestResultReporter.getLatestResults().doWithResults(result -> results.add(RemoteResult.fromTestResult(result)));
374-
return results;
375-
}
376-
377334
/**
378335
* Find reports folder based in unit testing framework present on classpath.
379336
* @return
380337
*/
381338
private String getJUnitReportsFolder() {
382-
383339
if (isPresent("org.testng.annotations.Test")) {
384340
return "test-output" + File.separator + "junitreports";
385341
} else if (isPresent("org.junit.Test")) {
386342
JUnitReporter jUnitReporter = new JUnitReporter();
387-
return jUnitReporter.getReportDirectory() + File.separator + jUnitReporter.getOutputDirectory();
343+
return jUnitReporter.getReportDirectory() +
344+
File.separator +
345+
jUnitReporter.getOutputDirectory();
388346
} else {
389347
return new LoggingReporter().getReportDirectory();
390348
}

0 commit comments

Comments
 (0)