| #!/usr/bin/python |
| |
| """Tests for drone_utility.""" |
| |
| import os |
| import unittest |
| |
| import common |
| from autotest_lib.client.common_lib import autotemp |
| from autotest_lib.client.common_lib.test_utils import mock |
| from autotest_lib.scheduler import drone_utility |
| |
| |
| class TestProcessRefresher(unittest.TestCase): |
| """Tests for the drone_utility.ProcessRefresher object.""" |
| |
| def setUp(self): |
| self._tempdir = autotemp.tempdir(unique_id='test_process_refresher') |
| self.addCleanup(self._tempdir.clean) |
| self._fake_command = '!faketest!' |
| self._fake_proc_info = {'pid': 3, 'pgid': 4, 'ppid': 2, |
| 'comm': self._fake_command, 'args': ''} |
| self.god = mock.mock_god() |
| self.god.stub_function(drone_utility, '_get_process_info') |
| self._mock_get_process_info = drone_utility._get_process_info |
| self.god.stub_function(drone_utility, '_process_has_dark_mark') |
| self._mock_process_has_dark_mark = ( |
| drone_utility._process_has_dark_mark) |
| |
| |
| def tearDown(self): |
| self.god.unstub_all() |
| |
| def test_no_processes(self): |
| """Sanity check the case when there is nothing to do""" |
| self._mock_get_process_info.expect_call().and_return([]) |
| process_refresher = drone_utility.ProcessRefresher(check_mark=False) |
| got, warnings = process_refresher([]) |
| expected = { |
| 'pidfiles': dict(), |
| 'all_processes': [], |
| 'autoserv_processes': [], |
| 'parse_processes': [], |
| 'pidfiles_second_read': dict(), |
| } |
| self.god.check_playback() |
| self.assertEqual(got, expected) |
| |
| |
| def test_read_pidfiles_use_pool(self): |
| """Readable subset of pidfile paths are included in the result |
| |
| Uses process pools. |
| """ |
| self._parameterized_test_read_pidfiles(use_pool=True) |
| |
| |
| def test_read_pidfiles_no_pool(self): |
| """Readable subset of pidfile paths are included in the result |
| |
| Does not use process pools. |
| """ |
| self._parameterized_test_read_pidfiles(use_pool=False) |
| |
| |
| def test_read_many_pidfiles(self): |
| """Read a large number of pidfiles (more than pool size).""" |
| self._mock_get_process_info.expect_call().and_return([]) |
| expected_pidfiles = {} |
| for i in range(1000): |
| data = 'data number %d' % i |
| path = self._write_pidfile('pidfile%d' % i, data) |
| expected_pidfiles[path] = data |
| |
| process_refresher = drone_utility.ProcessRefresher(check_mark=False, |
| use_pool=True) |
| got, _ = process_refresher(expected_pidfiles.keys()) |
| expected = { |
| 'pidfiles': expected_pidfiles, |
| 'all_processes': [], |
| 'autoserv_processes': [], |
| 'parse_processes': [], |
| 'pidfiles_second_read': expected_pidfiles, |
| } |
| self.god.check_playback() |
| self.assertEqual(got, expected) |
| |
| |
| def test_filter_processes(self): |
| """Various filtered results correctly classify processes by name.""" |
| self.maxDiff = None |
| process_refresher = drone_utility.ProcessRefresher(check_mark=False) |
| autoserv_processes = [self._proc_info_dict(3, 'autoserv')] |
| parse_processes = [self._proc_info_dict(4, 'parse'), |
| self._proc_info_dict(5, 'site_parse')] |
| all_processes = ([self._proc_info_dict(6, 'who_cares')] |
| + autoserv_processes + parse_processes) |
| |
| self._mock_get_process_info.expect_call().and_return(all_processes) |
| got, _warnings = process_refresher(self._tempdir.name) |
| expected = { |
| 'pidfiles': dict(), |
| 'all_processes': all_processes, |
| 'autoserv_processes': autoserv_processes, |
| 'parse_processes': parse_processes, |
| 'pidfiles_second_read': dict(), |
| } |
| self.god.check_playback() |
| self.assertEqual(got, expected) |
| |
| |
| def test_respect_dark_mark(self): |
| """When check_mark=True, dark mark check is performed and respected. |
| |
| Only filtered processes with dark mark should be returned. We only test |
| this with use_pool=False because mocking out _process_has_dark_mark with |
| multiprocessing.Pool is hard. |
| """ |
| self.maxDiff = None |
| process_refresher = drone_utility.ProcessRefresher(check_mark=True) |
| marked_process = self._proc_info_dict(3, 'autoserv') |
| unmarked_process = self._proc_info_dict(369, 'autoserv') |
| all_processes = [marked_process, unmarked_process] |
| self._mock_get_process_info.expect_call().and_return(all_processes) |
| self._mock_process_has_dark_mark.expect_call(3).and_return(True) |
| self._mock_process_has_dark_mark.expect_call(369).and_return(False) |
| got, warnings = process_refresher(self._tempdir.name) |
| expected = { |
| 'pidfiles': dict(), |
| 'all_processes': all_processes, |
| 'autoserv_processes': [marked_process], |
| 'parse_processes': [], |
| 'pidfiles_second_read': dict(), |
| } |
| self.god.check_playback() |
| self.assertEqual(got, expected) |
| self.assertEqual(len(warnings), 1) |
| self.assertRegexpMatches(warnings[0], '.*autoserv.*369.*') |
| |
| |
| def _parameterized_test_read_pidfiles(self, use_pool): |
| """Readable subset of pidfile paths are included in the result |
| |
| @param: use_pool: Argument use_pool for ProcessRefresher |
| """ |
| self._mock_get_process_info.expect_call().and_return([]) |
| path1 = self._write_pidfile('pidfile1', 'first pidfile') |
| path2 = self._write_pidfile('pidfile2', 'second pidfile', |
| subdir='somedir') |
| process_refresher = drone_utility.ProcessRefresher(check_mark=False, |
| use_pool=use_pool) |
| got, warnings = process_refresher( |
| [path1, path2, |
| os.path.join(self._tempdir.name, 'non_existent')]) |
| expected_pidfiles = { |
| path1: 'first pidfile', |
| path2: 'second pidfile', |
| } |
| expected = { |
| 'pidfiles': expected_pidfiles, |
| 'all_processes': [], |
| 'autoserv_processes': [], |
| 'parse_processes': [], |
| 'pidfiles_second_read': expected_pidfiles, |
| } |
| self.god.check_playback() |
| self.assertEqual(got, expected) |
| |
| |
| def _write_pidfile(self, filename, content, subdir=''): |
| parent_dir = self._tempdir.name |
| if subdir: |
| parent_dir = os.path.join(parent_dir, subdir) |
| os.makedirs(parent_dir) |
| path = os.path.join(parent_dir, filename) |
| with open(path, 'w') as f: |
| f.write(content) |
| return path |
| |
| def _proc_info_dict(self, pid, comm, pgid=33, ppid=44, args=''): |
| return {'pid': pid, 'comm': comm, 'pgid': pgid, 'ppid': ppid, |
| 'args': args} |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |