about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2008-12-22 17:37:02 -0800
committerEric Wong <normalperson@yhbt.net>2008-12-22 18:24:42 -0800
commitb7ba273a043633c01d9ae8cda8ae9db4cc5dcf07 (patch)
treed70359793d59061597655f21cc39ac9d54abe076
parentdf2af32f01bc995e9f311b51d3351ee042e23483 (diff)
downloadmogilefs-client-b7ba273a043633c01d9ae8cda8ae9db4cc5dcf07.tar.gz
These tests forced us to use TCPSocket, which wasn't friendly to
sockets created with Socket.new (for asynchronous connect(2)
calls).

No changes to the core MogileFS components have been
made.  I want to verify the new TempServer implementation
performs correctly with the current code.
-rw-r--r--test/setup.rb111
-rw-r--r--test/test_backend.rb101
-rw-r--r--test/test_mogilefs.rb160
-rw-r--r--test/test_utils.rb52
4 files changed, 203 insertions, 221 deletions
diff --git a/test/setup.rb b/test/setup.rb
index b0dc53f..d8d213d 100644
--- a/test/setup.rb
+++ b/test/setup.rb
@@ -1,3 +1,4 @@
+STDIN.sync = STDOUT.sync = STDERR.sync = true
 require 'test/unit'
 
 require 'fileutils'
@@ -48,95 +49,36 @@ class FakeBackend
 
 end
 
-class FakeSocket
-
-  attr_reader :read_s
-  attr_reader :write_s
-  attr_reader :sync
-
-  def initialize(read = '', write = StringIO.new)
-    @read_s = read.class.method_defined?(:sysread) ? read : StringIO.new(read)
-    @write_s = write
-    @closed = false
-    @sync = false
-  end
-
-  def sync=(do_sync)
-    @sync = do_sync
-    @write_s.sync = do_sync
-    @read_s.sync = do_sync
-  end
-
-  def closed?
-    @closed
-  end
-
-  def close
-    @closed = true
-    return nil
-  end
-
-  def gets
-    @read_s.gets
-  end
-
-  def peeraddr
-    ['AF_INET', 6001, 'localhost', '127.0.0.1']
-  end
-
-  def read(bytes = nil)
-    @read_s.read bytes
-  end
-
-  def sysread(bytes, buf = '')
-    @read_s.sysread bytes, buf
-  end
-
-  def recv_nonblock(bytes, flags = 0)
-    ret = @read_s.sysread(bytes)
-    # Ruby doesn't expose pread(2)
-    if (flags & Socket::MSG_PEEK) != 0
-      if @read_s.respond_to?(:sysseek)
-        @read_s.sysseek(-ret.size, IO::SEEK_CUR)
-      else
-        @read_s.seek(-ret.size, IO::SEEK_CUR)
-      end
-    end
-    ret
-  end
-  alias_method :recv, :recv_nonblock
-
-  def write(data)
-    @write_s.write data
-  end
-
-  def syswrite(data)
-    @write_s.syswrite data
-  end
-
-end
-
 class MogileFS::Client
   attr_writer :readonly
 end
 
-class TCPSocket
-
-  class << self
-
-    attr_accessor :connections
-    attr_accessor :sockets
-
-    alias old_new new
-
-    def new(host, port)
-      raise Errno::ECONNREFUSED if @sockets.empty?
-      @connections << [host, port]
-      @sockets.pop
+require 'socket'
+class TempServer
+  attr_reader :port
+
+  def self.destroy_all!
+    ObjectSpace.each_object(TempServer) { |t| t.destroy! }
+  end
+
+  def initialize(server_proc)
+    @port = @sock = nil
+    retries = 0
+    begin
+      @port = 5000 + $$ % 1000 + rand(60000)
+      @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+      @sock.bind(Socket.pack_sockaddr_in(@port, '127.0.0.1'))
+      @sock.listen(5)
+    rescue Errno::EADDRINUSE
+      @sock.close rescue nil
+      retry if (retries += 1) < 10
     end
+    @thr = Thread.new(@sock, @port) { |s,p| server_proc.call(s, p) }
+  end
 
-    alias open new
-
+  def destroy!
+    @sock.close rescue nil
+    Thread.kill(@thr) rescue nil
   end
 
 end
@@ -154,9 +96,6 @@ class TestMogileFS < Test::Unit::TestCase
                                   :root => @root
     @backend = FakeBackend.new
     @client.instance_variable_set '@backend', @backend
