#!/usr/bin/python # Copyright 2009 Google Inc. Released under the GPL v2 import time, unittest import common from autotest_lib.client.common_lib import error from autotest_lib.client.common_lib.test_utils import mock from autotest_lib.server import subcommand def _create_subcommand(func, args): # to avoid __init__ class wrapper(subcommand.subcommand): def __init__(self, func, args): self.func = func self.args = args self.subdir = None self.debug = None self.pid = None self.returncode = None return wrapper(func, args) class subcommand_test(unittest.TestCase): def setUp(self): self.god = mock.mock_god() def tearDown(self): self.god.unstub_all() # cleanup the hooks subcommand.subcommand.fork_hooks = [] subcommand.subcommand.join_hooks = [] def test_create(self): def check_attributes(cmd, func, args, subdir=None, debug=None, pid=None, returncode=None, fork_hooks=[], join_hooks=[]): self.assertEquals(cmd.func, func) self.assertEquals(cmd.args, args) self.assertEquals(cmd.subdir, subdir) self.assertEquals(cmd.debug, debug) self.assertEquals(cmd.pid, pid) self.assertEquals(cmd.returncode, returncode) self.assertEquals(cmd.fork_hooks, fork_hooks) self.assertEquals(cmd.join_hooks, join_hooks) def func(arg1, arg2): pass cmd = subcommand.subcommand(func, (2, 3)) check_attributes(cmd, func, (2, 3)) self.god.check_playback() self.god.stub_function(subcommand.os.path, 'abspath') self.god.stub_function(subcommand.os.path, 'exists') self.god.stub_function(subcommand.os, 'mkdir') subcommand.os.path.abspath.expect_call('dir').and_return('/foo/dir') subcommand.os.path.exists.expect_call('/foo/dir').and_return(False) subcommand.os.mkdir.expect_call('/foo/dir') (subcommand.os.path.exists.expect_call('/foo/dir/debug') .and_return(False)) subcommand.os.mkdir.expect_call('/foo/dir/debug') cmd = subcommand.subcommand(func, (2, 3), subdir='dir') check_attributes(cmd, func, (2, 3), subdir='/foo/dir', debug='/foo/dir/debug') self.god.check_playback() def _setup_fork_start_parent(self): self.god.stub_function(subcommand.os, 'fork') subcommand.os.fork.expect_call().and_return(1000) func = self.god.create_mock_function('func') cmd = _create_subcommand(func, []) cmd.fork_start() return cmd def test_fork_start_parent(self): cmd = self._setup_fork_start_parent() self.assertEquals(cmd.pid, 1000) self.god.check_playback() def _setup_fork_start_child(self): self.god.stub_function(subcommand.os, 'pipe') self.god.stub_function(subcommand.os, 'fork') self.god.stub_function(subcommand.os, 'close') self.god.stub_function(subcommand.os, 'write') self.god.stub_function(subcommand.cPickle, 'dumps') self.god.stub_function(subcommand.os, '_exit') def test_fork_start_child(self): self._setup_fork_start_child() func = self.god.create_mock_function('func') fork_hook = self.god.create_mock_function('fork_hook') join_hook = self.god.create_mock_function('join_hook') subcommand.subcommand.register_fork_hook(fork_hook) subcommand.subcommand.register_join_hook(join_hook) cmd = _create_subcommand(func, (1, 2)) subcommand.os.pipe.expect_call().and_return((10, 20)) subcommand.os.fork.expect_call().and_return(0) subcommand.os.close.expect_call(10) fork_hook.expect_call(cmd) func.expect_call(1, 2).and_return(True) subcommand.cPickle.dumps.expect_call(True, subcommand.cPickle.HIGHEST_PROTOCOL).and_return('True') subcommand.os.write.expect_call(20, 'True') subcommand.os.close.expect_call(20) join_hook.expect_call(cmd) subcommand.os._exit.expect_call(0) cmd.fork_start() self.god.check_playback() def test_fork_start_child_error(self): self._setup_fork_start_child() self.god.stub_function(subcommand.logging, 'exception') func = self.god.create_mock_function('func') cmd = _create_subcommand(func, (1, 2)) error = Exception('some error') subcommand.os.pipe.expect_call().and_return((10, 20)) subcommand.os.fork.expect_call().and_return(0) subcommand.os.close.expect_call(10) func.expect_call(1, 2).and_raises(error) subcommand.logging.exception.expect_call('function failed') subcommand.cPickle.dumps.expect_call(error, subcommand.cPickle.HIGHEST_PROTOCOL).and_return('error') subcommand.os.write.expect_call(20, 'error') subcommand.os.close.expect_call(20) subcommand.os._exit.expect_call(1) cmd.fork_start() self.god.check_playback() def _setup_poll(self): cmd = self._setup_fork_start_parent() self.god.stub_function(subcommand.os, 'waitpid') return cmd def test_poll_running(self): cmd = self._setup_poll() (subcommand.os.waitpid.expect_call(1000, subcommand.os.WNOHANG) .and_raises(subcommand.os.error('waitpid'))) self.assertEquals(cmd.poll(), None) self.god.check_playback() def test_poll_finished_success(self): cmd = self._setup_poll() (subcommand.os.waitpid.expect_call(1000, subcommand.os.WNOHANG) .and_return((1000, 0))) self.assertEquals(cmd.poll(), 0) self.god.check_playback() def test_poll_finished_failure(self): cmd = self._setup_poll() self.god.stub_function(cmd, '_handle_exitstatus') (subcommand.os.waitpid.expect_call(1000, subcommand.os.WNOHANG) .and_return((1000, 10))) cmd._handle_exitstatus.expect_call(10).and_raises(Exception('fail')) self.assertRaises(Exception, cmd.poll) self.god.check_playback() def test_wait_success(self): cmd = self._setup_poll() (subcommand.os.waitpid.expect_call(1000, 0) .and_return((1000, 0))) self.assertEquals(cmd.wait(), 0) self.god.check_playback() def test_wait_failure(self): cmd = self._setup_poll() self.god.stub_function(cmd, '_handle_exitstatus') (subcommand.os.waitpid.expect_call(1000, 0) .and_return((1000, 10))) cmd._handle_exitstatus.expect_call(10).and_raises(Exception('fail')) self.assertRaises(Exception, cmd.wait) self.god.check_playback() class real_subcommand_test(unittest.TestCase): """Test actually running subcommands (without mocking).""" def _setup_subcommand(self, func, *args): cmd = subcommand.subcommand(func, args) cmd.fork_start() return cmd def test_fork_waitfor_no_timeout(self): """Test fork_waitfor success with no timeout.""" cmd = self._setup_subcommand(lambda: None) self.assertEquals(cmd.fork_waitfor(), 0) def test_fork_waitfor_timeout(self): """Test fork_waitfor success with a timeout.""" cmd = self._setup_subcommand(lambda: None) self.assertEquals(cmd.fork_waitfor(timeout=60), 0) def test_fork_waitfor_exception(self): """Test fork_waitfor failure with an exception.""" cmd = self._setup_subcommand(lambda: None, 'foo') with self.assertRaises(error.AutoservSubcommandError): cmd.fork_waitfor(timeout=60) def test_fork_waitfor_timeout_fail(self): """Test fork_waitfor timing out.""" cmd = self._setup_subcommand(lambda: time.sleep(60)) with self.assertRaises(error.AutoservSubcommandError): cmd.fork_waitfor(timeout=1) class parallel_test(unittest.TestCase): def setUp(self): self.god = mock.mock_god() self.god.stub_function(subcommand.cPickle, 'load') def tearDown(self): self.god.unstub_all() def _get_cmd(self, func, args): cmd = _create_subcommand(func, args) cmd.result_pickle = self.god.create_mock_class(file, 'file') return self.god.create_mock_class(cmd, 'subcommand') def _get_tasklist(self): return [self._get_cmd(lambda x: x * 2, (3,)), self._get_cmd(lambda: None, [])] def _setup_common(self): tasklist = self._get_tasklist() for task in tasklist: task.fork_start.expect_call() return tasklist def test_success(self): tasklist = self._setup_common() for task in tasklist: task.fork_waitfor.expect_call(timeout=None).and_return(0) (subcommand.cPickle.load.expect_call(task.result_pickle) .and_return(6)) task.result_pickle.close.expect_call() subcommand.parallel(tasklist) self.god.check_playback() def test_failure(self): tasklist = self._setup_common() for task in tasklist: task.fork_waitfor.expect_call(timeout=None).and_return(1) (subcommand.cPickle.load.expect_call(task.result_pickle) .and_return(6)) task.result_pickle.close.expect_call() self.assertRaises(subcommand.error.AutoservError, subcommand.parallel, tasklist) self.god.check_playback() def test_timeout(self): self.god.stub_function(subcommand.time, 'time') tasklist = self._setup_common() timeout = 10 subcommand.time.time.expect_call().and_return(1) for task in tasklist: subcommand.time.time.expect_call().and_return(1) task.fork_waitfor.expect_call(timeout=timeout).and_return(None) (subcommand.cPickle.load.expect_call(task.result_pickle) .and_return(6)) task.result_pickle.close.expect_call() self.assertRaises(subcommand.error.AutoservError, subcommand.parallel, tasklist, timeout=timeout) self.god.check_playback() def test_return_results(self): tasklist = self._setup_common() tasklist[0].fork_waitfor.expect_call(timeout=None).and_return(0) (subcommand.cPickle.load.expect_call(tasklist[0].result_pickle) .and_return(6)) tasklist[0].result_pickle.close.expect_call() error = Exception('fail') tasklist[1].fork_waitfor.expect_call(timeout=None).and_return(1) (subcommand.cPickle.load.expect_call(tasklist[1].result_pickle) .and_return(error)) tasklist[1].result_pickle.close.expect_call() self.assertEquals(subcommand.parallel(tasklist, return_results=True), [6, error]) self.god.check_playback() class test_parallel_simple(unittest.TestCase): def setUp(self): self.god = mock.mock_god() self.god.stub_function(subcommand, 'parallel') ctor = self.god.create_mock_function('subcommand') self.god.stub_with(subcommand, 'subcommand', ctor) def tearDown(self): self.god.unstub_all() def test_simple_success(self): func = self.god.create_mock_function('func') func.expect_call(3) subcommand.parallel_simple(func, (3,)) self.god.check_playback() def test_simple_failure(self): func = self.god.create_mock_function('func') error = Exception('fail') func.expect_call(3).and_raises(error) self.assertRaises(Exception, subcommand.parallel_simple, func, (3,)) self.god.check_playback() def test_simple_return_value(self): func = self.god.create_mock_function('func') result = 1000 func.expect_call(3).and_return(result) self.assertEquals(subcommand.parallel_simple(func, (3,), return_results=True), [result]) self.god.check_playback() def test_default_subdirs_constructor(self): func = self.god.create_mock_function('func') args = range(4) for arg in args: subcommand.subcommand.expect_call( func, [arg], str(arg)).and_return(arg) subcommand.parallel.expect_call(args, None, return_results=False) subcommand.parallel_simple(func, args) self.god.check_playback() def test_nolog_skips_subdirs(self): func = self.god.create_mock_function('func') args = range(3) for arg in args: subcommand.subcommand.expect_call( func, [arg], None).and_return(arg) subcommand.parallel.expect_call(args, None, return_results=False) subcommand.parallel_simple(func, args, log=False) self.god.check_playback() def test_custom_subdirs_constructor(self): func = self.god.create_mock_function('func') args = range(7) subdirs = ['subdir%s' % arg for arg in args] for arg, subdir in zip(args, subdirs): subcommand.subcommand.expect_call( func, [arg], subdir).and_return(arg) subcommand.parallel.expect_call(args, None, return_results=False) subcommand.parallel_simple( func, args, subdir_name_constructor=lambda x: 'subdir%s' % x) self.god.check_playback() if __name__ == '__main__': unittest.main()