diff options
56 files changed, 1452 insertions, 132 deletions
diff --git a/spec/ruby/.rubocop_todo.yml b/spec/ruby/.rubocop_todo.yml index ac9cfae2bf..a59e64bd58 100644 --- a/spec/ruby/.rubocop_todo.yml +++ b/spec/ruby/.rubocop_todo.yml @@ -98,6 +98,7 @@ Lint/RescueException: - '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' diff --git a/spec/ruby/core/array/assoc_spec.rb b/spec/ruby/core/array/assoc_spec.rb index f8479d763c..af95528281 100644 --- a/spec/ruby/core/array/assoc_spec.rb +++ b/spec/ruby/core/array/assoc_spec.rb @@ -6,7 +6,7 @@ describe "Array#assoc" do s1 = ["colors", "red", "blue", "green"] s2 = [:letters, "a", "b", "c"] s3 = [4] - s4 = ["colors", "cyan", "yellow", "magenda"] + s4 = ["colors", "cyan", "yellow", "magenta"] s5 = [:letters, "a", "i", "u"] s_nil = [nil, nil] a = [s1, s2, s3, s4, s5, s_nil] diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb index a591444bab..02360e550d 100644 --- a/spec/ruby/core/array/fill_spec.rb +++ b/spec/ruby/core/array/fill_spec.rb @@ -52,11 +52,9 @@ describe "Array#fill" do end it "raises an ArgumentError if 4 or more arguments are passed when no block given" do - -> { [].fill('a') }.should_not raise_error(ArgumentError) - - -> { [].fill('a', 1) }.should_not raise_error(ArgumentError) - - -> { [].fill('a', 1, 2) }.should_not raise_error(ArgumentError) + [].fill('a').should == [] + [].fill('a', 1).should == [] + [].fill('a', 1, 2).should == [nil, 'a', 'a'] -> { [].fill('a', 1, 2, true) }.should raise_error(ArgumentError) end @@ -65,11 +63,9 @@ describe "Array#fill" do end it "raises an ArgumentError if 3 or more arguments are passed when a block given" do - -> { [].fill() {|i|} }.should_not raise_error(ArgumentError) - - -> { [].fill(1) {|i|} }.should_not raise_error(ArgumentError) - - -> { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError) + [].fill() {|i|}.should == [] + [].fill(1) {|i|}.should == [] + [].fill(1, 2) {|i|}.should == [nil, nil, nil] -> { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError) end @@ -213,23 +209,23 @@ describe "Array#fill with (filler, index, length)" do # See: https://blade.ruby-lang.org/ruby-core/17481 it "does not raise an exception if the given length is negative and its absolute value does not exceed the index" do - -> { [1, 2, 3, 4].fill('a', 3, -1)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill('a', 3, -2)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill('a', 3, -3)}.should_not raise_error(ArgumentError) + [1, 2, 3, 4].fill('a', 3, -1).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill('a', 3, -2).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill('a', 3, -3).should == [1, 2, 3, 4] - -> { [1, 2, 3, 4].fill(3, -1, &@never_passed)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill(3, -2, &@never_passed)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill(3, -3, &@never_passed)}.should_not raise_error(ArgumentError) + [1, 2, 3, 4].fill(3, -1, &@never_passed).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill(3, -2, &@never_passed).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill(3, -3, &@never_passed).should == [1, 2, 3, 4] end it "does not raise an exception even if the given length is negative and its absolute value exceeds the index" do - -> { [1, 2, 3, 4].fill('a', 3, -4)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill('a', 3, -5)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill('a', 3, -10000)}.should_not raise_error(ArgumentError) + [1, 2, 3, 4].fill('a', 3, -4).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill('a', 3, -5).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill('a', 3, -10000).should == [1, 2, 3, 4] - -> { [1, 2, 3, 4].fill(3, -4, &@never_passed)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill(3, -5, &@never_passed)}.should_not raise_error(ArgumentError) - -> { [1, 2, 3, 4].fill(3, -10000, &@never_passed)}.should_not raise_error(ArgumentError) + [1, 2, 3, 4].fill(3, -4, &@never_passed).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill(3, -5, &@never_passed).should == [1, 2, 3, 4] + [1, 2, 3, 4].fill(3, -10000, &@never_passed).should == [1, 2, 3, 4] end it "tries to convert the second and third arguments to Integers using #to_int" do diff --git a/spec/ruby/core/binding/source_location_spec.rb b/spec/ruby/core/binding/source_location_spec.rb index d439c3e399..d1c8191ea8 100644 --- a/spec/ruby/core/binding/source_location_spec.rb +++ b/spec/ruby/core/binding/source_location_spec.rb @@ -6,4 +6,9 @@ describe "Binding#source_location" do b = BindingSpecs::LocationMethod::TEST_BINDING b.source_location.should == [BindingSpecs::LocationMethod::FILE_PATH, 4] end + + it "works for eval with a given line" do + b = eval('binding', nil, "foo", 100) + b.source_location.should == ["foo", 100] + end end diff --git a/spec/ruby/core/dir/fchdir_spec.rb b/spec/ruby/core/dir/fchdir_spec.rb index 08b1cdfc7e..429e569691 100644 --- a/spec/ruby/core/dir/fchdir_spec.rb +++ b/spec/ruby/core/dir/fchdir_spec.rb @@ -2,17 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' ruby_version_is '3.3' do - has_fchdir = begin - dir = Dir.new('.') - Dir.fchdir(dir.fileno) - true - rescue NotImplementedError, NoMethodError - false - ensure - dir.close - end - - guard -> { has_fchdir } do + guard -> { Dir.respond_to? :fchdir } do describe "Dir.fchdir" do before :all do DirSpecs.create_mock_dirs @@ -67,7 +57,7 @@ ruby_version_is '3.3' do end end - guard_not -> { has_fchdir } do + guard_not -> { Dir.respond_to? :fchdir } do describe "Dir.fchdir" do it "raises NotImplementedError" do -> { Dir.fchdir 1 }.should raise_error(NotImplementedError) diff --git a/spec/ruby/core/dir/glob_spec.rb b/spec/ruby/core/dir/glob_spec.rb index 06b52b90fb..72d6337e15 100644 --- a/spec/ruby/core/dir/glob_spec.rb +++ b/spec/ruby/core/dir/glob_spec.rb @@ -182,6 +182,134 @@ describe "Dir.glob" do Dir.glob('**/**/**').should_not.empty? end + it "handles **/** with base keyword argument" do + Dir.glob('**/**', base: "dir").should == ["filename_ordering"] + + expected = %w[ + nested + nested/directory + nested/directory/structure + nested/directory/structure/bar + nested/directory/structure/baz + nested/directory/structure/file_one + nested/directory/structure/file_one.ext + nested/directory/structure/foo + nondotfile + ].sort + + Dir.glob('**/**', base: "deeply").sort.should == expected + end + + it "handles **/ with base keyword argument" do + expected = %w[ + / + directory/ + directory/structure/ + ] + Dir.glob('**/', base: "deeply/nested").sort.should == expected + end + + it "handles **/nondotfile with base keyword argument" do + expected = %w[ + deeply/nondotfile + nondotfile + subdir_one/nondotfile + subdir_two/nondotfile + ] + Dir.glob('**/nondotfile', base: ".").sort.should == expected + end + + it "handles **/nondotfile with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + .dotsubdir/nondotfile + deeply/nondotfile + nested/.dotsubir/nondotfile + nondotfile + subdir_one/nondotfile + subdir_two/nondotfile + ] + Dir.glob('**/nondotfile', File::FNM_DOTMATCH, base: ".").sort.should == expected + end + + it "handles **/.dotfile with base keyword argument" do + expected = %w[ + .dotfile + deeply/.dotfile + subdir_one/.dotfile + ] + Dir.glob('**/.dotfile', base: ".").sort.should == expected + end + + it "handles **/.dotfile with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + .dotfile + .dotsubdir/.dotfile + deeply/.dotfile + nested/.dotsubir/.dotfile + subdir_one/.dotfile + ] + Dir.glob('**/.dotfile', File::FNM_DOTMATCH, base: ".").sort.should == expected + end + + it "handles **/.* with base keyword argument" do + expected = %w[ + .dotfile.ext + directory/structure/.ext + ].sort + + Dir.glob('**/.*', base: "deeply/nested").sort.should == expected + end + + # 2.7 and 3.0 include a "." entry for every dir: ["directory/.", "directory/structure/.", ...] + ruby_version_is '3.1' do + it "handles **/.* with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + . + .dotfile.ext + directory/structure/.ext + ].sort + + Dir.glob('**/.*', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected + end + + it "handles **/** with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + . + .dotfile.ext + directory + directory/structure + directory/structure/.ext + directory/structure/bar + directory/structure/baz + directory/structure/file_one + directory/structure/file_one.ext + directory/structure/foo + ].sort + + Dir.glob('**/**', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected + end + end + + it "handles **/*pattern* with base keyword argument and FNM_DOTMATCH" do + expected = %w[ + .dotfile.ext + directory/structure/file_one + directory/structure/file_one.ext + ] + + Dir.glob('**/*file*', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected + end + + it "handles **/glob with base keyword argument and FNM_EXTGLOB" do + expected = %w[ + directory/structure/bar + directory/structure/file_one + directory/structure/file_one.ext + ] + + Dir.glob('**/*{file,bar}*', File::FNM_EXTGLOB, base: "deeply/nested").sort.should == expected + end + it "handles simple filename patterns" do Dir.glob('.dotfile').should == ['.dotfile'] end diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb index 848415eeb4..498d03581a 100644 --- a/spec/ruby/core/encoding/replicate_spec.rb +++ b/spec/ruby/core/encoding/replicate_spec.rb @@ -67,6 +67,14 @@ describe "Encoding#replicate" do end end + ruby_version_is "3.2"..."3.3" do + it "warns about deprecation" do + -> { + Encoding::US_ASCII.replicate('MY-US-ASCII') + }.should complain(/warning: Encoding#replicate is deprecated and will be removed in Ruby 3.3; use the original encoding instead/) + end + end + ruby_version_is "3.3" do it "has been removed" do Encoding::US_ASCII.should_not.respond_to?(:replicate, true) diff --git a/spec/ruby/core/enumerator/product_spec.rb b/spec/ruby/core/enumerator/product_spec.rb new file mode 100644 index 0000000000..44fc6441e1 --- /dev/null +++ b/spec/ruby/core/enumerator/product_spec.rb @@ -0,0 +1,88 @@ +require_relative '../../spec_helper' + +ruby_version_is "3.2" do + describe "Enumerator.product" do + it "returns a Cartesian product of enumerators" do + enum = Enumerator.product(1..2, ["A", "B"]) + enum.to_a.should == [[1, "A"], [1, "B"], [2, "A"], [2, "B"]] + end + + it "accepts a list of enumerators of any length" do + enum = Enumerator.product(1..2) + enum.to_a.should == [[1], [2]] + + enum = Enumerator.product(1..2, ["A"]) + enum.to_a.should == [[1, "A"], [2, "A"]] + + enum = Enumerator.product(1..2, ["A"], ["B"]) + enum.to_a.should == [[1, "A", "B"], [2, "A", "B"]] + + enum = Enumerator.product(2..3, ["A"], ["B"], ["C"]) + enum.to_a.should == [[2, "A", "B", "C"], [3, "A", "B", "C"]] + end + + it "returns an enumerator with an empty array when no arguments passed" do + enum = Enumerator.product + enum.to_a.should == [[]] + end + + it "returns an instance of Enumerator::Product" do + enum = Enumerator.product + enum.class.should == Enumerator::Product + end + + it "accepts infinite enumerators and returns infinite enumerator" do + enum = Enumerator.product(1.., ["A", "B"]) + enum.take(5).should == [[1, "A"], [1, "B"], [2, "A"], [2, "B"], [3, "A"]] + enum.size.should == Float::INFINITY + end + + it "accepts a block" do + elems = [] + enum = Enumerator.product(1..2, ["X", "Y"]) { elems << _1 } + + elems.should == [[1, "X"], [1, "Y"], [2, "X"], [2, "Y"]] + end + + it "reject keyword arguments" do + -> { + Enumerator.product(1..3, foo: 1, bar: 2) + }.should raise_error(ArgumentError, "unknown keywords: :foo, :bar") + end + + it "calls only #each_entry method on arguments" do + object = Object.new + def object.each_entry + yield 1 + yield 2 + end + + enum = Enumerator.product(object, ["A", "B"]) + enum.to_a.should == [[1, "A"], [1, "B"], [2, "A"], [2, "B"]] + end + + it "raises NoMethodError when argument doesn't respond to #each_entry" do + -> { + Enumerator.product(Object.new).to_a + }.should raise_error(NoMethodError, /undefined method `each_entry' for/) + end + + it "calls #each_entry lazily" do + Enumerator.product(Object.new).should be_kind_of(Enumerator) + end + + it "iterates through consuming enumerator elements only once" do + a = [1, 2, 3] + i = 0 + + enum = Enumerator.new do |y| + while i < a.size + y << a[i] + i += 1 + end + end + + Enumerator.product(['a', 'b'], enum).to_a.should == [["a", 1], ["a", 2], ["a", 3]] + end + end +end diff --git a/spec/ruby/core/env/clone_spec.rb b/spec/ruby/core/env/clone_spec.rb new file mode 100644 index 0000000000..991e4e6774 --- /dev/null +++ b/spec/ruby/core/env/clone_spec.rb @@ -0,0 +1,23 @@ +require_relative '../../spec_helper' + +describe "ENV#clone" do + it "raises ArgumentError when keyword argument 'freeze' is neither nil nor boolean" do + -> { + ENV.clone(freeze: 1) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError when keyword argument is not 'freeze'" do + -> { + ENV.clone(foo: nil) + }.should raise_error(ArgumentError) + end + + ruby_version_is "3.2" do + it "raises TypeError" do + -> { + ENV.clone + }.should raise_error(TypeError, /Cannot clone ENV, use ENV.to_h to get a copy of ENV as a hash/) + end + end +end diff --git a/spec/ruby/core/env/dup_spec.rb b/spec/ruby/core/env/dup_spec.rb new file mode 100644 index 0000000000..46d125aca8 --- /dev/null +++ b/spec/ruby/core/env/dup_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../spec_helper' + +describe "ENV#dup" do + ruby_version_is "3.1" do + it "raises TypeError" do + -> { + ENV.dup + }.should raise_error(TypeError, /Cannot dup ENV, use ENV.to_h to get a copy of ENV as a hash/) + end + end +end diff --git a/spec/ruby/core/exception/detailed_message_spec.rb b/spec/ruby/core/exception/detailed_message_spec.rb new file mode 100644 index 0000000000..bd85927dbe --- /dev/null +++ b/spec/ruby/core/exception/detailed_message_spec.rb @@ -0,0 +1,35 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/common' + +describe "Exception#detailed_message" do + ruby_version_is "3.2" do + it "returns decorated message" do + RuntimeError.new("new error").detailed_message.should == "new error (RuntimeError)" + end + + it "accepts highlight keyword argument and adds escape control sequences" do + RuntimeError.new("new error").detailed_message(highlight: true).should == "\e[1mnew error (\e[1;4mRuntimeError\e[m\e[1m)\e[m" + end + + it "allows and ignores other keyword arguments" do + RuntimeError.new("new error").detailed_message(foo: true).should == "new error (RuntimeError)" + end + + it "returns just a message if exception class is anonymous" do + Class.new(RuntimeError).new("message").detailed_message.should == "message" + end + + it "returns 'unhandled exception' for an instance of RuntimeError with empty message" do + RuntimeError.new("").detailed_message.should == "unhandled exception" + end + + it "returns just class name for an instance of RuntimeError sublass with empty message" do + DetailedMessageSpec::C.new("").detailed_message.should == "DetailedMessageSpec::C" + end + + it "returns a generated class name for an instance of RuntimeError anonymous subclass with empty message" do + klass = Class.new(RuntimeError) + klass.new("").detailed_message.should =~ /\A#<Class:0x\h+>\z/ + end + end +end diff --git a/spec/ruby/core/exception/fixtures/common.rb b/spec/ruby/core/exception/fixtures/common.rb index 0ffb3ed855..1e243098bd 100644 --- a/spec/ruby/core/exception/fixtures/common.rb +++ b/spec/ruby/core/exception/fixtures/common.rb @@ -93,3 +93,7 @@ class NameErrorSpecs end end end + +module DetailedMessageSpec + C = Class.new(RuntimeError) +end diff --git a/spec/ruby/core/exception/full_message_spec.rb b/spec/ruby/core/exception/full_message_spec.rb index 9757a2f407..ee66582022 100644 --- a/spec/ruby/core/exception/full_message_spec.rb +++ b/spec/ruby/core/exception/full_message_spec.rb @@ -103,4 +103,25 @@ describe "Exception#full_message" do exception.full_message.should include "intermediate exception" exception.full_message.should include "origin exception" end + + ruby_version_is "3.2" do + it "relies on #detailed_message" do + e = RuntimeError.new("new error") + e.define_singleton_method(:detailed_message) { |**opt| "DETAILED MESSAGE" } + + e.full_message.lines.first.should =~ /DETAILED MESSAGE/ + end + + it "passes all its own keyword arguments to #detailed_message" do + e = RuntimeError.new("new error") + opt_ = nil + e.define_singleton_method(:detailed_message) do |**opt| + opt_ = opt + "DETAILED MESSAGE" + end + + e.full_message(foo: "bar") + opt_.should == { foo: "bar", highlight: Exception.to_tty? } + end + end end diff --git a/spec/ruby/core/io/pread_spec.rb b/spec/ruby/core/io/pread_spec.rb index ac51545fc9..fb0645dec6 100644 --- a/spec/ruby/core/io/pread_spec.rb +++ b/spec/ruby/core/io/pread_spec.rb @@ -1,48 +1,50 @@ # -*- encoding: utf-8 -*- require_relative '../../spec_helper' -describe "IO#pread" do - before :each do - @fname = tmp("io_pread.txt") - @contents = "1234567890" - touch(@fname) { |f| f.write @contents } - @file = File.open(@fname, "r+") - end +guard -> { platform_is_not :windows or ruby_version_is "3.3" } do + describe "IO#pread" do + before :each do + @fname = tmp("io_pread.txt") + @contents = "1234567890" + touch(@fname) { |f| f.write @contents } + @file = File.open(@fname, "r+") + end - after :each do - @file.close - rm_r @fname - end + after :each do + @file.close + rm_r @fname + end - it "accepts a length, and an offset" do - @file.pread(4, 0).should == "1234" - @file.pread(3, 4).should == "567" - end + it "accepts a length, and an offset" do + @file.pread(4, 0).should == "1234" + @file.pread(3, 4).should == "567" + end - it "accepts a length, an offset, and an output buffer" do - buffer = "foo" - @file.pread(3, 4, buffer) - buffer.should == "567" - end + it "accepts a length, an offset, and an output buffer" do + buffer = "foo" + @file.pread(3, 4, buffer) + buffer.should == "567" + end - it "does not advance the file pointer" do - @file.pread(4, 0).should == "1234" - @file.read.should == "1234567890" - end + it "does not advance the file pointer" do + @file.pread(4, 0).should == "1234" + @file.read.should == "1234567890" + end - it "raises EOFError if end-of-file is reached" do - -> { @file.pread(1, 10) }.should raise_error(EOFError) - end + it "raises EOFError if end-of-file is reached" do + -> { @file.pread(1, 10) }.should raise_error(EOFError) + end - it "raises IOError when file is not open in read mode" do - File.open(@fname, "w") do |file| - -> { file.pread(1, 1) }.should raise_error(IOError) + it "raises IOError when file is not open in read mode" do + File.open(@fname, "w") do |file| + -> { file.pread(1, 1) }.should raise_error(IOError) + end end - end - it "raises IOError when file is closed" do - file = File.open(@fname, "r+") - file.close - -> { file.pread(1, 1) }.should raise_error(IOError) + it "raises IOError when file is closed" do + file = File.open(@fname, "r+") + file.close + -> { file.pread(1, 1) }.should raise_error(IOError) + end end end diff --git a/spec/ruby/core/io/pwrite_spec.rb b/spec/ruby/core/io/pwrite_spec.rb index da7d910364..c10578a8eb 100644 --- a/spec/ruby/core/io/pwrite_spec.rb +++ b/spec/ruby/core/io/pwrite_spec.rb @@ -1,41 +1,43 @@ # -*- encoding: utf-8 -*- require_relative '../../spec_helper' -describe "IO#pwrite" do - before :each do - @fname = tmp("io_pwrite.txt") - @file = File.open(@fname, "w+") - end +guard -> { platform_is_not :windows or ruby_version_is "3.3" } do + describe "IO#pwrite" do + before :each do + @fname = tmp("io_pwrite.txt") + @file = File.open(@fname, "w+") + end - after :each do - @file.close - rm_r @fname - end + after :each do + @file.close + rm_r @fname + end - it "returns the number of bytes written" do - @file.pwrite("foo", 0).should == 3 - end + it "returns the number of bytes written" do + @file.pwrite("foo", 0).should == 3 + end - it "accepts a string and an offset" do - @file.pwrite("foo", 2) - @file.pread(3, 2).should == "foo" - end + it "accepts a string and an offset" do + @file.pwrite("foo", 2) + @file.pread(3, 2).should == "foo" + end - it "does not advance the pointer in the file" do - @file.pwrite("bar", 3) - @file.write("foo") - @file.pread(6, 0).should == "foobar" - end + it "does not advance the pointer in the file" do + @file.pwrite("bar", 3) + @file.write("foo") + @file.pread(6, 0).should == "foobar" + end - it "raises IOError when file is not open in write mode" do - File.open(@fname, "r") do |file| - -> { file.pwrite("foo", 1) }.should raise_error(IOError) + it "raises IOError when file is not open in write mode" do + File.open(@fname, "r") do |file| + -> { file.pwrite("foo", 1) }.should raise_error(IOError) + end end - end - it "raises IOError when file is closed" do - file = File.open(@fname, "w+") - file.close - -> { file.pwrite("foo", 1) }.should raise_error(IOError) + it "raises IOError when file is closed" do + file = File.open(@fname, "w+") + file.close + -> { file.pwrite("foo", 1) }.should raise_error(IOError) + end end end diff --git a/spec/ruby/core/kernel/taint_spec.rb b/spec/ruby/core/kernel/taint_spec.rb index 9a58bb5f04..eff9b4a450 100644 --- a/spec/ruby/core/kernel/taint_spec.rb +++ b/spec/ruby/core/kernel/taint_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#taint" do - ruby_version_is ""..."3.0" do + ruby_version_is ""..."3.2" do it "is a no-op" do o = Object.new o.taint @@ -16,4 +16,10 @@ describe "Kernel#taint" do }.should complain(/Object#taint is deprecated and will be removed in Ruby 3.2/, verbose: true) end end + + ruby_version_is "3.2" do + it "has been removed" do + Object.new.should_not.respond_to?(:taint) + end + end end diff --git a/spec/ruby/core/kernel/tainted_spec.rb b/spec/ruby/core/kernel/tainted_spec.rb index 7511c730c9..4e81971e58 100644 --- a/spec/ruby/core/kernel/tainted_spec.rb +++ b/spec/ruby/core/kernel/tainted_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#tainted?" do - ruby_version_is ""..."3.0" do + ruby_version_is ""..."3.2" do it "is a no-op" do o = mock('o') p = mock('p') @@ -18,4 +18,10 @@ describe "Kernel#tainted?" do }.should complain(/Object#tainted\? is deprecated and will be removed in Ruby 3.2/, verbose: true) end end + + ruby_version_is "3.2" do + it "has been removed" do + Object.new.should_not.respond_to?(:tainted?) + end + end end diff --git a/spec/ruby/core/kernel/trust_spec.rb b/spec/ruby/core/kernel/trust_spec.rb index 4665036da6..3a49ab3085 100644 --- a/spec/ruby/core/kernel/trust_spec.rb +++ b/spec/ruby/core/kernel/trust_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#trust" do - ruby_version_is ""..."3.0" do + ruby_version_is ""..."3.2" do it "is a no-op" do o = Object.new.untrust o.should_not.untrusted? @@ -17,4 +17,10 @@ describe "Kernel#trust" do }.should complain(/Object#trust is deprecated and will be removed in Ruby 3.2/, verbose: true) end end + + ruby_version_is "3.2" do + it "has been removed" do + Object.new.should_not.respond_to?(:trust) + end + end end diff --git a/spec/ruby/core/kernel/untaint_spec.rb b/spec/ruby/core/kernel/untaint_spec.rb index 42fe8a4239..a543025c5d 100644 --- a/spec/ruby/core/kernel/untaint_spec.rb +++ b/spec/ruby/core/kernel/untaint_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#untaint" do - ruby_version_is ""..."3.0" do + ruby_version_is ""..."3.2" do it "is a no-op" do o = Object.new.taint o.should_not.tainted? @@ -17,4 +17,10 @@ describe "Kernel#untaint" do }.should complain(/Object#untaint is deprecated and will be removed in Ruby 3.2/, verbose: true) end end + + ruby_version_is "3.2" do + it "has been removed" do + Object.new.should_not.respond_to?(:untaint) + end + end end diff --git a/spec/ruby/core/kernel/untrust_spec.rb b/spec/ruby/core/kernel/untrust_spec.rb index ba0e073cf0..6eefb624c0 100644 --- a/spec/ruby/core/kernel/untrust_spec.rb +++ b/spec/ruby/core/kernel/untrust_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#untrust" do - ruby_version_is ""..."3.0" do + ruby_version_is ""..."3.2" do it "is a no-op" do o = Object.new o.untrust @@ -16,4 +16,10 @@ describe "Kernel#untrust" do }.should complain(/Object#untrust is deprecated and will be removed in Ruby 3.2/, verbose: true) end end + + ruby_version_is "3.2" do + it "has been removed" do + Object.new.should_not.respond_to?(:untrust) + end + end end diff --git a/spec/ruby/core/kernel/untrusted_spec.rb b/spec/ruby/core/kernel/untrusted_spec.rb index 517bd4711b..bd0bf154ed 100644 --- a/spec/ruby/core/kernel/untrusted_spec.rb +++ b/spec/ruby/core/kernel/untrusted_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#untrusted?" do - ruby_version_is ""..."3.0" do + ruby_version_is ""..."3.2" do it "is a no-op" do o = mock('o') o.should_not.untrusted? @@ -17,4 +17,10 @@ describe "Kernel#untrusted?" do }.should complain(/Object#untrusted\? is deprecated and will be removed in Ruby 3.2/, verbose: true) end end + + ruby_version_is "3.2" do + it "has been removed" do + Object.new.should_not.respond_to?(:untrusted?) + end + end end diff --git a/spec/ruby/core/matchdata/byteoffset_spec.rb b/spec/ruby/core/matchdata/byteoffset_spec.rb new file mode 100644 index 0000000000..6036097834 --- /dev/null +++ b/spec/ruby/core/matchdata/byteoffset_spec.rb @@ -0,0 +1,95 @@ +require_relative '../../spec_helper' + +describe "MatchData#byteoffset" do + ruby_version_is "3.2" do + it "returns beginning and ending byte-based offset of whole matched substring for 0 element" do + m = /(.)(.)(\d+)(\d)/.match("THX1138.") + m.byteoffset(0).should == [1, 7] + end + + it "returns beginning and ending byte-based offset of n-th match, all the subsequent elements are capturing groups" do + m = /(.)(.)(\d+)(\d)/.match("THX1138.") + + m.byteoffset(2).should == [2, 3] + m.byteoffset(3).should == [3, 6] + m.byteoffset(4).should == [6, 7] + end + + it "accepts String as a reference to a named capture" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + m.byteoffset("f").should == [0, 3] + m.byteoffset("b").should == [3, 6] + end + + it "accepts Symbol as a reference to a named capture" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + m.byteoffset(:f).should == [0, 3] + m.byteoffset(:b).should == [3, 6] + end + + it "returns [nil, nil] if a capturing group is optional and doesn't match" do + m = /(?<x>q..)?/.match("foobarbaz") + + m.byteoffset("x").should == [nil, nil] + m.byteoffset(1).should == [nil, nil] + end + + it "returns correct beginning and ending byte-based offset for multi-byte strings" do + m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044") + + m.byteoffset(1).should == [3, 6] + m.byteoffset(3).should == [6, 9] + end + + it "returns [nil, nil] if a capturing group is optional and doesn't match for multi-byte string" do + m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044") + + m.byteoffset(2).should == [nil, nil] + end + + it "converts argument into integer if is not String nor Symbol" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + obj = Object.new + def obj.to_int; 2; end + + m.byteoffset(1r).should == [0, 3] + m.byteoffset(1.1).should == [0, 3] + m.byteoffset(obj).should == [3, 6] + end + + it "raises IndexError if there is no group with provided name" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + -> { + m.byteoffset("y") + }.should raise_error(IndexError, "undefined group name reference: y") + + -> { + m.byteoffset(:y) + }.should raise_error(IndexError, "undefined group name reference: y") + end + + it "raises IndexError if index is out of matches" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + -> { + m.byteoffset(-1) + }.should raise_error(IndexError, "index -1 out of matches") + + -> { + m.byteoffset(3) + }.should raise_error(IndexError, "index 3 out of matches") + end + + it "raises TypeError if can't convert argument into Integer" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + -> { + m.byteoffset([]) + }.should raise_error(TypeError, "no implicit conversion of Array into Integer") + end + end +end diff --git a/spec/ruby/core/matchdata/captures_spec.rb b/spec/ruby/core/matchdata/captures_spec.rb index 58d4620709..f829a25481 100644 --- a/spec/ruby/core/matchdata/captures_spec.rb +++ b/spec/ruby/core/matchdata/captures_spec.rb @@ -1,15 +1,6 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' +require_relative 'shared/captures' describe "MatchData#captures" do - it "returns an array of the match captures" do - /(.)(.)(\d+)(\d)/.match("THX1138.").captures.should == ["H","X","113","8"] - end - - ruby_version_is "3.0" do - it "returns instances of String when given a String subclass" do - str = MatchDataSpecs::MyString.new("THX1138: The Movie") - /(.)(.)(\d+)(\d)/.match(str).captures.each { |c| c.should be_an_instance_of(String) } - end - end + it_behaves_like :matchdata_captures, :captures end diff --git a/spec/ruby/core/matchdata/deconstruct_keys_spec.rb b/spec/ruby/core/matchdata/deconstruct_keys_spec.rb new file mode 100644 index 0000000000..5b68f886c7 --- /dev/null +++ b/spec/ruby/core/matchdata/deconstruct_keys_spec.rb @@ -0,0 +1,65 @@ +require_relative '../../spec_helper' + +describe "MatchData#deconstruct_keys" do + ruby_version_is "3.2" do + it "returns whole hash for nil as an argument" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + m.deconstruct_keys(nil).should == { f: "foo", b: "bar" } + end + + it "returns only specified keys" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + m.deconstruct_keys([:f]).should == { f: "foo" } + end + + it "requires one argument" do + m = /l/.match("l") + + -> { + m.deconstruct_keys + }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1)") + end + + it "it raises error when argument is neither nil nor array" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + -> { m.deconstruct_keys(1) }.should raise_error(TypeError, "wrong argument type Integer (expected Array)") + -> { m.deconstruct_keys("asd") }.should raise_error(TypeError, "wrong argument type String (expected Array)") + -> { m.deconstruct_keys(:x) }.should raise_error(TypeError, "wrong argument type Symbol (expected Array)") + -> { m.deconstruct_keys({}) }.should raise_error(TypeError, "wrong argument type Hash (expected Array)") + end + + it "returns {} when passed []" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + m.deconstruct_keys([]).should == {} + end + + it "does not accept non-Symbol keys" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + + -> { + m.deconstruct_keys(['year', :foo]) + }.should raise_error(TypeError, "wrong argument type String (expected Symbol)") + end + + it "process keys till the first non-existing one" do + m = /(?<f>foo)(?<b>bar)(?<c>baz)/.match("foobarbaz") + + m.deconstruct_keys([:f, :a, :b]).should == { f: "foo" } + end + + it "returns {} when there are no named captured groups at all" do + m = /foo.+/.match("foobar") + + m.deconstruct_keys(nil).should == {} + end + + it "returns {} when passed more keys than named captured groups" do + m = /(?<f>foo)(?<b>bar)/.match("foobar") + m.deconstruct_keys([:f, :b, :c]).should == {} + end + end +end diff --git a/spec/ruby/core/matchdata/deconstruct_spec.rb b/spec/ruby/core/matchdata/deconstruct_spec.rb new file mode 100644 index 0000000000..6af55113b6 --- /dev/null +++ b/spec/ruby/core/matchdata/deconstruct_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../spec_helper' +require_relative 'shared/captures' + +describe "MatchData#deconstruct" do + ruby_version_is "3.2" do + it_behaves_like :matchdata_captures, :deconstruct + end +end diff --git a/spec/ruby/core/matchdata/shared/captures.rb b/spec/ruby/core/matchdata/shared/captures.rb new file mode 100644 index 0000000000..d2c85ece5a --- /dev/null +++ b/spec/ruby/core/matchdata/shared/captures.rb @@ -0,0 +1,15 @@ +require_relative '../../../spec_helper' +require_relative '../fixtures/classes' + +describe :matchdata_captures, shared: true do + it "returns an array of the match captures" do + /(.)(.)(\d+)(\d)/.match("THX1138.").send(@method).should == ["H","X","113","8"] + end + + ruby_version_is "3.0" do + it "returns instances of String when given a String subclass" do + str = MatchDataSpecs::MyString.new("THX1138: The Movie") + /(.)(.)(\d+)(\d)/.match(str).send(@method).each { |c| c.should be_an_instance_of(String) } + end + end +end diff --git a/spec/ruby/core/method/private_spec.rb b/spec/ruby/core/method/private_spec.rb index 230a4e9e81..fd550036a3 100644 --- a/spec/ruby/core/method/private_spec.rb +++ b/spec/ruby/core/method/private_spec.rb @@ -1,8 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "3.1"..."3.2" do - describe "Method#private?" do +describe "Method#private?" do + ruby_version_is "3.1"..."3.2" do it "returns false when the method is public" do obj = MethodSpecs::Methods.new obj.method(:my_public_method).private?.should == false @@ -18,4 +18,11 @@ ruby_version_is "3.1"..."3.2" do obj.method(:my_private_method).private?.should == true end end + + ruby_version_is "3.2" do + it "has been removed" do + obj = MethodSpecs::Methods.new + obj.method(:my_private_method).should_not.respond_to?(:private?) + end + end end diff --git a/spec/ruby/core/method/protected_spec.rb b/spec/ruby/core/method/protected_spec.rb index 6ee85f7738..8423e8c64c 100644 --- a/spec/ruby/core/method/protected_spec.rb +++ b/spec/ruby/core/method/protected_spec.rb @@ -1,8 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "3.1"..."3.2" do - describe "Method#protected?" do +describe "Method#protected?" do + ruby_version_is "3.1"..."3.2" do it "returns false when the method is public" do obj = MethodSpecs::Methods.new obj.method(:my_public_method).protected?.should == false @@ -18,4 +18,11 @@ ruby_version_is "3.1"..."3.2" do obj.method(:my_private_method).protected?.should == false end end + + ruby_version_is "3.2" do + it "has been removed" do + obj = MethodSpecs::Methods.new + obj.method(:my_protected_method).should_not.respond_to?(:protected?) + end + end end diff --git a/spec/ruby/core/method/public_spec.rb b/spec/ruby/core/method/public_spec.rb index 3988468551..20d0081a27 100644 --- a/spec/ruby/core/method/public_spec.rb +++ b/spec/ruby/core/method/public_spec.rb @@ -1,8 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "3.1"..."3.2" do - describe "Method#public?" do +describe "Method#public?" do + ruby_version_is "3.1"..."3.2" do it "returns true when the method is public" do obj = MethodSpecs::Methods.new obj.method(:my_public_method).public?.should == true @@ -18,4 +18,11 @@ ruby_version_is "3.1"..."3.2" do obj.method(:my_private_method).public?.should == false end end + + ruby_version_is "3.2" do + it "has been removed" do + obj = MethodSpecs::Methods.new + obj.method(:my_public_method).should_not.respond_to?(:public?) + end + end end diff --git a/spec/ruby/core/method/source_location_spec.rb b/spec/ruby/core/method/source_location_spec.rb index 1f476aaa9b..4cfb21c5d0 100644 --- a/spec/ruby/core/method/source_location_spec.rb +++ b/spec/ruby/core/method/source_location_spec.rb @@ -104,6 +104,13 @@ describe "Method#source_location" do end end + it "works for eval with a given line" do + c = Class.new do + eval('def self.m; end', nil, "foo", 100) + end + c.method(:m).source_location.should == ["foo", 100] + end + describe "for a Method generated by respond_to_missing?" do it "returns nil" do m = MethodSpecs::Methods.new diff --git a/spec/ruby/core/module/const_source_location_spec.rb b/spec/ruby/core/module/const_source_location_spec.rb index 145b069e2e..ded2aa51d7 100644 --- a/spec/ruby/core/module/const_source_location_spec.rb +++ b/spec/ruby/core/module/const_source_location_spec.rb @@ -210,6 +210,13 @@ describe "Module#const_source_location" do ConstantSpecs.const_source_location(:CS_PRIVATE).should == [@constants_fixture_path, ConstantSpecs::CS_PRIVATE_LINE] end + it "works for eval with a given line" do + c = Class.new do + eval('self::C = 1', nil, "foo", 100) + end + c.const_source_location(:C).should == ["foo", 100] + end + context 'autoload' do before :all do ConstantSpecs.autoload :CSL_CONST1, "#{__dir__}/notexisting.rb" diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb index bc6b940a6c..a434e7b0b8 100644 --- a/spec/ruby/core/module/fixtures/classes.rb +++ b/spec/ruby/core/module/fixtures/classes.rb @@ -596,6 +596,32 @@ module ModuleSpecs private :foo end EmptyFooMethod = m.instance_method(:foo) + + # for undefined_instance_methods spec + module UndefinedInstanceMethods + module Super + def super_included_method; end + end + + class Parent + def undefed_method; end + undef_method :undefed_method + + def parent_method; end + def another_parent_method; end + end + + class Child < Parent + include Super + + undef_method :parent_method + undef_method :another_parent_method + end + + class Grandchild < Child + undef_method :super_included_method + end + end end class Object diff --git a/spec/ruby/core/module/refinements_spec.rb b/spec/ruby/core/module/refinements_spec.rb new file mode 100644 index 0000000000..5648fcbd6f --- /dev/null +++ b/spec/ruby/core/module/refinements_spec.rb @@ -0,0 +1,45 @@ +require_relative '../../spec_helper' + +describe "Module#refinements" do + ruby_version_is "3.2" do + it "returns refinements defined in a module" do + ScratchPad.record [] + + m = Module.new do + refine String do + ScratchPad << self + end + + refine Array do + ScratchPad << self + end + end + + m.refinements.sort_by(&:object_id).should == ScratchPad.recorded.sort_by(&:object_id) + end + + it "does not return refinements defined in the included module" do + ScratchPad.record [] + + m1 = Module.new do + refine Integer do + nil + end + end + + m2 = Module.new do + include m1 + + refine String do + ScratchPad << self + end + end + + m2.refinements.should == ScratchPad.recorded + end + + it "returns an empty array if no refinements defined in a module" do + Module.new.refinements.should == [] + end + end +end diff --git a/spec/ruby/core/module/undefined_instance_methods_spec.rb b/spec/ruby/core/module/undefined_instance_methods_spec.rb new file mode 100644 index 0000000000..3be860d053 --- /dev/null +++ b/spec/ruby/core/module/undefined_instance_methods_spec.rb @@ -0,0 +1,26 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Module#undefined_instance_methods" do + ruby_version_is "3.2" do + it "returns methods undefined in the class" do + methods = ModuleSpecs::UndefinedInstanceMethods::Parent.undefined_instance_methods + methods.should == [:undefed_method] + end + + it "returns inherited methods undefined in the class" do + methods = ModuleSpecs::UndefinedInstanceMethods::Child.undefined_instance_methods + methods.should include(:parent_method, :another_parent_method) + end + + it "returns methods from an included module that are undefined in the class" do + methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods + methods.should include(:super_included_method) + end + + it "does not returns ancestors undefined methods" do + methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods + methods.should_not include(:parent_method, :another_parent_method) + end + end +end diff --git a/spec/ruby/core/module/used_refinements_spec.rb b/spec/ruby/core/module/used_refinements_spec.rb new file mode 100644 index 0000000000..c16cab0e3c --- /dev/null +++ b/spec/ruby/core/module/used_refinements_spec.rb @@ -0,0 +1,87 @@ +require_relative '../../spec_helper' + +describe "Module.used_refinements" do + ruby_version_is "3.2" do + it "returns list of all refinements imported in the current scope" do + refinement_int = nil + refinement_str = nil + ScratchPad.record [] + + m1 = Module.new do + refine Integer do + refinement_int = self + end + end + + m2 = Module.new do + refine String do + refinement_str = self + end + end + + Module.new do + using m1 + using m2 + + Module.used_refinements.each { |r| ScratchPad << r } + end + + ScratchPad.recorded.sort_by(&:object_id).should == [refinement_int, refinement_str].sort_by(&:object_id) + end + + it "returns empty array if does not have any refinements imported" do + used_refinements = nil + + Module.new do + used_refinements = Module.used_refinements + end + + used_refinements.should == [] + end + + it "ignores refinements imported in a module that is included into the current one" do + used_refinements = nil + + m1 = Module.new do + refine Integer do + nil + end + end + + m2 = Module.new do + using m1 + end + + Module.new do + include m2 + + used_refinements = Module.used_refinements + end + + used_refinements.should == [] + end + + it "returns refinements even not defined directly in a module refinements are imported from" do + used_refinements = nil + ScratchPad.record [] + + m1 = Module.new do + refine Integer do + ScratchPad << self + end + end + + m2 = Module.new do + include m1 + end + + Module.new do + using m2 + + used_refinements = Module.used_refinements + end + + used_refinements.should == ScratchPad.recorded + end + end +end diff --git a/spec/ruby/core/proc/shared/dup.rb b/spec/ruby/core/proc/shared/dup.rb index eda1d6929d..4480f3d0c9 100644 --- a/spec/ruby/core/proc/shared/dup.rb +++ b/spec/ruby/core/proc/shared/dup.rb @@ -7,4 +7,12 @@ describe :proc_dup, shared: true do a.call.should == b.call end + + ruby_version_is "3.2" do + it "returns an instance of subclass" do + cl = Class.new(Proc) + + cl.new{}.send(@method).class.should == cl + end + end end diff --git a/spec/ruby/core/proc/source_location_spec.rb b/spec/ruby/core/proc/source_location_spec.rb index f268499b82..a5895a7fcb 100644 --- a/spec/ruby/core/proc/source_location_spec.rb +++ b/spec/ruby/core/proc/source_location_spec.rb @@ -83,4 +83,9 @@ describe "Proc#source_location" do proc.source_location.should == nil end + + it "works for eval with a given line" do + proc = eval('-> {}', nil, "foo", 100) + proc.source_location.should == ["foo", 100] + end end diff --git a/spec/ruby/core/refinement/refined_class_spec.rb b/spec/ruby/core/refinement/refined_class_spec.rb new file mode 100644 index 0000000000..bcf48c4e25 --- /dev/null +++ b/spec/ruby/core/refinement/refined_class_spec.rb @@ -0,0 +1,17 @@ +require_relative '../../spec_helper' + +describe "Refinement#refined_class" do + ruby_version_is "3.2" do + it "returns the class refined by the receiver" do + refinement_int = nil + + Module.new do + refine Integer do + refinement_int = self + end + end + + refinement_int.refined_class.should == Integer + end + end +end diff --git a/spec/ruby/core/regexp/linear_time_spec.rb b/spec/ruby/core/regexp/linear_time_spec.rb new file mode 100644 index 0000000000..4dc436264f --- /dev/null +++ b/spec/ruby/core/regexp/linear_time_spec.rb @@ -0,0 +1,25 @@ +require_relative '../../spec_helper' + +ruby_version_is "3.2" do + describe "Regexp.linear_time?" do + it "returns true if matching can be done in linear time" do + Regexp.linear_time?(/a/).should == true + Regexp.linear_time?('a').should == true + end + + it "return false if matching can't be done in linear time" do + Regexp.linear_time?(/(a)\1/).should == false + Regexp.linear_time?("(a)\\1").should == false + end + + it "accepts flags for string argument" do + Regexp.linear_time?('a', Regexp::IGNORECASE).should == true + end + + it "warns about flags being ignored for regexp arguments" do + -> { + Regexp.linear_time?(/a/, Regexp::IGNORECASE) + }.should complain(/warning: flags ignored/) + end + end +end diff --git a/spec/ruby/core/string/bytesplice_spec.rb b/spec/ruby/core/string/bytesplice_spec.rb new file mode 100644 index 0000000000..f13024a79b --- /dev/null +++ b/spec/ruby/core/string/bytesplice_spec.rb @@ -0,0 +1,133 @@ +# -*- encoding: utf-8 -*- +require_relative '../../spec_helper' + +describe "String#bytesplice" do + ruby_version_is "3.2" do + it "raises IndexError when index is less than -bytesize" do + -> { "hello".bytesplice(-6, 0, "xxx") }.should raise_error(IndexError, "index -6 out of string") + end + + it "raises IndexError when index is greater than bytesize" do + -> { "hello".bytesplice(6, 0, "xxx") }.should raise_error(IndexError, "index 6 out of string") + end + + it "raises IndexError for negative length" do + -> { "abc".bytesplice(0, -2, "") }.should raise_error(IndexError, "negative length -2") + end + + it "replaces with integer indices" do + "hello".bytesplice(-5, 0, "xxx").should == "xxxhello" + "hello".bytesplice(0, 0, "xxx").should == "xxxhello" + "hello".bytesplice(0, 1, "xxx").should == "xxxello" + "hello".bytesplice(0, 5, "xxx").should == "xxx" + "hello".bytesplice(0, 6, "xxx").should == "xxx" + end + + it "raises RangeError when range left boundary is less than -bytesize" do + -> { "hello".bytesplice(-6...-6, "xxx") }.should raise_error(RangeError, "-6...-6 out of range") + end + + it "replaces with ranges" do + "hello".bytesplice(-5...-5, "xxx").should == "xxxhello" + "hello".bytesplice(0...0, "xxx").should == "xxxhello" + "hello".bytesplice(0..0, "xxx").should == "xxxello" + "hello".bytesplice(0...1, "xxx").should == "xxxello" + "hello".bytesplice(0..1, "xxx").should == "xxxllo" + "hello".bytesplice(0..-1, "xxx").should == "xxx" + "hello".bytesplice(0...5, "xxx").should == "xxx" + "hello".bytesplice(0...6, "xxx").should == "xxx" + end + + it "raises TypeError when integer index is provided without length argument" do + -> { "hello".bytesplice(0, "xxx") }.should raise_error(TypeError, "wrong argument type Integer (expected Range)") + end + + it "replaces on an empty string" do + "".bytesplice(0, 0, "").should == "" + "".bytesplice(0, 0, "xxx").should == "xxx" + end + + it "mutates self" do + s = "hello" + s.bytesplice(2, 1, "xxx").should.equal?(s) + end + + it "raises when string is frozen" do + s = "hello".freeze + -> { s.bytesplice(2, 1, "xxx") }.should raise_error(FrozenError, "can't modify frozen String: \"hello\"") + end + end +end + +describe "String#bytesplice with multibyte characters" do + ruby_version_is "3.2" do + it "raises IndexError when index is out of byte size boundary" do + -> { "こんにちは".bytesplice(-16, 0, "xxx") }.should raise_error(IndexError, "index -16 out of string") + end + + it "raises IndexError when index is not on a codepoint boundary" do + -> { "こんにちは".bytesplice(1, 0, "xxx") }.should raise_error(IndexError, "offset 1 does not land on character boundary") + end + + it "raises IndexError when length is not matching the codepoint boundary" do + -> { "こんにちは".bytesplice(0, 1, "xxx") }.should raise_error(IndexError, "offset 1 does not land on character boundary") + -> { "こんにちは".bytesplice(0, 2, "xxx") }.should raise_error(IndexError, "offset 2 does not land on character boundary") + end + + it "replaces with integer indices" do + "こんにちは".bytesplice(-15, 0, "xxx").should == "xxxこんにちは" + "こんにちは".bytesplice(0, 0, "xxx").should == "xxxこんにちは" + "こんにちは".bytesplice(0, 3, "xxx").should == "xxxんにちは" + "こんにちは".bytesplice(3, 3, "はは").should == "こははにちは" + "こんにちは".bytesplice(15, 0, "xxx").should == "こんにちはxxx" + end + + it "replaces with range" do + "こんにちは".bytesplice(-15...-16, "xxx").should == "xxxこんにちは" + "こんにちは".bytesplice(0...0, "xxx").should == "xxxこんにちは" + "こんにちは".bytesplice(0..2, "xxx").should == "xxxんにちは" + "こんにちは".bytesplice(0...3, "xxx").should == "xxxんにちは" + "こんにちは".bytesplice(0..5, "xxx").should == "xxxにちは" + "こんにちは".bytesplice(0..-1, "xxx").should == "xxx" + "こんにちは".bytesplice(0...15, "xxx").should == "xxx" + "こんにちは".bytesplice(0...18, "xxx").should == "xxx" + end + + it "treats negative length for range as 0" do + "こんにちは".bytesplice(0...-100, "xxx").should == "xxxこんにちは" + "こんにちは".bytesplice(3...-100, "xxx").should == "こxxxんにちは" + "こんにちは".bytesplice(-15...-100, "xxx").should == "xxxこんにちは" + end + + it "raises when ranges not match codepoint boundaries" do + -> { "こんにちは".bytesplice(0..0, "x") }.should raise_error(IndexError, "offset 1 does not land on character boundary") + -> { "こんにちは".bytesplice(0..1, "x") }.should raise_error(IndexError, "offset 2 does not land on character boundary") + # Begin is incorrect + -> { "こんにちは".bytesplice(-4..-1, "x") }.should raise_error(IndexError, "offset 11 does not land on character boundary") + -> { "こんにちは".bytesplice(-5..-1, "x") }.should raise_error(IndexError, "offset 10 does not land on character boundary") + # End is incorrect + -> { "こんにちは".bytesplice(-3..-2, "x") }.should raise_error(IndexError, "offset 14 does not land on character boundary") + -> { "こんにちは".bytesplice(-3..-3, "x") }.should raise_error(IndexError, "offset 13 does not land on character boundary") + end + + it "deals with a different encoded argument" do + s = "こんにちは" + s.encoding.should == Encoding::UTF_8 + sub = "xxxxxx" + sub.force_encoding(Encoding::US_ASCII) + + result = s.bytesplice(0, 3, sub) + result.should == "xxxxxxんにちは" + result.encoding.should == Encoding::UTF_8 + + s = "xxxxxx" + s.force_encoding(Encoding::US_ASCII) + sub = "こんにちは" + sub.encoding.should == Encoding::UTF_8 + + result = s.bytesplice(0, 3, sub) + result.should == "こんにちはxxx" + result.encoding.should == Encoding::UTF_8 + end + end +end diff --git a/spec/ruby/core/string/dedup_spec.rb b/spec/ruby/core/string/dedup_spec.rb index 919d440c51..57d2be2cfd 100644 --- a/spec/ruby/core/string/dedup_spec.rb +++ b/spec/ruby/core/string/dedup_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' require_relative 'shared/dedup' describe 'String#dedup' do - ruby_version_is '3.2'do + ruby_version_is '3.2' do it_behaves_like :string_dedup, :dedup end end diff --git a/spec/ruby/core/string/to_c_spec.rb b/spec/ruby/core/string/to_c_spec.rb index edc8a4f14f..9d24f1f56c 100644 --- a/spec/ruby/core/string/to_c_spec.rb +++ b/spec/ruby/core/string/to_c_spec.rb @@ -38,4 +38,16 @@ describe "String#to_c" do '79+4i'.encode("UTF-16").to_c }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16") end + + ruby_version_is "3.2" do + it "treats a sequence of underscores as an end of Complex string" do + "5+3_1i".to_c.should == Complex(5, 31) + "5+3__1i".to_c.should == Complex(5) + "5+3___1i".to_c.should == Complex(5) + + "12_3".to_c.should == Complex(123) + "12__3".to_c.should == Complex(12) + "12___3".to_c.should == Complex(12) + end + end end diff --git a/spec/ruby/core/time/deconstruct_keys_spec.rb b/spec/ruby/core/time/deconstruct_keys_spec.rb new file mode 100644 index 0000000000..fbb0ec2164 --- /dev/null +++ b/spec/ruby/core/time/deconstruct_keys_spec.rb @@ -0,0 +1,44 @@ +require_relative '../../spec_helper' + +ruby_version_is "3.2" do + describe "Time#deconstruct_keys" do + it "returns whole hash for nil as an argument" do + d = Time.utc(2022, 10, 5, 13, 30) + res = { year: 2022, month: 10, day: 5, yday: 278, wday: 3, hour: 13, + min: 30, sec: 0, subsec: 0, dst: false, zone: "UTC" } + d.deconstruct_keys(nil).should == res + end + + it "returns only specified keys" do + d = Time.utc(2022, 10, 5, 13, 39) + d.deconstruct_keys([:zone, :subsec]).should == { zone: "UTC", subsec: 0 } + end + + it "requires one argument" do + -> { + Time.new(2022, 10, 5, 13, 30).deconstruct_keys + }.should raise_error(ArgumentError) + end + + it "it raises error when argument is neither nil nor array" do + d = Time.new(2022, 10, 5, 13, 30) + + -> { d.deconstruct_keys(1) }.should raise_error(TypeError, "wrong argument type Integer (expected Array or nil)") + -> { d.deconstruct_keys("asd") }.should raise_error(TypeError, "wrong argument type String (expected Array or nil)") + -> { d.deconstruct_keys(:x) }.should raise_error(TypeError, "wrong argument type Symbol (expected Array or nil)") + -> { d.deconstruct_keys({}) }.should raise_error(TypeError, "wrong argument type Hash (expected Array or nil)") + end + + it "returns {} when passed []" do + Time.new(2022, 10, 5, 13, 30).deconstruct_keys([]).should == {} + end + + it "ignores non-Symbol keys" do + Time.new(2022, 10, 5, 13, 30).deconstruct_keys(['year', []]).should == {} + end + + it "ignores not existing Symbol keys" do + Time.new(2022, 10, 5, 13, 30).deconstruct_keys([:year, :a]).should == { year: 2022 } + end + end +end diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb index 69ec7bee5d..d95c82b20a 100644 --- a/spec/ruby/core/time/new_spec.rb +++ b/spec/ruby/core/time/new_spec.rb @@ -475,4 +475,164 @@ describe "Time.new with a timezone argument" do end end end + + ruby_version_is "3.2" do + describe "Time.new with a String argument" do + it "parses an ISO-8601 like format" do + t = Time.utc(2020, 12, 24, 15, 56, 17) + + Time.new("2020-12-24T15:56:17Z").should == t + Time.new("2020-12-25 00:56:17 +09:00").should == t + Time.new("2020-12-25 00:57:47 +09:01:30").should == t + Time.new("2020-12-25 00:56:17 +0900").should == t + Time.new("2020-12-25 00:57:47 +090130").should == t + Time.new("2020-12-25T00:56:17+09:00").should == t + end + + it "accepts precision keyword argument and truncates specified digits of sub-second part" do + Time.new("2021-12-25 00:00:00.123456789876 +09:00").subsec.should == 0.123456789r + Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: nil).subsec.should == 0.123456789876r + Time.new("2021-12-25 00:00:00 +09:00", precision: 0).subsec.should == 0 + Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: -1).subsec.should == 0.123456789876r + end + + it "returns Time in local timezone if not provided in the String argument" do + Time.new("2021-12-25 00:00:00").zone.should == Time.new(2021, 12, 25).zone + Time.new("2021-12-25 00:00:00").utc_offset.should == Time.new(2021, 12, 25).utc_offset + end + + it "returns Time in timezone specified in the String argument" do + Time.new("2021-12-25 00:00:00 +05:00").to_s.should == "2021-12-25 00:00:00 +0500" + end + + it "returns Time in timezone specified in the String argument even if the in keyword argument provided" do + Time.new("2021-12-25 00:00:00 +09:00", in: "-01:00").to_s.should == "2021-12-25 00:00:00 +0900" + end + + it "returns Time in timezone specified with in keyword argument if timezone isn't provided in the String argument" do + Time.new("2021-12-25 00:00:00", in: "-01:00").to_s.should == "2021-12-25 00:00:00 -0100" + end + + it "converts precision keyword argument into Integer if is not nil" do + obj = Object.new + def obj.to_int; 3; end + + Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: 1.2).subsec.should == 0.1r + Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: obj).subsec.should == 0.123r + Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: 3r).subsec.should == 0.123r + end + + it "raise TypeError is can't convert precision keyword argument into Integer" do + -> { + Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: "") + }.should raise_error(TypeError, "no implicit conversion from string") + end + + it "raises ArgumentError if part of time string is missing" do + -> { + Time.new("2020-12-25 00:56 +09:00") + }.should raise_error(ArgumentError, "missing sec part: 00:56 ") + + -> { + Time.new("2020-12-25 00 +09:00") + }.should raise_error(ArgumentError, "missing min part: 00 ") + end + + it "raises ArgumentError if subsecond is missing after dot" do + -> { + Time.new("2020-12-25 00:56:17. +0900") + }.should raise_error(ArgumentError, "subsecond expected after dot: 00:56:17. ") + end + + it "raises ArgumentError if String argument is not in the supported format" do + -> { + Time.new("021-12-25 00:00:00.123456 +09:00") + }.should raise_error(ArgumentError, "year must be 4 or more digits: 021") + + -> { + Time.new("2020-012-25 00:56:17 +0900") + }.should raise_error(ArgumentError, "two digits mon is expected after `-': -012-25 00:") + + -> { + Time.new("2020-2-25 00:56:17 +0900") + }.should raise_error(ArgumentError, "two digits mon is expected after `-': -2-25 00:56") + + -> { + Time.new("2020-12-215 00:56:17 +0900") + }.should raise_error(ArgumentError, "two digits mday is expected after `-': -215 00:56:") + + -> { + Time.new("2020-12-25 000:56:17 +0900") + }.should raise_error(ArgumentError, "two digits hour is expected: 000:56:17 ") + + -> { + Time.new("2020-12-25 0:56:17 +0900") + }.should raise_error(ArgumentError, "two digits hour is expected: 0:56:17 +0") + + -> { + Time.new("2020-12-25 00:516:17 +0900") + }.should raise_error(ArgumentError, "two digits min is expected after `:': :516:17 +09") + + -> { + Time.new("2020-12-25 00:6:17 +0900") + }.should raise_error(ArgumentError, "two digits min is expected after `:': :6:17 +0900") + + -> { + Time.new("2020-12-25 00:56:137 +0900") + }.should raise_error(ArgumentError, "two digits sec is expected after `:': :137 +0900") + + -> { + Time.new("2020-12-25 00:56:7 +0900") + }.should raise_error(ArgumentError, "two digits sec is expected after `:': :7 +0900") + + -> { + Time.new("2020-12-25 00:56. +0900") + }.should raise_error(ArgumentError, "fraction min is not supported: 00:56.") + + -> { + Time.new("2020-12-25 00. +0900") + }.should raise_error(ArgumentError, "fraction hour is not supported: 00.") + end + + it "raises ArgumentError if date/time parts values are not valid" do + -> { + Time.new("2020-13-25 00:56:17 +09:00") + }.should raise_error(ArgumentError, "mon out of range") + + -> { + Time.new("2020-12-32 00:56:17 +09:00") + }.should raise_error(ArgumentError, "mday out of range") + + -> { + Time.new("2020-12-25 25:56:17 +09:00") + }.should raise_error(ArgumentError, "hour out of range") + + -> { + Time.new("2020-12-25 00:61:17 +09:00") + }.should raise_error(ArgumentError, "min out of range") + + -> { + Time.new("2020-12-25 00:56:61 +09:00") + }.should raise_error(ArgumentError, "sec out of range") + + -> { + Time.new("2020-12-25 00:56:17 +23:59:60") + }.should raise_error(ArgumentError, "utc_offset out of range") + + -> { + Time.new("2020-12-25 00:56:17 +24:00") + }.should raise_error(ArgumentError, "utc_offset out of range") + + -> { + 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') + end + + it "raises ArgumentError if string has not ascii-compatible encoding" do + -> { + Time.new("2021-11-31 00:00:60 +09:00".encode("utf-32le")) + }.should raise_error(ArgumentError, "time string should have ASCII compatible encoding") + end + end + end end diff --git a/spec/ruby/core/unboundmethod/private_spec.rb b/spec/ruby/core/unboundmethod/private_spec.rb index fa735846bb..8ea50bb5d4 100644 --- a/spec/ruby/core/unboundmethod/private_spec.rb +++ b/spec/ruby/core/unboundmethod/private_spec.rb @@ -1,8 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "3.1"..."3.2" do - describe "UnboundMethod#private?" do +describe "UnboundMethod#private?" do + ruby_version_is "3.1"..."3.2" do it "returns false when the method is public" do obj = UnboundMethodSpecs::Methods.new obj.method(:my_public_method).unbind.private?.should == false @@ -18,4 +18,11 @@ ruby_version_is "3.1"..."3.2" do obj.method(:my_private_method).unbind.private?.should == true end end + + ruby_version_is "3.2" do + it "has been removed" do + obj = UnboundMethodSpecs::Methods.new + obj.method(:my_private_method).unbind.should_not.respond_to?(:private?) + end + end end diff --git a/spec/ruby/core/unboundmethod/protected_spec.rb b/spec/ruby/core/unboundmethod/protected_spec.rb index db00e7ef43..0c215d8638 100644 --- a/spec/ruby/core/unboundmethod/protected_spec.rb +++ b/spec/ruby/core/unboundmethod/protected_spec.rb @@ -1,8 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "3.1"..."3.2" do - describe "UnboundMethod#protected?" do +describe "UnboundMethod#protected?" do + ruby_version_is "3.1"..."3.2" do it "returns false when the method is public" do obj = UnboundMethodSpecs::Methods.new obj.method(:my_public_method).unbind.protected?.should == false @@ -18,4 +18,11 @@ ruby_version_is "3.1"..."3.2" do obj.method(:my_private_method).unbind.protected?.should == false end end + + ruby_version_is "3.2" do + it "has been removed" do + obj = UnboundMethodSpecs::Methods.new + obj.method(:my_protected_method).unbind.should_not.respond_to?(:protected?) + end + end end diff --git a/spec/ruby/core/unboundmethod/public_spec.rb b/spec/ruby/core/unboundmethod/public_spec.rb index 7b87a03b15..552bbf6eab 100644 --- a/spec/ruby/core/unboundmethod/public_spec.rb +++ b/spec/ruby/core/unboundmethod/public_spec.rb @@ -1,8 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "3.1"..."3.2" do - describe "UnboundMethod#public?" do +describe "UnboundMethod#public?" do + ruby_version_is "3.1"..."3.2" do it "returns true when the method is public" do obj = UnboundMethodSpecs::Methods.new obj.method(:my_public_method).unbind.public?.should == true @@ -18,4 +18,11 @@ ruby_version_is "3.1"..."3.2" do obj.method(:my_private_method).unbind.public?.should == false end end + + ruby_version_is "3.2" do + it "has been removed" do + obj = UnboundMethodSpecs::Methods.new + obj.method(:my_public_method).unbind.should_not.respond_to?(:public?) + end + end end diff --git a/spec/ruby/core/unboundmethod/source_location_spec.rb b/spec/ruby/core/unboundmethod/source_location_spec.rb index 96933a5d40..c6823aa84b 100644 --- a/spec/ruby/core/unboundmethod/source_location_spec.rb +++ b/spec/ruby/core/unboundmethod/source_location_spec.rb @@ -49,4 +49,11 @@ describe "UnboundMethod#source_location" do method.source_location[0].should =~ /#{__FILE__}/ method.source_location[1].should == line end + + it "works for eval with a given line" do + c = Class.new do + eval('def m; end', nil, "foo", 100) + end + c.instance_method(:m).source_location.should == ["foo", 100] + end end diff --git a/spec/ruby/library/coverage/supported_spec.rb b/spec/ruby/library/coverage/supported_spec.rb new file mode 100644 index 0000000000..78b3784ee0 --- /dev/null +++ b/spec/ruby/library/coverage/supported_spec.rb @@ -0,0 +1,32 @@ +require_relative '../../spec_helper' +require 'coverage' + +describe "Coverage.supported?" do + ruby_version_is "3.2" do + it "returns true or false if coverage measurement is supported for the given mode" do + [true, false].should.include?(Coverage.supported?(:lines)) + [true, false].should.include?(Coverage.supported?(:branches)) + [true, false].should.include?(Coverage.supported?(:methods)) + [true, false].should.include?(Coverage.supported?(:eval)) + end + + it "returns false for not existing modes" do + Coverage.supported?(:foo).should == false + Coverage.supported?(:bar).should == false + end + + it "raise TypeError if argument is not Symbol" do + -> { + Coverage.supported?("lines") + }.should raise_error(TypeError, "wrong argument type String (expected Symbol)") + + -> { + Coverage.supported?([]) + }.should raise_error(TypeError, "wrong argument type Array (expected Symbol)") + + -> { + Coverage.supported?(1) + }.should raise_error(TypeError, "wrong argument type Integer (expected Symbol)") + end + end +end diff --git a/spec/ruby/library/date/deconstruct_keys_spec.rb b/spec/ruby/library/date/deconstruct_keys_spec.rb new file mode 100644 index 0000000000..fc9caaf332 --- /dev/null +++ b/spec/ruby/library/date/deconstruct_keys_spec.rb @@ -0,0 +1,43 @@ +require_relative '../../spec_helper' +require 'date' + +ruby_version_is "3.2" do + describe "Date#deconstruct_keys" do + it "returns whole hash for nil as an argument" do + d = Date.new(2022, 10, 5) + d.deconstruct_keys(nil).should == { year: 2022, month: 10, day: 5, yday: 278, wday: 3 } + end + + it "returns only specified keys" do + d = Date.new(2022, 10, 5) + d.deconstruct_keys([:year, :month]).should == { year: 2022, month: 10 } + end + + it "requires one argument" do + -> { + Date.new(2022, 10, 5).deconstruct_keys + }.should raise_error(ArgumentError) + end + + it "it raises error when argument is neither nil nor array" do + d = Date.new(2022, 10, 5) + + -> { d.deconstruct_keys(1) }.should raise_error(TypeError, "wrong argument type Integer (expected Array or nil)") + -> { d.deconstruct_keys("asd") }.should raise_error(TypeError, "wrong argument type String (expected Array or nil)") + -> { d.deconstruct_keys(:x) }.should raise_error(TypeError, "wrong argument type Symbol (expected Array or nil)") + -> { d.deconstruct_keys({}) }.should raise_error(TypeError, "wrong argument type Hash (expected Array or nil)") + end + + it "returns {} when passed []" do + Date.new(2022, 10, 5).deconstruct_keys([]).should == {} + end + + it "ignores non-Symbol keys" do + Date.new(2022, 10, 5).deconstruct_keys(['year', []]).should == {} + end + + it "ignores not existing Symbol keys" do + Date.new(2022, 10, 5).deconstruct_keys([:year, :a]).should == { year: 2022 } + end + end +end diff --git a/spec/ruby/library/datetime/deconstruct_keys_spec.rb b/spec/ruby/library/datetime/deconstruct_keys_spec.rb new file mode 100644 index 0000000000..c6c0f71f55 --- /dev/null +++ b/spec/ruby/library/datetime/deconstruct_keys_spec.rb @@ -0,0 +1,45 @@ +require_relative '../../spec_helper' +require 'date' + +ruby_version_is "3.2" do + describe "DateTime#deconstruct_keys" do + it "returns whole hash for nil as an argument" do + d = DateTime.new(2022, 10, 5, 13, 30) + res = { year: 2022, month: 10, day: 5, yday: 278, wday: 3, hour: 13, + min: 30, sec: 0, sec_fraction: (0/1), zone: "+00:00" } + d.deconstruct_keys(nil).should == res + end + + it "returns only specified keys" do + d = DateTime.new(2022, 10, 5, 13, 39) + d.deconstruct_keys([:zone, :hour]).should == { zone: "+00:00", hour: 13 } + end + + it "requires one argument" do + -> { + DateTime.new(2022, 10, 5, 13, 30).deconstruct_keys + }.should raise_error(ArgumentError) + end + + it "it raises error when argument is neither nil nor array" do + d = DateTime.new(2022, 10, 5, 13, 30) + + -> { d.deconstruct_keys(1) }.should raise_error(TypeError, "wrong argument type Integer (expected Array or nil)") + -> { d.deconstruct_keys("asd") }.should raise_error(TypeError, "wrong argument type String (expected Array or nil)") + -> { d.deconstruct_keys(:x) }.should raise_error(TypeError, "wrong argument type Symbol (expected Array or nil)") + -> { d.deconstruct_keys({}) }.should raise_error(TypeError, "wrong argument type Hash (expected Array or nil)") + end + + it "returns {} when passed []" do + DateTime.new(2022, 10, 5, 13, 30).deconstruct_keys([]).should == {} + end + + it "ignores non-Symbol keys" do + DateTime.new(2022, 10, 5, 13, 30).deconstruct_keys(['year', []]).should == {} + end + + it "ignores not existing Symbol keys" do + DateTime.new(2022, 10, 5, 13, 30).deconstruct_keys([:year, :a]).should == { year: 2022 } + end + end +end diff --git a/spec/ruby/library/pathname/relative_path_from_spec.rb b/spec/ruby/library/pathname/relative_path_from_spec.rb index abe9c80a45..133a149849 100644 --- a/spec/ruby/library/pathname/relative_path_from_spec.rb +++ b/spec/ruby/library/pathname/relative_path_from_spec.rb @@ -48,4 +48,8 @@ describe "Pathname#relative_path_from" do relative_path_str('..', '..').should == '.' relative_path_str('..', '.').should == '..' end + + it 'converts string argument to Pathname' do + Pathname.new('/usr/bin/ls').relative_path_from('/usr').to_s.should == 'bin/ls' + end end diff --git a/spec/ruby/library/socket/tcpsocket/shared/new.rb b/spec/ruby/library/socket/tcpsocket/shared/new.rb index e7eb2f3c13..5d91435a2a 100644 --- a/spec/ruby/library/socket/tcpsocket/shared/new.rb +++ b/spec/ruby/library/socket/tcpsocket/shared/new.rb @@ -14,7 +14,7 @@ describe :tcpsocket_new, shared: true do } end - ruby_version_is "3.0"..."3.1" do + ruby_version_is "3.0"..."3.2" do it 'raises Errno::ETIMEDOUT with :connect_timeout when no server is listening on the given address' do -> { TCPSocket.send(@method, "192.0.2.1", 80, connect_timeout: 0) diff --git a/spec/ruby/library/uri/generic/host_spec.rb b/spec/ruby/library/uri/generic/host_spec.rb index f2076d2bc1..2de0e7ec09 100644 --- a/spec/ruby/library/uri/generic/host_spec.rb +++ b/spec/ruby/library/uri/generic/host_spec.rb @@ -2,7 +2,12 @@ require_relative '../../../spec_helper' require 'uri' describe "URI::Generic#host" do - it "needs to be reviewed for spec completeness" + ruby_version_is "3.2" do + # https://hackerone.com/reports/156615 + it "returns empty string when host is empty" do + URI.parse('http:////foo.com').host.should == '' + end + end end describe "URI::Generic#host=" do diff --git a/spec/ruby/library/uri/generic/to_s_spec.rb b/spec/ruby/library/uri/generic/to_s_spec.rb index 8c90d7645b..1dd1f2d134 100644 --- a/spec/ruby/library/uri/generic/to_s_spec.rb +++ b/spec/ruby/library/uri/generic/to_s_spec.rb @@ -2,5 +2,10 @@ require_relative '../../../spec_helper' require 'uri' describe "URI::Generic#to_s" do - it "needs to be reviewed for spec completeness" + ruby_version_is "3.2" do + # https://hackerone.com/reports/156615 + it "preserves / characters when host is empty" do + URI('http:///foo.com').to_s.should == 'http:///foo.com' + end + end end diff --git a/spec/ruby/library/zlib/gzipreader/mtime_spec.rb b/spec/ruby/library/zlib/gzipreader/mtime_spec.rb new file mode 100644 index 0000000000..e8e71fa72e --- /dev/null +++ b/spec/ruby/library/zlib/gzipreader/mtime_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../../spec_helper' +require 'zlib' +require 'stringio' + +describe "Zlib::GzipReader#mtime" do + it "returns the timestamp from the Gzip header" do + io = StringIO.new "\x1f\x8b\x08\x00\x44\x33\x22\x11\x00\xff\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00" + gz = Zlib::GzipReader.new(io) + gz.mtime.to_i.should == 0x11223344 + end +end |