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