mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00:00
Update to ruby/spec@2e11d2a
This commit is contained in:
parent
bbb4c7b88b
commit
85cd08e4f9
Notes:
git
2025-11-19 11:37:12 +00:00
@ -24,7 +24,7 @@ describe 'Comparable#clamp' do
|
||||
c.clamp(two, three).should equal(c)
|
||||
end
|
||||
|
||||
it 'returns the min parameter if smaller than it' do
|
||||
it 'returns the min parameter if less than it' do
|
||||
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
|
||||
two = ComparableSpecs::WithOnlyCompareDefined.new(2)
|
||||
c = ComparableSpecs::Weird.new(0)
|
||||
@ -52,7 +52,7 @@ describe 'Comparable#clamp' do
|
||||
c.clamp(two..three).should equal(c)
|
||||
end
|
||||
|
||||
it 'returns the minimum value of the range parameters if smaller than it' do
|
||||
it 'returns the minimum value of the range parameters if less than it' do
|
||||
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
|
||||
two = ComparableSpecs::WithOnlyCompareDefined.new(2)
|
||||
c = ComparableSpecs::Weird.new(0)
|
||||
@ -75,4 +75,70 @@ describe 'Comparable#clamp' do
|
||||
|
||||
-> { c.clamp(one...two) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
context 'with endless range' do
|
||||
it 'returns minimum value of the range parameters if less than it' do
|
||||
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
|
||||
zero = ComparableSpecs::WithOnlyCompareDefined.new(0)
|
||||
c = ComparableSpecs::Weird.new(0)
|
||||
|
||||
c.clamp(one..).should equal(one)
|
||||
c.clamp(zero..).should equal(c)
|
||||
end
|
||||
|
||||
it 'always returns self if greater than minimum value of the range parameters' do
|
||||
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
|
||||
two = ComparableSpecs::WithOnlyCompareDefined.new(2)
|
||||
c = ComparableSpecs::Weird.new(2)
|
||||
|
||||
c.clamp(one..).should equal(c)
|
||||
c.clamp(two..).should equal(c)
|
||||
end
|
||||
|
||||
it 'works with exclusive range' do
|
||||
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
|
||||
c = ComparableSpecs::Weird.new(2)
|
||||
|
||||
c.clamp(one...).should equal(c)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with beginless range' do
|
||||
it 'returns maximum value of the range parameters if greater than it' do
|
||||
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
|
||||
c = ComparableSpecs::Weird.new(2)
|
||||
|
||||
c.clamp(..one).should equal(one)
|
||||
end
|
||||
|
||||
it 'always returns self if less than maximum value of the range parameters' do
|
||||
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
|
||||
zero = ComparableSpecs::WithOnlyCompareDefined.new(0)
|
||||
c = ComparableSpecs::Weird.new(0)
|
||||
|
||||
c.clamp(..one).should equal(c)
|
||||
c.clamp(..zero).should equal(c)
|
||||
end
|
||||
|
||||
it 'raises an Argument error if the range parameter is exclusive' do
|
||||
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
|
||||
c = ComparableSpecs::Weird.new(0)
|
||||
|
||||
-> { c.clamp(...one) }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with beginless-and-endless range' do
|
||||
it 'always returns self' do
|
||||
c = ComparableSpecs::Weird.new(1)
|
||||
|
||||
c.clamp(nil..nil).should equal(c)
|
||||
end
|
||||
|
||||
it 'works with exclusive range' do
|
||||
c = ComparableSpecs::Weird.new(2)
|
||||
|
||||
c.clamp(nil...nil).should equal(c)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -103,7 +103,7 @@ describe :enumerable_inject, shared: true do
|
||||
|
||||
it "without inject arguments(legacy rubycon)" do
|
||||
# no inject argument
|
||||
EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| 999 } .should == 2
|
||||
EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| 999 }.should == 2
|
||||
EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| acc }.should == 2
|
||||
EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| x }.should == 2
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ describe "Hash#compact" do
|
||||
hash.compact.default_proc.should == pr
|
||||
end
|
||||
|
||||
it "retains compare_by_identity_flag" do
|
||||
it "retains compare_by_identity flag" do
|
||||
hash = {}.compare_by_identity
|
||||
hash.compact.compare_by_identity?.should == true
|
||||
hash[:a] = 1
|
||||
|
||||
@ -103,14 +103,14 @@ describe "Hash.[]" do
|
||||
HashSpecs::MyInitializerHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyInitializerHash)
|
||||
end
|
||||
|
||||
it "removes the default value" do
|
||||
it "does not retain the default value" do
|
||||
hash = Hash.new(1)
|
||||
Hash[hash].default.should be_nil
|
||||
hash[:a] = 1
|
||||
Hash[hash].default.should be_nil
|
||||
end
|
||||
|
||||
it "removes the default_proc" do
|
||||
it "does not retain the default_proc" do
|
||||
hash = Hash.new { |h, k| h[k] = [] }
|
||||
Hash[hash].default_proc.should be_nil
|
||||
hash[:a] = 1
|
||||
@ -118,10 +118,11 @@ describe "Hash.[]" do
|
||||
end
|
||||
|
||||
ruby_version_is '3.3' do
|
||||
it "does not retain compare_by_identity_flag" do
|
||||
hash = {}.compare_by_identity
|
||||
it "does not retain compare_by_identity flag" do
|
||||
hash = { a: 1 }.compare_by_identity
|
||||
Hash[hash].compare_by_identity?.should == false
|
||||
hash[:a] = 1
|
||||
|
||||
hash = {}.compare_by_identity
|
||||
Hash[hash].compare_by_identity?.should == false
|
||||
end
|
||||
end
|
||||
|
||||
@ -19,14 +19,24 @@ describe "Hash#except" do
|
||||
@hash.except(:a, :chunky_bacon).should == { b: 2, c: 3 }
|
||||
end
|
||||
|
||||
it "always returns a Hash without a default" do
|
||||
klass = Class.new(Hash)
|
||||
h = klass.new(:default)
|
||||
h[:bar] = 12
|
||||
h[:foo] = 42
|
||||
r = h.except(:foo)
|
||||
r.should == {bar: 12}
|
||||
r.class.should == Hash
|
||||
r.default.should == nil
|
||||
it "does not retain the default value" do
|
||||
h = Hash.new(1)
|
||||
h.except(:a).default.should be_nil
|
||||
h[:a] = 1
|
||||
h.except(:a).default.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h.except(:a).default_proc.should be_nil
|
||||
h[:a] = 1
|
||||
h.except(:a).default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "retains compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.except(:a)
|
||||
h2.compare_by_identity?.should == true
|
||||
end
|
||||
end
|
||||
|
||||
@ -24,4 +24,25 @@ describe "Hash#invert" do
|
||||
HashSpecs::MyHash[1 => 2, 3 => 4].invert.class.should == Hash
|
||||
HashSpecs::MyHash[].invert.class.should == Hash
|
||||
end
|
||||
|
||||
it "does not retain the default value" do
|
||||
h = Hash.new(1)
|
||||
h.invert.default.should be_nil
|
||||
h[:a] = 1
|
||||
h.invert.default.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h.invert.default_proc.should be_nil
|
||||
h[:a] = 1
|
||||
h.invert.default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.invert
|
||||
h2.compare_by_identity?.should == false
|
||||
end
|
||||
end
|
||||
|
||||
@ -93,6 +93,29 @@ describe "Hash#merge" do
|
||||
merged.should eql(hash)
|
||||
merged.should_not equal(hash)
|
||||
end
|
||||
|
||||
it "retains the default value" do
|
||||
h = Hash.new(1)
|
||||
h.merge(b: 1, d: 2).default.should == 1
|
||||
end
|
||||
|
||||
it "retains the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h.merge(b: 1, d: 2).default_proc.should == pr
|
||||
end
|
||||
|
||||
it "retains compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.merge(b: 1, d: 2)
|
||||
h2.compare_by_identity?.should == true
|
||||
end
|
||||
|
||||
it "ignores compare_by_identity flag of an argument" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = { b: 1, d: 2 }.merge(h)
|
||||
h2.compare_by_identity?.should == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "Hash#merge!" do
|
||||
|
||||
@ -44,6 +44,27 @@ describe "Hash#reject" do
|
||||
reject_pairs.should == reject_bang_pairs
|
||||
end
|
||||
|
||||
it "does not retain the default value" do
|
||||
h = Hash.new(1)
|
||||
h.reject { false }.default.should be_nil
|
||||
h[:a] = 1
|
||||
h.reject { false }.default.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h.reject { false }.default_proc.should be_nil
|
||||
h[:a] = 1
|
||||
h.reject { false }.default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "retains compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.reject { |k, _| k == :a }
|
||||
h2.compare_by_identity?.should == true
|
||||
end
|
||||
|
||||
it_behaves_like :hash_iteration_no_block, :reject
|
||||
it_behaves_like :enumeratorized_with_origin_size, :reject, { 1 => 2, 3 => 4, 5 => 6 }
|
||||
end
|
||||
|
||||
@ -23,39 +23,48 @@ describe "Hash#replace" do
|
||||
h.should == { 1 => 2 }
|
||||
end
|
||||
|
||||
it "transfers the compare_by_identity flag" do
|
||||
hash_a = { a: 1 }
|
||||
hash_b = { b: 2 }
|
||||
hash_b.compare_by_identity
|
||||
hash_a.should_not.compare_by_identity?
|
||||
hash_a.replace(hash_b)
|
||||
hash_a.should.compare_by_identity?
|
||||
|
||||
hash_a = { a: 1 }
|
||||
hash_b = { b: 2 }
|
||||
hash_a.compare_by_identity
|
||||
hash_a.should.compare_by_identity?
|
||||
hash_a.replace(hash_b)
|
||||
hash_a.should_not.compare_by_identity?
|
||||
it "does not retain the default value" do
|
||||
hash = Hash.new(1)
|
||||
hash.replace(b: 2).default.should be_nil
|
||||
end
|
||||
|
||||
it "does not transfer default values" do
|
||||
hash_a = {}
|
||||
hash_b = Hash.new(5)
|
||||
hash_a.replace(hash_b)
|
||||
hash_a.default.should == 5
|
||||
it "transfers the default value of an argument" do
|
||||
hash = Hash.new(1)
|
||||
{ a: 1 }.replace(hash).default.should == 1
|
||||
end
|
||||
|
||||
hash_a = {}
|
||||
hash_b = Hash.new { |h, k| k * 2 }
|
||||
hash_a.replace(hash_b)
|
||||
hash_a.default(5).should == 10
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
hash = Hash.new(&pr)
|
||||
hash.replace(b: 2).default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "transfers the default_proc of an argument" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
hash = Hash.new(&pr)
|
||||
{ a: 1 }.replace(hash).default_proc.should == pr
|
||||
end
|
||||
|
||||
it "does not call the default_proc of an argument" do
|
||||
hash_a = Hash.new { |h, k| k * 5 }
|
||||
hash_b = Hash.new(-> { raise "Should not invoke lambda" })
|
||||
hash_a.replace(hash_b)
|
||||
hash_a.default.should == hash_b.default
|
||||
end
|
||||
|
||||
it "transfers compare_by_identity flag of an argument" do
|
||||
h = { a: 1, c: 3 }
|
||||
h2 = { b: 2, d: 4 }.compare_by_identity
|
||||
h.replace(h2)
|
||||
h.compare_by_identity?.should == true
|
||||
end
|
||||
|
||||
it "does not retain compare_by_identity flag" do
|
||||
h = { a: 1, c: 3 }.compare_by_identity
|
||||
h.replace(b: 2, d: 4)
|
||||
h.compare_by_identity?.should == false
|
||||
end
|
||||
|
||||
it "raises a FrozenError if called on a frozen instance that would not be modified" do
|
||||
-> do
|
||||
HashSpecs.frozen_hash.replace(HashSpecs.frozen_hash)
|
||||
|
||||
@ -40,6 +40,27 @@ describe :hash_select, shared: true do
|
||||
@empty.send(@method).should be_an_instance_of(Enumerator)
|
||||
end
|
||||
|
||||
it "does not retain the default value" do
|
||||
h = Hash.new(1)
|
||||
h.send(@method) { true }.default.should be_nil
|
||||
h[:a] = 1
|
||||
h.send(@method) { true }.default.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h.send(@method) { true }.default_proc.should be_nil
|
||||
h[:a] = 1
|
||||
h.send(@method) { true }.default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "retains compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.send(@method) { |k, _| k == :a }
|
||||
h2.compare_by_identity?.should == true
|
||||
end
|
||||
|
||||
it_should_behave_like :hash_iteration_no_block
|
||||
|
||||
before :each do
|
||||
|
||||
@ -50,4 +50,25 @@ describe "Hash#slice" do
|
||||
|
||||
ScratchPad.recorded.should == []
|
||||
end
|
||||
|
||||
it "does not retain the default value" do
|
||||
h = Hash.new(1)
|
||||
h.slice(:a).default.should be_nil
|
||||
h[:a] = 1
|
||||
h.slice(:a).default.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h.slice(:a).default_proc.should be_nil
|
||||
h[:a] = 1
|
||||
h.slice(:a).default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "retains compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.slice(:a)
|
||||
h2.compare_by_identity?.should == true
|
||||
end
|
||||
end
|
||||
|
||||
@ -19,17 +19,22 @@ describe "Hash#to_h" do
|
||||
@h[:foo].should == :bar
|
||||
end
|
||||
|
||||
it "copies the default" do
|
||||
it "retains the default" do
|
||||
@h.default = 42
|
||||
@h.to_h.default.should == 42
|
||||
@h[:hello].should == 42
|
||||
end
|
||||
|
||||
it "copies the default_proc" do
|
||||
it "retains the default_proc" do
|
||||
@h.default_proc = prc = Proc.new{ |h, k| h[k] = 2 * k }
|
||||
@h.to_h.default_proc.should == prc
|
||||
@h[42].should == 84
|
||||
end
|
||||
|
||||
it "retains compare_by_identity flag" do
|
||||
@h.compare_by_identity
|
||||
@h.to_h.compare_by_identity?.should == true
|
||||
end
|
||||
end
|
||||
|
||||
context "with block" do
|
||||
@ -78,5 +83,24 @@ describe "Hash#to_h" do
|
||||
{ a: 1 }.to_h { |k| x }
|
||||
end.should raise_error(TypeError, /wrong element type MockObject/)
|
||||
end
|
||||
|
||||
it "does not retain the default value" do
|
||||
h = Hash.new(1)
|
||||
h2 = h.to_h { |k, v| [k.to_s, v*v]}
|
||||
h2.default.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h2 = h.to_h { |k, v| [k.to_s, v*v]}
|
||||
h2.default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.to_h { |k, v| [k.to_s, v*v]}
|
||||
h2.compare_by_identity?.should == false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -54,6 +54,27 @@ describe "Hash#transform_keys" do
|
||||
it "allows a combination of hash and block argument" do
|
||||
@hash.transform_keys({ a: :A }, &:to_s).should == { A: 1, 'b' => 2, 'c' => 3 }
|
||||
end
|
||||
|
||||
it "does not retain the default value" do
|
||||
h = Hash.new(1)
|
||||
h.transform_keys(&:succ).default.should be_nil
|
||||
h[:a] = 1
|
||||
h.transform_keys(&:succ).default.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h.transform_values(&:succ).default_proc.should be_nil
|
||||
h[:a] = 1
|
||||
h.transform_values(&:succ).default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.transform_keys(&:succ)
|
||||
h2.compare_by_identity?.should == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "Hash#transform_keys!" do
|
||||
|
||||
@ -39,6 +39,27 @@ describe "Hash#transform_values" do
|
||||
r[:foo].should == 84
|
||||
r.class.should == Hash
|
||||
end
|
||||
|
||||
it "does not retain the default value" do
|
||||
h = Hash.new(1)
|
||||
h.transform_values(&:succ).default.should be_nil
|
||||
h[:a] = 1
|
||||
h.transform_values(&:succ).default.should be_nil
|
||||
end
|
||||
|
||||
it "does not retain the default_proc" do
|
||||
pr = proc { |h, k| h[k] = [] }
|
||||
h = Hash.new(&pr)
|
||||
h.transform_values(&:succ).default_proc.should be_nil
|
||||
h[:a] = 1
|
||||
h.transform_values(&:succ).default_proc.should be_nil
|
||||
end
|
||||
|
||||
it "retains compare_by_identity flag" do
|
||||
h = { a: 9, c: 4 }.compare_by_identity
|
||||
h2 = h.transform_values(&:succ)
|
||||
h2.compare_by_identity?.should == true
|
||||
end
|
||||
end
|
||||
|
||||
describe "Hash#transform_values!" do
|
||||
|
||||
29
spec/ruby/core/io/buffer/empty_spec.rb
Normal file
29
spec/ruby/core/io/buffer/empty_spec.rb
Normal file
@ -0,0 +1,29 @@
|
||||
require_relative '../../../spec_helper'
|
||||
require_relative 'shared/null_and_empty'
|
||||
|
||||
describe "IO::Buffer#empty?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
it_behaves_like :io_buffer_null_and_empty, :empty?
|
||||
|
||||
it "is true for a 0-length String-backed buffer created with .for" do
|
||||
@buffer = IO::Buffer.for("")
|
||||
@buffer.empty?.should be_true
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is true for a 0-length String-backed buffer created with .string" do
|
||||
IO::Buffer.string(0) do |buffer|
|
||||
buffer.empty?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "is true for a 0-length slice of a buffer with size > 0" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice(3, 0).empty?.should be_true
|
||||
end
|
||||
end
|
||||
108
spec/ruby/core/io/buffer/external_spec.rb
Normal file
108
spec/ruby/core/io/buffer/external_spec.rb
Normal file
@ -0,0 +1,108 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#external?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "with a buffer created with .new" do
|
||||
it "is false for an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.external?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.external?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "is true for a regular mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.external?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is false for a private mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY | IO::Buffer::PRIVATE)
|
||||
@buffer.external?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
it "is true for a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.external?.should be_true
|
||||
end
|
||||
|
||||
it "is true for a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.external?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "is true" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.external?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Always false for slices
|
||||
context "with a slice of a buffer" do
|
||||
context "created with .new" do
|
||||
it "is false when slicing an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice.external?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.slice.external?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .map" do
|
||||
it "is false" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.slice.external?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .for" do
|
||||
it "is false when slicing a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.slice.external?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.slice.external?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.slice.external?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
104
spec/ruby/core/io/buffer/free_spec.rb
Normal file
104
spec/ruby/core/io/buffer/free_spec.rb
Normal file
@ -0,0 +1,104 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#free" do
|
||||
context "with a buffer created with .new" do
|
||||
it "frees internal memory and nullifies the buffer" do
|
||||
buffer = IO::Buffer.new(4)
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
|
||||
it "frees mapped memory and nullifies the buffer" do
|
||||
buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "frees mapped memory and nullifies the buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
context "without a block" do
|
||||
it "disassociates the buffer from the string and nullifies the buffer" do
|
||||
string = +"test"
|
||||
buffer = IO::Buffer.for(string)
|
||||
# Read-only buffer, can't modify the string.
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "with a block" do
|
||||
it "disassociates the buffer from the string and nullifies the buffer" do
|
||||
string = +"test"
|
||||
IO::Buffer.for(string) do |buffer|
|
||||
buffer.set_string("meat")
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
string.should == "meat"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "disassociates the buffer from the string and nullifies the buffer" do
|
||||
string =
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.set_string("meat")
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
string.should == "meat"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "can be called repeatedly without an error" do
|
||||
buffer = IO::Buffer.new(4)
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
|
||||
it "is disallowed while locked, raising IO::Buffer::LockedError" do
|
||||
buffer = IO::Buffer.new(4)
|
||||
buffer.locked do
|
||||
-> { buffer.free }.should raise_error(IO::Buffer::LockedError, "Buffer is locked!")
|
||||
end
|
||||
buffer.free
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
|
||||
context "with a slice of a buffer" do
|
||||
it "nullifies the slice, not touching the buffer" do
|
||||
buffer = IO::Buffer.new(4)
|
||||
slice = buffer.slice(0, 2)
|
||||
|
||||
slice.free
|
||||
slice.null?.should be_true
|
||||
buffer.null?.should be_false
|
||||
|
||||
buffer.free
|
||||
end
|
||||
|
||||
it "nullifies buffer, invalidating the slice" do
|
||||
buffer = IO::Buffer.new(4)
|
||||
slice = buffer.slice(0, 2)
|
||||
|
||||
buffer.free
|
||||
slice.null?.should be_false
|
||||
slice.valid?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
103
spec/ruby/core/io/buffer/initialize_spec.rb
Normal file
103
spec/ruby/core/io/buffer/initialize_spec.rb
Normal file
@ -0,0 +1,103 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#initialize" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
it "creates a new zero-filled buffer with default size" do
|
||||
@buffer = IO::Buffer.new
|
||||
@buffer.size.should == IO::Buffer::DEFAULT_SIZE
|
||||
@buffer.each(:U8).should.all? { |_offset, value| value.eql?(0) }
|
||||
end
|
||||
|
||||
it "creates a buffer with default state" do
|
||||
@buffer = IO::Buffer.new
|
||||
@buffer.should_not.shared?
|
||||
@buffer.should_not.readonly?
|
||||
|
||||
@buffer.should_not.empty?
|
||||
@buffer.should_not.null?
|
||||
|
||||
# This is run-time state, set by #locked.
|
||||
@buffer.should_not.locked?
|
||||
end
|
||||
|
||||
context "with size argument" do
|
||||
it "creates a new internal buffer if size is less than IO::Buffer::PAGE_SIZE" do
|
||||
size = IO::Buffer::PAGE_SIZE - 1
|
||||
@buffer = IO::Buffer.new(size)
|
||||
@buffer.size.should == size
|
||||
@buffer.should.internal?
|
||||
@buffer.should_not.mapped?
|
||||
@buffer.should_not.empty?
|
||||
end
|
||||
|
||||
it "creates a new mapped buffer if size is greater than or equal to IO::Buffer::PAGE_SIZE" do
|
||||
size = IO::Buffer::PAGE_SIZE
|
||||
@buffer = IO::Buffer.new(size)
|
||||
@buffer.size.should == size
|
||||
@buffer.should_not.internal?
|
||||
@buffer.should.mapped?
|
||||
@buffer.should_not.empty?
|
||||
end
|
||||
|
||||
it "creates a null buffer if size is 0" do
|
||||
@buffer = IO::Buffer.new(0)
|
||||
@buffer.size.should.zero?
|
||||
@buffer.should_not.internal?
|
||||
@buffer.should_not.mapped?
|
||||
@buffer.should.null?
|
||||
@buffer.should.empty?
|
||||
end
|
||||
|
||||
it "raises TypeError if size is not an Integer" do
|
||||
-> { IO::Buffer.new(nil) }.should raise_error(TypeError, "not an Integer")
|
||||
-> { IO::Buffer.new(10.0) }.should raise_error(TypeError, "not an Integer")
|
||||
end
|
||||
|
||||
it "raises ArgumentError if size is negative" do
|
||||
-> { IO::Buffer.new(-1) }.should raise_error(ArgumentError, "Size can't be negative!")
|
||||
end
|
||||
end
|
||||
|
||||
context "with size and flags arguments" do
|
||||
it "forces mapped buffer with IO::Buffer::MAPPED flag" do
|
||||
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE - 1, IO::Buffer::MAPPED)
|
||||
@buffer.should.mapped?
|
||||
@buffer.should_not.internal?
|
||||
@buffer.should_not.empty?
|
||||
end
|
||||
|
||||
it "forces internal buffer with IO::Buffer::INTERNAL flag" do
|
||||
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::INTERNAL)
|
||||
@buffer.should.internal?
|
||||
@buffer.should_not.mapped?
|
||||
@buffer.should_not.empty?
|
||||
end
|
||||
|
||||
it "raises IO::Buffer::AllocationError if neither IO::Buffer::MAPPED nor IO::Buffer::INTERNAL is given" do
|
||||
-> { IO::Buffer.new(10, IO::Buffer::READONLY) }.should raise_error(IO::Buffer::AllocationError, "Could not allocate buffer!")
|
||||
-> { IO::Buffer.new(10, 0) }.should raise_error(IO::Buffer::AllocationError, "Could not allocate buffer!")
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "raises ArgumentError if flags is negative" do
|
||||
-> { IO::Buffer.new(10, -1) }.should raise_error(ArgumentError, "Flags can't be negative!")
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is ""..."3.3" do
|
||||
it "raises IO::Buffer::AllocationError with non-Integer flags" do
|
||||
-> { IO::Buffer.new(10, 0.0) }.should raise_error(IO::Buffer::AllocationError, "Could not allocate buffer!")
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "raises TypeError with non-Integer flags" do
|
||||
-> { IO::Buffer.new(10, 0.0) }.should raise_error(TypeError, "not an Integer")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
108
spec/ruby/core/io/buffer/internal_spec.rb
Normal file
108
spec/ruby/core/io/buffer/internal_spec.rb
Normal file
@ -0,0 +1,108 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#internal?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "with a buffer created with .new" do
|
||||
it "is true for an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.internal?.should be_true
|
||||
end
|
||||
|
||||
it "is false for a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.internal?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "is false for a regular mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.internal?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is false for a private mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY | IO::Buffer::PRIVATE)
|
||||
@buffer.internal?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
it "is false for a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.internal?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.internal?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.internal?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Always false for slices
|
||||
context "with a slice of a buffer" do
|
||||
context "created with .new" do
|
||||
it "is false when slicing an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice.internal?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.slice.internal?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .map" do
|
||||
it "is false" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.slice.internal?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .for" do
|
||||
it "is false when slicing a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.slice.internal?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.slice.internal?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.slice.internal?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
75
spec/ruby/core/io/buffer/locked_spec.rb
Normal file
75
spec/ruby/core/io/buffer/locked_spec.rb
Normal file
@ -0,0 +1,75 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#locked" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "when buffer is locked" do
|
||||
it "allows reading and writing operations on the buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.set_string("test")
|
||||
@buffer.locked do
|
||||
@buffer.get_string.should == "test"
|
||||
@buffer.set_string("meat")
|
||||
end
|
||||
@buffer.get_string.should == "meat"
|
||||
end
|
||||
|
||||
it "disallows operations changing buffer itself, raising IO::Buffer::LockedError" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.locked do
|
||||
# Just an example, each method is responsible for checking the lock state.
|
||||
-> { @buffer.resize(8) }.should raise_error(IO::Buffer::LockedError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "disallows reentrant locking, raising IO::Buffer::LockedError" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.locked do
|
||||
-> { @buffer.locked {} }.should raise_error(IO::Buffer::LockedError, "Buffer already locked!")
|
||||
end
|
||||
end
|
||||
|
||||
it "does not propagate to buffer's slices" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
slice = @buffer.slice(0, 2)
|
||||
@buffer.locked do
|
||||
@buffer.locked?.should be_true
|
||||
slice.locked?.should be_false
|
||||
slice.locked { slice.locked?.should be_true }
|
||||
end
|
||||
end
|
||||
|
||||
it "does not propagate backwards from buffer's slices" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
slice = @buffer.slice(0, 2)
|
||||
slice.locked do
|
||||
slice.locked?.should be_true
|
||||
@buffer.locked?.should be_false
|
||||
@buffer.locked { @buffer.locked?.should be_true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "IO::Buffer#locked?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
it "is false by default" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.locked?.should be_false
|
||||
end
|
||||
|
||||
it "is true only inside of #locked block" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.locked do
|
||||
@buffer.locked?.should be_true
|
||||
end
|
||||
@buffer.locked?.should be_false
|
||||
end
|
||||
end
|
||||
108
spec/ruby/core/io/buffer/mapped_spec.rb
Normal file
108
spec/ruby/core/io/buffer/mapped_spec.rb
Normal file
@ -0,0 +1,108 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#mapped?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "with a buffer created with .new" do
|
||||
it "is false for an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.mapped?.should be_false
|
||||
end
|
||||
|
||||
it "is true for a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.mapped?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "is true for a regular mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.mapped?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is true for a private mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY | IO::Buffer::PRIVATE)
|
||||
@buffer.mapped?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
it "is false for a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.mapped?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.mapped?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.mapped?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Always false for slices
|
||||
context "with a slice of a buffer" do
|
||||
context "created with .new" do
|
||||
it "is false when slicing an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice.mapped?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.slice.mapped?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .map" do
|
||||
it "is false" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.slice.mapped?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .for" do
|
||||
it "is false when slicing a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.slice.mapped?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.slice.mapped?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.slice.mapped?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
29
spec/ruby/core/io/buffer/null_spec.rb
Normal file
29
spec/ruby/core/io/buffer/null_spec.rb
Normal file
@ -0,0 +1,29 @@
|
||||
require_relative '../../../spec_helper'
|
||||
require_relative 'shared/null_and_empty'
|
||||
|
||||
describe "IO::Buffer#null?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
it_behaves_like :io_buffer_null_and_empty, :null?
|
||||
|
||||
it "is false for a 0-length String-backed buffer created with .for" do
|
||||
@buffer = IO::Buffer.for("")
|
||||
@buffer.null?.should be_false
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is false for a 0-length String-backed buffer created with .string" do
|
||||
IO::Buffer.string(0) do |buffer|
|
||||
buffer.null?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "is false for a 0-length slice of a buffer with size > 0" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice(3, 0).null?.should be_false
|
||||
end
|
||||
end
|
||||
111
spec/ruby/core/io/buffer/private_spec.rb
Normal file
111
spec/ruby/core/io/buffer/private_spec.rb
Normal file
@ -0,0 +1,111 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
describe "IO::Buffer#private?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "with a buffer created with .new" do
|
||||
it "is false for an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::INTERNAL)
|
||||
@buffer.private?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.private?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "is false for a regular mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.private?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "is true for a private mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY | IO::Buffer::PRIVATE)
|
||||
@buffer.private?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
it "is false for a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.private?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.private?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.private?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Always false for slices
|
||||
context "with a slice of a buffer" do
|
||||
context "created with .new" do
|
||||
it "is false when slicing an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice.private?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.slice.private?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .map" do
|
||||
it "is false when slicing a regular file-backed buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.slice.private?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "is false when slicing a private file-backed buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY | IO::Buffer::PRIVATE)
|
||||
@buffer.slice.private?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .for" do
|
||||
it "is false when slicing a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.slice.private?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.slice.private?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.slice.private?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
143
spec/ruby/core/io/buffer/readonly_spec.rb
Normal file
143
spec/ruby/core/io/buffer/readonly_spec.rb
Normal file
@ -0,0 +1,143 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#readonly?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "with a buffer created with .new" do
|
||||
it "is false for an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::INTERNAL)
|
||||
@buffer.readonly?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "is false for a writable mapping" do
|
||||
File.open(__FILE__, "r+") do |file|
|
||||
@buffer = IO::Buffer.map(file)
|
||||
@buffer.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "is true for a readonly mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.readonly?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is false for a private mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::PRIVATE)
|
||||
@buffer.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
it "is true for a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for(+"test")
|
||||
@buffer.readonly?.should be_true
|
||||
end
|
||||
|
||||
it "is false for a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "is true for a buffer created with a block from a frozen string" do
|
||||
IO::Buffer.for(-"test") do |buffer|
|
||||
buffer.readonly?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This seems to be the only flag propagated from the source buffer to the slice.
|
||||
context "with a slice of a buffer" do
|
||||
context "created with .new" do
|
||||
it "is false when slicing an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice.readonly?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.slice.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .map" do
|
||||
it "is false when slicing a read-write file-backed buffer" do
|
||||
File.open(__FILE__, "r+") do |file|
|
||||
@buffer = IO::Buffer.map(file)
|
||||
@buffer.slice.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "is true when slicing a readonly file-backed buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.slice.readonly?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is false when slicing a private file-backed buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::PRIVATE)
|
||||
@buffer.slice.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .for" do
|
||||
it "is true when slicing a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for(+"test")
|
||||
@buffer.slice.readonly?.should be_true
|
||||
end
|
||||
|
||||
it "is false when slicing a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.slice.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "is true when slicing a buffer created with a block from a frozen string" do
|
||||
IO::Buffer.for(-"test") do |buffer|
|
||||
buffer.slice.readonly?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.slice.readonly?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
155
spec/ruby/core/io/buffer/resize_spec.rb
Normal file
155
spec/ruby/core/io/buffer/resize_spec.rb
Normal file
@ -0,0 +1,155 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#resize" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "with a buffer created with .new" do
|
||||
it "resizes internal buffer, preserving type" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.resize(IO::Buffer::PAGE_SIZE)
|
||||
@buffer.size.should == IO::Buffer::PAGE_SIZE
|
||||
@buffer.internal?.should be_true
|
||||
@buffer.mapped?.should be_false
|
||||
end
|
||||
|
||||
platform_is :linux do
|
||||
it "resizes mapped buffer, preserving type" do
|
||||
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::MAPPED)
|
||||
@buffer.resize(4)
|
||||
@buffer.size.should == 4
|
||||
@buffer.internal?.should be_false
|
||||
@buffer.mapped?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
platform_is_not :linux do
|
||||
it "resizes mapped buffer, changing type to internal" do
|
||||
@buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::MAPPED)
|
||||
@buffer.resize(4)
|
||||
@buffer.size.should == 4
|
||||
@buffer.internal?.should be_true
|
||||
@buffer.mapped?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "disallows resizing shared buffer, raising IO::Buffer::AccessError" do
|
||||
File.open(__FILE__, "r+") do |file|
|
||||
@buffer = IO::Buffer.map(file)
|
||||
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "resizes private buffer, discarding excess contents" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::PRIVATE)
|
||||
@buffer.resize(10)
|
||||
@buffer.size.should == 10
|
||||
@buffer.get_string.should == "require_re"
|
||||
@buffer.resize(12)
|
||||
@buffer.size.should == 12
|
||||
@buffer.get_string.should == "require_re\0\0"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
context "without a block" do
|
||||
it "disallows resizing, raising IO::Buffer::AccessError" do
|
||||
@buffer = IO::Buffer.for(+"test")
|
||||
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a block" do
|
||||
it "disallows resizing, raising IO::Buffer::AccessError" do
|
||||
IO::Buffer.for(+'test') do |buffer|
|
||||
-> { buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "disallows resizing, raising IO::Buffer::AccessError" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
-> { buffer.resize(10) }.should raise_error(IO::Buffer::AccessError, "Cannot resize external buffer!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a null buffer" do
|
||||
it "allows resizing a 0-sized buffer, creating a regular buffer according to new size" do
|
||||
@buffer = IO::Buffer.new(0)
|
||||
@buffer.resize(IO::Buffer::PAGE_SIZE)
|
||||
@buffer.size.should == IO::Buffer::PAGE_SIZE
|
||||
@buffer.internal?.should be_false
|
||||
@buffer.mapped?.should be_true
|
||||
end
|
||||
|
||||
it "allows resizing after a free, creating a regular buffer according to new size" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.free
|
||||
@buffer.resize(10)
|
||||
@buffer.size.should == 10
|
||||
@buffer.internal?.should be_true
|
||||
@buffer.mapped?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "allows resizing to 0, freeing memory" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.resize(0)
|
||||
@buffer.null?.should be_true
|
||||
end
|
||||
|
||||
it "can be called repeatedly" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.resize(10)
|
||||
@buffer.resize(27)
|
||||
@buffer.resize(1)
|
||||
@buffer.size.should == 1
|
||||
end
|
||||
|
||||
it "always clears extra memory" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.set_string("test")
|
||||
# This should not cause a re-allocation, just a technical resizing,
|
||||
# even with very aggressive memory allocation.
|
||||
@buffer.resize(2)
|
||||
@buffer.resize(4)
|
||||
@buffer.get_string.should == "te\0\0"
|
||||
end
|
||||
|
||||
it "is disallowed while locked, raising IO::Buffer::LockedError" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.locked do
|
||||
-> { @buffer.resize(10) }.should raise_error(IO::Buffer::LockedError, "Cannot resize locked buffer!")
|
||||
end
|
||||
end
|
||||
|
||||
it "raises ArgumentError if size is negative" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
-> { @buffer.resize(-1) }.should raise_error(ArgumentError, "Size can't be negative!")
|
||||
end
|
||||
|
||||
it "raises TypeError if size is not an Integer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
-> { @buffer.resize(nil) }.should raise_error(TypeError, "not an Integer")
|
||||
-> { @buffer.resize(10.0) }.should raise_error(TypeError, "not an Integer")
|
||||
end
|
||||
|
||||
context "with a slice of a buffer" do
|
||||
# Current behavior of slice resizing seems unintended (it's undocumented, too).
|
||||
# It either creates a completely new buffer, or breaks the slice on size 0.
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
||||
end
|
||||
59
spec/ruby/core/io/buffer/shared/null_and_empty.rb
Normal file
59
spec/ruby/core/io/buffer/shared/null_and_empty.rb
Normal file
@ -0,0 +1,59 @@
|
||||
describe :io_buffer_null_and_empty, shared: true do
|
||||
it "is false for a buffer with size > 0" do
|
||||
@buffer = IO::Buffer.new(1)
|
||||
@buffer.send(@method).should be_false
|
||||
end
|
||||
|
||||
it "is false for a slice with length > 0" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice(1, 2).send(@method).should be_false
|
||||
end
|
||||
|
||||
it "is false for a file-mapped buffer" do
|
||||
File.open(__FILE__, "rb") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.send(@method).should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "is false for a non-empty String-backed buffer created with .for" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.send(@method).should be_false
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is false for a non-empty String-backed buffer created with .string" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.send(@method).should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "is true for a 0-sized buffer" do
|
||||
@buffer = IO::Buffer.new(0)
|
||||
@buffer.send(@method).should be_true
|
||||
end
|
||||
|
||||
it "is true for a slice of a 0-sized buffer" do
|
||||
@buffer = IO::Buffer.new(0)
|
||||
@buffer.slice(0, 0).send(@method).should be_true
|
||||
end
|
||||
|
||||
it "is true for a freed buffer" do
|
||||
@buffer = IO::Buffer.new(1)
|
||||
@buffer.free
|
||||
@buffer.send(@method).should be_true
|
||||
end
|
||||
|
||||
it "is true for a buffer resized to 0" do
|
||||
@buffer = IO::Buffer.new(1)
|
||||
@buffer.resize(0)
|
||||
@buffer.send(@method).should be_true
|
||||
end
|
||||
|
||||
it "is true for a buffer whose memory was transferred" do
|
||||
buffer = IO::Buffer.new(1)
|
||||
@buffer = buffer.transfer
|
||||
buffer.send(@method).should be_true
|
||||
end
|
||||
end
|
||||
117
spec/ruby/core/io/buffer/shared_spec.rb
Normal file
117
spec/ruby/core/io/buffer/shared_spec.rb
Normal file
@ -0,0 +1,117 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#shared?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "with a buffer created with .new" do
|
||||
it "is false for an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::INTERNAL)
|
||||
@buffer.shared?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.shared?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "is true for a regular mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.shared?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is false for a private mapping" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY | IO::Buffer::PRIVATE)
|
||||
@buffer.shared?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
it "is false for a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.shared?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.shared?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.shared?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Always false for slices
|
||||
context "with a slice of a buffer" do
|
||||
context "created with .new" do
|
||||
it "is false when slicing an internal buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.slice.shared?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a mapped buffer" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
@buffer.slice.shared?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .map" do
|
||||
it "is false when slicing a regular file-backed buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.slice.shared?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "is false when slicing a private file-backed buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY | IO::Buffer::PRIVATE)
|
||||
@buffer.slice.shared?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "created with .for" do
|
||||
it "is false when slicing a buffer created without a block" do
|
||||
@buffer = IO::Buffer.for("test")
|
||||
@buffer.slice.shared?.should be_false
|
||||
end
|
||||
|
||||
it "is false when slicing a buffer created with a block" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
buffer.slice.shared?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "created with .string" do
|
||||
it "is false" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
buffer.slice.shared?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
118
spec/ruby/core/io/buffer/transfer_spec.rb
Normal file
118
spec/ruby/core/io/buffer/transfer_spec.rb
Normal file
@ -0,0 +1,118 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#transfer" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
context "with a buffer created with .new" do
|
||||
it "transfers internal memory to a new buffer, nullifying the original" do
|
||||
buffer = IO::Buffer.new(4)
|
||||
info = buffer.to_s
|
||||
@buffer = buffer.transfer
|
||||
@buffer.to_s.should == info
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
|
||||
it "transfers mapped memory to a new buffer, nullifying the original" do
|
||||
buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
info = buffer.to_s
|
||||
@buffer = buffer.transfer
|
||||
@buffer.to_s.should == info
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file-backed buffer created with .map" do
|
||||
it "transfers mapped memory to a new buffer, nullifying the original" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
info = buffer.to_s
|
||||
@buffer = buffer.transfer
|
||||
@buffer.to_s.should == info
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a String-backed buffer created with .for" do
|
||||
context "without a block" do
|
||||
it "transfers memory to a new buffer, nullifying the original" do
|
||||
buffer = IO::Buffer.for("test")
|
||||
info = buffer.to_s
|
||||
@buffer = buffer.transfer
|
||||
@buffer.to_s.should == info
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "with a block" do
|
||||
it "transfers memory to a new buffer, breaking the transaction by nullifying the original" do
|
||||
IO::Buffer.for(+"test") do |buffer|
|
||||
info = buffer.to_s
|
||||
@buffer = buffer.transfer
|
||||
@buffer.to_s.should == info
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
@buffer.null?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
context "with a String-backed buffer created with .string" do
|
||||
it "transfers memory to a new buffer, breaking the transaction by nullifying the original" do
|
||||
IO::Buffer.string(4) do |buffer|
|
||||
info = buffer.to_s
|
||||
@buffer = buffer.transfer
|
||||
@buffer.to_s.should == info
|
||||
buffer.null?.should be_true
|
||||
end
|
||||
@buffer.null?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "allows multiple transfers" do
|
||||
buffer_1 = IO::Buffer.new(4)
|
||||
buffer_2 = buffer_1.transfer
|
||||
@buffer = buffer_2.transfer
|
||||
buffer_1.null?.should be_true
|
||||
buffer_2.null?.should be_true
|
||||
@buffer.null?.should be_false
|
||||
end
|
||||
|
||||
it "is disallowed while locked, raising IO::Buffer::LockedError" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.locked do
|
||||
-> { @buffer.transfer }.should raise_error(IO::Buffer::LockedError, "Cannot transfer ownership of locked buffer!")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a slice of a buffer" do
|
||||
it "transfers source to a new slice, not touching the buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
slice = @buffer.slice(0, 2)
|
||||
@buffer.set_string("test")
|
||||
|
||||
new_slice = slice.transfer
|
||||
slice.null?.should be_true
|
||||
new_slice.null?.should be_false
|
||||
@buffer.null?.should be_false
|
||||
|
||||
new_slice.set_string("ea")
|
||||
@buffer.get_string.should == "east"
|
||||
end
|
||||
|
||||
it "nullifies buffer, invalidating the slice" do
|
||||
buffer = IO::Buffer.new(4)
|
||||
slice = buffer.slice(0, 2)
|
||||
@buffer = buffer.transfer
|
||||
|
||||
slice.null?.should be_false
|
||||
slice.valid?.should be_false
|
||||
-> { slice.get_string }.should raise_error(IO::Buffer::InvalidatedError, "Buffer has been invalidated!")
|
||||
end
|
||||
end
|
||||
end
|
||||
119
spec/ruby/core/io/buffer/valid_spec.rb
Normal file
119
spec/ruby/core/io/buffer/valid_spec.rb
Normal file
@ -0,0 +1,119 @@
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe "IO::Buffer#valid?" do
|
||||
after :each do
|
||||
@buffer&.free
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
# Non-slices are always valid
|
||||
context "with a non-slice buffer" do
|
||||
it "is true for a regular buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.valid?.should be_true
|
||||
end
|
||||
|
||||
it "is true for a 0-size buffer" do
|
||||
@buffer = IO::Buffer.new(0)
|
||||
@buffer.valid?.should be_true
|
||||
end
|
||||
|
||||
it "is true for a freed buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
@buffer.free
|
||||
@buffer.valid?.should be_true
|
||||
end
|
||||
|
||||
it "is true for a freed file-backed buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
@buffer.valid?.should be_true
|
||||
@buffer.free
|
||||
@buffer.valid?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
it "is true for a freed string-backed buffer" do
|
||||
@buffer = IO::Buffer.for("hello")
|
||||
@buffer.valid?.should be_true
|
||||
@buffer.free
|
||||
@buffer.valid?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
# "A buffer becomes invalid if it is a slice of another buffer (or string)
|
||||
# which has been freed or re-allocated at a different address."
|
||||
context "with a slice" do
|
||||
it "is true for a slice of a live buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
slice = @buffer.slice(0, 2)
|
||||
slice.valid?.should be_true
|
||||
end
|
||||
|
||||
context "when buffer is resized" do
|
||||
platform_is_not :windows do
|
||||
it "is true when slice is still inside the buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
slice = @buffer.slice(1, 2)
|
||||
@buffer.resize(3)
|
||||
slice.valid?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
it "is false when slice becomes outside the buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
slice = @buffer.slice(2, 2)
|
||||
@buffer.resize(3)
|
||||
slice.valid?.should be_false
|
||||
end
|
||||
|
||||
platform_is_not :linux do
|
||||
# This test does not cause a copy-resize on Linux.
|
||||
# `#resize` MAY cause the buffer to move, but there is no guarantee.
|
||||
it "is false when buffer is copied on resize" do
|
||||
@buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
|
||||
slice = @buffer.slice(0, 2)
|
||||
@buffer.resize(8)
|
||||
slice.valid?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "is false for a slice of a transferred buffer" do
|
||||
buffer = IO::Buffer.new(4)
|
||||
slice = buffer.slice(0, 2)
|
||||
@buffer = buffer.transfer
|
||||
slice.valid?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a slice of a freed buffer" do
|
||||
@buffer = IO::Buffer.new(4)
|
||||
slice = @buffer.slice(0, 2)
|
||||
@buffer.free
|
||||
slice.valid?.should be_false
|
||||
end
|
||||
|
||||
it "is false for a slice of a freed file-backed buffer" do
|
||||
File.open(__FILE__, "r") do |file|
|
||||
@buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
|
||||
slice = @buffer.slice(0, 2)
|
||||
slice.valid?.should be_true
|
||||
@buffer.free
|
||||
slice.valid?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "is true for a slice of a freed string-backed buffer while string is alive" do
|
||||
@buffer = IO::Buffer.for("alive")
|
||||
slice = @buffer.slice(0, 2)
|
||||
slice.valid?.should be_true
|
||||
@buffer.free
|
||||
slice.valid?.should be_true
|
||||
end
|
||||
|
||||
# There probably should be a test with a garbage-collected string,
|
||||
# but it's not clear how to force that.
|
||||
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
||||
end
|
||||
@ -231,6 +231,10 @@ describe :kernel_float, shared: true do
|
||||
@object.send(:Float, "0x0f").should == 15.0
|
||||
end
|
||||
|
||||
it "interprets negative hex value" do
|
||||
@object.send(:Float, "-0x10").should == -16.0
|
||||
end
|
||||
|
||||
it "accepts embedded _ if the number does not contain a-f" do
|
||||
@object.send(:Float, "0x1_0").should == 16.0
|
||||
end
|
||||
|
||||
@ -7,7 +7,9 @@ require_relative 'fixtures/classes'
|
||||
|
||||
autoload :KSAutoloadA, "autoload_a.rb"
|
||||
autoload :KSAutoloadB, fixture(__FILE__, "autoload_b.rb")
|
||||
autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.rb"
|
||||
define_autoload_KSAutoloadCallsRequire = -> {
|
||||
autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.rb"
|
||||
}
|
||||
|
||||
def check_autoload(const)
|
||||
autoload? const
|
||||
@ -43,6 +45,7 @@ describe "Kernel#autoload" do
|
||||
end
|
||||
|
||||
it "calls main.require(path) to load the file" do
|
||||
define_autoload_KSAutoloadCallsRequire.call
|
||||
main = TOPLEVEL_BINDING.eval("self")
|
||||
main.should_receive(:require).with("main_autoload_not_exist.rb")
|
||||
# The constant won't be defined since require is mocked to do nothing
|
||||
|
||||
132
spec/ruby/core/matchdata/bytebegin_spec.rb
Normal file
132
spec/ruby/core/matchdata/bytebegin_spec.rb
Normal file
@ -0,0 +1,132 @@
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
ruby_version_is "3.4" do
|
||||
describe "MatchData#bytebegin" do
|
||||
context "when passed an integer argument" do
|
||||
it "returns the byte-based offset of the start of the nth element" do
|
||||
match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
|
||||
match_data.bytebegin(0).should == 1
|
||||
match_data.bytebegin(2).should == 2
|
||||
end
|
||||
|
||||
it "returns nil when the nth match isn't found" do
|
||||
match_data = /something is( not)? (right)/.match("something is right")
|
||||
match_data.bytebegin(1).should be_nil
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi-byte strings" do
|
||||
match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
|
||||
match_data.bytebegin(0).should == 1
|
||||
match_data.bytebegin(2).should == 3
|
||||
end
|
||||
|
||||
not_supported_on :opal do
|
||||
it "returns the byte-based offset for multi-byte strings with unicode regexp" do
|
||||
match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
|
||||
match_data.bytebegin(0).should == 1
|
||||
match_data.bytebegin(2).should == 3
|
||||
end
|
||||
end
|
||||
|
||||
it "tries to convert the passed argument to an Integer using #to_int" do
|
||||
obj = mock('to_int')
|
||||
obj.should_receive(:to_int).and_return(2)
|
||||
|
||||
match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
|
||||
match_data.bytebegin(obj).should == 2
|
||||
end
|
||||
|
||||
it "raises IndexError if index is out of bounds" do
|
||||
match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
-> {
|
||||
match_data.bytebegin(-1)
|
||||
}.should raise_error(IndexError, "index -1 out of matches")
|
||||
|
||||
-> {
|
||||
match_data.bytebegin(3)
|
||||
}.should raise_error(IndexError, "index 3 out of matches")
|
||||
end
|
||||
end
|
||||
|
||||
context "when passed a String argument" do
|
||||
it "return the byte-based offset of the start of the named capture" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
|
||||
match_data.bytebegin("a").should == 1
|
||||
match_data.bytebegin("b").should == 3
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi byte strings" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.")
|
||||
match_data.bytebegin("a").should == 1
|
||||
match_data.bytebegin("b").should == 4
|
||||
end
|
||||
|
||||
not_supported_on :opal do
|
||||
it "returns the byte-based offset for multi byte strings with unicode regexp" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.")
|
||||
match_data.bytebegin("a").should == 1
|
||||
match_data.bytebegin("b").should == 4
|
||||
end
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for the farthest match when multiple named captures use the same name" do
|
||||
match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.")
|
||||
match_data.bytebegin("a").should == 3
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi-byte names" do
|
||||
match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
|
||||
match_data.bytebegin("æ").should == 1
|
||||
end
|
||||
|
||||
it "raises IndexError if there is no group with the provided name" do
|
||||
match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
-> {
|
||||
match_data.bytebegin("y")
|
||||
}.should raise_error(IndexError, "undefined group name reference: y")
|
||||
end
|
||||
end
|
||||
|
||||
context "when passed a Symbol argument" do
|
||||
it "return the byte-based offset of the start of the named capture" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
|
||||
match_data.bytebegin(:a).should == 1
|
||||
match_data.bytebegin(:b).should == 3
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi byte strings" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.")
|
||||
match_data.bytebegin(:a).should == 1
|
||||
match_data.bytebegin(:b).should == 4
|
||||
end
|
||||
|
||||
not_supported_on :opal do
|
||||
it "returns the byte-based offset for multi byte strings with unicode regexp" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.")
|
||||
match_data.bytebegin(:a).should == 1
|
||||
match_data.bytebegin(:b).should == 4
|
||||
end
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for the farthest match when multiple named captures use the same name" do
|
||||
match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.")
|
||||
match_data.bytebegin(:a).should == 3
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi-byte names" do
|
||||
match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
|
||||
match_data.bytebegin(:æ).should == 1
|
||||
end
|
||||
|
||||
it "raises IndexError if there is no group with the provided name" do
|
||||
match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
-> {
|
||||
match_data.bytebegin(:y)
|
||||
}.should raise_error(IndexError, "undefined group name reference: y")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
104
spec/ruby/core/matchdata/byteend_spec.rb
Normal file
104
spec/ruby/core/matchdata/byteend_spec.rb
Normal file
@ -0,0 +1,104 @@
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
ruby_version_is "3.4" do
|
||||
describe "MatchData#byteend" do
|
||||
context "when passed an integer argument" do
|
||||
it "returns the byte-based offset of the end of the nth element" do
|
||||
match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
|
||||
match_data.byteend(0).should == 7
|
||||
match_data.byteend(2).should == 3
|
||||
end
|
||||
|
||||
it "returns nil when the nth match isn't found" do
|
||||
match_data = /something is( not)? (right)/.match("something is right")
|
||||
match_data.byteend(1).should be_nil
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi-byte strings" do
|
||||
match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
|
||||
match_data.byteend(0).should == 8
|
||||
match_data.byteend(2).should == 4
|
||||
end
|
||||
|
||||
not_supported_on :opal do
|
||||
it "returns the byte-based offset for multi-byte strings with unicode regexp" do
|
||||
match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
|
||||
match_data.byteend(0).should == 8
|
||||
match_data.byteend(2).should == 4
|
||||
end
|
||||
end
|
||||
|
||||
it "tries to convert the passed argument to an Integer using #to_int" do
|
||||
obj = mock('to_int')
|
||||
obj.should_receive(:to_int).and_return(2)
|
||||
|
||||
match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
|
||||
match_data.byteend(obj).should == 3
|
||||
end
|
||||
end
|
||||
|
||||
context "when passed a String argument" do
|
||||
it "return the byte-based offset of the start of the named capture" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
|
||||
match_data.byteend("a").should == 2
|
||||
match_data.byteend("b").should == 6
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi byte strings" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.")
|
||||
match_data.byteend("a").should == 3
|
||||
match_data.byteend("b").should == 7
|
||||
end
|
||||
|
||||
not_supported_on :opal do
|
||||
it "returns the byte-based offset for multi byte strings with unicode regexp" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.")
|
||||
match_data.byteend("a").should == 3
|
||||
match_data.byteend("b").should == 7
|
||||
end
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for the farthest match when multiple named captures use the same name" do
|
||||
match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.")
|
||||
match_data.byteend("a").should == 6
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi-byte names" do
|
||||
match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
|
||||
match_data.byteend("æ").should == 2
|
||||
end
|
||||
end
|
||||
|
||||
context "when passed a Symbol argument" do
|
||||
it "return the byte-based offset of the start of the named capture" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
|
||||
match_data.byteend(:a).should == 2
|
||||
match_data.byteend(:b).should == 6
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi byte strings" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.")
|
||||
match_data.byteend(:a).should == 3
|
||||
match_data.byteend(:b).should == 7
|
||||
end
|
||||
|
||||
not_supported_on :opal do
|
||||
it "returns the byte-based offset for multi byte strings with unicode regexp" do
|
||||
match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.")
|
||||
match_data.byteend(:a).should == 3
|
||||
match_data.byteend(:b).should == 7
|
||||
end
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for the farthest match when multiple named captures use the same name" do
|
||||
match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.")
|
||||
match_data.byteend(:a).should == 6
|
||||
end
|
||||
|
||||
it "returns the byte-based offset for multi-byte names" do
|
||||
match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
|
||||
match_data.byteend(:æ).should == 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,30 +1,102 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
describe "MatchData#offset" do
|
||||
it "returns a two element array with the begin and end of the nth match" do
|
||||
match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
|
||||
match_data.offset(0).should == [1, 7]
|
||||
match_data.offset(4).should == [6, 7]
|
||||
it "returns beginning and ending character offset of whole matched substring for 0 element" do
|
||||
m = /(.)(.)(\d+)(\d)/.match("THX1138.")
|
||||
m.offset(0).should == [1, 7]
|
||||
end
|
||||
|
||||
it "returns [nil, nil] when the nth match isn't found" do
|
||||
match_data = /something is( not)? (right)/.match("something is right")
|
||||
match_data.offset(1).should == [nil, nil]
|
||||
it "returns beginning and ending character offset of n-th match, all the subsequent elements are capturing groups" do
|
||||
m = /(.)(.)(\d+)(\d)/.match("THX1138.")
|
||||
|
||||
m.offset(2).should == [2, 3]
|
||||
m.offset(3).should == [3, 6]
|
||||
m.offset(4).should == [6, 7]
|
||||
end
|
||||
|
||||
it "returns the offset for multi byte strings" do
|
||||
match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
|
||||
match_data.offset(0).should == [1, 7]
|
||||
match_data.offset(4).should == [6, 7]
|
||||
it "accepts String as a reference to a named capture" do
|
||||
m = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
m.offset("f").should == [0, 3]
|
||||
m.offset("b").should == [3, 6]
|
||||
end
|
||||
|
||||
it "accepts Symbol as a reference to a named capture" do
|
||||
m = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
m.offset(:f).should == [0, 3]
|
||||
m.offset(:b).should == [3, 6]
|
||||
end
|
||||
|
||||
it "returns [nil, nil] if a capturing group is optional and doesn't match" do
|
||||
m = /(?<x>q..)?/.match("foobarbaz")
|
||||
|
||||
m.offset("x").should == [nil, nil]
|
||||
m.offset(1).should == [nil, nil]
|
||||
end
|
||||
|
||||
it "returns correct beginning and ending character offset for multi-byte strings" do
|
||||
m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044")
|
||||
|
||||
m.offset(1).should == [1, 2]
|
||||
m.offset(3).should == [2, 3]
|
||||
end
|
||||
|
||||
not_supported_on :opal do
|
||||
it "returns the offset for multi byte strings with unicode regexp" do
|
||||
match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
|
||||
match_data.offset(0).should == [1, 7]
|
||||
match_data.offset(4).should == [6, 7]
|
||||
it "returns correct character offset for multi-byte strings with unicode regexp" do
|
||||
m = /\A\u3042(.)(.)?(.)\z/u.match("\u3042\u3043\u3044")
|
||||
|
||||
m.offset(1).should == [1, 2]
|
||||
m.offset(3).should == [2, 3]
|
||||
end
|
||||
end
|
||||
|
||||
it "returns [nil, nil] if a capturing group is optional and doesn't match for multi-byte string" do
|
||||
m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044")
|
||||
|
||||
m.offset(2).should == [nil, nil]
|
||||
end
|
||||
|
||||
it "converts argument into integer if is not String nor Symbol" do
|
||||
m = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
obj = Object.new
|
||||
def obj.to_int; 2; end
|
||||
|
||||
m.offset(1r).should == [0, 3]
|
||||
m.offset(1.1).should == [0, 3]
|
||||
m.offset(obj).should == [3, 6]
|
||||
end
|
||||
|
||||
it "raises IndexError if there is no group with the provided name" do
|
||||
m = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
-> {
|
||||
m.offset("y")
|
||||
}.should raise_error(IndexError, "undefined group name reference: y")
|
||||
|
||||
-> {
|
||||
m.offset(:y)
|
||||
}.should raise_error(IndexError, "undefined group name reference: y")
|
||||
end
|
||||
|
||||
it "raises IndexError if index is out of bounds" do
|
||||
m = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
-> {
|
||||
m.offset(-1)
|
||||
}.should raise_error(IndexError, "index -1 out of matches")
|
||||
|
||||
-> {
|
||||
m.offset(3)
|
||||
}.should raise_error(IndexError, "index 3 out of matches")
|
||||
end
|
||||
|
||||
it "raises TypeError if can't convert argument into Integer" do
|
||||
m = /(?<f>foo)(?<b>bar)/.match("foobar")
|
||||
|
||||
-> {
|
||||
m.offset([])
|
||||
}.should raise_error(TypeError, "no implicit conversion of Array into Integer")
|
||||
end
|
||||
end
|
||||
|
||||
@ -117,6 +117,7 @@ describe "Module#const_added" do
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:A, :B]
|
||||
ModuleSpecs::ConstAddedSpecs.send :remove_const, :NamedModule
|
||||
end
|
||||
|
||||
it "is called when a new class is defined under self" do
|
||||
@ -158,6 +159,7 @@ describe "Module#const_added" do
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:A, :B]
|
||||
ModuleSpecs::ConstAddedSpecs.send :remove_const, :NamedModuleB
|
||||
end
|
||||
|
||||
it "is called when an autoload is defined" do
|
||||
|
||||
@ -245,6 +245,14 @@ describe "Module#const_source_location" do
|
||||
@line = __LINE__ - 1
|
||||
end
|
||||
|
||||
before :each do
|
||||
@loaded_features = $".dup
|
||||
end
|
||||
|
||||
after :each do
|
||||
$".replace @loaded_features
|
||||
end
|
||||
|
||||
it 'returns the autoload location while not resolved' do
|
||||
ConstantSpecs.const_source_location('CSL_CONST1').should == [__FILE__, @line]
|
||||
end
|
||||
@ -265,6 +273,8 @@ describe "Module#const_source_location" do
|
||||
ConstantSpecs.const_source_location(:ConstSource).should == autoload_location
|
||||
ConstantSpecs::ConstSource::LOCATION.should == ConstantSpecs.const_source_location(:ConstSource)
|
||||
ConstantSpecs::BEFORE_DEFINE_LOCATION.should == autoload_location
|
||||
ConstantSpecs.send :remove_const, :ConstSource
|
||||
ConstantSpecs.send :remove_const, :BEFORE_DEFINE_LOCATION
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -190,6 +190,7 @@ describe "Module#name" do
|
||||
|
||||
ScratchPad.recorded.should.one?(/#<Module.+>::A$/)
|
||||
ScratchPad.recorded.should.one?(/#<Module.+>::A::B$/)
|
||||
ModuleSpecs::NameSpecs.send :remove_const, :NamedModule
|
||||
end
|
||||
|
||||
it "returns a frozen String" do
|
||||
|
||||
@ -86,6 +86,7 @@ ruby_version_is "3.3" do
|
||||
|
||||
ModuleSpecs::SetTemporaryNameSpec::M = m
|
||||
m::N.name.should == "ModuleSpecs::SetTemporaryNameSpec::M::N"
|
||||
ModuleSpecs::SetTemporaryNameSpec.send :remove_const, :M
|
||||
end
|
||||
|
||||
it "can update the name when assigned to a constant" do
|
||||
|
||||
@ -11,7 +11,7 @@ describe "Random.new" do
|
||||
it "returns Random instances initialized with different seeds" do
|
||||
first = Random.new
|
||||
second = Random.new
|
||||
(0..20).map { first.rand } .should_not == (0..20).map { second.rand }
|
||||
(0..20).map { first.rand }.should_not == (0..20).map { second.rand }
|
||||
end
|
||||
|
||||
it "accepts an Integer seed value as an argument" do
|
||||
|
||||
@ -39,7 +39,11 @@ describe "Regexps with encoding modifiers" do
|
||||
end
|
||||
|
||||
it "warns when using /n with a match string with non-ASCII characters and an encoding other than ASCII-8BIT" do
|
||||
-> { /./n.match("\303\251".dup.force_encoding('utf-8')) }.should complain(%r{historical binary regexp match /.../n against UTF-8 string})
|
||||
-> {
|
||||
eval <<~RUBY
|
||||
/./n.match("\303\251".dup.force_encoding('utf-8'))
|
||||
RUBY
|
||||
}.should complain(%r{historical binary regexp match /.../n against UTF-8 string})
|
||||
end
|
||||
|
||||
it 'uses US-ASCII as /n encoding if all chars are 7-bit' do
|
||||
|
||||
@ -136,10 +136,14 @@ describe "The rescue keyword" do
|
||||
|
||||
it 'captures successfully at the top-level' do
|
||||
ScratchPad.record []
|
||||
loaded_features = $".dup
|
||||
begin
|
||||
require_relative 'fixtures/rescue/top_level'
|
||||
|
||||
require_relative 'fixtures/rescue/top_level'
|
||||
|
||||
ScratchPad.recorded.should == ["message"]
|
||||
ScratchPad.recorded.should == ["message"]
|
||||
ensure
|
||||
$".replace loaded_features
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -3,10 +3,10 @@ require 'net/http'
|
||||
|
||||
describe "Net::HTTPServerException" do
|
||||
it "is a subclass of Net::ProtoServerError and is warned as deprecated" do
|
||||
-> { Net::HTTPServerException.should < Net::ProtoServerError }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
|
||||
-> { eval("Net::HTTPServerException").should < Net::ProtoServerError }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
|
||||
end
|
||||
|
||||
it "includes the Net::HTTPExceptions module and is warned as deprecated" do
|
||||
-> { Net::HTTPServerException.should < Net::HTTPExceptions }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
|
||||
-> { eval("Net::HTTPServerException").should < Net::HTTPExceptions }.should complain(/warning: constant Net::HTTPServerException is deprecated/)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
require_relative '../../spec_helper'
|
||||
require 'tempfile'
|
||||
|
||||
describe "Tempfile.callback" do
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
||||
176
spec/ruby/library/tempfile/create_spec.rb
Normal file
176
spec/ruby/library/tempfile/create_spec.rb
Normal file
@ -0,0 +1,176 @@
|
||||
require_relative '../../spec_helper'
|
||||
require 'tempfile'
|
||||
|
||||
describe "Tempfile.create" do
|
||||
after :each do
|
||||
if @tempfile
|
||||
@tempfile.close
|
||||
File.unlink(@tempfile.path) if File.file?(@tempfile.path)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns a new, open regular File instance placed in tmpdir" do
|
||||
@tempfile = Tempfile.create
|
||||
# Unlike Tempfile.open this returns a true File,
|
||||
# but `.should be_an_instance_of(File)` would be true either way.
|
||||
@tempfile.instance_of?(File).should be_true
|
||||
|
||||
@tempfile.should_not.closed?
|
||||
File.file?(@tempfile.path).should be_true
|
||||
|
||||
@tempfile.path.should.start_with?(Dir.tmpdir)
|
||||
@tempfile.path.should_not == "#{Dir.tmpdir}/"
|
||||
end
|
||||
|
||||
it "returns file in w+ mode" do
|
||||
@tempfile = Tempfile.create
|
||||
@tempfile << "Test!\nMore test!"
|
||||
@tempfile.rewind
|
||||
@tempfile.read.should == "Test!\nMore test!"
|
||||
|
||||
# Not "a+" mode, which would write at the end of the file.
|
||||
@tempfile.rewind
|
||||
@tempfile.print "Trust"
|
||||
@tempfile.rewind
|
||||
@tempfile.read.should == "Trust\nMore test!"
|
||||
end
|
||||
|
||||
platform_is_not :windows do
|
||||
it "returns a private, readable and writable file" do
|
||||
@tempfile = Tempfile.create
|
||||
stat = @tempfile.stat
|
||||
stat.should.readable?
|
||||
stat.should.writable?
|
||||
stat.should_not.executable?
|
||||
stat.should_not.world_readable?
|
||||
stat.should_not.world_writable?
|
||||
end
|
||||
end
|
||||
|
||||
platform_is :windows do
|
||||
it "returns a public, readable and writable file" do
|
||||
@tempfile = Tempfile.create
|
||||
stat = @tempfile.stat
|
||||
stat.should.readable?
|
||||
stat.should.writable?
|
||||
stat.should_not.executable?
|
||||
stat.should.world_readable?
|
||||
stat.should.world_writable?
|
||||
end
|
||||
end
|
||||
|
||||
context "when called with a block" do
|
||||
it "returns the value of the block" do
|
||||
value = Tempfile.create do |tempfile|
|
||||
tempfile << "Test!"
|
||||
"return"
|
||||
end
|
||||
value.should == "return"
|
||||
end
|
||||
|
||||
it "closes and unlinks file after block execution" do
|
||||
Tempfile.create do |tempfile|
|
||||
@tempfile = tempfile
|
||||
@tempfile.should_not.closed?
|
||||
File.exist?(@tempfile.path).should be_true
|
||||
end
|
||||
|
||||
@tempfile.should.closed?
|
||||
File.exist?(@tempfile.path).should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "when called with a single positional argument" do
|
||||
it "uses a String as a prefix for the filename" do
|
||||
@tempfile = Tempfile.create("create_spec")
|
||||
@tempfile.path.should.start_with?("#{Dir.tmpdir}/create_spec")
|
||||
@tempfile.path.should_not == "#{Dir.tmpdir}/create_spec"
|
||||
end
|
||||
|
||||
it "uses an array of one String as a prefix for the filename" do
|
||||
@tempfile = Tempfile.create(["create_spec"])
|
||||
@tempfile.path.should.start_with?("#{Dir.tmpdir}/create_spec")
|
||||
@tempfile.path.should_not == "#{Dir.tmpdir}/create_spec"
|
||||
end
|
||||
|
||||
it "uses an array of two Strings as a prefix and suffix for the filename" do
|
||||
@tempfile = Tempfile.create(["create_spec", ".temp"])
|
||||
@tempfile.path.should.start_with?("#{Dir.tmpdir}/create_spec")
|
||||
@tempfile.path.should.end_with?(".temp")
|
||||
end
|
||||
|
||||
it "ignores excessive array elements after the first two" do
|
||||
@tempfile = Tempfile.create(["create_spec", ".temp", :".txt"])
|
||||
@tempfile.path.should.start_with?("#{Dir.tmpdir}/create_spec")
|
||||
@tempfile.path.should.end_with?(".temp")
|
||||
end
|
||||
|
||||
it "raises ArgumentError if passed something else than a String or an array of Strings" do
|
||||
-> { Tempfile.create(:create_spec) }.should raise_error(ArgumentError, "unexpected prefix: :create_spec")
|
||||
-> { Tempfile.create([:create_spec]) }.should raise_error(ArgumentError, "unexpected prefix: :create_spec")
|
||||
-> { Tempfile.create(["create_spec", :temp]) }.should raise_error(ArgumentError, "unexpected suffix: :temp")
|
||||
end
|
||||
end
|
||||
|
||||
context "when called with a second positional argument" do
|
||||
it "uses it as a directory for the tempfile" do
|
||||
@tempfile = Tempfile.create("create_spec", "./")
|
||||
@tempfile.path.should.start_with?("./create_spec")
|
||||
end
|
||||
|
||||
it "raises TypeError if argument can not be converted to a String" do
|
||||
-> { Tempfile.create("create_spec", :temp) }.should raise_error(TypeError, "no implicit conversion of Symbol into String")
|
||||
end
|
||||
end
|
||||
|
||||
context "when called with a mode option" do
|
||||
it "ORs it with the default mode, forcing it to be readable and writable" do
|
||||
@tempfile = Tempfile.create(mode: File::RDONLY)
|
||||
@tempfile.puts "test"
|
||||
@tempfile.rewind
|
||||
@tempfile.read.should == "test\n"
|
||||
end
|
||||
|
||||
it "raises NoMethodError if passed a String mode" do
|
||||
-> { Tempfile.create(mode: "wb") }.should raise_error(NoMethodError, /undefined method ['`]|' for .+String/)
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.4" do
|
||||
context "when called with anonymous: true" do
|
||||
it "returns an already unlinked File without a proper path" do
|
||||
@tempfile = Tempfile.create(anonymous: true)
|
||||
@tempfile.should_not.closed?
|
||||
@tempfile.path.should == "#{Dir.tmpdir}/"
|
||||
File.file?(@tempfile.path).should be_false
|
||||
end
|
||||
|
||||
it "unlinks file before calling the block" do
|
||||
Tempfile.create(anonymous: true) do |tempfile|
|
||||
@tempfile = tempfile
|
||||
@tempfile.should_not.closed?
|
||||
@tempfile.path.should == "#{Dir.tmpdir}/"
|
||||
File.file?(@tempfile.path).should be_false
|
||||
end
|
||||
@tempfile.should.closed?
|
||||
end
|
||||
end
|
||||
|
||||
context "when called with anonymous: false" do
|
||||
it "returns a usual File with a path" do
|
||||
@tempfile = Tempfile.create(anonymous: false)
|
||||
@tempfile.should_not.closed?
|
||||
@tempfile.path.should.start_with?(Dir.tmpdir)
|
||||
File.file?(@tempfile.path).should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when called with other options" do
|
||||
it "passes them along to File.open" do
|
||||
@tempfile = Tempfile.create(encoding: "IBM037:IBM037", binmode: true)
|
||||
@tempfile.external_encoding.should == Encoding.find("IBM037")
|
||||
@tempfile.binmode?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -724,14 +724,16 @@ describe "C-API Encoding function" do
|
||||
end
|
||||
|
||||
describe "rb_define_dummy_encoding" do
|
||||
run = 0
|
||||
|
||||
it "defines the dummy encoding" do
|
||||
@s.rb_define_dummy_encoding("FOO")
|
||||
enc = Encoding.find("FOO")
|
||||
@s.rb_define_dummy_encoding("FOO#{run += 1}")
|
||||
enc = Encoding.find("FOO#{run}")
|
||||
enc.should.dummy?
|
||||
end
|
||||
|
||||
it "returns the index of the dummy encoding" do
|
||||
index = @s.rb_define_dummy_encoding("BAR")
|
||||
index = @s.rb_define_dummy_encoding("BAR#{run += 1}")
|
||||
index.should == Encoding.list.size - 1
|
||||
end
|
||||
|
||||
|
||||
@ -62,6 +62,10 @@ static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st) {
|
||||
return rb_struct_size(st);
|
||||
}
|
||||
|
||||
static VALUE struct_spec_rb_struct_initialize(VALUE self, VALUE st, VALUE values) {
|
||||
return rb_struct_initialize(st, values);
|
||||
}
|
||||
|
||||
#if defined(RUBY_VERSION_IS_3_3)
|
||||
/* Only allow setting three attributes, should be sufficient for testing. */
|
||||
static VALUE struct_spec_rb_data_define(VALUE self, VALUE superclass,
|
||||
@ -90,6 +94,7 @@ void Init_struct_spec(void) {
|
||||
rb_define_method(cls, "rb_struct_define_under", struct_spec_rb_struct_define_under, 5);
|
||||
rb_define_method(cls, "rb_struct_new", struct_spec_rb_struct_new, 4);
|
||||
rb_define_method(cls, "rb_struct_size", struct_spec_rb_struct_size, 1);
|
||||
rb_define_method(cls, "rb_struct_initialize", struct_spec_rb_struct_initialize, 2);
|
||||
#if defined(RUBY_VERSION_IS_3_3)
|
||||
rb_define_method(cls, "rb_data_define", struct_spec_rb_data_define, 4);
|
||||
#endif
|
||||
|
||||
@ -41,14 +41,19 @@ describe "CApiGlobalSpecs" do
|
||||
@f.sb_get_global_value.should == "XYZ"
|
||||
end
|
||||
|
||||
run = 0
|
||||
|
||||
it "rb_define_readonly_variable should define a new readonly global variable" do
|
||||
name = "ro_gvar#{run += 1}"
|
||||
eval <<~RUBY
|
||||
# Check the gvar doesn't exist and ensure rb_gv_get doesn't implicitly declare the gvar,
|
||||
# otherwise the rb_define_readonly_variable call will conflict.
|
||||
suppress_warning { @f.sb_gv_get("ro_gvar") } .should == nil
|
||||
suppress_warning { @f.sb_gv_get("#{name}") }.should == nil
|
||||
|
||||
@f.rb_define_readonly_variable("ro_gvar", 15)
|
||||
$ro_gvar.should == 15
|
||||
-> { $ro_gvar = 10 }.should raise_error(NameError)
|
||||
@f.rb_define_readonly_variable("#{name}", 15)
|
||||
$#{name}.should == 15
|
||||
-> { $#{name} = 10 }.should raise_error(NameError)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "rb_define_hooked_variable should define a C hooked global variable" do
|
||||
|
||||
@ -38,7 +38,7 @@ describe "CApiModule" do
|
||||
CApiModuleSpecs::C.const_set(:_INVALID, 1)
|
||||
}.should raise_error(NameError, /wrong constant name/)
|
||||
|
||||
@m.rb_const_set(CApiModuleSpecs::C, :_INVALID, 2)
|
||||
suppress_warning { @m.rb_const_set(CApiModuleSpecs::C, :_INVALID, 2) }
|
||||
@m.rb_const_get(CApiModuleSpecs::C, :_INVALID).should == 2
|
||||
|
||||
# Ruby-level should still not allow access
|
||||
|
||||
@ -208,20 +208,48 @@ describe "C-API Struct function" do
|
||||
@s.rb_struct_size(@struct).should == 3
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_struct_initialize" do
|
||||
it "sets all members" do
|
||||
@s.rb_struct_initialize(@struct, [1, 2, 3]).should == nil
|
||||
@struct.a.should == 1
|
||||
@struct.b.should == 2
|
||||
@struct.c.should == 3
|
||||
end
|
||||
|
||||
it "does not freeze the Struct instance" do
|
||||
@s.rb_struct_initialize(@struct, [1, 2, 3]).should == nil
|
||||
@struct.should_not.frozen?
|
||||
@s.rb_struct_initialize(@struct, [4, 5, 6]).should == nil
|
||||
@struct.a.should == 4
|
||||
@struct.b.should == 5
|
||||
@struct.c.should == 6
|
||||
end
|
||||
|
||||
it "raises ArgumentError if too many values" do
|
||||
-> { @s.rb_struct_initialize(@struct, [1, 2, 3, 4]) }.should raise_error(ArgumentError, "struct size differs")
|
||||
end
|
||||
|
||||
it "treats missing values as nil" do
|
||||
@s.rb_struct_initialize(@struct, [1, 2]).should == nil
|
||||
@struct.a.should == 1
|
||||
@struct.b.should == 2
|
||||
@struct.c.should == nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
describe "C-API Data function" do
|
||||
before :each do
|
||||
before :all do
|
||||
@s = CApiStructSpecs.new
|
||||
@klass = @s.rb_data_define(nil, "a", "b", "c")
|
||||
end
|
||||
|
||||
describe "rb_data_define" do
|
||||
it "returns a subclass of Data class when passed nil as the first argument" do
|
||||
klass = @s.rb_data_define(nil, "a", "b", "c")
|
||||
|
||||
klass.should.is_a? Class
|
||||
klass.superclass.should == Data
|
||||
@klass.should.is_a? Class
|
||||
@klass.superclass.should == Data
|
||||
end
|
||||
|
||||
it "returns a subclass of a class when passed as the first argument" do
|
||||
@ -233,8 +261,7 @@ ruby_version_is "3.3" do
|
||||
end
|
||||
|
||||
it "creates readers for the members" do
|
||||
klass = @s.rb_data_define(nil, "a", "b", "c")
|
||||
obj = klass.new(1, 2, 3)
|
||||
obj = @klass.new(1, 2, 3)
|
||||
|
||||
obj.a.should == 1
|
||||
obj.b.should == 2
|
||||
@ -242,8 +269,7 @@ ruby_version_is "3.3" do
|
||||
end
|
||||
|
||||
it "returns the member names as Symbols" do
|
||||
klass = @s.rb_data_define(nil, "a", "b", "c")
|
||||
obj = klass.new(0, 0, 0)
|
||||
obj = @klass.new(0, 0, 0)
|
||||
|
||||
obj.members.should == [:a, :b, :c]
|
||||
end
|
||||
@ -256,5 +282,35 @@ ruby_version_is "3.3" do
|
||||
-> { @s.rb_data_define([], "a", "b", "c") }.should raise_error(TypeError, "wrong argument type Array (expected Class)")
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_struct_initialize" do
|
||||
it "sets all members for a Data instance" do
|
||||
data = @klass.allocate
|
||||
@s.rb_struct_initialize(data, [1, 2, 3]).should == nil
|
||||
data.a.should == 1
|
||||
data.b.should == 2
|
||||
data.c.should == 3
|
||||
end
|
||||
|
||||
it "freezes the Data instance" do
|
||||
data = @klass.allocate
|
||||
@s.rb_struct_initialize(data, [1, 2, 3]).should == nil
|
||||
data.should.frozen?
|
||||
-> { @s.rb_struct_initialize(data, [1, 2, 3]) }.should raise_error(FrozenError)
|
||||
end
|
||||
|
||||
it "raises ArgumentError if too many values" do
|
||||
data = @klass.allocate
|
||||
-> { @s.rb_struct_initialize(data, [1, 2, 3, 4]) }.should raise_error(ArgumentError, "struct size differs")
|
||||
end
|
||||
|
||||
it "treats missing values as nil" do
|
||||
data = @klass.allocate
|
||||
@s.rb_struct_initialize(data, [1, 2]).should == nil
|
||||
data.a.should == 1
|
||||
data.b.should == 2
|
||||
data.c.should == nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user