243 lines
9.4 KiB
Python
243 lines
9.4 KiB
Python
"""Tests for gtp_proxy.py"""
|
|
|
|
from __future__ import with_statement
|
|
|
|
from gomill import gtp_controller
|
|
from gomill import gtp_proxy
|
|
from gomill.gtp_engine import GtpError, GtpFatalError
|
|
from gomill.gtp_controller import (
|
|
GtpChannelError, GtpProtocolError, GtpTransportError, GtpChannelClosed,
|
|
BadGtpResponse, Gtp_controller)
|
|
from gomill.gtp_proxy import BackEndError
|
|
|
|
from gomill_tests import test_framework
|
|
from gomill_tests import gomill_test_support
|
|
from gomill_tests import gtp_controller_test_support
|
|
from gomill_tests import gtp_engine_fixtures
|
|
from gomill_tests import gtp_engine_test_support
|
|
|
|
def make_tests(suite):
|
|
suite.addTests(gomill_test_support.make_simple_tests(globals()))
|
|
|
|
|
|
class Proxy_fixture(test_framework.Fixture):
|
|
"""Fixture managing a Gtp_proxy with the test engine as its back-end.
|
|
|
|
attributes:
|
|
proxy -- Gtp_proxy
|
|
controller -- Gtp_controller
|
|
channel -- Testing_gtp_channel (like get_test_channel())
|
|
engine -- the proxy engine
|
|
underlying_engine -- the underlying test engine (like get_test_engine())
|
|
commands_handled -- from the underlying Test_gtp_engine_protocol
|
|
|
|
"""
|
|
def __init__(self, tc):
|
|
self.tc = tc
|
|
self.channel = gtp_engine_fixtures.get_test_channel()
|
|
self.underlying_engine = self.channel.engine
|
|
self.controller = gtp_controller.Gtp_controller(
|
|
self.channel, 'testbackend')
|
|
self.proxy = gtp_proxy.Gtp_proxy()
|
|
self.proxy.set_back_end_controller(self.controller)
|
|
self.engine = self.proxy.engine
|
|
self.commands_handled = self.underlying_engine.commands_handled
|
|
|
|
def check_command(self, *args, **kwargs):
|
|
"""Send a command to the proxy engine and check its response.
|
|
|
|
(This is testing the proxy engine, not the underlying engine.)
|
|
|
|
parameters as for gtp_engine_test_support.check_engine()
|
|
|
|
"""
|
|
gtp_engine_test_support.check_engine(
|
|
self.tc, self.engine, *args, **kwargs)
|
|
|
|
|
|
def test_proxy(tc):
|
|
fx = Proxy_fixture(tc)
|
|
fx.check_command('test', ['ab', 'cd'], "args: ab cd")
|
|
fx.proxy.close()
|
|
tc.assertEqual(
|
|
fx.commands_handled,
|
|
[('list_commands', []), ('test', ['ab', 'cd']), ('quit', [])])
|
|
tc.assertTrue(fx.controller.channel.is_closed)
|
|
|
|
def test_close_after_quit(tc):
|
|
fx = Proxy_fixture(tc)
|
|
fx.check_command('quit', [], "", expect_end=True)
|
|
fx.proxy.close()
|
|
tc.assertEqual(
|
|
fx.commands_handled,
|
|
[('list_commands', []), ('quit', [])])
|
|
tc.assertTrue(fx.channel.is_closed)
|
|
|
|
def test_list_commands(tc):
|
|
fx = Proxy_fixture(tc)
|
|
tc.assertListEqual(
|
|
fx.engine.list_commands(),
|
|
['error', 'fatal', 'gomill-passthrough', 'known_command',
|
|
'list_commands', 'multiline', 'protocol_version', 'quit', 'test'])
|
|
fx.proxy.close()
|
|
|
|
def test_back_end_has_command(tc):
|
|
fx = Proxy_fixture(tc)
|
|
tc.assertTrue(fx.proxy.back_end_has_command('test'))
|
|
tc.assertFalse(fx.proxy.back_end_has_command('xyzzy'))
|
|
tc.assertFalse(fx.proxy.back_end_has_command('gomill-passthrough'))
|
|
|
|
def test_passthrough(tc):
|
|
fx = Proxy_fixture(tc)
|
|
fx.check_command('known_command', ['gomill-passthrough'], "true")
|
|
fx.check_command('gomill-passthrough', ['test', 'ab', 'cd'], "args: ab cd")
|
|
fx.check_command(
|
|
'gomill-passthrough', ['known_command', 'gomill-passthrough'], "false")
|
|
fx.check_command('gomill-passthrough', [],
|
|
"invalid arguments", expect_failure=True)
|
|
tc.assertEqual(
|
|
fx.commands_handled,
|
|
[('list_commands', []), ('test', ['ab', 'cd']),
|
|
('known_command', ['gomill-passthrough'])])
|
|
|
|
def test_pass_command(tc):
|
|
fx = Proxy_fixture(tc)
|
|
tc.assertEqual(fx.proxy.pass_command("test", ["ab", "cd"]), "args: ab cd")
|
|
with tc.assertRaises(BadGtpResponse) as ar:
|
|
fx.proxy.pass_command("error", [])
|
|
tc.assertEqual(ar.exception.gtp_error_message, "normal error")
|
|
tc.assertEqual(str(ar.exception),
|
|
"failure response from 'error' to testbackend:\n"
|
|
"normal error")
|
|
|
|
def test_pass_command_with_channel_error(tc):
|
|
fx = Proxy_fixture(tc)
|
|
fx.channel.fail_next_command = True
|
|
with tc.assertRaises(BackEndError) as ar:
|
|
fx.proxy.pass_command("test", [])
|
|
tc.assertEqual(str(ar.exception),
|
|
"transport error sending 'test' to testbackend:\n"
|
|
"forced failure for send_command_line")
|
|
tc.assertIsInstance(ar.exception.cause, GtpTransportError)
|
|
fx.proxy.close()
|
|
tc.assertEqual(fx.commands_handled, [('list_commands', [])])
|
|
|
|
def test_handle_command(tc):
|
|
def handle_xyzzy(args):
|
|
if args and args[0] == "error":
|
|
return fx.proxy.handle_command("error", [])
|
|
else:
|
|
return fx.proxy.handle_command("test", ["nothing", "happens"])
|
|
fx = Proxy_fixture(tc)
|
|
fx.engine.add_command("xyzzy", handle_xyzzy)
|
|
fx.check_command('xyzzy', [], "args: nothing happens")
|
|
fx.check_command('xyzzy', ['error'],
|
|
"normal error", expect_failure=True)
|
|
|
|
def test_handle_command_with_channel_error(tc):
|
|
def handle_xyzzy(args):
|
|
return fx.proxy.handle_command("test", [])
|
|
fx = Proxy_fixture(tc)
|
|
fx.engine.add_command("xyzzy", handle_xyzzy)
|
|
fx.channel.fail_next_command = True
|
|
fx.check_command('xyzzy', [],
|
|
"transport error sending 'test' to testbackend:\n"
|
|
"forced failure for send_command_line",
|
|
expect_failure=True, expect_end=True)
|
|
fx.proxy.close()
|
|
tc.assertEqual(fx.commands_handled, [('list_commands', [])])
|
|
|
|
def test_back_end_goes_away(tc):
|
|
fx = Proxy_fixture(tc)
|
|
tc.assertEqual(fx.proxy.pass_command("quit", []), "")
|
|
fx.check_command('test', ['ab', 'cd'],
|
|
"error sending 'test ab cd' to testbackend:\n"
|
|
"engine has closed the command channel",
|
|
expect_failure=True, expect_end=True)
|
|
|
|
def test_close_with_errors(tc):
|
|
fx = Proxy_fixture(tc)
|
|
fx.channel.fail_next_command = True
|
|
with tc.assertRaises(BackEndError) as ar:
|
|
fx.proxy.close()
|
|
tc.assertEqual(str(ar.exception),
|
|
"transport error sending 'quit' to testbackend:\n"
|
|
"forced failure for send_command_line")
|
|
tc.assertTrue(fx.channel.is_closed)
|
|
|
|
def test_quit_ignores_already_closed(tc):
|
|
fx = Proxy_fixture(tc)
|
|
tc.assertEqual(fx.proxy.pass_command("quit", []), "")
|
|
fx.check_command('quit', [], "", expect_end=True)
|
|
fx.proxy.close()
|
|
tc.assertEqual(fx.commands_handled,
|
|
[('list_commands', []), ('quit', [])])
|
|
|
|
def test_quit_with_failure_response(tc):
|
|
fx = Proxy_fixture(tc)
|
|
fx.underlying_engine.force_error("quit")
|
|
fx.check_command('quit', [], None,
|
|
expect_failure=True, expect_end=True)
|
|
fx.proxy.close()
|
|
tc.assertEqual(fx.commands_handled,
|
|
[('list_commands', []), ('quit', [])])
|
|
|
|
def test_quit_with_channel_error(tc):
|
|
fx = Proxy_fixture(tc)
|
|
fx.channel.fail_next_command = True
|
|
fx.check_command('quit', [],
|
|
"transport error sending 'quit' to testbackend:\n"
|
|
"forced failure for send_command_line",
|
|
expect_failure=True, expect_end=True)
|
|
fx.proxy.close()
|
|
tc.assertEqual(fx.commands_handled, [('list_commands', [])])
|
|
|
|
def test_nontgtp_backend(tc):
|
|
channel = gtp_controller_test_support.Preprogrammed_gtp_channel(
|
|
"Usage: randomprogram [options]\n\nOptions:\n"
|
|
"--help show this help message and exit\n")
|
|
controller = gtp_controller.Gtp_controller(channel, 'testbackend')
|
|
proxy = gtp_proxy.Gtp_proxy()
|
|
with tc.assertRaises(BackEndError) as ar:
|
|
proxy.set_back_end_controller(controller)
|
|
tc.assertEqual(str(ar.exception),
|
|
"GTP protocol error reading response to first command "
|
|
"(list_commands) from testbackend:\n"
|
|
"engine isn't speaking GTP: first byte is 'U'")
|
|
tc.assertIsInstance(ar.exception.cause, GtpProtocolError)
|
|
proxy.close()
|
|
|
|
def test_error_from_list_commands(tc):
|
|
channel = gtp_engine_fixtures.get_test_channel()
|
|
channel.engine.force_error("list_commands")
|
|
controller = gtp_controller.Gtp_controller(channel, 'testbackend')
|
|
proxy = gtp_proxy.Gtp_proxy()
|
|
with tc.assertRaises(BackEndError) as ar:
|
|
proxy.set_back_end_controller(controller)
|
|
tc.assertEqual(str(ar.exception),
|
|
"failure response from first command "
|
|
"(list_commands) to testbackend:\n"
|
|
"handler forced to fail")
|
|
tc.assertIsInstance(ar.exception.cause, BadGtpResponse)
|
|
proxy.close()
|
|
|
|
|
|
def test_set_back_end_subprocess(tc):
|
|
fx = gtp_engine_fixtures.State_reporter_fixture(tc)
|
|
proxy = gtp_proxy.Gtp_proxy()
|
|
# the state-report will be taken as the response to list_commands
|
|
proxy.set_back_end_subprocess(fx.cmd, stderr=fx.devnull)
|
|
proxy.expect_back_end_exit()
|
|
proxy.close()
|
|
|
|
def test_set_back_end_subprocess_nonexistent_program(tc):
|
|
proxy = gtp_proxy.Gtp_proxy()
|
|
with tc.assertRaises(BackEndError) as ar:
|
|
proxy.set_back_end_subprocess("/nonexistent/program")
|
|
tc.assertEqual(str(ar.exception),
|
|
"can't launch back end command\n"
|
|
"[Errno 2] No such file or directory")
|
|
tc.assertIsInstance(ar.exception.cause, GtpChannelError)
|
|
# check it's safe to close when the controller was never set
|
|
proxy.close()
|