Skip to content
This repository was archived by the owner on Nov 19, 2024. It is now read-only.
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
7 changes: 3 additions & 4 deletions plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
<changelist>-SNAPSHOT</changelist>
<gitHubRepo>jenkinsci/code-coverage-api-plugin</gitHubRepo>

<jenkins.baseline>2.346</jenkins.baseline>
<jenkins.version>2.346.3</jenkins.version>

<trove4j.version>3.0.3</trove4j.version>
<saxon-he.version>11.4</saxon-he.version>

Expand All @@ -37,6 +34,7 @@
<docker-fixtures.version>1.11</docker-fixtures.version>
<testcontainers.version>1.17.4</testcontainers.version>
<job-dsl.version>1.81</job-dsl.version>
<echarts-api.version>5.4.0-1</echarts-api.version>
</properties>

<developers>
Expand All @@ -63,7 +61,7 @@
<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>ionicons-api</artifactId>
<version>28.vccb_448cc14b_d</version>
<version>28.va_f3a_84439e5f</version>
</dependency>
<dependency>
<groupId>net.sf.trove4j</groupId>
Expand Down Expand Up @@ -112,6 +110,7 @@
<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>echarts-api</artifactId>
<version>${echarts-api.version}</version>
</dependency>
<dependency>
<groupId>io.jenkins.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import hudson.Functions;

import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider;
import io.jenkins.plugins.datatables.DetailedCell;
import io.jenkins.plugins.datatables.TableConfiguration;
import io.jenkins.plugins.datatables.TableConfiguration.SelectStyle;
Expand All @@ -32,10 +33,12 @@ class ChangeCoverageTableModel extends CoverageTableModel {
* The root of the change coverage tree
* @param renderer
* the renderer to use for the file names
* @param colorProvider
* The {@link ColorProvider} which provides the used colors
*/
ChangeCoverageTableModel(final String id, final CoverageNode root, final CoverageNode changeRoot,
final RowRenderer renderer) {
super(id, root, renderer);
final RowRenderer renderer, final ColorProvider colorProvider) {
super(id, root, renderer, colorProvider);

this.changeRoot = changeRoot;
}
Expand All @@ -50,7 +53,7 @@ public List<Object> getRows() {
Locale browserLocale = Functions.getCurrentLocale();
return changeRoot.getAllFileCoverageNodes().stream()
.map(file -> new ChangeCoverageRow(
getOriginalNode(file), file, browserLocale, getRenderer()))
getOriginalNode(file), file, browserLocale, getRenderer(), getColorProvider()))
.collect(Collectors.toList());
}

