-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable code coverage for ant, maven and gradle #1728
Changes from 10 commits
9819edf
bebd921
45ef2c1
575784f
b83ffee
d9a1f0c
d47bc83
98457c8
4f0da27
734f898
4fef915
2e8273e
5bfa5fb
a21312b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,17 @@ | ||
/// <reference path="../../definitions/vsts-task-lib.d.ts" /> | ||
|
||
import tl = require('vsts-task-lib/task'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright info There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not needed |
||
import path = require('path'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Get all user inputs in the top and assign to variables. Will keep code clean n organized. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make sure we have unified "File Path separator" or we should support both *nix/Windows file separators in nodejs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. some user inputs ex: classFilesDirectory will be available only when code coverage is selected. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have used path.join always so file path seperator doesnt come into picture here.All the task input help have linux style seperator. So not an issue |
||
import fs = require('fs'); | ||
|
||
var anttool = tl.which('ant', true); | ||
|
||
var antv = tl.createToolRunner(anttool); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extra empty lines |
||
antv.arg('-version'); | ||
|
||
var antb = tl.createToolRunner(anttool); | ||
var antBuildFile = tl.getPathInput('antBuildFile', true, true); | ||
antb.arg('-buildfile'); | ||
antb.pathArg(tl.getPathInput('antBuildFile', true, true)); | ||
antb.pathArg(antBuildFile); | ||
|
||
// options and targets are optional | ||
antb.argString(tl.getInput('options', false)); | ||
|
@@ -32,23 +34,23 @@ if (!antHome) { | |
// update JAVA_HOME if user selected specific JDK version or set path manually | ||
var javaHomeSelection = tl.getInput('javaHomeSelection', true); | ||
var specifiedJavaHome = null; | ||
|
||
if (javaHomeSelection == 'JDKVersion') { | ||
tl.debug('Using JDK version to find and set JAVA_HOME'); | ||
var jdkVersion = tl.getInput('jdkVersion'); | ||
var jdkArchitecture = tl.getInput('jdkArchitecture'); | ||
|
||
if(jdkVersion != 'default') { | ||
// jdkVersion should be in the form of 1.7, 1.8, or 1.10 | ||
// jdkArchitecture is either x64 or x86 | ||
// envName for version 1.7 and x64 would be "JAVA_HOME_7_X64" | ||
var envName = "JAVA_HOME_" + jdkVersion.slice(2) + "_" + jdkArchitecture.toUpperCase(); | ||
specifiedJavaHome = tl.getVariable(envName); | ||
if (!specifiedJavaHome) { | ||
tl.error('Failed to find specified JDK version. Please make sure environment variable ' + envName + ' exists and is set to the location of a corresponding JDK.'); | ||
tl.exit(1); | ||
} | ||
tl.debug('Using JDK version to find and set JAVA_HOME'); | ||
var jdkVersion = tl.getInput('jdkVersion'); | ||
var jdkArchitecture = tl.getInput('jdkArchitecture'); | ||
|
||
if (jdkVersion != 'default') { | ||
// jdkVersion should be in the form of 1.7, 1.8, or 1.10 | ||
// jdkArchitecture is either x64 or x86 | ||
// envName for version 1.7 and x64 would be "JAVA_HOME_7_X64" | ||
var envName = "JAVA_HOME_" + jdkVersion.slice(2) + "_" + jdkArchitecture.toUpperCase(); | ||
specifiedJavaHome = tl.getVariable(envName); | ||
if (!specifiedJavaHome) { | ||
tl.error('Failed to find specified JDK version. Please make sure environment variable ' + envName + ' exists and is set to the location of a corresponding JDK.'); | ||
tl.exit(1); | ||
} | ||
} | ||
} | ||
else { | ||
tl.debug('Using path from user input to set JAVA_HOME'); | ||
|
@@ -61,44 +63,151 @@ if (specifiedJavaHome) { | |
process.env['JAVA_HOME'] = specifiedJavaHome; | ||
} | ||
|
||
var ccTool = tl.getInput('codeCoverageTool'); | ||
var isCodeCoverageOpted = (typeof ccTool != "undefined" && ccTool && ccTool.toLowerCase() != 'none'); | ||
|
||
var buildRootPath = path.dirname(antBuildFile); | ||
var instrumentedClassesDirectory = path.join(buildRootPath, "InstrumentedClasses"); | ||
//delete any previous cobertura instrumented classes as they might interfere with ant execution. | ||
tl.rmRF(instrumentedClassesDirectory, true); | ||
|
||
if (isCodeCoverageOpted) { | ||
var summaryFile = null; | ||
var reportDirectory = null; | ||
var ccReportTask = null; | ||
var reportBuildFile = null; | ||
enableCodeCoverage(); | ||
} | ||
else { | ||
tl.debug("Option to enable code coverage was not selected and is being skipped."); | ||
} | ||
|
||
var publishJUnitResults = tl.getInput('publishJUnitResults'); | ||
var testResultsFiles = tl.getInput('testResultsFiles', true); | ||
|
||
antv.exec() | ||
.then(function(code) { | ||
return antb.exec(); | ||
}) | ||
.then(function(code) { | ||
publishTestResults(publishJUnitResults, testResultsFiles); | ||
publishCodeCoverage(isCodeCoverageOpted); | ||
tl.exit(code); | ||
}) | ||
.fail(function(err) { | ||
publishTestResults(publishJUnitResults, testResultsFiles); | ||
console.error(err.message); | ||
tl.debug('taskRunner fail'); | ||
tl.exit(1); | ||
}) | ||
|
||
function publishTestResults(publishJUnitResults, testResultsFiles: string) { | ||
if(publishJUnitResults == 'true') { | ||
//check for pattern in testResultsFiles | ||
if(testResultsFiles.indexOf('*') >= 0 || testResultsFiles.indexOf('?') >= 0) { | ||
tl.debug('Pattern found in testResultsFiles parameter'); | ||
var buildFolder = tl.getVariable('agent.buildDirectory'); | ||
var allFiles = tl.find(buildFolder); | ||
var matchingTestResultsFiles = tl.match(allFiles, testResultsFiles, { matchBase: true }); | ||
if (publishJUnitResults == 'true') { | ||
//check for pattern in testResultsFiles | ||
if (testResultsFiles.indexOf('*') >= 0 || testResultsFiles.indexOf('?') >= 0) { | ||
tl.debug('Pattern found in testResultsFiles parameter'); | ||
var buildFolder = tl.getVariable('agent.buildDirectory'); | ||
var allFiles = tl.find(buildFolder); | ||
var matchingTestResultsFiles = tl.match(allFiles, testResultsFiles, { matchBase: true }); | ||
} | ||
else { | ||
tl.debug('No pattern found in testResultsFiles parameter'); | ||
var matchingTestResultsFiles = [testResultsFiles]; | ||
} | ||
|
||
if (!matchingTestResultsFiles) { | ||
tl.warning('No test result files matching ' + testResultsFiles + ' were found, so publishing JUnit test results is being skipped.'); | ||
return 0; | ||
} | ||
|
||
var tp = new tl.TestPublisher("JUnit"); | ||
tp.publish(matchingTestResultsFiles, true, "", "", "", true); | ||
} | ||
} | ||
|
||
function enableCodeCoverage() { | ||
var classFilter = tl.getInput('classFilter'); | ||
var classFilesDirectories = tl.getInput('classFilesDirectories', true); | ||
var sourceDirectories = tl.getInput('srcDirectories'); | ||
// appending with small guid to keep it unique. Avoiding full guid to ensure no long path issues. | ||
var reportDirectoryName = "CCReport43F6D5EF"; | ||
reportDirectory = path.join(buildRootPath, reportDirectoryName); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hardcoded directory names? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. explained offline |
||
ccReportTask = "CodeCoverage_9064e1d0"; | ||
var reportBuildFileName = "CCReportBuildA4D283EG.xml"; | ||
reportBuildFile = path.join(buildRootPath, reportBuildFileName); | ||
var summaryFileName = "coverage.xml"; | ||
summaryFile = path.join(buildRootPath, reportDirectoryName); | ||
summaryFile = path.join(summaryFile, summaryFileName); | ||
var coberturaCCFile = path.join(buildRootPath, "cobertura.ser"); | ||
|
||
// clean any previous reports. | ||
tl.rmRF(coberturaCCFile, true); | ||
tl.rmRF(reportDirectory, true); | ||
tl.rmRF(reportBuildFile, true); | ||
|
||
var buildProps: { [key: string]: string } = {}; | ||
buildProps['buildfile'] = antBuildFile; | ||
buildProps['classfilter'] = classFilter | ||
buildProps['classfilesdirectories'] = classFilesDirectories; | ||
buildProps['sourcedirectories'] = sourceDirectories; | ||
buildProps['summaryfile'] = summaryFileName; | ||
buildProps['reportdirectory'] = reportDirectory; | ||
buildProps['ccreporttask'] = ccReportTask | ||
buildProps['reportbuildfile'] = reportBuildFile; | ||
try { | ||
var codeCoverageEnabler = new tl.CodeCoverageEnabler('Ant', ccTool); | ||
codeCoverageEnabler.enableCodeCoverage(buildProps); | ||
tl.debug("Code coverage is successfully enabled."); | ||
} | ||
else { | ||
tl.debug('No pattern found in testResultsFiles parameter'); | ||
var matchingTestResultsFiles = [testResultsFiles]; | ||
catch (Error) { | ||
tl.warning("Enabling code coverage failed. Check the build logs for errors."); | ||
} | ||
} | ||
|
||
if(!matchingTestResultsFiles) { | ||
tl.warning('No test result files matching ' + testResultsFiles + ' were found, so publishing JUnit test results is being skipped.'); | ||
return 0; | ||
function publishCodeCoverage(codeCoverageOpted: boolean) { | ||
if (codeCoverageOpted) { | ||
tl.debug("Collecting code coverage reports"); | ||
var antRunner = tl.createToolRunner(anttool); | ||
antRunner.arg('-buildfile'); | ||
if (isFileExists(reportBuildFile)) { | ||
antRunner.pathArg(reportBuildFile); | ||
antRunner.arg(ccReportTask); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. refactor this code. based on condition you are just changing one argument. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
else { | ||
antRunner.pathArg(antBuildFile); | ||
antRunner.arg(ccReportTask); | ||
} | ||
antRunner.exec().then(function(code) { | ||
if (isFileExists(summaryFile)) { | ||
tl.debug("Summary file = " + summaryFile); | ||
tl.debug("Report directory = " + reportDirectory); | ||
tl.debug("Publishing code coverage results to TFS"); | ||
var ccPublisher = new tl.CodeCoveragePublisher(); | ||
ccPublisher.publish(ccTool, summaryFile, reportDirectory, ""); | ||
} | ||
else { | ||
tl.warning("No code coverage results found to be published. This could occur if there were no tests executed or there was a build failure. Check the ant output for details."); | ||
} | ||
}).fail(function(err) { | ||
tl.warning("No code coverage results found to be published. This could occur if there were no tests executed or there was a build failure. Check the ant output for details."); | ||
}); | ||
} | ||
} | ||
|
||
var tp = new tl.TestPublisher("JUnit"); | ||
tp.publish(matchingTestResultsFiles, true, "", "","", true); | ||
} | ||
function isFileExists(path: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For clarity, this method should be named |
||
try { | ||
return tl.stats(path).isFile(); | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
antv.exec() | ||
.then(function(code) { | ||
return antb.exec(); | ||
}) | ||
.then(function(code) { | ||
publishTestResults(publishJUnitResults, testResultsFiles); | ||
tl.exit(code); | ||
}) | ||
.fail(function(err) { | ||
publishTestResults(publishJUnitResults, testResultsFiles); | ||
console.error(err.message); | ||
tl.debug('taskRunner fail'); | ||
tl.exit(1); | ||
}) | ||
function isDirectoryExists(path: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For clarity, this method should be named |
||
try { | ||
return tl.stats(path).isDirectory(); | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ | |
"version": { | ||
"Major": 1, | ||
"Minor": 0, | ||
"Patch": 42 | ||
"Patch": 43 | ||
}, | ||
"demands": [ | ||
"ant" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ | |
"version": { | ||
"Major": 1, | ||
"Minor": 0, | ||
"Patch": 42 | ||
"Patch": 43 | ||
}, | ||
"demands": [ | ||
"ant" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this a issue in production also?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ye. but this is not P0 as this is just cleanup.