diff options
author | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-10-28 15:15:48 +0000 |
---|---|---|
committer | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-10-28 15:15:48 +0000 |
commit | 8c5b60eb22d6d661e87992a65d54e3a5bc0aeed4 (patch) | |
tree | 7905b284cb5b3d62c17ad8a939e339621a498a2c /spec/ruby/core | |
parent | 6530b14cee76e2512424d225e64d3c61dd1f6511 (diff) |
Update to ruby/spec@a6b8805
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60525 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/ruby/core')
102 files changed, 2367 insertions, 122 deletions
diff --git a/spec/ruby/core/array/at_spec.rb b/spec/ruby/core/array/at_spec.rb index e40c26f2cc..f2af0e13a1 100644 --- a/spec/ruby/core/array/at_spec.rb +++ b/spec/ruby/core/array/at_spec.rb @@ -15,7 +15,7 @@ describe "Array#at" do a.at(7).should == nil end - it "returns the (-n)'th elemet from the last, for the given negative index n" do + it "returns the (-n)'th element from the last, for the given negative index n" do a = [1, 2, 3, 4, 5, 6] a.at(-1).should == 6 a.at(-2).should == 5 @@ -50,7 +50,7 @@ describe "Array#at" do lambda { [].at("cat") }.should raise_error(TypeError) end - it "raises an ArgumentError when 2 or more arguments is passed" do + it "raises an ArgumentError when 2 or more arguments are passed" do lambda { [:a, :b].at(0,1) }.should raise_error(ArgumentError) end end diff --git a/spec/ruby/core/array/intersection_spec.rb b/spec/ruby/core/array/intersection_spec.rb index 4d6c2a12d3..e399509ea7 100644 --- a/spec/ruby/core/array/intersection_spec.rb +++ b/spec/ruby/core/array/intersection_spec.rb @@ -19,7 +19,7 @@ describe "Array#&" do it "does not modify the original Array" do a = [1, 1, 3, 5] - a & [1, 2, 3] + (a & [1, 2, 3]).should == [1, 3] a.should == [1, 1, 3, 5] end @@ -52,7 +52,7 @@ describe "Array#&" do obj1.stub!(:hash).and_return(0) obj2.stub!(:hash).and_return(0) obj1.should_receive(:eql?).at_least(1).and_return(true) - obj2.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] diff --git a/spec/ruby/core/array/max_spec.rb b/spec/ruby/core/array/max_spec.rb index cf6a48c2e3..db1d755645 100644 --- a/spec/ruby/core/array/max_spec.rb +++ b/spec/ruby/core/array/max_spec.rb @@ -1,6 +1,12 @@ require File.expand_path('../../../spec_helper', __FILE__) describe "Array#max" do + ruby_version_is "2.4" do + it "is defined on Array" do + [1].method(:max).owner.should equal Array + end + end + it "returns nil with no values" do [].max.should == nil end diff --git a/spec/ruby/core/array/min_spec.rb b/spec/ruby/core/array/min_spec.rb index 53fe4e0692..59f3814da2 100644 --- a/spec/ruby/core/array/min_spec.rb +++ b/spec/ruby/core/array/min_spec.rb @@ -1,6 +1,12 @@ require File.expand_path('../../../spec_helper', __FILE__) describe "Array#min" do + ruby_version_is "2.4" do + it "is defined on Array" do + [1].method(:max).owner.should equal Array + end + end + it "returns nil with no values" do [].min.should == nil end diff --git a/spec/ruby/core/array/pack/buffer_spec.rb b/spec/ruby/core/array/pack/buffer_spec.rb new file mode 100644 index 0000000000..928f7db731 --- /dev/null +++ b/spec/ruby/core/array/pack/buffer_spec.rb @@ -0,0 +1,52 @@ +# encoding: ascii-8bit + +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is '2.4' do + describe "Aray#pack with `buffer` option" do + it "returns specified buffer" do + n = [ 65, 66, 67 ] + buffer = " "*3 + result = n.pack("ccc", buffer: buffer) #=> "ABC" + result.should equal(buffer) + end + + it "adds result at the end of buffer content" do + n = [ 65, 66, 67 ] # result without buffer is "ABC" + + buffer = "" + n.pack("ccc", buffer: buffer).should == "ABC" + + buffer = "123" + n.pack("ccc", buffer: buffer).should == "123ABC" + + buffer = "12345" + n.pack("ccc", buffer: buffer).should == "12345ABC" + end + + it "raises TypeError exception if buffer is not String" do + lambda { [65].pack("ccc", buffer: []) }.should raise_error( + TypeError, "buffer must be String, not Array") + end + + context "offset (@) is specified" do + it 'keeps buffer content if it is longer than offset' do + n = [ 65, 66, 67 ] + buffer = "123456" + n.pack("@3ccc", buffer: buffer).should == "123ABC" + end + + it "fills the gap with \0 if buffer content is shorter than offset" do + n = [ 65, 66, 67 ] + buffer = "123" + n.pack("@6ccc", buffer: buffer).should == "123\0\0\0ABC" + end + + it 'does not keep buffer content if it is longer than offset + result' do + n = [ 65, 66, 67 ] + buffer = "1234567890" + n.pack("@3ccc", buffer: buffer).should == "123ABC" + end + end + end +end diff --git a/spec/ruby/core/array/permutation_spec.rb b/spec/ruby/core/array/permutation_spec.rb index 8a80b93c3b..c0eba57a3e 100644 --- a/spec/ruby/core/array/permutation_spec.rb +++ b/spec/ruby/core/array/permutation_spec.rb @@ -52,7 +52,7 @@ describe "Array#permutation" do end it "returns no permutations when the given length has no permutations" do - @numbers.permutation(9).entries.size == 0 + @numbers.permutation(9).entries.size.should == 0 @numbers.permutation(9) { |n| @yielded << n } @yielded.should == [] end diff --git a/spec/ruby/core/array/pop_spec.rb b/spec/ruby/core/array/pop_spec.rb index 313dc4189e..ea649c6585 100644 --- a/spec/ruby/core/array/pop_spec.rb +++ b/spec/ruby/core/array/pop_spec.rb @@ -119,7 +119,7 @@ describe "Array#pop" do a.should == [] end - it "raises a TypeError when the passed n can be coerced to Integer" do + it "raises a TypeError when the passed n cannot be coerced to Integer" do lambda{ [1, 2].pop("cat") }.should raise_error(TypeError) lambda{ [1, 2].pop(nil) }.should raise_error(TypeError) end diff --git a/spec/ruby/core/array/shift_spec.rb b/spec/ruby/core/array/shift_spec.rb index e5d25e2050..a7b6f58392 100644 --- a/spec/ruby/core/array/shift_spec.rb +++ b/spec/ruby/core/array/shift_spec.rb @@ -104,7 +104,7 @@ describe "Array#shift" do a.should == [] end - it "raises a TypeError when the passed n can be coerced to Integer" do + it "raises a TypeError when the passed n cannot be coerced to Integer" do lambda{ [1, 2].shift("cat") }.should raise_error(TypeError) lambda{ [1, 2].shift(nil) }.should raise_error(TypeError) end diff --git a/spec/ruby/core/array/sum_spec.rb b/spec/ruby/core/array/sum_spec.rb new file mode 100644 index 0000000000..6548655c35 --- /dev/null +++ b/spec/ruby/core/array/sum_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.4' do + describe "Array#sum" do + it "returns the sum of elements" do + [1, 2, 3].sum.should == 6 + end + + it "applies a block to each element before adding if it's given" do + [1, 2, 3].sum { |i| i * 10 }.should == 60 + end + + it "returns init value if array is empty" do + [].sum(-1).should == -1 + end + + it "returns 0 if array is empty and init is omitted" do + [].sum.should == 0 + end + + it "adds init value to the sum of elemens" do + [1, 2, 3].sum(10).should == 16 + end + + it "can be used for non-numeric objects by providing init value" do + ["a", "b", "c"].sum("").should == "abc" + end + + it 'raises TypeError if any element are not numeric' do + lambda { ["a"].sum }.should raise_error(TypeError) + end + + it 'raises TypeError if any element cannot be added to init value' do + lambda { [1].sum([]) }.should raise_error(TypeError) + end + + it "calls + to sum the elements" do + a = mock("a") + b = mock("b") + a.should_receive(:+).with(b).and_return(42) + [b].sum(a).should == 42 + end + end +end diff --git a/spec/ruby/core/bignum/bignum_spec.rb b/spec/ruby/core/bignum/bignum_spec.rb index cf99bb4937..3df43aec2d 100644 --- a/spec/ruby/core/bignum/bignum_spec.rb +++ b/spec/ruby/core/bignum/bignum_spec.rb @@ -16,4 +16,16 @@ describe "Bignum" do Bignum.new end.should raise_error(NoMethodError) end + + ruby_version_is '2.4' do + it "unified into Integer" do + Bignum.should equal(Integer) + end + + it "is deprecated" do + -> { + Bignum + }.should complain(/constant ::Bignum is deprecated/) + end + end end diff --git a/spec/ruby/core/complex/finite_spec.rb b/spec/ruby/core/complex/finite_spec.rb new file mode 100644 index 0000000000..e9ee19bef3 --- /dev/null +++ b/spec/ruby/core/complex/finite_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "Complex#finite?" do + it "returns true if magnitude is finite" do + (1+1i).finite?.should == true + end + + it "returns false for positive infinity" do + value = Complex(Float::INFINITY, 42) + value.finite?.should == false + end + + it "returns false for positive complex with infinite imaginary" do + value = Complex(1, Float::INFINITY) + value.finite?.should == false + end + + it "returns false for negative infinity" do + value = -Complex(Float::INFINITY, 42) + value.finite?.should == false + end + + it "returns false for negative complex with infinite imaginary" do + value = -Complex(1, Float::INFINITY) + value.finite?.should == false + end + + ruby_bug "#14014", "2.4"..."2.5" do + it "returns false for NaN" do + value = Complex(Float::NAN, Float::NAN) + value.finite?.should == false + end + end + end +end diff --git a/spec/ruby/core/complex/infinite_spec.rb b/spec/ruby/core/complex/infinite_spec.rb new file mode 100644 index 0000000000..79792c3169 --- /dev/null +++ b/spec/ruby/core/complex/infinite_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "Complex#infinite?" do + it "returns nil if magnitude is finite" do + (1+1i).infinite?.should == nil + end + + it "returns 1 for positive infinity" do + value = Complex(Float::INFINITY, 42).infinite? + value.should == 1 + end + + it "returns 1 for positive complex with infinite imaginary" do + value = Complex(1, Float::INFINITY).infinite? + value.should == 1 + end + + it "returns -1 for negative infinity" do + value = -Complex(Float::INFINITY, 42).infinite? + value.should == -1 + end + + it "returns -1 for negative complex with infinite imaginary" do + value = -Complex(1, Float::INFINITY).infinite? + value.should == -1 + end + + it "returns nil for NaN" do + value = Complex(0, Float::NAN).infinite? + value.should == nil + end + end +end diff --git a/spec/ruby/core/dir/empty_spec.rb b/spec/ruby/core/dir/empty_spec.rb new file mode 100644 index 0000000000..861a538f84 --- /dev/null +++ b/spec/ruby/core/dir/empty_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "Dir.empty?" do + before :all do + @empty_dir = tmp("empty_dir") + mkdir_p @empty_dir + end + + after :all do + rm_r @empty_dir + end + + it "returns true for empty directories" do + result = Dir.empty? @empty_dir + result.should be_true + end + + it "returns false for non-empty directories" do + result = Dir.empty? __dir__ + result.should be_false + end + + it "returns false for a non-directory" do + result = Dir.empty? __FILE__ + result.should be_false + end + + it "raises ENOENT for nonexistent directories" do + lambda { Dir.empty? tmp("nonexistent") }.should raise_error(Errno::ENOENT) + end + end +end diff --git a/spec/ruby/core/dir/glob_spec.rb b/spec/ruby/core/dir/glob_spec.rb index b65b738b61..4a9230f8b3 100644 --- a/spec/ruby/core/dir/glob_spec.rb +++ b/spec/ruby/core/dir/glob_spec.rb @@ -68,7 +68,7 @@ describe "Dir.glob" do Dir.glob('**/', File::FNM_DOTMATCH).sort.should == expected end - # This is a seperate case to check **/ coming after a constant + # This is a separate case to check **/ coming after a constant # directory as well. it "recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH" do expected = %w[ diff --git a/spec/ruby/core/encoding/converter/primitive_convert_spec.rb b/spec/ruby/core/encoding/converter/primitive_convert_spec.rb index b9d6288bb2..ed479c6b13 100644 --- a/spec/ruby/core/encoding/converter/primitive_convert_spec.rb +++ b/spec/ruby/core/encoding/converter/primitive_convert_spec.rb @@ -89,7 +89,7 @@ with_feature :encoding do @ec.primitive_convert("","",nil,nil, {after_output: true}).should == :finished end - it "sets the destination buffer's encoding to the destination encoding if the conversion suceeded" do + it "sets the destination buffer's encoding to the destination encoding if the conversion succeeded" do dest = "".force_encoding('utf-8') dest.encoding.should == Encoding::UTF_8 @ec.primitive_convert("\u{98}",dest).should == :finished diff --git a/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb b/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb index f92c95c6d5..cba654b9fe 100644 --- a/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb +++ b/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb @@ -16,15 +16,13 @@ with_feature :encoding do it "returns [:source_buffer_empty,nil,nil,nil, nil] when #convert last succeeded" do ec = Encoding::Converter.new('ascii','utf-8') - ec.convert("a".force_encoding('ascii')).should == "a".\ - force_encoding('utf-8') + ec.convert("a".force_encoding('ascii')).should == "a".force_encoding('utf-8') ec.primitive_errinfo.should == [:source_buffer_empty, nil, nil, nil, nil] end it "returns [:destination_buffer_full,nil,nil,nil,nil] when #primitive_convert last returned :destination_buffer_full" do ec = Encoding::Converter.new("utf-8", "iso-2022-jp") - ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false) \ - .should == :destination_buffer_full + ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false).should == :destination_buffer_full ec.primitive_errinfo.should == [:destination_buffer_full, nil, nil, nil, nil] end diff --git a/spec/ruby/core/encoding/converter/putback_spec.rb b/spec/ruby/core/encoding/converter/putback_spec.rb index 69ce59e89b..3ed1ad9956 100644 --- a/spec/ruby/core/encoding/converter/putback_spec.rb +++ b/spec/ruby/core/encoding/converter/putback_spec.rb @@ -40,8 +40,7 @@ with_feature :encoding do src = "\x00\xd8\x61\x00" dst = "" ec.primitive_convert(src, dst).should == :invalid_byte_sequence - ec.primitive_errinfo.should == - [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"] + ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"] ec.putback(1).should == "\x00".force_encoding("utf-16le") ec.putback.should == "a".force_encoding("utf-16le") ec.putback.should == "" diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb index 41320c5207..428b292c68 100644 --- a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb +++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb @@ -23,7 +23,7 @@ with_feature :encoding do # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was # UTF-8, so UTF-8 is regarded as the source encoding. - it "is equal to the source encoding at the stage of the conversion path where the error occured" do + it "is equal to the source encoding at the stage of the conversion path where the error occurred" do @exception2.source_encoding_name.should == 'UTF-8' end end diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb index 75514e5229..09379acc5d 100644 --- a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb +++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb @@ -28,7 +28,7 @@ with_feature :encoding do # FIXME: Derive example where the failure occurs at the UTF-8 -> # ISO-8859-1 case so as to better illustrate the issue - it "is equal to the source encoding at the stage of the conversion path where the error occured" do + it "is equal to the source encoding at the stage of the conversion path where the error occurred" do @exception2.source_encoding.should == Encoding::EUC_JP end end diff --git a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb index 4a7aba2044..742184250f 100644 --- a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb +++ b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb @@ -23,7 +23,7 @@ with_feature :encoding do # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was # UTF-8, so UTF-8 is regarded as the source encoding. - it "is equal to the source encoding at the stage of the conversion path where the error occured" do + it "is equal to the source encoding at the stage of the conversion path where the error occurred" do @exception2.source_encoding_name.should == 'UTF-8' end end diff --git a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb index 984862646c..0489ad82bf 100644 --- a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb +++ b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb @@ -24,7 +24,7 @@ with_feature :encoding do # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was # UTF-8, so UTF-8 is regarded as the source encoding. - it "is equal to the source encoding at the stage of the conversion path where the error occured" do + it "is equal to the source encoding at the stage of the conversion path where the error occurred" do @exception2.source_encoding.should == Encoding::UTF_8 end end diff --git a/spec/ruby/core/enumerable/any_spec.rb b/spec/ruby/core/enumerable/any_spec.rb index 4f7af68b07..4a7511f649 100644 --- a/spec/ruby/core/enumerable/any_spec.rb +++ b/spec/ruby/core/enumerable/any_spec.rb @@ -68,11 +68,11 @@ describe "Enumerable#any?" do describe "with block" do it "returns true if the block ever returns other than false or nil" do - @enum.any? { true } == true - @enum.any? { 0 } == true - @enum.any? { 1 } == true + @enum.any? { true }.should == true + @enum.any? { 0 }.should == true + @enum.any? { 1 }.should == true - @enum1.any? { Object.new } == true + @enum1.any? { Object.new }.should == true @enum1.any?{ |o| o < 1 }.should == true @enum1.any?{ |o| 5 }.should == true diff --git a/spec/ruby/core/enumerable/cycle_spec.rb b/spec/ruby/core/enumerable/cycle_spec.rb index 2f5760992d..9089a94963 100644 --- a/spec/ruby/core/enumerable/cycle_spec.rb +++ b/spec/ruby/core/enumerable/cycle_spec.rb @@ -69,7 +69,7 @@ describe "Enumerable#cycle" do enum.cycle(obj).to_a.should == [3, 2, 1, 3, 2, 1] end - it "raises a TypeError when the passed n can be coerced to Integer" do + it "raises a TypeError when the passed n cannot be coerced to Integer" do enum = EnumerableSpecs::Numerous.new lambda{ enum.cycle("cat"){} }.should raise_error(TypeError) end diff --git a/spec/ruby/core/enumerable/drop_spec.rb b/spec/ruby/core/enumerable/drop_spec.rb index 1bcdc0ee9a..4013a639ce 100644 --- a/spec/ruby/core/enumerable/drop_spec.rb +++ b/spec/ruby/core/enumerable/drop_spec.rb @@ -34,7 +34,7 @@ describe "Enumerable#drop" do EnumerableSpecs::Numerous.new(3, 2, 1, :go).drop(4).should == [] end - it "raises a TypeError when the passed n can be coerced to Integer" do + it "raises a TypeError when the passed n cannot be coerced to Integer" do lambda{ @enum.drop("hat") }.should raise_error(TypeError) lambda{ @enum.drop(nil) }.should raise_error(TypeError) end diff --git a/spec/ruby/core/enumerable/uniq_spec.rb b/spec/ruby/core/enumerable/uniq_spec.rb index 0ede0170ce..5ca7556aed 100644 --- a/spec/ruby/core/enumerable/uniq_spec.rb +++ b/spec/ruby/core/enumerable/uniq_spec.rb @@ -7,6 +7,71 @@ ruby_version_is '2.4' do [0, 1, 2, 3].to_enum.uniq { |n| n.even? }.should == [0, 1] end + it "uses eql? semantics" do + [1.0, 1].to_enum.uniq.should == [1.0, 1] + end + + it "compares elements first with hash" do + x = mock('0') + x.should_receive(:hash).at_least(1).and_return(0) + y = mock('0') + y.should_receive(:hash).at_least(1).and_return(0) + + [x, y].to_enum.uniq.should == [x, y] + end + + it "does not compare elements with different hash codes via eql?" do + x = mock('0') + x.should_not_receive(:eql?) + y = mock('1') + y.should_not_receive(:eql?) + + x.should_receive(:hash).at_least(1).and_return(0) + y.should_receive(:hash).at_least(1).and_return(1) + + [x, y].to_enum.uniq.should == [x, y] + end + + it "compares elements with matching hash codes with #eql?" do + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) + + def obj.eql?(o) + # It's undefined whether the impl does a[0].eql?(a[1]) or + # a[1].eql?(a[0]) so we taint both. + taint + o.taint + false + end + + obj + end + + a.uniq.should == a + a[0].tainted?.should == true + a[1].tainted?.should == true + + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) + + def obj.eql?(o) + # It's undefined whether the impl does a[0].eql?(a[1]) or + # a[1].eql?(a[0]) so we taint both. + taint + o.taint + true + end + + obj + end + + a.to_enum.uniq.size.should == 1 + a[0].tainted?.should == true + a[1].tainted?.should == true + end + context 'when yielded with multiple arguments' do before :each do @enum = Object.new.to_enum diff --git a/spec/ruby/core/enumerator/lazy/force_spec.rb b/spec/ruby/core/enumerator/lazy/force_spec.rb index 03ff9a0fb6..1a218c1b0f 100644 --- a/spec/ruby/core/enumerator/lazy/force_spec.rb +++ b/spec/ruby/core/enumerator/lazy/force_spec.rb @@ -24,7 +24,7 @@ describe "Enumerator::Lazy#force" do (0..Float::INFINITY).lazy.map(&:succ).take(2).force.should == [1, 2] @eventsmixed.take(1).map(&:succ).force.should == [1] - ScratchPad.recorded == [:after_yields] + ScratchPad.recorded.should == [:before_yield] end end end diff --git a/spec/ruby/core/false/dup_spec.rb b/spec/ruby/core/false/dup_spec.rb new file mode 100644 index 0000000000..b04e641a41 --- /dev/null +++ b/spec/ruby/core/false/dup_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.4' do + describe "FalseClass#dup" do + it "returns self" do + false.dup.should equal(false) + end + end +end diff --git a/spec/ruby/core/file/empty_spec.rb b/spec/ruby/core/file/empty_spec.rb new file mode 100644 index 0000000000..766aa95e46 --- /dev/null +++ b/spec/ruby/core/file/empty_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/zero', __FILE__) + +describe "File.empty?" do + ruby_version_is "2.4" do + it_behaves_like :file_zero, :empty?, File + it_behaves_like :file_zero_missing, :empty?, File + + platform_is :solaris do + it "returns false for /dev/null" do + File.empty?('/dev/null').should == true + end + end + end +end diff --git a/spec/ruby/core/file/readlink_spec.rb b/spec/ruby/core/file/readlink_spec.rb index 6db2c09780..b529cd1355 100644 --- a/spec/ruby/core/file/readlink_spec.rb +++ b/spec/ruby/core/file/readlink_spec.rb @@ -3,7 +3,7 @@ require File.expand_path('../../../spec_helper', __FILE__) describe "File.readlink" do # symlink/readlink are not supported on Windows platform_is_not :windows do - describe "File.readlink with absolute paths" do + describe "with absolute paths" do before :each do @file = tmp('file_readlink.txt') @link = tmp('file_readlink.lnk') @@ -35,7 +35,26 @@ describe "File.readlink" do end end - describe "File.readlink when changing the working directory" do + describe "with paths containing unicode characters" do + before :each do + @file = tmp('tàrget.txt') + @link = tmp('lïnk.lnk') + File.symlink(@file, @link) + end + + after :each do + rm_r @file, @link + end + + it "returns the name of the file referenced by the given link" do + touch @file + result = File.readlink(@link) + result.encoding.should equal Encoding.find('filesystem') + result.should == @file.dup.force_encoding(Encoding.find('filesystem')) + end + end + + describe "when changing the working directory" do before :each do @cwd = Dir.pwd @tmpdir = tmp("/readlink") diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb index 80e37b3fff..9b423ae47e 100644 --- a/spec/ruby/core/file/shared/fnmatch.rb +++ b/spec/ruby/core/file/shared/fnmatch.rb @@ -75,11 +75,11 @@ describe :file_fnmatch, shared: true do File.send(@method, 'c*t', 'c/a/b/t').should == true end - it "matches ranges of characters using bracket expresions (e.g. [a-z])" do + it "matches ranges of characters using bracket expression (e.g. [a-z])" do File.send(@method, 'ca[a-z]', 'cat').should == true end - it "matches ranges of characters using bracket expresions, taking case into account" do + it "matches ranges of characters using bracket expression, taking case into account" do File.send(@method, '[a-z]', 'D').should == false File.send(@method, '[^a-z]', 'D').should == true File.send(@method, '[A-Z]', 'd').should == false @@ -92,7 +92,7 @@ describe :file_fnmatch, shared: true do File.send(@method, '/ca[s][s-t]/rul[a-b]/[z]he/[x-Z]orld', '/cats/rule/the/World').should == false end - it "matches ranges of characters using exclusive bracket expresions (e.g. [^t] or [!t])" do + it "matches ranges of characters using exclusive bracket expression (e.g. [^t] or [!t])" do File.send(@method, 'ca[^t]', 'cat').should == false File.send(@method, 'ca[!t]', 'cat').should == false end @@ -106,13 +106,13 @@ describe :file_fnmatch, shared: true do end platform_is_not :windows do - it "doesn't match case sensitive characters on platfroms with case sensitive paths, when flags include FNM_SYSCASE" do + it "doesn't match case sensitive characters on platforms with case sensitive paths, when flags include FNM_SYSCASE" do File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == false end end platform_is :windows do - it "matches case sensitive characters on platfroms with case insensitive paths, when flags include FNM_SYSCASE" do + it "matches case sensitive characters on platforms with case insensitive paths, when flags include FNM_SYSCASE" do File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == true end end diff --git a/spec/ruby/core/fixnum/fixnum_spec.rb b/spec/ruby/core/fixnum/fixnum_spec.rb index 8a050fd25e..7f72f95e94 100644 --- a/spec/ruby/core/fixnum/fixnum_spec.rb +++ b/spec/ruby/core/fixnum/fixnum_spec.rb @@ -16,4 +16,16 @@ describe "Fixnum" do Fixnum.new end.should raise_error(NoMethodError) end + + ruby_version_is '2.4' do + it "is unified into Integer" do + Fixnum.should equal(Integer) + end + + it "is deprecated" do + -> { + Fixnum + }.should complain(/constant ::Fixnum is deprecated/) + end + end end diff --git a/spec/ruby/core/float/ceil_spec.rb b/spec/ruby/core/float/ceil_spec.rb index 8037164c68..f1d9dcd823 100644 --- a/spec/ruby/core/float/ceil_spec.rb +++ b/spec/ruby/core/float/ceil_spec.rb @@ -10,4 +10,14 @@ describe "Float#ceil" do -9223372036854775808.1.ceil.should eql(-9223372036854775808) +9223372036854775808.1.ceil.should eql(+9223372036854775808) end + + ruby_version_is "2.4" do + it "returns the smallest number greater than or equal to self with an optionally given precision" do + 2.1679.ceil(0).should eql(3) + 214.94.ceil(-1).should eql(220) + 7.0.ceil(1).should eql(7.0) + -1.234.ceil(2).should eql(-1.23) + 5.123812.ceil(4).should eql(5.1239) + end + end end diff --git a/spec/ruby/core/float/dup_spec.rb b/spec/ruby/core/float/dup_spec.rb new file mode 100644 index 0000000000..775dc2913c --- /dev/null +++ b/spec/ruby/core/float/dup_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.4' do + describe "Float#dup" do + it "returns self" do + float = 2.4 + float.dup.should equal(float) + end + end +end diff --git a/spec/ruby/core/float/floor_spec.rb b/spec/ruby/core/float/floor_spec.rb index 63a5138366..6da5d5c5ee 100644 --- a/spec/ruby/core/float/floor_spec.rb +++ b/spec/ruby/core/float/floor_spec.rb @@ -10,4 +10,14 @@ describe "Float#floor" do -9223372036854775808.1.floor.should eql(-9223372036854775808) +9223372036854775808.1.floor.should eql(+9223372036854775808) end + + ruby_version_is "2.4" do + it "returns the largest number less than or equal to self with an optionally given precision" do + 2.1679.floor(0).should eql(2) + 214.94.floor(-1).should eql(210) + 7.0.floor(1).should eql(7.0) + -1.234.floor(2).should eql(-1.24) + 5.123812.floor(4).should eql(5.1238) + end + end end diff --git a/spec/ruby/core/float/round_spec.rb b/spec/ruby/core/float/round_spec.rb index 46d23b36d0..56668d5856 100644 --- a/spec/ruby/core/float/round_spec.rb +++ b/spec/ruby/core/float/round_spec.rb @@ -84,4 +84,18 @@ describe "Float#round" do -2.5e200.round(-200).should eql( -3 * 10 ** 200 ) -2.4e200.round(-200).should eql( -2 * 10 ** 200 ) end + + ruby_version_is "2.4" do + it "returns different rounded values depending on the half option" do + 2.5.round(half: :up).should eql(3) + 2.5.round(half: :down).should eql(2) + 2.5.round(half: :even).should eql(2) + 3.5.round(half: :up).should eql(4) + 3.5.round(half: :down).should eql(3) + 3.5.round(half: :even).should eql(4) + (-2.5).round(half: :up).should eql(-3) + (-2.5).round(half: :down).should eql(-2) + (-2.5).round(half: :even).should eql(-2) + end + end end diff --git a/spec/ruby/core/float/truncate_spec.rb b/spec/ruby/core/float/truncate_spec.rb index 85ee8ef580..7feeb81735 100644 --- a/spec/ruby/core/float/truncate_spec.rb +++ b/spec/ruby/core/float/truncate_spec.rb @@ -3,4 +3,14 @@ require File.expand_path('../shared/to_i', __FILE__) describe "Float#truncate" do it_behaves_like(:float_to_i, :truncate) + + ruby_version_is "2.4" do + it "returns self truncated to an optionally given precision" do + 2.1679.truncate(0).should eql(2) + 7.1.truncate(1).should eql(7.1) + 214.94.truncate(-1).should eql(210) + -1.234.truncate(2).should eql(-1.23) + 5.123812.truncate(4).should eql(5.1238) + end + end end diff --git a/spec/ruby/core/hash/delete_spec.rb b/spec/ruby/core/hash/delete_spec.rb index a45b8cd171..a41fad3586 100644 --- a/spec/ruby/core/hash/delete_spec.rb +++ b/spec/ruby/core/hash/delete_spec.rb @@ -11,13 +11,13 @@ describe "Hash#delete" do it "calls supplied block if the key is not found" do { a: 1, b: 10, c: 100 }.delete(:d) { 5 }.should == 5 Hash.new(:default).delete(:d) { 5 }.should == 5 - Hash.new { :defualt }.delete(:d) { 5 }.should == 5 + Hash.new { :default }.delete(:d) { 5 }.should == 5 end it "returns nil if the key is not found when no block is given" do { a: 1, b: 10, c: 100 }.delete(:d).should == nil Hash.new(:default).delete(:d).should == nil - Hash.new { :defualt }.delete(:d).should == nil + Hash.new { :default }.delete(:d).should == nil end # MRI explicitly implements this behavior diff --git a/spec/ruby/core/hash/transform_values_spec.rb b/spec/ruby/core/hash/transform_values_spec.rb index 7e4ff45bea..0c9e43d621 100644 --- a/spec/ruby/core/hash/transform_values_spec.rb +++ b/spec/ruby/core/hash/transform_values_spec.rb @@ -24,6 +24,15 @@ ruby_version_is "2.4" do enumerator.each(&:succ).should == { a: 2, b: 3, c: 4 } end end + + it "returns a Hash instance, even on subclasses" do + klass = Class.new(Hash) + h = klass.new + h[:foo] = 42 + r = h.transform_values{|v| 2 * v} + r[:foo].should == 84 + r.class.should == Hash + end end describe "Hash#transform_values!" do @@ -41,6 +50,14 @@ ruby_version_is "2.4" do @hash.should == { a: 2, b: 3, c: 4 } end + it "partially modifies the contents if we broke from the block" do + @hash.transform_values! do |v| + break if v == 3 + 100 + v + end + @hash.should == { a: 101, b: 102, c: 3} + end + context "when no block is given" do it "returns a sized Enumerator" do enumerator = @hash.transform_values! @@ -56,6 +73,10 @@ ruby_version_is "2.4" do @hash.freeze end + it "raises a RuntimeError on an empty hash" do + ->{ {}.freeze.transform_values!(&:succ) }.should raise_error(RuntimeError) + end + it "keeps pairs and raises a RuntimeError" do ->{ @hash.transform_values!(&:succ) }.should raise_error(RuntimeError) @hash.should == @initial_pairs diff --git a/spec/ruby/core/integer/ceil_spec.rb b/spec/ruby/core/integer/ceil_spec.rb index 7a49ede0df..31c56f378d 100644 --- a/spec/ruby/core/integer/ceil_spec.rb +++ b/spec/ruby/core/integer/ceil_spec.rb @@ -1,6 +1,21 @@ require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../shared/to_i', __FILE__) +require File.expand_path('../shared/integer_rounding', __FILE__) describe "Integer#ceil" do it_behaves_like(:integer_to_i, :ceil) + it_behaves_like(:integer_rounding_positive_precision, :ceil) + + ruby_version_is "2.4" do + context "precision argument specified as part of the ceil method is negative" do + it "returns the smallest integer greater than self with at least precision.abs trailing zeros" do + 18.ceil(-1).should eql(20) + 18.ceil(-2).should eql(100) + 18.ceil(-3).should eql(1000) + -1832.ceil(-1).should eql(-1830) + -1832.ceil(-2).should eql(-1800) + -1832.ceil(-3).should eql(-1000) + end + end + end end diff --git a/spec/ruby/core/integer/digits_spec.rb b/spec/ruby/core/integer/digits_spec.rb new file mode 100644 index 0000000000..3546a654eb --- /dev/null +++ b/spec/ruby/core/integer/digits_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "Integer#digits" do + it "returns an array of place values in base-10 by default" do + 12345.digits.should == [5,4,3,2,1] + end + + it "returns digits by place value of a given radix" do + 12345.digits(7).should == [4,6,6,0,5] + end + + it "converts the radix with #to_int" do + 12345.digits(mock_int(2)).should == [1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1] + end + + it "returns [0] when called on 0, regardless of base" do + 0.digits.should == [0] + 0.digits(7).should == [0] + end + + it "raises ArgumentError when calling with a radix less than 2" do + lambda { 12345.digits(1) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError when calling with a negative radix" do + lambda { 12345.digits(-2) }.should raise_error(ArgumentError) + end + + it "raises Math::DomainError when calling digits on a negative number" do + lambda { -12345.digits(7) }.should raise_error(Math::DomainError) + end + end +end diff --git a/spec/ruby/core/integer/dup_spec.rb b/spec/ruby/core/integer/dup_spec.rb new file mode 100644 index 0000000000..f46bdf89bd --- /dev/null +++ b/spec/ruby/core/integer/dup_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.4' do + describe "Integer#dup" do + it "returns self" do + int = 2 + int.dup.should equal(int) + end + end +end diff --git a/spec/ruby/core/integer/floor_spec.rb b/spec/ruby/core/integer/floor_spec.rb index b533a84ad4..9babcd9a3e 100644 --- a/spec/ruby/core/integer/floor_spec.rb +++ b/spec/ruby/core/integer/floor_spec.rb @@ -1,6 +1,21 @@ require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../shared/to_i', __FILE__) +require File.expand_path('../shared/integer_rounding', __FILE__) describe "Integer#floor" do it_behaves_like(:integer_to_i, :floor) + it_behaves_like(:integer_rounding_positive_precision, :floor) + + ruby_version_is "2.4" do + context "precision argument specified as part of the floor method is negative" do + it "returns the largest integer less than self with at least precision.abs trailing zeros" do + 1832.floor(-1).should eql(1830) + 1832.floor(-2).should eql(1800) + 1832.floor(-3).should eql(1000) + -1832.floor(-1).should eql(-1840) + -1832.floor(-2).should eql(-1900) + -1832.floor(-3).should eql(-2000) + end + end + end end diff --git a/spec/ruby/core/integer/integer_spec.rb b/spec/ruby/core/integer/integer_spec.rb index a6f406cba0..393f072563 100644 --- a/spec/ruby/core/integer/integer_spec.rb +++ b/spec/ruby/core/integer/integer_spec.rb @@ -4,6 +4,13 @@ describe "Integer" do it "includes Comparable" do Integer.include?(Comparable).should == true end + + ruby_version_is "2.4" do + it "is the class of both small and large integers" do + 42.class.should equal(Integer) + bignum_value.class.should equal(Integer) + end + end end describe "Integer#integer?" do diff --git a/spec/ruby/core/integer/round_spec.rb b/spec/ruby/core/integer/round_spec.rb index 5cc9aa3881..5a46e6cba6 100644 --- a/spec/ruby/core/integer/round_spec.rb +++ b/spec/ruby/core/integer/round_spec.rb @@ -1,10 +1,12 @@ require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../shared/to_i', __FILE__) +require File.expand_path('../shared/integer_rounding', __FILE__) describe "Integer#round" do it_behaves_like(:integer_to_i, :round) + it_behaves_like(:integer_rounding_positive_precision, :round) - ruby_version_is ""..."2.5" do + ruby_version_is ""..."2.5" do # Not just since 2.4 it "rounds itself as a float if passed a positive precision" do [2, -4, 10**70, -10**100].each do |v| v.round(42).should eql(v.to_f) @@ -12,20 +14,6 @@ describe "Integer#round" do end end - ruby_version_is "2.5" do - it "returns itself if passed a positive precision" do - [2, -4, 10**70, -10**100].each do |v| - v.round(42).should eql(v) - end - end - end - - it "returns itself if passed zero" do - [2, -4, 10**70, -10**100].each do |v| - v.round(0).should eql(v) - end - end - # redmine:5228 it "returns itself rounded if passed a negative value" do +249.round(-2).should eql(+200) @@ -74,4 +62,34 @@ describe "Integer#round" do obj.stub!(:to_int).and_return([]) lambda { 42.round(obj) }.should raise_error(TypeError) end + + ruby_version_is "2.4" do + it "returns different rounded values depending on the half option" do + 25.round(-1, half: :up).should eql(30) + 25.round(-1, half: :down).should eql(20) + 25.round(-1, half: :even).should eql(20) + 35.round(-1, half: :up).should eql(40) + 35.round(-1, half: :down).should eql(30) + 35.round(-1, half: :even).should eql(40) + (-25).round(-1, half: :up).should eql(-30) + (-25).round(-1, half: :down).should eql(-20) + (-25).round(-1, half: :even).should eql(-20) + end + end + + ruby_version_is "2.4"..."2.5" do + it "returns itself as a float if passed a positive precision and the half option" do + 35.round(1, half: :up).should eql(35.0) + 35.round(1, half: :down).should eql(35.0) + 35.round(1, half: :even).should eql(35.0) + end + end + + ruby_version_is "2.5" do + it "returns itself if passed a positive precision and the half option" do + 35.round(1, half: :up).should eql(35) + 35.round(1, half: :down).should eql(35) + 35.round(1, half: :even).should eql(35) + end + end end diff --git a/spec/ruby/core/integer/shared/integer_rounding.rb b/spec/ruby/core/integer/shared/integer_rounding.rb new file mode 100644 index 0000000000..ecbda1bb4a --- /dev/null +++ b/spec/ruby/core/integer/shared/integer_rounding.rb @@ -0,0 +1,31 @@ +describe :integer_rounding_positive_precision, shared: true do + it "returns self if not passed a precision" do + [2, -4, 10**70, -10**100].each do |v| + v.send(@method).should eql(v) + end + end + + ruby_version_is "2.4" do + it "returns self if passed a precision of zero" do + [2, -4, 10**70, -10**100].each do |v| + v.send(@method, 0).should eql(v) + end + end + end + + ruby_version_is "2.4"..."2.5" do + it "returns itself as a float if passed a positive precision" do + [2, -4, 10**70, -10**100].each do |v| + v.send(@method, 42).should eql(v.to_f) + end + end + end + + ruby_version_is "2.5" do + it "returns itself if passed a positive precision" do + [2, -4, 10**70, -10**100].each do |v| + v.send(@method, 42).should eql(v) + end + end + end +end diff --git a/spec/ruby/core/integer/sqrt_spec.rb b/spec/ruby/core/integer/sqrt_spec.rb new file mode 100644 index 0000000000..b7d9ef441b --- /dev/null +++ b/spec/ruby/core/integer/sqrt_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.5" do + describe "Integer.sqrt" do + it "returns an integer" do + Integer.sqrt(10).should be_kind_of(Integer) + end + + it "returns the integer square root of the argument" do + Integer.sqrt(0).should == 0 + Integer.sqrt(1).should == 1 + Integer.sqrt(24).should == 4 + Integer.sqrt(25).should == 5 + Integer.sqrt(10**400).should == 10**200 + end + + it "raises a Math::DomainError if the argument is negative" do + lambda { Integer.sqrt(-4) }.should raise_error(Math::DomainError) + end + + it "accepts any argument that can be coerced to Integer" do + Integer.sqrt(10.0).should == 3 + end + + it "converts the argument with #to_int" do + Integer.sqrt(mock_int(10)).should == 3 + end + + it "raises a TypeError if the argument cannot be coerced to Integer" do + lambda { Integer.sqrt("test") }.should raise_error(TypeError) + end + end +end diff --git a/spec/ruby/core/integer/truncate_spec.rb b/spec/ruby/core/integer/truncate_spec.rb index b503b1e4e8..429ab1a312 100644 --- a/spec/ruby/core/integer/truncate_spec.rb +++ b/spec/ruby/core/integer/truncate_spec.rb @@ -1,6 +1,21 @@ require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../shared/to_i', __FILE__) +require File.expand_path('../shared/integer_rounding', __FILE__) describe "Integer#truncate" do it_behaves_like(:integer_to_i, :truncate) + it_behaves_like(:integer_rounding_positive_precision, :truncate) + + ruby_version_is "2.4" do + context "precision argument specified as part of the truncate method is negative" do + it "returns an integer with at least precision.abs trailing zeros" do + 1832.truncate(-1).should eql(1830) + 1832.truncate(-2).should eql(1800) + 1832.truncate(-3).should eql(1000) + -1832.truncate(-1).should eql(-1830) + -1832.truncate(-2).should eql(-1800) + -1832.truncate(-3).should eql(-1000) + end + end + end end diff --git a/spec/ruby/core/io/advise_spec.rb b/spec/ruby/core/io/advise_spec.rb index 460b50a59d..44cd32df94 100644 --- a/spec/ruby/core/io/advise_spec.rb +++ b/spec/ruby/core/io/advise_spec.rb @@ -17,7 +17,7 @@ describe "IO#advise" do }.should raise_error(TypeError) end - it "raises a TypeError if offsert cannot be coerced to an Integer" do + it "raises a TypeError if offset cannot be coerced to an Integer" do lambda { @io.advise(:normal, "wat") }.should raise_error(TypeError) diff --git a/spec/ruby/core/io/fixtures/classes.rb b/spec/ruby/core/io/fixtures/classes.rb index fb431d5023..a771e3d929 100644 --- a/spec/ruby/core/io/fixtures/classes.rb +++ b/spec/ruby/core/io/fixtures/classes.rb @@ -20,6 +20,18 @@ module IOSpecs "Here is line six.\n" ] end + def self.lines_without_newline_characters + [ "Voici la ligne une.", + "Qui \303\250 la linea due.", + "", + "", + "Aqu\303\255 est\303\241 la l\303\255nea tres.", + "Hier ist Zeile vier.", + "", + "Est\303\241 aqui a linha cinco.", + "Here is line six." ] + end + def self.lines_limit [ "Voici la l", "igne une.\n", diff --git a/spec/ruby/core/io/gets_spec.rb b/spec/ruby/core/io/gets_spec.rb index d984e795c2..6e0518b512 100644 --- a/spec/ruby/core/io/gets_spec.rb +++ b/spec/ruby/core/io/gets_spec.rb @@ -138,6 +138,14 @@ describe "IO#gets" do end end end + + ruby_version_is "2.4" do + describe "when passed chomp" do + it "returns the first line without a trailing newline character" do + @io.gets(chomp: true).should == IOSpecs.lines_without_newline_characters[0] + end + end + end end describe "IO#gets" do @@ -191,7 +199,7 @@ describe "IO#gets" do @io.gets(obj, 5).should == "one\n" end - it "reads to the default seperator when passed a single argument greater than the number of bytes to the separator" do + it "reads to the default separator when passed a single argument greater than the number of bytes to the separator" do @io.gets(6).should == "one\n" end diff --git a/spec/ruby/core/io/readline_spec.rb b/spec/ruby/core/io/readline_spec.rb index 39706948eb..a1cddafe5c 100644 --- a/spec/ruby/core/io/readline_spec.rb +++ b/spec/ruby/core/io/readline_spec.rb @@ -42,4 +42,12 @@ describe "IO#readline" do $_.should == line end end + + ruby_version_is "2.4" do + describe "when passed chomp" do + it "returns the first line without a trailing newline character" do + @io.readline(chomp: true).should == IOSpecs.lines_without_newline_characters[0] + end + end + end end diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb index dc07434ecd..d87303b54a 100644 --- a/spec/ruby/core/io/shared/each.rb +++ b/spec/ruby/core/io/shared/each.rb @@ -114,6 +114,15 @@ describe :io_each, shared: true do ScratchPad.recorded.should == IOSpecs.paragraphs end end + + ruby_version_is "2.4" do + describe "when passed chomp" do + it "yields each line without trailing newline characters to the passed block" do + @io.send(@method, chomp: true) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters + end + end + end end describe :io_each_default_separator, shared: true do diff --git a/spec/ruby/core/io/shared/readlines.rb b/spec/ruby/core/io/shared/readlines.rb index 4cb821274a..f545d8876a 100644 --- a/spec/ruby/core/io/shared/readlines.rb +++ b/spec/ruby/core/io/shared/readlines.rb @@ -17,6 +17,13 @@ describe :io_readlines, shared: true do result = IO.send(@method, @name, "", &@object) (result ? result : ScratchPad.recorded).should == IOSpecs.lines_empty_separator end + + ruby_version_is "2.4" do + it "yields a sequence of lines without trailing newline characters when chomp is passed" do + result = IO.send(@method, @name, chomp: true, &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_without_newline_characters + end + end end describe :io_readlines_options_19, shared: true do diff --git a/spec/ruby/core/kernel/equal_value_spec.rb b/spec/ruby/core/kernel/equal_value_spec.rb index 670987ead2..a66493f283 100644 --- a/spec/ruby/core/kernel/equal_value_spec.rb +++ b/spec/ruby/core/kernel/equal_value_spec.rb @@ -7,9 +7,9 @@ describe "Kernel#==" do o2 = mock('o2') (o1 == o1).should == true (o2 == o2).should == true - (o1 == o2).should== false + (o1 == o2).should == false (nil == nil).should == true - (o1 == nil).should== false - (nil == o2).should== false + (o1 == nil).should == false + (nil == o2).should == false end end diff --git a/spec/ruby/core/main/fixtures/classes.rb b/spec/ruby/core/main/fixtures/classes.rb index 0b74080492..6aba948ce0 100644 --- a/spec/ruby/core/main/fixtures/classes.rb +++ b/spec/ruby/core/main/fixtures/classes.rb @@ -4,8 +4,11 @@ module MainSpecs module WrapIncludeModule end + + DATA = {} end + def main_public_method end public :main_public_method diff --git a/spec/ruby/core/main/fixtures/string_refinement.rb b/spec/ruby/core/main/fixtures/string_refinement.rb new file mode 100644 index 0000000000..2dc6de52ca --- /dev/null +++ b/spec/ruby/core/main/fixtures/string_refinement.rb @@ -0,0 +1,7 @@ +module StringRefinement + refine(String) do + def foo + 'foo' + end + end +end diff --git a/spec/ruby/core/main/fixtures/string_refinement_user.rb b/spec/ruby/core/main/fixtures/string_refinement_user.rb new file mode 100644 index 0000000000..48620c325f --- /dev/null +++ b/spec/ruby/core/main/fixtures/string_refinement_user.rb @@ -0,0 +1,11 @@ +using StringRefinement + +module MainSpecs + DATA[:in_module] = 'hello'.foo + + def self.call_foo(x) + x.foo + end +end + +MainSpecs::DATA[:toplevel] = 'hello'.foo diff --git a/spec/ruby/core/main/using_spec.rb b/spec/ruby/core/main/using_spec.rb new file mode 100644 index 0000000000..ed18cdca39 --- /dev/null +++ b/spec/ruby/core/main/using_spec.rb @@ -0,0 +1,134 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.0.0" do + require File.expand_path('../fixtures/classes', __FILE__) + require File.expand_path('../fixtures/string_refinement', __FILE__) + + describe "main.using" do + it "requires one Module argument" do + lambda do + eval('using', TOPLEVEL_BINDING) + end.should raise_error(ArgumentError) + + lambda do + eval('using "foo"', TOPLEVEL_BINDING) + end.should raise_error(TypeError) + end + + it "uses refinements from the given module only in the target file" do + load File.expand_path('../fixtures/string_refinement_user.rb', __FILE__) + MainSpecs::DATA[:in_module].should == 'foo' + MainSpecs::DATA[:toplevel].should == 'foo' + lambda do + 'hello'.foo + end.should raise_error(NoMethodError) + end + + it "uses refinements from the given module for method calls in the target file" do + load File.expand_path('../fixtures/string_refinement_user.rb', __FILE__) + lambda do + 'hello'.foo + end.should raise_error(NoMethodError) + MainSpecs.call_foo('hello').should == 'foo' + end + + it "uses refinements from the given module in the eval string" do + cls = MainSpecs::DATA[:cls] = Class.new {def foo; 'foo'; end} + MainSpecs::DATA[:mod] = Module.new do + refine(cls) do + def foo; 'bar'; end + end + end + eval(<<-EOS, TOPLEVEL_BINDING).should == 'bar' + using MainSpecs::DATA[:mod] + MainSpecs::DATA[:cls].new.foo + EOS + end + + it "does not affect methods defined before it is called" do + cls = Class.new {def foo; 'foo'; end} + MainSpecs::DATA[:mod] = Module.new do + refine(cls) do + def foo; 'bar'; end + end + end + x = MainSpecs::DATA[:x] = Object.new + eval <<-EOS, TOPLEVEL_BINDING + x = MainSpecs::DATA[:x] + def x.before_using(obj) + obj.foo + end + using MainSpecs::DATA[:mod] + def x.after_using(obj) + obj.foo + end + EOS + + obj = cls.new + x.before_using(obj).should == 'foo' + x.after_using(obj).should == 'bar' + end + + it "propagates refinements added to existing modules after it is called" do + cls = Class.new {def foo; 'foo'; end} + mod = MainSpecs::DATA[:mod] = Module.new do + refine(cls) do + def foo; 'quux'; end + end + end + x = MainSpecs::DATA[:x] = Object.new + eval <<-EOS, TOPLEVEL_BINDING + using MainSpecs::DATA[:mod] + x = MainSpecs::DATA[:x] + def x.call_foo(obj) + obj.foo + end + def x.call_bar(obj) + obj.bar + end + EOS + + obj = cls.new + x.call_foo(obj).should == 'quux' + + mod.module_eval do + refine(cls) do + def bar; 'quux'; end + end + end + + x.call_bar(obj).should == 'quux' + end + + it "does not propagate refinements of new modules added after it is called" do + cls = Class.new {def foo; 'foo'; end} + cls2 = Class.new {def bar; 'bar'; end} + mod = MainSpecs::DATA[:mod] = Module.new do + refine(cls) do + def foo; 'quux'; end + end + end + x = MainSpecs::DATA[:x] = Object.new + eval <<-EOS, TOPLEVEL_BINDING + using MainSpecs::DATA[:mod] + x = MainSpecs::DATA[:x] + def x.call_foo(obj) + obj.foo + end + def x.call_bar(obj) + obj.bar + end + EOS + + x.call_foo(cls.new).should == 'quux' + + mod.module_eval do + refine(cls2) do + def bar; 'quux'; end + end + end + + x.call_bar(cls2.new).should == 'bar' + end + end +end diff --git a/spec/ruby/core/module/fixtures/refine.rb b/spec/ruby/core/module/fixtures/refine.rb new file mode 100644 index 0000000000..46975361dd --- /dev/null +++ b/spec/ruby/core/module/fixtures/refine.rb @@ -0,0 +1,13 @@ +module ModuleSpecs + class ClassWithFoo + def foo; "foo" end + end + + module PrependedModule + def foo; "foo from prepended module"; end + end + + module IncludedModule + def foo; "foo from included module"; end + end +end diff --git a/spec/ruby/core/module/fixtures/using.rb b/spec/ruby/core/module/fixtures/using.rb new file mode 100644 index 0000000000..0ed9355af1 --- /dev/null +++ b/spec/ruby/core/module/fixtures/using.rb @@ -0,0 +1,10 @@ +module ModuleSpecs + module EmptyRefinement + end + + module RefinementForStringToS + refine String do + def to_s; "hello from refinement"; end + end + end +end diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb new file mode 100644 index 0000000000..03e70915e2 --- /dev/null +++ b/spec/ruby/core/module/refine_spec.rb @@ -0,0 +1,616 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/refine', __FILE__) + +describe "Module#refine" do + it "runs its block in an anonymous module" do + inner_self = nil + mod = Module.new do + refine String do + inner_self = self + end + end + + mod.should_not == inner_self + inner_self.should be_kind_of(Module) + inner_self.name.should == nil + end + + it "uses the same anonymous module for future refines of the same class" do + selves = [] + mod = Module.new do + refine String do + selves << self + end + end + + mod.module_eval do + refine String do + selves << self + end + end + + selves[0].should == selves[1] + end + + it "adds methods defined in its block to the anonymous module's public instance methods" do + inner_self = nil + mod = Module.new do + refine String do + def blah + "blah" + end + inner_self = self + end + end + + inner_self.public_instance_methods.should include(:blah) + end + + it "returns created anonymous module" do + inner_self = nil + result = nil + mod = Module.new do + result = refine String do + inner_self = self + end + end + + result.should == inner_self + end + + it "raises ArgumentError if not passed an argument" do + lambda do + Module.new do + refine {} + end + end.should raise_error(ArgumentError) + end + + it "raises TypeError if not passed a class" do + lambda do + Module.new do + refine("foo") {} + end + end.should raise_error(TypeError) + end + + ruby_version_is "" ... "2.4" do + it "raises TypeError if passed a module" do + lambda do + Module.new do + refine(Enumerable) {} + end + end.should raise_error(TypeError) + end + end + + ruby_version_is "2.4" do + it "accepts a module as argument" do + inner_self = nil + Module.new do + refine(Enumerable) do + def blah + end + inner_self = self + end + end + + inner_self.public_instance_methods.should include(:blah) + end + end + + it "raises ArgumentError if not given a block" do + lambda do + Module.new do + refine String + end + end.should raise_error(ArgumentError) + end + + it "applies refinements to calls in the refine block" do + result = nil + Module.new do + refine(String) do + def foo; "foo"; end + result = "hello".foo + end + end + result.should == "foo" + end + + it "doesn't apply refinements outside the refine block" do + Module.new do + refine(String) {def foo; "foo"; end} + -> () { + "hello".foo + }.should raise_error(NoMethodError) + end + end + + it "does not apply refinements to external scopes not using the module" do + Module.new do + refine(String) {def foo; 'foo'; end} + end + + lambda {"hello".foo}.should raise_error(NoMethodError) + end + + # When defining multiple refinements in the same module, + # inside a refine block all refinements from the same + # module are active when a refined method is called + it "makes available all refinements from the same module" do + refinement = Module.new do + refine Integer do + def to_json_format + to_s + end + end + + refine Array do + def to_json_format + "[" + map { |i| i.to_json_format }.join(", ") + "]" + end + end + + refine Hash do + def to_json_format + "{" + map { |k, v| k.to_s.dump + ": " + v.to_json_format }.join(", ") + "}" + end + end + end + + result = nil + + Module.new do + using refinement + + result = [{1 => 2}, {3 => 4}].to_json_format + end + + result.should == '[{"1": 2}, {"3": 4}]' + end + + it "does not make available methods from another refinement module" do + refinery_integer = Module.new do + refine Integer do + def to_json_format + to_s + end + end + end + + refinery_array = Module.new do + refine Array do + def to_json_format + "[" + map { |i| i.to_json_format }.join(",") + "]" + end + end + end + + result = nil + + -> () { + Module.new do + using refinery_integer + using refinery_array + + [1, 2].to_json_format + end + }.should raise_error(NoMethodError) + end + + # method lookup: + # * The prepended modules from the refinement for C + # * The refinement for C + # * The included modules from the refinement for C + # * The prepended modules of C + # * C + # * The included modules of C + describe "method lookup" do + it "looks in the object singleton class first" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + + obj = ModuleSpecs::ClassWithFoo.new + class << obj + def foo; "foo from singleton class"; end + end + result = obj.foo + end + + result.should == "foo from singleton class" + end + + it "looks in prepended modules from the refinement first" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + include ModuleSpecs::IncludedModule + prepend ModuleSpecs::PrependedModule + + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.foo + end + + result.should == "foo from prepended module" + end + + it "looks in refinement then" do + refinement = Module.new do + refine(ModuleSpecs::ClassWithFoo) do + include ModuleSpecs::IncludedModule + + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.foo + end + + result.should == "foo from refinement" + end + + it "looks in included modules from the refinement then" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + include ModuleSpecs::IncludedModule + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.foo + end + + result.should == "foo from included module" + end + + it "looks in the class then" do + refinement = Module.new do + refine(ModuleSpecs::ClassWithFoo) { } + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.foo + end + + result.should == "foo" + end + end + + + # methods in a subclass have priority over refinements in a superclass + it "does not override methods in subclasses" do + subclass = Class.new(ModuleSpecs::ClassWithFoo) do + def foo; "foo from subclass"; end + end + + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + result = subclass.new.foo + end + + result.should == "foo from subclass" + end + + context "for methods accesses indirectly" do + ruby_version_is "" ... "2.4" do + it "is not honored by Kernel#send" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.send :foo + end + + result.should == "foo" + end + + it "is not honored by BasicObject#__send__" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.__send__ :foo + end + + result.should == "foo" + end + + it "is not honored by Symbol#to_proc" do + refinement = Module.new do + refine Integer do + def to_s + "(#{super})" + end + end + end + + result = nil + Module.new do + using refinement + result = [1, 2, 3].map(&:to_s) + end + + result.should == ["1", "2", "3"] + end + end + + ruby_version_is "2.4" do + it "is honored by Kernel#send" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.send :foo + end + + result.should == "foo from refinement" + end + + it "is honored by BasicObject#__send__" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.__send__ :foo + end + + result.should == "foo from refinement" + end + + it "is honored by Symbol#to_proc" do + refinement = Module.new do + refine Integer do + def to_s + "(#{super})" + end + end + end + + result = nil + Module.new do + using refinement + result = [1, 2, 3].map(&:to_s) + end + + result.should == ["(1)", "(2)", "(3)"] + end + end + + it "is honored by Kernel#binding" do + refinement = Module.new do + refine String do + def to_s + "hello from refinement" + end + end + end + + klass = Class.new do + using refinement + + def foo + "foo".to_s + end + + def get_binding + binding + end + end + + result = Kernel.eval("self.foo()", klass.new.get_binding) + result.should == "hello from refinement" + end + + it "is not honored by Kernel#method" do + klass = Class.new + refinement = Module.new do + refine klass do + def foo; end + end + end + + -> { + Module.new do + using refinement + klass.new.method(:foo) + end + }.should raise_error(NameError, /undefined method `foo'/) + end + + it "is not honored by Kernel#respond_to?" do + klass = Class.new + refinement = Module.new do + refine klass do + def foo; end + end + end + + result = nil + Module.new do + using refinement + result = klass.new.respond_to?(:foo) + end + + result.should == false + end + end + + context "when super is called in a refinement" do + it "looks in the included to refinery module" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + include ModuleSpecs::IncludedModule + + def foo + super + end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.foo + end + + result.should == "foo from included module" + end + + it "looks in the refined class" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo + super + end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.foo + end + + result.should == "foo" + end + + # super in a method of a refinement invokes the method in the refined + # class even if there is another refinement which has been activated + # in the same context. + it "looks in the refined class even if there is another active refinement" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo + "foo from refinement" + end + end + end + + refinement_with_super = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo + super + end + end + end + + result = nil + Module.new do + using refinement + using refinement_with_super + result = ModuleSpecs::ClassWithFoo.new.foo + end + + result.should == "foo" + end + end + + # Refinements are inherited by module inclusion. + # That is, using activates all refinements in the ancestors of the specified module. + # Refinements in a descendant have priority over refinements in an ancestor. + context "module inclusion" do + it "activates all refinements from all ancestors" do + refinement_included = Module.new do + refine Integer do + def to_json_format + to_s + end + end + end + + refinement = Module.new do + include refinement_included + + refine Array do + def to_json_format + "[" + map { |i| i.to_s }.join(", ") + "]" + end + end + end + + result = nil + Module.new do + using refinement + result = [5.to_json_format, [1, 2, 3].to_json_format] + end + + result.should == ["5", "[1, 2, 3]"] + end + + it "overrides methods of ancestors by methods in descendants" do + refinement_included = Module.new do + refine Integer do + def to_json_format + to_s + end + end + end + + refinement = Module.new do + include refinement_included + + refine Integer do + def to_json_format + "hello from refinement" + end + end + end + + result = nil + Module.new do + using refinement + result = 5.to_json_format + end + + result.should == "hello from refinement" + end + end +end + diff --git a/spec/ruby/core/module/using_spec.rb b/spec/ruby/core/module/using_spec.rb new file mode 100644 index 0000000000..32e8f8f96b --- /dev/null +++ b/spec/ruby/core/module/using_spec.rb @@ -0,0 +1,276 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/using', __FILE__) + +describe "Module#using" do + it "imports class refinements from module into the current class/module" do + refinement = Module.new do + refine Integer do + def foo; "foo"; end + end + end + + result = nil + Module.new do + using refinement + result = 1.foo + end + + result.should == "foo" + end + + it "accepts module as argument" do + refinement = Module.new do + refine Integer do + def foo; "foo"; end + end + end + + -> () { + Module.new do + using refinement + end + }.should_not raise_error + end + + it "accepts module without refinements" do + mod = Module.new + + -> () { + Module.new do + using mod + end + }.should_not raise_error + end + + it "does not accept class" do + klass = Class.new + + -> () { + Module.new do + using klass + end + }.should raise_error(TypeError) + end + + it "raises TypeError if passed something other than module" do + -> () { + Module.new do + using "foo" + end + }.should raise_error(TypeError) + end + + it "returns self" do + refinement = Module.new + + result = nil + mod = Module.new do + result = using refinement + end + + result.should equal(mod) + end + + it "works in classes too" do + refinement = Module.new do + refine Integer do + def foo; "foo"; end + end + end + + result = nil + Class.new do + using refinement + result = 1.foo + end + + result.should == "foo" + end + + it "raises error in method scope" do + mod = Module.new do + def self.foo + using ModuleSpecs::EmptyRefinement + end + end + + -> () { + mod.foo + }.should raise_error(RuntimeError, /Module#using is not permitted in methods/) + end + + it "activates refinement even for existed objects" do + result = nil + + Module.new do + klass = Class.new do + def foo; "foo"; end + end + + refinement = Module.new do + refine klass do + def foo; "foo from refinement"; end + end + end + + obj = klass.new + using refinement + result = obj.foo + end + + result.should == "foo from refinement" + end + + it "activates updates when refinement reopens later" do + result = nil + + Module.new do + klass = Class.new do + def foo; "foo"; end + end + + refinement = Module.new do + refine klass do + def foo; "foo from refinement"; end + end + end + + using refinement + + refinement.class_eval do + refine klass do + def foo; "foo from reopened refinement"; end + end + end + + obj = klass.new + result = obj.foo + end + + result.should == "foo from reopened refinement" + end + + describe "scope of refinement" do + it "is active until the end of current class/module" do + ScratchPad.record [] + + Module.new do + Class.new do + using ModuleSpecs::RefinementForStringToS + ScratchPad << "1".to_s + end + + ScratchPad << "1".to_s + end + + ScratchPad.recorded.should == ["hello from refinement", "1"] + end + + # Refinements are lexical in scope. + # Refinements are only active within a scope after the call to using. + # Any code before the using statement will not have the refinement activated. + it "is not active before the `using` call" do + ScratchPad.record [] + + Module.new do + Class.new do + ScratchPad << "1".to_s + using ModuleSpecs::RefinementForStringToS + ScratchPad << "1".to_s + end + end + + ScratchPad.recorded.should == ["1", "hello from refinement"] + end + + # If you call a method that is defined outside the current scope + # the refinement will be deactivated + it "is not active for code defined outside the current scope" do + result = nil + + Module.new do + klass = Class.new do + def foo; "foo"; end + end + + refinement = Module.new do + refine klass do + def foo; "foo from refinement"; end + end + end + + def self.call_foo(c) + c.foo + end + + using refinement + + result = call_foo(klass.new) + end + + result.should == "foo" + end + + # If a method is defined in a scope where a refinement is active + # the refinement will be active when the method is called. + it "is active for method defined in a scope wherever it's called" do + klass = Class.new do + def foo; "foo"; end + end + + mod = Module.new do + refinement = Module.new do + refine klass do + def foo; "foo from refinement"; end + end + end + + using refinement + + def self.call_foo(c) + c.foo + end + end + + c = klass.new + mod.call_foo(c).should == "foo from refinement" + end + + it "is not active if `using` call is not evaluated" do + result = nil + + Module.new do + if false + using ModuleSpecs::RefinementForStringToS + end + result = "1".to_s + end + + result.should == "1" + end + + # The refinements in module are not activated automatically + # if the class is reopened later + it "is not active when class/module reopens" do + refinement = Module.new do + refine String do + def to_s + "hello from refinement" + end + end + end + + result = [] + klass = Class.new do + using refinement + result << "1".to_s + end + + klass.class_eval do + result << "1".to_s + end + + result.should == ["hello from refinement", "1"] + end + end +end diff --git a/spec/ruby/core/mutex/lock_spec.rb b/spec/ruby/core/mutex/lock_spec.rb index 98deabe056..b5c2b168e8 100644 --- a/spec/ruby/core/mutex/lock_spec.rb +++ b/spec/ruby/core/mutex/lock_spec.rb @@ -33,14 +33,9 @@ describe "Mutex#lock" do # related to this ML thread. it "raises a ThreadError when used recursively" do m = Mutex.new - - th = Thread.new do - m.lock + m.lock + -> { m.lock - end - - lambda do - th.join - end.should raise_error(ThreadError) + }.should raise_error(ThreadError) end end diff --git a/spec/ruby/core/nil/dup_spec.rb b/spec/ruby/core/nil/dup_spec.rb new file mode 100644 index 0000000000..10b1209736 --- /dev/null +++ b/spec/ruby/core/nil/dup_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.4' do + describe "NilClass#dup" do + it "returns self" do + nil.dup.should equal(nil) + end + end +end diff --git a/spec/ruby/core/numeric/coerce_spec.rb b/spec/ruby/core/numeric/coerce_spec.rb index 9b6297d5da..820d900dd5 100644 --- a/spec/ruby/core/numeric/coerce_spec.rb +++ b/spec/ruby/core/numeric/coerce_spec.rb @@ -17,7 +17,7 @@ describe "Numeric#coerce" do # I (emp) think that this behavior is actually a bug in MRI. It's here as documentation # of the behavior until we find out if it's a bug. quarantine! do - it "considers the presense of a metaclass when checking the class of the objects" do + it "considers the presence of a metaclass when checking the class of the objects" do a = NumericSpecs::Subclass.new b = NumericSpecs::Subclass.new diff --git a/spec/ruby/core/numeric/finite_spec.rb b/spec/ruby/core/numeric/finite_spec.rb new file mode 100644 index 0000000000..9fb2252845 --- /dev/null +++ b/spec/ruby/core/numeric/finite_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "Numeric#finite?" do + it "returns true by default" do + o = mock_numeric("finite") + o.finite?.should be_true + end + end +end diff --git a/spec/ruby/core/numeric/infinite_spec.rb b/spec/ruby/core/numeric/infinite_spec.rb new file mode 100644 index 0000000000..a527cb4370 --- /dev/null +++ b/spec/ruby/core/numeric/infinite_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "Numeric#infinite?" do + it "returns nil by default" do + o = mock_numeric("infinite") + o.infinite?.should == nil + end + end +end diff --git a/spec/ruby/core/process/wait2_spec.rb b/spec/ruby/core/process/wait2_spec.rb index 8e1ac763f1..d1f3a47b7f 100644 --- a/spec/ruby/core/process/wait2_spec.rb +++ b/spec/ruby/core/process/wait2_spec.rb @@ -5,8 +5,11 @@ describe "Process.wait2" do # HACK: this kludge is temporarily necessary because some # misbehaving spec somewhere else does not clear processes begin + Process.wait(-1, Process::WNOHANG) + $stderr.puts "Leaked process before wait2 specs! Waiting for it" leaked = Process.waitall - puts "leaked before wait2 specs: #{leaked}" unless leaked.empty? + $stderr.puts "leaked before wait2 specs: #{leaked}" + rescue Errno::ECHILD # No child processes rescue NotImplementedError end end diff --git a/spec/ruby/core/random/bytes_spec.rb b/spec/ruby/core/random/bytes_spec.rb index 2434a4e72e..d954261329 100644 --- a/spec/ruby/core/random/bytes_spec.rb +++ b/spec/ruby/core/random/bytes_spec.rb @@ -19,14 +19,14 @@ describe "Random#bytes" do end # Should double check this is official spec - it "returns the same numeric output for a given seed accross all implementations and platforms" do + it "returns the same numeric output for a given seed across all implementations and platforms" do rnd = Random.new(33) rnd.bytes(2).should == "\x14\\" rnd.bytes(1000) # skip some rnd.bytes(2).should == "\xA1p" end - it "returns the same numeric output for a given huge seed accross all implementations and platforms" do + it "returns the same numeric output for a given huge seed across all implementations and platforms" do rnd = Random.new(bignum_value ** 4) rnd.bytes(2).should == "_\x91" rnd.bytes(1000) # skip some diff --git a/spec/ruby/core/string/capitalize_spec.rb b/spec/ruby/core/string/capitalize_spec.rb index e2d2b68caa..497e1453cd 100644 --- a/spec/ruby/core/string/capitalize_spec.rb +++ b/spec/ruby/core/string/capitalize_spec.rb @@ -44,6 +44,14 @@ describe "String#capitalize!" do a.should == "Hello" end + ruby_version_is '2.4' do + it "capitalizes self in place for all of Unicode" do + a = "äöü" + a.capitalize!.should equal(a) + a.should == "Äöü" + end + end + it "returns nil when no changes are made" do a = "Hello" a.capitalize!.should == nil diff --git a/spec/ruby/core/string/casecmp_spec.rb b/spec/ruby/core/string/casecmp_spec.rb index fdb888ff2b..c77d97815c 100644 --- a/spec/ruby/core/string/casecmp_spec.rb +++ b/spec/ruby/core/string/casecmp_spec.rb @@ -1,4 +1,4 @@ -# -*- encoding: ascii-8bit -*- +# -*- encoding: utf-8 -*- require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../fixtures/classes.rb', __FILE__) @@ -40,10 +40,10 @@ describe "String#casecmp independent of case" do describe "in UTF-8 mode" do describe "for non-ASCII characters" do before :each do - @upper_a_tilde = "\xc3\x83" - @lower_a_tilde = "\xc3\xa3" - @upper_a_umlaut = "\xc3\x84" - @lower_a_umlaut = "\xc3\xa4" + @upper_a_tilde = "Ã" + @lower_a_tilde = "ã" + @upper_a_umlaut = "Ä" + @lower_a_umlaut = "ä" end it "returns -1 when numerically less than other" do @@ -118,3 +118,67 @@ describe "String#casecmp independent of case" do end end end + +ruby_version_is "2.4" do + describe 'String#casecmp? independent of case' do + it 'returns true when equal to other' do + 'abc'.casecmp?('abc').should == true + 'abc'.casecmp?('ABC').should == true + end + + it 'returns false when not equal to other' do + 'abc'.casecmp?('DEF').should == false + 'abc'.casecmp?('def').should == false + end + + it "tries to convert other to string using to_str" do + other = mock('x') + other.should_receive(:to_str).and_return("abc") + + "abc".casecmp?(other).should == true + end + + describe 'for UNICODE characters' do + it 'returns true when downcase(:fold) on unicode' do + 'äöü'.casecmp?('ÄÖÜ').should == true + end + end + + describe "when comparing a subclass instance" do + it 'returns true when equal to other' do + a = StringSpecs::MyString.new "a" + 'a'.casecmp?(a).should == true + 'A'.casecmp?(a).should == true + end + + it 'returns false when not equal to other' do + b = StringSpecs::MyString.new "a" + 'b'.casecmp?(b).should == false + 'B'.casecmp?(b).should == false + end + end + + describe "in UTF-8 mode" do + describe "for non-ASCII characters" do + before :each do + @upper_a_tilde = "Ã" + @lower_a_tilde = "ã" + @upper_a_umlaut = "Ä" + @lower_a_umlaut = "ä" + end + + it "returns true when they are the same with normalized case" do + @upper_a_tilde.casecmp?(@lower_a_tilde).should == true + end + + it "returns false when they are unrelated" do + @upper_a_tilde.casecmp?(@upper_a_umlaut).should == false + end + + it "returns true when they have the same bytes" do + @upper_a_tilde.casecmp?(@upper_a_tilde).should == true + end + end + end + end +end diff --git a/spec/ruby/core/string/downcase_spec.rb b/spec/ruby/core/string/downcase_spec.rb index 9ebc4f2bd1..f591c0fa09 100644 --- a/spec/ruby/core/string/downcase_spec.rb +++ b/spec/ruby/core/string/downcase_spec.rb @@ -46,6 +46,14 @@ describe "String#downcase!" do a.should == "hello" end + ruby_version_is '2.4' do + it "modifies self in place for all of Unicode" do + a = "ÄÖÜ" + a.downcase!.should equal(a) + a.should == "äöü" + end + end + it "returns nil if no modifications were made" do a = "hello" a.downcase!.should == nil diff --git a/spec/ruby/core/string/lines_spec.rb b/spec/ruby/core/string/lines_spec.rb index 6aa47ea728..e5f24816af 100644 --- a/spec/ruby/core/string/lines_spec.rb +++ b/spec/ruby/core/string/lines_spec.rb @@ -10,4 +10,13 @@ describe "String#lines" do ary = "hello world".send(@method, ' ') ary.should == ["hello ", "world"] end + + ruby_version_is '2.4' do + context "when `chomp` keyword argument is passed" do + it "removes new line characters" do + "hello \nworld\n".lines(chomp: true).should == ["hello ", "world"] + "hello \r\nworld\r\n".lines(chomp: true).should == ["hello ", "world"] + end + end + end end diff --git a/spec/ruby/core/string/new_spec.rb b/spec/ruby/core/string/new_spec.rb index 0a246f6f53..b429ac48d2 100644 --- a/spec/ruby/core/string/new_spec.rb +++ b/spec/ruby/core/string/new_spec.rb @@ -15,6 +15,13 @@ describe "String.new" do end end + ruby_version_is "2.4" do + it "accepts a capacity argument" do + String.new("", capacity: 100_000).should == "" + String.new("abc", capacity: 100_000).should == "abc" + end + end + it "returns a fully-formed String" do str = String.new str.size.should == 0 diff --git a/spec/ruby/core/string/shared/chars.rb b/spec/ruby/core/string/shared/chars.rb index 2f7280a95f..c1cf324dc5 100644 --- a/spec/ruby/core/string/shared/chars.rb +++ b/spec/ruby/core/string/shared/chars.rb @@ -42,10 +42,8 @@ describe :string_chars, shared: true do it "returns a different character if the String is transcoded" do s = "\u{20AC}".force_encoding('UTF-8') s.encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".force_encoding('UTF-8')] - s.encode('iso-8859-15').send(@method).to_a.should == [ - [0xA4].pack('C').force_encoding('iso-8859-15')] - s.encode('iso-8859-15').encode('UTF-8').send(@method).to_a.should == [ - "\u{20AC}".force_encoding('UTF-8')] + s.encode('iso-8859-15').send(@method).to_a.should == [[0xA4].pack('C').force_encoding('iso-8859-15')] + s.encode('iso-8859-15').encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".force_encoding('UTF-8')] end it "uses the String's encoding to determine what characters it contains" do diff --git a/spec/ruby/core/string/shared/codepoints.rb b/spec/ruby/core/string/shared/codepoints.rb index 1ee13c82f4..68f82b4468 100644 --- a/spec/ruby/core/string/shared/codepoints.rb +++ b/spec/ruby/core/string/shared/codepoints.rb @@ -48,7 +48,7 @@ describe :string_codepoints, shared: true do s.should == s2 end - it "is synonomous with #bytes for Strings which are single-byte optimisable" do + it "is synonymous with #bytes for Strings which are single-byte optimisable" do s = "(){}".encode('ascii') s.ascii_only?.should be_true s.send(@method).to_a.should == s.bytes.to_a diff --git a/spec/ruby/core/string/shared/each_line.rb b/spec/ruby/core/string/shared/each_line.rb index fe8b76b47b..dee741e270 100644 --- a/spec/ruby/core/string/shared/each_line.rb +++ b/spec/ruby/core/string/shared/each_line.rb @@ -133,4 +133,18 @@ end it "raises a TypeError when the separator is a symbol" do lambda { "hello world".send(@method, :o).to_a }.should raise_error(TypeError) end + + ruby_version_is '2.4' do + context "when `chomp` keyword argument is passed" do + it "removes new line characters" do + a = [] + "hello \nworld\n".send(@method, chomp: true) { |s| a << s } + a.should == ["hello ", "world"] + + a = [] + "hello \r\nworld\r\n".send(@method, chomp: true) { |s| a << s } + a.should == ["hello ", "world"] + end + end + end end diff --git a/spec/ruby/core/string/shared/to_sym.rb b/spec/ruby/core/string/shared/to_sym.rb index 501247078d..1180d64712 100644 --- a/spec/ruby/core/string/shared/to_sym.rb +++ b/spec/ruby/core/string/shared/to_sym.rb @@ -1,24 +1,63 @@ describe :string_to_sym, shared: true do it "returns the symbol corresponding to self" do - "Koala".send(@method).should == :Koala - 'cat'.send(@method).should == :cat - '@cat'.send(@method).should == :@cat - 'cat and dog'.send(@method).should == :"cat and dog" - "abc=".send(@method).should == :abc= + "Koala".send(@method).should equal :Koala + 'cat'.send(@method).should equal :cat + '@cat'.send(@method).should equal :@cat + 'cat and dog'.send(@method).should equal :"cat and dog" + "abc=".send(@method).should equal :abc= end it "does not special case +(binary) and -(binary)" do - "+(binary)".send(@method).should == :"+(binary)" - "-(binary)".send(@method).should == :"-(binary)" + "+(binary)".send(@method).should equal :"+(binary)" + "-(binary)".send(@method).should equal :"-(binary)" end it "does not special case certain operators" do - [ ["!@", :"!@"], - ["~@", :"~@"], - ["!(unary)", :"!(unary)"], - ["~(unary)", :"~(unary)"], - ["+(unary)", :"+(unary)"], - ["-(unary)", :"-(unary)"] - ].should be_computed_by(@method) + "!@".send(@method).should equal :"!@" + "~@".send(@method).should equal :"~@" + "!(unary)".send(@method).should equal :"!(unary)" + "~(unary)".send(@method).should equal :"~(unary)" + "+(unary)".send(@method).should equal :"+(unary)" + "-(unary)".send(@method).should equal :"-(unary)" + end + + it "returns a US-ASCII Symbol for a UTF-8 String containing only US-ASCII characters" do + sym = "foobar".send(@method) + sym.encoding.should == Encoding::US_ASCII + sym.should equal :"foobar" + end + + it "returns a US-ASCII Symbol for a binary String containing only US-ASCII characters" do + sym = "foobar".b.send(@method) + sym.encoding.should == Encoding::US_ASCII + sym.should equal :"foobar" + end + + it "returns a UTF-8 Symbol for a UTF-8 String containing non US-ASCII characters" do + sym = "il était une fois".send(@method) + sym.encoding.should == Encoding::UTF_8 + sym.should equal :"il était une #{'fois'}" + end + + it "returns a UTF-16LE Symbol for a UTF-16LE String containing non US-ASCII characters" do + utf16_str = "UtéF16".encode(Encoding::UTF_16LE) + sym = utf16_str.send(@method) + sym.encoding.should == Encoding::UTF_16LE + sym.to_s.should == utf16_str + end + + it "returns a binary Symbol for a binary String containing non US-ASCII characters" do + binary_string = "binarí".b + sym = binary_string.send(@method) + sym.encoding.should == Encoding::BINARY + sym.to_s.should == binary_string + end + + it "raises an EncodingError for UTF-8 String containing invalid bytes" do + invalid_utf8 = "\xC3" + invalid_utf8.valid_encoding?.should == false + -> { + invalid_utf8.send(@method) + }.should raise_error(EncodingError, /invalid/) end end diff --git a/spec/ruby/core/string/swapcase_spec.rb b/spec/ruby/core/string/swapcase_spec.rb index e9af647727..c2b583acab 100644 --- a/spec/ruby/core/string/swapcase_spec.rb +++ b/spec/ruby/core/string/swapcase_spec.rb @@ -41,6 +41,14 @@ describe "String#swapcase!" do a.should == "CyBeR_pUnK11" end + ruby_version_is '2.4' do + it "modifies self in place for all of Unicode" do + a = "äÖü" + a.swapcase!.should equal(a) + a.should == "ÄöÜ" + end + end + it "returns nil if no modifications were made" do a = "+++---111222???" a.swapcase!.should == nil diff --git a/spec/ruby/core/string/unpack1_spec.rb b/spec/ruby/core/string/unpack1_spec.rb new file mode 100644 index 0000000000..6941bc1173 --- /dev/null +++ b/spec/ruby/core/string/unpack1_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "String#unpack1" do + it "returns the first value of #unpack" do + "ABCD".unpack1('x3C').should == "ABCD".unpack('x3C')[0] + "\u{3042 3044 3046}".unpack1("U*").should == 0x3042 + "aG9nZWZ1Z2E=".unpack1("m").should == "hogefuga" + "A".unpack1("B*").should == "01000001" + end + end +end diff --git a/spec/ruby/core/string/upcase_spec.rb b/spec/ruby/core/string/upcase_spec.rb index 2fdcce86b9..0094380664 100644 --- a/spec/ruby/core/string/upcase_spec.rb +++ b/spec/ruby/core/string/upcase_spec.rb @@ -46,6 +46,15 @@ describe "String#upcase!" do a.should == "HELLO" end + + ruby_version_is '2.4' do + it "modifies self in place for all of Unicode" do + a = "äöü" + a.upcase!.should equal(a) + a.should == "ÄÖÜ" + end + end + it "returns nil if no modifications were made" do a = "HELLO" a.upcase!.should == nil diff --git a/spec/ruby/core/struct/initialize_spec.rb b/spec/ruby/core/struct/initialize_spec.rb index 59fc5ef9aa..989459114a 100644 --- a/spec/ruby/core/struct/initialize_spec.rb +++ b/spec/ruby/core/struct/initialize_spec.rb @@ -37,7 +37,7 @@ describe "Struct#initialize" do car.make.should == nil # still nil despite override in Honda#initialize b/c of super order end - it "can be overriden" do + it "can be overridden" do StructClasses::SubclassX.new(:y).new.key.should == :value end end diff --git a/spec/ruby/core/symbol/capitalize_spec.rb b/spec/ruby/core/symbol/capitalize_spec.rb index cf7e8a007f..73850a2a8c 100644 --- a/spec/ruby/core/symbol/capitalize_spec.rb +++ b/spec/ruby/core/symbol/capitalize_spec.rb @@ -21,6 +21,13 @@ describe "Symbol#capitalize" do end end + ruby_version_is '2.4' do + it "capitalizes the first character if it is Unicode" do + :"äöü".capitalize.should == :"Äöü" + :"aou".capitalize.should == :"Aou" + end + end + it "converts subsequent uppercase ASCII characters to their lowercase equivalents" do :lOWER.capitalize.should == :Lower end diff --git a/spec/ruby/core/symbol/casecmp_spec.rb b/spec/ruby/core/symbol/casecmp_spec.rb index 942bd15998..352c5b99cb 100644 --- a/spec/ruby/core/symbol/casecmp_spec.rb +++ b/spec/ruby/core/symbol/casecmp_spec.rb @@ -1,4 +1,4 @@ -# -*- encoding: binary -*- +# -*- encoding: utf-8 -*- require File.expand_path('../../../spec_helper', __FILE__) describe "Symbol#casecmp with Symbol" do @@ -11,10 +11,10 @@ describe "Symbol#casecmp with Symbol" do it "doesn't consider non-ascii characters equal that aren't" do # -- Latin-1 -- - upper_a_tilde = :"\xC3" - upper_a_umlaut = :"\xC4" - lower_a_tilde = :"\xE3" - lower_a_umlaut = :"\xE4" + upper_a_tilde = "\xC3".b.to_sym + upper_a_umlaut = "\xC4".b.to_sym + lower_a_tilde = "\xE3".b.to_sym + lower_a_umlaut = "\xE4".b.to_sym lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0 lower_a_umlaut.casecmp(lower_a_tilde).should_not == 0 @@ -22,10 +22,10 @@ describe "Symbol#casecmp with Symbol" do upper_a_umlaut.casecmp(upper_a_tilde).should_not == 0 # -- UTF-8 -- - upper_a_tilde = :"\xC3\x83" - upper_a_umlaut = :"\xC3\x84" - lower_a_tilde = :"\xC3\xA3" - lower_a_umlaut = :"\xC3\xA4" + upper_a_tilde = :"Ã" + lower_a_tilde = :"ã" + upper_a_umlaut = :"Ä" + lower_a_umlaut = :"ä" lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0 lower_a_umlaut.casecmp(lower_a_tilde).should_not == 0 @@ -35,10 +35,10 @@ describe "Symbol#casecmp with Symbol" do it "doesn't do case mapping for non-ascii characters" do # -- Latin-1 -- - upper_a_tilde = :"\xC3" - upper_a_umlaut = :"\xC4" - lower_a_tilde = :"\xE3" - lower_a_umlaut = :"\xE4" + upper_a_tilde = "\xC3".b.to_sym + upper_a_umlaut = "\xC4".b.to_sym + lower_a_tilde = "\xE3".b.to_sym + lower_a_umlaut = "\xE4".b.to_sym upper_a_tilde.casecmp(lower_a_tilde).should == -1 upper_a_umlaut.casecmp(lower_a_umlaut).should == -1 @@ -46,10 +46,10 @@ describe "Symbol#casecmp with Symbol" do lower_a_umlaut.casecmp(upper_a_umlaut).should == 1 # -- UTF-8 -- - upper_a_tilde = :"\xC3\x83" - upper_a_umlaut = :"\xC3\x84" - lower_a_tilde = :"\xC3\xA3" - lower_a_umlaut = :"\xC3\xA4" + upper_a_tilde = :"Ã" + lower_a_tilde = :"ã" + upper_a_umlaut = :"Ä" + lower_a_umlaut = :"ä" upper_a_tilde.casecmp(lower_a_tilde).should == -1 upper_a_umlaut.casecmp(lower_a_umlaut).should == -1 @@ -72,3 +72,75 @@ describe "Symbol#casecmp" do :abc.casecmp(obj).should be_nil end end + +ruby_version_is "2.4" do + describe 'Symbol#casecmp?' do + it "compares symbols without regard to case" do + :abcdef.casecmp?(:abcde).should == false + :aBcDeF.casecmp?(:abcdef).should == true + :abcdef.casecmp?(:abcdefg).should == false + :abcdef.casecmp?(:ABCDEF).should == true + end + + it "doesn't consider non-ascii characters equal that aren't" do + # -- Latin-1 -- + upper_a_tilde = "\xC3".b.to_sym + upper_a_umlaut = "\xC4".b.to_sym + lower_a_tilde = "\xE3".b.to_sym + lower_a_umlaut = "\xE4".b.to_sym + + lower_a_tilde.casecmp?(lower_a_umlaut).should_not == true + lower_a_umlaut.casecmp?(lower_a_tilde).should_not == true + upper_a_tilde.casecmp?(upper_a_umlaut).should_not == true + upper_a_umlaut.casecmp?(upper_a_tilde).should_not == true + + # -- UTF-8 -- + upper_a_tilde = :"Ã" + lower_a_tilde = :"ã" + upper_a_umlaut = :"Ä" + lower_a_umlaut = :"ä" + + lower_a_tilde.casecmp?(lower_a_umlaut).should_not == true + lower_a_umlaut.casecmp?(lower_a_tilde).should_not == true + upper_a_tilde.casecmp?(upper_a_umlaut).should_not == true + upper_a_umlaut.casecmp?(upper_a_tilde).should_not == true + end + + it "doesn't do case mapping for non-ascii and non-unicode characters" do + # -- Latin-1 -- + upper_a_tilde = "\xC3".b.to_sym + upper_a_umlaut = "\xC4".b.to_sym + lower_a_tilde = "\xE3".b.to_sym + lower_a_umlaut = "\xE4".b.to_sym + + upper_a_tilde.casecmp?(lower_a_tilde).should == false + upper_a_umlaut.casecmp?(lower_a_umlaut).should == false + lower_a_tilde.casecmp?(upper_a_tilde).should == false + lower_a_umlaut.casecmp?(upper_a_umlaut).should == false + end + + it 'does case mapping for unicode characters' do + # -- UTF-8 -- + upper_a_tilde = :"Ã" + lower_a_tilde = :"ã" + upper_a_umlaut = :"Ä" + lower_a_umlaut = :"ä" + + upper_a_tilde.casecmp?(lower_a_tilde).should == true + upper_a_umlaut.casecmp?(lower_a_umlaut).should == true + lower_a_tilde.casecmp?(upper_a_tilde).should == true + lower_a_umlaut.casecmp?(upper_a_umlaut).should == true + end + + it 'returns nil when comparing characters with different encodings' do + # -- Latin-1 -- + upper_a_tilde = "\xC3".b.to_sym + + # -- UTF-8 -- + lower_a_tilde = :"ã" + + upper_a_tilde.casecmp?(lower_a_tilde).should == nil + lower_a_tilde.casecmp?(upper_a_tilde).should == nil + end + end +end diff --git a/spec/ruby/core/symbol/downcase_spec.rb b/spec/ruby/core/symbol/downcase_spec.rb index 0b2422ad3e..6eb19e087a 100644 --- a/spec/ruby/core/symbol/downcase_spec.rb +++ b/spec/ruby/core/symbol/downcase_spec.rb @@ -20,6 +20,13 @@ describe "Symbol#downcase" do end end + ruby_version_is '2.4' do + it "uncapitalizes all Unicode characters" do + "ÄÖÜ".to_sym.downcase.should == :"äöü" + "AOU".to_sym.downcase.should == :"aou" + end + end + it "leaves non-alphabetic ASCII characters as they were" do "Glark?!?".to_sym.downcase.should == :"glark?!?" end diff --git a/spec/ruby/core/symbol/dup_spec.rb b/spec/ruby/core/symbol/dup_spec.rb new file mode 100644 index 0000000000..2ca4d84078 --- /dev/null +++ b/spec/ruby/core/symbol/dup_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.4' do + describe "Symbol#dup" do + it "returns self" do + :a_symbol.dup.should equal(:a_symbol) + end + end +end diff --git a/spec/ruby/core/symbol/swapcase_spec.rb b/spec/ruby/core/symbol/swapcase_spec.rb index fdc42ec477..3124f782bc 100644 --- a/spec/ruby/core/symbol/swapcase_spec.rb +++ b/spec/ruby/core/symbol/swapcase_spec.rb @@ -28,6 +28,13 @@ describe "Symbol#swapcase" do end end + ruby_version_is '2.4' do + it "swaps the case for Unicode characters" do + "äÖü".to_sym.swapcase.should == :"ÄöÜ" + "aOu".to_sym.swapcase.should == :"AoU" + end + end + it "leaves non-alphabetic ASCII characters as they were" do "Glark?!?".to_sym.swapcase.should == :"gLARK?!?" end diff --git a/spec/ruby/core/symbol/upcase_spec.rb b/spec/ruby/core/symbol/upcase_spec.rb index fce62af100..fe2c88d294 100644 --- a/spec/ruby/core/symbol/upcase_spec.rb +++ b/spec/ruby/core/symbol/upcase_spec.rb @@ -16,6 +16,13 @@ describe "Symbol#upcase" do end end + ruby_version_is '2.4' do + it "capitalizes all Unicode characters" do + "äöü".to_sym.upcase.should == :"ÄÖÜ" + "aou".to_sym.upcase.should == :"AOU" + end + end + it "leaves non-alphabetic ASCII characters as they were" do "Glark?!?".to_sym.upcase.should == :"GLARK?!?" end diff --git a/spec/ruby/core/thread/element_set_spec.rb b/spec/ruby/core/thread/element_set_spec.rb index 47b4d06328..b76078f685 100644 --- a/spec/ruby/core/thread/element_set_spec.rb +++ b/spec/ruby/core/thread/element_set_spec.rb @@ -9,12 +9,12 @@ describe "Thread#[]=" do it "raises a RuntimeError if the thread is frozen" do running = false t = Thread.new do - Thread.pass until running t.freeze - t[:foo] = "bar" + -> { + t[:foo] = "bar" + }.should raise_error(RuntimeError, /frozen/) end - running = true - lambda { t.join }.should raise_error(RuntimeError) + t.join end it "raises exceptions on the wrong type of keys" do diff --git a/spec/ruby/core/thread/fixtures/classes.rb b/spec/ruby/core/thread/fixtures/classes.rb index b572c8dd82..601e515e3e 100644 --- a/spec/ruby/core/thread/fixtures/classes.rb +++ b/spec/ruby/core/thread/fixtures/classes.rb @@ -120,7 +120,10 @@ module ThreadSpecs end def self.status_of_thread_with_uncaught_exception - t = Thread.new { raise "error" } + t = Thread.new { + Thread.current.report_on_exception = false + raise "error" + } begin t.join rescue RuntimeError @@ -159,6 +162,7 @@ module ThreadSpecs def self.dying_thread_ensures(kill_method_name=:kill) Thread.new do + Thread.current.report_on_exception = false begin Thread.current.send(kill_method_name) ensure @@ -169,6 +173,7 @@ module ThreadSpecs def self.dying_thread_with_outer_ensure(kill_method_name=:kill) Thread.new do + Thread.current.report_on_exception = false begin begin Thread.current.send(kill_method_name) diff --git a/spec/ruby/core/thread/join_spec.rb b/spec/ruby/core/thread/join_spec.rb index a6dd2da9a3..249b3d333e 100644 --- a/spec/ruby/core/thread/join_spec.rb +++ b/spec/ruby/core/thread/join_spec.rb @@ -46,7 +46,10 @@ describe "Thread#join" do end it "raises any exceptions encountered in the thread body" do - t = Thread.new { raise NotImplementedError.new("Just kidding") } + t = Thread.new { + Thread.current.report_on_exception = false + raise NotImplementedError.new("Just kidding") + } lambda { t.join }.should raise_error(NotImplementedError) end diff --git a/spec/ruby/core/thread/key_spec.rb b/spec/ruby/core/thread/key_spec.rb index 6cdfc3ca7f..d82a21ab39 100644 --- a/spec/ruby/core/thread/key_spec.rb +++ b/spec/ruby/core/thread/key_spec.rb @@ -9,7 +9,7 @@ describe "Thread#key?" do @th.join end - it "tests for existance of thread local variables using symbols or strings" do + it "tests for existence of thread local variables using symbols or strings" do @th.key?(:oliver).should == true @th.key?("oliver").should == true @th.key?(:stanley).should == false diff --git a/spec/ruby/core/thread/raise_spec.rb b/spec/ruby/core/thread/raise_spec.rb index 93e0f048b1..8724d26202 100644 --- a/spec/ruby/core/thread/raise_spec.rb +++ b/spec/ruby/core/thread/raise_spec.rb @@ -51,6 +51,7 @@ describe "Thread#raise on a sleeping thread" do it "is captured and raised by Thread#value" do t = Thread.new do + Thread.current.report_on_exception = false sleep end @@ -62,6 +63,7 @@ describe "Thread#raise on a sleeping thread" do it "raises a RuntimeError when called with no arguments inside rescue" do t = Thread.new do + Thread.current.report_on_exception = false begin 1/0 rescue ZeroDivisionError @@ -113,6 +115,7 @@ describe "Thread#raise on a running thread" do it "can go unhandled" do t = Thread.new do + Thread.current.report_on_exception = false loop { Thread.pass } end @@ -123,6 +126,7 @@ describe "Thread#raise on a running thread" do it "raises the given argument even when there is an active exception" do raised = false t = Thread.new do + Thread.current.report_on_exception = false begin 1/0 rescue ZeroDivisionError @@ -142,6 +146,7 @@ describe "Thread#raise on a running thread" do it "raises a RuntimeError when called with no arguments inside rescue" do raised = false t = Thread.new do + Thread.current.report_on_exception = false begin 1/0 rescue ZeroDivisionError @@ -164,6 +169,7 @@ describe "Thread#raise on same thread" do it "raises a RuntimeError when called with no arguments inside rescue" do t = Thread.new do + Thread.current.report_on_exception = false begin 1/0 rescue ZeroDivisionError diff --git a/spec/ruby/core/thread/report_on_exception_spec.rb b/spec/ruby/core/thread/report_on_exception_spec.rb new file mode 100644 index 0000000000..4128dad470 --- /dev/null +++ b/spec/ruby/core/thread/report_on_exception_spec.rb @@ -0,0 +1,102 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "Thread.report_on_exception" do + it "defaults to false" do + ruby_exe("p Thread.report_on_exception").should == "false\n" + end + end + + describe "Thread.report_on_exception=" do + before :each do + @report_on_exception = Thread.report_on_exception + end + + after :each do + Thread.report_on_exception = @report_on_exception + end + + it "changes the default value for new threads" do + Thread.report_on_exception = true + Thread.report_on_exception.should == true + t = Thread.new {} + t.join + t.report_on_exception.should == true + end + end + + describe "Thread#report_on_exception" do + it "returns whether the Thread will print a backtrace if it exits with an exception" do + t = Thread.new { Thread.current.report_on_exception = true } + t.join + t.report_on_exception.should == true + + t = Thread.new { Thread.current.report_on_exception = false } + t.join + t.report_on_exception.should == false + end + end + + describe "Thread#report_on_exception=" do + describe "when set to true" do + it "prints a backtrace on $stderr if it terminates with an exception" do + t = nil + -> { + t = Thread.new { + Thread.current.report_on_exception = true + raise RuntimeError, "Thread#report_on_exception specs" + } + Thread.pass while t.alive? + }.should output("", /Thread.+terminated with exception.+Thread#report_on_exception specs/m) + + -> { + t.join + }.should raise_error(RuntimeError, "Thread#report_on_exception specs") + end + end + + describe "when set to false" do + it "lets the thread terminates silently with an exception" do + t = nil + -> { + t = Thread.new { + Thread.current.report_on_exception = false + raise RuntimeError, "Thread#report_on_exception specs" + } + Thread.pass while t.alive? + }.should output("", "") + + -> { + t.join + }.should raise_error(RuntimeError, "Thread#report_on_exception specs") + end + end + + ruby_bug "#13163", "2.4"..."2.5" do + describe "when used in conjunction with Thread#abort_on_exception" do + it "first reports then send the exception back to the main Thread" do + t = nil + mutex = Mutex.new + mutex.lock + -> { + t = Thread.new { + Thread.current.abort_on_exception = true + Thread.current.report_on_exception = true + mutex.lock + mutex.unlock + raise RuntimeError, "Thread#report_on_exception specs" + } + + -> { + mutex.sleep(5) + }.should raise_error(RuntimeError, "Thread#report_on_exception specs") + }.should output("", /Thread.+terminated with exception.+Thread#report_on_exception specs/m) + + -> { + t.join + }.should raise_error(RuntimeError, "Thread#report_on_exception specs") + end + end + end + end +end diff --git a/spec/ruby/core/thread/shared/exit.rb b/spec/ruby/core/thread/shared/exit.rb index f15da360fd..0c9198c538 100644 --- a/spec/ruby/core/thread/shared/exit.rb +++ b/spec/ruby/core/thread/shared/exit.rb @@ -112,7 +112,7 @@ describe :thread_exit, shared: true do quarantine! do - it "propogates inner exception to Thread.join if there is an outer ensure clause" do + it "propagates inner exception to Thread.join if there is an outer ensure clause" do thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { } lambda { thread.join }.should raise_error(RuntimeError, "In dying thread") end diff --git a/spec/ruby/core/thread/value_spec.rb b/spec/ruby/core/thread/value_spec.rb index 82c0cbf762..3d900959df 100644 --- a/spec/ruby/core/thread/value_spec.rb +++ b/spec/ruby/core/thread/value_spec.rb @@ -7,7 +7,10 @@ describe "Thread#value" do end it "re-raises an error for an uncaught exception" do - t = Thread.new { raise "Hello" } + t = Thread.new { + Thread.current.report_on_exception = false + raise "Hello" + } lambda { t.value }.should raise_error(RuntimeError, "Hello") end diff --git a/spec/ruby/core/time/shared/now.rb b/spec/ruby/core/time/shared/now.rb index f570aeedd2..c35115fcf4 100644 --- a/spec/ruby/core/time/shared/now.rb +++ b/spec/ruby/core/time/shared/now.rb @@ -5,4 +5,16 @@ describe :time_now, shared: true do TimeSpecs::SubTime.send(@method).should be_an_instance_of(TimeSpecs::SubTime) TimeSpecs::MethodHolder.send(@method).should be_an_instance_of(Time) end + + it "sets the current time" do + now = TimeSpecs::MethodHolder.send(@method) + now.to_f.should be_close(Process.clock_gettime(Process::CLOCK_REALTIME), 10.0) + end + + it "uses the local timezone" do + with_timezone("PDT", -8) do + now = TimeSpecs::MethodHolder.send(@method) + now.utc_offset.should == (-8 * 60 * 60) + end + end end diff --git a/spec/ruby/core/time/shared/time_params.rb b/spec/ruby/core/time/shared/time_params.rb index 87b52d9f8d..120d8d3af1 100644 --- a/spec/ruby/core/time/shared/time_params.rb +++ b/spec/ruby/core/time/shared/time_params.rb @@ -116,10 +116,8 @@ describe :time_params, shared: true do end it "interprets all numerals as base 10" do - Time.send(@method, "2000", "08", "08", "08", "08", "08").should == - Time.send(@method, 2000, 8, 8, 8, 8, 8) - Time.send(@method, "2000", "09", "09", "09", "09", "09").should == - Time.send(@method, 2000, 9, 9, 9, 9, 9) + Time.send(@method, "2000", "08", "08", "08", "08", "08").should == Time.send(@method, 2000, 8, 8, 8, 8, 8) + Time.send(@method, "2000", "09", "09", "09", "09", "09").should == Time.send(@method, 2000, 9, 9, 9, 9, 9) end it "handles fractional seconds as a Float" do diff --git a/spec/ruby/core/tracepoint/callee_id_spec.rb b/spec/ruby/core/tracepoint/callee_id_spec.rb new file mode 100644 index 0000000000..b7571027d6 --- /dev/null +++ b/spec/ruby/core/tracepoint/callee_id_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is '2.4' do + describe "TracePoint#callee_id" do + it "returns the called name of the method being called" do + a = [] + obj = TracePointSpec::ClassWithMethodAlias.new + + TracePoint.new(:call) do |tp| + a << tp.callee_id + end.enable do + obj.m_alias + end + + a.should == [:m_alias] + end + end +end + diff --git a/spec/ruby/core/tracepoint/fixtures/classes.rb b/spec/ruby/core/tracepoint/fixtures/classes.rb new file mode 100644 index 0000000000..fd22ac319d --- /dev/null +++ b/spec/ruby/core/tracepoint/fixtures/classes.rb @@ -0,0 +1,8 @@ +module TracePointSpec + class ClassWithMethodAlias + def m + end + alias_method :m_alias, :m + end +end + diff --git a/spec/ruby/core/true/dup_spec.rb b/spec/ruby/core/true/dup_spec.rb new file mode 100644 index 0000000000..cdf60e5bd6 --- /dev/null +++ b/spec/ruby/core/true/dup_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.4' do + describe "TrueClass#dup" do + it "returns self" do + true.dup.should equal(true) + end + end +end diff --git a/spec/ruby/core/warning/warn_spec.rb b/spec/ruby/core/warning/warn_spec.rb new file mode 100644 index 0000000000..44e9eb707b --- /dev/null +++ b/spec/ruby/core/warning/warn_spec.rb @@ -0,0 +1,60 @@ +require File.expand_path("../../../spec_helper", __FILE__) + +describe "Warning.warn" do + ruby_version_is "2.4" do + it "complains" do + -> { + Warning.warn("Chunky bacon!") + }.should complain("Chunky bacon!") + end + + it "extends itself" do + Warning.singleton_class.ancestors.should include(Warning) + end + + it "has Warning as the method owner" do + ruby_exe("p Warning.method(:warn).owner").should == "Warning\n" + end + + it "can be overridden" do + code = <<-RUBY + $stdout.sync = true + $stderr.sync = true + def Warning.warn(msg) + if msg.start_with?("A") + puts msg.upcase + else + super + end + end + Warning.warn("A warning!") + Warning.warn("warning from stderr\n") + RUBY + ruby_exe(code, args: "2>&1").should == %Q[A WARNING!\nwarning from stderr\n] + end + + it "is called by parser warnings" do + Warning.should_receive(:warn) + verbose = $VERBOSE + $VERBOSE = false + begin + eval "{ key: :value, key: :value2 }" + ensure + $VERBOSE = verbose + end + end + end + + ruby_version_is "2.5" do + it "is called by Kernel.warn" do + Warning.should_receive(:warn) + verbose = $VERBOSE + $VERBOSE = false + begin + Kernel.warn("Chunky bacon!") + ensure + $VERBOSE = verbose + end + end + end +end |