This commit is contained in:
Benoit Daloze 2023-02-27 21:02:44 +01:00
parent de60139053
commit 18b4def471
230 changed files with 1889 additions and 189 deletions

View File

@ -0,0 +1,13 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#all?" do
@value_to_return = -> _ { true }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :all?
it "ignores the block if there is an argument" do
-> {
['bar', 'foobar'].all?(/bar/) { false }.should == true
}.should complain(/given block not used/)
end
end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#any?" do
describe 'with no block given (a default block of { |x| x } is implicit)' do
@ -19,6 +20,9 @@ describe "Array#any?" do
end
describe 'with a block given' do
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :any?
it 'is false if the array is empty' do
empty_array = []
empty_array.any? {|v| 1 == 1 }.should == false
@ -34,4 +38,12 @@ describe "Array#any?" do
array_with_members.any? {|v| v == 42 }.should == false
end
end
describe 'when given a pattern argument' do
it "ignores the block if there is an argument" do
-> {
['bar', 'foobar'].any?(/bar/) { false }.should == true
}.should complain(/given block not used/)
end
end
end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#count" do
it "returns the number of elements" do
@ -12,4 +13,14 @@ describe "Array#count" do
it "returns the number of element for which the block evaluates to true" do
[:a, :b, :c].count { |s| s != :b }.should == 2
end
it "ignores the block if there is an argument" do
-> {
[:a, :b, :b, :c].count(:b) { |e| e.size > 10 }.should == 2
}.should complain(/given block not used/)
end
context "when a block argument given" do
it_behaves_like :array_iterable_and_tolerating_size_increasing, :count
end
end

View File

@ -2,6 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize'
require_relative 'shared/delete_if'
require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized'
describe "Array#delete_if" do
@ -47,6 +48,35 @@ describe "Array#delete_if" do
-> { ArraySpecs.empty_frozen_array.delete_if {} }.should raise_error(FrozenError)
end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.delete_if { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "only removes elements for which the block returns true, keeping the element which raised an error." do
a = [1, 2, 3, 4]
begin
a.delete_if do |e|
case e
when 2 then true
when 3 then raise StandardError, 'Oops'
else false
end
end
rescue StandardError
end
a.should == [1, 3, 4]
end
it_behaves_like :enumeratorized_with_origin_size, :delete_if, [1,2,3]
it_behaves_like :delete_if, :delete_if
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :delete_if
end

View File

@ -1,7 +1,11 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#drop_while" do
@value_to_return = -> _ { true }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :drop_while
it "removes elements from the start of the array while the block evaluates to true" do
[1, 2, 3, 4].drop_while { |n| n < 4 }.should == [4]
end

View File

@ -40,3 +40,19 @@ describe "Array#each_index" do
it_behaves_like :enumeratorize, :each_index
it_behaves_like :enumeratorized_with_origin_size, :each_index, [1,2,3]
end
describe "Array#each_index" do
it "tolerates increasing an array size during iteration" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.each_index do |index|
ScratchPad << index
array << i if i < 100
i += 1
end
ScratchPad.recorded.should == (0..102).to_a # element indices
end
end

View File

@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize'
require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized'
# Mutating the array while it is being iterated is discouraged as it can result in confusing behavior.
@ -75,3 +76,7 @@ describe "Array#each" do
it_behaves_like :enumeratorize, :each
it_behaves_like :enumeratorized_with_origin_size, :each, [1,2,3]
end
describe "Array#each" do
it_behaves_like :array_iterable_and_tolerating_size_increasing, :each
end

View File

@ -72,6 +72,48 @@ describe "Array#fill" do
-> { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError)
-> { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError)
end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.fill { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "only changes elements before error is raised, keeping the element which raised an error." do
a = [1, 2, 3, 4]
begin
a.fill do |i|
case i
when 0 then -1
when 1 then -2
when 2 then raise StandardError, 'Oops'
else 0
end
end
rescue StandardError
end
a.should == [-1, -2, 3, 4]
end
it "tolerates increasing an array size during iteration" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.fill do |index|
ScratchPad << index
array << i if i < 100
i++
index
end
ScratchPad.recorded.should == [0, 1, 2]
end
end
describe "Array#fill with (filler, index, length)" do

View File

@ -53,7 +53,9 @@ describe "Array#initialize with no arguments" do
end
it "does not use the given block" do
->{ [1, 2, 3].send(:initialize) { raise } }.should_not raise_error
-> {
-> { [1, 2, 3].send(:initialize) { raise } }.should_not raise_error
}.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
end
end

View File

@ -1,17 +1,66 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'Array#intersect?' do
ruby_version_is '3.1' do # https://bugs.ruby-lang.org/issues/15198
describe 'when at least one element in two Arrays is the same' do
it 'returns true' do
[1, 2].intersect?([2, 3]).should == true
[1, 2].intersect?([2, 3, 4]).should == true
[2, 3, 4].intersect?([1, 2]).should == true
end
end
describe 'when there are no elements in common between two Arrays' do
it 'returns false' do
[1, 2].intersect?([3, 4]).should == false
[0, 1, 2].intersect?([3, 4]).should == false
[3, 4].intersect?([0, 1, 2]).should == false
[3, 4].intersect?([]).should == false
[].intersect?([0, 1, 2]).should == false
end
end
it "tries to convert the passed argument to an Array using #to_ary" do
obj = mock('[1,2,3]')
obj.should_receive(:to_ary).and_return([1, 2, 3])
[1, 2].intersect?(obj).should == true
end
it "determines equivalence between elements in the sense of eql?" do
obj1 = mock('1')
obj2 = mock('2')
obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0)
obj1.stub!(:eql?).and_return(true)
obj2.stub!(:eql?).and_return(true)
[obj1].intersect?([obj2]).should == true
obj1 = mock('3')
obj2 = mock('4')
obj1.stub!(:hash).and_return(0)
obj2.stub!(:hash).and_return(0)
obj1.stub!(:eql?).and_return(false)
obj2.stub!(:eql?).and_return(false)
[obj1].intersect?([obj2]).should == false
end
it "does not call to_ary on array subclasses" do
[5, 6].intersect?(ArraySpecs::ToAryArray[1, 2, 5, 6]).should == true
end
it "properly handles an identical item even when its #eql? isn't reflexive" do
x = mock('x')
x.stub!(:hash).and_return(42)
x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
[x].intersect?([x]).should == true
end
it "has semantic of !(a & b).empty?" do
[].intersect?([]).should == false
[nil].intersect?([nil]).should == true
end
end
end

View File

@ -26,7 +26,9 @@ describe "Array.new with no arguments" do
end
it "does not use the given block" do
->{ Array.new { raise } }.should_not raise_error
-> {
-> { Array.new { raise } }.should_not raise_error
}.should complain(/warning: given block not used/, verbose: true)
end
end

View File

@ -0,0 +1,13 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#none?" do
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :none?
it "ignores the block if there is an argument" do
-> {
['bar', 'foobar'].none?(/baz/) { true }.should == true
}.should complain(/given block not used/)
end
end

View File

@ -0,0 +1,13 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#one?" do
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :one?
it "ignores the block if there is an argument" do
-> {
['bar', 'foobar'].one?(/foo/) { false }.should == true
}.should complain(/given block not used/)
end
end

View File

@ -2,6 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize'
require_relative 'shared/delete_if'
require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized'
describe "Array#reject" do
@ -47,6 +48,10 @@ describe "Array#reject" do
it_behaves_like :enumeratorized_with_origin_size, :reject, [1,2,3]
end
describe "Array#reject" do
it_behaves_like :array_iterable_and_tolerating_size_increasing, :reject
end
describe "Array#reject!" do
it "removes elements for which block is true" do
a = [3, 4, 5, 6, 7, 8, 9, 10, 11]
@ -111,6 +116,11 @@ describe "Array#reject!" do
-> { ArraySpecs.empty_frozen_array.reject! {} }.should raise_error(FrozenError)
end
it "raises a FrozenError on a frozen array only during iteration if called without a block" do
enum = ArraySpecs.frozen_array.reject!
-> { enum.each {} }.should raise_error(FrozenError)
end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
@ -141,3 +151,8 @@ describe "Array#reject!" do
it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3]
it_behaves_like :delete_if, :reject!
end
describe "Array#reject!" do
@value_to_return = -> _ { false }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :reject!
end