-
-    TCPSocket.sockets = []
-    TCPSocket.connections = []
   end
 
   def teardown
diff --git a/test/test_backend.rb b/test/test_backend.rb
index 5db8189..8306984 100644
--- a/test/test_backend.rb
+++ b/test/test_backend.rb
@@ -16,8 +16,6 @@ end
 class TestBackend < Test::Unit::TestCase
 
   def setup
-    TCPSocket.connections = []
-    TCPSocket.sockets = []
     @backend = MogileFS::Backend.new :hosts => ['localhost:1']
   end
 
@@ -37,17 +35,21 @@ class TestBackend < Test::Unit::TestCase
   end
 
   def test_do_request
-    socket_request = ''
-    socket = Object.new
-    def socket.closed?() false end
-    def socket.send(request, flags) return request.length end
-    def @backend.select(*args) return [true] end
-    def socket.gets() return 'OK 1 you=win' end
+    received = ''
+    tmp = TempServer.new(Proc.new do |serv, port|
+      client, client_addr = serv.accept
+      client.sync = true
+      received = client.recv 4096
+      client.send "OK 1 you=win\r\n", 0
+    end)
 
-    @backend.instance_variable_set '@socket', socket
+    @backend.hosts = "127.0.0.1:#{tmp.port}"
 
     assert_equal({'you' => 'win'},
                  @backend.do_request('go!', { 'fight' => 'team fight!' }))
+    assert_equal "go! fight=team+fight%21\r\n", received
+    ensure
+      TempServer.destroy_all!
   end
 
   def test_do_request_send_error
@@ -116,31 +118,36 @@ class TestBackend < Test::Unit::TestCase
   end
 
   def test_readable_eh_readable
-    socket = Object.new
-    def socket.closed?() false end
-    def @backend.select(*args) return [true] end
-    @backend.instance_variable_set '@socket', socket
-
+    accept_nr = 0
+    tmp = TempServer.new(Proc.new do |serv, port|
+      client, client_addr = serv.accept
+      client.sync = true
+      accept_nr += 1
+      client.send('.', 0)
+      sleep
+    end)
+
+    @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{tmp.port}" ]
     assert_equal true, @backend.readable?
+    assert_equal 1, accept_nr
+    ensure
+      TempServer.destroy_all!
   end
 
   def test_readable_eh_not_readable
-    socket = FakeSocket.new
-    def socket.closed?() false end
-    def @backend.select(r=nil, w=nil, e=nil, t=1)
-      Kernel.sleep(t.to_f + 0.1)
-      []
-    end
-    @backend.instance_variable_set '@socket', socket
+    tmp = TempServer.new(Proc.new { |a,b| sleep })
+    @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{tmp.port}" ]
 
     begin
       @backend.readable?
     rescue MogileFS::UnreadableSocketError => e
-      assert_equal '127.0.0.1:6001 never became readable', e.message
+      assert_equal "127.0.0.1:#{tmp.port} never became readable", e.message
     rescue Exception => err
       flunk "MogileFS::UnreadableSocketError not raised #{err} #{err.backtrace}"
     else
       flunk "MogileFS::UnreadableSocketError not raised"
+    ensure
+      TempServer.destroy_all!
     end
   end
 
@@ -151,23 +158,51 @@ class TestBackend < Test::Unit::TestCase
   end
 
   def test_socket_robust
-    @backend.hosts = ['localhost:6001', 'localhost:6002']
-    def @backend.connect_to(host, port)
-      @first = (defined? @first) ? false : true
-      raise Errno::ECONNREFUSED if @first
+    bad_accept_nr = accept_nr = 0
+    bad = Proc.new { |serv,port| sleep; bad_accept_nr += 1 }
+    good = Proc.new do |serv,port|
+      client, client_addr = serv.accept
+      client.sync = true
+      accept_nr += 1
+      client.send '.', 0
+      client.flush
+      sleep
     end
-
-    assert_equal({}, @backend.dead)
-    @backend.socket
-    assert_equal false, @backend.dead.keys.empty?
+    nr = 10
+
+    nr.times do
+      begin
+        t1 = TempServer.new(bad)
+        t2 = TempServer.new(good)
+        hosts = ["0:#{t1.port}", "0:#{t2.port}"]
+        @backend = MogileFS::Backend.new(:hosts => hosts)
+        assert_equal({}, @backend.dead)
+        t1.destroy!
+        @backend.socket
+      ensure
+        TempServer.destroy_all!
+      end
+    end # nr.times
+    assert_equal 0, bad_accept_nr
+    assert_equal nr, accept_nr
   end
 
   def test_shutdown
