|
1
|
|
|
2
|
require 'test/unit'
|
|
3
|
require 'test/unit/ui/testrunnerutilities'
|
|
4
|
require 'optparse'
|
|
5
|
|
|
6
|
module Test
|
|
7
|
module Unit
|
|
8
|
class AutoRunner
|
|
9
|
def self.run(force_standalone=false, default_dir=nil, argv=ARGV, &block)
|
|
10
|
r = new(force_standalone || standalone?, &block)
|
|
11
|
r.base = default_dir
|
|
12
|
r.process_args(argv)
|
|
13
|
r.run
|
|
14
|
end
|
|
15
|
|
|
16
|
def self.standalone?
|
|
17
|
return false unless("-e" == $0)
|
|
18
|
ObjectSpace.each_object(Class) do |klass|
|
|
19
|
return false if(klass < TestCase)
|
|
20
|
end
|
|
21
|
true
|
|
22
|
end
|
|
23
|
|
|
24
|
RUNNERS = {
|
|
25
|
:console => proc do |r|
|
|
26
|
require 'test/unit/ui/console/testrunner'
|
|
27
|
Test::Unit::UI::Console::TestRunner
|
|
28
|
end,
|
|
29
|
:gtk => proc do |r|
|
|
30
|
require 'test/unit/ui/gtk/testrunner'
|
|
31
|
Test::Unit::UI::GTK::TestRunner
|
|
32
|
end,
|
|
33
|
:gtk2 => proc do |r|
|
|
34
|
require 'test/unit/ui/gtk2/testrunner'
|
|
35
|
Test::Unit::UI::GTK2::TestRunner
|
|
36
|
end,
|
|
37
|
:fox => proc do |r|
|
|
38
|
require 'test/unit/ui/fox/testrunner'
|
|
39
|
Test::Unit::UI::Fox::TestRunner
|
|
40
|
end,
|
|
41
|
:tk => proc do |r|
|
|
42
|
require 'test/unit/ui/tk/testrunner'
|
|
43
|
Test::Unit::UI::Tk::TestRunner
|
|
44
|
end,
|
|
45
|
}
|
|
46
|
|
|
47
|
OUTPUT_LEVELS = [
|
|
48
|
[:silent, UI::SILENT],
|
|
49
|
[:progress, UI::PROGRESS_ONLY],
|
|
50
|
[:normal, UI::NORMAL],
|
|
51
|
[:verbose, UI::VERBOSE],
|
|
52
|
]
|
|
53
|
|
|
54
|
COLLECTORS = {
|
|
55
|
:objectspace => proc do |r|
|
|
56
|
require 'test/unit/collector/objectspace'
|
|
57
|
c = Collector::ObjectSpace.new
|
|
58
|
c.filter = r.filters
|
|
59
|
c.collect($0.sub(/\.rb\Z/, ''))
|
|
60
|
end,
|
|
61
|
:dir => proc do |r|
|
|
62
|
require 'test/unit/collector/dir'
|
|
63
|
c = Collector::Dir.new
|
|
64
|
c.filter = r.filters
|
|
65
|
c.pattern.concat(r.pattern) if(r.pattern)
|
|
66
|
c.exclude.concat(r.exclude) if(r.exclude)
|
|
67
|
c.base = r.base
|
|
68
|
$:.push(r.base) if r.base
|
|
69
|
c.collect(*(r.to_run.empty? ? ['.'] : r.to_run))
|
|
70
|
end,
|
|
71
|
}
|
|
72
|
|
|
73
|
attr_reader :suite
|
|
74
|
attr_accessor :output_level, :filters, :to_run, :pattern, :exclude, :base, :workdir
|
|
75
|
attr_accessor :output_io
|
|
76
|
attr_writer :runner, :collector
|
|
77
|
|
|
78
|
def initialize(standalone)
|
|
79
|
Unit.run = true
|
|
80
|
@standalone = standalone
|
|
81
|
@runner = RUNNERS[:console]
|
|
82
|
@collector = COLLECTORS[(standalone ? :dir : :objectspace)]
|
|
83
|
@filters = []
|
|
84
|
@to_run = []
|
|
85
|
@output_level = UI::NORMAL
|
|
86
|
@workdir = nil
|
|
87
|
@output_io = STDOUT
|
|
88
|
yield(self) if(block_given?)
|
|
89
|
end
|
|
90
|
|
|
91
|
def process_args(args = ARGV)
|
|
92
|
begin
|
|
93
|
options.order!(args) {|arg| @to_run << arg}
|
|
94
|
rescue OptionParser::ParseError => e
|
|
95
|
puts e
|
|
96
|
puts options
|
|
97
|
$! = nil
|
|
98
|
abort
|
|
99
|
else
|
|
100
|
@filters << proc{false} unless(@filters.empty?)
|
|
101
|
end
|
|
102
|
not @to_run.empty?
|
|
103
|
end
|
|
104
|
|
|
105
|
def options
|
|
106
|
@options ||= OptionParser.new do |o|
|
|
107
|
o.banner = "Test::Unit automatic runner."
|
|
108
|
o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"
|
|
109
|
|
|
110
|
o.on
|
|
111
|
o.on('-r', '--runner=RUNNER', RUNNERS,
|
|
112
|
"Use the given RUNNER.",
|
|
113
|
"(" + keyword_display(RUNNERS) + ")") do |r|
|
|
114
|
@runner = r
|
|
115
|
end
|
|
116
|
|
|
117
|
if(@standalone)
|
|
118
|
o.on('-b', '--basedir=DIR', "Base directory of test suites.") do |b|
|
|
119
|
@base = b
|
|
120
|
end
|
|
121
|
|
|
122
|
o.on('-w', '--workdir=DIR', "Working directory to run tests.") do |w|
|
|
123
|
@workdir = w
|
|
124
|
end
|
|
125
|
|
|
126
|
o.on('-a', '--add=TORUN', Array,
|
|
127
|
"Add TORUN to the list of things to run;",
|
|
128
|
"can be a file or a directory.") do |a|
|
|
129
|
@to_run.concat(a)
|
|
130
|
end
|
|
131
|
|
|
132
|
@pattern = []
|
|
133
|
o.on('-p', '--pattern=PATTERN', Regexp,
|
|
134
|
"Match files to collect against PATTERN.") do |e|
|
|
135
|
@pattern << e
|
|
136
|
end
|
|
137
|
|
|
138
|
@exclude = []
|
|
139
|
o.on('-x', '--exclude=PATTERN', Regexp,
|
|
140
|
"Ignore files to collect against PATTERN.") do |e|
|
|
141
|
@exclude << e
|
|
142
|
end
|
|
143
|
end
|
|
144
|
|
|
145
|
o.on('-n', '--name=NAME', String,
|
|
146
|
"Runs tests matching NAME.",
|
|
147
|
"(patterns may be used).") do |n|
|
|
148
|
n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
|
|
149
|
case n
|
|
150
|
when Regexp
|
|
151
|
@filters << proc{|t| n =~ t.method_name ? true : nil}
|
|
152
|
else
|
|
153
|
@filters << proc{|t| n == t.method_name ? true : nil}
|
|
154
|
end
|
|
155
|
end
|
|
156
|
|
|
157
|
o.on('-o', '--output=[+]FILE', String,
|
|
158
|
"Write output to FILE.",
|
|
159
|
"Use '+' to append; FILE = STDOUT, STDERR or filename (default=STDOUT)") do |f|
|
|
160
|
case f
|
|
161
|
when nil, '', 'STDOUT'
|
|
162
|
@output_io = STDOUT
|
|
163
|
when 'STDERR'
|
|
164
|
@output_io = STDERR
|
|
165
|
else
|
|
166
|
if f =~ /^\+/
|
|
167
|
@output_io = File.new(f[1..f.length], 'a')
|
|
168
|
else
|
|
169
|
@output_io = File.new(f, 'w')
|
|
170
|
end
|
|
171
|
end
|
|
172
|
end
|
|
173
|
|
|
174
|
o.on('-t', '--testcase=TESTCASE', String,
|
|
175
|
"Runs tests in TestCases matching TESTCASE.",
|
|
176
|
"(patterns may be used).") do |n|
|
|
177
|
n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
|
|
178
|
case n
|
|
179
|
when Regexp
|
|
180
|
@filters << proc{|t| n =~ t.class.name ? true : nil}
|
|
181
|
else
|
|
182
|
@filters << proc{|t| n == t.class.name ? true : nil}
|
|
183
|
end
|
|
184
|
end
|
|
185
|
|
|
186
|
o.on('-I', "--load-path=DIR[#{File::PATH_SEPARATOR}DIR...]",
|
|
187
|
"Appends directory list to $LOAD_PATH.") do |dirs|
|
|
188
|
$LOAD_PATH.concat(dirs.split(File::PATH_SEPARATOR))
|
|
189
|
end
|
|
190
|
|
|
191
|
o.on('-v', '--verbose=[LEVEL]', OUTPUT_LEVELS,
|
|
192
|
"Set the output level (default is verbose).",
|
|
193
|
"(" + keyword_display(OUTPUT_LEVELS) + ")") do |l|
|
|
194
|
@output_level = l || UI::VERBOSE
|
|
195
|
end
|
|
196
|
|
|
197
|
o.on('--',
|
|
198
|
"Stop processing options so that the",
|
|
199
|
"remaining options will be passed to the",
|
|
200
|
"test."){o.terminate}
|
|
201
|
|
|
202
|
o.on('-h', '--help', 'Display this help.'){puts o; exit}
|
|
203
|
|
|
204
|
o.on_tail
|
|
205
|
o.on_tail('Deprecated options:')
|
|
206
|
|
|
207
|
o.on_tail('--console', 'Console runner (use --runner).') do
|
|
208
|
warn("Deprecated option (--console).")
|
|
209
|
@runner = RUNNERS[:console]
|
|
210
|
end
|
|
211
|
|
|
212
|
o.on_tail('--gtk', 'GTK runner (use --runner).') do
|
|
213
|
warn("Deprecated option (--gtk).")
|
|
214
|
@runner = RUNNERS[:gtk]
|
|
215
|
end
|
|
216
|
|
|
217
|
o.on_tail('--fox', 'Fox runner (use --runner).') do
|
|
218
|
warn("Deprecated option (--fox).")
|
|
219
|
@runner = RUNNERS[:fox]
|
|
220
|
end
|
|
221
|
|
|
222
|
o.on_tail
|
|
223
|
end
|
|
224
|
end
|
|
225
|
|
|
226
|
def keyword_display(array)
|
|
227
|
list = array.collect {|e, *| e.to_s}
|
|
228
|
Array === array or list.sort!
|
|
229
|
list.collect {|e| e.sub(/^(.)([A-Za-z]+)(?=\w*$)/, '\\1[\\2]')}.join(", ")
|
|
230
|
end
|
|
231
|
|
|
232
|
def run
|
|
233
|
@suite = @collector[self]
|
|
234
|
result = @runner[self] or return false
|
|
235
|
Dir.chdir(@workdir) if @workdir
|
|
236
|
result.run(@suite, @output_level, @output_io).passed?
|
|
237
|
end
|
|
238
|
end
|
|
239
|
end
|
|
240
|
end
|