View File

@ -38,6 +38,20 @@ describe "Array#reverse_each" do
[1, 2, 3].reverse_each.size.should == 3
end
it "tolerates increasing an array size during iteration" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.reverse_each do |e|
ScratchPad << e
array.prepend i if i < 100
i += 1
end
ScratchPad.recorded.should == [:c, :a, 1]
end
it_behaves_like :enumeratorize, :reverse_each
it_behaves_like :enumeratorized_with_origin_size, :reverse_each, [1,2,3]
end

View File

@ -68,6 +68,21 @@ describe "Array#rindex" do
seen.should == [3]
end
it "tolerates increasing an array size during iteration" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.rindex do |e|
ScratchPad << e
array.prepend i if i < 100
i += 1
false
end
ScratchPad.recorded.should == [:c, :a, 1]
end
describe "given no argument and no block" do
it "produces an Enumerator" do
enum = [4, 2, 1, 5, 1, 3].rindex

View File

@ -1,4 +1,5 @@
require_relative '../../enumerable/shared/enumeratorized'
require_relative '../shared/iterable_and_tolerating_size_increasing'
describe :array_collect, shared: true do
it "returns a copy of array with each element replaced by the value returned by block" do
@ -46,6 +47,8 @@ describe :array_collect, shared: true do
@object = [1, 2, 3, 4]
end
it_should_behave_like :enumeratorized_with_origin_size
it_should_behave_like :array_iterable_and_tolerating_size_increasing
end
describe :array_collect_b, shared: true do
@ -102,8 +105,37 @@ describe :array_collect_b, shared: true do
end
end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.send(@method) { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "only changes elements before error is raised, keeping the element which raised an error." do
a = [1, 2, 3, 4]
begin
a.send(@method) do |e|
case e
when 1 then -1
when 2 then -2
when 3 then raise StandardError, 'Oops'
else 0
end
end
rescue StandardError
end
a.should == [-1, -2, 3, 4]
end
before :all do
@object = [1, 2, 3, 4]
end
it_should_behave_like :enumeratorized_with_origin_size
it_should_behave_like :array_iterable_and_tolerating_size_increasing
end

View File

@ -1,3 +1,5 @@
require_relative '../shared/iterable_and_tolerating_size_increasing'
describe :array_index, shared: true do
it "returns the index of the first element == to object" do
x = mock('3')
@ -34,4 +36,6 @@ describe :array_index, shared: true do
[].send(@method).should be_an_instance_of(Enumerator)
end
end
it_should_behave_like :array_iterable_and_tolerating_size_increasing
end

View File

@ -11,7 +11,8 @@ describe :array_intersection, shared: true do
end
it "creates an array with elements in order they are first encountered" do
[ 1, 2, 3, 2, 5 ].send(@method, [ 5, 2, 3, 4 ]).should == [2, 3, 5]
[ 1, 2, 3, 2, 5, 6, 7, 8 ].send(@method, [ 5, 2, 3, 4 ]).should == [2, 3, 5] # array > other
[ 5, 2, 3, 4 ].send(@method, [ 1, 2, 3, 2, 5, 6, 7, 8 ]).should == [5, 2, 3] # array < other
end
it "does not modify the original Array" do

View File

@ -0,0 +1,25 @@
describe :array_iterable_and_tolerating_size_increasing, shared: true do
before do
@value_to_return ||= -> _ { nil }
end
it "tolerates increasing an array size during iteration" do
# The goal is to trigger potential reallocation of internal array storage, so we:
# - use elements of different types, starting with the less generic (Integer)
# - add reasonably big number of new elements (~ 100)
array = [1, 2, 3] # to test some methods we need several uniq elements
array_to_join = [:a, :b, :c] + (4..100).to_a
ScratchPad.record []
i = 0
array.send(@method) do |e|
ScratchPad << e
array << array_to_join[i] if i < array_to_join.size
i += 1
@value_to_return.call(e)
end
ScratchPad.recorded.should == [1, 2, 3] + array_to_join
end
end

View File

@ -1,4 +1,5 @@
require_relative '../../enumerable/shared/enumeratorized'
require_relative '../shared/iterable_and_tolerating_size_increasing'
describe :keep_if, shared: true do
it "deletes elements for which the block returns a false value" do
@ -56,5 +57,39 @@ describe :keep_if, shared: true do
-> { @frozen.send(@method) { false } }.should raise_error(FrozenError)
end
end
it "raises a FrozenError on a frozen array only during iteration if called without a block" do
enum = @frozen.send(@method)
-> { enum.each {} }.should raise_error(FrozenError)
end
end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.send(@method) { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "only changes elements before error is raised, keeping the element which raised an error." do
a = [1, 2, 3, 4]
begin
a.send(@method) do |e|
case e
when 2 then false
when 3 then raise StandardError, 'Oops'
else true
end
end
rescue StandardError
end
a.should == [1, 3, 4]
end
@value_to_return = -> _ { true }
it_should_behave_like :array_iterable_and_tolerating_size_increasing
end

View File

@ -2,11 +2,14 @@ require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/enumeratorize'
require_relative '../shared/keep_if'
require_relative '../shared/iterable_and_tolerating_size_increasing'
require_relative '../../enumerable/shared/enumeratorized'
describe :array_select, shared: true do
it_should_behave_like :enumeratorize
it_should_behave_like :array_iterable_and_tolerating_size_increasing
before :each do
@object = [1,2,3]
end

View File

@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized'
describe "Array#sort_by!" do
@ -31,6 +32,11 @@ describe "Array#sort_by!" do
-> { ArraySpecs.empty_frozen_array.sort_by! {}}.should raise_error(FrozenError)
end
it "raises a FrozenError on a frozen array only during iteration if called without a block" do
enum = ArraySpecs.frozen_array.sort_by!
-> { enum.each {} }.should raise_error(FrozenError)
end
it "returns the specified value when it would break in the given block" do
[1, 2, 3].sort_by!{ break :a }.should == :a
end
@ -48,5 +54,32 @@ describe "Array#sort_by!" do
[1].sort_by!(&:to_s).should == [1]
end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.sort_by! { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "doesn't change array if error is raised" do
a = [4, 3, 2, 1]
begin
a.sort_by! do |e|
raise StandardError, 'Oops' if e == 1
e
end
rescue StandardError
end
a.should == [4, 3, 2, 1]
end
it_behaves_like :enumeratorized_with_origin_size, :sort_by!, [1,2,3]
end
describe "Array#sort_by!" do
it_behaves_like :array_iterable_and_tolerating_size_increasing, :sort_by!
end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#sum" do
it "returns the sum of elements" do
@ -69,3 +70,8 @@ describe "Array#sum" do
[b].sum(a).should == 42
end
end
describe "Array#sum" do
@value_to_return = -> _ { 1 }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :sum
end

View File

@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#take_while" do
it "returns all elements until the block returns false" do
@ -26,3 +27,8 @@ describe "Array#take_while" do
end
end
end
describe "Array#take_while" do
@value_to_return = -> _ { true }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :take_while
end

View File

@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#to_h" do
it "converts empty array to empty hash" do
@ -77,3 +78,8 @@ describe "Array#to_h" do
end
end
end
describe "Array#to_h" do
@value_to_return = -> e { [e, e.to_s] }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :to_h
end

View File

@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#uniq" do
it "returns an array with no duplicates" do
@ -131,6 +132,11 @@ describe "Array#uniq" do
end
end
describe "Array#uniq" do
@value_to_return = -> e { e }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :uniq
end
describe "Array#uniq!" do
it "modifies the array in place" do
a = [ "a", "a", "b", "b", "c" ]
@ -214,4 +220,32 @@ describe "Array#uniq!" do
a.uniq!
a.should == [x]
end
it "does not truncate the array is the block raises an exception" do
a = [1, 2, 3]
begin
a.send(@method) { raise StandardError, 'Oops' }
rescue
end
a.should == [1, 2, 3]
end
it "doesn't change array if error is raised" do
a = [1, 1, 2, 2, 3, 3, 4, 4]
begin
a.send(@method) do |e|
raise StandardError, 'Oops' if e == 3
e
end
rescue StandardError
end
a.should == [1, 1, 2, 2, 3, 3, 4, 4]
end
end
describe "Array#uniq!" do
@value_to_return = -> e { e }
it_behaves_like :array_iterable_and_tolerating_size_increasing, :uniq!
end

View File

@ -15,8 +15,231 @@ module BasicObjectSpecs
include InstExec
end
module InstEvalCVar
instance_eval { @@count = 2 }
module InstEval
module CVar
module Get
class ReceiverScope
@@cvar = :value_defined_in_receiver_scope
end
class BlockDefinitionScope
@@cvar = :value_defined_in_block_definition_scope
def block
-> * { @@cvar }
end
end
class CallerScope
@@cvar = :value_defined_in_caller_scope
def get_class_variable_with_string(obj)
obj.instance_eval("@@cvar")
end
def get_class_variable_with_block(obj, block)
obj.instance_eval(&block)
end
end
class CallerWithoutCVarScope
def get_class_variable_with_string(obj)
obj.instance_eval("@@cvar")
end
end
ReceiverWithCVarDefinedInSingletonClass = Class.new.new.tap do |obj|
obj.singleton_class.class_variable_set(:@@cvar, :value_defined_in_receiver_singleton_class)
end
end
module Set
class ReceiverScope
end
class BlockDefinitionScope
def self.get_class_variable
@@cvar
end
def block_to_assign(value)
-> * { @@cvar = value }
end
end
class CallerScope
def self.get_class_variable
@@cvar
end
def set_class_variable_with_string(obj, value)
obj.instance_eval("@@cvar=#{value.inspect}")
end
def set_class_variable_with_block(obj, block)
obj.instance_eval(&block)
end
end
end
end
end
module InstEval
module Constants
module ConstantInReceiverSingletonClass
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
FOO = :Receiver
def initialize
self.singleton_class.const_set(:FOO, :singleton_class)
end
end
end
module CallerScope
FOO = :CallerScope
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
FOO = :Caller
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
module ConstantInReceiverClass
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
FOO = :Receiver
end
end
module CallerScope
FOO = :CallerScope
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
FOO = :Caller
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
module ConstantInCallerClass
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
# FOO is not declared in a receiver class
end
end
module CallerScope
FOO = :CallerScope
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
FOO = :Caller
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
module ConstantInCallerOuterScopes
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
# FOO is not declared in a receiver class
end
end
module CallerScope
FOO = :CallerScope
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
# FOO is not declared in a caller class
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
module ConstantInReceiverParentClass
module ReceiverScope
FOO = :ReceiverScope
class ReceiverParent
FOO = :ReceiverParent
end
class Receiver < ReceiverParent
# FOO is not declared in a receiver class
end
end
module CallerScope
# FOO is not declared in a caller outer scopes
class CallerParent
FOO = :CallerParent
end
class Caller < CallerParent
# FOO is not declared in a caller class
def get_constant_with_string(receiver)
receiver.instance_eval("FOO")
end
end
end
end
end
end
class InstEvalConst
@ -26,7 +249,6 @@ module BasicObjectSpecs
module InstEvalOuter
module Inner
obj = InstEvalConst.new
X_BY_STR = obj.instance_eval("INST_EVAL_CONST_X") rescue nil
X_BY_BLOCK = obj.instance_eval { INST_EVAL_CONST_X } rescue nil
end
end

View File

@ -89,6 +89,18 @@ describe "BasicObject#instance_eval" do
BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99
end
it "raises TypeError for frozen objects when tries to set receiver's instance variables" do
-> { nil.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen NilClass: nil")
-> { true.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen TrueClass: true")
-> { false.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen FalseClass: false")
-> { 1.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen Integer: 1")
-> { :symbol.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen Symbol: :symbol")
obj = Object.new
obj.freeze
-> { obj.instance_eval { @foo = 42 } }.should raise_error(FrozenError)
end
it "treats block-local variables as local to the block" do
prc = instance_eval <<-CODE
proc do |x, prc|
@ -105,11 +117,6 @@ describe "BasicObject#instance_eval" do
prc.call(false, prc).should == 1
end
it "sets class variables in the receiver" do
BasicObjectSpecs::InstEvalCVar.class_variables.should include(:@@count)
BasicObjectSpecs::InstEvalCVar.send(:class_variable_get, :@@count).should == 2
end
it "makes the receiver metaclass the scoped class when used with a string" do
obj = Object.new
obj.instance_eval %{
@ -119,8 +126,52 @@ describe "BasicObject#instance_eval" do
obj.singleton_class.const_get(:B).should be_an_instance_of(Class)
end
it "gets constants in the receiver if a string given" do
BasicObjectSpecs::InstEvalOuter::Inner::X_BY_STR.should == 2
describe "constants lookup when a String given" do
it "looks in the receiver singleton class first" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverSingletonClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverSingletonClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :singleton_class
end
ruby_version_is ""..."3.1" do
it "looks in the caller scope next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :Caller
end
end
ruby_version_is "3.1" do
it "looks in the receiver class next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :Receiver
end
end
it "looks in the caller class next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInCallerClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInCallerClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :Caller
end
it "looks in the caller outer scopes next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInCallerOuterScopes::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInCallerOuterScopes::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :CallerScope
end
it "looks in the receiver class hierarchy next" do
receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverParentClass::ReceiverScope::Receiver.new
caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverParentClass::CallerScope::Caller.new
caller.get_constant_with_string(receiver).should == :ReceiverParent
end
end
it "doesn't get constants in the receiver if a block given" do
@ -136,17 +187,51 @@ describe "BasicObject#instance_eval" do
end.should raise_error(TypeError)
end
quarantine! do # Not clean, leaves cvars lying around to break other specs
it "scopes class var accesses in the caller when called on an Integer" do
# Integer can take instance vars
Integer.class_eval "@@__tmp_instance_eval_spec = 1"
(defined? @@__tmp_instance_eval_spec).should be_nil
describe "class variables lookup" do
it "gets class variables in the caller class when called with a String" do
receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Get::CallerScope.new
@@__tmp_instance_eval_spec = 2
1.instance_eval { @@__tmp_instance_eval_spec }.should == 2
Integer.__send__(:remove_class_variable, :@@__tmp_instance_eval_spec)
caller.get_class_variable_with_string(receiver).should == :value_defined_in_caller_scope
end
it "gets class variables in the block definition scope when called with a block" do
receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Get::CallerScope.new
block = BasicObjectSpecs::InstEval::CVar::Get::BlockDefinitionScope.new.block
caller.get_class_variable_with_block(receiver, block).should == :value_defined_in_block_definition_scope
end
it "sets class variables in the caller class when called with a String" do
receiver = BasicObjectSpecs::InstEval::CVar::Set::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Set::CallerScope.new
caller.set_class_variable_with_string(receiver, 1)
BasicObjectSpecs::InstEval::CVar::Set::CallerScope.get_class_variable.should == 1
end
it "sets class variables in the block definition scope when called with a block" do
receiver = BasicObjectSpecs::InstEval::CVar::Set::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Set::CallerScope.new
block = BasicObjectSpecs::InstEval::CVar::Set::BlockDefinitionScope.new.block_to_assign(1)
caller.set_class_variable_with_block(receiver, block)
BasicObjectSpecs::InstEval::CVar::Set::BlockDefinitionScope.get_class_variable.should == 1
end
it "does not have access to class variables in the receiver class when called with a String" do
receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
caller = BasicObjectSpecs::InstEval::CVar::Get::CallerWithoutCVarScope.new
-> { caller.get_class_variable_with_string(receiver) }.should raise_error(NameError, /uninitialized class variable @@cvar/)
end
it "does not have access to class variables in the receiver's singleton class when called with a String" do
receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverWithCVarDefinedInSingletonClass
caller = BasicObjectSpecs::InstEval::CVar::Get::CallerWithoutCVarScope.new
-> { caller.get_class_variable_with_string(receiver) }.should raise_error(NameError, /uninitialized class variable @@cvar/)
end
end
end
it "raises a TypeError when defining methods on numerics" do
-> do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/basicobject/method_missing'
describe "BasicObject#method_missing" do

View File

@ -0,0 +1,26 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is "3.2" do
describe "Data.define" do
it "accepts no arguments" do
empty_data = Data.define
empty_data.members.should == []
end
it "accepts symbols" do
movie_with_symbol = Data.define(:title, :year)
movie_with_symbol.members.should == [:title, :year]
end
it "accepts strings" do
movie_with_string = Data.define("title", "year")
movie_with_string.members.should == [:title, :year]
end
it "accepts a mix of strings and symbols" do
blockbuster_movie = Data.define("title", :year, "genre")
blockbuster_movie.members.should == [:title, :year, :genre]
end
end
end

View File

@ -0,0 +1,5 @@
module DataSpecs
ruby_version_is "3.2" do
Measure = Data.define(:amount, :unit)
end
end

View File

@ -0,0 +1,58 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is "3.2" do
describe "Data#initialize" do
it "accepts positional arguments" do
data = DataSpecs::Measure.new(42, "km")
data.amount.should == 42
data.unit.should == "km"
end
it "accepts alternative positional arguments" do
data = DataSpecs::Measure[42, "km"]
data.amount.should == 42
data.unit.should == "km"
end
it "accepts keyword arguments" do
data = DataSpecs::Measure.new(amount: 42, unit: "km")
data.amount.should == 42
data.unit.should == "km"
end
it "accepts alternative keyword arguments" do
data = DataSpecs::Measure[amount: 42, unit: "km"]
data.amount.should == 42
data.unit.should == "km"
end
it "raises ArgumentError if no arguments are given" do
-> {
DataSpecs::Measure.new
}.should raise_error(ArgumentError) { |e|
e.message.should.include?("missing keywords: :amount, :unit")
}
end
it "raises ArgumentError if at least one argument is missing" do
-> {
DataSpecs::Measure.new(unit: "km")
}.should raise_error(ArgumentError) { |e|
e.message.should.include?("missing keyword: :amount")
}
end
it "raises ArgumentError if unknown keyword is given" do
-> {
DataSpecs::Measure.new(amount: 42, unit: "km", system: "metric")
}.should raise_error(ArgumentError) { |e|
e.message.should.include?("unknown keyword: :system")
}
end
end
end

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#destination_encoding" do

View File

@ -1,4 +1,5 @@
# -*- encoding: binary -*-
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#error_bytes" do

View File

@ -1,4 +1,5 @@
# -*- encoding: binary -*-
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#readagain_bytes" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding_name" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#source_encoding" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative 'shared/name'
describe "Encoding#name" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative 'shared/name'
describe "Encoding#to_s" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#destination_encoding_name" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#destination_encoding" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#error_char" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding_name" do

View File

@ -1,3 +1,4 @@
require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding" do

View File

@ -177,5 +177,11 @@ describe "Enumerable#all?" do
multi.all?(pattern).should == true
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end
it "ignores the block if there is an argument" do
-> {
EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).all?(String) { true }.should == false
}.should complain(/given block not used/)
end
end
end

View File

@ -190,5 +190,11 @@ describe "Enumerable#any?" do
multi.any?(pattern).should == false
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end
it "ignores the block if there is an argument" do
-> {
EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).any?(String) { true }.should == false
}.should complain(/given block not used/)
end
end
end

View File

@ -29,6 +29,11 @@ describe "Enumerable#chunk" do
result.should == [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]]
end
it "returns a partitioned Array of values" do
e = EnumerableSpecs::Numerous.new(1,2,3)
e.chunk { |x| x > 2 }.map(&:last).should == [[1, 2], [3]]
end
it "returns elements for which the block returns :_alone in separate Arrays" do
e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1)
result = e.chunk { |x| x < 2 && :_alone }.to_a

View File

@ -143,5 +143,11 @@ describe "Enumerable#none?" do
multi.none?(pattern).should == true
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end
it "ignores the block if there is an argument" do
-> {
EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).none?(String) { true }.should == true
}.should complain(/given block not used/)
end
end
end

View File

@ -83,7 +83,6 @@ describe "Enumerable#one?" do
end
end
describe 'when given a pattern argument' do
it "calls `===` on the pattern the return value " do
pattern = EnumerableSpecs::Pattern.new { |x| x == 1 }
@ -145,5 +144,11 @@ describe "Enumerable#one?" do
multi.one?(pattern).should == false
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end
it "ignores the block if there is an argument" do
-> {
EnumerableSpecs::Numerous.new(1, 2, 3, 4, "5").one?(String) { false }.should == true
}.should complain(/given block not used/)
end
end
end

View File

@ -1,3 +1,5 @@
require_relative '../../array/shared/iterable_and_tolerating_size_increasing'
describe :enumerable_inject, shared: true do
it "with argument takes a block with an accumulator (with argument as initial value) and the current element. Value of block becomes new accumulator" do
a = []
@ -17,8 +19,13 @@ describe :enumerable_inject, shared: true do
end
it "ignores the block if two arguments" do
EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-) { raise "we never get here"}.should == 4
[].send(@method, 3, :+) { raise "we never get here"}.should == 3
-> {
EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-) { raise "we never get here"}.should == 4
}.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
-> {
[1, 2, 3].send(@method, 10, :-) { raise "we never get here"}.should == 4
}.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
end
it "can take a symbol argument" do
@ -31,10 +38,10 @@ describe :enumerable_inject, shared: true do
a.should == [[2, 5], [5, 3], [3, 6], [6, 1], [1, 4]]
end
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMulti.new
multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
end
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMulti.new
multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
end
it "with inject arguments(legacy rubycon)" do
# with inject argument
@ -68,6 +75,22 @@ describe :enumerable_inject, shared: true do
EnumerableSpecs::EachDefiner.new().send(@method) {|acc,x| 999 }.should == nil
end
it "tolerates increasing a collection size during iterating Array" do
array = [:a, :b, :c]
ScratchPad.record []
i = 0
array.send(@method, nil) do |_, e|
ScratchPad << e
array << i if i < 100
i += 1
end
actual = ScratchPad.recorded
expected = [:a, :b, :c] + (0..99).to_a
actual.sort_by(&:to_s).should == expected.sort_by(&:to_s)
end
ruby_bug '#18635', ''...'3.2' do
it "raises an ArgumentError when no parameters or block is given" do
-> { [1,2].send(@method) }.should raise_error(ArgumentError)

View File

@ -11,4 +11,8 @@ describe "Enumerator::Chain#inspect" do
obj.should_receive(:inspect).and_return('some desc')
Enumerator::Chain.new(obj).inspect.should == "#<Enumerator::Chain: [some desc]>"
end
it "returns a not initialized representation if #initialized is not called yet" do
Enumerator::Chain.allocate.inspect.should == "#<Enumerator::Chain: uninitialized>"
end
end

View File

@ -14,4 +14,9 @@ describe "Enumerator#inspect" do
(1..3).each.each_slice(2).inspect.should == "#<Enumerator: #<Enumerator: 1..3:each>:each_slice(2)>"
end
end
it "returns a not initialized representation if #initialized is not called yet" do
Enumerator.allocate.inspect.should == "#<Enumerator: uninitialized>"
Enumerator::Lazy.allocate.inspect.should == "#<Enumerator::Lazy: uninitialized>"
end
end

View File

@ -1,4 +1,5 @@
require_relative '../../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is '3.1' do
describe "Enumerator::Lazy#compact" do
@ -7,5 +8,9 @@ ruby_version_is '3.1' do
arr.should be_an_instance_of(Enumerator::Lazy)
arr.force.should == [1, 3, false, 5]
end
it "sets #size to nil" do
Enumerator::Lazy.new(Object.new, 100) {}.compact.size.should == nil
end
end
end

View File

@ -0,0 +1,22 @@
ready = false
t = Thread.new do
f = Fiber.new do
begin
Fiber.yield
ensure
STDERR.puts "suspended fiber ensure"
end
end
f.resume
begin
ready = true
sleep
ensure
STDERR.puts "current fiber ensure"
end
end
Thread.pass until ready && t.stop?
# let the program end, it's the same as #exit or an exception for this behavior

View File

@ -0,0 +1,25 @@
ready = false
t = Thread.new do
f = Fiber.new do
begin
Fiber.yield
ensure
STDERR.puts "suspended fiber ensure"
end
end
f.resume
f2 = Fiber.new do
begin
ready = true
sleep
ensure
STDERR.puts "current fiber ensure"
end
end
f2.resume
end
Thread.pass until ready && t.stop?
# let the program end, it's the same as #exit or an exception for this behavior

View File

@ -42,4 +42,16 @@ describe "An Exception reaching the top level" do
EOS
end
end
describe "kills all threads and fibers, ensure clauses are only run for threads current fibers, not for suspended fibers" do
it "with ensure on the root fiber" do
file = fixture(__FILE__, "thread_fiber_ensure.rb")
ruby_exe(file, args: "2>&1", exit_status: 0).should == "current fiber ensure\n"
end
it "with ensure on non-root fiber" do
file = fixture(__FILE__, "thread_fiber_ensure_non_root_fiber.rb")
ruby_exe(file, args: "2>&1", exit_status: 0).should == "current fiber ensure\n"
end
end
end

View File

@ -78,6 +78,22 @@ describe "File.new" do
File.should.exist?(@file)
end
it "returns a new read-only File when mode is not specified" do
@fh = File.new(@file)
-> { @fh.puts("test") }.should raise_error(IOError)
@fh.read.should == ""
File.should.exist?(@file)
end
it "returns a new read-only File when mode is not specified but flags option is present" do
@fh = File.new(@file, flags: File::CREAT)
-> { @fh.puts("test") }.should raise_error(IOError)
@fh.read.should == ""
File.should.exist?(@file)
end
it "creates a new file when use File::EXCL mode" do
@fh = File.new(@file, File::EXCL)
@fh.should be_kind_of(File)
@ -112,13 +128,32 @@ describe "File.new" do
File.should.exist?(@file)
end
it "creates a new file when use File::WRONLY|File::TRUNC mode" do
@fh = File.new(@file, File::WRONLY|File::TRUNC)
@fh.should be_kind_of(File)
File.should.exist?(@file)
end
it "returns a new read-only File when use File::RDONLY|File::CREAT mode" do
@fh = File.new(@file, File::RDONLY|File::CREAT)
@fh.should be_kind_of(File)
File.should.exist?(@file)
# it's read-only
-> { @fh.puts("test") }.should raise_error(IOError)
@fh.read.should == ""
end
it "returns a new read-only File when use File::CREAT mode" do
@fh = File.new(@file, File::CREAT)
@fh.should be_kind_of(File)
File.should.exist?(@file)
# it's read-only
-> { @fh.puts("test") }.should raise_error(IOError)
@fh.read.should == ""
end
it "coerces filename using to_str" do
name = mock("file")
name.should_receive(:to_str).and_return(@file)
@ -133,6 +168,28 @@ describe "File.new" do
File.should.exist?(@file)
end
ruby_version_is "3.0" do
it "accepts options as a keyword argument" do
@fh = File.new(@file, 'w', 0755, flags: @flags)
@fh.should be_kind_of(File)
@fh.close
-> {
@fh = File.new(@file, 'w', 0755, {flags: @flags})
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
end
it "bitwise-ORs mode and flags option" do
-> {
@fh = File.new(@file, 'w', flags: File::EXCL)
}.should raise_error(Errno::EEXIST, /File exists/)
-> {
@fh = File.new(@file, mode: 'w', flags: File::EXCL)
}.should raise_error(Errno::EEXIST, /File exists/)
end
it "raises a TypeError if the first parameter can't be coerced to a string" do
-> { File.new(true) }.should raise_error(TypeError)
-> { File.new(false) }.should raise_error(TypeError)

View File

@ -565,6 +565,17 @@ describe "File.open" do
File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::BINARY}
end
ruby_version_is "3.0" do
it "accepts options as a keyword argument" do
@fh = File.open(@file, 'w', 0755, flags: File::CREAT)
@fh.should be_an_instance_of(File)
-> {
File.open(@file, 'w', 0755, {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
end
it "uses the second argument as an options Hash" do
@fh = File.open(@file, mode: "r")
@fh.should be_an_instance_of(File)

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative 'shared/abs'
describe "Float#magnitude" do

View File

@ -27,6 +27,19 @@ describe "IO#initialize" do
@io.fileno.should == fd
end
ruby_version_is "3.0" do
it "accepts options as keyword arguments" do
fd = new_fd @name, "w:utf-8"
@io.send(:initialize, fd, "w", flags: File::CREAT)
@io.fileno.should == fd
-> {
@io.send(:initialize, fd, "w", {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
end
end
it "raises a TypeError when passed an IO" do
-> { @io.send :initialize, STDOUT, 'w' }.should raise_error(TypeError)
end

View File

@ -23,6 +23,17 @@ describe "IO.read" do
IO.read(p)
end
ruby_version_is "3.0" do
# https://bugs.ruby-lang.org/issues/19354
it "accepts options as keyword arguments" do
IO.read(@fname, 3, 0, mode: "r+").should == @contents[0, 3]
-> {
IO.read(@fname, 3, 0, {mode: "r+"})
}.should raise_error(ArgumentError, /wrong number of arguments/)
end
end
it "accepts an empty options Hash" do
IO.read(@fname, **{}).should == @contents
end
@ -55,6 +66,31 @@ describe "IO.read" do
IO.read(@fname, mode: "a+").should == @contents
end
platform_is_not :windows do
it "uses an :open_args option" do
string = IO.read(@fname, nil, 0, open_args: ["r", nil, {encoding: Encoding::US_ASCII}])
string.encoding.should == Encoding::US_ASCII
string = IO.read(@fname, nil, 0, open_args: ["r", nil, {}])
string.encoding.should == Encoding::UTF_8
end
end
it "disregards other options if :open_args is given" do
string = IO.read(@fname,mode: "w", encoding: Encoding::UTF_32LE, open_args: ["r", encoding: Encoding::UTF_8])
string.encoding.should == Encoding::UTF_8
end
it "doesn't require mode to be specified in :open_args" do
string = IO.read(@fname, nil, 0, open_args: [{encoding: Encoding::US_ASCII}])
string.encoding.should == Encoding::US_ASCII
end
it "doesn't require mode to be specified in :open_args even if flags option passed" do
string = IO.read(@fname, nil, 0, open_args: [{encoding: Encoding::US_ASCII, flags: File::CREAT}])
string.encoding.should == Encoding::US_ASCII
end
it "treats second nil argument as no length limit" do
IO.read(@fname, nil).should == @contents
IO.read(@fname, nil, 5).should == IO.read(@fname, @contents.length, 5)

View File

@ -127,6 +127,12 @@ describe "IO#readlines" do
end
end
end
describe "when passed arbitrary keyword argument" do
it "tolerates it" do
@io.readlines(chomp: true, foo: :bar).should == IOSpecs.lines_without_newline_characters
end
end
end
describe "IO#readlines" do

View File

@ -21,6 +21,16 @@ describe :io_binwrite, shared: true do
IO.send(@method, @filename, "abcde").should == 5
end
ruby_version_is "3.0" do
it "accepts options as a keyword argument" do
IO.send(@method, @filename, "hi", 0, flags: File::CREAT).should == 2
-> {
IO.send(@method, @filename, "hi", 0, {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 2..3)")
end
end
it "creates a file if missing" do
fn = @filename + "xxx"
begin
@ -67,6 +77,11 @@ describe :io_binwrite, shared: true do
File.read(@filename).should == "\0\0foo"
end
it "accepts a :flags option without :mode one" do
IO.send(@method, @filename, "hello, world!", flags: File::CREAT)
File.read(@filename).should == "hello, world!"
end
it "raises an error if readonly mode is specified" do
-> { IO.send(@method, @filename, "abcde", mode: "r") }.should raise_error(IOError)
end

View File

@ -64,6 +64,17 @@ describe :io_new, shared: true do
@io.should be_an_instance_of(IO)
end
ruby_version_is "3.0" do
it "accepts options as keyword arguments" do
@io = IO.send(@method, @fd, "w", flags: File::CREAT)
@io.write("foo").should == 3
-> {
IO.send(@method, @fd, "w", {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
end
end
it "accepts a :mode option" do
@io = IO.send(@method, @fd, mode: "w")
@io.write("foo").should == 3

View File

@ -24,10 +24,6 @@ describe "IO#write on a file" do
-> { @readonly_file.write("") }.should_not raise_error
end
it "returns a length of 0 when writing a blank string" do
@file.write('').should == 0
end
before :each do
@external = Encoding.default_external
@internal = Encoding.default_internal
@ -40,6 +36,18 @@ describe "IO#write on a file" do
Encoding.default_internal = @internal
end
it "returns a length of 0 when writing a blank string" do
@file.write('').should == 0
end
it "returns a length of 0 when writing blank strings" do
@file.write('', '', '').should == 0
end
it "returns a length of 0 when passed no arguments" do
@file.write().should == 0
end
it "returns the number of bytes written" do
@file.write("hellø").should == 6
end
@ -54,6 +62,18 @@ describe "IO#write on a file" do
File.binread(@filename).bytes.should == [159]
end
it "does not modify arguments when passed multiple arguments and external encoding not set" do
a, b = "a".freeze, "b".freeze
File.open(@filename, "w") do |f|
f.write(a, b)
end
File.binread(@filename).bytes.should == [97, 98]
a.encoding.should == Encoding::UTF_8
b.encoding.should == Encoding::UTF_8
end
it "uses the encoding from the given option for non-ascii encoding" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("hi").should == 8
@ -61,8 +81,25 @@ describe "IO#write on a file" do
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end
it "uses an :open_args option" do
IO.write(@filename, 'hi', open_args: ["w", nil, {encoding: Encoding::UTF_32LE}]).should == 8
it "uses the encoding from the given option for non-ascii encoding even if in binary mode" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE, binmode: true) do |file|
file.should.binmode?
file.write("hi").should == 8
end
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
File.open(@filename, "wb", encoding: Encoding::UTF_32LE) do |file|
file.should.binmode?
file.write("hi").should == 8
end
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end
it "uses the encoding from the given option for non-ascii encoding when multiple arguments passes" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("h", "i").should == 8
end
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end
it "raises a invalid byte sequence error if invalid bytes are being written" do
@ -82,6 +119,20 @@ describe "IO#write on a file" do
res = "H#{ë}ll#{ö}"
File.binread(@filename).should == res.force_encoding(Encoding::BINARY)
end
platform_is_not :windows do
it "writes binary data if no encoding is given and multiple arguments passed" do
File.open(@filename, "w") do |file|
file.write("\x87".b, "ą") # 0x87 isn't a valid UTF-8 binary representation of a character
end
File.binread(@filename).bytes.should == [0x87, 0xC4, 0x85]
File.open(@filename, "w") do |file|
file.write("\x61".encode("utf-32le"), "ą")
end
File.binread(@filename).bytes.should == [0x61, 0x00, 0x00, 0x00, 0xC4, 0x85]
end
end
end
describe "IO.write" do
@ -96,10 +147,38 @@ describe "IO.write" do
File.read(@filename).should == "\0\0hi"
end
it "requires mode to be specified in :open_args" do
-> {
IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true}])
}.should raise_error(IOError, "not opened for writing")
IO.write(@filename, 'hi', open_args: ["w", {encoding: Encoding::UTF_32LE, binmode: true}]).should == 8
IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, mode: "w"}]).should == 8
end
it "requires mode to be specified in :open_args even if flags option passed" do
-> {
IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT}])
}.should raise_error(IOError, "not opened for writing")
IO.write(@filename, 'hi', open_args: ["w", {encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT}]).should == 8
IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT, mode: "w"}]).should == 8
end
it "uses the given encoding and returns the number of bytes written" do
IO.write(@filename, 'hi', mode: "w", encoding: Encoding::UTF_32LE).should == 8
end
it "raises ArgumentError if encoding is specified in mode parameter and is given as :encoding option" do
-> {
IO.write(@filename, 'hi', mode: "w:UTF-16LE:UTF-16BE", encoding: Encoding::UTF_32LE)
}.should raise_error(ArgumentError, "encoding specified twice")
-> {
IO.write(@filename, 'hi', mode: "w:UTF-16BE", encoding: Encoding::UTF_32LE)
}.should raise_error(ArgumentError, "encoding specified twice")
end
it "writes the file with the permissions in the :perm parameter" do
rm_r @filename
IO.write(@filename, 'write :perm spec', mode: "w", perm: 0o755).should == 16

