lab_bench 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
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.0
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: 65%;
30
+ height: 60%;
5
31
  position: fixed;
6
- top: 0;
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
- // append message to event log
36
- message.timestamp = new Date(message.milliseconds).toLocaleTimeString();
37
- var template = templates[message.event];
38
- $('eventlog')
39
- .append(Mustache.to_html(template, message))
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
- // update its data based on the event
49
- var testrun = $('#' + guid);
50
- var testrunData = testrun.data();
51
- if (message.event === "Test::Unit::UI::TestRunnerMediator::STARTED") {
52
- testrunData.guid = guid;
53
- testrunData.timestamp = new Date(message.milliseconds).toLocaleTimeString();
54
- testrunData.status = 'Initializing';
55
- testrunData.testCount = 0;
56
- testrunData.failedCount = 0;
57
- } else if (message.event === "Test::Unit::TestCase::STARTED") {
58
- testrunData.status = 'Running';
59
- testrunData.currentTest = message.args;
60
- testrunData.running = true;
61
- } else if (message.event === "FAULT") {
62
- testrunData.failedCount += 1;
63
- } else if (message.event === "Test::Unit::TestCase::FINISHED") {
64
- testrunData.running = false;
65
- testrunData.testCount += 1;
66
- } else if (message.event === "Test::Unit::UI::TestRunnerMediator::FINISHED") {
67
- if (testrunData.failedCount == 0) {
68
- testrunData.status = 'Success';
69
- } else {
70
- testrunData.status = 'Failed';
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
- // rerender the suite based on the updated data
75
- testrun.html(Mustache.to_html("\
76
- <testrunsummary class='{{status}}'> \
77
- <started>Test Run Started {{timestamp}} - {{status}}</started> \
78
- {{#running}} \
79
- <currenttest>Current Test: {{currentTest}}</currenttest> \
80
- {{/running}} \
81
- <statistics> \
82
- <testcount>{{testCount}} total test(s)</testcount> \
83
- <failedcount>{{failedCount}} failure(s)</failedcount> \
84
- </statistics> \
85
- </testrunsummary>", testrunData));
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
- redirect '/index.html'
31
+ haml :index
19
32
  end
20
33
 
21
- # lets the babyship ensure that we're up and running
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 babyship a chance to post events, that we just rebroadcast
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 { |e| puts "Error: #{e.message}" }
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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 0
10
- version: 0.2.0
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: &id001 !ruby/object:Gem::Requirement
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: *id001
130
+ requirement: *id007
35
131
  - !ruby/object:Gem::Dependency
36
132
  prerelease: false
37
133
  type: :development
38
134
  name: bundler
39
- version_requirements: &id002 !ruby/object:Gem::Requirement
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: *id002
146
+ requirement: *id008
51
147
  - !ruby/object:Gem::Dependency
52
148
  prerelease: false
53
149
  type: :development
54
150
  name: jeweler
55
- version_requirements: &id003 !ruby/object:Gem::Requirement
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: *id003
162
+ requirement: *id009
67
163
  - !ruby/object:Gem::Dependency
68
164
  prerelease: false
69
165
  type: :development
70
166
  name: rcov
71
- version_requirements: &id004 !ruby/object:Gem::Requirement
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: *id004
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: &id005 !ruby/object:Gem::Requirement
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: *id005
206
+ requirement: *id012
97
207
  - !ruby/object:Gem::Dependency
98
208
  prerelease: false
99
209
  type: :runtime
100
210
  name: eventmachine
101
- version_requirements: &id006 !ruby/object:Gem::Requirement
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: *id006
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: &id007 !ruby/object:Gem::Requirement
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: *id007
238
+ requirement: *id014
129
239
  - !ruby/object:Gem::Dependency
130
240
  prerelease: false
131
241
  type: :runtime
132
242
  name: thin
133
- version_requirements: &id008 !ruby/object:Gem::Requirement
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: *id008
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: &id009 !ruby/object:Gem::Requirement
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: *id009
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>