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