Skip to content

Commit

Permalink
Add PHPUnit plugin for parsing PHPUnit results
Browse files Browse the repository at this point in the history
  • Loading branch information
Steven Surowiec committed Jan 28, 2013
1 parent e0ae9c4 commit 938b666
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 18 deletions.
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Mergeatron comes with multiple different plugins you can opt to use. By default
* `github.api.host` - Optional parameter to set the host for the GitHub REST API. This should only be needed if you're running GitHub Enterprise and can be excluded entirely otherwise.
* `github.api.port` - Optional parameter to set the port for the GitHub REST API. This should only be needed if you're running GitHub Enterprise and can be excluded entirely otherwise.
* `phpcs.artifact` - The name of the artifact file that contains PHP Code Sniffer results. If no artifact with this name is found the plugin won't do anything.
* `phpunit.artifact` - The name of the artifact file that contains PHPUnit results. Currently Mergeatron assumes the results are in junit format.
* `phpunit.failure_limit` - The maximum number of verbose error messages to display in the failure comment.

## Setting Up GitHub

Expand Down Expand Up @@ -135,14 +137,14 @@ exports.init = function(config, mergeatron) {

## Events

* ''pull.found'' - This is the first event emitted in a builds life cycle. It allows any listening plugins to check the build to make sure it should be handled.
* ''pull.validated'' - If a build should be acted upon this event will be emitted. It allows any listening plugins to setup the build for processing. This means persisting it to a temporary, or permenant, data store of their choice and doing any other setup work they need to.
* ''pull.processed'' - Once a build has been pre-processed it is ready to be built. When that happens this event is emitted. Any listening plugins can start the build.
* ''build.started'' - This event is emitted when the build has been started.
* ''build.succeeded'' - This event is emitted when a build was successful.
* ''build.failed'' - This event is emitted when a build has failed.
* ''pull.inline_status'' - This event is emitted when a plugin is announcing that something was found on a specific line of a files diff within the build.
* ''build.artifact_found'' - This event is emitted once for each artifact found after the build has finished. Plugins receive the URL to the artifact and can download and act upon it if wanted.
* `pull.found` - This is the first event emitted in a builds life cycle. It allows any listening plugins to check the build to make sure it should be handled.
* `pull.validated` - If a build should be acted upon this event will be emitted. It allows any listening plugins to setup the build for processing. This means persisting it to a temporary, or permenant, data store of their choice and doing any other setup work they need to.
* `pull.processed` - Once a build has been pre-processed it is ready to be built. When that happens this event is emitted. Any listening plugins can start the build.
* `build.started` - This event is emitted when the build has been started.
* `build.succeeded` - This event is emitted when a build was successful.
* `build.failed` - This event is emitted when a build has failed.
* `pull.inline_status` - This event is emitted when a plugin is announcing that something was found on a specific line of a files diff within the build.
* `build.artifact_found` - This event is emitted once for each artifact found after the build has finished. Plugins receive the URL to the artifact and can download and act upon it if wanted.

## Contributing

Expand Down
6 changes: 6 additions & 0 deletions config.sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ exports.config = {
frequency: 2000
},
phpcs: {
enabled: false,
artifact: 'artifacts/phpcs.csv'
},
phpunit: {
enabled: false,
artifact: 'artifacts/junit.xml',
failure_limit: 3
}
}
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"request": ">=2.9.203",
"node-uuid": ">=1.3.3",
"async": ">=0.1.22",
"winston": ">=0.6.2"
"winston": ">=0.6.2",
"xml2js": ">=0.2.2"
},

"devDependencies": {
Expand Down
31 changes: 22 additions & 9 deletions plugins/github.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,15 +333,24 @@ GitHub.prototype.createStatus = function(sha, user, repo, state, build_url, desc
* @param comment {String}
*/
GitHub.prototype.createComment = function(pull, sha, file, position, comment) {
this.api.pullRequests.createComment({
user: this.config.user,
repo: pull.repo,
number: pull.number,
body: comment,
commit_id: sha,
path: file,
position: position
});
if (!file && !position && !comment) {
this.api.issues.createComment({
user: this.config.user,
repo: pull.repo,
number: pull.number,
body: sha
});
} else {
this.api.pullRequests.createComment({
user: this.config.user,
repo: pull.repo,
number: pull.number,
body: comment,
commit_id: sha,
path: file,
position: position
});
}
};

/**
Expand Down Expand Up @@ -437,6 +446,10 @@ exports.init = function(config, mergeatron) {
github.createComment(pull, sha, file, position, comment) ;
});

mergeatron.on('pull.status', function(pull, comment) {
github.createComment(pull, comment);
});

events.on('pull_request', function(pull) {
github.handlePullRequest(pull);
});
Expand Down
106 changes: 106 additions & 0 deletions plugins/phpunit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* The PHPUnit integration plugin
* @module PHPUnit
*/
"use strict";

var request = require('request'),
xml2js = require('xml2js');

/**
* @class PhpUnit
* @param config {Object} The plugins configs
* @param mergeatron {Mergeatron} An instance of the main Mergeatron object
* @constructor
*/
var PhpUnit = function(config, mergeatron) {
this.config = config;
this.mergeatron = mergeatron;
};

/**
* Processes a PHPUnit generated junit.xml
*
* @method process
* @param build {String}
* @param pull {Object}
* @param artifact_url {String}
*/
PhpUnit.prototype.process = function(build, pull, artifact_url) {
var self = this;

request({ url: artifact_url }, function(err, response) {
if (err) {
self.mergeatron.log.error(err);
return;
}

var parser = new xml2js.Parser();
parser.parseString(response.body, function (err, result) {
if (err) {
self.mergeatron.log.error('Failed to parse PHPUnit artifact', err);
return;
}

if (!result) {
return;
}

var error_count = 0,
errors = [],
message = '',
truncate = false;

for (var i in result.testsuites) {
var item = result.testsuites[i];

// First check if there were any errors in the test suite, if not there's nothing to do.
if (item[0]['$'].errors == 0) {
continue;
}

// We don't want to leave too big of a comment, so lets only check for individual
// errors if there aren't too many
error_count += parseInt(item[0]['$'].errors) + parseInt(item[0]['$'].failures);

if (errors.length < self.config.failure_limit) {
errors.push(item[0].testsuite[0].testcase[0].failure[0]._);
} else {
truncate = true;
}
}

if (error_count > 0) {
message = "Build failed due to test failures. __" + error_count + "__ test failure(s) found.\n";
}

if (errors.length > 0) {
errors.forEach(function(error, i) {
// The error message has a blank line between the message and the file, need to strip that out
error = error.replace(/\n\n/gi, "\n");

message += '>' + error + "\n";
});
}

if (truncate) {
message += "There were too many failures to display detailed information on all of them. View build details for full list of failures\n";
}

if (message) {
message += "\n\n[Click Here For More Information](" + build.url + ")";
self.mergeatron.emit('pull.status', pull, message);
}
});
});
};

exports.init = function(config, mergeatron) {
var phpunit = new PhpUnit(config, mergeatron);

mergeatron.on('build.artifact_found', function (build, pull, artifact) {
if (artifact.relativePath == config.artifact) {
phpunit.process(build, pull, artifact.url);
}
});
};

0 comments on commit 938b666

Please sign in to comment.