| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # Copyright 2020 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Tests for bisect_clang_crashes.""" |
| |
| import glob |
| import logging |
| import os.path |
| import subprocess |
| import unittest |
| import unittest.mock as mock |
| |
| import bisect_clang_crashes |
| |
| |
| class Test(unittest.TestCase): |
| """Tests for bisect_clang_crashes.""" |
| |
| class _SilencingFilter(object): |
| """Silences all log messages. |
| |
| Also collects info about log messages that would've been emitted. |
| """ |
| |
| def __init__(self): |
| self.messages = [] |
| |
| def filter(self, record): |
| self.messages.append(record.getMessage()) |
| return 0 |
| |
| @mock.patch.object(subprocess, "check_output") |
| def test_get_artifacts(self, mock_gsutil_ls): |
| pattern = ( |
| "gs://chromeos-toolchain-artifacts/clang-crash-diagnoses/" |
| "**/*clang_crash_diagnoses.tar.xz" |
| ) |
| mock_gsutil_ls.return_value = "artifact1\nartifact2\nartifact3" |
| results = bisect_clang_crashes.get_artifacts(pattern) |
| self.assertEqual(results, ["artifact1", "artifact2", "artifact3"]) |
| mock_gsutil_ls.assert_called_once_with( |
| ["gsutil.py", "ls", pattern], |
| stderr=subprocess.STDOUT, |
| encoding="utf-8", |
| ) |
| |
| @mock.patch.object(os.path, "exists") |
| @mock.patch.object(glob, "glob") |
| def test_get_crash_reproducers_succeed( |
| self, mock_file_search, mock_file_check |
| ): |
| working_dir = "SomeDirectory" |
| mock_file_search.return_value = ["a.c", "b.cpp", "c.cc"] |
| mock_file_check.side_effect = [True, True, True] |
| results = bisect_clang_crashes.get_crash_reproducers(working_dir) |
| mock_file_search.assert_called_once_with("%s/*.c*" % working_dir) |
| self.assertEqual(mock_file_check.call_count, 3) |
| self.assertEqual(mock_file_check.call_args_list[0], mock.call("a.sh")) |
| self.assertEqual(mock_file_check.call_args_list[1], mock.call("b.sh")) |
| self.assertEqual(mock_file_check.call_args_list[2], mock.call("c.sh")) |
| self.assertEqual( |
| results, [("a.c", "a.sh"), ("b.cpp", "b.sh"), ("c.cc", "c.sh")] |
| ) |
| |
| @mock.patch.object(os.path, "exists") |
| @mock.patch.object(glob, "glob") |
| def test_get_crash_reproducers_no_matching_script( |
| self, mock_file_search, mock_file_check |
| ): |
| def silence_logging(): |
| root = logging.getLogger() |
| filt = self._SilencingFilter() |
| root.addFilter(filt) |
| self.addCleanup(root.removeFilter, filt) |
| return filt |
| |
| log_filter = silence_logging() |
| working_dir = "SomeDirectory" |
| mock_file_search.return_value = ["a.c", "b.cpp", "c.cc"] |
| mock_file_check.side_effect = [True, False, True] |
| results = bisect_clang_crashes.get_crash_reproducers(working_dir) |
| mock_file_search.assert_called_once_with("%s/*.c*" % working_dir) |
| self.assertEqual(mock_file_check.call_count, 3) |
| self.assertEqual(mock_file_check.call_args_list[0], mock.call("a.sh")) |
| self.assertEqual(mock_file_check.call_args_list[1], mock.call("b.sh")) |
| self.assertEqual(mock_file_check.call_args_list[2], mock.call("c.sh")) |
| self.assertEqual(results, [("a.c", "a.sh"), ("c.cc", "c.sh")]) |
| self.assertTrue( |
| any( |
| "could not find the matching script of b.cpp" in x |
| for x in log_filter.messages |
| ), |
| log_filter.messages, |
| ) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |