Skip to content
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

Merged
merged 14 commits into from
May 18, 2016
4 changes: 1 addition & 3 deletions Tasks/ANT/ant.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ if($isCoverageEnabled)
}
}



$summaryFile = Join-Path $buildRootPath $reportDirectoryName
$summaryFile = Join-Path $summaryFile $summaryFileName
# ensuring unique code coverage report task name by using guid
Expand All @@ -141,7 +139,7 @@ if($isCoverageEnabled)
# Enable code coverage in build file
if ($codeCoverageTool -eq "Cobertura")
{
$coberturaCCFile = Join-Path $buildRootPath "cobertura.cer"
$coberturaCCFile = Join-Path $buildRootPath "cobertura.ser"
Copy link
Contributor

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?

Copy link
Contributor Author

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.

if(Test-Path $coberturaCCFile)
{
# delete any previous cobertura code coverage file
Expand Down
213 changes: 167 additions & 46 deletions Tasks/ANT/anttask.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
/// <reference path="../../definitions/vsts-task-lib.d.ts" />

import tl = require('vsts-task-lib/task');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright info

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

import path = require('path');
Copy link

Choose a reason for hiding this comment

The 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.

Copy link

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

@AshwiniChalla AshwiniChalla May 17, 2016

Choose a reason for hiding this comment

The 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);
Copy link

Choose a reason for hiding this comment

The 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));
Expand All @@ -32,23 +35,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');
Expand All @@ -61,44 +64,162 @@ 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");
if (isDirectoryExists(instrumentedClassesDirectory)) {
//delete any previous cobertura instrumented classes as they might interfere with ant execution.
tl.rmRF(instrumentedClassesDirectory);
}

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);
}
else {
tl.debug('No pattern found in testResultsFiles parameter');
var matchingTestResultsFiles = [testResultsFiles];
}

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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardcoded directory names?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
if (isFileExists(coberturaCCFile)) {
tl.rmRF(coberturaCCFile);
}

if(!matchingTestResultsFiles) {
tl.warning('No test result files matching ' + testResultsFiles + ' were found, so publishing JUnit test results is being skipped.');
return 0;
if (isDirectoryExists(reportDirectory)) {
tl.rmRF(reportDirectory);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to check these? Remove directly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

var tp = new tl.TestPublisher("JUnit");
tp.publish(matchingTestResultsFiles, true, "", "","", true);
}
if (isFileExists(reportBuildFile)) {
tl.rmRF(reportBuildFile);
}

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.");
}
catch (Error) {
tl.warning("Enabling code coverage failed. Check the build logs for errors.");
}
}

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 publishCodeCoverage(codeCoverageOpted: boolean) {
if (codeCoverageOpted) {
tl.debug("Collecting code coverage reports");
var antRunner = tl.createToolRunner(anttool);
if (isFileExists(reportBuildFile)) {
antRunner.arg('-buildfile');
antRunner.pathArg(reportBuildFile);
antRunner.arg(ccReportTask);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactor this code. based on condition you are just changing one argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

else {
antRunner.arg('-buildfile');
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.");
});
}
}

function isFileExists(path: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarity, this method should be named pathExistsAsFile().

try {
return tl.stats(path).isFile();
}
catch (error) {
return false;
}
}

function isDirectoryExists(path: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarity, this method should be named pathExistsAsDirectory().

try {
return tl.stats(path).isDirectory();
}
catch (error) {
return false;
}
}
2 changes: 1 addition & 1 deletion Tasks/ANT/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"version": {
"Major": 1,
"Minor": 0,
"Patch": 42
"Patch": 43
},
"demands": [
"ant"
Expand Down
2 changes: 1 addition & 1 deletion Tasks/ANT/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"version": {
"Major": 1,
"Minor": 0,
"Patch": 42
"Patch": 43
},
"demands": [
"ant"
Expand Down
Loading