Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
tlovett1 committed Feb 17, 2015
0 parents commit d1bf7f8
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
.idea
node_modules
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Dockunit
==========

Run containerized language agnostic unit tests.

## Purpose

We all want to test our applications on as many relevant platforms as possible. Sometimes this is easy.
Sometimes it's not. Dockunit let's you define a set of Docker containers to run your tests against. You can run your
test framework of choice in your language of choice on any type of environment. In the past many developers, myself
included, have relied on Travis CI to run tests in environments that aren't setup locally (i.e. PHP 5.2). With
Dockunit you don't need to do this anymore.

## Requirements

* [NodeJS](http://nodejs.org/)
* [npm](https://www.npmjs.com/)
* [Docker](https://www.docker.com/)

## Installation

Install via npm
8 changes: 8 additions & 0 deletions bin/dockunit
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env node

'use strict';
var path = require('path');
var fs = require('fs');
var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');

require(lib + '/command.js').execute();
68 changes: 68 additions & 0 deletions lib/command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use strict';

var fs = require('fs');
var argv = require('minimist')(process.argv.slice(2));
var Containers = require('./containers').containers;

/**
* Configuration for dockunit
*
* @type {{path: *, verbose: boolean}}
*/
global.config = {
path: process.cwd(),
verbose: false
};

/**
* Test arguments to pass to test command
*/
global.testArgs = {};

/**
* Supported dockunit arguments/options
*
* @type {{du-verbose: boolean}}
*/
global.defaultArgs = {
'du-verbose': false
};

/**
* Process command line options and arguments
*/
var processArgs = function() {
global.testArgs = argv;

if (argv['du-verbose']) {
config.verbose = true;
}

if (argv._.length) {
config.path = argv._[0];
delete testArgs._[0];
}

for (var key in testArgs) {
if (key !== '_' && typeof defaultArgs[key] !== 'undefined') {
delete testArgs[key];
}
}
};

/**
* Main script command
*/
exports.execute = function() {
processArgs();

try {
var json = JSON.parse(fs.readFileSync(config.path + '/Dockunit.json', 'utf8'));
} catch (exception) {
console.log('Could not parse Dockunit.json');
process.exit(1);
}

var containers = new Containers(json.containers);
containers.run();
};
113 changes: 113 additions & 0 deletions lib/container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
'use strict';

var spawn = require('child_process').spawn;

/**
* Initialize a new container
*
* @param containerArray
* @constructor
*/
var Container = function(containerArray) {
this.image = containerArray.image;
this.testCommand = containerArray.testCommand;
this.prettyName = containerArray.prettyName;
this.beforeScripts = containerArray.beforeScripts;
};

/**
* Run tests on given container object
*/
Container.prototype.run = function() {
var self = this,
containerId,
options = {};

if (global.config.verbose) {
options = { stdio: 'inherit' };
}

console.log('Testing on container ' + self.prettyName);

if (global.config.verbose) {
console.log('Pulling docker image: ' + self.image);
}

spawn('docker', ['pull', this.image], options).on('exit', function(code) {

var run = spawn('docker', ['run', '-d', '-v', global.config.path + ':/app/test', '-w', '/app/test', '-it', self.image, '/bin/bash']);

// Sanitize and store container ID
run.stdout.on('data', function(data) {
containerId = data.toString().trim().replace(/[^a-z0-9]/ig, '');
});

run.on('exit', function(code) {

if (self.beforeScripts && self.beforeScripts.length) {
if (global.config.verbose) {
console.log('Running before scripts');
}

if (self.beforeScripts.length) {

var index = 0;

var runBeforeScript = function() {
spawn('sh', ['-c', 'docker exec ' + containerId + ' ' + self.beforeScripts[index]], options).on('exit', function(code) {

if (index === self.beforeScripts.length) {
runTest();
} else {
index++;
runBeforeScript();
}

});
}

runBeforeScript();

} else {
runTest();
}
}

function runTest() {
var testArgString = '';

if (global.testArgs._.length) {
testArgString = global.testArgs._.join(' ');
}

for (var key in global.testArgs) {
if ('_' !== key) {
if (typeof global.testArgs[key] !== 'boolean') {
testArgString += ' --' + key + '=' + global.testArgs[key];
} else {
if (global.testArgs[key]) {
testArgString += ' --' + key;
}
}
}
}

if (global.config.verbose) {
console.log('Running "' + self.testCommand + testArgString + '" on container');
}

spawn('sh', ['-c', 'docker exec -i ' + containerId + ' bash + -c "source ~/.bashrc && ' + self.testCommand + ' ' + testArgString + ' " 1>&2'], { stdio: 'inherit' }).on('exit', function(code) {

spawn('docker', ['stop', containerId]).on('exit', function(code) {
if (global.config.verbose) {
console.log('Stopped container');
}
});
});
}
});
});
};


exports.container = Container;
29 changes: 29 additions & 0 deletions lib/containers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

var Container = require( './container').container;

/**
* Initialize new containers object
*
* @param containersArray
* @constructor
*/
var Containers = function(containersArray) {
this.containers = [];

for (var i = 0; i < containersArray.length; i++) {
this.containers.push(new Container(containersArray[i]))
}
};

/**
* Run all containers
*/
Containers.prototype.run = function() {
for (var i = 0; i < this.containers.length; i++) {
this.containers[i].run();
}
}


exports.containers = Containers;
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "dockunit",
"version": "0.1.0",
"description": "Containerized language agnostic unit testing.",
"main": "./lib/command.js",
"bin": {
"dockunit": "./bin/dockunit"
},
"author": "Taylor Lovett (http://taylorlovett.com)",
"license": "GPLv2+",
"dependencies": {
"minimist": "^1.1.0"
},
"keywords": [
"docker",
"phpunit",
"qunit",
"tests"
],
"engines": {
"node": "*"
},
"repository": {
"type": "git",
"url": "git://github.com/tlovett1/dockunit.git"
}
}

0 comments on commit d1bf7f8

Please sign in to comment.