summaryrefslogtreecommitdiff
path: root/spec/ruby/core/io/sysread_spec.rb
blob: 003bb9eb94911a45bd2a9fb967ddd82cc24dd1e2 (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
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

describe "IO#sysread on a file" do
  before :each do
    @file_name = tmp("IO_sysread_file") + $$.to_s
    File.open(@file_name, "w") do |f|
      # write some stuff
      f.write("012345678901234567890123456789\nabcdef")
    end
    @file = File.open(@file_name, "r+")
  end

  after :each do
    @file.close
    rm_r @file_name
  end

  it "reads the specified number of bytes from the file" do
    @file.sysread(15).should == "012345678901234"
  end

  it "reads the specified number of bytes from the file to the buffer" do
    buf = +"" # empty buffer
    @file.sysread(15, buf).should == buf
    buf.should == "012345678901234"

    @file.rewind

    buf = +"ABCDE" # small buffer
    @file.sysread(15, buf).should == buf
    buf.should == "012345678901234"

    @file.rewind

    buf = +"ABCDE" * 5 # large buffer
    @file.sysread(15, buf).should == buf
    buf.should == "012345678901234"
  end

  it "coerces the second argument to string and uses it as a buffer" do
    buf = +"ABCDE"
    (obj = mock("buff")).should_receive(:to_str).any_number_of_times.and_return(buf)
    @file.sysread(15, obj).should == buf
    buf.should == "012345678901234"
  end

  it "advances the position of the file by the specified number of bytes" do
    @file.sysread(15)
    @file.sysread(5).should == "56789"
  end

  it "raises an error when called after buffered reads" do
    @file.readline
    -> { @file.sysread(5) }.should raise_error(IOError)
  end

  it "reads normally even when called immediately after a buffered IO#read" do
    @file.read(15)
    @file.sysread(5).should == "56789"
  end

  it "does not raise error if called after IO#read followed by IO#write" do
    @file.read(5)
    @file.write("abcde")
    -> { @file.sysread(5) }.should_not raise_error(IOError)
  end

  it "does not raise error if called after IO#read followed by IO#syswrite" do
    @file.read(5)
    @file.syswrite("abcde")
    -> { @file.sysread(5) }.should_not raise_error(IOError)
  end

  it "reads updated content after the flushed buffered IO#write" do
    @file.write("abcde")
    @file.flush
    @file.sysread(5).should == "56789"
    File.open(@file_name) do |f|
      f.sysread(10).should == "abcde56789"
    end
  end

  it "raises IOError on closed stream" do
    -> { IOSpecs.closed_io.sysread(5) }.should raise_error(IOError)
  end

  it "immediately returns an empty string if the length argument is 0" do
    @file.sysread(0).should == ""
  end

  it "immediately returns the given buffer if the length argument is 0" do
    buffer = +"existing content"
    @file.sysread(0, buffer).should == buffer
    buffer.should == "existing content"
  end

  it "discards the existing buffer content upon successful read" do
    buffer = +"existing content"
    @file.sysread(11, buffer)
    buffer.should == "01234567890"
  end

  it "discards the existing buffer content upon error" do
    buffer = +"existing content"
    @file.seek(0, :END)
    -> { @file.sysread(1, buffer) }.should raise_error(EOFError)
    buffer.should be_empty
  end
end

describe "IO#sysread" do
  before do
    @read, @write = IO.pipe
  end

  after do
    @read.close
    @write.close
  end

  it "returns a smaller string if less than size bytes are available" do
    @write.syswrite "ab"
    @read.sysread(3).should == "ab"
  end

  guard_not -> { platform_is :windows and ruby_version_is ""..."3.2" } do # https://bugs.ruby-lang.org/issues/18880
    it "raises ArgumentError when length is less than 0" do
      -> { @read.sysread(-1) }.should raise_error(ArgumentError)
    end
  end
end