openlogic-turn 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +54 -0
- data/README.txt +116 -0
- data/Rakefile +40 -0
- data/Release.txt +33 -0
- data/Version.txt +1 -0
- data/bin/turn +4 -0
- data/demo/test_autorun_minitest.rb +26 -0
- data/demo/test_autorun_testunit.rb +26 -0
- data/demo/test_sample.rb +35 -0
- data/demo/test_sample2.rb +33 -0
- data/lib/turn.rb +19 -0
- data/lib/turn/autorun/minitest.rb +155 -0
- data/lib/turn/autorun/testunit.rb +116 -0
- data/lib/turn/bin.rb +4 -0
- data/lib/turn/colorize.rb +65 -0
- data/lib/turn/command.rb +210 -0
- data/lib/turn/components/case.rb +104 -0
- data/lib/turn/components/method.rb +42 -0
- data/lib/turn/components/suite.rb +85 -0
- data/lib/turn/controller.rb +204 -0
- data/lib/turn/core_ext.rb +31 -0
- data/lib/turn/reporter.rb +69 -0
- data/lib/turn/reporters/cue_reporter.rb +167 -0
- data/lib/turn/reporters/dot_reporter.rb +93 -0
- data/lib/turn/reporters/marshal_reporter.rb +17 -0
- data/lib/turn/reporters/outline_reporter.rb +144 -0
- data/lib/turn/reporters/pretty_reporter.rb +184 -0
- data/lib/turn/reporters/progress_reporter.rb +116 -0
- data/lib/turn/runners/crossrunner.rb +42 -0
- data/lib/turn/runners/isorunner.rb +167 -0
- data/lib/turn/runners/loadrunner.rb +48 -0
- data/lib/turn/runners/minirunner.rb +189 -0
- data/lib/turn/runners/solorunner.rb +8 -0
- data/lib/turn/runners/testrunner.rb +166 -0
- data/test/helper.rb +97 -0
- data/test/runner +2 -0
- data/test/test_framework.rb +131 -0
- data/test/test_reporters.rb +44 -0
- data/test/test_runners.rb +45 -0
- metadata +138 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module Turn
|
2
|
+
require 'turn/runners/isorunner'
|
3
|
+
|
4
|
+
# = Cross Runner
|
5
|
+
#
|
6
|
+
# Cross Runner runs test in pairs.
|
7
|
+
#
|
8
|
+
# TODO: This needs work in the test_loop_runner.
|
9
|
+
# It needs to show the files being cross tested.
|
10
|
+
#
|
11
|
+
# TODO: Cross runner output needs to be fixed
|
12
|
+
class CrossRunner < IsoRunner
|
13
|
+
|
14
|
+
#
|
15
|
+
def start
|
16
|
+
suite = TestSuite.new
|
17
|
+
|
18
|
+
files = @controller.files
|
19
|
+
viles = @controller.files # TODO: make selectable ?
|
20
|
+
|
21
|
+
#files = files.select{ |f| File.extname(f) == '.rb' and File.file?(f) }
|
22
|
+
#viles = viles.select{ |f| File.extname(f) == '.rb' and File.file?(f) }
|
23
|
+
|
24
|
+
pairs = files.inject([]){ |m, f| viles.collect{ |v| m << [f,v] }; m }
|
25
|
+
pairs = pairs.reject{ |f,v| f == v }
|
26
|
+
|
27
|
+
max = files.collect{ |f| f.sub(Dir.pwd+'/','').size }.max
|
28
|
+
|
29
|
+
testruns = pairs.collect do |file1, file2|
|
30
|
+
name1 = file1.sub(Dir.pwd+'/','')
|
31
|
+
name2 = file2.sub(Dir.pwd+'/','')
|
32
|
+
name = "%-#{max}s %-#{max}s" % [name1, name2]
|
33
|
+
suite.new_case(name, file1, file2)
|
34
|
+
end
|
35
|
+
|
36
|
+
test_loop_runner(suite)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Turn
|
2
|
+
require 'turn/colorize'
|
3
|
+
require 'yaml'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
# = IsoRunner
|
7
|
+
#
|
8
|
+
# Iso Runner provides means from running unit test
|
9
|
+
# in isolated processes. It can do this either by running
|
10
|
+
# each test in isolation (solo testing) or in pairs (cross testing).
|
11
|
+
#
|
12
|
+
# The IsoRunner proiveds some variery in ouput formats and can also
|
13
|
+
# log results to a file.
|
14
|
+
#
|
15
|
+
class IsoRunner
|
16
|
+
include Turn::Colorize
|
17
|
+
|
18
|
+
attr :reporter
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def initialize(controller)
|
23
|
+
@controller = controller
|
24
|
+
@reporter = controller.reporter
|
25
|
+
#yield(self) if block_given?
|
26
|
+
@loadpath = controller.loadpath
|
27
|
+
@requires = controller.requires
|
28
|
+
@live = controller.live?
|
29
|
+
@minitest = controller.framework == :minitest
|
30
|
+
end
|
31
|
+
|
32
|
+
public
|
33
|
+
|
34
|
+
# Runs the list of test calls passed to it.
|
35
|
+
# This is used by #test_solo and #test_cross.
|
36
|
+
#
|
37
|
+
def start
|
38
|
+
suite = TestSuite.new
|
39
|
+
testruns = @controller.files.collect do |file|
|
40
|
+
name = file.sub(Dir.pwd+'/','')
|
41
|
+
suite.new_case(name, file)
|
42
|
+
end
|
43
|
+
test_loop_runner(suite)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# The IsoRunner actually shells out to turn in
|
49
|
+
# manifest mode, to gather results from isolated
|
50
|
+
# runs.
|
51
|
+
def test_loop_runner(suite)
|
52
|
+
reporter.start_suite(suite)
|
53
|
+
|
54
|
+
recase = []
|
55
|
+
|
56
|
+
suite.each_with_index do |kase, index|
|
57
|
+
reporter.start_case(kase)
|
58
|
+
|
59
|
+
turn_path = File.expand_path(File.dirname(__FILE__) + '/../bin.rb')
|
60
|
+
|
61
|
+
files = kase.files.map{ |f| f.sub(Dir.pwd+'/', '') }
|
62
|
+
|
63
|
+
# FRACKING GENIUS RIGHT HERE !!!!!!!!!!!!
|
64
|
+
cmd = []
|
65
|
+
cmd << "ruby"
|
66
|
+
cmd << "-I#{@loadpath.join(':')}" unless @loadpath.empty?
|
67
|
+
cmd << "-r#{@requires.join(':')}" unless @requires.empty?
|
68
|
+
cmd << "--"
|
69
|
+
cmd << turn_path
|
70
|
+
cmd << "--marshal"
|
71
|
+
cmd << %[--loadpath="#{@loadpath.join(':')}"] unless @loadpath.empty?
|
72
|
+
cmd << %[--requires="#{@requires.join(':')}"] unless @requires.empty?
|
73
|
+
cmd << "--live" if @live
|
74
|
+
cmd << "--minitest" if @minitest
|
75
|
+
cmd << files.join(' ')
|
76
|
+
cmd = cmd.join(' ')
|
77
|
+
|
78
|
+
#out = `#{cmd}`
|
79
|
+
#err = ''
|
80
|
+
|
81
|
+
out, err = nil, nil
|
82
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr|
|
83
|
+
stdin.close
|
84
|
+
out = stdout.read.chomp
|
85
|
+
err = stderr.read.chomp
|
86
|
+
end
|
87
|
+
|
88
|
+
# TODO: how to report? will need to add something to reporter
|
89
|
+
# b/c it may have redirected stdout. Or use STDOUT?
|
90
|
+
#if !err.empty?
|
91
|
+
# puts err
|
92
|
+
# raise
|
93
|
+
#end
|
94
|
+
|
95
|
+
files = kase.files
|
96
|
+
|
97
|
+
# remove any unexpected output injected at the beginning
|
98
|
+
yaml = out[out.index(/^---/)..-1]
|
99
|
+
sub_suite = YAML.load(yaml)
|
100
|
+
|
101
|
+
# TODO: How to handle pairs?
|
102
|
+
#name = kase.name
|
103
|
+
kases = sub_suite.cases
|
104
|
+
suite.cases[index] = kases
|
105
|
+
|
106
|
+
kases.each do |kase|
|
107
|
+
kase.files = files
|
108
|
+
#reporter.start_case(kase)
|
109
|
+
kase.tests.each do |test|
|
110
|
+
reporter.start_test(test)
|
111
|
+
if test.error?
|
112
|
+
#reporter.error(test.message)
|
113
|
+
reporter.error(test.raised)
|
114
|
+
elsif test.fail?
|
115
|
+
#reporter.fail(test.message)
|
116
|
+
reporter.error(test.raised)
|
117
|
+
else
|
118
|
+
reporter.pass
|
119
|
+
end
|
120
|
+
reporter.finish_test(test)
|
121
|
+
end
|
122
|
+
reporter.finish_case(kase)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
suite.cases.flatten!
|
127
|
+
|
128
|
+
reporter.finish_suite(suite)
|
129
|
+
|
130
|
+
# shutdown auto runner
|
131
|
+
if @minitest
|
132
|
+
|
133
|
+
else
|
134
|
+
::Test::Unit.run=true rescue nil
|
135
|
+
end
|
136
|
+
|
137
|
+
suite
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
#def test_parse_result(result)
|
142
|
+
# if md = /(\d+) tests, (\d+) assertions, (\d+) failures, (\d+) errors/.match(result)
|
143
|
+
# count = md[1..4].collect{|q| q.to_i}
|
144
|
+
# else
|
145
|
+
# count = [1, 0, 0, 1] # SHOULD NEVER HAPPEN
|
146
|
+
# end
|
147
|
+
# return count
|
148
|
+
#end
|
149
|
+
|
150
|
+
# NOT USED YET.
|
151
|
+
def log_report(report)
|
152
|
+
if log #&& !dryrun?
|
153
|
+
#logfile = File.join('log', apply_naming_policy('testlog', 'txt'))
|
154
|
+
FileUtils.mkdir_p('log')
|
155
|
+
logfile = File.join('log', 'testlog.txt')
|
156
|
+
File.open(logfile, 'a') do |f|
|
157
|
+
f << "= #{self.class} Test @ #{Time.now}\n"
|
158
|
+
f << report
|
159
|
+
f << "\n"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end#class IsoRunner
|
165
|
+
|
166
|
+
end#module Turn
|
167
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# TODO
|
2
|
+
|
3
|
+
# Load each test independently to ensure there are no
|
4
|
+
# require dependency issues. This is actually a bit redundant
|
5
|
+
# as test-solo will also cover these results. So we may deprecate
|
6
|
+
# this in the future. This does not generate a test log entry.
|
7
|
+
|
8
|
+
def test_load(options={})
|
9
|
+
options = test_configuration(options)
|
10
|
+
|
11
|
+
tests = options['tests']
|
12
|
+
loadpath = options['loadpath']
|
13
|
+
requires = options['requires']
|
14
|
+
live = options['live']
|
15
|
+
exclude = options['exclude']
|
16
|
+
|
17
|
+
files = Dir.multiglob_r(*tests) - Dir.multiglob_r(*exclude)
|
18
|
+
|
19
|
+
return puts("No tests.") if files.empty?
|
20
|
+
|
21
|
+
max = files.collect{ |f| f.size }.max
|
22
|
+
list = []
|
23
|
+
|
24
|
+
files.each do |f|
|
25
|
+
next unless File.file?(f)
|
26
|
+
if r = system("ruby -I#{loadpath.join(':')} #{f} > /dev/null 2>&1")
|
27
|
+
puts "%-#{max}s [PASS]" % [f] #if verbose?
|
28
|
+
else
|
29
|
+
puts "%-#{max}s [FAIL]" % [f] #if verbose?
|
30
|
+
list << f
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
puts " #{list.size} Load Failures"
|
35
|
+
|
36
|
+
if verbose?
|
37
|
+
unless list.empty?
|
38
|
+
puts "\n-- Load Failures --\n"
|
39
|
+
list.each do |f|
|
40
|
+
print "* "
|
41
|
+
system "ruby -I#{loadpath} #{f} 2>&1"
|
42
|
+
#puts
|
43
|
+
end
|
44
|
+
puts
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,189 @@
|
|
1
|
+
#
|
2
|
+
|
3
|
+
# Becuase of some wierdness in MiniTest
|
4
|
+
debug, $DEBUG = $DEBUG, false
|
5
|
+
require 'minitest/unit'
|
6
|
+
$DEBUG = debug
|
7
|
+
|
8
|
+
Test = MiniTest
|
9
|
+
|
10
|
+
module Turn
|
11
|
+
|
12
|
+
# = MiniTest TestRunner
|
13
|
+
#
|
14
|
+
class MiniRunner < ::MiniTest::Unit
|
15
|
+
|
16
|
+
#
|
17
|
+
def initialize(controller)
|
18
|
+
|
19
|
+
controller.loadpath.each{ |path| $: << path } unless controller.live?
|
20
|
+
controller.requires.each{ |path| require(path) }
|
21
|
+
|
22
|
+
[controller.files].flatten.each{ |path| require(path) }
|
23
|
+
|
24
|
+
files = [controller.files].flatten
|
25
|
+
files.each{ |path| require(path) }
|
26
|
+
|
27
|
+
# TODO: Better name ?
|
28
|
+
@turn_suite_name = files.map{ |path| File.dirname(path).sub(Dir.pwd+'/','') }.uniq.join(',')
|
29
|
+
|
30
|
+
#sub_suites = []
|
31
|
+
#ObjectSpace.each_object(Class) do |klass|
|
32
|
+
# if(Test::Unit::TestCase > klass)
|
33
|
+
# sub_suites << klass.suite
|
34
|
+
# end
|
35
|
+
#end
|
36
|
+
#suite = Test::Unit::TestSuite.new('') # FIXME: Name?
|
37
|
+
#sub_suites.sort_by{|s|s.name}.each{|s| suite << s}
|
38
|
+
|
39
|
+
#suite.tests.each do |c|
|
40
|
+
# pattern = controller.pattern
|
41
|
+
# c.tests.reject! { |t| pattern !~ t.method_name }
|
42
|
+
#end
|
43
|
+
|
44
|
+
@turn_logger = controller.reporter
|
45
|
+
|
46
|
+
super()
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
def start(args=[])
|
51
|
+
run(args)
|
52
|
+
return @turn_suite
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
def run(args = [])
|
57
|
+
@verbose = true
|
58
|
+
|
59
|
+
filter = if args.first =~ /^(-n|--name)$/ then
|
60
|
+
args.shift
|
61
|
+
arg = args.shift
|
62
|
+
arg =~ /\/(.*)\// ? Regexp.new($1) : arg
|
63
|
+
else
|
64
|
+
/./ # anything - ^test_ already filtered by #tests
|
65
|
+
end
|
66
|
+
|
67
|
+
#@@out.puts "Loaded suite #{$0.sub(/\.rb$/, '')}\nStarted"
|
68
|
+
|
69
|
+
start = Time.now
|
70
|
+
|
71
|
+
run_test_suites(filter)
|
72
|
+
|
73
|
+
return failures + errors if @test_count > 0 # or return nil...
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
def run_test_suites(filter = /./)
|
78
|
+
@test_count, @assertion_count = 0, 0
|
79
|
+
old_sync, @@out.sync = @@out.sync, true if @@out.respond_to? :sync=
|
80
|
+
|
81
|
+
@turn_suite = Turn::TestSuite.new(@turn_suite_name)
|
82
|
+
@turn_suite.size = ::MiniTest::Unit::TestCase.test_suites.size
|
83
|
+
@turn_logger.start_suite(@turn_suite)
|
84
|
+
|
85
|
+
::MiniTest::Unit::TestCase.test_suites.each do |kase|
|
86
|
+
|
87
|
+
test_cases = kase.test_methods.grep(filter)
|
88
|
+
|
89
|
+
@turn_case = @turn_suite.new_case(kase.name)
|
90
|
+
|
91
|
+
turn_cases = test_cases.map do |test|
|
92
|
+
@turn_case.new_test(test)
|
93
|
+
end
|
94
|
+
|
95
|
+
@turn_logger.start_case(@turn_case)
|
96
|
+
|
97
|
+
turn_cases.each do |test|
|
98
|
+
#methname, tcase = name.scan(%r/^([^\(]+)\(([^\)]+)\)/o).flatten!
|
99
|
+
@turn_test = test #@turn_case.new_test(test)
|
100
|
+
@turn_logger.start_test(@turn_test)
|
101
|
+
|
102
|
+
inst = kase.new(test.name)
|
103
|
+
inst._assertions = 0
|
104
|
+
|
105
|
+
result = inst.run(self)
|
106
|
+
report = @report.last
|
107
|
+
|
108
|
+
case result
|
109
|
+
when :pass
|
110
|
+
@turn_logger.pass
|
111
|
+
when :error
|
112
|
+
#trace = ::MiniTest::filter_backtrace(report[:exception].backtrace).first
|
113
|
+
@turn_test.error!(report)
|
114
|
+
@turn_logger.error(report)
|
115
|
+
when :fail
|
116
|
+
#trace = ::MiniTest::filter_backtrace(report[:exception].backtrace).first
|
117
|
+
@turn_test.fail!(report)
|
118
|
+
@turn_logger.fail(report)
|
119
|
+
when :skip
|
120
|
+
@turn_test.skip! #(report)
|
121
|
+
@turn_logger.skip #(report)
|
122
|
+
end
|
123
|
+
|
124
|
+
@turn_logger.finish_test(@turn_test)
|
125
|
+
|
126
|
+
@test_count += 1
|
127
|
+
@assertion_count += inst._assertions
|
128
|
+
end
|
129
|
+
@turn_case.count_assertions = @assertion_count # for lack of a better appraoch
|
130
|
+
@turn_logger.finish_case(@turn_case)
|
131
|
+
end
|
132
|
+
@turn_logger.finish_suite(@turn_suite)
|
133
|
+
@@out.sync = old_sync if @@out.respond_to? :sync=
|
134
|
+
[@test_count, @assertion_count]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Overwrite #puke method so that is stores a hash
|
138
|
+
# with :message and :exception keys.
|
139
|
+
def puke(klass, meth, e)
|
140
|
+
result = nil
|
141
|
+
msg = case e
|
142
|
+
when ::MiniTest::Skip
|
143
|
+
@skips += 1
|
144
|
+
result = :skip
|
145
|
+
e.message
|
146
|
+
when ::MiniTest::Assertion
|
147
|
+
@failures += 1
|
148
|
+
result = :fail
|
149
|
+
e.message
|
150
|
+
else
|
151
|
+
@errors += 1
|
152
|
+
result = :error
|
153
|
+
"#{e.class}: #{e.message}\n"
|
154
|
+
end
|
155
|
+
|
156
|
+
@report << e #{:message => msg, :exception => e}
|
157
|
+
result
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
class ::MiniTest::Unit::TestCase
|
165
|
+
old_verbose, $VERBOSE = $VERBOSE, false
|
166
|
+
# Overwrite #run method so that is uses symbols
|
167
|
+
# as return values rather than characters.
|
168
|
+
def run(runner)
|
169
|
+
result = :pass
|
170
|
+
begin
|
171
|
+
@passed = nil
|
172
|
+
self.setup
|
173
|
+
self.__send__(self.__name__.to_s)
|
174
|
+
@passed = true
|
175
|
+
rescue Exception => e
|
176
|
+
@passed = false
|
177
|
+
result = runner.puke(self.class, self.__name__.to_s, e)
|
178
|
+
ensure
|
179
|
+
begin
|
180
|
+
self.teardown
|
181
|
+
rescue Exception => e
|
182
|
+
result = runner.puke(self.class, self.__name__.to_s, e)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
result
|
186
|
+
end
|
187
|
+
$VERBOSE = old_verbose
|
188
|
+
end
|
189
|
+
|