summaryrefslogtreecommitdiff
path: root/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb
blob: cc4275c417ac115709d68b5bb918cc792d628ad3 (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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
require_relative '../spec_helper'
require_relative '../fixtures/classes'

describe 'BasicSocket#recvmsg_nonblock' do
  SocketSpecs.each_ip_protocol do |family, ip_address|
    describe 'using a disconnected socket' do
      before do
        @client = Socket.new(family, :DGRAM)
        @server = Socket.new(family, :DGRAM)
      end

      after do
        @client.close
        @server.close
      end

      platform_is_not :windows do
        describe 'using an unbound socket' do
          it 'raises an exception extending IO::WaitReadable' do
            -> { @server.recvmsg_nonblock }.should raise_error(IO::WaitReadable)
          end
        end
      end

      describe 'using a bound socket' do
        before do
          @server.bind(Socket.sockaddr_in(0, ip_address))
        end

        describe 'without any data available' do
          it 'raises an exception extending IO::WaitReadable' do
            -> { @server.recvmsg_nonblock }.should raise_error(IO::WaitReadable)
          end

          it 'returns :wait_readable with exception: false' do
            @server.recvmsg_nonblock(exception: false).should == :wait_readable
          end
        end

        describe 'with data available' do
          before do
            @client.connect(@server.getsockname)

            @client.write('hello')

            IO.select([@server], nil, nil, 5)
          end

          it 'returns an Array containing the data, an Addrinfo and the flags' do
            @server.recvmsg_nonblock.should be_an_instance_of(Array)
          end

          describe 'without a maximum message length' do
            it 'reads all the available data' do
              @server.recvmsg_nonblock[0].should == 'hello'
            end
          end

          describe 'with a maximum message length' do
            platform_is_not :windows do
              it 'reads up to the maximum amount of bytes' do
                @server.recvmsg_nonblock(2)[0].should == 'he'
              end
            end
          end

          describe 'the returned Array' do
            before do
              @array = @server.recvmsg_nonblock
            end

            it 'stores the message at index 0' do
              @array[0].should == 'hello'
            end

            it 'stores an Addrinfo at index 1' do
              @array[1].should be_an_instance_of(Addrinfo)
            end

            platform_is_not :windows do
              it 'stores the flags at index 2' do
                @array[2].should be_kind_of(Integer)
              end
            end

            describe 'the returned Addrinfo' do
              before do
                @addr = @array[1]
              end

              it 'uses the IP address of the client' do
                @addr.ip_address.should == @client.local_address.ip_address
              end

              it 'uses the correct address family' do
                @addr.afamily.should == family
              end

              it 'uses the correct protocol family' do
                @addr.pfamily.should == family
              end

              it 'uses the correct socket type' do
                @addr.socktype.should == Socket::SOCK_DGRAM
              end

              it 'uses the port number of the client' do
                @addr.ip_port.should == @client.local_address.ip_port
              end
            end
          end
        end
      end
    end

    platform_is_not :windows do
      describe 'using a connected but not bound socket' do
        before do
          @server = Socket.new(family, :STREAM)
        end

        after do
          @server.close
        end

        it "raises Errno::ENOTCONN" do
          -> { @server.recvmsg_nonblock }.should raise_error(Errno::ENOTCONN)
          -> { @server.recvmsg_nonblock(exception: false) }.should raise_error(Errno::ENOTCONN)
        end
      end

      describe 'using a connected socket' do
        before do
          @client = Socket.new(family, :STREAM)
          @server = Socket.new(family, :STREAM)

          @server.bind(Socket.sockaddr_in(0, ip_address))
          @server.listen(1)

          @client.connect(@server.getsockname)
        end

        after do
          @client.close
          @server.close
        end

        describe 'without any data available' do
          it 'raises IO::WaitReadable' do
            -> {
              socket, _ = @server.accept
              begin
                socket.recvmsg_nonblock
              ensure
                socket.close
              end
            }.should raise_error(IO::WaitReadable)
          end
        end

        describe 'with data available' do
          before do
            @client.write('hello')

            @socket, _ = @server.accept
            IO.select([@socket])
          end

          after do
            @socket.close
          end

          it 'returns an Array containing the data, an Addrinfo and the flags' do
            @socket.recvmsg_nonblock.should be_an_instance_of(Array)
          end

          describe 'the returned Array' do
            before do
              @array = @socket.recvmsg_nonblock
            end

            it 'stores the message at index 0' do
              @array[0].should == 'hello'
            end

            it 'stores an Addrinfo at index 1' do
              @array[1].should be_an_instance_of(Addrinfo)
            end

            it 'stores the flags at index 2' do
              @array[2].should be_kind_of(Integer)
            end

            describe 'the returned Addrinfo' do
              before do
                @addr = @array[1]
              end

              it 'raises when receiving the ip_address message' do
                -> { @addr.ip_address }.should raise_error(SocketError)
              end

              it 'uses the correct address family' do
                @addr.afamily.should == Socket::AF_UNSPEC
              end

              it 'uses 0 for the protocol family' do
                @addr.pfamily.should == 0
              end

              it 'uses the correct socket type' do
                @addr.socktype.should == Socket::SOCK_STREAM
              end

              it 'raises when receiving the ip_port message' do
                -> { @addr.ip_port }.should raise_error(SocketError)
              end
            end
          end
        end
      end
    end
  end
end