minitest 5.15.0 → 5.20.0

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/lib/minitest/mock.rb CHANGED
@@ -28,11 +28,19 @@ module Minitest # :nodoc:
28
28
  end
29
29
 
30
30
  overridden_methods.map(&:to_sym).each do |method_id|
31
- define_method method_id do |*args, &b|
31
+ define_method method_id do |*args, **kwargs, &b|
32
32
  if @expected_calls.key? method_id then
33
- method_missing(method_id, *args, &b)
33
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
34
+ method_missing(method_id, *args, &b)
35
+ else
36
+ method_missing(method_id, *args, **kwargs, &b)
37
+ end
34
38
  else
35
- super(*args, &b)
39
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
40
+ super(*args, &b)
41
+ else
42
+ super(*args, **kwargs, &b)
43
+ end
36
44
  end
37
45
  end
38
46
  end
@@ -43,9 +51,11 @@ module Minitest # :nodoc:
43
51
  @actual_calls = Hash.new { |calls, name| calls[name] = [] }
44
52
  end
45
53
 
54
+ @@KW_WARNED = false # :nodoc:
55
+
46
56
  ##
47
- # Expect that method +name+ is called, optionally with +args+ or a
48
- # +blk+, and returns +retval+.
57
+ # Expect that method +name+ is called, optionally with +args+ (and
58
+ # +kwargs+ or a +blk+), and returns +retval+.
49
59
  #
50
60
  # @mock.expect(:meaning_of_life, 42)
51
61
  # @mock.meaning_of_life # => 42
@@ -78,15 +88,31 @@ module Minitest # :nodoc:
78
88
  # @mock.ordinal_increment # => raises MockExpectationError "No more expects available for :ordinal_increment"
79
89
  #
80
90
 
81
- def expect name, retval, args = [], &blk
91
+ def expect name, retval, args = [], **kwargs, &blk
82
92
  name = name.to_sym
83
93
 
84
94
  if block_given?
85
95
  raise ArgumentError, "args ignored when block given" unless args.empty?
96
+ raise ArgumentError, "kwargs ignored when block given" unless kwargs.empty?
86
97
  @expected_calls[name] << { :retval => retval, :block => blk }
87
98
  else
88
99
  raise ArgumentError, "args must be an array" unless Array === args
89
- @expected_calls[name] << { :retval => retval, :args => args }
100
+
101
+ if ENV["MT_KWARGS_HAC\K"] && (Hash === args.last ||
102
+ Hash == args.last) then
103
+ if kwargs.empty? then
104
+ kwargs = args.pop
105
+ else
106
+ unless @@KW_WARNED then
107
+ from = caller.first
108
+ warn "Using MT_KWARGS_HAC\K yet passing kwargs. From #{from}"
109
+ @@KW_WARNED = true
110
+ end
111
+ end
112
+ end
113
+
114
+ @expected_calls[name] <<
115
+ { :retval => retval, :args => args, :kwargs => kwargs }
90
116
  end
91
117
  self
92
118
  end
@@ -94,7 +120,13 @@ module Minitest # :nodoc:
94
120
  def __call name, data # :nodoc:
95
121
  case data
96
122
  when Hash then
97
- "#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}"
123
+ args = data[:args].inspect[1..-2]
124
+ kwargs = data[:kwargs]
125
+ if kwargs && !kwargs.empty? then
126
+ args << ", " unless args.empty?
127
+ args << kwargs.inspect[1..-2]
128
+ end
129
+ "#{name}(#{args}) => #{data[:retval].inspect}"
98
130
  else
99
131
  data.map { |d| __call name, d }.join ", "
100
132
  end
@@ -115,10 +147,14 @@ module Minitest # :nodoc:
115
147
  true
116
148
  end
117
149
 
118
- def method_missing sym, *args, &block # :nodoc:
150
+ def method_missing sym, *args, **kwargs, &block # :nodoc:
119
151
  unless @expected_calls.key?(sym) then
