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:
35
test/unit/helper.rb
Normal file
35
test/unit/helper.rb
Normal 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
|
125
test/unit/qmp_client/connectors/test_socket.rb
Normal file
125
test/unit/qmp_client/connectors/test_socket.rb
Normal 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
|
Reference in New Issue
Block a user