mislav-rspactor 0.3.0
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/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
|
+
|