1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60525 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
eregon 2017-10-28 15:15:48 +00:00
parent 6530b14cee
commit 8c5b60eb22
218 changed files with 4069 additions and 328 deletions

View file

@ -7,13 +7,28 @@ AllCops:
- command_line/fixtures/bad_syntax.rb - command_line/fixtures/bad_syntax.rb
DisabledByDefault: true DisabledByDefault: true
Layout/TrailingWhitespace:
Enabled: true
Lint: Lint:
Enabled: true Enabled: true
Lint/AssignmentInCondition: Lint/AssignmentInCondition:
Enabled: false Enabled: false
Lint/LiteralInCondition: Lint/BooleanSymbol:
Enabled: false
Lint/InterpolationCheck:
Enabled: false
Lint/LiteralAsCondition:
Enabled: false
Lint/UnneededRequireStatement:
Enabled: false
Lint/RescueWithoutErrorClass:
Enabled: false Enabled: false
Lint/UnifiedInteger: Lint/UnifiedInteger:
@ -38,3 +53,7 @@ Lint/Void:
Lint/EmptyExpression: Lint/EmptyExpression:
Exclude: Exclude:
- 'language/**/*.rb' - 'language/**/*.rb'
Lint/UriRegexp:
Exclude:
- 'library/uri/regexp_spec.rb'

View file

