pygo/gomill/gomill_tests/gtp_proxy_tests.py

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()