123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- """
- Copyright (c) Contributors to the Open 3D Engine Project.
- For complete copyright and license terms please see the LICENSE at the root of this distribution.
- SPDX-License-Identifier: Apache-2.0 OR MIT
- Unit Tests for watchdog.py
- """
- import unittest
- import unittest.mock as mock
- import pytest
- import ly_test_tools.environment.watchdog as watchdog
- pytestmark = pytest.mark.SUITE_smoke
- def mock_bool_fn():
- return
- class TestWatchdog(unittest.TestCase):
- @mock.patch('threading.Event')
- @mock.patch('threading.Thread')
- def test_Watchdog_Instantiated_CreatesEventAndThread(self, mock_thread, mock_event):
- mock_watchdog = watchdog.Watchdog(mock_bool_fn)
- mock_event.assert_called_once()
- mock_thread.assert_called_once_with(target=mock_watchdog._watchdog, name=mock_watchdog.name)
- @mock.patch('threading.Event', mock.MagicMock())
- @mock.patch('threading.Thread', mock.MagicMock())
- class TestWatchdogMethods(unittest.TestCase):
- def setUp(self):
- self.mock_watchdog = watchdog.Watchdog(mock_bool_fn)
- @mock.patch('threading.Thread.start')
- @mock.patch('threading.Event.clear')
- def test_Start_Called_ClearsEventAndStartsThread(self, mock_clear, mock_start):
- self.mock_watchdog.start()
- mock_clear.assert_called_once()
- mock_start.assert_called_once()
- @mock.patch('threading.Thread.join', mock.MagicMock())
- @mock.patch('threading.Event.set')
- def test_Stop_Called_CallsEventSet(self, under_test):
- self.mock_watchdog.stop()
- under_test.assert_called_once()
- @mock.patch('threading.Thread.join', mock.MagicMock())
- @mock.patch('threading.Event.set', mock.MagicMock())
- @mock.patch('ly_test_tools.environment.watchdog.logging.Logger.error')
- def test_Stop_NoCaughtFailures_NoRaiseOrError(self, mock_error_log):
- self.mock_watchdog.caught_failure = False
- try:
- self.mock_watchdog.stop()
- except watchdog.WatchdogError as e:
- self.fail(f"Unexpected WatchdogError called. Error: {e}")
- mock_error_log.assert_not_called()
- @mock.patch('threading.Thread.join', mock.MagicMock())
- @mock.patch('threading.Event.set', mock.MagicMock())
- @mock.patch('ly_test_tools.environment.watchdog.logging.Logger.error')
- def test_Stop_CaughtFailuresAndRaisesOnCondition_RaisesWatchdogError(self, mock_error_log):
- self.mock_watchdog.caught_failure = True
- self.mock_watchdog._raise_on_condition = True
- with pytest.raises(watchdog.WatchdogError):
- self.mock_watchdog.stop()
- mock_error_log.assert_not_called()
- @mock.patch('threading.Thread.join', mock.MagicMock())
- @mock.patch('threading.Event.set', mock.MagicMock())
- @mock.patch('ly_test_tools.environment.watchdog.logging.Logger.error')
- def test_Stop_CaughtFailuresAndNotRaisesOnCondition_LogsError(self, mock_error_log):
- self.mock_watchdog.caught_failure = True
- self.mock_watchdog._raise_on_condition = False
- try:
- self.mock_watchdog.stop()
- except watchdog.WatchdogError as e:
- self.fail(f"Unexpected WatchdogError called. Error: {e}")
- mock_error_log.assert_called_once()
- @mock.patch('threading.Thread.join')
- def test_Stop_Called_CallsJoin(self, under_test):
- self.mock_watchdog.caught_failure = False
- self.mock_watchdog.stop()
- under_test.assert_called_once()
- @mock.patch('threading.Thread.join', mock.MagicMock())
- @mock.patch('ly_test_tools.environment.watchdog.Watchdog.is_alive')
- @mock.patch('ly_test_tools.environment.watchdog.logging.Logger.error')
- def test_Stop_ThreadIsAlive_LogsError(self, under_test, mock_is_alive):
- mock_is_alive.return_value = True
- self.mock_watchdog.stop()
- under_test.assert_called_once()
- @mock.patch('threading.Thread.join', mock.MagicMock())
- @mock.patch('ly_test_tools.environment.watchdog.Watchdog.is_alive')
- @mock.patch('ly_test_tools.environment.watchdog.logging.Logger.error')
- def test_Stop_ThreadNotAlive_NoLogsError(self, under_test, mock_is_alive):
- mock_is_alive.return_value = False
- self.mock_watchdog.stop()
- under_test.assert_not_called()
- @mock.patch('threading.Thread.is_alive')
- def test_IsAlive_Called_CallsIsAlive(self, under_test):
- self.mock_watchdog.is_alive()
- under_test.assert_called_once()
- @mock.patch('threading.Event.wait')
- def test_WatchdogRunner_ShutdownEventNotSet_CallsBoolFn(self, mock_event_wait):
- mock_event_wait.side_effect = [False, True]
- mock_bool_fn = mock.MagicMock()
- mock_bool_fn.return_value = False
- self.mock_watchdog._bool_fn = mock_bool_fn
- self.mock_watchdog._watchdog()
- self.mock_watchdog._bool_fn.assert_called_once()
- assert self.mock_watchdog.caught_failure == False
- def test_WatchdogRunner_ShutdownEventSet_NoCallsBoolFn(self):
- self.mock_watchdog._shutdown.set()
- mock_bool_fn = mock.MagicMock()
- self.mock_watchdog._bool_fn = mock_bool_fn
- under_test = self.mock_watchdog._watchdog()
- assert under_test is None
- self.mock_watchdog._bool_fn.assert_not_called()
- @mock.patch('threading.Event.wait')
- def test_WatchdogRunner_BoolFnReturnsTrue_SetsCaughtFailureToTrue(self, mock_event_wait):
- mock_event_wait.side_effect = [False, True]
- mock_bool_fn = mock.MagicMock()
- mock_bool_fn.return_value = True
- self.mock_watchdog._bool_fn = mock_bool_fn
- self.mock_watchdog._watchdog()
- assert self.mock_watchdog.caught_failure == True
- class TestProcessUnresponsiveWatchdog(unittest.TestCase):
- mock_process_id = 11111
- mock_name = 'foo.exe'
- mock_process_not_resp_call = \
- '\r\n " \
- "Image Name PID Session Name Session# Mem Usage\r\n" \
- "========================= ======== ================ =========== ============\r\n" \
- "foo.exe %d Console 1 0 K\r\n"' % mock_process_id
- mock_process_resp_call = "INFO: No tasks are running which match the specified criteria.\r\n"
- @mock.patch('psutil.Process', mock.MagicMock())
- def setUp(self):
- self.mock_watchdog = watchdog.ProcessUnresponsiveWatchdog(self.mock_process_id)
- self.mock_watchdog._process_name = self.mock_name
- @mock.patch('ly_test_tools.environment.process_utils.check_output')
- def test_ProcessNotResponding_ProcessResponsive_ReturnsFalse(self, mock_check_output):
- mock_check_output.return_value = self.mock_process_resp_call
- self.mock_watchdog._pid = self.mock_process_id
- under_test = self.mock_watchdog._process_not_responding()
-
- assert not under_test
- assert self.mock_watchdog._calculated_timeout_point is None
- @mock.patch('time.time')
- @mock.patch('ly_test_tools.environment.process_utils.check_output')
- def test_ProcessNotResponding_ProcessUnresponsiveNoTimeout_ReturnsFalse(self, mock_check_output, mock_time):
- mock_time.return_value = 1
- mock_check_output.return_value = self.mock_process_not_resp_call
- self.mock_watchdog._pid = self.mock_process_id
- under_test = self.mock_watchdog._process_not_responding()
- timeout_under_test = mock_time.return_value + self.mock_watchdog._unresponsive_timeout
- assert not under_test
- assert self.mock_watchdog._calculated_timeout_point == timeout_under_test
- @mock.patch('time.time')
- @mock.patch('ly_test_tools.environment.process_utils.check_output')
- def test_ProcessNotResponding_ProcessUnresponsiveReachesTimeout_ReturnsTrue(self, mock_check_output, mock_time):
- mock_time.return_value = 3
- mock_check_output.return_value = self.mock_process_not_resp_call
- self.mock_watchdog._pid = self.mock_process_id
- self.mock_watchdog._calculated_timeout_point = 2
- under_test = self.mock_watchdog._process_not_responding()
- assert under_test
- def test_GetPid_Called_ReturnsAttribute(self):
- self.mock_watchdog._pid = self.mock_process_id
- assert self.mock_watchdog.get_pid() == self.mock_watchdog._pid
- class TestCrashLogWatchdog(unittest.TestCase):
- mock_log_path = 'C:/foo'
- @mock.patch('os.path.exists')
- def test_CrashExists_Called_CallsOsPathExists(self, under_test):
- under_test.side_effect = [False, mock.DEFAULT]
- mock_watchdog = watchdog.CrashLogWatchdog(self.mock_log_path)
- mock_watchdog._bool_fn()
- under_test.assert_called_with(self.mock_log_path)
- @mock.patch('os.path.exists')
- @mock.patch('os.remove')
- def test_CrashLogWatchdog_LogsExist_ClearsExistingLogs(self, under_test, mock_exists):
- mock_exists.return_value = True
- mock_watchdog = watchdog.CrashLogWatchdog(self.mock_log_path)
- under_test.assert_called_once_with(self.mock_log_path)
- @mock.patch('os.path.exists')
- @mock.patch('os.remove')
- def test_CrashLogWatchdog_LogsNotExist_NoClearsExistingLogs(self, under_test, mock_exists):
- mock_exists.return_value = False
- mock_watchdog = watchdog.CrashLogWatchdog(self.mock_log_path)
- under_test.assert_not_called()
- @mock.patch('threading.Thread.join', mock.MagicMock())
- @mock.patch('builtins.print')
- @mock.patch('builtins.open')
- @mock.patch('os.path.exists')
- def test_CrashLogWatchdogStop_LogsExists_OpensLogAndPrints(self, mock_exists, mock_open, mock_print):
- mock_exists.side_effect = [False, True]
- mock_watchdog = watchdog.CrashLogWatchdog(self.mock_log_path)
- mock_watchdog.caught_failure = True
- mock_watchdog._raise_on_condition = False
- mock_watchdog.stop()
- mock_open.assert_called_once_with(mock_watchdog._log_path, "r")
- assert mock_print.called
- @mock.patch('threading.Thread.join', mock.MagicMock())
- @mock.patch('builtins.print')
- @mock.patch('builtins.open')
- @mock.patch('os.path.exists')
- def test_CrashLogWatchdogStop_LogsNoExists_NoOpensAndNoPrintsLog(self, mock_exists, mock_open, mock_print):
- mock_exists.return_value = False
- mock_watchdog = watchdog.CrashLogWatchdog(self.mock_log_path)
- mock_watchdog.caught_failure = False
- mock_watchdog._raise_on_condition = False
- mock_watchdog.stop()
- assert not mock_open.called
- assert not mock_print.called
|