@ -1,6 +1,6 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config` # `rubocop --auto-gen-config`
# on 2017-06-18 19:12:38 +0200 using RuboCop version 0.49.1. # on 2017-10-09 20:22:01 +0200 using RuboCop version 0.50.0.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
@ -52,7 +52,7 @@ Lint/FloatOutOfRange:
Exclude: Exclude:
- 'core/string/modulo_spec.rb' - 'core/string/modulo_spec.rb'
# Offense count: 46 # Offense count: 43
Lint/FormatParameterMismatch: Lint/FormatParameterMismatch:
Exclude: Exclude:
- 'core/kernel/sprintf_spec.rb' - 'core/kernel/sprintf_spec.rb'
@ -137,6 +137,7 @@ Lint/RescueException:
- 'library/erb/filename_spec.rb' - 'library/erb/filename_spec.rb'
# Offense count: 1 # Offense count: 1
# Cop supports --auto-correct.
Lint/ScriptPermission: Lint/ScriptPermission:
Exclude: Exclude:
- 'command_line/fixtures/bin/launcher.rb' - 'command_line/fixtures/bin/launcher.rb'
@ -181,7 +182,7 @@ Lint/UnneededSplatExpansion:
- 'language/send_spec.rb' - 'language/send_spec.rb'
- 'language/variables_spec.rb' - 'language/variables_spec.rb'
# Offense count: 53 # Offense count: 55
Lint/UnreachableCode: Lint/UnreachableCode:
Exclude: Exclude:
- 'core/enumerator/lazy/fixtures/classes.rb' - 'core/enumerator/lazy/fixtures/classes.rb'
@ -198,7 +199,7 @@ Lint/UnreachableCode:
- 'optional/capi/kernel_spec.rb' - 'optional/capi/kernel_spec.rb'
- 'shared/kernel/raise.rb' - 'shared/kernel/raise.rb'
# Offense count: 9 # Offense count: 7
# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. # Configuration parameters: ContextCreatingMethods, MethodCreatingMethods.
Lint/UselessAccessModifier: Lint/UselessAccessModifier:
Exclude: Exclude:
@ -206,6 +207,4 @@ Lint/UselessAccessModifier:
- 'core/module/fixtures/classes.rb' - 'core/module/fixtures/classes.rb'
- 'core/module/module_function_spec.rb' - 'core/module/module_function_spec.rb'
- 'core/module/private_class_method_spec.rb' - 'core/module/private_class_method_spec.rb'
- 'core/module/private_spec.rb' - 'language/fixtures/send.rb'
- 'core/module/protected_spec.rb'
- 'core/module/public_spec.rb'

View file

@ -3,21 +3,23 @@ language: ruby
install: install:
- git clone https://github.com/ruby/mspec.git ../mspec - git clone https://github.com/ruby/mspec.git ../mspec
script: script:
- if [ -n "$RUBOCOP" ]; then gem install rubocop -v 0.49.1 && rubocop; fi - if [ -n "$RUBOCOP" ]; then gem install rubocop -v 0.51.0 && rubocop; fi
- ../mspec/bin/mspec $MSPEC_OPTS - ../mspec/bin/mspec $MSPEC_OPTS
matrix: matrix:
include: include:
- os: osx - os: osx
rvm: 2.4.0 osx_image: xcode9
rvm: 2.4.2
env: CHECK_LEAKS=true
- os: linux - os: linux
rvm: 2.4.1 rvm: 2.4.2
env: MSPEC_OPTS="-R2 -ff" env: MSPEC_OPTS="-R2 -ff"
- os: linux - os: linux
rvm: 2.2.7 rvm: 2.2.8
- os: linux - os: linux
rvm: 2.3.4 rvm: 2.3.5
- os: linux - os: linux
rvm: 2.4.1 rvm: 2.4.2
env: CHECK_LEAKS=true RUBOCOP=true env: CHECK_LEAKS=true RUBOCOP=true
- os: linux - os: linux
rvm: ruby-head rvm: ruby-head

View file

@ -48,9 +48,116 @@ You might also want to search for:
which indicates the file was generated but the method unspecified. which indicates the file was generated but the method unspecified.
### Matchers and expectations
Here is a list of frequently-used matchers, which should be enough for most specs.
There are a few extra specific matchers used in the couple specs that need it.
```ruby
(1 + 2).should == 3 # Calls #==
(1 + 2).should_not == 5
File.should equal(File) # Calls #equal? (tests identity)
(1 + 2).should eql(3) # Calls #eql? (Hash equality)
1.should < 2
2.should <= 2
3.should >= 3
4.should > 3
"Hello".should =~ /l{2}/ # Calls #=~ (Regexp match)
[].should be_empty # Calls #empty?
[1,2,3].should include(2) # Calls #include?
(0.1 + 0.2).should be_close(0.3, TOLERANCE) # (0.2-0.1).abs < TOLERANCE
(0.0/0.0).should be_nan # Calls Float#nan?
(1.0/0.0).should be_positive_infinity
(-1.0/0.0).should be_negative_infinity
3.14.should be_an_instance_of(Float) # Calls #instance_of?
3.14.should be_kind_of(Numeric) # Calls #is_a?
Numeric.should be_ancestor_of(Float) # Float.ancestors.include?(Numeric)
3.14.should respond_to(:to_i) # Calls #respond_to?
Fixnum.should have_instance_method(:+)
Array.should have_method(:new)
# Also have_constant, have_private_instance_method, have_singleton_method, etc
-> {
raise "oops"
}.should raise_error(RuntimeError, /oops/)
# To avoid! Instead, use an expectation testing what the code in the lambda does.
# If an exception is raised, it will fail the example anyway.
-> { ... }.should_not raise_error
-> {
Fixnum
}.should complain(/constant ::Fixnum is deprecated/) # Expect a warning
```
### Guards ### Guards
Different guards are available as defined by mspec. Different guards are available as defined by mspec.
Here is a list of the most commonly-used guards:
```ruby
ruby_version_is ""..."2.4" do
# Specs for RUBY_VERSION < 2.4
end
ruby_version_is "2.4" do
# Specs for RUBY_VERSION >= 2.4
end
platform_is :windows do
# Specs only valid on Windows
end
platform_is_not :windows do
# Specs valid on platforms other than Windows
end
platform_is :linux, :darwin do # OR
end
platform_is_not :linux, :darwin do # Not Linux and not Darwin
end
platform_is wordsize: 64 do
# 64-bit platform
end
big_endian do
# Big-endian platform
end
# In case there is a bug in MRI but the expected behavior is obvious
# First file a bug at https://bugs.ruby-lang.org/
# It is better to use a ruby_version_is guard if there was a release with the fix
ruby_bug '#13669', ''...'2.5' do
it "works like this" do
# Specify the expected behavior here, not the bug
end
end
# Combining guards
guard -> { platform_is :windows and ruby_version_is ""..."2.3" } do
# Windows and RUBY_VERSION < 2.3
end
guard_not -> { platform_is :windows and ruby_version_is ""..."2.3" } do
# The opposite
end
# Custom guard
max_uint = (1 << 32) - 1
guard -> { max_uint <= fixnum_max } do
end
```
In general, the usage of guards should be minimized as possible. In general, the usage of guards should be minimized as possible.
There are no guards to define implementation-specific behavior because There are no guards to define implementation-specific behavior because

View file

@ -15,7 +15,7 @@ describe "Array#at" do
a.at(7).should == nil a.at(7).should == nil
end 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 = [1, 2, 3, 4, 5, 6]
a.at(-1).should == 6 a.at(-1).should == 6
a.at(-2).should == 5 a.at(-2).should == 5
@ -50,7 +50,7 @@ describe "Array#at" do
lambda { [].at("cat") }.should raise_error(TypeError) lambda { [].at("cat") }.should raise_error(TypeError)
end 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) lambda { [:a, :b].at(0,1) }.should raise_error(ArgumentError)
end end
end end

View file

@ -19,7 +19,7 @@ describe "Array#&" do
it "does not modify the original Array" do it "does not modify the original Array" do
a = [1, 1, 3, 5] a = [1, 1, 3, 5]
a & [1, 2, 3] (a & [1, 2, 3]).should == [1, 3]
a.should == [1, 1, 3, 5] a.should == [1, 1, 3, 5]
end end
@ -52,7 +52,7 @@ describe "Array#&" do
obj1.stub!(:hash).and_return(0) obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0) obj2.stub!(:hash).and_return(0)
obj1.should_receive(:eql?).at_least(1).and_return(true) 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] & [obj2]).should == [obj1]
([obj1, obj1, obj2, obj2] & [obj2]).should == [obj1] ([obj1, obj1, obj2, obj2] & [obj2]).should == [obj1]

View file

@ -1,6 +1,12 @@
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
describe "Array#max" do 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 it "returns nil with no values" do
[].max.should == nil [].max.should == nil
end end

View file

@ -1,6 +1,12 @@
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
describe "Array#min" do 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 it "returns nil with no values" do
[].min.should == nil [].min.should == nil
end end

View file

@ -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

View file

@ -52,7 +52,7 @@ describe "Array#permutation" do
end end
it "returns no permutations when the given length has no permutations" do 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 } @numbers.permutation(9) { |n| @yielded << n }
@yielded.should == [] @yielded.should == []
end end

View file

@ -119,7 +119,7 @@ describe "Array#pop" do
a.should == [] a.should == []
end 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("cat") }.should raise_error(TypeError)
lambda{ [1, 2].pop(nil) }.should raise_error(TypeError) lambda{ [1, 2].pop(nil) }.should raise_error(TypeError)
end end

View file

@ -104,7 +104,7 @@ describe "Array#shift" do
a.should == [] a.should == []
end 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("cat") }.should raise_error(TypeError)
lambda{ [1, 2].shift(nil) }.should raise_error(TypeError) lambda{ [1, 2].shift(nil) }.should raise_error(TypeError)
end end

View file

@ -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

View file

@ -16,4 +16,16 @@ describe "Bignum" do
Bignum.new Bignum.new
end.should raise_error(NoMethodError) end.should raise_error(NoMethodError)
end 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 end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -68,7 +68,7 @@ describe "Dir.glob" do
Dir.glob('**/', File::FNM_DOTMATCH).sort.should == expected Dir.glob('**/', File::FNM_DOTMATCH).sort.should == expected
end 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. # directory as well.
it "recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH" do it "recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH" do
expected = %w[ expected = %w[

View file

@ -89,7 +89,7 @@ with_feature :encoding do
@ec.primitive_convert("","",nil,nil, {after_output: true}).should == :finished @ec.primitive_convert("","",nil,nil, {after_output: true}).should == :finished
end 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 = "".force_encoding('utf-8')
dest.encoding.should == Encoding::UTF_8 dest.encoding.should == Encoding::UTF_8
@ec.primitive_convert("\u{98}",dest).should == :finished @ec.primitive_convert("\u{98}",dest).should == :finished

View file

@ -16,15 +16,13 @@ with_feature :encoding do
it "returns [:source_buffer_empty,nil,nil,nil, nil] when #convert last succeeded" do it "returns [:source_buffer_empty,nil,nil,nil, nil] when #convert last succeeded" do
ec = Encoding::Converter.new('ascii','utf-8') ec = Encoding::Converter.new('ascii','utf-8')
ec.convert("a".force_encoding('ascii')).should == "a".\ ec.convert("a".force_encoding('ascii')).should == "a".force_encoding('utf-8')
force_encoding('utf-8')
ec.primitive_errinfo.should == [:source_buffer_empty, nil, nil, nil, nil] ec.primitive_errinfo.should == [:source_buffer_empty, nil, nil, nil, nil]
end end
it "returns [:destination_buffer_full,nil,nil,nil,nil] when #primitive_convert last returned :destination_buffer_full" do 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 = Encoding::Converter.new("utf-8", "iso-2022-jp")
ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false) \ ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false).should == :destination_buffer_full
.should == :destination_buffer_full
ec.primitive_errinfo.should == [:destination_buffer_full, nil, nil, nil, nil] ec.primitive_errinfo.should == [:destination_buffer_full, nil, nil, nil, nil]
end end

View file

@ -40,8 +40,7 @@ with_feature :encoding do
src = "\x00\xd8\x61\x00" src = "\x00\xd8\x61\x00"
dst = "" dst = ""
ec.primitive_convert(src, dst).should == :invalid_byte_sequence ec.primitive_convert(src, dst).should == :invalid_byte_sequence
ec.primitive_errinfo.should == ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
[:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
ec.putback(1).should == "\x00".force_encoding("utf-16le") ec.putback(1).should == "\x00".force_encoding("utf-16le")
ec.putback.should == "a".force_encoding("utf-16le") ec.putback.should == "a".force_encoding("utf-16le")
ec.putback.should == "" ec.putback.should == ""

View file

@ -23,7 +23,7 @@ with_feature :encoding do
# conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from # 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 to EUC-JP failed. IOW, it failed when the source encoding was
# UTF-8, so UTF-8 is regarded as the source encoding. # 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' @exception2.source_encoding_name.should == 'UTF-8'
end end
end end

View file

@ -28,7 +28,7 @@ with_feature :encoding do
# FIXME: Derive example where the failure occurs at the UTF-8 -> # FIXME: Derive example where the failure occurs at the UTF-8 ->
# ISO-8859-1 case so as to better illustrate the issue # 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 @exception2.source_encoding.should == Encoding::EUC_JP
end end
end end

View file

@ -23,7 +23,7 @@ with_feature :encoding do
# conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from # 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 to EUC-JP failed. IOW, it failed when the source encoding was
# UTF-8, so UTF-8 is regarded as the source encoding. # 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' @exception2.source_encoding_name.should == 'UTF-8'
end end
end end

View file

@ -24,7 +24,7 @@ with_feature :encoding do
# conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from # 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 to EUC-JP failed. IOW, it failed when the source encoding was
# UTF-8, so UTF-8 is regarded as the source encoding. # 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 @exception2.source_encoding.should == Encoding::UTF_8
end end
end end

View file

@ -68,11 +68,11 @@ describe "Enumerable#any?" do
describe "with block" do describe "with block" do
it "returns true if the block ever returns other than false or nil" do it "returns true if the block ever returns other than false or nil" do
@enum.any? { true } == true @enum.any? { true }.should == true
@enum.any? { 0 } == true @enum.any? { 0 }.should == true
@enum.any? { 1 } == 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| o < 1 }.should == true
@enum1.any?{ |o| 5 }.should == true @enum1.any?{ |o| 5 }.should == true

View file

@ -69,7 +69,7 @@ describe "Enumerable#cycle" do
enum.cycle(obj).to_a.should == [3, 2, 1, 3, 2, 1] enum.cycle(obj).to_a.should == [3, 2, 1, 3, 2, 1]
end 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 enum = EnumerableSpecs::Numerous.new
lambda{ enum.cycle("cat"){} }.should raise_error(TypeError) lambda{ enum.cycle("cat"){} }.should raise_error(TypeError)
end end

View file

@ -34,7 +34,7 @@ describe "Enumerable#drop" do
EnumerableSpecs::Numerous.new(3, 2, 1, :go).drop(4).should == [] EnumerableSpecs::Numerous.new(3, 2, 1, :go).drop(4).should == []
end 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("hat") }.should raise_error(TypeError)
lambda{ @enum.drop(nil) }.should raise_error(TypeError) lambda{ @enum.drop(nil) }.should raise_error(TypeError)
end end

View file

@ -7,6 +7,71 @@ ruby_version_is '2.4' do
[0, 1, 2, 3].to_enum.uniq { |n| n.even? }.should == [0, 1] [0, 1, 2, 3].to_enum.uniq { |n| n.even? }.should == [0, 1]
end 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 context 'when yielded with multiple arguments' do
before :each do before :each do
@enum = Object.new.to_enum @enum = Object.new.to_enum

View file

@ -24,7 +24,7 @@ describe "Enumerator::Lazy#force" do
(0..Float::INFINITY).lazy.map(&:succ).take(2).force.should == [1, 2] (0..Float::INFINITY).lazy.map(&:succ).take(2).force.should == [1, 2]
@eventsmixed.take(1).map(&:succ).force.should == [1] @eventsmixed.take(1).map(&:succ).force.should == [1]
ScratchPad.recorded == [:after_yields] ScratchPad.recorded.should == [:before_yield]
end end
end end
end end

View file

@ -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

View file

@ -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

View file

@ -3,7 +3,7 @@ require File.expand_path('../../../spec_helper', __FILE__)
describe "File.readlink" do describe "File.readlink" do
# symlink/readlink are not supported on Windows # symlink/readlink are not supported on Windows
platform_is_not :windows do platform_is_not :windows do
describe "File.readlink with absolute paths" do describe "with absolute paths" do
before :each do before :each do
@file = tmp('file_readlink.txt') @file = tmp('file_readlink.txt')
@link = tmp('file_readlink.lnk') @link = tmp('file_readlink.lnk')
@ -35,7 +35,26 @@ describe "File.readlink" do
end end
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 before :each do
@cwd = Dir.pwd @cwd = Dir.pwd
@tmpdir = tmp("/readlink") @tmpdir = tmp("/readlink")

View file

@ -75,11 +75,11 @@ describe :file_fnmatch, shared: true do
File.send(@method, 'c*t', 'c/a/b/t').should == true File.send(@method, 'c*t', 'c/a/b/t').should == true
end 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 File.send(@method, 'ca[a-z]', 'cat').should == true
end 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 == false
File.send(@method, '[^a-z]', 'D').should == true File.send(@method, '[^a-z]', 'D').should == true
File.send(@method, '[A-Z]', 'd').should == false 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 File.send(@method, '/ca[s][s-t]/rul[a-b]/[z]he/[x-Z]orld', '/cats/rule/the/World').should == false
end 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
File.send(@method, 'ca[!t]', 'cat').should == false File.send(@method, 'ca[!t]', 'cat').should == false
end end
@ -106,13 +106,13 @@ describe :file_fnmatch, shared: true do
end end
platform_is_not :windows do 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 File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == false
end end
end end
platform_is :windows do 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 File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == true
end end
end end

View file

@ -16,4 +16,16 @@ describe "Fixnum" do
Fixnum.new Fixnum.new
end.should raise_error(NoMethodError) end.should raise_error(NoMethodError)
end 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 end

View file

@ -10,4 +10,14 @@ describe "Float#ceil" do
-9223372036854775808.1.ceil.should eql(-9223372036854775808) -9223372036854775808.1.ceil.should eql(-9223372036854775808)
+9223372036854775808.1.ceil.should eql(+9223372036854775808) +9223372036854775808.1.ceil.should eql(+9223372036854775808)
end 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 end

View file

@ -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

View file

@ -10,4 +10,14 @@ describe "Float#floor" do
-9223372036854775808.1.floor.should eql(-9223372036854775808) -9223372036854775808.1.floor.should eql(-9223372036854775808)
+9223372036854775808.1.floor.should eql(+9223372036854775808) +9223372036854775808.1.floor.should eql(+9223372036854775808)
end 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 end

View file

@ -84,4 +84,18 @@ describe "Float#round" do
-2.5e200.round(-200).should eql( -3 * 10 ** 200 ) -2.5e200.round(-200).should eql( -3 * 10 ** 200 )
-2.4e200.round(-200).should eql( -2 * 10 ** 200 ) -2.4e200.round(-200).should eql( -2 * 10 ** 200 )
end 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 end

View file

@ -3,4 +3,14 @@ require File.expand_path('../shared/to_i', __FILE__)
describe "Float#truncate" do describe "Float#truncate" do
it_behaves_like(:float_to_i, :truncate) 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 end

View file

@ -11,13 +11,13 @@ describe "Hash#delete" do
it "calls supplied block if the key is not found" do it "calls supplied block if the key is not found" do
{ a: 1, b: 10, c: 100 }.delete(:d) { 5 }.should == 5 { a: 1, b: 10, c: 100 }.delete(:d) { 5 }.should == 5
Hash.new(:default).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 end
it "returns nil if the key is not found when no block is given" do 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 { a: 1, b: 10, c: 100 }.delete(:d).should == nil
Hash.new(:default).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 end
# MRI explicitly implements this behavior # MRI explicitly implements this behavior

View file

@ -24,6 +24,15 @@ ruby_version_is "2.4" do
enumerator.each(&:succ).should == { a: 2, b: 3, c: 4 } enumerator.each(&:succ).should == { a: 2, b: 3, c: 4 }
end end
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 end
describe "Hash#transform_values!" do describe "Hash#transform_values!" do
@ -41,6 +50,14 @@ ruby_version_is "2.4" do
@hash.should == { a: 2, b: 3, c: 4 } @hash.should == { a: 2, b: 3, c: 4 }
end 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 context "when no block is given" do
it "returns a sized Enumerator" do it "returns a sized Enumerator" do
enumerator = @hash.transform_values! enumerator = @hash.transform_values!
@ -56,6 +73,10 @@ ruby_version_is "2.4" do
@hash.freeze @hash.freeze
end 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 it "keeps pairs and raises a RuntimeError" do
->{ @hash.transform_values!(&:succ) }.should raise_error(RuntimeError) ->{ @hash.transform_values!(&:succ) }.should raise_error(RuntimeError)
@hash.should == @initial_pairs @hash.should == @initial_pairs

View file

@ -1,6 +1,21 @@
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/to_i', __FILE__) require File.expand_path('../shared/to_i', __FILE__)
require File.expand_path('../shared/integer_rounding', __FILE__)
describe "Integer#ceil" do describe "Integer#ceil" do
it_behaves_like(:integer_to_i, :ceil) 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 end

View file

@ -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

View file

@ -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

View file

@ -1,6 +1,21 @@
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/to_i', __FILE__) require File.expand_path('../shared/to_i', __FILE__)
require File.expand_path('../shared/integer_rounding', __FILE__)
describe "Integer#floor" do describe "Integer#floor" do
it_behaves_like(:integer_to_i, :floor) 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 end

View file

@ -4,6 +4,13 @@ describe "Integer" do
it "includes Comparable" do it "includes Comparable" do
Integer.include?(Comparable).should == true Integer.include?(Comparable).should == true
end 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 end
describe "Integer#integer?" do describe "Integer#integer?" do

View file

@ -1,10 +1,12 @@
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/to_i', __FILE__) require File.expand_path('../shared/to_i', __FILE__)
require File.expand_path('../shared/integer_rounding', __FILE__)
describe "Integer#round" do describe "Integer#round" do
it_behaves_like(:integer_to_i, :round) 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 it "rounds itself as a float if passed a positive precision" do
[2, -4, 10**70, -10**100].each do |v| [2, -4, 10**70, -10**100].each do |v|
v.round(42).should eql(v.to_f) v.round(42).should eql(v.to_f)
@ -12,20 +14,6 @@ describe "Integer#round" do
end 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.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 # redmine:5228
it "returns itself rounded if passed a negative value" do it "returns itself rounded if passed a negative value" do
+249.round(-2).should eql(+200) +249.round(-2).should eql(+200)
@ -74,4 +62,34 @@ describe "Integer#round" do
obj.stub!(:to_int).and_return([]) obj.stub!(:to_int).and_return([])
lambda { 42.round(obj) }.should raise_error(TypeError) lambda { 42.round(obj) }.should raise_error(TypeError)
end 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 end

View file

@ -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

View file

@ -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

View file

@ -1,6 +1,21 @@
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/to_i', __FILE__) require File.expand_path('../shared/to_i', __FILE__)
require File.expand_path('../shared/integer_rounding', __FILE__)
describe "Integer#truncate" do describe "Integer#truncate" do
it_behaves_like(:integer_to_i, :truncate) 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 end

View file

@ -17,7 +17,7 @@ describe "IO#advise" do
}.should raise_error(TypeError) }.should raise_error(TypeError)
end 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 { lambda {
@io.advise(:normal, "wat") @io.advise(:normal, "wat")
}.should raise_error(TypeError) }.should raise_error(TypeError)

View file

@ -20,6 +20,18 @@ module IOSpecs
"Here is line six.\n" ] "Here is line six.\n" ]
end 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 def self.lines_limit
[ "Voici la l", [ "Voici la l",
"igne une.\n", "igne une.\n",

View file

@ -138,6 +138,14 @@ describe "IO#gets" do
end end
end 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 end
describe "IO#gets" do describe "IO#gets" do
@ -191,7 +199,7 @@ describe "IO#gets" do
@io.gets(obj, 5).should == "one\n" @io.gets(obj, 5).should == "one\n"
end 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" @io.gets(6).should == "one\n"
end end

View file

@ -42,4 +42,12 @@ describe "IO#readline" do
$_.should == line $_.should == line
end 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.readline(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
end
end
end
end end

View file

@ -114,6 +114,15 @@ describe :io_each, shared: true do
ScratchPad.recorded.should == IOSpecs.paragraphs ScratchPad.recorded.should == IOSpecs.paragraphs
end end
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 end
describe :io_each_default_separator, shared: true do describe :io_each_default_separator, shared: true do

View file

@ -17,6 +17,13 @@ describe :io_readlines, shared: true do
result = IO.send(@method, @name, "", &@object) result = IO.send(@method, @name, "", &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_empty_separator (result ? result : ScratchPad.recorded).should == IOSpecs.lines_empty_separator
end 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 end
describe :io_readlines_options_19, shared: true do describe :io_readlines_options_19, shared: true do

View file

@ -7,9 +7,9 @@ describe "Kernel#==" do
o2 = mock('o2') o2 = mock('o2')
(o1 == o1).should == true (o1 == o1).should == true
(o2 == o2).should == true (o2 == o2).should == true
(o1 == o2).should== false (o1 == o2).should == false
(nil == nil).should == true (nil == nil).should == true
(o1 == nil).should== false (o1 == nil).should == false
(nil == o2).should== false (nil == o2).should == false
end end
end end

View file

@ -4,8 +4,11 @@ module MainSpecs
module WrapIncludeModule module WrapIncludeModule
end end
DATA = {}
end end
def main_public_method def main_public_method
end end
public :main_public_method public :main_public_method

View file

@ -0,0 +1,7 @@
module StringRefinement
refine(String) do
def foo
'foo'
end
end
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,10 @@
module ModuleSpecs
module EmptyRefinement
end
module RefinementForStringToS
refine String do
def to_s; "hello from refinement"; end
end
end
end

View file

@ -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

View file

@ -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

View file

@ -33,14 +33,9 @@ describe "Mutex#lock" do
# related to this ML thread. # related to this ML thread.
it "raises a ThreadError when used recursively" do it "raises a ThreadError when used recursively" do
m = Mutex.new m = Mutex.new
m.lock
th = Thread.new do -> {
m.lock m.lock
m.lock }.should raise_error(ThreadError)
end
lambda do
th.join
end.should raise_error(ThreadError)
end end
end end

View file

@ -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

View file

@ -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 # 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. # of the behavior until we find out if it's a bug.
quarantine! do 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 a = NumericSpecs::Subclass.new
b = NumericSpecs::Subclass.new b = NumericSpecs::Subclass.new

View file

@ -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

View file

@ -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

View file

@ -5,8 +5,11 @@ describe "Process.wait2" do
# HACK: this kludge is temporarily necessary because some # HACK: this kludge is temporarily necessary because some
# misbehaving spec somewhere else does not clear processes # misbehaving spec somewhere else does not clear processes
begin begin
Process.wait(-1, Process::WNOHANG)
$stderr.puts "Leaked process before wait2 specs! Waiting for it"
leaked = Process.waitall 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 rescue NotImplementedError
end end
end end

View file

@ -19,14 +19,14 @@ describe "Random#bytes" do
end end
# Should double check this is official spec # 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 = Random.new(33)
rnd.bytes(2).should == "\x14\\" rnd.bytes(2).should == "\x14\\"
rnd.bytes(1000) # skip some rnd.bytes(1000) # skip some
rnd.bytes(2).should == "\xA1p" rnd.bytes(2).should == "\xA1p"
end 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 = Random.new(bignum_value ** 4)
rnd.bytes(2).should == "_\x91" rnd.bytes(2).should == "_\x91"
rnd.bytes(1000) # skip some rnd.bytes(1000) # skip some

View file

@ -44,6 +44,14 @@ describe "String#capitalize!" do
a.should == "Hello" a.should == "Hello"
end 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 it "returns nil when no changes are made" do
a = "Hello" a = "Hello"
a.capitalize!.should == nil a.capitalize!.should == nil

View file

@ -1,4 +1,4 @@
# -*- encoding: ascii-8bit -*- # -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __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 "in UTF-8 mode" do
describe "for non-ASCII characters" do describe "for non-ASCII characters" do
before :each do before :each do
@upper_a_tilde = "\xc3\x83" @upper_a_tilde = "Ã"
@lower_a_tilde = "\xc3\xa3" @lower_a_tilde = "ã"
@upper_a_umlaut = "\xc3\x84" @upper_a_umlaut = "Ä"
@lower_a_umlaut = "\xc3\xa4" @lower_a_umlaut = "ä"
end end
it "returns -1 when numerically less than other" do it "returns -1 when numerically less than other" do
@ -118,3 +118,67 @@ describe "String#casecmp independent of case" do
end end
end 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

View file

@ -46,6 +46,14 @@ describe "String#downcase!" do
a.should == "hello" a.should == "hello"
end 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 it "returns nil if no modifications were made" do
a = "hello" a = "hello"
a.downcase!.should == nil a.downcase!.should == nil

View file

@ -10,4 +10,13 @@ describe "String#lines" do
ary = "hello world".send(@method, ' ') ary = "hello world".send(@method, ' ')
ary.should == ["hello ", "world"] ary.should == ["hello ", "world"]
end 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 end

View file

@ -15,6 +15,13 @@ describe "String.new" do
end end
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 it "returns a fully-formed String" do
str = String.new str = String.new
str.size.should == 0 str.size.should == 0

View file

@ -42,10 +42,8 @@ describe :string_chars, shared: true do
it "returns a different character if the String is transcoded" do it "returns a different character if the String is transcoded" do
s = "\u{20AC}".force_encoding('UTF-8') s = "\u{20AC}".force_encoding('UTF-8')
s.encode('UTF-8').send(@method).to_a.should == ["\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 == [ s.encode('iso-8859-15').send(@method).to_a.should == [[0xA4].pack('C').force_encoding('iso-8859-15')]
[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').encode('UTF-8').send(@method).to_a.should == [
"\u{20AC}".force_encoding('UTF-8')]
end end
it "uses the String's encoding to determine what characters it contains" do it "uses the String's encoding to determine what characters it contains" do

View file

@ -48,7 +48,7 @@ describe :string_codepoints, shared: true do
s.should == s2 s.should == s2
end 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 = "(){}".encode('ascii')
s.ascii_only?.should be_true s.ascii_only?.should be_true
s.send(@method).to_a.should == s.bytes.to_a s.send(@method).to_a.should == s.bytes.to_a

View file

@ -133,4 +133,18 @@ end
it "raises a TypeError when the separator is a symbol" do it "raises a TypeError when the separator is a symbol" do
lambda { "hello world".send(@method, :o).to_a }.should raise_error(TypeError) lambda { "hello world".send(@method, :o).to_a }.should raise_error(TypeError)
end 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 end

View file

@ -1,24 +1,63 @@
describe :string_to_sym, shared: true do describe :string_to_sym, shared: true do
it "returns the symbol corresponding to self" do it "returns the symbol corresponding to self" do
"Koala".send(@method).should == :Koala "Koala".send(@method).should equal :Koala
'cat'.send(@method).should == :cat 'cat'.send(@method).should equal :cat
'@cat'.send(@method).should == :@cat '@cat'.send(@method).should equal :@cat
'cat and dog'.send(@method).should == :"cat and dog" 'cat and dog'.send(@method).should equal :"cat and dog"
"abc=".send(@method).should == :abc= "abc=".send(@method).should equal :abc=
end end
it "does not special case +(binary) and -(binary)" do it "does not special case +(binary) and -(binary)" do
"+(binary)".send(@method).should == :"+(binary)" "+(binary)".send(@method).should equal :"+(binary)"
"-(binary)".send(@method).should == :"-(binary)" "-(binary)".send(@method).should equal :"-(binary)"
end end
it "does not special case certain operators" do it "does not special case certain operators" do
[ ["!@", :"!@"], "!@".send(@method).should equal :"!@"
["~@", :"~@"], "~@".send(@method).should equal :"~@"
["!(unary)", :"!(unary)"], "!(unary)".send(@method).should equal :"!(unary)"
["~(unary)", :"~(unary)"], "~(unary)".send(@method).should equal :"~(unary)"
["+(unary)", :"+(unary)"], "+(unary)".send(@method).should equal :"+(unary)"
["-(unary)", :"-(unary)"] "-(unary)".send(@method).should equal :"-(unary)"
].should be_computed_by(@method) 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
end end

View file

@ -41,6 +41,14 @@ describe "String#swapcase!" do
a.should == "CyBeR_pUnK11" a.should == "CyBeR_pUnK11"
end 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 it "returns nil if no modifications were made" do
a = "+++---111222???" a = "+++---111222???"
a.swapcase!.should == nil a.swapcase!.should == nil

View file

@ -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

View file

@ -46,6 +46,15 @@ describe "String#upcase!" do
a.should == "HELLO" a.should == "HELLO"
end 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 it "returns nil if no modifications were made" do
a = "HELLO" a = "HELLO"
a.upcase!.should == nil a.upcase!.should == nil

View file

@ -37,7 +37,7 @@ describe "Struct#initialize" do
car.make.should == nil # still nil despite override in Honda#initialize b/c of super order car.make.should == nil # still nil despite override in Honda#initialize b/c of super order
end end
it "can be overriden" do it "can be overridden" do
StructClasses::SubclassX.new(:y).new.key.should == :value StructClasses::SubclassX.new(:y).new.key.should == :value
end end
end end

View file

@ -21,6 +21,13 @@ describe "Symbol#capitalize" do
end end
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 it "converts subsequent uppercase ASCII characters to their lowercase equivalents" do
:lOWER.capitalize.should == :Lower :lOWER.capitalize.should == :Lower
end end

View file

@ -1,4 +1,4 @@
# -*- encoding: binary -*- # -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
describe "Symbol#casecmp with Symbol" do 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 it "doesn't consider non-ascii characters equal that aren't" do
# -- Latin-1 -- # -- Latin-1 --
upper_a_tilde = :"\xC3" upper_a_tilde = "\xC3".b.to_sym
upper_a_umlaut = :"\xC4" upper_a_umlaut = "\xC4".b.to_sym
lower_a_tilde = :"\xE3" lower_a_tilde = "\xE3".b.to_sym
lower_a_umlaut = :"\xE4" lower_a_umlaut = "\xE4".b.to_sym
lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0 lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0
lower_a_umlaut.casecmp(lower_a_tilde).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 upper_a_umlaut.casecmp(upper_a_tilde).should_not == 0
# -- UTF-8 -- # -- UTF-8 --
upper_a_tilde = :"\xC3\x83" upper_a_tilde = :"Ã"
upper_a_umlaut = :"\xC3\x84" lower_a_tilde = :"ã"
lower_a_tilde = :"\xC3\xA3" upper_a_umlaut = :"Ä"
lower_a_umlaut = :"\xC3\xA4" lower_a_umlaut = :"ä"
lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0 lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0
lower_a_umlaut.casecmp(lower_a_tilde).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 it "doesn't do case mapping for non-ascii characters" do
# -- Latin-1 -- # -- Latin-1 --
upper_a_tilde = :"\xC3" upper_a_tilde = "\xC3".b.to_sym
upper_a_umlaut = :"\xC4" upper_a_umlaut = "\xC4".b.to_sym
lower_a_tilde = :"\xE3" lower_a_tilde = "\xE3".b.to_sym
lower_a_umlaut = :"\xE4" lower_a_umlaut = "\xE4".b.to_sym
upper_a_tilde.casecmp(lower_a_tilde).should == -1 upper_a_tilde.casecmp(lower_a_tilde).should == -1
upper_a_umlaut.casecmp(lower_a_umlaut).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 lower_a_umlaut.casecmp(upper_a_umlaut).should == 1
# -- UTF-8 -- # -- UTF-8 --
upper_a_tilde = :"\xC3\x83" upper_a_tilde = :"Ã"
upper_a_umlaut = :"\xC3\x84" lower_a_tilde = :"ã"
lower_a_tilde = :"\xC3\xA3" upper_a_umlaut = :"Ä"
lower_a_umlaut = :"\xC3\xA4" lower_a_umlaut = :"ä"
upper_a_tilde.casecmp(lower_a_tilde).should == -1 upper_a_tilde.casecmp(lower_a_tilde).should == -1
upper_a_umlaut.casecmp(lower_a_umlaut).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 :abc.casecmp(obj).should be_nil
end end
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

View file

@ -20,6 +20,13 @@ describe "Symbol#downcase" do
end end
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 it "leaves non-alphabetic ASCII characters as they were" do
"Glark?!?".to_sym.downcase.should == :"glark?!?" "Glark?!?".to_sym.downcase.should == :"glark?!?"
end end

View file

@ -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

View file

@ -28,6 +28,13 @@ describe "Symbol#swapcase" do
end end
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 it "leaves non-alphabetic ASCII characters as they were" do
"Glark?!?".to_sym.swapcase.should == :"gLARK?!?" "Glark?!?".to_sym.swapcase.should == :"gLARK?!?"
end end

View file

@ -16,6 +16,13 @@ describe "Symbol#upcase" do
end end
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 it "leaves non-alphabetic ASCII characters as they were" do
"Glark?!?".to_sym.upcase.should == :"GLARK?!?" "Glark?!?".to_sym.upcase.should == :"GLARK?!?"
end end

View file

@ -9,12 +9,12 @@ describe "Thread#[]=" do
it "raises a RuntimeError if the thread is frozen" do it "raises a RuntimeError if the thread is frozen" do
running = false running = false
t = Thread.new do t = Thread.new do
Thread.pass until running
t.freeze t.freeze
t[:foo] = "bar" -> {
t[:foo] = "bar"
}.should raise_error(RuntimeError, /frozen/)
end end
running = true t.join
lambda { t.join }.should raise_error(RuntimeError)
end end
it "raises exceptions on the wrong type of keys" do it "raises exceptions on the wrong type of keys" do

View file

@ -120,7 +120,10 @@ module ThreadSpecs
end end
def self.status_of_thread_with_uncaught_exception 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 begin
t.join t.join
rescue RuntimeError rescue RuntimeError
@ -159,6 +162,7 @@ module ThreadSpecs
def self.dying_thread_ensures(kill_method_name=:kill) def self.dying_thread_ensures(kill_method_name=:kill)
Thread.new do Thread.new do
Thread.current.report_on_exception = false
begin begin
Thread.current.send(kill_method_name) Thread.current.send(kill_method_name)
ensure ensure
@ -169,6 +173,7 @@ module ThreadSpecs
def self.dying_thread_with_outer_ensure(kill_method_name=:kill) def self.dying_thread_with_outer_ensure(kill_method_name=:kill)
Thread.new do Thread.new do
Thread.current.report_on_exception = false
begin begin
begin begin
Thread.current.send(kill_method_name) Thread.current.send(kill_method_name)

View file

@ -46,7 +46,10 @@ describe "Thread#join" do
end end
it "raises any exceptions encountered in the thread body" do 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) lambda { t.join }.should raise_error(NotImplementedError)
end end

View file

@ -9,7 +9,7 @@ describe "Thread#key?" do
@th.join @th.join
end 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?("oliver").should == true @th.key?("oliver").should == true
@th.key?(:stanley).should == false @th.key?(:stanley).should == false

View file

@ -51,6 +51,7 @@ describe "Thread#raise on a sleeping thread" do
it "is captured and raised by Thread#value" do it "is captured and raised by Thread#value" do
t = Thread.new do t = Thread.new do
Thread.current.report_on_exception = false
sleep sleep
end 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 it "raises a RuntimeError when called with no arguments inside rescue" do
t = Thread.new do t = Thread.new do
Thread.current.report_on_exception = false
begin begin
1/0 1/0
rescue ZeroDivisionError rescue ZeroDivisionError
@ -113,6 +115,7 @@ describe "Thread#raise on a running thread" do
it "can go unhandled" do it "can go unhandled" do
t = Thread.new do t = Thread.new do
Thread.current.report_on_exception = false
loop { Thread.pass } loop { Thread.pass }
end 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 it "raises the given argument even when there is an active exception" do
raised = false raised = false
t = Thread.new do t = Thread.new do
Thread.current.report_on_exception = false
begin begin
1/0 1/0
rescue ZeroDivisionError 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 it "raises a RuntimeError when called with no arguments inside rescue" do
raised = false raised = false
t = Thread.new do t = Thread.new do
Thread.current.report_on_exception = false
begin begin
1/0 1/0
rescue ZeroDivisionError 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 it "raises a RuntimeError when called with no arguments inside rescue" do
t = Thread.new do t = Thread.new do
Thread.current.report_on_exception = false
begin begin
1/0 1/0
rescue ZeroDivisionError rescue ZeroDivisionError

View file

@ -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

View file

@ -112,7 +112,7 @@ describe :thread_exit, shared: true do
quarantine! 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) { } thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { }
lambda { thread.join }.should raise_error(RuntimeError, "In dying thread") lambda { thread.join }.should raise_error(RuntimeError, "In dying thread")
end end

View file

@ -7,7 +7,10 @@ describe "Thread#value" do
end end
it "re-raises an error for an uncaught exception" do 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") lambda { t.value }.should raise_error(RuntimeError, "Hello")
end end

Some files were not shown because too many files have changed in this diff Show more