diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..0b4705e --- /dev/null +++ b/.jshintrc @@ -0,0 +1,85 @@ +{ + // JSHint Default Configuration File (as on JSHint website) + // See http://jshint.com/docs/ for more details + + "maxerr" : 50, // {int} Maximum error before stopping + + // Enforcing + "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) + "camelcase" : false, // true: Identifiers must be in camelCase + "curly" : true, // true: Require {} for every new block or scope + "eqeqeq" : true, // true: Require triple equals (===) for comparison + "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` + "latedef" : false, // true: Require variables/functions to be defined before being used + "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` + "noempty" : true, // true: Prohibit use of empty blocks + "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) + "plusplus" : false, // true: Prohibit use of `++` & `--` + "quotmark" : false, // Quotation mark consistency: + // false : do nothing (default) + // true : ensure whatever is used is consistent + // "single" : require single quotes + // "double" : require double quotes + "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused" : false, // true: Require all defined variables be used (OVERRIDE) + "strict" : false, // true: Requires all functions run in ES5 Strict Mode (OVERRIDE) + "trailing" : false, // true: Prohibit trailing whitespaces + "maxparams" : false, // {int} Max number of formal params allowed per function + "maxdepth" : false, // {int} Max depth of nested blocks (within functions) + "maxstatements" : false, // {int} Max number statements per function + "maxcomplexity" : false, // {int} Max cyclomatic complexity per function + "maxlen" : false, // {int} Max number of characters per line + + // Relaxing + "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "boss" : false, // true: Tolerate assignments where comparisons would be expected + "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // true: Tolerate use of `== null` + "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) + "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) + "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) + // (ex: `for each`, multiple try/catch, function expression…) + "evil" : false, // true: Tolerate use of `eval` and `new Function()` + "expr" : false, // true: Tolerate `ExpressionStatement` as Programs + "funcscope" : false, // true: Tolerate defining variables inside control statements" + "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') + "iterator" : false, // true: Tolerate using the `__iterator__` property + "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block + "laxbreak" : false, // true: Tolerate possibly unsafe line breakings + "laxcomma" : false, // true: Tolerate comma-first style coding + "loopfunc" : true, // true: Tolerate functions being defined in loops + "multistr" : false, // true: Tolerate multi-line strings + "proto" : false, // true: Tolerate using the `__proto__` property + "scripturl" : false, // true: Tolerate script-targeted URLs + "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment + "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` + "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation + "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` + "validthis" : false, // true: Tolerate using this in a non-constructor function + + // Environments + "browser" : true, // Web Browser (window, document, etc) + "couch" : false, // CouchDB + "devel" : true, // Development/debugging (alert, confirm, etc) + "dojo" : false, // Dojo Toolkit + "jquery" : false, // jQuery + "mootools" : false, // MooTools + "node" : true, // Node.js + "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) + "prototypejs" : false, // Prototype and Scriptaculous + "rhino" : false, // Rhino + "worker" : false, // Web Workers + "wsh" : false, // Windows Scripting Host + "yui" : false, // Yahoo User Interface + + // Legacy + "nomen" : false, // true: Prohibit dangling `_` in variables + "onevar" : false, // true: Allow only one `var` statement per function + "passfail" : false, // true: Stop on first error + "white" : false, // true: Check against strict whitespace and indentation rules + + // Custom Globals + "globals" : {} // additional predefined global variables +} \ No newline at end of file diff --git a/Gulpfile.js b/Gulpfile.js new file mode 100644 index 0000000..8081811 --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,76 @@ +/*global require*/ + +var gulp = require('gulp'), + gutil = require('gulp-util'), + mocha = require('gulp-mocha'), + jshint = require('gulp-jshint'), + bump = require('gulp-bump'), + stylish = require('jshint-stylish'); + + +//Application Paths +var paths = { + js : [ + './lib/**/*.js', + './gulpfile.js', + './index.js' + ], + tests : [ + 'test/functional/faye.js', + 'test/functional/io.js', + 'test/lib/baseworker.js', + 'test/lib/benchmark.js', + 'test/lib/fayeworker.js', + 'test/lib/logger.js', + 'test/lib/monitor.js', + 'test/lib/socketioworker.js', + 'test/lib/steps.js', + 'test/lib/stopwatch.js' + ], + + bump : [ + './package.json' + ] +}; + +//Gulp Tasks +gulp.task('jshint', function () { + gulp.src(paths.js) + .pipe(jshint('./.jshintrc')) + .pipe(jshint.reporter(stylish)); +}); + +gulp.task('mocha', function (done) { + gulp.src(paths.tests) + .pipe(mocha({ + bail : true, + lookup : true, + ui : 'bdd', + reporter : 'spec' + })) + .on("error", gutil.log); +}); + +//Version Bump (gulp bump-major | bump-minor | bump-patch) + +gulp.task('bump-major', function () { + return gulp.src(paths.bump) + .pipe(bump({type : 'major'})) + .pipe(gulp.dest('./')); +}); + +gulp.task('bump-minor', function () { + return gulp.src(paths.bump) + .pipe(bump({type : 'minor'})) + .pipe(gulp.dest('./')); +}); + +gulp.task('bump-patch', function () { + return gulp.src(paths.bump) + .pipe(bump({type : 'patch'})) + .pipe(gulp.dest('./')); +}); + + +//Gulp Runner +gulp.task('default', ['jshint', 'mocha']); \ No newline at end of file diff --git a/README.md b/README.md index c6da689..4b2bc7e 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ -# websocket bench +# websocket bench [![Build Status](https://travis-ci.org/M6Web/websocket-bench.png?branch=master)](https://travis-ci.org/M6Web/websocket-bench) + Nodejs cli tool for benchmarking websocket servers. Currently supports: * [Socket.IO](https://github.com/LearnBoost/socket.io) * [Faye](https://github.com/faye/faye) * [Primus](https://github.com/primus/primus) -[![Build Status](https://travis-ci.org/M6Web/websocket-bench.png?branch=master)](https://travis-ci.org/M6Web/websocket-bench) +## Installation + `npm install -g websocket-bench` -## Installation +## Running Tests/Linting - npm install -g websocket-bench + First Install required dev-dependencies `npm install` + Run Gulp Build Tool `gulp mocha` ## Usage @@ -52,24 +55,25 @@ For benchmark message or more advanced connection you should provide your own `g generator structure : - (function() { +```javascript + module.exports = { /** * Before connection (optional, just for faye) * @param {client} client connection */ - exports.beforeConnect = function(client) { + beforeConnect : function(client) { // Example: // client.setHeader('Authorization', 'OAuth abcd-1234'); // client.disable('websocket'); - }; + }, /** * On client connection (required) * @param {client} client connection * @param {done} callback function(err) {} */ - exports.onConnect = function(client, done) { + exports.onConnect : function(client, done) { // Faye client // client.subscribe('/channel', function(message) { }); @@ -80,21 +84,22 @@ generator structure : // client.write('Sailing the seas of cheese'); done(); - }; + }, /** * Send a message (required) * @param {client} client connection * @param {done} callback function(err) {} */ - exports.sendMessage = function(client, done) { + exports.sendMessage : function(client, done) { // Example: // client.emit('test', { hello: 'world' }); // client.publish('/test', { hello: 'world' }); done(); - }; + } + }; - })(); +``` ## See also diff --git a/index.js b/index.js index 1532fca..f0ce6d7 100644 --- a/index.js +++ b/index.js @@ -48,7 +48,7 @@ if (!program.generator) { program.generator = __dirname + '/lib/generator.js'; } -if (program.generator.indexOf('/') != 0) { +if (program.generator.indexOf('/') !== 0) { program.generator = process.cwd() + '/' + program.generator; } @@ -60,7 +60,7 @@ if (!program.type) { program.type = 'socket.io'; } -if (program.type == 'primus' && !program.transport) { +if (program.type === 'primus' && !program.transport) { program.transPort = 'websockets'; } @@ -84,7 +84,7 @@ if (program.verbose) { var outputStream = null; if (program.output) { - if (program.generator.indexOf('/') != 0) { + if (program.generator.indexOf('/') !== 0) { program.output = __dirname + '/' + program.generator; } outputStream = fs.createWriteStream(program.output); diff --git a/lib/benchmark.js b/lib/benchmark.js index 281901f..8091c61 100644 --- a/lib/benchmark.js +++ b/lib/benchmark.js @@ -9,7 +9,6 @@ var Monitor = require('./monitor.js'), * @param {server} server to benchmark */ var Benchmark = function (server, reporter, options) { - this.server = server; this.monitor = new Monitor(); this.stopwatch = new StopWatch(); @@ -17,7 +16,6 @@ var Benchmark = function (server, reporter, options) { this.workers = []; this.options = options || {}; this.reporter = reporter; - }; /** @@ -27,6 +25,7 @@ var Benchmark = function (server, reporter, options) { * @param {workerNumber} number of worker */ Benchmark.prototype.launch = function (connectNumber, concurency, workerNumber, nbMessage) { + var cp = require('child_process'); this.current = { connectNumber : connectNumber, @@ -34,22 +33,13 @@ Benchmark.prototype.launch = function (connectNumber, concurency, workerNumber, nbMessage : nbMessage || 0 }; - var cp = require('child_process'); - - var _this = this; - for (var i = 0; i < workerNumber; i++) { this.workers[i] = cp.fork(__dirname + '/worker.js', [ this.server, this.options.generatorFile, this.options.type, this.options.transport ]); - this.workers[i].on('message', function (message) { - if (message.action == 'done') { - _this._processResult(message.monitor); - } - ; - }); + this.workers[i].on('message', this._onMessage.bind(this)); } this.stopwatch.start(); @@ -59,7 +49,12 @@ Benchmark.prototype.launch = function (connectNumber, concurency, workerNumber, } this._nextStep(concurency, connectNumber, concurency, nbMessage); +}; +Benchmark.prototype._onMessage = function(message) { + if (message.action === 'done') { + this._processResult(message.monitor); + } }; /** @@ -70,7 +65,6 @@ Benchmark.prototype.launch = function (connectNumber, concurency, workerNumber, * @api private */ Benchmark.prototype._nextStep = function (currentNumber, connectNumber, concurency, nbMessage) { - if (this.options.verbose) { logger.debug('trying : ' + currentNumber + ' ...'); } @@ -84,10 +78,9 @@ Benchmark.prototype._nextStep = function (currentNumber, connectNumber, concuren this.steps.addStep(concurency, stepMonitor, stepStopWatch); for (var i = 0; i < this.workers.length; i++) { - var nbConnection = Math.round(concurency / this.workers.length); - if (i == this.workers.length - 1) { + if (i === this.workers.length - 1) { nbConnection = concurency - nbConnection * i; } @@ -95,9 +88,7 @@ Benchmark.prototype._nextStep = function (currentNumber, connectNumber, concuren } if (currentNumber < connectNumber) { - setTimeout(function () { - if (currentNumber >= connectNumber - concurency) { concurency = connectNumber - currentNumber; @@ -109,7 +100,6 @@ Benchmark.prototype._nextStep = function (currentNumber, connectNumber, concuren _this._nextStep(currentNumber, connectNumber, concurency, nbMessage); }, 1000); } - }; /** @@ -118,7 +108,6 @@ Benchmark.prototype._nextStep = function (currentNumber, connectNumber, concuren * @api private */ Benchmark.prototype._processResult = function (monitor) { - this.monitor.merge(monitor); var step = this.steps.findStep(this.monitor.counter); @@ -129,7 +118,7 @@ Benchmark.prototype._processResult = function (monitor) { var numberForStep = (previousStep) ? step.number - previousStep.number : step.number; - if (numberForStep == step.monitor.counter) { + if (numberForStep === step.monitor.counter) { step.stopwatch.stop(); } @@ -143,19 +132,16 @@ Benchmark.prototype._processResult = function (monitor) { * Terminate all running workers */ Benchmark.prototype.close = function () { - for (var i = 0; i < this.workers.length; i++) { this.workers[i].send({ msg : 'close'}); } - }; /** * Terminate and then display result */ Benchmark.prototype.terminate = function () { - // Stop all running monitor this.stopwatch.stop(); @@ -175,10 +161,9 @@ Benchmark.prototype.terminate = function () { * @api private */ Benchmark.prototype._report = function () { - if (this.reporter) { this.reporter.report(this.steps.getSteps(), this.monitor, this.stopwatch); } -} +}; module.exports = Benchmark; diff --git a/lib/defaultreporter.js b/lib/defaultreporter.js index 907fde3..a78c0e8 100644 --- a/lib/defaultreporter.js +++ b/lib/defaultreporter.js @@ -1,6 +1,5 @@ /*global module, require*/ var Table = require('cli-table'); -var colors = require('colors'); /** * Class for display bench result diff --git a/lib/generator.js b/lib/generator.js index 1438ce5..e4d3f5f 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,4 +1,5 @@ /*global module, require*/ + var logger = require('./logger'); module.exports = { diff --git a/lib/steps.js b/lib/steps.js index da6ec28..f910756 100644 --- a/lib/steps.js +++ b/lib/steps.js @@ -12,7 +12,7 @@ var Steps = function () { * @return Object */ Steps.prototype.getLastStep = function () { - if (this._steps.length == 0) { + if (this._steps.length === 0) { return null; } return this._steps[this._steps.length - 1]; diff --git a/lib/stopwatch.js b/lib/stopwatch.js index bea5909..e02616d 100644 --- a/lib/stopwatch.js +++ b/lib/stopwatch.js @@ -22,7 +22,7 @@ StopWatch.prototype.start = function () { * Stop timer */ StopWatch.prototype.stop = function () { - if (this.timer.stop == 0) { + if (this.timer.stop === 0) { this.timer.stop = Date.now(); } }; @@ -31,7 +31,7 @@ StopWatch.prototype.stop = function () { * get duration */ StopWatch.prototype.getDuration = function () { - if (this.timer.start == 0 || this.timer.stop == 0) { + if (this.timer.start === 0 || this.timer.stop === 0) { return 0; } diff --git a/lib/worker.js b/lib/worker.js index 828d3b0..456363c 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -4,39 +4,38 @@ var logger = require('./logger'), server = process.argv[2], generatorFile = process.argv[3], workerType = process.argv[4], - verbose = process.argv[6] === 'true', - Worker; + verbose = process.argv[6] === 'true'; -if (!generatorFile || generatorFile == 'undefined') { +if (!generatorFile || generatorFile === 'undefined') { generatorFile = './generator.js'; } var generator = require(generatorFile); +var BenchmarkWorker = null; switch (workerType) { case 'socket.io': - Worker = require('./workers/socketioworker.js'); + BenchmarkWorker = require('./workers/socketioworker.js'); break; case 'faye': - Worker = require('./workers/fayeworker.js'); + BenchmarkWorker = require('./workers/fayeworker.js'); break; case 'primus': - Worker = require('./workers/primusworker.js'); + BenchmarkWorker = require('./workers/primusworker.js'); break; default: logger.error('error workerType ' + workerType); } -var worker = new Worker(server, generator, verbose); +var worker = new BenchmarkWorker(server, generator, verbose); process.on('message', function (message) { - if (message.msg == 'close') { + if (message.msg === 'close') { worker.close(); - process.exit(); } - if (message.msg == 'run') { + if (message.msg === 'run') { worker.launch(message.number, message.nbMessage); } }); diff --git a/lib/workers/baseworker.js b/lib/workers/baseworker.js index 887b810..3873839 100644 --- a/lib/workers/baseworker.js +++ b/lib/workers/baseworker.js @@ -21,16 +21,14 @@ var BaseWorker = function (server, generator, verbose) { /** * launch client creation and message * @param {number} number - * @param {generator} generator + * @param {messageNumber} messageNumber */ BaseWorker.prototype.launch = function (number, messageNumber) { - var _this = this; - var monitor = new Monitor(); for (var i = 0; i < number; i++) { - var client = this.createClient(function (err, client) { + this.createClient(function (err, client) { _this.clients.push(client); _this._onClientCreation(client, monitor, messageNumber, err); }); @@ -88,7 +86,7 @@ BaseWorker.prototype._onClientCreation = function (client, monitor, messageNumbe } }); } -} +}; /** * _onMessageSend internal method @@ -100,14 +98,14 @@ BaseWorker.prototype._onMessageSend = function (monitor, err) { } else { monitor.msgSend(); } -} +}; /** * _testLaunchDone internal method * @api private */ BaseWorker.prototype._testLaunchDone = function (monitor, number, messageNumber) { - if (monitor.counter == number && monitor.messageCounter == (monitor.results.connection * messageNumber)) { + if (monitor.counter === number && monitor.messageCounter === (monitor.results.connection * messageNumber)) { process.send({ action : 'done', monitor : monitor }); @@ -115,6 +113,6 @@ BaseWorker.prototype._testLaunchDone = function (monitor, number, messageNumber) } return false; -} +}; module.exports = BaseWorker; diff --git a/package.json b/package.json index 3d46cff..0ffcbbd 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "tool for benchmark websocket (socket.io, faye)", "main": "index.js", "scripts": { - "test": "mocha" + "test": "gulp mocha" }, "repository": { "type": "git", @@ -23,12 +23,18 @@ "faye": "~0.8.9", "winston": "~0.7.1", "socket.io": "~0.9.14", - "chai": "~1.6.1", "primus": "~1.5.2" }, "devDependencies": { "chai": "~1.6.0", "mocha": "*", - "sinon": "*" + "sinon": "*", + "gulp": "~3.5.2", + "gulp-util": "~2.2.14", + "gulp-jshint": "~1.4.2", + "jshint-stylish": "~0.1.5", + "gulp-mocha": "~0.4.1", + "gulp-bump": "~0.1.5", + "gulp-debug": "~0.2.0" } } diff --git a/test/lib/baseworker.js b/test/lib/baseworker.js index 36a88ea..f888530 100644 --- a/test/lib/baseworker.js +++ b/test/lib/baseworker.js @@ -1,4 +1,4 @@ -/*global require, describe, it, beforeEach, afterEach*/ +/*global require, describe, it, xit, beforeEach, afterEach*/ var mocha = require('mocha'), chai = require('chai'), @@ -32,7 +32,11 @@ describe('BaseWorker', function () { }); describe('#launch', function () { - it('Should create of clients with ', function () { + //Commented out test since it doesn't work as expected + //The callbacks aren't executed when stubbing methods, therefore + //The test hangs. When running with mocha you will see that it isn't functioning + //As Expected, IE Change values and it still passes. + xit('Should create of clients with ', function () { var stubCreateClient = sinon.stub(worker, 'createClient'); worker.launch(3, 2); diff --git a/test/lib/benchmark.js b/test/lib/benchmark.js index cf54fff..b3c1a20 100644 --- a/test/lib/benchmark.js +++ b/test/lib/benchmark.js @@ -40,5 +40,4 @@ describe('Benchmark', function () { assert(stub.called); }); }); -}); - +}); \ No newline at end of file