autobench 0.0.1alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +16 -0
- data/README.md +123 -0
- data/bin/autobench +180 -0
- data/bin/autobench-config +162 -0
- data/lib/autobench.rb +28 -0
- data/lib/autobench/client.rb +78 -0
- data/lib/autobench/common.rb +49 -0
- data/lib/autobench/config.rb +62 -0
- data/lib/autobench/render.rb +102 -0
- data/lib/autobench/version.rb +3 -0
- data/lib/autobench/yslow.rb +75 -0
- data/lib/phantomas/README.md +296 -0
- data/lib/phantomas/core/formatter.js +65 -0
- data/lib/phantomas/core/helper.js +64 -0
- data/lib/phantomas/core/modules/requestsMonitor/requestsMonitor.js +214 -0
- data/lib/phantomas/core/pads.js +16 -0
- data/lib/phantomas/core/phantomas.js +418 -0
- data/lib/phantomas/lib/args.js +27 -0
- data/lib/phantomas/lib/modules/_coffee-script.js +2 -0
- data/lib/phantomas/lib/modules/assert.js +326 -0
- data/lib/phantomas/lib/modules/events.js +216 -0
- data/lib/phantomas/lib/modules/http.js +55 -0
- data/lib/phantomas/lib/modules/path.js +441 -0
- data/lib/phantomas/lib/modules/punycode.js +510 -0
- data/lib/phantomas/lib/modules/querystring.js +214 -0
- data/lib/phantomas/lib/modules/tty.js +7 -0
- data/lib/phantomas/lib/modules/url.js +625 -0
- data/lib/phantomas/lib/modules/util.js +520 -0
- data/lib/phantomas/modules/ajaxRequests/ajaxRequests.js +15 -0
- data/lib/phantomas/modules/assetsTypes/assetsTypes.js +21 -0
- data/lib/phantomas/modules/cacheHits/cacheHits.js +28 -0
- data/lib/phantomas/modules/caching/caching.js +66 -0
- data/lib/phantomas/modules/cookies/cookies.js +54 -0
- data/lib/phantomas/modules/domComplexity/domComplexity.js +130 -0
- data/lib/phantomas/modules/domQueries/domQueries.js +148 -0
- data/lib/phantomas/modules/domains/domains.js +49 -0
- data/lib/phantomas/modules/globalVariables/globalVariables.js +44 -0
- data/lib/phantomas/modules/headers/headers.js +48 -0
- data/lib/phantomas/modules/localStorage/localStorage.js +14 -0
- data/lib/phantomas/modules/requestsStats/requestsStats.js +71 -0
- data/lib/phantomas/modules/staticAssets/staticAssets.js +40 -0
- data/lib/phantomas/modules/waterfall/waterfall.js +62 -0
- data/lib/phantomas/modules/windowPerformance/windowPerformance.js +36 -0
- data/lib/phantomas/package.json +27 -0
- data/lib/phantomas/phantomas.js +35 -0
- data/lib/phantomas/run-multiple.js +177 -0
- data/lib/yslow.js +5 -0
- metadata +135 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
/**
|
2
|
+
* Domains monitor
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
// count requests per domain
|
8
|
+
var domains = {},
|
9
|
+
domainsCount = 0;
|
10
|
+
|
11
|
+
phantomas.on('recv', function(entry,res) {
|
12
|
+
var domain = entry.domain;
|
13
|
+
|
14
|
+
// base64?
|
15
|
+
if (!domain) {
|
16
|
+
return;
|
17
|
+
}
|
18
|
+
|
19
|
+
// init domain entry
|
20
|
+
if (!domains[domain]) {
|
21
|
+
domainsCount++;
|
22
|
+
|
23
|
+
domains[domain] = {
|
24
|
+
requests: []
|
25
|
+
};
|
26
|
+
}
|
27
|
+
|
28
|
+
domains[domain].requests.push(res.url);
|
29
|
+
});
|
30
|
+
|
31
|
+
// add metrics
|
32
|
+
phantomas.on('loadFinished', function() {
|
33
|
+
//console.log(domains);
|
34
|
+
phantomas.setMetric('domains', domainsCount);
|
35
|
+
|
36
|
+
phantomas.addNotice('Requests per domain:');
|
37
|
+
for(var domain in domains) {
|
38
|
+
var entry = domains[domain],
|
39
|
+
requests = entry.requests;
|
40
|
+
|
41
|
+
// report URLs from each domain
|
42
|
+
phantomas.addNotice(' ' + domain + ': ' + requests.length + ' request(s)');
|
43
|
+
requests.forEach(function(url) {
|
44
|
+
//phantomas.addNotice(' * ' + url);
|
45
|
+
});
|
46
|
+
}
|
47
|
+
phantomas.addNotice();
|
48
|
+
});
|
49
|
+
};
|
@@ -0,0 +1,44 @@
|
|
1
|
+
/**
|
2
|
+
* Counts global JavaScript variables
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
|
8
|
+
phantomas.on('report', function() {
|
9
|
+
var globals = phantomas.evaluate(function() {
|
10
|
+
var globals = [],
|
11
|
+
allowed = ['Components','XPCNativeWrapper','XPCSafeJSObjectWrapper','getInterface','netscape','GetWeakReference', '_phantom', 'callPhantom', 'phantomas'],
|
12
|
+
varName,
|
13
|
+
iframe,
|
14
|
+
cleanWindow;
|
15
|
+
|
16
|
+
// create an empty iframe to get the list of core members
|
17
|
+
iframe = document.createElement('iframe');
|
18
|
+
iframe.style.display = 'none';
|
19
|
+
iframe.src = 'about:blank';
|
20
|
+
document.body.appendChild(iframe);
|
21
|
+
|
22
|
+
cleanWindow = iframe.contentWindow;
|
23
|
+
|
24
|
+
for (varName in cleanWindow) {
|
25
|
+
allowed.push(varName);
|
26
|
+
}
|
27
|
+
|
28
|
+
// get all members of window and filter them
|
29
|
+
for (varName in window) {
|
30
|
+
if (allowed.indexOf(varName) > -1) {
|
31
|
+
continue;
|
32
|
+
}
|
33
|
+
|
34
|
+
globals.push(varName);
|
35
|
+
}
|
36
|
+
|
37
|
+
return globals;
|
38
|
+
}) || [];
|
39
|
+
|
40
|
+
phantomas.setMetric('globalVariables', globals.length);
|
41
|
+
phantomas.addNotice('JavaScript globals (' + globals.length + '): ' + globals.join(', '));
|
42
|
+
phantomas.addNotice();
|
43
|
+
});
|
44
|
+
};
|
@@ -0,0 +1,48 @@
|
|
1
|
+
/**
|
2
|
+
* Analyzes HTTP headers in both requests and responses
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
phantomas.setMetric('headersCount');
|
8
|
+
phantomas.setMetric('headersSentCount');
|
9
|
+
phantomas.setMetric('headersRecvCount');
|
10
|
+
|
11
|
+
phantomas.setMetric('headersSize');
|
12
|
+
phantomas.setMetric('headersSentSize');
|
13
|
+
phantomas.setMetric('headersRecvSize');
|
14
|
+
|
15
|
+
function processHeaders(headers) {
|
16
|
+
var res = {
|
17
|
+
count: 0,
|
18
|
+
size: 0
|
19
|
+
};
|
20
|
+
|
21
|
+
headers && headers.forEach(function(header) {
|
22
|
+
res.count++;
|
23
|
+
res.size += (header.name + ': ' + header.value + '\r\n').length;
|
24
|
+
});
|
25
|
+
|
26
|
+
return res;
|
27
|
+
}
|
28
|
+
|
29
|
+
phantomas.on('send', function(entry, res) {
|
30
|
+
var data = processHeaders(res.headers);
|
31
|
+
|
32
|
+
phantomas.incrMetric('headersCount', data.count);
|
33
|
+
phantomas.incrMetric('headersSize', data.size);
|
34
|
+
|
35
|
+
phantomas.incrMetric('headersSentCount', data.count);
|
36
|
+
phantomas.incrMetric('headersSentSize', data.size);
|
37
|
+
});
|
38
|
+
|
39
|
+
phantomas.on('recv', function(entry, res) {
|
40
|
+
var data = processHeaders(res.headers);
|
41
|
+
|
42
|
+
phantomas.incrMetric('headersCount', data.count);
|
43
|
+
phantomas.incrMetric('headersSize', data.size);
|
44
|
+
|
45
|
+
phantomas.incrMetric('headersRecvCount', data.count);
|
46
|
+
phantomas.incrMetric('headersRecvSize', data.size);
|
47
|
+
});
|
48
|
+
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
/**
|
2
|
+
* localStorage metrics
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
// add metrics
|
8
|
+
phantomas.on('report', function() {
|
9
|
+
//console.log(domains);
|
10
|
+
phantomas.setMetricEvaluate('localStorageEntries', function() {
|
11
|
+
return window.localStorage.length;
|
12
|
+
});
|
13
|
+
});
|
14
|
+
};
|
@@ -0,0 +1,71 @@
|
|
1
|
+
/**
|
2
|
+
* Analyzes HTTP requests and generates stats metrics
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
var smallestResponse,
|
8
|
+
biggestResponse,
|
9
|
+
fastestResponse,
|
10
|
+
slowestResponse;
|
11
|
+
|
12
|
+
var responseTimes = [];
|
13
|
+
|
14
|
+
function median(arr) {
|
15
|
+
var half = Math.floor(arr.length/2);
|
16
|
+
|
17
|
+
arr.sort(function(a,b) {
|
18
|
+
return a - b;
|
19
|
+
});
|
20
|
+
|
21
|
+
return (arr.length % 2) ? arr[half] : ((arr[half-1] + arr[half]) / 2.0);
|
22
|
+
}
|
23
|
+
|
24
|
+
phantomas.on('recv', function(entry, res) {
|
25
|
+
// ignore anything different than HTTP 200
|
26
|
+
if (entry.status !== 200) {
|
27
|
+
return;
|
28
|
+
}
|
29
|
+
|
30
|
+
// size
|
31
|
+
if (!smallestResponse || smallestResponse.bodySize > entry.bodySize) {
|
32
|
+
smallestResponse = entry;
|
33
|
+
}
|
34
|
+
|
35
|
+
if (!biggestResponse || biggestResponse.bodySize < entry.bodySize) {
|
36
|
+
biggestResponse = entry;
|
37
|
+
}
|
38
|
+
|
39
|
+
// time
|
40
|
+
if (!fastestResponse || fastestResponse.timeToLastByte > entry.timeToLastByte) {
|
41
|
+
fastestResponse = entry;
|
42
|
+
}
|
43
|
+
|
44
|
+
if (!slowestResponse || slowestResponse.timeToLastByte < entry.timeToLastByte) {
|
45
|
+
slowestResponse = entry;
|
46
|
+
}
|
47
|
+
|
48
|
+
// store time to calculate median response time
|
49
|
+
responseTimes.push(entry.timeToLastByte);
|
50
|
+
});
|
51
|
+
|
52
|
+
phantomas.on('report', function() {
|
53
|
+
phantomas.setMetric('smallestResponse', smallestResponse.bodySize);
|
54
|
+
phantomas.setMetric('biggestResponse', biggestResponse.bodySize);
|
55
|
+
|
56
|
+
phantomas.addNotice('The smallest response (' + (smallestResponse.bodySize/1024).toFixed(2) + ' kB): ' + smallestResponse.url);
|
57
|
+
phantomas.addNotice('The biggest response (' + (biggestResponse.bodySize/1024).toFixed(2) + ' kB): ' + biggestResponse.url);
|
58
|
+
|
59
|
+
phantomas.addNotice();
|
60
|
+
|
61
|
+
phantomas.setMetric('fastestResponse', fastestResponse.timeToLastByte);
|
62
|
+
phantomas.setMetric('slowestResponse', slowestResponse.timeToLastByte);
|
63
|
+
|
64
|
+
phantomas.addNotice('The fastest response (' + fastestResponse.timeToLastByte + ' ms): ' + fastestResponse.url);
|
65
|
+
phantomas.addNotice('The slowest response (' + slowestResponse.timeToLastByte + ' ms): ' + slowestResponse.url);
|
66
|
+
|
67
|
+
phantomas.addNotice();
|
68
|
+
|
69
|
+
phantomas.setMetric('medianResponse', median(responseTimes));
|
70
|
+
});
|
71
|
+
};
|
@@ -0,0 +1,40 @@
|
|
1
|
+
/**
|
2
|
+
* Analyzes static assets (CSS, JS and images)
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
var BASE64_SIZE_THRESHOLD = 2 * 1024;
|
8
|
+
|
9
|
+
phantomas.setMetric('assetsNotGzipped');
|
10
|
+
phantomas.setMetric('assetsWithQueryString');
|
11
|
+
phantomas.setMetric('smallImages');
|
12
|
+
|
13
|
+
phantomas.on('recv', function(entry, res) {
|
14
|
+
// console.log(JSON.stringify(entry));
|
15
|
+
|
16
|
+
// check for query string -> foo.css?123
|
17
|
+
if (entry.isImage || entry.isJS || entry.isCSS) {
|
18
|
+
if (entry.url.indexOf('?') > -1) {
|
19
|
+
phantomas.addNotice(entry.url + ' (' + entry.type.toUpperCase() + ') served with query string');
|
20
|
+
phantomas.incrMetric('assetsWithQueryString');
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
// check for not-gzipped CSS / JS / HTML files
|
25
|
+
if (entry.isJS || entry.isCSS || entry.isHTML) {
|
26
|
+
if (!entry.gzip) {
|
27
|
+
phantomas.addNotice(entry.url + ' (' + entry.type.toUpperCase() + ') served without compression');
|
28
|
+
phantomas.incrMetric('assetsNotGzipped');
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
// check small images that can be base64 encoded
|
33
|
+
if (entry.isImage) {
|
34
|
+
if (entry.bodySize < BASE64_SIZE_THRESHOLD) {
|
35
|
+
phantomas.addNotice(entry.url + ' (' + (entry.bodySize/1024).toFixed(2) + ' kB) should be served as base64 encoded');
|
36
|
+
phantomas.incrMetric('smallImages');
|
37
|
+
}
|
38
|
+
}
|
39
|
+
});
|
40
|
+
};
|
@@ -0,0 +1,62 @@
|
|
1
|
+
/**
|
2
|
+
* Plots ASCII-art waterfall
|
3
|
+
*/
|
4
|
+
exports.version = '0.5';
|
5
|
+
|
6
|
+
exports.skip = true;
|
7
|
+
|
8
|
+
exports.module = function(phantomas) {
|
9
|
+
var requestsTimeline = {},
|
10
|
+
currentRequests = 0,
|
11
|
+
start = Date.now();
|
12
|
+
|
13
|
+
function updatePlot() {
|
14
|
+
requestsTimeline[ Date.now() - start ] = currentRequests;
|
15
|
+
}
|
16
|
+
|
17
|
+
phantomas.on('send', function(res) {
|
18
|
+
currentRequests++;
|
19
|
+
updatePlot();
|
20
|
+
});
|
21
|
+
|
22
|
+
phantomas.on('recv', function(entry,res) {
|
23
|
+
currentRequests--;
|
24
|
+
updatePlot();
|
25
|
+
});
|
26
|
+
|
27
|
+
// plot waterfall
|
28
|
+
phantomas.on('report', function() {
|
29
|
+
phantomas.addNotice('No of requests waterfall:');
|
30
|
+
|
31
|
+
var delta = 100,
|
32
|
+
lastValue = 0,
|
33
|
+
value,
|
34
|
+
lastTime = 0,
|
35
|
+
values = {};
|
36
|
+
|
37
|
+
// gather maximum values from each delta ms bucket
|
38
|
+
for (var time in requestsTimeline) {
|
39
|
+
value = requestsTimeline[time];
|
40
|
+
lastValue = Math.max(lastValue, value);
|
41
|
+
|
42
|
+
if (time > lastTime + delta) {
|
43
|
+
lastTime = Math.ceil(time / delta) * delta;
|
44
|
+
|
45
|
+
values[lastTime] = lastValue;
|
46
|
+
|
47
|
+
lastValue = 0;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
// now plot the waterfall for each bucket
|
52
|
+
lastValue = 1;
|
53
|
+
for (var i=0; i<time; i+=delta) {
|
54
|
+
// get the current bucket (or previous one)
|
55
|
+
value = values[i] || lastValue;
|
56
|
+
|
57
|
+
phantomas.addNotice(i + 'ms | ' + (new Array(value+1).join('=')));
|
58
|
+
|
59
|
+
lastValue = value;
|
60
|
+
}
|
61
|
+
});
|
62
|
+
};
|
@@ -0,0 +1,36 @@
|
|
1
|
+
/**
|
2
|
+
* Measure when onDOMready and window.onload events are fired
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
|
8
|
+
// emulate window.performance
|
9
|
+
// @see https://groups.google.com/d/topic/phantomjs/WnXZLIb_jVc/discussion
|
10
|
+
phantomas.on('init', function() {
|
11
|
+
phantomas.evaluate(function() {
|
12
|
+
window.phantomas.timingLoadStarted = Date.now();
|
13
|
+
|
14
|
+
document.addEventListener("DOMContentLoaded", function() {
|
15
|
+
window.phantomas.timingDOMContentLoaded = Date.now();
|
16
|
+
console.log('onDOMready');
|
17
|
+
}, false);
|
18
|
+
|
19
|
+
window.addEventListener("load", function() {
|
20
|
+
window.phantomas.timingOnLoad = Date.now();
|
21
|
+
console.log('window.onload');
|
22
|
+
}, false);
|
23
|
+
});
|
24
|
+
});
|
25
|
+
|
26
|
+
// called just before report is generated
|
27
|
+
phantomas.on('report', function() {
|
28
|
+
phantomas.setMetricEvaluate('onDOMReadyTime', function() {
|
29
|
+
return window.phantomas.timingDOMContentLoaded - window.phantomas.timingLoadStarted;
|
30
|
+
});
|
31
|
+
|
32
|
+
phantomas.setMetricEvaluate('windowOnLoadTime', function() {
|
33
|
+
return window.phantomas.timingOnLoad - window.phantomas.timingLoadStarted;
|
34
|
+
});
|
35
|
+
});
|
36
|
+
};
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"name": "phantomas",
|
3
|
+
"version": "0.4.1",
|
4
|
+
"author": "macbre <maciej.brencz@gmail.com> (http://macbre.net)",
|
5
|
+
"description": "PhantomJS-based web performance metrics collector",
|
6
|
+
"main": "./core/phantomas",
|
7
|
+
"repository": {
|
8
|
+
"type": "git",
|
9
|
+
"url": "git://github.com/macbre/phantomas.git"
|
10
|
+
},
|
11
|
+
"keywords": [
|
12
|
+
"phantomas",
|
13
|
+
"web performance",
|
14
|
+
"web development",
|
15
|
+
"metrics",
|
16
|
+
"phantomjs"
|
17
|
+
],
|
18
|
+
"license": "BSD",
|
19
|
+
"engines": {
|
20
|
+
"node": ">=0.6"
|
21
|
+
},
|
22
|
+
"dependencies": {
|
23
|
+
"phantomjs": ">=1.7"
|
24
|
+
},
|
25
|
+
"devDependencies": {},
|
26
|
+
"optionalDependencies": {}
|
27
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env phantomjs
|
2
|
+
/**
|
3
|
+
* PhantomJS-based web performance metrics collector
|
4
|
+
*
|
5
|
+
* Usage:
|
6
|
+
* ./phantomas.js
|
7
|
+
* --url=<page to check>
|
8
|
+
* [--timeout=5]
|
9
|
+
* [--format=json|csv|plain]
|
10
|
+
* [--verbose]
|
11
|
+
* [--silent]
|
12
|
+
* [--modules=moduleOne,moduleTwo]
|
13
|
+
* [--user-agent='Custom user agent']
|
14
|
+
*
|
15
|
+
* @version 0.4
|
16
|
+
*/
|
17
|
+
|
18
|
+
// parse script arguments
|
19
|
+
var args = require('system').args,
|
20
|
+
params = require('./lib/args').parse(args),
|
21
|
+
phantomas = require('./core/phantomas').phantomas,
|
22
|
+
instance;
|
23
|
+
|
24
|
+
// run phantomas
|
25
|
+
instance = new phantomas(params);
|
26
|
+
|
27
|
+
try {
|
28
|
+
instance.run();
|
29
|
+
}
|
30
|
+
catch(ex) {
|
31
|
+
console.log('phantomas v' + phantomas.version + ' failed with an error:');
|
32
|
+
console.log(ex);
|
33
|
+
|
34
|
+
phantom.exit(1);
|
35
|
+
}
|
@@ -0,0 +1,177 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
/**
|
3
|
+
* This is a helper NodeJS script allowing you to run phantomas multiple times and
|
4
|
+
* get a nice looking table with all the metrics + avg / median / min / max values
|
5
|
+
*
|
6
|
+
* Usage:
|
7
|
+
* ./run-multiple.js
|
8
|
+
* --url=<page to check>
|
9
|
+
* --runs=<number of runs, defaults to 3>
|
10
|
+
* --timeout=<in seconds (for each run), default to 15>
|
11
|
+
* [--modules=moduleOne,moduleTwo]
|
12
|
+
* [--format=plain|json] (plain is default)
|
13
|
+
* note: json format, prints only errorrs or the
|
14
|
+
* results in json format, no other
|
15
|
+
* messaging
|
16
|
+
*
|
17
|
+
* @version 0.1
|
18
|
+
*/
|
19
|
+
var exec = require('child_process').exec,
|
20
|
+
args = process.argv.slice(2),
|
21
|
+
params = require('./lib/args').parse(args),
|
22
|
+
pads = require('./core/pads'),
|
23
|
+
lpad = pads.lpad,
|
24
|
+
rpad = pads.rpad;
|
25
|
+
|
26
|
+
// handle --url and --runs CLI parameters
|
27
|
+
var url = params.url,
|
28
|
+
runs = parseInt(params.runs, 10) || 3,
|
29
|
+
format = params.format || 'plain',
|
30
|
+
remainingRuns = runs,
|
31
|
+
metrics = [];
|
32
|
+
|
33
|
+
function runPhantomas(params, callback) {
|
34
|
+
var timeMs = Date.now(),
|
35
|
+
cmd = 'phantomjs phantomas.js --format=json --url=' + params.url;
|
36
|
+
|
37
|
+
if (params.timeout > 0) {
|
38
|
+
cmd += ' --timeout=' + params.timeout;
|
39
|
+
}
|
40
|
+
|
41
|
+
if (params.modules) {
|
42
|
+
cmd += ' --modules=' + params.modules;
|
43
|
+
}
|
44
|
+
|
45
|
+
// @see http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
|
46
|
+
exec(cmd, function(error, stdout, stderr) {
|
47
|
+
var res = false;
|
48
|
+
|
49
|
+
try {
|
50
|
+
res = JSON.parse(stdout) || false;
|
51
|
+
} catch(e) {
|
52
|
+
console.log("Unable to parse JSON from phantomas!");
|
53
|
+
}
|
54
|
+
|
55
|
+
if (res === false) {
|
56
|
+
console.log(stdout);
|
57
|
+
}
|
58
|
+
|
59
|
+
if (typeof callback === 'function') {
|
60
|
+
callback(res, Date.now() - timeMs);
|
61
|
+
}
|
62
|
+
});
|
63
|
+
}
|
64
|
+
|
65
|
+
function run() {
|
66
|
+
if (remainingRuns--) {
|
67
|
+
if (format === 'plain') {
|
68
|
+
console.log('Remaining runs: ' + (remainingRuns + 1));
|
69
|
+
}
|
70
|
+
|
71
|
+
runPhantomas(params, function(res, timeMs) {
|
72
|
+
if (res) {
|
73
|
+
metrics.push(res.metrics);
|
74
|
+
}
|
75
|
+
|
76
|
+
if (format === 'plain') {
|
77
|
+
console.log('Run completed in ' + (timeMs/1000).toFixed(2) + ' s');
|
78
|
+
}
|
79
|
+
run();
|
80
|
+
});
|
81
|
+
} else {
|
82
|
+
if (format === 'plain') {
|
83
|
+
console.log('Done');
|
84
|
+
}
|
85
|
+
formatResults(metrics);
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
function formatResults(metrics) {
|
90
|
+
var entries = {},
|
91
|
+
entry,
|
92
|
+
metric;
|
93
|
+
|
94
|
+
// prepare entries
|
95
|
+
for(metric in metrics[0]) {
|
96
|
+
entries[metric] = {
|
97
|
+
values: [],
|
98
|
+
sum: 0,
|
99
|
+
min: 0,
|
100
|
+
max: 0,
|
101
|
+
median: 0,
|
102
|
+
average: 0
|
103
|
+
};
|
104
|
+
}
|
105
|
+
|
106
|
+
// process all runs
|
107
|
+
metrics.forEach(function(data) {
|
108
|
+
var metric;
|
109
|
+
for (metric in data) {
|
110
|
+
entries[metric].values.push(data[metric]);
|
111
|
+
}
|
112
|
+
});
|
113
|
+
|
114
|
+
// calculate stats
|
115
|
+
for (metric in entries) {
|
116
|
+
entry = entries[metric];
|
117
|
+
|
118
|
+
entry.values = entry.values.
|
119
|
+
filter(function(element) {
|
120
|
+
return element !== null;
|
121
|
+
}).
|
122
|
+
sort(function (a, b) {
|
123
|
+
return a - b;
|
124
|
+
});
|
125
|
+
|
126
|
+
if (entry.values.length === 0) {
|
127
|
+
continue;
|
128
|
+
}
|
129
|
+
|
130
|
+
entry.min = entry.values.slice(0, 1).pop();
|
131
|
+
entry.max = entry.values.slice(-1).pop();
|
132
|
+
|
133
|
+
for (var i=0, len = entry.values.length++; i<len; i++) {
|
134
|
+
entry.sum += entry.values[i];
|
135
|
+
}
|
136
|
+
|
137
|
+
entry.average = len && (entry.sum / len).toFixed(2);
|
138
|
+
entry.median = ( (len % 2 === 0) ? ((entry.values[len >> 1] + entry.values[len >> 1 + 1])/2) : entry.values[len >> 1] ).toFixed(2);
|
139
|
+
}
|
140
|
+
|
141
|
+
// print out a nice table
|
142
|
+
if (format === 'plain') {
|
143
|
+
console.log("-------------------------------------------------------------------------------------------");
|
144
|
+
console.log("| " + rpad("Report from " + runs + " run(s) for <" + params.url + ">", 87) + " |");
|
145
|
+
console.log("-------------------------------------------------------------------------------------------");
|
146
|
+
console.log("| Metric | Min | Max | Average | Median |");
|
147
|
+
console.log("-------------------------------------------------------------------------------------------");
|
148
|
+
|
149
|
+
for (metric in entries) {
|
150
|
+
entry = entries[metric];
|
151
|
+
|
152
|
+
console.log("| "+
|
153
|
+
[
|
154
|
+
rpad(metric, 27),
|
155
|
+
lpad(entry.min, 12),
|
156
|
+
lpad(entry.max, 12),
|
157
|
+
lpad(entry.average, 12),
|
158
|
+
lpad(entry.median, 12)
|
159
|
+
].join(" | ") +
|
160
|
+
" |");
|
161
|
+
}
|
162
|
+
|
163
|
+
console.log("-------------------------------------------------------------------------------------------");
|
164
|
+
} else {
|
165
|
+
console.log(JSON.stringify(metrics));
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
if (typeof url === 'undefined') {
|
170
|
+
console.log('--url argument must be provided!');
|
171
|
+
process.exit(1);
|
172
|
+
}
|
173
|
+
|
174
|
+
if (format === 'plain') {
|
175
|
+
console.log('Performing ' + runs + ' phantomas run(s) for <' + params.url + '>...');
|
176
|
+
}
|
177
|
+
run();
|