120
152
  if @delegator && @delegator.respond_to?(sym)
121
- return @delegator.public_send(sym, *args, &block)
153
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
154
+ return @delegator.public_send(sym, *args, &block)
155
+ else
156
+ return @delegator.public_send(sym, *args, **kwargs, &block)
157
+ end
122
158
  else
123
159
  raise NoMethodError, "unmocked method %p, expected one of %p" %
124
160
  [sym, @expected_calls.keys.sort_by(&:to_s)]
@@ -129,26 +165,34 @@ module Minitest # :nodoc:
129
165
  expected_call = @expected_calls[sym][index]
130
166
 
131
167
  unless expected_call then
132
- raise MockExpectationError, "No more expects available for %p: %p" %
133
- [sym, args]
168
+ raise MockExpectationError, "No more expects available for %p: %p %p" %
169
+ [sym, args, kwargs]
134
170
  end
135
171
 
136
- expected_args, retval, val_block =
137
- expected_call.values_at(:args, :retval, :block)
172
+ expected_args, expected_kwargs, retval, val_block =
173
+ expected_call.values_at(:args, :kwargs, :retval, :block)
174
+
175
+ expected_kwargs = kwargs.map { |ak, av| [ak, Object] }.to_h if
176
+ Hash == expected_kwargs
138
177
 
139
178
  if val_block then
140
179
  # keep "verify" happy
141
180
  @actual_calls[sym] << expected_call
142
181
 
143
- raise MockExpectationError, "mocked method %p failed block w/ %p" %
144
- [sym, args] unless val_block.call(*args, &block)
182
+ raise MockExpectationError, "mocked method %p failed block w/ %p %p" %
183
+ [sym, args, kwargs] unless val_block.call(*args, **kwargs, &block)
145
184
 
146
185
  return retval
147
186
  end
148
187
 
149
188
  if expected_args.size != args.size then
150
- raise ArgumentError, "mocked method %p expects %d arguments, got %d" %
151
- [sym, expected_args.size, args.size]
189
+ raise ArgumentError, "mocked method %p expects %d arguments, got %p" %
190
+ [sym, expected_args.size, args]
191
+ end
192
+
193
+ if expected_kwargs.size != kwargs.size then
194
+ raise ArgumentError, "mocked method %p expects %d keyword arguments, got %p" %
195
+ [sym, expected_kwargs.size, kwargs]
152
196
  end
153
197
 
154
198
  zipped_args = expected_args.zip(args)
@@ -157,13 +201,33 @@ module Minitest # :nodoc:
157
201
  }
158
202
 
159
203
  unless fully_matched then
160
- raise MockExpectationError, "mocked method %p called with unexpected arguments %p" %
161
- [sym, args]
204
+ fmt = "mocked method %p called with unexpected arguments %p"
205
+ raise MockExpectationError, fmt % [sym, args]
206
+ end
207
+
208
+ unless expected_kwargs.keys.sort == kwargs.keys.sort then
209
+ fmt = "mocked method %p called with unexpected keywords %p vs %p"
210
+ raise MockExpectationError, fmt % [sym, expected_kwargs.keys, kwargs.keys]
211
+ end
212
+
213
+ zipped_kwargs = expected_kwargs.map { |ek, ev|
214
+ av = kwargs[ek]
215
+ [ek, [ev, av]]
216
+ }.to_h
217
+
218
+ fully_matched = zipped_kwargs.all? { |ek, (ev, av)|
219
+ ev === av or ev == av
220
+ }
221
+
222
+ unless fully_matched then
223
+ fmt = "mocked method %p called with unexpected keyword arguments %p vs %p"
224
+ raise MockExpectationError, fmt % [sym, expected_kwargs, kwargs]
162
225
  end
163
226
 
