openlogic-turn 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|