File: socket_tests.rb

package info (click to toggle)
ruby-excon 0.112.0-4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,232 kB
  • sloc: ruby: 7,855; makefile: 5
file content (121 lines) | stat: -rw-r--r-- 3,432 bytes parent folder | download
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
# A fake Excon::Socket that allows passing in an arbitrary backend @socket
class MockExconSocket < Excon::Socket
  attr_reader :read_count

  def initialize(backend_socket, *args)
    super(*args)
    @read_count = 0
    @socket = backend_socket
  end

  def read_nonblock(*args)
    @read_count += 1
    super
  end

  def connect
    # pass
  end

  def select_with_timeout(*args)
    # don't actually wait, assume we're ready
  end
end

# A socket whose read_nonblock returns from an input list,
# and which counts the number of reads
class MockNonblockRubySocket
  attr_reader :sequence

  def initialize(nonblock_reads)
    @nonblock_reads = nonblock_reads
    @sequence = []
  end

  def read_nonblock(maxlen, buffer = nil)
    if @nonblock_reads.empty?
      @sequence << 'EOF'
      raise EOFError
    elsif @nonblock_reads.first.empty?
      @nonblock_reads.shift
      if @nonblock_reads.empty?
        @sequence << 'EOF'
        raise EOFError
      end
      @sequence << 'EAGAIN'
      raise Errno::EAGAIN
    elsif
      len = maxlen ? maxlen : @nonblock_reads.first.length
      ret = @nonblock_reads.first.slice!(0, len)
      @sequence << ret.length

      if buffer
        buffer.clear
        buffer << ret
        buffer
      else
        ret
      end
    end
  end

  # Returns the results of `block`, as well as how many times we called read on the Excon
  # socket, and the sequence of reads on the backend socket
  def self.check_reads(nonblock_reads, socket_args, &block)
    backend_socket = MockNonblockRubySocket.new(nonblock_reads)
    socket = MockExconSocket.new(backend_socket, { nonblock: true }.merge(socket_args))
    ret = block[socket]
    [ret, socket.read_count, backend_socket.sequence]
  end
end

Shindo.tests('socket') do
  CHUNK_SIZES = [nil, 512]
  CHUNK_SIZES.each do |chunk_size|
    tests("chunk_size: #{chunk_size}") do
      socket_args = {chunk_size: chunk_size}
      tests('read_nonblock') do
        tests('readline nonblock is efficient') do
          returns(["one\n", 1, [8, 'EOF']]) do
            MockNonblockRubySocket.check_reads(["one\ntwo\n"], socket_args) do |sock|
              sock.readline
            end
          end
        end

        tests('readline nonblock works sequentially') do
          returns([["one\n", "two\n"], 1, [8, 'EOF']]) do
            MockNonblockRubySocket.check_reads(["one\ntwo\n"], socket_args) do |sock|
              2.times.map { sock.readline }
            end
          end
        end

        tests('readline nonblock can handle partial reads') do
          returns([["one\n", "two\n"], 2, [5, 'EAGAIN', 3, 'EOF']]) do
            MockNonblockRubySocket.check_reads(["one\nt", "wo\n"], socket_args) do |sock|
              2.times.map { sock.readline }
            end
          end
        end

        tests('readline nonblock before read') do
          returns([["one\n", "two\n"], 2, [8, 'EOF']]) do
          MockNonblockRubySocket.check_reads(["one\ntwo\n"], socket_args) do |sock|
              [sock.readline, sock.read(6)]
            end
          end
        end

        tests('read_nonblock does not EOF early') do
          returns([["one", "two"], 2, [3, 'EAGAIN', 3, 'EOF']]) do
            # Data, EAGAIN, data, EOF
            MockNonblockRubySocket.check_reads(["one", "two"], socket_args) do |sock|
              [sock.read, sock.read]
            end
          end
        end
      end
    end
  end
end