mislav-rspactor 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/VERSION.yml +4 -0
- data/bin/rspactor +4 -0
- data/lib/inspector.rb +84 -0
- data/lib/interactor.rb +63 -0
- data/lib/listener.rb +86 -0
- data/lib/resulting.rb +47 -0
- data/lib/rspactor.rb +4 -0
- data/lib/runner.rb +82 -0
- data/spec/inspector_spec.rb +81 -0
- data/spec/listener_spec.rb +35 -0
- data/spec/runner_spec.rb +138 -0
- metadata +67 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Mislav Marohnić
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/VERSION.yml
ADDED
data/bin/rspactor
ADDED
data/lib/inspector.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Maps the changed filenames to list of specs to run in the next go.
|
2
|
+
# Assumes Rails-like directory structure
|
3
|
+
class Inspector
|
4
|
+
|
5
|
+
EXTENSIONS = %w(rb erb builder haml rhtml rxml yml conf opts)
|
6
|
+
|
7
|
+
def initialize(dir)
|
8
|
+
@root = dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def determine_spec_files(file)
|
12
|
+
candidates = translate(file)
|
13
|
+
spec_files = candidates.select { |candidate| File.exists? candidate }
|
14
|
+
|
15
|
+
if spec_files.empty?
|
16
|
+
$stderr.puts "doesn't exist: #{candidates.inspect}"
|
17
|
+
end
|
18
|
+
spec_files
|
19
|
+
end
|
20
|
+
|
21
|
+
# mappings for Rails are inspired by autotest mappings in rspec-rails
|
22
|
+
def translate(file)
|
23
|
+
file = file.sub(%r:^#{Regexp.escape(@root)}/:, '')
|
24
|
+
candidates = []
|
25
|
+
|
26
|
+
if spec_file?(file)
|
27
|
+
candidates << file
|
28
|
+
else
|
29
|
+
spec_file = append_spec_file_extension(file)
|
30
|
+
|
31
|
+
case file
|
32
|
+
when %r:^app/:
|
33
|
+
if file =~ %r:^app/controllers/application(_controller)?.rb$:
|
34
|
+
candidates << 'controllers'
|
35
|
+
elsif file == 'app/helpers/application_helper.rb'
|
36
|
+
candidates << 'helpers' << 'views'
|
37
|
+
else
|
38
|
+
candidates << spec_file.sub('app/', '')
|
39
|
+
|
40
|
+
if file =~ %r:^app/(views/.+\.[a-z]+)\.[a-z]+$:
|
41
|
+
candidates << append_spec_file_extension($1)
|
42
|
+
elsif file =~ %r:app/helpers/(\w+)_helper.rb:
|
43
|
+
candidates << "views/#{$1}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
when %r:^lib/:
|
47
|
+
candidates << spec_file
|
48
|
+
# lib/foo/bar_spec.rb -> lib/bar_spec.rb
|
49
|
+
candidates << candidates.last.sub($&, '')
|
50
|
+
# lib/bar_spec.rb -> bar_spec.rb
|
51
|
+
candidates << candidates.last.sub(%r:\w+/:, '') if candidates.last.index('/')
|
52
|
+
when 'config/routes.rb'
|
53
|
+
candidates << 'controllers' << 'helpers' << 'views'
|
54
|
+
when 'config/database.yml', 'db/schema.rb'
|
55
|
+
candidates << 'models'
|
56
|
+
when %r:^(spec/(spec_helper|shared/.*)|config/(boot|environment(s/test)?))\.rb$:, 'spec/spec.opts'
|
57
|
+
candidates << 'spec'
|
58
|
+
else
|
59
|
+
candidates << spec_file
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
candidates.map do |candidate|
|
64
|
+
if candidate.index('spec') == 0
|
65
|
+
candidate
|
66
|
+
else
|
67
|
+
'spec/' + candidate
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def append_spec_file_extension(file)
|
73
|
+
if File.extname(file) == ".rb"
|
74
|
+
file.sub(/.rb$/, "_spec.rb")
|
75
|
+
else
|
76
|
+
file + "_spec.rb"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def spec_file?(file)
|
81
|
+
file =~ /^spec\/.+_spec.rb$/
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/lib/interactor.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
class Interactor
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
ticker
|
7
|
+
end
|
8
|
+
|
9
|
+
def wait_for_enter_key(msg, seconds_to_wait)
|
10
|
+
begin
|
11
|
+
Timeout::timeout(seconds_to_wait) do
|
12
|
+
ticker(:start => true, :msg => msg)
|
13
|
+
$stdin.gets
|
14
|
+
return true
|
15
|
+
end
|
16
|
+
rescue Timeout::Error
|
17
|
+
false
|
18
|
+
ensure
|
19
|
+
ticker(:stop => true)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def start_termination_handler
|
24
|
+
@main_thread = Thread.current
|
25
|
+
Thread.new do
|
26
|
+
loop do
|
27
|
+
sleep 0.5
|
28
|
+
if $stdin.gets
|
29
|
+
if wait_for_enter_key("** Running all specs.. Hit <enter> again to exit RSpactor", 3)
|
30
|
+
@main_thread.exit
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
Runner.run_all_specs
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def ticker(opts = {})
|
43
|
+
if opts[:stop]
|
44
|
+
$stdout.puts "\n"
|
45
|
+
@pointer_running = false
|
46
|
+
elsif opts[:start]
|
47
|
+
@pointer_running = true
|
48
|
+
write(opts[:msg]) if opts[:msg]
|
49
|
+
else
|
50
|
+
Thread.new do
|
51
|
+
loop do
|
52
|
+
write('.') if @pointer_running == true
|
53
|
+
sleep 1.0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def write(msg)
|
60
|
+
$stdout.print(msg)
|
61
|
+
$stdout.flush
|
62
|
+
end
|
63
|
+
end
|
data/lib/listener.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'osx/foundation'
|
2
|
+
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
|
3
|
+
|
4
|
+
# Some code borrowed from http://rails.aizatto.com/2007/11/28/taming-the-autotest-beast-with-fsevents/
|
5
|
+
class Listener
|
6
|
+
|
7
|
+
attr_reader :last_check, :callback, :valid_extensions
|
8
|
+
|
9
|
+
def initialize(valid_extensions = nil)
|
10
|
+
@valid_extensions = valid_extensions
|
11
|
+
timestamp_checked
|
12
|
+
|
13
|
+
@callback = lambda do |stream, ctx, num_events, paths, marks, event_ids|
|
14
|
+
changed_files = extract_changed_files_from_paths(split_paths(paths, num_events))
|
15
|
+
timestamp_checked
|
16
|
+
yield changed_files unless changed_files.empty?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(directories)
|
21
|
+
dirs = Array(directories)
|
22
|
+
stream = OSX::FSEventStreamCreate(OSX::KCFAllocatorDefault, callback, nil, dirs, OSX::KFSEventStreamEventIdSinceNow, 0.5, 0)
|
23
|
+
unless stream
|
24
|
+
$stderr.puts "Failed to create stream"
|
25
|
+
exit(1)
|
26
|
+
end
|
27
|
+
|
28
|
+
OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode)
|
29
|
+
unless OSX::FSEventStreamStart(stream)
|
30
|
+
$stderr.puts "Failed to start stream"
|
31
|
+
exit(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
OSX::CFRunLoopRun()
|
36
|
+
rescue Interrupt
|
37
|
+
OSX::FSEventStreamStop(stream)
|
38
|
+
OSX::FSEventStreamInvalidate(stream)
|
39
|
+
OSX::FSEventStreamRelease(stream)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def timestamp_checked
|
44
|
+
@last_check = Time.now
|
45
|
+
end
|
46
|
+
|
47
|
+
def split_paths(paths, num_events)
|
48
|
+
paths.regard_as('*')
|
49
|
+
rpaths = []
|
50
|
+
num_events.times { |i| rpaths << paths[i] }
|
51
|
+
rpaths
|
52
|
+
end
|
53
|
+
|
54
|
+
def extract_changed_files_from_paths(paths)
|
55
|
+
changed_files = []
|
56
|
+
paths.each do |path|
|
57
|
+
next if ignore_path?(path)
|
58
|
+
Dir.glob(path + "*").each do |file|
|
59
|
+
next if ignore_file?(file)
|
60
|
+
changed_files << file if file_changed?(file)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
changed_files
|
64
|
+
end
|
65
|
+
|
66
|
+
def file_changed?(file)
|
67
|
+
File.stat(file).mtime > last_check
|
68
|
+
end
|
69
|
+
|
70
|
+
def ignore_path?(path)
|
71
|
+
path =~ /(?:^|\/)\.(git|svn)/
|
72
|
+
end
|
73
|
+
|
74
|
+
def ignore_file?(file)
|
75
|
+
File.basename(file).index('.') == 0 or not valid_extension?(file)
|
76
|
+
end
|
77
|
+
|
78
|
+
def file_extension(file)
|
79
|
+
file =~ /\.(\w+)$/ and $1
|
80
|
+
end
|
81
|
+
|
82
|
+
def valid_extension?(file)
|
83
|
+
valid_extensions.nil? or valid_extensions.include?(file_extension(file))
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/lib/resulting.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
class RSpactorFormatter
|
2
|
+
attr_accessor :example_group, :options, :where
|
3
|
+
def initialize(options, where)
|
4
|
+
@options = options
|
5
|
+
@where = where
|
6
|
+
end
|
7
|
+
|
8
|
+
def dump_summary(duration, example_count, failure_count, pending_count)
|
9
|
+
img = (failure_count == 0) ? "rails_ok.png" : "rails_fail.png"
|
10
|
+
growl "Test Results", "#{example_count} examples, #{failure_count} failures", File.dirname(__FILE__) + "/../asset/#{img}", 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def start(*ignore_these)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_example_group(*ignore_these)
|
17
|
+
end
|
18
|
+
|
19
|
+
def example_started(*ignore_these)
|
20
|
+
end
|
21
|
+
|
22
|
+
def example_passed(*ignore_these)
|
23
|
+
end
|
24
|
+
|
25
|
+
def example_failed(*ignore_these)
|
26
|
+
end
|
27
|
+
|
28
|
+
def example_pending( *ignore_these)
|
29
|
+
end
|
30
|
+
|
31
|
+
def start_dump
|
32
|
+
end
|
33
|
+
|
34
|
+
def dump_failure(*ignore_these)
|
35
|
+
end
|
36
|
+
|
37
|
+
def dump_pending
|
38
|
+
end
|
39
|
+
|
40
|
+
def close
|
41
|
+
end
|
42
|
+
|
43
|
+
def growl(title, msg, img, pri = 0)
|
44
|
+
system("growlnotify -w -n rspactor --image #{img} -p #{pri} -m #{msg.inspect} #{title} &")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
data/lib/rspactor.rb
ADDED
data/lib/runner.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'inspector'
|
2
|
+
require 'interactor'
|
3
|
+
require 'listener'
|
4
|
+
|
5
|
+
class Runner
|
6
|
+
|
7
|
+
def self.load
|
8
|
+
dotfile = File.join(ENV['HOME'], '.rspactor')
|
9
|
+
Kernel.load dotfile if File.exists?(dotfile)
|
10
|
+
|
11
|
+
dir = Dir.pwd
|
12
|
+
@inspector = Inspector.new(dir)
|
13
|
+
@interactor = Interactor.new
|
14
|
+
|
15
|
+
puts "** RSpactor is now watching at '#{dir}'"
|
16
|
+
|
17
|
+
aborted = initial_spec_run_abort
|
18
|
+
@interactor.start_termination_handler
|
19
|
+
run_all_specs unless aborted
|
20
|
+
|
21
|
+
Listener.new(Inspector::EXTENSIONS) do |files|
|
22
|
+
files_to_spec = []
|
23
|
+
files.each do |file|
|
24
|
+
spec_files = @inspector.determine_spec_files(file)
|
25
|
+
unless spec_files.empty?
|
26
|
+
puts spec_files.join("\n")
|
27
|
+
files_to_spec.concat spec_files
|
28
|
+
end
|
29
|
+
end
|
30
|
+
run_spec_command(files_to_spec)
|
31
|
+
end.run(dir)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.initial_spec_run_abort
|
35
|
+
@interactor.wait_for_enter_key("** Hit <enter> to skip initial spec run", 3)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.run_all_specs
|
39
|
+
run_spec_command('spec')
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.run_spec_command(paths)
|
43
|
+
paths = Array(paths)
|
44
|
+
return if paths.empty?
|
45
|
+
run_command [ruby_opts, spec_runner, paths, spec_opts].flatten.join(' ')
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.run_command(cmd)
|
49
|
+
system(cmd)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.spec_opts
|
53
|
+
if File.exist?('spec/spec.opts')
|
54
|
+
opts = File.read('spec/spec.opts').gsub("\n", ' ')
|
55
|
+
else
|
56
|
+
opts = "--color"
|
57
|
+
end
|
58
|
+
|
59
|
+
opts << ' ' << formatter_opts
|
60
|
+
# only add the "progress" formatter unless no other (besides growl) is specified
|
61
|
+
opts << ' -f progress' unless opts.scan(/\s(?:-f|--format)\b/).length > 1
|
62
|
+
|
63
|
+
opts
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.formatter_opts
|
67
|
+
"-r #{File.dirname(__FILE__)}/resulting.rb -f RSpactorFormatter:STDOUT"
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.spec_runner
|
71
|
+
if File.exist?("script/spec")
|
72
|
+
"script/spec"
|
73
|
+
else
|
74
|
+
"spec"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.ruby_opts
|
79
|
+
other = ENV['RUBYOPT'] ? " #{ENV['RUBYOPT']}" : ''
|
80
|
+
%(RUBYOPT='-Ilib:spec#{other}')
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'inspector'
|
2
|
+
|
3
|
+
describe Inspector do
|
4
|
+
before(:all) do
|
5
|
+
@inspector = Inspector.new('/project')
|
6
|
+
end
|
7
|
+
|
8
|
+
def translate(file)
|
9
|
+
@inspector.translate(file)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#translate" do
|
13
|
+
it "should consider all controllers when application_controller changes" do
|
14
|
+
translate('/project/app/controllers/application_controller.rb').should == ['spec/controllers']
|
15
|
+
translate('/project/app/controllers/application.rb').should == ['spec/controllers']
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should translate files under 'app/' directory" do
|
19
|
+
translate('/project/app/controllers/foo_controller.rb').should ==
|
20
|
+
['spec/controllers/foo_controller_spec.rb']
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should translate templates" do
|
24
|
+
translate('/project/app/views/foo/bar.erb').should == ['spec/views/foo/bar.erb_spec.rb']
|
25
|
+
translate('/project/app/views/foo/bar.html.haml').should ==
|
26
|
+
['spec/views/foo/bar.html.haml_spec.rb', 'spec/views/foo/bar.html_spec.rb']
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should consider all views when application_helper changes" do
|
30
|
+
translate('/project/app/helpers/application_helper.rb').should == ['spec/helpers', 'spec/views']
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should consider related templates when a helper changes" do
|
34
|
+
translate('/project/app/helpers/foo_helper.rb').should ==
|
35
|
+
['spec/helpers/foo_helper_spec.rb', 'spec/views/foo']
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should translate files under deep 'lib/' directory" do
|
39
|
+
translate('/project/lib/awesum/rox.rb').should ==
|
40
|
+
['spec/lib/awesum/rox_spec.rb', 'spec/awesum/rox_spec.rb', 'spec/rox_spec.rb']
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should translate files under shallow 'lib/' directory" do
|
44
|
+
translate('lib/runner.rb').should == ['spec/lib/runner_spec.rb', 'spec/runner_spec.rb']
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should handle relative paths" do
|
48
|
+
translate('foo.rb').should == ['spec/foo_spec.rb']
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should handle files without extension" do
|
52
|
+
translate('foo').should == ['spec/foo_spec.rb']
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should consider all controllers, helpers and views when routes.rb changes" do
|
56
|
+
translate('config/routes.rb').should == ['spec/controllers', 'spec/helpers', 'spec/views']
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should consider all models when config/database.yml changes" do
|
60
|
+
translate('config/database.yml').should == ['spec/models']
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should consider all models when db/schema.rb changes" do
|
64
|
+
translate('db/schema.rb').should == ['spec/models']
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should consider all specs when spec_helper changes" do
|
68
|
+
translate('spec/spec_helper.rb').should == ['spec']
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should consider all specs when code under spec/shared/ changes" do
|
72
|
+
translate('spec/shared/foo.rb').should == ['spec']
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should consider all specs when app configuration changes" do
|
76
|
+
translate('config/environment.rb').should == ['spec']
|
77
|
+
translate('config/environments/test.rb').should == ['spec']
|
78
|
+
translate('config/boot.rb').should == ['spec']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'listener'
|
2
|
+
|
3
|
+
describe Listener do
|
4
|
+
before(:all) do
|
5
|
+
@listener = Listener.new(%w(rb erb haml))
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should be timestamped" do
|
9
|
+
@listener.last_check.should be_instance_of(Time)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not ignore regular directories" do
|
13
|
+
@listener.ignore_path?('/project/foo/bar').should_not be
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should ignore .git directories" do
|
17
|
+
@listener.ignore_path?('/project/.git/index').should be
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should ignore dotfiles" do
|
21
|
+
@listener.ignore_file?('/project/.foo').should be
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not ignore files without extension" do
|
25
|
+
@listener.ignore_file?('/project/foo.rb').should_not be
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should ignore files without extension" do
|
29
|
+
@listener.ignore_file?('/project/foo').should be
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should ignore files with extensions that don't match those specified" do
|
33
|
+
@listener.ignore_file?('/project/foo.bar').should be
|
34
|
+
end
|
35
|
+
end
|
data/spec/runner_spec.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'runner'
|
2
|
+
|
3
|
+
class ::Runner
|
4
|
+
def self.run_command(cmd)
|
5
|
+
# never shell out in tests
|
6
|
+
cmd
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ::Runner do
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
@runner = ::Runner
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_env(name, value)
|
17
|
+
old_value = ENV[name]
|
18
|
+
ENV[name] = value
|
19
|
+
begin
|
20
|
+
yield
|
21
|
+
ensure
|
22
|
+
ENV[name] = old_value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "setup" do
|
27
|
+
before(:each) do
|
28
|
+
Dir.stub!(:pwd).and_return('/my/path')
|
29
|
+
File.stub!(:exists?).and_return(false)
|
30
|
+
@runner.stub!(:puts)
|
31
|
+
Inspector.stub!(:new)
|
32
|
+
Interactor.stub!(:new).and_return(mock('Interactor').as_null_object)
|
33
|
+
Listener.stub!(:new).and_return(mock('Listener').as_null_object)
|
34
|
+
end
|
35
|
+
|
36
|
+
def setup
|
37
|
+
@runner.load
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should initialize Inspector" do
|
41
|
+
Inspector.should_receive(:new).with('/my/path')
|
42
|
+
setup
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should start Interactor" do
|
46
|
+
interactor = mock('Interactor')
|
47
|
+
interactor.should_receive(:wait_for_enter_key).with(instance_of(String), 3)
|
48
|
+
interactor.should_receive(:start_termination_handler)
|
49
|
+
Interactor.should_receive(:new).and_return(interactor)
|
50
|
+
setup
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should run all specs if Interactor isn't interrupted" do
|
54
|
+
interactor = mock('Interactor', :start_termination_handler => nil)
|
55
|
+
interactor.should_receive(:wait_for_enter_key).and_return(nil)
|
56
|
+
Interactor.should_receive(:new).and_return(interactor)
|
57
|
+
@runner.should_receive(:run_spec_command).with('spec')
|
58
|
+
setup
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should skip running all specs if Interactor is interrupted" do
|
62
|
+
interactor = mock('Interactor', :start_termination_handler => nil)
|
63
|
+
interactor.should_receive(:wait_for_enter_key).and_return(true)
|
64
|
+
Interactor.should_receive(:new).and_return(interactor)
|
65
|
+
@runner.should_not_receive(:run_spec_command)
|
66
|
+
setup
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should run Listener" do
|
70
|
+
listener = mock('Listener')
|
71
|
+
listener.should_receive(:run).with('/my/path')
|
72
|
+
Listener.should_receive(:new).with(instance_of(Array)).and_return(listener)
|
73
|
+
setup
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should output 'watching' message on start" do
|
77
|
+
@runner.should_receive(:puts).with("** RSpactor is now watching at '/my/path'")
|
78
|
+
setup
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should load dotfile if found" do
|
82
|
+
with_env('HOME', '/home/moo') do
|
83
|
+
File.should_receive(:exists?).with('/home/moo/.rspactor').and_return(true)
|
84
|
+
Kernel.should_receive(:load).with('/home/moo/.rspactor')
|
85
|
+
setup
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#run_spec_command" do
|
91
|
+
def with_rubyopt(string, &block)
|
92
|
+
with_env('RUBYOPT', string, &block)
|
93
|
+
end
|
94
|
+
|
95
|
+
def run(paths)
|
96
|
+
@runner.run_spec_command(paths)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should exit if the paths argument is empty" do
|
100
|
+
@runner.should_not_receive(:run_command)
|
101
|
+
run([])
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should specify runner spec runner with joined paths" do
|
105
|
+
run(%w(foo bar)).should include(' spec foo bar ')
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should specify default options: --color" do
|
109
|
+
run('foo').should include(' --color')
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should setup RUBYOPT environment variable" do
|
113
|
+
with_rubyopt(nil) do
|
114
|
+
run('foo').should include("RUBYOPT='-Ilib:spec' ")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should concat existing RUBYOPTs" do
|
119
|
+
with_rubyopt('-rubygems -w') do
|
120
|
+
run('foo').should include("RUBYOPT='-Ilib:spec -rubygems -w' ")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should include growl formatter" do
|
125
|
+
run('foo').should include(' -f RSpactorFormatter:STDOUT')
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should include 'progress' formatter" do
|
129
|
+
run('foo').should include(' -f progress')
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should not include 'progress' formatter if there already are 2 or more formatters" do
|
133
|
+
@runner.should_receive(:formatter_opts).and_return('-f foo --format bar')
|
134
|
+
run('foo').should_not include('-f progress')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mislav-rspactor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Mislav Marohni\xC4\x87"
|
8
|
+
- Andreas Wolff
|
9
|
+
- Pelle Braendgaard
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
|
14
|
+
date: 2009-03-17 00:00:00 -07:00
|
15
|
+
default_executable: rspactor
|
16
|
+
dependencies: []
|
17
|
+
|
18
|
+
description:
|
19
|
+
email: mislav.marohnic@gmail.com
|
20
|
+
executables:
|
21
|
+
- rspactor
|
22
|
+
extensions: []
|
23
|
+
|
24
|
+
extra_rdoc_files:
|
25
|
+
- LICENSE
|
26
|
+
files:
|
27
|
+
- VERSION.yml
|
28
|
+
- bin/rspactor
|
29
|
+
- lib/inspector.rb
|
30
|
+
- lib/interactor.rb
|
31
|
+
- lib/listener.rb
|
32
|
+
- lib/resulting.rb
|
33
|
+
- lib/rspactor.rb
|
34
|
+
- lib/runner.rb
|
35
|
+
- spec/inspector_spec.rb
|
36
|
+
- spec/listener_spec.rb
|
37
|
+
- spec/runner_spec.rb
|
38
|
+
- LICENSE
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/mislav/rspactor
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --inline-source
|
44
|
+
- --charset=UTF-8
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.2.0
|
63
|
+
signing_key:
|
64
|
+
specification_version: 2
|
65
|
+
summary: RSpactor is a command line tool to automatically run your changed specs (much like autotest).
|
66
|
+
test_files: []
|
67
|
+
|