Skip to content

Commit

Permalink
Add support for Serenity metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
bottomline.tech\paul.ireland committed Feb 4, 2020
1 parent 8709336 commit 0893a83
Show file tree
Hide file tree
Showing 8 changed files with 1,448 additions and 1 deletion.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,11 @@ under jenkinsci.plugins.influxdb.**InfluxDbStep**.DescriptorImpl.
- `perfpublisher_test_mnetric`
- Test name
- Metric name/value/relevancy

- `serenity_data`
- Test scenario counts: total, success, pending, ignored, skipped, failure, error, compromised
- Test scenario percentages: success, pending, ignored, skipped, failure, error, compromised
- Test scenario timings (in milliseconds): total test duration, total clock duration, min test duration, max test duration, average test duration
- Test scenario tags

## Configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import hudson.util.Secret;
import jenkins.model.Jenkins;
import jenkinsci.plugins.influxdb.generators.*;
import jenkinsci.plugins.influxdb.generators.serenity.SerenityJsonSummaryFile;
import jenkinsci.plugins.influxdb.generators.serenity.SerenityPointGenerator;
import jenkinsci.plugins.influxdb.models.Target;
import jenkinsci.plugins.influxdb.renderer.MeasurementRenderer;
import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
Expand Down Expand Up @@ -244,6 +246,15 @@ public void perform(Run<?, ?> build, TaskListener listener, EnvVars env) {
logger.log(Level.FINE, "Plugin skipped: SonarQube");
}

SerenityJsonSummaryFile serenityJsonSummaryFile = new SerenityJsonSummaryFile(env.get("WORKSPACE"));
SerenityPointGenerator serenityGen = new SerenityPointGenerator(measurementRenderer, customPrefix, build, timestamp, listener, serenityJsonSummaryFile);
if (serenityGen.hasReport()) {
listener.getLogger().println("[InfluxDB Plugin] Serenity data found. Writing to InfluxDB...");
addPoints(pointsToWrite, serenityGen, listener);
} else {
logger.log(Level.FINE, "Plugin skipped: Serenity");
}

