test-unit-ruby-core 1.0.14 → 1.0.16

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/core_assertions.rb +53 -22
  3. data/lib/envutil.rb +16 -5
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1a6b243df63415aab0c4d40274b847b50f386b16bd422a88b26ee788559f547
4
- data.tar.gz: 7d216b5c7c53872f9c50c5b425ecda7b467e4e09ff0e86743288cbfe492f6548
3
+ metadata.gz: 542bb539eb9d5482af2e15d1f343129f6ce264e68a382e31b12205cb93f01e0d
4
+ data.tar.gz: 68ac63efa6ccc2e04299a7cee8443347553bf6670cd1fdf00d8a429b8b65d24f
5
5
  SHA512:
6
- metadata.gz: ee5b3f46d0cdab4dfc899b280980e78a2d394af4134756ca8bad77d620cf857aeaa5e576cda72fc30baeeddfcca064f20c21fdaf895f141f60a96aae7fac7209
7
- data.tar.gz: '0593598f1f9f1756ab80fb9121dd230951d88a7fc3de06ab333b651a99fb42ccb188e19f138c1e014893b4f513409fcc7f9bca079c3308c7e2a5ab9ce998510a'
6
+ metadata.gz: d460cda4850819f3f04cfa3b9ca4a75df95982276e31bb60017515af5169734100bbd7371e3fbf79424d827c83d9de6d449531b40c0c2c526f141b18a8e8362c
7
+ data.tar.gz: aec38efd7edcdeb6b860ded7a6919ee48224229ba15cca086f2a2e73e35dc2c78937b5521fc9fb7d09567f9a1a885618125c8fb65f6f251abf140fc32172a1db
@@ -18,11 +18,11 @@ module Test
18
18
 
19
19
  unless $DEBUG then
20
20
  bt.each do |line|
21
- break if pattern.match?(line)
21
+ break if pattern =~ line
22
22
  new_bt << line
23
23
  end
24
24
 
25
- new_bt = bt.reject { |line| pattern.match?(line) } if new_bt.empty?
25
+ new_bt = bt.reject { |line| pattern =~ line } if new_bt.empty?
26
26
  new_bt = bt.dup if new_bt.empty?
27
27
  else
28
28
  new_bt = bt.dup
@@ -303,9 +303,34 @@ module Test
303
303
 
304
304
  def separated_runner(token, out = nil)
305
305
  include(*Test::Unit::TestCase.ancestors.select {|c| !c.is_a?(Class) })
306
+
306
307
  out = out ? IO.new(out, 'w') : STDOUT
308
+
309
+ # avoid method redefinitions
310
+ out_write = out.method(:write)
311
+ integer_to_s = Integer.instance_method(:to_s)
312
+ array_pack = Array.instance_method(:pack)
313
+ marshal_dump = Marshal.method(:dump)
314
+ assertions_ivar_set = Test::Unit::Assertions.method(:instance_variable_set)
315
+ assertions_ivar_get = Test::Unit::Assertions.method(:instance_variable_get)
316
+ Test::Unit::Assertions.module_eval do
317
+ @_assertions = 0
318
+
319
+ undef _assertions=
320
+ define_method(:_assertions=, ->(n) {assertions_ivar_set.call(:@_assertions, n)})
321
+
322
+ undef _assertions
323
+ define_method(:_assertions, -> {assertions_ivar_get.call(:@_assertions)})
324
+ end
325
+ # assume Method#call and UnboundMethod#bind_call need to work as the original
326
+
307
327
  at_exit {
308
- out.puts "#{token}<error>", [Marshal.dump($!)].pack('m'), "#{token}</error>", "#{token}assertions=#{self._assertions}"
328
+ assertions = assertions_ivar_get.call(:@_assertions)
329
+ out_write.call <<~OUT
330
+ <error id="#{token}" assertions=#{integer_to_s.bind(assertions).call}>
331
+ #{array_pack.bind([marshal_dump.call($!)]).call('m0')}
332
+ </error id="#{token}">
333
+ OUT
309
334
  }