164
227
  @actual_calls[sym] << {
165
228
  :retval => retval,
166
- :args => zipped_args.map! { |mod, a| mod === a ? mod : a },
229
+ :args => zipped_args.map { |e, a| e === a ? e : a },
230
+ :kwargs => zipped_kwargs.map { |k, (e, a)| [k, e === a ? e : a] }.to_h,
167
231
  }
168
232
 
169
233
  retval
@@ -211,31 +275,50 @@ class Object
211
275
  # NOTE: keyword args in callables are NOT checked for correctness
212
276
  # against the existing method. Too many edge cases to be worth it.
213
277
 
214
- def stub name, val_or_callable, *block_args
278
+ def stub name, val_or_callable, *block_args, **block_kwargs, &block
215
279
  new_name = "__minitest_stub__#{name}"
216
280
 
217
281
  metaclass = class << self; self; end
218
282
 
219
283
  if respond_to? name and not methods.map(&:to_s).include? name.to_s then
220
- metaclass.send :define_method, name do |*args|
221
- super(*args)
284
+ metaclass.send :define_method, name do |*args, **kwargs|
285
+ super(*args, **kwargs)
222
286
  end
223
287
  end
224
288
 
225
289
  metaclass.send :alias_method, new_name, name
226
290
 
227
- metaclass.send :define_method, name do |*args, &blk|
228
- if val_or_callable.respond_to? :call then
229
- val_or_callable.call(*args, &blk)
230
- else
231
- blk.call(*block_args) if blk
232
- val_or_callable
291
+ if ENV["MT_KWARGS_HAC\K"] then
292
+ metaclass.send :define_method, name do |*args, &blk|
293
+ if val_or_callable.respond_to? :call then
294
+ val_or_callable.call(*args, &blk)
295
+ else
296
+ blk.call(*block_args, **block_kwargs) if blk
297
+ val_or_callable
298
+ end
299
+ end
300
+ else
301
+ metaclass.send :define_method, name do |*args, **kwargs, &blk|
302
+ if val_or_callable.respond_to? :call then
303
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
304
+ val_or_callable.call(*args, &blk)
305
+ else
306
+ val_or_callable.call(*args, **kwargs, &blk)
307
+ end
308
+ else
309
+ if blk then
310
+ if block_kwargs.empty? then # FIX: drop this after 2.7 dead
311
+ blk.call(*block_args)
312
+ else
313
+ blk.call(*block_args, **block_kwargs)
314
+ end
315
+ end
316
+ val_or_callable
317
+ end
233
318
  end
234
319
  end
235
320
 
236
- metaclass.send(:ruby2_keywords, name) if metaclass.respond_to?(:ruby2_keywords, true)
237
-
238
- yield self
321
+ block[self]
239
322
  ensure
240
323
  metaclass.send :undef_method, name
241
324
  metaclass.send :alias_method, name, new_name
@@ -48,7 +48,7 @@ module Minitest
48
48
  def initialize io # :nodoc:
49
49
  @io = io
50
50
  # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm
51
- # also reference http://en.wikipedia.org/wiki/ANSI_escape_code
51
+ # also reference https://en.wikipedia.org/wiki/ANSI_escape_code
52
52
  @colors ||= (31..36).to_a
53
53
  @size = @colors.size
54
54
  @index = 0
data/lib/minitest/spec.rb CHANGED
@@ -6,6 +6,10 @@ class Module # :nodoc:
6
6
  dont_flip = false if block
7
7
  target_obj = block ? '_{obj.method}' : '_(obj)'
8
8
 
9
+ # https://eregon.me/blog/2021/02/13/correct-delegation-in-ruby-2-27-3.html
10
+ # Drop this when we can drop ruby 2.6 (aka after rails 6.1 EOL, ~2024-06)
11
+ kw_extra = "ruby2_keywords %p" % [new_name] if respond_to?(:ruby2_keywords, true)
12
+
9
13
  # warn "%-22p -> %p %p" % [meth, new_name, dont_flip]