-    fake_socket = FakeSocket.new
-    @backend.socket = fake_socket
-    assert_equal fake_socket, @backend.socket
+    accept_nr = 0
+    tmp = TempServer.new(Proc.new do |serv,port|
+      client, client_addr = serv.accept
+      accept_nr += 1
+      sleep
+    end)
+    @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{tmp.port}" ]
+    assert @backend.socket
+    assert ! @backend.socket.closed?
     @backend.shutdown
     assert_equal nil, @backend.instance_variable_get(:@socket)
+    assert_equal 1, accept_nr
+
+    ensure
+      TempServer.destroy_all!
   end
 
   def test_url_decode
diff --git a/test/test_mogilefs.rb b/test/test_mogilefs.rb
index 6765452..7ffcd8a 100644
--- a/test/test_mogilefs.rb
+++ b/test/test_mogilefs.rb
@@ -20,16 +20,28 @@ class TestMogileFS__MogileFS < TestMogileFS
   end
 
   def test_get_file_data_http
-    socket = FakeSocket.new("HTTP/1.0 200 OK\r\n" \
-                            "Content-Length: 5\r\n\r\ndata!")
-    TCPSocket.sockets << socket
-
-    path1 = 'http://rur-1/dev1/0/000/000/0000000062.fid'
-    path2 = 'http://rur-2/dev2/0/000/000/0000000062.fid'
+    accept_nr = 0
+    svr = Proc.new do |serv, port|
+      client, client_addr = serv.accept
+      client.sync = true
+      readed = client.recv(4096, 0)
+      assert(readed =~ \
+            %r{\AGET /dev[12]/0/000/000/0000000062\.fid HTTP/1.[01]\r\n\r\n\Z})
+      client.send("HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\ndata!", 0)
+      accept_nr += 1
+      client.close
+    end
+    t1 = TempServer.new(svr)
+    t2 = TempServer.new(svr)
+    path1 = "http://127.0.0.1:#{t1.port}/dev1/0/000/000/0000000062.fid"
+    path2 = "http://127.0.0.1:#{t2.port}/dev2/0/000/000/0000000062.fid"
 
     @backend.get_paths = { 'paths' => 2, 'path1' => path1, 'path2' => path2 }
 
     assert_equal 'data!', @client.get_file_data('key')
+    assert_equal 1, accept_nr
+    ensure
+      TempServer.destroy_all!
   end
 
   def test_get_file_data_http_block
@@ -43,11 +55,22 @@ class TestMogileFS__MogileFS < TestMogileFS
     nr.times { assert_equal chunk_size, tmpfp.syswrite(' ' * chunk_size) }
     assert_equal expect_size + header.size, File.size(tmpfp.path)
     tmpfp.sysseek(0)
-    socket = FakeSocket.new(tmpfp)
-    TCPSocket.sockets << socket
 
-    path1 = 'http://rur-1/dev1/0/000/000/0000000062.fid'
-    path2 = 'http://rur-2/dev2/0/000/000/0000000062.fid'
+    accept_nr = 0
+    svr = Proc.new do |serv, port|
+      client, client_addr = serv.accept
+      client.sync = true
+      accept_nr += 1
+      readed = client.recv(4096, 0)
+      assert(readed =~ \
+            %r{\AGET /dev[12]/0/000/000/0000000062\.fid HTTP/1.[01]\r\n\r\n\Z})
+      syswrloop(tmpfp, client)
+      client.close
+    end
+    t1 = TempServer.new(svr)
+    t2 = TempServer.new(svr)
+    path1 = "http://127.0.0.1:#{t1.port}/dev1/0/000/000/0000000062.fid"
+    path2 = "http://127.0.0.1:#{t2.port}/dev2/0/000/000/0000000062.fid"
 
     @backend.get_paths = { 'paths' => 2, 'path1' => path1, 'path2' => path2 }
 
@@ -66,6 +89,7 @@ class TestMogileFS__MogileFS < TestMogileFS
         end
       end
       assert_equal expect_size, nr, "size mismatch"
+      assert_equal 1, accept_nr
     end
   end
 
