diff --git a/Tasks/GradleV2/Strings/resources.resjson/en-US/resources.resjson b/Tasks/GradleV2/Strings/resources.resjson/en-US/resources.resjson index a24e95530d07..3fbf460b1a8e 100644 --- a/Tasks/GradleV2/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/GradleV2/Strings/resources.resjson/en-US/resources.resjson @@ -28,6 +28,8 @@ "loc.input.help.classFilter": "Comma-separated list of filters to include or exclude classes from collecting code coverage. For example: +:com.*,+:org.*,-:my.app*.*.", "loc.input.label.failIfCoverageEmpty": "Fail when code coverage results are missing", "loc.input.help.failIfCoverageEmpty": "Fail the build if code coverage did not produce any results to publish.", + "loc.input.label.gradle5xOrHigher": "Gradle version >= 5.x", + "loc.input.help.gradle5xOrHigher": "Set this to 'true' if gradle version is >= 5.x.'True' by default.", "loc.input.label.javaHomeSelection": "Set JAVA_HOME by", "loc.input.help.javaHomeSelection": "Sets JAVA_HOME either by selecting a JDK version that will be discovered during builds or by manually entering a JDK path.", "loc.input.label.jdkVersion": "JDK version", diff --git a/Tasks/GradleV2/Tests/L0.ts b/Tasks/GradleV2/Tests/L0.ts index 3b1ad8d46998..27c5d92dd7d6 100644 --- a/Tasks/GradleV2/Tests/L0.ts +++ b/Tasks/GradleV2/Tests/L0.ts @@ -810,6 +810,58 @@ describe('Gradle L0 Suite', function () { } }); + it('Appends correct code coverage data when gradle is 5.x or higher', function (done) { + let tp: string = path.join(__dirname, 'L0JacocoGradle5x.js'); + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + try { + createTemporaryFolders(); + + tr.run(); + + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.invokedToolCount === 2, 'should have only run gradle 2 times'); + assert(tr.stderr.length === 0, 'should not have written to stderr'); + assert(tr.ran(`${gradleWrapper} properties`), 'should have run Gradle with properties'); + assert(tr.ran(`${gradleWrapper} clean build jacocoTestReport`), 'should have run Gradle with code coverage'); + assert(tr.stdOutContained('Code coverage package is appending correct data (gradle 5.x and higher)'), 'should have appended correct code coverage plugin data'); + cleanTemporaryFolders(); + + done(); + } catch (err) { + console.log(tr.stdout); + console.log(tr.stderr); + console.log(err); + done(err); + } + }); + + it('Appends correct code coverage data when gradle is 4.x or lower', function (done) { + let tp: string = path.join(__dirname, 'L0JacocoGradle4x.js'); + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + try { + createTemporaryFolders(); + + tr.run(); + + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.invokedToolCount === 2, 'should have only run gradle 2 times'); + assert(tr.stderr.length === 0, 'should not have written to stderr'); + assert(tr.ran(`${gradleWrapper} properties`), 'should have run Gradle with properties'); + assert(tr.ran(`${gradleWrapper} clean build jacocoTestReport`), 'should have run Gradle with code coverage'); + assert(tr.stdOutContained('Code coverage package is appending correct data (gradle 4.x and lower)'), 'should have appended correct code coverage plugin data'); + cleanTemporaryFolders(); + + done(); + } catch (err) { + console.log(tr.stdout); + console.log(tr.stderr); + console.log(err); + done(err); + } + }); + // /* BEGIN Tools tests */ function verifyModuleResult(results: AnalysisResult[], moduleName: string , expectedViolationCount: number, expectedFileCount: number, expectedReports: string[]) { let analysisResults = results.filter(ar => ar.moduleName === moduleName); diff --git a/Tasks/GradleV2/Tests/L0JacocoGradle4x.ts b/Tasks/GradleV2/Tests/L0JacocoGradle4x.ts new file mode 100644 index 000000000000..163be3e769b6 --- /dev/null +++ b/Tasks/GradleV2/Tests/L0JacocoGradle4x.ts @@ -0,0 +1,82 @@ +import * as ma from 'azure-pipelines-task-lib/mock-answer'; +import * as tmrm from 'azure-pipelines-task-lib/mock-run'; +import path = require('path'); + +import * as fs from 'fs'; + +let taskPath = path.join(__dirname, '..', 'gradletask.js'); +let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tr.setInput('wrapperScript', 'gradlew'); +tr.setInput('cwd', '/home/repo/src'); +tr.setInput('javaHomeSelection', 'JDKVersion'); +tr.setInput('jdkVersion', 'default'); +tr.setInput('testResultsFiles', '**/build/test-results/TEST-*.xml'); +tr.setInput('codeCoverageTool', 'jacoco'); +tr.setInput('failIfCoverageEmpty', 'false'); +tr.setInput('gradle5xOrHigher', 'false'); + +tr.setInput('tasks', 'build'); + +let a: ma.TaskLibAnswers = { + 'checkPath': { + 'gradlew': true, + 'gradlew.bat': true, + '/home/repo/src': true + }, + 'exec': { + 'gradlew.bat properties': { + 'code': 0, + 'stdout': 'More sample gradle output' + }, + 'gradlew properties': { + 'code': 0, + 'stdout': 'More sample gradle output' + }, + 'gradlew.bat clean build jacocoTestReport': { + 'code': 0, + 'stdout': 'More sample gradle output' + }, + 'gradlew clean build jacocoTestReport': { + 'code': 0, + 'stdout': 'More sample gradle output' + } + }, + 'rmRF': { + [path.join('/home/repo/src', 'CCReport43F6D5EF')]: { + success: true + } + }, + 'stats': { + [path.join('/home/repo/src', 'build.gradle')]: { + isFile() { + return true; + } + } + }, + 'exist': { + [path.join('/home/repo/src', 'CCReport43F6D5EF/summary.xml')]: true + } +}; +tr.setAnswers(a); + +const fsClone = Object.assign({}, fs); +Object.assign(fsClone, { + // If gradle version is 5.x or higher, codecoverage-common package should try to insert assignment in this form: + // classDirectories.setFrom files() + // Copare this to how assignments look in gradle 4.x or lower: + // classDirectories = files() + // Since we're checking for gradle 4.x and lower here, we'll look for the latter one + appendFileSync(filePath: string, data: string) { + if (path.join('/home/repo/src', 'build.gradle') === filePath) { + if (data.includes('classDirectories = file')) { + console.log('Code coverage package is appending correct data (gradle 4.x and lower)'); + } else { + throw new Error(`Code coverage package tried to append incorrect data: ${data}`); + } + } + } +}); +tr.registerMock('fs', fsClone); + +tr.run(); diff --git a/Tasks/GradleV2/Tests/L0JacocoGradle5x.ts b/Tasks/GradleV2/Tests/L0JacocoGradle5x.ts new file mode 100644 index 000000000000..ff20b3b9a2ae --- /dev/null +++ b/Tasks/GradleV2/Tests/L0JacocoGradle5x.ts @@ -0,0 +1,82 @@ +import * as ma from 'azure-pipelines-task-lib/mock-answer'; +import * as tmrm from 'azure-pipelines-task-lib/mock-run'; +import path = require('path'); + +import * as fs from 'fs'; + +let taskPath = path.join(__dirname, '..', 'gradletask.js'); +let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tr.setInput('wrapperScript', 'gradlew'); +tr.setInput('cwd', '/home/repo/src'); +tr.setInput('javaHomeSelection', 'JDKVersion'); +tr.setInput('jdkVersion', 'default'); +tr.setInput('testResultsFiles', '**/build/test-results/TEST-*.xml'); +tr.setInput('codeCoverageTool', 'jacoco'); +tr.setInput('failIfCoverageEmpty', 'false'); +tr.setInput('gradle5xOrHigher', 'true'); + +tr.setInput('tasks', 'build'); + +let a: ma.TaskLibAnswers = { + 'checkPath': { + 'gradlew': true, + 'gradlew.bat': true, + '/home/repo/src': true + }, + 'exec': { + 'gradlew.bat properties': { + 'code': 0, + 'stdout': 'More sample gradle output' + }, + 'gradlew properties': { + 'code': 0, + 'stdout': 'More sample gradle output' + }, + 'gradlew.bat clean build jacocoTestReport': { + 'code': 0, + 'stdout': 'More sample gradle output' + }, + 'gradlew clean build jacocoTestReport': { + 'code': 0, + 'stdout': 'More sample gradle output' + } + }, + 'rmRF': { + [path.join('/home/repo/src', 'CCReport43F6D5EF')]: { + success: true + } + }, + 'stats': { + [path.join('/home/repo/src', 'build.gradle')]: { + isFile() { + return true; + } + } + }, + 'exist': { + [path.join('/home/repo/src', 'CCReport43F6D5EF/summary.xml')]: true + } +}; +tr.setAnswers(a); + +const fsClone = Object.assign({}, fs); +Object.assign(fsClone, { + // If gradle version is 5.x or higher, codecoverage-common package should try to insert assignment in this form: + // classDirectories.setFrom files() + // Copare this to how assignments look in gradle 4.x or lower: + // classDirectories = files() + // Since we're checking for gradle 5.x and higher here, we'll look for the former one + appendFileSync(filePath: string, data: string) { + if (path.join('/home/repo/src', 'build.gradle') === filePath) { + if (data.includes('classDirectories.setFrom file')) { + console.log('Code coverage package is appending correct data (gradle 5.x and higher)'); + } else { + throw new Error(`Code coverage package tried to append incorrect data: ${data}`); + } + } + } +}); +tr.registerMock('fs', fsClone); + +tr.run();