diff options
Diffstat (limited to 'spec/ruby/core/range')
33 files changed, 1484 insertions, 931 deletions
diff --git a/spec/ruby/core/range/bsearch_spec.rb b/spec/ruby/core/range/bsearch_spec.rb index d31c5d5d51..151a1798cf 100644 --- a/spec/ruby/core/range/bsearch_spec.rb +++ b/spec/ruby/core/range/bsearch_spec.rb @@ -3,38 +3,46 @@ require_relative '../enumerable/shared/enumeratorized' describe "Range#bsearch" do it "returns an Enumerator when not passed a block" do - (0..1).bsearch.should be_an_instance_of(Enumerator) + (0..1).bsearch.should.instance_of?(Enumerator) end it_behaves_like :enumeratorized_with_unknown_size, :bsearch, (1..3) it "raises a TypeError if the block returns an Object" do - -> { (0..1).bsearch { Object.new } }.should raise_error(TypeError) + -> { (0..1).bsearch { Object.new } }.should.raise(TypeError, "wrong argument type Object (must be numeric, true, false or nil)") end - it "raises a TypeError if the block returns a String" do - -> { (0..1).bsearch { "1" } }.should raise_error(TypeError) + it "raises a TypeError if the block returns a String and boundaries are Integer values" do + -> { (0..1).bsearch { "1" } }.should.raise(TypeError, "wrong argument type String (must be numeric, true, false or nil)") + end + + it "raises a TypeError if the block returns a String and boundaries are Float values" do + -> { (0.0..1.0).bsearch { "1" } }.should.raise(TypeError, "wrong argument type String (must be numeric, true, false or nil)") end it "raises a TypeError if the Range has Object values" do value = mock("range bsearch") r = Range.new value, value - -> { r.bsearch { true } }.should raise_error(TypeError) + -> { r.bsearch { true } }.should.raise(TypeError, "can't do binary search for MockObject") end it "raises a TypeError if the Range has String values" do - -> { ("a".."e").bsearch { true } }.should raise_error(TypeError) + -> { ("a".."e").bsearch { true } }.should.raise(TypeError, "can't do binary search for String") + end + + it "raises TypeError when non-Numeric begin/end and block not passed" do + -> { ("a".."e").bsearch }.should.raise(TypeError, "can't do binary search for String") end context "with Integer values" do context "with a block returning true or false" do it "returns nil if the block returns false for every element" do - (0...3).bsearch { |x| x > 3 }.should be_nil + (0...3).bsearch { |x| x > 3 }.should == nil end it "returns nil if the block returns nil for every element" do - (0..3).bsearch { |x| nil }.should be_nil + (0..3).bsearch { |x| nil }.should == nil end it "returns minimum element if the block returns true for every element" do @@ -54,21 +62,21 @@ describe "Range#bsearch" do context "with a block returning negative, zero, positive numbers" do it "returns nil if the block returns less than zero for every element" do - (0..3).bsearch { |x| x <=> 5 }.should be_nil + (0..3).bsearch { |x| x <=> 5 }.should == nil end it "returns nil if the block returns greater than zero for every element" do - (0..3).bsearch { |x| x <=> -1 }.should be_nil + (0..3).bsearch { |x| x <=> -1 }.should == nil end it "returns nil if the block never returns zero" do - (0..3).bsearch { |x| x < 2 ? 1 : -1 }.should be_nil + (0..3).bsearch { |x| x < 2 ? 1 : -1 }.should == nil end it "accepts (+/-)Float::INFINITY from the block" do - (0..4).bsearch { |x| Float::INFINITY }.should be_nil - (0..4).bsearch { |x| -Float::INFINITY }.should be_nil + (0..4).bsearch { |x| Float::INFINITY }.should == nil + (0..4).bsearch { |x| -Float::INFINITY }.should == nil end it "returns an element at an index for which block returns 0.0" do @@ -78,7 +86,7 @@ describe "Range#bsearch" do it "returns an element at an index for which block returns 0" do result = (0..4).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } - [1, 2].should include(result) + [1, 2].should.include?(result) end end @@ -94,16 +102,20 @@ describe "Range#bsearch" do (4..2).bsearch { 0 }.should == nil (4..2).bsearch { -1 }.should == nil end + + it "returns enumerator when block not passed" do + (0...3).bsearch.kind_of?(Enumerator).should == true + end end context "with Float values" do context "with a block returning true or false" do it "returns nil if the block returns false for every element" do - (0.1...2.3).bsearch { |x| x > 3 }.should be_nil + (0.1...2.3).bsearch { |x| x > 3 }.should == nil end it "returns nil if the block returns nil for every element" do - (-0.0..2.3).bsearch { |x| nil }.should be_nil + (-0.0..2.3).bsearch { |x| nil }.should == nil end it "returns minimum element if the block returns true for every element" do @@ -151,21 +163,20 @@ describe "Range#bsearch" do context "with a block returning negative, zero, positive numbers" do it "returns nil if the block returns less than zero for every element" do - (-2.0..3.2).bsearch { |x| x <=> 5 }.should be_nil + (-2.0..3.2).bsearch { |x| x <=> 5 }.should == nil end it "returns nil if the block returns greater than zero for every element" do - (0.3..3.0).bsearch { |x| x <=> -1 }.should be_nil - + (0.3..3.0).bsearch { |x| x <=> -1 }.should == nil end it "returns nil if the block never returns zero" do - (0.2..2.3).bsearch { |x| x < 2 ? 1 : -1 }.should be_nil + (0.2..2.3).bsearch { |x| x < 2 ? 1 : -1 }.should == nil end it "accepts (+/-)Float::INFINITY from the block" do - (0.1..4.5).bsearch { |x| Float::INFINITY }.should be_nil - (-5.0..4.0).bsearch { |x| -Float::INFINITY }.should be_nil + (0.1..4.5).bsearch { |x| Float::INFINITY }.should == nil + (-5.0..4.0).bsearch { |x| -Float::INFINITY }.should == nil end it "returns an element at an index for which block returns 0.0" do @@ -213,228 +224,243 @@ describe "Range#bsearch" do (0...inf).bsearch { |x| x >= Float::MAX ? 0 : 1 }.should == Float::MAX end end + + it "returns enumerator when block not passed" do + (0.1...2.3).bsearch.kind_of?(Enumerator).should == true + end end - ruby_version_is "2.6" do - context "with endless ranges and Integer values" do - context "with a block returning true or false" do - it "returns minimum element if the block returns true for every element" do - eval("(-2..)").bsearch { |x| true }.should == -2 - end + context "with endless ranges and Integer values" do + context "with a block returning true or false" do + it "returns minimum element if the block returns true for every element" do + eval("(-2..)").bsearch { |x| true }.should == -2 + end - it "returns the smallest element for which block returns true" do - eval("(0..)").bsearch { |x| x >= 2 }.should == 2 - eval("(-1..)").bsearch { |x| x >= 1 }.should == 1 - end + it "returns the smallest element for which block returns true" do + eval("(0..)").bsearch { |x| x >= 2 }.should == 2 + eval("(-1..)").bsearch { |x| x >= 1 }.should == 1 end + end - context "with a block returning negative, zero, positive numbers" do - it "returns nil if the block returns less than zero for every element" do - eval("(0..)").bsearch { |x| -1 }.should be_nil - end + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns less than zero for every element" do + eval("(0..)").bsearch { |x| -1 }.should == nil + end - it "returns nil if the block never returns zero" do - eval("(0..)").bsearch { |x| x > 5 ? -1 : 1 }.should be_nil - end + it "returns nil if the block never returns zero" do + eval("(0..)").bsearch { |x| x > 5 ? -1 : 1 }.should == nil + end - it "accepts -Float::INFINITY from the block" do - eval("(0..)").bsearch { |x| -Float::INFINITY }.should be_nil - end + it "accepts -Float::INFINITY from the block" do + eval("(0..)").bsearch { |x| -Float::INFINITY }.should == nil + end - it "returns an element at an index for which block returns 0.0" do - result = eval("(0..)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } - result.should == 2 - end + it "returns an element at an index for which block returns 0.0" do + result = eval("(0..)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end - it "returns an element at an index for which block returns 0" do - result = eval("(0..)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } - [1, 2, 3].should include(result) - end + it "returns an element at an index for which block returns 0" do + result = eval("(0..)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + [1, 2, 3].should.include?(result) end end - context "with endless ranges and Float values" do - context "with a block returning true or false" do - it "returns nil if the block returns false for every element" do - eval("(0.1..)").bsearch { |x| x < 0.0 }.should be_nil - eval("(0.1...)").bsearch { |x| x < 0.0 }.should be_nil - end - - it "returns nil if the block returns nil for every element" do - eval("(-0.0..)").bsearch { |x| nil }.should be_nil - eval("(-0.0...)").bsearch { |x| nil }.should be_nil - end - - it "returns minimum element if the block returns true for every element" do - eval("(-0.2..)").bsearch { |x| true }.should == -0.2 - eval("(-0.2...)").bsearch { |x| true }.should == -0.2 - end - - it "returns the smallest element for which block returns true" do - eval("(0..)").bsearch { |x| x >= 2 }.should == 2 - eval("(-1.2..)").bsearch { |x| x >= 1 }.should == 1 - end - - it "works with infinity bounds" do - inf = Float::INFINITY - eval("(inf..)").bsearch { |x| true }.should == inf - eval("(inf...)").bsearch { |x| true }.should == nil - eval("(-inf..)").bsearch { |x| true }.should == -inf - eval("(-inf...)").bsearch { |x| true }.should == -inf - end - end - - context "with a block returning negative, zero, positive numbers" do - it "returns nil if the block returns less than zero for every element" do - eval("(-2.0..)").bsearch { |x| -1 }.should be_nil - eval("(-2.0...)").bsearch { |x| -1 }.should be_nil - end - - it "returns nil if the block returns greater than zero for every element" do - eval("(0.3..)").bsearch { |x| 1 }.should be_nil - eval("(0.3...)").bsearch { |x| 1 }.should be_nil - end - - it "returns nil if the block never returns zero" do - eval("(0.2..)").bsearch { |x| x < 2 ? 1 : -1 }.should be_nil - end - - it "accepts (+/-)Float::INFINITY from the block" do - eval("(0.1..)").bsearch { |x| Float::INFINITY }.should be_nil - eval("(-5.0..)").bsearch { |x| -Float::INFINITY }.should be_nil - end - - it "returns an element at an index for which block returns 0.0" do - result = eval("(0.0..)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } - result.should == 2 - end - - it "returns an element at an index for which block returns 0" do - result = eval("(0.1..)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } - result.should >= 1 - result.should <= 3 - end - - it "works with infinity bounds" do - inf = Float::INFINITY - eval("(inf..)").bsearch { |x| 1 }.should == nil - eval("(inf...)").bsearch { |x| 1 }.should == nil - eval("(inf..)").bsearch { |x| x == inf ? 0 : 1 }.should == inf - eval("(inf...)").bsearch { |x| x == inf ? 0 : 1 }.should == nil - eval("(-inf..)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf - eval("(-inf...)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf - eval("(-inf..)").bsearch { |x| 3 - x }.should == 3 - eval("(-inf...)").bsearch { |x| 3 - x }.should == 3 - eval("(0.0...)").bsearch { 0 }.should != inf - end + it "returns enumerator when block not passed" do + eval("(-2..)").bsearch.kind_of?(Enumerator).should == true + end + end + + context "with endless ranges and Float values" do + context "with a block returning true or false" do + it "returns nil if the block returns false for every element" do + eval("(0.1..)").bsearch { |x| x < 0.0 }.should == nil + eval("(0.1...)").bsearch { |x| x < 0.0 }.should == nil + end + + it "returns nil if the block returns nil for every element" do + eval("(-0.0..)").bsearch { |x| nil }.should == nil + eval("(-0.0...)").bsearch { |x| nil }.should == nil + end + + it "returns minimum element if the block returns true for every element" do + eval("(-0.2..)").bsearch { |x| true }.should == -0.2 + eval("(-0.2...)").bsearch { |x| true }.should == -0.2 + end + + it "returns the smallest element for which block returns true" do + eval("(0..)").bsearch { |x| x >= 2 }.should == 2 + eval("(-1.2..)").bsearch { |x| x >= 1 }.should == 1 + end + + it "works with infinity bounds" do + inf = Float::INFINITY + eval("(inf..)").bsearch { |x| true }.should == inf + eval("(inf...)").bsearch { |x| true }.should == nil + eval("(-inf..)").bsearch { |x| true }.should == -inf + eval("(-inf...)").bsearch { |x| true }.should == -inf + end + end + + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns less than zero for every element" do + eval("(-2.0..)").bsearch { |x| -1 }.should == nil + eval("(-2.0...)").bsearch { |x| -1 }.should == nil + end + + it "returns nil if the block returns greater than zero for every element" do + eval("(0.3..)").bsearch { |x| 1 }.should == nil + eval("(0.3...)").bsearch { |x| 1 }.should == nil + end + + it "returns nil if the block never returns zero" do + eval("(0.2..)").bsearch { |x| x < 2 ? 1 : -1 }.should == nil + end + + it "accepts (+/-)Float::INFINITY from the block" do + eval("(0.1..)").bsearch { |x| Float::INFINITY }.should == nil + eval("(-5.0..)").bsearch { |x| -Float::INFINITY }.should == nil + end + + it "returns an element at an index for which block returns 0.0" do + result = eval("(0.0..)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end + + it "returns an element at an index for which block returns 0" do + result = eval("(0.1..)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + result.should >= 1 + result.should <= 3 + end + + it "works with infinity bounds" do + inf = Float::INFINITY + eval("(inf..)").bsearch { |x| 1 }.should == nil + eval("(inf...)").bsearch { |x| 1 }.should == nil + eval("(inf..)").bsearch { |x| x == inf ? 0 : 1 }.should == inf + eval("(inf...)").bsearch { |x| x == inf ? 0 : 1 }.should == nil + eval("(-inf..)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf + eval("(-inf...)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf + eval("(-inf..)").bsearch { |x| 3 - x }.should == 3 + eval("(-inf...)").bsearch { |x| 3 - x }.should == 3 + eval("(0.0...)").bsearch { 0 }.should != inf end end + + it "returns enumerator when block not passed" do + eval("(0.1..)").bsearch.kind_of?(Enumerator).should == true + end end + context "with beginless ranges and Integer values" do + context "with a block returning true or false" do + it "returns the smallest element for which block returns true" do + (..10).bsearch { |x| x >= 2 }.should == 2 + (...-1).bsearch { |x| x >= -10 }.should == -10 + end + end + + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns greater than zero for every element" do + (..0).bsearch { |x| 1 }.should == nil + end + + it "returns nil if the block never returns zero" do + (..0).bsearch { |x| x > 5 ? -1 : 1 }.should == nil + end - ruby_version_is "2.7" do - context "with beginless ranges and Integer values" do - context "with a block returning true or false" do - it "returns the smallest element for which block returns true" do - eval("(..10)").bsearch { |x| x >= 2 }.should == 2 - eval("(...-1)").bsearch { |x| x >= -10 }.should == -10 - end + it "accepts Float::INFINITY from the block" do + (..0).bsearch { |x| Float::INFINITY }.should == nil end - context "with a block returning negative, zero, positive numbers" do - it "returns nil if the block returns greater than zero for every element" do - eval("(..0)").bsearch { |x| 1 }.should be_nil - end + it "returns an element at an index for which block returns 0.0" do + result = (..10).bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end - it "returns nil if the block never returns zero" do - eval("(..0)").bsearch { |x| x > 5 ? -1 : 1 }.should be_nil - end + it "returns an element at an index for which block returns 0" do + result = (...10).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + [1, 2, 3].should.include?(result) + end + end - it "accepts Float::INFINITY from the block" do - eval("(..0)").bsearch { |x| Float::INFINITY }.should be_nil - end + it "returns enumerator when block not passed" do + (..10).bsearch.kind_of?(Enumerator).should == true + end + end - it "returns an element at an index for which block returns 0.0" do - result = eval("(..10)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } - result.should == 2 - end + context "with beginless ranges and Float values" do + context "with a block returning true or false" do + it "returns nil if the block returns true for every element" do + (..-0.1).bsearch { |x| x > 0.0 }.should == nil + (...-0.1).bsearch { |x| x > 0.0 }.should == nil + end - it "returns an element at an index for which block returns 0" do - result = eval("(...10)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } - [1, 2, 3].should include(result) - end + it "returns nil if the block returns nil for every element" do + (..-0.1).bsearch { |x| nil }.should == nil + (...-0.1).bsearch { |x| nil }.should == nil + end + + it "returns the smallest element for which block returns true" do + (..10).bsearch { |x| x >= 2 }.should == 2 + (..10).bsearch { |x| x >= 1 }.should == 1 + end + + it "works with infinity bounds" do + inf = Float::INFINITY + (..inf).bsearch { |x| true }.should == -inf + (...inf).bsearch { |x| true }.should == -inf + (..-inf).bsearch { |x| true }.should == -inf + (...-inf).bsearch { |x| true }.should == nil end end - context "with beginless ranges and Float values" do - context "with a block returning true or false" do - it "returns nil if the block returns true for every element" do - eval("(..-0.1)").bsearch { |x| x > 0.0 }.should be_nil - eval("(...-0.1)").bsearch { |x| x > 0.0 }.should be_nil - end - - it "returns nil if the block returns nil for every element" do - eval("(..-0.1)").bsearch { |x| nil }.should be_nil - eval("(...-0.1)").bsearch { |x| nil }.should be_nil - end - - it "returns the smallest element for which block returns true" do - eval("(..10)").bsearch { |x| x >= 2 }.should == 2 - eval("(..10)").bsearch { |x| x >= 1 }.should == 1 - end - - it "works with infinity bounds" do - inf = Float::INFINITY - eval("(..inf)").bsearch { |x| true }.should == -inf - eval("(...inf)").bsearch { |x| true }.should == -inf - eval("(..-inf)").bsearch { |x| true }.should == -inf - eval("(...-inf)").bsearch { |x| true }.should == nil - end - end - - context "with a block returning negative, zero, positive numbers" do - it "returns nil if the block returns less than zero for every element" do - eval("(..5.0)").bsearch { |x| -1 }.should be_nil - eval("(...5.0)").bsearch { |x| -1 }.should be_nil - end - - it "returns nil if the block returns greater than zero for every element" do - eval("(..1.1)").bsearch { |x| 1 }.should be_nil - eval("(...1.1)").bsearch { |x| 1 }.should be_nil - end - - it "returns nil if the block never returns zero" do - eval("(..6.3)").bsearch { |x| x < 2 ? 1 : -1 }.should be_nil - end - - it "accepts (+/-)Float::INFINITY from the block" do - eval("(..5.0)").bsearch { |x| Float::INFINITY }.should be_nil - eval("(..7.0)").bsearch { |x| -Float::INFINITY }.should be_nil - end - - it "returns an element at an index for which block returns 0.0" do - result = eval("(..8.0)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } - result.should == 2 - end - - it "returns an element at an index for which block returns 0" do - result = eval("(..8.0)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } - result.should >= 1 - result.should <= 3 - end - - it "works with infinity bounds" do - inf = Float::INFINITY - eval("(..-inf)").bsearch { |x| 1 }.should == nil - eval("(...-inf)").bsearch { |x| 1 }.should == nil - eval("(..inf)").bsearch { |x| x == inf ? 0 : 1 }.should == inf - eval("(...inf)").bsearch { |x| x == inf ? 0 : 1 }.should == nil - eval("(..-inf)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf - eval("(...-inf)").bsearch { |x| x == -inf ? 0 : -1 }.should == nil - eval("(..inf)").bsearch { |x| 3 - x }.should == 3 - eval("(...inf)").bsearch { |x| 3 - x }.should == 3 - end + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns less than zero for every element" do + (..5.0).bsearch { |x| -1 }.should == nil + (...5.0).bsearch { |x| -1 }.should == nil + end + + it "returns nil if the block returns greater than zero for every element" do + (..1.1).bsearch { |x| 1 }.should == nil + (...1.1).bsearch { |x| 1 }.should == nil + end + + it "returns nil if the block never returns zero" do + (..6.3).bsearch { |x| x < 2 ? 1 : -1 }.should == nil + end + + it "accepts (+/-)Float::INFINITY from the block" do + (..5.0).bsearch { |x| Float::INFINITY }.should == nil + (..7.0).bsearch { |x| -Float::INFINITY }.should == nil end + + it "returns an element at an index for which block returns 0.0" do + result = (..8.0).bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end + + it "returns an element at an index for which block returns 0" do + result = (..8.0).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + result.should >= 1 + result.should <= 3 + end + + it "works with infinity bounds" do + inf = Float::INFINITY + (..-inf).bsearch { |x| 1 }.should == nil + (...-inf).bsearch { |x| 1 }.should == nil + (..inf).bsearch { |x| x == inf ? 0 : 1 }.should == inf + (...inf).bsearch { |x| x == inf ? 0 : 1 }.should == nil + (..-inf).bsearch { |x| x == -inf ? 0 : -1 }.should == -inf + (...-inf).bsearch { |x| x == -inf ? 0 : -1 }.should == nil + (..inf).bsearch { |x| 3 - x }.should == 3 + (...inf).bsearch { |x| 3 - x }.should == 3 + end + end + + it "returns enumerator when block not passed" do + (..-0.1).bsearch.kind_of?(Enumerator).should == true end end end diff --git a/spec/ruby/core/range/case_compare_spec.rb b/spec/ruby/core/range/case_compare_spec.rb index b1afa90a41..7a76487d68 100644 --- a/spec/ruby/core/range/case_compare_spec.rb +++ b/spec/ruby/core/range/case_compare_spec.rb @@ -3,31 +3,15 @@ require_relative 'shared/cover_and_include' require_relative 'shared/cover' describe "Range#===" do - ruby_version_is ""..."2.6" do - it "returns the result of calling #include? on self" do - range = 0...10 - range.should_receive(:include?).with(2).and_return(:true) - (range === 2).should == :true - end - - it "requires #succ method to be implemented" do - range = RangeSpecs::WithoutSucc.new(0)..RangeSpecs::WithoutSucc.new(10) - - -> do - range === RangeSpecs::WithoutSucc.new(2) - end.should raise_error(TypeError, /can't iterate from/) - end + it "returns the result of calling #cover? on self" do + range = RangeSpecs::WithoutSucc.new(0)..RangeSpecs::WithoutSucc.new(10) + (range === RangeSpecs::WithoutSucc.new(2)).should == true end - ruby_version_is "2.6" do - it "returns the result of calling #cover? on self" do - range = RangeSpecs::WithoutSucc.new(0)..RangeSpecs::WithoutSucc.new(10) - (range === RangeSpecs::WithoutSucc.new(2)).should == true - end - end + it_behaves_like :range_cover_and_include, :=== + it_behaves_like :range_cover, :=== - ruby_version_is "2.7" do - it_behaves_like :range_cover_and_include, :=== - it_behaves_like :range_cover, :=== + it "returns true on any value if begin and end are both nil" do + (nil..nil).should === 1 end end diff --git a/spec/ruby/core/range/clone_spec.rb b/spec/ruby/core/range/clone_spec.rb new file mode 100644 index 0000000000..cf6ce74da0 --- /dev/null +++ b/spec/ruby/core/range/clone_spec.rb @@ -0,0 +1,26 @@ +require_relative '../../spec_helper' + +describe "Range#clone" do + it "duplicates the range" do + original = (1..3) + copy = original.clone + copy.begin.should == 1 + copy.end.should == 3 + copy.should_not.exclude_end? + copy.should_not.equal? original + + original = ("a"..."z") + copy = original.clone + copy.begin.should == "a" + copy.end.should == "z" + copy.should.exclude_end? + copy.should_not.equal? original + end + + it "maintains the frozen state" do + (1..2).clone.frozen?.should == (1..2).frozen? + (1..).clone.frozen?.should == (1..).frozen? + Range.new(1, 2).clone.frozen?.should == Range.new(1, 2).frozen? + Class.new(Range).new(1, 2).clone.frozen?.should == Class.new(Range).new(1, 2).frozen? + end +end diff --git a/spec/ruby/core/range/count_spec.rb b/spec/ruby/core/range/count_spec.rb index f6f60fa054..24d4a9caf3 100644 --- a/spec/ruby/core/range/count_spec.rb +++ b/spec/ruby/core/range/count_spec.rb @@ -1,14 +1,12 @@ require_relative '../../spec_helper' describe "Range#count" do - ruby_version_is "2.7" do - it "returns Infinity for beginless ranges without arguments or blocks" do - inf = Float::INFINITY - eval("('a'...)").count.should == inf - eval("(7..)").count.should == inf - eval("(...'a')").count.should == inf - eval("(...nil)").count.should == inf - eval("(..10.0)").count.should == inf - end + it "returns Infinity for beginless ranges without arguments or blocks" do + inf = Float::INFINITY + eval("('a'...)").count.should == inf + eval("(7..)").count.should == inf + (...'a').count.should == inf + (...nil).count.should == inf + (..10.0).count.should == inf end end diff --git a/spec/ruby/core/range/cover_spec.rb b/spec/ruby/core/range/cover_spec.rb index fa881607e9..eb8d5453bf 100644 --- a/spec/ruby/core/range/cover_spec.rb +++ b/spec/ruby/core/range/cover_spec.rb @@ -1,4 +1,4 @@ -# -*- encoding: binary -*- +# encoding: binary require_relative '../../spec_helper' require_relative 'shared/cover_and_include' require_relative 'shared/cover' @@ -7,4 +7,8 @@ describe "Range#cover?" do it_behaves_like :range_cover_and_include, :cover? it_behaves_like :range_cover, :cover? it_behaves_like :range_cover_subrange, :cover? + + it "covers U+9995 in the range U+0999..U+9999" do + ("\u{999}".."\u{9999}").cover?("\u{9995}").should == true + end end diff --git a/spec/ruby/core/range/dup_spec.rb b/spec/ruby/core/range/dup_spec.rb index 6c9d0c954a..fab3c3f1b2 100644 --- a/spec/ruby/core/range/dup_spec.rb +++ b/spec/ruby/core/range/dup_spec.rb @@ -2,14 +2,22 @@ require_relative '../../spec_helper' describe "Range#dup" do it "duplicates the range" do - copy = (1..3).dup + original = (1..3) + copy = original.dup copy.begin.should == 1 copy.end.should == 3 copy.should_not.exclude_end? + copy.should_not.equal?(original) copy = ("a"..."z").dup copy.begin.should == "a" copy.end.should == "z" copy.should.exclude_end? end + + it "creates an unfrozen range" do + (1..2).dup.should_not.frozen? + (1..).dup.should_not.frozen? + Range.new(1, 2).dup.should_not.frozen? + end end diff --git a/spec/ruby/core/range/each_spec.rb b/spec/ruby/core/range/each_spec.rb index 1dce9c1f1e..b4389f864d 100644 --- a/spec/ruby/core/range/each_spec.rb +++ b/spec/ruby/core/range/each_spec.rb @@ -38,57 +38,58 @@ describe "Range#each" do a.should == ["Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω"] end - ruby_version_is "2.6" do - it "works with endless ranges" do - a = [] - eval("(-2..)").each { |x| break if x > 2; a << x } - a.should == [-2, -1, 0, 1, 2] - - a = [] - eval("(-2...)").each { |x| break if x > 2; a << x } - a.should == [-2, -1, 0, 1, 2] - end - - it "works with String endless ranges" do - a = [] - eval("('A'..)").each { |x| break if x > "D"; a << x } - a.should == ["A", "B", "C", "D"] - - a = [] - eval("('A'...)").each { |x| break if x > "D"; a << x } - a.should == ["A", "B", "C", "D"] - end + it "works with endless ranges" do + a = [] + (-2..).each { |x| break if x > 2; a << x } + a.should == [-2, -1, 0, 1, 2] + + a = [] + (-2...).each { |x| break if x > 2; a << x } + a.should == [-2, -1, 0, 1, 2] end - ruby_version_is "2.7" do - it "raises a TypeError beginless ranges" do - -> { eval("(..2)").each { |x| x } }.should raise_error(TypeError) - end + it "works with String endless ranges" do + a = [] + ('A'..).each { |x| break if x > "D"; a << x } + a.should == ["A", "B", "C", "D"] + + a = [] + ('A'...).each { |x| break if x > "D"; a << x } + a.should == ["A", "B", "C", "D"] + end + + it "raises a TypeError beginless ranges" do + -> { (..2).each { |x| x } }.should.raise(TypeError) end it "raises a TypeError if the first element does not respond to #succ" do - -> { (0.5..2.4).each { |i| i } }.should raise_error(TypeError) + -> { (0.5..2.4).each { |i| i } }.should.raise(TypeError) b = mock('x') (a = mock('1')).should_receive(:<=>).with(b).and_return(1) - -> { (a..b).each { |i| i } }.should raise_error(TypeError) + -> { (a..b).each { |i| i } }.should.raise(TypeError) end it "returns self" do range = 1..10 - range.each{}.should equal(range) + range.each{}.should.equal?(range) end it "returns an enumerator when no block given" do enum = (1..3).each - enum.should be_an_instance_of(Enumerator) + enum.should.instance_of?(Enumerator) enum.to_a.should == [1, 2, 3] end - it "raises a TypeError if the first element is a Time object" do - t = Time.now - -> { (t..t+1).each { |i| i } }.should raise_error(TypeError) + it "supports Time objects that respond to #succ" do + t = Time.utc(1970) + def t.succ; self + 1 end + t_succ = t.succ + def t_succ.succ; self + 1; end + + (t..t_succ).to_a.should == [Time.utc(1970), Time.utc(1970, nil, nil, nil, nil, 1)] + (t...t_succ).to_a.should == [Time.utc(1970)] end it "passes each Symbol element by using #succ" do diff --git a/spec/ruby/core/range/eql_spec.rb b/spec/ruby/core/range/eql_spec.rb index fa6c71840e..cdc19c9331 100644 --- a/spec/ruby/core/range/eql_spec.rb +++ b/spec/ruby/core/range/eql_spec.rb @@ -5,6 +5,6 @@ describe "Range#eql?" do it_behaves_like :range_eql, :eql? it "returns false if the endpoints are not eql?" do - (0..1).should_not eql(0..1.0) + (0..1).should_not.eql?(0..1.0) end end diff --git a/spec/ruby/core/range/equal_value_spec.rb b/spec/ruby/core/range/equal_value_spec.rb index f88d3029bb..83dcf5cec8 100644 --- a/spec/ruby/core/range/equal_value_spec.rb +++ b/spec/ruby/core/range/equal_value_spec.rb @@ -8,15 +8,11 @@ describe "Range#==" do (0..1).should == (0..1.0) end - ruby_version_is "2.6" do - it "returns true if the endpoints are == for endless ranges" do - eval("(1.0..)").should == eval("(1.0..)") - end + it "returns true if the endpoints are == for endless ranges" do + eval("(1.0..)").should == eval("(1.0..)") end - ruby_version_is "2.7" do - it "returns true if the endpoints are == for beginless ranges" do - eval("(...10)").should == eval("(...10)") - end + it "returns true if the endpoints are == for beginless ranges" do + (...10).should == (...10) end end diff --git a/spec/ruby/core/range/first_spec.rb b/spec/ruby/core/range/first_spec.rb index c5c90800ac..54bd73a4e8 100644 --- a/spec/ruby/core/range/first_spec.rb +++ b/spec/ruby/core/range/first_spec.rb @@ -21,7 +21,7 @@ describe "Range#first" do end it "raises an ArgumentError when count is negative" do - -> { (0..2).first(-1) }.should raise_error(ArgumentError) + -> { (0..2).first(-1) }.should.raise(ArgumentError) end it "calls #to_int to convert the argument" do @@ -32,7 +32,7 @@ describe "Range#first" do it "raises a TypeError if #to_int does not return an Integer" do obj = mock("to_int") obj.should_receive(:to_int).and_return("1") - -> { (2..3).first(obj) }.should raise_error(TypeError) + -> { (2..3).first(obj) }.should.raise(TypeError) end it "truncates the value when passed a Float" do @@ -40,16 +40,14 @@ describe "Range#first" do end it "raises a TypeError when passed nil" do - -> { (2..3).first(nil) }.should raise_error(TypeError) + -> { (2..3).first(nil) }.should.raise(TypeError) end it "raises a TypeError when passed a String" do - -> { (2..3).first("1") }.should raise_error(TypeError) + -> { (2..3).first("1") }.should.raise(TypeError) end - ruby_version_is "2.7" do - it "raises a RangeError when called on an beginless range" do - -> { eval("(..1)").first }.should raise_error(RangeError) - end + it "raises a RangeError when called on an beginless range" do + -> { (..1).first }.should.raise(RangeError) end end diff --git a/spec/ruby/core/range/frozen_spec.rb b/spec/ruby/core/range/frozen_spec.rb new file mode 100644 index 0000000000..8dab5e5339 --- /dev/null +++ b/spec/ruby/core/range/frozen_spec.rb @@ -0,0 +1,25 @@ +require_relative '../../spec_helper' + +# There is no Range#frozen? method but this feels like the best place for these specs +describe "Range#frozen?" do + it "is true for literal ranges" do + (1..2).should.frozen? + (1..).should.frozen? + (..1).should.frozen? + end + + it "is true for Range.new" do + Range.new(1, 2).should.frozen? + Range.new(1, nil).should.frozen? + Range.new(nil, 1).should.frozen? + end + + it "is false for instances of a subclass of Range" do + sub_range = Class.new(Range).new(1, 2) + sub_range.should_not.frozen? + end + + it "is false for Range.allocate" do + Range.allocate.should_not.frozen? + end +end diff --git a/spec/ruby/core/range/hash_spec.rb b/spec/ruby/core/range/hash_spec.rb index 4f2681e86e..087f5d6de8 100644 --- a/spec/ruby/core/range/hash_spec.rb +++ b/spec/ruby/core/range/hash_spec.rb @@ -15,10 +15,10 @@ describe "Range#hash" do end it "generates an Integer for the hash value" do - (0..0).hash.should be_an_instance_of(Integer) - (0..1).hash.should be_an_instance_of(Integer) - (0...10).hash.should be_an_instance_of(Integer) - (0..10).hash.should be_an_instance_of(Integer) + (0..0).hash.should.instance_of?(Integer) + (0..1).hash.should.instance_of?(Integer) + (0...10).hash.should.instance_of?(Integer) + (0..10).hash.should.instance_of?(Integer) end end diff --git a/spec/ruby/core/range/include_spec.rb b/spec/ruby/core/range/include_spec.rb index b2c7a54545..66a049a90d 100644 --- a/spec/ruby/core/range/include_spec.rb +++ b/spec/ruby/core/range/include_spec.rb @@ -1,4 +1,4 @@ -# -*- encoding: binary -*- +# encoding: binary require_relative '../../spec_helper' require_relative 'shared/cover_and_include' require_relative 'shared/include' @@ -7,4 +7,8 @@ require_relative 'shared/cover' describe "Range#include?" do it_behaves_like :range_cover_and_include, :include? it_behaves_like :range_include, :include? + + it "does not include U+9995 in the range U+0999..U+9999" do + ("\u{999}".."\u{9999}").include?("\u{9995}").should == false + end end diff --git a/spec/ruby/core/range/initialize_spec.rb b/spec/ruby/core/range/initialize_spec.rb index 8a6ca65daa..b1a0565ab2 100644 --- a/spec/ruby/core/range/initialize_spec.rb +++ b/spec/ruby/core/range/initialize_spec.rb @@ -6,45 +6,36 @@ describe "Range#initialize" do end it "is private" do - Range.should have_private_instance_method("initialize") + Range.private_instance_methods(false).should.include?(:initialize) end it "initializes correctly the Range object when given 2 arguments" do - -> { @range.send(:initialize, 0, 1) }.should_not raise_error + -> { @range.send(:initialize, 0, 1) }.should_not.raise end it "initializes correctly the Range object when given 3 arguments" do - -> { @range.send(:initialize, 0, 1, true) }.should_not raise_error + -> { @range.send(:initialize, 0, 1, true) }.should_not.raise end it "raises an ArgumentError if passed without or with only one argument" do - -> { @range.send(:initialize) }.should raise_error(ArgumentError) - -> { @range.send(:initialize, 1) }.should raise_error(ArgumentError) + -> { @range.send(:initialize) }.should.raise(ArgumentError) + -> { @range.send(:initialize, 1) }.should.raise(ArgumentError) end it "raises an ArgumentError if passed with four or more arguments" do - -> { @range.send(:initialize, 1, 3, 5, 7) }.should raise_error(ArgumentError) - -> { @range.send(:initialize, 1, 3, 5, 7, 9) }.should raise_error(ArgumentError) + -> { @range.send(:initialize, 1, 3, 5, 7) }.should.raise(ArgumentError) + -> { @range.send(:initialize, 1, 3, 5, 7, 9) }.should.raise(ArgumentError) end - ruby_version_is ""..."3.0" do - it "raises a NameError if called on an already initialized Range" do - -> { (0..1).send(:initialize, 1, 3) }.should raise_error(NameError) - -> { (0..1).send(:initialize, 1, 3, true) }.should raise_error(NameError) - end - end - - ruby_version_is "3.0" do - it "raises a FrozenError if called on an already initialized Range" do - -> { (0..1).send(:initialize, 1, 3) }.should raise_error(FrozenError) - -> { (0..1).send(:initialize, 1, 3, true) }.should raise_error(FrozenError) - end + it "raises a FrozenError if called on an already initialized Range" do + -> { (0..1).send(:initialize, 1, 3) }.should.raise(FrozenError) + -> { (0..1).send(:initialize, 1, 3, true) }.should.raise(FrozenError) end it "raises an ArgumentError if arguments don't respond to <=>" do o1 = Object.new o2 = Object.new - -> { @range.send(:initialize, o1, o2) }.should raise_error(ArgumentError) + -> { @range.send(:initialize, o1, o2) }.should.raise(ArgumentError) end end diff --git a/spec/ruby/core/range/inspect_spec.rb b/spec/ruby/core/range/inspect_spec.rb index 6a206a0355..072de123b7 100644 --- a/spec/ruby/core/range/inspect_spec.rb +++ b/spec/ruby/core/range/inspect_spec.rb @@ -12,36 +12,18 @@ describe "Range#inspect" do (0.5..2.4).inspect.should == "0.5..2.4" end - ruby_version_is "2.6" do - it "works for endless ranges" do - eval("(1..)").inspect.should == "1.." - eval("(0.1...)").inspect.should == "0.1..." - end + it "works for endless ranges" do + eval("(1..)").inspect.should == "1.." + eval("(0.1...)").inspect.should == "0.1..." end - ruby_version_is '2.7' do - it "works for beginless ranges" do - eval("(..1)").inspect.should == "..1" - eval("(...0.1)").inspect.should == "...0.1" - end - - it "works for nil ... nil ranges" do - eval("(..nil)").inspect.should == "nil..nil" - eval("(nil...)").inspect.should == "nil...nil" - end + it "works for beginless ranges" do + (..1).inspect.should == "..1" + (...0.1).inspect.should == "...0.1" end - ruby_version_is ''...'2.7' do - it "returns a tainted string if either end is tainted" do - (("a".taint)..."c").inspect.tainted?.should be_true - ("a"...("c".taint)).inspect.tainted?.should be_true - ("a"..."c").taint.inspect.tainted?.should be_true - end - - it "returns a untrusted string if either end is untrusted" do - (("a".untrust)..."c").inspect.untrusted?.should be_true - ("a"...("c".untrust)).inspect.untrusted?.should be_true - ("a"..."c").untrust.inspect.untrusted?.should be_true - end + it "works for nil ... nil ranges" do + (..nil).inspect.should == "nil..nil" + eval("(nil...)").inspect.should == "nil...nil" end end diff --git a/spec/ruby/core/range/last_spec.rb b/spec/ruby/core/range/last_spec.rb index 54884ba4d6..a7db7f85a7 100644 --- a/spec/ruby/core/range/last_spec.rb +++ b/spec/ruby/core/range/last_spec.rb @@ -8,6 +8,10 @@ describe "Range#last" do (1..5).last(3).should == [3, 4, 5] end + it "returns the specified number if elements for single element inclusive range" do + (1..1).last(1).should == [1] + end + it "returns an empty array for an empty Range" do (0...0).last(2).should == [] end @@ -21,7 +25,7 @@ describe "Range#last" do end it "raises an ArgumentError when count is negative" do - -> { (0..2).last(-1) }.should raise_error(ArgumentError) + -> { (0..2).last(-1) }.should.raise(ArgumentError) end it "calls #to_int to convert the argument" do @@ -32,7 +36,7 @@ describe "Range#last" do it "raises a TypeError if #to_int does not return an Integer" do obj = mock("to_int") obj.should_receive(:to_int).and_return("1") - -> { (2..3).last(obj) }.should raise_error(TypeError) + -> { (2..3).last(obj) }.should.raise(TypeError) end it "truncates the value when passed a Float" do @@ -40,16 +44,14 @@ describe "Range#last" do end it "raises a TypeError when passed nil" do - -> { (2..3).last(nil) }.should raise_error(TypeError) + -> { (2..3).last(nil) }.should.raise(TypeError) end it "raises a TypeError when passed a String" do - -> { (2..3).last("1") }.should raise_error(TypeError) + -> { (2..3).last("1") }.should.raise(TypeError) end - ruby_version_is "2.6" do - it "raises a RangeError when called on an endless range" do - -> { eval("(1..)").last }.should raise_error(RangeError) - end + it "raises a RangeError when called on an endless range" do + -> { eval("(1..)").last }.should.raise(RangeError) end end diff --git a/spec/ruby/core/range/max_spec.rb b/spec/ruby/core/range/max_spec.rb index f2672170d9..57714967ce 100644 --- a/spec/ruby/core/range/max_spec.rb +++ b/spec/ruby/core/range/max_spec.rb @@ -14,56 +14,66 @@ describe "Range#max" do end it "raises TypeError when called on an exclusive range and a non Integer value" do - -> { (303.20...908.1111).max }.should raise_error(TypeError) + -> { (303.20...908.1111).max }.should.raise(TypeError) end it "returns nil when the endpoint is less than the start point" do - (100..10).max.should be_nil - ('z'..'l').max.should be_nil + (100..10).max.should == nil + ('z'..'l').max.should == nil end it "returns nil when the endpoint equals the start point and the range is exclusive" do - (5...5).max.should be_nil + (5...5).max.should == nil end it "returns the endpoint when the endpoint equals the start point and the range is inclusive" do - (5..5).max.should equal(5) + (5..5).max.should.equal?(5) end it "returns nil when the endpoint is less than the start point in a Float range" do - (3003.20..908.1111).max.should be_nil + (3003.20..908.1111).max.should == nil end it "returns end point when the range is Time..Time(included end point)" do time_start = Time.now time_end = Time.now + 1.0 - (time_start..time_end).max.should equal(time_end) + (time_start..time_end).max.should.equal?(time_end) end it "raises TypeError when called on a Time...Time(excluded end point)" do time_start = Time.now time_end = Time.now + 1.0 - -> { (time_start...time_end).max }.should raise_error(TypeError) + -> { (time_start...time_end).max }.should.raise(TypeError) end - ruby_version_is "2.6" do - it "raises RangeError when called on an endless range" do - -> { eval("(1..)").max }.should raise_error(RangeError) - end + it "raises RangeError when called on an endless range" do + -> { eval("(1..)").max }.should.raise(RangeError) end - ruby_version_is "3.0" do - it "returns the end point for beginless ranges" do - eval("(..1)").max.should == 1 - eval("(..1.0)").max.should == 1.0 - end + it "returns the end point for beginless ranges" do + (..1).max.should == 1 + (..1.0).max.should == 1.0 + end - it "raises for an exclusive beginless range" do + ruby_version_is ""..."4.0" do + it "raises for an exclusive beginless Integer range" do -> { - eval("(...1)").max - }.should raise_error(TypeError, 'cannot exclude end value with non Integer begin value') + (...1).max + }.should.raise(TypeError, 'cannot exclude end value with non Integer begin value') end end + + ruby_version_is "4.0" do + it "returns the end point for exclusive beginless Integer ranges" do + (...1).max.should == 0 + end + end + + it "raises for an exclusive beginless non Integer range" do + -> { + (...1.0).max + }.should.raise(TypeError, 'cannot exclude non Integer end value') + end end describe "Range#max given a block" do @@ -72,7 +82,7 @@ describe "Range#max given a block" do (1..10).max {|a,b| acc << [a,b]; a } acc.flatten! (1..10).each do |value| - acc.include?(value).should be_true + acc.include?(value).should == true end end @@ -94,14 +104,12 @@ describe "Range#max given a block" do end it "returns nil when the endpoint is less than the start point" do - (100..10).max {|x,y| x <=> y}.should be_nil - ('z'..'l').max {|x,y| x <=> y}.should be_nil - (5...5).max {|x,y| x <=> y}.should be_nil + (100..10).max {|x,y| x <=> y}.should == nil + ('z'..'l').max {|x,y| x <=> y}.should == nil + (5...5).max {|x,y| x <=> y}.should == nil end - ruby_version_is "2.7" do - it "raises RangeError when called with custom comparison method on an beginless range" do - -> { eval("(..1)").max {|a, b| a} }.should raise_error(RangeError) - end + it "raises RangeError when called with custom comparison method on an beginless range" do + -> { (..1).max {|a, b| a} }.should.raise(RangeError) end end diff --git a/spec/ruby/core/range/member_spec.rb b/spec/ruby/core/range/member_spec.rb index ab61f92951..78299ae9e5 100644 --- a/spec/ruby/core/range/member_spec.rb +++ b/spec/ruby/core/range/member_spec.rb @@ -1,4 +1,4 @@ -# -*- encoding: binary -*- +# encoding: binary require_relative '../../spec_helper' require_relative 'shared/cover_and_include' require_relative 'shared/include' diff --git a/spec/ruby/core/range/min_spec.rb b/spec/ruby/core/range/min_spec.rb index ffd40f3a07..9c83d3deca 100644 --- a/spec/ruby/core/range/min_spec.rb +++ b/spec/ruby/core/range/min_spec.rb @@ -11,45 +11,41 @@ describe "Range#min" do end it "returns nil when the start point is greater than the endpoint" do - (100..10).min.should be_nil - ('z'..'l').min.should be_nil + (100..10).min.should == nil + ('z'..'l').min.should == nil end it "returns nil when the endpoint equals the start point and the range is exclusive" do - (7...7).min.should be_nil + (7...7).min.should == nil end it "returns the start point when the endpoint equals the start point and the range is inclusive" do - (7..7).min.should equal(7) + (7..7).min.should.equal?(7) end it "returns nil when the start point is greater than the endpoint in a Float range" do - (3003.20..908.1111).min.should be_nil + (3003.20..908.1111).min.should == nil end it "returns start point when the range is Time..Time(included end point)" do time_start = Time.now time_end = Time.now + 1.0 - (time_start..time_end).min.should equal(time_start) + (time_start..time_end).min.should.equal?(time_start) end it "returns start point when the range is Time...Time(excluded end point)" do time_start = Time.now time_end = Time.now + 1.0 - (time_start...time_end).min.should equal(time_start) + (time_start...time_end).min.should.equal?(time_start) end - ruby_version_is "2.6" do - it "returns the start point for endless ranges" do - eval("(1..)").min.should == 1 - eval("(1.0...)").min.should == 1.0 - end + it "returns the start point for endless ranges" do + eval("(1..)").min.should == 1 + eval("(1.0...)").min.should == 1.0 end - ruby_version_is "2.7" do - it "raises RangeError when called on an beginless range" do - -> { eval("(..1)").min }.should raise_error(RangeError) - end + it "raises RangeError when called on an beginless range" do + -> { (..1).min }.should.raise(RangeError) end end @@ -59,7 +55,7 @@ describe "Range#min given a block" do (1..10).min {|a,b| acc << [a,b]; a } acc.flatten! (1..10).each do |value| - acc.include?(value).should be_true + acc.include?(value).should == true end end @@ -81,14 +77,12 @@ describe "Range#min given a block" do end it "returns nil when the start point is greater than the endpoint" do - (100..10).min {|x,y| x <=> y}.should be_nil - ('z'..'l').min {|x,y| x <=> y}.should be_nil - (7...7).min {|x,y| x <=> y}.should be_nil + (100..10).min {|x,y| x <=> y}.should == nil + ('z'..'l').min {|x,y| x <=> y}.should == nil + (7...7).min {|x,y| x <=> y}.should == nil end - ruby_version_is "2.6" do - it "raises RangeError when called with custom comparison method on an endless range" do - -> { eval("(1..)").min {|a, b| a} }.should raise_error(RangeError) - end + it "raises RangeError when called with custom comparison method on an endless range" do + -> { eval("(1..)").min {|a, b| a} }.should.raise(RangeError) end end diff --git a/spec/ruby/core/range/minmax_spec.rb b/spec/ruby/core/range/minmax_spec.rb index fa0637ac56..16c7626ea3 100644 --- a/spec/ruby/core/range/minmax_spec.rb +++ b/spec/ruby/core/range/minmax_spec.rb @@ -1,6 +1,5 @@ require_relative '../../spec_helper' -# These specs use Range.new instead of the literal notation so they parse fine on Ruby < 2.6 describe 'Range#minmax' do before(:each) do @x = mock('x') @@ -13,37 +12,26 @@ describe 'Range#minmax' do end describe 'on an inclusive range' do - ruby_version_is '2.6'...'2.7' do - it 'should try to iterate endlessly on an endless range' do - @x.should_receive(:succ).once.and_return(@y) - range = Range.new(@x, nil) - - -> { range.minmax }.should raise_error(NoMethodError, /^undefined method `succ' for/) - end - end - - ruby_version_is '2.7' do - it 'should raise RangeError on an endless range without iterating the range' do - @x.should_not_receive(:succ) - - range = Range.new(@x, nil) + it 'should raise RangeError on an endless range without iterating the range' do + @x.should_not_receive(:succ) - -> { range.minmax }.should raise_error(RangeError, 'cannot get the maximum of endless range') - end + range = (@x..) - it 'raises RangeError or ArgumentError on a beginless range' do - range = Range.new(nil, @x) + -> { range.minmax }.should.raise(RangeError, 'cannot get the maximum of endless range') + end - -> { range.minmax }.should raise_error(StandardError) { |e| - if RangeError === e - # error from #min - -> { raise e }.should raise_error(RangeError, 'cannot get the minimum of beginless range') - else - # error from #max - -> { raise e }.should raise_error(ArgumentError, 'comparison of NilClass with MockObject failed') - end - } - end + it 'raises RangeError or ArgumentError on a beginless range' do + range = (..@x) + + -> { range.minmax }.should.raise(StandardError) { |e| + if RangeError === e + # error from #min + -> { raise e }.should.raise(RangeError, 'cannot get the minimum of beginless range') + else + # error from #max + -> { raise e }.should.raise(ArgumentError, 'comparison of NilClass with MockObject failed') + end + } end it 'should return beginning of range if beginning and end are equal without iterating the range' do @@ -58,34 +46,22 @@ describe 'Range#minmax' do (@y..@x).minmax.should == [nil, nil] end - ruby_version_is ''...'2.7' do - it 'should return the minimum and maximum values for a non-numeric range by iterating the range' do - @x.should_receive(:succ).once.and_return(@y) - - (@x..@y).minmax.should == [@x, @y] - end - end - - ruby_version_is '2.7' do - it 'should return the minimum and maximum values for a non-numeric range without iterating the range' do - @x.should_not_receive(:succ) + it 'should return the minimum and maximum values for a non-numeric range without iterating the range' do + @x.should_not_receive(:succ) - (@x..@y).minmax.should == [@x, @y] - end + (@x..@y).minmax.should == [@x, @y] end it 'should return the minimum and maximum values for a numeric range' do (1..3).minmax.should == [1, 3] end - ruby_version_is '2.7' do - it 'should return the minimum and maximum values for a numeric range without iterating the range' do - # We cannot set expectations on integers, - # so we "prevent" iteration by picking a value that would iterate until the spec times out. - range_end = Float::INFINITY + it 'should return the minimum and maximum values for a numeric range without iterating the range' do + # We cannot set expectations on integers, + # so we "prevent" iteration by picking a value that would iterate until the spec times out. + range_end = Float::INFINITY - (1..range_end).minmax.should == [1, range_end] - end + (1..range_end).minmax.should == [1, range_end] end it 'should return the minimum and maximum values according to the provided block by iterating the range' do @@ -96,69 +72,53 @@ describe 'Range#minmax' do end describe 'on an exclusive range' do - ruby_version_is '2.6'...'2.7' do - # Endless ranges introduced in 2.6 - it 'should try to iterate endlessly on an endless range' do - @x.should_receive(:succ).once.and_return(@y) - range = Range.new(@x, nil, true) + it 'should raise RangeError on an endless range' do + @x.should_not_receive(:succ) + range = (@x...) - -> { range.minmax }.should raise_error(NoMethodError, /^undefined method `succ' for/) - end + -> { range.minmax }.should.raise(RangeError, 'cannot get the maximum of endless range') end - ruby_version_is '2.7' do - it 'should raise RangeError on an endless range' do - @x.should_not_receive(:succ) - range = Range.new(@x, nil, true) - - -> { range.minmax }.should raise_error(RangeError, 'cannot get the maximum of endless range') - end - - it 'should raise RangeError on a beginless range' do - range = Range.new(nil, @x, true) + it 'should raise RangeError on a beginless range' do + range = (...@x) - -> { range.minmax }.should raise_error(RangeError, - /cannot get the maximum of beginless range with custom comparison method|cannot get the minimum of beginless range/) - end + -> { range.minmax }.should.raise(RangeError, + /cannot get the maximum of beginless range with custom comparison method|cannot get the minimum of beginless range/) end - ruby_bug "#17014", "2.7.0"..."3.0" do - it 'should return nil pair if beginning and end are equal without iterating the range' do - @x.should_not_receive(:succ) + it 'should return nil pair if beginning and end are equal without iterating the range' do + @x.should_not_receive(:succ) - (@x...@x).minmax.should == [nil, nil] - end + (@x...@x).minmax.should == [nil, nil] + end - it 'should return nil pair if beginning is greater than end without iterating the range' do - @y.should_not_receive(:succ) + it 'should return nil pair if beginning is greater than end without iterating the range' do + @y.should_not_receive(:succ) - (@y...@x).minmax.should == [nil, nil] - end + (@y...@x).minmax.should == [nil, nil] + end - it 'should return the minimum and maximum values for a non-numeric range by iterating the range' do - @x.should_receive(:succ).once.and_return(@y) + it 'should return the minimum and maximum values for a non-numeric range by iterating the range' do + @x.should_receive(:succ).once.and_return(@y) - (@x...@y).minmax.should == [@x, @x] - end + (@x...@y).minmax.should == [@x, @x] end it 'should return the minimum and maximum values for a numeric range' do (1...3).minmax.should == [1, 2] end - ruby_version_is '2.7' do - it 'should return the minimum and maximum values for a numeric range without iterating the range' do - # We cannot set expectations on integers, - # so we "prevent" iteration by picking a value that would iterate until the spec times out. - range_end = bignum_value + it 'should return the minimum and maximum values for a numeric range without iterating the range' do + # We cannot set expectations on integers, + # so we "prevent" iteration by picking a value that would iterate until the spec times out. + range_end = bignum_value - (1...range_end).minmax.should == [1, range_end - 1] - end + (1...range_end).minmax.should == [1, range_end - 1] + end - it 'raises TypeError if the end value is not an integer' do - range = (0...Float::INFINITY) - -> { range.minmax }.should raise_error(TypeError, 'cannot exclude non Integer end value') - end + it 'raises TypeError if the end value is not an integer' do + range = (0...Float::INFINITY) + -> { range.minmax }.should.raise(TypeError, 'cannot exclude non Integer end value') end it 'should return the minimum and maximum values according to the provided block by iterating the range' do diff --git a/spec/ruby/core/range/new_spec.rb b/spec/ruby/core/range/new_spec.rb index be10ff244e..9a35f28c7e 100644 --- a/spec/ruby/core/range/new_spec.rb +++ b/spec/ruby/core/range/new_spec.rb @@ -25,12 +25,12 @@ describe "Range.new" do end it "raises an ArgumentError when the given start and end can't be compared by using #<=>" do - -> { Range.new(1, mock('x')) }.should raise_error(ArgumentError) - -> { Range.new(mock('x'), mock('y')) }.should raise_error(ArgumentError) + -> { Range.new(1, mock('x')) }.should.raise(ArgumentError) + -> { Range.new(mock('x'), mock('y')) }.should.raise(ArgumentError) b = mock('x') (a = mock('nil')).should_receive(:<=>).with(b).and_return(nil) - -> { Range.new(a, b) }.should raise_error(ArgumentError) + -> { Range.new(a, b) }.should.raise(ArgumentError) end it "does not rescue exception raised in #<=> when compares the given start and end" do @@ -38,48 +38,40 @@ describe "Range.new" do a = mock('b') a.should_receive(:<=>).with(b).and_raise(RangeSpecs::ComparisonError) - -> { Range.new(a, b) }.should raise_error(RangeSpecs::ComparisonError) + -> { Range.new(a, b) }.should.raise(RangeSpecs::ComparisonError) end describe "beginless/endless range" do - ruby_version_is ""..."2.7" do - it "does not allow range without left boundary" do - -> { Range.new(nil, 1) }.should raise_error(ArgumentError, /bad value for range/) - end + it "allows beginless left boundary" do + range = Range.new(nil, 1) + range.begin.should == nil end - ruby_version_is "2.7" do - it "allows beginless left boundary" do - range = Range.new(nil, 1) - range.begin.should == nil - end + it "distinguishes ranges with included and excluded right boundary" do + range_exclude = Range.new(nil, 1, true) + range_include = Range.new(nil, 1, false) - it "distinguishes ranges with included and excluded right boundary" do - range_exclude = Range.new(nil, 1, true) - range_include = Range.new(nil, 1, false) - - range_exclude.should_not == range_include - end + range_exclude.should_not == range_include end - ruby_version_is ""..."2.6" do - it "does not allow range without right boundary" do - -> { Range.new(1, nil) }.should raise_error(ArgumentError, /bad value for range/) - end + it "allows endless right boundary" do + range = Range.new(1, nil) + range.end.should == nil end - ruby_version_is "2.6" do - it "allows endless right boundary" do - range = Range.new(1, nil) - range.end.should == nil - end + it "distinguishes ranges with included and excluded right boundary" do + range_exclude = Range.new(1, nil, true) + range_include = Range.new(1, nil, false) + + range_exclude.should_not == range_include + end - it "distinguishes ranges with included and excluded right boundary" do - range_exclude = Range.new(1, nil, true) - range_include = Range.new(1, nil, false) + it "creates a frozen range if the class is Range.class" do + Range.new(1, 2).should.frozen? + end - range_exclude.should_not == range_include - end + it "does not create a frozen range if the class is not Range.class" do + Class.new(Range).new(1, 2).should_not.frozen? end end end diff --git a/spec/ruby/core/range/overlap_spec.rb b/spec/ruby/core/range/overlap_spec.rb new file mode 100644 index 0000000000..201cd2b1ff --- /dev/null +++ b/spec/ruby/core/range/overlap_spec.rb @@ -0,0 +1,87 @@ +require_relative '../../spec_helper' + +describe "Range#overlap?" do + it "returns true if other Range overlaps self" do + (0..2).overlap?(1..3).should == true + (1..3).overlap?(0..2).should == true + (0..2).overlap?(0..2).should == true + (0..3).overlap?(1..2).should == true + (1..2).overlap?(0..3).should == true + + ('a'..'c').overlap?('b'..'d').should == true + end + + it "returns false if other Range does not overlap self" do + (0..2).overlap?(3..4).should == false + (0..2).overlap?(-4..-1).should == false + + ('a'..'c').overlap?('d'..'f').should == false + end + + it "raises TypeError when called with non-Range argument" do + -> { + (0..2).overlap?(1) + }.should.raise(TypeError, "wrong argument type Integer (expected Range)") + end + + it "returns true when beginningless and endless Ranges overlap" do + (0..2).overlap?(..3).should == true + (0..2).overlap?(..1).should == true + (0..2).overlap?(..0).should == true + + (..3).overlap?(0..2).should == true + (..1).overlap?(0..2).should == true + (..0).overlap?(0..2).should == true + + (0..2).overlap?(-1..).should == true + (0..2).overlap?(1..).should == true + (0..2).overlap?(2..).should == true + + (-1..).overlap?(0..2).should == true + (1..).overlap?(0..2).should == true + (2..).overlap?(0..2).should == true + + (0..).overlap?(2..).should == true + (..0).overlap?(..2).should == true + end + + it "returns false when beginningless and endless Ranges do not overlap" do + (0..2).overlap?(..-1).should == false + (0..2).overlap?(3..).should == false + + (..-1).overlap?(0..2).should == false + (3..).overlap?(0..2).should == false + end + + it "returns false when Ranges are not compatible" do + (0..2).overlap?('a'..'d').should == false + end + + it "return false when self is empty" do + (2..0).overlap?(1..3).should == false + (2...2).overlap?(1..3).should == false + (1...1).overlap?(1...1).should == false + (2..0).overlap?(2..0).should == false + + ('c'..'a').overlap?('b'..'d').should == false + ('a'...'a').overlap?('b'..'d').should == false + ('b'...'b').overlap?('b'...'b').should == false + ('c'...'a').overlap?('c'...'a').should == false + end + + it "return false when other Range is empty" do + (1..3).overlap?(2..0).should == false + (1..3).overlap?(2...2).should == false + + ('b'..'d').overlap?('c'..'a').should == false + ('b'..'d').overlap?('c'...'c').should == false + end + + it "takes into account exclusive end" do + (0...2).overlap?(2..4).should == false + (2..4).overlap?(0...2).should == false + + ('a'...'c').overlap?('c'..'e').should == false + ('c'..'e').overlap?('a'...'c').should == false + end +end diff --git a/spec/ruby/core/range/percent_spec.rb b/spec/ruby/core/range/percent_spec.rb index 41badd4f72..5ec6770ddb 100644 --- a/spec/ruby/core/range/percent_spec.rb +++ b/spec/ruby/core/range/percent_spec.rb @@ -1,18 +1,16 @@ require_relative '../../spec_helper' -ruby_version_is "2.6" do - describe "Range#%" do - it "works as a Range#step" do - aseq = (1..10) % 2 - aseq.class.should == Enumerator::ArithmeticSequence - aseq.begin.should == 1 - aseq.end.should == 10 - aseq.step.should == 2 - aseq.to_a.should == [1, 3, 5, 7, 9] - end +describe "Range#%" do + it "works as a Range#step" do + aseq = (1..10) % 2 + aseq.class.should == Enumerator::ArithmeticSequence + aseq.begin.should == 1 + aseq.end.should == 10 + aseq.step.should == 2 + aseq.to_a.should == [1, 3, 5, 7, 9] + end - it "produces an arithmetic sequence with a percent sign in #inspect" do - ((1..10) % 2).inspect.should == "((1..10).%(2))" - end + it "produces an arithmetic sequence with a percent sign in #inspect" do + ((1..10) % 2).inspect.should == "((1..10).%(2))" end end diff --git a/spec/ruby/core/range/reverse_each_spec.rb b/spec/ruby/core/range/reverse_each_spec.rb new file mode 100644 index 0000000000..49790e8b0a --- /dev/null +++ b/spec/ruby/core/range/reverse_each_spec.rb @@ -0,0 +1,125 @@ +require_relative '../../spec_helper' + +describe "Range#reverse_each" do + it "traverses the Range in reverse order and passes each element to block" do + a = [] + (1..3).reverse_each { |i| a << i } + a.should == [3, 2, 1] + + a = [] + (1...3).reverse_each { |i| a << i } + a.should == [2, 1] + end + + it "returns self" do + r = (1..3) + r.reverse_each { |x| }.should.equal?(r) + end + + it "returns an Enumerator if no block given" do + enum = (1..3).reverse_each + enum.should.instance_of?(Enumerator) + enum.to_a.should == [3, 2, 1] + end + + it "raises a TypeError for endless Ranges of Integers" do + -> { + (1..).reverse_each.take(3) + }.should.raise(TypeError, "can't iterate from NilClass") + end + + it "raises a TypeError for endless Ranges of non-Integers" do + -> { + ("a"..).reverse_each.take(3) + }.should.raise(TypeError, "can't iterate from NilClass") + end + + context "Integer boundaries" do + it "supports beginningless Ranges" do + (..5).reverse_each.take(3).should == [5, 4, 3] + end + end + + context "non-Integer boundaries" do + it "uses #succ to iterate a Range of non-Integer elements" do + y = mock('y') + x = mock('x') + + x.should_receive(:succ).any_number_of_times.and_return(y) + x.should_receive(:<=>).with(y).any_number_of_times.and_return(-1) + x.should_receive(:<=>).with(x).any_number_of_times.and_return(0) + y.should_receive(:<=>).with(x).any_number_of_times.and_return(1) + y.should_receive(:<=>).with(y).any_number_of_times.and_return(0) + + a = [] + (x..y).each { |i| a << i } + a.should == [x, y] + end + + it "uses #succ to iterate a Range of Strings" do + a = [] + ('A'..'D').reverse_each { |i| a << i } + a.should == ['D','C','B','A'] + end + + it "uses #succ to iterate a Range of Symbols" do + a = [] + (:A..:D).reverse_each { |i| a << i } + a.should == [:D, :C, :B, :A] + end + + it "raises a TypeError when `begin` value does not respond to #succ" do + -> { (Time.now..Time.now).reverse_each { |x| x } }.should.raise(TypeError, /can't iterate from Time/) + -> { (//..//).reverse_each { |x| x } }.should.raise(TypeError, /can't iterate from Regexp/) + -> { ([]..[]).reverse_each { |x| x } }.should.raise(TypeError, /can't iterate from Array/) + end + + it "does not support beginningless Ranges" do + -> { + (..'a').reverse_each { |x| x } + }.should.raise(TypeError, /can't iterate from NilClass/) + end + end + + context "when no block is given" do + describe "returned Enumerator size" do + it "returns the Range size when Range size is finite" do + (1..3).reverse_each.size.should == 3 + (1...3).reverse_each.size.should == 2 + + (1..3.3).reverse_each.size.should == 3 + (1...3.3).reverse_each.size.should == 3 + end + + ruby_version_is ""..."3.4" do + it "returns a size when it is not iterable" do + (1.1..3).reverse_each.size.should == 2 + (1.1..3.3).reverse_each.size.should == 3 + (1.1..nil).reverse_each.size.should == Float::INFINITY + (nil..3.3).reverse_each.size.should == Float::INFINITY + (nil..nil).reverse_each.size.should == nil + end + end + + ruby_version_is "3.4" do + it "raises TypeError when the range is not iterable" do + -> { (1.1..3).reverse_each.size }.should.raise(TypeError, /can't iterate from Integer/) + -> { (1.1..3.3).reverse_each.size }.should.raise(TypeError, /can't iterate from Float/) + -> { (1.1..nil).reverse_each.size }.should.raise(TypeError, /can't iterate from NilClass/) + -> { (nil..3.3).reverse_each.size }.should.raise(TypeError, /can't iterate from Float/) + -> { (nil..nil).reverse_each.size }.should.raise(TypeError, /can't iterate from NilClass/) + end + end + + ruby_bug "#20936", "3.4"..."4.0" do + it "returns Infinity when Range size is infinite" do + (..3).reverse_each.size.should == Float::INFINITY + end + end + + it "returns nil when Range size is unknown" do + ('a'..'z').reverse_each.size.should == nil + end + end + end +end diff --git a/spec/ruby/core/range/shared/cover.rb b/spec/ruby/core/range/shared/cover.rb index 5b09cea4e0..189f3da4bf 100644 --- a/spec/ruby/core/range/shared/cover.rb +++ b/spec/ruby/core/range/shared/cover.rb @@ -1,4 +1,4 @@ -# -*- encoding: binary -*- +# encoding: binary require_relative '../../../spec_helper' require_relative '../fixtures/classes' @@ -6,26 +6,26 @@ describe :range_cover, shared: true do it "uses the range element's <=> to make the comparison" do a = mock('a') a.should_receive(:<=>).twice.and_return(-1,-1) - (a..'z').send(@method, 'b').should be_true + (a..'z').send(@method, 'b').should == true end it "uses a continuous inclusion test" do - ('a'..'f').send(@method, 'aa').should be_true - ('a'..'f').send(@method, 'babe').should be_true - ('a'..'f').send(@method, 'baby').should be_true - ('a'..'f').send(@method, 'ga').should be_false - (-10..-2).send(@method, -2.5).should be_true + ('a'..'f').send(@method, 'aa').should == true + ('a'..'f').send(@method, 'babe').should == true + ('a'..'f').send(@method, 'baby').should == true + ('a'..'f').send(@method, 'ga').should == false + (-10..-2).send(@method, -2.5).should == true end describe "on string elements" do it "returns true if other is matched by element.succ" do - ('a'..'c').send(@method, 'b').should be_true - ('a'...'c').send(@method, 'b').should be_true + ('a'..'c').send(@method, 'b').should == true + ('a'...'c').send(@method, 'b').should == true end it "returns true if other is not matched by element.succ" do - ('a'..'c').send(@method, 'bc').should be_true - ('a'...'c').send(@method, 'bc').should be_true + ('a'..'c').send(@method, 'bc').should == true + ('a'...'c').send(@method, 'bc').should == true end end @@ -36,27 +36,27 @@ describe :range_cover, shared: true do end it "returns false if other is less than first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should == false end it "returns true if other is equal as first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should == true end it "returns true if other is matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should == true end it "returns true if other is not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should == true end it "returns true if other is equal as last element but not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should == true end it "returns false if other is greater than last element but matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should == false end end @@ -66,90 +66,128 @@ describe :range_cover, shared: true do end it "returns false if other is less than first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should == false end it "returns true if other is equal as first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should == true end it "returns true if other is matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should == true end it "returns true if other is not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should == true end it "returns false if other is equal as last element but not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should == false end it "returns false if other is greater than last element but matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should == false end end end end describe :range_cover_subrange, shared: true do - ruby_version_is "2.6" do - context "range argument" do - it "accepts range argument" do - (0..10).send(@method, (3..7)).should be_true - (0..10).send(@method, (3..15)).should be_false - (0..10).send(@method, (-2..7)).should be_false - - (1.1..7.9).send(@method, (2.5..6.5)).should be_true - (1.1..7.9).send(@method, (2.5..8.5)).should be_false - (1.1..7.9).send(@method, (0.5..6.5)).should be_false - - ('c'..'i').send(@method, ('d'..'f')).should be_true - ('c'..'i').send(@method, ('d'..'z')).should be_false - ('c'..'i').send(@method, ('a'..'f')).should be_false - - range_10_100 = RangeSpecs::TenfoldSucc.new(10)..RangeSpecs::TenfoldSucc.new(100) - range_20_90 = RangeSpecs::TenfoldSucc.new(20)..RangeSpecs::TenfoldSucc.new(90) - range_20_110 = RangeSpecs::TenfoldSucc.new(20)..RangeSpecs::TenfoldSucc.new(110) - range_0_90 = RangeSpecs::TenfoldSucc.new(0)..RangeSpecs::TenfoldSucc.new(90) - - range_10_100.send(@method, range_20_90).should be_true - range_10_100.send(@method, range_20_110).should be_false - range_10_100.send(@method, range_0_90).should be_false - end + context "range argument" do + it "accepts range argument" do + (0..10).send(@method, (3..7)).should == true + (0..10).send(@method, (3..15)).should == false + (0..10).send(@method, (-2..7)).should == false + + (1.1..7.9).send(@method, (2.5..6.5)).should == true + (1.1..7.9).send(@method, (2.5..8.5)).should == false + (1.1..7.9).send(@method, (0.5..6.5)).should == false + + ('c'..'i').send(@method, ('d'..'f')).should == true + ('c'..'i').send(@method, ('d'..'z')).should == false + ('c'..'i').send(@method, ('a'..'f')).should == false + + range_10_100 = RangeSpecs::TenfoldSucc.new(10)..RangeSpecs::TenfoldSucc.new(100) + range_20_90 = RangeSpecs::TenfoldSucc.new(20)..RangeSpecs::TenfoldSucc.new(90) + range_20_110 = RangeSpecs::TenfoldSucc.new(20)..RangeSpecs::TenfoldSucc.new(110) + range_0_90 = RangeSpecs::TenfoldSucc.new(0)..RangeSpecs::TenfoldSucc.new(90) + + range_10_100.send(@method, range_20_90).should == true + range_10_100.send(@method, range_20_110).should == false + range_10_100.send(@method, range_0_90).should == false + end - it "supports boundaries of different comparable types" do - (0..10).send(@method, (3.1..7.9)).should be_true - (0..10).send(@method, (3.1..15.9)).should be_false - (0..10).send(@method, (-2.1..7.9)).should be_false - end + it "supports boundaries of different comparable types" do + (0..10).send(@method, (3.1..7.9)).should == true + (0..10).send(@method, (3.1..15.9)).should == false + (0..10).send(@method, (-2.1..7.9)).should == false + end - it "returns false if types are not comparable" do - (0..10).send(@method, ('a'..'z')).should be_false - (0..10).send(@method, (RangeSpecs::TenfoldSucc.new(0)..RangeSpecs::TenfoldSucc.new(100))).should be_false - end + it "returns false if types are not comparable" do + (0..10).send(@method, ('a'..'z')).should == false + (0..10).send(@method, (RangeSpecs::TenfoldSucc.new(0)..RangeSpecs::TenfoldSucc.new(100))).should == false + end - it "honors exclusion of right boundary (:exclude_end option)" do - # Integer - (0..10).send(@method, (0..10)).should be_true - (0...10).send(@method, (0...10)).should be_true + it "honors exclusion of right boundary (:exclude_end option)" do + # Integer + (0..10).send(@method, (0..10)).should == true + (0...10).send(@method, (0...10)).should == true - (0..10).send(@method, (0...10)).should be_true - (0...10).send(@method, (0..10)).should be_false + (0..10).send(@method, (0...10)).should == true + (0...10).send(@method, (0..10)).should == false - (0...11).send(@method, (0..10)).should be_true - (0..10).send(@method, (0...11)).should be_true + (0...11).send(@method, (0..10)).should == true + (0..10).send(@method, (0...11)).should == true - # Float - (0..10.1).send(@method, (0..10.1)).should be_true - (0...10.1).send(@method, (0...10.1)).should be_true + # Float + (0..10.1).send(@method, (0..10.1)).should == true + (0...10.1).send(@method, (0...10.1)).should == true - (0..10.1).send(@method, (0...10.1)).should be_true - (0...10.1).send(@method, (0..10.1)).should be_false + (0..10.1).send(@method, (0...10.1)).should == true + (0...10.1).send(@method, (0..10.1)).should == false - (0...11.1).send(@method, (0..10.1)).should be_true - (0..10.1).send(@method, (0...11.1)).should be_false - end + (0...11.1).send(@method, (0..10.1)).should == true + (0..10.1).send(@method, (0...11.1)).should == false end end + + it "allows self to be a beginless range" do + (...10).send(@method, (3..7)).should == true + (...10).send(@method, (3..15)).should == false + + (..7.9).send(@method, (2.5..6.5)).should == true + (..7.9).send(@method, (2.5..8.5)).should == false + + (..'i').send(@method, ('d'..'f')).should == true + (..'i').send(@method, ('d'..'z')).should == false + end + + it "allows self to be a endless range" do + eval("(0...)").send(@method, (3..7)).should == true + eval("(5...)").send(@method, (3..15)).should == false + + eval("(1.1..)").send(@method, (2.5..6.5)).should == true + eval("(3.3..)").send(@method, (2.5..8.5)).should == false + + eval("('a'..)").send(@method, ('d'..'f')).should == true + eval("('p'..)").send(@method, ('d'..'z')).should == false + end + + it "accepts beginless range argument" do + (..10).send(@method, (...10)).should == true + (0..10).send(@method, (...10)).should == false + + (1.1..7.9).send(@method, (...10.5)).should == false + + ('c'..'i').send(@method, (..'i')).should == false + end + + it "accepts endless range argument" do + eval("(0..)").send(@method, eval("(0...)")).should == true + (0..10).send(@method, eval("(0...)")).should == false + + (1.1..7.9).send(@method, eval("(0.8...)")).should == false + + ('c'..'i').send(@method, eval("('a'..)")).should == false + end end diff --git a/spec/ruby/core/range/shared/cover_and_include.rb b/spec/ruby/core/range/shared/cover_and_include.rb index b308524310..97721a7307 100644 --- a/spec/ruby/core/range/shared/cover_and_include.rb +++ b/spec/ruby/core/range/shared/cover_and_include.rb @@ -1,4 +1,4 @@ -# -*- encoding: binary -*- +# encoding: binary require_relative '../../../spec_helper' describe :range_cover_and_include, shared: true do @@ -19,11 +19,25 @@ describe :range_cover_and_include, shared: true do (0.5...2.4).send(@method, 2.4).should == false end - ruby_version_is "2.6" do - it "returns true if other is an element of self for endless ranges" do - eval("(1..)").send(@method, 2.4).should == true - eval("(0.5...)").send(@method, 2.4).should == true - end + it "returns true if other is an element of self for endless ranges" do + (1..).send(@method, 2.4).should == true + (0.5...).send(@method, 2.4).should == true + end + + it "returns true if other is an element of self for beginless ranges" do + (..10).send(@method, 2.4).should == true + (...10.5).send(@method, 2.4).should == true + end + + it "returns false if values are not comparable" do + (1..10).send(@method, nil).should == false + (1...10).send(@method, nil).should == false + + (..10).send(@method, nil).should == false + (...10).send(@method, nil).should == false + + (1..).send(@method, nil).should == false + (1...).send(@method, nil).should == false end it "compares values using <=>" do @@ -32,42 +46,41 @@ describe :range_cover_and_include, shared: true do m.should_receive(:coerce).and_return([1, 2]) m.should_receive(:<=>).and_return(1) - rng.send(@method, m).should be_false + rng.send(@method, m).should == false end it "raises an ArgumentError without exactly one argument" do - ->{ (1..2).send(@method) }.should raise_error(ArgumentError) - ->{ (1..2).send(@method, 1, 2) }.should raise_error(ArgumentError) + ->{ (1..2).send(@method) }.should.raise(ArgumentError) + ->{ (1..2).send(@method, 1, 2) }.should.raise(ArgumentError) end it "returns true if argument is equal to the first value of the range" do - (0..5).send(@method, 0).should be_true - ('f'..'s').send(@method, 'f').should be_true + (0..5).send(@method, 0).should == true + ('f'..'s').send(@method, 'f').should == true end it "returns true if argument is equal to the last value of the range" do - (0..5).send(@method, 5).should be_true - (0...5).send(@method, 4).should be_true - ('f'..'s').send(@method, 's').should be_true + (0..5).send(@method, 5).should == true + (0...5).send(@method, 4).should == true + ('f'..'s').send(@method, 's').should == true end it "returns true if argument is less than the last value of the range and greater than the first value" do - (20..30).send(@method, 28).should be_true - ('e'..'h').send(@method, 'g').should be_true - ("\u{999}".."\u{9999}").send @method, "\u{9995}" + (20..30).send(@method, 28).should == true + ('e'..'h').send(@method, 'g').should == true end it "returns true if argument is sole element in the range" do - (30..30).send(@method, 30).should be_true + (30..30).send(@method, 30).should == true end it "returns false if range is empty" do - (30...30).send(@method, 30).should be_false - (30...30).send(@method, nil).should be_false + (30...30).send(@method, 30).should == false + (30...30).send(@method, nil).should == false end it "returns false if the range does not contain the argument" do - ('A'..'C').send(@method, 20.9).should be_false - ('A'...'C').send(@method, 'C').should be_false + ('A'..'C').send(@method, 20.9).should == false + ('A'...'C').send(@method, 'C').should == false end end diff --git a/spec/ruby/core/range/shared/equal_value.rb b/spec/ruby/core/range/shared/equal_value.rb index 8872b4efc0..363c6be558 100644 --- a/spec/ruby/core/range/shared/equal_value.rb +++ b/spec/ruby/core/range/shared/equal_value.rb @@ -43,11 +43,9 @@ describe :range_eql, shared: true do a.send(@method, b).should == true end - ruby_version_is "2.6" do - it "works for endless Ranges" do - eval("(1..)").send(@method, eval("(1..)")).should == true - eval("(0.5...)").send(@method, eval("(0.5...)")).should == true - eval("(1..)").send(@method, eval("(1...)")).should == false - end + it "works for endless Ranges" do + eval("(1..)").send(@method, eval("(1..)")).should == true + eval("(0.5...)").send(@method, eval("(0.5...)")).should == true + eval("(1..)").send(@method, eval("(1...)")).should == false end end diff --git a/spec/ruby/core/range/shared/include.rb b/spec/ruby/core/range/shared/include.rb index c6c5c2becf..5f0db48008 100644 --- a/spec/ruby/core/range/shared/include.rb +++ b/spec/ruby/core/range/shared/include.rb @@ -1,17 +1,17 @@ -# -*- encoding: binary -*- +# encoding: binary require_relative '../../../spec_helper' require_relative '../fixtures/classes' describe :range_include, shared: true do describe "on string elements" do it "returns true if other is matched by element.succ" do - ('a'..'c').send(@method, 'b').should be_true - ('a'...'c').send(@method, 'b').should be_true + ('a'..'c').send(@method, 'b').should == true + ('a'...'c').send(@method, 'b').should == true end it "returns false if other is not matched by element.succ" do - ('a'..'c').send(@method, 'bc').should be_false - ('a'...'c').send(@method, 'bc').should be_false + ('a'..'c').send(@method, 'bc').should == false + ('a'...'c').send(@method, 'bc').should == false end end @@ -22,27 +22,27 @@ describe :range_include, shared: true do end it "returns false if other is less than first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should == false end it "returns true if other is equal as first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should == true end it "returns true if other is matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should == true end it "returns false if other is not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should == false end it "returns false if other is equal as last element but not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should == false end it "returns false if other is greater than last element but matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should == false end end @@ -52,27 +52,27 @@ describe :range_include, shared: true do end it "returns false if other is less than first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should == false end it "returns true if other is equal as first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should == true end it "returns true if other is matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true + @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should == true end it "returns false if other is not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should == false end it "returns false if other is equal as last element but not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should == false end it "returns false if other is greater than last element but matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false + @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should == false end end end diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb index 4bf525e541..3a8843b99d 100644 --- a/spec/ruby/core/range/size_spec.rb +++ b/spec/ruby/core/range/size_spec.rb @@ -4,49 +4,89 @@ describe "Range#size" do it "returns the number of elements in the range" do (1..16).size.should == 16 (1...16).size.should == 15 - - (1.0..16.0).size.should == 16 - (1.0...16.0).size.should == 15 - (1.0..15.9).size.should == 15 - (1.1..16.0).size.should == 15 - (1.1..15.9).size.should == 15 end it "returns 0 if last is less than first" do (16..0).size.should == 0 - (16.0..0.0).size.should == 0 - (Float::INFINITY..0).size.should == 0 end it 'returns Float::INFINITY for increasing, infinite ranges' do (0..Float::INFINITY).size.should == Float::INFINITY - (-Float::INFINITY..0).size.should == Float::INFINITY - (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY end - ruby_version_is "2.6" do + it 'returns Float::INFINITY for endless ranges if the start is numeric' do + eval("(1..)").size.should == Float::INFINITY + end + + it 'returns nil for endless ranges if the start is not numeric' do + eval("('z'..)").size.should == nil + end + + ruby_version_is ""..."3.4" do + it 'returns Float::INFINITY for all beginless ranges if the end is numeric' do + (..1).size.should == Float::INFINITY + (...0.5).size.should == Float::INFINITY + end + + it 'returns nil for all beginless ranges if the end is not numeric' do + (...'o').size.should == nil + end + + it 'returns nil if the start and the end is both nil' do + (nil..nil).size.should == nil + end + end + + ruby_version_is ""..."3.4" do + it "returns the number of elements in the range" do + (1.0..16.0).size.should == 16 + (1.0...16.0).size.should == 15 + (1.0..15.9).size.should == 15 + (1.1..16.0).size.should == 15 + (1.1..15.9).size.should == 15 + end + + it "returns 0 if last is less than first" do + (16.0..0.0).size.should == 0 + (Float::INFINITY..0).size.should == 0 + end + + it 'returns Float::INFINITY for increasing, infinite ranges' do + (-Float::INFINITY..0).size.should == Float::INFINITY + (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY + end + it 'returns Float::INFINITY for endless ranges if the start is numeric' do - eval("(1..)").size.should == Float::INFINITY eval("(0.5...)").size.should == Float::INFINITY end it 'returns nil for endless ranges if the start is not numeric' do - eval("('z'..)").size.should == nil eval("([]...)").size.should == nil end end - ruby_version_is "2.7" do - it 'returns Float::INFINITY for all beginless ranges' do - eval("(..1)").size.should == Float::INFINITY - eval("(...0.5)").size.should == Float::INFINITY - eval("(..nil)").size.should == Float::INFINITY - eval("(...'o')").size.should == Float::INFINITY + ruby_version_is "3.4" do + it 'raises TypeError if a range is not iterable' do + -> { (1.0..16.0).size }.should.raise(TypeError, /can't iterate from/) + -> { (1.0...16.0).size }.should.raise(TypeError, /can't iterate from/) + -> { (1.0..15.9).size }.should.raise(TypeError, /can't iterate from/) + -> { (1.1..16.0).size }.should.raise(TypeError, /can't iterate from/) + -> { (1.1..15.9).size }.should.raise(TypeError, /can't iterate from/) + -> { (16.0..0.0).size }.should.raise(TypeError, /can't iterate from/) + -> { (Float::INFINITY..0).size }.should.raise(TypeError, /can't iterate from/) + -> { (-Float::INFINITY..0).size }.should.raise(TypeError, /can't iterate from/) + -> { (-Float::INFINITY..Float::INFINITY).size }.should.raise(TypeError, /can't iterate from/) + -> { (..1).size }.should.raise(TypeError, /can't iterate from/) + -> { (...0.5).size }.should.raise(TypeError, /can't iterate from/) + -> { (..nil).size }.should.raise(TypeError, /can't iterate from/) + -> { (...'o').size }.should.raise(TypeError, /can't iterate from/) + -> { eval("(0.5...)").size }.should.raise(TypeError, /can't iterate from/) + -> { eval("([]...)").size }.should.raise(TypeError, /can't iterate from/) end end it "returns nil if first and last are not Numeric" do - (:a..:z).size.should be_nil - ('a'..'z').size.should be_nil + (:a..:z).size.should == nil + ('a'..'z').size.should == nil end end diff --git a/spec/ruby/core/range/step_spec.rb b/spec/ruby/core/range/step_spec.rb index e284551ab3..faab95d88d 100644 --- a/spec/ruby/core/range/step_spec.rb +++ b/spec/ruby/core/range/step_spec.rb @@ -7,143 +7,188 @@ describe "Range#step" do it "returns self" do r = 1..2 - r.step { }.should equal(r) + r.step { }.should.equal?(r) end - it "raises TypeError if step" do - obj = mock("mock") - -> { (1..10).step(obj) { } }.should raise_error(TypeError) - end + ruby_version_is ""..."3.4" do + it "calls #to_int to coerce step to an Integer" do + obj = mock("Range#step") + obj.should_receive(:to_int).and_return(1) - it "calls #to_int to coerce step to an Integer" do - obj = mock("Range#step") - obj.should_receive(:to_int).and_return(1) + (1..2).step(obj) { |x| ScratchPad << x } + ScratchPad.recorded.should.eql?([1, 2]) + end - (1..2).step(obj) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([1, 2]) - end + it "raises a TypeError if step does not respond to #to_int" do + obj = mock("Range#step non-integer") - it "raises a TypeError if step does not respond to #to_int" do - obj = mock("Range#step non-integer") + -> { (1..2).step(obj) { } }.should.raise(TypeError) + end - -> { (1..2).step(obj) { } }.should raise_error(TypeError) - end + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock("Range#step non-integer") + obj.should_receive(:to_int).and_return("1") + + -> { (1..2).step(obj) { } }.should.raise(TypeError) + end - it "raises a TypeError if #to_int does not return an Integer" do - obj = mock("Range#step non-integer") - obj.should_receive(:to_int).and_return("1") + it "raises a TypeError if the first element does not respond to #succ" do + obj = mock("Range#step non-comparable") + obj.should_receive(:<=>).with(obj).and_return(1) - -> { (1..2).step(obj) { } }.should raise_error(TypeError) + -> { (obj..obj).step { |x| x } }.should.raise(TypeError) + end end - it "coerces the argument to integer by invoking to_int" do - (obj = mock("2")).should_receive(:to_int).and_return(2) - res = [] - (1..10).step(obj) {|x| res << x} - res.should == [1, 3, 5, 7, 9] - end + ruby_version_is "3.4" do + it "calls #coerce to coerce step to an Integer" do + obj = mock("Range#step") + obj.should_receive(:coerce).at_least(:once).and_return([1, 2]) - it "raises a TypeError if the first element does not respond to #succ" do - obj = mock("Range#step non-comparable") - obj.should_receive(:<=>).with(obj).and_return(1) + (1..3).step(obj) { |x| ScratchPad << x } + ScratchPad.recorded.should.eql?([1, 3]) + end + + it "raises a TypeError if step does not respond to #coerce" do + obj = mock("Range#step non-coercible") - -> { (obj..obj).step { |x| x } }.should raise_error(TypeError) + -> { (1..2).step(obj) { } }.should.raise(TypeError) + end end it "raises an ArgumentError if step is 0" do - -> { (-1..1).step(0) { |x| x } }.should raise_error(ArgumentError) + -> { (-1..1).step(0) { |x| x } }.should.raise(ArgumentError) end it "raises an ArgumentError if step is 0.0" do - -> { (-1..1).step(0.0) { |x| x } }.should raise_error(ArgumentError) + -> { (-1..1).step(0.0) { |x| x } }.should.raise(ArgumentError) end - it "raises an ArgumentError if step is negative" do - -> { (-1..1).step(-2) { |x| x } }.should raise_error(ArgumentError) + ruby_version_is "3.4" do + it "does not iterate if step is 0 for bounded non-numeric ranges" do + t = Time.utc(2023, 2, 24) + (t..t + 1).step(0) { |x| ScratchPad << x } + ScratchPad.recorded.should == [] + end + + it "raises an ArgumentError when iterating a beginless range" do + -> { (..10).step(1) { break } }.should.raise(ArgumentError, + "#step iteration for beginless ranges is meaningless") + end + end + + ruby_version_is ""..."3.4" do + it "raises an ArgumentError if step is negative" do + -> { (-1..1).step(-2) { |x| x } }.should.raise(ArgumentError) + end end describe "with inclusive end" do describe "and Integer values" do it "yields Integer values incremented by 1 and less than or equal to end when not passed a step" do (-2..2).step { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2, -1, 0, 1, 2]) + ScratchPad.recorded.should.eql?([-2, -1, 0, 1, 2]) end it "yields Integer values incremented by an Integer step" do (-5..5).step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-5, -3, -1, 1, 3, 5]) + ScratchPad.recorded.should.eql?([-5, -3, -1, 1, 3, 5]) end it "yields Float values incremented by a Float step" do (-2..2).step(1.5) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -0.5, 1.0]) + ScratchPad.recorded.should.eql?([-2.0, -0.5, 1.0]) + end + + ruby_version_is "3.4" do + it "does not iterate if step is negative for forward range" do + (-1..1).step(-1) { |x| ScratchPad << x } + ScratchPad.recorded.should.eql?([]) + end + + it "iterates backward if step is negative for backward range" do + (1..-1).step(-1) { |x| ScratchPad << x } + ScratchPad.recorded.should.eql?([1, 0, -1]) + end end end describe "and Float values" do it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do (-2.0..2.0).step { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0]) + ScratchPad.recorded.should.eql?([-2.0, -1.0, 0.0, 1.0, 2.0]) end it "yields Float values incremented by an Integer step" do (-5.0..5.0).step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) + ScratchPad.recorded.should.eql?([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) end it "yields Float values incremented by a Float step" do (-1.0..1.0).step(0.5) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0]) + ScratchPad.recorded.should.eql?([-1.0, -0.5, 0.0, 0.5, 1.0]) end it "returns Float values of 'step * n + begin <= end'" do (1.0..6.4).step(1.8) { |x| ScratchPad << x } (1.0..12.7).step(1.3) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([1.0, 2.8, 4.6, 6.4, 1.0, 2.3, 3.6, + ScratchPad.recorded.should.eql?([1.0, 2.8, 4.6, 6.4, 1.0, 2.3, 3.6, 4.9, 6.2, 7.5, 8.8, 10.1, 11.4, 12.7]) end it "handles infinite values at either end" do (-Float::INFINITY..0.0).step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 } - ScratchPad.recorded.should eql([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY]) + ScratchPad.recorded.should.eql?([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY]) ScratchPad.record [] (0.0..Float::INFINITY).step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 } - ScratchPad.recorded.should eql([0.0, 2.0, 4.0]) + ScratchPad.recorded.should.eql?([0.0, 2.0, 4.0]) + end + + ruby_version_is "3.4" do + it "does not iterate if step is negative for forward range" do + (-1.0..1.0).step(-0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should.eql?([]) + end + + it "iterates backward if step is negative for backward range" do + (1.0..-1.0).step(-0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should.eql?([1.0, 0.5, 0.0, -0.5, -1.0]) + end end end describe "and Integer, Float values" do it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do (-2..2.0).step { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0]) + ScratchPad.recorded.should.eql?([-2.0, -1.0, 0.0, 1.0, 2.0]) end it "yields Float values incremented by an Integer step" do (-5..5.0).step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) + ScratchPad.recorded.should.eql?([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) end it "yields Float values incremented by a Float step" do (-1..1.0).step(0.5) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0]) + ScratchPad.recorded.should.eql?([-1.0, -0.5, 0.0, 0.5, 1.0]) end end describe "and Float, Integer values" do it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do (-2.0..2).step { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0]) + ScratchPad.recorded.should.eql?([-2.0, -1.0, 0.0, 1.0, 2.0]) end it "yields Float values incremented by an Integer step" do (-5.0..5).step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) + ScratchPad.recorded.should.eql?([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) end it "yields Float values incremented by a Float step" do (-1.0..1).step(0.5) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0]) + ScratchPad.recorded.should.eql?([-1.0, -0.5, 0.0, 0.5, 1.0]) end end @@ -159,16 +204,99 @@ describe "Range#step" do end it "raises a TypeError when passed a Float step" do - -> { ("A".."G").step(2.0) { } }.should raise_error(TypeError) + -> { ("A".."G").step(2.0) { } }.should.raise(TypeError) end - it "calls #succ on begin and each element returned by #succ" do - obj = mock("Range#step String start") - obj.should_receive(:<=>).exactly(3).times.and_return(-1, -1, -1, 0) - obj.should_receive(:succ).exactly(2).times.and_return(obj) + ruby_version_is ""..."3.4" do + it "calls #succ on begin and each element returned by #succ" do + obj = mock("Range#step String start") + obj.should_receive(:<=>).exactly(3).times.and_return(-1, -1, -1, 0) + obj.should_receive(:succ).exactly(2).times.and_return(obj) + + (obj..obj).step { |x| ScratchPad << x } + ScratchPad.recorded.should == [obj, obj, obj] + end + end + + ruby_version_is "3.4" do + it "yields String values adjusted by step and less than or equal to end" do + ("A".."AAA").step("A") { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "AA", "AAA"] + end + + it "raises a TypeError when passed an incompatible type step" do + -> { ("A".."G").step([]) { } }.should.raise(TypeError) + end + + it "calls #+ on begin and each element returned by #+" do + start = mock("Range#step String start") + stop = mock("Range#step String stop") + + mid1 = mock("Range#step String mid1") + mid2 = mock("Range#step String mid2") + + step = mock("Range#step String step") + + # Deciding on the direction of iteration + start.should_receive(:<=>).with(stop).at_least(:twice).and_return(-1) + # Deciding whether the step moves iteration in the right direction + start.should_receive(:<=>).with(mid1).and_return(-1) + # Iteration 1 + start.should_receive(:+).at_least(:once).with(step).and_return(mid1) + # Iteration 2 + mid1.should_receive(:<=>).with(stop).and_return(-1) + mid1.should_receive(:+).with(step).and_return(mid2) + # Iteration 3 + mid2.should_receive(:<=>).with(stop).and_return(0) + + (start..stop).step(step) { |x| ScratchPad << x } + ScratchPad.recorded.should == [start, mid1, mid2] + end + + it "iterates backward if the step is decreasing values, and the range is backward" do + start = mock("Range#step String start") + stop = mock("Range#step String stop") + + mid1 = mock("Range#step String mid1") + mid2 = mock("Range#step String mid2") + + step = mock("Range#step String step") + + # Deciding on the direction of iteration + start.should_receive(:<=>).with(stop).at_least(:twice).and_return(1) + # Deciding whether the step moves iteration in the right direction + start.should_receive(:<=>).with(mid1).and_return(1) + # Iteration 1 + start.should_receive(:+).at_least(:once).with(step).and_return(mid1) + # Iteration 2 + mid1.should_receive(:<=>).with(stop).and_return(1) + mid1.should_receive(:+).with(step).and_return(mid2) + # Iteration 3 + mid2.should_receive(:<=>).with(stop).and_return(0) + + (start..stop).step(step) { |x| ScratchPad << x } + ScratchPad.recorded.should == [start, mid1, mid2] + end + + it "does no iteration of the direction of the range and of the step don't match" do + start = mock("Range#step String start") + stop = mock("Range#step String stop") - (obj..obj).step { |x| ScratchPad << x } - ScratchPad.recorded.should == [obj, obj, obj] + mid1 = mock("Range#step String mid1") + mid2 = mock("Range#step String mid2") + + step = mock("Range#step String step") + + # Deciding on the direction of iteration: stop > start + start.should_receive(:<=>).with(stop).at_least(:twice).and_return(1) + # Deciding whether the step moves iteration in the right direction + # start + step < start, the direction is opposite to the range's + start.should_receive(:+).with(step).and_return(mid1) + start.should_receive(:<=>).with(mid1).and_return(-1) + + (start..stop).step(step) { |x| ScratchPad << x } + ScratchPad.recorded.should == [] + end end end end @@ -177,258 +305,270 @@ describe "Range#step" do describe "and Integer values" do it "yields Integer values incremented by 1 and less than end when not passed a step" do (-2...2).step { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2, -1, 0, 1]) + ScratchPad.recorded.should.eql?([-2, -1, 0, 1]) end it "yields Integer values incremented by an Integer step" do (-5...5).step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-5, -3, -1, 1, 3]) + ScratchPad.recorded.should.eql?([-5, -3, -1, 1, 3]) end it "yields Float values incremented by a Float step" do (-2...2).step(1.5) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -0.5, 1.0]) + ScratchPad.recorded.should.eql?([-2.0, -0.5, 1.0]) end end describe "and Float values" do it "yields Float values incremented by 1 and less than end when not passed a step" do (-2.0...2.0).step { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0]) + ScratchPad.recorded.should.eql?([-2.0, -1.0, 0.0, 1.0]) end it "yields Float values incremented by an Integer step" do (-5.0...5.0).step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0]) + ScratchPad.recorded.should.eql?([-5.0, -3.0, -1.0, 1.0, 3.0]) end it "yields Float values incremented by a Float step" do (-1.0...1.0).step(0.5) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5]) + ScratchPad.recorded.should.eql?([-1.0, -0.5, 0.0, 0.5]) end it "returns Float values of 'step * n + begin < end'" do (1.0...6.4).step(1.8) { |x| ScratchPad << x } + ScratchPad.recorded.should.eql?([1.0, 2.8, 4.6]) + end + + it "correctly handles values near the upper limit" do # https://bugs.ruby-lang.org/issues/16612 (1.0...55.6).step(18.2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([1.0, 2.8, 4.6, 1.0, 19.2, 37.4]) + ScratchPad.recorded.should.eql?([1.0, 19.2, 37.4, 55.599999999999994]) + + (1.0...55.6).step(18.2).size.should == 4 end it "handles infinite values at either end" do (-Float::INFINITY...0.0).step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 } - ScratchPad.recorded.should eql([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY]) + ScratchPad.recorded.should.eql?([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY]) ScratchPad.record [] (0.0...Float::INFINITY).step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 } - ScratchPad.recorded.should eql([0.0, 2.0, 4.0]) + ScratchPad.recorded.should.eql?([0.0, 2.0, 4.0]) + end + + ruby_version_is "3.4" do + it "iterates backward with exclusive end if step is negative" do + (1.0...-1.0).step(-0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should.eql?([1.0, 0.5, 0.0, -0.5]) + end end end describe "and Integer, Float values" do it "yields Float values incremented by 1 and less than end when not passed a step" do (-2...2.0).step { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0]) + ScratchPad.recorded.should.eql?([-2.0, -1.0, 0.0, 1.0]) end it "yields Float values incremented by an Integer step" do (-5...5.0).step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0]) + ScratchPad.recorded.should.eql?([-5.0, -3.0, -1.0, 1.0, 3.0]) end it "yields an Float and then Float values incremented by a Float step" do (-1...1.0).step(0.5) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5]) + ScratchPad.recorded.should.eql?([-1.0, -0.5, 0.0, 0.5]) end end describe "and Float, Integer values" do it "yields Float values incremented by 1 and less than end when not passed a step" do (-2.0...2).step { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0]) + ScratchPad.recorded.should.eql?([-2.0, -1.0, 0.0, 1.0]) end it "yields Float values incremented by an Integer step" do (-5.0...5).step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0]) + ScratchPad.recorded.should.eql?([-5.0, -3.0, -1.0, 1.0, 3.0]) end it "yields Float values incremented by a Float step" do (-1.0...1).step(0.5) { |x| ScratchPad << x } - ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5]) + ScratchPad.recorded.should.eql?([-1.0, -0.5, 0.0, 0.5]) end end describe "and String values" do - it "yields String values incremented by #succ and less than or equal to end when not passed a step" do - ("A"..."E").step { |x| ScratchPad << x } - ScratchPad.recorded.should == ["A", "B", "C", "D"] - end + ruby_version_is ""..."3.4" do + it "yields String values incremented by #succ and less than or equal to end when not passed a step" do + ("A"..."E").step { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "B", "C", "D"] + end - it "yields String values incremented by #succ called Integer step times" do - ("A"..."G").step(2) { |x| ScratchPad << x } - ScratchPad.recorded.should == ["A", "C", "E"] + it "yields String values incremented by #succ called Integer step times" do + ("A"..."G").step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "C", "E"] + end + + it "raises a TypeError when passed a Float step" do + -> { ("A"..."G").step(2.0) { } }.should.raise(TypeError) + end end - it "raises a TypeError when passed a Float step" do - -> { ("A"..."G").step(2.0) { } }.should raise_error(TypeError) + ruby_version_is "3.4" do + it "yields String values adjusted by step and less than or equal to end" do + ("A"..."AAA").step("A") { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "AA"] + end + + it "raises a TypeError when passed an incompatible type step" do + -> { ("A".."G").step([]) { } }.should.raise(TypeError) + end end end end - ruby_version_is "2.6" do - describe "with an endless range" do - describe "and Integer values" do - it "yield Integer values incremented by 1 when not passed a step" do - eval("(-2..)").step { |x| break if x > 2; ScratchPad << x } - ScratchPad.recorded.should eql([-2, -1, 0, 1, 2]) + describe "with an endless range" do + describe "and Integer values" do + it "yield Integer values incremented by 1 when not passed a step" do + (-2..).step { |x| break if x > 2; ScratchPad << x } + ScratchPad.recorded.should.eql?([-2, -1, 0, 1, 2]) - ScratchPad.record [] - eval("(-2...)").step { |x| break if x > 2; ScratchPad << x } - ScratchPad.recorded.should eql([-2, -1, 0, 1, 2]) - end + ScratchPad.record [] + (-2...).step { |x| break if x > 2; ScratchPad << x } + ScratchPad.recorded.should.eql?([-2, -1, 0, 1, 2]) + end - it "yields Integer values incremented by an Integer step" do - eval("(-5..)").step(2) { |x| break if x > 3; ScratchPad << x } - ScratchPad.recorded.should eql([-5, -3, -1, 1, 3]) + it "yields Integer values incremented by an Integer step" do + (-5..).step(2) { |x| break if x > 3; ScratchPad << x } + ScratchPad.recorded.should.eql?([-5, -3, -1, 1, 3]) - ScratchPad.record [] - eval("(-5...)").step(2) { |x| break if x > 3; ScratchPad << x } - ScratchPad.recorded.should eql([-5, -3, -1, 1, 3]) - end + ScratchPad.record [] + (-5...).step(2) { |x| break if x > 3; ScratchPad << x } + ScratchPad.recorded.should.eql?([-5, -3, -1, 1, 3]) + end - it "yields Float values incremented by a Float step" do - eval("(-2..)").step(1.5) { |x| break if x > 1.0; ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -0.5, 1.0]) + it "yields Float values incremented by a Float step" do + (-2..).step(1.5) { |x| break if x > 1.0; ScratchPad << x } + ScratchPad.recorded.should.eql?([-2.0, -0.5, 1.0]) - ScratchPad.record [] - eval("(-2..)").step(1.5) { |x| break if x > 1.0; ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -0.5, 1.0]) - end + ScratchPad.record [] + (-2..).step(1.5) { |x| break if x > 1.0; ScratchPad << x } + ScratchPad.recorded.should.eql?([-2.0, -0.5, 1.0]) end + end - describe "and Float values" do - it "yields Float values incremented by 1 and less than end when not passed a step" do - eval("(-2.0..)").step { |x| break if x > 1.5; ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0]) + describe "and Float values" do + it "yields Float values incremented by 1 and less than end when not passed a step" do + (-2.0..).step { |x| break if x > 1.5; ScratchPad << x } + ScratchPad.recorded.should.eql?([-2.0, -1.0, 0.0, 1.0]) - ScratchPad.record [] - eval("(-2.0...)").step { |x| break if x > 1.5; ScratchPad << x } - ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0]) - end + ScratchPad.record [] + (-2.0...).step { |x| break if x > 1.5; ScratchPad << x } + ScratchPad.recorded.should.eql?([-2.0, -1.0, 0.0, 1.0]) + end + + it "yields Float values incremented by an Integer step" do + (-5.0..).step(2) { |x| break if x > 3.5; ScratchPad << x } + ScratchPad.recorded.should.eql?([-5.0, -3.0, -1.0, 1.0, 3.0]) - it "yields Float values incremented by an Integer step" do - eval("(-5.0..)").step(2) { |x| break if x > 3.5; ScratchPad << x } - ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0]) + ScratchPad.record [] + (-5.0...).step(2) { |x| break if x > 3.5; ScratchPad << x } + ScratchPad.recorded.should.eql?([-5.0, -3.0, -1.0, 1.0, 3.0]) + end - ScratchPad.record [] - eval("(-5.0...)").step(2) { |x| break if x > 3.5; ScratchPad << x } - ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0]) - end + it "yields Float values incremented by a Float step" do + (-1.0..).step(0.5) { |x| break if x > 0.6; ScratchPad << x } + ScratchPad.recorded.should.eql?([-1.0, -0.5, 0.0, 0.5]) - it "yields Float values incremented by a Float step" do - eval("(-1.0..)").step(0.5) { |x| break if x > 0.6; ScratchPad << x } - ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5]) + ScratchPad.record [] + (-1.0...).step(0.5) { |x| break if x > 0.6; ScratchPad << x } + ScratchPad.recorded.should.eql?([-1.0, -0.5, 0.0, 0.5]) + end - ScratchPad.record [] - eval("(-1.0...)").step(0.5) { |x| break if x > 0.6; ScratchPad << x } - ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5]) - end + it "computes each value independently to avoid accumulating floating-point errors" do + result = [] + (0.0..).step(0.1) { |x| result << x; break if result.size == 20 } + expected = 20.times.map { |i| i * 0.1 + 0.0 } + result.should.eql?(expected) + end - it "handles infinite values at the start" do - eval("(-Float::INFINITY..)").step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 } - ScratchPad.recorded.should eql([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY]) + it "handles infinite values at the start" do + (-Float::INFINITY..).step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 } + ScratchPad.recorded.should.eql?([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY]) - ScratchPad.record [] - eval("(-Float::INFINITY...)").step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 } - ScratchPad.recorded.should eql([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY]) - end + ScratchPad.record [] + (-Float::INFINITY...).step(2) { |x| ScratchPad << x; break if ScratchPad.recorded.size == 3 } + ScratchPad.recorded.should.eql?([-Float::INFINITY, -Float::INFINITY, -Float::INFINITY]) end + end - describe "and String values" do - it "yields String values incremented by #succ and less than or equal to end when not passed a step" do - eval("('A'..)").step { |x| break if x > "D"; ScratchPad << x } - ScratchPad.recorded.should == ["A", "B", "C", "D"] + describe "and String values" do + it "yields String values incremented by #succ and less than or equal to end when not passed a step" do + ('A'..).step { |x| break if x > "D"; ScratchPad << x } + ScratchPad.recorded.should == ["A", "B", "C", "D"] - ScratchPad.record [] - eval("('A'...)").step { |x| break if x > "D"; ScratchPad << x } - ScratchPad.recorded.should == ["A", "B", "C", "D"] - end + ScratchPad.record [] + ('A'...).step { |x| break if x > "D"; ScratchPad << x } + ScratchPad.recorded.should == ["A", "B", "C", "D"] + end - it "yields String values incremented by #succ called Integer step times" do - eval("('A'..)").step(2) { |x| break if x > "F"; ScratchPad << x } - ScratchPad.recorded.should == ["A", "C", "E"] + it "yields String values incremented by #succ called Integer step times" do + ('A'..).step(2) { |x| break if x > "F"; ScratchPad << x } + ScratchPad.recorded.should == ["A", "C", "E"] + + ScratchPad.record [] + ('A'...).step(2) { |x| break if x > "F"; ScratchPad << x } + ScratchPad.recorded.should == ["A", "C", "E"] + end + + it "raises a TypeError when passed a Float step" do + -> { ('A'..).step(2.0) { } }.should.raise(TypeError) + -> { ('A'...).step(2.0) { } }.should.raise(TypeError) + end + + ruby_version_is "3.4" do + it "yields String values adjusted by step" do + ('A'..).step("A") { |x| break if x > "AAA"; ScratchPad << x } + ScratchPad.recorded.should == ["A", "AA", "AAA"] ScratchPad.record [] - eval("('A'...)").step(2) { |x| break if x > "F"; ScratchPad << x } - ScratchPad.recorded.should == ["A", "C", "E"] + ('A'...).step("A") { |x| break if x > "AAA"; ScratchPad << x } + ScratchPad.recorded.should == ["A", "AA", "AAA"] end - it "raises a TypeError when passed a Float step" do - -> { eval("('A'..)").step(2.0) { } }.should raise_error(TypeError) - -> { eval("('A'...)").step(2.0) { } }.should raise_error(TypeError) + it "raises a TypeError when passed an incompatible type step" do + -> { ('A'..).step([]) { } }.should.raise(TypeError) + -> { ('A'...).step([]) { } }.should.raise(TypeError) end end end end describe "when no block is given" do - ruby_version_is "3.0" do - it "raises an ArgumentError if step is 0" do - -> { (-1..1).step(0) }.should raise_error(ArgumentError) - end + it "raises an ArgumentError if step is 0" do + -> { (-1..1).step(0) }.should.raise(ArgumentError) end describe "returned Enumerator" do describe "size" do - ruby_version_is ""..."3.0" do + ruby_version_is ""..."3.4" do it "raises a TypeError if step does not respond to #to_int" do obj = mock("Range#step non-integer") - enum = (1..2).step(obj) - -> { enum.size }.should raise_error(TypeError) + -> { (1..2).step(obj) }.should.raise(TypeError) end it "raises a TypeError if #to_int does not return an Integer" do obj = mock("Range#step non-integer") obj.should_receive(:to_int).and_return("1") - enum = (1..2).step(obj) - - -> { enum.size }.should raise_error(TypeError) + -> { (1..2).step(obj) }.should.raise(TypeError) end end - ruby_version_is "3.0" do - it "raises a TypeError if step does not respond to #to_int" do - obj = mock("Range#step non-integer") - -> { (1..2).step(obj) }.should raise_error(TypeError) - end - - it "raises a TypeError if #to_int does not return an Integer" do + ruby_version_is "3.4" do + it "does not raise if step is incompatible" do obj = mock("Range#step non-integer") - obj.should_receive(:to_int).and_return("1") - -> { (1..2).step(obj) }.should raise_error(TypeError) - end - end - - ruby_version_is ""..."2.6" do - it "raises an ArgumentError if step is 0" do - enum = (-1..1).step(0) - -> { enum.size }.should raise_error(ArgumentError) - end - - it "raises an ArgumentError if step is 0.0" do - enum = (-1..1).step(0.0) - -> { enum.size }.should raise_error(ArgumentError) - end - - it "raises an ArgumentError if step is negative" do - enum = (-1..1).step(-2) - -> { enum.size }.should raise_error(ArgumentError) - end - end - - ruby_version_is "2.6"..."3.0" do - it "returns Float::INFINITY for zero step" do - (-1..1).step(0).size.should == Float::INFINITY - (-1..1).step(0.0).size.should == Float::INFINITY + -> { (1..2).step(obj) }.should_not.raise end end @@ -445,11 +585,9 @@ describe "Range#step" do (-5...5).step(2).size.should == 5 end - ruby_version_is "2.6" do - it "returns the ceil of range size divided by the number of steps even if step is negative" do - (-1..1).step(-1).size.should == 0 - (1..-1).step(-1).size.should == 3 - end + it "returns the ceil of range size divided by the number of steps even if step is negative" do + (-1..1).step(-1).size.should == 0 + (1..-1).step(-1).size.should == 3 end it "returns the correct number of steps when one of the arguments is a float" do @@ -469,42 +607,115 @@ describe "Range#step" do (-2...2.0).step.size.should == 4 (-2.0...2).step.size.should == 4 (1.0...6.4).step(1.8).size.should == 3 - (1.0...55.6).step(18.2).size.should == 3 end - it "returns nil with begin and end are String" do - ("A".."E").step(2).size.should == nil - ("A"..."E").step(2).size.should == nil - ("A".."E").step.size.should == nil - ("A"..."E").step.size.should == nil + ruby_version_is ""..."3.4" do + it "returns nil with begin and end are String" do + ("A".."E").step(2).size.should == nil + ("A"..."E").step(2).size.should == nil + ("A".."E").step.size.should == nil + ("A"..."E").step.size.should == nil + end + + it "return nil and not raises a TypeError if the first element does not respond to #succ" do + obj = mock("Range#step non-comparable") + obj.should_receive(:<=>).with(obj).and_return(1) + enum = (obj..obj).step + -> { enum.size }.should_not.raise + enum.size.should == nil + end end - it "return nil and not raises a TypeError if the first element does not respond to #succ" do - obj = mock("Range#step non-comparable") - obj.should_receive(:<=>).with(obj).and_return(1) - enum = (obj..obj).step - -> { enum.size }.should_not raise_error - enum.size.should == nil + ruby_version_is "3.4" do + it "returns nil with begin and end are String" do + ("A".."E").step("A").size.should == nil + ("A"..."E").step("A").size.should == nil + end + + it "return nil and not raises a TypeError if the first element is not of compatible type" do + obj = mock("Range#step non-comparable") + obj.should_receive(:<=>).with(obj).and_return(1) + enum = (obj..obj).step(obj) + -> { enum.size }.should_not.raise + enum.size.should == nil + end end end + # We use .take below to ensure the enumerator works + # because that's an Enumerable method and so it uses the Enumerator behavior + # not just a method overridden in Enumerator::ArithmeticSequence. describe "type" do - ruby_version_is ""..."2.6" do - it "returns an instance of Enumerator" do - (1..10).step.class.should == Enumerator + context "when both begin and end are numerics" do + it "returns an instance of Enumerator::ArithmeticSequence" do + (1..10).step.class.should == Enumerator::ArithmeticSequence + (1..10).step(3).take(4).should == [1, 4, 7, 10] + end + end + + context "when begin is not defined and end is numeric" do + it "returns an instance of Enumerator::ArithmeticSequence" do + (..10).step.class.should == Enumerator::ArithmeticSequence end end - ruby_version_is "2.6" do - context "when both begin and end are numerics" do - it "returns an instance of Enumerator::ArithmeticSequence" do - (1..10).step.class.should == Enumerator::ArithmeticSequence + context "when range is endless" do + it "returns an instance of Enumerator::ArithmeticSequence when begin is numeric" do + (1..).step.class.should == Enumerator::ArithmeticSequence + (1..).step(2).take(3).should == [1, 3, 5] + end + + ruby_version_is ""..."3.4" do + it "returns an instance of Enumerator when begin is not numeric" do + ("a"..).step.class.should == Enumerator + ("a"..).step(2).take(3).should == %w[a c e] + end + end + + ruby_version_is "3.4" do + it "returns an instance of Enumerator when begin is not numeric" do + ("a"..).step("a").class.should == Enumerator + ("a"..).step("a").take(3).should == %w[a aa aaa] + end + end + end + + context "when range is beginless and endless" do + ruby_version_is ""..."3.4" do + it "returns an instance of Enumerator" do + Range.new(nil, nil).step.class.should == Enumerator + end + end + + ruby_version_is "3.4" do + it "raises an ArgumentError" do + -> { Range.new(nil, nil).step(1) }.should.raise(ArgumentError, + "#step for non-numeric beginless ranges is meaningless") end end + end - context "when begin and end are not numerics" do + context "when range is beginless and finite" do + ruby_version_is "3.4" do + it "raises an ArgumentError if step is non-numeric" do + -> { (..10).step("a") }.should.raise(ArgumentError, + "#step for non-numeric beginless ranges is meaningless") + end + end + end + + context "when begin and end are not numerics" do + ruby_version_is ""..."3.4" do it "returns an instance of Enumerator" do ("a".."z").step.class.should == Enumerator + ("a".."z").step(3).take(4).should == %w[a d g j] + end + end + + ruby_version_is "3.4" do + it "returns an instance of Enumerator" do + ("a".."z").step("a").class.should == Enumerator + ("a".."z").step("a").take(4).should == %w[a aa aaa aaaa] end end end diff --git a/spec/ruby/core/range/to_a_spec.rb b/spec/ruby/core/range/to_a_spec.rb index 08f50e4d9e..6221ae5f71 100644 --- a/spec/ruby/core/range/to_a_spec.rb +++ b/spec/ruby/core/range/to_a_spec.rb @@ -6,7 +6,7 @@ describe "Range#to_a" do ('A'..'D').to_a.should == ['A','B','C','D'] ('A'...'D').to_a.should == ['A','B','C'] (0xfffd...0xffff).to_a.should == [0xfffd,0xfffe] - -> { (0.5..2.4).to_a }.should raise_error(TypeError) + -> { (0.5..2.4).to_a }.should.raise(TypeError) end it "returns empty array for descending-ordered" do @@ -16,6 +16,11 @@ describe "Range#to_a" do (0xffff...0xfffd).to_a.should == [] end + it "works with Ranges of 64-bit integers" do + large = 1 << 40 + (large..large+1).to_a.should == [1099511627776, 1099511627777] + end + it "works with Ranges of Symbols" do (:A..:z).to_a.size.should == 58 end @@ -24,15 +29,11 @@ describe "Range#to_a" do ('Σ'..'Ω').to_a.should == ["Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω"] end - ruby_version_is "2.6" do - it "throws an exception for endless ranges" do - -> { eval("(1..)").to_a }.should raise_error(RangeError) - end + it "throws an exception for endless ranges" do + -> { eval("(1..)").to_a }.should.raise(RangeError) end - ruby_version_is "2.7" do - it "throws an exception for beginless ranges" do - -> { eval("(..1)").to_a }.should raise_error(TypeError) - end + it "throws an exception for beginless ranges" do + -> { (..1).to_a }.should.raise(TypeError) end end diff --git a/spec/ruby/core/range/to_s_spec.rb b/spec/ruby/core/range/to_s_spec.rb index ccbc5d8e7e..460c330912 100644 --- a/spec/ruby/core/range/to_s_spec.rb +++ b/spec/ruby/core/range/to_s_spec.rb @@ -11,24 +11,13 @@ describe "Range#to_s" do (0.5..2.4).to_s.should == "0.5..2.4" end - ruby_version_is "2.6" do - it "can show endless ranges" do - eval("(1..)").to_s.should == "1.." - eval("(1.0...)").to_s.should == "1.0..." - end + it "can show endless ranges" do + eval("(1..)").to_s.should == "1.." + eval("(1.0...)").to_s.should == "1.0..." end - ruby_version_is ''...'2.7' do - it "returns a tainted string if either end is tainted" do - (("a".taint)..."c").to_s.tainted?.should be_true - ("a"...("c".taint)).to_s.tainted?.should be_true - ("a"..."c").taint.to_s.tainted?.should be_true - end - - it "returns a untrusted string if either end is untrusted" do - (("a".untrust)..."c").to_s.untrusted?.should be_true - ("a"...("c".untrust)).to_s.untrusted?.should be_true - ("a"..."c").untrust.to_s.untrusted?.should be_true - end + it "can show beginless ranges" do + (..1).to_s.should == "..1" + (...1.0).to_s.should == "...1.0" end end diff --git a/spec/ruby/core/range/to_set_spec.rb b/spec/ruby/core/range/to_set_spec.rb new file mode 100644 index 0000000000..ac81d2cc4b --- /dev/null +++ b/spec/ruby/core/range/to_set_spec.rb @@ -0,0 +1,54 @@ +require_relative '../../spec_helper' +require_relative '../enumerable/fixtures/classes' + +describe "Range#to_set" do + it "returns a new Set created from self" do + (1..4).to_set.should == Set[1, 2, 3, 4] + (1...4).to_set.should == Set[1, 2, 3] + end + + it "passes down passed blocks" do + (1..3).to_set { |x| x * x }.should == Set[1, 4, 9] + end + + it "raises a TypeError for a beginningless range" do + -> { + (..0).to_set + }.should.raise(TypeError, "can't iterate from NilClass") + end + + ruby_version_is "4.0" do + it "raises a RangeError if the range is endless" do + -> { (1..).to_set }.should.raise(RangeError, "cannot convert endless range to a set") + -> { (1...).to_set }.should.raise(RangeError, "cannot convert endless range to a set") + end + end + + context "given positional arguments" do + ruby_version_is ""..."4.0" do + it "instantiates an object of provided as the first argument set class" do + set = (1..3).to_set(EnumerableSpecs::SetSubclass) + set.should.is_a?(EnumerableSpecs::SetSubclass) + set.to_a.sort.should == [1, 2, 3] + end + end + + ruby_version_is "4.0"..."4.1" do + it "instantiates an object of provided as the first argument set class and warns" do + -> { + set = (1..3).to_set(EnumerableSpecs::SetSubclass) + set.should.is_a?(EnumerableSpecs::SetSubclass) + set.to_a.sort.should == [1, 2, 3] + }.should complain(/warning: passing arguments to Enumerable#to_set is deprecated/) + end + end + + ruby_version_is "4.1" do + it "does not accept any positional argument" do + -> { + (1..3).to_set(EnumerableSpecs::SetSubclass) + }.should.raise(ArgumentError, "wrong number of arguments (given 1, expected 0)") + end + end + end +end |