View File

@ -573,10 +573,17 @@ describe "Integer() given a String and base", shared: true do
-> { Integer("0#{d}1", base) }.should raise_error(ArgumentError)
end
end
end
it "raises an ArgumentError if a base is given for a non-String value" do
-> { Integer(98, 15) }.should raise_error(ArgumentError)
end
it "raises an ArgumentError if a base is given for a non-String value" do
-> { Integer(98, 15) }.should raise_error(ArgumentError)
end
it "tries to convert the base to an integer using to_int" do
obj = mock('8')
obj.should_receive(:to_int).and_return(8)
Integer("777", obj).should == 0777
end
describe "when passed exception: false" do

View File

@ -1,67 +1,16 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative '../../shared/kernel/at_exit'
describe "Kernel.at_exit" do
it_behaves_like :kernel_at_exit, :at_exit
it "is a private method" do
Kernel.should have_private_instance_method(:at_exit)
end
it "runs after all other code" do
ruby_exe("at_exit {print 5}; print 6").should == "65"
end
it "runs in reverse order of registration" do
code = "at_exit {print 4};at_exit {print 5}; print 6; at_exit {print 7}"
ruby_exe(code).should == "6754"
end
it "allows calling exit inside at_exit handler" do
code = "at_exit {print 3}; at_exit {print 4; exit; print 5}; at_exit {print 6}"
ruby_exe(code).should == "643"
end
it "gives access to the last raised exception" do
code = <<-EOC
at_exit do
puts "The exception matches: \#{$! == $exception} (message=\#{$!.message})"
end
begin
raise "foo"
rescue => $exception
raise
end
EOC
result = ruby_exe(code, args: "2>&1", exit_status: 1)
result.lines.should.include?("The exception matches: true (message=foo)\n")
end
it "both exceptions in at_exit and in the main script are printed" do
code = 'at_exit { raise "at_exit_error" }; raise "main_script_error"'
result = ruby_exe(code, args: "2>&1", exit_status: 1)
result.should.include?('at_exit_error (RuntimeError)')
result.should.include?('main_script_error (RuntimeError)')
end
it "decides the exit status if both at_exit and the main script raise SystemExit" do
ruby_exe('at_exit { exit 43 }; exit 42', args: "2>&1", exit_status: 43)
$?.exitstatus.should == 43
end
it "runs all at_exit even if some raise exceptions" do
code = 'at_exit { STDERR.puts "last" }; at_exit { exit 43 }; at_exit { STDERR.puts "first" }; exit 42'
result = ruby_exe(code, args: "2>&1", exit_status: 43)
result.should == "first\nlast\n"
$?.exitstatus.should == 43
end
it "runs at_exit handlers even if the main script fails to parse" do
script = fixture(__FILE__, "at_exit.rb")
result = ruby_exe('{', options: "-r#{script}", args: "2>&1", exit_status: 1)
$?.should_not.success?
result.should.include?("at_exit ran\n")
result.should.include?("syntax error")
it "raises ArgumentError if called without a block" do
-> { at_exit }.should raise_error(ArgumentError, "called without a block")
end
end

