spec_run_queue 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in spec_run_queue.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,44 @@
1
+ WHAT
2
+ ====
3
+
4
+ spec_run_queue lets you build a lightweight queue watcher to watch for commands to run specs for your project.
5
+
6
+ On projects with a slow test suite, I don't like waiting for an autotest-like setup to complete a full spec run of a file. Also, I don't like watching my editor lock up waiting for a spec run to finish.
7
+
8
+ My development flow on such files tends to be 1) write a new spec example 2) run a focused example 3) repeat a few times 4) run the full spec
9
+
10
+ The runner works by doing a blocking queue read, until it receives a YAML dump of a hash like
11
+
12
+ { :target => "spec/models/foo_spec.rb" }
13
+
14
+ or
15
+
16
+ { :target => "spec/models/bar_spec.rb", :line => 42 }
17
+
18
+ and then runs the command, via backticks, in the shell. Summary results from the run are sent to the Notifier#notify method for notifiers you have setup.
19
+
20
+ NOTIFIERS
21
+ =========
22
+
23
+ Notifiers must provide at least one instane method, <tt>notify</tt>, which processes the output from the spec run. Currently, there are two runners, Stdout, and Growl. Stdout prints the results from the run to the terminal window, while growl sends a brief pass/fail summary to growl.
24
+
25
+
26
+ USAGE
27
+ =====
28
+
29
+ In your project root, run
30
+
31
+ redis_runner [1|2]
32
+
33
+ where 1 or 2 indicates the major version of rspec you are using for the project.
34
+
35
+
36
+ In order to trigger a queue run, you need your editor to insert a YAML dump of the instructions described above to the queue.
37
+
38
+
39
+ TODO
40
+ ====
41
+
42
+ * Genericify and configure the runner
43
+ * Include code or a plugin for the vim script I'm using to inject instructions into the queue
44
+ * Investigate a custom rspec runner in place of the current shell execution method
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/redis_runner ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #TODO make this more generic and configurable
4
+
5
+ require 'spec_run_queue'
6
+ require 'spec_run_queue/queue/redis'
7
+ require 'spec_run_queue/notifier/growl'
8
+ require 'spec_run_queue/notifier/stdout'
9
+
10
+ rspec_major_version = ARGV.shift
11
+ unless rspec_major_version
12
+ $stderr.puts "please specify 1 or 2 for rspec version"
13
+ exit -1
14
+ end
15
+
16
+ runner = SpecRunQueue::SystemRunner.new(rspec_major_version)
17
+ runner.add_notifier SpecRunQueue::Notifier::Growl.new(:password => "gr0wl")
18
+ runner.add_notifier SpecRunQueue::Notifier::Stdout.new()
19
+
20
+ queue = SpecRunQueue::Queue::Redis.new(runner)
21
+ queue.run
22
+
@@ -0,0 +1,21 @@
1
+ module SpecRunQueue
2
+ module Notifier
3
+ class Base
4
+ attr_reader :config, :message
5
+
6
+ def initialize(config = {})
7
+ @config = config
8
+ end
9
+
10
+ def notify(message, options = {})
11
+ @message = message
12
+ end
13
+
14
+ private
15
+
16
+ def failed?
17
+ message !~ /\s+0\s+failure/
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ module SpecRunQueue
2
+ module Notifier
3
+ class Growl < Base
4
+ def notify(message, options = {})
5
+ super
6
+ message = options[:use_full_message] ? message : short_message
7
+ growl.notify "rspec-growl Notification", "rspec-growl", message, priority
8
+ end
9
+
10
+ private
11
+
12
+ def priority
13
+ failed? ? fail_priority : pass_priority
14
+ end
15
+
16
+ def fail_priority
17
+ config[:fail_priority] || 1
18
+ end
19
+
20
+ def pass_priority
21
+ config[:pass_priority] || -2
22
+ end
23
+
24
+ def growl
25
+ @growl ||= Growl.new "127.0.0.1", "rspec-growl", ["rspec-growl Notification"], nil, config[:password]
26
+ end
27
+
28
+ def short_message
29
+ if message =~ /0 failure/
30
+ "specs passed"
31
+ elsif message =~ /(\d+\s+failures?)/
32
+ $1
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ module SpecRunQueue
2
+ module Notifier
3
+ class Stdout < Base
4
+ def notify(message, options = {})
5
+ super
6
+ puts message
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module SpecRunQueue
2
+ module Queue
3
+ class Base
4
+ attr_reader :runner
5
+
6
+ def initialize(runner)
7
+ @runner = runner
8
+ connect
9
+ reset_queue
10
+ end
11
+
12
+ def run
13
+ while (raw_instruction = queue_fetch)
14
+ instruction = YAML.load(raw_instruction[1])
15
+ runner.run_spec(instruction)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def connect
22
+ raise "Abstract method"
23
+ end
24
+
25
+ def reset_queue
26
+ raise "Abstract method"
27
+ end
28
+
29
+ def queue_fetch
30
+ raise "Abstract method"
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,27 @@
1
+ require 'redis'
2
+
3
+ module SpecRunQueue
4
+ module Queue
5
+ class Redis < Base
6
+ attr_reader :redis
7
+
8
+ def self.queue_key
9
+ "rspec"
10
+ end
11
+
12
+ private
13
+
14
+ def connect
15
+ @redis = ::Redis.new
16
+ end
17
+
18
+ def reset_queue
19
+ redis.del self.class.queue_key
20
+ end
21
+
22
+ def queue_fetch
23
+ redis.blpop self.class.queue_key, 0
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
1
+ module SpecRunQueue
2
+ class SystemRunner
3
+ attr_reader :version, :options, :notifiers
4
+
5
+ def initialize(version, options = {})
6
+ @version = version.to_i
7
+ @options = options
8
+ end
9
+
10
+ def rspec_bin
11
+ if version == 1
12
+ options[:spec_bin] || "spec"
13
+ elsif version == 2
14
+ options[:rspec_bin] || "rspec"
15
+ else
16
+ raise "Couldn't determine rspec bin'"
17
+ end
18
+ end
19
+
20
+ def add_notifier(notifier)
21
+ @notifiers ||= []
22
+ @notifiers << notifier
23
+ end
24
+
25
+ def run_spec(instruction)
26
+ unless sane_instruction?(instruction)
27
+ $stderr.puts "Insane instruction #{instruction.inspect}"
28
+ return
29
+ end
30
+
31
+ begin
32
+ cmd = "#{rspec_bin} -f progress --drb"
33
+ cmd << " -l #{instruction[:line]}" if instruction[:line]
34
+ cmd << " #{instruction[:target]}"
35
+ output = run_cmd(cmd)
36
+
37
+ output_to_notifiers(output)
38
+ rescue => e
39
+ shutdown_message = "Exception #{e.class} occurred, shutting down"
40
+ output_to_notifiers(shutdown_message, :use_full_message => true)
41
+ sleep(1)
42
+ raise e
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def run_cmd(cmd)
49
+ `#{cmd}`
50
+ end
51
+
52
+ def output_to_notifiers(output, options = {})
53
+ notifiers.each do |notifier|
54
+ notifier.notify(output, options)
55
+ end
56
+ end
57
+
58
+ def sane_instruction?(instruction)
59
+ # Line must be numeric or nil, and the target must be a path to a spec file
60
+ (instruction[:line].nil? || instruction[:line].to_s =~ /^\d+$/) &&
61
+ (instruction[:target] =~ /^[a-z0-9_\-\/]+_spec\.rb$/)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module SpecRunQueue
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require 'ruby-growl'
4
+
5
+ module SpecRunQueue
6
+ end
7
+
8
+ require 'spec_run_queue/queue/base'
9
+ require 'spec_run_queue/notifier/base'
10
+ require 'spec_run_queue/system_runner'
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ class FooNotifier < SpecRunQueue::Notifier::Base
4
+ def notify(message, options = {})
5
+ super
6
+ some_callout unless failed?
7
+ end
8
+
9
+ def some_callout
10
+ end
11
+ end
12
+
13
+ describe "Base" do
14
+ let(:notifier) { @notifier ||= FooNotifier.new() }
15
+
16
+ before(:each) do
17
+ @foo_notifier = FooNotifier.new
18
+ end
19
+
20
+ it "should store the config" do
21
+ config = { :foo => :bar }
22
+ notifier = FooNotifier.new(config)
23
+ notifier.config.should == config
24
+ end
25
+
26
+ it "should be considered failed if the message doesn't match '0 failure'" do
27
+ notifier.should_not_receive(:some_callout)
28
+ notifier.notify('13 examples, 2 failures')
29
+ end
30
+
31
+ it "should not be consider failed if the message doesn't match '0 failure'" do
32
+ notifier.should_receive(:some_callout)
33
+ notifier.notify('13 examples, 0 failures')
34
+ end
35
+
36
+ describe "notify" do
37
+ it "should assign the message" do
38
+ message = '13 examples, 0 failures'
39
+ notifier.notify(message)
40
+ notifier.message.should == message
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'spec_run_queue/notifier/growl'
3
+
4
+ describe SpecRunQueue::Notifier::Growl do
5
+ before(:each) do
6
+ @message = "13 examples, 0 failures"
7
+ @fail_message = "12 examples, 1 failure"
8
+ @growl_mock = mock("Growl", :notify => true)
9
+ @notifier = SpecRunQueue::Notifier::Growl.new(:password => "secret")
10
+ @notifier.stub!(:growl).and_return(@growl_mock)
11
+ end
12
+
13
+ describe "notify" do
14
+ it "should assign the message" do
15
+ @notifier.notify(@message)
16
+ @notifier.message.should == @message
17
+ end
18
+
19
+ context "with a failure message" do
20
+ it "should send growl the expected message" do
21
+ @growl_mock.should_receive(:notify).with('rspec-growl Notification', 'rspec-growl', '1 failure', 1)
22
+ @notifier.notify(@fail_message)
23
+ end
24
+ end
25
+
26
+ context "with a passing message" do
27
+ it "should send growl the expected message" do
28
+ @growl_mock.should_receive(:notify).with('rspec-growl Notification', 'rspec-growl', 'specs passed', -2)
29
+ @notifier.notify(@message)
30
+ end
31
+ end
32
+
33
+ context "when instruction to use the full message" do
34
+ it "should send growl the full message" do
35
+ @growl_mock.should_receive(:notify).with('rspec-growl Notification', 'rspec-growl', 'the full message', 1)
36
+ @notifier.notify("the full message", :use_full_message => true)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'spec_run_queue/notifier/stdout'
3
+
4
+ describe SpecRunQueue::Notifier::Stdout do
5
+ before(:each) do
6
+ @message = "spec message"
7
+ @notifier = SpecRunQueue::Notifier::Stdout.new
8
+ end
9
+
10
+ describe "notify" do
11
+
12
+ it "should assign the message" do
13
+ @notifier.notify(@message)
14
+ @notifier.message.should == @message
15
+ end
16
+
17
+ it "should puts the message" do
18
+ @notifier.should_receive(:puts).with(@message)
19
+ @notifier.notify(@message)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ class SpecBase < SpecRunQueue::Queue::Base
4
+ attr_accessor :connect_called, :reset_queue_called
5
+
6
+ def connect
7
+ self.connect_called = true
8
+ end
9
+
10
+ def reset_queue
11
+ self.reset_queue_called = true
12
+ end
13
+ end
14
+
15
+ describe "Base" do
16
+ let(:runner) { @runner ||= mock("Runner", :run_spec => true) }
17
+ let(:spec_base) { @spec_base ||= SpecBase.new(runner) }
18
+
19
+ describe "initilization" do
20
+ it "should set the runner" do
21
+ spec_base.runner.should == runner
22
+ end
23
+
24
+ it "should call to connect" do
25
+ spec_base.connect_called.should be_true
26
+ end
27
+
28
+ it "should reset the queue" do
29
+ spec_base.reset_queue_called.should be_true
30
+ end
31
+ end
32
+
33
+ describe "running of the queue" do
34
+ before(:each) do
35
+ @first_raw_instruction = YAML.dump({ :target => "foo_spec.rb" })
36
+ @second_raw_instruction = YAML.dump({ :target => "bar_spec.rb", :line => 42 })
37
+ end
38
+
39
+ it "should fetch raw instructions from the queue" do
40
+ spec_base.should_receive(:queue_fetch).and_return(nil)
41
+ spec_base.run
42
+ end
43
+
44
+ it "should parse each raw instruction as yaml" do
45
+ spec_base.stub!(:queue_fetch).and_return(["rspec", @first_raw_instruction], ["rspec", @second_raw_instruction], nil)
46
+ YAML.should_receive(:load).with(@first_raw_instruction).ordered
47
+ YAML.should_receive(:load).with(@second_raw_instruction).ordered
48
+ spec_base.run
49
+ end
50
+
51
+ it "should pass the parsed instruction to runner.run_spec" do
52
+ spec_base.stub!(:queue_fetch).and_return(["rspec", @first_raw_instruction], ["rspec", @second_raw_instruction], nil)
53
+ runner.should_receive(:run_spec).with({ :target => "foo_spec.rb" }).ordered
54
+ runner.should_receive(:run_spec).with({ :target => "bar_spec.rb", :line => 42 }).ordered
55
+ spec_base.run
56
+ end
57
+ end
58
+ end
59
+
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'spec_run_queue/queue/redis'
3
+
4
+ describe SpecRunQueue::Queue::Redis do
5
+ before(:each) do
6
+ @redis_mock = mock("Redis", :del => true, :blpop => true)
7
+ ::Redis.stub!(:new).and_return(@redis_mock)
8
+ @runner_mock = mock("Runner")
9
+ @redis_queue = SpecRunQueue::Queue::Redis.new(@runner_mock)
10
+ end
11
+
12
+ it "should have 'rspec' as the key" do
13
+ SpecRunQueue::Queue::Redis.queue_key.should == "rspec"
14
+ end
15
+
16
+ it "should reset the queue by deleting the queue key" do
17
+ @redis_mock.should_receive(:del).with("rspec")
18
+ SpecRunQueue::Queue::Redis.new(@runner_mock)
19
+ end
20
+
21
+ it "should blpop instructions off the redis queue" do
22
+ @redis_mock.should_receive(:blpop).with("rspec", 0).and_return(nil)
23
+ @redis_queue.run
24
+ end
25
+ end
@@ -0,0 +1 @@
1
+ require "spec_run_queue"
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpecRunQueue::SystemRunner do
4
+ let(:foo_notifier) { @foo_notifier ||= mock("FooNotifier", :notify => true) }
5
+ let(:bar_notifier) { @bar_notifier ||= mock("BarNotifier", :notify => true) }
6
+ let(:runner) { @runner ||=
7
+ begin
8
+ runner = SpecRunQueue::SystemRunner.new(1)
9
+ runner.add_notifier(foo_notifier)
10
+ runner.add_notifier(bar_notifier)
11
+ runner
12
+ end
13
+ }
14
+
15
+ describe "initializing" do
16
+ it "should set the version" do
17
+ runner = SpecRunQueue::SystemRunner.new(1, { :password => "foo" })
18
+ runner.version.should == 1
19
+ end
20
+
21
+ it "should set the options" do
22
+ runner = SpecRunQueue::SystemRunner.new(1, { :password => "foo" })
23
+ runner.options.should == { :password => "foo" }
24
+ end
25
+ end
26
+
27
+ describe "getting the rspec binary" do
28
+ it "for version 1 is spec by default" do
29
+ runner = SpecRunQueue::SystemRunner.new(1)
30
+ runner.rspec_bin.should == "spec"
31
+ end
32
+
33
+ it "for varsion 2 is rspec by default" do
34
+ runner = SpecRunQueue::SystemRunner.new(2)
35
+ runner.rspec_bin.should == "rspec"
36
+ end
37
+ end
38
+
39
+ describe "adding a notifier" do
40
+ it "should append the notifier onto the notifiers list" do
41
+ runner = SpecRunQueue::SystemRunner.new(1)
42
+ foo_notifier = mock("FooNotifier")
43
+ runner.add_notifier(foo_notifier)
44
+ bar_notifier = mock("BarNotifier")
45
+ runner.add_notifier(bar_notifier)
46
+ runner.notifiers.should == [foo_notifier, bar_notifier]
47
+ end
48
+ end
49
+
50
+ describe "running a spec" do
51
+ it "should not run a spec if it's not a safe target" do
52
+ runner.should_not_receive(:run_cmd)
53
+ runner.run_spec(:target => "ls /tmp")
54
+ runner.run_spec(:target => "ls /tmp;foo_spec.rb")
55
+ end
56
+
57
+ it "should not run a spec if the target doesn't end in _spec.rb" do
58
+ runner.should_not_receive(:run_cmd)
59
+ runner.run_spec(:target => "spec/foo.rb")
60
+ end
61
+
62
+ it "should not run a spec if the line number doesn't look like a number" do
63
+ runner.should_not_receive(:run_cmd)
64
+ runner.run_spec(:target => "foo_spec.rb", :line => "foo")
65
+ end
66
+
67
+ it "should call to run the spec" do
68
+ runner.should_receive(:run_cmd).with("spec -f progress --drb foo_spec.rb")
69
+ runner.run_spec(:target => "foo_spec.rb")
70
+ end
71
+
72
+ it "should send the output from the run to all the notifiers" do
73
+ runner.should_receive(:run_cmd).with("spec -f progress --drb foo_spec.rb").and_return("spec output")
74
+ foo_notifier.should_receive(:notify).with("spec output", {})
75
+ bar_notifier.should_receive(:notify).with("spec output", {})
76
+ runner.run_spec(:target => "foo_spec.rb")
77
+ end
78
+
79
+ context "with a line number" do
80
+ it "should call to run the spec using the -l flag" do
81
+ runner.should_receive(:run_cmd).with("spec -f progress --drb -l 42 foo_spec.rb")
82
+ runner.run_spec(:target => "foo_spec.rb", :line => 42)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "spec_run_queue/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "spec_run_queue"
7
+ s.version = SpecRunQueue::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Brendon Murphy"]
10
+ s.email = ["xternal1+github@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Use a queue to run specs outside your editor}
13
+ s.description = %q{Use a queue to run specs outside your editor}
14
+
15
+ s.rubyforge_project = "spec_run_queue"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = ["redis_runner"]
20
+ s.require_paths = ["lib"]
21
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spec_run_queue
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Brendon Murphy
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-24 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Use a queue to run specs outside your editor
23
+ email:
24
+ - xternal1+github@gmail.com
25
+ executables:
26
+ - redis_runner
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.markdown
35
+ - Rakefile
36
+ - bin/redis_runner
37
+ - lib/spec_run_queue.rb
38
+ - lib/spec_run_queue/notifier/base.rb
39
+ - lib/spec_run_queue/notifier/growl.rb
40
+ - lib/spec_run_queue/notifier/stdout.rb
41
+ - lib/spec_run_queue/queue/base.rb
42
+ - lib/spec_run_queue/queue/redis.rb
43
+ - lib/spec_run_queue/system_runner.rb
44
+ - lib/spec_run_queue/version.rb
45
+ - spec/notifier/base_spec.rb
46
+ - spec/notifier/growl_spec.rb
47
+ - spec/notifier/stdout_spec.rb
48
+ - spec/queue/base_spec.rb
49
+ - spec/queue/redis_spec.rb
50
+ - spec/spec_helper.rb
51
+ - spec/system_runner_spec.rb
52
+ - spec_run_queue.gemspec
53
+ has_rdoc: true
54
+ homepage: ""
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project: spec_run_queue
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Use a queue to run specs outside your editor
87
+ test_files:
88
+ - spec/notifier/base_spec.rb
89
+ - spec/notifier/growl_spec.rb
90
+ - spec/notifier/stdout_spec.rb
91
+ - spec/queue/base_spec.rb
92
+ - spec/queue/redis_spec.rb
93
+ - spec/spec_helper.rb
94
+ - spec/system_runner_spec.rb