Skip to content

Commit

Permalink
Initial source import
Browse files Browse the repository at this point in the history
  • Loading branch information
rubyphunk committed Mar 20, 2008
0 parents commit 51264f3
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 0 deletions.
Binary file added asset/rails_fail.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added asset/rails_ok.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions bin/rspactor
@@ -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
92 changes: 92 additions & 0 deletions lib/inspector.rb
@@ -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
63 changes: 63 additions & 0 deletions lib/interactor.rb
@@ -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
55 changes: 55 additions & 0 deletions lib/listener.rb
@@ -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
47 changes: 47 additions & 0 deletions lib/resulting.rb
@@ -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

81 changes: 81 additions & 0 deletions lib/runner.rb
@@ -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
17 changes: 17 additions & 0 deletions rspactor.gemspec
@@ -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

0 comments on commit 51264f3

Please sign in to comment.