lab_bench 0.2.0 → 0.2.1
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.
- data/Gemfile +8 -2
- data/Gemfile.lock +25 -0
- data/README.rdoc +20 -0
- data/VERSION +1 -1
- data/assets/css/app.css +28 -2
- data/assets/images/connect.png +0 -0
- data/assets/images/disconnect.png +0 -0
- data/assets/js/app.js +54 -48
- data/assets/js/underscore-min.js +25 -0
- data/assets/views/index.haml +16 -0
- data/bin/lab_bench_server +26 -4
- data/lab_bench.gemspec +114 -0
- metadata +136 -22
- data/assets/index.html +0 -18
data/Gemfile
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
|
+
|
2
3
|
# Add dependencies required to use your gem here.
|
3
|
-
|
4
|
-
|
4
|
+
gem 'haml', '~> 2.2.0'
|
5
|
+
gem 'sinatra', '~> 1.1.0'
|
6
|
+
gem 'eventmachine', '~> 0.12.10'
|
7
|
+
gem 'em-websocket', '~> 0.2.0'
|
8
|
+
gem 'thin', '~> 1.2.0'
|
9
|
+
gem 'yajl-ruby', '~> 0.7.8'
|
5
10
|
|
6
11
|
# Add dependencies to develop your gem here.
|
7
12
|
# Include everything needed to run rake, tests, features, etc.
|
@@ -10,4 +15,5 @@ group :development do
|
|
10
15
|
gem "bundler", "~> 1.0.0"
|
11
16
|
gem "jeweler", "~> 1.5.1"
|
12
17
|
gem "rcov", ">= 0"
|
18
|
+
gem 'hpricot'
|
13
19
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,45 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
addressable (2.2.2)
|
5
|
+
daemons (1.1.0)
|
6
|
+
em-websocket (0.2.0)
|
7
|
+
addressable (>= 2.1.1)
|
8
|
+
eventmachine (>= 0.12.9)
|
9
|
+
eventmachine (0.12.10)
|
4
10
|
git (1.2.5)
|
11
|
+
haml (2.2.24)
|
12
|
+
hpricot (0.8.3)
|
5
13
|
jeweler (1.5.1)
|
6
14
|
bundler (~> 1.0.0)
|
7
15
|
git (>= 1.2.5)
|
8
16
|
rake
|
17
|
+
rack (1.2.1)
|
9
18
|
rake (0.8.7)
|
10
19
|
rcov (0.9.9)
|
11
20
|
shoulda (2.11.3)
|
21
|
+
sinatra (1.1.0)
|
22
|
+
rack (~> 1.1)
|
23
|
+
tilt (~> 1.1)
|
24
|
+
thin (1.2.7)
|
25
|
+
daemons (>= 1.0.9)
|
26
|
+
eventmachine (>= 0.12.6)
|
27
|
+
rack (>= 1.0.0)
|
28
|
+
tilt (1.1)
|
29
|
+
yajl-ruby (0.7.8)
|
12
30
|
|
13
31
|
PLATFORMS
|
14
32
|
ruby
|
15
33
|
|
16
34
|
DEPENDENCIES
|
17
35
|
bundler (~> 1.0.0)
|
36
|
+
em-websocket (~> 0.2.0)
|
37
|
+
eventmachine (~> 0.12.10)
|
38
|
+
haml (~> 2.2.0)
|
39
|
+
hpricot
|
18
40
|
jeweler (~> 1.5.1)
|
19
41
|
rcov
|
20
42
|
shoulda
|
43
|
+
sinatra (~> 1.1.0)
|
44
|
+
thin (~> 1.2.0)
|
45
|
+
yajl-ruby (~> 0.7.8)
|
data/README.rdoc
CHANGED
@@ -2,8 +2,28 @@
|
|
2
2
|
|
3
3
|
A browser-based runner for ruby Test::Unit tests.
|
4
4
|
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
Install the gem:
|
8
|
+
|
9
|
+
<tt>gem install lab_bench</tt>
|
10
|
+
|
11
|
+
If you're using Rails 2.x, add this to config/environment.rb:
|
12
|
+
|
13
|
+
<tt>config.gem 'lab_bench'</tt>
|
14
|
+
|
15
|
+
Start the server in a new console / screen:
|
16
|
+
|
17
|
+
<tt>lab_bench_server</tt>
|
18
|
+
|
19
|
+
Send your browser to http://localhost:9020/ then run your tests like this:
|
20
|
+
|
21
|
+
<tt>rake test TESTOPTS="--runner=lab_bench"</tt>
|
22
|
+
|
5
23
|
== TODO
|
6
24
|
|
25
|
+
* Buffer test events in server and replay to new browser clients so they can start "hot"
|
26
|
+
* Browser should show whether it is connected to the server and reconnect if it dies
|
7
27
|
* Show me test failures as they happen
|
8
28
|
* Give me more context on the test suite (names of files being run, command line, project directory, etc)
|
9
29
|
* Allow me to rerun a suite
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/assets/css/app.css
CHANGED
@@ -1,9 +1,35 @@
|
|
1
|
+
appstatus {
|
2
|
+
display: block;
|
3
|
+
height: 5%;
|
4
|
+
position: fixed;
|
5
|
+
top: 0;
|
6
|
+
left: 0;
|
7
|
+
right: 0;
|
8
|
+
padding: 2px;
|
9
|
+
padding-left: 30px;
|
10
|
+
background-image: url(/images/disconnect.png);
|
11
|
+
background-repeat: no-repeat;
|
12
|
+
background-position: 6px 13px;
|
13
|
+
background-color: #222;
|
14
|
+
color: white;
|
15
|
+
font-size: 1.6em;
|
16
|
+
line-height: 1.6em;
|
17
|
+
}
|
18
|
+
|
19
|
+
appstatus.backfilling {
|
20
|
+
background-image: url(/images/spinner.gif);
|
21
|
+
}
|
22
|
+
|
23
|
+
appstatus.realtime {
|
24
|
+
background-image: url(/images/connect.png);
|
25
|
+
}
|
26
|
+
|
1
27
|
testruns {
|
2
28
|
display: block;
|
3
29
|
overflow-y: scroll;
|
4
|
-
height:
|
30
|
+
height: 60%;
|
5
31
|
position: fixed;
|
6
|
-
top:
|
32
|
+
top: 6%;
|
7
33
|
left: 0;
|
8
34
|
right: 0;
|
9
35
|
}
|
Binary file
|
Binary file
|
data/assets/js/app.js
CHANGED
@@ -31,58 +31,64 @@ head.ready(function(){
|
|
31
31
|
socket.onmessage = function(jsonMessage) {
|
32
32
|
if (jsonMessage && jsonMessage.data) {
|
33
33
|
var message = $.parseJSON(jsonMessage.data);
|
34
|
+
|
35
|
+
if (_.isEqual(message, ['backfilling'])) {
|
36
|
+
$('appstatus').attr('class', 'backfilling');
|
37
|
+
} else if (_.isEqual(message,['realtime'])) {
|
38
|
+
$('appstatus').attr('class', 'realtime');
|
39
|
+
} else {
|
40
|
+
// append message to event log
|
41
|
+
message.timestamp = new Date(message.milliseconds).toLocaleTimeString();
|
42
|
+
var template = templates[message.event];
|
43
|
+
$('eventlog')
|
44
|
+
.append(Mustache.to_html(template, message))
|
45
|
+
.scrollTop($('eventlog').attr('scrollHeight') - $('eventlog').height());
|
34
46
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
.scrollTop($('eventlog').attr('scrollHeight') - $('eventlog').height());
|
41
|
-
|
42
|
-
// find or create the corresponding suite
|
43
|
-
var guid = message.guid;
|
44
|
-
if ($('#' + guid).length == 0) {
|
45
|
-
$('testruns').prepend('<testrun id=' + guid + '>');
|
46
|
-
}
|
47
|
+
// find or create the corresponding suite
|
48
|
+
var guid = message.guid;
|
49
|
+
if ($('#' + guid).length == 0) {
|
50
|
+
$('testruns').prepend('<testrun id=' + guid + '>');
|
51
|
+
}
|
47
52
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
53
|
+
// update its data based on the event
|
54
|
+
var testrun = $('#' + guid);
|
55
|
+
var testrunData = testrun.data();
|
56
|
+
if (message.event === "Test::Unit::UI::TestRunnerMediator::STARTED") {
|
57
|
+
testrunData.guid = guid;
|
58
|
+
testrunData.timestamp = new Date(message.milliseconds).toLocaleTimeString();
|
59
|
+
testrunData.status = 'Initializing';
|
60
|
+
testrunData.testCount = 0;
|
61
|
+
testrunData.failedCount = 0;
|
62
|
+
} else if (message.event === "Test::Unit::TestCase::STARTED") {
|
63
|
+
testrunData.status = 'Running';
|
64
|
+
testrunData.currentTest = message.args;
|
65
|
+
testrunData.running = true;
|
66
|
+
} else if (message.event === "FAULT") {
|
67
|
+
testrunData.failedCount += 1;
|
68
|
+
} else if (message.event === "Test::Unit::TestCase::FINISHED") {
|
69
|
+
testrunData.running = false;
|
70
|
+
testrunData.testCount += 1;
|
71
|
+
} else if (message.event === "Test::Unit::UI::TestRunnerMediator::FINISHED") {
|
72
|
+
if (testrunData.failedCount == 0) {
|
73
|
+
testrunData.status = 'Success';
|
74
|
+
} else {
|
75
|
+
testrunData.status = 'Failed';
|
76
|
+
}
|
71
77
|
}
|
72
|
-
}
|
73
78
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
79
|
+
// rerender the suite based on the updated data
|
80
|
+
testrun.html(Mustache.to_html("\
|
81
|
+
<testrunsummary class='{{status}}'> \
|
82
|
+
<started>Test Run Started {{timestamp}} - {{status}}</started> \
|
83
|
+
{{#running}} \
|
84
|
+
<currenttest>Current Test: {{currentTest}}</currenttest> \
|
85
|
+
{{/running}} \
|
86
|
+
<statistics> \
|
87
|
+
<testcount>{{testCount}} total test(s)</testcount> \
|
88
|
+
<failedcount>{{failedCount}} failure(s)</failedcount> \
|
89
|
+
</statistics> \
|
90
|
+
</testrunsummary>", testrunData));
|
91
|
+
}
|
86
92
|
}
|
87
93
|
};
|
88
94
|
});
|
@@ -0,0 +1,25 @@
|
|
1
|
+
// Underscore.js 1.1.3
|
2
|
+
// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
|
3
|
+
// Underscore is freely distributable under the MIT license.
|
4
|
+
// Portions of Underscore are inspired or borrowed from Prototype,
|
5
|
+
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
6
|
+
// For all details and documentation:
|
7
|
+
// http://documentcloud.github.com/underscore
|
8
|
+
(function(){var p=this,C=p._,m={},j=Array.prototype,n=Object.prototype,i=j.slice,D=j.unshift,E=n.toString,q=n.hasOwnProperty,s=j.forEach,t=j.map,u=j.reduce,v=j.reduceRight,w=j.filter,x=j.every,y=j.some,o=j.indexOf,z=j.lastIndexOf;n=Array.isArray;var F=Object.keys,c=function(a){return new l(a)};if(typeof module!=="undefined"&&module.exports){module.exports=c;c._=c}else p._=c;c.VERSION="1.1.3";var k=c.each=c.forEach=function(a,b,d){if(s&&a.forEach===s)a.forEach(b,d);else if(c.isNumber(a.length))for(var e=
|
9
|
+
0,f=a.length;e<f;e++){if(b.call(d,a[e],e,a)===m)break}else for(e in a)if(q.call(a,e))if(b.call(d,a[e],e,a)===m)break};c.map=function(a,b,d){if(t&&a.map===t)return a.map(b,d);var e=[];k(a,function(f,g,h){e[e.length]=b.call(d,f,g,h)});return e};c.reduce=c.foldl=c.inject=function(a,b,d,e){var f=d!==void 0;if(u&&a.reduce===u){if(e)b=c.bind(b,e);return f?a.reduce(b,d):a.reduce(b)}k(a,function(g,h,G){d=!f&&h===0?g:b.call(e,d,g,h,G)});return d};c.reduceRight=c.foldr=function(a,b,d,e){if(v&&a.reduceRight===
|
10
|
+
v){if(e)b=c.bind(b,e);return d!==void 0?a.reduceRight(b,d):a.reduceRight(b)}a=(c.isArray(a)?a.slice():c.toArray(a)).reverse();return c.reduce(a,b,d,e)};c.find=c.detect=function(a,b,d){var e;A(a,function(f,g,h){if(b.call(d,f,g,h)){e=f;return true}});return e};c.filter=c.select=function(a,b,d){if(w&&a.filter===w)return a.filter(b,d);var e=[];k(a,function(f,g,h){if(b.call(d,f,g,h))e[e.length]=f});return e};c.reject=function(a,b,d){var e=[];k(a,function(f,g,h){b.call(d,f,g,h)||(e[e.length]=f)});return e};
|
11
|
+
c.every=c.all=function(a,b,d){b=b||c.identity;if(x&&a.every===x)return a.every(b,d);var e=true;k(a,function(f,g,h){if(!(e=e&&b.call(d,f,g,h)))return m});return e};var A=c.some=c.any=function(a,b,d){b=b||c.identity;if(y&&a.some===y)return a.some(b,d);var e=false;k(a,function(f,g,h){if(e=b.call(d,f,g,h))return m});return e};c.include=c.contains=function(a,b){if(o&&a.indexOf===o)return a.indexOf(b)!=-1;var d=false;A(a,function(e){if(d=e===b)return true});return d};c.invoke=function(a,b){var d=i.call(arguments,
|
12
|
+
2);return c.map(a,function(e){return(b?e[b]:e).apply(e,d)})};c.pluck=function(a,b){return c.map(a,function(d){return d[b]})};c.max=function(a,b,d){if(!b&&c.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};k(a,function(f,g,h){g=b?b.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};c.min=function(a,b,d){if(!b&&c.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};k(a,function(f,g,h){g=b?b.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});
|
13
|
+
return e.value};c.sortBy=function(a,b,d){return c.pluck(c.map(a,function(e,f,g){return{value:e,criteria:b.call(d,e,f,g)}}).sort(function(e,f){var g=e.criteria,h=f.criteria;return g<h?-1:g>h?1:0}),"value")};c.sortedIndex=function(a,b,d){d=d||c.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(b)?e=g+1:f=g}return e};c.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(c.isArray(a))return a;if(c.isArguments(a))return i.call(a);return c.values(a)};c.size=function(a){return c.toArray(a).length};
|
14
|
+
c.first=c.head=function(a,b,d){return b&&!d?i.call(a,0,b):a[0]};c.rest=c.tail=function(a,b,d){return i.call(a,c.isUndefined(b)||d?1:b)};c.last=function(a){return a[a.length-1]};c.compact=function(a){return c.filter(a,function(b){return!!b})};c.flatten=function(a){return c.reduce(a,function(b,d){if(c.isArray(d))return b.concat(c.flatten(d));b[b.length]=d;return b},[])};c.without=function(a){var b=i.call(arguments,1);return c.filter(a,function(d){return!c.include(b,d)})};c.uniq=c.unique=function(a,
|
15
|
+
b){return c.reduce(a,function(d,e,f){if(0==f||(b===true?c.last(d)!=e:!c.include(d,e)))d[d.length]=e;return d},[])};c.intersect=function(a){var b=i.call(arguments,1);return c.filter(c.uniq(a),function(d){return c.every(b,function(e){return c.indexOf(e,d)>=0})})};c.zip=function(){for(var a=i.call(arguments),b=c.max(c.pluck(a,"length")),d=Array(b),e=0;e<b;e++)d[e]=c.pluck(a,""+e);return d};c.indexOf=function(a,b){if(o&&a.indexOf===o)return a.indexOf(b);for(var d=0,e=a.length;d<e;d++)if(a[d]===b)return d;
|
16
|
+
return-1};c.lastIndexOf=function(a,b){if(z&&a.lastIndexOf===z)return a.lastIndexOf(b);for(var d=a.length;d--;)if(a[d]===b)return d;return-1};c.range=function(a,b,d){var e=i.call(arguments),f=e.length<=1;a=f?0:e[0];b=f?e[0]:e[1];d=e[2]||1;e=Math.max(Math.ceil((b-a)/d),0);f=0;for(var g=Array(e);f<e;){g[f++]=a;a+=d}return g};c.bind=function(a,b){var d=i.call(arguments,2);return function(){return a.apply(b||{},d.concat(i.call(arguments)))}};c.bindAll=function(a){var b=i.call(arguments,1);if(b.length==
|
17
|
+
0)b=c.functions(a);k(b,function(d){a[d]=c.bind(a[d],a)});return a};c.memoize=function(a,b){var d={};b=b||c.identity;return function(){var e=b.apply(this,arguments);return e in d?d[e]:d[e]=a.apply(this,arguments)}};c.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};c.defer=function(a){return c.delay.apply(c,[a,1].concat(i.call(arguments,1)))};var B=function(a,b,d){var e;return function(){var f=this,g=arguments,h=function(){e=null;a.apply(f,g)};d&&
|
18
|
+
clearTimeout(e);if(d||!e)e=setTimeout(h,b)}};c.throttle=function(a,b){return B(a,b,false)};c.debounce=function(a,b){return B(a,b,true)};c.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments));return b.apply(b,d)}};c.compose=function(){var a=i.call(arguments);return function(){for(var b=i.call(arguments),d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};c.keys=F||function(a){if(c.isArray(a))return c.range(0,a.length);var b=[],d;for(d in a)if(q.call(a,d))b[b.length]=d;return b};
|
19
|
+
c.values=function(a){return c.map(a,c.identity)};c.functions=c.methods=function(a){return c.filter(c.keys(a),function(b){return c.isFunction(a[b])}).sort()};c.extend=function(a){k(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};c.clone=function(a){return c.isArray(a)?a.slice():c.extend({},a)};c.tap=function(a,b){b(a);return a};c.isEqual=function(a,b){if(a===b)return true;var d=typeof a;if(d!=typeof b)return false;if(a==b)return true;if(!a&&b||a&&!b)return false;if(a.isEqual)return a.isEqual(b);
|
20
|
+
if(c.isDate(a)&&c.isDate(b))return a.getTime()===b.getTime();if(c.isNaN(a)&&c.isNaN(b))return false;if(c.isRegExp(a)&&c.isRegExp(b))return a.source===b.source&&a.global===b.global&&a.ignoreCase===b.ignoreCase&&a.multiline===b.multiline;if(d!=="object")return false;if(a.length&&a.length!==b.length)return false;d=c.keys(a);var e=c.keys(b);if(d.length!=e.length)return false;for(var f in a)if(!(f in b)||!c.isEqual(a[f],b[f]))return false;return true};c.isEmpty=function(a){if(c.isArray(a)||c.isString(a))return a.length===
|
21
|
+
0;for(var b in a)if(q.call(a,b))return false;return true};c.isElement=function(a){return!!(a&&a.nodeType==1)};c.isArray=n||function(a){return!!(a&&a.concat&&a.unshift&&!a.callee)};c.isArguments=function(a){return!!(a&&a.callee)};c.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};c.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};c.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};c.isNaN=function(a){return E.call(a)==="[object Number]"&&isNaN(a)};
|
22
|
+
c.isBoolean=function(a){return a===true||a===false};c.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};c.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};c.isNull=function(a){return a===null};c.isUndefined=function(a){return a===void 0};c.noConflict=function(){p._=C;return this};c.identity=function(a){return a};c.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};c.mixin=function(a){k(c.functions(a),function(b){H(b,c[b]=a[b])})};var I=
|
23
|
+
0;c.uniqueId=function(a){var b=I++;return a?a+b:b};c.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g};c.template=function(a,b){var d=c.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(e,f){return"',"+f.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(e,f){return"');"+f.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,
|
24
|
+
"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return b?d(b):d};var l=function(a){this._wrapped=a};c.prototype=l.prototype;var r=function(a,b){return b?c(a).chain():a},H=function(a,b){l.prototype[a]=function(){var d=i.call(arguments);D.call(d,this._wrapped);return r(b.apply(c,d),this._chain)}};c.mixin(c);k(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=j[a];l.prototype[a]=function(){b.apply(this._wrapped,arguments);
|
25
|
+
return r(this._wrapped,this._chain)}});k(["concat","join","slice"],function(a){var b=j[a];l.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}})();
|
@@ -0,0 +1,16 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title
|
5
|
+
LabBench Test Status
|
6
|
+
%link{ :href => css('app'), :rel => "stylesheet" }
|
7
|
+
%script{ :src => js('head.min') }
|
8
|
+
%body
|
9
|
+
%appstatus LabBench
|
10
|
+
%testruns
|
11
|
+
%eventlog
|
12
|
+
%script{ :type => "text/javascript" }
|
13
|
+
head.js("http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js");
|
14
|
+
head.js("#{js('mustache')}");
|
15
|
+
head.js("#{js('underscore-min')}");
|
16
|
+
head.js("#{js('app')}");
|
data/bin/lab_bench_server
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'lab_bench'
|
5
5
|
|
6
|
+
require 'haml'
|
6
7
|
require 'eventmachine'
|
7
8
|
require 'em-websocket'
|
8
9
|
require 'sinatra/base'
|
@@ -10,22 +11,36 @@ require 'thin'
|
|
10
11
|
require 'yajl'
|
11
12
|
|
12
13
|
SOCKETS = []
|
14
|
+
MESSAGES = [] # TODO this is unbounded
|
13
15
|
|
14
16
|
class LabBenchServer < Sinatra::Base
|
15
17
|
set :public, LabBench::ASSETS
|
18
|
+
set :views, File.join(LabBench::ASSETS, 'views')
|
19
|
+
|
20
|
+
helpers do
|
21
|
+
def css(css)
|
22
|
+
"/css/#{css}.css?" + File.mtime(File.join(LabBench::ASSETS, "css", "#{css}.css")).to_i.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def js(js)
|
26
|
+
"/js/#{js}.js?" + File.mtime(File.join(LabBench::ASSETS, "js", "#{js}.js")).to_i.to_s
|
27
|
+
end
|
28
|
+
end
|
16
29
|
|
17
30
|
get '/' do
|
18
|
-
|
31
|
+
haml :index
|
19
32
|
end
|
20
33
|
|
21
|
-
# lets the
|
34
|
+
# lets the TestRunner ensure that we're up and running
|
22
35
|
get '/ping' do
|
23
36
|
'Pong!'
|
24
37
|
end
|
25
38
|
|
26
|
-
# give the
|
39
|
+
# give the TestRunner a chance to post events, which we cache and rebrooadcase
|
27
40
|
post '/test_event' do
|
28
41
|
message = Yajl::Encoder.encode(params.merge(:milliseconds => (Time.now.to_f * 1000).to_i))
|
42
|
+
MESSAGES << message
|
43
|
+
|
29
44
|
SOCKETS.each { |s| s.send message }
|
30
45
|
|
31
46
|
# should vend back a UID for this test run or something
|
@@ -38,6 +53,13 @@ EventMachine.run do
|
|
38
53
|
# HACK - websockets and HTTP on different (adjacent) ports
|
39
54
|
EventMachine::WebSocket.start(:host => '0.0.0.0', :port => 9021) do |ws|
|
40
55
|
ws.onopen do
|
56
|
+
# backfill old messages to the client
|
57
|
+
# TODO is this blocking? I would guess no...
|
58
|
+
ws.send Yajl::Encoder.encode(['backfilling'])
|
59
|
+
MESSAGES.each { |message| ws.send(message) }
|
60
|
+
|
61
|
+
# put the client in the list of real-time recipients
|
62
|
+
ws.send Yajl::Encoder.encode(['realtime'])
|
41
63
|
SOCKETS << ws
|
42
64
|
end
|
43
65
|
|
@@ -45,7 +67,7 @@ EventMachine.run do
|
|
45
67
|
SOCKETS.delete(ws)
|
46
68
|
end
|
47
69
|
|
48
|
-
ws.onerror
|
70
|
+
ws.onerror { |e| puts "Error: #{e.message}" }
|
49
71
|
end
|
50
72
|
|
51
73
|
LabBenchServer.run!(:host => '0.0.0.0', :port => 9020)
|
data/lab_bench.gemspec
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{lab_bench}
|
8
|
+
s.version = "0.2.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Bradley Buda"]
|
12
|
+
s.date = %q{2010-12-03}
|
13
|
+
s.default_executable = %q{lab_bench_server}
|
14
|
+
s.description = %q{Like autotest, but in your browser. Currently unsuitable for use by anyone.}
|
15
|
+
s.email = %q{bradleybuda@gmail.com}
|
16
|
+
s.executables = ["lab_bench_server"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE.txt",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".rvmrc",
|
24
|
+
"Gemfile",
|
25
|
+
"Gemfile.lock",
|
26
|
+
"LICENSE.txt",
|
27
|
+
"README.rdoc",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"assets/css/app.css",
|
31
|
+
"assets/images/connect.png",
|
32
|
+
"assets/images/disconnect.png",
|
33
|
+
"assets/images/spinner.gif",
|
34
|
+
"assets/js/app.js",
|
35
|
+
"assets/js/head.min.js",
|
36
|
+
"assets/js/mustache.js",
|
37
|
+
"assets/js/underscore-min.js",
|
38
|
+
"assets/views/index.haml",
|
39
|
+
"bin/lab_bench_server",
|
40
|
+
"lab_bench.gemspec",
|
41
|
+
"lib/lab_bench.rb",
|
42
|
+
"lib/lab_bench/test_runner.rb",
|
43
|
+
"test/helper.rb",
|
44
|
+
"test/test_lab_bench.rb"
|
45
|
+
]
|
46
|
+
s.homepage = %q{http://github.com/bradleybuda/lab_bench}
|
47
|
+
s.licenses = ["MIT"]
|
48
|
+
s.require_paths = ["lib"]
|
49
|
+
s.rubygems_version = %q{1.3.7}
|
50
|
+
s.summary = %q{A browser-based runner for Test::Unit}
|
51
|
+
s.test_files = [
|
52
|
+
"test/helper.rb",
|
53
|
+
"test/test_lab_bench.rb"
|
54
|
+
]
|
55
|
+
|
56
|
+
if s.respond_to? :specification_version then
|
57
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
58
|
+
s.specification_version = 3
|
59
|
+
|
60
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
61
|
+
s.add_runtime_dependency(%q<haml>, ["~> 2.2.0"])
|
62
|
+
s.add_runtime_dependency(%q<sinatra>, ["~> 1.1.0"])
|
63
|
+
s.add_runtime_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
64
|
+
s.add_runtime_dependency(%q<em-websocket>, ["~> 0.2.0"])
|
65
|
+
s.add_runtime_dependency(%q<thin>, ["~> 1.2.0"])
|
66
|
+
s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
|
67
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
69
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
|
70
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
71
|
+
s.add_development_dependency(%q<hpricot>, [">= 0"])
|
72
|
+
s.add_runtime_dependency(%q<sinatra>, ["~> 1.1.0"])
|
73
|
+
s.add_runtime_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
74
|
+
s.add_runtime_dependency(%q<em-websocket>, ["~> 0.2.0"])
|
75
|
+
s.add_runtime_dependency(%q<thin>, ["~> 1.2.0"])
|
76
|
+
s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
|
77
|
+
else
|
78
|
+
s.add_dependency(%q<haml>, ["~> 2.2.0"])
|
79
|
+
s.add_dependency(%q<sinatra>, ["~> 1.1.0"])
|
80
|
+
s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
81
|
+
s.add_dependency(%q<em-websocket>, ["~> 0.2.0"])
|
82
|
+
s.add_dependency(%q<thin>, ["~> 1.2.0"])
|
83
|
+
s.add_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
|
84
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
85
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
86
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
87
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
88
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
89
|
+
s.add_dependency(%q<sinatra>, ["~> 1.1.0"])
|
90
|
+
s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
91
|
+
s.add_dependency(%q<em-websocket>, ["~> 0.2.0"])
|
92
|
+
s.add_dependency(%q<thin>, ["~> 1.2.0"])
|
93
|
+
s.add_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
|
94
|
+
end
|
95
|
+
else
|
96
|
+
s.add_dependency(%q<haml>, ["~> 2.2.0"])
|
97
|
+
s.add_dependency(%q<sinatra>, ["~> 1.1.0"])
|
98
|
+
s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
99
|
+
s.add_dependency(%q<em-websocket>, ["~> 0.2.0"])
|
100
|
+
s.add_dependency(%q<thin>, ["~> 1.2.0"])
|
101
|
+
s.add_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
|
102
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
103
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
104
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
105
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
106
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
107
|
+
s.add_dependency(%q<sinatra>, ["~> 1.1.0"])
|
108
|
+
s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
|
109
|
+
s.add_dependency(%q<em-websocket>, ["~> 0.2.0"])
|
110
|
+
s.add_dependency(%q<thin>, ["~> 1.2.0"])
|
111
|
+
s.add_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lab_bench
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 1
|
10
|
+
version: 0.2.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Bradley Buda
|
@@ -18,11 +18,107 @@ cert_chain: []
|
|
18
18
|
date: 2010-12-03 00:00:00 -08:00
|
19
19
|
default_executable: lab_bench_server
|
20
20
|
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
type: :runtime
|
24
|
+
name: haml
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 7
|
31
|
+
segments:
|
32
|
+
- 2
|
33
|
+
- 2
|
34
|
+
- 0
|
35
|
+
version: 2.2.0
|
36
|
+
requirement: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
prerelease: false
|
39
|
+
type: :runtime
|
40
|
+
name: sinatra
|
41
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 19
|
47
|
+
segments:
|
48
|
+
- 1
|
49
|
+
- 1
|
50
|
+
- 0
|
51
|
+
version: 1.1.0
|
52
|
+
requirement: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
prerelease: false
|
55
|
+
type: :runtime
|
56
|
+
name: eventmachine
|
57
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 59
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
- 12
|
66
|
+
- 10
|
67
|
+
version: 0.12.10
|
68
|
+
requirement: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
prerelease: false
|
71
|
+
type: :runtime
|
72
|
+
name: em-websocket
|
73
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 23
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
- 2
|
82
|
+
- 0
|
83
|
+
version: 0.2.0
|
84
|
+
requirement: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
prerelease: false
|
87
|
+
type: :runtime
|
88
|
+
name: thin
|
89
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ~>
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 31
|
95
|
+
segments:
|
96
|
+
- 1
|
97
|
+
- 2
|
98
|
+
- 0
|
99
|
+
version: 1.2.0
|
100
|
+
requirement: *id005
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
prerelease: false
|
103
|
+
type: :runtime
|
104
|
+
name: yajl-ruby
|
105
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
hash: 19
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
- 7
|
114
|
+
- 8
|
115
|
+
version: 0.7.8
|
116
|
+
requirement: *id006
|
21
117
|
- !ruby/object:Gem::Dependency
|
22
118
|
prerelease: false
|
23
119
|
type: :development
|
24
120
|
name: shoulda
|
25
|
-
version_requirements: &
|
121
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
26
122
|
none: false
|
27
123
|
requirements:
|
28
124
|
- - ">="
|
@@ -31,12 +127,12 @@ dependencies:
|
|
31
127
|
segments:
|
32
128
|
- 0
|
33
129
|
version: "0"
|
34
|
-
requirement: *
|
130
|
+
requirement: *id007
|
35
131
|
- !ruby/object:Gem::Dependency
|
36
132
|
prerelease: false
|
37
133
|
type: :development
|
38
134
|
name: bundler
|
39
|
-
version_requirements: &
|
135
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
40
136
|
none: false
|
41
137
|
requirements:
|
42
138
|
- - ~>
|
@@ -47,12 +143,12 @@ dependencies:
|
|
47
143
|
- 0
|
48
144
|
- 0
|
49
145
|
version: 1.0.0
|
50
|
-
requirement: *
|
146
|
+
requirement: *id008
|
51
147
|
- !ruby/object:Gem::Dependency
|
52
148
|
prerelease: false
|
53
149
|
type: :development
|
54
150
|
name: jeweler
|
55
|
-
version_requirements: &
|
151
|
+
version_requirements: &id009 !ruby/object:Gem::Requirement
|
56
152
|
none: false
|
57
153
|
requirements:
|
58
154
|
- - ~>
|
@@ -63,12 +159,12 @@ dependencies:
|
|
63
159
|
- 5
|
64
160
|
- 1
|
65
161
|
version: 1.5.1
|
66
|
-
requirement: *
|
162
|
+
requirement: *id009
|
67
163
|
- !ruby/object:Gem::Dependency
|
68
164
|
prerelease: false
|
69
165
|
type: :development
|
70
166
|
name: rcov
|
71
|
-
version_requirements: &
|
167
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
72
168
|
none: false
|
73
169
|
requirements:
|
74
170
|
- - ">="
|
@@ -77,12 +173,26 @@ dependencies:
|
|
77
173
|
segments:
|
78
174
|
- 0
|
79
175
|
version: "0"
|
80
|
-
requirement: *
|
176
|
+
requirement: *id010
|
177
|
+
- !ruby/object:Gem::Dependency
|
178
|
+
prerelease: false
|
179
|
+
type: :development
|
180
|
+
name: hpricot
|
181
|
+
version_requirements: &id011 !ruby/object:Gem::Requirement
|
182
|
+
none: false
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
hash: 3
|
187
|
+
segments:
|
188
|
+
- 0
|
189
|
+
version: "0"
|
190
|
+
requirement: *id011
|
81
191
|
- !ruby/object:Gem::Dependency
|
82
192
|
prerelease: false
|
83
193
|
type: :runtime
|
84
194
|
name: sinatra
|
85
|
-
version_requirements: &
|
195
|
+
version_requirements: &id012 !ruby/object:Gem::Requirement
|
86
196
|
none: false
|
87
197
|
requirements:
|
88
198
|
- - ~>
|
@@ -93,12 +203,12 @@ dependencies:
|
|
93
203
|
- 1
|
94
204
|
- 0
|
95
205
|
version: 1.1.0
|
96
|
-
requirement: *
|
206
|
+
requirement: *id012
|
97
207
|
- !ruby/object:Gem::Dependency
|
98
208
|
prerelease: false
|
99
209
|
type: :runtime
|
100
210
|
name: eventmachine
|
101
|
-
version_requirements: &
|
211
|
+
version_requirements: &id013 !ruby/object:Gem::Requirement
|
102
212
|
none: false
|
103
213
|
requirements:
|
104
214
|
- - ~>
|
@@ -109,12 +219,12 @@ dependencies:
|
|
109
219
|
- 12
|
110
220
|
- 10
|
111
221
|
version: 0.12.10
|
112
|
-
requirement: *
|
222
|
+
requirement: *id013
|
113
223
|
- !ruby/object:Gem::Dependency
|
114
224
|
prerelease: false
|
115
225
|
type: :runtime
|
116
226
|
name: em-websocket
|
117
|
-
version_requirements: &
|
227
|
+
version_requirements: &id014 !ruby/object:Gem::Requirement
|
118
228
|
none: false
|
119
229
|
requirements:
|
120
230
|
- - ~>
|
@@ -125,12 +235,12 @@ dependencies:
|
|
125
235
|
- 2
|
126
236
|
- 0
|
127
237
|
version: 0.2.0
|
128
|
-
requirement: *
|
238
|
+
requirement: *id014
|
129
239
|
- !ruby/object:Gem::Dependency
|
130
240
|
prerelease: false
|
131
241
|
type: :runtime
|
132
242
|
name: thin
|
133
|
-
version_requirements: &
|
243
|
+
version_requirements: &id015 !ruby/object:Gem::Requirement
|
134
244
|
none: false
|
135
245
|
requirements:
|
136
246
|
- - ~>
|
@@ -141,12 +251,12 @@ dependencies:
|
|
141
251
|
- 2
|
142
252
|
- 0
|
143
253
|
version: 1.2.0
|
144
|
-
requirement: *
|
254
|
+
requirement: *id015
|
145
255
|
- !ruby/object:Gem::Dependency
|
146
256
|
prerelease: false
|
147
257
|
type: :runtime
|
148
258
|
name: yajl-ruby
|
149
|
-
version_requirements: &
|
259
|
+
version_requirements: &id016 !ruby/object:Gem::Requirement
|
150
260
|
none: false
|
151
261
|
requirements:
|
152
262
|
- - ~>
|
@@ -157,7 +267,7 @@ dependencies:
|
|
157
267
|
- 7
|
158
268
|
- 8
|
159
269
|
version: 0.7.8
|
160
|
-
requirement: *
|
270
|
+
requirement: *id016
|
161
271
|
description: Like autotest, but in your browser. Currently unsuitable for use by anyone.
|
162
272
|
email: bradleybuda@gmail.com
|
163
273
|
executables:
|
@@ -177,12 +287,16 @@ files:
|
|
177
287
|
- Rakefile
|
178
288
|
- VERSION
|
179
289
|
- assets/css/app.css
|
290
|
+
- assets/images/connect.png
|
291
|
+
- assets/images/disconnect.png
|
180
292
|
- assets/images/spinner.gif
|
181
|
-
- assets/index.html
|
182
293
|
- assets/js/app.js
|
183
294
|
- assets/js/head.min.js
|
184
295
|
- assets/js/mustache.js
|
296
|
+
- assets/js/underscore-min.js
|
297
|
+
- assets/views/index.haml
|
185
298
|
- bin/lab_bench_server
|
299
|
+
- lab_bench.gemspec
|
186
300
|
- lib/lab_bench.rb
|
187
301
|
- lib/lab_bench/test_runner.rb
|
188
302
|
- test/helper.rb
|
data/assets/index.html
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
<!DOCTYPE HTML>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>LabBench Test Status</title>
|
5
|
-
<link rel="stylesheet" href="/css/app.css">
|
6
|
-
<script src="/js/head.min.js"></script>
|
7
|
-
</head>
|
8
|
-
<body>
|
9
|
-
<testruns></testruns>
|
10
|
-
<eventlog></eventlog>
|
11
|
-
|
12
|
-
<script type='text/javascript'>
|
13
|
-
head.js("http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js");
|
14
|
-
head.js("/js/mustache.js");
|
15
|
-
head.js("/js/app.js");
|
16
|
-
</script>
|
17
|
-
</body>
|
18
|
-
</html>
|