summaryrefslogtreecommitdiff
path: root/spec/ruby/core/range
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/range')
-rw-r--r--spec/ruby/core/range/bsearch_spec.rb345
-rw-r--r--spec/ruby/core/range/case_compare_spec.rb27
-rw-r--r--spec/ruby/core/range/clone_spec.rb26
-rw-r--r--spec/ruby/core/range/count_spec.rb12
-rw-r--r--spec/ruby/core/range/cover_spec.rb7
-rw-r--r--spec/ruby/core/range/dup_spec.rb14
-rw-r--r--spec/ruby/core/range/each_spec.rb45
-rw-r--r--spec/ruby/core/range/equal_value_spec.rb8
-rw-r--r--spec/ruby/core/range/exclude_end_spec.rb20
-rw-r--r--spec/ruby/core/range/first_spec.rb12
-rw-r--r--spec/ruby/core/range/frozen_spec.rb25
-rw-r--r--spec/ruby/core/range/hash_spec.rb10
-rw-r--r--spec/ruby/core/range/include_spec.rb6
-rw-r--r--spec/ruby/core/range/initialize_spec.rb20
-rw-r--r--spec/ruby/core/range/inspect_spec.rb19
-rw-r--r--spec/ruby/core/range/last_spec.rb16
-rw-r--r--spec/ruby/core/range/max_spec.rb37
-rw-r--r--spec/ruby/core/range/member_spec.rb2
-rw-r--r--spec/ruby/core/range/min_spec.rb13
-rw-r--r--spec/ruby/core/range/minmax_spec.rb130
-rw-r--r--spec/ruby/core/range/new_spec.rb68
-rw-r--r--spec/ruby/core/range/overlap_spec.rb89
-rw-r--r--spec/ruby/core/range/percent_spec.rb24
-rw-r--r--spec/ruby/core/range/reverse_each_spec.rb103
-rw-r--r--spec/ruby/core/range/shared/cover.rb140
-rw-r--r--spec/ruby/core/range/shared/cover_and_include.rb28
-rw-r--r--spec/ruby/core/range/shared/equal_value.rb6
-rw-r--r--spec/ruby/core/range/shared/include.rb2
-rw-r--r--spec/ruby/core/range/size_spec.rb81
-rw-r--r--spec/ruby/core/range/step_spec.rb482
-rw-r--r--spec/ruby/core/range/to_a_spec.rb19
-rw-r--r--spec/ruby/core/range/to_s_spec.rb14
-rw-r--r--spec/ruby/core/range/to_set_spec.rb55
33 files changed, 1601 insertions, 304 deletions
diff --git a/spec/ruby/core/range/bsearch_spec.rb b/spec/ruby/core/range/bsearch_spec.rb
index 399bd49623..5254ab756c 100644
--- a/spec/ruby/core/range/bsearch_spec.rb
+++ b/spec/ruby/core/range/bsearch_spec.rb
@@ -9,22 +9,30 @@ describe "Range#bsearch" do
it_behaves_like :enumeratorized_with_unknown_size, :bsearch, (1..3)
it "raises a TypeError if the block returns an Object" do
- lambda { (0..1).bsearch { Object.new } }.should raise_error(TypeError)
+ -> { (0..1).bsearch { Object.new } }.should raise_error(TypeError, "wrong argument type Object (must be numeric, true, false or nil)")
end
- it "raises a TypeError if the block returns a String" do
- lambda { (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_error(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_error(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
- lambda { r.bsearch { true } }.should raise_error(TypeError)
+ -> { r.bsearch { true } }.should raise_error(TypeError, "can't do binary search for MockObject")
end
it "raises a TypeError if the Range has String values" do
- lambda { ("a".."e").bsearch { true } }.should raise_error(TypeError)
+ -> { ("a".."e").bsearch { true } }.should raise_error(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_error(TypeError, "can't do binary search for String")
end
context "with Integer values" do
@@ -81,6 +89,23 @@ describe "Range#bsearch" do
[1, 2].should include(result)
end
end
+
+ it "returns nil for empty ranges" do
+ (0...0).bsearch { true }.should == nil
+ (0...0).bsearch { false }.should == nil
+ (0...0).bsearch { 1 }.should == nil
+ (0...0).bsearch { 0 }.should == nil
+ (0...0).bsearch { -1 }.should == nil
+
+ (4..2).bsearch { true }.should == nil
+ (4..2).bsearch { 1 }.should == nil
+ (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
@@ -94,13 +119,46 @@ describe "Range#bsearch" do
end
it "returns minimum element if the block returns true for every element" do
- (-0.2..4.8).bsearch { |x| x < 4 }.should == -0.2
+ (-0.2..4.8).bsearch { |x| x < 5 }.should == -0.2
end
it "returns the smallest element for which block returns true" do
(0..4.2).bsearch { |x| x >= 2 }.should == 2
(-1.2..4.3).bsearch { |x| x >= 1 }.should == 1
end
+
+ it "returns a boundary element if appropriate" do
+ (1.0..3.0).bsearch { |x| x >= 3.0 }.should == 3.0
+ (1.0...3.0).bsearch { |x| x >= 3.0.prev_float }.should == 3.0.prev_float
+ (1.0..3.0).bsearch { |x| x >= 1.0 }.should == 1.0
+ (1.0...3.0).bsearch { |x| x >= 1.0 }.should == 1.0
+ end
+
+ it "works with infinity bounds" do
+ inf = Float::INFINITY
+ (0..inf).bsearch { |x| x == inf }.should == inf
+ (0...inf).bsearch { |x| x == inf }.should == nil
+ (-inf..0).bsearch { |x| x != -inf }.should == -Float::MAX
+ (-inf...0).bsearch { |x| x != -inf }.should == -Float::MAX
+ (inf..inf).bsearch { |x| true }.should == inf
+ (inf...inf).bsearch { |x| true }.should == nil
+ (-inf..-inf).bsearch { |x| true }.should == -inf
+ (-inf...-inf).bsearch { |x| true }.should == nil
+ (inf..0).bsearch { true }.should == nil
+ (inf...0).bsearch { true }.should == nil
+ (0..-inf).bsearch { true }.should == nil
+ (0...-inf).bsearch { true }.should == nil
+ (inf..-inf).bsearch { true }.should == nil
+ (inf...-inf).bsearch { true }.should == nil
+ (0..inf).bsearch { |x| x >= 3 }.should == 3.0
+ (0...inf).bsearch { |x| x >= 3 }.should == 3.0
+ (-inf..0).bsearch { |x| x >= -3 }.should == -3.0
+ (-inf...0).bsearch { |x| x >= -3 }.should == -3.0
+ (-inf..inf).bsearch { |x| x >= 3 }.should == 3.0
+ (-inf...inf).bsearch { |x| x >= 3 }.should == 3.0
+ (0..inf).bsearch { |x| x >= Float::MAX }.should == Float::MAX
+ (0...inf).bsearch { |x| x >= Float::MAX }.should == Float::MAX
+ end
end
context "with a block returning negative, zero, positive numbers" do
@@ -110,7 +168,6 @@ describe "Range#bsearch" do
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
-
end
it "returns nil if the block never returns zero" do
@@ -130,8 +187,280 @@ describe "Range#bsearch" do
it "returns an element at an index for which block returns 0" do
result = (0.1..4.9).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
result.should >= 1
- result.should <= 2
+ result.should <= 3
+ end
+
+ it "returns an element at an index for which block returns 0 (small numbers)" do
+ result = (0.1..0.3).bsearch { |x| x < 0.1 ? 1 : x > 0.3 ? -1 : 0 }
+ result.should >= 0.1
+ result.should <= 0.3
+ end
+
+ it "returns a boundary element if appropriate" do
+ (1.0..3.0).bsearch { |x| 3.0 - x }.should == 3.0
+ (1.0...3.0).bsearch { |x| 3.0.prev_float - x }.should == 3.0.prev_float
+ (1.0..3.0).bsearch { |x| 1.0 - x }.should == 1.0
+ (1.0...3.0).bsearch { |x| 1.0 - x }.should == 1.0
+ end
+
+ it "works with infinity bounds" do
+ inf = Float::INFINITY
+ (0..inf).bsearch { |x| x == inf ? 0 : 1 }.should == inf
+ (0...inf).bsearch { |x| x == inf ? 0 : 1 }.should == nil
+ (-inf...0).bsearch { |x| x == -inf ? 0 : -1 }.should == -inf
+ (-inf..0).bsearch { |x| x == -inf ? 0 : -1 }.should == -inf
+ (inf..inf).bsearch { 0 }.should == inf
+ (inf...inf).bsearch { 0 }.should == nil
+ (-inf..-inf).bsearch { 0 }.should == -inf
+ (-inf...-inf).bsearch { 0 }.should == nil
+ (inf..0).bsearch { 0 }.should == nil
+ (inf...0).bsearch { 0 }.should == nil
+ (0..-inf).bsearch { 0 }.should == nil
+ (0...-inf).bsearch { 0 }.should == nil
+ (inf..-inf).bsearch { 0 }.should == nil
+ (inf...-inf).bsearch { 0 }.should == nil
+ (-inf..inf).bsearch { |x| 3 - x }.should == 3.0
+ (-inf...inf).bsearch { |x| 3 - x }.should == 3.0
+ (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
+
+ 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
+ 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
+
+ it "returns nil if the block never returns zero" do
+ eval("(0..)").bsearch { |x| x > 5 ? -1 : 1 }.should be_nil
+ end
+
+ it "accepts -Float::INFINITY from the block" do
+ eval("(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..)").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
+ 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 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
+ 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 be_nil
+ end
+
+ it "returns nil if the block never returns zero" do
+ (..0).bsearch { |x| x > 5 ? -1 : 1 }.should be_nil
+ end
+
+ it "accepts Float::INFINITY from the block" do
+ (..0).bsearch { |x| Float::INFINITY }.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 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 "returns enumerator when block not passed" do
+ (..10).bsearch.kind_of?(Enumerator).should == true
+ 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
+ (..-0.1).bsearch { |x| x > 0.0 }.should be_nil
+ (...-0.1).bsearch { |x| x > 0.0 }.should be_nil
+ end
+
+ it "returns nil if the block returns nil for every element" do
+ (..-0.1).bsearch { |x| nil }.should be_nil
+ (...-0.1).bsearch { |x| nil }.should be_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 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 be_nil
+ (...5.0).bsearch { |x| -1 }.should be_nil
+ end
+
+ it "returns nil if the block returns greater than zero for every element" do
+ (..1.1).bsearch { |x| 1 }.should be_nil
+ (...1.1).bsearch { |x| 1 }.should be_nil
+ end
+
+ it "returns nil if the block never returns zero" do
+ (..6.3).bsearch { |x| x < 2 ? 1 : -1 }.should be_nil
+ end
+
+ it "accepts (+/-)Float::INFINITY from the block" do
+ (..5.0).bsearch { |x| Float::INFINITY }.should be_nil
+ (..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 = (..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 37d8cc4677..c9b253f0a5 100644
--- a/spec/ruby/core/range/case_compare_spec.rb
+++ b/spec/ruby/core/range/case_compare_spec.rb
@@ -3,26 +3,17 @@ 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)
-
- lambda 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
+ it_behaves_like :range_cover_and_include, :===
+ it_behaves_like :range_cover, :===
+
+ ruby_bug "#19533", ""..."3.3" do
+ it "returns true on any value if begin and end are both nil" do
+ (nil..nil).should === 1
end
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
new file mode 100644
index 0000000000..24d4a9caf3
--- /dev/null
+++ b/spec/ruby/core/range/count_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+describe "Range#count" 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
+ (...'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 29c0e0bfa8..c05bb50614 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'
@@ -6,4 +6,9 @@ require_relative 'shared/cover'
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 be_true
+ end
end
diff --git a/spec/ruby/core/range/dup_spec.rb b/spec/ruby/core/range/dup_spec.rb
index d1c029c6b7..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.exclude_end?.should == false
+ copy.should_not.exclude_end?
+ copy.should_not.equal?(original)
copy = ("a"..."z").dup
copy.begin.should == "a"
copy.end.should == "z"
- copy.exclude_end?.should == true
+ 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 c5253dafd9..f10330d61d 100644
--- a/spec/ruby/core/range/each_spec.rb
+++ b/spec/ruby/core/range/each_spec.rb
@@ -32,13 +32,43 @@ describe "Range#each" do
a.should == [x, y]
end
+ it "works for non-ASCII ranges" do
+ a = []
+ ('Σ'..'Ω').each { |i| a << i }
+ a.should == ["Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω"]
+ 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
+
+ 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_error(TypeError)
+ end
+
it "raises a TypeError if the first element does not respond to #succ" do
- lambda { (0.5..2.4).each { |i| i } }.should raise_error(TypeError)
+ -> { (0.5..2.4).each { |i| i } }.should raise_error(TypeError)
b = mock('x')
(a = mock('1')).should_receive(:<=>).with(b).and_return(1)
- lambda { (a..b).each { |i| i } }.should raise_error(TypeError)
+ -> { (a..b).each { |i| i } }.should raise_error(TypeError)
end
it "returns self" do
@@ -52,9 +82,14 @@ describe "Range#each" do
enum.to_a.should == [1, 2, 3]
end
- it "raises a TypeError if the first element is a Time object" do
- t = Time.now
- lambda { (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/equal_value_spec.rb b/spec/ruby/core/range/equal_value_spec.rb
index 889557fc2a..83dcf5cec8 100644
--- a/spec/ruby/core/range/equal_value_spec.rb
+++ b/spec/ruby/core/range/equal_value_spec.rb
@@ -7,4 +7,12 @@ describe "Range#==" do
it "returns true if the endpoints are ==" do
(0..1).should == (0..1.0)
end
+
+ 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 beginless ranges" do
+ (...10).should == (...10)
+ end
end
diff --git a/spec/ruby/core/range/exclude_end_spec.rb b/spec/ruby/core/range/exclude_end_spec.rb
index a209603d18..c4006fea78 100644
--- a/spec/ruby/core/range/exclude_end_spec.rb
+++ b/spec/ruby/core/range/exclude_end_spec.rb
@@ -2,18 +2,18 @@ require_relative '../../spec_helper'
describe "Range#exclude_end?" do
it "returns false if the range does not exclude the end value" do
- (-2..2).exclude_end?.should == false
- ('A'..'B').exclude_end?.should == false
- (0.5..2.4).exclude_end?.should == false
- (0xfffd..0xffff).exclude_end?.should == false
- Range.new(0, 1).exclude_end?.should == false
+ (-2..2).should_not.exclude_end?
+ ('A'..'B').should_not.exclude_end?
+ (0.5..2.4).should_not.exclude_end?
+ (0xfffd..0xffff).should_not.exclude_end?
+ Range.new(0, 1).should_not.exclude_end?
end
it "returns true if the range excludes the end value" do
- (0...5).exclude_end?.should == true
- ('A'...'B').exclude_end?.should == true
- (0.5...2.4).exclude_end?.should == true
- (0xfffd...0xffff).exclude_end?.should == true
- Range.new(0, 1, true).exclude_end?.should == true
+ (0...5).should.exclude_end?
+ ('A'...'B').should.exclude_end?
+ (0.5...2.4).should.exclude_end?
+ (0xfffd...0xffff).should.exclude_end?
+ Range.new(0, 1, true).should.exclude_end?
end
end
diff --git a/spec/ruby/core/range/first_spec.rb b/spec/ruby/core/range/first_spec.rb
index b7ae72ec6c..2af935f439 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
- lambda { (0..2).first(-1) }.should raise_error(ArgumentError)
+ -> { (0..2).first(-1) }.should raise_error(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")
- lambda { (2..3).first(obj) }.should raise_error(TypeError)
+ -> { (2..3).first(obj) }.should raise_error(TypeError)
end
it "truncates the value when passed a Float" do
@@ -40,10 +40,14 @@ describe "Range#first" do
end
it "raises a TypeError when passed nil" do
- lambda { (2..3).first(nil) }.should raise_error(TypeError)
+ -> { (2..3).first(nil) }.should raise_error(TypeError)
end
it "raises a TypeError when passed a String" do
- lambda { (2..3).first("1") }.should raise_error(TypeError)
+ -> { (2..3).first("1") }.should raise_error(TypeError)
+ end
+
+ it "raises a RangeError when called on an beginless range" do
+ -> { (..1).first }.should raise_error(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 90aeee7890..4f2681e86e 100644
--- a/spec/ruby/core/range/hash_spec.rb
+++ b/spec/ruby/core/range/hash_spec.rb
@@ -14,11 +14,11 @@ describe "Range#hash" do
(0..10).hash.should_not == (0...10).hash
end
- it "generates a Fixnum for the hash value" do
- (0..0).hash.should be_an_instance_of(Fixnum)
- (0..1).hash.should be_an_instance_of(Fixnum)
- (0...10).hash.should be_an_instance_of(Fixnum)
- (0..10).hash.should be_an_instance_of(Fixnum)
+ 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)
end
end
diff --git a/spec/ruby/core/range/include_spec.rb b/spec/ruby/core/range/include_spec.rb
index b2c7a54545..449e18985b 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 be_false
+ end
end
diff --git a/spec/ruby/core/range/initialize_spec.rb b/spec/ruby/core/range/initialize_spec.rb
index 53f6485954..c653caf0c6 100644
--- a/spec/ruby/core/range/initialize_spec.rb
+++ b/spec/ruby/core/range/initialize_spec.rb
@@ -10,32 +10,32 @@ describe "Range#initialize" do
end
it "initializes correctly the Range object when given 2 arguments" do
- lambda { @range.send(:initialize, 0, 1) }.should_not raise_error
+ -> { @range.send(:initialize, 0, 1) }.should_not raise_error
end
it "initializes correctly the Range object when given 3 arguments" do
- lambda { @range.send(:initialize, 0, 1, true) }.should_not raise_error
+ -> { @range.send(:initialize, 0, 1, true) }.should_not raise_error
end
it "raises an ArgumentError if passed without or with only one argument" do
- lambda { @range.send(:initialize) }.should raise_error(ArgumentError)
- lambda { @range.send(:initialize, 1) }.should raise_error(ArgumentError)
+ -> { @range.send(:initialize) }.should raise_error(ArgumentError)
+ -> { @range.send(:initialize, 1) }.should raise_error(ArgumentError)
end
it "raises an ArgumentError if passed with four or more arguments" do
- lambda { @range.send(:initialize, 1, 3, 5, 7) }.should raise_error(ArgumentError)
- lambda { @range.send(:initialize, 1, 3, 5, 7, 9) }.should raise_error(ArgumentError)
+ -> { @range.send(:initialize, 1, 3, 5, 7) }.should raise_error(ArgumentError)
+ -> { @range.send(:initialize, 1, 3, 5, 7, 9) }.should raise_error(ArgumentError)
end
- it "raises a NameError if called on an already initialized Range" do
- lambda { (0..1).send(:initialize, 1, 3) }.should raise_error(NameError)
- lambda { (0..1).send(:initialize, 1, 3, true) }.should raise_error(NameError)
+ 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 an ArgumentError if arguments don't respond to <=>" do
o1 = Object.new
o2 = Object.new
- lambda { @range.send(:initialize, o1, o2) }.should raise_error(ArgumentError)
+ -> { @range.send(:initialize, o1, o2) }.should raise_error(ArgumentError)
end
end
diff --git a/spec/ruby/core/range/inspect_spec.rb b/spec/ruby/core/range/inspect_spec.rb
index 3c130812d0..072de123b7 100644
--- a/spec/ruby/core/range/inspect_spec.rb
+++ b/spec/ruby/core/range/inspect_spec.rb
@@ -12,15 +12,18 @@ describe "Range#inspect" do
(0.5..2.4).inspect.should == "0.5..2.4"
end
- 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
+ it "works for endless ranges" do
+ eval("(1..)").inspect.should == "1.."
+ eval("(0.1...)").inspect.should == "0.1..."
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
+ it "works for beginless ranges" do
+ (..1).inspect.should == "..1"
+ (...0.1).inspect.should == "...0.1"
+ 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 8983f80cfb..82b3e2ff53 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
- lambda { (0..2).last(-1) }.should raise_error(ArgumentError)
+ -> { (0..2).last(-1) }.should raise_error(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")
- lambda { (2..3).last(obj) }.should raise_error(TypeError)
+ -> { (2..3).last(obj) }.should raise_error(TypeError)
end
it "truncates the value when passed a Float" do
@@ -40,10 +44,14 @@ describe "Range#last" do
end
it "raises a TypeError when passed nil" do
- lambda { (2..3).last(nil) }.should raise_error(TypeError)
+ -> { (2..3).last(nil) }.should raise_error(TypeError)
end
it "raises a TypeError when passed a String" do
- lambda { (2..3).last("1") }.should raise_error(TypeError)
+ -> { (2..3).last("1") }.should raise_error(TypeError)
+ end
+
+ it "raises a RangeError when called on an endless range" do
+ -> { eval("(1..)").last }.should raise_error(RangeError)
end
end
diff --git a/spec/ruby/core/range/max_spec.rb b/spec/ruby/core/range/max_spec.rb
index e6915cc450..09371f5298 100644
--- a/spec/ruby/core/range/max_spec.rb
+++ b/spec/ruby/core/range/max_spec.rb
@@ -14,7 +14,7 @@ describe "Range#max" do
end
it "raises TypeError when called on an exclusive range and a non Integer value" do
- lambda { (303.20...908.1111).max }.should raise_error(TypeError)
+ -> { (303.20...908.1111).max }.should raise_error(TypeError)
end
it "returns nil when the endpoint is less than the start point" do
@@ -43,7 +43,36 @@ describe "Range#max" do
it "raises TypeError when called on a Time...Time(excluded end point)" do
time_start = Time.now
time_end = Time.now + 1.0
- lambda { (time_start...time_end).max }.should raise_error(TypeError)
+ -> { (time_start...time_end).max }.should raise_error(TypeError)
+ end
+
+ it "raises RangeError when called on an endless range" do
+ -> { eval("(1..)").max }.should raise_error(RangeError)
+ end
+
+ it "returns the end point for beginless ranges" do
+ (..1).max.should == 1
+ (..1.0).max.should == 1.0
+ end
+
+ ruby_version_is ""..."4.0" do
+ it "raises for an exclusive beginless Integer range" do
+ -> {
+ (...1).max
+ }.should raise_error(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_error(TypeError, 'cannot exclude non Integer end value')
end
end
@@ -79,4 +108,8 @@ describe "Range#max given a block" do
('z'..'l').max {|x,y| x <=> y}.should be_nil
(5...5).max {|x,y| x <=> y}.should be_nil
end
+
+ it "raises RangeError when called with custom comparison method on an beginless range" do
+ -> { (..1).max {|a, b| a} }.should raise_error(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 424bd1dc87..89310ee589 100644
--- a/spec/ruby/core/range/min_spec.rb
+++ b/spec/ruby/core/range/min_spec.rb
@@ -38,6 +38,15 @@ describe "Range#min" do
time_end = Time.now + 1.0
(time_start...time_end).min.should equal(time_start)
end
+
+ it "returns the start point for endless ranges" do
+ eval("(1..)").min.should == 1
+ eval("(1.0...)").min.should == 1.0
+ end
+
+ it "raises RangeError when called on an beginless range" do
+ -> { (..1).min }.should raise_error(RangeError)
+ end
end
describe "Range#min given a block" do
@@ -72,4 +81,8 @@ describe "Range#min given a block" do
('z'..'l').min {|x,y| x <=> y}.should be_nil
(7...7).min {|x,y| x <=> y}.should be_nil
end
+
+ 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
end
diff --git a/spec/ruby/core/range/minmax_spec.rb b/spec/ruby/core/range/minmax_spec.rb
new file mode 100644
index 0000000000..6651ae3726
--- /dev/null
+++ b/spec/ruby/core/range/minmax_spec.rb
@@ -0,0 +1,130 @@
+require_relative '../../spec_helper'
+
+describe 'Range#minmax' do
+ before(:each) do
+ @x = mock('x')
+ @y = mock('y')
+
+ @x.should_receive(:<=>).with(@y).any_number_of_times.and_return(-1) # x < y
+ @x.should_receive(:<=>).with(@x).any_number_of_times.and_return(0) # x == x
+ @y.should_receive(:<=>).with(@x).any_number_of_times.and_return(1) # y > x
+ @y.should_receive(:<=>).with(@y).any_number_of_times.and_return(0) # y == y
+ end
+
+ describe 'on an inclusive range' do
+ it 'should raise RangeError on an endless range without iterating the range' do
+ @x.should_not_receive(:succ)
+
+ range = (@x..)
+
+ -> { range.minmax }.should raise_error(RangeError, 'cannot get the maximum of endless range')
+ end
+
+ it 'raises RangeError or ArgumentError on a beginless range' do
+ range = (..@x)
+
+ -> { 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 'should return beginning of range if beginning and end are equal without iterating the range' do
+ @x.should_not_receive(:succ)
+
+ (@x..@x).minmax.should == [@x, @x]
+ end
+
+ 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
+
+ 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
+
+ it 'should return the minimum and maximum values for a numeric range' do
+ (1..3).minmax.should == [1, 3]
+ end
+
+ 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
+
+ it 'should return the minimum and maximum values according to the provided block by iterating the range' do
+ @x.should_receive(:succ).once.and_return(@y)
+
+ (@x..@y).minmax { |x, y| - (x <=> y) }.should == [@y, @x]
+ end
+ end
+
+ describe 'on an exclusive range' do
+ it 'should raise RangeError on an endless range' do
+ @x.should_not_receive(:succ)
+ range = (@x...)
+
+ -> { range.minmax }.should raise_error(RangeError, 'cannot get the maximum of endless range')
+ end
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ it 'should return the minimum and maximum values for a numeric range' do
+ (1...3).minmax.should == [1, 2]
+ end
+
+ 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
+
+ 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 'should return the minimum and maximum values according to the provided block by iterating the range' do
+ @x.should_receive(:succ).once.and_return(@y)
+
+ (@x...@y).minmax { |x, y| - (x <=> y) }.should == [@x, @x]
+ end
+ end
+end
diff --git a/spec/ruby/core/range/new_spec.rb b/spec/ruby/core/range/new_spec.rb
index 367511742c..3cab887799 100644
--- a/spec/ruby/core/range/new_spec.rb
+++ b/spec/ruby/core/range/new_spec.rb
@@ -25,63 +25,53 @@ describe "Range.new" do
end
it "raises an ArgumentError when the given start and end can't be compared by using #<=>" do
- lambda { Range.new(1, mock('x')) }.should raise_error(ArgumentError)
- lambda { Range.new(mock('x'), mock('y')) }.should raise_error(ArgumentError)
+ -> { Range.new(1, mock('x')) }.should raise_error(ArgumentError)
+ -> { Range.new(mock('x'), mock('y')) }.should raise_error(ArgumentError)
b = mock('x')
(a = mock('nil')).should_receive(:<=>).with(b).and_return(nil)
- lambda { Range.new(a, b) }.should raise_error(ArgumentError)
+ -> { Range.new(a, b) }.should raise_error(ArgumentError)
end
- ruby_version_is "2.5" do
- it "does not rescue exception raised in #<=> when compares the given start and end" do
- b = mock('a')
- a = mock('b')
- a.should_receive(:<=>).with(b).and_raise(RangeSpecs::ComparisonError)
+ it "does not rescue exception raised in #<=> when compares the given start and end" do
+ b = mock('a')
+ a = mock('b')
+ a.should_receive(:<=>).with(b).and_raise(RangeSpecs::ComparisonError)
- -> { Range.new(a, b) }.should raise_error(RangeSpecs::ComparisonError)
- end
+ -> { Range.new(a, b) }.should raise_error(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)
- 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 "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..9b6fc13493
--- /dev/null
+++ b/spec/ruby/core/range/overlap_spec.rb
@@ -0,0 +1,89 @@
+require_relative '../../spec_helper'
+
+ruby_version_is '3.3' do
+ 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_error(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
+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..56390cc0da
--- /dev/null
+++ b/spec/ruby/core/range/reverse_each_spec.rb
@@ -0,0 +1,103 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.3" do
+ 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 be_an_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_error(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_error(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_error(TypeError, /can't iterate from Time/)
+ -> { (//..//).reverse_each { |x| x } }.should raise_error(TypeError, /can't iterate from Regexp/)
+ -> { ([]..[]).reverse_each { |x| x } }.should raise_error(TypeError, /can't iterate from Array/)
+ end
+
+ it "does not support beginningless Ranges" do
+ -> {
+ (..'a').reverse_each { |x| x }
+ }.should raise_error(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
+ 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
+end
diff --git a/spec/ruby/core/range/shared/cover.rb b/spec/ruby/core/range/shared/cover.rb
index 33d416fef5..eaefb45942 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'
@@ -90,64 +90,104 @@ describe :range_cover, shared: true do
end
end
end
+end
- 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
+describe :range_cover_subrange, shared: true 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
- 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 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 "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 be_false
+ (0..10).send(@method, (RangeSpecs::TenfoldSucc.new(0)..RangeSpecs::TenfoldSucc.new(100))).should be_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 be_true
+ (0...10).send(@method, (0...10)).should be_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 be_true
+ (0...10).send(@method, (0..10)).should be_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 be_true
+ (0..10).send(@method, (0...11)).should be_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 be_true
+ (0...10.1).send(@method, (0...10.1)).should be_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 be_true
+ (0...10.1).send(@method, (0..10.1)).should be_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 be_true
+ (0..10.1).send(@method, (0...11.1)).should be_false
end
end
+
+ it "allows self to be a beginless range" do
+ (...10).send(@method, (3..7)).should be_true
+ (...10).send(@method, (3..15)).should be_false
+
+ (..7.9).send(@method, (2.5..6.5)).should be_true
+ (..7.9).send(@method, (2.5..8.5)).should be_false
+
+ (..'i').send(@method, ('d'..'f')).should be_true
+ (..'i').send(@method, ('d'..'z')).should be_false
+ end
+
+ it "allows self to be a endless range" do
+ eval("(0...)").send(@method, (3..7)).should be_true
+ eval("(5...)").send(@method, (3..15)).should be_false
+
+ eval("(1.1..)").send(@method, (2.5..6.5)).should be_true
+ eval("(3.3..)").send(@method, (2.5..8.5)).should be_false
+
+ eval("('a'..)").send(@method, ('d'..'f')).should be_true
+ eval("('p'..)").send(@method, ('d'..'z')).should be_false
+ end
+
+ it "accepts beginless range argument" do
+ (..10).send(@method, (...10)).should be_true
+ (0..10).send(@method, (...10)).should be_false
+
+ (1.1..7.9).send(@method, (...10.5)).should be_false
+
+ ('c'..'i').send(@method, (..'i')).should be_false
+ end
+
+ it "accepts endless range argument" do
+ eval("(0..)").send(@method, eval("(0...)")).should be_true
+ (0..10).send(@method, eval("(0...)")).should be_false
+
+ (1.1..7.9).send(@method, eval("(0.8...)")).should be_false
+
+ ('c'..'i').send(@method, eval("('a'..)")).should be_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 bed2c77038..13fc5e1790 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,6 +19,27 @@ describe :range_cover_and_include, shared: true do
(0.5...2.4).send(@method, 2.4).should == false
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
rng = (1..5)
m = mock("int")
@@ -29,8 +50,8 @@ describe :range_cover_and_include, shared: true do
end
it "raises an ArgumentError without exactly one argument" do
- lambda{ (1..2).send(@method) }.should raise_error(ArgumentError)
- lambda{ (1..2).send(@method, 1, 2) }.should raise_error(ArgumentError)
+ ->{ (1..2).send(@method) }.should raise_error(ArgumentError)
+ ->{ (1..2).send(@method, 1, 2) }.should raise_error(ArgumentError)
end
it "returns true if argument is equal to the first value of the range" do
@@ -47,7 +68,6 @@ describe :range_cover_and_include, shared: true do
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}"
end
it "returns true if argument is sole element in the range" do
diff --git a/spec/ruby/core/range/shared/equal_value.rb b/spec/ruby/core/range/shared/equal_value.rb
index 9d8bb13351..363c6be558 100644
--- a/spec/ruby/core/range/shared/equal_value.rb
+++ b/spec/ruby/core/range/shared/equal_value.rb
@@ -42,4 +42,10 @@ describe :range_eql, shared: true do
b = RangeSpecs::MyRange.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5))
a.send(@method, b).should == true
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..15a0e5fb9f 100644
--- a/spec/ruby/core/range/shared/include.rb
+++ b/spec/ruby/core/range/shared/include.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb
index 09759940dd..1a3ddd197e 100644
--- a/spec/ruby/core/range/size_spec.rb
+++ b/spec/ruby/core/range/size_spec.rb
@@ -4,24 +4,85 @@ 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
+
+ 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("(0.5...)").size.should == Float::INFINITY
+ end
+
+ it 'returns nil for endless ranges if the start is not numeric' do
+ eval("([]...)").size.should == nil
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it 'raises TypeError if a range is not iterable' do
+ -> { (1.0..16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.0...16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.0..15.9).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.1..16.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (1.1..15.9).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (16.0..0.0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (Float::INFINITY..0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (-Float::INFINITY..0).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (-Float::INFINITY..Float::INFINITY).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (..1).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (...0.5).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (..nil).size }.should raise_error(TypeError, /can't iterate from/)
+ -> { (...'o').size }.should raise_error(TypeError, /can't iterate from/)
+ -> { eval("(0.5...)").size }.should raise_error(TypeError, /can't iterate from/)
+ -> { eval("([]...)").size }.should raise_error(TypeError, /can't iterate from/)
+ end
end
it "returns nil if first and last are not Numeric" do
diff --git a/spec/ruby/core/range/step_spec.rb b/spec/ruby/core/range/step_spec.rb
index eb136a8902..0d0caf746d 100644
--- a/spec/ruby/core/range/step_spec.rb
+++ b/spec/ruby/core/range/step_spec.rb
@@ -10,56 +10,71 @@ describe "Range#step" do
r.step { }.should equal(r)
end
- it "raises TypeError if step" do
- obj = mock("mock")
- lambda { (1..10).step(obj) { } }.should raise_error(TypeError)
- end
+ ruby_version_is ""..."3.4" do
+ it "calls #to_int to coerce step to an Integer" do
+ obj = mock("Range#step")
+ obj.should_receive(:to_int).and_return(1)
- it "calls #to_int to coerce step to an Integer" do
- obj = mock("Range#step")
- obj.should_receive(:to_int).and_return(1)
+ (1..2).step(obj) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([1, 2])
+ end
- (1..2).step(obj) { |x| ScratchPad << x }
- ScratchPad.recorded.should eql([1, 2])
- end
+ it "raises a TypeError if step does not respond to #to_int" do
+ obj = mock("Range#step non-integer")
- it "raises a TypeError if step does not respond to #to_int" do
- obj = mock("Range#step non-integer")
+ -> { (1..2).step(obj) { } }.should raise_error(TypeError)
+ end
- lambda { (1..2).step(obj) { } }.should raise_error(TypeError)
- end
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock("Range#step non-integer")
+ obj.should_receive(:to_int).and_return("1")
- it "raises a TypeError if #to_int does not return an Integer" do
- obj = mock("Range#step non-integer")
- obj.should_receive(:to_int).and_return("1")
+ -> { (1..2).step(obj) { } }.should raise_error(TypeError)
+ end
- lambda { (1..2).step(obj) { } }.should raise_error(TypeError)
- end
+ it "raises a TypeError if the first element does not respond to #succ" do
+ obj = mock("Range#step non-comparable")
+ obj.should_receive(:<=>).with(obj).and_return(1)
- it "coerces the argument to integer by invoking to_int" do
- (obj = mock("2")).should_receive(:to_int).and_return(2)
- res = []
- (1..10).step(obj) {|x| res << x}
- res.should == [1, 3, 5, 7, 9]
+ -> { (obj..obj).step { |x| x } }.should raise_error(TypeError)
+ end
end
- it "raises a TypeError if the first element does not respond to #succ" do
- obj = mock("Range#step non-comparable")
- obj.should_receive(:<=>).with(obj).and_return(1)
+ ruby_version_is "3.4" do
+ it "calls #coerce to coerce step to an Integer" do
+ obj = mock("Range#step")
+ obj.should_receive(:coerce).at_least(:once).and_return([1, 2])
+
+ (1..3).step(obj) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([1, 3])
+ end
+
+ it "raises a TypeError if step does not respond to #coerce" do
+ obj = mock("Range#step non-coercible")
- lambda { (obj..obj).step { |x| x } }.should raise_error(TypeError)
+ -> { (1..2).step(obj) { } }.should raise_error(TypeError)
+ end
end
it "raises an ArgumentError if step is 0" do
- lambda { (-1..1).step(0) { |x| x } }.should raise_error(ArgumentError)
+ -> { (-1..1).step(0) { |x| x } }.should raise_error(ArgumentError)
end
it "raises an ArgumentError if step is 0.0" do
- lambda { (-1..1).step(0.0) { |x| x } }.should raise_error(ArgumentError)
+ -> { (-1..1).step(0.0) { |x| x } }.should raise_error(ArgumentError)
+ end
+
+ ruby_version_is "3.4" do
+ it "does not raise an ArgumentError if step is 0 for non-numeric ranges" do
+ t = Time.utc(2023, 2, 24)
+ -> { (t..t+1).step(0) { break } }.should_not raise_error(ArgumentError)
+ end
end
- it "raises an ArgumentError if step is negative" do
- lambda { (-1..1).step(-2) { |x| x } }.should raise_error(ArgumentError)
+ ruby_version_is ""..."3.4" do
+ it "raises an ArgumentError if step is negative" do
+ -> { (-1..1).step(-2) { |x| x } }.should raise_error(ArgumentError)
+ end
end
describe "with inclusive end" do
@@ -78,6 +93,18 @@ describe "Range#step" do
(-2..2).step(1.5) { |x| ScratchPad << x }
ScratchPad.recorded.should eql([-2.0, -0.5, 1.0])
end
+
+ ruby_version_is "3.4" do
+ it "does not iterate if step is negative for forward range" do
+ (-1..1).step(-1) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([])
+ end
+
+ it "iterates backward if step is negative for backward range" do
+ (1..-1).step(-1) { |x| ScratchPad << x }
+ ScratchPad.recorded.should eql([1, 0, -1])
+ end
+ end
end
describe "and Float values" do
@@ -159,16 +186,99 @@ describe "Range#step" do
end
it "raises a TypeError when passed a Float step" do
- lambda { ("A".."G").step(2.0) { } }.should raise_error(TypeError)
+ -> { ("A".."G").step(2.0) { } }.should raise_error(TypeError)
end
- it "calls #succ on begin and each element returned by #succ" do
- obj = mock("Range#step String start")
- obj.should_receive(:<=>).exactly(3).times.and_return(-1, -1, -1, 0)
- obj.should_receive(:succ).exactly(2).times.and_return(obj)
+ ruby_version_is ""..."3.4" do
+ it "calls #succ on begin and each element returned by #succ" do
+ obj = mock("Range#step String start")
+ obj.should_receive(:<=>).exactly(3).times.and_return(-1, -1, -1, 0)
+ obj.should_receive(:succ).exactly(2).times.and_return(obj)
- (obj..obj).step { |x| ScratchPad << x }
- ScratchPad.recorded.should == [obj, obj, obj]
+ (obj..obj).step { |x| ScratchPad << x }
+ ScratchPad.recorded.should == [obj, obj, obj]
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "yields String values adjusted by step and less than or equal to end" do
+ ("A".."AAA").step("A") { |x| ScratchPad << x }
+ ScratchPad.recorded.should == ["A", "AA", "AAA"]
+ end
+
+ it "raises a TypeError when passed an incompatible type step" do
+ -> { ("A".."G").step([]) { } }.should raise_error(TypeError)
+ end
+
+ it "calls #+ on begin and each element returned by #+" do
+ start = mock("Range#step String start")
+ stop = mock("Range#step String stop")
+
+ mid1 = mock("Range#step String mid1")
+ mid2 = mock("Range#step String mid2")
+
+ step = mock("Range#step String step")
+
+ # Deciding on the direction of iteration
+ start.should_receive(:<=>).with(stop).at_least(:twice).and_return(-1)
+ # Deciding whether the step moves iteration in the right direction
+ start.should_receive(:<=>).with(mid1).and_return(-1)
+ # Iteration 1
+ start.should_receive(:+).at_least(:once).with(step).and_return(mid1)
+ # Iteration 2
+ mid1.should_receive(:<=>).with(stop).and_return(-1)
+ mid1.should_receive(:+).with(step).and_return(mid2)
+ # Iteration 3
+ mid2.should_receive(:<=>).with(stop).and_return(0)
+
+ (start..stop).step(step) { |x| ScratchPad << x }
+ ScratchPad.recorded.should == [start, mid1, mid2]
+ end
+
+ it "iterates backward if the step is decreasing values, and the range is backward" do
+ start = mock("Range#step String start")
+ stop = mock("Range#step String stop")
+
+ mid1 = mock("Range#step String mid1")
+ mid2 = mock("Range#step String mid2")
+
+ step = mock("Range#step String step")
+
+ # Deciding on the direction of iteration
+ start.should_receive(:<=>).with(stop).at_least(:twice).and_return(1)
+ # Deciding whether the step moves iteration in the right direction
+ start.should_receive(:<=>).with(mid1).and_return(1)
+ # Iteration 1
+ start.should_receive(:+).at_least(:once).with(step).and_return(mid1)
+ # Iteration 2
+ mid1.should_receive(:<=>).with(stop).and_return(1)
+ mid1.should_receive(:+).with(step).and_return(mid2)
+ # Iteration 3
+ mid2.should_receive(:<=>).with(stop).and_return(0)
+
+ (start..stop).step(step) { |x| ScratchPad << x }
+ ScratchPad.recorded.should == [start, mid1, mid2]
+ end
+
+ it "does no iteration of the direction of the range and of the step don't match" do
+ start = mock("Range#step String start")
+ stop = mock("Range#step String stop")
+
+ mid1 = mock("Range#step String mid1")
+ mid2 = mock("Range#step String mid2")
+
+ step = mock("Range#step String step")
+
+ # Deciding on the direction of iteration: stop > start
+ start.should_receive(:<=>).with(stop).at_least(:twice).and_return(1)
+ # Deciding whether the step moves iteration in the right direction
+ # start + step < start, the direction is opposite to the range's
+ start.should_receive(:+).with(step).and_return(mid1)
+ start.should_receive(:<=>).with(mid1).and_return(-1)
+
+ (start..stop).step(step) { |x| ScratchPad << x }
+ ScratchPad.recorded.should == []
+ end
end
end
end
@@ -209,8 +319,14 @@ describe "Range#step" do
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
@@ -258,60 +374,169 @@ describe "Range#step" do
end
describe "and String values" do
+ 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"]
+ end
+
+ it "raises a TypeError when passed a Float step" do
+ -> { ("A"..."G").step(2.0) { } }.should raise_error(TypeError)
+ 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"]
+ end
+
+ it "raises a TypeError when passed an incompatible type step" do
+ -> { ("A".."G").step([]) { } }.should raise_error(TypeError)
+ end
+ end
+ end
+ end
+
+ 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 []
+ (-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
+ (-5..).step(2) { |x| break if x > 3; ScratchPad << x }
+ ScratchPad.recorded.should eql([-5, -3, -1, 1, 3])
+
+ 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
+ (-2..).step(1.5) { |x| break if x > 1.0; ScratchPad << x }
+ ScratchPad.recorded.should eql([-2.0, -0.5, 1.0])
+
+ 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
+ (-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 []
+ (-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])
+
+ 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
+
+ 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])
+
+ 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
+
+ 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 []
+ (-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
- ("A"..."E").step { |x| ScratchPad << x }
+ ('A'..).step { |x| break if x > "D"; ScratchPad << x }
+ ScratchPad.recorded.should == ["A", "B", "C", "D"]
+
+ 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
- ("A"..."G").step(2) { |x| ScratchPad << x }
+ ('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
- lambda { ("A"..."G").step(2.0) { } }.should raise_error(TypeError)
+ -> { ('A'..).step(2.0) { } }.should raise_error(TypeError)
+ -> { ('A'...).step(2.0) { } }.should raise_error(TypeError)
end
- end
- end
- describe "when no block is given" do
- describe "returned Enumerator" do
- describe "size" do
- it "raises a TypeError if step does not respond to #to_int" do
- obj = mock("Range#step non-integer")
- enum = (1..2).step(obj)
- lambda { enum.size }.should raise_error(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"]
- 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)
+ ScratchPad.record []
+ ('A'...).step("A") { |x| break if x > "AAA"; ScratchPad << x }
+ ScratchPad.recorded.should == ["A", "AA", "AAA"]
+ end
- lambda { enum.size }.should raise_error(TypeError)
+ it "raises a TypeError when passed an incompatible type step" do
+ -> { ('A'..).step([]) { } }.should raise_error(TypeError)
+ -> { ('A'...).step([]) { } }.should raise_error(TypeError)
end
+ end
+ end
+ end
- ruby_version_is ""..."2.6" do
- it "raises an ArgumentError if step is 0" do
- enum = (-1..1).step(0)
- lambda { enum.size }.should raise_error(ArgumentError)
- end
+ describe "when no block is given" 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.0" do
- enum = (-1..1).step(0.0)
- lambda { enum.size }.should raise_error(ArgumentError)
+ describe "returned Enumerator" do
+ describe "size" 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")
+ -> { (1..2).step(obj) }.should raise_error(TypeError)
end
- it "raises an ArgumentError if step is negative" do
- enum = (-1..1).step(-2)
- lambda { enum.size }.should raise_error(ArgumentError)
+ it "raises a TypeError if #to_int does not return an Integer" do
+ obj = mock("Range#step non-integer")
+ obj.should_receive(:to_int).and_return("1")
+ -> { (1..2).step(obj) }.should raise_error(TypeError)
end
end
- ruby_version_is "2.6" 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
+ ruby_version_is "3.4" do
+ it "does not raise if step is incompatible" do
+ obj = mock("Range#step non-integer")
+ -> { (1..2).step(obj) }.should_not raise_error
end
end
@@ -328,11 +553,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
@@ -352,42 +575,105 @@ 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_error
+ enum.size.should == nil
+ end
end
- it "return nil and not raises a TypeError if the first element does not respond to #succ" do
- obj = mock("Range#step non-comparable")
- obj.should_receive(:<=>).with(obj).and_return(1)
- enum = (obj..obj).step
- lambda { enum.size }.should_not raise_error
- enum.size.should == nil
+ ruby_version_is "3.4" do
+ it "returns nil with begin and end are String" do
+ ("A".."E").step("A").size.should == nil
+ ("A"..."E").step("A").size.should == nil
+ end
+
+ it "return nil and not raises a TypeError if the first element is not of compatible type" do
+ obj = mock("Range#step non-comparable")
+ obj.should_receive(:<=>).with(obj).and_return(1)
+ enum = (obj..obj).step(obj)
+ -> { enum.size }.should_not raise_error
+ enum.size.should == nil
+ end
end
end
+ # 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 begin and end are not numerics" do
+ 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_error(ArgumentError)
+ 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 ad2fbf5223..b1d3de32db 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]
- lambda { (0.5..2.4).to_a }.should raise_error(TypeError)
+ -> { (0.5..2.4).to_a }.should raise_error(TypeError)
end
it "returns empty array for descending-ordered" do
@@ -16,7 +16,24 @@ 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
+
+ it "works for non-ASCII ranges" do
+ ('Σ'..'Ω').to_a.should == ["Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω"]
+ end
+
+ it "throws an exception for endless ranges" do
+ -> { eval("(1..)").to_a }.should raise_error(RangeError)
+ end
+
+ it "throws an exception for beginless ranges" do
+ -> { (..1).to_a }.should raise_error(TypeError)
+ end
end
diff --git a/spec/ruby/core/range/to_s_spec.rb b/spec/ruby/core/range/to_s_spec.rb
index 4c37e81fe0..460c330912 100644
--- a/spec/ruby/core/range/to_s_spec.rb
+++ b/spec/ruby/core/range/to_s_spec.rb
@@ -11,15 +11,13 @@ describe "Range#to_s" do
(0.5..2.4).to_s.should == "0.5..2.4"
end
- 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
+ it "can show endless ranges" do
+ eval("(1..)").to_s.should == "1.."
+ eval("(1.0...)").to_s.should == "1.0..."
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
+ 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..589c0e9aed
--- /dev/null
+++ b/spec/ruby/core/range/to_set_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+require_relative '../enumerable/fixtures/classes'
+
+describe "Enumerable#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
+
+ ruby_version_is "4.0" do
+ it "raises a RangeError if the range is infinite" do
+ -> { (1..).to_set }.should raise_error(RangeError, "cannot convert endless range to a set")
+ -> { (1...).to_set }.should raise_error(RangeError, "cannot convert endless range to a set")
+ end
+ end
+
+ 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 be_kind_of(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 = nil
+ proc {
+ set = (1..3).to_set(EnumerableSpecs::SetSubclass)
+ }.should complain(/Enumerable#to_set/)
+ set.should be_kind_of(EnumerableSpecs::SetSubclass)
+ set.to_a.sort.should == [1, 2, 3]
+ end
+ end
+
+ ruby_version_is "4.1" do
+ it "does not accept any positional argument" do
+ -> {
+ (1..3).to_set(EnumerableSpecs::SetSubclass)
+ }.should raise_error(ArgumentError, 'wrong number of arguments (given 1, expected 0)')
+ end
+ end
+
+ it "does not need explicit `require 'set'`" do
+ output = ruby_exe(<<~RUBY, options: '--disable-gems', args: '2>&1')
+ puts (1..3).to_set.to_a.inspect
+ RUBY
+
+ output.chomp.should == "[1, 2, 3]"
+ end
+end