10
14
  self.class_eval <<-EOM, __FILE__, __LINE__ + 1
11
15
  def #{new_name} *args
@@ -14,6 +18,7 @@ class Module # :nodoc:
14
18
  Kernel.warn "DEPRECATED: global use of #{new_name} from #\{where}. Use #{target_obj}.#{new_name} instead. This will fail in Minitest 6."
15
19
  Minitest::Expectation.new(self, Minitest::Spec.current).#{new_name}(*args)
16
20
  end
21
+ #{kw_extra}
17
22
  EOM
18
23
 
19
24
  Minitest::Expectation.class_eval <<-EOM, __FILE__, __LINE__ + 1
@@ -28,6 +33,7 @@ class Module # :nodoc:
28
33
  ctx.#{meth}(args.first, target, *args[1..-1])
29
34
  end
30
35
  end
36
+ #{kw_extra}
31
37
  EOM
32
38
  end
33
39
  end
@@ -66,7 +72,7 @@ module Kernel
66
72
  #
67
73
  # For some suggestions on how to improve your specs, try:
68
74
  #
69
- # http://betterspecs.org
75
+ # https://betterspecs.org
70
76
  #
71
77
  # but do note that several items there are debatable or specific to
72
78
  # rspec.
data/lib/minitest/test.rb CHANGED
@@ -18,6 +18,10 @@ module Minitest
18
18
 
19
19
  PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, SystemExit] # :nodoc:
20
20
 
21
+ SETUP_METHODS = %w[ before_setup setup after_setup ] # :nodoc:
22
+
23
+ TEARDOWN_METHODS = %w[ before_teardown teardown after_teardown ] # :nodoc:
24
+
21
25
  # :stopdoc:
22
26
  class << self; attr_accessor :io_lock; end
23
27
  self.io_lock = Mutex.new
@@ -67,8 +71,8 @@ module Minitest
67
71
 
68
72
  case self.test_order
69
73
  when :random, :parallel then
70
- max = methods.size
71
- methods.sort.sort_by { rand max }
74
+ srand Minitest.seed
75
+ methods.sort.shuffle
72
76
  when :alpha, :sorted then
73
77
  methods.sort
74
78
  else
@@ -76,16 +80,6 @@ module Minitest
76
80
  end
77
81
  end
78
82
 
79
- ##
80
- # Defines the order to run tests (:random by default). Override
81
- # this or use a convenience method to change it for your tests.
82
-
83
- def self.test_order
84
- :random
85
- end
86
-
87
- TEARDOWN_METHODS = %w[ before_teardown teardown after_teardown ] # :nodoc:
88
-
89
83
  ##
90
84
  # Runs a single test with setup/teardown hooks.
91
85
 
@@ -93,7 +87,9 @@ module Minitest
93
87
  with_info_handler do
94
88
  time_it do
95
89
  capture_exceptions do
96
- before_setup; setup; after_setup
90
+ SETUP_METHODS.each do |hook|
91
+ self.send hook
92
+ end
97
93
 
98
94
  self.send self.name
99
95
  end
@@ -145,7 +141,7 @@ module Minitest
145
141
  # end
146
142
  # end
147
143
  #
148
- # class MiniTest::Test
144
+ # class Minitest::Test
149
145
  # include MyMinitestPlugin
150
146
  # end
151
147
 
@@ -203,12 +199,34 @@ module Minitest
203
199
 
204
200
  def sanitize_exception e # :nodoc:
205
201
  Marshal.dump e
206
- e
207
- rescue TypeError
202
+ e # good: use as-is
203
+ rescue
204
+ neuter_exception e
205
+ end
206
+
207
+ def neuter_exception e # :nodoc:
208
208
  bt = e.backtrace
