Skip to content

Commit 554134a

Browse files
juan-fernandezdevflow.devflow-routing-intake
andauthored
Add code coverage report flags (#11981)
Add code coverage report flags Remove redundant coverage flag trimming Correct code coverage flags configuration type Merge branch 'master' into juan-fernandez/coverage-report-flags Fix coverage flags test after JUnit utilities move Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
1 parent 4719f5a commit 554134a

8 files changed

Lines changed: 287 additions & 100 deletions

File tree

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityCoverageServices.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ static class Parent {
3737
CoverageReportUploader coverageReportUploader =
3838
executionSettings.isCodeCoverageReportUploadEnabled()
3939
? new CoverageReportUploader(
40-
services.ciIntake, repoServices.ciTags, services.metricCollector)
40+
services.ciIntake,
41+
repoServices.ciTags,
42+
services.config.getCodeCoverageFlags(),
43+
services.metricCollector)
4144
: null;
4245

4346
coverageProcessorFactory =

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/coverage/report/CoverageReportUploader.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
import java.io.InputStream;
1717
import java.lang.reflect.Type;
1818
import java.nio.charset.StandardCharsets;
19+
import java.util.ArrayList;
20+
import java.util.Collections;
1921
import java.util.HashMap;
22+
import java.util.List;
2023
import java.util.Map;
2124
import java.util.zip.GZIPOutputStream;
2225
import okhttp3.MediaType;
@@ -28,26 +31,32 @@ public class CoverageReportUploader {
2831

2932
private final BackendApi backendApi;
3033
private final Map<String, String> ciTags;
34+
private final List<String> flags;
3135
private final CiVisibilityMetricCollector metricCollector;
32-
private final JsonAdapter<Map<String, String>> eventAdapter;
36+
private final JsonAdapter<Map<String, Object>> eventAdapter;
3337

3438
public CoverageReportUploader(
3539
BackendApi backendApi,
3640
Map<String, String> ciTags,
41+
List<String> flags,
3742
CiVisibilityMetricCollector metricCollector) {
3843
this.backendApi = backendApi;
3944
this.ciTags = ciTags;
45+
this.flags = Collections.unmodifiableList(new ArrayList<>(flags));
4046
this.metricCollector = metricCollector;
4147

4248
Moshi moshi = new Moshi.Builder().build();
43-
Type type = Types.newParameterizedType(Map.class, String.class, String.class);
49+
Type type = Types.newParameterizedType(Map.class, String.class, Object.class);
4450
eventAdapter = moshi.adapter(type);
4551
}
4652

4753
public void upload(String format, InputStream reportStream) throws IOException {
48-
Map<String, String> event = new HashMap<>(ciTags);
54+
Map<String, Object> event = new HashMap<>(ciTags);
4955
event.put("format", format);
5056
event.put("type", "coverage_report");
57+
if (!flags.isEmpty()) {
58+
event.put("report.flags", flags);
59+
}
5160
String eventJson = eventAdapter.toJson(event);
5261
RequestBody eventBody = jsonRequestBodyOf(eventJson.getBytes(StandardCharsets.UTF_8));
5362

dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/coverage/report/CoverageReportUploaderTest.groovy

Lines changed: 0 additions & 96 deletions
This file was deleted.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package datadog.trace.civisibility.coverage.report;
2+
3+
import static datadog.trace.agent.test.server.http.JavaTestHttpServer.httpServer;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertFalse;
6+
7+
import com.fasterxml.jackson.core.type.TypeReference;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import datadog.communication.BackendApi;
10+
import datadog.communication.IntakeApi;
11+
import datadog.communication.http.HttpRetryPolicy;
12+
import datadog.communication.http.OkHttpUtils;
13+
import datadog.trace.agent.test.server.http.JavaTestHttpServer;
14+
import datadog.trace.api.civisibility.telemetry.NoOpMetricCollector;
15+
import datadog.trace.api.intake.Intake;
16+
import datadog.trace.test.util.MultipartRequestParser;
17+
import java.io.ByteArrayInputStream;
18+
import java.io.ByteArrayOutputStream;
19+
import java.io.IOException;
20+
import java.net.URI;
21+
import java.nio.charset.StandardCharsets;
22+
import java.util.Arrays;
23+
import java.util.Collections;
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.zip.GZIPInputStream;
27+
import okhttp3.HttpUrl;
28+
import okhttp3.OkHttpClient;
29+
import org.apache.commons.fileupload.FileItem;
30+
import org.junit.jupiter.api.Test;
31+
32+
class CoverageReportUploaderTest {
33+
34+
private static final int REQUEST_TIMEOUT_MILLIS = 15_000;
35+
36+
private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
37+
private static final TypeReference<Map<String, Object>> EVENT_TYPE =
38+
new TypeReference<Map<String, Object>>() {};
39+
40+
private static final String COVERAGE_REPORT_BODY = "report-body";
41+
private static final String JACOCO_FORMAT = "jacoco";
42+
private static final String CI_TAG_KEY = "ci-tag-key";
43+
private static final String CI_TAG_VALUE = "ci-tag-value";
44+
45+
@Test
46+
void uploadsCoverageReportWithoutFlags() throws IOException {
47+
CapturedRequest request = uploadCoverageReport(Collections.emptyList());
48+
49+
assertEquals(3, request.event.size());
50+
assertEquals(JACOCO_FORMAT, request.event.get("format"));
51+
assertEquals("coverage_report", request.event.get("type"));
52+
assertEquals(CI_TAG_VALUE, request.event.get(CI_TAG_KEY));
53+
assertFalse(request.event.containsKey("report.flags"));
54+
assertEquals(COVERAGE_REPORT_BODY, new String(request.coverage, StandardCharsets.UTF_8));
55+
}
56+
57+
@Test
58+
void uploadsCoverageReportWithOrderedDuplicateFlags() throws IOException {
59+
List<String> flags = Arrays.asList("type:unit-tests", "jvm-21", "jvm-21");
60+
61+
CapturedRequest request = uploadCoverageReport(flags);
62+
63+
assertEquals(4, request.event.size());
64+
assertEquals(JACOCO_FORMAT, request.event.get("format"));
65+
assertEquals("coverage_report", request.event.get("type"));
66+
assertEquals(CI_TAG_VALUE, request.event.get(CI_TAG_KEY));
67+
assertEquals(flags, request.event.get("report.flags"));
68+
assertEquals(COVERAGE_REPORT_BODY, new String(request.coverage, StandardCharsets.UTF_8));
69+
}
70+
71+
private static CapturedRequest uploadCoverageReport(List<String> flags) throws IOException {
72+
CapturedRequest capturedRequest = new CapturedRequest();
73+
try (JavaTestHttpServer server =
74+
httpServer(
75+
s ->
76+
s.handlers(
77+
h ->
78+
h.prefix(
79+
"/api/v2/cicovreprt",
80+
api -> {
81+
Map<String, List<FileItem>> multipart =
82+
MultipartRequestParser.parseRequest(
83+
api.getRequest().getBody(),
84+
api.getRequest().getHeader("Content-Type"));
85+
capturedRequest.event =
86+
JSON_MAPPER.readValue(
87+
multipart.get("event").get(0).get(), EVENT_TYPE);
88+
capturedRequest.coverage =
89+
gunzip(multipart.get("coverage").get(0).get());
90+
api.getResponse().status(200).send();
91+
})))) {
92+
BackendApi backendApi = givenIntakeApi(server.getAddress());
93+
CoverageReportUploader uploader =
94+
new CoverageReportUploader(
95+
backendApi,
96+
Collections.singletonMap(CI_TAG_KEY, CI_TAG_VALUE),
97+
flags,
98+
NoOpMetricCollector.INSTANCE);
99+
100+
uploader.upload(
101+
JACOCO_FORMAT,
102+
new ByteArrayInputStream(COVERAGE_REPORT_BODY.getBytes(StandardCharsets.UTF_8)));
103+
}
104+
return capturedRequest;
105+
}
106+
107+
private static byte[] gunzip(byte[] compressed) throws IOException {
108+
try (ByteArrayInputStream input = new ByteArrayInputStream(compressed);
109+
GZIPInputStream gzip = new GZIPInputStream(input);
110+
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
111+
byte[] buffer = new byte[8192];
112+
for (int readCount; (readCount = gzip.read(buffer)) != -1; ) {
113+
output.write(buffer, 0, readCount);
114+
}
115+
return output.toByteArray();
116+
}
117+
}
118+
119+
private static BackendApi givenIntakeApi(URI address) {
120+
HttpUrl intakeUrl =
121+
HttpUrl.get(String.format("%s/api/%s/", address, Intake.CI_INTAKE.getVersion()));
122+
HttpRetryPolicy.Factory retryPolicyFactory = new HttpRetryPolicy.Factory(5, 100, 2.0);
123+
OkHttpClient client = OkHttpUtils.buildHttpClient(intakeUrl, REQUEST_TIMEOUT_MILLIS);
124+
return new IntakeApi(intakeUrl, "api-key", "a-trace-id", retryPolicyFactory, client, false);
125+
}
126+
127+
private static final class CapturedRequest {
128+
private Map<String, Object> event;
129+
private byte[] coverage;
130+
}
131+
}

dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public final class CiVisibilityConfig {
9090
public static final String GIT_COMMIT_HEAD_SHA = "git.commit.head.sha";
9191

9292
/* COVERAGE SETTINGS */
93+
public static final String CODE_COVERAGE_FLAGS = "code.coverage.flags";
9394
public static final String CIVISIBILITY_CODE_COVERAGE_ENABLED =
9495
"civisibility.code.coverage.enabled";
9596
public static final String CIVISIBILITY_CODE_COVERAGE_LINES_ENABLED =

internal-api/src/main/java/datadog/trace/api/Config.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@
300300
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_TEST_SKIPPING_ENABLED;
301301
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_TOTAL_FLAKY_RETRY_COUNT;
302302
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_TRACE_SANITATION_ENABLED;
303+
import static datadog.trace.api.config.CiVisibilityConfig.CODE_COVERAGE_FLAGS;
303304
import static datadog.trace.api.config.CiVisibilityConfig.GIT_COMMIT_HEAD_SHA;
304305
import static datadog.trace.api.config.CiVisibilityConfig.GIT_PULL_REQUEST_BASE_BRANCH;
305306
import static datadog.trace.api.config.CiVisibilityConfig.GIT_PULL_REQUEST_BASE_BRANCH_SHA;
@@ -822,6 +823,7 @@
822823
public class Config {
823824

824825
private static final Logger log = LoggerFactory.getLogger(Config.class);
826+
private static final int MAX_CODE_COVERAGE_FLAGS = 32;
825827

826828
private static final Pattern COLON = Pattern.compile(":");
827829

@@ -1148,6 +1150,7 @@ public static String getHostName() {
11481150
private final boolean ciVisibilityCodeCoverageEnabled;
11491151
private final Boolean ciVisibilityCoverageLinesEnabled;
11501152
private final String ciVisibilityCodeCoverageReportDumpDir;
1153+
private final List<String> codeCoverageFlags;
11511154
private final String ciVisibilityCompilerPluginVersion;
11521155
private final String ciVisibilityJacocoPluginVersion;
11531156
private final boolean ciVisibilityJacocoPluginVersionProvided;
@@ -2700,6 +2703,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())
27002703
configProvider.getBoolean(CIVISIBILITY_CODE_COVERAGE_LINES_ENABLED);
27012704
ciVisibilityCodeCoverageReportDumpDir =
27022705
configProvider.getString(CIVISIBILITY_CODE_COVERAGE_REPORT_DUMP_DIR);
2706+
codeCoverageFlags = parseCodeCoverageFlags(configProvider.getList(CODE_COVERAGE_FLAGS));
27032707
ciVisibilityCompilerPluginVersion =
27042708
configProvider.getString(
27052709
CIVISIBILITY_COMPILER_PLUGIN_VERSION, DEFAULT_CIVISIBILITY_COMPILER_PLUGIN_VERSION);
@@ -4399,6 +4403,10 @@ public String getCiVisibilityCodeCoverageReportDumpDir() {
43994403
return ciVisibilityCodeCoverageReportDumpDir;
44004404
}
44014405

4406+
public List<String> getCodeCoverageFlags() {
4407+
return codeCoverageFlags;
4408+
}
4409+
44024410
public String getCiVisibilityCompilerPluginVersion() {
44034411
return ciVisibilityCompilerPluginVersion;
44044412
}
@@ -6081,6 +6089,29 @@ private static Set<String> parseStringIntoSetOfNonEmptyStrings(
60816089
return Collections.unmodifiableSet(result);
60826090
}
60836091

6092+
private static List<String> parseCodeCoverageFlags(List<String> configuredFlags) {
6093+
if (configuredFlags.isEmpty()) {
6094+
return Collections.emptyList();
6095+
}
6096+
6097+
List<String> flags = new ArrayList<>(configuredFlags.size());
6098+
for (String configuredFlag : configuredFlags) {
6099+
if (!configuredFlag.isEmpty()) {
6100+
flags.add(configuredFlag);
6101+
}
6102+
}
6103+
6104+
if (flags.size() > MAX_CODE_COVERAGE_FLAGS) {
6105+
log.warn(
6106+
"Cannot apply {} code coverage report flags: the maximum supported number is {}. The report will be uploaded without flags.",
6107+
flags.size(),
6108+
MAX_CODE_COVERAGE_FLAGS);
6109+
return Collections.emptyList();
6110+
}
6111+
6112+
return Collections.unmodifiableList(flags);
6113+
}
6114+
60846115
private static <T> Set<T> convertStringSetToSet(
60856116
String setting, final Set<String> input, Function<String, T> mapper) {
60866117
if (input.isEmpty()) {
@@ -6269,6 +6300,8 @@ public String toString() {
62696300
+ experimentalFeaturesEnabled
62706301
+ ", integrationSynapseLegacyOperationName="
62716302
+ integrationSynapseLegacyOperationName
6303+
+ ", codeCoverageFlags="
6304+
+ codeCoverageFlags
62726305
+ ", writerType='"
62736306
+ writerType
62746307
+ '\''

0 commit comments

Comments
 (0)