From 7c57f7b363f1a13ddefe852e5bf3db42b8c64624 Mon Sep 17 00:00:00 2001 From: kares Date: Wed, 30 Oct 2024 14:39:56 +0100 Subject: [PATCH 01/17] [deps] bump rexml default gem to 3.3.9 --- lib/pom.rb | 2 +- lib/pom.xml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pom.rb b/lib/pom.rb index d910b800bd0..a93ce28dd41 100644 --- a/lib/pom.rb +++ b/lib/pom.rb @@ -135,7 +135,7 @@ def log(message=nil) ['rake', '${rake.version}'], # Depends on many CRuby internals # ['rbs', '2.0.0'], - ['rexml', '3.3.4'], + ['rexml', '3.3.9'], ['rss', '0.2.9'], ['test-unit', '3.5.3'], # Depends on many CRuby internals diff --git a/lib/pom.xml b/lib/pom.xml index fedb1636185..45f975bbb92 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -1035,7 +1035,7 @@ DO NOT MODIFY - GENERATED CODE rubygems rexml - 3.3.4 + 3.3.9 gem provided @@ -1169,7 +1169,7 @@ DO NOT MODIFY - GENERATED CODE specifications/prime-0.1.2* specifications/power_assert-2.0.1* specifications/rake-${rake.version}* - specifications/rexml-3.3.4* + specifications/rexml-3.3.9* specifications/rss-0.2.9* specifications/test-unit-3.5.3* gems/rubygems-update-3.3.26*/**/* @@ -1249,7 +1249,7 @@ DO NOT MODIFY - GENERATED CODE gems/prime-0.1.2*/**/* gems/power_assert-2.0.1*/**/* gems/rake-${rake.version}*/**/* - gems/rexml-3.3.4*/**/* + gems/rexml-3.3.9*/**/* gems/rss-0.2.9*/**/* gems/test-unit-3.5.3*/**/* cache/rubygems-update-3.3.26* @@ -1329,7 +1329,7 @@ DO NOT MODIFY - GENERATED CODE cache/prime-0.1.2* cache/power_assert-2.0.1* cache/rake-${rake.version}* - cache/rexml-3.3.4* + cache/rexml-3.3.9* cache/rss-0.2.9* cache/test-unit-3.5.3* From a2e8eb1f3a46376a0e0319ee92e815d6b59a3d97 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 31 Oct 2024 09:02:50 -0500 Subject: [PATCH 02/17] Always check for thread events after lock acquisition Upon acquiring a lock, we should always check for interrupts that may have occurred while sleeping or acquiring the lock. This avoids proceeding with a critical section of code while there are interrupts pending. Fixes jruby/jruby#8403 --- core/src/main/java/org/jruby/ext/thread/Mutex.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/jruby/ext/thread/Mutex.java b/core/src/main/java/org/jruby/ext/thread/Mutex.java index afe401d5c8e..a80f7d1eb38 100644 --- a/core/src/main/java/org/jruby/ext/thread/Mutex.java +++ b/core/src/main/java/org/jruby/ext/thread/Mutex.java @@ -105,7 +105,7 @@ public IRubyObject lock(ThreadContext context) { for (;;) { try { context.getThread().lockInterruptibly(lock); - return this; + break; } catch (InterruptedException ex) { /// ignore, check thread events and try again! context.pollThreadEvents(); @@ -113,6 +113,9 @@ public IRubyObject lock(ThreadContext context) { } } + // always check for thread interrupts after acquiring lock + thread.pollThreadEvents(context); + return this; } From 547c6b150eb2c15799349ba727d036cbb62a4069 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Mon, 4 Nov 2024 14:42:23 -0600 Subject: [PATCH 03/17] Version 9.4.9.0 updated for release --- VERSION | 2 +- core/pom.xml | 4 ++-- lib/pom.xml | 4 ++-- pom.xml | 5 ++++- shaded/pom.xml | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/VERSION b/VERSION index 756943524da..045beacb85c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.4.9.0-SNAPSHOT +9.4.9.0 diff --git a/core/pom.xml b/core/pom.xml index c3298a30d57..ae9b46a5d95 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.9.0-SNAPSHOT + 9.4.9.0 jruby-base JRuby Base @@ -716,7 +716,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-base - 9.4.9.0-SNAPSHOT + 9.4.9.0 diff --git a/lib/pom.xml b/lib/pom.xml index 45f975bbb92..18f934c2a5e 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.9.0-SNAPSHOT + 9.4.9.0 jruby-stdlib JRuby Lib Setup @@ -28,7 +28,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-core - 9.4.9.0-SNAPSHOT + 9.4.9.0 test diff --git a/pom.xml b/pom.xml index 3e228d704d2..5512a2c7634 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.9.0-SNAPSHOT + 9.4.9.0 pom JRuby JRuby is the effort to recreate the Ruby (https://www.ruby-lang.org) interpreter in Java. @@ -273,6 +273,9 @@ DO NOT MODIFY - GENERATED CODE [3.3.0,) + + No Snapshots Allowed! + diff --git a/shaded/pom.xml b/shaded/pom.xml index dd3dc1be360..dbbe1729539 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.9.0-SNAPSHOT + 9.4.9.0 jruby-core JRuby Core From e40e88570ae29f34315cfad97d77f96786ddba57 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 11:58:51 -0600 Subject: [PATCH 04/17] Update to ruby/mspec@6499325 --- spec/mspec/lib/mspec/guards/platform.rb | 24 ++++++++++-- spec/mspec/lib/mspec/helpers/numeric.rb | 26 +++++++++++-- .../lib/mspec/runner/actions/leakchecker.rb | 6 +-- spec/mspec/spec/guards/platform_spec.rb | 38 +++++++++---------- 4 files changed, 65 insertions(+), 29 deletions(-) diff --git a/spec/mspec/lib/mspec/guards/platform.rb b/spec/mspec/lib/mspec/guards/platform.rb index e87b08a4c13..fadd8d75efb 100644 --- a/spec/mspec/lib/mspec/guards/platform.rb +++ b/spec/mspec/lib/mspec/guards/platform.rb @@ -53,16 +53,27 @@ def self.wsl? end end + # In bits WORD_SIZE = 1.size * 8 + deprecate_constant :WORD_SIZE + # In bits POINTER_SIZE = begin require 'rbconfig/sizeof' RbConfig::SIZEOF["void*"] * 8 rescue LoadError - WORD_SIZE + [0].pack('j').size * 8 + end + + # In bits + C_LONG_SIZE = if defined?(RbConfig::SIZEOF[]) + RbConfig::SIZEOF["long"] * 8 + else + [0].pack('l!').size * 8 end def self.wordsize?(size) + warn "#wordsize? is deprecated, use #c_long_size?" size == WORD_SIZE end @@ -70,6 +81,10 @@ def self.pointer_size?(size) size == POINTER_SIZE end + def self.c_long_size?(size) + size == C_LONG_SIZE + end + def initialize(*args) if args.last.is_a?(Hash) @options, @platforms = args.last, args[0..-2] @@ -85,10 +100,13 @@ def match? case key when :os match &&= PlatformGuard.os?(*value) - when :wordsize - match &&= PlatformGuard.wordsize? value when :pointer_size match &&= PlatformGuard.pointer_size? value + when :wordsize + warn ":wordsize is deprecated, use :c_long_size" + match &&= PlatformGuard.wordsize? value + when :c_long_size + match &&= PlatformGuard::c_long_size? value end end match diff --git a/spec/mspec/lib/mspec/helpers/numeric.rb b/spec/mspec/lib/mspec/helpers/numeric.rb index c1ed81a2332..0b47855cd29 100644 --- a/spec/mspec/lib/mspec/helpers/numeric.rb +++ b/spec/mspec/lib/mspec/helpers/numeric.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true require 'mspec/guards/platform' def nan_value @@ -15,11 +16,11 @@ def bignum_value(plus = 0) end def max_long - 2**(0.size * 8 - 1) - 1 + 2**(PlatformGuard::C_LONG_SIZE - 1) - 1 end def min_long - -(2**(0.size * 8 - 1)) + -(2**(PlatformGuard::C_LONG_SIZE - 1)) end # This is a bit hairy, but we need to be able to write specs that cover the @@ -28,7 +29,24 @@ def min_long # specs based on the relationship between values rather than specific # values. if PlatformGuard.standard? or PlatformGuard.implementation? :topaz - if PlatformGuard.wordsize? 32 + limits_available = begin + require 'rbconfig/sizeof' + defined?(RbConfig::LIMITS.[]) && ['FIXNUM_MAX', 'FIXNUM_MIN'].all? do |key| + Integer === RbConfig::LIMITS[key] + end + rescue LoadError + false + end + + if limits_available + def fixnum_max + RbConfig::LIMITS['FIXNUM_MAX'] + end + + def fixnum_min + RbConfig::LIMITS['FIXNUM_MIN'] + end + elsif PlatformGuard.c_long_size? 32 def fixnum_max (2**30) - 1 end @@ -36,7 +54,7 @@ def fixnum_max def fixnum_min -(2**30) end - elsif PlatformGuard.wordsize? 64 + elsif PlatformGuard.c_long_size? 64 def fixnum_max (2**62) - 1 end diff --git a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb index 71797b9815c..0a8c9c32521 100644 --- a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb +++ b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb @@ -132,14 +132,14 @@ class << self attr_accessor :count end - def new(data) + def new(...) LeakChecker::TempfileCounter.count += 1 - super(data) + super end } LeakChecker.const_set(:TempfileCounter, m) - class << Tempfile::Remover + class << Tempfile prepend LeakChecker::TempfileCounter end end diff --git a/spec/mspec/spec/guards/platform_spec.rb b/spec/mspec/spec/guards/platform_spec.rb index 88a7ad86f23..bd374328004 100644 --- a/spec/mspec/spec/guards/platform_spec.rb +++ b/spec/mspec/spec/guards/platform_spec.rb @@ -81,44 +81,44 @@ end end -RSpec.describe Object, "#platform_is :wordsize => SIZE_SPEC" do +RSpec.describe Object, "#platform_is :c_long_size => SIZE_SPEC" do before :each do - @guard = PlatformGuard.new :darwin, :wordsize => 32 + @guard = PlatformGuard.new :darwin, :c_long_size => 32 allow(PlatformGuard).to receive(:os?).and_return(true) allow(PlatformGuard).to receive(:new).and_return(@guard) ScratchPad.clear end - it "yields when #wordsize? returns true" do - allow(PlatformGuard).to receive(:wordsize?).and_return(true) - platform_is(:wordsize => 32) { ScratchPad.record :yield } + it "yields when #c_long_size? returns true" do + allow(PlatformGuard).to receive(:c_long_size?).and_return(true) + platform_is(:c_long_size => 32) { ScratchPad.record :yield } expect(ScratchPad.recorded).to eq(:yield) end - it "doesn not yield when #wordsize? returns false" do - allow(PlatformGuard).to receive(:wordsize?).and_return(false) - platform_is(:wordsize => 32) { ScratchPad.record :yield } + it "doesn not yield when #c_long_size? returns false" do + allow(PlatformGuard).to receive(:c_long_size?).and_return(false) + platform_is(:c_long_size => 32) { ScratchPad.record :yield } expect(ScratchPad.recorded).not_to eq(:yield) end end -RSpec.describe Object, "#platform_is_not :wordsize => SIZE_SPEC" do +RSpec.describe Object, "#platform_is_not :c_long_size => SIZE_SPEC" do before :each do - @guard = PlatformGuard.new :darwin, :wordsize => 32 + @guard = PlatformGuard.new :darwin, :c_long_size => 32 allow(PlatformGuard).to receive(:os?).and_return(true) allow(PlatformGuard).to receive(:new).and_return(@guard) ScratchPad.clear end - it "yields when #wordsize? returns false" do - allow(PlatformGuard).to receive(:wordsize?).and_return(false) - platform_is_not(:wordsize => 32) { ScratchPad.record :yield } + it "yields when #c_long_size? returns false" do + allow(PlatformGuard).to receive(:c_long_size?).and_return(false) + platform_is_not(:c_long_size => 32) { ScratchPad.record :yield } expect(ScratchPad.recorded).to eq(:yield) end - it "doesn not yield when #wordsize? returns true" do - allow(PlatformGuard).to receive(:wordsize?).and_return(true) - platform_is_not(:wordsize => 32) { ScratchPad.record :yield } + it "doesn not yield when #c_long_size? returns true" do + allow(PlatformGuard).to receive(:c_long_size?).and_return(true) + platform_is_not(:c_long_size => 32) { ScratchPad.record :yield } expect(ScratchPad.recorded).not_to eq(:yield) end end @@ -184,13 +184,13 @@ end end -RSpec.describe PlatformGuard, ".wordsize?" do +RSpec.describe PlatformGuard, ".c_long_size?" do it "returns true when arg is 32 and 1.size is 4" do - expect(PlatformGuard.wordsize?(32)).to eq(1.size == 4) + expect(PlatformGuard.c_long_size?(32)).to eq(1.size == 4) end it "returns true when arg is 64 and 1.size is 8" do - expect(PlatformGuard.wordsize?(64)).to eq(1.size == 8) + expect(PlatformGuard.c_long_size?(64)).to eq(1.size == 8) end end From 947a31f63ceaee926ebfbd67d0ccba23a878762f Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 11:58:53 -0600 Subject: [PATCH 05/17] Update to ruby/spec@a230079 --- spec/ruby/.mspec.constants | 1 + spec/ruby/.rubocop.yml | 18 + spec/ruby/.rubocop_todo.yml | 44 +- spec/ruby/CONTRIBUTING.md | 6 +- spec/ruby/bin/rubocop | 12 + spec/ruby/command_line/fixtures/debug_info.rb | 1 - spec/ruby/command_line/frozen_strings_spec.rb | 13 +- spec/ruby/command_line/rubyopt_spec.rb | 6 +- spec/ruby/core/array/fetch_values_spec.rb | 48 +++ spec/ruby/core/array/pack/l_spec.rb | 16 +- spec/ruby/core/array/pack/shared/integer.rb | 4 +- .../core/basicobject/instance_exec_spec.rb | 20 +- .../builtin_constants_spec.rb | 28 ++ spec/ruby/core/complex/equal_value_spec.rb | 2 +- spec/ruby/core/data/to_h_spec.rb | 65 +++ spec/ruby/core/encoding/compatible_spec.rb | 379 ++++++++++++++++++ .../core/enumerator/each_with_index_spec.rb | 6 +- spec/ruby/core/enumerator/with_index_spec.rb | 17 + spec/ruby/core/exception/full_message_spec.rb | 18 + spec/ruby/core/fiber/raise_spec.rb | 2 +- spec/ruby/core/file/chown_spec.rb | 16 +- spec/ruby/core/file/shared/update_time.rb | 2 +- spec/ruby/core/float/ceil_spec.rb | 5 + spec/ruby/core/float/comparison_spec.rb | 2 +- spec/ruby/core/float/floor_spec.rb | 5 + spec/ruby/core/hash/element_reference_spec.rb | 2 +- spec/ruby/core/hash/hash_spec.rb | 2 +- spec/ruby/core/hash/new_spec.rb | 20 +- spec/ruby/core/hash/shared/to_s.rb | 46 +-- spec/ruby/core/integer/ceil_spec.rb | 5 + spec/ruby/core/integer/floor_spec.rb | 12 +- spec/ruby/core/integer/pow_spec.rb | 8 +- spec/ruby/core/integer/remainder_spec.rb | 4 +- spec/ruby/core/integer/round_spec.rb | 6 +- .../integer/shared/integer_ceil_precision.rb | 43 ++ .../integer/shared/integer_floor_precision.rb | 43 ++ spec/ruby/core/integer/size_spec.rb | 4 +- spec/ruby/core/io/dup_spec.rb | 30 +- spec/ruby/core/io/puts_spec.rb | 2 +- spec/ruby/core/io/read_spec.rb | 22 +- spec/ruby/core/kernel/eval_spec.rb | 20 + spec/ruby/core/kernel/extend_spec.rb | 12 + spec/ruby/core/kernel/format_spec.rb | 32 ++ spec/ruby/core/kernel/raise_spec.rb | 16 + spec/ruby/core/kernel/select_spec.rb | 4 +- spec/ruby/core/kernel/sleep_spec.rb | 14 + spec/ruby/core/marshal/dump_spec.rb | 2 +- .../core/marshal/fixtures/marshal_data.rb | 14 +- .../fixtures/marshal_multibyte_data.rb | 12 + spec/ruby/core/marshal/shared/load.rb | 2 +- spec/ruby/core/module/ancestors_spec.rb | 11 + spec/ruby/core/module/const_added_spec.rb | 43 ++ spec/ruby/core/module/fixtures/const_added.rb | 4 + spec/ruby/core/module/fixtures/name.rb | 3 + spec/ruby/core/module/include_spec.rb | 23 ++ spec/ruby/core/module/name_spec.rb | 41 ++ spec/ruby/core/module/new_spec.rb | 4 + spec/ruby/core/module/refine_spec.rb | 2 +- .../core/objectspace/add_finalizer_spec.rb | 5 - .../core/objectspace/call_finalizer_spec.rb | 5 - .../core/objectspace/define_finalizer_spec.rb | 25 ++ spec/ruby/core/objectspace/finalizers_spec.rb | 5 - .../core/objectspace/remove_finalizer_spec.rb | 5 - .../objectspace/undefine_finalizer_spec.rb | 30 +- spec/ruby/core/proc/curry_spec.rb | 9 +- spec/ruby/core/process/constants_spec.rb | 116 +++--- spec/ruby/core/process/daemon_spec.rb | 3 + spec/ruby/core/queue/freeze_spec.rb | 6 + spec/ruby/core/range/step_spec.rb | 333 +++++++++++---- spec/ruby/core/regexp/shared/new.rb | 4 + spec/ruby/core/regexp/shared/quote.rb | 4 +- spec/ruby/core/sizedqueue/freeze_spec.rb | 6 + spec/ruby/core/string/append_as_bytes_spec.rb | 46 +++ spec/ruby/core/string/byteslice_spec.rb | 2 +- spec/ruby/core/string/bytesplice_spec.rb | 164 ++++++++ spec/ruby/core/string/modulo_spec.rb | 6 +- spec/ruby/core/string/unpack/l_spec.rb | 16 +- .../core/thread/element_reference_spec.rb | 11 + spec/ruby/core/thread/element_set_spec.rb | 25 +- spec/ruby/core/thread/group_spec.rb | 15 +- spec/ruby/core/thread/key_spec.rb | 7 + .../core/thread/thread_variable_get_spec.rb | 19 +- .../core/thread/thread_variable_set_spec.rb | 4 +- spec/ruby/core/thread/thread_variable_spec.rb | 19 +- spec/ruby/core/thread/wakeup_spec.rb | 12 - spec/ruby/core/time/iso8601_spec.rb | 6 + spec/ruby/core/time/new_spec.rb | 12 +- spec/ruby/core/time/shared/xmlschema.rb | 31 ++ spec/ruby/core/time/xmlschema_spec.rb | 6 + spec/ruby/core/tracepoint/inspect_spec.rb | 2 +- spec/ruby/core/warning/warn_spec.rb | 2 +- spec/ruby/fixtures/constants.rb | 2 +- spec/ruby/language/fixtures/private.rb | 26 +- spec/ruby/language/fixtures/send.rb | 6 +- spec/ruby/language/pattern_matching_spec.rb | 3 +- spec/ruby/language/precedence_spec.rb | 16 +- spec/ruby/language/predefined_spec.rb | 10 + spec/ruby/language/regexp_spec.rb | 2 +- spec/ruby/language/super_spec.rb | 2 +- spec/ruby/library/bigdecimal/fix_spec.rb | 28 +- spec/ruby/library/bigdecimal/sqrt_spec.rb | 2 +- spec/ruby/library/date/mon_spec.rb | 3 +- spec/ruby/library/date/month_spec.rb | 6 +- spec/ruby/library/date/shared/month.rb | 6 + spec/ruby/library/erb/new_spec.rb | 2 +- spec/ruby/library/logger/logger/new_spec.rb | 26 +- spec/ruby/library/net-http/http/post_spec.rb | 2 +- .../net-http/http/send_request_spec.rb | 2 +- .../library/objectspace/memsize_of_spec.rb | 2 +- spec/ruby/library/pathname/glob_spec.rb | 8 + spec/ruby/library/pp/pp_spec.rb | 2 +- spec/ruby/library/rbconfig/rbconfig_spec.rb | 7 + spec/ruby/library/set/merge_spec.rb | 12 + .../socket/addrinfo/initialize_spec.rb | 2 +- .../library/socket/basicsocket/send_spec.rb | 38 +- .../library/socket/socket/connect_spec.rb | 14 + .../ruby/library/stringio/fixtures/classes.rb | 4 +- .../stringio/set_encoding_by_bom_spec.rb | 237 +++++++++++ spec/ruby/library/stringio/shared/sysread.rb | 2 +- spec/ruby/library/stringio/sysread_spec.rb | 5 + .../library/stringscanner/check_until_spec.rb | 10 +- spec/ruby/library/stringscanner/exist_spec.rb | 10 +- .../library/stringscanner/scan_until_spec.rb | 10 +- .../library/stringscanner/search_full_spec.rb | 10 +- .../library/stringscanner/skip_until_spec.rb | 10 +- spec/ruby/library/time/iso8601_spec.rb | 2 +- spec/ruby/library/time/shared/rfc2822.rb | 24 +- spec/ruby/library/time/shared/xmlschema.rb | 52 +-- spec/ruby/library/time/xmlschema_spec.rb | 2 +- spec/ruby/library/uri/shared/parse.rb | 13 +- spec/ruby/optional/capi/bignum_spec.rb | 26 +- spec/ruby/optional/capi/debug_spec.rb | 7 +- spec/ruby/optional/capi/ext/io_spec.c | 20 +- spec/ruby/optional/capi/ext/kernel_spec.c | 26 +- spec/ruby/optional/capi/ext/mutex_spec.c | 23 +- spec/ruby/optional/capi/ext/string_spec.c | 5 + spec/ruby/optional/capi/fixnum_spec.rb | 4 +- spec/ruby/optional/capi/io_spec.rb | 12 +- spec/ruby/optional/capi/kernel_spec.rb | 101 ++++- spec/ruby/optional/capi/mutex_spec.rb | 13 + spec/ruby/optional/capi/numeric_spec.rb | 8 +- spec/ruby/optional/capi/string_spec.rb | 32 +- spec/ruby/optional/capi/util_spec.rb | 2 +- spec/ruby/shared/queue/freeze.rb | 18 + spec/ruby/shared/string/times.rb | 4 +- 145 files changed, 2564 insertions(+), 562 deletions(-) create mode 100755 spec/ruby/bin/rubocop create mode 100644 spec/ruby/core/array/fetch_values_spec.rb create mode 100644 spec/ruby/core/data/to_h_spec.rb create mode 100644 spec/ruby/core/integer/shared/integer_ceil_precision.rb create mode 100644 spec/ruby/core/integer/shared/integer_floor_precision.rb create mode 100644 spec/ruby/core/marshal/fixtures/marshal_multibyte_data.rb create mode 100644 spec/ruby/core/module/fixtures/const_added.rb delete mode 100644 spec/ruby/core/objectspace/add_finalizer_spec.rb delete mode 100644 spec/ruby/core/objectspace/call_finalizer_spec.rb delete mode 100644 spec/ruby/core/objectspace/finalizers_spec.rb delete mode 100644 spec/ruby/core/objectspace/remove_finalizer_spec.rb create mode 100644 spec/ruby/core/queue/freeze_spec.rb create mode 100644 spec/ruby/core/sizedqueue/freeze_spec.rb create mode 100644 spec/ruby/core/string/append_as_bytes_spec.rb create mode 100644 spec/ruby/core/time/iso8601_spec.rb create mode 100644 spec/ruby/core/time/shared/xmlschema.rb create mode 100644 spec/ruby/core/time/xmlschema_spec.rb create mode 100644 spec/ruby/library/date/shared/month.rb create mode 100644 spec/ruby/library/stringio/set_encoding_by_bom_spec.rb create mode 100644 spec/ruby/shared/queue/freeze.rb diff --git a/spec/ruby/.mspec.constants b/spec/ruby/.mspec.constants index 6e09a44362d..4da36337152 100644 --- a/spec/ruby/.mspec.constants +++ b/spec/ruby/.mspec.constants @@ -146,6 +146,7 @@ Prime Private ProcFromMethod Psych +RactorLocalSingleton REXML RUBY_SIGNALS RbReadline diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml index 5a902e0f015..a385b5e79c6 100644 --- a/spec/ruby/.rubocop.yml +++ b/spec/ruby/.rubocop.yml @@ -5,9 +5,13 @@ AllCops: DisplayCopNames: true Exclude: - command_line/fixtures/bad_syntax.rb + - core/exception/fixtures/syntax_error.rb DisabledByDefault: true NewCops: disable +Layout/IndentationConsistency: + Enabled: true + Layout/TrailingWhitespace: Enabled: true @@ -43,9 +47,23 @@ Lint/InterpolationCheck: Lint/LiteralAsCondition: Enabled: false +# Required to support Ruby 3.0 +Lint/RedundantRequireStatement: + Exclude: + - core/fiber/**/*.rb + - library/fiber/**/*.rb + - optional/capi/fiber_spec.rb + +Lint/RedundantSafeNavigation: + Exclude: + - language/safe_navigator_spec.rb + Lint/RedundantSplatExpansion: Enabled: false +Lint/RescueException: + Enabled: false + Lint/UnifiedInteger: Enabled: false diff --git a/spec/ruby/.rubocop_todo.yml b/spec/ruby/.rubocop_todo.yml index 3ebb23a8bb7..6b43db9b9a4 100644 --- a/spec/ruby/.rubocop_todo.yml +++ b/spec/ruby/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2019-12-12 22:16:26 +0900 using RuboCop version 0.77.0. +# on 2024-10-12 16:01:45 UTC using RuboCop version 1.66.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -21,6 +21,7 @@ Lint/DuplicateMethods: - 'fixtures/class.rb' # Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). Lint/EnsureReturn: Exclude: - 'language/fixtures/ensure.rb' @@ -39,6 +40,7 @@ Lint/FloatOutOfRange: - 'core/string/modulo_spec.rb' # Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). Lint/ImplicitStringConcatenation: Exclude: - 'language/string_spec.rb' @@ -50,8 +52,8 @@ Lint/IneffectiveAccessModifier: - 'core/module/fixtures/classes.rb' - 'language/fixtures/private.rb' -# Offense count: 72 -# Cop supports --auto-correct. +# Offense count: 71 +# This cop supports safe autocorrection (--autocorrect). Lint/LiteralInInterpolation: Exclude: - 'core/module/refine_spec.rb' @@ -65,47 +67,28 @@ Lint/LiteralInInterpolation: - 'language/undef_spec.rb' # Offense count: 8 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Lint/MultipleComparison: Exclude: - 'language/precedence_spec.rb' # Offense count: 9 +# This cop supports safe autocorrection (--autocorrect). Lint/ParenthesesAsGroupedExpression: Exclude: - - 'core/string/fixtures/freeze_magic_comment.rb' - 'language/block_spec.rb' - - 'language/fixtures/send.rb' - 'language/method_spec.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Lint/RedundantStringCoercion: Exclude: - 'core/io/print_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. -Lint/RedundantWithIndex: - Exclude: - - 'core/enumerator/with_index_spec.rb' - -# Offense count: 22 -Lint/RescueException: +Lint/SelfAssignment: Exclude: - - 'command_line/fixtures/debug_info.rb' - - 'core/dir/fileno_spec.rb' - - 'core/exception/cause_spec.rb' - - 'core/exception/no_method_error_spec.rb' - - 'core/fiber/kill_spec.rb' - - 'core/kernel/fixtures/autoload_frozen.rb' - - 'core/kernel/raise_spec.rb' - - 'core/module/autoload_spec.rb' - - 'core/mutex/sleep_spec.rb' - - 'core/thread/abort_on_exception_spec.rb' - - 'core/thread/shared/exit.rb' - - 'language/rescue_spec.rb' - - 'library/erb/filename_spec.rb' + - 'core/gc/auto_compact_spec.rb' # Offense count: 4 # Configuration parameters: IgnoreImplicitReferences. @@ -113,8 +96,8 @@ Lint/ShadowedArgument: Exclude: - 'language/fixtures/super.rb' -# Offense count: 39 -# Configuration parameters: AllowComments. +# Offense count: 45 +# Configuration parameters: AllowComments, AllowNil. Lint/SuppressedException: Enabled: false @@ -127,7 +110,8 @@ Lint/UnderscorePrefixedVariableName: - 'language/block_spec.rb' # Offense count: 7 -# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods. Lint/UselessAccessModifier: Exclude: - 'core/module/define_method_spec.rb' diff --git a/spec/ruby/CONTRIBUTING.md b/spec/ruby/CONTRIBUTING.md index 73fba01de2e..5c3256948b8 100644 --- a/spec/ruby/CONTRIBUTING.md +++ b/spec/ruby/CONTRIBUTING.md @@ -164,7 +164,7 @@ end platform_is_not :linux, :darwin do # Not Linux and not Darwin end -platform_is wordsize: 64 do +platform_is pointer_size: 64 do # 64-bit platform end @@ -179,7 +179,9 @@ In case there is a bug in MRI and the fix will be backported to previous version If it is not backported or not likely, use `ruby_version_is` instead. First, file a bug at https://bugs.ruby-lang.org/. The problem is `ruby_bug` would make non-MRI implementations fail this spec while MRI itself does not pass it, so it should only be used if the bug is/will be fixed and backported. -Otherwise, non-MRI implementations would have to choose between being incompatible with the latest release of MRI to pass the spec or fail the spec, both which make no sense. +Otherwise, non-MRI implementations would have to choose between being incompatible with the latest release of MRI (which has the bug) to pass the spec, or behave the same as the latest release of MRI (which has the bug) and fail the spec, both which make no sense. + +IOW, `ruby_bug '#NN', ''...'X.Y' do` is equivalent to `guard_not { RUBY_ENGINE == "ruby" && ruby_version_is ''...'X.Y' } do`. So it skips tests on MRI on specified versions (where a bug is present) and runs tests on alternative implementations only. ```ruby ruby_bug '#13669', ''...'3.2' do diff --git a/spec/ruby/bin/rubocop b/spec/ruby/bin/rubocop new file mode 100755 index 00000000000..38254f13e48 --- /dev/null +++ b/spec/ruby/bin/rubocop @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'bundler/inline' + +gemfile do + source 'https://rubygems.org' + + gem 'rubocop', '1.66.1' +end + +exec(Gem.bin_path('rubocop', 'rubocop'), *ARGV) diff --git a/spec/ruby/command_line/fixtures/debug_info.rb b/spec/ruby/command_line/fixtures/debug_info.rb index ee607910c0d..f02b0419208 100644 --- a/spec/ruby/command_line/fixtures/debug_info.rb +++ b/spec/ruby/command_line/fixtures/debug_info.rb @@ -1,4 +1,3 @@ -# frozen_string_literal: true a = 'string' b = a c = b diff --git a/spec/ruby/command_line/frozen_strings_spec.rb b/spec/ruby/command_line/frozen_strings_spec.rb index 06889755d25..014153e0b4a 100644 --- a/spec/ruby/command_line/frozen_strings_spec.rb +++ b/spec/ruby/command_line/frozen_strings_spec.rb @@ -57,9 +57,18 @@ describe "The --debug flag produces" do it "debugging info on attempted frozen string modification" do - error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--debug', args: "2>&1") + error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--enable-frozen-string-literal --debug', args: "2>&1") error_str.should include("can't modify frozen String") error_str.should include("created at") - error_str.should include("command_line/fixtures/debug_info.rb:2") + error_str.should include("command_line/fixtures/debug_info.rb:1") + end + + guard -> { ruby_version_is "3.4" and !"test".frozen? } do + it "debugging info on mutating chilled string" do + error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '-w --debug', args: "2>&1") + error_str.should include("literal string will be frozen in the future") + error_str.should include("the string was created here") + error_str.should include("command_line/fixtures/debug_info.rb:1") + end end end diff --git a/spec/ruby/command_line/rubyopt_spec.rb b/spec/ruby/command_line/rubyopt_spec.rb index 18a5959b18c..38e29c0d214 100644 --- a/spec/ruby/command_line/rubyopt_spec.rb +++ b/spec/ruby/command_line/rubyopt_spec.rb @@ -22,15 +22,15 @@ result.should =~ /value of \$DEBUG is true/ end - guard -> { not CROSS_COMPILING } do + guard -> { RbConfig::CONFIG["CROSS_COMPILING"] != "yes" } do it "prints the version number for '-v'" do ENV["RUBYOPT"] = '-v' - ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION.sub("+PRISM ", "") + ruby_exe("").sub("+PRISM ", "")[/\A.*/].should == RUBY_DESCRIPTION.sub("+PRISM ", "") end it "ignores whitespace around the option" do ENV["RUBYOPT"] = ' -v ' - ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION.sub("+PRISM ", "") + ruby_exe("").sub("+PRISM ", "")[/\A.*/].should == RUBY_DESCRIPTION.sub("+PRISM ", "") end end diff --git a/spec/ruby/core/array/fetch_values_spec.rb b/spec/ruby/core/array/fetch_values_spec.rb new file mode 100644 index 00000000000..559b6c2b2fc --- /dev/null +++ b/spec/ruby/core/array/fetch_values_spec.rb @@ -0,0 +1,48 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Array#fetch_values" do + before :each do + @array = [:a, :b, :c] + end + + ruby_version_is "3.4" do + describe "with matched indexes" do + it "returns the values for indexes" do + @array.fetch_values(0).should == [:a] + @array.fetch_values(0, 2).should == [:a, :c] + end + + it "returns the values for indexes ordered in the order of the requested indexes" do + @array.fetch_values(2, 0).should == [:c, :a] + end + end + + describe "with unmatched indexes" do + it "raises a index error if no block is provided" do + -> { @array.fetch_values(0, 1, 44) }.should raise_error(IndexError) + end + + it "returns the default value from block" do + @array.fetch_values(44) { |index| "`#{index}' is not found" }.should == ["`44' is not found"] + @array.fetch_values(0, 44) { |index| "`#{index}' is not found" }.should == [:a, "`44' is not found"] + end + end + + describe "without keys" do + it "returns an empty Array" do + @array.fetch_values.should == [] + end + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + @array.fetch_values(obj).should == [:c] + end + + it "raises a TypeError when the passed argument can't be coerced to Integer" do + -> { [].fetch_values("cat") }.should raise_error(TypeError) + end + end +end diff --git a/spec/ruby/core/array/pack/l_spec.rb b/spec/ruby/core/array/pack/l_spec.rb index b446a7a36a8..f6dfb1da83b 100644 --- a/spec/ruby/core/array/pack/l_spec.rb +++ b/spec/ruby/core/array/pack/l_spec.rb @@ -29,7 +29,7 @@ it_behaves_like :array_pack_32bit_be, 'L>' end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do describe "with modifier '<' and '_'" do it_behaves_like :array_pack_32bit_le, 'L<_' it_behaves_like :array_pack_32bit_le, 'L_<' @@ -51,7 +51,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "with modifier '<' and '_'" do it_behaves_like :array_pack_64bit_le, 'L<_' it_behaves_like :array_pack_64bit_le, 'L_<' @@ -83,7 +83,7 @@ it_behaves_like :array_pack_32bit_be, 'l>' end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do describe "with modifier '<' and '_'" do it_behaves_like :array_pack_32bit_le, 'l<_' it_behaves_like :array_pack_32bit_le, 'l_<' @@ -105,7 +105,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "with modifier '<' and '_'" do it_behaves_like :array_pack_64bit_le, 'l<_' it_behaves_like :array_pack_64bit_le, 'l_<' @@ -137,7 +137,7 @@ it_behaves_like :array_pack_32bit_le, 'l' end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do describe "Array#pack with format 'L' with modifier '_'" do it_behaves_like :array_pack_32bit_le, 'L_' end @@ -155,7 +155,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "Array#pack with format 'L' with modifier '_'" do it_behaves_like :array_pack_64bit_le, 'L_' end @@ -183,7 +183,7 @@ it_behaves_like :array_pack_32bit_be, 'l' end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do describe "Array#pack with format 'L' with modifier '_'" do it_behaves_like :array_pack_32bit_be, 'L_' end @@ -201,7 +201,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "Array#pack with format 'L' with modifier '_'" do it_behaves_like :array_pack_64bit_be, 'L_' end diff --git a/spec/ruby/core/array/pack/shared/integer.rb b/spec/ruby/core/array/pack/shared/integer.rb index fd21b25b19f..a89b5b733bf 100644 --- a/spec/ruby/core/array/pack/shared/integer.rb +++ b/spec/ruby/core/array/pack/shared/integer.rb @@ -273,7 +273,7 @@ str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78" end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "encodes the least significant 32 bits of a number that is greater than 32 bits" do [ [[0xff_7865_4321], "\x21\x43\x65\x78"], [[-0xff_7865_4321], "\xdf\xbc\x9a\x87"] @@ -299,7 +299,7 @@ str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21" end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "encodes the least significant 32 bits of a number that is greater than 32 bits" do [ [[0xff_7865_4321], "\x78\x65\x43\x21"], [[-0xff_7865_4321], "\x87\x9a\xbc\xdf"] diff --git a/spec/ruby/core/basicobject/instance_exec_spec.rb b/spec/ruby/core/basicobject/instance_exec_spec.rb index 289fdd889b5..370f03d33c6 100644 --- a/spec/ruby/core/basicobject/instance_exec_spec.rb +++ b/spec/ruby/core/basicobject/instance_exec_spec.rb @@ -84,17 +84,17 @@ def foo end.should raise_error(TypeError) end -quarantine! do # Not clean, leaves cvars lying around to break other specs - it "scopes class var accesses in the caller when called on an Integer" do - # Integer can take instance vars - Integer.class_eval "@@__tmp_instance_exec_spec = 1" - (defined? @@__tmp_instance_exec_spec).should == nil - - @@__tmp_instance_exec_spec = 2 - 1.instance_exec { @@__tmp_instance_exec_spec }.should == 2 - Integer.__send__(:remove_class_variable, :@@__tmp_instance_exec_spec) + quarantine! do # Not clean, leaves cvars lying around to break other specs + it "scopes class var accesses in the caller when called on an Integer" do + # Integer can take instance vars + Integer.class_eval "@@__tmp_instance_exec_spec = 1" + (defined? @@__tmp_instance_exec_spec).should == nil + + @@__tmp_instance_exec_spec = 2 + 1.instance_exec { @@__tmp_instance_exec_spec }.should == 2 + Integer.__send__(:remove_class_variable, :@@__tmp_instance_exec_spec) + end end -end it "raises a TypeError when defining methods on numerics" do -> do diff --git a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb index 1960f5721f9..eaf311783a4 100644 --- a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb +++ b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb @@ -4,6 +4,10 @@ it "is a String" do RUBY_VERSION.should be_kind_of(String) end + + it "is frozen" do + RUBY_VERSION.should.frozen? + end end describe "RUBY_PATCHLEVEL" do @@ -16,34 +20,58 @@ it "is a String" do RUBY_COPYRIGHT.should be_kind_of(String) end + + it "is frozen" do + RUBY_COPYRIGHT.should.frozen? + end end describe "RUBY_DESCRIPTION" do it "is a String" do RUBY_DESCRIPTION.should be_kind_of(String) end + + it "is frozen" do + RUBY_DESCRIPTION.should.frozen? + end end describe "RUBY_ENGINE" do it "is a String" do RUBY_ENGINE.should be_kind_of(String) end + + it "is frozen" do + RUBY_ENGINE.should.frozen? + end end describe "RUBY_PLATFORM" do it "is a String" do RUBY_PLATFORM.should be_kind_of(String) end + + it "is frozen" do + RUBY_PLATFORM.should.frozen? + end end describe "RUBY_RELEASE_DATE" do it "is a String" do RUBY_RELEASE_DATE.should be_kind_of(String) end + + it "is frozen" do + RUBY_RELEASE_DATE.should.frozen? + end end describe "RUBY_REVISION" do it "is a String" do RUBY_REVISION.should be_kind_of(String) end + + it "is frozen" do + RUBY_REVISION.should.frozen? + end end diff --git a/spec/ruby/core/complex/equal_value_spec.rb b/spec/ruby/core/complex/equal_value_spec.rb index ad7236b1bd5..97c486d8204 100644 --- a/spec/ruby/core/complex/equal_value_spec.rb +++ b/spec/ruby/core/complex/equal_value_spec.rb @@ -76,7 +76,7 @@ (Complex(real, 0) == @other).should be_true end - it "returns false when when the imaginary part is not zero" do + it "returns false when the imaginary part is not zero" do (Complex(3, 1) == @other).should be_false end end diff --git a/spec/ruby/core/data/to_h_spec.rb b/spec/ruby/core/data/to_h_spec.rb new file mode 100644 index 00000000000..41d6960c97a --- /dev/null +++ b/spec/ruby/core/data/to_h_spec.rb @@ -0,0 +1,65 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +ruby_version_is "3.2" do + describe "Data#to_h" do + it "transforms the data object into a hash" do + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + data.to_h.should == { amount: 42, unit: 'km' } + end + + context "with block" do + it "transforms [key, value] pairs returned by the block into a hash" do + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + data.to_h { |key, value| [value, key] }.should == { 42 => :amount, 'km' => :unit } + end + + it "passes to a block each pair's key and value as separate arguments" do + ScratchPad.record [] + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + data.to_h { |k, v| ScratchPad << [k, v]; [k, v] } + ScratchPad.recorded.sort.should == [[:amount, 42], [:unit, 'km']] + + ScratchPad.record [] + data.to_h { |*args| ScratchPad << args; [args[0], args[1]] } + ScratchPad.recorded.sort.should == [[:amount, 42], [:unit, 'km']] + end + + it "raises ArgumentError if block returns longer or shorter array" do + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + -> do + data.to_h { |k, v| [k.to_s, v*v, 1] } + end.should raise_error(ArgumentError, /element has wrong array length/) + + -> do + data.to_h { |k, v| [k] } + end.should raise_error(ArgumentError, /element has wrong array length/) + end + + it "raises TypeError if block returns something other than Array" do + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + -> do + data.to_h { |k, v| "not-array" } + end.should raise_error(TypeError, /wrong element type String/) + end + + it "coerces returned pair to Array with #to_ary" do + x = mock('x') + x.stub!(:to_ary).and_return([:b, 'b']) + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + + data.to_h { |k| x }.should == { :b => 'b' } + end + + it "does not coerce returned pair to Array with #to_a" do + x = mock('x') + x.stub!(:to_a).and_return([:b, 'b']) + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + + -> do + data.to_h { |k| x } + end.should raise_error(TypeError, /wrong element type MockObject/) + end + end + end +end diff --git a/spec/ruby/core/encoding/compatible_spec.rb b/spec/ruby/core/encoding/compatible_spec.rb index f18d8680a96..d5b958ea0b1 100644 --- a/spec/ruby/core/encoding/compatible_spec.rb +++ b/spec/ruby/core/encoding/compatible_spec.rb @@ -161,6 +161,379 @@ Encoding.compatible?(@str, "").should == Encoding::UTF_7 end end + + # Encoding negotiation depends on whether encodings are ASCII-compatible, empty + # and contain only ASCII characters (that take 7 bits). Check US-ASCII, UTF-8 and + # BINARY encodings (as most common) as well as an ASCII-compatible, a non-ASCII-compatible and a dummy + # encodings in all possible combinations. + describe "compatibility matrix" do + +# Use the following script to regenerate the matrix: +# +# ``` +# # -*- encoding: binary -*- +# +# ENCODINGS = [ +# "US-ASCII", +# "UTF-8", +# "ASCII-8BIT", +# "ISO-8859-1", # ASCII-compatible +# "UTF-16BE", # non-ASCII-compatible +# "ISO-2022-JP" # dummy +# ] +# +# TYPES = [:empty, :"7bits", :non7bits] +# +# VALUES = { +# empty: "", +# :"7bits" => "\x01", +# non7bits: "\x81" +# } +# +# ENCODINGS.product(TYPES, ENCODINGS, TYPES).each do |encoding1, type1, encoding2, type2| +# value1 = VALUES[type1].dup.force_encoding(encoding1) +# value2 = VALUES[type2].dup.force_encoding(encoding2) +# +# result_encoding = Encoding.compatible?(value1, value2) +# +# puts "[#{encoding1.inspect}, #{value1.inspect}, #{encoding2.inspect}, #{value2.inspect}, #{result_encoding&.name.inspect}]," +# end +# ``` + + matrix = [ + ["US-ASCII", "", "US-ASCII", "", "US-ASCII"], + ["US-ASCII", "", "US-ASCII", "\x01", "US-ASCII"], + ["US-ASCII", "", "US-ASCII", "\x81", "US-ASCII"], + ["US-ASCII", "", "UTF-8", "", "US-ASCII"], + ["US-ASCII", "", "UTF-8", "\u0001", "US-ASCII"], + ["US-ASCII", "", "UTF-8", "\x81", "UTF-8"], + ["US-ASCII", "", "ASCII-8BIT", "", "US-ASCII"], + ["US-ASCII", "", "ASCII-8BIT", "\x01", "US-ASCII"], + ["US-ASCII", "", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["US-ASCII", "", "ISO-8859-1", "", "US-ASCII"], + ["US-ASCII", "", "ISO-8859-1", "\x01", "US-ASCII"], + ["US-ASCII", "", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["US-ASCII", "", "UTF-16BE", "", "US-ASCII"], + ["US-ASCII", "", "UTF-16BE", "\x01", "UTF-16BE"], + ["US-ASCII", "", "UTF-16BE", "\x81", "UTF-16BE"], + ["US-ASCII", "", "ISO-2022-JP", "", "US-ASCII"], + ["US-ASCII", "", "ISO-2022-JP", "\x01", "ISO-2022-JP"], + ["US-ASCII", "", "ISO-2022-JP", "\x81", "ISO-2022-JP"], + ["US-ASCII", "\x01", "US-ASCII", "", "US-ASCII"], + ["US-ASCII", "\x01", "US-ASCII", "\x01", "US-ASCII"], + ["US-ASCII", "\x01", "US-ASCII", "\x81", "US-ASCII"], + ["US-ASCII", "\x01", "UTF-8", "", "US-ASCII"], + ["US-ASCII", "\x01", "UTF-8", "\u0001", "US-ASCII"], + ["US-ASCII", "\x01", "UTF-8", "\x81", "UTF-8"], + ["US-ASCII", "\x01", "ASCII-8BIT", "", "US-ASCII"], + ["US-ASCII", "\x01", "ASCII-8BIT", "\x01", "US-ASCII"], + ["US-ASCII", "\x01", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["US-ASCII", "\x01", "ISO-8859-1", "", "US-ASCII"], + ["US-ASCII", "\x01", "ISO-8859-1", "\x01", "US-ASCII"], + ["US-ASCII", "\x01", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["US-ASCII", "\x01", "UTF-16BE", "", "US-ASCII"], + ["US-ASCII", "\x01", "UTF-16BE", "\x01", nil], + ["US-ASCII", "\x01", "UTF-16BE", "\x81", nil], + ["US-ASCII", "\x01", "ISO-2022-JP", "", "US-ASCII"], + ["US-ASCII", "\x01", "ISO-2022-JP", "\x01", nil], + ["US-ASCII", "\x01", "ISO-2022-JP", "\x81", nil], + ["US-ASCII", "\x81", "US-ASCII", "", "US-ASCII"], + ["US-ASCII", "\x81", "US-ASCII", "\x01", "US-ASCII"], + ["US-ASCII", "\x81", "US-ASCII", "\x81", "US-ASCII"], + ["US-ASCII", "\x81", "UTF-8", "", "US-ASCII"], + ["US-ASCII", "\x81", "UTF-8", "\u0001", "US-ASCII"], + ["US-ASCII", "\x81", "UTF-8", "\x81", nil], + ["US-ASCII", "\x81", "ASCII-8BIT", "", "US-ASCII"], + ["US-ASCII", "\x81", "ASCII-8BIT", "\x01", "US-ASCII"], + ["US-ASCII", "\x81", "ASCII-8BIT", "\x81", nil], + ["US-ASCII", "\x81", "ISO-8859-1", "", "US-ASCII"], + ["US-ASCII", "\x81", "ISO-8859-1", "\x01", "US-ASCII"], + ["US-ASCII", "\x81", "ISO-8859-1", "\x81", nil], + ["US-ASCII", "\x81", "UTF-16BE", "", "US-ASCII"], + ["US-ASCII", "\x81", "UTF-16BE", "\x01", nil], + ["US-ASCII", "\x81", "UTF-16BE", "\x81", nil], + ["US-ASCII", "\x81", "ISO-2022-JP", "", "US-ASCII"], + ["US-ASCII", "\x81", "ISO-2022-JP", "\x01", nil], + ["US-ASCII", "\x81", "ISO-2022-JP", "\x81", nil], + ["UTF-8", "", "US-ASCII", "", "UTF-8"], + ["UTF-8", "", "US-ASCII", "\x01", "UTF-8"], + ["UTF-8", "", "US-ASCII", "\x81", "US-ASCII"], + ["UTF-8", "", "UTF-8", "", "UTF-8"], + ["UTF-8", "", "UTF-8", "\u0001", "UTF-8"], + ["UTF-8", "", "UTF-8", "\x81", "UTF-8"], + ["UTF-8", "", "ASCII-8BIT", "", "UTF-8"], + ["UTF-8", "", "ASCII-8BIT", "\x01", "UTF-8"], + ["UTF-8", "", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["UTF-8", "", "ISO-8859-1", "", "UTF-8"], + ["UTF-8", "", "ISO-8859-1", "\x01", "UTF-8"], + ["UTF-8", "", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["UTF-8", "", "UTF-16BE", "", "UTF-8"], + ["UTF-8", "", "UTF-16BE", "\x01", "UTF-16BE"], + ["UTF-8", "", "UTF-16BE", "\x81", "UTF-16BE"], + ["UTF-8", "", "ISO-2022-JP", "", "UTF-8"], + ["UTF-8", "", "ISO-2022-JP", "\x01", "ISO-2022-JP"], + ["UTF-8", "", "ISO-2022-JP", "\x81", "ISO-2022-JP"], + ["UTF-8", "\u0001", "US-ASCII", "", "UTF-8"], + ["UTF-8", "\u0001", "US-ASCII", "\x01", "UTF-8"], + ["UTF-8", "\u0001", "US-ASCII", "\x81", "US-ASCII"], + ["UTF-8", "\u0001", "UTF-8", "", "UTF-8"], + ["UTF-8", "\u0001", "UTF-8", "\u0001", "UTF-8"], + ["UTF-8", "\u0001", "UTF-8", "\x81", "UTF-8"], + ["UTF-8", "\u0001", "ASCII-8BIT", "", "UTF-8"], + ["UTF-8", "\u0001", "ASCII-8BIT", "\x01", "UTF-8"], + ["UTF-8", "\u0001", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["UTF-8", "\u0001", "ISO-8859-1", "", "UTF-8"], + ["UTF-8", "\u0001", "ISO-8859-1", "\x01", "UTF-8"], + ["UTF-8", "\u0001", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["UTF-8", "\u0001", "UTF-16BE", "", "UTF-8"], + ["UTF-8", "\u0001", "UTF-16BE", "\x01", nil], + ["UTF-8", "\u0001", "UTF-16BE", "\x81", nil], + ["UTF-8", "\u0001", "ISO-2022-JP", "", "UTF-8"], + ["UTF-8", "\u0001", "ISO-2022-JP", "\x01", nil], + ["UTF-8", "\u0001", "ISO-2022-JP", "\x81", nil], + ["UTF-8", "\x81", "US-ASCII", "", "UTF-8"], + ["UTF-8", "\x81", "US-ASCII", "\x01", "UTF-8"], + ["UTF-8", "\x81", "US-ASCII", "\x81", nil], + ["UTF-8", "\x81", "UTF-8", "", "UTF-8"], + ["UTF-8", "\x81", "UTF-8", "\u0001", "UTF-8"], + ["UTF-8", "\x81", "UTF-8", "\x81", "UTF-8"], + ["UTF-8", "\x81", "ASCII-8BIT", "", "UTF-8"], + ["UTF-8", "\x81", "ASCII-8BIT", "\x01", "UTF-8"], + ["UTF-8", "\x81", "ASCII-8BIT", "\x81", nil], + ["UTF-8", "\x81", "ISO-8859-1", "", "UTF-8"], + ["UTF-8", "\x81", "ISO-8859-1", "\x01", "UTF-8"], + ["UTF-8", "\x81", "ISO-8859-1", "\x81", nil], + ["UTF-8", "\x81", "UTF-16BE", "", "UTF-8"], + ["UTF-8", "\x81", "UTF-16BE", "\x01", nil], + ["UTF-8", "\x81", "UTF-16BE", "\x81", nil], + ["UTF-8", "\x81", "ISO-2022-JP", "", "UTF-8"], + ["UTF-8", "\x81", "ISO-2022-JP", "\x01", nil], + ["UTF-8", "\x81", "ISO-2022-JP", "\x81", nil], + ["ASCII-8BIT", "", "US-ASCII", "", "ASCII-8BIT"], + ["ASCII-8BIT", "", "US-ASCII", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "", "US-ASCII", "\x81", "US-ASCII"], + ["ASCII-8BIT", "", "UTF-8", "", "ASCII-8BIT"], + ["ASCII-8BIT", "", "UTF-8", "\u0001", "ASCII-8BIT"], + ["ASCII-8BIT", "", "UTF-8", "\x81", "UTF-8"], + ["ASCII-8BIT", "", "ASCII-8BIT", "", "ASCII-8BIT"], + ["ASCII-8BIT", "", "ASCII-8BIT", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["ASCII-8BIT", "", "ISO-8859-1", "", "ASCII-8BIT"], + ["ASCII-8BIT", "", "ISO-8859-1", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["ASCII-8BIT", "", "UTF-16BE", "", "ASCII-8BIT"], + ["ASCII-8BIT", "", "UTF-16BE", "\x01", "UTF-16BE"], + ["ASCII-8BIT", "", "UTF-16BE", "\x81", "UTF-16BE"], + ["ASCII-8BIT", "", "ISO-2022-JP", "", "ASCII-8BIT"], + ["ASCII-8BIT", "", "ISO-2022-JP", "\x01", "ISO-2022-JP"], + ["ASCII-8BIT", "", "ISO-2022-JP", "\x81", "ISO-2022-JP"], + ["ASCII-8BIT", "\x01", "US-ASCII", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "US-ASCII", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "US-ASCII", "\x81", "US-ASCII"], + ["ASCII-8BIT", "\x01", "UTF-8", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "UTF-8", "\u0001", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "UTF-8", "\x81", "UTF-8"], + ["ASCII-8BIT", "\x01", "ASCII-8BIT", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "ASCII-8BIT", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "ISO-8859-1", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "ISO-8859-1", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["ASCII-8BIT", "\x01", "UTF-16BE", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "UTF-16BE", "\x01", nil], + ["ASCII-8BIT", "\x01", "UTF-16BE", "\x81", nil], + ["ASCII-8BIT", "\x01", "ISO-2022-JP", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x01", "ISO-2022-JP", "\x01", nil], + ["ASCII-8BIT", "\x01", "ISO-2022-JP", "\x81", nil], + ["ASCII-8BIT", "\x81", "US-ASCII", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "US-ASCII", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "US-ASCII", "\x81", nil], + ["ASCII-8BIT", "\x81", "UTF-8", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "UTF-8", "\u0001", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "UTF-8", "\x81", nil], + ["ASCII-8BIT", "\x81", "ASCII-8BIT", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "ASCII-8BIT", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "ISO-8859-1", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "ISO-8859-1", "\x01", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "ISO-8859-1", "\x81", nil], + ["ASCII-8BIT", "\x81", "UTF-16BE", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "UTF-16BE", "\x01", nil], + ["ASCII-8BIT", "\x81", "UTF-16BE", "\x81", nil], + ["ASCII-8BIT", "\x81", "ISO-2022-JP", "", "ASCII-8BIT"], + ["ASCII-8BIT", "\x81", "ISO-2022-JP", "\x01", nil], + ["ASCII-8BIT", "\x81", "ISO-2022-JP", "\x81", nil], + ["ISO-8859-1", "", "US-ASCII", "", "ISO-8859-1"], + ["ISO-8859-1", "", "US-ASCII", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "", "US-ASCII", "\x81", "US-ASCII"], + ["ISO-8859-1", "", "UTF-8", "", "ISO-8859-1"], + ["ISO-8859-1", "", "UTF-8", "\u0001", "ISO-8859-1"], + ["ISO-8859-1", "", "UTF-8", "\x81", "UTF-8"], + ["ISO-8859-1", "", "ASCII-8BIT", "", "ISO-8859-1"], + ["ISO-8859-1", "", "ASCII-8BIT", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["ISO-8859-1", "", "ISO-8859-1", "", "ISO-8859-1"], + ["ISO-8859-1", "", "ISO-8859-1", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["ISO-8859-1", "", "UTF-16BE", "", "ISO-8859-1"], + ["ISO-8859-1", "", "UTF-16BE", "\x01", "UTF-16BE"], + ["ISO-8859-1", "", "UTF-16BE", "\x81", "UTF-16BE"], + ["ISO-8859-1", "", "ISO-2022-JP", "", "ISO-8859-1"], + ["ISO-8859-1", "", "ISO-2022-JP", "\x01", "ISO-2022-JP"], + ["ISO-8859-1", "", "ISO-2022-JP", "\x81", "ISO-2022-JP"], + ["ISO-8859-1", "\x01", "US-ASCII", "", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "US-ASCII", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "US-ASCII", "\x81", "US-ASCII"], + ["ISO-8859-1", "\x01", "UTF-8", "", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "UTF-8", "\u0001", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "UTF-8", "\x81", "UTF-8"], + ["ISO-8859-1", "\x01", "ASCII-8BIT", "", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "ASCII-8BIT", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["ISO-8859-1", "\x01", "ISO-8859-1", "", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "ISO-8859-1", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "UTF-16BE", "", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "UTF-16BE", "\x01", nil], + ["ISO-8859-1", "\x01", "UTF-16BE", "\x81", nil], + ["ISO-8859-1", "\x01", "ISO-2022-JP", "", "ISO-8859-1"], + ["ISO-8859-1", "\x01", "ISO-2022-JP", "\x01", nil], + ["ISO-8859-1", "\x01", "ISO-2022-JP", "\x81", nil], + ["ISO-8859-1", "\x81", "US-ASCII", "", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "US-ASCII", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "US-ASCII", "\x81", nil], + ["ISO-8859-1", "\x81", "UTF-8", "", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "UTF-8", "\u0001", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "UTF-8", "\x81", nil], + ["ISO-8859-1", "\x81", "ASCII-8BIT", "", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "ASCII-8BIT", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "ASCII-8BIT", "\x81", nil], + ["ISO-8859-1", "\x81", "ISO-8859-1", "", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "ISO-8859-1", "\x01", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "UTF-16BE", "", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "UTF-16BE", "\x01", nil], + ["ISO-8859-1", "\x81", "UTF-16BE", "\x81", nil], + ["ISO-8859-1", "\x81", "ISO-2022-JP", "", "ISO-8859-1"], + ["ISO-8859-1", "\x81", "ISO-2022-JP", "\x01", nil], + ["ISO-8859-1", "\x81", "ISO-2022-JP", "\x81", nil], + ["UTF-16BE", "", "US-ASCII", "", "UTF-16BE"], + ["UTF-16BE", "", "US-ASCII", "\x01", "US-ASCII"], + ["UTF-16BE", "", "US-ASCII", "\x81", "US-ASCII"], + ["UTF-16BE", "", "UTF-8", "", "UTF-16BE"], + ["UTF-16BE", "", "UTF-8", "\u0001", "UTF-8"], + ["UTF-16BE", "", "UTF-8", "\x81", "UTF-8"], + ["UTF-16BE", "", "ASCII-8BIT", "", "UTF-16BE"], + ["UTF-16BE", "", "ASCII-8BIT", "\x01", "ASCII-8BIT"], + ["UTF-16BE", "", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["UTF-16BE", "", "ISO-8859-1", "", "UTF-16BE"], + ["UTF-16BE", "", "ISO-8859-1", "\x01", "ISO-8859-1"], + ["UTF-16BE", "", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["UTF-16BE", "", "UTF-16BE", "", "UTF-16BE"], + ["UTF-16BE", "", "UTF-16BE", "\x01", "UTF-16BE"], + ["UTF-16BE", "", "UTF-16BE", "\x81", "UTF-16BE"], + ["UTF-16BE", "", "ISO-2022-JP", "", "UTF-16BE"], + ["UTF-16BE", "", "ISO-2022-JP", "\x01", "ISO-2022-JP"], + ["UTF-16BE", "", "ISO-2022-JP", "\x81", "ISO-2022-JP"], + ["UTF-16BE", "\x01", "US-ASCII", "", "UTF-16BE"], + ["UTF-16BE", "\x01", "US-ASCII", "\x01", nil], + ["UTF-16BE", "\x01", "US-ASCII", "\x81", nil], + ["UTF-16BE", "\x01", "UTF-8", "", "UTF-16BE"], + ["UTF-16BE", "\x01", "UTF-8", "\u0001", nil], + ["UTF-16BE", "\x01", "UTF-8", "\x81", nil], + ["UTF-16BE", "\x01", "ASCII-8BIT", "", "UTF-16BE"], + ["UTF-16BE", "\x01", "ASCII-8BIT", "\x01", nil], + ["UTF-16BE", "\x01", "ASCII-8BIT", "\x81", nil], + ["UTF-16BE", "\x01", "ISO-8859-1", "", "UTF-16BE"], + ["UTF-16BE", "\x01", "ISO-8859-1", "\x01", nil], + ["UTF-16BE", "\x01", "ISO-8859-1", "\x81", nil], + ["UTF-16BE", "\x01", "UTF-16BE", "", "UTF-16BE"], + ["UTF-16BE", "\x01", "UTF-16BE", "\x01", "UTF-16BE"], + ["UTF-16BE", "\x01", "UTF-16BE", "\x81", "UTF-16BE"], + ["UTF-16BE", "\x01", "ISO-2022-JP", "", "UTF-16BE"], + ["UTF-16BE", "\x01", "ISO-2022-JP", "\x01", nil], + ["UTF-16BE", "\x01", "ISO-2022-JP", "\x81", nil], + ["UTF-16BE", "\x81", "US-ASCII", "", "UTF-16BE"], + ["UTF-16BE", "\x81", "US-ASCII", "\x01", nil], + ["UTF-16BE", "\x81", "US-ASCII", "\x81", nil], + ["UTF-16BE", "\x81", "UTF-8", "", "UTF-16BE"], + ["UTF-16BE", "\x81", "UTF-8", "\u0001", nil], + ["UTF-16BE", "\x81", "UTF-8", "\x81", nil], + ["UTF-16BE", "\x81", "ASCII-8BIT", "", "UTF-16BE"], + ["UTF-16BE", "\x81", "ASCII-8BIT", "\x01", nil], + ["UTF-16BE", "\x81", "ASCII-8BIT", "\x81", nil], + ["UTF-16BE", "\x81", "ISO-8859-1", "", "UTF-16BE"], + ["UTF-16BE", "\x81", "ISO-8859-1", "\x01", nil], + ["UTF-16BE", "\x81", "ISO-8859-1", "\x81", nil], + ["UTF-16BE", "\x81", "UTF-16BE", "", "UTF-16BE"], + ["UTF-16BE", "\x81", "UTF-16BE", "\x01", "UTF-16BE"], + ["UTF-16BE", "\x81", "UTF-16BE", "\x81", "UTF-16BE"], + ["UTF-16BE", "\x81", "ISO-2022-JP", "", "UTF-16BE"], + ["UTF-16BE", "\x81", "ISO-2022-JP", "\x01", nil], + ["UTF-16BE", "\x81", "ISO-2022-JP", "\x81", nil], + ["ISO-2022-JP", "", "US-ASCII", "", "ISO-2022-JP"], + ["ISO-2022-JP", "", "US-ASCII", "\x01", "US-ASCII"], + ["ISO-2022-JP", "", "US-ASCII", "\x81", "US-ASCII"], + ["ISO-2022-JP", "", "UTF-8", "", "ISO-2022-JP"], + ["ISO-2022-JP", "", "UTF-8", "\u0001", "UTF-8"], + ["ISO-2022-JP", "", "UTF-8", "\x81", "UTF-8"], + ["ISO-2022-JP", "", "ASCII-8BIT", "", "ISO-2022-JP"], + ["ISO-2022-JP", "", "ASCII-8BIT", "\x01", "ASCII-8BIT"], + ["ISO-2022-JP", "", "ASCII-8BIT", "\x81", "ASCII-8BIT"], + ["ISO-2022-JP", "", "ISO-8859-1", "", "ISO-2022-JP"], + ["ISO-2022-JP", "", "ISO-8859-1", "\x01", "ISO-8859-1"], + ["ISO-2022-JP", "", "ISO-8859-1", "\x81", "ISO-8859-1"], + ["ISO-2022-JP", "", "UTF-16BE", "", "ISO-2022-JP"], + ["ISO-2022-JP", "", "UTF-16BE", "\x01", "UTF-16BE"], + ["ISO-2022-JP", "", "UTF-16BE", "\x81", "UTF-16BE"], + ["ISO-2022-JP", "", "ISO-2022-JP", "", "ISO-2022-JP"], + ["ISO-2022-JP", "", "ISO-2022-JP", "\x01", "ISO-2022-JP"], + ["ISO-2022-JP", "", "ISO-2022-JP", "\x81", "ISO-2022-JP"], + ["ISO-2022-JP", "\x01", "US-ASCII", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x01", "US-ASCII", "\x01", nil], + ["ISO-2022-JP", "\x01", "US-ASCII", "\x81", nil], + ["ISO-2022-JP", "\x01", "UTF-8", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x01", "UTF-8", "\u0001", nil], + ["ISO-2022-JP", "\x01", "UTF-8", "\x81", nil], + ["ISO-2022-JP", "\x01", "ASCII-8BIT", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x01", "ASCII-8BIT", "\x01", nil], + ["ISO-2022-JP", "\x01", "ASCII-8BIT", "\x81", nil], + ["ISO-2022-JP", "\x01", "ISO-8859-1", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x01", "ISO-8859-1", "\x01", nil], + ["ISO-2022-JP", "\x01", "ISO-8859-1", "\x81", nil], + ["ISO-2022-JP", "\x01", "UTF-16BE", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x01", "UTF-16BE", "\x01", nil], + ["ISO-2022-JP", "\x01", "UTF-16BE", "\x81", nil], + ["ISO-2022-JP", "\x01", "ISO-2022-JP", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x01", "ISO-2022-JP", "\x01", "ISO-2022-JP"], + ["ISO-2022-JP", "\x01", "ISO-2022-JP", "\x81", "ISO-2022-JP"], + ["ISO-2022-JP", "\x81", "US-ASCII", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x81", "US-ASCII", "\x01", nil], + ["ISO-2022-JP", "\x81", "US-ASCII", "\x81", nil], + ["ISO-2022-JP", "\x81", "UTF-8", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x81", "UTF-8", "\u0001", nil], + ["ISO-2022-JP", "\x81", "UTF-8", "\x81", nil], + ["ISO-2022-JP", "\x81", "ASCII-8BIT", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x81", "ASCII-8BIT", "\x01", nil], + ["ISO-2022-JP", "\x81", "ASCII-8BIT", "\x81", nil], + ["ISO-2022-JP", "\x81", "ISO-8859-1", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x81", "ISO-8859-1", "\x01", nil], + ["ISO-2022-JP", "\x81", "ISO-8859-1", "\x81", nil], + ["ISO-2022-JP", "\x81", "UTF-16BE", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x81", "UTF-16BE", "\x01", nil], + ["ISO-2022-JP", "\x81", "UTF-16BE", "\x81", nil], + ["ISO-2022-JP", "\x81", "ISO-2022-JP", "", "ISO-2022-JP"], + ["ISO-2022-JP", "\x81", "ISO-2022-JP", "\x01", "ISO-2022-JP"], + ["ISO-2022-JP", "\x81", "ISO-2022-JP", "\x81", "ISO-2022-JP"], + ] + + matrix.each do |encoding1, value1, encoding2, value2, compatible_encoding| + it "returns #{compatible_encoding} for #{value1.inspect} in #{encoding1} and #{value2.inspect} in #{encoding2}" do + actual_encoding = Encoding.compatible?(value1.dup.force_encoding(encoding1), value2.dup.force_encoding(encoding2)) + actual_encoding&.name.should == compatible_encoding + end + end + end end describe "Encoding.compatible? String, Regexp" do @@ -377,3 +750,9 @@ Encoding.compatible?(:sym, Object.new).should be_nil end end + +describe "Encoding.compatible? nil, nil" do + it "returns nil" do + Encoding.compatible?(nil, nil).should be_nil + end +end diff --git a/spec/ruby/core/enumerator/each_with_index_spec.rb b/spec/ruby/core/enumerator/each_with_index_spec.rb index 96e53a2804c..271e61fec65 100644 --- a/spec/ruby/core/enumerator/each_with_index_spec.rb +++ b/spec/ruby/core/enumerator/each_with_index_spec.rb @@ -21,16 +21,16 @@ it "passes on the given block's return value" do arr = [1,2,3] - arr.delete_if.with_index { |a,b| false } + arr.delete_if.each_with_index { |a,b| false } arr.should == [1,2,3] end it "returns the iterator's return value" do - [1,2,3].select.with_index { |a,b| false }.should == [] + [1,2,3].select.each_with_index { |a,b| false }.should == [] + [1,2,3].select.each_with_index { |a,b| true }.should == [1,2,3] end it "returns the correct value if chained with itself" do [:a].each_with_index.each_with_index.to_a.should == [[[:a,0],0]] - [:a].each.with_index.with_index.to_a.should == [[[:a,0],0]] end end diff --git a/spec/ruby/core/enumerator/with_index_spec.rb b/spec/ruby/core/enumerator/with_index_spec.rb index ac37cee508f..3aeb3fc869a 100644 --- a/spec/ruby/core/enumerator/with_index_spec.rb +++ b/spec/ruby/core/enumerator/with_index_spec.rb @@ -69,4 +69,21 @@ @enum.with_index(-1) { |*x| res << x} res.should == [[1,-1], [2,0], [3,1], [4,2]] end + + it "passes on the given block's return value" do + arr = [1,2,3] + arr.delete_if.with_index { |a,b| false } + arr.should == [1,2,3] + + arr.delete_if.with_index { |a,b| true } + arr.should == [] + end + + it "returns the iterator's return value" do + @enum.select.with_index { |a,b| false }.should == [] + end + + it "returns the correct value if chained with itself" do + [:a].each.with_index.with_index.to_a.should == [[[:a,0],0]] + end end diff --git a/spec/ruby/core/exception/full_message_spec.rb b/spec/ruby/core/exception/full_message_spec.rb index 51543545551..d752083db2f 100644 --- a/spec/ruby/core/exception/full_message_spec.rb +++ b/spec/ruby/core/exception/full_message_spec.rb @@ -79,6 +79,24 @@ err.full_message(highlight: true).should !~ /unhandled exception/ err.full_message(highlight: false).should !~ /unhandled exception/ end + + it "adds escape sequences to highlight some strings if the message is not specified and :highlight option is specified" do + e = RuntimeError.new("") + + full_message = e.full_message(highlight: true, order: :top).lines + full_message[0].should.end_with? "\e[1;4munhandled exception\e[m\n" + + full_message = e.full_message(highlight: true, order: :bottom).lines + full_message[0].should == "\e[1mTraceback\e[m (most recent call last):\n" + full_message[-1].should.end_with? "\e[1;4munhandled exception\e[m\n" + + full_message = e.full_message(highlight: false, order: :top).lines + full_message[0].should.end_with? "unhandled exception\n" + + full_message = e.full_message(highlight: false, order: :bottom).lines + full_message[0].should == "Traceback (most recent call last):\n" + full_message[-1].should.end_with? "unhandled exception\n" + end end describe "generic Error" do diff --git a/spec/ruby/core/fiber/raise_spec.rb b/spec/ruby/core/fiber/raise_spec.rb index b3e021e6360..2f2baa4a12a 100644 --- a/spec/ruby/core/fiber/raise_spec.rb +++ b/spec/ruby/core/fiber/raise_spec.rb @@ -42,7 +42,7 @@ -> { FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError, 'test error' }.should raise_error(FiberSpecs::CustomError, 'test error') end - it 'accepts error class with with error message and backtrace information' do + it 'accepts error class with error message and backtrace information' do -> { FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError, 'test error', ['foo', 'boo'] }.should raise_error(FiberSpecs::CustomError) { |e| diff --git a/spec/ruby/core/file/chown_spec.rb b/spec/ruby/core/file/chown_spec.rb index 8cc8f0d04be..4db0e3712c2 100644 --- a/spec/ruby/core/file/chown_spec.rb +++ b/spec/ruby/core/file/chown_spec.rb @@ -78,15 +78,15 @@ end describe "File#chown" do - before :each do - @fname = tmp('file_chown_test') - @file = File.open(@fname, 'w') - end + before :each do + @fname = tmp('file_chown_test') + @file = File.open(@fname, 'w') + end - after :each do - @file.close unless @file.closed? - rm_r @fname - end + after :each do + @file.close unless @file.closed? + rm_r @fname + end as_superuser do platform_is :windows do diff --git a/spec/ruby/core/file/shared/update_time.rb b/spec/ruby/core/file/shared/update_time.rb index 9c063a8e935..3fe7266a00e 100644 --- a/spec/ruby/core/file/shared/update_time.rb +++ b/spec/ruby/core/file/shared/update_time.rb @@ -84,7 +84,7 @@ end platform_is :linux do - platform_is wordsize: 64 do + platform_is pointer_size: 64 do it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps # "Therefore, timestamps should not overflow until May 2446." diff --git a/spec/ruby/core/float/ceil_spec.rb b/spec/ruby/core/float/ceil_spec.rb index 7fc18d304c2..75f56102922 100644 --- a/spec/ruby/core/float/ceil_spec.rb +++ b/spec/ruby/core/float/ceil_spec.rb @@ -1,6 +1,11 @@ require_relative '../../spec_helper' +require_relative '../integer/shared/integer_ceil_precision' describe "Float#ceil" do + context "with precision" do + it_behaves_like :integer_ceil_precision, :Float + end + it "returns the smallest Integer greater than or equal to self" do -1.2.ceil.should eql( -1) -1.0.ceil.should eql( -1) diff --git a/spec/ruby/core/float/comparison_spec.rb b/spec/ruby/core/float/comparison_spec.rb index 1373b3a1fb2..d2e47937ff1 100644 --- a/spec/ruby/core/float/comparison_spec.rb +++ b/spec/ruby/core/float/comparison_spec.rb @@ -72,7 +72,7 @@ def coerce(other) (-Float::MAX.to_i*2 <=> -infinity_value).should == 1 end - it "returns 0 when self is Infinity and other other is infinite?=1" do + it "returns 0 when self is Infinity and other is infinite?=1" do obj = Object.new def obj.infinite? 1 diff --git a/spec/ruby/core/float/floor_spec.rb b/spec/ruby/core/float/floor_spec.rb index 046216d36d4..8b492ef4732 100644 --- a/spec/ruby/core/float/floor_spec.rb +++ b/spec/ruby/core/float/floor_spec.rb @@ -1,6 +1,11 @@ require_relative '../../spec_helper' +require_relative '../integer/shared/integer_floor_precision' describe "Float#floor" do + context "with precision" do + it_behaves_like :integer_floor_precision, :Float + end + it "returns the largest Integer less than or equal to self" do -1.2.floor.should eql( -2) -1.0.floor.should eql( -1) diff --git a/spec/ruby/core/hash/element_reference_spec.rb b/spec/ruby/core/hash/element_reference_spec.rb index 94e82378395..d5859cb3427 100644 --- a/spec/ruby/core/hash/element_reference_spec.rb +++ b/spec/ruby/core/hash/element_reference_spec.rb @@ -12,7 +12,7 @@ h[[]].should == "baz" end - it "returns nil as default default value" do + it "returns nil as default value" do { 0 => 0 }[5].should == nil end diff --git a/spec/ruby/core/hash/hash_spec.rb b/spec/ruby/core/hash/hash_spec.rb index 19eb806dc4e..9b47d4b2bf9 100644 --- a/spec/ruby/core/hash/hash_spec.rb +++ b/spec/ruby/core/hash/hash_spec.rb @@ -47,7 +47,7 @@ a = 1 b = 2 - eval('{a:, b:}.should == { a: 1, b: 2 }') + eval('{a:, b:}.should == { a: 1, b: 2 }') end end end diff --git a/spec/ruby/core/hash/new_spec.rb b/spec/ruby/core/hash/new_spec.rb index 6279815fd6e..5ae3e1f98d6 100644 --- a/spec/ruby/core/hash/new_spec.rb +++ b/spec/ruby/core/hash/new_spec.rb @@ -34,7 +34,7 @@ -> { Hash.new(nil) { 0 } }.should raise_error(ArgumentError) end - ruby_version_is "3.3" do + ruby_version_is "3.3"..."3.4" do it "emits a deprecation warning if keyword arguments are passed" do -> { Hash.new(unknown: true) }.should complain( Regexp.new(Regexp.escape("Calling Hash.new with keyword arguments is deprecated and will be removed in Ruby 3.4; use Hash.new({ key: value }) instead")) @@ -46,4 +46,22 @@ Hash.new({ unknown: true }).default.should == { unknown: true } end end + + ruby_version_is "3.4" do + it "accepts a capacity: argument" do + Hash.new(5, capacity: 42).default.should == 5 + Hash.new(capacity: 42).default.should == nil + (Hash.new(capacity: 42) { 1 }).default_proc.should_not == nil + end + + it "ignores negative capacity" do + -> { Hash.new(capacity: -42) }.should_not raise_error + end + + it "raises an error if unknown keyword arguments are passed" do + -> { Hash.new(unknown: true) }.should raise_error(ArgumentError) + -> { Hash.new(1, unknown: true) }.should raise_error(ArgumentError) + -> { Hash.new(unknown: true) { 0 } }.should raise_error(ArgumentError) + end + end end diff --git a/spec/ruby/core/hash/shared/to_s.rb b/spec/ruby/core/hash/shared/to_s.rb index 7864d7cd4c6..5f0a8f97fdb 100644 --- a/spec/ruby/core/hash/shared/to_s.rb +++ b/spec/ruby/core/hash/shared/to_s.rb @@ -4,14 +4,8 @@ describe :hash_to_s, shared: true do it "returns a string representation with same order as each()" do h = { a: [1, 2], b: -2, d: -6, nil => nil } - - pairs = [] - h.each do |key, value| - pairs << key.inspect + '=>' + value.inspect - end - - str = '{' + pairs.join(', ') + '}' - h.send(@method).should == str + expected = ruby_version_is("3.4") ? "{a: [1, 2], b: -2, d: -6, nil => nil}" : "{:a=>[1, 2], :b=>-2, :d=>-6, nil=>nil}" + h.send(@method).should == expected end it "calls #inspect on keys and values" do @@ -19,31 +13,31 @@ val = mock('val') key.should_receive(:inspect).and_return('key') val.should_receive(:inspect).and_return('val') - - { key => val }.send(@method).should == '{key=>val}' + expected = ruby_version_is("3.4") ? "{key => val}" : "{key=>val}" + { key => val }.send(@method).should == expected end it "does not call #to_s on a String returned from #inspect" do str = +"abc" str.should_not_receive(:to_s) - - { a: str }.send(@method).should == '{:a=>"abc"}' + expected = ruby_version_is("3.4") ? '{a: "abc"}' : '{:a=>"abc"}' + { a: str }.send(@method).should == expected end it "calls #to_s on the object returned from #inspect if the Object isn't a String" do obj = mock("Hash#inspect/to_s calls #to_s") obj.should_receive(:inspect).and_return(obj) obj.should_receive(:to_s).and_return("abc") - - { a: obj }.send(@method).should == "{:a=>abc}" + expected = ruby_version_is("3.4") ? "{a: abc}" : "{:a=>abc}" + { a: obj }.send(@method).should == expected end it "does not call #to_str on the object returned from #inspect when it is not a String" do obj = mock("Hash#inspect/to_s does not call #to_str") obj.should_receive(:inspect).and_return(obj) obj.should_not_receive(:to_str) - - { a: obj }.send(@method).should =~ /^\{:a=>#\}$/ + expected_pattern = ruby_version_is("3.4") ? /^\{a: #\}$/ : /^\{:a=>#\}$/ + { a: obj }.send(@method).should =~ expected_pattern end it "does not call #to_str on the object returned from #to_s when it is not a String" do @@ -51,8 +45,8 @@ obj.should_receive(:inspect).and_return(obj) obj.should_receive(:to_s).and_return(obj) obj.should_not_receive(:to_str) - - { a: obj }.send(@method).should =~ /^\{:a=>#\}$/ + expected_pattern = ruby_version_is("3.4") ? /^\{a: #\}$/ : /^\{:a=>#\}$/ + { a: obj }.send(@method).should =~ expected_pattern end it "does not swallow exceptions raised by #to_s" do @@ -66,24 +60,28 @@ it "handles hashes with recursive values" do x = {} x[0] = x - x.send(@method).should == '{0=>{...}}' + expected = ruby_version_is("3.4") ? '{0 => {...}}' : '{0=>{...}}' + x.send(@method).should == expected x = {} y = {} x[0] = y y[1] = x - x.send(@method).should == "{0=>{1=>{...}}}" - y.send(@method).should == "{1=>{0=>{...}}}" + expected_x = ruby_version_is("3.4") ? '{0 => {1 => {...}}}' : '{0=>{1=>{...}}}' + expected_y = ruby_version_is("3.4") ? '{1 => {0 => {...}}}' : '{1=>{0=>{...}}}' + x.send(@method).should == expected_x + y.send(@method).should == expected_y end it "does not raise if inspected result is not default external encoding" do utf_16be = mock("utf_16be") utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE)) - - {a: utf_16be}.send(@method).should == '{:a=>"utf_16be \u3042"}' + expected = ruby_version_is("3.4") ? '{a: "utf_16be \u3042"}' : '{:a=>"utf_16be \u3042"}' + {a: utf_16be}.send(@method).should == expected end it "works for keys and values whose #inspect return a frozen String" do - { true => false }.to_s.should == "{true=>false}" + expected = ruby_version_is("3.4") ? "{true => false}" : "{true=>false}" + { true => false }.to_s.should == expected end end diff --git a/spec/ruby/core/integer/ceil_spec.rb b/spec/ruby/core/integer/ceil_spec.rb index 13bdaf838d9..eb633fba784 100644 --- a/spec/ruby/core/integer/ceil_spec.rb +++ b/spec/ruby/core/integer/ceil_spec.rb @@ -1,11 +1,16 @@ require_relative '../../spec_helper' require_relative 'shared/to_i' require_relative 'shared/integer_rounding' +require_relative 'shared/integer_ceil_precision' describe "Integer#ceil" do it_behaves_like :integer_to_i, :ceil it_behaves_like :integer_rounding_positive_precision, :ceil + context "with precision" do + it_behaves_like :integer_ceil_precision, :Integer + end + context "precision argument specified as part of the ceil method is negative" do it "returns the smallest integer greater than self with at least precision.abs trailing zeros" do 18.ceil(-1).should eql(20) diff --git a/spec/ruby/core/integer/floor_spec.rb b/spec/ruby/core/integer/floor_spec.rb index aaa816fdc53..8fb84d58cb8 100644 --- a/spec/ruby/core/integer/floor_spec.rb +++ b/spec/ruby/core/integer/floor_spec.rb @@ -1,19 +1,13 @@ require_relative '../../spec_helper' require_relative 'shared/to_i' require_relative 'shared/integer_rounding' +require_relative 'shared/integer_floor_precision' describe "Integer#floor" do it_behaves_like :integer_to_i, :floor it_behaves_like :integer_rounding_positive_precision, :floor - context "precision argument specified as part of the floor method is negative" do - it "returns the largest integer less than self with at least precision.abs trailing zeros" do - 1832.floor(-1).should eql(1830) - 1832.floor(-2).should eql(1800) - 1832.floor(-3).should eql(1000) - -1832.floor(-1).should eql(-1840) - -1832.floor(-2).should eql(-1900) - -1832.floor(-3).should eql(-2000) - end + context "with precision" do + it_behaves_like :integer_floor_precision, :Integer end end diff --git a/spec/ruby/core/integer/pow_spec.rb b/spec/ruby/core/integer/pow_spec.rb index 47129110952..ecaca01eff3 100644 --- a/spec/ruby/core/integer/pow_spec.rb +++ b/spec/ruby/core/integer/pow_spec.rb @@ -19,13 +19,13 @@ 2.pow(61, 5843009213693951).should eql 3697379018277258 2.pow(62, 5843009213693952).should eql 1551748822859776 2.pow(63, 5843009213693953).should eql 3103497645717974 - 2.pow(64, 5843009213693954).should eql 363986077738838 + 2.pow(64, 5843009213693954).should eql 363986077738838 end it "handles sign like #divmod does" do - 2.pow(5, 12).should == 8 - 2.pow(5, -12).should == -4 - -2.pow(5, 12).should == 4 + 2.pow(5, 12).should == 8 + 2.pow(5, -12).should == -4 + -2.pow(5, 12).should == 4 -2.pow(5, -12).should == -8 end diff --git a/spec/ruby/core/integer/remainder_spec.rb b/spec/ruby/core/integer/remainder_spec.rb index 96268b3af5c..757e42fbe84 100644 --- a/spec/ruby/core/integer/remainder_spec.rb +++ b/spec/ruby/core/integer/remainder_spec.rb @@ -15,8 +15,8 @@ end it "keeps sign of self" do - 5.remainder( 3).should == 2 - 5.remainder(-3).should == 2 + 5.remainder( 3).should == 2 + 5.remainder(-3).should == 2 -5.remainder( 3).should == -2 -5.remainder(-3).should == -2 end diff --git a/spec/ruby/core/integer/round_spec.rb b/spec/ruby/core/integer/round_spec.rb index 45ac126fd36..189384f11ad 100644 --- a/spec/ruby/core/integer/round_spec.rb +++ b/spec/ruby/core/integer/round_spec.rb @@ -21,10 +21,8 @@ (-25 * 10**70).round(-71).should eql(-30 * 10**70) end - platform_is_not wordsize: 32 do - it "raises a RangeError when passed a big negative value" do - -> { 42.round(fixnum_min) }.should raise_error(RangeError) - end + it "raises a RangeError when passed a big negative value" do + -> { 42.round(min_long - 1) }.should raise_error(RangeError) end it "raises a RangeError when passed Float::INFINITY" do diff --git a/spec/ruby/core/integer/shared/integer_ceil_precision.rb b/spec/ruby/core/integer/shared/integer_ceil_precision.rb new file mode 100644 index 00000000000..9f31c2cf615 --- /dev/null +++ b/spec/ruby/core/integer/shared/integer_ceil_precision.rb @@ -0,0 +1,43 @@ +describe :integer_ceil_precision, shared: true do + context "precision is zero" do + it "returns integer self" do + send(@method, 0).ceil(0).should.eql?(0) + send(@method, 123).ceil(0).should.eql?(123) + send(@method, -123).ceil(0).should.eql?(-123) + end + end + + context "precision is positive" do + it "returns self" do + send(@method, 0).ceil(1).should.eql?(send(@method, 0)) + send(@method, 0).ceil(10).should.eql?(send(@method, 0)) + + send(@method, 123).ceil(10).should.eql?(send(@method, 123)) + send(@method, -123).ceil(10).should.eql?(send(@method, -123)) + end + end + + context "precision is negative" do + it "always returns 0 when self is 0" do + send(@method, 0).ceil(-1).should.eql?(0) + send(@method, 0).ceil(-10).should.eql?(0) + end + + it "returns largest integer less than self with at least precision.abs trailing zeros" do + send(@method, 123).ceil(-1).should.eql?(130) + send(@method, 123).ceil(-2).should.eql?(200) + send(@method, 123).ceil(-3).should.eql?(1000) + + send(@method, -123).ceil(-1).should.eql?(-120) + send(@method, -123).ceil(-2).should.eql?(-100) + send(@method, -123).ceil(-3).should.eql?(0) + end + + ruby_bug "#20654", ""..."3.4" do + it "returns 10**precision.abs when precision.abs is larger than the number digits of self" do + send(@method, 123).ceil(-20).should.eql?(100000000000000000000) + send(@method, 123).ceil(-50).should.eql?(100000000000000000000000000000000000000000000000000) + end + end + end +end diff --git a/spec/ruby/core/integer/shared/integer_floor_precision.rb b/spec/ruby/core/integer/shared/integer_floor_precision.rb new file mode 100644 index 00000000000..4c5888c6c48 --- /dev/null +++ b/spec/ruby/core/integer/shared/integer_floor_precision.rb @@ -0,0 +1,43 @@ +describe :integer_floor_precision, shared: true do + context "precision is zero" do + it "returns integer self" do + send(@method, 0).floor(0).should.eql?(0) + send(@method, 123).floor(0).should.eql?(123) + send(@method, -123).floor(0).should.eql?(-123) + end + end + + context "precision is positive" do + it "returns self" do + send(@method, 0).floor(1).should.eql?(send(@method, 0)) + send(@method, 0).floor(10).should.eql?(send(@method, 0)) + + send(@method, 123).floor(10).should.eql?(send(@method, 123)) + send(@method, -123).floor(10).should.eql?(send(@method, -123)) + end + end + + context "precision is negative" do + it "always returns 0 when self is 0" do + send(@method, 0).floor(-1).should.eql?(0) + send(@method, 0).floor(-10).should.eql?(0) + end + + it "returns largest integer less than self with at least precision.abs trailing zeros" do + send(@method, 123).floor(-1).should.eql?(120) + send(@method, 123).floor(-2).should.eql?(100) + send(@method, 123).floor(-3).should.eql?(0) + + send(@method, -123).floor(-1).should.eql?(-130) + send(@method, -123).floor(-2).should.eql?(-200) + send(@method, -123).floor(-3).should.eql?(-1000) + end + + ruby_bug "#20654", ""..."3.4" do + it "returns -(10**precision.abs) when self is negative and precision.abs is larger than the number digits of self" do + send(@method, -123).floor(-20).should.eql?(-100000000000000000000) + send(@method, -123).floor(-50).should.eql?(-100000000000000000000000000000000000000000000000000) + end + end + end +end diff --git a/spec/ruby/core/integer/size_spec.rb b/spec/ruby/core/integer/size_spec.rb index a134e823842..725e9eb062b 100644 --- a/spec/ruby/core/integer/size_spec.rb +++ b/spec/ruby/core/integer/size_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' describe "Integer#size" do - platform_is wordsize: 32 do + platform_is c_long_size: 32 do it "returns the number of bytes in the machine representation of self" do -1.size.should == 4 0.size.should == 4 @@ -9,7 +9,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "returns the number of bytes in the machine representation of self" do -1.size.should == 8 0.size.should == 8 diff --git a/spec/ruby/core/io/dup_spec.rb b/spec/ruby/core/io/dup_spec.rb index 68d538377f6..564e0074387 100644 --- a/spec/ruby/core/io/dup_spec.rb +++ b/spec/ruby/core/io/dup_spec.rb @@ -25,27 +25,27 @@ @i.fileno.should_not == @f.fileno end -quarantine! do # This does not appear to be consistent across platforms - it "shares the original stream between the two IOs" do - start = @f.pos - @i.pos.should == start + quarantine! do # This does not appear to be consistent across platforms + it "shares the original stream between the two IOs" do + start = @f.pos + @i.pos.should == start - s = "Hello, wo.. wait, where am I?\n" - s2 = " Muhahahaa!" + s = "Hello, wo.. wait, where am I?\n" + s2 = " Muhahahaa!" - @f.write s - @i.pos.should == @f.pos + @f.write s + @i.pos.should == @f.pos - @i.rewind - @i.gets.should == s + @i.rewind + @i.gets.should == s - @i.rewind - @i.write s2 + @i.rewind + @i.write s2 - @f.rewind - @f.gets.should == "#{s2}\n" + @f.rewind + @f.gets.should == "#{s2}\n" + end end -end it "allows closing the new IO without affecting the original" do @i.close diff --git a/spec/ruby/core/io/puts_spec.rb b/spec/ruby/core/io/puts_spec.rb index 9ed343c94c3..a186ddaa5d8 100644 --- a/spec/ruby/core/io/puts_spec.rb +++ b/spec/ruby/core/io/puts_spec.rb @@ -33,7 +33,7 @@ def @io.write(str) ScratchPad.recorded.should == "\n" end - it "writes empty string with a newline when when given nil as multiple args" do + it "writes empty string with a newline when given nil as multiple args" do @io.puts(nil, nil).should == nil ScratchPad.recorded.should == "\n\n" end diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb index 8741d9f0179..567daa55dfc 100644 --- a/spec/ruby/core/io/read_spec.rb +++ b/spec/ruby/core/io/read_spec.rb @@ -217,19 +217,19 @@ end end -quarantine! do # The process tried to write to a nonexistent pipe. - platform_is :windows do - # TODO: It should raise Errno::ESPIPE on Windows as well - # once https://bugs.ruby-lang.org/issues/12230 is fixed. - it "raises Errno::EINVAL if passed an offset" do - -> { - suppress_warning do # https://bugs.ruby-lang.org/issues/19630 - IO.read("|cmd.exe /C echo hello", 1, 1) - end - }.should raise_error(Errno::EINVAL) + quarantine! do # The process tried to write to a nonexistent pipe. + platform_is :windows do + # TODO: It should raise Errno::ESPIPE on Windows as well + # once https://bugs.ruby-lang.org/issues/12230 is fixed. + it "raises Errno::EINVAL if passed an offset" do + -> { + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + IO.read("|cmd.exe /C echo hello", 1, 1) + end + }.should raise_error(Errno::EINVAL) + end end end -end ruby_version_is "3.3" do # https://bugs.ruby-lang.org/issues/19630 diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb index 454bc4a58e5..5f4cd27da02 100644 --- a/spec/ruby/core/kernel/eval_spec.rb +++ b/spec/ruby/core/kernel/eval_spec.rb @@ -274,6 +274,26 @@ class EvalSpecs eval("").should == nil end + context "with shebang" do + it "ignores shebang with ruby interpreter" do + pid = eval(<<~CODE.b) + #!/usr/bin/env ruby + Process.pid + CODE + + pid.should == Process.pid + end + + it "ignores shebang with non-ruby interpreter" do + pid = eval(<<~CODE.b) + #!/usr/bin/env puma + Process.pid + CODE + + pid.should == Process.pid + end + end + # See language/magic_comment_spec.rb for more magic comments specs describe "with a magic encoding comment" do it "uses the magic comment encoding for the encoding of literal strings" do diff --git a/spec/ruby/core/kernel/extend_spec.rb b/spec/ruby/core/kernel/extend_spec.rb index 47b22f3a182..6342d8cae15 100644 --- a/spec/ruby/core/kernel/extend_spec.rb +++ b/spec/ruby/core/kernel/extend_spec.rb @@ -76,4 +76,16 @@ def self.append_features(o) -> { @frozen.extend @module }.should raise_error(FrozenError) end end + + it "updated class methods of a module when it extends self and includes another module" do + a = Module.new do + extend self + end + b = Module.new do + def foo; :foo; end + end + + a.include b + a.foo.should == :foo + end end diff --git a/spec/ruby/core/kernel/format_spec.rb b/spec/ruby/core/kernel/format_spec.rb index e8b031e4801..1d0c000c158 100644 --- a/spec/ruby/core/kernel/format_spec.rb +++ b/spec/ruby/core/kernel/format_spec.rb @@ -12,4 +12,36 @@ it "is accessible as a module function" do Kernel.format("%s", "hello").should == "hello" end + + describe "when $VERBOSE is true" do + it "warns if too many arguments are passed" do + code = <<~RUBY + $VERBOSE = true + format("test", 1) + RUBY + + ruby_exe(code, args: "2>&1").should include("warning: too many arguments for format string") + end + + it "does not warns if too many keyword arguments are passed" do + code = <<~RUBY + $VERBOSE = true + format("test %{test}", test: 1, unused: 2) + RUBY + + ruby_exe(code, args: "2>&1").should_not include("warning") + end + + ruby_bug "#20593", ""..."3.4" do + it "doesn't warns if keyword arguments are passed and none are used" do + code = <<~RUBY + $VERBOSE = true + format("test", test: 1) + format("test", {}) + RUBY + + ruby_exe(code, args: "2>&1").should_not include("warning") + end + end + end end diff --git a/spec/ruby/core/kernel/raise_spec.rb b/spec/ruby/core/kernel/raise_spec.rb index 4f190c120b7..a038dcf031a 100644 --- a/spec/ruby/core/kernel/raise_spec.rb +++ b/spec/ruby/core/kernel/raise_spec.rb @@ -46,6 +46,22 @@ cause = StandardError.new -> { raise(cause: cause) }.should raise_error(ArgumentError) end + + it "re-raises a rescued exception" do + -> do + begin + raise StandardError, "aaa" + rescue Exception + begin + raise ArgumentError + rescue ArgumentError + end + + # should raise StandardError "aaa" + raise + end + end.should raise_error(StandardError, "aaa") + end end describe "Kernel#raise" do diff --git a/spec/ruby/core/kernel/select_spec.rb b/spec/ruby/core/kernel/select_spec.rb index e0d82f3079b..df23414b284 100644 --- a/spec/ruby/core/kernel/select_spec.rb +++ b/spec/ruby/core/kernel/select_spec.rb @@ -10,9 +10,9 @@ describe "Kernel.select" do it 'does not block when timeout is 0' do IO.pipe do |read, write| - IO.select([read], [], [], 0).should == nil + select([read], [], [], 0).should == nil write.write 'data' - IO.select([read], [], [], 0).should == [[read], [], []] + select([read], [], [], 0).should == [[read], [], []] end end end diff --git a/spec/ruby/core/kernel/sleep_spec.rb b/spec/ruby/core/kernel/sleep_spec.rb index d35e3130063..1de52a707fc 100644 --- a/spec/ruby/core/kernel/sleep_spec.rb +++ b/spec/ruby/core/kernel/sleep_spec.rb @@ -51,6 +51,20 @@ def o.divmod(*); [0, 0.001]; end t.value.should == 5 end + platform_is_not :darwin do + it "sleeps with nanosecond precision" do + start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 100.times do + sleep(0.0001) + end + end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + actual_duration = end_time - start_time + (actual_duration > 0.01).should == true # 100 * 0.0001 => 0.01 + (actual_duration < 0.03).should == true + end + end + ruby_version_is ""..."3.3" do it "raises a TypeError when passed nil" do -> { sleep(nil) }.should raise_error(TypeError) diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb index 0f77279a4fa..adabfdf025c 100644 --- a/spec/ruby/core/marshal/dump_spec.rb +++ b/spec/ruby/core/marshal/dump_spec.rb @@ -38,7 +38,7 @@ ].should be_computed_by(:dump) end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "dumps a positive Fixnum > 31 bits as a Bignum" do Marshal.dump(2**31 + 1).should == "\x04\bl+\a\x01\x00\x00\x80" end diff --git a/spec/ruby/core/marshal/fixtures/marshal_data.rb b/spec/ruby/core/marshal/fixtures/marshal_data.rb index a508b6bea1d..aae3fce0aa2 100644 --- a/spec/ruby/core/marshal/fixtures/marshal_data.rb +++ b/spec/ruby/core/marshal/fixtures/marshal_data.rb @@ -1,4 +1,7 @@ # -*- encoding: binary -*- + +require_relative 'marshal_multibyte_data' + class UserDefined class Nested def ==(other) @@ -267,17 +270,6 @@ def self.name end end - module_eval(<<~ruby.dup.force_encoding(Encoding::UTF_8)) - class MultibyteぁあぃいClass - end - - module MultibyteけげこごModule - end - - class MultibyteぁあぃいTime < Time - end - ruby - class ObjectWithFreezeRaisingException < Object def freeze raise diff --git a/spec/ruby/core/marshal/fixtures/marshal_multibyte_data.rb b/spec/ruby/core/marshal/fixtures/marshal_multibyte_data.rb new file mode 100644 index 00000000000..98a0d433921 --- /dev/null +++ b/spec/ruby/core/marshal/fixtures/marshal_multibyte_data.rb @@ -0,0 +1,12 @@ +# -*- encoding: utf-8 -*- + +module MarshalSpec + class MultibyteぁあぃいClass + end + + module MultibyteけげこごModule + end + + class MultibyteぁあぃいTime < Time + end +end diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb index f5990425293..4eac21a9526 100644 --- a/spec/ruby/core/marshal/shared/load.rb +++ b/spec/ruby/core/marshal/shared/load.rb @@ -1049,7 +1049,7 @@ def io.binmode; raise "binmode"; end end describe "for a Bignum" do - platform_is wordsize: 64 do + platform_is c_long_size: 64 do context "that is Bignum on 32-bit platforms but Fixnum on 64-bit" do it "dumps a Fixnum" do val = Marshal.send(@method, "\004\bl+\ab:wU") diff --git a/spec/ruby/core/module/ancestors_spec.rb b/spec/ruby/core/module/ancestors_spec.rb index 5e4c1962066..43ebdb864f5 100644 --- a/spec/ruby/core/module/ancestors_spec.rb +++ b/spec/ruby/core/module/ancestors_spec.rb @@ -21,6 +21,17 @@ class << ModuleSpecs::Child; self; end.ancestors.should include(ModuleSpecs::Int ModuleSpecs::Parent.ancestors.should == ModuleSpecs::Parent.ancestors.uniq end + it "returns a module that is included later into a nested module as well" do + m1 = Module.new + m2 = Module.new + m3 = Module.new do + include m2 + end + m2.include m1 # should be after m3 includes m2 + + m3.ancestors.should == [m3, m2, m1] + end + describe "when called on a singleton class" do it "includes the singleton classes of ancestors" do parent = Class.new diff --git a/spec/ruby/core/module/const_added_spec.rb b/spec/ruby/core/module/const_added_spec.rb index f9edda3a074..4b10dd59637 100644 --- a/spec/ruby/core/module/const_added_spec.rb +++ b/spec/ruby/core/module/const_added_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'fixtures/const_added' describe "Module#const_added" do ruby_version_is "3.2" do @@ -63,6 +64,27 @@ module SubModule ScratchPad.recorded.should == [:SubModule] end + it "is called when a new module is defined under a named module (assigned to a constant)" do + ScratchPad.record [] + + ModuleSpecs::ConstAddedSpecs::NamedModule = Module.new do + def self.const_added(name) + ScratchPad << name + end + + module self::A + def self.const_added(name) + ScratchPad << name + end + + module self::B + end + end + end + + ScratchPad.recorded.should == [:A, :B] + end + it "is called when a new class is defined under self" do ScratchPad.record [] @@ -83,6 +105,27 @@ class SubClass ScratchPad.recorded.should == [:SubClass] end + it "is called when a new class is defined under a named module (assigned to a constant)" do + ScratchPad.record [] + + ModuleSpecs::ConstAddedSpecs::NamedModuleB = Module.new do + def self.const_added(name) + ScratchPad << name + end + + class self::A + def self.const_added(name) + ScratchPad << name + end + + class self::B + end + end + end + + ScratchPad.recorded.should == [:A, :B] + end + it "is called when an autoload is defined" do ScratchPad.record [] diff --git a/spec/ruby/core/module/fixtures/const_added.rb b/spec/ruby/core/module/fixtures/const_added.rb new file mode 100644 index 00000000000..0f5baad65db --- /dev/null +++ b/spec/ruby/core/module/fixtures/const_added.rb @@ -0,0 +1,4 @@ +module ModuleSpecs + module ConstAddedSpecs + end +end diff --git a/spec/ruby/core/module/fixtures/name.rb b/spec/ruby/core/module/fixtures/name.rb index fb9e66c309e..25c74d3944a 100644 --- a/spec/ruby/core/module/fixtures/name.rb +++ b/spec/ruby/core/module/fixtures/name.rb @@ -7,4 +7,7 @@ def name Cß.name end end + + module NameSpecs + end end diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb index 78f6b410317..862c6976e1b 100644 --- a/spec/ruby/core/module/include_spec.rb +++ b/spec/ruby/core/module/include_spec.rb @@ -581,6 +581,29 @@ def foo c2.include(m) c2.new.foo.should == [:c2, :m1] end + + it "update a module when a nested module is updated and includes a module on its own" do + m1 = Module.new + m2 = Module.new do + def m2; [:m2]; end + end + m3 = Module.new do + def m3; [:m3]; end + end + m4 = Module.new do + def m4; [:m4]; end + end + c = Class.new + + c.include(m1) + m1.include(m2) + m2.include(m3) + m3.include(m4) + + c.new.m2.should == [:m2] + c.new.m3.should == [:m3] + c.new.m4.should == [:m4] + end end describe "Module#include?" do diff --git a/spec/ruby/core/module/name_spec.rb b/spec/ruby/core/module/name_spec.rb index 0d1f4e24d58..33e8400e88a 100644 --- a/spec/ruby/core/module/name_spec.rb +++ b/spec/ruby/core/module/name_spec.rb @@ -140,6 +140,47 @@ module ModuleSpecs::Anonymous valid_names.should include(m::N.name) # You get one of the two, but you don't know which one. end + ruby_version_is "3.2" do + it "is set in #const_added callback when a module defined in the top-level scope" do + ruby_exe(<<~RUBY, args: "2>&1").chomp.should == "TEST1\nTEST2" + class Module + def const_added(name) + puts const_get(name).name + end + end + + # module with name + module TEST1 + end + + # anonymous module + TEST2 = Module.new + RUBY + end + + it "is set in #const_added callback for a nested module when an outer module defined in the top-level scope" do + ScratchPad.record [] + + ModuleSpecs::NameSpecs::NamedModule = Module.new do + def self.const_added(name) + ScratchPad << const_get(name).name + end + + module self::A + def self.const_added(name) + ScratchPad << const_get(name).name + end + + module self::B + end + end + end + + ScratchPad.recorded.should.one?(/#::A$/) + ScratchPad.recorded.should.one?(/#::A::B$/) + end + end + it "returns a frozen String" do ModuleSpecs.name.should.frozen? end diff --git a/spec/ruby/core/module/new_spec.rb b/spec/ruby/core/module/new_spec.rb index da7f3b87207..ec521360bd7 100644 --- a/spec/ruby/core/module/new_spec.rb +++ b/spec/ruby/core/module/new_spec.rb @@ -6,6 +6,10 @@ Module.new.is_a?(Module).should == true end + it "creates a module without a name" do + Module.new.name.should be_nil + end + it "creates a new Module and passes it to the provided block" do test_mod = nil m = Module.new do |mod| diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb index 11085c325b1..8b9ea5eca8c 100644 --- a/spec/ruby/core/module/refine_spec.rb +++ b/spec/ruby/core/module/refine_spec.rb @@ -245,7 +245,7 @@ def foo; "foo from singleton class"; end ruby_version_is ""..."3.2" do it "looks in the included modules for builtin methods" do - result = ruby_exe(<<-RUBY) + result = ruby_exe(<<-RUBY) a = Module.new do def /(other) quo(other) end end diff --git a/spec/ruby/core/objectspace/add_finalizer_spec.rb b/spec/ruby/core/objectspace/add_finalizer_spec.rb deleted file mode 100644 index 3540ac0413d..00000000000 --- a/spec/ruby/core/objectspace/add_finalizer_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "ObjectSpace.add_finalizer" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/objectspace/call_finalizer_spec.rb b/spec/ruby/core/objectspace/call_finalizer_spec.rb deleted file mode 100644 index 6dce92ddd6e..00000000000 --- a/spec/ruby/core/objectspace/call_finalizer_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "ObjectSpace.call_finalizer" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/objectspace/define_finalizer_spec.rb b/spec/ruby/core/objectspace/define_finalizer_spec.rb index effecc41d0a..4d4cfa9270c 100644 --- a/spec/ruby/core/objectspace/define_finalizer_spec.rb +++ b/spec/ruby/core/objectspace/define_finalizer_spec.rb @@ -168,6 +168,31 @@ def finalizer(zelf) ruby_exe(code).lines.sort.should == ["finalized1\n", "finalized2\n"] end + it "defines same finalizer only once" do + code = <<~RUBY + obj = Object.new + p = proc { |id| print "ok" } + ObjectSpace.define_finalizer(obj, p.dup) + ObjectSpace.define_finalizer(obj, p.dup) + RUBY + + ruby_exe(code).should == "ok" + end + + it "returns the defined finalizer" do + obj = Object.new + p = proc { |id| } + p2 = p.dup + + ret = ObjectSpace.define_finalizer(obj, p) + ret.should == [0, p] + ret[1].should.equal?(p) + + ret = ObjectSpace.define_finalizer(obj, p2) + ret.should == [0, p] + ret[1].should.equal?(p) + end + ruby_version_is "3.1" do describe "when $VERBOSE is not nil" do it "warns if an exception is raised in finalizer" do diff --git a/spec/ruby/core/objectspace/finalizers_spec.rb b/spec/ruby/core/objectspace/finalizers_spec.rb deleted file mode 100644 index e7f20fc8a02..00000000000 --- a/spec/ruby/core/objectspace/finalizers_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "ObjectSpace.finalizers" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/objectspace/remove_finalizer_spec.rb b/spec/ruby/core/objectspace/remove_finalizer_spec.rb deleted file mode 100644 index 0b2b8cf16b6..00000000000 --- a/spec/ruby/core/objectspace/remove_finalizer_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "ObjectSpace.remove_finalizer" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/objectspace/undefine_finalizer_spec.rb b/spec/ruby/core/objectspace/undefine_finalizer_spec.rb index 11d43121f86..f57d5a78452 100644 --- a/spec/ruby/core/objectspace/undefine_finalizer_spec.rb +++ b/spec/ruby/core/objectspace/undefine_finalizer_spec.rb @@ -1,5 +1,33 @@ require_relative '../../spec_helper' describe "ObjectSpace.undefine_finalizer" do - it "needs to be reviewed for spec completeness" + it "removes finalizers for an object" do + code = <<~RUBY + obj = Object.new + ObjectSpace.define_finalizer(obj, proc { |id| puts "hello" }) + ObjectSpace.undefine_finalizer(obj) + RUBY + + ruby_exe(code).should.empty? + end + + it "should not remove finalizers for a frozen object" do + code = <<~RUBY + obj = Object.new + ObjectSpace.define_finalizer(obj, proc { |id| print "ok" }) + obj.freeze + begin + ObjectSpace.undefine_finalizer(obj) + rescue + end + RUBY + + ruby_exe(code).should == "ok" + end + + it "should raise when removing finalizers for a frozen object" do + obj = Object.new + obj.freeze + -> { ObjectSpace.undefine_finalizer(obj) }.should raise_error(FrozenError) + end end diff --git a/spec/ruby/core/proc/curry_spec.rb b/spec/ruby/core/proc/curry_spec.rb index 24df2a8a72f..6daabe0ee17 100644 --- a/spec/ruby/core/proc/curry_spec.rb +++ b/spec/ruby/core/proc/curry_spec.rb @@ -159,15 +159,14 @@ end it "can be passed more than _arity_ arguments if created from a proc" do - -> { @proc_add.curry(3)[1,2,3,4].should == 6 }.should_not - raise_error(ArgumentError) - -> { @proc_add.curry(1)[1,2].curry(3)[3,4,5,6].should == 6 }.should_not - raise_error(ArgumentError) + @proc_add.curry(3)[1,2,3,4].should == 6 + + @proc_add.curry(3)[1,2].curry(3)[3,4,5,6].should == 6 end it "raises an ArgumentError if passed more than _arity_ arguments when created from a lambda" do -> { @lambda_add.curry(3)[1,2,3,4] }.should raise_error(ArgumentError) - -> { @lambda_add.curry(1)[1,2].curry(3)[3,4,5,6] }.should raise_error(ArgumentError) + -> { @lambda_add.curry(3)[1,2].curry(3)[3,4,5,6] }.should raise_error(ArgumentError) end it "returns Procs with arities of -1 regardless of the value of _arity_" do diff --git a/spec/ruby/core/process/constants_spec.rb b/spec/ruby/core/process/constants_spec.rb index 616c54b8e1b..57cacadef2b 100644 --- a/spec/ruby/core/process/constants_spec.rb +++ b/spec/ruby/core/process/constants_spec.rb @@ -2,69 +2,91 @@ describe "Process::Constants" do platform_is :darwin, :netbsd, :freebsd do - it "has the correct constant values on BSD-like systems" do - Process::WNOHANG.should == 1 - Process::WUNTRACED.should == 2 - Process::PRIO_PROCESS.should == 0 - Process::PRIO_PGRP.should == 1 - Process::PRIO_USER.should == 2 - Process::RLIM_INFINITY.should == 9223372036854775807 - Process::RLIMIT_CPU.should == 0 - Process::RLIMIT_FSIZE.should == 1 - Process::RLIMIT_DATA.should == 2 - Process::RLIMIT_STACK.should == 3 - Process::RLIMIT_CORE.should == 4 - Process::RLIMIT_RSS.should == 5 - Process::RLIMIT_MEMLOCK.should == 6 - Process::RLIMIT_NPROC.should == 7 - Process::RLIMIT_NOFILE.should == 8 + it "are all present on BSD-like systems" do + %i[ + WNOHANG + WUNTRACED + PRIO_PROCESS + PRIO_PGRP + PRIO_USER + RLIM_INFINITY + RLIMIT_CPU + RLIMIT_FSIZE + RLIMIT_DATA + RLIMIT_STACK + RLIMIT_CORE + RLIMIT_RSS + RLIMIT_MEMLOCK + RLIMIT_NPROC + RLIMIT_NOFILE + ].each do |const| + Process.const_defined?(const).should be_true + Process.const_get(const).should be_an_instance_of(Integer) + end end end platform_is :darwin do - it "has the correct constant values on Darwin" do - Process::RLIM_SAVED_MAX.should == 9223372036854775807 - Process::RLIM_SAVED_CUR.should == 9223372036854775807 - Process::RLIMIT_AS.should == 5 + it "are all present on Darwin" do + %i[ + RLIM_SAVED_MAX + RLIM_SAVED_CUR + RLIMIT_AS + ].each do |const| + Process.const_defined?(const).should be_true + Process.const_get(const).should be_an_instance_of(Integer) + end end end platform_is :linux do - it "has the correct constant values on Linux" do - Process::WNOHANG.should == 1 - Process::WUNTRACED.should == 2 - Process::PRIO_PROCESS.should == 0 - Process::PRIO_PGRP.should == 1 - Process::PRIO_USER.should == 2 - Process::RLIMIT_CPU.should == 0 - Process::RLIMIT_FSIZE.should == 1 - Process::RLIMIT_DATA.should == 2 - Process::RLIMIT_STACK.should == 3 - Process::RLIMIT_CORE.should == 4 - Process::RLIMIT_RSS.should == 5 - Process::RLIMIT_NPROC.should == 6 - Process::RLIMIT_NOFILE.should == 7 - Process::RLIMIT_MEMLOCK.should == 8 - Process::RLIMIT_AS.should == 9 - - # These values appear to change according to the platform. - values = [4294967295, 9223372036854775807, 18446744073709551615] - values.include?(Process::RLIM_INFINITY).should be_true - values.include?(Process::RLIM_SAVED_MAX).should be_true - values.include?(Process::RLIM_SAVED_CUR).should be_true + it "are all present on Linux" do + %i[ + WNOHANG + WUNTRACED + PRIO_PROCESS + PRIO_PGRP + PRIO_USER + RLIMIT_CPU + RLIMIT_FSIZE + RLIMIT_DATA + RLIMIT_STACK + RLIMIT_CORE + RLIMIT_RSS + RLIMIT_NPROC + RLIMIT_NOFILE + RLIMIT_MEMLOCK + RLIMIT_AS + RLIM_INFINITY + RLIM_SAVED_MAX + RLIM_SAVED_CUR + ].each do |const| + Process.const_defined?(const).should be_true + Process.const_get(const).should be_an_instance_of(Integer) + end end end platform_is :netbsd, :freebsd do - it "has the correct constant values on NetBSD and FreeBSD" do - Process::RLIMIT_SBSIZE.should == 9 # FIXME: what's it equal? - Process::RLIMIT_AS.should == 10 + it "are all present on NetBSD and FreeBSD" do + %i[ + RLIMIT_SBSIZE + RLIMIT_AS + ].each do |const| + Process.const_defined?(const).should be_true + Process.const_get(const).should be_an_instance_of(Integer) + end end end platform_is :freebsd do - it "has the correct constant values on FreeBSD" do - Process::RLIMIT_NPTS.should == 11 + it "are all present on FreeBSD" do + %i[ + RLIMIT_NPTS + ].each do |const| + Process.const_defined?(const).should be_true + Process.const_get(const).should be_an_instance_of(Integer) + end end end diff --git a/spec/ruby/core/process/daemon_spec.rb b/spec/ruby/core/process/daemon_spec.rb index 70ffd1b3201..36825771d1d 100644 --- a/spec/ruby/core/process/daemon_spec.rb +++ b/spec/ruby/core/process/daemon_spec.rb @@ -2,6 +2,9 @@ require_relative 'fixtures/common' platform_is_not :windows do + # macOS 15 beta is not working this examples + return if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion` + describe :process_daemon_keep_stdio_open_false, shared: true do it "redirects stdout to /dev/null" do @daemon.invoke("keep_stdio_open_false_stdout", @object).should == "" diff --git a/spec/ruby/core/queue/freeze_spec.rb b/spec/ruby/core/queue/freeze_spec.rb new file mode 100644 index 00000000000..ced2cc52dde --- /dev/null +++ b/spec/ruby/core/queue/freeze_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative '../../shared/queue/freeze' + +describe "Queue#freeze" do + it_behaves_like :queue_freeze, :freeze, -> { Queue.new } +end diff --git a/spec/ruby/core/range/step_spec.rb b/spec/ruby/core/range/step_spec.rb index 64ea3de4ed5..31cfd400ccc 100644 --- a/spec/ruby/core/range/step_spec.rb +++ b/spec/ruby/core/range/step_spec.rb @@ -10,44 +10,50 @@ r.step { }.should equal(r) end - it "raises TypeError if step" do - obj = mock("mock") - -> { (1..10).step(obj) { } }.should raise_error(TypeError) - end + ruby_version_is ""..."3.4" do + it "calls #to_int to coerce step to an Integer" do + obj = mock("Range#step") + obj.should_receive(:to_int).and_return(1) - it "calls #to_int to coerce step to an Integer" do - obj = mock("Range#step") - obj.should_receive(:to_int).and_return(1) + (1..2).step(obj) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([1, 2]) + end - (1..2).step(obj) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([1, 2]) - end + it "raises a TypeError if step does not respond to #to_int" do + obj = mock("Range#step non-integer") - it "raises a TypeError if step does not respond to #to_int" do - obj = mock("Range#step non-integer") + -> { (1..2).step(obj) { } }.should raise_error(TypeError) + end - -> { (1..2).step(obj) { } }.should raise_error(TypeError) - end + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock("Range#step non-integer") + obj.should_receive(:to_int).and_return("1") - it "raises a TypeError if #to_int does not return an Integer" do - obj = mock("Range#step non-integer") - obj.should_receive(:to_int).and_return("1") + -> { (1..2).step(obj) { } }.should raise_error(TypeError) + end - -> { (1..2).step(obj) { } }.should raise_error(TypeError) - end + it "raises a TypeError if the first element does not respond to #succ" do + obj = mock("Range#step non-comparable") + obj.should_receive(:<=>).with(obj).and_return(1) - it "coerces the argument to integer by invoking to_int" do - (obj = mock("2")).should_receive(:to_int).and_return(2) - res = [] - (1..10).step(obj) {|x| res << x} - res.should == [1, 3, 5, 7, 9] + -> { (obj..obj).step { |x| x } }.should raise_error(TypeError) + end end - it "raises a TypeError if the first element does not respond to #succ" do - obj = mock("Range#step non-comparable") - obj.should_receive(:<=>).with(obj).and_return(1) + ruby_version_is "3.4" do + it "calls #coerce to coerce step to an Integer" do + obj = mock("Range#step") + obj.should_receive(:coerce).at_least(:once).and_return([1, 2]) + + (1..3).step(obj) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([1, 3]) + end + + it "raises a TypeError if step does not respond to #coerce" do + obj = mock("Range#step non-coercible") - -> { (obj..obj).step { |x| x } }.should raise_error(TypeError) + -> { (1..2).step(obj) { } }.should raise_error(TypeError) + end end it "raises an ArgumentError if step is 0" do @@ -58,8 +64,17 @@ -> { (-1..1).step(0.0) { |x| x } }.should raise_error(ArgumentError) end - it "raises an ArgumentError if step is negative" do - -> { (-1..1).step(-2) { |x| x } }.should raise_error(ArgumentError) + ruby_version_is "3.4" do + it "does not raise an ArgumentError if step is 0 for non-numeric ranges" do + t = Time.utc(2023, 2, 24) + -> { (t..t+1).step(0) { break } }.should_not raise_error(ArgumentError) + end + end + + ruby_version_is ""..."3.4" do + it "raises an ArgumentError if step is negative" do + -> { (-1..1).step(-2) { |x| x } }.should raise_error(ArgumentError) + end end describe "with inclusive end" do @@ -78,6 +93,18 @@ (-2..2).step(1.5) { |x| ScratchPad << x } ScratchPad.recorded.should eql([-2.0, -0.5, 1.0]) end + + ruby_version_is "3.4" do + it "does not iterate if step is negative for forward range" do + (-1..1).step(-1) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([]) + end + + it "iterates backward if step is negative for backward range" do + (1..-1).step(-1) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([1, 0, -1]) + end + end end describe "and Float values" do @@ -162,13 +189,96 @@ -> { ("A".."G").step(2.0) { } }.should raise_error(TypeError) end - it "calls #succ on begin and each element returned by #succ" do - obj = mock("Range#step String start") - obj.should_receive(:<=>).exactly(3).times.and_return(-1, -1, -1, 0) - obj.should_receive(:succ).exactly(2).times.and_return(obj) + ruby_version_is ""..."3.4" do + it "calls #succ on begin and each element returned by #succ" do + obj = mock("Range#step String start") + obj.should_receive(:<=>).exactly(3).times.and_return(-1, -1, -1, 0) + obj.should_receive(:succ).exactly(2).times.and_return(obj) + + (obj..obj).step { |x| ScratchPad << x } + ScratchPad.recorded.should == [obj, obj, obj] + end + end + + ruby_version_is "3.4" do + it "yields String values adjusted by step and less than or equal to end" do + ("A".."AAA").step("A") { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "AA", "AAA"] + end + + it "raises a TypeError when passed an incompatible type step" do + -> { ("A".."G").step([]) { } }.should raise_error(TypeError) + end + + it "calls #+ on begin and each element returned by #+" do + start = mock("Range#step String start") + stop = mock("Range#step String stop") + + mid1 = mock("Range#step String mid1") + mid2 = mock("Range#step String mid2") + + step = mock("Range#step String step") + + # Deciding on the direction of iteration + start.should_receive(:<=>).with(stop).at_least(:twice).and_return(-1) + # Deciding whether the step moves iteration in the right direction + start.should_receive(:<=>).with(mid1).and_return(-1) + # Iteration 1 + start.should_receive(:+).at_least(:once).with(step).and_return(mid1) + # Iteration 2 + mid1.should_receive(:<=>).with(stop).and_return(-1) + mid1.should_receive(:+).with(step).and_return(mid2) + # Iteration 3 + mid2.should_receive(:<=>).with(stop).and_return(0) + + (start..stop).step(step) { |x| ScratchPad << x } + ScratchPad.recorded.should == [start, mid1, mid2] + end + + it "iterates backward if the step is decreasing values, and the range is backward" do + start = mock("Range#step String start") + stop = mock("Range#step String stop") + + mid1 = mock("Range#step String mid1") + mid2 = mock("Range#step String mid2") + + step = mock("Range#step String step") + + # Deciding on the direction of iteration + start.should_receive(:<=>).with(stop).at_least(:twice).and_return(1) + # Deciding whether the step moves iteration in the right direction + start.should_receive(:<=>).with(mid1).and_return(1) + # Iteration 1 + start.should_receive(:+).at_least(:once).with(step).and_return(mid1) + # Iteration 2 + mid1.should_receive(:<=>).with(stop).and_return(1) + mid1.should_receive(:+).with(step).and_return(mid2) + # Iteration 3 + mid2.should_receive(:<=>).with(stop).and_return(0) + + (start..stop).step(step) { |x| ScratchPad << x } + ScratchPad.recorded.should == [start, mid1, mid2] + end + + it "does no iteration of the direction of the range and of the step don't match" do + start = mock("Range#step String start") + stop = mock("Range#step String stop") + + mid1 = mock("Range#step String mid1") + mid2 = mock("Range#step String mid2") + + step = mock("Range#step String step") + + # Deciding on the direction of iteration: stop > start + start.should_receive(:<=>).with(stop).at_least(:twice).and_return(1) + # Deciding whether the step moves iteration in the right direction + # start + step < start, the direction is opposite to the range's + start.should_receive(:+).with(step).and_return(mid1) + start.should_receive(:<=>).with(mid1).and_return(-1) - (obj..obj).step { |x| ScratchPad << x } - ScratchPad.recorded.should == [obj, obj, obj] + (start..stop).step(step) { |x| ScratchPad << x } + ScratchPad.recorded.should == [] + end end end end @@ -266,18 +376,31 @@ end describe "and String values" do - it "yields String values incremented by #succ and less than or equal to end when not passed a step" do - ("A"..."E").step { |x| ScratchPad << x } - ScratchPad.recorded.should == ["A", "B", "C", "D"] - end + ruby_version_is ""..."3.4" do + it "yields String values incremented by #succ and less than or equal to end when not passed a step" do + ("A"..."E").step { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "B", "C", "D"] + end - it "yields String values incremented by #succ called Integer step times" do - ("A"..."G").step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should == ["A", "C", "E"] + it "yields String values incremented by #succ called Integer step times" do + ("A"..."G").step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "C", "E"] + end + + it "raises a TypeError when passed a Float step" do + -> { ("A"..."G").step(2.0) { } }.should raise_error(TypeError) + end end - it "raises a TypeError when passed a Float step" do - -> { ("A"..."G").step(2.0) { } }.should raise_error(TypeError) + ruby_version_is "3.4" do + it "yields String values adjusted by step and less than or equal to end" do + ("A"..."AAA").step("A") { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "AA"] + end + + it "raises a TypeError when passed an incompatible type step" do + -> { ("A".."G").step([]) { } }.should raise_error(TypeError) + end end end end @@ -373,6 +496,22 @@ -> { eval("('A'..)").step(2.0) { } }.should raise_error(TypeError) -> { eval("('A'...)").step(2.0) { } }.should raise_error(TypeError) end + + ruby_version_is "3.4" do + it "yields String values adjusted by step" do + eval("('A'..)").step("A") { |x| break if x > "AAA"; ScratchPad << x } + ScratchPad.recorded.should == ["A", "AA", "AAA"] + + ScratchPad.record [] + eval("('A'...)").step("A") { |x| break if x > "AAA"; ScratchPad << x } + ScratchPad.recorded.should == ["A", "AA", "AAA"] + end + + it "raises a TypeError when passed an incompatible type step" do + -> { eval("('A'..)").step([]) { } }.should raise_error(TypeError) + -> { eval("('A'...)").step([]) { } }.should raise_error(TypeError) + end + end end end @@ -383,15 +522,24 @@ describe "returned Enumerator" do describe "size" do - it "raises a TypeError if step does not respond to #to_int" do - obj = mock("Range#step non-integer") - -> { (1..2).step(obj) }.should raise_error(TypeError) + ruby_version_is ""..."3.4" do + it "raises a TypeError if step does not respond to #to_int" do + obj = mock("Range#step non-integer") + -> { (1..2).step(obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock("Range#step non-integer") + obj.should_receive(:to_int).and_return("1") + -> { (1..2).step(obj) }.should raise_error(TypeError) + end end - it "raises a TypeError if #to_int does not return an Integer" do - obj = mock("Range#step non-integer") - obj.should_receive(:to_int).and_return("1") - -> { (1..2).step(obj) }.should raise_error(TypeError) + ruby_version_is "3.4" do + it "does not raise if step is incompatible" do + obj = mock("Range#step non-integer") + -> { (1..2).step(obj) }.should_not raise_error + end end it "returns the ceil of range size divided by the number of steps" do @@ -431,19 +579,36 @@ (1.0...6.4).step(1.8).size.should == 3 end - it "returns nil with begin and end are String" do - ("A".."E").step(2).size.should == nil - ("A"..."E").step(2).size.should == nil - ("A".."E").step.size.should == nil - ("A"..."E").step.size.should == nil + ruby_version_is ""..."3.4" do + it "returns nil with begin and end are String" do + ("A".."E").step(2).size.should == nil + ("A"..."E").step(2).size.should == nil + ("A".."E").step.size.should == nil + ("A"..."E").step.size.should == nil + end + + it "return nil and not raises a TypeError if the first element does not respond to #succ" do + obj = mock("Range#step non-comparable") + obj.should_receive(:<=>).with(obj).and_return(1) + enum = (obj..obj).step + -> { enum.size }.should_not raise_error + enum.size.should == nil + end end - it "return nil and not raises a TypeError if the first element does not respond to #succ" do - obj = mock("Range#step non-comparable") - obj.should_receive(:<=>).with(obj).and_return(1) - enum = (obj..obj).step - -> { enum.size }.should_not raise_error - enum.size.should == nil + ruby_version_is "3.4" do + it "returns nil with begin and end are String" do + ("A".."E").step("A").size.should == nil + ("A"..."E").step("A").size.should == nil + end + + it "return nil and not raises a TypeError if the first element is not of compatible type" do + obj = mock("Range#step non-comparable") + obj.should_receive(:<=>).with(obj).and_return(1) + enum = (obj..obj).step(obj) + -> { enum.size }.should_not raise_error + enum.size.should == nil + end end end @@ -470,22 +635,48 @@ (1..).step(2).take(3).should == [1, 3, 5] end - it "returns an instance of Enumerator when begin is not numeric" do - ("a"..).step.class.should == Enumerator - ("a"..).step(2).take(3).should == %w[a c e] + ruby_version_is ""..."3.4" do + it "returns an instance of Enumerator when begin is not numeric" do + ("a"..).step.class.should == Enumerator + ("a"..).step(2).take(3).should == %w[a c e] + end + end + + ruby_version_is "3.4" do + it "returns an instance of Enumerator when begin is not numeric" do + ("a"..).step("a").class.should == Enumerator + ("a"..).step("a").take(3).should == %w[a aa aaa] + end end end context "when range is beginless and endless" do - it "returns an instance of Enumerator" do - Range.new(nil, nil).step.class.should == Enumerator + ruby_version_is ""..."3.4" do + it "returns an instance of Enumerator" do + Range.new(nil, nil).step.class.should == Enumerator + end + end + + ruby_version_is "3.4" do + it "raises an ArgumentError" do + -> { Range.new(nil, nil).step(1) }.should raise_error(ArgumentError) + end end end context "when begin and end are not numerics" do - it "returns an instance of Enumerator" do - ("a".."z").step.class.should == Enumerator - ("a".."z").step(3).take(4).should == %w[a d g j] + ruby_version_is ""..."3.4" do + it "returns an instance of Enumerator" do + ("a".."z").step.class.should == Enumerator + ("a".."z").step(3).take(4).should == %w[a d g j] + end + end + + ruby_version_is "3.4" do + it "returns an instance of Enumerator" do + ("a".."z").step("a").class.should == Enumerator + ("a".."z").step("a").take(4).should == %w[a aa aaa aaaa] + end end end end diff --git a/spec/ruby/core/regexp/shared/new.rb b/spec/ruby/core/regexp/shared/new.rb index 7052bcab638..3ca50d5a478 100644 --- a/spec/ruby/core/regexp/shared/new.rb +++ b/spec/ruby/core/regexp/shared/new.rb @@ -460,6 +460,10 @@ def obj.to_int() ScratchPad.record(:called) end -> { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode list: /\\u{}/"))) end + it "raises a RegexpError if the \\u{} escape contains non hexadecimal digits" do + -> { Regexp.send(@method, "\\" + "u{abcX}") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode list: /\\u{abcX}/"))) + end + it "raises a RegexpError if more than six hexadecimal digits are given" do -> { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode range: /\\u{0ffffff}/"))) end diff --git a/spec/ruby/core/regexp/shared/quote.rb b/spec/ruby/core/regexp/shared/quote.rb index b5ecc35f042..48179444f09 100644 --- a/spec/ruby/core/regexp/shared/quote.rb +++ b/spec/ruby/core/regexp/shared/quote.rb @@ -2,8 +2,8 @@ describe :regexp_quote, shared: true do it "escapes any characters with special meaning in a regular expression" do - Regexp.send(@method, '\*?{}.+^[]()- ').should == '\\\\\*\?\{\}\.\+\^\[\]\(\)\-\\ ' - Regexp.send(@method, "\*?{}.+^[]()- ").should == '\\*\\?\\{\\}\\.\\+\\^\\[\\]\\(\\)\\-\\ ' + Regexp.send(@method, '\*?{}.+^$[]()- ').should == '\\\\\*\?\{\}\.\+\^\$\[\]\(\)\-\\ ' + Regexp.send(@method, "\*?{}.+^$[]()- ").should == '\\*\\?\\{\\}\\.\\+\\^\\$\\[\\]\\(\\)\\-\\ ' Regexp.send(@method, '\n\r\f\t').should == '\\\\n\\\\r\\\\f\\\\t' Regexp.send(@method, "\n\r\f\t").should == '\\n\\r\\f\\t' end diff --git a/spec/ruby/core/sizedqueue/freeze_spec.rb b/spec/ruby/core/sizedqueue/freeze_spec.rb new file mode 100644 index 00000000000..98f01cae2f8 --- /dev/null +++ b/spec/ruby/core/sizedqueue/freeze_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative '../../shared/queue/freeze' + +describe "SizedQueue#freeze" do + it_behaves_like :queue_freeze, :freeze, -> { SizedQueue.new(1) } +end diff --git a/spec/ruby/core/string/append_as_bytes_spec.rb b/spec/ruby/core/string/append_as_bytes_spec.rb new file mode 100644 index 00000000000..0e1d09558bd --- /dev/null +++ b/spec/ruby/core/string/append_as_bytes_spec.rb @@ -0,0 +1,46 @@ +require_relative '../../spec_helper' + +describe "String#append_bytes" do + ruby_version_is "3.4" do + it "doesn't allow to mutate frozen strings" do + str = "hello".freeze + -> { str.append_as_bytes("\xE2\x82") }.should raise_error(FrozenError) + end + + it "allows creating broken strings" do + str = +"hello" + str.append_as_bytes("\xE2\x82") + str.valid_encoding?.should == false + + str.append_as_bytes("\xAC") + str.valid_encoding?.should == true + + str = "abc".encode(Encoding::UTF_32LE) + str.append_as_bytes("def") + str.encoding.should == Encoding::UTF_32LE + str.valid_encoding?.should == false + end + + it "never changes the receiver encoding" do + str = "".b + str.append_as_bytes("€") + str.encoding.should == Encoding::BINARY + end + + it "accepts variadic String or Integer arguments" do + str = "hello".b + str.append_as_bytes("\xE2\x82", 12, 43, "\xAC") + str.encoding.should == Encoding::BINARY + str.should == "hello\xE2\x82\f+\xAC".b + end + + it "only accepts strings or integers, and doesn't attempt to cast with #to_str or #to_int" do + to_str = mock("to_str") + to_str.should_not_receive(:to_str) + to_str.should_not_receive(:to_int) + + str = +"hello" + -> { str.append_as_bytes(to_str) }.should raise_error(TypeError, "wrong argument type MockObject (expected String or Integer)") + end + end +end diff --git a/spec/ruby/core/string/byteslice_spec.rb b/spec/ruby/core/string/byteslice_spec.rb index 5b1027f4a57..9fe504aeb1c 100644 --- a/spec/ruby/core/string/byteslice_spec.rb +++ b/spec/ruby/core/string/byteslice_spec.rb @@ -17,7 +17,7 @@ it_behaves_like :string_slice_range, :byteslice end -describe "String#byteslice on on non ASCII strings" do +describe "String#byteslice on non ASCII strings" do it "returns byteslice of unicode strings" do "\u3042".byteslice(1).should == "\x81".dup.force_encoding("UTF-8") "\u3042".byteslice(1, 2).should == "\x81\x82".dup.force_encoding("UTF-8") diff --git a/spec/ruby/core/string/bytesplice_spec.rb b/spec/ruby/core/string/bytesplice_spec.rb index 967edcba298..5cb3b2c1581 100644 --- a/spec/ruby/core/string/bytesplice_spec.rb +++ b/spec/ruby/core/string/bytesplice_spec.rb @@ -58,6 +58,79 @@ -> { s.bytesplice(2, 1, "xxx") }.should raise_error(FrozenError, "can't modify frozen String: \"hello\"") end end + + ruby_version_is "3.3" do + it "raises IndexError when str_index is less than -bytesize" do + -> { "hello".bytesplice(2, 1, "HELLO", -6, 0) }.should raise_error(IndexError, "index -6 out of string") + end + + it "raises IndexError when str_index is greater than bytesize" do + -> { "hello".bytesplice(2, 1, "HELLO", 6, 0) }.should raise_error(IndexError, "index 6 out of string") + end + + it "raises IndexError for negative str length" do + -> { "abc".bytesplice(0, 1, "", 0, -2) }.should raise_error(IndexError, "negative length -2") + end + + it "replaces with integer str indices" do + "hello".bytesplice(1, 2, "HELLO", -5, 0).should == "hlo" + "hello".bytesplice(1, 2, "HELLO", 0, 0).should == "hlo" + "hello".bytesplice(1, 2, "HELLO", 0, 1).should == "hHlo" + "hello".bytesplice(1, 2, "HELLO", 0, 5).should == "hHELLOlo" + "hello".bytesplice(1, 2, "HELLO", 0, 6).should == "hHELLOlo" + end + + it "raises RangeError when str range left boundary is less than -bytesize" do + -> { "hello".bytesplice(0..1, "HELLO", -6...-6) }.should raise_error(RangeError, "-6...-6 out of range") + end + + it "replaces with str ranges" do + "hello".bytesplice(1..2, "HELLO", -5...-5).should == "hlo" + "hello".bytesplice(1..2, "HELLO", 0...0).should == "hlo" + "hello".bytesplice(1..2, "HELLO", 0..0).should == "hHlo" + "hello".bytesplice(1..2, "HELLO", 0...1).should == "hHlo" + "hello".bytesplice(1..2, "HELLO", 0..1).should == "hHElo" + "hello".bytesplice(1..2, "HELLO", 0..-1).should == "hHELLOlo" + "hello".bytesplice(1..2, "HELLO", 0...5).should == "hHELLOlo" + "hello".bytesplice(1..2, "HELLO", 0...6).should == "hHELLOlo" + end + + it "raises ArgumentError when integer str index is provided without str length argument" do + -> { "hello".bytesplice(0, 1, "xxx", 0) }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 2, 3, or 5)") + end + + it "replaces on an empty string with str index/length" do + "".bytesplice(0, 0, "", 0, 0).should == "" + "".bytesplice(0, 0, "xxx", 0, 1).should == "x" + end + + it "mutates self with substring and str index/length" do + s = "hello" + s.bytesplice(2, 1, "xxx", 1, 2).should.equal?(s) + s.should.eql?("hexxlo") + end + + it "raises when string is frozen and str index/length" do + s = "hello".freeze + -> { s.bytesplice(2, 1, "xxx", 0, 1) }.should raise_error(FrozenError, "can't modify frozen String: \"hello\"") + end + + it "replaces on an empty string with str range" do + "".bytesplice(0..0, "", 0..0).should == "" + "".bytesplice(0..0, "xyz", 0..1).should == "xy" + end + + it "mutates self with substring and str range" do + s = "hello" + s.bytesplice(2..2, "xyz", 1..2).should.equal?(s) + s.should.eql?("heyzlo") + end + + it "raises when string is frozen and str range" do + s = "hello".freeze + -> { s.bytesplice(2..2, "yzx", 0..1) }.should raise_error(FrozenError, "can't modify frozen String: \"hello\"") + end + end end describe "String#bytesplice with multibyte characters" do @@ -131,4 +204,95 @@ result.encoding.should == Encoding::UTF_8 end end + + ruby_version_is "3.3" do + it "raises IndexError when str_index is out of byte size boundary" do + -> { "こんにちは".bytesplice(3, 3, "こんにちは", -16, 0) }.should raise_error(IndexError, "index -16 out of string") + end + + it "raises IndexError when str_index is not on a codepoint boundary" do + -> { "こんにちは".bytesplice(3, 3, "こんにちは", 1, 0) }.should raise_error(IndexError, "offset 1 does not land on character boundary") + end + + it "raises IndexError when str_length is not matching the codepoint boundary" do + -> { "こんにちは".bytesplice(3, 3, "こんにちは", 0, 1) }.should raise_error(IndexError, "offset 1 does not land on character boundary") + -> { "こんにちは".bytesplice(3, 3, "こんにちは", 0, 2) }.should raise_error(IndexError, "offset 2 does not land on character boundary") + end + + it "replaces with integer str indices" do + "こんにちは".bytesplice(3, 3, "こんにちは", -15, 0).should == "こにちは" + "こんにちは".bytesplice(3, 3, "こんにちは", 0, 0).should == "こにちは" + "こんにちは".bytesplice(3, 3, "こんにちは", 0, 3).should == "ここにちは" + "こんにちは".bytesplice(3, 3, "はは", 3, 3).should == "こはにちは" + "こんにちは".bytesplice(3, 3, "こんにちは", 15, 0).should == "こにちは" + end + + it "replaces with str range" do + "こんにちは".bytesplice(0..2, "こんにちは", -15...-16).should == "んにちは" + "こんにちは".bytesplice(0..2, "こんにちは", 0...0).should == "んにちは" + "こんにちは".bytesplice(0..2, "こんにちは", 3..5).should == "んんにちは" + "こんにちは".bytesplice(0..2, "こんにちは", 3...6).should == "んんにちは" + "こんにちは".bytesplice(0..2, "こんにちは", 3..8).should == "んにんにちは" + "こんにちは".bytesplice(0..2, "こんにちは", 0..-1).should == "こんにちはんにちは" + "こんにちは".bytesplice(0..2, "こんにちは", 0...15).should == "こんにちはんにちは" + "こんにちは".bytesplice(0..2, "こんにちは", 0...18).should == "こんにちはんにちは" + end + + it "treats negative length for str range as 0" do + "こんにちは".bytesplice(0..2, "こんにちは", 0...-100).should == "んにちは" + "こんにちは".bytesplice(0..2, "こんにちは", 3...-100).should == "んにちは" + "こんにちは".bytesplice(0..2, "こんにちは", -15...-100).should == "んにちは" + end + + it "raises when ranges not match codepoint boundaries in str" do + -> { "こんにちは".bytesplice(3...3, "こ", 0..0) }.should raise_error(IndexError, "offset 1 does not land on character boundary") + -> { "こんにちは".bytesplice(3...3, "こ", 0..1) }.should raise_error(IndexError, "offset 2 does not land on character boundary") + # Begin is incorrect + -> { "こんにちは".bytesplice(3...3, "こんにちは", -4..-1) }.should raise_error(IndexError, "offset 11 does not land on character boundary") + -> { "こんにちは".bytesplice(3...3, "こんにちは", -5..-1) }.should raise_error(IndexError, "offset 10 does not land on character boundary") + # End is incorrect + -> { "こんにちは".bytesplice(3...3, "こんにちは", -3..-2) }.should raise_error(IndexError, "offset 14 does not land on character boundary") + -> { "こんにちは".bytesplice(3...3, "こんにちは", -3..-3) }.should raise_error(IndexError, "offset 13 does not land on character boundary") + end + + it "deals with a different encoded argument with str index/length" do + s = "こんにちは" + s.encoding.should == Encoding::UTF_8 + sub = "goodbye" + sub.force_encoding(Encoding::US_ASCII) + + result = s.bytesplice(3, 3, sub, 0, 3) + result.should == "こgooにちは" + result.encoding.should == Encoding::UTF_8 + + s = "hello" + s.force_encoding(Encoding::US_ASCII) + sub = "こんにちは" + sub.encoding.should == Encoding::UTF_8 + + result = s.bytesplice(1, 2, sub, 3, 3) + result.should == "hんlo" + result.encoding.should == Encoding::UTF_8 + end + + it "deals with a different encoded argument with str range" do + s = "こんにちは" + s.encoding.should == Encoding::UTF_8 + sub = "goodbye" + sub.force_encoding(Encoding::US_ASCII) + + result = s.bytesplice(3..5, sub, 0..2) + result.should == "こgooにちは" + result.encoding.should == Encoding::UTF_8 + + s = "hello" + s.force_encoding(Encoding::US_ASCII) + sub = "こんにちは" + sub.encoding.should == Encoding::UTF_8 + + result = s.bytesplice(1..2, sub, 3..5) + result.should == "hんlo" + result.encoding.should == Encoding::UTF_8 + end + end end diff --git a/spec/ruby/core/string/modulo_spec.rb b/spec/ruby/core/string/modulo_spec.rb index 9045afa263a..8e3853551f3 100644 --- a/spec/ruby/core/string/modulo_spec.rb +++ b/spec/ruby/core/string/modulo_spec.rb @@ -570,7 +570,7 @@ def universal.to_f() 0.0 end ("%1$p" % [10, 5]).should == "10" ("%-22p" % 10).should == "10 " ("%*p" % [10, 10]).should == " 10" - ("%p" % {capture: 1}).should == "{:capture=>1}" + ("%p" % {capture: 1}).should == {capture: 1}.inspect ("%p" % "str").should == "\"str\"" end @@ -749,6 +749,9 @@ def obj.to_s() "obj" end (format % "-10.4e-20").should == (format % -10.4e-20) (format % ".5").should == (format % 0.5) (format % "-.5").should == (format % -0.5) + ruby_bug("#20705", ""..."3.4") do + (format % "10.").should == (format % 10) + end # Something's strange with this spec: # it works just fine in individual mode, but not when run as part of a group (format % "10_1_0.5_5_5").should == (format % 1010.555) @@ -758,7 +761,6 @@ def obj.to_s() "obj" end -> { format % "" }.should raise_error(ArgumentError) -> { format % "x" }.should raise_error(ArgumentError) -> { format % "." }.should raise_error(ArgumentError) - -> { format % "10." }.should raise_error(ArgumentError) -> { format % "5x" }.should raise_error(ArgumentError) -> { format % "0b1" }.should raise_error(ArgumentError) -> { format % "10e10.5" }.should raise_error(ArgumentError) diff --git a/spec/ruby/core/string/unpack/l_spec.rb b/spec/ruby/core/string/unpack/l_spec.rb index 18bb68b8d06..0adb567eca6 100644 --- a/spec/ruby/core/string/unpack/l_spec.rb +++ b/spec/ruby/core/string/unpack/l_spec.rb @@ -14,7 +14,7 @@ it_behaves_like :string_unpack_32bit_be_unsigned, 'L>' end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do describe "with modifier '<' and '_'" do it_behaves_like :string_unpack_32bit_le, 'L<_' it_behaves_like :string_unpack_32bit_le, 'L_<' @@ -44,7 +44,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "with modifier '<' and '_'" do it_behaves_like :string_unpack_64bit_le, 'L<_' it_behaves_like :string_unpack_64bit_le, 'L_<' @@ -86,7 +86,7 @@ it_behaves_like :string_unpack_32bit_be_signed, 'l>' end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do describe "with modifier '<' and '_'" do it_behaves_like :string_unpack_32bit_le, 'l<_' it_behaves_like :string_unpack_32bit_le, 'l_<' @@ -116,7 +116,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "with modifier '<' and '_'" do it_behaves_like :string_unpack_64bit_le, 'l<_' it_behaves_like :string_unpack_64bit_le, 'l_<' @@ -160,7 +160,7 @@ it_behaves_like :string_unpack_32bit_le_signed, 'l' end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do describe "String#unpack with format 'L' with modifier '_'" do it_behaves_like :string_unpack_32bit_le, 'L_' it_behaves_like :string_unpack_32bit_le_unsigned, 'L_' @@ -182,7 +182,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "String#unpack with format 'L' with modifier '_'" do it_behaves_like :string_unpack_64bit_le, 'L_' it_behaves_like :string_unpack_64bit_le_unsigned, 'L_' @@ -218,7 +218,7 @@ it_behaves_like :string_unpack_32bit_be_signed, 'l' end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do describe "String#unpack with format 'L' with modifier '_'" do it_behaves_like :string_unpack_32bit_be, 'L_' it_behaves_like :string_unpack_32bit_be_unsigned, 'L_' @@ -240,7 +240,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "String#unpack with format 'L' with modifier '_'" do it_behaves_like :string_unpack_64bit_be, 'L_' it_behaves_like :string_unpack_64bit_be_unsigned, 'L_' diff --git a/spec/ruby/core/thread/element_reference_spec.rb b/spec/ruby/core/thread/element_reference_spec.rb index 85280cb2871..fde9d1f4401 100644 --- a/spec/ruby/core/thread/element_reference_spec.rb +++ b/spec/ruby/core/thread/element_reference_spec.rb @@ -37,6 +37,17 @@ t2["value"].should == 2 end + it "converts a key that is neither String nor Symbol with #to_str" do + key = mock('value') + key.should_receive(:to_str).and_return('value') + + th = Thread.new do + Thread.current[:value] = 1 + end.join + + th[key].should == 1 + end + it "raises exceptions on the wrong type of keys" do -> { Thread.current[nil] }.should raise_error(TypeError) -> { Thread.current[5] }.should raise_error(TypeError) diff --git a/spec/ruby/core/thread/element_set_spec.rb b/spec/ruby/core/thread/element_set_spec.rb index c7498f7ac9d..f2051773041 100644 --- a/spec/ruby/core/thread/element_set_spec.rb +++ b/spec/ruby/core/thread/element_set_spec.rb @@ -12,10 +12,33 @@ th.freeze -> { th[:foo] = "bar" - }.should raise_error(FrozenError, /frozen/) + }.should raise_error(FrozenError, "can't modify frozen thread locals") end.join end + it "accepts Strings and Symbols" do + t1 = Thread.new do + Thread.current[:value] = 1 + end.join + t2 = Thread.new do + Thread.current["value"] = 2 + end.join + + t1[:value].should == 1 + t2[:value].should == 2 + end + + it "converts a key that is neither String nor Symbol with #to_str" do + key = mock('value') + key.should_receive(:to_str).and_return('value') + + th = Thread.new do + Thread.current[key] = 1 + end.join + + th[:value].should == 1 + end + it "raises exceptions on the wrong type of keys" do -> { Thread.current[nil] = true }.should raise_error(TypeError) -> { Thread.current[5] = true }.should raise_error(TypeError) diff --git a/spec/ruby/core/thread/group_spec.rb b/spec/ruby/core/thread/group_spec.rb index 59f5ac37c8b..d0d4704b66f 100644 --- a/spec/ruby/core/thread/group_spec.rb +++ b/spec/ruby/core/thread/group_spec.rb @@ -1,5 +1,16 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' + describe "Thread#group" do - it "needs to be reviewed for spec completeness" + it "returns the default thread group for the main thread" do + Thread.main.group.should == ThreadGroup::Default + end + + it "returns the thread group explicitly set for this thread" do + thread = Thread.new { nil } + thread_group = ThreadGroup.new + thread_group.add(thread) + thread.group.should == thread_group + ensure + thread.join if thread + end end diff --git a/spec/ruby/core/thread/key_spec.rb b/spec/ruby/core/thread/key_spec.rb index 6940cf5f282..339fa98f533 100644 --- a/spec/ruby/core/thread/key_spec.rb +++ b/spec/ruby/core/thread/key_spec.rb @@ -16,6 +16,13 @@ @th.key?(:stanley.to_s).should == false end + it "converts a key that is neither String nor Symbol with #to_str" do + key = mock('key') + key.should_receive(:to_str).and_return('oliver') + + @th.key?(key).should == true + end + it "raises exceptions on the wrong type of keys" do -> { Thread.current.key? nil }.should raise_error(TypeError) -> { Thread.current.key? 5 }.should raise_error(TypeError) diff --git a/spec/ruby/core/thread/thread_variable_get_spec.rb b/spec/ruby/core/thread/thread_variable_get_spec.rb index 67017771fdf..1ea34cf2b31 100644 --- a/spec/ruby/core/thread/thread_variable_get_spec.rb +++ b/spec/ruby/core/thread/thread_variable_get_spec.rb @@ -41,13 +41,20 @@ @t.thread_variable_get(:a).should be_nil end - it "does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do - @t.thread_variable_get(123).should be_nil + it "raises a TypeError if the key is neither Symbol nor String when thread variables are already set" do + @t.thread_variable_set(:a, 49) + -> { @t.thread_variable_get(123) }.should raise_error(TypeError, /123 is not a symbol/) end - it "does not try to convert the key with #to_sym" do - key = mock('key') - key.should_not_receive(:to_sym) - @t.thread_variable_get(key).should be_nil + ruby_version_is '3.4' do + it "raises a TypeError if the key is neither Symbol nor String when no thread variables are set" do + -> { @t.thread_variable_get(123) }.should raise_error(TypeError, /123 is not a symbol/) + end + + it "raises a TypeError if the key is neither Symbol nor String without calling #to_sym" do + key = mock('key') + key.should_not_receive(:to_sym) + -> { @t.thread_variable_get(key) }.should raise_error(TypeError, /#{Regexp.escape(key.inspect)} is not a symbol/) + end end end diff --git a/spec/ruby/core/thread/thread_variable_set_spec.rb b/spec/ruby/core/thread/thread_variable_set_spec.rb index c262a6614ef..eadee76afba 100644 --- a/spec/ruby/core/thread/thread_variable_set_spec.rb +++ b/spec/ruby/core/thread/thread_variable_set_spec.rb @@ -51,12 +51,12 @@ end it "raises a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do - -> { @t.thread_variable_set(123, 1) }.should raise_error(TypeError, '123 is not a symbol') + -> { @t.thread_variable_set(123, 1) }.should raise_error(TypeError, /123 is not a symbol/) end it "does not try to convert the key with #to_sym" do key = mock('key') key.should_not_receive(:to_sym) - -> { @t.thread_variable_set(key, 42) }.should raise_error(TypeError, "#{key.inspect} is not a symbol") + -> { @t.thread_variable_set(key, 42) }.should raise_error(TypeError, /#{Regexp.quote(key.inspect)} is not a symbol/) end end diff --git a/spec/ruby/core/thread/thread_variable_spec.rb b/spec/ruby/core/thread/thread_variable_spec.rb index d64e6ec63dc..1b021e94044 100644 --- a/spec/ruby/core/thread/thread_variable_spec.rb +++ b/spec/ruby/core/thread/thread_variable_spec.rb @@ -41,13 +41,20 @@ @t.thread_variable?(:a).should be_false end - it "does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do - @t.thread_variable?(123).should be_false + it "raises a TypeError if the key is neither Symbol nor String when thread variables are already set" do + @t.thread_variable_set(:a, 49) + -> { @t.thread_variable?(123) }.should raise_error(TypeError, /123 is not a symbol/) end - it "does not try to convert the key with #to_sym" do - key = mock('key') - key.should_not_receive(:to_sym) - @t.thread_variable?(key).should be_false + ruby_version_is '3.4' do + it "raises a TypeError if the key is neither Symbol nor String when no thread variables are set" do + -> { @t.thread_variable?(123) }.should raise_error(TypeError, /123 is not a symbol/) + end + + it "raises a TypeError if the key is neither Symbol nor String without calling #to_sym" do + key = mock('key') + key.should_not_receive(:to_sym) + -> { @t.thread_variable?(key) }.should raise_error(TypeError, /#{Regexp.escape(key.inspect)} is not a symbol/) + end end end diff --git a/spec/ruby/core/thread/wakeup_spec.rb b/spec/ruby/core/thread/wakeup_spec.rb index ec1339a7a72..da5dfea3772 100644 --- a/spec/ruby/core/thread/wakeup_spec.rb +++ b/spec/ruby/core/thread/wakeup_spec.rb @@ -4,16 +4,4 @@ describe "Thread#wakeup" do it_behaves_like :thread_wakeup, :wakeup - - it "sleeps with nanosecond precision" do - start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - 100.times do - sleep(0.0001) - end - end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - - actual_duration = end_time - start_time - (actual_duration > 0.01).should == true # 100 * 0.0001 => 0.01 - (actual_duration < 0.03).should == true - end end diff --git a/spec/ruby/core/time/iso8601_spec.rb b/spec/ruby/core/time/iso8601_spec.rb new file mode 100644 index 00000000000..ad60c3bb322 --- /dev/null +++ b/spec/ruby/core/time/iso8601_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/xmlschema' + +describe "Time#iso8601" do + it_behaves_like :time_xmlschema, :iso8601 +end diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb index d686355270c..fa5b5c56d60 100644 --- a/spec/ruby/core/time/new_spec.rb +++ b/spec/ruby/core/time/new_spec.rb @@ -625,15 +625,21 @@ def obj.to_int; 3; end -> { Time.new("2020-12-25 00:56:17 +23:59:60") - }.should raise_error(ArgumentError, "utc_offset out of range") + }.should raise_error(ArgumentError, /utc_offset/) -> { Time.new("2020-12-25 00:56:17 +24:00") - }.should raise_error(ArgumentError, "utc_offset out of range") + }.should raise_error(ArgumentError, /utc_offset/) -> { Time.new("2020-12-25 00:56:17 +23:61") - }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +23:61') + }.should raise_error(ArgumentError, /utc_offset/) + + ruby_bug '#20797', ''...'3.4' do + -> { + Time.new("2020-12-25 00:56:17 +00:23:61") + }.should raise_error(ArgumentError, /utc_offset/) + end end it "raises ArgumentError if string has not ascii-compatible encoding" do diff --git a/spec/ruby/core/time/shared/xmlschema.rb b/spec/ruby/core/time/shared/xmlschema.rb new file mode 100644 index 00000000000..d68c18df364 --- /dev/null +++ b/spec/ruby/core/time/shared/xmlschema.rb @@ -0,0 +1,31 @@ +describe :time_xmlschema, shared: true do + ruby_version_is "3.4" do + it "generates ISO-8601 strings in Z for UTC times" do + t = Time.utc(1985, 4, 12, 23, 20, 50, 521245) + t.send(@method).should == "1985-04-12T23:20:50Z" + t.send(@method, 2).should == "1985-04-12T23:20:50.52Z" + t.send(@method, 9).should == "1985-04-12T23:20:50.521245000Z" + end + + it "generates ISO-8601 string with timeone offset for non-UTC times" do + t = Time.new(1985, 4, 12, 23, 20, 50, "+02:00") + t.send(@method).should == "1985-04-12T23:20:50+02:00" + t.send(@method, 2).should == "1985-04-12T23:20:50.00+02:00" + end + + it "year is always at least 4 digits" do + t = Time.utc(12, 4, 12) + t.send(@method).should == "0012-04-12T00:00:00Z" + end + + it "year can be more than 4 digits" do + t = Time.utc(40_000, 4, 12) + t.send(@method).should == "40000-04-12T00:00:00Z" + end + + it "year can be negative" do + t = Time.utc(-2000, 4, 12) + t.send(@method).should == "-2000-04-12T00:00:00Z" + end + end +end diff --git a/spec/ruby/core/time/xmlschema_spec.rb b/spec/ruby/core/time/xmlschema_spec.rb new file mode 100644 index 00000000000..bdf1dc7923e --- /dev/null +++ b/spec/ruby/core/time/xmlschema_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/xmlschema' + +describe "Time#xmlschema" do + it_behaves_like :time_xmlschema, :xmlschema +end diff --git a/spec/ruby/core/tracepoint/inspect_spec.rb b/spec/ruby/core/tracepoint/inspect_spec.rb index d0e633c49b0..6cc2ebe2434 100644 --- a/spec/ruby/core/tracepoint/inspect_spec.rb +++ b/spec/ruby/core/tracepoint/inspect_spec.rb @@ -67,7 +67,7 @@ def trace_point_spec_test_return end trace_point_spec_test_return end - ruby_version_is("3.4") { line = "(?:#{line}|#{line-1})" } + ruby_version_is("3.4") { line -= 1 } inspect.should =~ /\A#\z/ end diff --git a/spec/ruby/core/warning/warn_spec.rb b/spec/ruby/core/warning/warn_spec.rb index 1c21fe29e06..572885c2b4e 100644 --- a/spec/ruby/core/warning/warn_spec.rb +++ b/spec/ruby/core/warning/warn_spec.rb @@ -121,7 +121,7 @@ def Warning.warn(msg) end end - ruby_bug '#19530', ''...'3.4' do + ruby_bug '#20573', ''...'3.4' do it "isn't called by Kernel.warn when category is :deprecated but Warning[:deprecated] is false" do warn_deprecated = Warning[:deprecated] begin diff --git a/spec/ruby/fixtures/constants.rb b/spec/ruby/fixtures/constants.rb index ffe45fb1f67..7f0b88daab7 100644 --- a/spec/ruby/fixtures/constants.rb +++ b/spec/ruby/fixtures/constants.rb @@ -10,7 +10,7 @@ # variety in class and module configurations, including hierarchy, # containment, inclusion, singletons and toplevel. # -# Constants are numbered for for uniqueness. The CS_ prefix is uniformly used +# Constants are numbered for uniqueness. The CS_ prefix is uniformly used # and is to minimize clashes with other toplevel constants (see e.g. ModuleA # which is included in Object). Constant values are symbols. A numbered suffix # is used to distinguish constants with the same name defined in different diff --git a/spec/ruby/language/fixtures/private.rb b/spec/ruby/language/fixtures/private.rb index 96f73cea3fd..da3e0a97f9e 100644 --- a/spec/ruby/language/fixtures/private.rb +++ b/spec/ruby/language/fixtures/private.rb @@ -43,17 +43,17 @@ def foo end end - class E - include D - end - - class G - def foo - "foo" - end - end - - class H < A - private :foo - end + class E + include D + end + + class G + def foo + "foo" + end + end + + class H < A + private :foo + end end diff --git a/spec/ruby/language/fixtures/send.rb b/spec/ruby/language/fixtures/send.rb index 918241e1711..5d1d9da2143 100644 --- a/spec/ruby/language/fixtures/send.rb +++ b/spec/ruby/language/fixtures/send.rb @@ -43,9 +43,9 @@ class PrivateSetter attr_writer :foo private :foo= - def call_self_foo_equals(value) - self.foo = value - end + def call_self_foo_equals(value) + self.foo = value + end def call_self_foo_equals_masgn(value) a, self.foo = 1, value diff --git a/spec/ruby/language/pattern_matching_spec.rb b/spec/ruby/language/pattern_matching_spec.rb index 52608b48bea..94432b1fa00 100644 --- a/spec/ruby/language/pattern_matching_spec.rb +++ b/spec/ruby/language/pattern_matching_spec.rb @@ -232,11 +232,12 @@ end }.should raise_error(NoMatchingPatternError, /\[0, 1\]/) + error_pattern = ruby_version_is("3.4") ? /\{a: 0, b: 1\}/ : /\{:a=>0, :b=>1\}/ -> { case {a: 0, b: 1} in a: 1, b: 1 end - }.should raise_error(NoMatchingPatternError, /\{:a=>0, :b=>1\}/) + }.should raise_error(NoMatchingPatternError, error_pattern) end it "raises NoMatchingPatternError if no pattern matches and evaluates the expression only once" do diff --git a/spec/ruby/language/precedence_spec.rb b/spec/ruby/language/precedence_spec.rb index c5adcca2c04..5e606c16d86 100644 --- a/spec/ruby/language/precedence_spec.rb +++ b/spec/ruby/language/precedence_spec.rb @@ -294,14 +294,14 @@ class FalseClass; undef_method :=~; end -> { eval("1...2...3") }.should raise_error(SyntaxError) end - it ".. ... have higher precedence than ? :" do - # Use variables to avoid warnings - from = 1 - to = 2 - # These are flip-flop, not Range instances - (from..to ? 3 : 4).should == 3 - (from...to ? 3 : 4).should == 3 - end + it ".. ... have higher precedence than ? :" do + # Use variables to avoid warnings + from = 1 + to = 2 + # These are flip-flop, not Range instances + (from..to ? 3 : 4).should == 3 + (from...to ? 3 : 4).should == 3 + end it "? : is right-associative" do (true ? 2 : 3 ? 4 : 5).should == 2 diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb index ac28f1e8a0f..cc231e341ed 100644 --- a/spec/ruby/language/predefined_spec.rb +++ b/spec/ruby/language/predefined_spec.rb @@ -243,6 +243,16 @@ def test(arg) end describe "Predefined global $!" do + it "is Fiber-local" do + Fiber.new do + raise "hi" + rescue + Fiber.yield + end.resume + + $!.should == nil + end + # See http://jira.codehaus.org/browse/JRUBY-5550 it "remains nil after a failed core class \"checked\" coercion against a class that defines method_missing" do $!.should == nil diff --git a/spec/ruby/language/regexp_spec.rb b/spec/ruby/language/regexp_spec.rb index 89d09148072..dbf341b19ea 100644 --- a/spec/ruby/language/regexp_spec.rb +++ b/spec/ruby/language/regexp_spec.rb @@ -62,7 +62,7 @@ end end - it "supports non-paired delimiters delimiters with %r" do + it "supports non-paired delimiters with %r" do LanguageSpecs.non_paired_delimiters.each do |c| eval("%r#{c} foo #{c}").should == / foo / end diff --git a/spec/ruby/language/super_spec.rb b/spec/ruby/language/super_spec.rb index a98b3b3091e..7d9e896d8b1 100644 --- a/spec/ruby/language/super_spec.rb +++ b/spec/ruby/language/super_spec.rb @@ -70,7 +70,7 @@ SuperSpecs::S4::B.new.foo([],"test").should == ["B#foo(a,test)", "A#foo"] end - it "raises an error error when super method does not exist" do + it "raises an error when super method does not exist" do sup = Class.new sub_normal = Class.new(sup) do def foo diff --git a/spec/ruby/library/bigdecimal/fix_spec.rb b/spec/ruby/library/bigdecimal/fix_spec.rb index 231c9a587e3..2c6276899ed 100644 --- a/spec/ruby/library/bigdecimal/fix_spec.rb +++ b/spec/ruby/library/bigdecimal/fix_spec.rb @@ -2,20 +2,20 @@ require 'bigdecimal' describe "BigDecimal#fix" do - before :each do - @zero = BigDecimal("0") - @mixed = BigDecimal("1.23456789") - @pos_int = BigDecimal("2E5555") - @neg_int = BigDecimal("-2E5555") - @pos_frac = BigDecimal("2E-9999") - @neg_frac = BigDecimal("-2E-9999") - - @infinity = BigDecimal("Infinity") - @infinity_neg = BigDecimal("-Infinity") - @nan = BigDecimal("NaN") - @zero_pos = BigDecimal("+0") - @zero_neg = BigDecimal("-0") - end + before :each do + @zero = BigDecimal("0") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + end it "returns a BigDecimal" do BigDecimal("2E100000000").fix.kind_of?(BigDecimal).should == true diff --git a/spec/ruby/library/bigdecimal/sqrt_spec.rb b/spec/ruby/library/bigdecimal/sqrt_spec.rb index 8fd1ec0f39f..42cf4545cb3 100644 --- a/spec/ruby/library/bigdecimal/sqrt_spec.rb +++ b/spec/ruby/library/bigdecimal/sqrt_spec.rb @@ -36,7 +36,7 @@ BigDecimal('121').sqrt(5).should be_close(11, 0.00001) end - platform_is_not wordsize: 32 do # fails on i686 + platform_is_not c_long_size: 32 do # fails on i686 it "returns square root of 0.9E-99999 with desired precision" do @frac_2.sqrt(1).to_s.should =~ /\A0\.3E-49999\z/i end diff --git a/spec/ruby/library/date/mon_spec.rb b/spec/ruby/library/date/mon_spec.rb index 724e7d6564e..616d72cf882 100644 --- a/spec/ruby/library/date/mon_spec.rb +++ b/spec/ruby/library/date/mon_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' +require_relative 'shared/month' require 'date' describe "Date#mon" do - it "needs to be reviewed for spec completeness" + it_behaves_like :date_month, :mon end diff --git a/spec/ruby/library/date/month_spec.rb b/spec/ruby/library/date/month_spec.rb index e040f9a94c9..f493ec81192 100644 --- a/spec/ruby/library/date/month_spec.rb +++ b/spec/ruby/library/date/month_spec.rb @@ -1,9 +1,7 @@ require_relative '../../spec_helper' +require_relative 'shared/month' require 'date' describe "Date#month" do - it "returns the month" do - m = Date.new(2000, 7, 1).month - m.should == 7 - end + it_behaves_like :date_month, :month end diff --git a/spec/ruby/library/date/shared/month.rb b/spec/ruby/library/date/shared/month.rb new file mode 100644 index 00000000000..5fcb2cbeb0c --- /dev/null +++ b/spec/ruby/library/date/shared/month.rb @@ -0,0 +1,6 @@ +describe :date_month, shared: true do + it "returns the month" do + m = Date.new(2000, 7, 1).send(@method) + m.should == 7 + end +end diff --git a/spec/ruby/library/erb/new_spec.rb b/spec/ruby/library/erb/new_spec.rb index a5aeeaeed10..f721529ab00 100644 --- a/spec/ruby/library/erb/new_spec.rb +++ b/spec/ruby/library/erb/new_spec.rb @@ -130,7 +130,7 @@ <%#= item %> <%# end %> END - ERBSpecs.new_erb(input).result.should == "\n\n\n" + ERBSpecs.new_erb(input).result.should == "\n\n\n" ERBSpecs.new_erb(input, trim_mode: '<>').result.should == "\n" end diff --git a/spec/ruby/library/logger/logger/new_spec.rb b/spec/ruby/library/logger/logger/new_spec.rb index d3100ee2d1b..6dcb030ae1f 100644 --- a/spec/ruby/library/logger/logger/new_spec.rb +++ b/spec/ruby/library/logger/logger/new_spec.rb @@ -13,19 +13,19 @@ rm_r @file_path end - it "creates a new logger object" do - l = Logger.new(STDERR) - -> { l.add(Logger::WARN, "Foo") }.should output_to_fd(/Foo/, STDERR) - end - - it "receives a logging device as first argument" do - l = Logger.new(@log_file) - l.add(Logger::WARN, "Test message") - - @log_file.rewind - LoggerSpecs.strip_date(@log_file.readline).should == "WARN -- : Test message\n" - l.close - end + it "creates a new logger object" do + l = Logger.new(STDERR) + -> { l.add(Logger::WARN, "Foo") }.should output_to_fd(/Foo/, STDERR) + end + + it "receives a logging device as first argument" do + l = Logger.new(@log_file) + l.add(Logger::WARN, "Test message") + + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readline).should == "WARN -- : Test message\n" + l.close + end it "receives a frequency rotation as second argument" do -> { Logger.new(@log_file, "daily") }.should_not raise_error diff --git a/spec/ruby/library/net-http/http/post_spec.rb b/spec/ruby/library/net-http/http/post_spec.rb index d7d94fec4a7..ac020bd6bed 100644 --- a/spec/ruby/library/net-http/http/post_spec.rb +++ b/spec/ruby/library/net-http/http/post_spec.rb @@ -27,7 +27,7 @@ it "sends Content-Type: application/x-www-form-urlencoded by default" do response = Net::HTTP.post(URI("http://localhost:#{NetHTTPSpecs.port}/request/header"), "test=test") - response.body.should include('"Content-Type"=>"application/x-www-form-urlencoded"') + response.body.should include({ "Content-Type" => "application/x-www-form-urlencoded" }.inspect.delete("{}")) end it "does not support HTTP Basic Auth" do diff --git a/spec/ruby/library/net-http/http/send_request_spec.rb b/spec/ruby/library/net-http/http/send_request_spec.rb index e82b2a96a18..af35c068cea 100644 --- a/spec/ruby/library/net-http/http/send_request_spec.rb +++ b/spec/ruby/library/net-http/http/send_request_spec.rb @@ -54,7 +54,7 @@ @methods.each do |method| response = @http.send_request(method, "/request/header", "test=test", "referer" => referer) - response.body.should include('"Referer"=>"' + referer + '"') + response.body.should include({ "Referer" => referer }.inspect.delete("{}")) end end end diff --git a/spec/ruby/library/objectspace/memsize_of_spec.rb b/spec/ruby/library/objectspace/memsize_of_spec.rb index eefafbb334d..cbb5a07d54f 100644 --- a/spec/ruby/library/objectspace/memsize_of_spec.rb +++ b/spec/ruby/library/objectspace/memsize_of_spec.rb @@ -13,7 +13,7 @@ end it "returns 0 for literal Symbols" do - ObjectSpace.memsize_of(:abc).should == 0 + ObjectSpace.memsize_of(:object_space_memsize_spec_static_sym).should == 0 end it "returns a positive Integer for an Object" do diff --git a/spec/ruby/library/pathname/glob_spec.rb b/spec/ruby/library/pathname/glob_spec.rb index ced810fa907..de322bab476 100644 --- a/spec/ruby/library/pathname/glob_spec.rb +++ b/spec/ruby/library/pathname/glob_spec.rb @@ -21,6 +21,10 @@ Pathname.glob(@dir + 'lib/*.js').should == [] end + it 'returns [] when the pathname does not exist' do + Pathname.glob('i_dont_exist/lib/*.js').should == [] + end + it 'returns matching file paths' do Pathname.glob(@dir + 'lib/*i*.rb').sort.should == [Pathname.new(@file_1), Pathname.new(@file_2)].sort end @@ -67,6 +71,10 @@ Pathname.new(@dir).glob('lib/*.js').should == [] end + it 'returns [] when the pathname does not exist' do + Pathname.new('./i_dont_exist').glob('lib/*.js').should == [] + end + it 'returns matching file paths' do Pathname.new(@dir).glob('lib/*i*.rb').sort.should == [Pathname.new(@file_1), Pathname.new(@file_2)].sort end diff --git a/spec/ruby/library/pp/pp_spec.rb b/spec/ruby/library/pp/pp_spec.rb index 243478efd96..e45a6bb94f6 100644 --- a/spec/ruby/library/pp/pp_spec.rb +++ b/spec/ruby/library/pp/pp_spec.rb @@ -25,6 +25,6 @@ hash = { 'key' => 42 } -> { PP.pp hash - }.should output('{"key"=>42}' + "\n") + }.should output("#{hash.inspect}\n") end end diff --git a/spec/ruby/library/rbconfig/rbconfig_spec.rb b/spec/ruby/library/rbconfig/rbconfig_spec.rb index 3e06219621f..b9a4588bf0b 100644 --- a/spec/ruby/library/rbconfig/rbconfig_spec.rb +++ b/spec/ruby/library/rbconfig/rbconfig_spec.rb @@ -9,6 +9,13 @@ end end + it 'has MAJOR, MINOR, TEENY, and PATCHLEVEL matching RUBY_VERSION and RUBY_PATCHLEVEL' do + major, minor, teeny = RUBY_VERSION.split('.') + RbConfig::CONFIG.values_at("MAJOR", "MINOR", "TEENY", "PATCHLEVEL").should == [ + major, minor, teeny, RUBY_PATCHLEVEL.to_s + ] + end + # These directories have no meanings before the installation. guard -> { RbConfig::TOPDIR } do it "['rubylibdir'] returns the directory containing Ruby standard libraries" do diff --git a/spec/ruby/library/set/merge_spec.rb b/spec/ruby/library/set/merge_spec.rb index a8e3ffc8709..3ae0da827c8 100644 --- a/spec/ruby/library/set/merge_spec.rb +++ b/spec/ruby/library/set/merge_spec.rb @@ -16,4 +16,16 @@ -> { Set[1, 2].merge(1) }.should raise_error(ArgumentError) -> { Set[1, 2].merge(Object.new) }.should raise_error(ArgumentError) end + + ruby_version_is ""..."3.3" do + it "accepts only a single argument" do + -> { Set[].merge([], []) }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 1)") + end + end + + ruby_version_is "3.3" do + it "accepts multiple arguments" do + Set[:a, :b].merge(Set[:b, :c], [:d]).should == Set[:a, :b, :c, :d] + end + end end diff --git a/spec/ruby/library/socket/addrinfo/initialize_spec.rb b/spec/ruby/library/socket/addrinfo/initialize_spec.rb index 83b204b5757..d8885c5d622 100644 --- a/spec/ruby/library/socket/addrinfo/initialize_spec.rb +++ b/spec/ruby/library/socket/addrinfo/initialize_spec.rb @@ -335,7 +335,7 @@ @sockaddr = ['AF_INET6', 80, 'hostname', '127.0.0.1'] end - it "raises SocketError when using any Socket constant except except AF_INET(6)/PF_INET(6)" do + it "raises SocketError when using any Socket constant except AF_INET(6)/PF_INET(6)" do Socket.constants.grep(/(^AF_|^PF_)(?!INET)/).each do |constant| value = Socket.const_get(constant) -> { diff --git a/spec/ruby/library/socket/basicsocket/send_spec.rb b/spec/ruby/library/socket/basicsocket/send_spec.rb index 86b5567026d..1c028480e3e 100644 --- a/spec/ruby/library/socket/basicsocket/send_spec.rb +++ b/spec/ruby/library/socket/basicsocket/send_spec.rb @@ -16,27 +16,27 @@ @socket.close end - it "sends a message to another socket and returns the number of bytes sent" do - data = +"" - t = Thread.new do - client = @server.accept - loop do - got = client.recv(5) - break if got.nil? || got.empty? - data << got - end - client.close - end - Thread.pass while t.status and t.status != "sleep" - t.status.should_not be_nil + it "sends a message to another socket and returns the number of bytes sent" do + data = +"" + t = Thread.new do + client = @server.accept + loop do + got = client.recv(5) + break if got.nil? || got.empty? + data << got + end + client.close + end + Thread.pass while t.status and t.status != "sleep" + t.status.should_not be_nil - @socket.send('hello', 0).should == 5 - @socket.shutdown(1) # indicate, that we are done sending - @socket.recv(10) + @socket.send('hello', 0).should == 5 + @socket.shutdown(1) # indicate, that we are done sending + @socket.recv(10) - t.join - data.should == 'hello' - end + t.join + data.should == 'hello' + end platform_is_not :solaris, :windows do it "accepts flags to specify unusual sending behaviour" do diff --git a/spec/ruby/library/socket/socket/connect_spec.rb b/spec/ruby/library/socket/socket/connect_spec.rb index 8653fba552b..175c9942bcd 100644 --- a/spec/ruby/library/socket/socket/connect_spec.rb +++ b/spec/ruby/library/socket/socket/connect_spec.rb @@ -53,4 +53,18 @@ end end end + + ruby_version_is "3.4" do + it "fails with timeout" do + # TEST-NET-1 IP address are reserved for documentation and example purposes. + address = Socket.pack_sockaddr_in(1, "192.0.2.1") + + client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM) + client.timeout = 0 + + -> { + client.connect(address) + }.should raise_error(IO::TimeoutError) + end + end end diff --git a/spec/ruby/library/stringio/fixtures/classes.rb b/spec/ruby/library/stringio/fixtures/classes.rb index bb8dc354cc4..832c5457d77 100644 --- a/spec/ruby/library/stringio/fixtures/classes.rb +++ b/spec/ruby/library/stringio/fixtures/classes.rb @@ -4,12 +4,12 @@ class StringSubclass < String; end module StringIOSpecs def self.build - str = <<-EOS + str = <<-EOS each peach pear plum - EOS + EOS StringIO.new(str) end end diff --git a/spec/ruby/library/stringio/set_encoding_by_bom_spec.rb b/spec/ruby/library/stringio/set_encoding_by_bom_spec.rb new file mode 100644 index 00000000000..1030aad042c --- /dev/null +++ b/spec/ruby/library/stringio/set_encoding_by_bom_spec.rb @@ -0,0 +1,237 @@ +require 'stringio' +require_relative '../../spec_helper' + +# Should be synced with specs for IO#set_encoding_by_bom +describe "StringIO#set_encoding_by_bom" do + it "returns nil if not readable" do + io = StringIO.new("".b, "wb") + + io.set_encoding_by_bom.should be_nil + io.external_encoding.should == Encoding::ASCII_8BIT + end + + it "returns the result encoding if found BOM UTF-8 sequence" do + io = StringIO.new("\u{FEFF}".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_8 + io.external_encoding.should == Encoding::UTF_8 + io.read.b.should == "".b + + io = StringIO.new("\u{FEFF}abc".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_8 + io.external_encoding.should == Encoding::UTF_8 + io.read.b.should == "abc".b + end + + it "returns the result encoding if found BOM UTF_16LE sequence" do + io = StringIO.new("\xFF\xFE".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_16LE + io.external_encoding.should == Encoding::UTF_16LE + io.read.b.should == "".b + + io = StringIO.new("\xFF\xFEabc".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_16LE + io.external_encoding.should == Encoding::UTF_16LE + io.read.b.should == "abc".b + end + + it "returns the result encoding if found BOM UTF_16BE sequence" do + io = StringIO.new("\xFE\xFF".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_16BE + io.external_encoding.should == Encoding::UTF_16BE + io.read.b.should == "".b + + io = StringIO.new("\xFE\xFFabc".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_16BE + io.external_encoding.should == Encoding::UTF_16BE + io.read.b.should == "abc".b + end + + it "returns the result encoding if found BOM UTF_32LE sequence" do + io = StringIO.new("\xFF\xFE\x00\x00".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_32LE + io.external_encoding.should == Encoding::UTF_32LE + io.read.b.should == "".b + + io = StringIO.new("\xFF\xFE\x00\x00abc".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_32LE + io.external_encoding.should == Encoding::UTF_32LE + io.read.b.should == "abc".b + end + + it "returns the result encoding if found BOM UTF_32BE sequence" do + io = StringIO.new("\x00\x00\xFE\xFF".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_32BE + io.external_encoding.should == Encoding::UTF_32BE + io.read.b.should == "".b + + io = StringIO.new("\x00\x00\xFE\xFFabc".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_32BE + io.external_encoding.should == Encoding::UTF_32BE + io.read.b.should == "abc".b + end + + it "returns nil if io is empty" do + io = StringIO.new("".b, "rb") + io.set_encoding_by_bom.should be_nil + io.external_encoding.should == Encoding::ASCII_8BIT + end + + it "returns nil if UTF-8 BOM sequence is incomplete" do + io = StringIO.new("\xEF".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\xEF".b + + io = StringIO.new("\xEFa".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\xEFa".b + + io = StringIO.new("\xEF\xBB".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\xEF\xBB".b + + io = StringIO.new("\xEF\xBBa".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\xEF\xBBa".b + end + + it "returns nil if UTF-16BE BOM sequence is incomplete" do + io = StringIO.new("\xFE".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\xFE".b + + io = StringIO.new("\xFEa".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\xFEa".b + end + + it "returns nil if UTF-16LE/UTF-32LE BOM sequence is incomplete" do + io = StringIO.new("\xFF".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\xFF".b + + io = StringIO.new("\xFFa".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\xFFa".b + end + + it "returns UTF-16LE if UTF-32LE BOM sequence is incomplete" do + io = StringIO.new("\xFF\xFE".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_16LE + io.external_encoding.should == Encoding::UTF_16LE + io.read.b.should == "".b + + io = StringIO.new("\xFF\xFE\x00".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_16LE + io.external_encoding.should == Encoding::UTF_16LE + io.read.b.should == "\x00".b + + io = StringIO.new("\xFF\xFE\x00a".b, "rb") + + io.set_encoding_by_bom.should == Encoding::UTF_16LE + io.external_encoding.should == Encoding::UTF_16LE + io.read.b.should == "\x00a".b + end + + it "returns nil if UTF-32BE BOM sequence is incomplete" do + io = StringIO.new("\x00".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\x00".b + + io = StringIO.new("\x00a".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\x00a".b + + io = StringIO.new("\x00\x00".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\x00\x00".b + + io = StringIO.new("\x00\x00a".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\x00\x00a".b + + io = StringIO.new("\x00\x00\xFE".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\x00\x00\xFE".b + + io = StringIO.new("\x00\x00\xFEa".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read.b.should == "\x00\x00\xFEa".b + end + + it "returns nil if found BOM sequence not provided" do + io = StringIO.new("abc".b, "rb") + + io.set_encoding_by_bom.should == nil + io.external_encoding.should == Encoding::ASCII_8BIT + io.read(3).should == "abc".b + end + + it "does not raise exception if io not in binary mode" do + io = StringIO.new("", 'r') + io.set_encoding_by_bom.should == nil + end + + it "does not raise exception if encoding already set" do + io = StringIO.new("".b, "rb") + io.set_encoding("utf-8") + io.set_encoding_by_bom.should == nil + end + + it "does not raise exception if encoding conversion is already set" do + io = StringIO.new("".b, "rb") + io.set_encoding(Encoding::UTF_8, Encoding::UTF_16BE) + + io.set_encoding_by_bom.should == nil + end + + it "raises FrozenError when io is frozen" do + io = StringIO.new() + io.freeze + -> { io.set_encoding_by_bom }.should raise_error(FrozenError) + end + + it "does not raise FrozenError when initial string is frozen" do + io = StringIO.new("".freeze) + io.set_encoding_by_bom.should == nil + end +end diff --git a/spec/ruby/library/stringio/shared/sysread.rb b/spec/ruby/library/stringio/shared/sysread.rb index 937bac705cb..3e23fbc2335 100644 --- a/spec/ruby/library/stringio/shared/sysread.rb +++ b/spec/ruby/library/stringio/shared/sysread.rb @@ -10,6 +10,6 @@ it "raises an EOFError when passed length > 0 and no data remains" do @io.read.should == "example" - -> { @io.sysread(1) }.should raise_error(EOFError) + -> { @io.send(@method, 1) }.should raise_error(EOFError) end end diff --git a/spec/ruby/library/stringio/sysread_spec.rb b/spec/ruby/library/stringio/sysread_spec.rb index 8f78073f428..fabb06dd9a6 100644 --- a/spec/ruby/library/stringio/sysread_spec.rb +++ b/spec/ruby/library/stringio/sysread_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' require "stringio" require_relative 'shared/read' +require_relative 'shared/sysread' describe "StringIO#sysread when passed length, buffer" do it_behaves_like :stringio_read, :sysread @@ -32,6 +33,10 @@ end end +describe "StringIO#sysread when passed length" do + it_behaves_like :stringio_sysread_length, :sysread +end + describe "StringIO#sysread when passed [length]" do before :each do @io = StringIO.new("example") diff --git a/spec/ruby/library/stringscanner/check_until_spec.rb b/spec/ruby/library/stringscanner/check_until_spec.rb index ad222fd76b3..1d89f88a25b 100644 --- a/spec/ruby/library/stringscanner/check_until_spec.rb +++ b/spec/ruby/library/stringscanner/check_until_spec.rb @@ -13,9 +13,11 @@ @s.check_until(/test/).should == "This is a test" end - it "raises TypeError if given a String" do - -> { - @s.check_until('T') - }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + ruby_version_is ""..."3.4" do + it "raises TypeError if given a String" do + -> { + @s.check_until('T') + }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + end end end diff --git a/spec/ruby/library/stringscanner/exist_spec.rb b/spec/ruby/library/stringscanner/exist_spec.rb index ff860a0d3e3..a18f5ce3527 100644 --- a/spec/ruby/library/stringscanner/exist_spec.rb +++ b/spec/ruby/library/stringscanner/exist_spec.rb @@ -22,9 +22,11 @@ @s.exist?(/i/).should == nil end - it "raises TypeError if given a String" do - -> { - @s.exist?('T') - }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + ruby_version_is ""..."3.4" do + it "raises TypeError if given a String" do + -> { + @s.exist?('T') + }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + end end end diff --git a/spec/ruby/library/stringscanner/scan_until_spec.rb b/spec/ruby/library/stringscanner/scan_until_spec.rb index a8162f1f039..1e318d053b9 100644 --- a/spec/ruby/library/stringscanner/scan_until_spec.rb +++ b/spec/ruby/library/stringscanner/scan_until_spec.rb @@ -21,9 +21,11 @@ @s.scan_until(/^h/).should == "h" end - it "raises TypeError if given a String" do - -> { - @s.scan_until('T') - }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + ruby_version_is ""..."3.4" do + it "raises TypeError if given a String" do + -> { + @s.scan_until('T') + }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + end end end diff --git a/spec/ruby/library/stringscanner/search_full_spec.rb b/spec/ruby/library/stringscanner/search_full_spec.rb index 7d2a714fa56..713ab00d22b 100644 --- a/spec/ruby/library/stringscanner/search_full_spec.rb +++ b/spec/ruby/library/stringscanner/search_full_spec.rb @@ -28,9 +28,11 @@ @s.pos.should == 4 end - it "raises TypeError if given a String" do - -> { - @s.search_full('T', true, true) - }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + ruby_version_is ""..."3.4" do + it "raises TypeError if given a String" do + -> { + @s.search_full('T', true, true) + }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + end end end diff --git a/spec/ruby/library/stringscanner/skip_until_spec.rb b/spec/ruby/library/stringscanner/skip_until_spec.rb index 7b56f13e4f6..b6a020f9ba8 100644 --- a/spec/ruby/library/stringscanner/skip_until_spec.rb +++ b/spec/ruby/library/stringscanner/skip_until_spec.rb @@ -16,9 +16,11 @@ @s.skip_until(/d+/).should == nil end - it "raises TypeError if given a String" do - -> { - @s.skip_until('T') - }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + ruby_version_is ""..."3.4" do + it "raises TypeError if given a String" do + -> { + @s.skip_until('T') + }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)') + end end end diff --git a/spec/ruby/library/time/iso8601_spec.rb b/spec/ruby/library/time/iso8601_spec.rb index 4a9eb456138..9e2607fbd0b 100644 --- a/spec/ruby/library/time/iso8601_spec.rb +++ b/spec/ruby/library/time/iso8601_spec.rb @@ -3,5 +3,5 @@ require 'time' describe "Time.xmlschema" do - it_behaves_like :time_xmlschema, :iso8601 + it_behaves_like :time_library_xmlschema, :iso8601 end diff --git a/spec/ruby/library/time/shared/rfc2822.rb b/spec/ruby/library/time/shared/rfc2822.rb index d99f1f76dec..e460d655a68 100644 --- a/spec/ruby/library/time/shared/rfc2822.rb +++ b/spec/ruby/library/time/shared/rfc2822.rb @@ -1,33 +1,33 @@ describe :time_rfc2822, shared: true do it "parses RFC-822 strings" do t1 = (Time.utc(1976, 8, 26, 14, 30) + 4 * 3600) - t2 = Time.rfc2822("26 Aug 76 14:30 EDT") + t2 = Time.send(@method, "26 Aug 76 14:30 EDT") t1.should == t2 t3 = Time.utc(1976, 8, 27, 9, 32) + 7 * 3600 - t4 = Time.rfc2822("27 Aug 76 09:32 PDT") + t4 = Time.send(@method, "27 Aug 76 09:32 PDT") t3.should == t4 end it "parses RFC-2822 strings" do t1 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600 - t2 = Time.rfc2822("Fri, 21 Nov 1997 09:55:06 -0600") + t2 = Time.send(@method, "Fri, 21 Nov 1997 09:55:06 -0600") t1.should == t2 t3 = Time.utc(2003, 7, 1, 10, 52, 37) - 2 * 3600 - t4 = Time.rfc2822("Tue, 1 Jul 2003 10:52:37 +0200") + t4 = Time.send(@method, "Tue, 1 Jul 2003 10:52:37 +0200") t3.should == t4 t5 = Time.utc(1997, 11, 21, 10, 1, 10) + 6 * 3600 - t6 = Time.rfc2822("Fri, 21 Nov 1997 10:01:10 -0600") + t6 = Time.send(@method, "Fri, 21 Nov 1997 10:01:10 -0600") t5.should == t6 t7 = Time.utc(1997, 11, 21, 11, 0, 0) + 6 * 3600 - t8 = Time.rfc2822("Fri, 21 Nov 1997 11:00:00 -0600") + t8 = Time.send(@method, "Fri, 21 Nov 1997 11:00:00 -0600") t7.should == t8 t9 = Time.utc(1997, 11, 24, 14, 22, 1) + 8 * 3600 - t10 = Time.rfc2822("Mon, 24 Nov 1997 14:22:01 -0800") + t10 = Time.send(@method, "Mon, 24 Nov 1997 14:22:01 -0800") t9.should == t10 begin @@ -36,11 +36,11 @@ # ignore else t11 = Time.utc(1969, 2, 13, 23, 32, 54) + 3 * 3600 + 30 * 60 - t12 = Time.rfc2822("Thu, 13 Feb 1969 23:32:54 -0330") + t12 = Time.send(@method, "Thu, 13 Feb 1969 23:32:54 -0330") t11.should == t12 t13 = Time.utc(1969, 2, 13, 23, 32, 0) + 3 * 3600 + 30 * 60 - t14 = Time.rfc2822(" Thu, + t14 = Time.send(@method, " Thu, 13 Feb 1969 @@ -50,16 +50,16 @@ end t15 = Time.utc(1997, 11, 21, 9, 55, 6) - t16 = Time.rfc2822("21 Nov 97 09:55:06 GMT") + t16 = Time.send(@method, "21 Nov 97 09:55:06 GMT") t15.should == t16 t17 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600 - t18 = Time.rfc2822("Fri, 21 Nov 1997 09 : 55 : 06 -0600") + t18 = Time.send(@method, "Fri, 21 Nov 1997 09 : 55 : 06 -0600") t17.should == t18 -> { # inner comment is not supported. - Time.rfc2822("Fri, 21 Nov 1997 09(comment): 55 : 06 -0600") + Time.send(@method, "Fri, 21 Nov 1997 09(comment): 55 : 06 -0600") }.should raise_error(ArgumentError) end end diff --git a/spec/ruby/library/time/shared/xmlschema.rb b/spec/ruby/library/time/shared/xmlschema.rb index 44d33cda7e3..0002886ca5b 100644 --- a/spec/ruby/library/time/shared/xmlschema.rb +++ b/spec/ruby/library/time/shared/xmlschema.rb @@ -1,24 +1,24 @@ -describe :time_xmlschema, shared: true do +describe :time_library_xmlschema, shared: true do it "parses ISO-8601 strings" do t = Time.utc(1985, 4, 12, 23, 20, 50, 520000) s = "1985-04-12T23:20:50.52Z" - t.should == Time.xmlschema(s) - #s.should == t.xmlschema(2) + t.should == Time.send(@method, s) + #s.should == t.send(@method, 2) t = Time.utc(1996, 12, 20, 0, 39, 57) s = "1996-12-19T16:39:57-08:00" - t.should == Time.xmlschema(s) + t.should == Time.send(@method, s) # There is no way to generate time string with arbitrary timezone. s = "1996-12-20T00:39:57Z" - t.should == Time.xmlschema(s) - #assert_equal(s, t.xmlschema) + t.should == Time.send(@method, s) + #assert_equal(s, t.send(@method)) t = Time.utc(1990, 12, 31, 23, 59, 60) s = "1990-12-31T23:59:60Z" - t.should == Time.xmlschema(s) + t.should == Time.send(@method, s) # leap second is representable only if timezone file has it. s = "1990-12-31T15:59:60-08:00" - t.should == Time.xmlschema(s) + t.should == Time.send(@method, s) begin Time.at(-1) @@ -27,27 +27,27 @@ else t = Time.utc(1937, 1, 1, 11, 40, 27, 870000) s = "1937-01-01T12:00:27.87+00:20" - t.should == Time.xmlschema(s) + t.should == Time.send(@method, s) end # more - # (Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600).should == Time.xmlschema("1999-05-31T13:20:00-05:00") - # (Time.local(2000, 1, 20, 12, 0, 0)).should == Time.xmlschema("2000-01-20T12:00:00") - # (Time.utc(2000, 1, 20, 12, 0, 0)).should == Time.xmlschema("2000-01-20T12:00:00Z") - # (Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600).should == Time.xmlschema("2000-01-20T12:00:00+12:00") - # (Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600).should == Time.xmlschema("2000-01-20T12:00:00-13:00") - # (Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600).should == Time.xmlschema("2000-03-04T23:00:00+03:00") - # (Time.utc(2000, 3, 4, 20, 0, 0)).should == Time.xmlschema("2000-03-04T20:00:00Z") - # (Time.local(2000, 1, 15, 0, 0, 0)).should == Time.xmlschema("2000-01-15T00:00:00") - # (Time.local(2000, 2, 15, 0, 0, 0)).should == Time.xmlschema("2000-02-15T00:00:00") - # (Time.local(2000, 1, 15, 12, 0, 0)).should == Time.xmlschema("2000-01-15T12:00:00") - # (Time.utc(2000, 1, 16, 12, 0, 0)).should == Time.xmlschema("2000-01-16T12:00:00Z") - # (Time.local(2000, 1, 1, 12, 0, 0)).should == Time.xmlschema("2000-01-01T12:00:00") - # (Time.utc(1999, 12, 31, 23, 0, 0)).should == Time.xmlschema("1999-12-31T23:00:00Z") - # (Time.local(2000, 1, 16, 12, 0, 0)).should == Time.xmlschema("2000-01-16T12:00:00") - # (Time.local(2000, 1, 16, 0, 0, 0)).should == Time.xmlschema("2000-01-16T00:00:00") - # (Time.utc(2000, 1, 12, 12, 13, 14)).should == Time.xmlschema("2000-01-12T12:13:14Z") - # (Time.utc(2001, 4, 17, 19, 23, 17, 300000)).should == Time.xmlschema("2001-04-17T19:23:17.3Z") + # (Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600).should == Time.send(@method, "1999-05-31T13:20:00-05:00") + # (Time.local(2000, 1, 20, 12, 0, 0)).should == Time.send(@method, "2000-01-20T12:00:00") + # (Time.utc(2000, 1, 20, 12, 0, 0)).should == Time.send(@method, "2000-01-20T12:00:00Z") + # (Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600).should == Time.send(@method, "2000-01-20T12:00:00+12:00") + # (Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600).should == Time.send(@method, "2000-01-20T12:00:00-13:00") + # (Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600).should == Time.send(@method, "2000-03-04T23:00:00+03:00") + # (Time.utc(2000, 3, 4, 20, 0, 0)).should == Time.send(@method, "2000-03-04T20:00:00Z") + # (Time.local(2000, 1, 15, 0, 0, 0)).should == Time.send(@method, "2000-01-15T00:00:00") + # (Time.local(2000, 2, 15, 0, 0, 0)).should == Time.send(@method, "2000-02-15T00:00:00") + # (Time.local(2000, 1, 15, 12, 0, 0)).should == Time.send(@method, "2000-01-15T12:00:00") + # (Time.utc(2000, 1, 16, 12, 0, 0)).should == Time.send(@method, "2000-01-16T12:00:00Z") + # (Time.local(2000, 1, 1, 12, 0, 0)).should == Time.send(@method, "2000-01-01T12:00:00") + # (Time.utc(1999, 12, 31, 23, 0, 0)).should == Time.send(@method, "1999-12-31T23:00:00Z") + # (Time.local(2000, 1, 16, 12, 0, 0)).should == Time.send(@method, "2000-01-16T12:00:00") + # (Time.local(2000, 1, 16, 0, 0, 0)).should == Time.send(@method, "2000-01-16T00:00:00") + # (Time.utc(2000, 1, 12, 12, 13, 14)).should == Time.send(@method, "2000-01-12T12:13:14Z") + # (Time.utc(2001, 4, 17, 19, 23, 17, 300000)).should == Time.send(@method, "2001-04-17T19:23:17.3Z") end end diff --git a/spec/ruby/library/time/xmlschema_spec.rb b/spec/ruby/library/time/xmlschema_spec.rb index 42793111997..ff3c864a02e 100644 --- a/spec/ruby/library/time/xmlschema_spec.rb +++ b/spec/ruby/library/time/xmlschema_spec.rb @@ -3,5 +3,5 @@ require 'time' describe "Time.xmlschema" do - it_behaves_like :time_xmlschema, :xmlschema + it_behaves_like :time_library_xmlschema, :xmlschema end diff --git a/spec/ruby/library/uri/shared/parse.rb b/spec/ruby/library/uri/shared/parse.rb index 87e1ee933ea..c5057b6c4bb 100644 --- a/spec/ruby/library/uri/shared/parse.rb +++ b/spec/ruby/library/uri/shared/parse.rb @@ -192,8 +192,15 @@ file.should be_kind_of(URI::Generic) end - it "raises errors on malformed URIs" do - -> { @object.parse('http://a_b:80/') }.should raise_error(URI::InvalidURIError) - -> { @object.parse('http://a_b/') }.should raise_error(URI::InvalidURIError) + if URI::DEFAULT_PARSER == URI::RFC2396_Parser + it "raises errors on malformed URIs" do + -> { @object.parse('http://a_b:80/') }.should raise_error(URI::InvalidURIError) + -> { @object.parse('http://a_b/') }.should raise_error(URI::InvalidURIError) + end + elsif URI::DEFAULT_PARSER == URI::RFC3986_Parser + it "does not raise errors on URIs contained underscore" do + -> { @object.parse('http://a_b:80/') }.should_not raise_error(URI::InvalidURIError) + -> { @object.parse('http://a_b/') }.should_not raise_error(URI::InvalidURIError) + end end end diff --git a/spec/ruby/optional/capi/bignum_spec.rb b/spec/ruby/optional/capi/bignum_spec.rb index cde929af28d..179f053eec4 100644 --- a/spec/ruby/optional/capi/bignum_spec.rb +++ b/spec/ruby/optional/capi/bignum_spec.rb @@ -7,21 +7,23 @@ def ensure_bignum(n) n end -full_range_longs = (fixnum_max == 2**(0.size * 8 - 1) - 1) +full_range_longs = (fixnum_max == max_long) +max_ulong = begin + require 'rbconfig/sizeof' + RbConfig::LIMITS['ULONG_MAX'] +rescue LoadError + nil +end +# If the system doesn't offer ULONG_MAX, assume 2's complement and derive it +# from LONG_MAX. +max_ulong ||= 2 * (max_long + 1) - 1 describe "CApiBignumSpecs" do before :each do @s = CApiBignumSpecs.new - - if full_range_longs - @max_long = 2**(0.size * 8 - 1) - 1 - @min_long = -@max_long - 1 - @max_ulong = ensure_bignum(2**(0.size * 8) - 1) - else - @max_long = ensure_bignum(2**(0.size * 8 - 1) - 1) - @min_long = ensure_bignum(-@max_long - 1) - @max_ulong = ensure_bignum(2**(0.size * 8) - 1) - end + @max_long = max_long + @min_long = min_long + @max_ulong = ensure_bignum(max_ulong) end describe "rb_big2long" do @@ -123,7 +125,7 @@ def ensure_bignum(n) val.should == @max_ulong end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "packs max_ulong into 2 ulongs to allow sign bit" do val = @s.rb_big_pack_length(@max_ulong) val.should == 2 diff --git a/spec/ruby/optional/capi/debug_spec.rb b/spec/ruby/optional/capi/debug_spec.rb index 148b8c38fbb..14ba25609c5 100644 --- a/spec/ruby/optional/capi/debug_spec.rb +++ b/spec/ruby/optional/capi/debug_spec.rb @@ -37,9 +37,12 @@ it "matches the locations in rb_debug_inspector_backtrace_locations" do frames = @o.rb_debug_inspector_open(42) - frames.each do |_s, _klass, binding, _iseq, backtrace_location| + frames.each do |_s, klass, binding, iseq, backtrace_location| if binding - binding.source_location.should == [backtrace_location.path, backtrace_location.lineno] + # YJIT modifies Array#each backtraces but leaves its source_location as is + unless defined?(RubyVM::YJIT) && klass == Array && iseq.label == "each" + binding.source_location.should == [backtrace_location.path, backtrace_location.lineno] + end method_name = binding.eval('__method__') if method_name method_name.should == backtrace_location.base_label.to_sym diff --git a/spec/ruby/optional/capi/ext/io_spec.c b/spec/ruby/optional/capi/ext/io_spec.c index 1b4d4fccce4..e0b1df0de7a 100644 --- a/spec/ruby/optional/capi/ext/io_spec.c +++ b/spec/ruby/optional/capi/ext/io_spec.c @@ -143,7 +143,11 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { errno = saved_errno; } +#ifdef RUBY_VERSION_IS_3_1 + ret = rb_io_maybe_wait_readable(errno, io, Qnil); +#else ret = rb_io_wait_readable(fd); +#endif if (RTEST(read_p)) { ssize_t r = read(fd, buf, RB_IO_WAIT_READABLE_BUF); @@ -162,7 +166,11 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { } VALUE io_spec_rb_io_wait_writable(VALUE self, VALUE io) { +#ifdef RUBY_VERSION_IS_3_1 + int ret = rb_io_maybe_wait_writable(errno, io, Qnil); +#else int ret = rb_io_wait_writable(io_spec_get_fd(io)); +#endif return ret ? Qtrue : Qfalse; } @@ -230,13 +238,23 @@ VALUE io_spec_rb_thread_wait_fd(VALUE self, VALUE io) { } VALUE io_spec_rb_wait_for_single_fd(VALUE self, VALUE io, VALUE events, VALUE secs, VALUE usecs) { - int fd = io_spec_get_fd(io); +#ifdef RUBY_VERSION_IS_3_0 + VALUE timeout = Qnil; + if (!NIL_P(secs)) { + timeout = rb_float_new((double)FIX2INT(secs) + (0.000001f * FIX2INT(usecs))); + } + VALUE result = rb_io_wait(io, events, timeout); + if (result == Qfalse) return INT2FIX(0); + else return result; +#else struct timeval tv; if (!NIL_P(secs)) { tv.tv_sec = FIX2INT(secs); tv.tv_usec = FIX2INT(usecs); } + int fd = io_spec_get_fd(io); return INT2FIX(rb_wait_for_single_fd(fd, FIX2INT(events), NIL_P(secs) ? NULL : &tv)); +#endif } VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) { diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c index 04252b28488..e6bf4870ec1 100644 --- a/spec/ruby/optional/capi/ext/kernel_spec.c +++ b/spec/ruby/optional/capi/ext/kernel_spec.c @@ -7,16 +7,12 @@ extern "C" { #endif -VALUE kernel_spec_call_proc(VALUE arg_array) { +static VALUE kernel_spec_call_proc(VALUE arg_array) { VALUE arg = rb_ary_pop(arg_array); VALUE proc = rb_ary_pop(arg_array); return rb_funcall(proc, rb_intern("call"), 1, arg); } -VALUE kernel_spec_call_proc_raise(VALUE arg_array, VALUE raised_exc) { - return kernel_spec_call_proc(arg_array); -} - static VALUE kernel_spec_rb_block_given_p(VALUE self) { return rb_block_given_p() ? Qtrue : Qfalse; } @@ -134,7 +130,16 @@ VALUE kernel_spec_rb_throw_obj(VALUE self, VALUE obj, VALUE result) { return ID2SYM(rb_intern("rb_throw_failed")); } -VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) { +VALUE kernel_spec_rb_errinfo(VALUE self) { + return rb_errinfo(); +} + +VALUE kernel_spec_rb_set_errinfo(VALUE self, VALUE exc) { + rb_set_errinfo(exc); + return Qnil; +} + +static VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) { VALUE argv[2]; int argc; @@ -181,7 +186,7 @@ VALUE kernel_spec_rb_rescue2(int argc, VALUE *args, VALUE self) { rb_ary_push(raise_array, args[3]); return rb_rescue2(kernel_spec_call_proc, main_array, - kernel_spec_call_proc_raise, raise_array, args[4], args[5], (VALUE)0); + kernel_spec_call_proc_with_raised_exc, raise_array, args[4], args[5], (VALUE)0); } static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) { @@ -195,7 +200,7 @@ static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) { return res; } -static VALUE kernel_spec_rb_protect_errinfo(VALUE self, VALUE obj, VALUE ary) { +static VALUE kernel_spec_rb_protect_ignore_status(VALUE self, VALUE obj, VALUE ary) { int status = 0; VALUE res = rb_protect(rb_yield, obj, &status); rb_ary_store(ary, 0, INT2NUM(23)); @@ -382,10 +387,13 @@ void Init_kernel_spec(void) { rb_define_method(cls, "rb_raise", kernel_spec_rb_raise, 1); rb_define_method(cls, "rb_throw", kernel_spec_rb_throw, 1); rb_define_method(cls, "rb_throw_obj", kernel_spec_rb_throw_obj, 2); + rb_define_method(cls, "rb_errinfo", kernel_spec_rb_errinfo, 0); + rb_define_method(cls, "rb_set_errinfo", kernel_spec_rb_set_errinfo, 1); + rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4); rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4); rb_define_method(cls, "rb_rescue2", kernel_spec_rb_rescue2, -1); rb_define_method(cls, "rb_protect_yield", kernel_spec_rb_protect_yield, 2); - rb_define_method(cls, "rb_protect_errinfo", kernel_spec_rb_protect_errinfo, 2); + rb_define_method(cls, "rb_protect_ignore_status", kernel_spec_rb_protect_ignore_status, 2); rb_define_method(cls, "rb_protect_null_status", kernel_spec_rb_protect_null_status, 1); rb_define_method(cls, "rb_eval_string_protect", kernel_spec_rb_eval_string_protect, 2); rb_define_method(cls, "rb_catch", kernel_spec_rb_catch, 2); diff --git a/spec/ruby/optional/capi/ext/mutex_spec.c b/spec/ruby/optional/capi/ext/mutex_spec.c index c2fdf917ac0..d2c8f98e893 100644 --- a/spec/ruby/optional/capi/ext/mutex_spec.c +++ b/spec/ruby/optional/capi/ext/mutex_spec.c @@ -29,15 +29,34 @@ VALUE mutex_spec_rb_mutex_sleep(VALUE self, VALUE mutex, VALUE timeout) { return rb_mutex_sleep(mutex, timeout); } - VALUE mutex_spec_rb_mutex_callback(VALUE arg) { return rb_funcall(arg, rb_intern("call"), 0); } +VALUE mutex_spec_rb_mutex_naughty_callback(VALUE arg) { + int *result = (int *) arg; + return (VALUE) result; +} + +VALUE mutex_spec_rb_mutex_callback_basic(VALUE arg) { + return arg; +} + VALUE mutex_spec_rb_mutex_synchronize(VALUE self, VALUE mutex, VALUE value) { return rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_callback, value); } +VALUE mutex_spec_rb_mutex_synchronize_with_naughty_callback(VALUE self, VALUE mutex) { + // a naughty callback accepts or returns not a Ruby object but arbitrary value + int arg = 42; + VALUE result = rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_naughty_callback, (VALUE) &arg); + return INT2NUM(*((int *) result)); +} + +VALUE mutex_spec_rb_mutex_synchronize_with_native_callback(VALUE self, VALUE mutex, VALUE value) { + return rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_callback_basic, value); +} + void Init_mutex_spec(void) { VALUE cls = rb_define_class("CApiMutexSpecs", rb_cObject); rb_define_method(cls, "rb_mutex_new", mutex_spec_rb_mutex_new, 0); @@ -47,6 +66,8 @@ void Init_mutex_spec(void) { rb_define_method(cls, "rb_mutex_unlock", mutex_spec_rb_mutex_unlock, 1); rb_define_method(cls, "rb_mutex_sleep", mutex_spec_rb_mutex_sleep, 2); rb_define_method(cls, "rb_mutex_synchronize", mutex_spec_rb_mutex_synchronize, 2); + rb_define_method(cls, "rb_mutex_synchronize_with_naughty_callback", mutex_spec_rb_mutex_synchronize_with_naughty_callback, 1); + rb_define_method(cls, "rb_mutex_synchronize_with_native_callback", mutex_spec_rb_mutex_synchronize_with_native_callback, 2); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/string_spec.c b/spec/ruby/optional/capi/ext/string_spec.c index cec3f65f458..94f412267ff 100644 --- a/spec/ruby/optional/capi/ext/string_spec.c +++ b/spec/ruby/optional/capi/ext/string_spec.c @@ -117,6 +117,10 @@ VALUE string_spec_rb_str_cmp(VALUE self, VALUE str1, VALUE str2) { return INT2NUM(rb_str_cmp(str1, str2)); } +VALUE string_spec_rb_str_strlen(VALUE self, VALUE str) { + return LONG2NUM(rb_str_strlen(str)); +} + VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) { rb_encoding* from_enc; rb_encoding* to_enc; @@ -600,6 +604,7 @@ void Init_string_spec(void) { rb_define_method(cls, "rb_str_cat_cstr", string_spec_rb_str_cat_cstr, 2); rb_define_method(cls, "rb_str_cat_cstr_constant", string_spec_rb_str_cat_cstr_constant, 1); rb_define_method(cls, "rb_str_cmp", string_spec_rb_str_cmp, 2); + rb_define_method(cls, "rb_str_strlen", string_spec_rb_str_strlen, 1); rb_define_method(cls, "rb_str_conv_enc", string_spec_rb_str_conv_enc, 3); rb_define_method(cls, "rb_str_conv_enc_opts", string_spec_rb_str_conv_enc_opts, 5); rb_define_method(cls, "rb_str_drop_bytes", string_spec_rb_str_drop_bytes, 2); diff --git a/spec/ruby/optional/capi/fixnum_spec.rb b/spec/ruby/optional/capi/fixnum_spec.rb index aa02a0543b7..e691aa38936 100644 --- a/spec/ruby/optional/capi/fixnum_spec.rb +++ b/spec/ruby/optional/capi/fixnum_spec.rb @@ -25,7 +25,7 @@ end end - platform_is wordsize: 64 do # sizeof(long) > sizeof(int) + platform_is c_long_size: 64 do # sizeof(long) > sizeof(int) it "raises a TypeError if passed nil" do -> { @s.FIX2INT(nil) }.should raise_error(TypeError) end @@ -74,7 +74,7 @@ end end - platform_is wordsize: 64 do # sizeof(long) > sizeof(int) + platform_is c_long_size: 64 do # sizeof(long) > sizeof(int) it "raises a TypeError if passed nil" do -> { @s.FIX2UINT(nil) }.should raise_error(TypeError) end diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb index bdec46f5e18..01588408e10 100644 --- a/spec/ruby/optional/capi/io_spec.rb +++ b/spec/ruby/optional/capi/io_spec.rb @@ -458,10 +458,6 @@ @o.rb_io_maybe_wait(Errno::EINTR::Errno, @w_io, IO::WRITABLE, nil).should == IO::WRITABLE end - it "returns false if there is no error condition" do - @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil).should == false - end - it "raises an IOError if the IO is closed" do @w_io.close -> { @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil) }.should raise_error(IOError, "closed stream") @@ -521,6 +517,14 @@ end end end + + ruby_version_is "3.4" do + describe "rb_io_maybe_wait" do + it "returns nil if there is no error condition" do + @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil).should == nil + end + end + end end describe "rb_fd_fix_cloexec" do diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb index 3b61d4f0f1c..a169813cd53 100644 --- a/spec/ruby/optional/capi/kernel_spec.rb +++ b/spec/ruby/optional/capi/kernel_spec.rb @@ -3,11 +3,19 @@ kernel_path = load_extension("kernel") +class CApiKernelSpecs::Exc < StandardError +end +exception_class = CApiKernelSpecs::Exc + describe "C-API Kernel function" do before :each do @s = CApiKernelSpecs.new end + after :each do + @s.rb_errinfo.should == nil + end + describe "rb_block_given_p" do it "returns false if no block is passed" do @s.should_not.rb_block_given_p @@ -78,6 +86,22 @@ -> { @s.rb_raise(h) }.should raise_error(TypeError) h[:stage].should == :before end + + it "re-raises a rescued exception" do + -> do + begin + raise StandardError, "aaa" + rescue Exception + begin + @s.rb_raise({}) + rescue TypeError + end + + # should raise StandardError "aaa" + raise + end + end.should raise_error(StandardError, "aaa") + end end describe "rb_throw" do @@ -295,7 +319,7 @@ it "will allow cleanup code to run after a raise" do proof = [] # Hold proof of work performed after the yield. -> do - @s.rb_protect_yield(77, proof) { |x| raise NameError} + @s.rb_protect_yield(77, proof) { |x| raise NameError } end.should raise_error(NameError) proof[0].should == 23 end @@ -303,7 +327,7 @@ it "will return nil if an error was raised" do proof = [] # Hold proof of work performed after the yield. -> do - @s.rb_protect_yield(77, proof) { |x| raise NameError} + @s.rb_protect_yield(77, proof) { |x| raise NameError } end.should raise_error(NameError) proof[0].should == 23 proof[1].should == nil @@ -311,14 +335,21 @@ it "accepts NULL as status and returns nil if it failed" do @s.rb_protect_null_status(42) { |x| x + 1 }.should == 43 - @s.rb_protect_null_status(42) { |x| raise }.should == nil + @s.rb_protect_null_status(42) { |x| raise NameError }.should == nil + @s.rb_errinfo().should.is_a? NameError + ensure + @s.rb_set_errinfo(nil) end - it "populates errinfo with the captured exception" do + it "populates rb_errinfo() with the captured exception" do proof = [] - @s.rb_protect_errinfo(77, proof) { |x| raise NameError }.class.should == NameError + @s.rb_protect_ignore_status(77, proof) { |x| raise NameError } + @s.rb_errinfo().should.is_a? NameError + # Note: on CRuby $! is the NameError here, but not clear if that is desirable or bug proof[0].should == 23 proof[1].should == nil + ensure + @s.rb_set_errinfo(nil) end end @@ -382,9 +413,21 @@ -> { @s.rb_rescue(@std_error_proc, nil, @std_error_proc, nil) }.should raise_error(StandardError) end - it "makes $! available only during the 'rescue function' execution" do - @s.rb_rescue(@std_error_proc, nil, -> *_ { $! }, nil).class.should == StandardError + it "sets $! and rb_errinfo() during the 'rescue function' execution" do + @s.rb_rescue(-> *_ { raise exception_class, '' }, nil, -> _, exc { + exc.should.is_a?(exception_class) + $!.should.equal?(exc) + @s.rb_errinfo.should.equal?(exc) + }, nil) + + @s.rb_rescue(-> _ { @s.rb_raise({}) }, nil, -> _, exc { + exc.should.is_a?(TypeError) + $!.should.equal?(exc) + @s.rb_errinfo.should.equal?(exc) + }, nil) + $!.should == nil + @s.rb_errinfo.should == nil end it "returns the break value if the passed function yields to a block with a break" do @@ -402,7 +445,7 @@ def proc_caller describe "rb_rescue2" do it "only rescues if one of the passed exceptions is raised" do - proc = -> x { x } + proc = -> x, _exc { x } arg_error_proc = -> *_ { raise ArgumentError, '' } run_error_proc = -> *_ { raise RuntimeError, '' } type_error_proc = -> *_ { raise Exception, 'custom error' } @@ -418,6 +461,23 @@ def proc_caller @s.rb_rescue2(-> *_ { raise RuntimeError, "foo" }, :no_exc, -> x { x }, :exc, Object.new, 42) }.should raise_error(TypeError, /class or module required/) end + + it "sets $! and rb_errinfo() during the 'rescue function' execution" do + @s.rb_rescue2(-> *_ { raise exception_class, '' }, :no_exc, -> _, exc { + exc.should.is_a?(exception_class) + $!.should.equal?(exc) + @s.rb_errinfo.should.equal?(exc) + }, :exc, exception_class, ScriptError) + + @s.rb_rescue2(-> *_ { @s.rb_raise({}) }, :no_exc, -> _, exc { + exc.should.is_a?(TypeError) + $!.should.equal?(exc) + @s.rb_errinfo.should.equal?(exc) + }, :exc, TypeError, ArgumentError) + + $!.should == nil + @s.rb_errinfo.should == nil + end end describe "rb_catch" do @@ -486,12 +546,33 @@ def proc_caller it "executes passed 'ensure function' when an exception is raised" do foo = nil - raise_proc = -> { raise '' } + raise_proc = -> _ { raise exception_class } ensure_proc = -> x { foo = x } - @s.rb_ensure(raise_proc, nil, ensure_proc, :foo) rescue nil + -> { + @s.rb_ensure(raise_proc, nil, ensure_proc, :foo) + }.should raise_error(exception_class) foo.should == :foo end + it "sets $! and rb_errinfo() during the 'ensure function' execution" do + -> { + @s.rb_ensure(-> _ { raise exception_class }, nil, -> _ { + $!.should.is_a?(exception_class) + @s.rb_errinfo.should.is_a?(exception_class) + }, nil) + }.should raise_error(exception_class) + + -> { + @s.rb_ensure(-> _ { @s.rb_raise({}) }, nil, -> _ { + $!.should.is_a?(TypeError) + @s.rb_errinfo.should.is_a?(TypeError) + }, nil) + }.should raise_error(TypeError) + + $!.should == nil + @s.rb_errinfo.should == nil + end + it "raises the same exception raised inside passed function" do raise_proc = -> *_ { raise RuntimeError, 'foo' } proc = -> *_ { } diff --git a/spec/ruby/optional/capi/mutex_spec.rb b/spec/ruby/optional/capi/mutex_spec.rb index 34659974f58..71a2212e36c 100644 --- a/spec/ruby/optional/capi/mutex_spec.rb +++ b/spec/ruby/optional/capi/mutex_spec.rb @@ -85,5 +85,18 @@ callback = -> { @m.locked?.should be_true } @s.rb_mutex_synchronize(@m, callback) end + + it "returns a value returned from a callback" do + callback = -> { :foo } + @s.rb_mutex_synchronize(@m, callback).should == :foo + end + + it "calls a C-function that accepts and returns non-VALUE values" do + @s.rb_mutex_synchronize_with_naughty_callback(@m).should == 42 + end + + it "calls a native function" do + @s.rb_mutex_synchronize_with_native_callback(@m, 42).should == 42 + end end end diff --git a/spec/ruby/optional/capi/numeric_spec.rb b/spec/ruby/optional/capi/numeric_spec.rb index 95213d3f2b9..e9667da5ba1 100644 --- a/spec/ruby/optional/capi/numeric_spec.rb +++ b/spec/ruby/optional/capi/numeric_spec.rb @@ -106,7 +106,7 @@ @s.NUM2LONG(5).should == 5 end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do it "converts -1 to an signed number" do @s.NUM2LONG(-1).should == -1 end @@ -120,7 +120,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "converts -1 to an signed number" do @s.NUM2LONG(-1).should == -1 end @@ -210,7 +210,7 @@ @s.NUM2ULONG(5).should == 5 end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do it "converts -1 to an unsigned number" do @s.NUM2ULONG(-1).should == 4294967295 end @@ -231,7 +231,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "converts -1 to an unsigned number" do @s.NUM2ULONG(-1).should == 18446744073709551615 end diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb index a8edf998b54..8f2244bceab 100644 --- a/spec/ruby/optional/capi/string_spec.rb +++ b/spec/ruby/optional/capi/string_spec.rb @@ -449,6 +449,20 @@ def inspect end end + describe "rb_str_strlen" do + it 'returns 0 as the length of an empty string' do + @s.rb_str_strlen('').should == 0 + end + + it 'returns the number of characters in a string' do + @s.rb_str_strlen('hello').should == 5 + end + + it 'returns the number of characters in a string with multi-byte characters' do + @s.rb_str_strlen('こんにちは').should == 5 + end + end + describe "rb_str_split" do it "splits strings over a splitter" do @s.rb_str_split("Hello,Goodbye").should == ["Hello", "Goodbye"] @@ -888,16 +902,20 @@ def inspect end it "returns the original String if a transcoding error occurs" do - a = [0xEE].pack('C').force_encoding("utf-8") - @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should equal(a) + a = [0xEE].pack('C').force_encoding(Encoding::UTF_8) + @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should.equal?(a) + a.encoding.should == Encoding::UTF_8 + + a = "\x80".b + @s.rb_str_conv_enc(a, Encoding::BINARY, Encoding::UTF_8).should.equal?(a) + a.encoding.should == Encoding::BINARY end it "returns a transcoded String" do - a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding("utf-8") + a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding(Encoding::UTF_8) result = @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP) - x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8') - result.should == x.force_encoding("euc-jp") - result.encoding.should equal(Encoding::EUC_JP) + result.should == [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding(Encoding::EUC_JP) + result.encoding.should == Encoding::EUC_JP end describe "when the String encoding is equal to the destination encoding" do @@ -1096,7 +1114,7 @@ def inspect end it "tries to convert the passed argument to a string by calling #to_s" do - @s.rb_String({"bar" => "foo"}).should == '{"bar"=>"foo"}' + @s.rb_String({"bar" => "foo"}).should == {"bar" => "foo"}.to_s end end diff --git a/spec/ruby/optional/capi/util_spec.rb b/spec/ruby/optional/capi/util_spec.rb index 9ff8b4760a8..6cf064bf973 100644 --- a/spec/ruby/optional/capi/util_spec.rb +++ b/spec/ruby/optional/capi/util_spec.rb @@ -209,7 +209,7 @@ end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do describe "rb_long2int" do it "raises a RangeError if the value is outside the range of a C int" do -> { @o.rb_long2int(0xffff_ffff_ffff) }.should raise_error(RangeError) diff --git a/spec/ruby/shared/queue/freeze.rb b/spec/ruby/shared/queue/freeze.rb new file mode 100644 index 00000000000..4c506a42355 --- /dev/null +++ b/spec/ruby/shared/queue/freeze.rb @@ -0,0 +1,18 @@ +describe :queue_freeze, shared: true do + ruby_version_is ""..."3.3" do + it "can be frozen" do + queue = @object.call + queue.freeze + queue.should.frozen? + end + end + + ruby_version_is "3.3" do + it "raises an exception when freezing" do + queue = @object.call + -> { + queue.freeze + }.should raise_error(TypeError, "cannot freeze #{queue}") + end + end +end diff --git a/spec/ruby/shared/string/times.rb b/spec/ruby/shared/string/times.rb index aaf748bad9f..4814f894cf0 100644 --- a/spec/ruby/shared/string/times.rb +++ b/spec/ruby/shared/string/times.rb @@ -44,13 +44,13 @@ class MyString < String; end result.encoding.should equal(Encoding::UTF_8) end - platform_is wordsize: 32 do + platform_is c_long_size: 32 do it "raises an ArgumentError if the length of the resulting string doesn't fit into a long" do -> { @object.call("abc", (2 ** 31) - 1) }.should raise_error(ArgumentError) end end - platform_is wordsize: 64 do + platform_is c_long_size: 64 do it "raises an ArgumentError if the length of the resulting string doesn't fit into a long" do -> { @object.call("abc", (2 ** 63) - 1) }.should raise_error(ArgumentError) end From f054980f1165b945b64c0945b6d1cba907eb09c0 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 13:08:43 -0600 Subject: [PATCH 06/17] Actually freeze several unfrozen globals These global variables were supposed to have been frozen years ago but the original attempt was a copy/paste failure. --- core/src/main/java/org/jruby/RubyGlobal.java | 31 +++++++------------- core/src/main/java/org/jruby/RubyString.java | 11 +++++++ 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyGlobal.java b/core/src/main/java/org/jruby/RubyGlobal.java index 635da1303b9..663d47e4cde 100644 --- a/core/src/main/java/org/jruby/RubyGlobal.java +++ b/core/src/main/java/org/jruby/RubyGlobal.java @@ -129,47 +129,38 @@ public static void createGlobals(Ruby runtime) { // Version information: IRubyObject version; IRubyObject patchlevel; - IRubyObject release = runtime.newString(Constants.COMPILE_DATE); - release.setFrozen(true); - IRubyObject platform = runtime.newString(Constants.PLATFORM); - release.setFrozen(true); - IRubyObject engine = runtime.newString(Constants.ENGINE); - release.setFrozen(true); - - version = runtime.newString(Constants.RUBY_VERSION); - release.setFrozen(true); + IRubyObject release = RubyString.newFString(runtime, Constants.COMPILE_DATE); + IRubyObject platform = RubyString.newFString(runtime, Constants.PLATFORM); + IRubyObject engine = RubyString.newFString(runtime, Constants.ENGINE); + + version = RubyString.newFString(runtime, Constants.RUBY_VERSION); patchlevel = runtime.newFixnum(0); runtime.defineGlobalConstant("RUBY_VERSION", version); runtime.defineGlobalConstant("RUBY_PATCHLEVEL", patchlevel); runtime.defineGlobalConstant("RUBY_RELEASE_DATE", release); runtime.defineGlobalConstant("RUBY_PLATFORM", platform); - IRubyObject description = runtime.newString(OutputStrings.getVersionString()); - release.setFrozen(true); + IRubyObject description = RubyString.newFString(runtime, OutputStrings.getVersionString()); runtime.defineGlobalConstant("RUBY_DESCRIPTION", description); - IRubyObject copyright = runtime.newString(OutputStrings.getCopyrightString()); - release.setFrozen(true); + IRubyObject copyright = RubyString.newFString(runtime, OutputStrings.getCopyrightString()); runtime.defineGlobalConstant("RUBY_COPYRIGHT", copyright); runtime.defineGlobalConstant("RELEASE_DATE", release); runtime.defineGlobalConstant("PLATFORM", platform); - IRubyObject jrubyVersion = runtime.newString(Constants.VERSION); - release.setFrozen(true); - IRubyObject jrubyRevision = runtime.newString(Constants.REVISION); - release.setFrozen(true); + IRubyObject jrubyVersion = RubyString.newFString(runtime, Constants.VERSION); + IRubyObject jrubyRevision = RubyString.newFString(runtime, Constants.REVISION); runtime.defineGlobalConstant("JRUBY_VERSION", jrubyVersion); runtime.defineGlobalConstant("JRUBY_REVISION", jrubyRevision); - runtime.defineGlobalConstant("RUBY_REVISION", runtime.newString(Constants.REVISION)); + runtime.defineGlobalConstant("RUBY_REVISION", RubyString.newFString(runtime, Constants.REVISION)); runtime.defineGlobalConstant("RUBY_ENGINE", engine); runtime.defineGlobalConstant("RUBY_ENGINE_VERSION", jrubyVersion); RubyInstanceConfig.Verbosity verbosity = runtime.getInstanceConfig().getVerbosity(); runtime.defineVariable(new WarningGlobalVariable(runtime, "$-W", verbosity), GLOBAL); - IRubyObject defaultRS = runtime.newString(runtime.getInstanceConfig().getRecordSeparator()); - release.setFrozen(true); + IRubyObject defaultRS = RubyString.newFString(runtime, runtime.getInstanceConfig().getRecordSeparator()); GlobalVariable rs = new StringGlobalVariable(runtime, "$/", defaultRS); runtime.defineVariable(rs, GLOBAL); runtime.setRecordSeparatorVar(rs); diff --git a/core/src/main/java/org/jruby/RubyString.java b/core/src/main/java/org/jruby/RubyString.java index 2dbabf729c9..22c8e4df9d3 100644 --- a/core/src/main/java/org/jruby/RubyString.java +++ b/core/src/main/java/org/jruby/RubyString.java @@ -1037,6 +1037,17 @@ public void setReadLength(int length) { } } + /** + * Create anew or deduplicate a RubyString based on the given Java String content. + * + * @param runtime the JRuby runtime + * @param content the Java String content + * @return a frozen, deduplicated RubyString hosting the given content + */ + public static RubyString newFString(Ruby runtime, String content) { + return runtime.freezeAndDedupString(newString(runtime, content)); + } + // MRI: rb_str_new_frozen, at least in spirit // also aliased to rb_str_new4 public RubyString newFrozen() { From 3c975976b048f8d2e1d6c427862fd7cd40ad2ee8 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 13:19:21 -0600 Subject: [PATCH 07/17] Off-by-one checking for extraneous format args nextIndex is 1-based so the check should be <= total given args. --- core/src/main/java/org/jruby/util/Sprintf.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/jruby/util/Sprintf.java b/core/src/main/java/org/jruby/util/Sprintf.java index 4b07298ce40..6dec732b3ea 100644 --- a/core/src/main/java/org/jruby/util/Sprintf.java +++ b/core/src/main/java/org/jruby/util/Sprintf.java @@ -1550,7 +1550,7 @@ else if (precision > 0) { } // main while loop (offset < length) // MRI behavior: validate only the unnumbered arguments - if (args.positionIndex >= 0 && args.nextIndex < args.length) { + if (args.positionIndex >= 0 && args.nextIndex <= args.length) { if (args.runtime.isDebug()) { args.raiseArgumentError("too many arguments for format string"); } else if (args.runtime.isVerbose()) { From c0eb792a2ac2091c5d526c9611f5e795e9f15780 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 13:44:21 -0600 Subject: [PATCH 08/17] Don't add equivalent finalizer twice --- .../main/java/org/jruby/RubyBasicObject.java | 28 +++++++++++++++++-- .../main/java/org/jruby/RubyObjectSpace.java | 11 ++++++-- .../org/jruby/ir/operands/UndefinedValue.java | 4 +++ .../java/org/jruby/runtime/ObjectSpace.java | 7 ++++- .../jruby/runtime/builtin/IRubyObject.java | 6 ++++ 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyBasicObject.java b/core/src/main/java/org/jruby/RubyBasicObject.java index f2c7ae270ce..d855f339de9 100644 --- a/core/src/main/java/org/jruby/RubyBasicObject.java +++ b/core/src/main/java/org/jruby/RubyBasicObject.java @@ -1242,11 +1242,17 @@ public boolean eql(IRubyObject other) { return invokedynamic(metaClass.runtime.getCurrentContext(), this, EQL, other).isTrue(); } + @Deprecated + @Override + public void addFinalizer(IRubyObject f) { + addFinalizer(getRuntime().getCurrentContext(), f); + } + /** * Adds the specified object as a finalizer for this object. */ @Override - public void addFinalizer(IRubyObject f) { + public void addFinalizer(ThreadContext context, IRubyObject f) { Finalizer finalizer = (Finalizer) getInternalVariable("__finalizer__"); if (finalizer == null) { // since this is the first time we're registering a finalizer, we @@ -1262,7 +1268,7 @@ public void addFinalizer(IRubyObject f) { setInternalVariable("__finalizer__", finalizer); getRuntime().addFinalizer(finalizer); } - finalizer.addFinalizer(f); + finalizer.addFinalizer(context, f); } /** @@ -1918,11 +1924,27 @@ public Finalizer(RubyFixnum id) { this.finalized = new AtomicBoolean(false); } + @Deprecated public void addFinalizer(IRubyObject finalizer) { + addFinalizer(finalizer.getRuntime().getCurrentContext(), finalizer); + } + + public void addFinalizer(ThreadContext context, IRubyObject finalizer) { if (firstFinalizer == null) { firstFinalizer = finalizer; } else { - if (finalizers == null) finalizers = new ArrayList(4); + if (firstFinalizer.op_equal(context, finalizer).isTrue()) { + // do not add equivalent finalizer twice + return; + } + + if (finalizers == null) { + finalizers = new ArrayList(4); + } else if (finalizers.stream().anyMatch((f) -> finalizer.op_equal(context, finalizer).isTrue())) { + // do not add equivalent finalizer twice + return; + } + finalizers.add(finalizer); } } diff --git a/core/src/main/java/org/jruby/RubyObjectSpace.java b/core/src/main/java/org/jruby/RubyObjectSpace.java index 1b1239295e2..8f429d99190 100644 --- a/core/src/main/java/org/jruby/RubyObjectSpace.java +++ b/core/src/main/java/org/jruby/RubyObjectSpace.java @@ -70,9 +70,14 @@ public static RubyModule createObjectSpaceModule(Ruby runtime) { return objectSpaceModule; } - @JRubyMethod(required = 1, optional = 1, checkArity = false, module = true, visibility = PRIVATE) + @Deprecated public static IRubyObject define_finalizer(IRubyObject recv, IRubyObject[] args, Block block) { - Ruby runtime = recv.getRuntime(); + return define_finalizer(recv.getRuntime().getCurrentContext(), recv, args, block); + } + + @JRubyMethod(required = 1, optional = 1, checkArity = false, module = true, visibility = PRIVATE) + public static IRubyObject define_finalizer(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { + Ruby runtime = context.runtime; int argc = Arity.checkArgumentCount(runtime, args, 1, 2); @@ -93,7 +98,7 @@ public static IRubyObject define_finalizer(IRubyObject recv, IRubyObject[] args, if (blockReferencesObject(obj, block)) referenceWarning(runtime); finalizer = runtime.newProc(Block.Type.PROC, block); } - runtime.getObjectSpace().addFinalizer(obj, finalizer); + runtime.getObjectSpace().addFinalizer(context, obj, finalizer); return runtime.newArray(RubyFixnum.zero(runtime), finalizer); } diff --git a/core/src/main/java/org/jruby/ir/operands/UndefinedValue.java b/core/src/main/java/org/jruby/ir/operands/UndefinedValue.java index 11307fdfa7f..61dda1c63de 100644 --- a/core/src/main/java/org/jruby/ir/operands/UndefinedValue.java +++ b/core/src/main/java/org/jruby/ir/operands/UndefinedValue.java @@ -231,9 +231,13 @@ private RuntimeException undefinedOperation() { @Override public boolean eql(IRubyObject other) { throw undefinedOperation(); } + @SuppressWarnings("deprecation") @Override public void addFinalizer(IRubyObject finalizer) { throw undefinedOperation(); } + @Override + public void addFinalizer(ThreadContext context, IRubyObject finalizer) { throw undefinedOperation(); } + @Override public void removeFinalizers() { throw undefinedOperation(); } diff --git a/core/src/main/java/org/jruby/runtime/ObjectSpace.java b/core/src/main/java/org/jruby/runtime/ObjectSpace.java index c21214f8e7a..064442cf7d6 100644 --- a/core/src/main/java/org/jruby/runtime/ObjectSpace.java +++ b/core/src/main/java/org/jruby/runtime/ObjectSpace.java @@ -120,8 +120,13 @@ public long idOf(IRubyObject rubyObject) { return createAndRegisterObjectId(rubyObject); } + @Deprecated public void addFinalizer(IRubyObject object, IRubyObject proc) { - object.addFinalizer(proc); + addFinalizer(object.getRuntime().getCurrentContext(), object, proc); + } + + public void addFinalizer(ThreadContext context, IRubyObject object, IRubyObject proc) { + object.addFinalizer(context, proc); } public void removeFinalizers(long id) { diff --git a/core/src/main/java/org/jruby/runtime/builtin/IRubyObject.java b/core/src/main/java/org/jruby/runtime/builtin/IRubyObject.java index da425d1fbb9..0f4e06e8465 100644 --- a/core/src/main/java/org/jruby/runtime/builtin/IRubyObject.java +++ b/core/src/main/java/org/jruby/runtime/builtin/IRubyObject.java @@ -312,8 +312,14 @@ static IRubyObject[] array(int length) { public IRubyObject op_eqq(ThreadContext context, IRubyObject other); public boolean eql(IRubyObject other); + @Deprecated public void addFinalizer(IRubyObject finalizer); + @SuppressWarnings("deprecation") + public default void addFinalizer(ThreadContext context, IRubyObject finalizer) { + addFinalizer(finalizer); + } + public void removeFinalizers(); // From 6eb86560c2065edc6ec7456d22a89d614366fa70 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 14:01:15 -0600 Subject: [PATCH 09/17] Return previous equivalent finalizer if found --- .../main/java/org/jruby/RubyBasicObject.java | 40 ++++++++++++------- .../main/java/org/jruby/RubyObjectSpace.java | 2 +- .../org/jruby/ir/operands/UndefinedValue.java | 2 +- .../java/org/jruby/runtime/ObjectSpace.java | 4 +- .../jruby/runtime/builtin/IRubyObject.java | 3 +- 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyBasicObject.java b/core/src/main/java/org/jruby/RubyBasicObject.java index d855f339de9..09f8a877b40 100644 --- a/core/src/main/java/org/jruby/RubyBasicObject.java +++ b/core/src/main/java/org/jruby/RubyBasicObject.java @@ -1252,7 +1252,7 @@ public void addFinalizer(IRubyObject f) { * Adds the specified object as a finalizer for this object. */ @Override - public void addFinalizer(ThreadContext context, IRubyObject f) { + public IRubyObject addFinalizer(ThreadContext context, IRubyObject f) { Finalizer finalizer = (Finalizer) getInternalVariable("__finalizer__"); if (finalizer == null) { // since this is the first time we're registering a finalizer, we @@ -1268,7 +1268,7 @@ public void addFinalizer(ThreadContext context, IRubyObject f) { setInternalVariable("__finalizer__", finalizer); getRuntime().addFinalizer(finalizer); } - finalizer.addFinalizer(context, f); + return finalizer.addFinalizer(context, f); } /** @@ -1929,24 +1929,36 @@ public void addFinalizer(IRubyObject finalizer) { addFinalizer(finalizer.getRuntime().getCurrentContext(), finalizer); } - public void addFinalizer(ThreadContext context, IRubyObject finalizer) { + public IRubyObject addFinalizer(ThreadContext context, IRubyObject finalizer) { if (firstFinalizer == null) { firstFinalizer = finalizer; + + return finalizer; + } + + IRubyObject existing = firstFinalizer; + + if (existing.op_equal(context, finalizer).isTrue()) { + // do not add equivalent finalizer twice + return existing; + } + + if (finalizers == null) { + finalizers = new ArrayList<>(4); } else { - if (firstFinalizer.op_equal(context, finalizer).isTrue()) { - // do not add equivalent finalizer twice - return; - } + for (int i = 0; i < finalizers.size(); i++) { + existing = finalizers.get(i); - if (finalizers == null) { - finalizers = new ArrayList(4); - } else if (finalizers.stream().anyMatch((f) -> finalizer.op_equal(context, finalizer).isTrue())) { - // do not add equivalent finalizer twice - return; + if (existing.op_equal(context, finalizer).isTrue()) { + // do not add equivalent finalizer twice + return existing; + } } - - finalizers.add(finalizer); } + + finalizers.add(finalizer); + + return finalizer; } public void removeFinalizers() { diff --git a/core/src/main/java/org/jruby/RubyObjectSpace.java b/core/src/main/java/org/jruby/RubyObjectSpace.java index 8f429d99190..fe6f15a2c28 100644 --- a/core/src/main/java/org/jruby/RubyObjectSpace.java +++ b/core/src/main/java/org/jruby/RubyObjectSpace.java @@ -98,7 +98,7 @@ public static IRubyObject define_finalizer(ThreadContext context, IRubyObject se if (blockReferencesObject(obj, block)) referenceWarning(runtime); finalizer = runtime.newProc(Block.Type.PROC, block); } - runtime.getObjectSpace().addFinalizer(context, obj, finalizer); + finalizer = runtime.getObjectSpace().addFinalizer(context, obj, finalizer); return runtime.newArray(RubyFixnum.zero(runtime), finalizer); } diff --git a/core/src/main/java/org/jruby/ir/operands/UndefinedValue.java b/core/src/main/java/org/jruby/ir/operands/UndefinedValue.java index 61dda1c63de..4fc0b6cfbea 100644 --- a/core/src/main/java/org/jruby/ir/operands/UndefinedValue.java +++ b/core/src/main/java/org/jruby/ir/operands/UndefinedValue.java @@ -236,7 +236,7 @@ private RuntimeException undefinedOperation() { public void addFinalizer(IRubyObject finalizer) { throw undefinedOperation(); } @Override - public void addFinalizer(ThreadContext context, IRubyObject finalizer) { throw undefinedOperation(); } + public IRubyObject addFinalizer(ThreadContext context, IRubyObject finalizer) { throw undefinedOperation(); } @Override public void removeFinalizers() { throw undefinedOperation(); } diff --git a/core/src/main/java/org/jruby/runtime/ObjectSpace.java b/core/src/main/java/org/jruby/runtime/ObjectSpace.java index 064442cf7d6..63bbee66281 100644 --- a/core/src/main/java/org/jruby/runtime/ObjectSpace.java +++ b/core/src/main/java/org/jruby/runtime/ObjectSpace.java @@ -125,8 +125,8 @@ public void addFinalizer(IRubyObject object, IRubyObject proc) { addFinalizer(object.getRuntime().getCurrentContext(), object, proc); } - public void addFinalizer(ThreadContext context, IRubyObject object, IRubyObject proc) { - object.addFinalizer(context, proc); + public IRubyObject addFinalizer(ThreadContext context, IRubyObject object, IRubyObject proc) { + return object.addFinalizer(context, proc); } public void removeFinalizers(long id) { diff --git a/core/src/main/java/org/jruby/runtime/builtin/IRubyObject.java b/core/src/main/java/org/jruby/runtime/builtin/IRubyObject.java index 0f4e06e8465..70171bd6a39 100644 --- a/core/src/main/java/org/jruby/runtime/builtin/IRubyObject.java +++ b/core/src/main/java/org/jruby/runtime/builtin/IRubyObject.java @@ -316,8 +316,9 @@ static IRubyObject[] array(int length) { public void addFinalizer(IRubyObject finalizer); @SuppressWarnings("deprecation") - public default void addFinalizer(ThreadContext context, IRubyObject finalizer) { + public default IRubyObject addFinalizer(ThreadContext context, IRubyObject finalizer) { addFinalizer(finalizer); + return finalizer; } public void removeFinalizers(); From 9e29163550336739b1b273e634bd0f2d51dbf552 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 14:04:31 -0600 Subject: [PATCH 10/17] Do not remove finalizers if frozen --- core/src/main/java/org/jruby/RubyBasicObject.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/jruby/RubyBasicObject.java b/core/src/main/java/org/jruby/RubyBasicObject.java index 09f8a877b40..4f191742204 100644 --- a/core/src/main/java/org/jruby/RubyBasicObject.java +++ b/core/src/main/java/org/jruby/RubyBasicObject.java @@ -1276,6 +1276,7 @@ public IRubyObject addFinalizer(ThreadContext context, IRubyObject f) { */ @Override public void removeFinalizers() { + checkFrozen(); Finalizer finalizer = (Finalizer) getInternalVariable("__finalizer__"); if (finalizer != null) { finalizer.removeFinalizers(); From 8959cdaeaa64e812b1bb62a29a56108dde2f72dd Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 14:21:58 -0600 Subject: [PATCH 11/17] Fix Thread variable management spec failures * Thread#[]=, #[], #key?, #thread_variable_get, #thread_variable_set, and #thread_variable? should coerce incoming keys to Symbol through #to_str. * Thread#[]= reports error matching CRuby when Thread is frozen. --- core/src/main/java/org/jruby/Ruby.java | 9 +++- .../main/java/org/jruby/RubyBasicObject.java | 8 +-- core/src/main/java/org/jruby/RubyBinding.java | 2 +- core/src/main/java/org/jruby/RubyKernel.java | 2 +- core/src/main/java/org/jruby/RubyModule.java | 2 +- core/src/main/java/org/jruby/RubySymbol.java | 50 +++++++++++++++---- core/src/main/java/org/jruby/RubyThread.java | 23 ++++++--- .../java/org/jruby/runtime/JavaSites.java | 5 ++ 8 files changed, 76 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/org/jruby/Ruby.java b/core/src/main/java/org/jruby/Ruby.java index 541bb1f4454..e98ca615261 100644 --- a/core/src/main/java/org/jruby/Ruby.java +++ b/core/src/main/java/org/jruby/Ruby.java @@ -4148,9 +4148,16 @@ public RaiseException newFrozenError(IRubyObject receiver) { ThreadContext context = getCurrentContext(); IRubyObject inspected = context.safeRecurse(Ruby::inspectFrozenObject, this, receiver, "inspect", true); + String message = "can't modify frozen " + receiver.getType() + ": " + inspected.convertToString().toString(); + + return newFrozenError(receiver, message); + } + + public RaiseException newFrozenError(IRubyObject receiver, String message) { + ThreadContext context = getCurrentContext(); return RubyFrozenError.newFrozenError(context, - newString("can't modify frozen " + receiver.getType() + ": " + inspected.convertToString().toString()), + newString(message), receiver) .toThrowable(); } diff --git a/core/src/main/java/org/jruby/RubyBasicObject.java b/core/src/main/java/org/jruby/RubyBasicObject.java index 4f191742204..337a2593cab 100644 --- a/core/src/main/java/org/jruby/RubyBasicObject.java +++ b/core/src/main/java/org/jruby/RubyBasicObject.java @@ -1662,7 +1662,7 @@ public static IRubyObject method_missing19(ThreadContext context, IRubyObject re @JRubyMethod(name = "__send__", omit = true, keywords = true) public IRubyObject send(ThreadContext context, IRubyObject arg0, Block block) { - String name = RubySymbol.checkID(arg0); + String name = RubySymbol.idStringFromObject(context, arg0); StaticScope staticScope = context.getCurrentStaticScope(); @@ -1670,7 +1670,7 @@ public IRubyObject send(ThreadContext context, IRubyObject arg0, Block block) { } @JRubyMethod(name = "__send__", omit = true, keywords = true) public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) { - String name = RubySymbol.checkID(arg0); + String name = RubySymbol.idStringFromObject(context, arg0); StaticScope staticScope = context.getCurrentStaticScope(); @@ -1679,7 +1679,7 @@ public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg } @JRubyMethod(name = "__send__", omit = true, keywords = true) public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) { - String name = RubySymbol.checkID(arg0); + String name = RubySymbol.idStringFromObject(context, arg0); StaticScope staticScope = context.getCurrentStaticScope(); @@ -1711,7 +1711,7 @@ public IRubyObject send(ThreadContext context, IRubyObject[] args, Block block) args[argc - 1] = dupIfKeywordRestAtCallsite(context, args[argc - 1]); } } - String name = RubySymbol.checkID(args[0]); + String name = RubySymbol.idStringFromObject(context, args[0]); StaticScope staticScope = context.getCurrentStaticScope(); diff --git a/core/src/main/java/org/jruby/RubyBinding.java b/core/src/main/java/org/jruby/RubyBinding.java index ce36b1944ca..871119cde6f 100644 --- a/core/src/main/java/org/jruby/RubyBinding.java +++ b/core/src/main/java/org/jruby/RubyBinding.java @@ -172,7 +172,7 @@ public IRubyObject local_variable_set(ThreadContext context, IRubyObject symbol, // MRI: check_local_id private String checkLocalId(ThreadContext context, IRubyObject obj) { - String id = RubySymbol.checkID(obj); + String id = RubySymbol.idStringFromObject(context, obj); if (!RubyLexer.isIdentifierChar(id.charAt(0))) { throw context.runtime.newNameError(str(context.runtime, "wrong local variable name `", obj, "' for ", this), id); diff --git a/core/src/main/java/org/jruby/RubyKernel.java b/core/src/main/java/org/jruby/RubyKernel.java index 2a0bdf16887..58379dfe26c 100644 --- a/core/src/main/java/org/jruby/RubyKernel.java +++ b/core/src/main/java/org/jruby/RubyKernel.java @@ -2230,7 +2230,7 @@ public static IRubyObject public_send(ThreadContext context, IRubyObject recv, I throw context.runtime.newArgumentError("no method name given"); } - String name = RubySymbol.checkID(args[0]); + String name = RubySymbol.idStringFromObject(context, args[0]); if (args.length > 1) { args[args.length - 1] = dupIfKeywordRestAtCallsite(context, args[args.length - 1]); diff --git a/core/src/main/java/org/jruby/RubyModule.java b/core/src/main/java/org/jruby/RubyModule.java index 6114b4326ed..fa1f1f8d5f2 100644 --- a/core/src/main/java/org/jruby/RubyModule.java +++ b/core/src/main/java/org/jruby/RubyModule.java @@ -2985,7 +2985,7 @@ public IRubyObject ruby2_keywords(ThreadContext context, IRubyObject[] args) { checkFrozen(); for (IRubyObject name: args) { - String id = RubySymbol.checkID(name); + String id = RubySymbol.idStringFromObject(context, name); // FIXME: id == null or bad symbol error missing diff --git a/core/src/main/java/org/jruby/RubySymbol.java b/core/src/main/java/org/jruby/RubySymbol.java index 6ec5001d656..11654541202 100644 --- a/core/src/main/java/org/jruby/RubySymbol.java +++ b/core/src/main/java/org/jruby/RubySymbol.java @@ -53,6 +53,7 @@ import org.jruby.runtime.ClassIndex; import org.jruby.runtime.ContextAwareBlockBody; import org.jruby.runtime.Helpers; +import org.jruby.runtime.JavaSites; import org.jruby.runtime.MethodIndex; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.Signature; @@ -1471,21 +1472,48 @@ public static String objectToSymbolString(IRubyObject object) { return object.convertToString().getByteList().toString(); } - // MRI: rb_check_id + @Deprecated public static String checkID(IRubyObject object) { - if (object instanceof RubySymbol) return ((RubySymbol) object).idString(); + return idStringFromObject(object.getRuntime().getCurrentContext(), object); + } - if (!(object instanceof RubyString)) { - IRubyObject tmp = TypeConverter.checkStringType(object.getRuntime(), object); + // MRI: rb_check_id but producing a Java String + public static String idStringFromObject(ThreadContext context, IRubyObject object) { + IRubyObject symOrStr = prepareID(object.getRuntime().getCurrentContext(), object); - if (tmp.isNil()) { - throw object.getRuntime().newTypeError(str(object.getRuntime(), "", object, " is not a symbol nor a string")); - } + if (symOrStr instanceof RubySymbol) return ((RubySymbol) symOrStr).idString(); + + return ((RubyString) symOrStr).getByteList().toString(); + } + + // MRI: rb_check_id + public static RubySymbol idSymbolFromObject(ThreadContext context, IRubyObject object) { + IRubyObject symOrStr = prepareID(context, object); + + if (symOrStr instanceof RubySymbol) return (RubySymbol) symOrStr; + + return newSymbol(context.runtime, ((RubyString) symOrStr).getByteList()); + } + + /** + * Return the given object if it is a Symbol or String, or convert it to a String. + * + * @param context the current context + * @param object the object + * @return the object, if it is a Symbol or String, or a String produced by calling #to_str on the object. + */ + public static IRubyObject prepareID(ThreadContext context, IRubyObject object) { + if (object instanceof RubySymbol || object instanceof RubyString) return object; - object = tmp; + Ruby runtime = context.runtime; + + IRubyObject tmp = TypeConverter.checkStringType(context, sites(context).to_str_checked, object); + + if (tmp.isNil()) { + throw runtime.newTypeError(str(runtime, "", object, " is not a symbol nor a string")); } - return ((RubyString) object).getByteList().toString(); + return tmp; } public static final class SymbolProcBody extends ContextAwareBlockBody { @@ -1571,6 +1599,10 @@ public ArgumentDescriptor[] getArgumentDescriptors() { } } + private static JavaSites.SymbolSites sites(ThreadContext context) { + return context.sites.Symbol; + } + @Deprecated @Override public IRubyObject taint(ThreadContext context) { diff --git a/core/src/main/java/org/jruby/RubyThread.java b/core/src/main/java/org/jruby/RubyThread.java index 6b91b62627a..5c5fe9f69f6 100644 --- a/core/src/main/java/org/jruby/RubyThread.java +++ b/core/src/main/java/org/jruby/RubyThread.java @@ -1100,7 +1100,7 @@ public IRubyObject fetch(ThreadContext context, IRubyObject key, IRubyObject _de @JRubyMethod(name = "[]") public IRubyObject op_aref(ThreadContext context, IRubyObject key) { - key = getSymbolKey(key); + key = RubySymbol.idSymbolFromObject(context, key); final Map locals = getFiberLocals(); synchronized (locals) { IRubyObject value; @@ -1109,10 +1109,12 @@ public IRubyObject op_aref(ThreadContext context, IRubyObject key) { } @JRubyMethod(name = "[]=") - public IRubyObject op_aset(IRubyObject key, IRubyObject value) { - checkFrozen(); + public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) { + if (isFrozen()) { + throw getRuntime().newFrozenError(this, "can't modify frozen thread locals"); + } - key = getSymbolKey(key); + key = RubySymbol.idSymbolFromObject(context, key); final Map locals = getFiberLocals(); synchronized (locals) { locals.put(key, value); @@ -1122,7 +1124,7 @@ public IRubyObject op_aset(IRubyObject key, IRubyObject value) { @JRubyMethod(name = "key?") public RubyBoolean key_p(ThreadContext context, IRubyObject key) { - key = getSymbolKey(key); + key = RubySymbol.idSymbolFromObject(context, key); final Map locals = getFiberLocals(); synchronized (locals) { return RubyBoolean.newBoolean(context, locals.containsKey(key)); @@ -1145,7 +1147,7 @@ public RubyArray keys() { @JRubyMethod(name = "thread_variable?") public IRubyObject thread_variable_p(ThreadContext context, IRubyObject key) { - key = getSymbolKey(key); + key = RubySymbol.idSymbolFromObject(context, key); final Map locals = getThreadLocals(); synchronized (locals) { return RubyBoolean.newBoolean(context, locals.containsKey(key)); @@ -1154,7 +1156,7 @@ public IRubyObject thread_variable_p(ThreadContext context, IRubyObject key) { @JRubyMethod(name = "thread_variable_get") public IRubyObject thread_variable_get(ThreadContext context, IRubyObject key) { - key = getSymbolKey(key); + key = RubySymbol.idSymbolFromObject(context, key); final Map locals = getThreadLocals(); synchronized (locals) { IRubyObject value; @@ -1165,7 +1167,7 @@ public IRubyObject thread_variable_get(ThreadContext context, IRubyObject key) { @JRubyMethod(name = "thread_variable_set") public IRubyObject thread_variable_set(ThreadContext context, IRubyObject key, IRubyObject value) { checkFrozen(); - key = getSymbolKey(key); + key = RubySymbol.idSymbolFromObject(context, key); final Map locals = getThreadLocals(); synchronized (locals) { locals.put(key, value); @@ -2682,4 +2684,9 @@ public IRubyObject join(ThreadContext context, IRubyObject[] args) { throw context.runtime.newArgumentError(args.length, 0, 1); } } + + @Deprecated + public IRubyObject op_aset(IRubyObject key, IRubyObject value) { + return op_aset(getRuntime().getCurrentContext(), key, value); + } } diff --git a/core/src/main/java/org/jruby/runtime/JavaSites.java b/core/src/main/java/org/jruby/runtime/JavaSites.java index 92a2e915707..638487d0d22 100644 --- a/core/src/main/java/org/jruby/runtime/JavaSites.java +++ b/core/src/main/java/org/jruby/runtime/JavaSites.java @@ -55,6 +55,7 @@ public class JavaSites { public final StructSites Struct = new StructSites(); public final DigestSites Digest = new DigestSites(); public final MethodSites Method = new MethodSites(); + public final SymbolSites Symbol = new SymbolSites(); public static class BasicObjectSites { public final CallSite respond_to = new FunctionalCachingCallSite("respond_to?"); @@ -551,6 +552,10 @@ public static class MethodSites { public final CallSite curry = new FunctionalCachingCallSite("curry"); } + public static class SymbolSites { + public final CheckedSites to_str_checked = new CheckedSites("to_str"); + } + public static class CheckedSites { public final RespondToCallSite respond_to_X; public final CachingCallSite respond_to_missing = new FunctionalCachingCallSite("respond_to_missing?"); From e68e96d4a4826faf55fa87988343ab37bd75aced Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 14:58:52 -0600 Subject: [PATCH 12/17] Add RbConfig::CONFIG["PATCHLEVEL"] for compat with CRuby --- core/src/main/java/org/jruby/ext/rbconfig/RbConfigLibrary.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/jruby/ext/rbconfig/RbConfigLibrary.java b/core/src/main/java/org/jruby/ext/rbconfig/RbConfigLibrary.java index 699809759db..bfacb7e0766 100644 --- a/core/src/main/java/org/jruby/ext/rbconfig/RbConfigLibrary.java +++ b/core/src/main/java/org/jruby/ext/rbconfig/RbConfigLibrary.java @@ -241,6 +241,7 @@ public void load(Ruby runtime, boolean wrap) { setConfig(context, CONFIG, "MAJOR", major); setConfig(context, CONFIG, "MINOR", minor); setConfig(context, CONFIG, "TEENY", teeny); + setConfig(context, CONFIG, "PATCHLEVEL", "0"); setConfig(context, CONFIG, "ruby_version", major + '.' + minor + ".0"); // Rubygems is too specific on host cpu so until we have real need lets default to universal //setConfig(CONFIG, "arch", System.getProperty("os.arch") + "-java" + System.getProperty("java.specification.version")); From f37acf123dfb02f1f65fccd0448681e001c3168c Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 15:02:28 -0600 Subject: [PATCH 13/17] Temporarily tag these specs until ruby/stringio#101 is released --- .../stringio/set_encoding_by_bom_tags.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 spec/tags/ruby/library/stringio/set_encoding_by_bom_tags.txt diff --git a/spec/tags/ruby/library/stringio/set_encoding_by_bom_tags.txt b/spec/tags/ruby/library/stringio/set_encoding_by_bom_tags.txt new file mode 100644 index 00000000000..59c5e97111e --- /dev/null +++ b/spec/tags/ruby/library/stringio/set_encoding_by_bom_tags.txt @@ -0,0 +1,18 @@ +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns nil if not readable +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns the result encoding if found BOM UTF-8 sequence +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns the result encoding if found BOM UTF_16LE sequence +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns the result encoding if found BOM UTF_16BE sequence +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns the result encoding if found BOM UTF_32LE sequence +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns the result encoding if found BOM UTF_32BE sequence +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns nil if io is empty +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns nil if UTF-8 BOM sequence is incomplete +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns nil if UTF-16BE BOM sequence is incomplete +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns nil if UTF-16LE/UTF-32LE BOM sequence is incomplete +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns UTF-16LE if UTF-32LE BOM sequence is incomplete +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns nil if UTF-32BE BOM sequence is incomplete +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom returns nil if found BOM sequence not provided +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom does not raise exception if io not in binary mode +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom does not raise exception if encoding already set +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom does not raise exception if encoding conversion is already set +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom raises FrozenError when io is frozen +fails(waiting on release of ruby/stringio#101):StringIO#set_encoding_by_bom does not raise FrozenError when initial string is frozen From feb1ec55c6bca7a2b11f4257860ed4e12e0cc654 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 15:11:36 -0600 Subject: [PATCH 14/17] Don't check format args length if passed as Hash --- core/src/main/java/org/jruby/util/Sprintf.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/jruby/util/Sprintf.java b/core/src/main/java/org/jruby/util/Sprintf.java index 6dec732b3ea..7ddd89bdbeb 100644 --- a/core/src/main/java/org/jruby/util/Sprintf.java +++ b/core/src/main/java/org/jruby/util/Sprintf.java @@ -1550,7 +1550,7 @@ else if (precision > 0) { } // main while loop (offset < length) // MRI behavior: validate only the unnumbered arguments - if (args.positionIndex >= 0 && args.nextIndex <= args.length) { + if (args.rubyHash == null && args.positionIndex >= 0 && args.nextIndex <= args.length) { if (args.runtime.isDebug()) { args.raiseArgumentError("too many arguments for format string"); } else if (args.runtime.isVerbose()) { From 0498f9ec393cd28cfee2ed4b43313170b4790651 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 15:12:26 -0600 Subject: [PATCH 15/17] Tag remaining spec failures --- spec/tags/ruby/core/float/ceil_tags.txt | 1 + spec/tags/ruby/core/float/floor_tags.txt | 1 + spec/tags/ruby/core/integer/ceil_tags.txt | 1 + spec/tags/ruby/core/integer/floor_tags.txt | 1 + spec/tags/ruby/core/string/modulo_tags.txt | 5 +++++ spec/tags/ruby/library/set/merge_tags.txt | 1 + 6 files changed, 10 insertions(+) create mode 100644 spec/tags/ruby/core/float/ceil_tags.txt create mode 100644 spec/tags/ruby/core/float/floor_tags.txt create mode 100644 spec/tags/ruby/core/integer/ceil_tags.txt create mode 100644 spec/tags/ruby/core/integer/floor_tags.txt create mode 100644 spec/tags/ruby/core/string/modulo_tags.txt create mode 100644 spec/tags/ruby/library/set/merge_tags.txt diff --git a/spec/tags/ruby/core/float/ceil_tags.txt b/spec/tags/ruby/core/float/ceil_tags.txt new file mode 100644 index 00000000000..a786489e0fc --- /dev/null +++ b/spec/tags/ruby/core/float/ceil_tags.txt @@ -0,0 +1 @@ +fails:Float#ceil with precision precision is negative returns 10**precision.abs when precision.abs is larger than the number digits of self diff --git a/spec/tags/ruby/core/float/floor_tags.txt b/spec/tags/ruby/core/float/floor_tags.txt new file mode 100644 index 00000000000..78ab60812d1 --- /dev/null +++ b/spec/tags/ruby/core/float/floor_tags.txt @@ -0,0 +1 @@ +fails:Float#floor with precision precision is negative returns -(10**precision.abs) when self is negative and precision.abs is larger than the number digits of self diff --git a/spec/tags/ruby/core/integer/ceil_tags.txt b/spec/tags/ruby/core/integer/ceil_tags.txt new file mode 100644 index 00000000000..52131663847 --- /dev/null +++ b/spec/tags/ruby/core/integer/ceil_tags.txt @@ -0,0 +1 @@ +fails:Integer#ceil with precision precision is negative returns 10**precision.abs when precision.abs is larger than the number digits of self diff --git a/spec/tags/ruby/core/integer/floor_tags.txt b/spec/tags/ruby/core/integer/floor_tags.txt new file mode 100644 index 00000000000..01ea50f6d7e --- /dev/null +++ b/spec/tags/ruby/core/integer/floor_tags.txt @@ -0,0 +1 @@ +fails:Integer#floor with precision precision is negative returns -(10**precision.abs) when self is negative and precision.abs is larger than the number digits of self diff --git a/spec/tags/ruby/core/string/modulo_tags.txt b/spec/tags/ruby/core/string/modulo_tags.txt new file mode 100644 index 00000000000..9e32e92470e --- /dev/null +++ b/spec/tags/ruby/core/string/modulo_tags.txt @@ -0,0 +1,5 @@ +fails:String#% behaves as if calling Kernel#Float for %e arguments, when the passed argument does not respond to #to_ary +fails:String#% behaves as if calling Kernel#Float for %E arguments, when the passed argument does not respond to #to_ary +fails:String#% behaves as if calling Kernel#Float for %f arguments, when the passed argument does not respond to #to_ary +fails:String#% behaves as if calling Kernel#Float for %g arguments, when the passed argument does not respond to #to_ary +fails:String#% behaves as if calling Kernel#Float for %G arguments, when the passed argument does not respond to #to_ary diff --git a/spec/tags/ruby/library/set/merge_tags.txt b/spec/tags/ruby/library/set/merge_tags.txt new file mode 100644 index 00000000000..2fc9a275286 --- /dev/null +++ b/spec/tags/ruby/library/set/merge_tags.txt @@ -0,0 +1 @@ +fails:Set#merge accepts only a single argument From 831f4f50530cd1f2689ba6bfa06b637468e46845 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 15:30:24 -0600 Subject: [PATCH 16/17] Always set Thread group when added explicitly Even do this if the thread is dead. --- core/src/main/java/org/jruby/RubyThreadGroup.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/org/jruby/RubyThreadGroup.java b/core/src/main/java/org/jruby/RubyThreadGroup.java index e52fe5dc110..652c4939b83 100644 --- a/core/src/main/java/org/jruby/RubyThreadGroup.java +++ b/core/src/main/java/org/jruby/RubyThreadGroup.java @@ -95,6 +95,9 @@ public IRubyObject add(ThreadContext context, IRubyObject rubyThread, Block bloc } } + // always set group when added explicitly + thread.setThreadGroup(this); + // we only add live threads if (thread.alive_p(context).isTrue()) { addDirectly(thread); From a2511e3dc88a35f940c24b119800d2f9222edead Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 6 Nov 2024 18:06:15 -0600 Subject: [PATCH 17/17] Set group only if we don't do add logic Setting the new group before the add logic causes breaks the removal from the thread's old group. Instead, only directly set the thread's group if we won't run the add logic. --- core/src/main/java/org/jruby/RubyThreadGroup.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyThreadGroup.java b/core/src/main/java/org/jruby/RubyThreadGroup.java index 652c4939b83..9518e013d41 100644 --- a/core/src/main/java/org/jruby/RubyThreadGroup.java +++ b/core/src/main/java/org/jruby/RubyThreadGroup.java @@ -95,12 +95,11 @@ public IRubyObject add(ThreadContext context, IRubyObject rubyThread, Block bloc } } - // always set group when added explicitly - thread.setThreadGroup(this); - // we only add live threads if (thread.alive_p(context).isTrue()) { addDirectly(thread); + } else { + thread.setThreadGroup(this); } return this;