Project framework + socket connector

The socket connector is responsible for reading from a socket and
converting the lines that were read to messages, using a passed-in
deserialiser. It also provides an interface to push messages onto
the socket, via a serialiser which is also passed in.
This commit is contained in:
Nick Thomas
2011-11-12 21:45:14 +00:00
commit 1688e55459
6 changed files with 380 additions and 0 deletions

35
test/unit/helper.rb Normal file
View File

@@ -0,0 +1,35 @@
ROOT = File.expand_path(File.join(File.basename(__FILE__), '..'))
$: << File.join(ROOT, 'lib')
require 'minitest/unit'
require 'minitest/autorun'
require 'mocha'
require 'timeout' # assert_doesnt_time_out
module EnvHelpers
def silence_warnings
old = $VERBOSE
$VERBOSE = nil
yield
ensure
$VERBOSE = old
end
end
class BaseTestCase < MiniTest::Unit::TestCase
include EnvHelpers
def assert_doesnt_time_out(n, reason = "", &blk)
Timeout::timeout(n, &blk)
rescue TimeoutError => err
r_full = [reason, "(timed out after #{n}s)", "\n" + err.backtrace.join("\n")].join(" ")
raise MiniTest::Assertion.new(r_full)
end
end
class QMPClientTestCase < BaseTestCase
end

View File

@@ -0,0 +1,125 @@
require 'helper'
require 'qmp_client/connectors/socket'
module TestQMPClient
module TestConnectors
class TestSocket < QMPClientTestCase
def setup
@serialiser = mock("(serialiser)")
@deserialiser = mock("(deserialiser)")
@connector = QMPClient::Connectors::Socket.new(@serialiser, @deserialiser)
@msg_received_q = Queue.new
@connector.register_receive_queue(@msg_received_q)
@read_r, @read_w = IO.pipe
@write_r, @write_w = IO.pipe
end
def teardown
[@read_r, @read_w, @write_r, @write_w].each {|s| s.close unless s.closed? }
super
end
def with_connector(reason="Run block", t=1, &blk)
assert_doesnt_time_out(t, reason) do
@connector.run(@read_r, @write_w) do |writer|
@write_interface = writer
yield @connector, writer
end
end
end
def message_from_server(symbol)
@deserialiser.expects(:deserialise).with(symbol.to_s).once.
returns(symbol)
@read_w.puts symbol.to_s
end
def assert_server_saw(symbol)
assert_doesnt_time_out(1, "Checking message #{symbol.inspect} was sent to server") do
assert_equal(symbol.to_s + "\n", @write_r.gets)
end
end
def message_from_client(symbol)
@serialiser.expects(:serialise).with(symbol).once.returns(symbol.to_s)
@write_interface.push(symbol)
end
def assert_client_saw(symbol, discard = 0)
assert_doesnt_time_out(1, "checking message #{symbol.inspect} was received from server") do
discard.times { @msg_received_q.pop }
assert_equal(symbol, @msg_received_q.pop)
end
end
def test_run_fires_up_read_and_write_sides_and_operates_correctly
with_connector do
message_from_client(:test_1)
assert_server_saw(:test_1)
message_from_server(:test_2)
assert_client_saw(:test_2)
end
# After shutting down like this, all our sockets should be open
refute(@read_r.closed?, "read side of read socket closed")
refute(@read_w.closed?, "write side of read socket closed")
refute(@write_r.closed?, "read side of write socket closed")
refute(@write_w.closed?, "write side of write socket closed")
end
def test_writes_are_threadsafe
with_connector do |connector, writer|
(1..5).each {|n| Thread.new { message_from_client(:"Message #{n}")}}
data = (1..5).collect {|n| @write_r.gets }
assert_equal(
["Message 1\n", "Message 2\n", "Message 3\n", "Message 4\n", "Message 5\n"],
data.sort
)
end
end
def test_register_unregister_receive_queue
with_connector do |connector, writer|
rq1 = Queue.new # We expect both of these to get both messages
rq2 = Queue.new
urq = Queue.new # We're going to unregister this one after one msg
connector.register_receive_queue(rq1)
connector.register_receive_queue(rq2)
connector.register_receive_queue(urq)
message_from_server(:test_1)
assert_client_saw(:test_1)
connector.unregister_receive_queue(urq)
message_from_server(:test_2)
assert_client_saw(:test_2)
[rq1, rq2].each do |q|
assert_equal(2, q.size, "receive q missed a message")
assert_equal(:test_1, q.pop)
assert_equal(:test_2, q.pop)
end
assert_equal(1, urq.size, "Unregister queue has wrong # of messages")
assert_equal(:test_1, urq.pop, "Unregister queue has wrong message")
end
end
# This closes the socket before Kernel#select can get to it
def test_closing_socket_finishes_run_block_cleanly
with_connector { @read_r.close }
end
def test_run_block_finishes_cleanly_if_socket_reports_error
with_connector do |connector, writer|
Kernel.expects(:select).once.returns([[], [], [@read_r]])
end
end
end
end
end