marcinbunsch-bolt 0.1.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/README.textile +24 -0
- data/Rakefile +74 -0
- data/bin/bolt +36 -0
- data/images/LICENSE +34 -0
- data/images/failed.png +0 -0
- data/images/pending.png +0 -0
- data/images/success.png +0 -0
- data/lib/bolt/listener.rb +77 -0
- data/lib/bolt/listeners/generic.rb +76 -0
- data/lib/bolt/listeners/osx.rb +114 -0
- data/lib/bolt/notifier.rb +37 -0
- data/lib/bolt/notifiers/generic.rb +26 -0
- data/lib/bolt/notifiers/growl.rb +55 -0
- data/lib/bolt/runner.rb +27 -0
- data/lib/bolt/runners/rspec.rb +141 -0
- data/lib/bolt/runners/test_unit.rb +166 -0
- data/lib/bolt.rb +38 -0
- data/spec/bolt/runners/rspec_spec.rb +47 -0
- data/spec/bolt/runners/test_unit_spec.rb +47 -0
- metadata +79 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 Marcin Bunsch
|
|
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/README.textile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
h1. Bolt - fast autotest alternative
|
|
2
|
+
|
|
3
|
+
Welcome to Bolt!
|
|
4
|
+
|
|
5
|
+
Bolt is a merge of several gems aimed at producing a fast, stable and easy to use version autotest.
|
|
6
|
+
|
|
7
|
+
What's the main difference? First of all, it does not use rakes for running test. Instead, it loads the environment and runs the tests within the main process instantly, reloading the changed files only.
|
|
8
|
+
|
|
9
|
+
When working with autotest, I was frustrated by the delay of the environment being loaded with every test run. And when trying to use mislav-rspactor it turned out it does not work with Mac OS X 10.4 (Tiger). So I decided to go ahead and make my own gem which would take the best elements of all these gems and work on my machine.
|
|
10
|
+
|
|
11
|
+
This is still in experimental phase, so all feedback is appreciated.
|
|
12
|
+
|
|
13
|
+
It takes solutions from several gems:
|
|
14
|
+
|
|
15
|
+
* *mislav-rspactor* - inspiration, structure and growl notifier
|
|
16
|
+
* *autotest* - obviously. Whole concept, parts of generic listener
|
|
17
|
+
* *Roman2K-rails_test_serving* - concept of running tests with preloaded environment, elements of test::unit runner
|
|
18
|
+
|
|
19
|
+
h1. Copyright
|
|
20
|
+
|
|
21
|
+
* *autotest* - ZenTest
|
|
22
|
+
* *rails-test-serving* - Roman2K
|
|
23
|
+
* *rspactor* - Mislav Marohnić, Andreas Wolff, Pelle Braendgaard
|
|
24
|
+
* Icons by DryIcons http://dryicons.com
|
data/Rakefile
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# This Rakefile has been copied from mislav/rspactor
|
|
2
|
+
# Mislav, you rock, did I tell you this already? :)
|
|
3
|
+
|
|
4
|
+
task :default => :spec
|
|
5
|
+
|
|
6
|
+
desc "starts Bolt"
|
|
7
|
+
task :spec do
|
|
8
|
+
system "ruby -Ilib bin/bolt"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
desc "generates .gemspec file"
|
|
12
|
+
task :gemspec => "version:read" do
|
|
13
|
+
spec = Gem::Specification.new do |gem|
|
|
14
|
+
gem.name = "bolt"
|
|
15
|
+
gem.summary = "Bolt is a merge of autotest and mislav/rspactor to produce a lightning fast, configurable and simple to set up autotest clone"
|
|
16
|
+
gem.email = "marcin@applicake.com"
|
|
17
|
+
gem.homepage = "http://github.com/marcinbunsch/bolt"
|
|
18
|
+
gem.authors = ["Marcin Bunsch", "Mislav Marohnić"]
|
|
19
|
+
gem.has_rdoc = false
|
|
20
|
+
|
|
21
|
+
gem.version = GEM_VERSION
|
|
22
|
+
gem.files = FileList['Rakefile', '{bin,lib,images,spec}/**/*', 'README*', 'LICENSE*']
|
|
23
|
+
gem.executables = Dir['bin/*'].map { |f| File.basename(f) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
spec_string = spec.to_ruby
|
|
27
|
+
|
|
28
|
+
begin
|
|
29
|
+
Thread.new { eval("$SAFE = 3\n#{spec_string}", binding) }.join
|
|
30
|
+
rescue
|
|
31
|
+
abort "unsafe gemspec: #{$!}"
|
|
32
|
+
else
|
|
33
|
+
File.open("#{spec.name}.gemspec", 'w') { |file| file.write spec_string }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
desc "bump the version up"
|
|
38
|
+
task :bump => ["version:bump", :gemspec]
|
|
39
|
+
|
|
40
|
+
desc "reinstall the gem locally"
|
|
41
|
+
task :reinstall do
|
|
42
|
+
GEM_VERSION = File.read("VERSION")
|
|
43
|
+
system('sudo gem uninstall bolt')
|
|
44
|
+
system("gem build bolt.gemspec")
|
|
45
|
+
system("sudo gem install bolt-#{GEM_VERSION}.gem")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
namespace :version do
|
|
49
|
+
task :read do
|
|
50
|
+
unless defined? GEM_VERSION
|
|
51
|
+
if File.exists?('VERSION')
|
|
52
|
+
GEM_VERSION = File.read("VERSION")
|
|
53
|
+
else
|
|
54
|
+
GEM_VERSION = '0.0.1'
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
desc "bump the version up"
|
|
60
|
+
task :bump => :read do
|
|
61
|
+
if ENV['VERSION']
|
|
62
|
+
GEM_VERSION.replace ENV['VERSION']
|
|
63
|
+
else
|
|
64
|
+
GEM_VERSION.sub!(/\d+$/) { |num| num.to_i + 1 }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
File.open("VERSION", 'w') { |v| v.write GEM_VERSION }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
task :release => :bump do
|
|
72
|
+
system %(git commit VERSION *.gemspec -m "release v#{GEM_VERSION}")
|
|
73
|
+
system %(git tag -am "release v#{GEM_VERSION}" v#{GEM_VERSION})
|
|
74
|
+
end
|
data/bin/bolt
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'bolt'
|
|
4
|
+
|
|
5
|
+
# Rails support
|
|
6
|
+
if File.exists?('test/test_helper.rb')
|
|
7
|
+
puts '** Rails found, loading environment'
|
|
8
|
+
ENV['RAILS_ENV'] = 'test'
|
|
9
|
+
require 'test/test_helper.rb'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# This is a hack for Rails Test::Unit to prevent raising errors when a test file is loaded again
|
|
13
|
+
module ActiveSupport
|
|
14
|
+
module Testing
|
|
15
|
+
module Declarative
|
|
16
|
+
# test "verify something" do
|
|
17
|
+
# ...
|
|
18
|
+
# end
|
|
19
|
+
def test(name, &block)
|
|
20
|
+
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
|
21
|
+
defined = instance_method(test_name) rescue false
|
|
22
|
+
# raise "#{test_name} is already defined in #{self}" if defined # do not raise this error
|
|
23
|
+
if block_given?
|
|
24
|
+
define_method(test_name, &block)
|
|
25
|
+
else
|
|
26
|
+
define_method(test_name) do
|
|
27
|
+
flunk "No implementation provided for #{name}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# start a listener
|
|
36
|
+
Bolt::Listener.new
|
data/images/LICENSE
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
DryIcons Free License Agreement
|
|
2
|
+
|
|
3
|
+
Read Full Legal Code
|
|
4
|
+
|
|
5
|
+
DryIcons is a service provided by our team of enthusiastic graphic and web designers and programmers. The purpose of this service is to provide only high-quality, free icons and free icon sets, as well as free vector graphics to the general public, with a specific target to designers, software and web developers.
|
|
6
|
+
All DryIcons' Works (meaning "icons, icon sets and graphics") are free of charge, but please read further under what Terms and Conditions.
|
|
7
|
+
All DryIcons Works are licensed under a DryIcons Free License. This means that you can use our icons, icon sets and graphics in any publicly accessible web site, web application or any form of presentation publicly accessible through the World Wide Web only according to the DryIcons Free License Terms and Conditions:
|
|
8
|
+
|
|
9
|
+
* You must put a back link with credits to http://dryicons.com on every page where DryIcons' Works are used (example: Icons by DryIcons);
|
|
10
|
+
* You must include the correct back link to DryIcons website, which is: http://dryicons.com;
|
|
11
|
+
* You must place the link on an easy-to-see, recognizable place, so there is no confusion about the Original Author of the Works (DryIcons);
|
|
12
|
+
* When copying, or paraphrasing description text (or title) on one of the Works, you must make sure there are no spelling mistakes;
|
|
13
|
+
* Do not try to take credit or imply in any way that you and not DryIcons is the Original Author of the Licensed Material (icons, icon sets and graphics).
|
|
14
|
+
|
|
15
|
+
What you CAN DO:
|
|
16
|
+
|
|
17
|
+
1. All DryIcons' Works are being provided to You under the Terms of this agreement, which allows for use of our Works but does not transfer ownership. All DryIcons' Works remain property of DryIcons;
|
|
18
|
+
2. You may use DryIcons' Works in any personal or commercial project unlimited number of times according to the DryIcons Free License Terms and Conditions;
|
|
19
|
+
3. You may use DryIcons' Works in any Open Source project and application according to the DryIcons Free License Terms and Conditions;
|
|
20
|
+
4. Your rights to DryIcons' Works are worldwide and for the duration of DryIcons' rights in the Works;
|
|
21
|
+
5. Any uses other than the ones mentioned above must be approved by DryIcons in writing;
|
|
22
|
+
6. Unauthorized use will result in immediate termination of this License, and with it, your rights to use DryIcons' Works.
|
|
23
|
+
|
|
24
|
+
What you CAN NOT DO:
|
|
25
|
+
|
|
26
|
+
1. You may not alter, crop, modify, manipulate and create derivative works of DryIcons' Works. All Works must be used "AS IS";
|
|
27
|
+
2. You may not redistribute, license, sell, lease, assign, convey or transfer DryIcons' Works, or offer free downloads in their present form or in a modified form to any third party;
|
|
28
|
+
3. You may not distribute the DryIcons' Works (icons, icon sets and graphics) online in a downloadable format or enable them to be distributed via mobile devices. You may link to http://dryicons.com instead;
|
|
29
|
+
4. You may not incorporate DryIcons' Works into a logo, trademark or service mark;
|
|
30
|
+
5. You may not use DryIcons' Works directly from dryicons.com or any other location hosted on the dryicons.com domain or any other domain owned by DryIcons.
|
|
31
|
+
|
|
32
|
+
Copyright
|
|
33
|
+
|
|
34
|
+
1. DryIcons.com reserves the copyrights and ownership rights of all DryIcons' Works downloaded from this website. We reserve the right to change parts of this License without notice and at our sole discretion.
|
data/images/failed.png
ADDED
|
Binary file
|
data/images/pending.png
ADDED
|
Binary file
|
data/images/success.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'bolt/notifier'
|
|
2
|
+
require 'bolt/runner'
|
|
3
|
+
#
|
|
4
|
+
# Bolt::Listener
|
|
5
|
+
#
|
|
6
|
+
# The Listener waits for files to be saved and when one is found, it launches the finder to match the file with a test
|
|
7
|
+
#
|
|
8
|
+
module Bolt
|
|
9
|
+
class Listener
|
|
10
|
+
attr_accessor :selected, :notifier, :runner
|
|
11
|
+
|
|
12
|
+
# Constructor
|
|
13
|
+
def initialize
|
|
14
|
+
$stdout.puts "** Starting Bolt..."
|
|
15
|
+
# find appropriate listener
|
|
16
|
+
$stdout.puts "** Using #{listener.class}... "
|
|
17
|
+
|
|
18
|
+
# trap the INT signal
|
|
19
|
+
add_sigint_handler
|
|
20
|
+
|
|
21
|
+
# attach a notifier
|
|
22
|
+
self.notifier = Bolt::Notifier.new.selected
|
|
23
|
+
|
|
24
|
+
# attach a mapper
|
|
25
|
+
self.runner = Bolt::Runner.new.selected
|
|
26
|
+
self.runner.notifier = self.notifier
|
|
27
|
+
|
|
28
|
+
# attach the notifier to listener
|
|
29
|
+
listener.notifier = self.notifier
|
|
30
|
+
|
|
31
|
+
# attach runner mappings to listener to avoid searching in all files
|
|
32
|
+
listener.mappings = self.runner.class::MAPPINGS
|
|
33
|
+
|
|
34
|
+
# attach listener wrapper
|
|
35
|
+
listener.parent = self
|
|
36
|
+
|
|
37
|
+
# display info to user
|
|
38
|
+
notifier.info 'Bolt running', "Bolt is enabled and running in #{Dir.pwd}"
|
|
39
|
+
|
|
40
|
+
# if in Rails, start environment
|
|
41
|
+
listener.start
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# handle updated files found by specific listener
|
|
46
|
+
def handle(updated_files)
|
|
47
|
+
# notifier.spotted(updated_files.first)
|
|
48
|
+
# send them to mapper
|
|
49
|
+
|
|
50
|
+
runner.handle(updated_files.first)
|
|
51
|
+
# run appropriate tests in runner
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Pick a listener to launch
|
|
55
|
+
def listener
|
|
56
|
+
return selected if selected
|
|
57
|
+
# TODO: os identification via RUBY_PLATFORM is flawed as it will return 'java' in jruby. Look for a different solution
|
|
58
|
+
os_string = RUBY_PLATFORM.downcase
|
|
59
|
+
self.selected= Bolt::Listeners::Generic.new
|
|
60
|
+
self.selected= Bolt::Listeners::Osx.start if os_string.include?("darwin")
|
|
61
|
+
# TODO:
|
|
62
|
+
# self.selected= Bolt::Listeners::Windows.new if os_string.include?("mswin")
|
|
63
|
+
# self.selected= Bolt::Listeners::Linux.new if os_string.include?("linux")
|
|
64
|
+
selected
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# capture the INT signal
|
|
68
|
+
def add_sigint_handler
|
|
69
|
+
trap 'INT' do
|
|
70
|
+
$stdout.puts "\n** Exiting Bolt..."
|
|
71
|
+
notifier.info 'Bolt terminated', "Bolt has been terminated"
|
|
72
|
+
exit(0)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'find'
|
|
2
|
+
#
|
|
3
|
+
# Bolt::Listeners::Generic
|
|
4
|
+
#
|
|
5
|
+
# The generic Listener, which polls the files after a specific interval
|
|
6
|
+
#
|
|
7
|
+
module Bolt
|
|
8
|
+
module Listeners
|
|
9
|
+
class Generic
|
|
10
|
+
attr_accessor :files, :interval, :busy, :notifier, :parent, :mappings
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
self.interval = 2 # decrease the CPU load by increasing the interval
|
|
14
|
+
self.busy = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def start
|
|
18
|
+
puts "** #{self.class} is scanning for files... "
|
|
19
|
+
# build a file collection
|
|
20
|
+
find_files
|
|
21
|
+
puts "** #{self.class} watching #{files.size} files... "
|
|
22
|
+
wait
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# source: ZenTest/autotest.rb
|
|
26
|
+
def wait
|
|
27
|
+
Kernel.sleep self.interval until check_files
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# check files to find these that have changed
|
|
31
|
+
def check_files
|
|
32
|
+
return if busy # if working on something already, skip the iteration
|
|
33
|
+
updated = []
|
|
34
|
+
files.each do |filename, mtime|
|
|
35
|
+
current_mtime = File.stat(filename).mtime
|
|
36
|
+
if current_mtime != mtime
|
|
37
|
+
updated << filename
|
|
38
|
+
# update the mtime in file registry so we it's only send once
|
|
39
|
+
files[filename] = current_mtime
|
|
40
|
+
$stdout.puts ">> Spotted change in #{filename}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
parent.handle(updated) if updated != []
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# Find the files to process, ignoring temporary files, source
|
|
49
|
+
# configuration management files, etc., and return a Hash mapping
|
|
50
|
+
# filename to modification time.
|
|
51
|
+
# source: ZenTest/autotest.rb
|
|
52
|
+
def find_files
|
|
53
|
+
result = {}
|
|
54
|
+
targets = ['.'] # start simple
|
|
55
|
+
targets.each do |target|
|
|
56
|
+
order = []
|
|
57
|
+
Find.find(target) do |f|
|
|
58
|
+
|
|
59
|
+
in_mappings = f =~ self.mappings
|
|
60
|
+
next if in_mappings.nil?
|
|
61
|
+
next if test ?d, f
|
|
62
|
+
next if f =~ /(swp|~|rej|orig)$/ # temporary/patch files
|
|
63
|
+
next if f =~ /\/\.?#/ # Emacs autosave/cvs merge files
|
|
64
|
+
|
|
65
|
+
filename = f.sub(/^\.\//, '')
|
|
66
|
+
|
|
67
|
+
result[filename] = File.stat(filename).mtime rescue next
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
self.files = result
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Bolt::Listeners::OSX
|
|
3
|
+
#
|
|
4
|
+
# OSX-specific Listener, using solution taken from mislav/rspactor listener.rb
|
|
5
|
+
#
|
|
6
|
+
# TODO: I have Mac OS X 10.4 and this works only in OS X 10.5. Which means I haven't been able to test it and adapt it.
|
|
7
|
+
#
|
|
8
|
+
module Bolt
|
|
9
|
+
module Listeners
|
|
10
|
+
class Osx
|
|
11
|
+
|
|
12
|
+
attr_reader :last_check, :callback, :valid_extensions
|
|
13
|
+
|
|
14
|
+
def initialize(valid_extensions = nil)
|
|
15
|
+
@valid_extensions = %w(rb erb builder haml rhtml rxml yml conf opts)
|
|
16
|
+
timestamp_checked
|
|
17
|
+
|
|
18
|
+
@callback = lambda do |stream, ctx, num_events, paths, marks, event_ids|
|
|
19
|
+
changed_files = extract_changed_files_from_paths(split_paths(paths, num_events))
|
|
20
|
+
timestamp_checked
|
|
21
|
+
yield changed_files unless changed_files.empty?
|
|
22
|
+
end
|
|
23
|
+
run(Dir.pwd)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.start
|
|
27
|
+
begin
|
|
28
|
+
require 'osx/foundation'
|
|
29
|
+
rescue LoadError
|
|
30
|
+
puts "** Could not load osx/foundation. RubyCocoa not installed? Falling back to Bolt::Listeners::Generic"
|
|
31
|
+
return Bolt::Listeners::Generic.new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
begin
|
|
35
|
+
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
|
|
36
|
+
return self.new
|
|
37
|
+
rescue NameError
|
|
38
|
+
puts "** There was an error loading Bolt::Listeners::Osx. Falling back to Bolt::Listeners::Generic"
|
|
39
|
+
return Bolt::Listeners::Generic.new
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def run(directories)
|
|
44
|
+
dirs = Array(directories)
|
|
45
|
+
stream = OSX::FSEventStreamCreate(OSX::KCFAllocatorDefault, callback, nil, dirs, OSX::KFSEventStreamEventIdSinceNow, 0.5, 0)
|
|
46
|
+
unless stream
|
|
47
|
+
$stderr.puts "Failed to create stream"
|
|
48
|
+
exit(1)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode)
|
|
52
|
+
unless OSX::FSEventStreamStart(stream)
|
|
53
|
+
$stderr.puts "Failed to start stream"
|
|
54
|
+
exit(1)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
OSX::CFRunLoopRun()
|
|
59
|
+
rescue Interrupt
|
|
60
|
+
OSX::FSEventStreamStop(stream)
|
|
61
|
+
OSX::FSEventStreamInvalidate(stream)
|
|
62
|
+
OSX::FSEventStreamRelease(stream)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def timestamp_checked
|
|
67
|
+
@last_check = Time.now
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def split_paths(paths, num_events)
|
|
71
|
+
paths.regard_as('*')
|
|
72
|
+
rpaths = []
|
|
73
|
+
num_events.times { |i| rpaths << paths[i] }
|
|
74
|
+
rpaths
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def extract_changed_files_from_paths(paths)
|
|
78
|
+
changed_files = []
|
|
79
|
+
paths.each do |path|
|
|
80
|
+
next if ignore_path?(path)
|
|
81
|
+
Dir.glob(path + "*").each do |file|
|
|
82
|
+
next if ignore_file?(file)
|
|
83
|
+
changed_files << file if file_changed?(file)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
changed_files
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def file_changed?(file)
|
|
90
|
+
File.stat(file).mtime > last_check
|
|
91
|
+
rescue Errno::ENOENT
|
|
92
|
+
false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def ignore_path?(path)
|
|
96
|
+
path =~ /(?:^|\/)\.(git|svn)/
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def ignore_file?(file)
|
|
100
|
+
File.basename(file).index('.') == 0 or not valid_extension?(file)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def file_extension(file)
|
|
104
|
+
file =~ /\.(\w+)$/ and $1
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def valid_extension?(file)
|
|
108
|
+
valid_extensions.nil? or valid_extensions.include?(file_extension(file))
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Bolt::Notifier
|
|
3
|
+
#
|
|
4
|
+
# The Notifier sends notification of the test results to the user
|
|
5
|
+
#
|
|
6
|
+
module Bolt
|
|
7
|
+
class Notifier
|
|
8
|
+
attr_accessor :selected
|
|
9
|
+
|
|
10
|
+
# Constructor
|
|
11
|
+
def initialize
|
|
12
|
+
# find appropriate listener
|
|
13
|
+
$stdout.puts "** Using #{notifier.class}... \n"
|
|
14
|
+
|
|
15
|
+
# launch appropriate listener
|
|
16
|
+
# notifier.new
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Pick a listener to launch
|
|
21
|
+
def notifier
|
|
22
|
+
return selected if selected
|
|
23
|
+
self.selected= Bolt::Notifiers::Generic.new
|
|
24
|
+
# growl
|
|
25
|
+
output = %x[which growlnotify]
|
|
26
|
+
if output.to_s.include?('/growlnotify')
|
|
27
|
+
self.selected= Bolt::Notifiers::Growl.new
|
|
28
|
+
end
|
|
29
|
+
#self.selected= Bolt::Listeners::Generic
|
|
30
|
+
# self.selected= Bolt::Listeners::OSX if os_string.include?("darwin")
|
|
31
|
+
#self.selected= Bolt::Listeners::Windows if os_string.include?("mswin")
|
|
32
|
+
#self.selected= Bolt::Listeners::Linux if os_string.include?("linux")
|
|
33
|
+
selected
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Bolt::Notifiers::Generic
|
|
3
|
+
#
|
|
4
|
+
# The Generic Notifier does not do anything, it's for stability
|
|
5
|
+
#
|
|
6
|
+
module Bolt
|
|
7
|
+
module Notifiers
|
|
8
|
+
class Generic
|
|
9
|
+
|
|
10
|
+
# info message
|
|
11
|
+
def info(name, description)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# message to be displayed when test file is missing
|
|
15
|
+
def test_file_missing(filename)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def result(filename, results)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def error(name, description)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Bolt::Notifiers::Generic
|
|
3
|
+
#
|
|
4
|
+
# The Generic Notifier does not do anything, it's for stability
|
|
5
|
+
# The Growl Notifer is copied from mislav/rspactor growl module
|
|
6
|
+
#
|
|
7
|
+
module Bolt
|
|
8
|
+
module Notifiers
|
|
9
|
+
class Growl
|
|
10
|
+
|
|
11
|
+
# generic notify method
|
|
12
|
+
def notify(title, msg, img, pri = 0)
|
|
13
|
+
system("growlnotify -w -n rspactor --image #{img} -p #{pri} -m #{msg.inspect} #{title} &")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# info message
|
|
17
|
+
def info(name, description)
|
|
18
|
+
image_path = File.dirname(__FILE__) + "/../../../images/pending.png"
|
|
19
|
+
notify name, description.to_s, image_path
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# message to be displayed when test file is missing
|
|
23
|
+
def test_file_missing(filename)
|
|
24
|
+
image_path = File.dirname(__FILE__) + "/../../../images/failed.png"
|
|
25
|
+
message = "The following test file could not be found: #{filename}"
|
|
26
|
+
notify "Could not find test file", message, image_path
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def result(filename, results)
|
|
30
|
+
message = results
|
|
31
|
+
if results.match('example') #rspec
|
|
32
|
+
if results.match('pending')
|
|
33
|
+
icon = 'pending'
|
|
34
|
+
elsif results.match('0 failures')
|
|
35
|
+
icon = 'success'
|
|
36
|
+
else
|
|
37
|
+
icon = 'failed'
|
|
38
|
+
end
|
|
39
|
+
elsif (results.match('0 failures, 0 errors')) # test::unit
|
|
40
|
+
icon = 'success'
|
|
41
|
+
else
|
|
42
|
+
icon = 'failed'
|
|
43
|
+
end
|
|
44
|
+
image_path = File.dirname(__FILE__) + "/../../../images/#{icon}.png"
|
|
45
|
+
notify "Test results for: #{filename}", message, image_path
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def error(name, description)
|
|
49
|
+
image_path = File.dirname(__FILE__) + "/../../../images/failed.png"
|
|
50
|
+
notify name, description.to_s, image_path
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/bolt/runner.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Bolt::Runner
|
|
3
|
+
#
|
|
4
|
+
# The Runner maps the changed file to the appropriate test file and runs it
|
|
5
|
+
#
|
|
6
|
+
module Bolt
|
|
7
|
+
class Runner
|
|
8
|
+
attr_accessor :selected, :notifier
|
|
9
|
+
|
|
10
|
+
# Constructor
|
|
11
|
+
def initialize
|
|
12
|
+
# find appropriate listener
|
|
13
|
+
$stdout.puts "** Using #{runner.class}... \n"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Pick a listener to launch
|
|
17
|
+
def runner
|
|
18
|
+
return selected if selected
|
|
19
|
+
# TODO: os identification via RUBY_PLATFORM is flawed as it will return 'java' in jruby. Look for a different solution
|
|
20
|
+
|
|
21
|
+
self.selected= Bolt::Runners::TestUnit.new
|
|
22
|
+
self.selected= Bolt::Runners::RSpec.new if File.directory?('spec')
|
|
23
|
+
selected
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
require 'spec'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
#
|
|
4
|
+
# Bolt::Runners::Rspec
|
|
5
|
+
#
|
|
6
|
+
# The Rspec Runner maps the filename to the appropriate spec
|
|
7
|
+
#
|
|
8
|
+
module Bolt
|
|
9
|
+
module Runners
|
|
10
|
+
class RSpec
|
|
11
|
+
|
|
12
|
+
MAPPINGS = /(\.\/app\/|\.\/lib\/|\.\/spec\/controllers|\.\/spec\/models|\.\/spec)/
|
|
13
|
+
|
|
14
|
+
attr_accessor :notifier, :test_io
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# handle specified file
|
|
20
|
+
def handle(filename)
|
|
21
|
+
puts '=> Rspec running test for ' + filename
|
|
22
|
+
|
|
23
|
+
# force reload of file
|
|
24
|
+
$".delete(filename)
|
|
25
|
+
$".delete(File.join(Dir.pwd, filename))
|
|
26
|
+
|
|
27
|
+
klassname = filename.sub('app/controllers', '').sub('app/models', '').sub('lib/', '')
|
|
28
|
+
test_class = klassname.sub('.rb', '').gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
|
29
|
+
root_klass = test_class.split('::').first
|
|
30
|
+
|
|
31
|
+
# remove the top constant/class from memory
|
|
32
|
+
# this is required to rebuild classes before test run
|
|
33
|
+
# one limitation - Spec/Test cannot be reloaded or it will crash
|
|
34
|
+
Object.send(:remove_const, root_klass) unless root_klass == 'Spec' or root_klass == 'Test'
|
|
35
|
+
|
|
36
|
+
if filename.include?('app/controllers') or filename.include?('app/models') or filename.include?('lib/')
|
|
37
|
+
begin
|
|
38
|
+
require File.join(Dir.pwd, filename)
|
|
39
|
+
rescue LoadError
|
|
40
|
+
notifier.error("Error in #{filename}", $!)
|
|
41
|
+
return false
|
|
42
|
+
rescue ArgumentError
|
|
43
|
+
notifier.error("Error in #{filename}", $!)
|
|
44
|
+
return false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
test_files = translate(filename)
|
|
49
|
+
|
|
50
|
+
return if test_files == []
|
|
51
|
+
|
|
52
|
+
puts '==== Rspec running: ' + test_files.join(', ') + ' ===='
|
|
53
|
+
|
|
54
|
+
run(test_files)
|
|
55
|
+
|
|
56
|
+
puts '==== Rspec completed run ===='
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# check whether file exists
|
|
60
|
+
def file_verified?(filename)
|
|
61
|
+
if !File.exists?(filename)
|
|
62
|
+
notifier.test_file_missing(filename)
|
|
63
|
+
puts "=> ERROR: could not find spec file: #{filename}"
|
|
64
|
+
return false
|
|
65
|
+
end
|
|
66
|
+
return true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# mapping is a copied and modified version of mislav/rspactor Inspector#translate
|
|
70
|
+
def translate(file)
|
|
71
|
+
|
|
72
|
+
basename = File.basename(file)
|
|
73
|
+
candidates = []
|
|
74
|
+
test_filename = nil
|
|
75
|
+
case file
|
|
76
|
+
when %r:^app/controllers/:
|
|
77
|
+
test_filename = file.sub('.rb', '_spec.rb').sub('app/controllers', 'spec/controllers')
|
|
78
|
+
when %r:^app/models/:
|
|
79
|
+
test_filename = "spec/models/#{basename.sub('.rb', '_spec.rb')}"
|
|
80
|
+
when %r:^app/views/:
|
|
81
|
+
file = file.sub('app/views/', '')
|
|
82
|
+
directory = file.split('/')[0..-2].compact.join('/')
|
|
83
|
+
test_filename = "spec/controllers/#{directory}_controller_spec.rb"
|
|
84
|
+
when %r:^spec/:
|
|
85
|
+
test_filename = file
|
|
86
|
+
when %r:^lib/:
|
|
87
|
+
# map libs to straight specs
|
|
88
|
+
test_filename = "spec/#{file.sub('lib/', '').sub('.rb', '_spec.rb')}"
|
|
89
|
+
when 'config/routes.rb'
|
|
90
|
+
test_filename = "spec/controllers/#{basename.sub('.rb', '_spec.rb')}"
|
|
91
|
+
when 'config/database.yml', 'db/schema.rb'
|
|
92
|
+
#candidates << 'models'
|
|
93
|
+
else
|
|
94
|
+
#
|
|
95
|
+
end
|
|
96
|
+
if test_filename and file_verified?(test_filename)
|
|
97
|
+
candidates << test_filename
|
|
98
|
+
end
|
|
99
|
+
if candidates == []
|
|
100
|
+
puts "=> NOTICE: could not find spec file for: #{file}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
candidates
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def run(files)
|
|
107
|
+
file = files.first
|
|
108
|
+
|
|
109
|
+
require 'spec'
|
|
110
|
+
|
|
111
|
+
# redirect spec output to StringIO
|
|
112
|
+
io = StringIO.new
|
|
113
|
+
::Spec::Runner.use(::Spec::Runner::OptionParser.new($stderr, io).options)
|
|
114
|
+
|
|
115
|
+
# refresh the loaded test file
|
|
116
|
+
$".delete(file)
|
|
117
|
+
require file
|
|
118
|
+
|
|
119
|
+
# run the tests in the Spec::Runner
|
|
120
|
+
::Spec::Runner::CommandLine.run
|
|
121
|
+
|
|
122
|
+
# recreate the reporter to refresh the example count
|
|
123
|
+
::Spec::Runner::Reporter.new(::Spec::Runner.options)
|
|
124
|
+
|
|
125
|
+
# remove all examples up to date
|
|
126
|
+
::Spec::Runner.options.example_groups.each { |g| ::Spec::Runner.options.remove_example_group(g) }
|
|
127
|
+
|
|
128
|
+
# read the buffer
|
|
129
|
+
result = io.string.to_s.dup
|
|
130
|
+
|
|
131
|
+
# send buffer to stdout
|
|
132
|
+
puts result
|
|
133
|
+
|
|
134
|
+
# sent result to notifier
|
|
135
|
+
notifier.result(file, result.split("\n").compact.last)
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'test/unit/ui/console/testrunner'
|
|
3
|
+
#
|
|
4
|
+
# Bolt::Runners::TestUnit
|
|
5
|
+
#
|
|
6
|
+
# The TestUnit Runners maps the filename to the appropriate test
|
|
7
|
+
#
|
|
8
|
+
module Bolt
|
|
9
|
+
module Runners
|
|
10
|
+
class TestUnit
|
|
11
|
+
|
|
12
|
+
MAPPINGS = /(\.\/app\/|\.\/lib\/|\.\/test\/functional|\.\/test\/unit)/
|
|
13
|
+
|
|
14
|
+
attr_accessor :notifier, :test_io
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
fix_test_unit_io
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def fix_test_unit_io
|
|
21
|
+
# Test::Unit stdio capture workaround, taken from Roman2K-rails-test-serving
|
|
22
|
+
self.test_io = StringIO.new
|
|
23
|
+
io = test_io
|
|
24
|
+
Test::Unit::UI::Console::TestRunner.class_eval do
|
|
25
|
+
alias_method :old_initialize, :initialize
|
|
26
|
+
def initialize(suite, output_level, io=Thread.current["test_runner_io"])
|
|
27
|
+
old_initialize(suite, output_level, io)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
Thread.current["test_runner_io"] = io
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# handle specified file
|
|
34
|
+
def handle(file)
|
|
35
|
+
|
|
36
|
+
# force reload of file
|
|
37
|
+
$".delete(file)
|
|
38
|
+
$".delete(File.join(Dir.pwd, file))
|
|
39
|
+
|
|
40
|
+
if file.include?('app/controllers') or file.include?('app/models') or file.include?('lib/')
|
|
41
|
+
begin
|
|
42
|
+
require File.join(Dir.pwd, file)
|
|
43
|
+
rescue LoadError
|
|
44
|
+
notifier.error("Error in #{file}", $!)
|
|
45
|
+
return []
|
|
46
|
+
rescue ArgumentError
|
|
47
|
+
notifier.error("Error in #{file}", $!)
|
|
48
|
+
return []
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
puts '=> Test::Unit running test for ' + file
|
|
53
|
+
test_files = translate(file)
|
|
54
|
+
|
|
55
|
+
puts '==== Test::Unit running: ' + test_files.join(', ') + ' ===='
|
|
56
|
+
|
|
57
|
+
run(test_files) if test_files != []
|
|
58
|
+
|
|
59
|
+
puts '==== Test::Unit completed run ===='
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# check whether file exists
|
|
64
|
+
def file_verified?(filename)
|
|
65
|
+
if !File.exists?(filename)
|
|
66
|
+
notifier.test_file_missing(filename)
|
|
67
|
+
puts "=> ERROR: could not find test file: #{filename}"
|
|
68
|
+
return false
|
|
69
|
+
end
|
|
70
|
+
return true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# mapping is a copied and modified version of mislav/rspactor Inspector#translate
|
|
74
|
+
def translate(file)
|
|
75
|
+
|
|
76
|
+
basename = File.basename(file)
|
|
77
|
+
candidates = []
|
|
78
|
+
test_filename = nil
|
|
79
|
+
case file
|
|
80
|
+
when %r:^app/controllers/:
|
|
81
|
+
test_filename = file.sub('.rb', '_test.rb').sub('app/controllers', 'test/functional')
|
|
82
|
+
when %r:^app/models/:
|
|
83
|
+
test_filename = "test/unit/#{basename.sub('.rb', '_test.rb')}"
|
|
84
|
+
when %r:^app/views/:
|
|
85
|
+
file = file.sub('app/views/', '')
|
|
86
|
+
directory = file.split('/')[0..-2].compact.join('/')
|
|
87
|
+
test_filename = "test/functional/#{directory}_controller_test.rb"
|
|
88
|
+
when %r:^test/:
|
|
89
|
+
test_filename = file
|
|
90
|
+
when %r:^lib/:
|
|
91
|
+
# map libs to units
|
|
92
|
+
test_filename = "test/unit/#{file.sub('lib/', '').sub('.rb', '_test.rb')}"
|
|
93
|
+
when 'config/routes.rb'
|
|
94
|
+
test_filename = "test/functional/#{basename.sub('.rb', '_test.rb')}"
|
|
95
|
+
#candidates << 'controllers' << 'helpers' << 'views'
|
|
96
|
+
when 'config/database.yml', 'db/schema.rb'
|
|
97
|
+
#candidates << 'models'
|
|
98
|
+
else
|
|
99
|
+
#
|
|
100
|
+
end
|
|
101
|
+
if test_filename and file_verified?(test_filename)
|
|
102
|
+
candidates << test_filename
|
|
103
|
+
end
|
|
104
|
+
if candidates == []
|
|
105
|
+
puts "=> NOTICE: could not find test file for: #{file}"
|
|
106
|
+
end
|
|
107
|
+
# puts candidates.inspect
|
|
108
|
+
candidates
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def run(files)
|
|
112
|
+
file = files.first
|
|
113
|
+
puts "** Running #{file}"
|
|
114
|
+
# make sure that you reload the test file
|
|
115
|
+
#load file
|
|
116
|
+
#contents = File.open(file).read
|
|
117
|
+
# puts contents
|
|
118
|
+
#eval contents
|
|
119
|
+
|
|
120
|
+
# This is Rails' String#camelcase
|
|
121
|
+
klassname = file.sub('test/functional/', '').sub('test/unit/', '')
|
|
122
|
+
test_class = klassname.sub('.rb', '').gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
|
123
|
+
|
|
124
|
+
# create dummy wrapper modules if test is in subfolder
|
|
125
|
+
test_class.split('::').each do |part|
|
|
126
|
+
eval "module ::#{part}; end" if !part.match('Test')
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
$".delete(file)
|
|
130
|
+
|
|
131
|
+
#(defined?(ActiveRecord::Base) ? ActiveRecord::Base.instance_eval { subclasses }.each { |c| c.reset_column_information } : nil)
|
|
132
|
+
#(defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies.clear : nil)
|
|
133
|
+
|
|
134
|
+
begin
|
|
135
|
+
require file
|
|
136
|
+
rescue LoadError
|
|
137
|
+
notifier.error("Error in #{file}", $!)
|
|
138
|
+
return
|
|
139
|
+
rescue ArgumentError
|
|
140
|
+
notifier.error("Error in #{file}", $!)
|
|
141
|
+
return
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# TODO: change that to run multiple suites
|
|
145
|
+
#klass = Kernel.const_get(test_class) - this threw errors
|
|
146
|
+
klass = eval(test_class)
|
|
147
|
+
|
|
148
|
+
Test::Unit::UI::Console::TestRunner.run(klass)
|
|
149
|
+
|
|
150
|
+
# Invoke method to test that writes to stdout.
|
|
151
|
+
result = test_io.string.to_s.dup
|
|
152
|
+
|
|
153
|
+
# clear the buffer
|
|
154
|
+
test_io.truncate(0)
|
|
155
|
+
|
|
156
|
+
# sent result to notifier
|
|
157
|
+
notifier.result(file, result.split("\n").compact.last)
|
|
158
|
+
|
|
159
|
+
# sent result to stdio
|
|
160
|
+
puts result
|
|
161
|
+
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
data/lib/bolt.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Why Bolt? Cause it's a cool name, that's why :)
|
|
2
|
+
module Bolt
|
|
3
|
+
autoload :Mapper, 'bolt/mapper'
|
|
4
|
+
autoload :Runner, 'bolt/runner'
|
|
5
|
+
autoload :Notifier, 'bolt/notifier'
|
|
6
|
+
autoload :Listener, 'bolt/listener'
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# Bolt::Listeners
|
|
10
|
+
#
|
|
11
|
+
# Wrapper for specific listeners
|
|
12
|
+
#
|
|
13
|
+
module Listeners
|
|
14
|
+
autoload :Generic, 'bolt/listeners/generic'
|
|
15
|
+
autoload :Kqueue, 'bolt/listeners/kqueue'
|
|
16
|
+
autoload :Osx, 'bolt/listeners/osx'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
#
|
|
20
|
+
# Bolt::Runners
|
|
21
|
+
#
|
|
22
|
+
# Wrapper for specific runners
|
|
23
|
+
#
|
|
24
|
+
module Runners
|
|
25
|
+
autoload :TestUnit, 'bolt/runners/test_unit'
|
|
26
|
+
autoload :RSpec, 'bolt/runners/rspec'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Bolt::Notifiers
|
|
31
|
+
#
|
|
32
|
+
# Wrapper for specific notifier
|
|
33
|
+
#
|
|
34
|
+
module Notifiers
|
|
35
|
+
autoload :Generic, 'bolt/notifiers/generic'
|
|
36
|
+
autoload :Growl, 'bolt/notifiers/growl'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'spec'
|
|
2
|
+
require 'bolt/runners/rspec'
|
|
3
|
+
|
|
4
|
+
describe Bolt::Runners::RSpec do
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
@runner = described_class.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'should translate controllers' do
|
|
11
|
+
@runner.stub('file_verified?').and_return(true)
|
|
12
|
+
@runner.translate('app/controllers/test_controller.rb').should == ['spec/controllers/test_controller_spec.rb']
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should translate models' do
|
|
16
|
+
@runner.stub('file_verified?').and_return(true)
|
|
17
|
+
@runner.translate('app/models/test.rb').should == ['spec/models/test_spec.rb']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should translate views' do
|
|
21
|
+
@runner.stub('file_verified?').and_return(true)
|
|
22
|
+
@runner.translate('app/views/test/test.html.erb').should == ['spec/controllers/test_controller_spec.rb']
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should translate lib' do
|
|
26
|
+
@runner.stub('file_verified?').and_return(true)
|
|
27
|
+
@runner.translate('lib/test.rb').should == ['spec/test_spec.rb']
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should translate lib with subfolders' do
|
|
31
|
+
@runner.stub('file_verified?').and_return(true)
|
|
32
|
+
@runner.translate('lib/testing/test.rb').should == ['spec/testing/test_spec.rb']
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'should translate specs to themselves' do
|
|
36
|
+
@runner.stub('file_verified?').and_return(true)
|
|
37
|
+
@runner.translate('spec/controllers/test_controller_spec.rb').should == ['spec/controllers/test_controller_spec.rb']
|
|
38
|
+
@runner.translate('spec/test_spec.rb').should == ['spec/test_spec.rb']
|
|
39
|
+
@runner.translate('spec/testing/test_spec.rb').should == ['spec/testing/test_spec.rb']
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'should return no results if file is not present' do
|
|
43
|
+
@runner.stub('file_verified?').and_return(false)
|
|
44
|
+
@runner.translate('lib/testing/test.rb').should == []
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'spec'
|
|
2
|
+
require 'bolt/runners/test_unit'
|
|
3
|
+
|
|
4
|
+
describe Bolt::Runners::TestUnit do
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
@runner = described_class.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'should translate controllers' do
|
|
11
|
+
@runner.stub('file_verified?').and_return(true)
|
|
12
|
+
@runner.translate('app/controllers/test_controller.rb').should == ['test/functional/test_controller_test.rb']
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should translate models' do
|
|
16
|
+
@runner.stub('file_verified?').and_return(true)
|
|
17
|
+
@runner.translate('app/models/test.rb').should == ['test/unit/test_test.rb']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should translate views' do
|
|
21
|
+
@runner.stub('file_verified?').and_return(true)
|
|
22
|
+
@runner.translate('app/views/test/test.html.erb').should == ['test/functional/test_controller_test.rb']
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should translate lib' do
|
|
26
|
+
@runner.stub('file_verified?').and_return(true)
|
|
27
|
+
@runner.translate('lib/test.rb').should == ['test/unit/test_test.rb']
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should translate lib with subfolders' do
|
|
31
|
+
@runner.stub('file_verified?').and_return(true)
|
|
32
|
+
@runner.translate('lib/testing/test.rb').should == ['test/unit/testing/test_test.rb']
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'should translate tests to itself' do
|
|
36
|
+
@runner.stub('file_verified?').and_return(true)
|
|
37
|
+
@runner.translate('test/functional/test_controller_test.rb').should == ['test/functional/test_controller_test.rb']
|
|
38
|
+
@runner.translate('test/unit/test_test.rb').should == ['test/unit/test_test.rb']
|
|
39
|
+
@runner.translate('test/unit/testing/test_test.rb').should == ['test/unit/testing/test_test.rb']
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'should return no results if file is not present' do
|
|
43
|
+
@runner.stub('file_verified?').and_return(false)
|
|
44
|
+
@runner.translate('lib/testing/test.rb').should == []
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: marcinbunsch-bolt
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Marcin Bunsch
|
|
8
|
+
- "Mislav Marohni\xC4\x87"
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2009-06-15 00:00:00 -07:00
|
|
14
|
+
default_executable: bolt
|
|
15
|
+
dependencies: []
|
|
16
|
+
|
|
17
|
+
description:
|
|
18
|
+
email: marcin@applicake.com
|
|
19
|
+
executables:
|
|
20
|
+
- bolt
|
|
21
|
+
extensions: []
|
|
22
|
+
|
|
23
|
+
extra_rdoc_files: []
|
|
24
|
+
|
|
25
|
+
files:
|
|
26
|
+
- Rakefile
|
|
27
|
+
- bin/bolt
|
|
28
|
+
- lib/bolt
|
|
29
|
+
- lib/bolt/listener.rb
|
|
30
|
+
- lib/bolt/listeners
|
|
31
|
+
- lib/bolt/listeners/generic.rb
|
|
32
|
+
- lib/bolt/listeners/osx.rb
|
|
33
|
+
- lib/bolt/notifier.rb
|
|
34
|
+
- lib/bolt/notifiers
|
|
35
|
+
- lib/bolt/notifiers/generic.rb
|
|
36
|
+
- lib/bolt/notifiers/growl.rb
|
|
37
|
+
- lib/bolt/runner.rb
|
|
38
|
+
- lib/bolt/runners
|
|
39
|
+
- lib/bolt/runners/rspec.rb
|
|
40
|
+
- lib/bolt/runners/test_unit.rb
|
|
41
|
+
- lib/bolt.rb
|
|
42
|
+
- images/failed.png
|
|
43
|
+
- images/LICENSE
|
|
44
|
+
- images/pending.png
|
|
45
|
+
- images/success.png
|
|
46
|
+
- spec/bolt
|
|
47
|
+
- spec/bolt/runners
|
|
48
|
+
- spec/bolt/runners/rspec_spec.rb
|
|
49
|
+
- spec/bolt/runners/test_unit_spec.rb
|
|
50
|
+
- README.textile
|
|
51
|
+
- LICENSE
|
|
52
|
+
has_rdoc: false
|
|
53
|
+
homepage: http://github.com/marcinbunsch/bolt
|
|
54
|
+
post_install_message:
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
|
|
57
|
+
require_paths:
|
|
58
|
+
- lib
|
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: "0"
|
|
64
|
+
version:
|
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: "0"
|
|
70
|
+
version:
|
|
71
|
+
requirements: []
|
|
72
|
+
|
|
73
|
+
rubyforge_project:
|
|
74
|
+
rubygems_version: 1.2.0
|
|
75
|
+
signing_key:
|
|
76
|
+
specification_version: 3
|
|
77
|
+
summary: Bolt is a merge of autotest and mislav/rspactor to produce a lightning fast, configurable and simple to set up autotest clone
|
|
78
|
+
test_files: []
|
|
79
|
+
|