diff --git a/lib/all-badge-examples.js b/lib/all-badge-examples.js index a6802e92dd..ba4c1d730e 100644 --- a/lib/all-badge-examples.js +++ b/lib/all-badge-examples.js @@ -215,13 +215,17 @@ const allBadgeExamples = [ previewUri: '/jenkins/s/https/jenkins.qa.ubuntu.com/view/Precise/view/All%20Precise/job/precise-desktop-amd64_default.svg' }, { - title: 'Jenkins tests', + title: 'Jenkins Tests', previewUri: '/jenkins/t/https/jenkins.qa.ubuntu.com/view/Precise/view/All%20Precise/job/precise-desktop-amd64_default.svg' }, { - title: 'Jenkins coverage', + title: 'Jenkins Coverage (Cobertura)', previewUri: '/jenkins/c/https/jenkins.qa.ubuntu.com/view/Utopic/view/All/job/address-book-service-utopic-i386-ci.svg' }, + { + title: 'Jenkins Coverage (Jacoco)', + previewUri: '/jenkins/j/https/jenkins.qa.ubuntu.com/view/Utopic/view/All/job/address-book-service-utopic-i386-ci.svg' + }, { title: 'Bitbucket Pipelines', previewUri: '/bitbucket/pipelines/atlassian/adf-builder-javascript.svg' diff --git a/server.js b/server.js index 0db023f762..1fa0c8c9a9 100644 --- a/server.js +++ b/server.js @@ -4587,21 +4587,30 @@ cache(function(data, match, sendBadge, request) { }); })); -// Jenkins coverage integration -camp.route(/^\/jenkins(?:-ci)?\/c\/(http(?:s)?)\/([^/]+)\/(.+)\.(svg|png|gif|jpg|json)$/, +// Jenkins coverage integration (cobertura + jacoco) +camp.route(/^\/jenkins(?:-ci)?\/(c|j)\/(http(?:s)?)\/([^/]+)\/(.+)\.(svg|png|gif|jpg|json)$/, cache(function(data, match, sendBadge, request) { - var scheme = match[1]; // http(s) - var host = match[2]; // example.org:8080 - var job = match[3]; // folder/job - var format = match[4]; - var options = { + const type = match[1]; // c - cobertura | j - jacoco + const scheme = match[2]; // http(s) + const host = match[3]; // example.org:8080 + const job = match[4]; // folder/job + const format = match[5]; + const options = { json: true, - uri: scheme + '://' + host + '/job/' + job - + '/lastBuild/cobertura/api/json?tree=results[elements[name,denominator,numerator,ratio]]' + uri: `${scheme}://${host}/job/${job}/` }; + if (job.indexOf('/') > -1 ) { - options.uri = scheme + '://' + host + '/' + job - + '/lastBuild/cobertura/api/json?tree=results[elements[name,denominator,numerator,ratio]]'; + options.uri = `${scheme}://${host}/${job}/`; + } + + switch (type) { + case 'c': + options.uri += 'lastBuild/cobertura/api/json?tree=results[elements[name,denominator,numerator,ratio]]'; + break; + case 'j': + options.uri += 'lastBuild/jacoco/api/json?tree=instructionCoverage[covered,missed,percentage,total]'; + break; } if (serverSecrets && serverSecrets.jenkins_user) { @@ -4611,25 +4620,22 @@ cache(function(data, match, sendBadge, request) { }; } - var badgeData = getBadgeData('coverage', data); + const badgeData = getBadgeData('coverage', data); request(options, function(err, res, json) { - if (err !== null) { - badgeData.text[1] = 'inaccessible'; + if (checkErrorResponse(badgeData, err, res)) { sendBadge(format, badgeData); return; } try { - var coverageObject = json.results.elements.filter(function (obj) { - return obj.name === 'Lines'; - })[0]; + const coverageObject = json.instructionCoverage; if (coverageObject === undefined) { badgeData.text[1] = 'inaccessible'; sendBadge(format, badgeData); return; } - var coverage = coverageObject.ratio; - if (+coverage !== +coverage) { + const coverage = coverageObject.percentage; + if (isNaN(coverage)) { badgeData.text[1] = 'unknown'; sendBadge(format, badgeData); return; diff --git a/services/jenkins/jenkins.tester.js b/services/jenkins/jenkins.tester.js index 8ef3fa21ba..0b4f4a2ae7 100644 --- a/services/jenkins/jenkins.tester.js +++ b/services/jenkins/jenkins.tester.js @@ -6,7 +6,7 @@ const ServiceTester = require('../service-tester'); const t = new ServiceTester({ id: 'jenkins', title: 'Jenkins' }); module.exports = t; -t.create('latest version') +t.create('cobertura: latest version') .get('/plugin/v/blueocean.json') .intercept(nock => nock('https://updates.jenkins-ci.org') .get('/current/update-center.actual.json') @@ -17,7 +17,7 @@ t.create('latest version') value: Joi.string().regex(/^v(.*)$/) })); -t.create('version 0') +t.create('cobertura: version 0') .get('/plugin/v/blueocean.json') .intercept(nock => nock('https://updates.jenkins-ci.org') .get('/current/update-center.actual.json') @@ -28,7 +28,7 @@ t.create('version 0') value: Joi.string().regex(/^v0$/) })); -t.create('inexistent artifact') +t.create('cobertura: inexistent artifact') .get('/plugin/v/inexistent-artifact-id.json') .intercept(nock => nock('https://updates.jenkins-ci.org') .get('/current/update-center.actual.json') @@ -36,7 +36,44 @@ t.create('inexistent artifact') ) .expectJSON({ name: 'plugin', value: 'not found' }); -t.create('connection error') +t.create('cobertura: connection error') .get('/plugin/v/blueocean.json') .networkOff() .expectJSON({ name: 'plugin', value: 'inaccessible' }); + +t.create('jacoco: 81% | valid coverage') + .get('/j/https/updates.jenkins-ci.org/job/hello-project/job/master.json') + .intercept(nock => nock('https://updates.jenkins-ci.org') + .get('/job/hello-project/job/master/lastBuild/jacoco/api/json?tree=instructionCoverage[covered,missed,percentage,total]') + .reply(200, { instructionCoverage: { covered: 39498, missed: 9508, percentage: 81, percentageFloat: 80.5983, total: 49006 } }) + ) + .expectJSONTypes({ name: 'coverage', value: '81%' }); + +t.create('jacoco: inaccessible | request error') + .get('/j/https/updates.jenkins-ci.org/job/hello-project/job/master.json') + .networkOff() + .expectJSONTypes({ name: 'coverage', value: 'inaccessible' }); + +t.create('jacoco: inaccessible | invalid coverage object') + .get('/j/https/updates.jenkins-ci.org/job/hello-project/job/master.json') + .intercept(nock => nock('https://updates.jenkins-ci.org') + .get('/job/hello-project/job/master/lastBuild/jacoco/api/json?tree=instructionCoverage[covered,missed,percentage,total]') + .reply(200, { invalidCoverageObject: { covered: 39498, missed: 9508, percentage: 81, percentageFloat: 80.5983, total: 49006 } }) + ) + .expectJSONTypes({ name: 'coverage', value: 'inaccessible' }); + +t.create('jacoco: unknown | invalid coverage (non-numeric)') + .get('/j/https/updates.jenkins-ci.org/job/hello-project/job/master.json') + .intercept(nock => nock('https://updates.jenkins-ci.org') + .get('/job/hello-project/job/master/lastBuild/jacoco/api/json?tree=instructionCoverage[covered,missed,percentage,total]') + .reply(200, { instructionCoverage: { covered: 39498, missed: 9508, percentage: 'non-numeric', percentageFloat: 80.5983, total: 49006 } }) + ) + .expectJSONTypes({ name: 'coverage', value: 'unknown' }); + +t.create('jacoco: unknown | exception handling') + .get('/j/https/updates.jenkins-ci.org/job/hello-project/job/master.json') + .intercept(nock => nock('https://updates.jenkins-ci.org') + .get('/job/hello-project/job/master/lastBuild/jacoco/api/json?tree=instructionCoverage[covered,missed,percentage,total]') + .reply(200, { instructionCoverage: { covered: 39498, missed: 9508, percentage: '81.x', percentageFloat: 80.5983, total: 49006 } }) + ) + .expectJSONTypes({ name: 'coverage', value: 'unknown' });