@@ -136,12 +160,22 @@ class TestMogileFS__MogileFS < TestMogileFS
     @backend.list_keys = { 'key_count' => 2, 'next_after' => 'new_key_2',
                            'key_1' => 'new_key_1', 'key_2' => 'new_key_2' }
     http_resp = "HTTP/1.0 200 OK\r\nContent-Length: %u\r\n"
-    TCPSocket.sockets << FakeSocket.new(http_resp % 10)
-    TCPSocket.sockets << FakeSocket.new(http_resp % 5)
-
-    @backend.get_paths = { 'paths' => 2, 'path1' => 'http://a',
-                           'path2' => 'http://b' }
-    @backend.get_paths = { 'paths' => 1, 'path1' => 'http://c' }
+    srv = Proc.new do |serv, port, size|
+      client, client_addr = serv.accept
+      client.sync = true
+      readed = client.readpartial(4096)
+      assert %r{\AHEAD } =~ readed
+      client.send(http_resp % size, 0)
+      client.close
+    end
+    t1 = TempServer.new(Proc.new { |serv, port| srv.call(serv, port, 5) })
+    t2 = TempServer.new(Proc.new { |serv, port| srv.call(serv, port, 5) })
+    t3 = TempServer.new(Proc.new { |serv, port| srv.call(serv, port, 10) })
+    @backend.get_paths = { 'paths' => 2,
+                           'path1' => "http://127.0.0.1:#{t1.port}/",
+                           'path2' => "http://127.0.0.1:#{t2.port}/" }
+    @backend.get_paths = { 'paths' => 1,
+                           'path1' => "http://127.0.0.1:#{t3.port}/" }
 
     res = []
     keys, next_after = @client.list_keys('new') do |key,length,devcount|
@@ -152,6 +186,8 @@ class TestMogileFS__MogileFS < TestMogileFS
     assert_equal expect_res, res
     assert_equal ['new_key_1', 'new_key_2'], keys.sort
     assert_equal 'new_key_2', next_after
+    ensure
+      TempServer.destroy_all!
   end
 
   def test_new_file_http
@@ -169,25 +205,22 @@ class TestMogileFS__MogileFS < TestMogileFS
   end
 
   def test_size_http
-    socket = FakeSocket.new <<-EOF
-HTTP/1.0 200 OK\r
-Content-Length: 5\r
-    EOF
-
-    TCPSocket.sockets << socket
-
-    path = 'http://example.com/path'
-
+    accept_nr = 0
+    t = TempServer.new(Proc.new do |serv,port|
+      client, client_addr = serv.accept
+      client.sync = true
+      readed = client.recv(4096, 0) rescue nil
+      assert_equal "HEAD /path HTTP/1.0\r\n\r\n", readed
+      client.send("HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\n", 0)
+      accept_nr += 1
+      client.close
+    end)
+
+    path = "http://127.0.0.1:#{t.port}/path"
     @backend.get_paths = { 'paths' => 1, 'path1' => path }
 
     assert_equal 5, @client.size('key')
-
-    socket.write_s.rewind
-
-    assert_equal "HEAD /path HTTP/1.0\r\n", socket.write_s.gets
-
-    assert_equal ['example.com', 80], TCPSocket.connections.shift
-    assert_empty TCPSocket.connections
+    assert_equal 1, accept_nr
   end
 
   def test_size_nfs
@@ -201,36 +234,41 @@ Content-Length: 5\r
   end
 
   def test_store_content_http
-    socket = FakeSocket.new 'HTTP/1.0 200 OK'
+    received = ''
+    expected = "PUT /path HTTP/1.0\r\nContent-Length: 4\r\n\r\ndata"
 
-    TCPSocket.sockets << socket
+    t = TempServer.new(Proc.new do |serv, accept|
+      client, client_addr = serv.accept
+      client.sync = true
+      received = client.recv(4096, 0)
+      client.send("HTTP/1.0 200 OK\r\n\r\n", 0)
+      client.close
+    end)
 
     @backend.create_open = {
       'devid' => '1',
-      'path' => 'http://example.com/path',
+      'path' => "http://127.0.0.1:#{t.port}/path",
     }
 
     @client.store_content 'new_key', 'test', 'data'
 
-    expected = <<-EOF.chomp
-PUT /path HTTP/1.0\r
-Content-Length: 4\r
-\r
-data
-    EOF
-
-    assert_equal expected, socket.write_s.string
-
-    assert_equal ['example.com', 80], TCPSocket.connections.shift
-    assert_empty TCPSocket.connections
+    assert_equal expected, received
+    ensure
+      TempServer.destroy_all!
   end
 
   def test_store_content_http_fail
