summaryrefslogtreecommitdiff
path: root/spec/ruby/core/io/buffer/resize_spec.rb
blob: 0da3a23356c0dc2fe81d0041b5b92da9a296a790 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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