marcinbunsch-bolt 0.2.3 → 0.2.4
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/.bolt +1 -3
- data/Rakefile +12 -0
- data/lib/bolt/listener.rb +6 -3
- data/lib/bolt/listeners/generic.rb +2 -2
- data/lib/bolt/listeners/osx.rb +2 -2
- data/lib/bolt/notifier.rb +3 -3
- data/lib/bolt/runner.rb +5 -4
- data/lib/bolt/runners/base.rb +131 -0
- data/lib/bolt/runners/cucumber.rb +142 -0
- data/lib/bolt/runners/rspec.rb +7 -65
- data/lib/bolt/runners/test_unit.rb +14 -77
- data/lib/bolt.rb +15 -2
- data/spec/bolt/runners/test_unit_spec.rb +41 -0
- metadata +4 -2
data/.bolt
CHANGED
data/Rakefile
CHANGED
|
@@ -35,6 +35,18 @@ task :run do
|
|
|
35
35
|
system('ruby -I lib bin/bolt')
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
desc 'Reinstall the gem locally'
|
|
39
|
+
task :reinstall do
|
|
40
|
+
f = File.open('VERSION')
|
|
41
|
+
version = f.read.gsub("\n", '')
|
|
42
|
+
f.close
|
|
43
|
+
system("sudo gem uninstall bolt")
|
|
44
|
+
system("gem build bolt.gemspec")
|
|
45
|
+
system("sudo gem install bolt-#{version}.gem")
|
|
46
|
+
system("rm bolt-#{version}.gem")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
38
50
|
task :default => :run
|
|
39
51
|
|
|
40
52
|
require 'rake/rdoctask'
|
data/lib/bolt/listener.rb
CHANGED
|
@@ -12,7 +12,9 @@ module Bolt
|
|
|
12
12
|
# Constructor
|
|
13
13
|
def initialize
|
|
14
14
|
# find appropriate listener
|
|
15
|
-
|
|
15
|
+
listener
|
|
16
|
+
|
|
17
|
+
$stdout.puts "** Using #{listener.class} " if Bolt['verbose']
|
|
16
18
|
|
|
17
19
|
# trap the INT signal
|
|
18
20
|
add_sigint_handler
|
|
@@ -57,11 +59,11 @@ module Bolt
|
|
|
57
59
|
if Bolt['listener'] and ['generic', 'osx'].include?(Bolt['listener'])
|
|
58
60
|
self.selected= Bolt::Listeners::Generic.new if Bolt['listener'] == 'generic'
|
|
59
61
|
self.selected= Bolt::Listeners::Osx.new if Bolt['listener'] == 'osx'
|
|
60
|
-
$stdout.puts "** Found listener setting in .bolt"
|
|
62
|
+
$stdout.puts "** Found listener setting in .bolt" if Bolt['verbose']
|
|
61
63
|
return self.selected
|
|
62
64
|
end
|
|
63
65
|
|
|
64
|
-
$stdout.puts "** Determining listener..."
|
|
66
|
+
$stdout.puts "** Determining listener..." if Bolt['verbose']
|
|
65
67
|
|
|
66
68
|
# TODO: os identification via RUBY_PLATFORM is flawed as it will return 'java' in jruby. Look for a different solution
|
|
67
69
|
os_string = RUBY_PLATFORM.downcase
|
|
@@ -74,6 +76,7 @@ module Bolt
|
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
# capture the INT signal
|
|
79
|
+
# TODO: Move to Bolt.add_sigint_handler
|
|
77
80
|
def add_sigint_handler
|
|
78
81
|
trap 'INT' do
|
|
79
82
|
$stdout.puts "\n** Exiting Bolt..."
|
|
@@ -15,7 +15,7 @@ module Bolt
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def start
|
|
18
|
-
puts "** #{self.class} is scanning for files... "
|
|
18
|
+
puts "** #{self.class} is scanning for files... " if Bolt['verbose']
|
|
19
19
|
# build a file collection
|
|
20
20
|
find_files
|
|
21
21
|
puts "** #{self.class} watching #{files.size} files... "
|
|
@@ -37,7 +37,7 @@ module Bolt
|
|
|
37
37
|
updated << filename
|
|
38
38
|
# update the mtime in file registry so we it's only send once
|
|
39
39
|
files[filename] = current_mtime
|
|
40
|
-
$stdout.puts ">> Spotted change in #{filename}"
|
|
40
|
+
$stdout.puts ">> Spotted change in #{filename}" if Bolt['verbose']
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
parent.handle(updated) if updated != []
|
data/lib/bolt/listeners/osx.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Bolt
|
|
|
27
27
|
begin
|
|
28
28
|
require 'osx/foundation'
|
|
29
29
|
rescue LoadError
|
|
30
|
-
puts "** Could not load osx/foundation. RubyCocoa not installed? Falling back to Bolt::Listeners::Generic"
|
|
30
|
+
puts "** Could not load osx/foundation. RubyCocoa not installed? Falling back to Bolt::Listeners::Generic" if Bolt['verbose']
|
|
31
31
|
return Bolt::Listeners::Generic.new
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -35,7 +35,7 @@ module Bolt
|
|
|
35
35
|
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
|
|
36
36
|
return self.new
|
|
37
37
|
rescue NameError
|
|
38
|
-
puts "** There was an error loading Bolt::Listeners::Osx. Falling back to Bolt::Listeners::Generic"
|
|
38
|
+
puts "** There was an error loading Bolt::Listeners::Osx. Falling back to Bolt::Listeners::Generic" if Bolt['verbose']
|
|
39
39
|
return Bolt::Listeners::Generic.new
|
|
40
40
|
end
|
|
41
41
|
end
|
data/lib/bolt/notifier.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Bolt
|
|
|
12
12
|
# find appropriate notifier
|
|
13
13
|
notifier
|
|
14
14
|
# present
|
|
15
|
-
$stdout.puts "** Using #{notifier.class} \n"
|
|
15
|
+
$stdout.puts "** Using #{notifier.class} \n" if Bolt['verbose']
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Pick a listener to launch
|
|
@@ -24,11 +24,11 @@ module Bolt
|
|
|
24
24
|
self.selected= Bolt::Notifiers::Growl.new if Bolt['notifier'] == 'growl'
|
|
25
25
|
self.selected= Bolt::Notifiers::Generic.new if Bolt['notifier'] == 'generic'
|
|
26
26
|
self.selected= Bolt::Notifiers::NotifyOsd.new if Bolt['notifier'] == 'notify_send'
|
|
27
|
-
$stdout.puts "** Found 'notifier' setting in .bolt"
|
|
27
|
+
$stdout.puts "** Found 'notifier' setting in .bolt" if Bolt['verbose']
|
|
28
28
|
return self.selected
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
$stdout.puts "** Determining notifier... \n"
|
|
31
|
+
$stdout.puts "** Determining notifier... \n" if Bolt['verbose']
|
|
32
32
|
|
|
33
33
|
# default - growl (if growlnotify is present)
|
|
34
34
|
output = %x[which growlnotify]
|
data/lib/bolt/runner.rb
CHANGED
|
@@ -12,20 +12,21 @@ module Bolt
|
|
|
12
12
|
# find appropriate runner
|
|
13
13
|
runner
|
|
14
14
|
|
|
15
|
-
$stdout.puts "** Using #{selected.class} \n"
|
|
15
|
+
$stdout.puts "** Using #{selected.class} \n" if Bolt['verbose']
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Pick a listener to launch
|
|
19
19
|
def runner
|
|
20
20
|
return selected if selected
|
|
21
21
|
|
|
22
|
-
if Bolt['runner'] and ['test_unit', 'rspec'].include?(Bolt['runner'])
|
|
22
|
+
if Bolt['runner'] and ['test_unit', 'rspec', 'cucumber'].include?(Bolt['runner'])
|
|
23
23
|
self.selected= Bolt::Runners::TestUnit.new if Bolt['runner'] == 'test_unit'
|
|
24
24
|
self.selected= Bolt::Runners::RSpec.new if Bolt['runner'] == 'rspec'
|
|
25
|
-
|
|
25
|
+
self.selected= Bolt::Runners::Cucumber.new if Bolt['runner'] == 'cucumber'
|
|
26
|
+
$stdout.puts "** Found 'runner' setting in .bolt" if Bolt['verbose']
|
|
26
27
|
return self.selected
|
|
27
28
|
end
|
|
28
|
-
$stdout.puts "** Determining runner... \n"
|
|
29
|
+
$stdout.puts "** Determining runner... \n" if Bolt['verbose']
|
|
29
30
|
self.selected= Bolt::Runners::TestUnit.new
|
|
30
31
|
self.selected= Bolt::Runners::RSpec.new if File.directory?('spec')
|
|
31
32
|
self.selected
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Bolt::Runners::Base
|
|
3
|
+
#
|
|
4
|
+
# Abstract base class for runners
|
|
5
|
+
#
|
|
6
|
+
module Bolt
|
|
7
|
+
module Runners
|
|
8
|
+
class Base
|
|
9
|
+
|
|
10
|
+
# mappings define which folders hold the files that the listener should listen to
|
|
11
|
+
MAPPINGS = /(\.\/app\/|\.\/lib\/)/
|
|
12
|
+
|
|
13
|
+
# class map specifies the folders holding classes that can be reloaded
|
|
14
|
+
CLASS_MAP = /(app\/controllers|app\/models|lib\/)/
|
|
15
|
+
|
|
16
|
+
# do not allow this class to be instantiated
|
|
17
|
+
def initialized
|
|
18
|
+
raise NotImplementedError
|
|
19
|
+
end
|
|
20
|
+
# /initialized
|
|
21
|
+
|
|
22
|
+
# handle an updated file
|
|
23
|
+
def handle(filename)
|
|
24
|
+
|
|
25
|
+
reload filename
|
|
26
|
+
|
|
27
|
+
puts "=> #{self.class} running test for #{filename}" if Bolt['verbose']
|
|
28
|
+
test_files = translate(filename)
|
|
29
|
+
|
|
30
|
+
return if test_files == []
|
|
31
|
+
|
|
32
|
+
puts "==== #{self.class} running: #{ test_files.join(', ')} ===="
|
|
33
|
+
|
|
34
|
+
run(test_files)
|
|
35
|
+
|
|
36
|
+
puts "==== #{self.class} completed run ===="
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# translate the filename into a test
|
|
41
|
+
def translate(filename)
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# run the specified test
|
|
46
|
+
def run(test_filename)
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# check whether file exists
|
|
51
|
+
def file_verified?(filename)
|
|
52
|
+
if !File.exists?(filename)
|
|
53
|
+
notifier.test_file_missing(filename)
|
|
54
|
+
puts "=> ERROR: could not find test file: #{filename}"
|
|
55
|
+
return false
|
|
56
|
+
end
|
|
57
|
+
return true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# get the classname based on file
|
|
61
|
+
def resolve_classname(filename)
|
|
62
|
+
filename.sub('.rb', '').gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# resolve the class based on filename
|
|
66
|
+
def resolve_class(filename)
|
|
67
|
+
klassname = resolve_classname(filename)
|
|
68
|
+
begin
|
|
69
|
+
return eval(klassname)
|
|
70
|
+
rescue NameError
|
|
71
|
+
return false
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# clear the class of methods before reload
|
|
76
|
+
def clear_class(klass)
|
|
77
|
+
begin
|
|
78
|
+
klass.instance_methods.each do |m|
|
|
79
|
+
next if m.to_s == '__id__'
|
|
80
|
+
next if m.to_s == '__send__'
|
|
81
|
+
begin
|
|
82
|
+
klass.send(:remove_method, m)
|
|
83
|
+
rescue
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
rescue NameError
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def load_file(filename)
|
|
91
|
+
# load file again to rebuild the class we just ripped apart
|
|
92
|
+
if filename.match(self.class::CLASS_MAP)
|
|
93
|
+
begin
|
|
94
|
+
require File.join(Dir.pwd, filename)
|
|
95
|
+
rescue LoadError
|
|
96
|
+
notifier.error("Error in #{filename}", $!)
|
|
97
|
+
return false
|
|
98
|
+
rescue ArgumentError
|
|
99
|
+
notifier.error("Error in #{filename}", $!)
|
|
100
|
+
return false
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# force reload of a file/class
|
|
106
|
+
def reload(filename)
|
|
107
|
+
|
|
108
|
+
# remove the file from list of loaded files
|
|
109
|
+
$".delete(filename)
|
|
110
|
+
$".delete(File.join(Dir.pwd, filename))
|
|
111
|
+
|
|
112
|
+
# remove methods from class (if present)
|
|
113
|
+
if filename.match(self.class::CLASS_MAP)
|
|
114
|
+
class_filename = filename.sub(self.class::CLASS_MAP, '')
|
|
115
|
+
|
|
116
|
+
# get the class
|
|
117
|
+
klass = resolve_class(class_filename)
|
|
118
|
+
|
|
119
|
+
# remove all methods - don't worry, the reload will bring them back refreshed
|
|
120
|
+
clear_class(klass) if klass
|
|
121
|
+
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
load_file(filename)
|
|
125
|
+
|
|
126
|
+
end
|
|
127
|
+
# /reload
|
|
128
|
+
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Bolt::Runners::Cucumber
|
|
3
|
+
#
|
|
4
|
+
# The Cucumber Runner maps the filename to the appropriate feature
|
|
5
|
+
#
|
|
6
|
+
module Bolt
|
|
7
|
+
module Runners
|
|
8
|
+
class Cucumber < Bolt::Runners::Base
|
|
9
|
+
|
|
10
|
+
# mappings define which folders hold the files that the listener should listen to
|
|
11
|
+
MAPPINGS = /(\.\/app\/|\.\/lib\/|\.\/features\/)/
|
|
12
|
+
|
|
13
|
+
# class map specifies the folders holding classes that can be reloaded
|
|
14
|
+
CLASS_MAP = /(app\/controllers\/|app\/models\/|lib\/)/
|
|
15
|
+
|
|
16
|
+
attr_accessor :notifier, :test_io, :heard
|
|
17
|
+
|
|
18
|
+
# step mother storage
|
|
19
|
+
@@mother = nil
|
|
20
|
+
|
|
21
|
+
def self.mother=(step_mother)
|
|
22
|
+
@@mother = step_mother
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.mother
|
|
26
|
+
@@mother
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# mapping is a copied and modified version of mislav/rspactor Inspector#translate
|
|
30
|
+
def translate(file)
|
|
31
|
+
self.heard = file
|
|
32
|
+
if file.match('other')
|
|
33
|
+
return ['features/other.feature']
|
|
34
|
+
else
|
|
35
|
+
return ['features/posts.feature']
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
basename = File.basename(file)
|
|
39
|
+
candidates = []
|
|
40
|
+
test_filename = nil
|
|
41
|
+
case file
|
|
42
|
+
when %r:^app/controllers/:
|
|
43
|
+
test_filename = file.sub('.rb', '_spec.rb').sub('app/controllers', 'spec/controllers')
|
|
44
|
+
when %r:^app/models/:
|
|
45
|
+
test_filename = "spec/models/#{basename.sub('.rb', '_spec.rb')}"
|
|
46
|
+
when %r:^app/views/:
|
|
47
|
+
file = file.sub('app/views/', '')
|
|
48
|
+
directory = file.split('/')[0..-2].compact.join('/')
|
|
49
|
+
test_filename = "spec/controllers/#{directory}_controller_spec.rb"
|
|
50
|
+
when %r:^spec/:
|
|
51
|
+
test_filename = file
|
|
52
|
+
when %r:^lib/:
|
|
53
|
+
# map libs to straight specs
|
|
54
|
+
test_filename = "spec/#{file.sub('lib/', '').sub('.rb', '_spec.rb')}"
|
|
55
|
+
when 'config/routes.rb'
|
|
56
|
+
test_filename = "spec/controllers/#{basename.sub('.rb', '_spec.rb')}"
|
|
57
|
+
when 'config/database.yml', 'db/schema.rb'
|
|
58
|
+
#candidates << 'models'
|
|
59
|
+
else
|
|
60
|
+
#
|
|
61
|
+
end
|
|
62
|
+
if test_filename and file_verified?(test_filename)
|
|
63
|
+
candidates << test_filename
|
|
64
|
+
end
|
|
65
|
+
if candidates == []
|
|
66
|
+
puts "=> NOTICE: could not find feature file for: #{file}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
candidates
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def run(files)
|
|
73
|
+
file = files.first
|
|
74
|
+
|
|
75
|
+
# redirect spec output to StringIO
|
|
76
|
+
io = StringIO.new
|
|
77
|
+
|
|
78
|
+
$stdout, old = io, $stdout
|
|
79
|
+
# refresh the loaded test file
|
|
80
|
+
#$".delete(file)
|
|
81
|
+
#require file
|
|
82
|
+
|
|
83
|
+
Bolt::Runners::Cucumber.mother.reload_definitions! if Bolt::Runners::Cucumber.mother and self.heard.match('_steps.rb')
|
|
84
|
+
|
|
85
|
+
::Cucumber::Cli::Main.execute([file])
|
|
86
|
+
|
|
87
|
+
Bolt::Runners::Cucumber.mother.clear_steps_and_scenarios!
|
|
88
|
+
# read the buffer
|
|
89
|
+
result = io.string.to_s.dup
|
|
90
|
+
|
|
91
|
+
$stdout = old
|
|
92
|
+
|
|
93
|
+
# send buffer to stdout
|
|
94
|
+
puts result
|
|
95
|
+
|
|
96
|
+
last_three = result.split("\n")[-3..-1].join(' ')
|
|
97
|
+
last_three = last_three.gsub("\e[32m", '').gsub("\e[0m", '').gsub("\e[36m", '').gsub("\e[31m", '') # get ri of the color codes
|
|
98
|
+
|
|
99
|
+
# sent result to notifier
|
|
100
|
+
notifier.result(file, last_three)
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Cucumber hacks
|
|
109
|
+
require 'cucumber'
|
|
110
|
+
require 'cucumber/rspec_neuter'
|
|
111
|
+
require 'cucumber/cli/main'
|
|
112
|
+
|
|
113
|
+
module Cucumber
|
|
114
|
+
module StepMother
|
|
115
|
+
|
|
116
|
+
def reload_definitions!
|
|
117
|
+
step_definitions.clear
|
|
118
|
+
Dir['features/step_definitions/*'].map do |f|
|
|
119
|
+
$".delete(f)
|
|
120
|
+
require "features/step_definitions/#{File.basename(f)}"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def clear_steps_and_scenarios!
|
|
125
|
+
steps.clear
|
|
126
|
+
scenarios.clear
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
module Cli
|
|
131
|
+
class Main
|
|
132
|
+
|
|
133
|
+
def self.execute(args)
|
|
134
|
+
instance = new(args)
|
|
135
|
+
instance.execute!(@step_mother)
|
|
136
|
+
Bolt::Runners::Cucumber.mother = @step_mother
|
|
137
|
+
instance
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
data/lib/bolt/runners/rspec.rb
CHANGED
|
@@ -7,75 +7,16 @@ require 'stringio'
|
|
|
7
7
|
#
|
|
8
8
|
module Bolt
|
|
9
9
|
module Runners
|
|
10
|
-
class RSpec
|
|
10
|
+
class RSpec < Bolt::Runners::Base
|
|
11
11
|
|
|
12
|
+
# mappings define which folders hold the files that the listener should listen to
|
|
12
13
|
MAPPINGS = /(\.\/app\/|\.\/lib\/|\.\/spec\/controllers|\.\/spec\/models|\.\/spec)/
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def initialize
|
|
17
|
-
end
|
|
15
|
+
# class map specifies the folders holding classes that can be reloaded
|
|
16
|
+
CLASS_MAP = /(app\/controllers\/|app\/models\/|lib\/)/
|
|
18
17
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
puts '=> Rspec running test for ' + filename
|
|
22
|
-
|
|
23
|
-
# force reload of file
|
|
24
|
-
$".delete(filename)
|
|
25
|
-
$".delete(File.join(Dir.pwd, filename))
|
|
26
|
-
file = filename
|
|
27
|
-
# reload tests and classes
|
|
28
|
-
if file.match(/(app\/controllers|app\/models|lib\/)/)
|
|
29
|
-
puts '=================='
|
|
30
|
-
klassname = file.sub('app/controllers/', '').sub('app/models/', '').sub('lib/', '')
|
|
31
|
-
klass_to_be_tested = klassname.sub('.rb', '').gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# remove all methods - don't worry, the reload will bring them back refreshed
|
|
35
|
-
begin
|
|
36
|
-
klass = eval(klass_to_be_tested)
|
|
37
|
-
klass.instance_methods.each { |m|
|
|
38
|
-
begin
|
|
39
|
-
klass.send(:remove_method, m)
|
|
40
|
-
rescue
|
|
41
|
-
end
|
|
42
|
-
}
|
|
43
|
-
rescue NameError
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
if filename.include?('app/controllers') or filename.include?('app/models') or filename.include?('lib/')
|
|
48
|
-
begin
|
|
49
|
-
require File.join(Dir.pwd, filename)
|
|
50
|
-
rescue LoadError
|
|
51
|
-
notifier.error("Error in #{filename}", $!)
|
|
52
|
-
return false
|
|
53
|
-
rescue ArgumentError
|
|
54
|
-
notifier.error("Error in #{filename}", $!)
|
|
55
|
-
return false
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
test_files = translate(filename)
|
|
60
|
-
|
|
61
|
-
return if test_files == []
|
|
62
|
-
|
|
63
|
-
puts '==== Rspec running: ' + test_files.join(', ') + ' ===='
|
|
64
|
-
|
|
65
|
-
run(test_files)
|
|
66
|
-
|
|
67
|
-
puts '==== Rspec completed run ===='
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# check whether file exists
|
|
71
|
-
def file_verified?(filename)
|
|
72
|
-
if !File.exists?(filename)
|
|
73
|
-
notifier.test_file_missing(filename)
|
|
74
|
-
puts "=> ERROR: could not find spec file: #{filename}"
|
|
75
|
-
return false
|
|
76
|
-
end
|
|
77
|
-
return true
|
|
78
|
-
end
|
|
18
|
+
# accesors
|
|
19
|
+
attr_accessor :notifier, :test_io
|
|
79
20
|
|
|
80
21
|
# mapping is a copied and modified version of mislav/rspactor Inspector#translate
|
|
81
22
|
def translate(file)
|
|
@@ -114,6 +55,7 @@ module Bolt
|
|
|
114
55
|
candidates
|
|
115
56
|
end
|
|
116
57
|
|
|
58
|
+
# run the appropriate test
|
|
117
59
|
def run(files)
|
|
118
60
|
file = files.first
|
|
119
61
|
|
|
@@ -7,10 +7,14 @@ require 'test/unit/ui/console/testrunner'
|
|
|
7
7
|
#
|
|
8
8
|
module Bolt
|
|
9
9
|
module Runners
|
|
10
|
-
class TestUnit
|
|
10
|
+
class TestUnit < Bolt::Runners::Base
|
|
11
11
|
|
|
12
|
+
# mappings define which folders hold the files that the listener should listen to
|
|
12
13
|
MAPPINGS = /(\.\/app\/|\.\/lib\/|\.\/test\/functional|\.\/test\/unit)/
|
|
13
14
|
|
|
15
|
+
# class map specifies the folders holding classes that can be reloaded
|
|
16
|
+
CLASS_MAP = /(test\/functional\/|test\/unit\/|app\/controllers\/|app\/models\/|lib\/)/
|
|
17
|
+
|
|
14
18
|
attr_accessor :notifier, :test_io
|
|
15
19
|
|
|
16
20
|
def initialize
|
|
@@ -29,69 +33,7 @@ module Bolt
|
|
|
29
33
|
end
|
|
30
34
|
Thread.current["test_runner_io"] = io
|
|
31
35
|
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
|
-
# reload tests and classes
|
|
41
|
-
if file.match(/(test\/functional|test\/unit|app\/controllers|app\/models|lib\/)/)
|
|
42
|
-
puts '=================='
|
|
43
|
-
klassname = file.sub('test/unit/', '').sub('test/functional/', '').sub('app/controllers/', '').sub('app/models/', '').sub('lib/', '')
|
|
44
|
-
klass_to_be_tested = klassname.sub('.rb', '').gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# remove all methods - don't worry, the reload will bring them back refreshed
|
|
48
|
-
begin
|
|
49
|
-
klass = eval(klass_to_be_tested)
|
|
50
|
-
klass.instance_methods.each { |m|
|
|
51
|
-
begin
|
|
52
|
-
klass.send(:remove_method, m)
|
|
53
|
-
rescue
|
|
54
|
-
end
|
|
55
|
-
}
|
|
56
|
-
rescue NameError
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
if file.include?('app/controllers') or file.include?('app/models') or file.include?('lib/')
|
|
61
|
-
begin
|
|
62
|
-
filename = File.join(Dir.pwd, file)
|
|
63
|
-
filename << '.rb' if !filename.match('.rb')
|
|
64
|
-
load filename
|
|
65
|
-
rescue LoadError
|
|
66
|
-
notifier.error("Error in #{file}", $!)
|
|
67
|
-
return []
|
|
68
|
-
rescue ArgumentError
|
|
69
|
-
notifier.error("Error in #{file}", $!)
|
|
70
|
-
return []
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
puts '=> Test::Unit running test for ' + file
|
|
75
|
-
test_files = translate(file)
|
|
76
|
-
|
|
77
|
-
puts '==== Test::Unit running: ' + test_files.join(', ') + ' ===='
|
|
78
|
-
|
|
79
|
-
run(test_files) if test_files != []
|
|
80
|
-
|
|
81
|
-
puts '==== Test::Unit completed run ===='
|
|
82
|
-
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# check whether file exists
|
|
86
|
-
def file_verified?(filename)
|
|
87
|
-
if !File.exists?(filename)
|
|
88
|
-
notifier.test_file_missing(filename)
|
|
89
|
-
puts "=> ERROR: could not find test file: #{filename}"
|
|
90
|
-
return false
|
|
91
|
-
end
|
|
92
|
-
return true
|
|
93
|
-
end
|
|
94
|
-
|
|
36
|
+
|
|
95
37
|
# mapping is a copied and modified version of mislav/rspactor Inspector#translate
|
|
96
38
|
def translate(file)
|
|
97
39
|
|
|
@@ -124,7 +66,7 @@ module Bolt
|
|
|
124
66
|
candidates << test_filename
|
|
125
67
|
end
|
|
126
68
|
if candidates == []
|
|
127
|
-
puts "=> NOTICE: could not find test file for: #{file}"
|
|
69
|
+
puts "=> NOTICE: could not find test file for: #{file}" if Bolt['verbose']
|
|
128
70
|
end
|
|
129
71
|
# puts candidates.inspect
|
|
130
72
|
candidates
|
|
@@ -133,26 +75,20 @@ module Bolt
|
|
|
133
75
|
def run(files)
|
|
134
76
|
file = files.first
|
|
135
77
|
puts "** Running #{file}"
|
|
136
|
-
# make sure that you reload the test file
|
|
137
|
-
#load file
|
|
138
|
-
#contents = File.open(file).read
|
|
139
|
-
# puts contents
|
|
140
|
-
#eval contents
|
|
141
78
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
79
|
+
class_filename = file.sub(self.class::CLASS_MAP, '')
|
|
80
|
+
|
|
81
|
+
# get the class
|
|
82
|
+
test_class = resolve_classname(class_filename)
|
|
145
83
|
|
|
146
84
|
# create dummy wrapper modules if test is in subfolder
|
|
147
85
|
test_class.split('::').each do |part|
|
|
148
86
|
eval "module ::#{part}; end" if !part.match('Test')
|
|
149
87
|
end
|
|
150
88
|
|
|
89
|
+
# TODO: make this reload use load_file
|
|
151
90
|
$".delete(file)
|
|
152
91
|
|
|
153
|
-
#(defined?(ActiveRecord::Base) ? ActiveRecord::Base.instance_eval { subclasses }.each { |c| c.reset_column_information } : nil)
|
|
154
|
-
#(defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies.clear : nil)
|
|
155
|
-
|
|
156
92
|
begin
|
|
157
93
|
require file
|
|
158
94
|
rescue LoadError
|
|
@@ -174,7 +110,8 @@ module Bolt
|
|
|
174
110
|
klass = eval(test_class)
|
|
175
111
|
|
|
176
112
|
Test::Unit::UI::Console::TestRunner.run(klass)
|
|
177
|
-
|
|
113
|
+
Test::Unit.run = false
|
|
114
|
+
|
|
178
115
|
# Invoke method to test that writes to stdout.
|
|
179
116
|
result = test_io.string.to_s.dup
|
|
180
117
|
|
data/lib/bolt.rb
CHANGED
|
@@ -23,8 +23,16 @@ module Bolt
|
|
|
23
23
|
# read the .bolt file for configuration
|
|
24
24
|
def self.read_dotfile
|
|
25
25
|
if File.exists?('.bolt')
|
|
26
|
-
$stdout.puts "** Found .bolt file"
|
|
27
|
-
|
|
26
|
+
$stdout.puts "** Found .bolt file" if Bolt['verbose']
|
|
27
|
+
read = YAML.load_file('.bolt')
|
|
28
|
+
@@config.merge!(read) if read
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# read the arguments passed in cli
|
|
33
|
+
def self.read_argv
|
|
34
|
+
ARGV.each do |arg|
|
|
35
|
+
self['verbose'] = true if arg == '-v'
|
|
28
36
|
end
|
|
29
37
|
end
|
|
30
38
|
|
|
@@ -34,6 +42,9 @@ module Bolt
|
|
|
34
42
|
# read the dotfile
|
|
35
43
|
Bolt.read_dotfile
|
|
36
44
|
|
|
45
|
+
# read the arguments passed
|
|
46
|
+
Bolt.read_argv
|
|
47
|
+
|
|
37
48
|
Bolt::Listener.new
|
|
38
49
|
end
|
|
39
50
|
|
|
@@ -69,6 +80,8 @@ module Bolt
|
|
|
69
80
|
@@noticed_files
|
|
70
81
|
end
|
|
71
82
|
|
|
83
|
+
autoload :Base, 'bolt/runners/base'
|
|
84
|
+
autoload :Cucumber, 'bolt/runners/cucumber'
|
|
72
85
|
autoload :TestUnit, 'bolt/runners/test_unit'
|
|
73
86
|
autoload :RSpec, 'bolt/runners/rspec'
|
|
74
87
|
end
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
require 'spec'
|
|
2
2
|
require 'bolt/runners/test_unit'
|
|
3
3
|
|
|
4
|
+
# test fixture for testing of class clearing
|
|
5
|
+
module Bolt
|
|
6
|
+
class ClassForTesting
|
|
7
|
+
def one; end
|
|
8
|
+
def two; end
|
|
9
|
+
end
|
|
10
|
+
module Nested
|
|
11
|
+
class ClassForTesting
|
|
12
|
+
def three; end
|
|
13
|
+
def four; end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
4
18
|
describe Bolt::Runners::TestUnit do
|
|
5
19
|
|
|
6
20
|
before(:all) do
|
|
@@ -47,4 +61,31 @@ describe Bolt::Runners::TestUnit do
|
|
|
47
61
|
$stdout = old
|
|
48
62
|
end
|
|
49
63
|
|
|
64
|
+
it 'should return no results if file is not present' do
|
|
65
|
+
Bolt::ClassForTesting.should_not be_nil
|
|
66
|
+
Bolt::Nested::ClassForTesting.should_not be_nil
|
|
67
|
+
@tester1 = Bolt::ClassForTesting.new
|
|
68
|
+
@tester1.respond_to?(:one).should be_true
|
|
69
|
+
@tester1.respond_to?(:two).should be_true
|
|
70
|
+
@tester2 = Bolt::Nested::ClassForTesting.new
|
|
71
|
+
@tester2.respond_to?(:three).should be_true
|
|
72
|
+
@tester2.respond_to?(:four).should be_true
|
|
73
|
+
@runner.clear_class(Bolt::ClassForTesting)
|
|
74
|
+
@runner.clear_class(Bolt::Nested::ClassForTesting)
|
|
75
|
+
|
|
76
|
+
# it should applied to current instances
|
|
77
|
+
@tester1.respond_to?(:one).should be_false
|
|
78
|
+
@tester1.respond_to?(:two).should be_false
|
|
79
|
+
@tester2.respond_to?(:three).should be_false
|
|
80
|
+
@tester2.respond_to?(:four).should be_false
|
|
81
|
+
|
|
82
|
+
# and to new instances
|
|
83
|
+
@tester1a = Bolt::ClassForTesting.new
|
|
84
|
+
@tester2a = Bolt::Nested::ClassForTesting.new
|
|
85
|
+
@tester1a.respond_to?(:one).should be_false
|
|
86
|
+
@tester1a.respond_to?(:two).should be_false
|
|
87
|
+
@tester2a.respond_to?(:three).should be_false
|
|
88
|
+
@tester2a.respond_to?(:four).should be_false
|
|
89
|
+
end
|
|
90
|
+
|
|
50
91
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: marcinbunsch-bolt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marcin Bunsch
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-06-
|
|
12
|
+
date: 2009-06-23 00:00:00 -07:00
|
|
13
13
|
default_executable: bolt
|
|
14
14
|
dependencies: []
|
|
15
15
|
|
|
@@ -44,6 +44,8 @@ files:
|
|
|
44
44
|
- lib/bolt/notifiers/growl.rb
|
|
45
45
|
- lib/bolt/notifiers/notify_osd.rb
|
|
46
46
|
- lib/bolt/runner.rb
|
|
47
|
+
- lib/bolt/runners/base.rb
|
|
48
|
+
- lib/bolt/runners/cucumber.rb
|
|
47
49
|
- lib/bolt/runners/rspec.rb
|
|
48
50
|
- lib/bolt/runners/test_unit.rb
|
|
49
51
|
- spec/bolt/runners/rspec_spec.rb
|