209
- e = RuntimeError.new "Wrapped undumpable exception for: #{e.class}: #{e.message}"
210
- e.set_backtrace bt
211
- e
209
+ msg = e.message.dup
210
+
211
+ new_exception e.class, msg, bt # e.class can be a problem...
212
+ rescue
213
+ msg.prepend "Neutered Exception #{e.class}: "
214
+
215
+ new_exception RuntimeError, msg, bt, true # but if this raises, we die
216
+ end
217
+
218
+ def new_exception klass, msg, bt, kill = false # :nodoc:
219
+ ne = klass.new msg
220
+ ne.set_backtrace bt
221
+
222
+ if kill then
223
+ ne.instance_variables.each do |v|
224
+ ne.remove_instance_variable v
225
+ end
226
+ end
227
+
228
+ Marshal.dump ne # can raise TypeError
229
+ ne
212
230
  end
213
231
 
214
232
  def with_info_handler &block # :nodoc:
@@ -227,4 +245,4 @@ module Minitest
227
245
  end # Test
228
246
  end
229
247
 
230
- require "minitest/unit" unless defined?(MiniTest) # compatibility layer only
248
+ require "minitest/unit" if ENV["MT_COMPAT"] # compatibility layer only
@@ -0,0 +1,302 @@
1
+ require "shellwords"
2
+ require "rbconfig"
3
+ require "rake/tasklib"
4
+
5
+ module Minitest # :nodoc:
6
+
7
+ ##
8
+ # Minitest::TestTask is a rake helper that generates several rake
9
+ # tasks under the main test task's name-space.
10
+ #
11
+ # task <name> :: the main test task
12
+ # task <name>:cmd :: prints the command to use
13
+ # task <name>:deps :: runs each test file by itself to find dependency errors
14
+ # task <name>:slow :: runs the tests and reports the slowest 25 tests.
15
+ #
16
+ # Examples:
17
+ #
18
+ # Minitest::TestTask.create
19
+ #
20
+ # The most basic and default setup.
21
+ #
22
+ # Minitest::TestTask.create :my_tests
23
+ #
24
+ # The most basic/default setup, but with a custom name
25
+ #
26
+ # Minitest::TestTask.create :unit do |t|
27
+ # t.test_globs = ["test/unit/**/*_test.rb"]
28
+ # t.warning = false
29
+ # end
30
+ #
31
+ # Customize the name and only run unit tests.
32
+ #
33
+ # NOTE: To hook this task up to the default, make it a dependency:
34
+ #
35
+ # task default: :unit
36
+
37
+ class TestTask < Rake::TaskLib
38
+ WINDOWS = RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ # :nodoc:
39
+
40
+ ##
41
+ # Create several test-oriented tasks under +name+. Takes an
42
+ # optional block to customize variables.
43
+
44
+ def self.create name = :test, &block
45
+ task = new name
46
+ task.instance_eval(&block) if block
47
+ task.process_env
48
+ task.define
49
+ task
50
+ end
51
+
52
+ ##
53
+ # Extra arguments to pass to the tests. Defaults empty but gets
54
+ # populated by a number of enviroment variables:
55
+ #
56
+ # N (-n flag) :: a string or regexp of tests to run.
57
+ # X (-e flag) :: a string or regexp of tests to exclude.
58
+ # A (arg) :: quick way to inject an arbitrary argument (eg A=--help).
59
+ #
60
+ # See #process_env
61
+
62
+ attr_accessor :extra_args
63
+
64
+ ##
65
+ # The code to load the framework. Defaults to requiring
66
+ # minitest/autorun...
67
+ #
68
+ # Why do I have this as an option?
69
+
70
+ attr_accessor :framework
71
+
72
+ ##
73
+ # Extra library directories to include. Defaults to %w[lib test
74
+ # .]. Also uses $MT_LIB_EXTRAS allowing you to dynamically
75
+ # override/inject directories for custom runs.
76
+
77
+ attr_accessor :libs
78
+
79
+ ##
80
+ # The name of the task and base name for the other tasks generated.
81
+
82
+ attr_accessor :name
83
+
84
+ ##
85
+ # File globs to find test files. Defaults to something sensible to
86
+ # find test files under the test directory.
87
+
88
+ attr_accessor :test_globs
89
+
90
+ ##
91
+ # Turn on ruby warnings (-w flag). Defaults to true.
92
+
93
+ attr_accessor :warning
94
+
95
+ ##
96
+ # Optional: Additional ruby to run before the test framework is loaded.
97
+
98
+ attr_accessor :test_prelude
99
+
100
+ ##
101
+ # Print out commands as they run. Defaults to Rake's +trace+ (-t
102
+ # flag) option.
103
+
104
+ attr_accessor :verbose
105
+
106
+ ##
107
+ # Use TestTask.create instead.
108
+
109
+ def initialize name = :test # :nodoc:
110
+ self.extra_args = []
111
+ self.framework = %(require "minitest/autorun")
112
+ self.libs = %w[lib test .]
113
+ self.name = name
114
+ self.test_globs = ["test/**/test_*.rb",
115
+ "test/**/*_test.rb"]
116
+ self.test_prelude = nil
117
+ self.verbose = Rake.application.options.trace
118
+ self.warning = true
119
+ end
120
+
121
+ ##
122
+ # Extract variables from the environment and convert them to
123
+ # command line arguments. See #extra_args.
124
+ #
125
+ # Environment Variables:
126
+ #
127
+ # MT_LIB_EXTRAS :: Extra libs to dynamically override/inject for custom runs.
128
+ # N :: Tests to run (string or /regexp/).
129
+ # X :: Tests to exclude (string or /regexp/).
130
+ # A :: Any extra arguments. Honors shell quoting.
131
+ #
132
+ # Deprecated:
133
+ #
134
+ # TESTOPTS :: For argument passing, use +A+.
135
+ # N :: For parallel testing, use +MT_CPU+.
136
+ # FILTER :: Same as +TESTOPTS+.
137
+
138
+ def process_env
139
+ warn "TESTOPTS is deprecated in Minitest::TestTask. Use A instead" if
140
+ ENV["TESTOPTS"]
141
+ warn "FILTER is deprecated in Minitest::TestTask. Use A instead" if
142
+ ENV["FILTER"]
143
+ warn "N is deprecated in Minitest::TestTask. Use MT_CPU instead" if
144
+ ENV["N"] && ENV["N"].to_i > 0
145
+
146
+ lib_extras = (ENV["MT_LIB_EXTRAS"] || "").split File::PATH_SEPARATOR
147
+ self.libs[0,0] = lib_extras
148
+
149
+ extra_args << "-n" << ENV["N"] if ENV["N"]
150
+ extra_args << "-e" << ENV["X"] if ENV["X"]
151
+ extra_args.concat Shellwords.split(ENV["TESTOPTS"]) if ENV["TESTOPTS"]
152
+ extra_args.concat Shellwords.split(ENV["FILTER"]) if ENV["FILTER"]
153
+ extra_args.concat Shellwords.split(ENV["A"]) if ENV["A"]
154
+
155
+ ENV.delete "N" if ENV["N"]
156
+
157
+ # TODO? RUBY_DEBUG = ENV["RUBY_DEBUG"]
158
+ # TODO? ENV["RUBY_FLAGS"]
159
+
160
+ extra_args.compact!
161
+ end
162
+
163
+ def define # :nodoc:
164
+ desc "Run the test suite. Use N, X, A, and TESTOPTS to add flags/args."
165
+ task name do
166
+ ruby make_test_cmd, verbose:verbose
167
+ end
168
+
169
+ desc "Print out the test command. Good for profiling and other tools."
170
+ task "#{name}:cmd" do
171
+ puts "ruby #{make_test_cmd}"
172
+ end
173
+
174
+ desc "Show which test files fail when run in isolation."
175
+ task "#{name}:isolated" do
176
+ tests = Dir[*self.test_globs].uniq
177
+
178
+ # 3 seems to be the magic number... (tho not by that much)
179
+ bad, good, n = {}, [], (ENV.delete("K") || 3).to_i
180
+ file = ENV.delete("F")
181
+ times = {}
182
+
183
+ tt0 = Time.now
184
+
185
+ n.threads_do tests.sort do |path|
186
+ t0 = Time.now
187
+ output = `#{Gem.ruby} #{make_test_cmd path} 2>&1`
188
+ t1 = Time.now - t0
189
+
190
+ times[path] = t1
191
+
192
+ if $?.success?
193
+ $stderr.print "."
194
+ good << path
195
+ else
196
+ $stderr.print "x"
197
+ bad[path] = output
198
+ end
199
+ end
200
+
201
+ puts "done"
202
+ puts "Ran in %.2f seconds" % [ Time.now - tt0 ]
203
+
204
+ if file then
205
+ require "json"
206
+ File.open file, "w" do |io|
207
+ io.puts JSON.pretty_generate times
208
+ end
209
+ end
210
+
211
+ unless good.empty?
212
+ puts
213
+ puts "# Good tests:"
214
+ puts
215
+ good.sort.each do |path|
216
+ puts "%.2fs: %s" % [times[path], path]
217
+ end
218
+ end
219
+
220
+ unless bad.empty?
221
+ puts
222
+ puts "# Bad tests:"
223
+ puts
224
+ bad.keys.sort.each do |path|
225
+ puts "%.2fs: %s" % [times[path], path]
226
+ end
227
+ puts
228
+ puts "# Bad Test Output:"
229
+ puts
230
+ bad.sort.each do |path, output|
231
+ puts
232
+ puts "# #{path}:"
233
+ puts output
234
+ end
235
+ exit 1
236
+ end
237
+ end
238
+
239
+ task "#{name}:deps" => "#{name}:isolated" # now just an alias
240
+
241
+ desc "Show bottom 25 tests wrt time."
242
+ task "#{name}:slow" do
243
+ sh ["rake #{name} A=-v",
244
+ "egrep '#test_.* s = .'",
245
+ "sort -n -k2 -t=",
246
+ "tail -25"].join " | "
247
+ end
248
+ end
249
+
250
+ ##
251
+ # Generate the test command-line.
252
+
253
+ def make_test_cmd globs = test_globs
254
+ tests = []
255
+ tests.concat Dir[*globs].sort.shuffle # TODO: SEED -> srand first?
256
+ tests.map! { |f| %(require "#{f}") }
257
+
258
+ runner = []
259
+ runner << test_prelude if test_prelude
260
+ runner << framework
261
+ runner.concat tests
262
+ runner = runner.join "; "
263
+
264
+ args = []
265
+ args << "-I#{libs.join(File::PATH_SEPARATOR)}" unless libs.empty?
266
+ args << "-w" if warning
267
+ args << '-e'
268
+ args << "'#{runner}'"
269
+ args << '--'
270
+ args << extra_args.map(&:shellescape)
271
+
272
+ args.join " "
273
+ end
274
+ end
275
+ end
276
+
277
+ class Work < Queue # :nodoc:
278
+ def initialize jobs = [] # :nodoc:
279
+ super()
280
+
281
+ jobs.each do |job|
282
+ self << job
283
+ end
284
+
285
+ close
286
+ end
287
+ end
288
+
289
+ class Integer # :nodoc:
290
+ def threads_do(jobs) # :nodoc:
291
+ require "thread"
292
+ q = Work.new jobs
293
+
294
+ self.times.map {
295
+ Thread.new do
296
+ while job = q.pop # go until quit value
297
+ yield job
298
+ end
299
+ end
300
+ }.each(&:join)
301
+ end
302
+ end