Expand All @@ -71,8 +74,8 @@ private static class ChangeCoverageRow extends CoverageRow {
private final FileCoverageNode changedFileNode;

ChangeCoverageRow(final FileCoverageNode root, final FileCoverageNode changedFileNode,
final Locale browserLocale, final RowRenderer renderer) {
super(root, browserLocale, renderer);
final Locale browserLocale, final RowRenderer renderer, final ColorProvider colorProvider) {
super(root, browserLocale, renderer, colorProvider);

this.changedFileNode = changedFileNode;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider;
import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory;
import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageChangeTendency;
import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel;
import io.jenkins.plugins.datatables.DetailedCell;
Expand All @@ -28,10 +27,17 @@
* UI table model for the coverage details table.
*/
class CoverageTableModel extends TableModel {
private static final ColorProvider COLOR_PROVIDER = ColorProviderFactory.createColorProvider();
private static final int NO_COVERAGE_SORT = -1_000;

/**
* The alpha value for colors to be used to highlight the coverage within the table view.
*/
private static final int TABLE_COVERAGE_COLOR_ALPHA = 80;

static final DetailedCell<Integer> NO_COVERAGE = new DetailedCell<>(Messages.Coverage_Not_Available(),
NO_COVERAGE_SORT);

private final ColorProvider colorProvider;
private final CoverageNode root;
private final RowRenderer renderer;
private final String id;
Expand All @@ -45,13 +51,16 @@ class CoverageTableModel extends TableModel {
* The root of the coverage tree
* @param renderer
* the renderer to use for the file names
* @param colors
* The {@link ColorProvider} which provides the used colors
*/
CoverageTableModel(final String id, final CoverageNode root, final RowRenderer renderer) {
CoverageTableModel(final String id, final CoverageNode root, final RowRenderer renderer, final ColorProvider colors) {
super();

this.id = id;
this.root = root;
this.renderer = renderer;
colorProvider = colors;
}

RowRenderer getRenderer() {
Expand Down Expand Up @@ -132,14 +141,18 @@ public List<TableColumn> getColumns() {
public List<Object> getRows() {
Locale browserLocale = Functions.getCurrentLocale();
return root.getAll(CoverageMetric.FILE).stream()
.map(file -> new CoverageRow(file, browserLocale, renderer))
.map(file -> new CoverageRow(file, browserLocale, renderer, colorProvider))
.collect(Collectors.toList());
}

protected CoverageNode getRoot() {
return root;
}

protected ColorProvider getColorProvider() {
return colorProvider;
}

/**
* UI row model for the coverage details table.
*/
Expand All @@ -149,11 +162,13 @@ static class CoverageRow {
private final CoverageNode root;
private final Locale browserLocale;
private final RowRenderer renderer;
private final ColorProvider colorProvider;

CoverageRow(final CoverageNode root, final Locale browserLocale, final RowRenderer renderer) {
CoverageRow(final CoverageNode root, final Locale browserLocale, final RowRenderer renderer, final ColorProvider colors) {
this.root = root;
this.browserLocale = browserLocale;
this.renderer = renderer;
this.colorProvider = colors;
}

public String getFileHash() {
Expand Down Expand Up @@ -206,12 +221,12 @@ public int getLoc() {
protected DetailedCell<?> createColoredCoverageColumn(final Coverage coverage, final String tooltip) {
if (coverage.isSet()) {
double percentage = coverage.getCoveredPercentage().getDoubleValue();
DisplayColors colors = CoverageLevel.getDisplayColorsOfCoverageLevel(percentage, COLOR_PROVIDER);
DisplayColors colors = CoverageLevel.getDisplayColorsOfCoverageLevel(percentage, colorProvider);
String cell = div().withClasses(COVERAGE_COLUMN_OUTER).with(
div().withClasses(COVERAGE_COLUMN_INNER)
.withStyle(String.format(
"background-image: linear-gradient(90deg, %s %f%%, transparent %f%%);",
colors.getFillColorAsHex(),
colors.getFillColorAsRGBAHex(TABLE_COVERAGE_COLOR_ALPHA),
percentage, percentage))
.withTitle(tooltip)
.withText(coverage.formatCoveredPercentage(browserLocale)))
Expand All @@ -234,10 +249,11 @@ protected DetailedCell<?> createColoredCoverageColumn(final Coverage coverage, f
protected DetailedCell<?> createColoredCoverageDeltaColumn(
final CoveragePercentage coveragePercentage, final String tooltip) {
double coverageValue = coveragePercentage.getDoubleValue();
DisplayColors colors = CoverageChangeTendency.getDisplayColorsForTendency(coverageValue, COLOR_PROVIDER);
DisplayColors colors = CoverageChangeTendency.getDisplayColorsForTendency(coverageValue, colorProvider);
String cell = div().withClasses(COVERAGE_COLUMN_OUTER).with(
div().withClasses(COVERAGE_COLUMN_INNER)
.withStyle(String.format("background-color:%s;", colors.getFillColorAsHex()))
.withStyle(String.format("background-color:%s;", colors.getFillColorAsRGBAHex(
TABLE_COVERAGE_COLOR_ALPHA)))
.withText(coveragePercentage.formatDeltaPercentage(browserLocale))
.withTitle(tooltip))
.render();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.SortedMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -13,6 +16,10 @@
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.math.Fraction;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import edu.hm.hafner.echarts.JacksonFacade;
import edu.hm.hafner.echarts.LinesChartModel;
import edu.hm.hafner.echarts.TreeMapNode;
Expand All @@ -30,6 +37,7 @@
import io.jenkins.plugins.coverage.model.visualization.code.SourceCodeFacade;
import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider;
import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory;
import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageColorJenkinsId;
import io.jenkins.plugins.coverage.model.visualization.tree.TreeMapNodeConverter;
import io.jenkins.plugins.datatables.DefaultAsyncTableContentProvider;
import io.jenkins.plugins.datatables.TableModel;
Expand All @@ -45,8 +53,7 @@
@SuppressWarnings({"PMD.GodClass", "PMD.ExcessivePublicCount", "checkstyle:ClassDataAbstractionCoupling", "checkstyle:ClassFanOutComplexity"})
public class CoverageViewModel extends DefaultAsyncTableContentProvider implements ModelObject {
private static final JacksonFacade JACKSON_FACADE = new JacksonFacade();
private static final ColorProvider COLOR_PROVIDER = ColorProviderFactory.createColorProvider();
private static final TreeMapNodeConverter TREE_MAP_NODE_CONVERTER = new TreeMapNodeConverter(COLOR_PROVIDER);
private static final TreeMapNodeConverter TREE_MAP_NODE_CONVERTER = new TreeMapNodeConverter();
private static final BuildResultNavigator NAVIGATOR = new BuildResultNavigator();
private static final SourceCodeFacade SOURCE_CODE_FACADE = new SourceCodeFacade();

Expand All @@ -62,6 +69,8 @@ public class CoverageViewModel extends DefaultAsyncTableContentProvider implemen
private final CoverageNode changeCoverageTreeRoot;
private final CoverageNode indirectCoverageChangesTreeRoot;

private ColorProvider colorProvider = ColorProviderFactory.createDefaultColorProvider();

/**
* Creates a new view model instance.
*
Expand Down Expand Up @@ -99,6 +108,48 @@ public String getDisplayName() {
return Messages.Coverage_Title(node.getName());
}

/**
* Gets a set of color IDs which can be used to dynamically load the defined Jenkins colors.
*
* @return the available color IDs
*/
@JavaScriptMethod
@SuppressWarnings("unused")
public Set<String> getJenkinsColorIDs() {
return CoverageColorJenkinsId.getAll();
}

/**
* Creates a new {@link ColorProvider} based on the passed color json string which contains the set Jenkins colors.
*
* @param colors
* The dynamically loaded Jenkins colors to be used for highlighting the coverage tree as json string
*/
@JavaScriptMethod
@SuppressWarnings("unused")
public void setJenkinsColors(final String colors) {
colorProvider = createColorProvider(colors);
}

/**
* Parses the passed color json string to a {@link ColorProvider}.
*
* @param json
* The color json
*
* @return the created color provider
*/
private ColorProvider createColorProvider(final String json) {
try {
ObjectMapper mapper = new ObjectMapper();
Map<String, String> colorMapping = mapper.readValue(json, new ColorMappingType());
return ColorProviderFactory.createColorProvider(colorMapping);
}
catch (JsonProcessingException e) {
return ColorProviderFactory.createDefaultColorProvider();
}
}

@JavaScriptMethod
public CoverageOverview getOverview() {
return new CoverageOverview(getNode().filterPackageStructure());
Expand Down Expand Up @@ -149,7 +200,7 @@ private Optional<CoverageBuildAction> getLatestAction() {
@SuppressWarnings("unused")
public TreeMapNode getCoverageTree(final String coverageMetric) {
CoverageMetric metric = getCoverageMetricFromText(coverageMetric);
return TREE_MAP_NODE_CONVERTER.toTeeChartModel(getNode(), metric);
return TREE_MAP_NODE_CONVERTER.toTeeChartModel(getNode(), metric, colorProvider);
}

/**
Expand All @@ -165,7 +216,7 @@ public TreeMapNode getCoverageTree(final String coverageMetric) {
@SuppressWarnings("unused")
public TreeMapNode getChangeCoverageTree(final String coverageMetric) {
CoverageMetric metric = getCoverageMetricFromText(coverageMetric);
return TREE_MAP_NODE_CONVERTER.toTeeChartModel(changeCoverageTreeRoot, metric);
return TREE_MAP_NODE_CONVERTER.toTeeChartModel(changeCoverageTreeRoot, metric, colorProvider);
}

/**
Expand All @@ -181,7 +232,7 @@ public TreeMapNode getChangeCoverageTree(final String coverageMetric) {
@SuppressWarnings("unused")
public TreeMapNode getCoverageChangesTree(final String coverageMetric) {
CoverageMetric metric = getCoverageMetricFromText(coverageMetric);
return TREE_MAP_NODE_CONVERTER.toTeeChartModel(indirectCoverageChangesTreeRoot, metric);
return TREE_MAP_NODE_CONVERTER.toTeeChartModel(indirectCoverageChangesTreeRoot, metric, colorProvider);
}

/**
Expand Down Expand Up @@ -223,11 +274,13 @@ public TableModel getTableModel(final String tableId) {

switch (actualId) {
case ABSOLUTE_COVERAGE_TABLE_ID:
return new CoverageTableModel(tableId, getNode(), renderer);
return new CoverageTableModel(tableId, getNode(), renderer, colorProvider);
case CHANGE_COVERAGE_TABLE_ID:
return new ChangeCoverageTableModel(tableId, getNode(), changeCoverageTreeRoot, renderer);
return new ChangeCoverageTableModel(tableId, getNode(), changeCoverageTreeRoot, renderer,
colorProvider);
case INDIRECT_COVERAGE_TABLE_ID:
return new IndirectCoverageChangesTable(tableId, getNode(), indirectCoverageChangesTreeRoot, renderer);
return new IndirectCoverageChangesTable(tableId, getNode(), indirectCoverageChangesTreeRoot, renderer,
colorProvider);
default:
throw new NoSuchElementException("No such table with id " + actualId);
}
Expand Down Expand Up @@ -488,4 +541,10 @@ private SortedMap<CoverageMetric, Coverage> getMetricsDistribution() {
return coverage.getMetricsDistribution();
}
}

/**
* Used for parsing a Jenkins color mapping JSON string to a color map.
*/
private static final class ColorMappingType extends TypeReference<HashMap<String, String>> {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import hudson.Functions;

import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider;
import io.jenkins.plugins.datatables.DetailedCell;

/**
Expand All @@ -29,11 +30,13 @@ class IndirectCoverageChangesTable extends CoverageTableModel {
* @param changeRoot
* The root of the indirect coverage changes tree
* @param renderer
* the renderer to use for the file names
* The renderer to use for the file names
* @param colorProvider
* The {@link ColorProvider} which provides the used colors
*/
IndirectCoverageChangesTable(final String id, final CoverageNode root, final CoverageNode changeRoot,
final RowRenderer renderer) {
super(id, root, renderer);
final RowRenderer renderer, final ColorProvider colorProvider) {
super(id, root, renderer, colorProvider);

this.changeRoot = changeRoot;
}
Expand All @@ -43,7 +46,7 @@ public List<Object> getRows() {
Locale browserLocale = Functions.getCurrentLocale();
return changeRoot.getAllFileCoverageNodes().stream()
.map(file -> new IndirectCoverageChangesRow(
getOriginalNode(file), file, browserLocale, getRenderer()))
getOriginalNode(file), file, browserLocale, getRenderer(), getColorProvider()))
.collect(Collectors.toList());
}

Expand All @@ -64,8 +67,8 @@ private static class IndirectCoverageChangesRow extends CoverageRow {
private final FileCoverageNode changedFileNode;

IndirectCoverageChangesRow(final FileCoverageNode root, final FileCoverageNode changedFileNode,
final Locale browserLocale, final RowRenderer renderer) {
super(root, browserLocale, renderer);
final Locale browserLocale, final RowRenderer renderer, final ColorProvider colorProvider) {
super(root, browserLocale, renderer, colorProvider);

this.changedFileNode = changedFileNode;
}
Expand Down
Loading