View File

@ -10,6 +10,10 @@ describe "Kernel#exit" do
it_behaves_like :process_exit, :exit, KernelSpecs::Method.new
end
describe "Kernel.exit" do
it_behaves_like :process_exit, :exit, Kernel
end
describe "Kernel#exit!" do
it "is a private method" do
Kernel.should have_private_instance_method(:exit!)
@ -18,10 +22,6 @@ describe "Kernel#exit!" do
it_behaves_like :process_exit!, :exit!, "self"
end
describe "Kernel.exit" do
it_behaves_like :process_exit, :exit, Kernel
end
describe "Kernel.exit!" do
it_behaves_like :process_exit!, :exit!, Kernel
it_behaves_like :process_exit!, :exit!, "Kernel"
end

View File

@ -1,3 +0,0 @@
at_exit do
STDERR.puts "at_exit ran"
end

View File

@ -72,6 +72,17 @@ describe "Kernel#open" do
-> { open }.should raise_error(ArgumentError)
end
ruby_version_is "3.0" do
it "accepts options as keyword arguments" do
@file = open(@name, "r", 0666, flags: File::CREAT)
@file.should be_kind_of(File)
-> {
open(@name, "r", 0666, {flags: File::CREAT})
}.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
end
describe "when given an object that responds to to_open" do
before :each do
ScratchPad.clear
@ -109,11 +120,21 @@ describe "Kernel#open" do
it "passes its arguments onto #to_open" do
obj = mock('to_open')
obj.should_receive(:to_open).with(1,2,3)
obj.should_receive(:to_open).with(1, 2, 3)
open(obj, 1, 2, 3)
end
it "passes keyword arguments onto #to_open as keyword arguments if to_open accepts them" do
obj = Object.new
def obj.to_open(*args, **kw)
ScratchPad << {args: args, kw: kw}
end
ScratchPad.record []
open(obj, 1, 2, 3, a: "b")
ScratchPad.recorded.should == [args: [1, 2, 3], kw: {a: "b"}]
end
it "passes the return value from #to_open to a block" do
obj = mock('to_open')
obj.should_receive(:to_open).and_return(:value)

