Make sure select(2) calls are IO.select
[ruby-mogilefs-client.git] / test / test_backend.rb
blob830698401cc348f230cc8eb4521e8337972e8e9e
1 require 'test/unit'
2 require 'test/setup'
4 $TESTING = true
6 require 'mogilefs/backend'
8 class MogileFS::Backend
10   attr_accessor :hosts
11   attr_reader :timeout, :dead
12   attr_writer :lasterr, :lasterrstr, :socket
14 end
16 class TestBackend < Test::Unit::TestCase
18   def setup
19     @backend = MogileFS::Backend.new :hosts => ['localhost:1']
20   end
22   def test_initialize
23     assert_raises ArgumentError do MogileFS::Backend.new end
24     assert_raises ArgumentError do MogileFS::Backend.new :hosts => [] end
25     assert_raises ArgumentError do MogileFS::Backend.new :hosts => [''] end
27     assert_equal ['localhost:1'], @backend.hosts
28     assert_equal 3, @backend.timeout
29     assert_equal nil, @backend.lasterr
30     assert_equal nil, @backend.lasterrstr
31     assert_equal({}, @backend.dead)
33     @backend = MogileFS::Backend.new :hosts => ['localhost:6001'], :timeout => 1
34     assert_equal 1, @backend.timeout
35   end
37   def test_do_request
38     received = ''
39     tmp = TempServer.new(Proc.new do |serv, port|
40       client, client_addr = serv.accept
41       client.sync = true
42       received = client.recv 4096
43       client.send "OK 1 you=win\r\n", 0
44     end)
46     @backend.hosts = "127.0.0.1:#{tmp.port}"
48     assert_equal({'you' => 'win'},
49                  @backend.do_request('go!', { 'fight' => 'team fight!' }))
50     assert_equal "go! fight=team+fight%21\r\n", received
51     ensure
52       TempServer.destroy_all!
53   end
55   def test_do_request_send_error
56     socket_request = ''
57     socket = Object.new
58     def socket.closed?() false end
59     def socket.send(request, flags) raise SystemCallError, 'dummy' end
61     @backend.instance_variable_set '@socket', socket
63     assert_raises MogileFS::UnreachableBackendError do
64       @backend.do_request 'go!', { 'fight' => 'team fight!' }
65     end
67     assert_equal nil, @backend.instance_variable_get('@socket')
68   end
70   def test_automatic_exception
71     assert ! MogileFS::Backend.const_defined?('PebkacError')
72     assert @backend.error('pebkac')
73     assert_equal MogileFS::Error, @backend.error('PebkacError').superclass
74     assert MogileFS::Backend.const_defined?('PebkacError')
76     assert ! MogileFS::Backend.const_defined?('PebKacError')
77     assert @backend.error('peb_kac')
78     assert_equal MogileFS::Error, @backend.error('PebKacError').superclass
79     assert MogileFS::Backend.const_defined?('PebKacError')
80   end
82   def test_do_request_truncated
83     socket_request = ''
84     socket = Object.new
85     def socket.closed?() false end
86     def socket.send(request, flags) return request.length - 1 end
88     @backend.instance_variable_set '@socket', socket
90     assert_raises MogileFS::RequestTruncatedError do
91       @backend.do_request 'go!', { 'fight' => 'team fight!' }
92     end
93   end
95   def test_make_request
96     assert_equal "go! fight=team+fight%21\r\n",
97                  @backend.make_request('go!', { 'fight' => 'team fight!' })
98   end
100   def test_parse_response
101     assert_equal({'foo' => 'bar', 'baz' => 'hoge'},
102                  @backend.parse_response('OK 1 foo=bar&baz=hoge'))
104     err = nil
105     begin
106       @backend.parse_response('ERR you totally suck')
107     rescue MogileFS::Error => err
108       assert_equal 'MogileFS::Backend::YouError', err.class.to_s
109     end
110     assert_equal 'MogileFS::Backend::YouError', err.class.to_s
112     assert_equal 'you', @backend.lasterr
113     assert_equal 'totally suck', @backend.lasterrstr
115     assert_raises MogileFS::InvalidResponseError do
116       @backend.parse_response 'garbage'
117     end
118   end
120   def test_readable_eh_readable
121     accept_nr = 0
122     tmp = TempServer.new(Proc.new do |serv, port|
123       client, client_addr = serv.accept
124       client.sync = true
125       accept_nr += 1
126       client.send('.', 0)
127       sleep
128     end)
130     @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{tmp.port}" ]
131     assert_equal true, @backend.readable?
132     assert_equal 1, accept_nr
133     ensure
134       TempServer.destroy_all!
135   end
137   def test_readable_eh_not_readable
138     tmp = TempServer.new(Proc.new { |a,b| sleep })
139     @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{tmp.port}" ]
141     begin
142       @backend.readable?
143     rescue MogileFS::UnreadableSocketError => e
144       assert_equal "127.0.0.1:#{tmp.port} never became readable", e.message
145     rescue Exception => err
146       flunk "MogileFS::UnreadableSocketError not raised #{err} #{err.backtrace}"
147     else
148       flunk "MogileFS::UnreadableSocketError not raised"
149     ensure
150       TempServer.destroy_all!
151     end
152   end
154   def test_socket
155     assert_equal({}, @backend.dead)
156     assert_raises MogileFS::UnreachableBackendError do @backend.socket end
157     assert_equal(['localhost:1'], @backend.dead.keys)
158   end
160   def test_socket_robust
161     bad_accept_nr = accept_nr = 0
162     bad = Proc.new { |serv,port| sleep; bad_accept_nr += 1 }
163     good = Proc.new do |serv,port|
164       client, client_addr = serv.accept
165       client.sync = true
166       accept_nr += 1
167       client.send '.', 0
168       client.flush
169       sleep
170     end
171     nr = 10
173     nr.times do
174       begin
175         t1 = TempServer.new(bad)
176         t2 = TempServer.new(good)
177         hosts = ["0:#{t1.port}", "0:#{t2.port}"]
178         @backend = MogileFS::Backend.new(:hosts => hosts)
179         assert_equal({}, @backend.dead)
180         t1.destroy!
181         @backend.socket
182       ensure
183         TempServer.destroy_all!
184       end
185     end # nr.times
186     assert_equal 0, bad_accept_nr
187     assert_equal nr, accept_nr
188   end
190   def test_shutdown
191     accept_nr = 0
192     tmp = TempServer.new(Proc.new do |serv,port|
193       client, client_addr = serv.accept
194       accept_nr += 1
195       sleep
196     end)
197     @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{tmp.port}" ]
198     assert @backend.socket
199     assert ! @backend.socket.closed?
200     @backend.shutdown
201     assert_equal nil, @backend.instance_variable_get(:@socket)
202     assert_equal 1, accept_nr
204     ensure
205       TempServer.destroy_all!
206   end
208   def test_url_decode
209     assert_equal({"\272z" => "\360opy", "f\000" => "\272r"},
210                  @backend.url_decode("%baz=%f0opy&f%00=%bar"))
211   end
213   def test_url_encode
214     params = [["f\000", "\272r"], ["\272z", "\360opy"]]
215     assert_equal "f%00=%bar&%baz=%f0opy", @backend.url_encode(params)
216   end
218   def test_url_escape # \n for unit_diff
219     actual = (0..255).map { |c| @backend.url_escape c.chr }.join "\n"
221     expected = []
222     expected.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
223     expected << '+'
224     expected.push(*(0x21..0x2b).map { |c| "%%%0.2x" % c })
225     expected.push(*%w[, - . /])
226     expected.push(*('0'..'9'))
227     expected.push(*%w[: %3b %3c %3d %3e %3f %40])
228     expected.push(*('A'..'Z'))
229     expected.push(*%w[%5b \\ %5d %5e _ %60])
230     expected.push(*('a'..'z'))
231     expected.push(*(0x7b..0xff).map { |c| "%%%0.2x" % c })
233     expected = expected.join "\n"
235     assert_equal expected, actual
236   end
238   def test_url_unescape
239     input = []
240     input.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
241     input << '+'
242     input.push(*(0x21..0x2b).map { |c| "%%%0.2x" % c })
243     input.push(*%w[, - . /])
244     input.push(*('0'..'9'))
245     input.push(*%w[: %3b %3c %3d %3e %3f %40])
246     input.push(*('A'..'Z'))
247     input.push(*%w[%5b \\ %5d %5e _ %60])
248     input.push(*('a'..'z'))
249     input.push(*(0x7b..0xff).map { |c| "%%%0.2x" % c })
251     actual = input.map { |c| @backend.url_unescape c }.join "\n"
253     expected = (0..255).map { |c| c.chr }.join "\n"
254     expected.sub! '+', ' '
256     assert_equal expected, actual
257   end