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