View File

@ -20,10 +20,26 @@ describe "Kernel#singleton_class" do
end
it "raises TypeError for Integer" do
-> { 123.singleton_class }.should raise_error(TypeError)
-> { 123.singleton_class }.should raise_error(TypeError, "can't define singleton")
end
it "raises TypeError for Float" do
-> { 3.14.singleton_class }.should raise_error(TypeError, "can't define singleton")
end
it "raises TypeError for Symbol" do
-> { :foo.singleton_class }.should raise_error(TypeError)
-> { :foo.singleton_class }.should raise_error(TypeError, "can't define singleton")
end
it "raises TypeError for a frozen deduplicated String" do
-> { (-"string").singleton_class }.should raise_error(TypeError, "can't define singleton")
-> { a = -"string"; a.singleton_class }.should raise_error(TypeError, "can't define singleton")
-> { a = "string"; (-a).singleton_class }.should raise_error(TypeError, "can't define singleton")
end
it "returns a frozen singleton class if object is frozen" do
obj = Object.new
obj.freeze
obj.singleton_class.frozen?.should be_true
end
end

View File

@ -8,7 +8,11 @@ describe :marshal_load, shared: true do
it "raises an ArgumentError when the dumped data is truncated" do
obj = {first: 1, second: 2, third: 3}
-> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError)
-> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError, "marshal data too short")
end
it "raises an ArgumentError when the argument is empty String" do
-> { Marshal.send(@method, "") }.should raise_error(ArgumentError, "marshal data too short")
end
it "raises an ArgumentError when the dumped class is missing" do

