├── .coveralls.yml ├── .gitignore ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── current-processes.js ├── lib ├── datasource │ ├── index.js │ ├── ps.js │ └── wmic.js ├── model │ └── process.js └── parser │ ├── csv.js │ ├── fixed-columns.js │ ├── fluid-columns.js │ └── index.js ├── package.json ├── readme.md └── test ├── fixtures └── datasource │ └── fixed-columns │ └── _inferColumnWidths.js ├── mocha.opts ├── run-coveralls.js └── spec ├── datasource ├── index.js ├── ps.js └── wmic.js ├── integration-tests.js ├── model └── process.js └── parser ├── csv.js ├── fixed-columns.js ├── fluid-columns.js └── index.js /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: xyz 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE's 2 | .idea 3 | 4 | # Node.js 5 | node_modules 6 | npm-debug.log 7 | coverage 8 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Configuration File 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : false, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : true, // true: Identifiers must be in camelCase 10 | "curly" : false, // true: Require {} for every new block or scope 11 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 12 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 14 | "indent" : 4, // {int} Number of spaces to use for indentation 15 | "latedef" : false, // true: Require variables/functions to be defined before being used 16 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` 17 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 18 | "noempty" : false, // true: Prohibit use of empty blocks 19 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 20 | "plusplus" : false, // true: Prohibit use of `++` & `--` 21 | "quotmark" : "single", // Quotation mark consistency: 22 | // false : do nothing (default) 23 | // true : ensure whatever is used is consistent 24 | // "single" : require single quotes 25 | // "double" : require double quotes 26 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 27 | "unused" : true, // true: Require all defined variables be used 28 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 29 | "trailing" : true, // true: Prohibit trailing whitespaces 30 | "maxparams" : 4, // {int} Max number of formal params allowed per function 31 | "maxdepth" : 3, // {int} Max depth of nested blocks (within functions) 32 | "maxstatements" : 15, // {int} Max number statements per function 33 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 34 | "maxlen" : 120, // {int} Max number of characters per line 35 | 36 | // Relaxing 37 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 38 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 39 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 40 | "eqnull" : false, // true: Tolerate use of `== null` 41 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 42 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 43 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 44 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 45 | "funcscope" : false, // true: Tolerate defining variables inside control statements" 46 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 47 | "iterator" : false, // true: Tolerate using the `__iterator__` property 48 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 49 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 50 | "laxcomma" : false, // true: Tolerate comma-first style coding 51 | "loopfunc" : true, // true: Tolerate functions being defined in loops 52 | "multistr" : false, // true: Tolerate multi-line strings 53 | "proto" : false, // true: Tolerate using the `__proto__` property 54 | "scripturl" : false, // true: Tolerate script-targeted URLs 55 | "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment 56 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 57 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 58 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 59 | "validthis" : false, // true: Tolerate using this in a non-constructor function 60 | 61 | // Environments 62 | "browser" : true, // Web Browser (window, document, etc) 63 | "couch" : false, // CouchDB 64 | "devel" : false, // Development/debugging (alert, confirm, etc) 65 | "dojo" : false, // Dojo Toolkit 66 | "jquery" : true, // jQuery 67 | "mootools" : false, // MooTools 68 | "node" : true, // Node.js 69 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 70 | "prototypejs" : false, // Prototype and Scriptaculous 71 | "rhino" : false, // Rhino 72 | "worker" : false, // Web Workers 73 | "wsh" : false, // Windows Scripting Host 74 | "yui" : false, // Yahoo User Interface 75 | 76 | // Legacy 77 | "nomen" : false, // true: Prohibit dangling `_` in variables 78 | "onevar" : false, // true: Allow only one `var` statement per function 79 | "passfail" : false, // true: Stop on first error 80 | "white" : false, // true: Check against strict whitespace and indentation rules 81 | 82 | // Custom Globals - additional predefined global variables 83 | "predef" : ["describe", "it", "xdescribe", "xit"] 84 | } 85 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | - 0.11 5 | -------------------------------------------------------------------------------- /current-processes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var async = require('async'); 4 | 5 | var dataSource = require('./lib/datasource').get(); 6 | var parser = require('./lib/parser').get(); 7 | var procFactory = require('./lib/model/process').factory; 8 | 9 | module.exports.get = function getCurrentProcesses(cb) { 10 | 11 | async.waterfall([ 12 | dataSource, 13 | parser, 14 | procFactory 15 | ], cb); 16 | }; 17 | -------------------------------------------------------------------------------- /lib/datasource/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var platform = process.platform; 4 | 5 | var ps = require('./ps'); 6 | var wmic = require('./wmic'); 7 | 8 | // 9 | // Decide which data source to use 10 | // 11 | module.exports.get = function _getDataSourceByPlatform() { 12 | 13 | if (platform === 'win32') { 14 | return wmic; 15 | } 16 | return ps; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/datasource/ps.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exec = require('child_process').exec; 4 | 5 | module.exports = function dataSourcePS(cb) { 6 | 7 | var cmd = 'ps -A -o pid,rss,vsz,pcpu,comm'; 8 | 9 | exec(cmd, function(err, stdout) { 10 | if (err) { 11 | cb('Command `ps` returned an error!'); 12 | } else { 13 | cb(null, stdout); 14 | } 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /lib/datasource/wmic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exec = require('child_process').exec; 4 | 5 | module.exports = function dataSourceWMIC(cb) { 6 | 7 | var cols = 'IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes'; 8 | var cmd = 'wmic path Win32_PerfFormattedData_PerfProc_Process get ' + cols + ' /format:csv'; 9 | 10 | exec(cmd, function(err, stdout) { 11 | if (err) { 12 | cb('Command `wmic` returned an error!'); 13 | } else { 14 | cb(null, stdout); 15 | } 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /lib/model/process.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var totalmem = require('os').totalmem(); 4 | 5 | // Expose module 6 | module.exports = Process; 7 | module.exports.factory = processFactory; 8 | 9 | // 10 | // Model: Process 11 | // 12 | function Process(opt) { 13 | 14 | this.pid = parseInt(opt.pid, 10); 15 | 16 | this.name = opt.name.substr(opt.name.lastIndexOf('/') + 1); 17 | 18 | this.cpu = parseFloat(opt.cpu); 19 | 20 | this.mem = { 21 | private: parseInt(opt.pmem, 10), 22 | virtual: parseInt(opt.vmem, 10), 23 | usage: opt.pmem / totalmem * 100 24 | }; 25 | } 26 | 27 | // 28 | // Factory method 29 | // 30 | function processFactory(data, cb) { 31 | 32 | cb(null, data.map(function(opt) { 33 | return new Process(opt); 34 | })); 35 | } 36 | -------------------------------------------------------------------------------- /lib/parser/csv.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var os = require('os'); 4 | var csv = require('csv-parse'); 5 | 6 | module.exports = function parserCSV(data, cb) { 7 | 8 | csv(data.trim(), { rowDelimiter: os.EOL }, function(err, lines) { 9 | 10 | cb(null, lines 11 | .filter(function(line, index) { 12 | return index > 0 && line.length === 6; 13 | }) 14 | .map(function(line) { 15 | return { 16 | pid: line[1], 17 | name: _getProcessName(line[2]), 18 | cpu: line[3], 19 | pmem: line[4], 20 | vmem: line[5] 21 | }; 22 | }) 23 | .filter(function(proc) { 24 | return proc.pid !== '0' && proc.pid !== 0; 25 | })); 26 | }); 27 | }; 28 | 29 | // 30 | // Strip the optional postfixed # 31 | // 32 | function _getProcessName(name) { 33 | 34 | var pos = name.indexOf('#'); 35 | if (~pos) { 36 | return name.substr(0, pos); 37 | } else { 38 | return name; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/parser/fixed-columns.js: -------------------------------------------------------------------------------- 1 | /* jshint -W071 */ 2 | 'use strict'; 3 | 4 | var os = require('os'); 5 | 6 | module.exports = function parserFixedColumns(data, cb) { 7 | 8 | var lines = _trimNewlines(data).split(os.EOL); 9 | var widths = _getColumnWidths(lines[0]); 10 | 11 | cb(null, lines 12 | .filter(function(line, index) { 13 | return line && index >= 1; 14 | }) 15 | .map(function(line) { 16 | return { 17 | pid: widths[0].getValue(line), 18 | name: widths[4].getValue(line), 19 | cpu: widths[3].getValue(line), 20 | pmem: parseInt(widths[1].getValue(line), 10) * 1024, 21 | vmem: parseInt(widths[2].getValue(line), 10) * 1024 22 | }; 23 | })); 24 | }; 25 | 26 | // 27 | // Trims only newlines (no spaces) at the beginning and end of the string 28 | // 29 | function _trimNewlines(str) { 30 | return str.replace(/^[\r\n]+|[\r\n]+$/g, ''); 31 | } 32 | 33 | // 34 | // Grab column widths from first line 35 | // 36 | function _getColumnWidths(line) { 37 | 38 | var command = 'COMM'; 39 | if (~line.indexOf('COMMAND')) { 40 | command = 'COMMAND'; 41 | } 42 | 43 | return _inferColumnWidths([ 44 | { query: 'PID', align: 'right' }, 45 | { query: 'RSS', align: 'right' }, 46 | { query: 'VSZ', align: 'right' }, 47 | { query: '%CPU', align: 'right' }, 48 | { query: command, align: 'left' } 49 | ], line); 50 | } 51 | 52 | // 53 | // String helper for parsing a column-based text output 54 | // 55 | function _inferColumnWidths(columns, string) { 56 | 57 | var i, nextColumn; 58 | var len = columns.length; 59 | var last = len - 1; 60 | 61 | // 62 | // Pass 1: Start with header positions 63 | // 64 | for (i = 0; i < len; i++) { 65 | columns[i].pos = { 66 | start: string.indexOf(columns[i].query), 67 | length: columns[i].query.length 68 | }; 69 | } 70 | 71 | // 72 | // Pass 2: Fix alignments 73 | // 74 | for (i = 0; i < len; i++) { 75 | 76 | // First column 77 | if (i === 0) { 78 | 79 | if (columns[i].align === 'left') { 80 | nextColumn = columns[i + 1].pos; 81 | columns[i].pos.length = (nextColumn.start - columns[i].pos.start); 82 | } 83 | 84 | else if (columns[i].align === 'right') { 85 | columns[i].pos.length += columns[i].pos.start; 86 | } 87 | 88 | else { 89 | throw 'StringHelper.inferColumnWidths `align` has incorrect value, valid options: left, right'; 90 | } 91 | 92 | columns[i].pos.start = 0; 93 | } 94 | 95 | // Middle column 96 | else if (i < last) { 97 | 98 | if (columns[i].align === 'left') { 99 | nextColumn = columns[i + 1].pos; 100 | columns[i].pos.length = (nextColumn.start - columns[i].pos.start); 101 | } 102 | 103 | else if (columns[i].align === 'right') { 104 | 105 | var previousColumn = columns[i - 1].pos; 106 | var currentEndPosition = (columns[i].pos.start + columns[i].pos.length); 107 | var previousEndPosition = (previousColumn.start + previousColumn.length); 108 | 109 | columns[i].pos.length = currentEndPosition - previousEndPosition; 110 | columns[i].pos.start = previousEndPosition; 111 | } 112 | 113 | else { 114 | throw 'StringHelper.inferColumnWidths `align` has incorrect value, valid options: left, right'; 115 | } 116 | } 117 | 118 | // Last column 119 | if (i === last) { 120 | columns[i].pos.length = undefined; 121 | } 122 | } 123 | 124 | // 125 | // Pass 3: Create getValue() methods 126 | // 127 | for (i = 0; i < len; i++) { 128 | columns[i].getValue = (function(pos) { 129 | return function(str) { 130 | return String.prototype.substr.call(str, pos.start, pos.length).trim(); 131 | }; 132 | }(columns[i].pos)); 133 | } 134 | 135 | return columns; 136 | } 137 | -------------------------------------------------------------------------------- /lib/parser/fluid-columns.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var os = require('os'); 4 | 5 | module.exports = function parserFluidColumns(data, cb) { 6 | 7 | var lines = data.trim().split(os.EOL); 8 | 9 | cb(null, lines 10 | .filter(function(line, index) { 11 | return line && index >= 1; 12 | }) 13 | .map(function(line) { 14 | return _splitLimit(line.trim(), 5); 15 | }) 16 | .map(function(proc) { 17 | return { 18 | pid: proc[0], 19 | name: proc[4], 20 | cpu: proc[3], 21 | pmem: parseInt(proc[1], 10) * 1024, 22 | vmem: parseInt(proc[2], 10) * 1024 23 | }; 24 | })); 25 | }; 26 | 27 | // 28 | // String helper which implements a split() method with a limit, 29 | // it differs from JS' String.split() because it concats the remaining string 30 | // 31 | function _splitLimit(string, limit) { 32 | 33 | var arr = string.split(/\s+/); 34 | var result = arr.splice(0, limit - 1); 35 | 36 | result.push(arr.join(' ')); 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /lib/parser/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var platform = process.platform; 4 | 5 | var csv = require('./csv'); 6 | var fixedColumns = require('./fixed-columns'); 7 | var fluidColumns = require('./fluid-columns'); 8 | 9 | // 10 | // Decide which parser to use 11 | // 12 | module.exports.get = function _getParserByPlatform() { 13 | 14 | if (platform === 'win32') { 15 | return csv; 16 | } else if (platform === 'sunos') { 17 | return fluidColumns; 18 | } 19 | return fixedColumns; 20 | }; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "current-processes", 3 | "version": "0.2.1", 4 | "description": "Get a snapshot of the currently running processes, OS-agnostic", 5 | "main": "current-processes.js", 6 | "scripts": { 7 | "pretest": "node node_modules/jshint/bin/jshint .", 8 | "test": "node node_modules/istanbul/lib/cli cover node_modules/mocha/bin/_mocha", 9 | "coveralls": "node test/run-coveralls coverage/lcov.info node_modules/coveralls/bin/coveralls.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/branneman/current-processes.git" 14 | }, 15 | "dependencies": { 16 | "async": "^0.9.0", 17 | "csv-parse": "0.0.5" 18 | }, 19 | "devDependencies": { 20 | "jshint": "^2.5.1", 21 | "mocha": "^1.20.1", 22 | "chai": "^1.9.1", 23 | "rewire": "^2.0.1", 24 | "istanbul": "^0.2.11", 25 | "coveralls": "^2.10.1" 26 | }, 27 | "author": "Bran van der Meer (http://bran.name/)", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/branneman/current-processes/issues" 31 | }, 32 | "homepage": "https://github.com/branneman/current-processes", 33 | "keywords": [ 34 | "processes", 35 | "os", 36 | "cpu", 37 | "memory", 38 | "usage", 39 | "tasks", 40 | "tasklist", 41 | "top", 42 | "vtop", 43 | "ps", 44 | "wmi", 45 | "wmic" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # current-processes 2 | [![Build Status](https://travis-ci.org/branneman/current-processes.svg?branch=master)](https://travis-ci.org/branneman/current-processes) 3 | [![Coverage Status](https://img.shields.io/coveralls/branneman/current-processes.svg)](https://coveralls.io/r/branneman/current-processes) 4 | [![Dependency Status](http://img.shields.io/david/branneman/current-processes.svg)](https://david-dm.org/branneman/current-processes) 5 | [![npm version](https://badge.fury.io/js/current-processes.svg)](https://www.npmjs.org/package/current-processes) 6 | [![npm Downloads](http://img.shields.io/npm/dm/current-processes.svg)](https://www.npmjs.org/package/current-processes) 7 | 8 | Node.js library to get a snapshot of the currently running processes, OS-agnostic. Needs root/Admin permissions. 9 | 10 | ## Usage example 11 | ```js 12 | import _ from 'lodash'; 13 | import ps from 'current-processes'; 14 | 15 | ps.get((err, processes) => { 16 | 17 | const sorted = _.sortBy(processes, 'cpu'); 18 | const top5 = sorted.reverse().splice(0, 5); 19 | 20 | console.log(top5); 21 | }); 22 | ``` 23 | 24 | ## Process object 25 | The library will return an array consisting of multiple process objects, structured like this: 26 | ```js 27 | { 28 | pid: 1337, // Process ID 29 | name: 'chrome', // Process name 30 | mem: { 31 | private: 23054560, // Private memory, in bytes 32 | virtual: 78923608, // Virtual memory (private + shared libraries + swap space), in bytes 33 | usage: 0.02 // Used physical memory (%) by this process 34 | }, 35 | cpu: 0.3 // CPU usage (%) as reported by `ps` and `wmic` 36 | } 37 | ``` 38 | 39 | ## Platform-specific notes 40 | ### Windows 41 | WMI (specifically `wmic`) is used to gather the information itself. WMI is fairly slow the first time it's called, it 42 | might even take up to 2-3 seconds. Make sure your app will gracefully handle this. Subsequent calls will be much faster. 43 | -------------------------------------------------------------------------------- /test/fixtures/datasource/fixed-columns/_inferColumnWidths.js: -------------------------------------------------------------------------------- 1 | module.exports.correct = [ 2 | { 3 | columns: [ 4 | { 5 | query: 'PID', 6 | align: 'left' 7 | }, 8 | { 9 | query: 'VSZ', 10 | align: 'left' 11 | }, 12 | { 13 | query: '%CPU', 14 | align: 'left' 15 | }, 16 | { 17 | query: 'COMM', 18 | align: 'left' 19 | } 20 | ], 21 | string: ' PID VSZ %CPU COMM', 22 | expect: [ 23 | { start: 0, length: 7 }, 24 | { start: 8, length: 7 }, 25 | { start: 15, length: 7 }, 26 | { start: 22, length: undefined } 27 | ] 28 | }, 29 | { 30 | columns: [ 31 | { 32 | query: 'PID', 33 | align: 'right' 34 | }, 35 | { 36 | query: 'VSZ', 37 | align: 'right' 38 | }, 39 | { 40 | query: '%CPU', 41 | align: 'right' 42 | }, 43 | { 44 | query: 'COMMAND', 45 | align: 'left' 46 | } 47 | ], 48 | string: ' PID VSZ %CPU COMMAND', 49 | expect: [ 50 | { start: 0, length: 4 }, 51 | { start: 4, length: 7 }, 52 | { start: 11, length: 6 }, 53 | { start: 18, length: undefined } 54 | ] 55 | }, 56 | { 57 | columns: [ 58 | { 59 | query: 'PID', 60 | align: 'right' 61 | }, 62 | { 63 | query: 'VSZ', 64 | align: 'right' 65 | }, 66 | { 67 | query: 'COMMAND', 68 | align: 'left' 69 | } 70 | ], 71 | string: ' PID VSZ COMMAND', 72 | expect: [ 73 | { start: 0, length: 5 }, 74 | { start: 5, length: 7 }, 75 | { start: 13, length: undefined } 76 | ] 77 | }, 78 | { 79 | columns: [ 80 | { 81 | query: 'IDProcess', 82 | align: 'left' 83 | }, 84 | { 85 | query: 'Name', 86 | align: 'left' 87 | }, 88 | { 89 | query: 'PercentProcessorTime', 90 | align: 'left' 91 | }, 92 | { 93 | query: 'WorkingSetPrivate', 94 | align: 'left' 95 | } 96 | ], 97 | string: 'IDProcess Name PercentProcessorTime WorkingSetPrivate', 98 | expect: [ 99 | { start: 0, length: 11 }, 100 | { start: 11, length: 33 }, 101 | { start: 44, length: 22 }, 102 | { start: 66, length: undefined } 103 | ] 104 | }, 105 | { 106 | columns: [ 107 | { 108 | query: 'PID', 109 | align: 'right' 110 | }, 111 | { 112 | query: 'VSZ', 113 | align: 'right' 114 | }, 115 | { 116 | query: '%CPU', 117 | align: 'right' 118 | }, 119 | { 120 | query: 'COMMAND', 121 | align: 'left' 122 | } 123 | ], 124 | string: ' PID VSZ %CPU COMMAND', 125 | expect: [ 126 | { start: 0, length: 5 }, 127 | { start: 5, length: 6 }, 128 | { start: 11, length: 6 }, 129 | { start: 18, length: undefined } 130 | ] 131 | }, 132 | { 133 | columns: [ 134 | { 135 | query: 'PID', 136 | align: 'right' 137 | }, 138 | { 139 | query: 'VSZ', 140 | align: 'right' 141 | }, 142 | { 143 | query: '%CPU', 144 | align: 'right' 145 | }, 146 | { 147 | query: 'COMM', 148 | align: 'left' 149 | } 150 | ], 151 | string: ' PID VSZ %CPU COMM', 152 | expect: [ 153 | { start: 0, length: 4 }, 154 | { start: 4, length: 9 }, 155 | { start: 13, length: 6 }, 156 | { start: 20, length: undefined } 157 | ] 158 | }, 159 | { 160 | columns: [ 161 | { 162 | query: 'PID', 163 | align: 'right' 164 | }, 165 | { 166 | query: 'RSS', 167 | align: 'right' 168 | }, 169 | { 170 | query: 'VSZ', 171 | align: 'right' 172 | }, 173 | { 174 | query: '%CPU', 175 | align: 'right' 176 | }, 177 | { 178 | query: 'COMMAND', 179 | align: 'left' 180 | } 181 | ], 182 | string: ' PID RSS VSZ %CPU COMMAND', 183 | expect: [ 184 | { start: 0, length: 4 }, 185 | { start: 4, length: 9 }, 186 | { start: 13, length: 6 }, 187 | { start: 19, length: 6 }, 188 | { start: 26, length: undefined } 189 | ] 190 | } 191 | ]; 192 | 193 | module.exports.incorrect = [ 194 | { 195 | columns: [ 196 | { 197 | query: 'PID', 198 | align: 'middle' 199 | }, 200 | { 201 | query: 'COMMAND', 202 | align: 'left' 203 | } 204 | ], 205 | string: ' PID COMMAND' 206 | }, 207 | { 208 | columns: [ 209 | { 210 | query: 'PID', 211 | align: 'right' 212 | }, 213 | { 214 | query: 'PID', 215 | align: 'middle' 216 | }, 217 | { 218 | query: 'COMMAND', 219 | align: 'left' 220 | } 221 | ], 222 | string: ' PID VSZ COMMAND' 223 | } 224 | ]; -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --colors 2 | --ui bdd 3 | --reporter spec 4 | --recursive 5 | test/spec/ 6 | -------------------------------------------------------------------------------- /test/run-coveralls.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var spawn = require('child_process').spawn; 5 | 6 | if (!process.argv[2] || !fs.existsSync(process.argv[2]) || 7 | !process.argv[3] || !fs.existsSync(process.argv[3])) { 8 | console.log('Synopsis: `node `'); 9 | process.exit(-1); 10 | } 11 | 12 | var lcov = fs.createReadStream(process.argv[2], { encoding: 'utf8' }); 13 | var coveralls = spawn('node', [process.argv[3]]); 14 | 15 | lcov.pipe(coveralls.stdin); 16 | coveralls.stdout.pipe(process.stdout); 17 | coveralls.stderr.pipe(process.stdout); 18 | -------------------------------------------------------------------------------- /test/spec/datasource/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('chai').assert; 4 | var rewire = require('rewire'); 5 | 6 | describe('DataSource', function() { 7 | 8 | it('should not fail when require()\'d', function() { 9 | 10 | var DataSource = require('../../../lib/datasource'); 11 | 12 | assert(DataSource); 13 | }); 14 | 15 | it('should have a get() method', function() { 16 | 17 | var DataSource = require('../../../lib/datasource'); 18 | 19 | assert.isFunction(DataSource.get); 20 | }); 21 | 22 | it('should return the right module for each platform', function() { 23 | 24 | var fixtures = [ 25 | { 26 | platform: 'darwin', 27 | function: 'dataSourcePS' 28 | }, 29 | { 30 | platform: 'freebsd', 31 | function: 'dataSourcePS' 32 | }, 33 | { 34 | platform: 'linux', 35 | function: 'dataSourcePS' 36 | }, 37 | { 38 | platform: 'sunos', 39 | function: 'dataSourcePS' 40 | }, 41 | { 42 | platform: 'win32', 43 | function: 'dataSourceWMIC' 44 | } 45 | ]; 46 | 47 | fixtures.forEach(function(fixture) { 48 | 49 | var DataSource = rewire('../../../lib/datasource'); 50 | DataSource.__set__('platform', fixture.platform); 51 | 52 | var result = DataSource.get(); 53 | 54 | assert.equal(result.name, fixture.function); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/spec/datasource/ps.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('chai').assert; 4 | var rewire = require('rewire'); 5 | 6 | describe('DataSource - PS', function() { 7 | 8 | it('should not fail when require()\'d', function() { 9 | 10 | var dataSourcePS = require('../../../lib/datasource/ps'); 11 | 12 | assert(dataSourcePS); 13 | assert.isFunction(dataSourcePS); 14 | }); 15 | 16 | it('should invoke the callback function with the result', function(done) { 17 | 18 | var dataSourcePS = rewire('../../../lib/datasource/ps'); 19 | 20 | dataSourcePS.__set__('exec', function(cmd, cb) { 21 | cb(null, 'FAKE RESULT'); 22 | }); 23 | 24 | dataSourcePS(function(err, result) { 25 | assert.isNull(err); 26 | assert.equal(result, 'FAKE RESULT'); 27 | done(); 28 | }); 29 | }); 30 | 31 | it('should fail by invoking the callback function', function(done) { 32 | 33 | var dataSourcePS = rewire('../../../lib/datasource/ps'); 34 | 35 | dataSourcePS.__set__('exec', function(cmd, cb) { 36 | cb('FAKE ERROR'); 37 | }); 38 | 39 | dataSourcePS(function(err, result) { 40 | assert.isString(err); 41 | assert.isUndefined(result); 42 | done(); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/spec/datasource/wmic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('chai').assert; 4 | var rewire = require('rewire'); 5 | 6 | describe('DataSource - WMIC', function() { 7 | 8 | it('should not fail when require()\'d', function() { 9 | 10 | var dataSourceWMIC = require('../../../lib/datasource/wmic'); 11 | 12 | assert(dataSourceWMIC); 13 | assert.isFunction(dataSourceWMIC); 14 | }); 15 | 16 | it('should invoke the callback function with the result', function(done) { 17 | 18 | var dataSourceWMIC = rewire('../../../lib/datasource/wmic'); 19 | 20 | dataSourceWMIC.__set__('exec', function(cmd, cb) { 21 | cb(null, 'FAKE RESULT'); 22 | }); 23 | 24 | dataSourceWMIC(function(err, result) { 25 | assert.isNull(err); 26 | assert.equal(result, 'FAKE RESULT'); 27 | done(); 28 | }); 29 | }); 30 | 31 | it('should fail by invoking the callback function', function(done) { 32 | 33 | var dataSourceWMIC = rewire('../../../lib/datasource/wmic'); 34 | 35 | dataSourceWMIC.__set__('exec', function(cmd, cb) { 36 | cb('FAKE ERROR'); 37 | }); 38 | 39 | dataSourceWMIC(function(err, result) { 40 | assert.isString(err); 41 | assert.isUndefined(result); 42 | done(); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/spec/integration-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('chai').assert; 4 | 5 | var CurrentProcesses; 6 | 7 | describe('Integration tests (for current platform)', function() { 8 | 9 | it('should not fail when require()\'d', function() { 10 | CurrentProcesses = require('../../current-processes'); 11 | assert(CurrentProcesses); 12 | }); 13 | 14 | it('should have a get() method', function() { 15 | assert.isFunction(CurrentProcesses.get); 16 | }); 17 | 18 | describe('Method get()', function() { 19 | 20 | // WMIC can take a while to start up for the first time 21 | this.timeout(15000); 22 | 23 | it('should return an array', function(done) { 24 | 25 | CurrentProcesses.get(function(err, processes) { 26 | 27 | if (err) { 28 | assert.fail(err); 29 | return done(); 30 | } 31 | 32 | assert.isArray(processes); 33 | done(); 34 | }); 35 | }); 36 | 37 | it('should never return an empty array', function(done) { 38 | 39 | CurrentProcesses.get(function(err, processes) { 40 | 41 | if (err) { 42 | assert.fail(err); 43 | return done(); 44 | } 45 | 46 | assert(processes.length); 47 | done(); 48 | }); 49 | }); 50 | 51 | it('should include the process PID', function(done) { 52 | 53 | CurrentProcesses.get(function(err, processes) { 54 | 55 | if (err) { 56 | assert.fail(err); 57 | return done(); 58 | } 59 | 60 | processes.forEach(function(proc) { 61 | assert.isNumber(proc.pid); 62 | var isInt = proc.pid % 1 === 0; 63 | assert(isInt, 'PID is not an integer:' + proc.pid); 64 | }); 65 | done(); 66 | }); 67 | }); 68 | 69 | it('should include the process name', function(done) { 70 | 71 | CurrentProcesses.get(function(err, processes) { 72 | 73 | if (err) { 74 | assert.fail(err); 75 | return done(); 76 | } 77 | 78 | processes.forEach(function(proc) { 79 | assert.isString(proc.name); 80 | }); 81 | done(); 82 | }); 83 | }); 84 | 85 | it('should include the 3 types of process memory usage', function(done) { 86 | 87 | CurrentProcesses.get(function(err, processes) { 88 | 89 | if (err) { 90 | assert.fail(err); 91 | return done(); 92 | } 93 | 94 | processes.forEach(function(proc) { 95 | assert.isObject(proc.mem); 96 | assert.isNumber(proc.mem.private); 97 | assert.isNumber(proc.mem.virtual); 98 | assert.isNumber(proc.mem.usage); 99 | }); 100 | done(); 101 | }); 102 | }); 103 | 104 | it('should include the process cpu usage', function(done) { 105 | 106 | CurrentProcesses.get(function(err, processes) { 107 | 108 | if (err) { 109 | assert.fail(err); 110 | return done(); 111 | } 112 | 113 | processes.forEach(function(proc) { 114 | assert.isNumber(proc.cpu); 115 | }); 116 | done(); 117 | }); 118 | }); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /test/spec/model/process.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('chai').assert; 4 | var rewire = require('rewire'); 5 | 6 | describe('Process', function() { 7 | 8 | it('should not fail when require()\'d', function() { 9 | 10 | var Process = require('../../../lib/model/process'); 11 | 12 | assert(Process); 13 | }); 14 | 15 | it('should be a function', function() { 16 | 17 | var Process = require('../../../lib/model/process'); 18 | 19 | assert.isFunction(Process); 20 | }); 21 | 22 | it('should have a PID', function() { 23 | 24 | var Process = require('../../../lib/model/process'); 25 | var opt = { pid: 1337, name: '', cpu: 1, pmem: 2, vmem: 3 }; 26 | 27 | var proc = new Process(opt); 28 | 29 | assert.isNumber(proc.pid); 30 | assert.equal(proc.pid, opt.pid); 31 | }); 32 | 33 | it('should have a formatted name', function() { 34 | 35 | var Process = require('../../../lib/model/process'); 36 | var fixtures = [ 37 | { pid: 1, name: 'noot', cpu: 2, pmem: 3, vmem: 4 }, 38 | { pid: 1, name: 'aap/noot', cpu: 2, pmem: 3, vmem: 4 }, 39 | { pid: 1, name: '/aap/noot', cpu: 2, pmem: 3, vmem: 4 } 40 | ]; 41 | 42 | fixtures.forEach(function(fixture) { 43 | 44 | var proc = new Process(fixture); 45 | 46 | assert.isString(proc.name); 47 | assert.equal(proc.name, 'noot'); 48 | }); 49 | }); 50 | 51 | it('should have cpu usage', function() { 52 | 53 | var Process = require('../../../lib/model/process'); 54 | var opt = { pid: 1, name: '', cpu: 2.5, pmem: 2, vmem: 3 }; 55 | 56 | var proc = new Process(opt); 57 | 58 | assert.isNumber(proc.cpu); 59 | assert.equal(proc.cpu, opt.cpu); 60 | }); 61 | 62 | it('should have private and virtual memory', function() { 63 | 64 | var Process = require('../../../lib/model/process'); 65 | var opt = { pid: 1, name: '', cpu: 2.5, pmem: 2, vmem: 3 }; 66 | 67 | var proc = new Process(opt); 68 | 69 | assert.isObject(proc.mem); 70 | assert.isNumber(proc.mem.private); 71 | assert.isNumber(proc.mem.virtual); 72 | 73 | assert.equal(proc.mem.private, opt.pmem); 74 | assert.equal(proc.mem.virtual, opt.vmem); 75 | }); 76 | 77 | it('should calculate correct memory usage', function() { 78 | 79 | var Process = rewire('../../../lib/model/process'); 80 | Process.__set__('totalmem', 12); 81 | var opt = { pid: 1, name: '', cpu: 2, pmem: 3, vmem: 4 }; 82 | 83 | var proc = new Process(opt); 84 | 85 | assert.isNumber(proc.mem.usage); 86 | assert.equal(proc.mem.usage, 25); // result = pmem / totalmem * 100 87 | }); 88 | 89 | describe('processFactory', function() { 90 | 91 | it('should be a function', function() { 92 | 93 | var processFactory = require('../../../lib/model/process').factory; 94 | 95 | assert.isFunction(processFactory); 96 | }); 97 | 98 | it('should invoke the callback function with the result', function(done) { 99 | 100 | var processFactory = require('../../../lib/model/process').factory; 101 | var data = [{ pid: 1, name: '', cpu: 2, pmem: 3, vmem: 4 }]; 102 | 103 | processFactory(data, function(err, processes) { 104 | assert.isArray(processes); 105 | done(); 106 | }); 107 | }); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /test/spec/parser/csv.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var os = require('os'); 4 | var assert = require('chai').assert; 5 | 6 | describe('Parser - CSV', function() { 7 | 8 | it('should not fail when require()\'d', function() { 9 | 10 | var parserCSV = require('../../../lib/parser/csv'); 11 | 12 | assert(parserCSV); 13 | }); 14 | 15 | it('should be a function', function() { 16 | 17 | var parserCSV = require('../../../lib/parser/csv'); 18 | 19 | assert.isFunction(parserCSV); 20 | }); 21 | 22 | it('should invoke the callback function with the result', function(done) { 23 | 24 | var parserCSV = require('../../../lib/parser/csv'); 25 | var data = 26 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 27 | 'COMPUTERNAME,3308,chrome#1,0,126222336,419917824'; 28 | 29 | parserCSV(data, function(err, processes) { 30 | 31 | assert.isNull(err); 32 | assert.isArray(processes); 33 | assert.lengthOf(processes, 1); 34 | done(); 35 | }); 36 | }); 37 | 38 | it('should generate simplified process objects', function(done) { 39 | 40 | var parserCSV = require('../../../lib/parser/csv'); 41 | var data = 42 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 43 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 44 | 'COMPUTERNAME,3308,chrome#1,0,126222336,419917824' + os.EOL + 45 | 'COMPUTERNAME,15752,chrome#20,0,33488896,217202688'; 46 | 47 | parserCSV(data, function(err, processes) { 48 | 49 | assert.lengthOf(processes, 2); 50 | 51 | processes.forEach(function(proc) { 52 | assert.property(proc, 'pid'); 53 | assert.property(proc, 'name'); 54 | assert.property(proc, 'cpu'); 55 | assert.property(proc, 'pmem'); 56 | assert.property(proc, 'vmem'); 57 | }); 58 | done(); 59 | }); 60 | }); 61 | 62 | it('should generate a PID', function(done) { 63 | 64 | var parserCSV = require('../../../lib/parser/csv'); 65 | var data = 66 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 67 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 68 | 'COMPUTERNAME,3308,chrome#1,0,126222336,419917824'; 69 | 70 | parserCSV(data, function(err, processes) { 71 | 72 | assert.lengthOf(processes, 1); 73 | assert.equal(processes[0].pid, 3308); 74 | done(); 75 | }); 76 | }); 77 | 78 | it('should skip a PID of zero', function(done) { 79 | 80 | var parserCSV = require('../../../lib/parser/csv'); 81 | var data = 82 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 83 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 84 | 'COMPUTERNAME,3308,chrome#1,0,126222336,419917824' + os.EOL + 85 | 'COMPUTERNAME,15752,chrome#20,0,33488896,217202688' + os.EOL + 86 | 'COMPUTERNAME,0,Idle,100,0,65536'; 87 | 88 | parserCSV(data, function(err, processes) { 89 | 90 | assert.lengthOf(processes, 2); 91 | done(); 92 | }); 93 | }); 94 | 95 | it('should generate a string name', function(done) { 96 | 97 | var parserCSV = require('../../../lib/parser/csv'); 98 | var data = 99 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 100 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 101 | 'COMPUTERNAME,3308,chrome,0,126222336,419917824'; 102 | 103 | parserCSV(data, function(err, processes) { 104 | 105 | assert.lengthOf(processes, 1); 106 | assert.isString(processes[0].name); 107 | done(); 108 | }); 109 | }); 110 | 111 | it('should generate a cpu', function(done) { 112 | 113 | var parserCSV = require('../../../lib/parser/csv'); 114 | var data = 115 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 116 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 117 | 'COMPUTERNAME,3308,chrome#1,25,126222336,419917824'; 118 | 119 | parserCSV(data, function(err, processes) { 120 | 121 | assert.lengthOf(processes, 1); 122 | assert.equal(processes[0].cpu, 25); 123 | done(); 124 | }); 125 | }); 126 | 127 | it('should generate a pmem', function(done) { 128 | 129 | var parserCSV = require('../../../lib/parser/csv'); 130 | var data = 131 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 132 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 133 | 'COMPUTERNAME,3308,chrome#1,0,126222336,419917824'; 134 | 135 | parserCSV(data, function(err, processes) { 136 | 137 | assert.lengthOf(processes, 1); 138 | assert.equal(processes[0].pmem, 126222336); 139 | done(); 140 | }); 141 | }); 142 | 143 | it('should generate a vmem', function(done) { 144 | 145 | var parserCSV = require('../../../lib/parser/csv'); 146 | var data = 147 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 148 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 149 | 'COMPUTERNAME,3308,chrome#1,0,126222336,419917824'; 150 | 151 | parserCSV(data, function(err, processes) { 152 | 153 | assert.lengthOf(processes, 1); 154 | assert.equal(processes[0].vmem, 419917824); 155 | done(); 156 | }); 157 | }); 158 | 159 | it('should skip non-data lines', function(done) { 160 | 161 | var parserCSV = require('../../../lib/parser/csv'); 162 | var data = 163 | os.EOL + 164 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 165 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 166 | 'COMPUTERNAME,15752,chrome#20,0,33488896,217202688' + os.EOL + 167 | 'COMPUTERNAME,0,Idle,100,0,65536' + os.EOL + os.EOL; 168 | 169 | parserCSV(data, function(err, processes) { 170 | 171 | assert.lengthOf(processes, 1); 172 | done(); 173 | }); 174 | }); 175 | 176 | it('should strip the process name of a postfixed id', function(done) { 177 | 178 | var parserCSV = require('../../../lib/parser/csv'); 179 | var data = 180 | 'Node,IDProcess,Name,PercentProcessorTime,PrivateBytes,VirtualBytes' + os.EOL + 181 | 'COMPUTERNAME,0,_Total,100,9190395904,46864580607' + os.EOL + 182 | 'COMPUTERNAME,15752,chrome#13,0,33488896,217202688' + os.EOL + 183 | 'COMPUTERNAME,3308,chrome#37,0,126222336,419917824'; 184 | 185 | parserCSV(data, function(err, processes) { 186 | 187 | assert.lengthOf(processes, 2); 188 | assert.isString(processes[0].name); 189 | assert.equal(processes[0].name, 'chrome'); 190 | done(); 191 | }); 192 | }); 193 | }); 194 | -------------------------------------------------------------------------------- /test/spec/parser/fixed-columns.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var os = require('os'); 4 | var assert = require('chai').assert; 5 | var rewire = require('rewire'); 6 | 7 | describe('Parser - FixedColumns', function() { 8 | 9 | it('should not fail when require()\'d', function() { 10 | 11 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 12 | 13 | assert(parserFixedColumns); 14 | }); 15 | 16 | it('should be a function', function() { 17 | 18 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 19 | 20 | assert.isFunction(parserFixedColumns); 21 | }); 22 | 23 | it('should invoke the callback function with the result', function(done) { 24 | 25 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 26 | var data = 27 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 28 | ' 0 0 0 0.0 sched' + os.EOL + 29 | ' 4 0 0 0.0 kcfpoold' + os.EOL + 30 | ' 1 1320 2640 0.0 /sbin/init' + os.EOL + 31 | ' 2 0 0 0.0 pageout' + os.EOL + 32 | ' 3 0 0 0.1 fsflush'; 33 | 34 | parserFixedColumns(data, function(err, processes) { 35 | 36 | assert.isNull(err); 37 | assert.isArray(processes); 38 | assert.lengthOf(processes, 5); 39 | done(); 40 | }); 41 | }); 42 | 43 | it('should generate simplified process objects', function(done) { 44 | 45 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 46 | var data = 47 | ' PID RSS VSZ %CPU COMM' + os.EOL + 48 | ' 0 0 0 0.0 sched' + os.EOL + 49 | ' 4 0 0 0.0 kcfpoold' + os.EOL + 50 | ' 1 1320 2640 0.0 /sbin/init' + os.EOL + 51 | ' 2 0 0 0.0 pageout' + os.EOL + 52 | ' 3 0 0 0.1 fsflush'; 53 | 54 | parserFixedColumns(data, function(err, processes) { 55 | 56 | assert.lengthOf(processes, 5); 57 | 58 | processes.forEach(function(proc) { 59 | assert.property(proc, 'pid'); 60 | assert.property(proc, 'name'); 61 | assert.property(proc, 'cpu'); 62 | assert.property(proc, 'pmem'); 63 | assert.property(proc, 'vmem'); 64 | }); 65 | done(); 66 | }); 67 | }); 68 | 69 | it('should generate a PID', function(done) { 70 | 71 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 72 | var data = 73 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 74 | ' 428 672 15408 0.1 upstart-file-br'; 75 | 76 | parserFixedColumns(data, function(err, processes) { 77 | 78 | assert.lengthOf(processes, 1); 79 | assert.equal(processes[0].pid, 428); 80 | done(); 81 | }); 82 | }); 83 | 84 | it('should generate a string name', function(done) { 85 | 86 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 87 | var data = 88 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 89 | ' 4 0 0 0.0 kcfpoold' + os.EOL + 90 | ' 1 1320 2640 0.0 /sbin/init' + os.EOL + 91 | ' 3 0 0 0.1 fsflush'; 92 | 93 | parserFixedColumns(data, function(err, processes) { 94 | 95 | assert.lengthOf(processes, 3); 96 | assert.isString(processes[0].name); 97 | done(); 98 | }); 99 | }); 100 | 101 | it('should generate a cpu', function(done) { 102 | 103 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 104 | var data = 105 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 106 | ' 0 0 0 0.1 sched' + os.EOL + 107 | ' 4 0 0 0.1 kcfpoold' + os.EOL + 108 | ' 1 1320 2640 0.1 /sbin/init' + os.EOL + 109 | ' 2 0 0 0.1 pageout' + os.EOL + 110 | ' 3 0 0 0.1 fsflush'; 111 | 112 | parserFixedColumns(data, function(err, processes) { 113 | 114 | assert.lengthOf(processes, 5); 115 | assert.equal(processes[0].cpu, '0.1'); 116 | done(); 117 | }); 118 | }); 119 | 120 | it('should generate a pmem', function(done) { 121 | 122 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 123 | var data = 124 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 125 | ' 1 1320 2640 0.0 /sbin/init'; 126 | 127 | parserFixedColumns(data, function(err, processes) { 128 | 129 | assert.lengthOf(processes, 1); 130 | assert.equal(processes[0].pmem, 1320 * 1024); 131 | done(); 132 | }); 133 | }); 134 | 135 | it('should generate a vmem', function(done) { 136 | 137 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 138 | var data = 139 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 140 | ' 1 1320 2640 0.0 /sbin/init'; 141 | 142 | parserFixedColumns(data, function(err, processes) { 143 | 144 | assert.lengthOf(processes, 1); 145 | assert.equal(processes[0].vmem, 2640 * 1024); 146 | done(); 147 | }); 148 | }); 149 | 150 | it('should skip non-data lines', function(done) { 151 | 152 | var parserFixedColumns = require('../../../lib/parser/fixed-columns'); 153 | var data = 154 | os.EOL + 155 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 156 | ' 0 0 0 0.0 sched' + os.EOL + 157 | ' 1 1320 2640 0.0 /sbin/init' + os.EOL + 158 | ' 3 0 0 0.1 fsflush' + os.EOL + os.EOL; 159 | 160 | parserFixedColumns(data, function(err, processes) { 161 | 162 | assert.lengthOf(processes, 3); 163 | done(); 164 | }); 165 | }); 166 | 167 | describe('_trimNewlines()', function() { 168 | 169 | var parserFixedColumns = rewire('../../../lib/parser/fixed-columns'); 170 | var _trimNewlines = parserFixedColumns.__get__('_trimNewlines'); 171 | 172 | it('should trim \\r and \\n newlines', function() { 173 | 174 | var str = '\r\nbla1 bla2 bla3 bla4\n\n'; 175 | 176 | var result = _trimNewlines(str); 177 | 178 | assert.equal(result, 'bla1 bla2 bla3 bla4'); 179 | }); 180 | 181 | it('should leave newlines that are not at the beginning or end', function() { 182 | 183 | var str = '\nbla1 bla2\nbla3 bla4\n\n'; 184 | 185 | var result = _trimNewlines(str); 186 | 187 | assert.equal(result, 'bla1 bla2\nbla3 bla4'); 188 | }); 189 | }); 190 | 191 | describe('_inferColumnWidths()', function() { 192 | 193 | var fixtures = require('../../fixtures/datasource/fixed-columns/_inferColumnWidths'); 194 | var parserFixedColumns = rewire('../../../lib/parser/fixed-columns'); 195 | var _inferColumnWidths = parserFixedColumns.__get__('_inferColumnWidths'); 196 | 197 | it('should augment the columns with a pos{start,length} object', function() { 198 | 199 | var string = fixtures.correct[0].string; 200 | var columns = fixtures.correct[0].columns; 201 | 202 | var result = _inferColumnWidths(columns, string); 203 | 204 | result.forEach(function(col) { 205 | 206 | assert.isObject(col.pos, '`pos` is not an object'); 207 | assert.isNumber(col.pos.start, '`pos.start` is not a number'); 208 | 209 | if (col.pos.length !== undefined) { 210 | assert.isNumber(col.pos.length, '`pos.length` is not a number'); 211 | } 212 | }); 213 | }); 214 | 215 | it('should augment the columns with a getValue() method', function() { 216 | 217 | var string = fixtures.correct[1].string; 218 | var columns = fixtures.correct[1].columns; 219 | 220 | var result = _inferColumnWidths(columns, string); 221 | 222 | result.forEach(function(col) { 223 | assert.isFunction(col.getValue); 224 | }); 225 | }); 226 | 227 | it('should return the right position values', function() { 228 | 229 | fixtures.correct.forEach(function(fixture, fixtureIndex) { 230 | 231 | var result = _inferColumnWidths(fixture.columns, fixture.string); 232 | 233 | fixture.expect.forEach(function(expect, columnIndex) { 234 | 235 | var errMsg = 'At fixture ' + fixtureIndex + ', column ' + fixture.columns[columnIndex].query + ': '; 236 | var getValue = ', getValue() would return \'' + result[columnIndex].getValue(fixture.string) + '\''; 237 | 238 | var pos = result[columnIndex].pos; 239 | assert.equal(pos.start, expect.start, errMsg + 'incorrect `start`' + getValue); 240 | assert.equal(pos.length, expect.length, errMsg + 'incorrect `length`' + getValue); 241 | }); 242 | }); 243 | }); 244 | 245 | it('should throw when passed unsupported values', function() { 246 | 247 | fixtures.incorrect.forEach(function(fixture) { 248 | 249 | var fn = _inferColumnWidths.bind({}, fixture.columns, fixture.string); 250 | 251 | assert.throw(fn); 252 | }); 253 | }); 254 | }); 255 | }); 256 | -------------------------------------------------------------------------------- /test/spec/parser/fluid-columns.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var os = require('os'); 4 | var assert = require('chai').assert; 5 | var rewire = require('rewire'); 6 | 7 | describe('Parser - FluidColumns', function() { 8 | 9 | it('should not fail when require()\'d', function() { 10 | 11 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 12 | 13 | assert(parserFluidColumns); 14 | }); 15 | 16 | it('should be a function', function() { 17 | 18 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 19 | 20 | assert.isFunction(parserFluidColumns); 21 | }); 22 | 23 | it('should invoke the callback function with the result', function(done) { 24 | 25 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 26 | var data = 27 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 28 | ' 0 0 0 0.0 sched' + os.EOL + 29 | ' 4 0 0 0.0 kcfpoold' + os.EOL + 30 | ' 1 1320 2640 0.0 /sbin/init' + os.EOL + 31 | ' 2 0 0 0.0 pageout' + os.EOL + 32 | ' 3 0 0 0.1 fsflush'; 33 | 34 | parserFluidColumns(data, function(err, processes) { 35 | 36 | assert.isNull(err); 37 | assert.isArray(processes); 38 | assert.lengthOf(processes, 5); 39 | done(); 40 | }); 41 | }); 42 | 43 | it('should generate simplified process objects', function(done) { 44 | 45 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 46 | var data = 47 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 48 | ' 0 0 0 0.0 sched' + os.EOL + 49 | ' 4 0 0 0.0 kcfpoold' + os.EOL + 50 | ' 1 1320 2640 0.0 /sbin/init' + os.EOL + 51 | ' 2 0 0 0.0 pageout' + os.EOL + 52 | ' 3 0 0 0.1 fsflush'; 53 | 54 | parserFluidColumns(data, function(err, processes) { 55 | 56 | assert.lengthOf(processes, 5); 57 | 58 | processes.forEach(function(proc) { 59 | assert.property(proc, 'pid'); 60 | assert.property(proc, 'name'); 61 | assert.property(proc, 'cpu'); 62 | assert.property(proc, 'pmem'); 63 | assert.property(proc, 'vmem'); 64 | }); 65 | done(); 66 | }); 67 | }); 68 | 69 | it('should generate a PID', function(done) { 70 | 71 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 72 | var data = 73 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 74 | ' 2 0 0 0.0 pageout'; 75 | 76 | parserFluidColumns(data, function(err, processes) { 77 | 78 | assert.lengthOf(processes, 1); 79 | assert.equal(processes[0].pid, 2); 80 | done(); 81 | }); 82 | }); 83 | 84 | it('should generate a string name', function(done) { 85 | 86 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 87 | var data = 88 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 89 | ' 4 0 0 0.0 kcfpoold' + os.EOL + 90 | ' 1 1320 2640 0.0 /sbin/init' + os.EOL + 91 | ' 3 0 0 0.1 fsflush'; 92 | 93 | parserFluidColumns(data, function(err, processes) { 94 | 95 | assert.lengthOf(processes, 3); 96 | assert.isString(processes[0].name); 97 | done(); 98 | }); 99 | }); 100 | 101 | it('should generate a cpu', function(done) { 102 | 103 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 104 | var data = 105 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 106 | ' 0 0 0 0.1 sched' + os.EOL + 107 | ' 4 0 0 0.1 kcfpoold' + os.EOL + 108 | ' 1 1320 2640 0.1 /sbin/init' + os.EOL + 109 | ' 2 0 0 0.1 pageout' + os.EOL + 110 | ' 3 0 0 0.1 fsflush'; 111 | 112 | parserFluidColumns(data, function(err, processes) { 113 | 114 | assert.lengthOf(processes, 5); 115 | assert.equal(processes[0].cpu, '0.1'); 116 | done(); 117 | }); 118 | }); 119 | 120 | it('should generate a pmem', function(done) { 121 | 122 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 123 | var data = 124 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 125 | ' 1 1320 2640 0.0 /sbin/init'; 126 | 127 | parserFluidColumns(data, function(err, processes) { 128 | 129 | assert.lengthOf(processes, 1); 130 | assert.equal(processes[0].pmem, 1320 * 1024); 131 | done(); 132 | }); 133 | }); 134 | 135 | it('should generate a vmem', function(done) { 136 | 137 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 138 | var data = 139 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 140 | ' 1 1320 2640 0.0 /sbin/init'; 141 | 142 | parserFluidColumns(data, function(err, processes) { 143 | 144 | assert.lengthOf(processes, 1); 145 | assert.equal(processes[0].vmem, 2640 * 1024); 146 | done(); 147 | }); 148 | }); 149 | 150 | it('should skip non-data lines', function(done) { 151 | 152 | var parserFluidColumns = require('../../../lib/parser/fluid-columns'); 153 | var data = 154 | os.EOL + 155 | ' PID RSS VSZ %CPU COMMAND' + os.EOL + 156 | ' 0 0 0 0.0 sched' + os.EOL + 157 | ' 1 1320 2640 0.0 /sbin/init' + os.EOL + 158 | ' 3 0 0 0.1 fsflush' + os.EOL + os.EOL; 159 | 160 | parserFluidColumns(data, function(err, processes) { 161 | 162 | assert.lengthOf(processes, 3); 163 | done(); 164 | }); 165 | }); 166 | 167 | describe('_splitLimit()', function() { 168 | 169 | var parserFluidColumns = rewire('../../../lib/parser/fluid-columns'); 170 | var _splitLimit = parserFluidColumns.__get__('_splitLimit'); 171 | 172 | it('should always return an array', function() { 173 | 174 | var string = 'Split this, but not this'; 175 | var limit = 3; 176 | 177 | var result = _splitLimit(string, limit); 178 | 179 | assert.isArray(result); 180 | }); 181 | 182 | it('should never return more array items than the limit', function() { 183 | 184 | var string = 'Split this, but not this'; 185 | var limit = 3; 186 | 187 | var result = _splitLimit(string, limit); 188 | 189 | assert.lengthOf(result, limit); 190 | }); 191 | 192 | it('should handle multiple spaces', function() { 193 | 194 | var string = '716 6128 0.0 /usr/lib/spotify webhelper'; 195 | var limit = 4; 196 | 197 | var result = _splitLimit(string, limit); 198 | 199 | assert.lengthOf(result, limit); 200 | assert.equal(result[3], '/usr/lib/spotify webhelper'); 201 | }); 202 | }); 203 | }); 204 | -------------------------------------------------------------------------------- /test/spec/parser/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('chai').assert; 4 | var rewire = require('rewire'); 5 | 6 | describe('Parser', function() { 7 | 8 | it('should not fail when require()\'d', function() { 9 | 10 | var Parser = require('../../../lib/parser'); 11 | 12 | assert(Parser); 13 | }); 14 | 15 | it('should have a get() method', function() { 16 | 17 | var Parser = require('../../../lib/parser'); 18 | 19 | assert.isFunction(Parser.get); 20 | }); 21 | 22 | it('should return the right module for each platform', function() { 23 | 24 | var fixtures = [ 25 | { 26 | platform: 'darwin', 27 | function: 'parserFixedColumns' 28 | }, 29 | { 30 | platform: 'freebsd', 31 | function: 'parserFixedColumns' 32 | }, 33 | { 34 | platform: 'linux', 35 | function: 'parserFixedColumns' 36 | }, 37 | { 38 | platform: 'sunos', 39 | function: 'parserFluidColumns' 40 | }, 41 | { 42 | platform: 'win32', 43 | function: 'parserCSV' 44 | } 45 | ]; 46 | 47 | fixtures.forEach(function(fixture) { 48 | 49 | var Parser = rewire('../../../lib/parser'); 50 | Parser.__set__('platform', fixture.platform); 51 | 52 | var result = Parser.get(); 53 | 54 | assert.equal(result.name, fixture.function); 55 | }); 56 | }); 57 | }); 58 | --------------------------------------------------------------------------------