1# Copyright 2021 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Tests for stack_parser.""" 15import os 16import unittest 17from unittest import mock 18 19import parameterized 20from pyfakefs import fake_filesystem_unittest 21 22import stack_parser 23 24# NOTE: This integration test relies on 25# https://github.com/google/oss-fuzz/tree/master/projects/example project. 26EXAMPLE_PROJECT = 'example' 27 28# Location of data used for testing. 29TEST_DATA_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 30 'test_data') 31 32 33class ParseOutputTest(fake_filesystem_unittest.TestCase): 34 """Tests parse_fuzzer_output.""" 35 36 def setUp(self): 37 self.setUpPyfakefs() 38 self.maxDiff = None # pylint: disable=invalid-name 39 40 @parameterized.parameterized.expand([('example_crash_fuzzer_output.txt', 41 'example_crash_fuzzer_bug_summary.txt'), 42 ('msan_crash_fuzzer_output.txt', 43 'msan_crash_fuzzer_bug_summary.txt')]) 44 def test_parse_valid_output(self, fuzzer_output_file, bug_summary_file): 45 """Checks that the parse fuzzer output can correctly parse output.""" 46 # Read the fuzzer output from disk. 47 fuzzer_output_path = os.path.join(TEST_DATA_PATH, fuzzer_output_file) 48 self.fs.add_real_file(fuzzer_output_path) 49 with open(fuzzer_output_path, 'rb') as fuzzer_output_handle: 50 fuzzer_output = fuzzer_output_handle.read() 51 bug_summary_path = '/bug-summary.txt' 52 with mock.patch('logging.info') as mocked_info: 53 stack_parser.parse_fuzzer_output(fuzzer_output, bug_summary_path) 54 mocked_info.assert_not_called() 55 56 with open(bug_summary_path) as bug_summary_handle: 57 bug_summary = bug_summary_handle.read() 58 59 # Compare the bug to the expected one. 60 expected_bug_summary_path = os.path.join(TEST_DATA_PATH, bug_summary_file) 61 self.fs.add_real_file(expected_bug_summary_path) 62 with open(expected_bug_summary_path) as expected_bug_summary_handle: 63 expected_bug_summary = expected_bug_summary_handle.read() 64 65 self.assertEqual(expected_bug_summary, bug_summary) 66 67 def test_parse_invalid_output(self): 68 """Checks that no files are created when an invalid input was given.""" 69 artifact_path = '/bug-summary.txt' 70 with mock.patch('logging.error') as mocked_error: 71 stack_parser.parse_fuzzer_output(b'not a valid output_string', 72 artifact_path) 73 assert mocked_error.call_count 74 self.assertFalse(os.path.exists(artifact_path)) 75 76 77if __name__ == '__main__': 78 unittest.main() 79