Skip to content

Commit

Permalink
Added Cobertura Jenkins plugin as possible source for the 'test line …
Browse files Browse the repository at this point in the history
…coverage', 'test branch coverage', and 'source up-to-dateness' metrics. Closes #1520.
  • Loading branch information
fniessink committed Oct 3, 2020
1 parent 5af2003 commit 879b236
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 27 deletions.
3 changes: 3 additions & 0 deletions components/collector/src/source_collectors/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"""Metric collectors per source."""

from .api_source_collectors.azure_devops import AzureDevopsIssues, AzureDevopsUserStoryPoints
from .api_source_collectors.cobertura_jenkins_plugin import (
CoberturaJenkinsPluginSourceUpToDateness, CoberturaJenkinsPluginUncoveredBranches,
CoberturaJenkinsPluginUncoveredLines)
from .api_source_collectors.cxsast import CxSASTSecurityWarnings, CxSASTSourceUpToDateness
from .api_source_collectors.gitlab import (
GitLabFailedJobs, GitLabSourceUpToDateness, GitLabUnmergedBranches, GitLabUnusedJobs)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Cobertura Jenkins plugin coverage report collector."""

from base_collectors import JenkinsPluginSourceUpToDatenessCollector, SourceCollector
from collector_utilities.type import URL
from source_model import SourceMeasurement, SourceResponses


class CoberturaJenkinsPluginBaseClass(SourceCollector):
"""Base class for Cobertura Jenkins plugin collectors."""

async def _landing_url(self, responses: SourceResponses) -> URL:
return URL(f"{await super()._api_url()}/lastSuccessfulBuild/cobertura")


class CoberturaJenkinsPluginCoverageBaseClass(CoberturaJenkinsPluginBaseClass):
"""Base class for Cobertura Jenkins plugin coverage collectors."""

coverage_type = "subclass responsibility"

async def _api_url(self) -> URL:
return URL(f"{await super()._api_url()}/lastSuccessfulBuild/cobertura/api/json?depth=2")

async def _parse_source_responses(self, responses: SourceResponses) -> SourceMeasurement:
elements = (await responses[0].json())["results"]["elements"]
coverage = [element for element in elements if element["name"].lower() == self.coverage_type][0]
total = int(coverage["denominator"])
return SourceMeasurement(value=str(total - int(coverage["numerator"])), total=str(total))


class CoberturaJenkinsPluginUncoveredLines(CoberturaJenkinsPluginCoverageBaseClass):
"""Collector for Cobertura Jenkins plugin uncovered lines."""

coverage_type = "lines"


class CoberturaJenkinsPluginUncoveredBranches(CoberturaJenkinsPluginCoverageBaseClass):
"""Collector for Cobertura Jenkins plugin uncovered branches."""

coverage_type = "conditionals"


class CoberturaJenkinsPluginSourceUpToDateness(
CoberturaJenkinsPluginBaseClass, JenkinsPluginSourceUpToDatenessCollector):
"""Collector for the up to dateness of the Cobertura Jenkins plugin coverage report."""
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ async def _api_url(self) -> URL:
return URL(f"{await super()._api_url()}/lastSuccessfulBuild/jacoco/api/json")

async def _parse_source_responses(self, responses: SourceResponses) -> SourceMeasurement:
line_coverage = (await responses[0].json())[f"{self.coverage_type}Coverage"]
return SourceMeasurement(value=str(line_coverage["missed"]), total=str(line_coverage["total"]))
coverage = (await responses[0].json())[f"{self.coverage_type}Coverage"]
return SourceMeasurement(value=str(coverage["missed"]), total=str(coverage["total"]))


class JacocoJenkinsPluginUncoveredLines(JacocoJenkinsPluginCoverageBaseClass):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Generic unit tests for Jenkins plugin sources."""

from datetime import datetime

from tests.source_collectors.source_collector_test_case import SourceCollectorTestCase

from collector_utilities.functions import days_ago


class JenkinsPluginTestCase(SourceCollectorTestCase):
"""Test case for Jenkins plugin sources."""

source_type = "subclass responsbility"

def setUp(self):
super().setUp()
self.sources = dict(source_id=dict(type=self.source_type, parameters=dict(url="https://jenkins/job")))


class JenkinsPluginTestsMixin: # pylint: disable=too-few-public-methods
"""Generic unit tests for Jenkins plugin sources to be mixed in."""

