rspactor 0.0.2
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/asset/rails_fail.png +0 -0
- data/asset/rails_ok.png +0 -0
- data/bin/rspactor +8 -0
- data/lib/inspector.rb +92 -0
- data/lib/interactor.rb +34 -0
- data/lib/listener.rb +55 -0
- data/lib/resulting.rb +47 -0
- data/lib/runner.rb +78 -0
- metadata +60 -0
Binary file
|
data/asset/rails_ok.png
ADDED
Binary file
|
data/bin/rspactor
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'interactor')
|
4
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'listener')
|
5
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'inspector')
|
6
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'runner')
|
7
|
+
|
8
|
+
Runner.load
|
data/lib/inspector.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# The inspector make some assumptions about how your project is structured and where your spec files are located.
|
2
|
+
# That said: The 'spec' directory, containing all your test files, must rest in the root directory of your project.
|
3
|
+
# Futhermore it tries to locate controller, model, helper and view specs for a rails app (or projects with an identical structure)
|
4
|
+
# in root/spec/controllers, root/spec/models, root/spec/helpers and root/spec/views.
|
5
|
+
|
6
|
+
class Inspector
|
7
|
+
|
8
|
+
attr_accessor :base_spec_root
|
9
|
+
|
10
|
+
def self.file_is_invalid?(file)
|
11
|
+
return true unless File.basename(file) =~ /.rb\z|.rhtml\z|.erb\z|.haml\z/
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_spec_file(file)
|
16
|
+
begin
|
17
|
+
return file if file_is_a_spec?(file)
|
18
|
+
spec_root = find_base_spec_root_by_file(file)
|
19
|
+
if spec_root
|
20
|
+
guessed_spec_location = guess_spec_location(file, spec_root)
|
21
|
+
if File.exist?(guessed_spec_location)
|
22
|
+
@base_spec_root = spec_root
|
23
|
+
return guessed_spec_location
|
24
|
+
end
|
25
|
+
end
|
26
|
+
nil
|
27
|
+
rescue => e
|
28
|
+
puts "Error while parsing a file: '#{file}'"
|
29
|
+
puts e
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def inner_spec_directory(path)
|
34
|
+
spec_base_root = find_base_spec_root_by_file(Dir.pwd + "/.")
|
35
|
+
inner_location = extract_inner_project_location(Dir.pwd, spec_base_root)
|
36
|
+
File.join(spec_base_root, inner_location)
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_base_spec_root_by_file(file)
|
40
|
+
if @base_spec_root
|
41
|
+
return @base_spec_root
|
42
|
+
else
|
43
|
+
dir_parts = File.dirname(file).split("/")
|
44
|
+
dir_parts.size.times do |i|
|
45
|
+
search_dir = dir_parts[0..dir_parts.length - i - 1].join("/") + "/"
|
46
|
+
if Dir.entries(search_dir).include?('spec')
|
47
|
+
@assumed_spec_root = search_dir + "spec"
|
48
|
+
break
|
49
|
+
end
|
50
|
+
end
|
51
|
+
return @assumed_spec_root
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def guess_spec_location(file, spec_root)
|
56
|
+
inner_location = extract_inner_project_location(file, spec_root)
|
57
|
+
append_spec_file_extension(File.join(spec_root, inner_location))
|
58
|
+
end
|
59
|
+
|
60
|
+
def project_root(spec_root)
|
61
|
+
spec_root.split("/")[0...-1].join("/")
|
62
|
+
end
|
63
|
+
|
64
|
+
def extract_inner_project_location(file, spec_root)
|
65
|
+
location = file.sub(project_root(spec_root), "")
|
66
|
+
adapt_rails_specific_app_structure(location)
|
67
|
+
end
|
68
|
+
|
69
|
+
def adapt_rails_specific_app_structure(location)
|
70
|
+
# Removing 'app' if its a rails controller, model, helper or view
|
71
|
+
fu = location.split("/")
|
72
|
+
if fu[1] == "app" && (fu[2] == 'controllers' || fu[2] == 'helpers' || fu[2] == 'models' || fu[2] == 'views')
|
73
|
+
return "/" + fu[2..fu.length].join("/")
|
74
|
+
end
|
75
|
+
location
|
76
|
+
end
|
77
|
+
|
78
|
+
def append_spec_file_extension(spec_file)
|
79
|
+
if File.extname(spec_file) == ".rb"
|
80
|
+
return File.join(File.dirname(spec_file), File.basename(spec_file, ".rb")) + "_spec.rb"
|
81
|
+
else
|
82
|
+
return spec_file + "_spec.rb"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def file_is_a_spec?(file)
|
87
|
+
if file.split("/").include?('spec') && File.basename(file).match(/_spec.rb\z/)
|
88
|
+
return true
|
89
|
+
end
|
90
|
+
false
|
91
|
+
end
|
92
|
+
end
|
data/lib/interactor.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
class Interactor
|
4
|
+
|
5
|
+
def wait_for_enter_key(seconds_to_wait)
|
6
|
+
begin
|
7
|
+
Timeout::timeout(seconds_to_wait) do
|
8
|
+
loop do
|
9
|
+
sleep 0.2
|
10
|
+
return true if $stdin.gets
|
11
|
+
end
|
12
|
+
end
|
13
|
+
rescue Timeout::Error
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_termination_handler
|
19
|
+
@main_thread = Thread.current
|
20
|
+
Thread.new do
|
21
|
+
loop do
|
22
|
+
sleep 0.5
|
23
|
+
if $stdin.gets
|
24
|
+
puts "** Running all specs.. Hit <enter> twice to exit RSpactor."
|
25
|
+
if wait_for_enter_key(1)
|
26
|
+
@main_thread.exit
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
Runner.run_all_specs
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/listener.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Some code borrowed from http://rails.aizatto.com/2007/11/28/taming-the-autotest-beast-with-fsevents/
|
2
|
+
|
3
|
+
class Listener
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
require 'osx/foundation'
|
7
|
+
begin
|
8
|
+
@spec_run_time = Time.now
|
9
|
+
callback = lambda do |stream, ctx, num_events, paths, marks, event_ids|
|
10
|
+
changed_files = extract_changed_files_from_paths(split_paths(paths, num_events))
|
11
|
+
@spec_run_time = Time.now
|
12
|
+
yield changed_files
|
13
|
+
end
|
14
|
+
|
15
|
+
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
|
16
|
+
stream = OSX::FSEventStreamCreate(OSX::KCFAllocatorDefault, callback, nil, [Dir.pwd], OSX::KFSEventStreamEventIdSinceNow, 0.5, 0)
|
17
|
+
unless stream
|
18
|
+
puts "Failed to create stream"
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode)
|
23
|
+
unless OSX::FSEventStreamStart(stream)
|
24
|
+
puts "Failed to start stream"
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
OSX::CFRunLoopRun()
|
29
|
+
rescue Interrupt
|
30
|
+
OSX::FSEventStreamStop(stream)
|
31
|
+
OSX::FSEventStreamInvalidate(stream)
|
32
|
+
OSX::FSEventStreamRelease(stream)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def split_paths(paths, num_events)
|
37
|
+
paths.regard_as('*')
|
38
|
+
rpaths = []
|
39
|
+
num_events.times { |i| rpaths << paths[i] }
|
40
|
+
rpaths
|
41
|
+
end
|
42
|
+
|
43
|
+
def extract_changed_files_from_paths(paths)
|
44
|
+
changed_files = []
|
45
|
+
paths.each do |path|
|
46
|
+
Dir.glob(path + "*").each do |file|
|
47
|
+
next if Inspector.file_is_invalid?(file)
|
48
|
+
file_time = File.stat(file).mtime
|
49
|
+
changed_files << file if file_time > @spec_run_time
|
50
|
+
end
|
51
|
+
end
|
52
|
+
changed_files
|
53
|
+
end
|
54
|
+
|
55
|
+
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(example_count)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_example_group(example_group)
|
17
|
+
end
|
18
|
+
|
19
|
+
def example_started(example)
|
20
|
+
end
|
21
|
+
|
22
|
+
def example_passed(example)
|
23
|
+
end
|
24
|
+
|
25
|
+
def example_failed(example, counter, failure)
|
26
|
+
end
|
27
|
+
|
28
|
+
def example_pending(example_group_description, example, message)
|
29
|
+
end
|
30
|
+
|
31
|
+
def start_dump
|
32
|
+
end
|
33
|
+
|
34
|
+
def dump_failure(counter, failure)
|
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
|
+
%x(growlnotify -w -n rspactor --image #{img} -p #{pri} -m #{msg.inspect} #{title})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
data/lib/runner.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
class Runner
|
2
|
+
|
3
|
+
def self.load
|
4
|
+
@inspector = Inspector.new
|
5
|
+
@interactor = Interactor.new
|
6
|
+
|
7
|
+
puts ">> RSpactor is now watching at '#{Dir.pwd}'"
|
8
|
+
|
9
|
+
run_all_specs unless initial_spec_run_abort
|
10
|
+
|
11
|
+
@interactor.start_termination_handler
|
12
|
+
Listener.new do |files|
|
13
|
+
files_to_spec = []
|
14
|
+
files.each do |file|
|
15
|
+
spec_file = @inspector.find_spec_file(file)
|
16
|
+
if spec_file
|
17
|
+
puts spec_file
|
18
|
+
files_to_spec << spec_file
|
19
|
+
end
|
20
|
+
end
|
21
|
+
run_spec_command(files_to_spec) unless files_to_spec.empty?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.run_all_specs
|
26
|
+
run_spec_command([@inspector.inner_spec_directory(Dir.pwd)])
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.run_specs_for_files(files, verbose = false)
|
30
|
+
files_to_spec = []
|
31
|
+
files.each do |file|
|
32
|
+
spec_file = @inspector.find_spec_file(file)
|
33
|
+
if spec_file
|
34
|
+
puts spec_file if verbose
|
35
|
+
files_to_spec << spec_file
|
36
|
+
end
|
37
|
+
end
|
38
|
+
run_spec_command(files_to_spec) unless files_to_spec.empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.run_spec_command(locations)
|
42
|
+
base_spec_root = extract_spec_root(locations.first)
|
43
|
+
spec_runner_bin = script_runner(locations.first)
|
44
|
+
locations = locations.join(" ")
|
45
|
+
cmd = "RAILS_ENV=test; "
|
46
|
+
cmd << "#{spec_runner_bin} "
|
47
|
+
cmd << "#{locations} #{spec_opts(base_spec_root)} "
|
48
|
+
cmd << "-r #{File.dirname(__FILE__)}/../lib/resulting.rb -f RSpactorFormatter:STDOUT"
|
49
|
+
#puts cmd
|
50
|
+
system(cmd)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.extract_spec_root(file)
|
54
|
+
file[0..file.index("spec") + 4]
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.spec_opts(base_spec_root)
|
58
|
+
if File.exist?("#{base_spec_root}spec.opts")
|
59
|
+
return "-O #{base_spec_root}spec.opts"
|
60
|
+
else
|
61
|
+
return "-c -f progress"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.initial_spec_run_abort
|
66
|
+
puts "** Hit <enter> to skip initial spec run.."
|
67
|
+
@interactor.wait_for_enter_key(2)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.script_runner(file)
|
71
|
+
root = file[0..file.index("spec") - 1]
|
72
|
+
if File.exist?(root + "script/spec")
|
73
|
+
return root + "script/spec"
|
74
|
+
else
|
75
|
+
"spec"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspactor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andreas Wolff
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-03-11 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: treas@dynamicdudes.com
|
18
|
+
executables:
|
19
|
+
- rspactor
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- bin/rspactor
|
26
|
+
- lib/inspector.rb
|
27
|
+
- lib/interactor.rb
|
28
|
+
- lib/listener.rb
|
29
|
+
- lib/resulting.rb
|
30
|
+
- lib/runner.rb
|
31
|
+
- asset/rails_fail.png
|
32
|
+
- asset/rails_ok.png
|
33
|
+
has_rdoc: true
|
34
|
+
homepage: http://rubyphunk.com
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
requirements: []
|
53
|
+
|
54
|
+
rubyforge_project: rspactor
|
55
|
+
rubygems_version: 1.0.1
|
56
|
+
signing_key:
|
57
|
+
specification_version: 2
|
58
|
+
summary: RSpactor is a little command line tool to automatically run your changed specs (much like autotest).
|
59
|
+
test_files: []
|
60
|
+
|