forked from herrtreas/rspactor
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
rubyphunk
committed
Mar 20, 2008
0 parents
commit 51264f3
Showing
10 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#!/usr/bin/env ruby | ||
|
||
require File.join(File.dirname(__FILE__), '..', 'lib', 'interactor') | ||
require File.join(File.dirname(__FILE__), '..', 'lib', 'listener') | ||
require File.join(File.dirname(__FILE__), '..', 'lib', 'inspector') | ||
require File.join(File.dirname(__FILE__), '..', 'lib', 'runner') | ||
|
||
Runner.load |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# The inspector make some assumptions about how your project is structured and where your spec files are located. | ||
# That said: The 'spec' directory, containing all your test files, must rest in the root directory of your project. | ||
# Futhermore it tries to locate controller, model, helper and view specs for a rails app (or projects with an identical structure) | ||
# in root/spec/controllers, root/spec/models, root/spec/helpers and root/spec/views. | ||
|
||
class Inspector | ||
|
||
attr_accessor :base_spec_root | ||
|
||
def self.file_is_invalid?(file) | ||
return true unless File.basename(file) =~ /.rb\z|.rhtml\z|.erb\z|.haml\z/ | ||
false | ||
end | ||
|
||
def find_spec_file(file) | ||
begin | ||
return file if file_is_a_spec?(file) | ||
spec_root = find_base_spec_root_by_file(file) | ||
if spec_root | ||
guessed_spec_location = guess_spec_location(file, spec_root) | ||
if File.exist?(guessed_spec_location) | ||
@base_spec_root = spec_root | ||
return guessed_spec_location | ||
end | ||
end | ||
nil | ||
rescue => e | ||
puts "Error while parsing a file: '#{file}'" | ||
puts e | ||
end | ||
end | ||
|
||
def inner_spec_directory(path) | ||
spec_base_root = find_base_spec_root_by_file(Dir.pwd + "/.") | ||
inner_location = extract_inner_project_location(Dir.pwd, spec_base_root) | ||
File.join(spec_base_root, inner_location) | ||
end | ||
|
||
def find_base_spec_root_by_file(file) | ||
if @base_spec_root | ||
return @base_spec_root | ||
else | ||
dir_parts = File.dirname(file).split("/") | ||
dir_parts.size.times do |i| | ||
search_dir = dir_parts[0..dir_parts.length - i - 1].join("/") + "/" | ||
if Dir.entries(search_dir).include?('spec') | ||
@assumed_spec_root = search_dir + "spec" | ||
break | ||
end | ||
end | ||
return @assumed_spec_root | ||
end | ||
end | ||
|
||
def guess_spec_location(file, spec_root) | ||
inner_location = extract_inner_project_location(file, spec_root) | ||
append_spec_file_extension(File.join(spec_root, inner_location)) | ||
end | ||
|
||
def project_root(spec_root) | ||
spec_root.split("/")[0...-1].join("/") | ||
end | ||
|
||
def extract_inner_project_location(file, spec_root) | ||
location = file.sub(project_root(spec_root), "") | ||
adapt_rails_specific_app_structure(location) | ||
end | ||
|
||
def adapt_rails_specific_app_structure(location) | ||
# Removing 'app' if its a rails controller, model, helper or view | ||
fu = location.split("/") | ||
if fu[1] == "app" && (fu[2] == 'controllers' || fu[2] == 'helpers' || fu[2] == 'models' || fu[2] == 'views') | ||
return "/" + fu[2..fu.length].join("/") | ||
end | ||
location | ||
end | ||
|
||
def append_spec_file_extension(spec_file) | ||
if File.extname(spec_file) == ".rb" | ||
return File.join(File.dirname(spec_file), File.basename(spec_file, ".rb")) + "_spec.rb" | ||
else | ||
return spec_file + "_spec.rb" | ||
end | ||
end | ||
|
||
def file_is_a_spec?(file) | ||
if file.split("/").include?('spec') && File.basename(file).match(/_spec.rb\z/) | ||
return true | ||
end | ||
false | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
require 'timeout' | ||
|
||
class Interactor | ||
|
||
def initialize | ||
ticker | ||
end | ||
|
||
def wait_for_enter_key(msg, seconds_to_wait) | ||
begin | ||
Timeout::timeout(seconds_to_wait) do | ||
ticker(:start => true, :msg => msg) | ||
$stdin.gets | ||
return true | ||
end | ||
rescue Timeout::Error | ||
false | ||
ensure | ||
ticker(:stop => true) | ||
end | ||
end | ||
|
||
def start_termination_handler | ||
@main_thread = Thread.current | ||
Thread.new do | ||
loop do | ||
sleep 0.5 | ||
if $stdin.gets | ||
if wait_for_enter_key("** Running all specs.. Hit <enter> again to exit RSpactor", 3) | ||
@main_thread.exit | ||
exit | ||
end | ||
Runner.run_all_specs | ||
end | ||
end | ||
end | ||
end | ||
|
||
|
||
private | ||
|
||
def ticker(opts = {}) | ||
if opts[:stop] | ||
$stdout.puts "\n" | ||
@pointer_running = false | ||
elsif opts[:start] | ||
@pointer_running = true | ||
write(opts[:msg]) if opts[:msg] | ||
else | ||
Thread.new do | ||
loop do | ||
write('.') if @pointer_running == true | ||
sleep 1.0 | ||
end | ||
end | ||
end | ||
end | ||
|
||
def write(msg) | ||
$stdout.print(msg) | ||
$stdout.flush | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Some code borrowed from http://rails.aizatto.com/2007/11/28/taming-the-autotest-beast-with-fsevents/ | ||
|
||
class Listener | ||
|
||
def initialize(&block) | ||
require 'osx/foundation' | ||
begin | ||
@spec_run_time = Time.now | ||
callback = lambda do |stream, ctx, num_events, paths, marks, event_ids| | ||
changed_files = extract_changed_files_from_paths(split_paths(paths, num_events)) | ||
@spec_run_time = Time.now | ||
yield changed_files | ||
end | ||
|
||
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework' | ||
stream = OSX::FSEventStreamCreate(OSX::KCFAllocatorDefault, callback, nil, [Dir.pwd], OSX::KFSEventStreamEventIdSinceNow, 0.5, 0) | ||
unless stream | ||
puts "Failed to create stream" | ||
exit | ||
end | ||
|
||
OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode) | ||
unless OSX::FSEventStreamStart(stream) | ||
puts "Failed to start stream" | ||
exit | ||
end | ||
|
||
OSX::CFRunLoopRun() | ||
rescue Interrupt | ||
OSX::FSEventStreamStop(stream) | ||
OSX::FSEventStreamInvalidate(stream) | ||
OSX::FSEventStreamRelease(stream) | ||
end | ||
end | ||
|
||
def split_paths(paths, num_events) | ||
paths.regard_as('*') | ||
rpaths = [] | ||
num_events.times { |i| rpaths << paths[i] } | ||
rpaths | ||
end | ||
|
||
def extract_changed_files_from_paths(paths) | ||
changed_files = [] | ||
paths.each do |path| | ||
Dir.glob(path + "*").each do |file| | ||
next if Inspector.file_is_invalid?(file) | ||
file_time = File.stat(file).mtime | ||
changed_files << file if file_time > @spec_run_time | ||
end | ||
end | ||
changed_files | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
class RSpactorFormatter | ||
attr_accessor :example_group, :options, :where | ||
def initialize(options, where) | ||
@options = options | ||
@where = where | ||
end | ||
|
||
def dump_summary(duration, example_count, failure_count, pending_count) | ||
img = (failure_count == 0) ? "rails_ok.png" : "rails_fail.png" | ||
growl "Test Results", "#{example_count} examples, #{failure_count} failures", File.dirname(__FILE__) + "/../asset/#{img}", 0 | ||
end | ||
|
||
def start(example_count) | ||
end | ||
|
||
def add_example_group(example_group) | ||
end | ||
|
||
def example_started(example) | ||
end | ||
|
||
def example_passed(example) | ||
end | ||
|
||
def example_failed(example, counter, failure) | ||
end | ||
|
||
def example_pending(example_group_description, example, message) | ||
end | ||
|
||
def start_dump | ||
end | ||
|
||
def dump_failure(counter, failure) | ||
end | ||
|
||
def dump_pending | ||
end | ||
|
||
def close | ||
end | ||
|
||
def growl(title, msg, img, pri = 0) | ||
system("growlnotify -w -n rspactor --image #{img} -p #{pri} -m #{msg.inspect} #{title} &") | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
class Runner | ||
|
||
def self.load | ||
@inspector = Inspector.new | ||
@interactor = Interactor.new | ||
|
||
puts "** RSpactor is now watching at '#{Dir.pwd}'" | ||
|
||
if initial_spec_run_abort | ||
@interactor.start_termination_handler | ||
else | ||
@interactor.start_termination_handler | ||
run_all_specs | ||
end | ||
|
||
Listener.new do |files| | ||
files_to_spec = [] | ||
files.each do |file| | ||
spec_file = @inspector.find_spec_file(file) | ||
if spec_file | ||
puts spec_file | ||
files_to_spec << spec_file | ||
end | ||
end | ||
run_spec_command(files_to_spec) unless files_to_spec.empty? | ||
end | ||
end | ||
|
||
def self.run_all_specs | ||
run_spec_command([@inspector.inner_spec_directory(Dir.pwd)]) | ||
end | ||
|
||
def self.run_specs_for_files(files, verbose = false) | ||
files_to_spec = [] | ||
files.each do |file| | ||
spec_file = @inspector.find_spec_file(file) | ||
if spec_file | ||
puts spec_file if verbose | ||
files_to_spec << spec_file | ||
end | ||
end | ||
run_spec_command(files_to_spec) unless files_to_spec.empty? | ||
end | ||
|
||
def self.run_spec_command(locations) | ||
base_spec_root = extract_spec_root(locations.first) | ||
spec_runner_bin = script_runner(locations.first) | ||
locations = locations.join(" ") | ||
cmd = "RAILS_ENV=test; " | ||
cmd << "#{spec_runner_bin} " | ||
cmd << "#{locations} #{spec_opts(base_spec_root)} " | ||
cmd << "-r #{File.dirname(__FILE__)}/../lib/resulting.rb -f RSpactorFormatter:STDOUT" | ||
#puts cmd | ||
system(cmd) | ||
end | ||
|
||
def self.extract_spec_root(file) | ||
file[0..file.index("spec") + 4] | ||
end | ||
|
||
def self.spec_opts(base_spec_root) | ||
if File.exist?("#{base_spec_root}spec.opts") | ||
return "-O #{base_spec_root}spec.opts" | ||
else | ||
return "-c -f progress" | ||
end | ||
end | ||
|
||
def self.initial_spec_run_abort | ||
@interactor.wait_for_enter_key("** Hit <enter> to skip initial spec run", 3) | ||
end | ||
|
||
def self.script_runner(file) | ||
root = file[0..file.index("spec") - 1] | ||
if File.exist?(root + "script/spec") | ||
return root + "script/spec" | ||
else | ||
"spec" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
require "rake" | ||
|
||
spec = Gem::Specification.new do |s| | ||
s.name = "rspactor" | ||
s.version = "0.2.0" | ||
s.author = "Andreas Wolff" | ||
s.email = "treas@dynamicdudes.com" | ||
s.homepage = "http://rubyphunk.com" | ||
s.platform = Gem::Platform::RUBY | ||
s.summary = "RSpactor is a little command line tool to automatically run your changed specs (much like autotest)." | ||
s.files = FileList["{bin,lib,asset}/**/*"].to_a | ||
s.require_path = "lib" | ||
s.has_rdoc = true | ||
s.rubyforge_project = "rspactor" | ||
s.executables << 'rspactor' | ||
#s.add_dependency("dependency", ">= 0.x.x") | ||
end |
Oops, something went wrong.