View File

@ -67,6 +67,11 @@ describe "Module#const_source_location" do
end
describe "with statically assigned constants" do
it "works for the module and class keywords" do
ConstantSpecs.const_source_location(:ModuleB).should == [@constants_fixture_path, ConstantSpecs::ModuleB::LINE]
ConstantSpecs.const_source_location(:ClassA).should == [@constants_fixture_path, ConstantSpecs::ClassA::LINE]
end
it "searches location path the immediate class or module first" do
ConstantSpecs::ClassA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CONST10_LINE]
ConstantSpecs::ModuleA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ModuleA::CS_CONST10_LINE]
@ -133,7 +138,7 @@ describe "Module#const_source_location" do
it "calls #to_str to convert the given name to a String" do
name = mock("ClassA")
name.should_receive(:to_str).and_return("ClassA")
ConstantSpecs.const_source_location(name).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CLASS_A_LINE]
ConstantSpecs.const_source_location(name).should == [@constants_fixture_path, ConstantSpecs::ClassA::LINE]
end
it "raises a TypeError if conversion to a String by calling #to_str fails" do

View File

@ -2,6 +2,10 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Module#method_added" do
before :each do
ScratchPad.record []
end
it "is a private instance method" do
Module.should have_private_instance_method(:method_added)
end
@ -13,8 +17,6 @@ describe "Module#method_added" do
end
it "is called when a new instance method is defined in self" do
ScratchPad.record []
Module.new do
def self.method_added(name)
ScratchPad << name
@ -32,8 +34,6 @@ describe "Module#method_added" do
it "is not called when a singleton method is added" do
# obj.singleton_method_added is called instead
ScratchPad.record []
klass = Class.new
def klass.method_added(name)
ScratchPad << name
@ -60,8 +60,71 @@ describe "Module#method_added" do
m.should_not have_method(:method_to_undef)
end
it "is not called when a method changes visibility" do
Module.new do
def public_method
end
def private_method
end
def self.method_added(name)
ScratchPad << name
end
public :public_method
private :public_method
private :private_method
public :private_method
end
ScratchPad.recorded.should == []
end
it "is called when using #private in a subclass" do
parent = Class.new do
def foo
end
end
Class.new(parent) do
def self.method_added(name)
ScratchPad << name
end
# Create an instance as that might initialize some method lookup caches, which is interesting to test
self.new.foo
private :foo
public :foo
end
ScratchPad.recorded.should == [:foo]
end
it "is not called when a method is copied via module_function, rather #singleton_method_added is called" do
Module.new do
def mod_function
end
def self.method_added(name)
ScratchPad << [:method_added, name]
end
def self.singleton_method_added(name)
ScratchPad << [:singleton_method_added, name]
end
ScratchPad.record []
module_function :mod_function
end
ScratchPad.recorded.should == [[:singleton_method_added, :mod_function]]
end
it "is called with a precise caller location with the line of the 'def'" do
ScratchPad.record []
line = nil
Module.new do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative 'shared/abs'
describe "Numeric#magnitude" do

