459 lines
17 KiB
Python
459 lines
17 KiB
Python
|
"""Tests for game_jobs.py"""
|
||
|
|
||
|
from __future__ import with_statement
|
||
|
|
||
|
import os
|
||
|
from textwrap import dedent
|
||
|
|
||
|
from gomill import gtp_controller
|
||
|
from gomill import game_jobs
|
||
|
from gomill.gtp_engine import GtpError, GtpFatalError
|
||
|
from gomill.job_manager import JobFailed
|
||
|
|
||
|
from gomill_tests import test_framework
|
||
|
from gomill_tests import gomill_test_support
|
||
|
from gomill_tests import gtp_engine_fixtures
|
||
|
|
||
|
def make_tests(suite):
|
||
|
suite.addTests(gomill_test_support.make_simple_tests(globals()))
|
||
|
|
||
|
|
||
|
### Game_job proper
|
||
|
|
||
|
class Test_game_job(game_jobs.Game_job):
|
||
|
"""Variant of Game_job that doesn't write sgf files.
|
||
|
|
||
|
Additional attributes:
|
||
|
_sgf_pathname_written -- pathname sgf file would have been written to
|
||
|
_sgf_written -- contents that would have been written
|
||
|
_mkdir_pathname -- directory pathname that would have been created
|
||
|
|
||
|
"""
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
game_jobs.Game_job.__init__(self, *args, **kwargs)
|
||
|
self._sgf_pathname_written = None
|
||
|
self._sgf_written = None
|
||
|
self._mkdir_pathname = None
|
||
|
|
||
|
def _write_sgf(self, pathname, sgf_string):
|
||
|
self._sgf_pathname_written = pathname
|
||
|
self._sgf_written = sgf_string
|
||
|
|
||
|
def _mkdir(self, pathname):
|
||
|
self._mkdir_pathname = pathname
|
||
|
|
||
|
def _get_sgf_written(self):
|
||
|
"""Return the 'scrubbed' sgf contents."""
|
||
|
return gomill_test_support.scrub_sgf(self._sgf_written)
|
||
|
|
||
|
class Game_job_fixture(test_framework.Fixture):
|
||
|
"""Fixture setting up a Game_job.
|
||
|
|
||
|
attributes:
|
||
|
job -- game_jobs.Game_job (in fact, a Test_game_job)
|
||
|
|
||
|
"""
|
||
|
def __init__(self, tc):
|
||
|
player_b = game_jobs.Player()
|
||
|
player_b.code = 'one'
|
||
|
player_b.cmd_args = ['test', 'id=one']
|
||
|
player_w = game_jobs.Player()
|
||
|
player_w.code = 'two'
|
||
|
player_w.cmd_args = ['test', 'id=two']
|
||
|
self.job = Test_game_job()
|
||
|
self.job.game_id = 'gameid'
|
||
|
self.job.player_b = player_b
|
||
|
self.job.player_w = player_w
|
||
|
self.job.board_size = 9
|
||
|
self.job.komi = 7.5
|
||
|
self.job.move_limit = 1000
|
||
|
self.job.sgf_dirname = "/sgf/test.games"
|
||
|
self.job.void_sgf_dirname = "/sgf/test.void"
|
||
|
self.job.sgf_filename = "gjtest.sgf"
|
||
|
|
||
|
def test_player_copy(tc):
|
||
|
gj = Game_job_fixture(tc)
|
||
|
p1 = gj.job.player_b
|
||
|
p2 = p1.copy("clone")
|
||
|
tc.assertEqual(p2.code, "clone")
|
||
|
tc.assertEqual(p2.cmd_args, ['test', 'id=one'])
|
||
|
tc.assertIsNot(p1.cmd_args, p2.cmd_args)
|
||
|
|
||
|
def test_game_job(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.game_data = 'gamedata'
|
||
|
gj.job.sgf_game_name = "gjt 0_000"
|
||
|
gj.job.sgf_event = "game_job_tests"
|
||
|
gj.job.sgf_note = "test sgf_note\non two lines"
|
||
|
result = gj.job.run()
|
||
|
# Win by 18 on the board minus 7.5 komi
|
||
|
tc.assertEqual(result.game_result.sgf_result, "B+10.5")
|
||
|
tc.assertEqual(result.game_id, 'gameid')
|
||
|
tc.assertEqual(result.game_result.game_id, 'gameid')
|
||
|
tc.assertEqual(result.game_data, 'gamedata')
|
||
|
tc.assertEqual(result.warnings, [])
|
||
|
tc.assertEqual(result.log_entries, [])
|
||
|
channel = fx.get_channel('one')
|
||
|
tc.assertIsNone(channel.requested_stderr)
|
||
|
tc.assertIsNone(channel.requested_cwd)
|
||
|
tc.assertIsNone(channel.requested_env)
|
||
|
tc.assertEqual(gj.job._sgf_pathname_written, '/sgf/test.games/gjtest.sgf')
|
||
|
tc.assertIsNone(gj.job._mkdir_pathname)
|
||
|
tc.assertMultiLineEqual(gj.job._get_sgf_written(), dedent("""\
|
||
|
(;FF[4]AP[gomill:VER]
|
||
|
C[Event: game_job_tests
|
||
|
Game id gameid
|
||
|
Date ***
|
||
|
Result one beat two B+10.5
|
||
|
test sgf_note
|
||
|
on two lines
|
||
|
Black one one
|
||
|
White two two]
|
||
|
CA[UTF-8]DT[***]EV[game_job_tests]GM[1]GN[gjt 0_000]KM[7.5]PB[one]
|
||
|
PW[two]RE[B+10.5]SZ[9];B[ei];W[gi];B[eh];W[gh];B[eg];W[gg];B[ef];W[gf];B[ee];
|
||
|
W[ge];B[ed];W[gd];B[ec];W[gc];B[eb];W[gb];B[ea];W[ga];B[tt];
|
||
|
C[one beat two B+10.5]W[tt])
|
||
|
"""))
|
||
|
|
||
|
def test_duplicate_player_codes(tc):
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_w.code = "one"
|
||
|
tc.assertRaisesRegexp(
|
||
|
JobFailed, "error creating game: player codes must be distinct",
|
||
|
gj.job.run)
|
||
|
|
||
|
def test_game_job_no_sgf(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.sgf_dirname = None
|
||
|
result = gj.job.run()
|
||
|
tc.assertEqual(result.game_result.sgf_result, "B+10.5")
|
||
|
tc.assertIsNone(gj.job._sgf_pathname_written)
|
||
|
|
||
|
def test_game_job_forfeit(tc):
|
||
|
def handle_genmove(args):
|
||
|
raise GtpError("error")
|
||
|
def reject_genmove(channel):
|
||
|
channel.engine.add_command('genmove', handle_genmove)
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('reject_genmove', reject_genmove)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_w.cmd_args.append('init=reject_genmove')
|
||
|
result = gj.job.run()
|
||
|
tc.assertEqual(result.game_result.sgf_result, "B+F")
|
||
|
tc.assertEqual(
|
||
|
result.game_result.detail,
|
||
|
"forfeit: failure response from 'genmove w' to player two:\n"
|
||
|
"error")
|
||
|
tc.assertEqual(
|
||
|
result.warnings,
|
||
|
["forfeit: failure response from 'genmove w' to player two:\n"
|
||
|
"error"])
|
||
|
tc.assertEqual(result.log_entries, [])
|
||
|
tc.assertEqual(gj.job._sgf_pathname_written, '/sgf/test.games/gjtest.sgf')
|
||
|
|
||
|
def test_game_job_forfeit_and_quit(tc):
|
||
|
def handle_genmove(args):
|
||
|
raise GtpFatalError("I'm out of here")
|
||
|
def reject_genmove(channel):
|
||
|
channel.engine.add_command('genmove', handle_genmove)
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('reject_genmove', reject_genmove)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_w.cmd_args.append('init=reject_genmove')
|
||
|
result = gj.job.run()
|
||
|
tc.assertEqual(result.game_result.sgf_result, "B+F")
|
||
|
tc.assertEqual(
|
||
|
result.game_result.detail,
|
||
|
"forfeit: failure response from 'genmove w' to player two:\n"
|
||
|
"I'm out of here")
|
||
|
tc.assertEqual(
|
||
|
result.warnings,
|
||
|
["forfeit: failure response from 'genmove w' to player two:\n"
|
||
|
"I'm out of here"])
|
||
|
tc.assertEqual(
|
||
|
result.log_entries,
|
||
|
["error sending 'known_command gomill-cpu_time' to player two:\n"
|
||
|
"engine has closed the command channel"])
|
||
|
tc.assertEqual(gj.job._sgf_pathname_written, '/sgf/test.games/gjtest.sgf')
|
||
|
|
||
|
def test_game_job_exec_failure(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_w.cmd_args.append('fail=startup')
|
||
|
with tc.assertRaises(JobFailed) as ar:
|
||
|
gj.job.run()
|
||
|
tc.assertEqual(str(ar.exception),
|
||
|
"aborting game due to error:\n"
|
||
|
"error starting subprocess for player two:\n"
|
||
|
"exec forced to fail")
|
||
|
# No void sgf file unless at least one move was played
|
||
|
tc.assertIsNone(gj.job._sgf_pathname_written)
|
||
|
|
||
|
def test_game_job_channel_error(tc):
|
||
|
def fail_first_genmove(channel):
|
||
|
channel.fail_command = 'genmove'
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('fail_first_genmove', fail_first_genmove)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_w.cmd_args.append('init=fail_first_genmove')
|
||
|
with tc.assertRaises(JobFailed) as ar:
|
||
|
gj.job.run()
|
||
|
tc.assertEqual(str(ar.exception),
|
||
|
"aborting game due to error:\n"
|
||
|
"transport error sending 'genmove w' to player two:\n"
|
||
|
"forced failure for send_command_line")
|
||
|
tc.assertEqual(gj.job._sgf_pathname_written, '/sgf/test.void/gjtest.sgf')
|
||
|
tc.assertEqual(gj.job._mkdir_pathname, '/sgf/test.void')
|
||
|
tc.assertMultiLineEqual(gj.job._get_sgf_written(), dedent("""\
|
||
|
(;FF[4]AP[gomill:VER]
|
||
|
C[Game id gameid
|
||
|
Date ***
|
||
|
Black one one
|
||
|
White two two]CA[UTF-8]
|
||
|
DT[***]GM[1]GN[gameid]KM[7.5]PB[one]PW[two]RE[Void]SZ[9];B[ei]
|
||
|
C[aborting game due to error:
|
||
|
transport error sending 'genmove w' to player two:
|
||
|
forced failure for send_command_line]
|
||
|
)
|
||
|
"""))
|
||
|
|
||
|
def test_game_job_late_errors(tc):
|
||
|
def fail_close(channel):
|
||
|
channel.fail_close = True
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('fail_close', fail_close)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_w.cmd_args.append('init=fail_close')
|
||
|
result = gj.job.run()
|
||
|
tc.assertEqual(result.game_result.sgf_result, "B+10.5")
|
||
|
tc.assertEqual(result.warnings, [])
|
||
|
tc.assertEqual(result.log_entries,
|
||
|
["error closing player two:\nforced failure for close"])
|
||
|
|
||
|
def test_game_job_late_error_from_void_game(tc):
|
||
|
def fail_genmove_and_close(channel):
|
||
|
channel.fail_command = 'genmove'
|
||
|
channel.fail_close = True
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('fail_genmove_and_close', fail_genmove_and_close)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_w.cmd_args.append('init=fail_genmove_and_close')
|
||
|
with tc.assertRaises(JobFailed) as ar:
|
||
|
gj.job.run()
|
||
|
tc.assertMultiLineEqual(
|
||
|
str(ar.exception),
|
||
|
"aborting game due to error:\n"
|
||
|
"transport error sending 'genmove w' to player two:\n"
|
||
|
"forced failure for send_command_line\n"
|
||
|
"also:\n"
|
||
|
"error closing player two:\n"
|
||
|
"forced failure for close")
|
||
|
tc.assertEqual(gj.job._sgf_pathname_written, '/sgf/test.void/gjtest.sgf')
|
||
|
|
||
|
def test_game_job_cwd_env(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_b.cwd = "/nonexistent_directory"
|
||
|
gj.job.player_b.environ = {'GOMILL_TEST' : 'gomill'}
|
||
|
result = gj.job.run()
|
||
|
channel = fx.get_channel('one')
|
||
|
tc.assertIsNone(channel.requested_stderr)
|
||
|
tc.assertEqual(channel.requested_cwd, "/nonexistent_directory")
|
||
|
tc.assertEqual(channel.requested_env['GOMILL_TEST'], 'gomill')
|
||
|
# Check environment was merged, not replaced
|
||
|
tc.assertIn('PATH', channel.requested_env)
|
||
|
tc.assertEqual(gj.job._sgf_pathname_written, '/sgf/test.games/gjtest.sgf')
|
||
|
|
||
|
def test_game_job_stderr_discarded(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_b.discard_stderr = True
|
||
|
result = gj.job.run()
|
||
|
channel = fx.get_channel('one')
|
||
|
tc.assertIsInstance(channel.requested_stderr, file)
|
||
|
tc.assertEqual(channel.requested_stderr.name, os.devnull)
|
||
|
|
||
|
def test_game_job_stderr_set(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.stderr_pathname = "/dev/full"
|
||
|
result = gj.job.run()
|
||
|
channel = fx.get_channel('one')
|
||
|
tc.assertIsInstance(channel.requested_stderr, file)
|
||
|
tc.assertEqual(channel.requested_stderr.name, "/dev/full")
|
||
|
|
||
|
def test_game_job_stderr_set_and_discarded(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_b.discard_stderr = True
|
||
|
result = gj.job.run()
|
||
|
channel = fx.get_channel('one')
|
||
|
tc.assertIsInstance(channel.requested_stderr, file)
|
||
|
tc.assertEqual(channel.requested_stderr.name, os.devnull)
|
||
|
|
||
|
def test_game_job_gtp_aliases(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_w.gtp_aliases = {'genmove': 'fail'}
|
||
|
result = gj.job.run()
|
||
|
tc.assertEqual(result.game_result.sgf_result, "B+F")
|
||
|
|
||
|
def test_game_job_claim(tc):
|
||
|
def handle_genmove_ex(args):
|
||
|
tc.assertIn('claim', args)
|
||
|
return "claim"
|
||
|
def register_genmove_ex(channel):
|
||
|
channel.engine.add_command('gomill-genmove_ex', handle_genmove_ex)
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('genmove_ex', register_genmove_ex)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_b.cmd_args.append('init=genmove_ex')
|
||
|
gj.job.player_w.cmd_args.append('init=genmove_ex')
|
||
|
gj.job.player_w.allow_claim = True
|
||
|
result = gj.job.run()
|
||
|
tc.assertEqual(result.game_result.sgf_result, "W+")
|
||
|
tc.assertEqual(gj.job._sgf_pathname_written, '/sgf/test.games/gjtest.sgf')
|
||
|
|
||
|
def test_game_job_handicap(tc):
|
||
|
def handle_fixed_handicap(args):
|
||
|
return "D4 K10 D10"
|
||
|
def register_fixed_handicap(channel):
|
||
|
channel.engine.add_command('fixed_handicap', handle_fixed_handicap)
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('handicap', register_fixed_handicap)
|
||
|
gj = Game_job_fixture(tc)
|
||
|
gj.job.player_b.cmd_args.append('init=handicap')
|
||
|
gj.job.player_w.cmd_args.append('init=handicap')
|
||
|
gj.job.board_size = 13
|
||
|
gj.job.handicap = 3
|
||
|
gj.job.handicap_is_free = False
|
||
|
gj.job.internal_scorer_handicap_compensation = 'full'
|
||
|
result = gj.job.run()
|
||
|
# area score 53, less 7.5 komi, less 3 handicap compensation
|
||
|
tc.assertEqual(result.game_result.sgf_result, "B+42.5")
|
||
|
|
||
|
|
||
|
|
||
|
### check_player
|
||
|
|
||
|
class Player_check_fixture(test_framework.Fixture):
|
||
|
"""Fixture setting up a Player_check.
|
||
|
|
||
|
attributes:
|
||
|
player -- game_jobs.Player
|
||
|
check -- game_jobs.Player_check
|
||
|
|
||
|
"""
|
||
|
def __init__(self, tc):
|
||
|
self.player = game_jobs.Player()
|
||
|
self.player.code = 'test'
|
||
|
self.player.cmd_args = ['test', 'id=test']
|
||
|
self.check = game_jobs.Player_check()
|
||
|
self.check.player = self.player
|
||
|
self.check.board_size = 9
|
||
|
self.check.komi = 7.0
|
||
|
|
||
|
def test_check_player(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
tc.assertEqual(game_jobs.check_player(ck.check), [])
|
||
|
channel = fx.get_channel('test')
|
||
|
tc.assertIsNone(channel.requested_stderr)
|
||
|
tc.assertIsNone(channel.requested_cwd)
|
||
|
tc.assertIsNone(channel.requested_env)
|
||
|
|
||
|
def test_check_player_discard_stderr(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
tc.assertEqual(game_jobs.check_player(ck.check, discard_stderr=True), [])
|
||
|
channel = fx.get_channel('test')
|
||
|
tc.assertIsInstance(channel.requested_stderr, file)
|
||
|
tc.assertEqual(channel.requested_stderr.name, os.devnull)
|
||
|
|
||
|
def test_check_player_boardsize_fails(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
engine = gtp_engine_fixtures.get_test_engine()
|
||
|
fx.register_engine('no_boardsize', engine)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
ck.player.cmd_args.append('engine=no_boardsize')
|
||
|
with tc.assertRaises(game_jobs.CheckFailed) as ar:
|
||
|
game_jobs.check_player(ck.check)
|
||
|
tc.assertEqual(str(ar.exception),
|
||
|
"failure response from 'boardsize 9' to test:\n"
|
||
|
"unknown command")
|
||
|
|
||
|
def test_check_player_startup_gtp_commands(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
ck.player.startup_gtp_commands = [('list_commands', []),
|
||
|
('nonexistent', ['command'])]
|
||
|
with tc.assertRaises(game_jobs.CheckFailed) as ar:
|
||
|
game_jobs.check_player(ck.check)
|
||
|
tc.assertEqual(str(ar.exception),
|
||
|
"failure response from 'nonexistent command' to test:\n"
|
||
|
"unknown command")
|
||
|
|
||
|
def test_check_player_nonexistent_cwd(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
ck.player.cwd = "/nonexistent/directory"
|
||
|
with tc.assertRaises(game_jobs.CheckFailed) as ar:
|
||
|
game_jobs.check_player(ck.check)
|
||
|
tc.assertEqual(str(ar.exception),
|
||
|
"bad working directory: /nonexistent/directory")
|
||
|
|
||
|
def test_check_player_cwd(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
ck.player.cwd = "/"
|
||
|
tc.assertEqual(game_jobs.check_player(ck.check), [])
|
||
|
channel = fx.get_channel('test')
|
||
|
tc.assertEqual(channel.requested_cwd, "/")
|
||
|
|
||
|
def test_check_player_env(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
ck.player.environ = {'GOMILL_TEST' : 'gomill'}
|
||
|
tc.assertEqual(game_jobs.check_player(ck.check), [])
|
||
|
channel = fx.get_channel('test')
|
||
|
tc.assertEqual(channel.requested_env['GOMILL_TEST'], 'gomill')
|
||
|
# Check environment was merged, not replaced
|
||
|
tc.assertIn('PATH', channel.requested_env)
|
||
|
|
||
|
def test_check_player_exec_failure(tc):
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
ck.player.cmd_args.append('fail=startup')
|
||
|
with tc.assertRaises(game_jobs.CheckFailed) as ar:
|
||
|
game_jobs.check_player(ck.check)
|
||
|
tc.assertEqual(str(ar.exception),
|
||
|
"error starting subprocess for test:\n"
|
||
|
"exec forced to fail")
|
||
|
|
||
|
def test_check_player_channel_error(tc):
|
||
|
def fail_first_command(channel):
|
||
|
channel.fail_next_command = True
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('fail_first_command', fail_first_command)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
ck.player.cmd_args.append('init=fail_first_command')
|
||
|
with tc.assertRaises(game_jobs.CheckFailed) as ar:
|
||
|
game_jobs.check_player(ck.check)
|
||
|
tc.assertEqual(str(ar.exception),
|
||
|
"transport error sending first command (protocol_version) "
|
||
|
"to test:\n"
|
||
|
"forced failure for send_command_line")
|
||
|
|
||
|
def test_check_player_channel_error_on_close(tc):
|
||
|
def fail_close(channel):
|
||
|
channel.fail_close = True
|
||
|
fx = gtp_engine_fixtures.Mock_subprocess_fixture(tc)
|
||
|
fx.register_init_callback('fail_close', fail_close)
|
||
|
ck = Player_check_fixture(tc)
|
||
|
ck.player.cmd_args.append('init=fail_close')
|
||
|
tc.assertEqual(game_jobs.check_player(ck.check),
|
||
|
["error closing test:\nforced failure for close"])
|
||
|
|