• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2020 The Chromium OS Authors. All rights reserved.
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 = 'gs://chromeos-toolchain-artifacts/clang-crash-diagnoses/' \
38              '**/*clang_crash_diagnoses.tar.xz'
39    mock_gsutil_ls.return_value = 'artifact1\nartifact2\nartifact3'
40    results = bisect_clang_crashes.get_artifacts(pattern)
41    self.assertEqual(results, ['artifact1', 'artifact2', 'artifact3'])
42    mock_gsutil_ls.assert_called_once_with(['gsutil.py', 'ls', pattern],
43                                           stderr=subprocess.STDOUT,
44                                           encoding='utf-8')
45
46  @mock.patch.object(os.path, 'exists')
47  @mock.patch.object(glob, 'glob')
48  def test_get_crash_reproducers_succeed(self, mock_file_search,
49                                         mock_file_check):
50    working_dir = 'SomeDirectory'
51    mock_file_search.return_value = ['a.c', 'b.cpp', 'c.cc']
52    mock_file_check.side_effect = [True, True, True]
53    results = bisect_clang_crashes.get_crash_reproducers(working_dir)
54    mock_file_search.assert_called_once_with('%s/*.c*' % working_dir)
55    self.assertEqual(mock_file_check.call_count, 3)
56    self.assertEqual(mock_file_check.call_args_list[0], mock.call('a.sh'))
57    self.assertEqual(mock_file_check.call_args_list[1], mock.call('b.sh'))
58    self.assertEqual(mock_file_check.call_args_list[2], mock.call('c.sh'))
59    self.assertEqual(results, [('a.c', 'a.sh'), ('b.cpp', 'b.sh'),
60                               ('c.cc', 'c.sh')])
61
62  @mock.patch.object(os.path, 'exists')
63  @mock.patch.object(glob, 'glob')
64  def test_get_crash_reproducers_no_matching_script(self, mock_file_search,
65                                                    mock_file_check):
66
67    def silence_logging():
68      root = logging.getLogger()
69      filt = self._SilencingFilter()
70      root.addFilter(filt)
71      self.addCleanup(root.removeFilter, filt)
72      return filt
73
74    log_filter = silence_logging()
75    working_dir = 'SomeDirectory'
76    mock_file_search.return_value = ['a.c', 'b.cpp', 'c.cc']
77    mock_file_check.side_effect = [True, False, True]
78    results = bisect_clang_crashes.get_crash_reproducers(working_dir)
79    mock_file_search.assert_called_once_with('%s/*.c*' % working_dir)
80    self.assertEqual(mock_file_check.call_count, 3)
81    self.assertEqual(mock_file_check.call_args_list[0], mock.call('a.sh'))
82    self.assertEqual(mock_file_check.call_args_list[1], mock.call('b.sh'))
83    self.assertEqual(mock_file_check.call_args_list[2], mock.call('c.sh'))
84    self.assertEqual(results, [('a.c', 'a.sh'), ('c.cc', 'c.sh')])
85    self.assertTrue(
86        any('could not find the matching script of b.cpp' in x
87            for x in log_filter.messages), log_filter.messages)
88
89
90if __name__ == '__main__':
91  unittest.main()
92