diff options
Diffstat (limited to 'spec/ruby/core')
26 files changed, 650 insertions, 152 deletions
diff --git a/spec/ruby/core/array/fetch_values_spec.rb b/spec/ruby/core/array/fetch_values_spec.rb index 075dcc7a52..cf377b3b71 100644 --- a/spec/ruby/core/array/fetch_values_spec.rb +++ b/spec/ruby/core/array/fetch_values_spec.rb @@ -21,7 +21,7 @@ describe "Array#fetch_values" do 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) + -> { @array.fetch_values(0, 1, 44) }.should raise_error(IndexError, "index 44 outside of array bounds: -3...3") end it "returns the default value from block" do @@ -42,8 +42,14 @@ describe "Array#fetch_values" do @array.fetch_values(obj).should == [:c] end + it "does not support a Range object as argument" do + -> { + @array.fetch_values(1..2) + }.should raise_error(TypeError, "no implicit conversion of Range into Integer") + end + it "raises a TypeError when the passed argument can't be coerced to Integer" do - -> { [].fetch_values("cat") }.should raise_error(TypeError) + -> { [].fetch_values("cat") }.should raise_error(TypeError, "no implicit conversion of String into Integer") end end end diff --git a/spec/ruby/core/data/deconstruct_keys_spec.rb b/spec/ruby/core/data/deconstruct_keys_spec.rb index 5cae4cbd68..df378f8b98 100644 --- a/spec/ruby/core/data/deconstruct_keys_spec.rb +++ b/spec/ruby/core/data/deconstruct_keys_spec.rb @@ -1,10 +1,11 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -describe "Data#deconstruct" do +describe "Data#deconstruct_keys" do it "returns a hash of attributes" do klass = Data.define(:x, :y) d = klass.new(1, 2) + d.deconstruct_keys([:x, :y]).should == {x: 1, y: 2} end @@ -29,6 +30,7 @@ describe "Data#deconstruct" do it "accepts string attribute names" do klass = Data.define(:x, :y) d = klass.new(1, 2) + d.deconstruct_keys(['x', 'y']).should == {'x' => 1, 'y' => 2} end @@ -58,6 +60,7 @@ describe "Data#deconstruct" do it "returns an empty hash when there are more keys than attributes" do klass = Data.define(:x, :y) d = klass.new(1, 2) + d.deconstruct_keys([:x, :y, :x]).should == {} end @@ -84,6 +87,28 @@ describe "Data#deconstruct" do d.deconstruct_keys(nil).should == {x: 1, y: 2} end + it "tries to convert a key with #to_int if index is not a String nor a Symbol, but responds to #to_int" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) + + key = mock("to_int") + key.should_receive(:to_int).and_return(1) + + d.deconstruct_keys([key]).should == { key => 2 } + end + + it "raises a TypeError if the conversion with #to_int does not return an Integer" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) + + key = mock("to_int") + key.should_receive(:to_int).and_return("not an Integer") + + -> { + d.deconstruct_keys([key]) + }.should raise_error(TypeError, /can't convert MockObject to Integer/) + end + it "raises TypeError if index is not a String, a Symbol and not convertible to Integer " do klass = Data.define(:x, :y) d = klass.new(1, 2) diff --git a/spec/ruby/core/data/fixtures/classes.rb b/spec/ruby/core/data/fixtures/classes.rb index 5db263fa20..2d48780496 100644 --- a/spec/ruby/core/data/fixtures/classes.rb +++ b/spec/ruby/core/data/fixtures/classes.rb @@ -9,5 +9,13 @@ module DataSpecs end class DataSubclass < Data; end + + MeasureSubclass = Class.new(Measure) do + def initialize(amount:, unit:) + super + end + end + + Empty = Data.define() end end diff --git a/spec/ruby/core/data/initialize_spec.rb b/spec/ruby/core/data/initialize_spec.rb index 37a6c8f2dd..0f75f32f57 100644 --- a/spec/ruby/core/data/initialize_spec.rb +++ b/spec/ruby/core/data/initialize_spec.rb @@ -60,4 +60,15 @@ describe "Data#initialize" do e.message.should.include?("unknown keyword: :system") } end + + it "supports super from a subclass" do + ms = DataSpecs::MeasureSubclass.new(amount: 1, unit: "km") + + ms.amount.should == 1 + ms.unit.should == "km" + end + + it "supports Data with no fields" do + -> { DataSpecs::Empty.new }.should_not raise_error + end end diff --git a/spec/ruby/core/data/shared/inspect.rb b/spec/ruby/core/data/shared/inspect.rb index 7f54a46de3..6cd5664da7 100644 --- a/spec/ruby/core/data/shared/inspect.rb +++ b/spec/ruby/core/data/shared/inspect.rb @@ -50,5 +50,13 @@ describe :data_inspect, shared: true do a.send(@method).should == "#<data DataSpecs::Measure amount=42, unit=#<data DataSpecs::Measure:...>>" end + + it "returns string representation with recursive attribute replaced with ... when an anonymous class" do + klass = Class.new(DataSpecs::Measure) + a = klass.allocate + a.send(:initialize, amount: 42, unit: a) + + a.send(@method).should =~ /#<data amount=42, unit=#<data #<Class:0x.+?>:\.\.\.>>/ + end end end diff --git a/spec/ruby/core/enumerable/filter_spec.rb b/spec/ruby/core/enumerable/filter_spec.rb index c9ee23c541..1c3a7e9ff5 100644 --- a/spec/ruby/core/enumerable/filter_spec.rb +++ b/spec/ruby/core/enumerable/filter_spec.rb @@ -3,5 +3,5 @@ require_relative 'fixtures/classes' require_relative 'shared/find_all' describe "Enumerable#filter" do - it_behaves_like(:enumerable_find_all, :filter) + it_behaves_like :enumerable_find_all, :filter end diff --git a/spec/ruby/core/exception/frozen_error_spec.rb b/spec/ruby/core/exception/frozen_error_spec.rb index 979ec2ff98..51eb79cace 100644 --- a/spec/ruby/core/exception/frozen_error_spec.rb +++ b/spec/ruby/core/exception/frozen_error_spec.rb @@ -21,6 +21,20 @@ describe "FrozenError#receiver" do end end +describe "FrozenError#message" do + it "includes a receiver" do + object = Object.new + object.freeze + + -> { + def object.x; end + }.should raise_error(FrozenError, "can't modify frozen object: #{object}") + + object = [].freeze + -> { object << nil }.should raise_error(FrozenError, "can't modify frozen Array: []") + end +end + describe "Modifying a frozen object" do context "#inspect is redefined and modifies the object" do it "returns ... instead of String representation of object" do diff --git a/spec/ruby/core/exception/set_backtrace_spec.rb b/spec/ruby/core/exception/set_backtrace_spec.rb index 12c1da919c..2cd93326ec 100644 --- a/spec/ruby/core/exception/set_backtrace_spec.rb +++ b/spec/ruby/core/exception/set_backtrace_spec.rb @@ -1,13 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' +require_relative 'shared/set_backtrace' describe "Exception#set_backtrace" do - it "accepts an Array of Strings" do - err = RuntimeError.new - err.set_backtrace ["unhappy"] - err.backtrace.should == ["unhappy"] - end - it "allows the user to set the backtrace from a rescued exception" do bt = ExceptionSpecs::Backtrace.backtrace err = RuntimeError.new @@ -20,65 +15,9 @@ describe "Exception#set_backtrace" do err.backtrace_locations.should == nil end - ruby_version_is "3.4" do - it "allows the user to set backtrace locations from a rescued exception" do - bt_locations = ExceptionSpecs::Backtrace.backtrace_locations - err = RuntimeError.new - err.backtrace.should == nil - err.backtrace_locations.should == nil - - err.set_backtrace bt_locations - - err.backtrace_locations.size.should == bt_locations.size - err.backtrace_locations.each_with_index do |loc, index| - other_loc = bt_locations[index] - - loc.path.should == other_loc.path - loc.label.should == other_loc.label - loc.base_label.should == other_loc.base_label - loc.lineno.should == other_loc.lineno - loc.absolute_path.should == other_loc.absolute_path - loc.to_s.should == other_loc.to_s - end - err.backtrace.size.should == err.backtrace_locations.size - end - end - - it "accepts an empty Array" do - err = RuntimeError.new - err.set_backtrace [] - err.backtrace.should == [] - end - - it "accepts a String" do + it_behaves_like :exception_set_backtrace, -> backtrace { err = RuntimeError.new - err.set_backtrace "unhappy" - err.backtrace.should == ["unhappy"] - end - - it "accepts nil" do - err = RuntimeError.new - err.set_backtrace nil - err.backtrace.should be_nil - end - - it "raises a TypeError when passed a Symbol" do - err = RuntimeError.new - -> { err.set_backtrace :unhappy }.should raise_error(TypeError) - end - - it "raises a TypeError when the Array contains a Symbol" do - err = RuntimeError.new - -> { err.set_backtrace ["String", :unhappy] }.should raise_error(TypeError) - end - - it "raises a TypeError when the array contains nil" do - err = Exception.new - -> { err.set_backtrace ["String", nil] }.should raise_error(TypeError) - end - - it "raises a TypeError when the argument is a nested array" do - err = Exception.new - -> { err.set_backtrace ["String", ["String"]] }.should raise_error(TypeError) - end + err.set_backtrace(backtrace) + err + } end diff --git a/spec/ruby/core/exception/shared/set_backtrace.rb b/spec/ruby/core/exception/shared/set_backtrace.rb new file mode 100644 index 0000000000..c6213b42b4 --- /dev/null +++ b/spec/ruby/core/exception/shared/set_backtrace.rb @@ -0,0 +1,64 @@ +require_relative '../fixtures/common' + +describe :exception_set_backtrace, shared: true do + it "accepts an Array of Strings" do + err = @method.call(["unhappy"]) + err.backtrace.should == ["unhappy"] + end + + it "allows the user to set the backtrace from a rescued exception" do + bt = ExceptionSpecs::Backtrace.backtrace + err = @method.call(bt) + err.backtrace.should == bt + end + + ruby_version_is "3.4" do + it "allows the user to set backtrace locations from a rescued exception" do + bt_locations = ExceptionSpecs::Backtrace.backtrace_locations + err = @method.call(bt_locations) + err.backtrace_locations.size.should == bt_locations.size + err.backtrace_locations.each_with_index do |loc, index| + other_loc = bt_locations[index] + + loc.path.should == other_loc.path + loc.label.should == other_loc.label + loc.base_label.should == other_loc.base_label + loc.lineno.should == other_loc.lineno + loc.absolute_path.should == other_loc.absolute_path + loc.to_s.should == other_loc.to_s + end + err.backtrace.size.should == err.backtrace_locations.size + end + end + + it "accepts an empty Array" do + err = @method.call([]) + err.backtrace.should == [] + end + + it "accepts a String" do + err = @method.call("unhappy") + err.backtrace.should == ["unhappy"] + end + + it "accepts nil" do + err = @method.call(nil) + err.backtrace.should be_nil + end + + it "raises a TypeError when passed a Symbol" do + -> { @method.call(:unhappy) }.should raise_error(TypeError) + end + + it "raises a TypeError when the Array contains a Symbol" do + -> { @method.call(["String", :unhappy]) }.should raise_error(TypeError) + end + + it "raises a TypeError when the array contains nil" do + -> { @method.call(["String", nil]) }.should raise_error(TypeError) + end + + it "raises a TypeError when the argument is a nested array" do + -> { @method.call(["String", ["String"]]) }.should raise_error(TypeError) + end +end diff --git a/spec/ruby/core/fiber/storage_spec.rb b/spec/ruby/core/fiber/storage_spec.rb index 3d39f32009..015caaf3bb 100644 --- a/spec/ruby/core/fiber/storage_spec.rb +++ b/spec/ruby/core/fiber/storage_spec.rb @@ -84,15 +84,17 @@ describe "Fiber.[]" do Fiber.new { Fiber[:life] }.resume.should be_nil end - it "can use dynamically defined keys" do - key = :"#{self.class.name}#.#{self.object_id}" - Fiber.new { Fiber[key] = 42; Fiber[key] }.resume.should == 42 - end + ruby_version_is "3.2.3" do + it "can use dynamically defined keys" do + key = :"#{self.class.name}#.#{self.object_id}" + Fiber.new { Fiber[key] = 42; Fiber[key] }.resume.should == 42 + end - it "can't use invalid keys" do - invalid_keys = [Object.new, 12] - invalid_keys.each do |key| - -> { Fiber[key] }.should raise_error(TypeError) + it "can't use invalid keys" do + invalid_keys = [Object.new, 12] + invalid_keys.each do |key| + -> { Fiber[key] }.should raise_error(TypeError) + end end end diff --git a/spec/ruby/core/integer/divide_spec.rb b/spec/ruby/core/integer/divide_spec.rb index 665f4d57be..0d5e16e986 100644 --- a/spec/ruby/core/integer/divide_spec.rb +++ b/spec/ruby/core/integer/divide_spec.rb @@ -106,4 +106,21 @@ describe "Integer#/" do -> { @bignum / :symbol }.should raise_error(TypeError) end end + + it "coerces the RHS and calls #coerce" do + obj = mock("integer plus") + obj.should_receive(:coerce).with(6).and_return([6, 3]) + (6 / obj).should == 2 + end + + it "coerces the RHS and calls #coerce even if it's private" do + obj = Object.new + class << obj + private def coerce(n) + [n, 3] + end + end + + (6 / obj).should == 2 + end end diff --git a/spec/ruby/core/integer/minus_spec.rb b/spec/ruby/core/integer/minus_spec.rb index aadf416a05..6072ba7c8b 100644 --- a/spec/ruby/core/integer/minus_spec.rb +++ b/spec/ruby/core/integer/minus_spec.rb @@ -40,4 +40,21 @@ describe "Integer#-" do -> { @bignum - :symbol }.should raise_error(TypeError) end end + + it "coerces the RHS and calls #coerce" do + obj = mock("integer plus") + obj.should_receive(:coerce).with(5).and_return([5, 10]) + (5 - obj).should == -5 + end + + it "coerces the RHS and calls #coerce even if it's private" do + obj = Object.new + class << obj + private def coerce(n) + [n, 10] + end + end + + (5 - obj).should == -5 + end end diff --git a/spec/ruby/core/integer/plus_spec.rb b/spec/ruby/core/integer/plus_spec.rb index d01a76ab58..38428e56c5 100644 --- a/spec/ruby/core/integer/plus_spec.rb +++ b/spec/ruby/core/integer/plus_spec.rb @@ -55,4 +55,21 @@ describe "Integer#+" do RUBY ruby_exe(code).should == "-1" end + + it "coerces the RHS and calls #coerce" do + obj = mock("integer plus") + obj.should_receive(:coerce).with(6).and_return([6, 3]) + (6 + obj).should == 9 + end + + it "coerces the RHS and calls #coerce even if it's private" do + obj = Object.new + class << obj + private def coerce(n) + [n, 3] + end + end + + (6 + obj).should == 9 + end end diff --git a/spec/ruby/core/kernel/Float_spec.rb b/spec/ruby/core/kernel/Float_spec.rb index 6cedfe0617..1705205996 100644 --- a/spec/ruby/core/kernel/Float_spec.rb +++ b/spec/ruby/core/kernel/Float_spec.rb @@ -222,59 +222,99 @@ describe :kernel_float, shared: true do end end - describe "for hexadecimal literals with binary exponent" do - %w(p P).each do |p| - it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do - @object.send(:Float, "0x10#{p}0").should == 16.0 - end - - it "interprets the exponent (on the right of '#{p}') in decimal" do - @object.send(:Float, "0x1#{p}10").should == 1024.0 - end - - it "raises an ArgumentError if #{p} is the trailing character" do - -> { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError) - end - - it "raises an ArgumentError if #{p} is the leading character" do - -> { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError) - end - - it "returns Infinity for '0x1#{p}10000'" do - @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY - end - - it "returns 0 for '0x1#{p}-10000'" do - @object.send(:Float, "0x1#{p}-10000").should == 0 - end + context "for hexadecimal literals" do + it "interprets the 0x prefix as hexadecimal" do + @object.send(:Float, "0x10").should == 16.0 + @object.send(:Float, "0x0F").should == 15.0 + @object.send(:Float, "0x0f").should == 15.0 + end - it "allows embedded _ in a number on either side of the #{p}" do - @object.send(:Float, "0x1_0#{p}10").should == 16384.0 - @object.send(:Float, "0x10#{p}1_0").should == 16384.0 - @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0 - end + it "accepts embedded _ if the number does not contain a-f" do + @object.send(:Float, "0x1_0").should == 16.0 + end - it "raises an exception if a space is embedded on either side of the '#{p}'" do - -> { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError) - -> { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError) + ruby_version_is ""..."3.4.3" do + it "does not accept embedded _ if the number contains a-f" do + -> { @object.send(:Float, "0x1_0a") }.should raise_error(ArgumentError) + @object.send(:Float, "0x1_0a", exception: false).should be_nil end + end - it "raises an exception if there's a leading _ on either side of the '#{p}'" do - -> { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError) - -> { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError) + ruby_version_is "3.4.3" do + it "accepts embedded _ if the number contains a-f" do + @object.send(:Float, "0x1_0a").should == 0x10a.to_f end + end - it "raises an exception if there's a trailing _ on either side of the '#{p}'" do - -> { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError) - -> { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError) - end + it "does not accept _ before, after or inside the 0x prefix" do + -> { @object.send(:Float, "_0x10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0_x10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0x_10") }.should raise_error(ArgumentError) + @object.send(:Float, "_0x10", exception: false).should be_nil + @object.send(:Float, "0_x10", exception: false).should be_nil + @object.send(:Float, "0x_10", exception: false).should be_nil + end - it "allows hexadecimal points on the left side of the '#{p}'" do - @object.send(:Float, "0x1.8#{p}0").should == 1.5 + ruby_version_is "3.4" do + it "accepts a fractional part" do + @object.send(:Float, "0x0.8").should == 0.5 end + end - it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do - -> { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError) + describe "with binary exponent" do + %w(p P).each do |p| + it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do + @object.send(:Float, "0x10#{p}0").should == 16.0 + end + + it "interprets the exponent (on the right of '#{p}') in decimal" do + @object.send(:Float, "0x1#{p}10").should == 1024.0 + end + + it "raises an ArgumentError if #{p} is the trailing character" do + -> { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if #{p} is the leading character" do + -> { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError) + end + + it "returns Infinity for '0x1#{p}10000'" do + @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY + end + + it "returns 0 for '0x1#{p}-10000'" do + @object.send(:Float, "0x1#{p}-10000").should == 0 + end + + it "allows embedded _ in a number on either side of the #{p}" do + @object.send(:Float, "0x1_0#{p}10").should == 16384.0 + @object.send(:Float, "0x10#{p}1_0").should == 16384.0 + @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0 + end + + it "raises an exception if a space is embedded on either side of the '#{p}'" do + -> { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a leading _ on either side of the '#{p}'" do + -> { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a trailing _ on either side of the '#{p}'" do + -> { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError) + end + + it "allows hexadecimal points on the left side of the '#{p}'" do + @object.send(:Float, "0x1.8#{p}0").should == 1.5 + end + + it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do + -> { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError) + end end end end diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb index c189d5f0a2..e027294347 100644 --- a/spec/ruby/core/kernel/eval_spec.rb +++ b/spec/ruby/core/kernel/eval_spec.rb @@ -175,6 +175,75 @@ describe "Kernel#eval" do end end + context "parameter forwarding" do + it "allows anonymous rest parameter forwarding" do + object = Object.new + def object.foo(a, b, c) + [a, b, c] + end + def object.bar(*) + eval "foo(*)" + end + + object.bar(1, 2, 3).should == [1, 2, 3] + end + + it "allows anonymous keyword parameters forwarding" do + object = Object.new + def object.foo(a:, b:, c:) + [a, b, c] + end + def object.bar(**) + eval "foo(**)" + end + + object.bar(a: 1, b: 2, c: 3).should == [1, 2, 3] + end + + it "allows anonymous block parameter forwarding" do + object = Object.new + def object.foo(&block) + block.call + end + def object.bar(&) + eval "foo(&)" + end + + object.bar { :foobar }.should == :foobar + end + + it "allows ... forwarding" do + object = Object.new + def object.foo(a, b:, &block) + [a, b, block.call] + end + def object.bar(...) + eval "foo(...)" + end + + object.bar(1, b: 2) { 3 }.should == [1, 2, 3] + end + + it "allows parameter forwarding to super" do + m = Module.new do + def foo(a, b:, &block) + [a, b, block.call] + end + end + + c = Class.new do + include m + + def foo(a, b:, &block) + eval "super" + end + end + + object = c.new + object.foo(1, b: 2) { 3 }.should == [1, 2, 3] + end + end + ruby_version_is "3.3" do it "uses (eval at __FILE__:__LINE__) if none is provided" do eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})" diff --git a/spec/ruby/core/kernel/raise_spec.rb b/spec/ruby/core/kernel/raise_spec.rb index a038dcf031..cc1d5846e5 100644 --- a/spec/ruby/core/kernel/raise_spec.rb +++ b/spec/ruby/core/kernel/raise_spec.rb @@ -44,7 +44,53 @@ describe "Kernel#raise" do it "raises an ArgumentError when only cause is given" do cause = StandardError.new - -> { raise(cause: cause) }.should raise_error(ArgumentError) + -> { raise(cause: cause) }.should raise_error(ArgumentError, "only cause is given with no arguments") + end + + it "raises an ArgumentError when only cause is given even if it has nil value" do + -> { raise(cause: nil) }.should raise_error(ArgumentError, "only cause is given with no arguments") + end + + it "raises an ArgumentError when given cause is not an instance of Exception" do + -> { raise "message", cause: Object.new }.should raise_error(TypeError, "exception object expected") + end + + it "doesn't raise an ArgumentError when given cause is nil" do + -> { raise "message", cause: nil }.should raise_error(RuntimeError, "message") + end + + it "allows cause equal an exception" do + e = RuntimeError.new("message") + -> { raise e, cause: e }.should raise_error(e) + end + + it "doesn't set given cause when it equals an exception" do + e = RuntimeError.new("message") + + begin + raise e, cause: e + rescue + end + + e.cause.should == nil + end + + it "raises ArgumentError when exception is part of the cause chain" do + -> { + begin + raise "Error 1" + rescue => e1 + begin + raise "Error 2" + rescue => e2 + begin + raise "Error 3" + rescue => e3 + raise e1, cause: e3 + end + end + end + }.should raise_error(ArgumentError, "circular causes") end it "re-raises a rescued exception" do @@ -62,6 +108,101 @@ describe "Kernel#raise" do end end.should raise_error(StandardError, "aaa") end + + it "re-raises a previously rescued exception without overwriting the cause" do + begin + begin + begin + begin + raise "Error 1" + rescue => e1 + raise "Error 2" + end + rescue => e2 + raise "Error 3" + end + rescue + e2.cause.should == e1 + raise e2 + end + rescue => e + e.cause.should == e1 + end + end + + it "re-raises a previously rescued exception with overwriting the cause when it's explicitly specified with :cause option" do + e4 = RuntimeError.new("Error 4") + + begin + begin + begin + begin + raise "Error 1" + rescue => e1 + raise "Error 2" + end + rescue => e2 + raise "Error 3" + end + rescue + e2.cause.should == e1 + raise e2, cause: e4 + end + rescue => e + e.cause.should == e4 + end + end + + it "re-raises a previously rescued exception without overwriting the cause when it's explicitly specified with :cause option and has nil value" do + begin + begin + begin + begin + raise "Error 1" + rescue => e1 + raise "Error 2" + end + rescue => e2 + raise "Error 3" + end + rescue + e2.cause.should == e1 + raise e2, cause: nil + end + rescue => e + e.cause.should == e1 + end + end + + it "re-raises a previously rescued exception without setting a cause implicitly" do + begin + begin + raise "Error 1" + rescue => e1 + raise + end + rescue => e + e.should == e1 + e.cause.should == nil + end + end + + it "re-raises a previously rescued exception that has a cause without setting a cause implicitly" do + begin + begin + raise "Error 1" + rescue => e1 + begin + raise "Error 2" + rescue => e2 + raise + end + end + rescue => e + e.should == e2 + e.cause.should == e1 + end + end end describe "Kernel#raise" do diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb index 26bd189593..a68389a7b4 100644 --- a/spec/ruby/core/kernel/shared/sprintf.rb +++ b/spec/ruby/core/kernel/shared/sprintf.rb @@ -362,6 +362,10 @@ describe :kernel_sprintf, shared: true do obj.should_receive(:inspect).and_return("<inspect-result>") @method.call("%p", obj).should == "<inspect-result>" end + + it "substitutes 'nil' for nil" do + @method.call("%p", nil).should == "nil" + end end describe "s" do diff --git a/spec/ruby/core/regexp/linear_time_spec.rb b/spec/ruby/core/regexp/linear_time_spec.rb index a6f8dccd46..c3b3500549 100644 --- a/spec/ruby/core/regexp/linear_time_spec.rb +++ b/spec/ruby/core/regexp/linear_time_spec.rb @@ -6,6 +6,10 @@ describe "Regexp.linear_time?" do Regexp.linear_time?('a').should == true end + it "returns true if matching can be done in linear time for a binary Regexp" do + Regexp.linear_time?(/[\x80-\xff]/n).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 diff --git a/spec/ruby/core/string/to_f_spec.rb b/spec/ruby/core/string/to_f_spec.rb index ab1ba43fb3..a91ccc168e 100644 --- a/spec/ruby/core/string/to_f_spec.rb +++ b/spec/ruby/core/string/to_f_spec.rb @@ -120,9 +120,11 @@ describe "String#to_f" do "\3771.2".b.to_f.should == 0 end - it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do - -> { - '1.2'.encode("UTF-16").to_f - }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16") + ruby_version_is "3.2.3" do + it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do + -> { + '1.2'.encode("UTF-16").to_f + }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16") + end end end diff --git a/spec/ruby/core/struct/deconstruct_keys_spec.rb b/spec/ruby/core/struct/deconstruct_keys_spec.rb index 602403d183..e16b50f930 100644 --- a/spec/ruby/core/struct/deconstruct_keys_spec.rb +++ b/spec/ruby/core/struct/deconstruct_keys_spec.rb @@ -43,6 +43,13 @@ describe "Struct#deconstruct_keys" do s.deconstruct_keys([-1] ).should == {-1 => 30} end + it "ignores incorrect position numbers" do + struct = Struct.new(:x, :y, :z) + s = struct.new(10, 20, 30) + + s.deconstruct_keys([0, 3]).should == {0 => 10} + end + it "support mixing attribute names and argument position numbers" do struct = Struct.new(:x, :y) s = struct.new(1, 2) @@ -80,6 +87,28 @@ describe "Struct#deconstruct_keys" do obj.deconstruct_keys(nil).should == {x: 1, y: 2} end + it "tries to convert a key with #to_int if index is not a String nor a Symbol, but responds to #to_int" do + struct = Struct.new(:x, :y) + s = struct.new(1, 2) + + key = mock("to_int") + key.should_receive(:to_int).and_return(1) + + s.deconstruct_keys([key]).should == { key => 2 } + end + + it "raises a TypeError if the conversion with #to_int does not return an Integer" do + struct = Struct.new(:x, :y) + s = struct.new(1, 2) + + key = mock("to_int") + key.should_receive(:to_int).and_return("not an Integer") + + -> { + s.deconstruct_keys([key]) + }.should raise_error(TypeError, /can't convert MockObject to Integer/) + end + it "raises TypeError if index is not a String, a Symbol and not convertible to Integer" do struct = Struct.new(:x, :y) s = struct.new(1, 2) diff --git a/spec/ruby/core/struct/element_set_spec.rb b/spec/ruby/core/struct/element_set_spec.rb index 6ba7b081a9..0a0e34a5ee 100644 --- a/spec/ruby/core/struct/element_set_spec.rb +++ b/spec/ruby/core/struct/element_set_spec.rb @@ -26,4 +26,11 @@ describe "Struct#[]=" do -> { car[-4] = true }.should raise_error(IndexError) -> { car[Object.new] = true }.should raise_error(TypeError) end + + it "raises a FrozenError on a frozen struct" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.freeze + + -> { car[:model] = 'Escape' }.should raise_error(FrozenError) + end end diff --git a/spec/ruby/core/struct/new_spec.rb b/spec/ruby/core/struct/new_spec.rb index 6d014cb94d..1d35de7b87 100644 --- a/spec/ruby/core/struct/new_spec.rb +++ b/spec/ruby/core/struct/new_spec.rb @@ -164,6 +164,35 @@ describe "Struct.new" do obj.args.should == 42 obj2.args.should == 42 end + + context "given positional and keyword arguments" do + it "treats keyword arguments as a positional parameter" do + type = Struct.new(:a, :b) + s = type.new("a", b: "b") + s.a.should == "a" + s.b.should == {b: "b"} + + type = Struct.new(:a, :b, :c) + s = type.new("a", b: "b", c: "c") + s.a.should == "a" + s.b.should == {b: "b", c: "c"} + s.c.should == nil + end + + it "ignores empty keyword arguments" do + type = Struct.new(:a, :b) + h = {} + s = type.new("a", **h) + + s.a.should == "a" + s.b.should == nil + end + + it "raises ArgumentError when all struct attribute values are specified" do + type = Struct.new(:a, :b) + -> { type.new("a", "b", c: "c") }.should raise_error(ArgumentError, "struct size differs") + end + end end context "keyword_init: true option" do diff --git a/spec/ruby/core/struct/struct_spec.rb b/spec/ruby/core/struct/struct_spec.rb index 8817dc1a58..1b6a4488ce 100644 --- a/spec/ruby/core/struct/struct_spec.rb +++ b/spec/ruby/core/struct/struct_spec.rb @@ -33,6 +33,13 @@ describe "Struct anonymous class instance methods" do car['model'].should == 'F150' car[1].should == 'F150' end + + it "writer methods raise a FrozenError on a frozen struct" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.freeze + + -> { car.model = 'Escape' }.should raise_error(FrozenError) + end end describe "Struct subclasses" do diff --git a/spec/ruby/core/time/minus_spec.rb b/spec/ruby/core/time/minus_spec.rb index 8449778465..9182d99652 100644 --- a/spec/ruby/core/time/minus_spec.rb +++ b/spec/ruby/core/time/minus_spec.rb @@ -109,7 +109,7 @@ describe "Time#-" do it "does not return a subclass instance" do c = Class.new(Time) - x = c.now + 1 + x = c.now - 1 x.should be_an_instance_of(Time) end diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb index f288da84dd..dc3ccbdc00 100644 --- a/spec/ruby/core/time/new_spec.rb +++ b/spec/ruby/core/time/new_spec.rb @@ -524,6 +524,36 @@ describe "Time.new with a timezone argument" do Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: 3r).subsec.should == 0.123r end + it "returns Time with correct subseconds when given seconds fraction is shorted than 6 digits" do + Time.new("2020-12-25T00:56:17.123 +09:00").nsec.should == 123000000 + Time.new("2020-12-25T00:56:17.123 +09:00").usec.should == 123000 + Time.new("2020-12-25T00:56:17.123 +09:00").subsec.should == 0.123 + end + + it "returns Time with correct subseconds when given seconds fraction is milliseconds" do + Time.new("2020-12-25T00:56:17.123456 +09:00").nsec.should == 123456000 + Time.new("2020-12-25T00:56:17.123456 +09:00").usec.should == 123456 + Time.new("2020-12-25T00:56:17.123456 +09:00").subsec.should == 0.123456 + end + + it "returns Time with correct subseconds when given seconds fraction is longer that 6 digits but shorted than 9 digits" do + Time.new("2020-12-25T00:56:17.12345678 +09:00").nsec.should == 123456780 + Time.new("2020-12-25T00:56:17.12345678 +09:00").usec.should == 123456 + Time.new("2020-12-25T00:56:17.12345678 +09:00").subsec.should == 0.12345678 + end + + it "returns Time with correct subseconds when given seconds fraction is nanoseconds" do + Time.new("2020-12-25T00:56:17.123456789 +09:00").nsec.should == 123456789 + Time.new("2020-12-25T00:56:17.123456789 +09:00").usec.should == 123456 + Time.new("2020-12-25T00:56:17.123456789 +09:00").subsec.should == 0.123456789 + end + + it "returns Time with correct subseconds when given seconds fraction is longer than 9 digits" do + Time.new("2020-12-25T00:56:17.123456789876 +09:00").nsec.should == 123456789 + Time.new("2020-12-25T00:56:17.123456789876 +09:00").usec.should == 123456 + Time.new("2020-12-25T00:56:17.123456789876 +09:00").subsec.should == 0.123456789 + end + ruby_version_is ""..."3.3" do it "raise TypeError is can't convert precision keyword argument into Integer" do -> { @@ -550,16 +580,18 @@ describe "Time.new with a timezone argument" do }.should raise_error(ArgumentError, /missing min part: 00 |can't parse:/) end - it "raises ArgumentError if the time part is missing" do - -> { - Time.new("2020-12-25") - }.should raise_error(ArgumentError, /no time information|can't parse:/) - end + ruby_version_is "3.2.3" do + it "raises ArgumentError if the time part is missing" do + -> { + Time.new("2020-12-25") + }.should raise_error(ArgumentError, /no time information|can't parse:/) + end - it "raises ArgumentError if day is missing" do - -> { - Time.new("2020-12") - }.should raise_error(ArgumentError, /no time information|can't parse:/) + it "raises ArgumentError if day is missing" do + -> { + Time.new("2020-12") + }.should raise_error(ArgumentError, /no time information|can't parse:/) + end end it "raises ArgumentError if subsecond is missing after dot" do @@ -698,22 +730,24 @@ describe "Time.new with a timezone argument" do }.should raise_error(ArgumentError, /can't parse.+ abc/) end - it "raises ArgumentError when there are leading space characters" do - -> { Time.new(" 2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("\t2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("\n2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("\v2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("\f2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("\r2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) - end + ruby_version_is "3.2.3" do + it "raises ArgumentError when there are leading space characters" do + -> { Time.new(" 2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("\t2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("\n2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("\v2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("\f2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("\r2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/) + end - it "raises ArgumentError when there are trailing whitespaces" do - -> { Time.new("2020-12-02 00:00:00 ") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("2020-12-02 00:00:00\t") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("2020-12-02 00:00:00\n") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("2020-12-02 00:00:00\v") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("2020-12-02 00:00:00\f") }.should raise_error(ArgumentError, /can't parse/) - -> { Time.new("2020-12-02 00:00:00\r") }.should raise_error(ArgumentError, /can't parse/) + it "raises ArgumentError when there are trailing whitespaces" do + -> { Time.new("2020-12-02 00:00:00 ") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("2020-12-02 00:00:00\t") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("2020-12-02 00:00:00\n") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("2020-12-02 00:00:00\v") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("2020-12-02 00:00:00\f") }.should raise_error(ArgumentError, /can't parse/) + -> { Time.new("2020-12-02 00:00:00\r") }.should raise_error(ArgumentError, /can't parse/) + end end end end diff --git a/spec/ruby/core/time/shared/time_params.rb b/spec/ruby/core/time/shared/time_params.rb index b6a6c88c8e..9832fd17fe 100644 --- a/spec/ruby/core/time/shared/time_params.rb +++ b/spec/ruby/core/time/shared/time_params.rb @@ -179,6 +179,10 @@ describe :time_params, shared: true do }.should raise_error(ArgumentError, "argument out of range") end + it "raises ArgumentError when given 8 arguments" do + -> { Time.send(@method, *[0]*8) }.should raise_error(ArgumentError) + end + it "raises ArgumentError when given 9 arguments" do -> { Time.send(@method, *[0]*9) }.should raise_error(ArgumentError) end |