├── .gitignore ├── .travis.yml ├── History.md ├── Makefile ├── README.md ├── index.js ├── package.json └── test ├── _request.js ├── index.js └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *.swu 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 4 | - 0.8 5 | - 0.10 6 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.5.0 / 2013-09-12 3 | ================== 4 | 5 | * whitelist some globals under Windows for node >=v0.8.x #6 [yannickcr](https://github.com/yannickcr) 6 | * update travis img 7 | 8 | 0.4.0 / 2013-04-06 9 | ================== 10 | 11 | * no longer guaranteed compatibility with node 0.4x 12 | 13 | 0.3.0 / 2013-04-06 14 | ================== 15 | 16 | * added; node v0.10 dtrace garbage 17 | * added; node v0.10 setImmediate, clearImmediate #5 [jonathanong](https://github.com/onathanong) 18 | * tests; handle express 2 or 3 19 | * fixed; test for node 0.4.x 20 | 21 | 0.2.4 / 06-22-2012 22 | ================== 23 | 24 | * added; compatibility with node 0.7.x & node 0.8.x 25 | 26 | 0.2.3 / 01-27-2012 27 | ================== 28 | 29 | * fixed; tests for node v0.6x 30 | 31 | 0.2.2 / 11-23-2011 32 | ================== 33 | 34 | * fixed; works on node v0.5.0-pre 35 | * fixed; node engine 36 | 37 | 0.2.1 / 10-18-2011 38 | ================== 39 | 40 | * fixed; package.json dependency versioning 41 | 42 | 0.2.0 / 10-11-2011 43 | ================== 44 | 45 | * added; node v0.5 / v0.6 support 46 | 47 | 0.1.3 / 09-22-2011 48 | ================== 49 | 50 | * use old school node engine format in package.json 51 | 52 | 0.1.2 / 09-08-2011 53 | ================== 54 | 55 | * changed; utilize detectNew in middleware 56 | * updated; docs 57 | 58 | 0.1.1 / 09-07-2011 59 | ================== 60 | 61 | * added; #detectNew method 62 | 63 | 0.1.0 / 09-06-2011 64 | ================== 65 | 66 | * added; #ignore method 67 | * added; multiple instance support 68 | 69 | 0.0.2 / 09-06-2011 70 | ================== 71 | 72 | * added; allow whitelisting by variable name 73 | 74 | 0.0.1 / 09-03-2011 75 | ================== 76 | 77 | * initial release 78 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TESTS = test/index.js 2 | 3 | test: 4 | @NODE_ENV=test node $(TESTFLAGS) $(TESTS) 5 | 6 | .PHONY: test 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gleak 2 | 3 | Global variable leak detection for Node.js [![Build Status](https://travis-ci.org/aheckmann/gleak.png?branch=master)](https://travis-ci.org/aheckmann/gleak) 4 | 5 | var detector = require('gleak')(); 6 | 7 | detector.detect().forEach(function (name) { 8 | console.warn('found global leak: %s', name); 9 | }); 10 | 11 | Global variable leaks in javascript can bite you when you least 12 | expect it. Do something about it now and run this module after 13 | your tests, after HTTP requests, and after you brush your teeth. 14 | 15 | ## Detectable 16 | 17 | As demonstrated, gleak comes with the `detect` method which returns 18 | an array of all found variable leaks. 19 | 20 | Often times we want to run the detector many times, progressively 21 | checking for any new leaks that occurred since we last checked. In 22 | this scenario we can utilize the `detectNew` method. 23 | 24 | var detector = require('gleak')(); 25 | 26 | x = 1; 27 | detector.detectNew(); // ['x'] 28 | detector.detectNew(); // [] 29 | y = 3; 30 | detector.detectNew(); // ['y'] 31 | 32 | ## Configurable: 33 | 34 | Gleak comes configured for Node.js and will ignore built-ins by default 35 | but you can configure it however your like: 36 | 37 | var gleak = require('gleak')(); 38 | gleak.ignore(app, db); 39 | 40 | The `gleak.ignore` method allows us to add globals we want to ignore 41 | while safely ignoring duplicates. 42 | 43 | `gleak.whitelist` is an array that holds all globals we are ignoring. 44 | You can push to it or blow it away completely with your own list too. 45 | 46 | var gleak = require('gleak')(); 47 | gleak.whitelist = [dnode, cluster]; 48 | 49 | Changes to your whitelists do not impact any global settings. For example: 50 | 51 | var gleak = require('gleak'); 52 | var g1 = gleak(); 53 | var g2 = gleak(); 54 | 55 | g1.ignore(myglobal); 56 | g2.whitelist.indexOf(myglobal) === -1; 57 | 58 | `g2` does not inherit changes to `g1`s whitelist. 59 | 60 | ## Printable 61 | 62 | If you don't want anything fancy and want to quickly dump all 63 | global leaks to your console, just call `print()`. 64 | 65 | var gleak = require('gleak')(); 66 | gleak.print(); // prints "Gleak!: leakedVarName" 67 | 68 | ## Expressable 69 | 70 | We might want to print leaked variables to our console after each 71 | HTTP request. This is especially helpful during development. 72 | To accomplish this we can utilize the bundled [express](http://expressjs.com) middleware: 73 | 74 | var app = express.createServer(); 75 | app.use(gleak.middleware()); 76 | 77 | What if we want to output to a different stream than stderr? 78 | 79 | app.use(gleak.middleware(stream)); 80 | 81 | How about customized logging formats? 82 | 83 | app.use(gleak.middleware('\x1b[31mLeak!\x1b[0m %s')); 84 | 85 | Combining formats and streams? 86 | 87 | app.use(gleak.middleware(stream, '\x1b[31mLeak!\x1b[0m %s')); 88 | 89 | ## Installable 90 | 91 | npm install gleak 92 | 93 | ### Node version 94 | Compatible with Node >=0.6 95 | 96 | Node >= 0.4.1 probably still works too but the tests no longer confirm it. 97 | 98 | ## License 99 | 100 | (The MIT License) 101 | 102 | Copyright (c) 2011 [Aaron Heckmann](aaron.heckmann+github@gmail.com) 103 | 104 | Permission is hereby granted, free of charge, to any person obtaining 105 | a copy of this software and associated documentation files (the 106 | 'Software'), to deal in the Software without restriction, including 107 | without limitation the rights to use, copy, modify, merge, publish, 108 | distribute, sublicense, and/or sell copies of the Software, and to 109 | permit persons to whom the Software is furnished to do so, subject to 110 | the following conditions: 111 | 112 | The above copyright notice and this permission notice shall be 113 | included in all copies or substantial portions of the Software. 114 | 115 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 116 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 117 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 118 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 119 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 120 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 121 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 122 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gleak - detect global var leaks. 3 | * @api public 4 | */ 5 | 6 | module.exports = exports = function gleak () { 7 | return new Gleak; 8 | } 9 | 10 | /** 11 | * Version. 12 | * @api public 13 | */ 14 | 15 | exports.version = JSON.parse( 16 | require('fs').readFileSync(__dirname + '/package.json', 'utf8') 17 | ).version; 18 | 19 | /** 20 | * Express middleware. 21 | * @api public 22 | */ 23 | 24 | exports.middleware = function gleakMiddleware (stream, format) { 25 | var g = new Gleak; 26 | 27 | if (!format) { 28 | switch (typeof stream) { 29 | case 'string': 30 | format = stream; 31 | stream = process.stderr; 32 | break; 33 | case 'undefined': 34 | format = g.format; 35 | stream = process.stderr; 36 | break; 37 | default: 38 | format = g.format; 39 | } 40 | } 41 | 42 | setTimeout(print, 1000); 43 | 44 | function print () { 45 | g.detectNew().forEach(function (leak) { 46 | stream.write(format.replace(/%s/, leak) + '\n'); 47 | }); 48 | } 49 | 50 | return function gleakMiddleware (req, res, next) { 51 | if (res._gleak) return next(); 52 | res._gleak = true; 53 | 54 | var send = res.send; 55 | 56 | res.send = function () { 57 | res.send = send; 58 | res.send.apply(res, arguments); 59 | print(); 60 | } 61 | 62 | next(); 63 | } 64 | } 65 | 66 | /** 67 | * Gleak constructor 68 | * @api private 69 | */ 70 | 71 | function Gleak () { 72 | this.whitelist = this.whitelist.slice(); 73 | } 74 | 75 | /** 76 | * Whitelisted globals. 77 | * @api public 78 | */ 79 | 80 | var dtraceLeaks = [ 81 | 'DTRACE_NET_SERVER_CONNECTION' 82 | , 'DTRACE_NET_STREAM_END' 83 | , 'DTRACE_NET_SOCKET_READ' 84 | , 'DTRACE_NET_SOCKET_WRITE' 85 | , 'DTRACE_HTTP_SERVER_REQUEST' 86 | , 'DTRACE_HTTP_SERVER_RESPONSE' 87 | , 'DTRACE_HTTP_CLIENT_REQUEST' 88 | , 'DTRACE_HTTP_CLIENT_RESPONSE' 89 | ] 90 | 91 | var countersLeaks = [ 92 | 'COUNTER_NET_SERVER_CONNECTION' 93 | , 'COUNTER_NET_SERVER_CONNECTION_CLOSE' 94 | , 'COUNTER_HTTP_SERVER_REQUEST' 95 | , 'COUNTER_HTTP_SERVER_RESPONSE' 96 | , 'COUNTER_HTTP_CLIENT_REQUEST' 97 | , 'COUNTER_HTTP_CLIENT_RESPONSE' 98 | ] 99 | 100 | // v0.4.x 101 | Gleak.prototype.whitelist = [ 102 | setTimeout 103 | , setInterval 104 | , clearTimeout 105 | , clearInterval 106 | , console 107 | , Buffer 108 | , process 109 | , global 110 | , GLOBAL 111 | , root 112 | ]; 113 | 114 | // check for new globals in >= v0.5x 115 | var version = process.version.replace(/^v/, '').split('.'); 116 | 117 | if ('0' == version[0]) { 118 | if (version[1] > 4 && process.version != 'v0.5.0-pre') { 119 | Gleak.prototype.whitelist.push( 120 | ArrayBuffer 121 | , Int8Array 122 | , Uint8Array 123 | , Int16Array 124 | , Uint16Array 125 | , Int32Array 126 | , Uint32Array 127 | , Float32Array 128 | , Float64Array 129 | , DataView 130 | , 'errno' // node >= v0.5.x hack 131 | ) 132 | } 133 | 134 | if (version[1] > 6) { 135 | Gleak.prototype.whitelist.push(Uint8ClampedArray); 136 | } 137 | 138 | if (version[1] >= 8) { 139 | dtraceLeaks.forEach(function (leak) { 140 | if ('undefined' != typeof global[leak]) 141 | Gleak.prototype.whitelist.push(global[leak]); 142 | }) 143 | } 144 | 145 | if (version[1] > 8) { 146 | Gleak.prototype.whitelist.push(setImmediate, clearImmediate); 147 | countersLeaks.forEach(function (leak) { 148 | if ('undefined' != typeof global[leak]) 149 | Gleak.prototype.whitelist.push(global[leak]); 150 | }) 151 | } 152 | } 153 | 154 | /** 155 | * Default format. 156 | * @api public 157 | */ 158 | 159 | Gleak.prototype.format = '\x1b[31mGleak!:\x1b[0m %s'; 160 | 161 | /** 162 | * Detects global variable leaks. 163 | * @api public 164 | */ 165 | 166 | Gleak.prototype.detect = function detect () { 167 | var whitelist = this.whitelist 168 | , ret = [] 169 | 170 | Object.keys(global).forEach(function (key) { 171 | var w = whitelist.length 172 | , bad = true 173 | , white 174 | 175 | while (w--) { 176 | white = whitelist[w]; 177 | if (global[key] === white || 'string' === typeof white && key === white) { 178 | bad = false; 179 | break; 180 | } 181 | } 182 | 183 | if (bad) ret.push(key); 184 | }); 185 | 186 | return ret; 187 | }; 188 | 189 | /** 190 | * Return only new leaks since the last time `detectNew` 191 | * was run. 192 | * @api public 193 | */ 194 | 195 | Gleak.prototype.detectNew = function detectNew () { 196 | var found = this.found || (this.found = []); 197 | var ret = []; 198 | 199 | this.detect().forEach(function (leak) { 200 | if (~found.indexOf(leak)) return; 201 | found.push(leak); 202 | ret.push(leak); 203 | }); 204 | 205 | return ret; 206 | } 207 | 208 | /** 209 | * Prints all gleaks to stderr. 210 | * @api public 211 | */ 212 | 213 | Gleak.prototype.print = function print () { 214 | var format = this.format; 215 | this.detect().forEach(function (leak) { 216 | console.error(format, leak); 217 | }); 218 | } 219 | 220 | /** 221 | * Add items to the whitelist disallowing duplicates. 222 | * @api public 223 | */ 224 | 225 | Gleak.prototype.ignore = function ignore () { 226 | var i = arguments.length; 227 | while (i--) { 228 | if (~this.whitelist.indexOf(arguments[i])) continue; 229 | this.whitelist.push(arguments[i]); 230 | } 231 | return this; 232 | } 233 | 234 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Aaron Heckmann ", 3 | "name": "gleak", 4 | "description": "Node global variable leak detector", 5 | "version": "0.5.0", 6 | "repository": { 7 | "type": "git" 8 | , "url": "git://github.com/aheckmann/gleak.git" 9 | }, 10 | "main": "./index.js", 11 | "scripts": { 12 | "test": "make test" 13 | }, 14 | "engines": { 15 | "node": ">=0.6" 16 | }, 17 | "dependencies": {}, 18 | "devDependencies": { 19 | "express": ">=2.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/_request.js: -------------------------------------------------------------------------------- 1 | 2 | var http = require('http') 3 | 4 | // make requests to the server 5 | var request = module.exports = function request (o, cb ) { 6 | if (!request.address) throw new Error('missing host/port'); 7 | return new Request; 8 | } 9 | 10 | function Request () { 11 | this.host = request.address.address; 12 | this.port = request.address.port; 13 | this.path; 14 | this.method; 15 | this.headers = {}; 16 | this.data= []; 17 | } 18 | 19 | Request.prototype.post = function (path) { 20 | this.path = path; 21 | this.method = 'post'; 22 | return this; 23 | } 24 | Request.prototype.get= function (path) { 25 | this.path = path; 26 | this.method = 'get'; 27 | return this; 28 | } 29 | Request.prototype.header = function (key, val) { 30 | this.headers[key] = val; 31 | return this; 32 | } 33 | Request.prototype.write = function (data) { 34 | this.data.push(data); 35 | return this; 36 | } 37 | Request.prototype.end = function (cb) { 38 | var req = http.request({ 39 | method: this.method 40 | , host: this.host 41 | , port: this.port 42 | , path: this.path || '/' 43 | , headers: this.headers 44 | }); 45 | 46 | this.data.forEach(function (data) { 47 | req.write(data); 48 | }); 49 | 50 | req.on('response', function (res) { 51 | var buf = ''; 52 | res.setEncoding('utf8'); 53 | res.on('data', function (data) { 54 | buf += data; 55 | }); 56 | res.on('end', function () { 57 | res.body = buf; 58 | cb(res); 59 | }); 60 | }); 61 | req.end(); 62 | } 63 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var mod = require('./tests') 2 | var tests = Object.keys(mod); 3 | var total = tests.length; 4 | var failed = []; 5 | 6 | ;(function run () { 7 | var desc = tests.shift(); 8 | if (!desc) return report(); 9 | var fn = mod[desc]; 10 | runTest(desc, fn, run); 11 | })() 12 | 13 | function runTest (desc, test, done) { 14 | function complete () { 15 | if (complete.ran) { 16 | failed.push([desc, new Error('done called more than once')]) 17 | return; 18 | } 19 | complete.ran = true; 20 | done(); 21 | } 22 | if (test.length) { 23 | runTestAsync(desc, test, complete); 24 | } else { 25 | runTestSync(desc, test); 26 | process.nextTick(complete); 27 | } 28 | } 29 | 30 | function runTestAsync (desc, test, done) { 31 | var start = new Date; 32 | var e; 33 | 34 | function handleError (err) { 35 | failed.push([desc, e = err]); 36 | fail(desc, start); 37 | } 38 | 39 | function handleUncaught (err) { 40 | handleError(err); 41 | complete(); 42 | } 43 | 44 | function complete () { 45 | process.removeListener('uncaughtException', handleUncaught); 46 | done(); 47 | } 48 | 49 | process.on('uncaughtException', handleUncaught); 50 | 51 | try { 52 | test(function (err) { 53 | if (e) return; 54 | if (err) { 55 | handleError(err); 56 | complete(); 57 | return; 58 | } 59 | pass(desc, start); 60 | complete(); 61 | }); 62 | } catch (err) { 63 | handleError(err); 64 | process.nextTick(complete) 65 | } 66 | } 67 | 68 | function fail (desc, start) { 69 | console.log('\x1b[31mx ' + desc + ':\x1b[30m ' + (Date.now() - start) + 'ms\x1b[0m'); 70 | } 71 | 72 | function pass (desc, start) { 73 | console.log('\x1b[32m√\x1b[30m ' + desc + ': ' + (Date.now() - start) + 'ms\x1b[0m'); 74 | } 75 | 76 | function runTestSync (desc, test) { 77 | var start = new Date; 78 | var e; 79 | 80 | try { 81 | test(); 82 | } catch (err) { 83 | failed.push([desc, err]); 84 | fail(desc, start); 85 | return; 86 | } 87 | 88 | pass(desc, start); 89 | } 90 | 91 | function report () { 92 | console.log(); 93 | 94 | if (!failed.length) { 95 | console.log('\x1b[32m%d tests complete\x1b[0m', total); 96 | } else { 97 | console.log('\x1b[31m%d failed\x1b[0m', failed.length); 98 | console.error(); 99 | 100 | failed.forEach(function (failure, i) { 101 | console.error((i+1) + ') ' + failure[0], failure[1].stack); 102 | console.error(); 103 | }) 104 | } 105 | 106 | process.exit(failed.length); 107 | } 108 | -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert') 3 | var gleak = require('../index') 4 | var request = require('./_request') 5 | var express; 6 | 7 | // node 4 tests? 8 | var v = process.version.replace(/^v/,'').split('.'); 9 | if ('0' == v[0] && v[1] > 4) 10 | express = require('express') 11 | 12 | exports['version exists'] = function () { 13 | assert.equal('string', typeof gleak.version); 14 | } 15 | 16 | exports['middleware exists'] = function () { 17 | assert.equal('function', typeof gleak.middleware); 18 | } 19 | 20 | exports['gleak is a function'] = function () { 21 | assert.equal('function', typeof gleak); 22 | } 23 | 24 | exports['default format is correct'] = function () { 25 | var g = gleak(); 26 | assert.equal('\x1b[31mGleak!:\x1b[0m %s', g.format); 27 | } 28 | 29 | exports['whitelist is an array'] = function () { 30 | var g = gleak(); 31 | assert.ok(Array.isArray(g.whitelist)); 32 | } 33 | 34 | exports['setTimeout is a default'] = function () { 35 | var g = gleak(); 36 | assert.ok(~g.whitelist.indexOf(setTimeout)); 37 | }; 38 | 39 | exports['setInterval is a default'] = function () { 40 | var g = gleak(); 41 | assert.ok(~g.whitelist.indexOf(setInterval)); 42 | }; 43 | exports['clearTimeout is a default'] = function () { 44 | var g = gleak(); 45 | assert.ok(~g.whitelist.indexOf(clearTimeout)); 46 | }; 47 | exports['clearInterval is a default'] = function () { 48 | var g = gleak(); 49 | assert.ok(~g.whitelist.indexOf(clearInterval)); 50 | }; 51 | exports['console is a default'] = function () { 52 | var g = gleak(); 53 | assert.ok(~g.whitelist.indexOf(console)); 54 | }; 55 | exports['Buffer is a default'] = function () { 56 | var g = gleak(); 57 | assert.ok(~g.whitelist.indexOf(Buffer)); 58 | }; 59 | exports['process is a default'] = function () { 60 | var g = gleak(); 61 | assert.ok(~g.whitelist.indexOf(process)); 62 | }; 63 | exports['global is a default'] = function () { 64 | var g = gleak(); 65 | assert.ok(~g.whitelist.indexOf(global)); 66 | }; 67 | 68 | exports['whitelist is mutable'] = function () { 69 | var g = gleak(); 70 | var i = g.whitelist.push(assert); 71 | assert.ok(~g.whitelist.indexOf(assert)); 72 | g.whitelist.splice(i-1, 1); 73 | assert.ok(!~g.whitelist.indexOf(assert)); 74 | } 75 | 76 | exports['#detect is a function'] = function () { 77 | var g = gleak(); 78 | assert.ok('function' === typeof g.detect); 79 | } 80 | 81 | exports['detect()'] = function () { 82 | var g = gleak(); 83 | var found = g.detect(); 84 | assert.ok(Array.isArray(found)); 85 | assert.ok(0 === found.length); 86 | haha = "lol" 87 | assert.equal(1, g.detect().length); 88 | assert.equal("haha", g.detect()[0]); 89 | } 90 | 91 | exports['unknown values can be whitelisted by passing strings'] = function () { 92 | var g = gleak(); 93 | ignoreme = 1; 94 | assert.ok(~g.detect().indexOf('ignoreme')); 95 | g.whitelist.push('ignoreme'); 96 | assert.ok(!~g.detect().indexOf('ignoreme')); 97 | delete global.ignoreme; 98 | } 99 | 100 | exports['#ignore'] = function () { 101 | var g = gleak(); 102 | assert.equal('function', typeof g.ignore); 103 | } 104 | 105 | exports['ignore identical whitelisted values'] = function () { 106 | var g = gleak(); 107 | var len = g.whitelist.length; 108 | var an = 'another'; 109 | g.ignore('another', 'another', 'another', an); 110 | assert.equal(len + 1, g.whitelist.length); 111 | } 112 | 113 | exports['#print'] = function () { 114 | var write = console.error; 115 | try { 116 | var g = gleak(); 117 | var times = 0; 118 | haha = "heh"; 119 | console.error = function (format, item) { 120 | assert.equal(g.format, format); 121 | assert.equal("haha", item); 122 | ++times; 123 | } 124 | g.print(); 125 | assert.equal(1, times); 126 | } catch (_) { 127 | console.error = write; 128 | throw _; 129 | } 130 | } 131 | 132 | exports['whitelists are seperate from other instances'] = function () { 133 | var g1 = gleak() 134 | , g2 = gleak(); 135 | 136 | g1.ignore('the', 'bad'); 137 | assert.ok(~g1.whitelist.indexOf('the')); 138 | assert.ok(!~g2.whitelist.indexOf('the')); 139 | } 140 | 141 | exports['formats are seperate from other instances'] = function () { 142 | var g1 = gleak() 143 | , g2 = gleak(); 144 | 145 | g1.format = "different %s"; 146 | assert.ok(~g1.format !== g1.format); 147 | } 148 | 149 | exports['#detectNew'] = function () { 150 | var g = gleak(); 151 | assert.equal('function', typeof g.detectNew); 152 | var found = g.detectNew(); 153 | assert.equal(true, Array.isArray(found)); 154 | assert.equal(found.length, 1); 155 | assert.equal(g.detectNew().length, 0); 156 | zombocom = 'welcome'; 157 | found = g.detectNew(); 158 | assert.equal(found.length, 1); 159 | assert.equal(found[0], 'zombocom'); 160 | assert.equal(g.detectNew().length, 0); 161 | delete global.zombocom; 162 | } 163 | 164 | exports['test middleware'] = function (done) { 165 | if ('0' == v[0] && v[1] < 5) return done(); 166 | var called = false; 167 | var req = {}; 168 | var res = { send: function (x) { assert.equal(x, 'yes'); called = true; }}; 169 | var m = gleak.middleware(); 170 | m(req, res, function(){}); 171 | assert.equal(res._gleak, true); 172 | res.send('yes'); 173 | assert.equal(true, called); 174 | 175 | // another leak 176 | meToo = 47; 177 | 178 | // mock stream 179 | function makeStream (tests) { 180 | return { 181 | i: 0 182 | , write: function (data) { 183 | assert.equal(tests[this.i], data); 184 | ++this.i; 185 | } 186 | } 187 | } 188 | 189 | var express3 = express.version.split('.')[0] > 2; 190 | var app = express3 191 | ? express() 192 | : express.createServer(); 193 | 194 | var sout = [ 195 | '\x1b[31mGleak!:\x1b[0m haha\n' 196 | , '\x1b[31mGleak!:\x1b[0m meToo\n' 197 | ]; 198 | var stream1 = makeStream(sout); 199 | 200 | app.get('/stream', gleak.middleware(stream1), function (req, res, next) { 201 | res.send('passed a stream'); 202 | }); 203 | 204 | var both = [ 205 | 'yes : haha\n' 206 | , 'yes : meToo\n' 207 | ]; 208 | var stream2 = makeStream(both); 209 | 210 | app.get('/formatstream', gleak.middleware(stream2, 'yes : %s'), function (req, res, next) { 211 | res.send('passed format and stream'); 212 | }); 213 | 214 | var pending = 2; 215 | 216 | var svr = app.listen(0, function () { 217 | request.address = svr.address(); 218 | ready(); 219 | }); 220 | 221 | function ready () { 222 | request() 223 | .get('/stream') 224 | .end(function (res) { 225 | assert.equal(200, res.statusCode); 226 | assert.equal('passed a stream', res.body); 227 | assert.equal(2, stream1.i); 228 | finish(); 229 | }); 230 | 231 | request() 232 | .get('/formatstream') 233 | .end(function (res) { 234 | assert.equal(200, res.statusCode); 235 | assert.equal('passed format and stream', res.body); 236 | assert.equal(stream2.i, 2); 237 | finish(); 238 | }); 239 | } 240 | 241 | function finish () { 242 | if (--pending) return; 243 | delete global.meToo; 244 | delete global.haha; 245 | done(); 246 | } 247 | } 248 | 249 | --------------------------------------------------------------------------------