Commit 6bc04064530d2138f207be18ba7325863afd46b7
1 parent
3c3b7093
tests: Add warnings to log_helper test
Encountered an example where a 3rd-party library issued a warning that messed up the json output. Create test to reproduce this.
Showing
3 changed files
with
48 additions
and
1 deletions
tests/common/log_helper/log_helper_test_imported.py
| ... | ... | @@ -4,6 +4,7 @@ by the main test file |
| 4 | 4 | """ |
| 5 | 5 | |
| 6 | 6 | from oletools.common.log_helper import log_helper |
| 7 | +import warnings | |
| 7 | 8 | |
| 8 | 9 | DEBUG_MESSAGE = 'imported: debug log' |
| 9 | 10 | INFO_MESSAGE = 'imported: info log' |
| ... | ... | @@ -11,7 +12,10 @@ WARNING_MESSAGE = 'imported: warning log' |
| 11 | 12 | ERROR_MESSAGE = 'imported: error log' |
| 12 | 13 | CRITICAL_MESSAGE = 'imported: critical log' |
| 13 | 14 | RESULT_MESSAGE = 'imported: result log' |
| 15 | + | |
| 14 | 16 | RESULT_TYPE = 'imported: result' |
| 17 | +ACTUAL_WARNING = 'Feature XYZ provided by this module might be deprecated at '\ | |
| 18 | + 'some point in the future ... or not' | |
| 15 | 19 | |
| 16 | 20 | logger = log_helper.get_or_create_silent_logger('test_imported') |
| 17 | 21 | |
| ... | ... | @@ -27,3 +31,7 @@ def log(): |
| 27 | 31 | logger.error(ERROR_MESSAGE) |
| 28 | 32 | logger.critical(CRITICAL_MESSAGE) |
| 29 | 33 | logger.info(RESULT_MESSAGE, type=RESULT_TYPE) |
| 34 | + | |
| 35 | + | |
| 36 | +def warn(): | |
| 37 | + warnings.warn(ACTUAL_WARNING) | ... | ... |
tests/common/log_helper/log_helper_test_main.py
| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | import sys |
| 4 | 4 | import logging |
| 5 | +import warnings | |
| 5 | 6 | from tests.common.log_helper import log_helper_test_imported |
| 6 | 7 | from oletools.common.log_helper import log_helper |
| 7 | 8 | |
| ... | ... | @@ -11,7 +12,9 @@ WARNING_MESSAGE = 'main: warning log' |
| 11 | 12 | ERROR_MESSAGE = 'main: error log' |
| 12 | 13 | CRITICAL_MESSAGE = 'main: critical log' |
| 13 | 14 | RESULT_MESSAGE = 'main: result log' |
| 15 | + | |
| 14 | 16 | RESULT_TYPE = 'main: result' |
| 17 | +ACTUAL_WARNING = 'Warnings can pop up anywhere, have to be prepared!' | |
| 15 | 18 | |
| 16 | 19 | logger = log_helper.get_or_create_silent_logger('test_main') |
| 17 | 20 | |
| ... | ... | @@ -24,7 +27,8 @@ def enable_logging(): |
| 24 | 27 | |
| 25 | 28 | def main(args): |
| 26 | 29 | """ |
| 27 | - Try to cover possible logging scenarios. For each scenario covered, here's the expected args and outcome: | |
| 30 | + Try to cover possible logging scenarios. For each scenario covered, here's | |
| 31 | + the expected args and outcome: | |
| 28 | 32 | - Log without enabling: ['<level>'] |
| 29 | 33 | * logging when being imported - should never print |
| 30 | 34 | - Log as JSON without enabling: ['as-json', '<level>'] |
| ... | ... | @@ -35,6 +39,8 @@ def main(args): |
| 35 | 39 | * logging as JSON when being run as script - should log messages as JSON |
| 36 | 40 | - Enable, log as JSON and throw: ['enable', 'as-json', 'throw', '<level>'] |
| 37 | 41 | * should produce JSON-compatible output, even after an unhandled exception |
| 42 | + - Enable, log as JSON and warn: ['enable', 'as-json', 'warn', '<level>'] | |
| 43 | + * should produce JSON-compatible output, even after a warning | |
| 38 | 44 | """ |
| 39 | 45 | |
| 40 | 46 | # the level should always be the last argument passed |
| ... | ... | @@ -42,6 +48,7 @@ def main(args): |
| 42 | 48 | use_json = 'as-json' in args |
| 43 | 49 | throw = 'throw' in args |
| 44 | 50 | percent_autoformat = '%-autoformat' in args |
| 51 | + warn = 'warn' in args | |
| 45 | 52 | |
| 46 | 53 | log_helper_test_imported.logger.setLevel(logging.ERROR) |
| 47 | 54 | |
| ... | ... | @@ -53,6 +60,10 @@ def main(args): |
| 53 | 60 | if throw: |
| 54 | 61 | raise Exception('An exception occurred before ending the logging') |
| 55 | 62 | |
| 63 | + if warn: | |
| 64 | + warnings.warn(ACTUAL_WARNING) | |
| 65 | + log_helper_test_imported.warn() | |
| 66 | + | |
| 56 | 67 | log_helper.end_logging() |
| 57 | 68 | |
| 58 | 69 | ... | ... |
tests/common/log_helper/test_log_helper.py
| ... | ... | @@ -142,6 +142,32 @@ class TestLogHelper(unittest.TestCase): |
| 142 | 142 | self.assertIn('INFO:test_main:main: info log', output) |
| 143 | 143 | self.assertIn('INFO:test_imported:imported: info log', output) |
| 144 | 144 | |
| 145 | + def test_json_correct_on_warnings(self): | |
| 146 | + """ | |
| 147 | + Test that even on warnings our JSON is always correct | |
| 148 | + """ | |
| 149 | + output = self._run_test(['enable', 'as-json', 'warn', 'warning']) | |
| 150 | + expected_messages = [ | |
| 151 | + log_helper_test_main.WARNING_MESSAGE, | |
| 152 | + log_helper_test_main.ERROR_MESSAGE, | |
| 153 | + log_helper_test_main.CRITICAL_MESSAGE, | |
| 154 | + log_helper_test_imported.WARNING_MESSAGE, | |
| 155 | + log_helper_test_imported.ERROR_MESSAGE, | |
| 156 | + log_helper_test_imported.CRITICAL_MESSAGE, | |
| 157 | + ] | |
| 158 | + | |
| 159 | + for msg in expected_messages: | |
| 160 | + self.assertIn(msg, output) | |
| 161 | + | |
| 162 | + # last two entries of output should be warnings | |
| 163 | + jout = json.loads(output) | |
| 164 | + self.assertEqual(jout[-2]['level'], 'WARNING') | |
| 165 | + self.assertEqual(jout[-1]['level'], 'WARNING') | |
| 166 | + self.assertEqual(jout[-2]['type'], 'warning') | |
| 167 | + self.assertEqual(jout[-1]['type'], 'warning') | |
| 168 | + self.assertIn(log_helper_test_main.ACTUAL_WARNING, jout[-2]['msg']) | |
| 169 | + self.assertIn(log_helper_test_imported.ACTUAL_WARNING, jout[-1]['msg']) | |
| 170 | + | |
| 145 | 171 | def _assert_json_messages(self, output, messages): |
| 146 | 172 | try: |
| 147 | 173 | json_data = json.loads(output) |
| ... | ... | @@ -163,6 +189,8 @@ class TestLogHelper(unittest.TestCase): |
| 163 | 189 | When arg `run_third_party` is `True`, we do not run the `TEST_FILE` as |
| 164 | 190 | main moduel but the `TEST_FILE_3RD_PARTY` and return contents of |
| 165 | 191 | `stderr` instead of `stdout`. |
| 192 | + | |
| 193 | + TODO: use tests.utils.call_and_capture | |
| 166 | 194 | """ |
| 167 | 195 | all_args = [PYTHON_EXECUTABLE, ] |
| 168 | 196 | if run_third_party: | ... | ... |