View File

@ -6,5 +6,5 @@ describe "Process.exit" do
end
describe "Process.exit!" do
it_behaves_like :process_exit!, :exit!, Process
it_behaves_like :process_exit!, :exit!, "Process"
end

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
describe "Random.new" do
it "returns a new instance of Random" do
Random.new.should be_an_instance_of(Random)

View File

@ -474,10 +474,14 @@ describe "Range#step" do
end
end
# We use .take below to ensure the enumerator works
# because that's an Enumerable method and so it uses the Enumerator behavior
# not just a method overridden in Enumerator::ArithmeticSequence.
describe "type" do
context "when both begin and end are numerics" do
it "returns an instance of Enumerator::ArithmeticSequence" do
(1..10).step.class.should == Enumerator::ArithmeticSequence
(1..10).step(3).take(4).should == [1, 4, 7, 10]
end
end
@ -490,10 +494,12 @@ describe "Range#step" do
context "when range is endless" do
it "returns an instance of Enumerator::ArithmeticSequence when begin is numeric" do
(1..).step.class.should == Enumerator::ArithmeticSequence
(1..).step(2).take(3).should == [1, 3, 5]
end
it "returns an instance of Enumerator when begin is not numeric" do
("a"..).step.class.should == Enumerator
("a"..).step(2).take(3).should == %w[a c e]
end
end
@ -506,6 +512,7 @@ describe "Range#step" do
context "when begin and end are not numerics" do
it "returns an instance of Enumerator" do
("a".."z").step.class.should == Enumerator
("a".."z").step(3).take(4).should == %w[a d g j]
end
end
end

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/abs'
describe "Rational#abs" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/ceil'
describe "Rational#ceil" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/coerce'
describe "Rational#coerce" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/comparison'
describe "Rational#<=> when passed a Rational object" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/denominator'
describe "Rational#denominator" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/div'
describe "Rational#div" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/divide'
require_relative '../../shared/rational/arithmetic_exception_in_coerce'

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/divmod'
describe "Rational#divmod when passed a Rational" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/equal_value'
describe "Rational#==" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/exponent'
describe "Rational#**" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/fdiv'
describe "Rational#fdiv" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/floor'
describe "Rational#floor" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/hash'
describe "Rational#hash" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/inspect'
describe "Rational#inspect" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
describe "Rational#integer?" do
# Guard against the Mathn library
guard -> { !defined?(Math.rsqrt) } do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/abs'
describe "Rational#abs" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/modulo'
describe "Rational#%" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/multiply'
require_relative '../../shared/rational/arithmetic_exception_in_coerce'

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/numerator'
describe "Rational#numerator" do

View File

@ -1,3 +1,4 @@
require_relative "../../spec_helper"
require_relative '../../shared/rational/plus'
require_relative '../../shared/rational/arithmetic_exception_in_coerce'

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