autobench 0.0.1alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +16 -0
  3. data/README.md +123 -0
  4. data/bin/autobench +180 -0
  5. data/bin/autobench-config +162 -0
  6. data/lib/autobench.rb +28 -0
  7. data/lib/autobench/client.rb +78 -0
  8. data/lib/autobench/common.rb +49 -0
  9. data/lib/autobench/config.rb +62 -0
  10. data/lib/autobench/render.rb +102 -0
  11. data/lib/autobench/version.rb +3 -0
  12. data/lib/autobench/yslow.rb +75 -0
  13. data/lib/phantomas/README.md +296 -0
  14. data/lib/phantomas/core/formatter.js +65 -0
  15. data/lib/phantomas/core/helper.js +64 -0
  16. data/lib/phantomas/core/modules/requestsMonitor/requestsMonitor.js +214 -0
  17. data/lib/phantomas/core/pads.js +16 -0
  18. data/lib/phantomas/core/phantomas.js +418 -0
  19. data/lib/phantomas/lib/args.js +27 -0
  20. data/lib/phantomas/lib/modules/_coffee-script.js +2 -0
  21. data/lib/phantomas/lib/modules/assert.js +326 -0
  22. data/lib/phantomas/lib/modules/events.js +216 -0
  23. data/lib/phantomas/lib/modules/http.js +55 -0
  24. data/lib/phantomas/lib/modules/path.js +441 -0
  25. data/lib/phantomas/lib/modules/punycode.js +510 -0
  26. data/lib/phantomas/lib/modules/querystring.js +214 -0
  27. data/lib/phantomas/lib/modules/tty.js +7 -0
  28. data/lib/phantomas/lib/modules/url.js +625 -0
  29. data/lib/phantomas/lib/modules/util.js +520 -0
  30. data/lib/phantomas/modules/ajaxRequests/ajaxRequests.js +15 -0
  31. data/lib/phantomas/modules/assetsTypes/assetsTypes.js +21 -0
  32. data/lib/phantomas/modules/cacheHits/cacheHits.js +28 -0
  33. data/lib/phantomas/modules/caching/caching.js +66 -0
  34. data/lib/phantomas/modules/cookies/cookies.js +54 -0
  35. data/lib/phantomas/modules/domComplexity/domComplexity.js +130 -0
  36. data/lib/phantomas/modules/domQueries/domQueries.js +148 -0
  37. data/lib/phantomas/modules/domains/domains.js +49 -0
  38. data/lib/phantomas/modules/globalVariables/globalVariables.js +44 -0
  39. data/lib/phantomas/modules/headers/headers.js +48 -0
  40. data/lib/phantomas/modules/localStorage/localStorage.js +14 -0
  41. data/lib/phantomas/modules/requestsStats/requestsStats.js +71 -0
  42. data/lib/phantomas/modules/staticAssets/staticAssets.js +40 -0
  43. data/lib/phantomas/modules/waterfall/waterfall.js +62 -0
  44. data/lib/phantomas/modules/windowPerformance/windowPerformance.js +36 -0
  45. data/lib/phantomas/package.json +27 -0
  46. data/lib/phantomas/phantomas.js +35 -0
  47. data/lib/phantomas/run-multiple.js +177 -0
  48. data/lib/yslow.js +5 -0
  49. 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();