summaryrefslogtreecommitdiff
path: root/spec/ruby/core
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core')
-rw-r--r--spec/ruby/core/array/deconstruct_spec.rb11
-rw-r--r--spec/ruby/core/array/element_set_spec.rb47
-rw-r--r--spec/ruby/core/array/fill_spec.rb9
-rw-r--r--spec/ruby/core/array/intersection_spec.rb88
-rw-r--r--spec/ruby/core/array/shared/intersection.rb84
-rw-r--r--spec/ruby/core/array/shared/join.rb14
-rw-r--r--spec/ruby/core/array/slice_spec.rb12
-rw-r--r--spec/ruby/core/array/values_at_spec.rb7
-rw-r--r--spec/ruby/core/complex/comparision_spec.rb (renamed from spec/ruby/core/complex/spaceship_spec.rb)2
-rw-r--r--spec/ruby/core/enumerator/each_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/lazy/eager_spec.rb29
-rw-r--r--spec/ruby/core/enumerator/new_spec.rb38
-rw-r--r--spec/ruby/core/enumerator/produce_spec.rb36
-rw-r--r--spec/ruby/core/enumerator/yielder/to_proc_spec.rb18
-rw-r--r--spec/ruby/core/exception/destination_encoding_name_spec.rb23
-rw-r--r--spec/ruby/core/exception/destination_encoding_spec.rb23
-rw-r--r--spec/ruby/core/exception/error_bytes_spec.rb12
-rw-r--r--spec/ruby/core/exception/error_char_spec.rb12
-rw-r--r--spec/ruby/core/exception/initialize_spec.rb1
-rw-r--r--spec/ruby/core/exception/readagain_bytes_spec.rb12
-rw-r--r--spec/ruby/core/exception/source_encoding_name_spec.rb23
-rw-r--r--spec/ruby/core/exception/source_encoding_spec.rb23
-rw-r--r--spec/ruby/core/hash/deconstruct_keys_spec.rb25
-rw-r--r--spec/ruby/core/integer/element_reference_spec.rb85
-rw-r--r--spec/ruby/core/kernel/caller_locations_spec.rb28
-rw-r--r--spec/ruby/core/kernel/printf_spec.rb8
-rw-r--r--spec/ruby/core/kernel/warn_spec.rb2
-rw-r--r--spec/ruby/core/marshal/dump_spec.rb3
-rw-r--r--spec/ruby/core/marshal/shared/load.rb6
-rw-r--r--spec/ruby/core/range/bsearch_spec.rb200
-rw-r--r--spec/ruby/core/range/each_spec.rb22
-rw-r--r--spec/ruby/core/range/equal_value_spec.rb6
-rw-r--r--spec/ruby/core/range/inspect_spec.rb7
-rw-r--r--spec/ruby/core/range/last_spec.rb6
-rw-r--r--spec/ruby/core/range/max_spec.rb6
-rw-r--r--spec/ruby/core/range/min_spec.rb7
-rw-r--r--spec/ruby/core/range/shared/cover_and_include.rb7
-rw-r--r--spec/ruby/core/range/shared/equal_value.rb8
-rw-r--r--spec/ruby/core/range/size_spec.rb7
-rw-r--r--spec/ruby/core/range/step_spec.rb96
-rw-r--r--spec/ruby/core/range/to_a_spec.rb6
-rw-r--r--spec/ruby/core/range/to_s_spec.rb7
-rw-r--r--spec/ruby/core/string/capitalize_spec.rb18
-rw-r--r--spec/ruby/core/string/downcase_spec.rb12
-rw-r--r--spec/ruby/core/string/shared/length.rb13
-rw-r--r--spec/ruby/core/string/split_spec.rb18
-rw-r--r--spec/ruby/core/string/swapcase_spec.rb18
-rw-r--r--spec/ruby/core/string/upcase_spec.rb18
-rw-r--r--spec/ruby/core/struct/deconstruct_keys_spec.rb70
-rw-r--r--spec/ruby/core/struct/deconstruct_spec.rb12
-rw-r--r--spec/ruby/core/struct/hash_spec.rb16
-rw-r--r--spec/ruby/core/thread/backtrace_locations_spec.rb37
-rw-r--r--spec/ruby/core/time/inspect_spec.rb15
53 files changed, 1098 insertions, 221 deletions
diff --git a/spec/ruby/core/array/deconstruct_spec.rb b/spec/ruby/core/array/deconstruct_spec.rb
new file mode 100644
index 0000000000..2b07152dfc
--- /dev/null
+++ b/spec/ruby/core/array/deconstruct_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Array#deconstruct" do
+ it "returns self" do
+ array = [1]
+
+ array.deconstruct.should equal array
+ end
+ end
+end
diff --git a/spec/ruby/core/array/element_set_spec.rb b/spec/ruby/core/array/element_set_spec.rb
index 9375ff9b80..f58f0ffdc4 100644
--- a/spec/ruby/core/array/element_set_spec.rb
+++ b/spec/ruby/core/array/element_set_spec.rb
@@ -396,6 +396,14 @@ describe "Array#[]= with [m..n]" do
a.should == [1, 2, 3, 8, 4, 5]
end
+ it "inserts at the end if m > the array size" do
+ a = [1, 2, 3]
+ a[3..3] = [4]
+ a.should == [1, 2, 3, 4]
+ a[5..7] = [6]
+ a.should == [1, 2, 3, 4, nil, 6]
+ end
+
describe "Range subclasses" do
before :each do
@range_incl = ArraySpecs::MyRange.new(1, 2)
@@ -425,6 +433,45 @@ describe "Array#[]= with [m..n]" do
end
end
+ruby_version_is "2.6" do
+ describe "Array#[]= with [m..]" do
+
+ it "just sets the section defined by range to nil even if the rhs is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[eval("(2..)")] = nil
+ a.should == [1, 2, nil]
+ end
+
+ it "just sets the section defined by range to nil if m and n < 0 and the rhs is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[eval("(-3..)")] = nil
+ a.should == [1, 2, nil]
+ end
+
+ it "replaces the section defined by range" do
+ a = [6, 5, 4, 3, 2, 1]
+ a[eval("(3...)")] = 9
+ a.should == [6, 5, 4, 9]
+ a[eval("(2..)")] = [7, 7, 7]
+ a.should == [6, 5, 7, 7, 7]
+ end
+
+ it "replaces the section if m and n < 0" do
+ a = [1, 2, 3, 4, 5]
+ a[eval("(-3..)")] = [7, 8, 9]
+ a.should == [1, 2, 7, 8, 9]
+ end
+
+ it "inserts at the end if m > the array size" do
+ a = [1, 2, 3]
+ a[eval("(3..)")] = [4]
+ a.should == [1, 2, 3, 4]
+ a[eval("(5..)")] = [6]
+ a.should == [1, 2, 3, 4, nil, 6]
+ end
+ end
+end
+
describe "Array#[] after a shift" do
it "works for insertion" do
a = [1,2]
diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb
index 1c1beef25e..0f44d3c010 100644
--- a/spec/ruby/core/array/fill_spec.rb
+++ b/spec/ruby/core/array/fill_spec.rb
@@ -314,4 +314,13 @@ describe "Array#fill with (filler, range)" do
def obj.<=>(rhs); rhs == self ? 0 : nil end
-> { [].fill('a', obj..obj) }.should raise_error(TypeError)
end
+
+ ruby_version_is "2.6" do
+ it "works with endless ranges" do
+ [1, 2, 3, 4].fill('x', eval("(1..)")).should == [1, 'x', 'x', 'x']
+ [1, 2, 3, 4].fill('x', eval("(3...)")).should == [1, 2, 3, 'x']
+ [1, 2, 3, 4].fill(eval("(1..)")) { |x| x + 2 }.should == [1, 3, 4, 5]
+ [1, 2, 3, 4].fill(eval("(3...)")) { |x| x + 2 }.should == [1, 2, 3, 5]
+ end
+ end
end
diff --git a/spec/ruby/core/array/intersection_spec.rb b/spec/ruby/core/array/intersection_spec.rb
index 7bf2ec4dbe..27d90f1e44 100644
--- a/spec/ruby/core/array/intersection_spec.rb
+++ b/spec/ruby/core/array/intersection_spec.rb
@@ -1,87 +1,21 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative 'shared/intersection'
describe "Array#&" do
- it "creates an array with elements common to both arrays (intersection)" do
- ([] & []).should == []
- ([1, 2] & []).should == []
- ([] & [1, 2]).should == []
- ([ 1, 3, 5 ] & [ 1, 2, 3 ]).should == [1, 3]
- end
-
- it "creates an array with no duplicates" do
- ([ 1, 1, 3, 5 ] & [ 1, 2, 3 ]).uniq!.should == nil
- end
-
- it "creates an array with elements in order they are first encountered" do
- ([ 1, 2, 3, 2, 5 ] & [ 5, 2, 3, 4 ]).should == [2, 3, 5]
- end
-
- it "does not modify the original Array" do
- a = [1, 1, 3, 5]
- (a & [1, 2, 3]).should == [1, 3]
- a.should == [1, 1, 3, 5]
- end
-
- it "properly handles recursive arrays" do
- empty = ArraySpecs.empty_recursive_array
- (empty & empty).should == empty
-
- (ArraySpecs.recursive_array & []).should == []
- ([] & ArraySpecs.recursive_array).should == []
-
- (ArraySpecs.recursive_array & ArraySpecs.recursive_array).should == [1, 'two', 3.0, ArraySpecs.recursive_array]
- end
+ it_behaves_like :array_intersection, :&
+end
- it "tries to convert the passed argument to an Array using #to_ary" do
- obj = mock('[1,2,3]')
- obj.should_receive(:to_ary).and_return([1, 2, 3])
- ([1, 2] & obj).should == ([1, 2])
- end
+ruby_version_is "2.7" do
+ describe "Array#intersection" do
+ it_behaves_like :array_intersection, :intersection
- it "determines equivalence between elements in the sense of eql?" do
- not_supported_on :opal do
- ([5.0, 4.0] & [5, 4]).should == []
+ it "accepts multiple arguments" do
+ [1, 2, 3, 4].intersection([1, 2, 3], [2, 3, 4]).should == [2, 3]
end
- str = "x"
- ([str] & [str.dup]).should == [str]
-
- obj1 = mock('1')
- obj2 = mock('2')
- obj1.stub!(:hash).and_return(0)
- obj2.stub!(:hash).and_return(0)
- obj1.should_receive(:eql?).at_least(1).and_return(true)
- obj2.stub!(:eql?).and_return(true)
-
- ([obj1] & [obj2]).should == [obj1]
- ([obj1, obj1, obj2, obj2] & [obj2]).should == [obj1]
-
- obj1 = mock('3')
- obj2 = mock('4')
- obj1.stub!(:hash).and_return(0)
- obj2.stub!(:hash).and_return(0)
- obj1.should_receive(:eql?).at_least(1).and_return(false)
-
- ([obj1] & [obj2]).should == []
- ([obj1, obj1, obj2, obj2] & [obj2]).should == [obj2]
- end
-
- it "does return subclass instances for Array subclasses" do
- (ArraySpecs::MyArray[1, 2, 3] & []).should be_an_instance_of(Array)
- (ArraySpecs::MyArray[1, 2, 3] & ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
- ([] & ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
- end
-
- it "does not call to_ary on array subclasses" do
- ([5, 6] & ArraySpecs::ToAryArray[1, 2, 5, 6]).should == [5, 6]
- end
-
- it "properly handles an identical item even when its #eql? isn't reflexive" do
- x = mock('x')
- x.stub!(:hash).and_return(42)
- x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
-
- ([x] & [x]).should == [x]
+ it "preserves elements order from original array" do
+ [1, 2, 3, 4].intersection([3, 2, 1]).should == [1, 2, 3]
+ end
end
end
diff --git a/spec/ruby/core/array/shared/intersection.rb b/spec/ruby/core/array/shared/intersection.rb
new file mode 100644
index 0000000000..49849b08c2
--- /dev/null
+++ b/spec/ruby/core/array/shared/intersection.rb
@@ -0,0 +1,84 @@
+describe :array_intersection, shared: true do
+ it "creates an array with elements common to both arrays (intersection)" do
+ [].send(@method, []).should == []
+ [1, 2].send(@method, []).should == []
+ [].send(@method, [1, 2]).should == []
+ [ 1, 3, 5 ].send(@method, [ 1, 2, 3 ]).should == [1, 3]
+ end
+
+ it "creates an array with no duplicates" do
+ [ 1, 1, 3, 5 ].send(@method, [ 1, 2, 3 ]).uniq!.should == nil
+ end
+
+ it "creates an array with elements in order they are first encountered" do
+ [ 1, 2, 3, 2, 5 ].send(@method, [ 5, 2, 3, 4 ]).should == [2, 3, 5]
+ end
+
+ it "does not modify the original Array" do
+ a = [1, 1, 3, 5]
+ a.send(@method, [1, 2, 3]).should == [1, 3]
+ a.should == [1, 1, 3, 5]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.send(@method, empty).should == empty
+
+ ArraySpecs.recursive_array.send(@method, []).should == []
+ [].send(@method, ArraySpecs.recursive_array).should == []
+
+ ArraySpecs.recursive_array.send(@method, ArraySpecs.recursive_array).should == [1, 'two', 3.0, ArraySpecs.recursive_array]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('[1,2,3]')
+ obj.should_receive(:to_ary).and_return([1, 2, 3])
+ [1, 2].send(@method, obj).should == ([1, 2])
+ end
+
+ it "determines equivalence between elements in the sense of eql?" do
+ not_supported_on :opal do
+ [5.0, 4.0].send(@method, [5, 4]).should == []
+ end
+
+ str = "x"
+ [str].send(@method, [str.dup]).should == [str]
+
+ obj1 = mock('1')
+ obj2 = mock('2')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj1.should_receive(:eql?).at_least(1).and_return(true)
+ obj2.stub!(:eql?).and_return(true)
+
+ [obj1].send(@method, [obj2]).should == [obj1]
+ [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1]
+
+ obj1 = mock('3')
+ obj2 = mock('4')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj1.should_receive(:eql?).at_least(1).and_return(false)
+
+ [obj1].send(@method, [obj2]).should == []
+ [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj2]
+ end
+
+ it "does return subclass instances for Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
+ [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
+ end
+
+ it "does not call to_ary on array subclasses" do
+ [5, 6].send(@method, ArraySpecs::ToAryArray[1, 2, 5, 6]).should == [5, 6]
+ end
+
+ it "properly handles an identical item even when its #eql? isn't reflexive" do
+ x = mock('x')
+ x.stub!(:hash).and_return(42)
+ x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
+
+ [x].send(@method, [x]).should == [x]
+ end
+end
diff --git a/spec/ruby/core/array/shared/join.rb b/spec/ruby/core/array/shared/join.rb
index 5e7193de8a..c443d9a628 100644
--- a/spec/ruby/core/array/shared/join.rb
+++ b/spec/ruby/core/array/shared/join.rb
@@ -113,6 +113,20 @@ describe :array_join_with_default_separator, shared: true do
-> { ary_utf8_bad_binary.send(@method) }.should raise_error(EncodingError)
end
+
+ ruby_version_is "2.7" do
+ context "when $, is not nil" do
+ before do
+ suppress_warning do
+ $, = '*'
+ end
+ end
+
+ it "warns" do
+ -> { [].join }.should complain(/warning: \$, is set to non-nil value/)
+ end
+ end
+ end
end
describe :array_join_with_string_separator, shared: true do
diff --git a/spec/ruby/core/array/slice_spec.rb b/spec/ruby/core/array/slice_spec.rb
index 16220bdf0d..2e9cfd39b3 100644
--- a/spec/ruby/core/array/slice_spec.rb
+++ b/spec/ruby/core/array/slice_spec.rb
@@ -153,6 +153,18 @@ describe "Array#slice!" do
it "raises a #{frozen_error_class} on a frozen array" do
-> { ArraySpecs.frozen_array.slice!(0, 0) }.should raise_error(frozen_error_class)
end
+
+ ruby_version_is "2.6" do
+ it "works with endless ranges" do
+ a = [1, 2, 3]
+ a.slice!(eval("(1..)")).should == [2, 3]
+ a.should == [1]
+
+ a = [1, 2, 3]
+ a.slice!(eval("(2...)")).should == [3]
+ a.should == [1, 2]
+ end
+ end
end
describe "Array#slice" do
diff --git a/spec/ruby/core/array/values_at_spec.rb b/spec/ruby/core/array/values_at_spec.rb
index 13860150bb..f6801335f8 100644
--- a/spec/ruby/core/array/values_at_spec.rb
+++ b/spec/ruby/core/array/values_at_spec.rb
@@ -60,4 +60,11 @@ describe "Array#values_at" do
it "does not return subclass instance on Array subclasses" do
ArraySpecs::MyArray[1, 2, 3].values_at(0, 1..2, 1).should be_an_instance_of(Array)
end
+
+ ruby_version_is "2.6" do
+ it "works when given endless ranges" do
+ [1, 2, 3, 4].values_at(eval("(1..)")).should == [2, 3, 4]
+ [1, 2, 3, 4].values_at(eval("(3...)")).should == [4]
+ end
+ end
end
diff --git a/spec/ruby/core/complex/spaceship_spec.rb b/spec/ruby/core/complex/comparision_spec.rb
index 7b2849a86a..2a437afb71 100644
--- a/spec/ruby/core/complex/spaceship_spec.rb
+++ b/spec/ruby/core/complex/comparision_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Complex#<=>" do
- ruby_version_is '2.7' do
+ ruby_version_is "2.7" do
it "returns nil if either self or argument has imaginary part" do
(Complex(5, 1) <=> Complex(2)).should be_nil
(Complex(1) <=> Complex(2, 1)).should be_nil
diff --git a/spec/ruby/core/enumerator/each_spec.rb b/spec/ruby/core/enumerator/each_spec.rb
index d88c09cdb5..99ac3120af 100644
--- a/spec/ruby/core/enumerator/each_spec.rb
+++ b/spec/ruby/core/enumerator/each_spec.rb
@@ -40,10 +40,10 @@ describe "Enumerator#each" do
end
it "calls the method given in the constructor until it's exhausted" do
- each = mock('each')
- each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3)
+ each = mock('peach')
+ each.should_receive(:peach).and_yield(1).and_yield(2).and_yield(3)
acc = []
- each.to_enum.each {|e| acc << e }
+ each.to_enum(:peach).each {|e| acc << e }
acc.should == [1,2,3]
end
diff --git a/spec/ruby/core/enumerator/lazy/eager_spec.rb b/spec/ruby/core/enumerator/lazy/eager_spec.rb
new file mode 100644
index 0000000000..30ba2dfe0e
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/eager_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Enumerator::Lazy#eager" do
+ it "returns a non-lazy Enumerator converted from the lazy enumerator" do
+ enum = [1, 2, 3].lazy
+
+ enum.class.should == Enumerator::Lazy
+ enum.eager.class.should == Enumerator
+ end
+
+ it "does not enumerate an enumerator" do
+ ScratchPad.record []
+
+ sequence = [1, 2, 3]
+ enum_lazy = Enumerator::Lazy.new(sequence) do |yielder, value|
+ yielder << value
+ ScratchPad << value
+ end
+
+ ScratchPad.recorded.should == []
+ enum = enum_lazy.eager
+ ScratchPad.recorded.should == []
+
+ enum.map { |i| i }.should == [1, 2, 3]
+ ScratchPad.recorded.should == [1, 2, 3]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/new_spec.rb b/spec/ruby/core/enumerator/new_spec.rb
index 170809dbc1..15e42d247e 100644
--- a/spec/ruby/core/enumerator/new_spec.rb
+++ b/spec/ruby/core/enumerator/new_spec.rb
@@ -38,4 +38,42 @@ describe "Enumerator.new" do
end
enum.to_a.should == [:bar]
end
+
+ context "when passed a block" do
+ it "defines iteration with block, yielder argument and calling << method" do
+ enum = Enumerator.new do |yielder|
+ a = 1
+
+ loop do
+ yielder << a
+ a = a + 1
+ end
+ end
+
+ enum.take(3).should == [1, 2, 3]
+ end
+
+ it "defines iteration with block, yielder argument and calling yield method" do
+ enum = Enumerator.new do |yielder|
+ a = 1
+
+ loop do
+ yielder.yield(a)
+ a = a + 1
+ end
+ end
+
+ enum.take(3).should == [1, 2, 3]
+ end
+
+ ruby_version_is "2.7" do
+ it "defines iteration with block, yielder argument and treating it as a proc" do
+ enum = Enumerator.new do |yielder|
+ "a\nb\nc".each_line(&yielder)
+ end
+
+ enum.to_a.should == ["a\n", "b\n", "c"]
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/enumerator/produce_spec.rb b/spec/ruby/core/enumerator/produce_spec.rb
new file mode 100644
index 0000000000..f6f1dcd429
--- /dev/null
+++ b/spec/ruby/core/enumerator/produce_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Enumerator.produce" do
+ it "creates an infinite enumerator" do
+ enum = Enumerator.produce(0) { |prev| prev + 1 }
+ enum.take(5).should == [0, 1, 2, 3, 4]
+ end
+
+ it "terminates iteration when block raises StopIteration exception" do
+ enum = Enumerator.produce(0) do | prev|
+ raise StopIteration if prev >= 2
+ prev + 1
+ end
+
+ enum.to_a.should == [0, 1, 2]
+ end
+
+ context "when initial value skipped" do
+ it "uses nil instead" do
+ ScratchPad.record []
+ enum = Enumerator.produce { |prev| ScratchPad << prev; (prev || 0) + 1 }
+
+ enum.take(3).should == [1, 2, 3]
+ ScratchPad.recorded.should == [nil, 1, 2]
+ end
+
+ it "starts enumerable from result of first block call" do
+ array = "a\nb\nc\nd".lines
+ lines = Enumerator.produce { array.shift }.take_while { |s| s }
+
+ lines.should == ["a\n", "b\n", "c\n", "d"]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/yielder/to_proc_spec.rb b/spec/ruby/core/enumerator/yielder/to_proc_spec.rb
new file mode 100644
index 0000000000..0ed1645853
--- /dev/null
+++ b/spec/ruby/core/enumerator/yielder/to_proc_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Enumerator::Yielder#to_proc" do
+ it "returns a Proc object that takes an argument and yields it to the block" do
+ ScratchPad.record []
+ y = Enumerator::Yielder.new { |*args| ScratchPad << args; "foobar" }
+
+ callable = y.to_proc
+ callable.class.should == Proc
+
+ result = callable.call(1, 2)
+ ScratchPad.recorded.should == [[1, 2]]
+
+ result.should == "foobar"
+ end
+ end
+end
diff --git a/spec/ruby/core/exception/destination_encoding_name_spec.rb b/spec/ruby/core/exception/destination_encoding_name_spec.rb
deleted file mode 100644
index a9e6474974..0000000000
--- a/spec/ruby/core/exception/destination_encoding_name_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Encoding::UndefinedConversionError#destination_encoding_name" do
- it "returns the destination encoding name" do
- ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
- begin
- ec.convert("\xa0")
- rescue Encoding::UndefinedConversionError => e
- e.destination_encoding_name.should == "EUC-JP"
- end
- end
-end
-
-describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do
- it "returns the destination encoding name" do
- ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- begin
- ec.convert("\xa0")
- rescue Encoding::InvalidByteSequenceError => e
- e.destination_encoding_name.should == "UTF-8"
- end
- end
-end
diff --git a/spec/ruby/core/exception/destination_encoding_spec.rb b/spec/ruby/core/exception/destination_encoding_spec.rb
deleted file mode 100644
index 5709c31e55..0000000000
--- a/spec/ruby/core/exception/destination_encoding_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Encoding::UndefinedConversionError#destination_encoding" do
- it "returns the destination encoding" do
- ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
- begin
- ec.convert("\xa0")
- rescue Encoding::UndefinedConversionError => e
- e.destination_encoding.should == Encoding::EUC_JP
- end
- end
-end
-
-describe "Encoding::InvalidByteSequenceError#destination_encoding" do
- it "returns the destination encoding" do
- ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- begin
- ec.convert("\xa0")
- rescue Encoding::InvalidByteSequenceError => e
- e.destination_encoding.should == Encoding::UTF_8
- end
- end
-end
diff --git a/spec/ruby/core/exception/error_bytes_spec.rb b/spec/ruby/core/exception/error_bytes_spec.rb
deleted file mode 100644
index 66dd4b62c1..0000000000
--- a/spec/ruby/core/exception/error_bytes_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Encoding::InvalidByteSequenceError#error_bytes" do
- it "returns the error bytes" do
- ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- begin
- ec.convert("\xa0")
- rescue Encoding::InvalidByteSequenceError => e
- e.error_bytes.should == "\xA0".force_encoding("ASCII-8BIT")
- end
- end
-end
diff --git a/spec/ruby/core/exception/error_char_spec.rb b/spec/ruby/core/exception/error_char_spec.rb
deleted file mode 100644
index f95ae2a6ce..0000000000
--- a/spec/ruby/core/exception/error_char_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Encoding::UndefinedConversionError#error_char" do
- it "returns the error char" do
- ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
- begin
- ec.convert("\xa0")
- rescue Encoding::UndefinedConversionError => e
- e.error_char.should == "\u00A0"
- end
- end
-end
diff --git a/spec/ruby/core/exception/initialize_spec.rb b/spec/ruby/core/exception/initialize_spec.rb
deleted file mode 100644
index e724feaa39..0000000000
--- a/spec/ruby/core/exception/initialize_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../spec_helper'
diff --git a/spec/ruby/core/exception/readagain_bytes_spec.rb b/spec/ruby/core/exception/readagain_bytes_spec.rb
deleted file mode 100644
index 0f1e24f1cf..0000000000
--- a/spec/ruby/core/exception/readagain_bytes_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Encoding::InvalidByteSequenceError#readagain_bytes" do
- it "returns the next byte" do
- begin
- "abc\xa4def".encode("ISO-8859-1", "EUC-JP")
- rescue Encoding::InvalidByteSequenceError => e
- e.error_bytes.should == "\xA4".force_encoding("ASCII-8BIT")
- e.readagain_bytes.should == 'd'
- end
- end
-end
diff --git a/spec/ruby/core/exception/source_encoding_name_spec.rb b/spec/ruby/core/exception/source_encoding_name_spec.rb
deleted file mode 100644
index 6f5dbd01aa..0000000000
--- a/spec/ruby/core/exception/source_encoding_name_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Encoding::UndefinedConversionError#source_encoding_name" do
- it "returns the source encoding name" do
- ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
- begin
- ec.convert("\xa0")
- rescue Encoding::UndefinedConversionError => e
- e.source_encoding_name.should == "UTF-8"
- end
- end
-end
-
-describe "Encoding::InvalidByteSequenceError#source_encoding_name" do
- it "returns the source encoding name" do
- ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- begin
- ec.convert("\xa0")
- rescue Encoding::InvalidByteSequenceError => e
- e.source_encoding_name.should == "EUC-JP"
- end
- end
-end
diff --git a/spec/ruby/core/exception/source_encoding_spec.rb b/spec/ruby/core/exception/source_encoding_spec.rb
deleted file mode 100644
index fac38e75f4..0000000000
--- a/spec/ruby/core/exception/source_encoding_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Encoding::UndefinedConversionError#source_encoding" do
- it "returns the source encoding" do
- ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
- begin
- ec.convert("\xa0")
- rescue Encoding::UndefinedConversionError => e
- e.source_encoding.should == Encoding::UTF_8
- end
- end
-end
-
-describe "Encoding::InvalidByteSequenceError#source_encoding" do
- it "returns the source encoding" do
- ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- begin
- ec.convert("\xa0")
- rescue Encoding::InvalidByteSequenceError => e
- e.source_encoding.should == Encoding::EUC_JP
- end
- end
-end
diff --git a/spec/ruby/core/hash/deconstruct_keys_spec.rb b/spec/ruby/core/hash/deconstruct_keys_spec.rb
new file mode 100644
index 0000000000..b265732616
--- /dev/null
+++ b/spec/ruby/core/hash/deconstruct_keys_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Hash#deconstruct_keys" do
+ it "returns self" do
+ hash = {a: 1, b: 2}
+
+ hash.deconstruct_keys([:a, :b]).should equal hash
+ end
+
+ it "requires one argument" do
+ -> {
+ {a: 1}.deconstruct_keys
+ }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
+ end
+
+ it "ignores argument" do
+ hash = {a: 1, b: 2}
+
+ hash.deconstruct_keys([:a]).should == {a: 1, b: 2}
+ hash.deconstruct_keys(0 ).should == {a: 1, b: 2}
+ hash.deconstruct_keys('' ).should == {a: 1, b: 2}
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/element_reference_spec.rb b/spec/ruby/core/integer/element_reference_spec.rb
index 99283fac34..4c236a11e7 100644
--- a/spec/ruby/core/integer/element_reference_spec.rb
+++ b/spec/ruby/core/integer/element_reference_spec.rb
@@ -78,6 +78,91 @@ describe "Integer#[]" do
it "returns 0 when passed a Float in the range of a Bignum" do
3[bignum_value.to_f].should == 0
end
+
+ ruby_version_is "2.7" do
+ context "when index and length passed" do
+ it "returns specified number of bits from specified position" do
+ 0b101001101[2, 4].should == 0b0011
+ 0b101001101[2, 5].should == 0b10011
+ 0b101001101[2, 7].should == 0b1010011
+ end
+
+ it "ensures n[i, len] equals to (n >> i) & ((1 << len) - 1)" do
+ n = 0b101001101; i = 2; len = 4
+ n[i, len].should == (n >> i) & ((1 << len) - 1)
+ end
+
+ it "moves start position to the most significant bits when negative index passed" do
+ 0b000001[-1, 4].should == 0b10
+ 0b000001[-2, 4].should == 0b100
+ 0b000001[-3, 4].should == 0b1000
+ end
+
+ it "ignores negative length" do
+ 0b101001101[1, -1].should == 0b10100110
+ 0b101001101[2, -1].should == 0b1010011
+ 0b101001101[3, -1].should == 0b101001
+
+ 0b101001101[3, -5].should == 0b101001
+ 0b101001101[3, -15].should == 0b101001
+ 0b101001101[3, -125].should == 0b101001
+ end
+ end
+
+ context "when range passed" do
+ it "returns bits specified by range" do
+ 0b101001101[2..5].should == 0b0011
+ 0b101001101[2..6].should == 0b10011
+ 0b101001101[2..8].should == 0b1010011
+ end
+
+ it "ensures n[i..j] equals to (n >> i) & ((1 << (j - i + 1)) - 1)" do
+ n = 0b101001101; i = 2; j = 5
+ n[i..j].should == (n >> i) & ((1 << (j - i + 1)) - 1)
+ end
+
+ it "ensures n[i..] equals to (n >> i)" do
+ eval("0b101001101[3..]").should == 0b101001101 >> 3
+ end
+
+ it "moves lower boundary to the most significant bits when negative value passed" do
+ 0b000001[-1, 4].should == 0b10
+ 0b000001[-2, 4].should == 0b100
+ 0b000001[-3, 4].should == 0b1000
+ end
+
+ it "ignores negative upper boundary" do
+ 0b101001101[1..-1].should == 0b10100110
+ 0b101001101[1..-2].should == 0b10100110
+ 0b101001101[1..-3].should == 0b10100110
+ end
+
+ it "ignores upper boundary smaller than lower boundary" do
+ 0b101001101[4..1].should == 0b10100
+ 0b101001101[4..2].should == 0b10100
+ 0b101001101[4..3].should == 0b10100
+ end
+
+ it "raises FloatDomainError if any boundary is infinity" do
+ -> { 0x0001[3..Float::INFINITY] }.should raise_error(FloatDomainError, /Infinity/)
+ -> { 0x0001[-Float::INFINITY..3] }.should raise_error(FloatDomainError, /-Infinity/)
+ end
+
+ context "when passed (..i)" do
+ it "returns 0 if all i bits equal 0" do
+ eval("0b10000[..1]").should == 0
+ eval("0b10000[..2]").should == 0
+ eval("0b10000[..3]").should == 0
+ end
+
+ it "raises ArgumentError if any of i bit equals 1" do
+ -> {
+ eval("0b111110[..3]")
+ }.should raise_error(ArgumentError, /The beginless range for Integer#\[\] results in infinity/)
+ end
+ end
+ end
+ end
end
context "bignum" do
diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb
index 4de6c2ffd6..8f9429845d 100644
--- a/spec/ruby/core/kernel/caller_locations_spec.rb
+++ b/spec/ruby/core/kernel/caller_locations_spec.rb
@@ -22,6 +22,30 @@ describe 'Kernel#caller_locations' do
locations.length.should == 1
end
+ it "can be called with a range" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations(2..4)
+ locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
+ end
+
+ it "can be called with a range whose end is negative" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations(2..-1)
+ locations3 = caller_locations(2..-2)
+ locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
+ locations1[2..-2].map(&:to_s).should == locations3.map(&:to_s)
+ end
+
+ it "must return nil if omitting more locations than available" do
+ caller_locations(100).should == nil
+ caller_locations(100..-1).should == nil
+ end
+
+ it "must return [] if omitting exactly the number of locations available" do
+ omit = caller_locations(0).length
+ caller_locations(omit).should == []
+ end
+
it 'returns the locations as Thread::Backtrace::Location instances' do
locations = KernelSpecs::CallerLocationsTest.locations
@@ -29,4 +53,8 @@ describe 'Kernel#caller_locations' do
location.kind_of?(Thread::Backtrace::Location).should == true
end
end
+
+ it "must return the same locations when called with 1..-1 and when called with no arguments" do
+ caller_locations.map(&:to_s).should == caller_locations(1..-1).map(&:to_s)
+ end
end
diff --git a/spec/ruby/core/kernel/printf_spec.rb b/spec/ruby/core/kernel/printf_spec.rb
index 4592c08aaf..d1743dbcde 100644
--- a/spec/ruby/core/kernel/printf_spec.rb
+++ b/spec/ruby/core/kernel/printf_spec.rb
@@ -1,7 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/sprintf'
-require "stringio"
describe "Kernel#printf" do
it "is a private method" do
@@ -32,8 +31,14 @@ describe "Kernel.printf" do
object.should_receive(:write).with("string")
Kernel.printf(object, "%s", "string")
end
+end
+describe "Kernel.printf" do
describe "formatting" do
+ before :each do
+ require "stringio"
+ end
+
context "io is specified" do
it_behaves_like :kernel_sprintf, -> format, *args {
io = StringIO.new
@@ -45,7 +50,6 @@ describe "Kernel.printf" do
context "io is not specified" do
it_behaves_like :kernel_sprintf, -> format, *args {
stdout = $stdout
-
begin
$stdout = io = StringIO.new
Kernel.printf(format, *args)
diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb
index 2a404a2a35..b0672997f6 100644
--- a/spec/ruby/core/kernel/warn_spec.rb
+++ b/spec/ruby/core/kernel/warn_spec.rb
@@ -111,7 +111,7 @@ describe "Kernel#warn" do
-> { w.f4(obj, 2) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f2_call_lineno}: warning: to_s called|)
end
- it "does not prepend caller information if line number is too big" do
+ it "does not prepend caller information if the uplevel argument is too large" do
w = KernelSpecs::WarnInNestedCall.new
-> { w.f4("foo", 100) }.should output(nil, "warning: foo\n")
end
diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb
index ea021991a8..4ffc586364 100644
--- a/spec/ruby/core/marshal/dump_spec.rb
+++ b/spec/ruby/core/marshal/dump_spec.rb
@@ -556,13 +556,10 @@ describe "Marshal.dump" do
end
describe "when passed a StringIO" do
-
it "should raise an error" do
require "stringio"
-
-> { Marshal.dump(StringIO.new) }.should raise_error(TypeError)
end
-
end
it "raises a TypeError if marshalling a Method instance" do
diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb
index 78fc9fb286..85e645507f 100644
--- a/spec/ruby/core/marshal/shared/load.rb
+++ b/spec/ruby/core/marshal/shared/load.rb
@@ -1,6 +1,5 @@
# -*- encoding: binary -*-
require_relative '../fixtures/marshal_data'
-require 'stringio'
describe :marshal_load, shared: true do
before :all do
@@ -410,8 +409,9 @@ describe :marshal_load, shared: true do
end
it "loads a string through StringIO stream" do
- obj = "This is a string which should be unmarshalled through StringIO stream!"
- Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj
+ require 'stringio'
+ obj = "This is a string which should be unmarshalled through StringIO stream!"
+ Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj
end
it "loads a string with an ivar" do
diff --git a/spec/ruby/core/range/bsearch_spec.rb b/spec/ruby/core/range/bsearch_spec.rb
index 009cafb00e..38ccd35170 100644
--- a/spec/ruby/core/range/bsearch_spec.rb
+++ b/spec/ruby/core/range/bsearch_spec.rb
@@ -81,6 +81,19 @@ 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
end
context "with Float values" do
@@ -94,13 +107,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 == nil
+ (-inf...0).bsearch { |x| x == -inf }.should == nil
+ (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
@@ -130,7 +176,157 @@ 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 == nil
+ (0...inf).bsearch { |x| x == inf ? 0 : -1 }.should == nil
+ (-inf...0).bsearch { |x| x == -inf ? 0 : 1 }.should == nil
+ (-inf..0).bsearch { |x| x == -inf ? 0 : 1 }.should == nil
+ (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
+ 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
+
+ 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].should include(result)
+ end
+ 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
end
end
diff --git a/spec/ruby/core/range/each_spec.rb b/spec/ruby/core/range/each_spec.rb
index 110b0602d0..8935d86829 100644
--- a/spec/ruby/core/range/each_spec.rb
+++ b/spec/ruby/core/range/each_spec.rb
@@ -32,6 +32,28 @@ describe "Range#each" do
a.should == [x, y]
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
+ 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)
diff --git a/spec/ruby/core/range/equal_value_spec.rb b/spec/ruby/core/range/equal_value_spec.rb
index 889557fc2a..43d5e0e38a 100644
--- a/spec/ruby/core/range/equal_value_spec.rb
+++ b/spec/ruby/core/range/equal_value_spec.rb
@@ -7,4 +7,10 @@ describe "Range#==" do
it "returns true if the endpoints are ==" 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
+ end
end
diff --git a/spec/ruby/core/range/inspect_spec.rb b/spec/ruby/core/range/inspect_spec.rb
index 837f7e69ab..d1072531e9 100644
--- a/spec/ruby/core/range/inspect_spec.rb
+++ b/spec/ruby/core/range/inspect_spec.rb
@@ -12,6 +12,13 @@ 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
+ 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
diff --git a/spec/ruby/core/range/last_spec.rb b/spec/ruby/core/range/last_spec.rb
index c7e629e62c..54884ba4d6 100644
--- a/spec/ruby/core/range/last_spec.rb
+++ b/spec/ruby/core/range/last_spec.rb
@@ -46,4 +46,10 @@ describe "Range#last" do
it "raises a TypeError when passed a String" do
-> { (2..3).last("1") }.should raise_error(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
+ end
end
diff --git a/spec/ruby/core/range/max_spec.rb b/spec/ruby/core/range/max_spec.rb
index faac0a2032..728053cefd 100644
--- a/spec/ruby/core/range/max_spec.rb
+++ b/spec/ruby/core/range/max_spec.rb
@@ -45,6 +45,12 @@ describe "Range#max" do
time_end = Time.now + 1.0
-> { (time_start...time_end).max }.should raise_error(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
+ end
end
describe "Range#max given a block" do
diff --git a/spec/ruby/core/range/min_spec.rb b/spec/ruby/core/range/min_spec.rb
index 424bd1dc87..f1dff73e6d 100644
--- a/spec/ruby/core/range/min_spec.rb
+++ b/spec/ruby/core/range/min_spec.rb
@@ -72,4 +72,11 @@ 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
+
+ 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
+ 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 a19e2c6ead..b308524310 100644
--- a/spec/ruby/core/range/shared/cover_and_include.rb
+++ b/spec/ruby/core/range/shared/cover_and_include.rb
@@ -19,6 +19,13 @@ 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
+ end
+
it "compares values using <=>" do
rng = (1..5)
m = mock("int")
diff --git a/spec/ruby/core/range/shared/equal_value.rb b/spec/ruby/core/range/shared/equal_value.rb
index 9d8bb13351..8872b4efc0 100644
--- a/spec/ruby/core/range/shared/equal_value.rb
+++ b/spec/ruby/core/range/shared/equal_value.rb
@@ -42,4 +42,12 @@ 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
+
+ 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
+ end
end
diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb
index 09759940dd..b687342b72 100644
--- a/spec/ruby/core/range/size_spec.rb
+++ b/spec/ruby/core/range/size_spec.rb
@@ -24,6 +24,13 @@ describe "Range#size" do
(-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY
end
+ ruby_version_is "2.6" do
+ it 'returns Float::INFINITY for endless ranges' do
+ eval("(1..)").size.should == Float::INFINITY
+ eval("(0.5...)").size.should == Float::INFINITY
+ 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
diff --git a/spec/ruby/core/range/step_spec.rb b/spec/ruby/core/range/step_spec.rb
index d564e4a5cd..d83868a0fb 100644
--- a/spec/ruby/core/range/step_spec.rb
+++ b/spec/ruby/core/range/step_spec.rb
@@ -274,6 +274,102 @@ describe "Range#step" do
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])
+
+ ScratchPad.record []
+ eval("(-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])
+
+ ScratchPad.record []
+ eval("(-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])\
+
+ 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
+ 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])
+
+ 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
+
+ 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 []
+ 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
+ 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 []
+ 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 "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])
+
+ 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
+ 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"]
+
+ ScratchPad.record []
+ eval("('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"]
+
+ ScratchPad.record []
+ eval("('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
+ -> { eval("('A'..)").step(2.0) { } }.should raise_error(TypeError)
+ -> { eval("('A'...)").step(2.0) { } }.should raise_error(TypeError)
+ end
+ end
+ end
+ end
+
describe "when no block is given" do
describe "returned Enumerator" do
describe "size" do
diff --git a/spec/ruby/core/range/to_a_spec.rb b/spec/ruby/core/range/to_a_spec.rb
index 15f0b44a9c..b0067f0e07 100644
--- a/spec/ruby/core/range/to_a_spec.rb
+++ b/spec/ruby/core/range/to_a_spec.rb
@@ -19,4 +19,10 @@ describe "Range#to_a" do
it "works with Ranges of Symbols" do
(:A..:z).to_a.size.should == 58
end
+
+ ruby_version_is "2.6" do
+ it "throws an exception for endless ranges" do
+ -> { eval("(1..)").to_a }.should raise_error(RangeError)
+ end
+ end
end
diff --git a/spec/ruby/core/range/to_s_spec.rb b/spec/ruby/core/range/to_s_spec.rb
index 7392aa9890..ccbc5d8e7e 100644
--- a/spec/ruby/core/range/to_s_spec.rb
+++ b/spec/ruby/core/range/to_s_spec.rb
@@ -11,6 +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
+ 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
diff --git a/spec/ruby/core/string/capitalize_spec.rb b/spec/ruby/core/string/capitalize_spec.rb
index 41dd63f63e..376db3aa72 100644
--- a/spec/ruby/core/string/capitalize_spec.rb
+++ b/spec/ruby/core/string/capitalize_spec.rb
@@ -93,6 +93,12 @@ describe "String#capitalize!" do
a.should == "Hello"
end
+ it "modifies self in place for non-ascii-compatible encodings" do
+ a = "heLLo".encode("utf-16le")
+ a.capitalize!
+ a.should == "Hello".encode("utf-16le")
+ end
+
describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do
a = "äöÜ"
@@ -106,6 +112,12 @@ describe "String#capitalize!" do
a.should == "Ss"
end
+ it "works for non-ascii-compatible encodings" do
+ a = "äöü".encode("utf-16le")
+ a.capitalize!
+ a.should == "Äöü".encode("utf-16le")
+ end
+
it "updates string metadata" do
capitalized = "ßeT"
capitalized.capitalize!
@@ -123,6 +135,12 @@ describe "String#capitalize!" do
a.capitalize!(:ascii)
a.should == "ßet"
end
+
+ it "works for non-ascii-compatible encodings" do
+ a = "aBc".encode("utf-16le")
+ a.capitalize!(:ascii)
+ a.should == "Abc".encode("utf-16le")
+ end
end
describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do
diff --git a/spec/ruby/core/string/downcase_spec.rb b/spec/ruby/core/string/downcase_spec.rb
index 84e94ee104..a78b6de373 100644
--- a/spec/ruby/core/string/downcase_spec.rb
+++ b/spec/ruby/core/string/downcase_spec.rb
@@ -88,6 +88,12 @@ describe "String#downcase!" do
a.should == "hello"
end
+ it "modifies self in place for non-ascii-compatible encodings" do
+ a = "HeLlO".encode("utf-16le")
+ a.downcase!
+ a.should == "hello".encode("utf-16le")
+ end
+
describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do
a = "ÄÖÜ"
@@ -112,6 +118,12 @@ describe "String#downcase!" do
a.downcase!(:ascii)
a.should == "cÅr"
end
+
+ it "works for non-ascii-compatible encodings" do
+ a = "ABC".encode("utf-16le")
+ a.downcase!(:ascii)
+ a.should == "abc".encode("utf-16le")
+ end
end
describe "full Unicode case mapping adapted for Turkic languages" do
diff --git a/spec/ruby/core/string/shared/length.rb b/spec/ruby/core/string/shared/length.rb
index f387fb251c..b9eae5170f 100644
--- a/spec/ruby/core/string/shared/length.rb
+++ b/spec/ruby/core/string/shared/length.rb
@@ -23,4 +23,17 @@ describe :string_length, shared: true do
str.force_encoding('BINARY').send(@method).should == 12
end
+
+ it "returns the correct length after force_encoding(BINARY)" do
+ utf8 = "あ"
+ ascii = "a"
+ concat = utf8 + ascii
+
+ concat.encoding.should == Encoding::UTF_8
+ concat.bytesize.should == 4
+
+ concat.size.should == 2
+ concat.force_encoding(Encoding::ASCII_8BIT)
+ concat.size.should == 4
+ end
end
diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb
index cfb030ad8d..8a740b04ca 100644
--- a/spec/ruby/core/string/split_spec.rb
+++ b/spec/ruby/core/string/split_spec.rb
@@ -84,6 +84,24 @@ describe "String#split with String" do
$; = old_fs
end
end
+
+ ruby_version_is "2.7" do
+ context "when $; is not nil" do
+ before do
+ suppress_warning do
+ @old_value, $; = $;, 'foobar'
+ end
+ end
+
+ after do
+ $; = @old_value
+ end
+
+ it "warns" do
+ -> { "".split }.should complain(/warning: \$; is set to non-nil value/)
+ end
+ end
+ end
end
it "ignores leading and continuous whitespace when string is a single space" do
diff --git a/spec/ruby/core/string/swapcase_spec.rb b/spec/ruby/core/string/swapcase_spec.rb
index c1a1608a81..b5d9b20451 100644
--- a/spec/ruby/core/string/swapcase_spec.rb
+++ b/spec/ruby/core/string/swapcase_spec.rb
@@ -86,6 +86,12 @@ describe "String#swapcase!" do
a.should == "CyBeR_pUnK11"
end
+ it "modifies self in place for non-ascii-compatible encodings" do
+ a = "cYbEr_PuNk11".encode("utf-16le")
+ a.swapcase!
+ a.should == "CyBeR_pUnK11".encode("utf-16le")
+ end
+
describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do
a = "äÖü"
@@ -93,6 +99,12 @@ describe "String#swapcase!" do
a.should == "ÄöÜ"
end
+ it "works for non-ascii-compatible encodings" do
+ a = "äÖü".encode("utf-16le")
+ a.swapcase!
+ a.should == "ÄöÜ".encode("utf-16le")
+ end
+
it "updates string metadata" do
swapcased = "Aßet"
swapcased.swapcase!
@@ -110,6 +122,12 @@ describe "String#swapcase!" do
a.swapcase!(:ascii)
a.should == "AßET"
end
+
+ it "works for non-ascii-compatible encodings" do
+ a = "aBc".encode("utf-16le")
+ a.swapcase!(:ascii)
+ a.should == "AbC".encode("utf-16le")
+ end
end
describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do
diff --git a/spec/ruby/core/string/upcase_spec.rb b/spec/ruby/core/string/upcase_spec.rb
index eb7d708fe0..4857079a84 100644
--- a/spec/ruby/core/string/upcase_spec.rb
+++ b/spec/ruby/core/string/upcase_spec.rb
@@ -85,6 +85,12 @@ describe "String#upcase!" do
a.should == "HELLO"
end
+ it "modifies self in place for non-ascii-compatible encodings" do
+ a = "HeLlO".encode("utf-16le")
+ a.upcase!
+ a.should == "HELLO".encode("utf-16le")
+ end
+
describe "full Unicode case mapping" do
it "modifies self in place for all of Unicode with no option" do
a = "äöü"
@@ -92,6 +98,12 @@ describe "String#upcase!" do
a.should == "ÄÖÜ"
end
+ it "works for non-ascii-compatible encodings" do
+ a = "äöü".encode("utf-16le")
+ a.upcase!
+ a.should == "ÄÖÜ".encode("utf-16le")
+ end
+
it "updates string metadata for self" do
upcased = "aßet"
upcased.upcase!
@@ -109,6 +121,12 @@ describe "String#upcase!" do
a.upcase!(:ascii)
a.should == "AßET"
end
+
+ it "works for non-ascii-compatible encodings" do
+ a = "abc".encode("utf-16le")
+ a.upcase!(:ascii)
+ a.should == "ABC".encode("utf-16le")
+ end
end
describe "modifies self in place for full Unicode case mapping adapted for Turkic languages" do
diff --git a/spec/ruby/core/struct/deconstruct_keys_spec.rb b/spec/ruby/core/struct/deconstruct_keys_spec.rb
new file mode 100644
index 0000000000..41b9c31e02
--- /dev/null
+++ b/spec/ruby/core/struct/deconstruct_keys_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Struct#deconstruct_keys" do
+ it "returns a hash of attributes" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ s.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
+ end
+
+ it "requires one argument" do
+ struct = Struct.new(:x)
+ obj = struct.new(1)
+
+ -> {
+ obj.deconstruct_keys
+ }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
+ end
+
+ it "returns only specified keys" do
+ struct = Struct.new(:x, :y, :z)
+ s = struct.new(1, 2, 3)
+
+ s.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
+ s.deconstruct_keys([:x] ).should == {x: 1}
+ s.deconstruct_keys([] ).should == {}
+ end
+
+ it "accepts string attribute names" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ s.deconstruct_keys(['x', 'y']).should == {'x' => 1, 'y' => 2}
+ end
+
+ it "accepts argument position number as well but returns them as keys" do
+ struct = Struct.new(:x, :y, :z)
+ s = struct.new(10, 20, 30)
+
+ s.deconstruct_keys([0, 1, 2]).should == {0 => 10, 1 => 20, 2 => 30}
+ s.deconstruct_keys([0, 1] ).should == {0 => 10, 1 => 20}
+ s.deconstruct_keys([0] ).should == {0 => 10}
+ end
+
+ it "ignores not existing attribute names" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ s.deconstruct_keys([:a, :b, :c]).should == {}
+ end
+
+ it "accepts nil argument and return all the attributes" do
+ struct = Struct.new(:x, :y)
+ obj = struct.new(1, 2)
+
+ obj.deconstruct_keys(nil).should == {x: 1, y: 2}
+ end
+
+ it "raise TypeError if passed anything accept nil or array" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ -> { s.deconstruct_keys('x') }.should raise_error(TypeError, /expected Array or nil/)
+ -> { s.deconstruct_keys(1) }.should raise_error(TypeError, /expected Array or nil/)
+ -> { s.deconstruct_keys(:x) }.should raise_error(TypeError, /expected Array or nil/)
+ -> { s.deconstruct_keys({}) }.should raise_error(TypeError, /expected Array or nil/)
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/deconstruct_spec.rb b/spec/ruby/core/struct/deconstruct_spec.rb
new file mode 100644
index 0000000000..7518a40987
--- /dev/null
+++ b/spec/ruby/core/struct/deconstruct_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Struct#deconstruct" do
+ it "returns an array of attribute values" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ s.deconstruct.should == [1, 2]
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/hash_spec.rb b/spec/ruby/core/struct/hash_spec.rb
index 71e8ccbd3a..5ed4ace489 100644
--- a/spec/ruby/core/struct/hash_spec.rb
+++ b/spec/ruby/core/struct/hash_spec.rb
@@ -19,6 +19,22 @@ describe "Struct#hash" do
car.hash.should == similar_car.hash
end
+ it "returns different hashes for structs with different values" do
+ s1 = StructClasses::Ruby.new('2.7.0', 'linux')
+ s2 = StructClasses::Ruby.new('2.7.0', 'macos')
+ s1.hash.should_not == s2.hash
+ end
+
+ ruby_version_is "2.5" do
+ it "returns different hashes for structs with different values when using keyword_init: true" do
+ key = :"1 non symbol member"
+ struct_class = Struct.new(key, keyword_init: true)
+ t1 = struct_class.new(key => 1)
+ t2 = struct_class.new(key => 2)
+ t1.hash.should_not == t2.hash
+ end
+ end
+
it "allows for overriding methods in an included module" do
mod = Module.new do
def hash
diff --git a/spec/ruby/core/thread/backtrace_locations_spec.rb b/spec/ruby/core/thread/backtrace_locations_spec.rb
index 66947f8ea6..211858899c 100644
--- a/spec/ruby/core/thread/backtrace_locations_spec.rb
+++ b/spec/ruby/core/thread/backtrace_locations_spec.rb
@@ -19,6 +19,43 @@ describe "Thread#backtrace_locations" do
locations.each { |loc| loc.should be_an_instance_of(Thread::Backtrace::Location) }
end
+ it "can be called with a number of locations to omit" do
+ locations1 = Thread.current.backtrace_locations
+ locations2 = Thread.current.backtrace_locations(2)
+ locations1[2..-1].length.should == locations2.length
+ locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
+ end
+
+ it "can be called with a maximum number of locations to return as second parameter" do
+ locations1 = Thread.current.backtrace_locations
+ locations2 = Thread.current.backtrace_locations(2, 3)
+ locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
+ end
+
+ it "can be called with a range" do
+ locations1 = Thread.current.backtrace_locations
+ locations2 = Thread.current.backtrace_locations(2..4)
+ locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
+ end
+
+ it "can be called with a range whose end is negative" do
+ locations1 = Thread.current.backtrace_locations
+ locations2 = Thread.current.backtrace_locations(2..-1)
+ locations3 = Thread.current.backtrace_locations(2..-2)
+ locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
+ locations1[2..-2].map(&:to_s).should == locations3.map(&:to_s)
+ end
+
+ it "returns nil if omitting more locations than available" do
+ Thread.current.backtrace_locations(100).should == nil
+ Thread.current.backtrace_locations(100..-1).should == nil
+ end
+
+ it "returns [] if omitting exactly the number of locations available" do
+ omit = Thread.current.backtrace_locations.length
+ Thread.current.backtrace_locations(omit).should == []
+ end
+
it "without argument is the same as showing all locations with 0..-1" do
Thread.current.backtrace_locations.map(&:to_s).should == Thread.current.backtrace_locations(0..-1).map(&:to_s)
end
diff --git a/spec/ruby/core/time/inspect_spec.rb b/spec/ruby/core/time/inspect_spec.rb
index 01e1dfdaa6..85133838e2 100644
--- a/spec/ruby/core/time/inspect_spec.rb
+++ b/spec/ruby/core/time/inspect_spec.rb
@@ -3,4 +3,19 @@ require_relative 'shared/inspect'
describe "Time#inspect" do
it_behaves_like :inspect, :inspect
+
+ ruby_version_is "2.7" do
+ it "preserves milliseconds" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 123456)
+ t.inspect.should == "2007-11-01 15:25:00.123456 UTC"
+ end
+
+ it "formats nanoseconds as a Rational" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789)
+ t.nsec.should == 123456789
+ t.strftime("%N").should == "123456789"
+
+ t.inspect.should == "2007-11-01 15:25:00 8483885939586761/68719476736000000 UTC"
+ end
+ end
end