Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Optional;

public class GatewayApi extends RetrofitApiBase {
Expand Down Expand Up @@ -78,6 +79,10 @@ public void updateRuntimeCaseStatus(CaseStatusUpdateRequest request) throws CbCl
executeAsync(retroApi.updateRuntimeCaseStatus(request.getRunId(), request.getInstanceId(), request));
}

public void bulkUpdateRuntimeCaseStatus(String runId, String instanceId, List<CaseStatusUpdateRequest> requests) throws CbClientException {
executeAsync(retroApi.bulkUpdateRuntimeCaseStatus(runId, instanceId, requests));
}

public void updateRuntimeSuiteStatus(SuiteStatusUpdateRequest request) throws CbClientException {
executeAsync(retroApi.updateRuntimeSuiteStatus(request.getRunId(), request.getInstanceId(), request));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
import retrofit2.http.POST;
import retrofit2.http.Path;

import java.util.List;

public interface GatewayApiRetro {
@POST("testresult/Status")
Call<Void> updateTestCaseStatus(@Body TestStatusRequest statusRequest);
@POST("testresult/load/run/{runId}/instance/{instanceId}/metrics")
Call<Void> updateLoadTestMetrics(@Path("runId") String runId, @Path("instanceId") String instanceId, @Body LoadTestMetricsUpdateRequest request);
@POST("testresult/runtime/run/{runId}/instance/{instanceId}/case/status")
Call<Void> updateRuntimeCaseStatus(@Path("runId") String runId, @Path("instanceId") String instanceId, @Body CaseStatusUpdateRequest request);
@POST("testresult/runtime/run/{runId}/instance/{instanceId}/case/status/bulk")
Call<Void> bulkUpdateRuntimeCaseStatus(@Path("runId") String runId, @Path("instanceId") String instanceId, @Body List<CaseStatusUpdateRequest> requests);
@POST("testresult/runtime/run/{runId}/instance/{instanceId}/suite/status")
Call<Void> updateRuntimeSuiteStatus(@Path("runId") String runId, @Path("instanceId") String instanceId, @Body SuiteStatusUpdateRequest request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,59 @@ public void reportPendingCase(final String name, final String fqn, final String
}
}

/**
* A case's name/fqn/parent, as known upfront during test discovery - before any CaseResult
* exists. See {@link #reportPendingCases}.
*/
public static class PendingCaseInfo {
public final String name;
public final String fqn;
public final String parentFqn;
public final String parentName;

public PendingCaseInfo(String name, String fqn, String parentFqn, String parentName) {
this.name = name;
this.fqn = fqn;
this.parentFqn = parentFqn;
this.parentName = parentName;
}
}

/**
* Announces every case that will run, before any of them start - the bulk equivalent of
* {@link #reportPendingCase}. A whole suite's worth of cases reported one at a time each pays
* its own HTTP round trip, throttled by OkHttp's default 5-concurrent-per-host cap, so the
* full Pending list can take a while to finish populating on the live progress screen for a
* suite with many test methods. Sending them all in a single request avoids that entirely.
*/
public void reportPendingCases(final List<PendingCaseInfo> cases) {
if (!config.isRunningInCb() || !this.gatewayApi.isPresent() || result == null || cases.isEmpty())
return;
try {
List<CaseStatusUpdateRequest> reqList = new ArrayList<>(cases.size());
for (PendingCaseInfo c : cases) {
CaseStatusUpdateRequest req = new CaseStatusUpdateRequest();
req.setTimestamp(System.currentTimeMillis());
req.setRunId(result.getRunId());
req.setInstanceId(result.getInstanceId());
req.setId(UUID.randomUUID().toString());
req.setFqn(c.fqn);
req.setName(c.name);
if (c.parentFqn != null) {
req.setParentFqn(c.parentFqn);
req.setParentName(c.parentName);
}
req.setRunStatus(RunStatusEnum.PENDING);
req.setFramework(frameworkName);
req.setLanguage(language);
reqList.add(req);
}
this.gatewayApi.get().bulkUpdateRuntimeCaseStatus(result.getRunId(), result.getInstanceId(), reqList);
} catch (CbClientException e) {
// best-effort - never fail the test run because live-status reporting failed
}
}

/**
* Reports a suite's status to the new Redis-backed runtime status API. See
* {@link #reportRuntimeCaseStatus} for the gating/best-effort rationale.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import io.cloudbeat.common.CbTestContext;
import io.cloudbeat.common.reporter.CbTestReporter;
import io.cloudbeat.common.reporter.CbTestReporter.PendingCaseInfo;
import org.junit.platform.engine.TestSource;
import org.junit.platform.engine.support.descriptor.ClassSource;
import org.junit.platform.engine.support.descriptor.MethodSource;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestIdentifier;
import org.junit.platform.launcher.TestPlan;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
Expand All @@ -32,12 +35,21 @@ public void testPlanExecutionStarted(TestPlan testPlan) {
reporter.setFramework("JUnit", "5");
JunitReporterUtils.startInstance(reporter, true);
}
// Suites (classes) are reported individually - there's no bulk endpoint for them, but a
// suite typically only has a handful of classes, unlike the potentially large number of
// test methods below, so the per-request overhead there is negligible.
List<PendingCaseInfo> pendingCases = new ArrayList<>();
for (TestIdentifier root : testPlan.getRoots()) {
reportPending(testPlan, root, reporter);
reportPending(testPlan, root, reporter, pendingCases);
}
// Cases are collected and sent as a single bulk request instead of one HTTP call per
// case - with dozens of test methods, reporting them one at a time (even as fire-and-forget
// calls) gets throttled by OkHttp's default 5-concurrent-per-host cap, so the full Pending
// list can take a noticeable while to finish appearing on the live progress screen.
reporter.reportPendingCases(pendingCases);
}

private void reportPending(TestPlan testPlan, TestIdentifier identifier, CbTestReporter reporter) {
private void reportPending(TestPlan testPlan, TestIdentifier identifier, CbTestReporter reporter, List<PendingCaseInfo> pendingCases) {
Optional<TestSource> source = identifier.getSource();
if (source.isPresent() && source.get() instanceof ClassSource) {
final String classFqn = ((ClassSource) source.get()).getClassName();
Expand All @@ -47,10 +59,10 @@ else if (source.isPresent() && source.get() instanceof MethodSource) {
final MethodSource methodSource = (MethodSource) source.get();
final String classFqn = methodSource.getClassName();
final String methodFqn = String.format(JunitReporterUtils.JAVA_METHOD_FQN_FORMAT, classFqn, methodSource.getMethodName());
reporter.reportPendingCase(identifier.getDisplayName(), methodFqn, classFqn, classFqn);
pendingCases.add(new PendingCaseInfo(identifier.getDisplayName(), methodFqn, classFqn, classFqn));
}
for (TestIdentifier child : testPlan.getChildren(identifier)) {
reportPending(testPlan, child, reporter);
reportPending(testPlan, child, reporter, pendingCases);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.cloudbeat.testng;

import io.cloudbeat.common.reporter.CbTestReporter;
import io.cloudbeat.common.reporter.CbTestReporter.PendingCaseInfo;
import io.cloudbeat.common.reporter.model.CaseResult;
import io.cloudbeat.common.reporter.model.StepResult;
import io.cloudbeat.common.reporter.model.SuiteResult;
Expand All @@ -11,6 +12,8 @@
import org.testng.ITestResult;
import org.testng.xml.XmlSuite;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

Expand Down Expand Up @@ -59,11 +62,14 @@ public static void reportPendingMethods(CbTestReporter reporter, ISuite suite) {
if (!reporter.getInstance().isPresent())
return;
final String suiteFqn = generateFqnForSuite(suite.getXmlSuite());
// collected and sent as a single bulk request - see CbTestReporter.reportPendingCases
List<PendingCaseInfo> pendingCases = new ArrayList<>();
for (ITestNGMethod testMethod : suite.getAllMethods()) {
final String methodDisplayName = testMethod.getMethodName();
final String methodFqn = fixFqnWithHash(testMethod.getQualifiedName());
reporter.reportPendingCase(methodDisplayName, methodFqn, suiteFqn, suite.getName());
pendingCases.add(new PendingCaseInfo(methodDisplayName, methodFqn, suiteFqn, suite.getName()));
}
reporter.reportPendingCases(pendingCases);
}

public static void endSuite(CbTestReporter reporter, ISuite suite) {
Expand Down