-    TCPSocket.sockets << FakeSocket.new('HTTP/1.0 500 Internal Server Error')
+    t = TempServer.new(Proc.new do |serv, accept|
+      client, client_addr = serv.accept
+      client.sync = true
+      client.recv(4096, 0)
+      client.send("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0)
+      client.close
+    end)
 
     @backend.create_open = {
       'devid' => '1',
-      'path' => 'http://example.com/path',
+      'path' => "http://127.0.0.1:#{t.port}/path",
     }
 
     assert_raises MogileFS::HTTPFile::BadResponseError do
@@ -239,27 +277,23 @@ data
   end
 
   def test_store_content_http_empty
-    socket = FakeSocket.new 'HTTP/1.0 200 OK'
-
-    TCPSocket.sockets << socket
+    received = ''
+    expected = "PUT /path HTTP/1.0\r\nContent-Length: 0\r\n\r\n"
+    t = TempServer.new(Proc.new do |serv, accept|
+      client, client_addr = serv.accept
+      client.sync = true
+      received = client.recv(4096, 0)
+      client.send("HTTP/1.0 200 OK\r\n\r\n", 0)
+      client.close
+    end)
 
     @backend.create_open = {
       'devid' => '1',
-      'path' => 'http://example.com/path',
+      'path' => "http://127.0.0.1:#{t.port}/path",
     }
 
     @client.store_content 'new_key', 'test', ''
-
-    expected = <<-EOF
-PUT /path HTTP/1.0\r
-Content-Length: 0\r
-\r
-    EOF
-
-    assert_equal expected, socket.write_s.string
-
-    assert_equal ['example.com', 80], TCPSocket.connections.shift
-    assert_empty TCPSocket.connections
+    assert_equal expected, received
   end
 
   def test_store_content_nfs
diff --git a/test/test_utils.rb b/test/test_utils.rb
index 3f8819c..146d420 100644
--- a/test/test_utils.rb
+++ b/test/test_utils.rb
@@ -1,7 +1,4 @@
-require 'test/unit'
-require 'pp'
-require 'socket'
-$TESTING = true
+require 'test/setup'
 require 'mogilefs'
 require 'mogilefs/util'
 
@@ -9,45 +6,22 @@ class TestUtils < Test::Unit::TestCase
   include MogileFS::Util
 
   def test_verify_uris
-    good_serv, good_port = server_start
-    good_acceptor = Thread.new do
-      good_client, good_client_addr = good_serv.accept
-      good_client.readpartial(4096)
-      good_client.syswrite("HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n")
-    end
-    bad_serv, bad_port = server_start
-    bad_acceptor = Thread.new do
-      bad_client, bad_client_addr = bad_serv.accept
-      bad_client.close rescue nil
-    end
+    good = TempServer.new(Proc.new do |serv,port|
+      client,client_addr = serv.accept
+      client.readpartial(4096)
+      client.syswrite("HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n")
+    end)
+    bad = TempServer.new(Proc.new do |serv,port|
+      client, client_addr = serv.accept
+      client.close rescue nil
+    end)
 
-    good_uri = URI.parse("http://127.0.0.1:#{good_port}/")
-    bad_uri = URI.parse("http://127.0.0.1:#{bad_port}/")
+    good_uri = URI.parse("http://127.0.0.1:#{good.port}/")
+    bad_uri = URI.parse("http://127.0.0.1:#{bad.port}/")
     ok = verify_uris([ good_uri, bad_uri ])
     assert_equal [ good_uri ], ok
     ensure
-      Thread.kill(good_acceptor) rescue nil
-      Thread.kill(bad_acceptor) rescue nil
-      good_serv.close rescue nil
-      bad_serv.close rescue nil
+      TempServer.destroy_all!
   end
 
-  private
-
-    def server_start
-      port = nil
-      sock = nil
-      retries = 0
-      begin
-        port = 5000 + $$ % 1000 + rand(60000)
-        sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
-        sock.bind(Socket.pack_sockaddr_in(port, '127.0.0.1'))
-        sock.listen(5)
-      rescue Errno::EADDRINUSE
-        sock.close rescue nil
-        retry if (retries += 1) < 10
-      end
-      [ sock, port ]
-    end
-
 end