async def test_source_up_to_dateness(self):
"""Test that the source up to dateness is returned."""
metric = dict(type="source_up_to_dateness", addition="max", sources=self.sources)
response = await self.collect(metric, get_request_json_return_value=dict(timestamp="1565284457173"))
expected_age = days_ago(datetime.fromtimestamp(1565284457173 / 1000.))
self.assert_measurement(response, value=str(expected_age))
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Unit tests for the Cobertura Jenkins plugin source."""

from .jenkins_plugin_test_case import JenkinsPluginTestCase, JenkinsPluginTestsMixin


class CoberturaJenkinsPluginTest(JenkinsPluginTestCase, JenkinsPluginTestsMixin):
"""Unit tests for the Cobertura Jenkins plugin metrics."""

source_type = "cobertura_jenkins_plugin"

async def test_uncovered_lines(self):
"""Test that the number of uncovered lines and the total number of lines are returned."""
metric = dict(type="uncovered_lines", sources=self.sources, addition="sum")
response = await self.collect(
metric,
get_request_json_return_value=dict(
results=dict(elements=[dict(denominator=15, numerator=13, name="Lines")])))
self.assert_measurement(response, value="2", total="15")

async def test_uncovered_branches(self):
"""Test that the number of uncovered branches and the total number of branches are returned."""
metric = dict(type="uncovered_branches", sources=self.sources, addition="sum")
response = await self.collect(
metric,
get_request_json_return_value=dict(
results=dict(elements=[dict(denominator=15, numerator=15, name="Conditionals")])))
self.assert_measurement(response, value="0", total="15")
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
"""Unit tests for the JaCoCo Jenkins plugin source."""

from datetime import datetime
from .jenkins_plugin_test_case import JenkinsPluginTestCase, JenkinsPluginTestsMixin

from tests.source_collectors.source_collector_test_case import SourceCollectorTestCase

from collector_utilities.functions import days_ago


class JaCoCoJenkinsPluginTest(SourceCollectorTestCase):
class JaCoCoJenkinsPluginTest(JenkinsPluginTestCase, JenkinsPluginTestsMixin):
"""Unit tests for the JaCoCo Jenkins plugin metrics."""

def setUp(self):
super().setUp()
self.sources = dict(source_id=dict(type="jacoco_jenkins_plugin", parameters=dict(url="https://jenkins/job")))
source_type = "jacoco_jenkins_plugin"

async def test_uncovered_lines(self):
"""Test that the number of uncovered lines and the total number of lines are returned."""
Expand All @@ -26,10 +20,3 @@ async def test_uncovered_branches(self):
response = await self.collect(
metric, get_request_json_return_value=dict(branchCoverage=dict(total=6, missed=2)))
self.assert_measurement(response, value="2", total="6")

async def test_source_up_to_dateness(self):
"""Test that the source up to dateness is returned."""
metric = dict(type="source_up_to_dateness", addition="max", sources=self.sources)
response = await self.collect(metric, get_request_json_return_value=dict(timestamp="1565284457173"))
expected_age = days_ago(datetime.fromtimestamp(1565284457173 / 1000.))
self.assert_measurement(response, value=str(expected_age))
4 changes: 2 additions & 2 deletions components/frontend/src/logos/Logo.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import NCover from './ncover.png';
import npm from './npm.png';
import OpenVAS from './openvas.png';
import OWASPDependencyCheck from './owasp_dependency_check.png';
import OWASPDependencyCheckJenkinsPlugin from './owasp_dependency_check_jenkins_plugin.png';
import OWASPZAP from './owasp_zap.png';
import Python from './python.png';
import Pyupio from './pyupio.png';
Expand All @@ -37,6 +36,7 @@ export function Logo(props) {
azure_devops: AzureDevops,
bandit: Bandit,
cobertura: Cobertura,
cobertura_jenkins_plugin: Cobertura,
composer: Composer,
cxsast: Checkmarx,
gitlab: GitLab,
Expand All @@ -50,7 +50,7 @@ export function Logo(props) {
npm: npm,
openvas: OpenVAS,
owasp_dependency_check: OWASPDependencyCheck,
owasp_dependency_check_jenkins_plugin: OWASPDependencyCheckJenkinsPlugin,
owasp_dependency_check_jenkins_plugin: OWASPDependencyCheck,
owasp_zap: OWASPZAP,
pip: Python,
pyupio_safety: Pyupio,
Expand Down
Binary file not shown.
57 changes: 56 additions & 1 deletion components/server/src/data/datamodel.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@
"bandit",
"calendar",
"cobertura",
"cobertura_jenkins_plugin",
"cxsast",
"gitlab",
"jacoco",
Expand Down Expand Up @@ -562,6 +563,7 @@
"default_source": "sonarqube",
"sources": [
"cobertura",
"cobertura_jenkins_plugin",
"jacoco",
"jacoco_jenkins_plugin",
"manual_number",
Expand Down Expand Up @@ -589,6 +591,7 @@
"default_source": "sonarqube",
"sources": [
"cobertura",
"cobertura_jenkins_plugin",
"jacoco",
"jacoco_jenkins_plugin",
"manual_number",
Expand Down Expand Up @@ -1910,6 +1913,56 @@
},
"entities": {}
},
"cobertura_jenkins_plugin": {
"name": "Cobertura Jenkins plugin",
"description": "Jenkins plugin for Cobertura, a free Java tool that calculates the percentage of code accessed by tests.",
"url": "https://plugins.jenkins.io/cobertura/",
"parameters": {
"url": {
"name": "URL to Jenkins job",
"short_name": "URL",
"type": "url",
"help": "URL to a Jenkins job with a coverage report generated by the Cobertura plugin. For example, 'http://jenkins.example.org/job/cobertura' or http://jenkins.example.org/job/cobertura/job/master' in case of a pipeline job.",
"validate_on": [
"username",
"password"
],
"mandatory": true,
"default_value": "",
"metrics": [
"source_up_to_dateness",
"uncovered_branches",
"uncovered_lines"
]
},
"username": {
"name": "Username for basic authentication",
"short_name": "username",
"type": "string",
"mandatory": false,
"default_value": "",
"metrics": [
"source_up_to_dateness",
"uncovered_branches",
"uncovered_lines"
]
},
"password": {
"name": "Password or API token for basic authentication",
"short_name": "password",
"type": "password",
"help_url": "https://wiki.jenkins.io/display/JENKINS/Authenticating+scripted+clients",
"mandatory": false,
"default_value": "",
"metrics": [
"source_up_to_dateness",
"uncovered_branches",
"uncovered_lines"
]
}
},
"entities": {}
},
"composer": {
"name": "Composer",
"description": "A Dependency Manager for PHP.",
Expand Down Expand Up @@ -2571,7 +2624,7 @@
"url": "https://plugins.jenkins.io/jacoco",
"parameters": {
"url": {
"name": "URL to job",
"name": "URL to Jenkins job",
"short_name": "URL",
"type": "url",
"help": "URL to a Jenkins job with a coverage report generated by the JaCoCo plugin. For example, 'http://jenkins.example.org/job/jacoco' or http://jenkins.example.org/job/jacoco/job/master' in case of a pipeline job.",
Expand Down Expand Up @@ -4360,6 +4413,7 @@
"Checkmarx CxSAST",
"cloc",
"Cobertura",
"Cobertura Jenkins plugin",
"Composer",
"GitLab",
"JaCoCo",
Expand Down Expand Up @@ -4399,6 +4453,7 @@
"Checkmarx CxSAST": "cxsast",
"cloc": "cloc",
"Cobertura": "cobertura",
"Cobertura Jenkins plugin": "cobertura_jenkins_plugin",
"Composer": "composer",
"GitLab": "gitlab",
"JaCoCo": "jacoco",
Expand Down
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- The 'slow transactions' metric with the Performancetest-runner as source allows for ignoring transactions by name or by regular expression. Closes [#1493](https://github.com/ICTU/quality-time/issues/1493).
- The 'tests' metric now also supports the percentage scale so it's possible to e.g. report the percentage of tests failed. Closes [#1494](https://github.com/ICTU/quality-time/issues/1494).
- Added Cobertura Jenkins plugin as possible source for the 'test line coverage', 'test branch coverage', and 'source up-to-dateness' metrics. Closes [#1520](https://github.com/ICTU/quality-time/issues/1520).

### Fixed

Expand Down
Loading

0 comments on commit 879b236

Please sign in to comment.