• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2020 The ChromiumOS Authors
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Tests for bisect_clang_crashes."""
8
9import glob
10import logging
11import os.path
12import subprocess
13import unittest
14import unittest.mock as mock
15
16import bisect_clang_crashes
17
18
19class Test(unittest.TestCase):
20    """Tests for bisect_clang_crashes."""
21
22    class _SilencingFilter(object):
23        """Silences all log messages.
24
25        Also collects info about log messages that would've been emitted.
26        """
27
28        def __init__(self):
29            self.messages = []
30
31        def filter(self, record):
32            self.messages.append(record.getMessage())
33            return 0
34
35    @mock.patch.object(subprocess, "check_output")
36    def test_get_artifacts(self, mock_gsutil_ls):
37        pattern = (
38            "gs://chromeos-toolchain-artifacts/clang-crash-diagnoses/"
39            "**/*clang_crash_diagnoses.tar.xz"
40        )
41        mock_gsutil_ls.return_value = "artifact1\nartifact2\nartifact3"
42        results = bisect_clang_crashes.get_artifacts(pattern)
43        self.assertEqual(results, ["artifact1", "artifact2", "artifact3"])
44        mock_gsutil_ls.assert_called_once_with(
45            ["gsutil.py", "ls", pattern],
46            stderr=subprocess.STDOUT,
47            encoding="utf-8",
48        )
49
50    @mock.patch.object(os.path, "exists")
51    @mock.patch.object(glob, "glob")
52    def test_get_crash_reproducers_succeed(
53        self, mock_file_search, mock_file_check
54    ):
55        working_dir = "SomeDirectory"
56        mock_file_search.return_value = ["a.c", "b.cpp", "c.cc"]
57        mock_file_check.side_effect = [True, True, True]
58        results = bisect_clang_crashes.get_crash_reproducers(working_dir)
59        mock_file_search.assert_called_once_with("%s/*.c*" % working_dir)
60        self.assertEqual(mock_file_check.call_count, 3)
61        self.assertEqual(mock_file_check.call_args_list[0], mock.call("a.sh"))
62        self.assertEqual(mock_file_check.call_args_list[1], mock.call("b.sh"))
63        self.assertEqual(mock_file_check.call_args_list[2], mock.call("c.sh"))
64        self.assertEqual(
65            results, [("a.c", "a.sh"), ("b.cpp", "b.sh"), ("c.cc", "c.sh")]
66        )
67
68    @mock.patch.object(os.path, "exists")
69    @mock.patch.object(glob, "glob")
70    def test_get_crash_reproducers_no_matching_script(
71        self, mock_file_search, mock_file_check
72    ):
73        def silence_logging():
74            root = logging.getLogger()
75            filt = self._SilencingFilter()
76            root.addFilter(filt)
77            self.addCleanup(root.removeFilter, filt)
78            return filt
79
80        log_filter = silence_logging()
81        working_dir = "SomeDirectory"
82        mock_file_search.return_value = ["a.c", "b.cpp", "c.cc"]
83        mock_file_check.side_effect = [True, False, True]
84        results = bisect_clang_crashes.get_crash_reproducers(working_dir)
85        mock_file_search.assert_called_once_with("%s/*.c*" % working_dir)
86        self.assertEqual(mock_file_check.call_count, 3)
87        self.assertEqual(mock_file_check.call_args_list[0], mock.call("a.sh"))
88        self.assertEqual(mock_file_check.call_args_list[1], mock.call("b.sh"))
89        self.assertEqual(mock_file_check.call_args_list[2], mock.call("c.sh"))
90        self.assertEqual(results, [("a.c", "a.sh"), ("c.cc", "c.sh")])
91        self.assertTrue(
92            any(
93                "could not find the matching script of b.cpp" in x
94                for x in log_filter.messages
95            ),
96            log_filter.messages,
97        )
98
99
100if __name__ == "__main__":
101    unittest.main()
102