autobench 0.0.1alpha1
Sign up to get free protection for your applications and to get access to all the features.
- 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();
|