Commit f2d2fe46fc282fa28b5ab1ebd4c30e7dafa1c932
1 parent
0c8e06d9
unittest: Create unittests for json output validity in msodde
Showing
2 changed files
with
96 additions
and
0 deletions
tests/json/__init__.py
0 → 100644
tests/json/test_output.py
0 → 100644
| 1 | +""" Test validity of json output | ||
| 2 | + | ||
| 3 | +Some scripts have a json output flag. Verify that at default log levels output | ||
| 4 | +can be captured as-is and parsed by a json parser -- at least if the scripts | ||
| 5 | +return 0 | ||
| 6 | +""" | ||
| 7 | + | ||
| 8 | +import unittest | ||
| 9 | +import sys | ||
| 10 | +import json | ||
| 11 | +import os | ||
| 12 | +from os.path import join, dirname, normpath | ||
| 13 | +from oletools import msodde | ||
| 14 | +from tests.test_utils import OutputCapture | ||
| 15 | + | ||
| 16 | +if sys.version_info[0] <= 2: | ||
| 17 | + from oletools import olevba | ||
| 18 | +else: | ||
| 19 | + from oletools import olevba3 as olevba | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +# Directory with test data, independent of current working directory | ||
| 23 | +DATA_DIR = normpath(join(dirname(__file__), '..', 'test-data')) | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +class TestValidJson(unittest.TestCase): | ||
| 27 | + """ Ensure that script output is valid json (if return code is 0) """ | ||
| 28 | + | ||
| 29 | + def iter_test_files(self): | ||
| 30 | + """ Iterate over all test files in DATA_DIR """ | ||
| 31 | + for dirpath, _, filenames in os.walk(DATA_DIR): | ||
| 32 | + for filename in filenames: | ||
| 33 | + yield join(dirpath, filename) | ||
| 34 | + | ||
| 35 | + def run_and_parse(self, program, args, print_output=False): | ||
| 36 | + """ run single program with single file and parse output """ | ||
| 37 | + return_code = None | ||
| 38 | + with OutputCapture() as capturer: # capture stdout | ||
| 39 | + try: | ||
| 40 | + return_code = program(args) | ||
| 41 | + except Exception: | ||
| 42 | + return_code = 1 # would result in non-zero exit | ||
| 43 | + except SystemExit as se: | ||
| 44 | + return_code = se.code or 0 # se.code can be None | ||
| 45 | + if return_code is not 0: | ||
| 46 | + if print_output: | ||
| 47 | + print('Command failed ({0}) -- not parsing output' | ||
| 48 | + .format(return_code)) | ||
| 49 | + return # no need to test | ||
| 50 | + | ||
| 51 | + self.assertNotEqual(return_code, None, | ||
| 52 | + msg='self-test fail: return_code not set') | ||
| 53 | + | ||
| 54 | + # now test output | ||
| 55 | + if print_output: | ||
| 56 | + print(capturer.buffer.getvalue()) | ||
| 57 | + capturer.buffer.seek(0) # re-set position in file-like stream | ||
| 58 | + try: | ||
| 59 | + json_data = json.load(capturer.buffer) | ||
| 60 | + except ValueError: | ||
| 61 | + self.fail('Invalid json:\n' + capturer.buffer.getvalue()) | ||
| 62 | + self.assertNotEqual(len(json_data), 0, msg='Output was empty') | ||
| 63 | + | ||
| 64 | + def run_all_files(self, program, args_without_filename, print_output=False): | ||
| 65 | + """ run test for a single program over all test files """ | ||
| 66 | + n_files = 0 | ||
| 67 | + for testfile in self.iter_test_files(): # loop over all input | ||
| 68 | + args = args_without_filename + [testfile, ] | ||
| 69 | + self.run_and_parse(program, args, print_output) | ||
| 70 | + n_files += 1 | ||
| 71 | + self.assertNotEqual(n_files, 0, | ||
| 72 | + msg='self-test fail: No test files found') | ||
| 73 | + | ||
| 74 | + def test_msodde(self): | ||
| 75 | + """ Test msodde.py """ | ||
| 76 | + self.run_all_files(msodde.main, ['-j', ]) | ||
| 77 | + | ||
| 78 | + @unittest.skip('olevba needs patching to accept custom cmd line args') | ||
| 79 | + def test_olevba(self): | ||
| 80 | + """ Test olevba.py with default args """ | ||
| 81 | + self.run_all_files(olevba.main, ['-j', ]) | ||
| 82 | + | ||
| 83 | + @unittest.skip('olevba needs patching to accept custom cmd line args') | ||
| 84 | + def test_olevba_analysis(self): | ||
| 85 | + """ Test olevba.py with default args """ | ||
| 86 | + self.run_all_files(olevba.main, ['-j', '-a', ]) | ||
| 87 | + | ||
| 88 | + @unittest.skip('olevba needs patching to accept custom cmd line args') | ||
| 89 | + def test_olevba_recurse(self): | ||
| 90 | + """ Test olevba.py with default args """ | ||
| 91 | + self.run_and_parse(olevba.main, ['-j', '-r', DATA_DIR], True) | ||
| 92 | + | ||
| 93 | + | ||
| 94 | +# just in case somebody calls this file as a script | ||
| 95 | +if __name__ == '__main__': | ||
| 96 | + unittest.main() |