310
335
  if defined?(Test::Unit::Runner)
311
336
  Test::Unit::Runner.class_variable_set(:@@stop_auto_run, true)
@@ -339,6 +364,8 @@ eom
339
364
  args = args.dup
340
365
  args.insert((Hash === args.first ? 1 : 0), "-w", "--disable=gems", *$:.map {|l| "-I#{l}"})
341
366
  args << "--debug" if RUBY_ENGINE == 'jruby' # warning: tracing (e.g. set_trace_func) will not capture all events without --debug flag
367
+ # power_assert 3 requires ruby 3.1 or later
368
+ args << "-W:no-experimental" if ("2.7."..."3.1.").cover?(RUBY_VERSION)
342
369
  stdout, stderr, status = EnvUtil.invoke_ruby(args, src, capture_stdout, true, **opt)
343
370
 
344
371
  if sanitizers&.lsan_enabled?
@@ -357,27 +384,30 @@ eom
357
384
  end
358
385
  raise if $!
359
386
  abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig))
387
+ marshal_error = nil
360
388
  assert(!abort, FailDesc[status, nil, stderr])
361
- self._assertions += res[/^#{token_re}assertions=(\d+)/, 1].to_i
362
- begin
363
- res = Marshal.load(res[/^#{token_re}<error>\n\K.*\n(?=#{token_re}<\/error>$)/m].unpack1("m"))
364
- rescue => marshal_error
365
- ignore_stderr = nil
366
- res = nil
367
- end
368
- if res and !(SystemExit === res)
369
- if bt = res.backtrace
370
- bt.each do |l|
371
- l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"}
372
- end
373
- bt.concat(caller)
389
+ res.scan(/^<error id="#{token_re}" assertions=(\d+)>\n(.*?)\n(?=<\/error id="#{token_re}">$)/m) do
390
+ self._assertions += $1.to_i
391
+ begin
392
+ res = Marshal.load($2.unpack1("m")) or next
393
+ rescue => marshal_error
394
+ ignore_stderr = nil
395
+ res = nil
374
396
  else
375
- res.set_backtrace(caller)
397
+ next if SystemExit === res
398
+ if bt = res.backtrace
399
+ bt.each do |l|
400
+ l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"}
401
+ end
402
+ bt.concat(caller)
403
+ else
404
+ res.set_backtrace(caller)
405
+ end
406
+ raise res
376
407
  end
377
- raise res
378
408
  end
379
409
 
380
- # really is it succeed?
410
+ # really did it succeed?
381
411
  unless ignore_stderr
382
412
  # the body of assert_separately must not output anything to detect error
383
413
  assert(stderr.empty?, FailDesc[status, "assert_separately failed with error message", stderr])
@@ -388,7 +418,7 @@ eom
388
418
 
389
419
  # Run Ractor-related test without influencing the main test suite
390
420
  def assert_ractor(src, args: [], require: nil, require_relative: nil, file: nil, line: nil, ignore_stderr: nil, **opt)
391
- return unless defined?(Ractor)
421
+ omit unless defined?(Ractor)
392
422
 
393
423
  # https://bugs.ruby-lang.org/issues/21262
394
424
  shim_value = "class Ractor; alias value take; end" unless Ractor.method_defined?(:value)
@@ -883,10 +913,11 @@ eom
883
913
 
884
914
  first = seq.first
885
915
  *arg = pre.call(first)
886
- times = (0..(rehearsal || (2 * first))).map do
916
+ raw_times = (0..(rehearsal || (2 * first))).map do
887
917
  measure[arg, "rehearsal"].nonzero?
888
918
  end
889
- times.compact!
919
+ times = raw_times.compact
920
+ raise "all measurements are zero: #{raw_times.inspect}" if times.empty?
890
921
  tmin, tmax = times.minmax
891
922
 
892
923
  # safe_factor * tmax * rehearsal_time_variance_factor(equals to 1 when variance is small)
data/lib/envutil.rb CHANGED
@@ -63,6 +63,14 @@ module EnvUtil
63
63
  end
64
64
  end
65
65
 
66
+ if RUBY_ENGINE == "truffleruby"
67
+ # Tests relying on timeout have high variance on TruffleRuby due to the highly-optimizing JIT, deoptimization, profiling interpreter, different GC, etc.
68
+ # Setting a default timeout scale helps avoid transient failures for tests relying on timeouts.
69
+ # We choose 10 because it is the same number used in CRuby CI on macOS:
70
+ # https://github.com/ruby/ruby/blob/9d46b0c735877f152a0b4b16b8153c6f395dee28/.github/workflows/macos.yml#L133
71
+ self.timeout_scale = 10
72
+ end
73
+
66
74
  def apply_timeout_scale(t)
67
75
  if scale = EnvUtil.timeout_scale
68
76
  t * scale
@@ -122,7 +130,7 @@ module EnvUtil
122
130
 
123
131
  register("gdb") do
124
132
  class << self
125
- def usable?; system(*%w[gdb --batch --quiet --nx -ex exit]); end
133
+ def usable?; system(*%w[gdb --batch --quiet --nx -ex exit], out: IO::NULL, err: IO::NULL); end
126
134
  def start(pid, *args, **opts)
127
135
  spawn(*%W[gdb --batch --quiet --pid #{pid}], *args, **opts)
128
136
  end
@@ -132,7 +140,7 @@ module EnvUtil
132
140
 
133
141
  register("lldb") do
134
142
  class << self
135
- def usable?; system(*%w[lldb -Q --no-lldbinit -o exit]); end
143
+ def usable?; system(*%w[lldb -Q --no-lldbinit -o exit], out: IO::NULL, err: IO::NULL); end
136
144
  def start(pid, *args, **opts)
137
145
  spawn(*%W[lldb --batch -Q --attach-pid #{pid}], *args, **opts)
138
146
  end
@@ -141,7 +149,10 @@ module EnvUtil
141
149
  end
142
150
 
143
151
  def self.search
144
- @debugger ||= @list.find(&:usable?)
152
+ # Cache the result even when no debugger is available, not to probe
153
+ # unusable debuggers repeatedly.
154
+ return @debugger if defined?(@debugger)
155
+ @debugger = @list.find(&:usable?)
145
156
  end
146
157
  end
147
158
 
@@ -228,7 +239,7 @@ module EnvUtil
228
239
  args = [args] if args.kind_of?(String)
229
240
  # use the same parser as current ruby
230
241
  if (args.none? { |arg| arg.start_with?("--parser=") } and
231
- /^ +--parser=/ =~ IO.popen([rubybin, "--help"], &:read))
242
+ /^ +--parser=/ =~ IO.popen([{"PAGER"=>nil, "RUBY_PAGER"=>nil}, rubybin, "--help", err: %i[child out]], &:read))
232
243
  args = ["--parser=#{current_parser}"] + args
233
244
  end
234
245
  pid = spawn(child_env, *precommand, rubybin, *args, opt)
@@ -279,7 +290,7 @@ module EnvUtil
279
290
  module_function :invoke_ruby
280
291
 
281
292
  def current_parser
282
- features = RUBY_DESCRIPTION[%r{\)\K [-+*/%._0-9a-zA-Z ]*(?=\[[-+*/%._0-9a-zA-Z]+\]\z)}]
293
+ features = RUBY_DESCRIPTION[%r{\)\K [-+*/%._0-9a-zA-Z\[\] ]*(?=\[[-+*/%._0-9a-zA-Z]+\]\z)}]
283
294
  features&.split&.include?("+PRISM") ? "prism" : "parse.y"
284
295
  end
285
296
  module_function :current_parser
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-unit-ruby-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.14
4
+ version: 1.0.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroshi SHIBATA
8
8
  - Nobu Nakada
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-25 00:00:00.000000000 Z
11
+ date: 2026-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: test-unit