186 lines
7 KiB
Python
Executable file
186 lines
7 KiB
Python
Executable file
#!/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()
|