test_log_monitor.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. Unit tests for ly_test_tools.log.log_monitor
  6. """
  7. import io
  8. import unittest.mock as mock
  9. import pytest
  10. import ly_test_tools.log.log_monitor
  11. import ly_test_tools.launchers.platforms.base
  12. pytestmark = pytest.mark.SUITE_smoke
  13. mock_launcher = mock.MagicMock(ly_test_tools.launchers.platforms.base.Launcher)
  14. def mock_log_monitor():
  15. """Returns a LogMonitor() object with all required parameters & resets attributes for each test."""
  16. log_monitor = ly_test_tools.log.log_monitor.LogMonitor(
  17. launcher=mock_launcher, log_file_path='mock_path')
  18. log_monitor.unexpected_lines_found = []
  19. log_monitor.expected_lines_not_found = []
  20. return log_monitor
  21. class NotALauncher(object):
  22. """For simulating test failure when the wrong class is passed as a launcher parameter in LogMonitor()."""
  23. pass
  24. @mock.patch('time.sleep', mock.MagicMock)
  25. class TestLogMonitor(object):
  26. def test_Init_HasRequiredParams_ReturnsLogMonitorObject(self):
  27. assert ly_test_tools.log.log_monitor.LogMonitor(
  28. launcher=mock_launcher, log_file_path='some_log_file.log')
  29. def test_CheckExactMatch_HasMatch_ReturnsString(self):
  30. line = '9189.9998188 - INFO - [MainThread] - example.tests.test_system_example - Log Monitoring test 1'
  31. expected_line = 'Log Monitoring test 1'
  32. under_test = ly_test_tools.log.log_monitor.check_exact_match(line, expected_line)
  33. assert under_test == expected_line
  34. def test_CheckExactMatch_NoMatch_ReturnsNone(self):
  35. line = 'log line'
  36. expected_line = 'no match'
  37. under_test = ly_test_tools.log.log_monitor.check_exact_match(line, expected_line)
  38. assert under_test is None
  39. def test_CheckExactMatch_HasExactMatchWithEscapeCharacter_ReturnsString(self):
  40. line = '9189.9998188 - INFO - [MainThread] - Log Monitoring Test ($1/1).t text'
  41. under_test = ly_test_tools.log.log_monitor.check_exact_match(line, line)
  42. assert under_test == line
  43. def test_CheckExactMatch_NoMatchWithEscapeCharacter_ReturnsNone(self):
  44. line = r'<script\\x20type=\"text/javascript\">javascript:alert(1);</script>'
  45. expected_line = 'no match'
  46. under_test = ly_test_tools.log.log_monitor.check_exact_match(line, expected_line)
  47. assert under_test is None
  48. def test_CheckExactMatch_HasExactMatchWithEscapeCharacterAtEndOfString_ReturnsString(self):
  49. line = 'Testing with escape character as the last character in this (line)'
  50. under_test = ly_test_tools.log.log_monitor.check_exact_match(line, line)
  51. assert under_test == line
  52. def test_CheckExactMatch_HasExactMatchWithNonWordCharacterAtStartOfString_ReturnsString(self):
  53. line = '(Testing with non-word character as the first character in this line'
  54. under_test = ly_test_tools.log.log_monitor.check_exact_match(line, line)
  55. assert under_test == line
  56. def test_CheckSubstringMatch_SubstringMatchWithinWord_ReturnsNone(self):
  57. line = 'SubstringMatchWithinWord'
  58. expected_line = 'Match'
  59. under_test = ly_test_tools.log.log_monitor.check_exact_match(line, expected_line)
  60. assert under_test is None
  61. def test_CheckSubstringMatch_SubstringMatchBetweenWords_ReturnsMatch(self):
  62. line = 'Substring Match Within Word'
  63. expected_line = 'Match'
  64. under_test = ly_test_tools.log.log_monitor.check_exact_match(line, expected_line)
  65. assert under_test == expected_line
  66. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  67. def test_Monitor_UTF8StringsPresentAndExpected_Success(self):
  68. mock_file = io.StringIO('gr\xc3\xb6\xc3\x9feren pr\xc3\xbcfung \xd1\x82\xd0\xb5\xd1\x81\xd1\x82\xd1\x83\xd0\xb2\xd0\xb0\xd0\xbd\xd0\xbd\xd1\x8f\n\xc3\x80\xc3\x88\xc3\x8c\xc3\x92\xc3\x99\n\xc3\x85lpha\xc3\x9fravo\xc3\xa7harlie\n')
  69. mock_launcher.is_alive.side_effect = [True, True, True, False]
  70. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  71. mock_log_monitor().monitor_log_for_lines(['gr\xc3\xb6\xc3\x9feren pr\xc3\xbcfung \xd1\x82\xd0\xb5\xd1\x81\xd1\x82\xd1\x83\xd0\xb2\xd0\xb0\xd0\xbd\xd0\xbd\xd1\x8f',
  72. '\xc3\x80\xc3\x88\xc3\x8c\xc3\x92\xc3\x99',
  73. '\xc3\x85lpha\xc3\x9fravo\xc3\xa7harlie'])
  74. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  75. def test_Monitor_AllLinesFound_Success(self):
  76. mock_file = io.StringIO(u'a\nb\nc\n')
  77. mock_launcher.is_alive.side_effect = [True, True, True, False]
  78. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  79. mock_log_monitor().monitor_log_for_lines(['a', 'b', 'c'], ['d'])
  80. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  81. def test_Monitor_AllLinesNotFound_RaisesLogMonitorException(self):
  82. mock_file = io.StringIO(u'a\nb\nc\n')
  83. mock_launcher.is_alive.side_effect = [True, True, True, False]
  84. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  85. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  86. mock_log_monitor().monitor_log_for_lines(['a', 'b', 'c', 'd'], ['c'])
  87. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  88. def test_Monitor_SomeUnexpectedLinesFound_RaiseLogMonitorException(self):
  89. mock_file = io.StringIO(u'foo\nbar\n')
  90. mock_launcher.is_alive.side_effect = [True, True, True, False]
  91. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  92. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  93. mock_log_monitor().monitor_log_for_lines(['foo', 'bar'], ['bar'], halt_on_unexpected=True)
  94. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  95. def test_Monitor_ExpectedLinesNotFound_RaiseLogMonitorException(self):
  96. mock_file = io.StringIO(u'foo\nbar\n')
  97. mock_launcher.is_alive.side_effect = [True, True, True, False]
  98. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  99. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  100. mock_log_monitor().monitor_log_for_lines(['foo', 'not bar'], [''])
  101. @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock)
  102. @mock.patch('os.path.exists')
  103. def test_Monitor_NoLogPathExists_RaiseLogMonitorException(self, mock_path_exists):
  104. mock_path_exists.return_value = False
  105. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  106. mock_log_monitor().monitor_log_for_lines([''], [''])
  107. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  108. def test_Monitor_InvalidLauncherType_RaiseLogMonitorException(self):
  109. invalid_log_monitor = ly_test_tools.log.log_monitor.LogMonitor(
  110. launcher=NotALauncher, log_file_path='mock_path')
  111. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  112. invalid_log_monitor.monitor_log_for_lines(['foo'], [''])
  113. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  114. def test_Monitor_NoneTypeUnexpectedLines_CastsToList(self):
  115. mock_file = io.StringIO(u'foo\n')
  116. mock_launcher.is_alive.side_effect = [True, True, True, False]
  117. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  118. mock_log_monitor().monitor_log_for_lines(['foo'], None)
  119. @mock.patch('ly_test_tools.log.log_monitor.logging.Logger.warning')
  120. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  121. def test_Monitor_NoneTypeExpectedLines_LogsWarningAndCastsToList(self, mock_log_warning):
  122. mock_file = io.StringIO(u'foo\n')
  123. mock_launcher.is_alive.side_effect = [True, True, True, False]
  124. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  125. mock_log_monitor().monitor_log_for_lines(None, ['bar'])
  126. mock_log_warning.assert_called_once()
  127. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  128. def test_Monitor_ExpectedLinesExactMatch_SucceedsOnExactMatch(self):
  129. mock_file = io.StringIO(u'exact match\n')
  130. mock_launcher.is_alive.side_effect = [True, True, True, False]
  131. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  132. mock_log_monitor().monitor_log_for_lines(['exact match', 'exact', 'match'], [])
  133. @mock.patch('os.path.exists', mock.MagicMock(return_value=True))
  134. def test_Monitor_ExpectedLinesPartialMatch_RaisesLogMonitorException(self):
  135. mock_file = io.StringIO(u'exactlyy\n')
  136. mock_launcher.is_alive.side_effect = [True, True, True, False]
  137. with mock.patch('ly_test_tools.log.log_monitor.open', return_value=mock_file, create=True):
  138. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  139. mock_log_monitor().monitor_log_for_lines(['exactly'], [])
  140. def test_ValidateResults_Valid_ReturnsTrue(self):
  141. mock_lm = mock_log_monitor()
  142. mock_expected_lines = ['expected_foo']
  143. mock_unexpected_lines = ['unexpected_foo']
  144. mock_expected_lines_not_found = []
  145. mock_unexpected_lines_found = []
  146. under_test = mock_lm._validate_results(mock_expected_lines_not_found, mock_unexpected_lines_found,
  147. mock_expected_lines, mock_unexpected_lines)
  148. assert under_test
  149. def test_ValidateResults_ExpectedLineNotFound_RaisesException(self):
  150. mock_lm = mock_log_monitor()
  151. mock_expected_lines = ['expected_foo']
  152. mock_unexpected_lines = ['unexpected_foo']
  153. mock_expected_lines_not_found = ['expected_foo']
  154. mock_unexpected_lines_found = []
  155. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  156. mock_lm._validate_results(mock_expected_lines_not_found, mock_unexpected_lines_found, mock_expected_lines,
  157. mock_unexpected_lines)
  158. def test_ValidateResults_UnexpectedLineFound_RaisesException(self):
  159. mock_lm = mock_log_monitor()
  160. mock_expected_lines = ['expected_foo']
  161. mock_unexpected_lines = ['unexpected_foo']
  162. mock_expected_lines_not_found = []
  163. mock_unexpected_lines_found = ['unexpected_foo']
  164. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  165. mock_lm._validate_results(mock_expected_lines_not_found, mock_unexpected_lines_found, mock_expected_lines,
  166. mock_unexpected_lines)
  167. def test_ValidateResults_ExpectedNotFoundAndUnexpectedFound_RaisesException(self):
  168. mock_lm = mock_log_monitor()
  169. mock_expected_lines = ['expected_foo']
  170. mock_unexpected_lines = ['unexpected_foo']
  171. mock_expected_lines_not_found = ['expected_foo']
  172. mock_unexpected_lines_found = ['unexpected_foo']
  173. with pytest.raises(ly_test_tools.log.log_monitor.LogMonitorException):
  174. mock_lm._validate_results(mock_expected_lines_not_found, mock_unexpected_lines_found, mock_expected_lines,
  175. mock_unexpected_lines)