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