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.
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();