ChangeLogPointGenerator changeLogGen = new ChangeLogPointGenerator(measurementRenderer, customPrefix, build, timestamp);
if (changeLogGen.hasReport()) {
listener.getLogger().println("[InfluxDB Plugin] Change Log data found. Writing to InfluxDB...");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package jenkinsci.plugins.influxdb.generators.serenity;

import java.io.IOException;
import java.nio.file.Path;

public interface ISerenityJsonSummaryFile {
boolean exists();
Path getPath();
String getContents() throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package jenkinsci.plugins.influxdb.generators.serenity;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class SerenityJsonSummaryFile implements ISerenityJsonSummaryFile {

private static final String SERENITY_OUTPUT_DIRECTORY = "target/site/serenity";
private static final String SERENITY_JSON_SUMMARY_FILE = "serenity-summary.json";

private final String workspace;

public SerenityJsonSummaryFile(String workspace) {
this.workspace = workspace;
}

public boolean exists() {
return Files.exists(getPath());
}

public Path getPath() {
return java.nio.file.Paths.get(workspace, SERENITY_OUTPUT_DIRECTORY, SERENITY_JSON_SUMMARY_FILE);
}

public String getContents() throws IOException {
return new String(Files.readAllBytes(getPath()), StandardCharsets.UTF_8);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package jenkinsci.plugins.influxdb.generators.serenity;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.influxdb.dto.Point;

import java.io.IOException;

import hudson.model.Run;
import hudson.model.TaskListener;
import jenkinsci.plugins.influxdb.generators.AbstractPointGenerator;
import jenkinsci.plugins.influxdb.renderer.MeasurementRenderer;

public class SerenityPointGenerator extends AbstractPointGenerator {

private static final String SERENITY_RESULTS_COUNTS_TOTAL = "serenity_results_counts_total";
private static final String SERENITY_RESULTS_COUNTS_SUCCESS = "serenity_results_counts_success";
private static final String SERENITY_RESULTS_COUNTS_PENDING = "serenity_results_counts_pending";
private static final String SERENITY_RESULTS_COUNTS_IGNORED = "serenity_results_counts_ignored";
private static final String SERENITY_RESULTS_COUNTS_SKIPPED = "serenity_results_counts_skipped";
private static final String SERENITY_RESULTS_COUNTS_FAILURE = "serenity_results_counts_failure";
private static final String SERENITY_RESULTS_COUNTS_ERROR = "serenity_results_counts_error";
private static final String SERENITY_RESULTS_COUNTS_COMPROMISED = "serenity_results_counts_compromised";

private static final String SERENITY_RESULTS_PERCENTAGES_SUCCESS = "serenity_results_percentages_success";
private static final String SERENITY_RESULTS_PERCENTAGES_PENDING = "serenity_results_percentages_pending";
private static final String SERENITY_RESULTS_PERCENTAGES_IGNORED = "serenity_results_percentages_ignored";
private static final String SERENITY_RESULTS_PERCENTAGES_SKIPPED = "serenity_results_percentages_skipped";
private static final String SERENITY_RESULTS_PERCENTAGES_FAILURE = "serenity_results_percentages_failure";
private static final String SERENITY_RESULTS_PERCENTAGES_ERROR = "serenity_results_percentages_error";
private static final String SERENITY_RESULTS_PERCENTAGES_COMPROMISED = "serenity_results_percentages_compromised";

private static final String SERENITY_RESULTS_TOTAL_TEST_DURATION = "serenity_results_total_test_duration";
private static final String SERENITY_RESULTS_TOTAL_CLOCK_DURATION = "serenity_results_total_clock_duration";
private static final String SERENITY_RESULTS_MIN_TEST_DURATION = "serenity_results_min_test_duration";
private static final String SERENITY_RESULTS_MAX_TEST_DURATION = "serenity_results_max_test_duration";
private static final String SERENITY_RESULTS_AVERAGE_TEST_DURATION = "serenity_results_average_test_duration";

private final Run<?, ?> build;
private final String customPrefix;
private final TaskListener listener;

// support for dependency injection
ISerenityJsonSummaryFile serenityJsonSummaryFile = null;

public SerenityPointGenerator(MeasurementRenderer<Run<?, ?>> projectNameRenderer, String customPrefix,
Run<?, ?> build, long timestamp, TaskListener listener,
ISerenityJsonSummaryFile serenityJsonSummaryFile) {
super(projectNameRenderer, timestamp);
this.build = build;
this.customPrefix = customPrefix;
this.listener = listener;
this.serenityJsonSummaryFile = serenityJsonSummaryFile;
}

public boolean hasReport() {
return (serenityJsonSummaryFile.exists());
}

public Point[] generate() {
String contents;
try {
contents = serenityJsonSummaryFile.getContents();
} catch (IOException e) {
listener.getLogger().println("Failed to read from file " + serenityJsonSummaryFile.getPath() + ", due to: " + e);
return null;
}

Point point = null;
JSONObject root = JSONObject.fromObject(contents);
JSONObject results = root.getJSONObject("results");
JSONObject resultsCounts = results.getJSONObject("counts");
JSONObject resultsPercentages = results.getJSONObject("percentages");
JSONArray tagTypes = root.getJSONArray("tags");

Point.Builder builder = buildPoint("serenity_data", customPrefix, build);

// include results.counts fields
builder
.addField(SERENITY_RESULTS_COUNTS_TOTAL, resultsCounts.getInt("total"))
.addField(SERENITY_RESULTS_COUNTS_SUCCESS, resultsCounts.getInt("success"))
.addField(SERENITY_RESULTS_COUNTS_PENDING, resultsCounts.getInt("pending"))
.addField(SERENITY_RESULTS_COUNTS_IGNORED, resultsCounts.getInt("ignored"))
.addField(SERENITY_RESULTS_COUNTS_SKIPPED, resultsCounts.getInt("skipped"))
.addField(SERENITY_RESULTS_COUNTS_FAILURE, resultsCounts.getInt("failure"))
.addField(SERENITY_RESULTS_COUNTS_ERROR, resultsCounts.getInt("error"))
.addField(SERENITY_RESULTS_COUNTS_COMPROMISED, resultsCounts.getInt("compromised"));

// include results.percentages fields
builder
.addField(SERENITY_RESULTS_PERCENTAGES_SUCCESS, resultsPercentages.getInt("success"))
.addField(SERENITY_RESULTS_PERCENTAGES_PENDING, resultsPercentages.getInt("pending"))
.addField(SERENITY_RESULTS_PERCENTAGES_IGNORED, resultsPercentages.getInt("ignored"))
.addField(SERENITY_RESULTS_PERCENTAGES_SKIPPED, resultsPercentages.getInt("skipped"))
.addField(SERENITY_RESULTS_PERCENTAGES_FAILURE, resultsPercentages.getInt("failure"))
.addField(SERENITY_RESULTS_PERCENTAGES_ERROR, resultsPercentages.getInt("error"))
.addField(SERENITY_RESULTS_PERCENTAGES_COMPROMISED, resultsPercentages.getInt("compromised"));

// include remaining results fields
builder
.addField(SERENITY_RESULTS_TOTAL_TEST_DURATION, results.getLong("totalTestDuration"))
.addField(SERENITY_RESULTS_TOTAL_CLOCK_DURATION, results.getLong("totalClockDuration"))
.addField(SERENITY_RESULTS_MIN_TEST_DURATION, results.getLong("minTestDuration"))
.addField(SERENITY_RESULTS_MAX_TEST_DURATION, results.getLong("maxTestDuration"))
.addField(SERENITY_RESULTS_AVERAGE_TEST_DURATION, results.getLong("averageTestDuration"));

// include tags fields
for (int iTagType = 0; iTagType < tagTypes.size(); iTagType++) {
JSONObject tagType = tagTypes.getJSONObject(iTagType);
String tagTypeType = tagType.getString("tagType");
JSONArray tagResults = tagType.getJSONArray("tagResults");
for (int iTag = 0; iTag < tagResults.size(); iTag++) {
JSONObject tagResult = tagResults.getJSONObject(iTag);
String field = "serenity_tags_" + tagTypeType + ":" + tagResult.getString("tagName");
builder
.addField(field, tagResult.getInt("count"));
}
}

// bring it all together
point = builder.build();

return new Point[]{point};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package jenkinsci.plugins.influxdb.generators.serenity;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SerenityCannedJsonSummaryFile implements ISerenityJsonSummaryFile {

public SerenityCannedJsonSummaryFile() {}

public boolean exists() {
return Files.exists(getPath());
}

public Path getPath() {
try {
return Paths.get(ClassLoader.getSystemResource("serenity/serenity-summary.json").toURI());
} catch (URISyntaxException e) {
//
}
return null;
}

public String getContents() throws IOException {
return new String(Files.readAllBytes(getPath()), StandardCharsets.UTF_8);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package jenkinsci.plugins.influxdb.generators.serenity;

import org.influxdb.dto.Point;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import hudson.model.Job;
import hudson.model.Run;
import jenkins.model.Jenkins;
import jenkinsci.plugins.influxdb.renderer.MeasurementRenderer;
import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;

import static org.junit.Assert.assertTrue;

public class SerenityPointGeneratorTest {

private static final String JOB_NAME = "master";
private static final int BUILD_NUMBER = 11;
private static final String CUSTOM_PREFIX = "test_prefix";

private Run build;

private MeasurementRenderer<Run<?, ?>> measurementRenderer;

private long currTime;

String points = null;

@Before
public void before() {
build = Mockito.mock(Run.class);
Job job = Mockito.mock(Job.class);
measurementRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);

Mockito.when(build.getNumber()).thenReturn(BUILD_NUMBER);
Mockito.when(build.getParent()).thenReturn(job);
Mockito.when(job.getName()).thenReturn(JOB_NAME);
Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);

currTime = System.currentTimeMillis();

SerenityCannedJsonSummaryFile serenityCannedJsonSummaryFile = new SerenityCannedJsonSummaryFile();
SerenityPointGenerator serenityGen = new SerenityPointGenerator(measurementRenderer, CUSTOM_PREFIX, build,
currTime, null, serenityCannedJsonSummaryFile);

if (serenityGen.hasReport()) {
List<Point> pointsToWrite = new ArrayList<>();
pointsToWrite.addAll(Arrays.asList(serenityGen.generate()));
// points.fields is private so just get all fields as a single string
points = pointsToWrite.get(0).toString();
}
}

@Test
public void verifyResultsCounts() {
assertTrue(points.contains("serenity_results_counts_total=99"));
assertTrue(points.contains("serenity_results_counts_success=91"));
assertTrue(points.contains("serenity_results_counts_pending=8"));
assertTrue(points.contains("serenity_results_counts_error=0"));
}

@Test
public void verifyResultsPercentages() {
assertTrue(points.contains("serenity_results_percentages_success=92"));
assertTrue(points.contains("serenity_results_percentages_pending=8"));
assertTrue(points.contains("serenity_results_percentages_ignored=0"));
}

@Test
public void verifyResultsTimings() {
assertTrue(points.contains("serenity_results_max_test_duration=199957"));
assertTrue(points.contains("serenity_results_total_clock_duration=489836"));
assertTrue(points.contains("serenity_results_min_test_duration=1714"));
}

@Test
public void verifyTags() {
assertTrue(points.contains("serenity_tags_:branding=14"));
assertTrue(points.contains("serenity_tags_context:API=73"));
assertTrue(points.contains("serenity_tags_context:UI=26"));
}
}

0 comments on commit 0893a83

Please sign in to comment.