1#!/usr/bin/env python3 2# Copyright 2019 The ChromiumOS Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Unit tests for retrieving the LLVM hash.""" 7 8import contextlib 9from pathlib import Path 10import shutil 11import subprocess 12import tempfile 13import textwrap 14import unittest 15from unittest import mock 16 17import get_llvm_hash 18import llvm_next 19import subprocess_helpers 20 21 22# We grab protected stuff from get_llvm_hash. That's OK. 23# pylint: disable=protected-access 24 25 26def mock_run_results(returncode: int, stderr: bytes) -> mock.MagicMock: 27 m = mock.MagicMock() 28 m.returncode = returncode 29 m.stderr = stderr 30 return m 31 32 33class TestGetLLVMHash(unittest.TestCase): 34 """The LLVMHash test class.""" 35 36 def make_tempdir(self): 37 d = Path(tempfile.mkdtemp(prefix="get_llvm_hash_unittest_")) 38 self.addCleanup(shutil.rmtree, d) 39 return d 40 41 def setUp(self): 42 # We mock out quite a bit. Ensure every test is self-contained. 43 get_llvm_hash.GetLLVMMajorVersion.cache_clear() 44 45 @mock.patch.object(subprocess, "run") 46 def testCloneRepoSucceedsWhenGitSucceeds(self, run_mock): 47 run_mock.return_value = mock_run_results(returncode=0, stderr=b"") 48 llvm_hash = get_llvm_hash.LLVMHash() 49 50 into_tempdir = "/tmp/tmpTest" 51 llvm_hash.CloneLLVMRepo(into_tempdir) 52 run_mock.assert_called_with( 53 ["git", "clone", get_llvm_hash._LLVM_GIT_URL, into_tempdir], 54 check=False, 55 stderr=subprocess.PIPE, 56 ) 57 58 @mock.patch.object(subprocess, "run") 59 def testCloneRepoFailsWhenGitFails(self, run_mock): 60 run_mock.return_value = mock_run_results( 61 returncode=1, stderr=b"some stderr" 62 ) 63 64 with self.assertRaisesRegex(ValueError, "Failed to clone.*some stderr"): 65 get_llvm_hash.LLVMHash().CloneLLVMRepo("/tmp/tmp1") 66 67 @mock.patch.object(get_llvm_hash, "GetGitHashFrom") 68 def testGetGitHashWorks(self, mock_get_git_hash): 69 mock_get_git_hash.return_value = "a13testhash2" 70 71 self.assertEqual( 72 get_llvm_hash.GetGitHashFrom("/tmp/tmpTest", 100), "a13testhash2" 73 ) 74 75 mock_get_git_hash.assert_called_once() 76 77 @mock.patch.object(get_llvm_hash.LLVMHash, "GetLLVMHash") 78 @mock.patch.object(get_llvm_hash, "GetGoogle3LLVMVersion") 79 def testReturnGoogle3LLVMHash( 80 self, mock_google3_llvm_version, mock_get_llvm_hash 81 ): 82 mock_get_llvm_hash.return_value = "a13testhash3" 83 mock_google3_llvm_version.return_value = 1000 84 self.assertEqual( 85 get_llvm_hash.LLVMHash().GetGoogle3LLVMHash(), "a13testhash3" 86 ) 87 mock_get_llvm_hash.assert_called_once_with(1000) 88 89 @mock.patch.object(get_llvm_hash.LLVMHash, "GetLLVMHash") 90 @mock.patch.object(get_llvm_hash, "GetGoogle3LLVMVersion") 91 def testReturnGoogle3UnstableLLVMHash( 92 self, mock_google3_llvm_version, mock_get_llvm_hash 93 ): 94 mock_get_llvm_hash.return_value = "a13testhash3" 95 mock_google3_llvm_version.return_value = 1000 96 self.assertEqual( 97 get_llvm_hash.LLVMHash().GetGoogle3UnstableLLVMHash(), 98 "a13testhash3", 99 ) 100 mock_get_llvm_hash.assert_called_once_with(1000) 101 102 @mock.patch.object(subprocess, "check_output") 103 def testSuccessfullyGetGitHashFromToTOfLLVM(self, mock_check_output): 104 mock_check_output.return_value = "a123testhash1 path/to/main\n" 105 self.assertEqual( 106 get_llvm_hash.LLVMHash().GetTopOfTrunkGitHash(), "a123testhash1" 107 ) 108 mock_check_output.assert_called_once() 109 110 @mock.patch.object(subprocess, "Popen") 111 def testCheckoutBranch(self, mock_popen): 112 mock_popen.return_value = contextlib.nullcontext( 113 mock.MagicMock(communicate=lambda: (None, None), returncode=0) 114 ) 115 get_llvm_hash.CheckoutBranch("fake/src_dir", "fake_branch") 116 self.assertEqual( 117 mock_popen.call_args_list[0][0], 118 (["git", "-C", "fake/src_dir", "checkout", "fake_branch"],), 119 ) 120 self.assertEqual( 121 mock_popen.call_args_list[1][0], 122 (["git", "-C", "fake/src_dir", "pull"],), 123 ) 124 125 def testParseLLVMMajorVersion(self): 126 cmakelist_42 = ( 127 "set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON)\n" 128 "if(NOT DEFINED LLVM_VERSION_MAJOR)\n" 129 " set(LLVM_VERSION_MAJOR 42)\n" 130 "endif()" 131 ) 132 self.assertEqual( 133 get_llvm_hash.ParseLLVMMajorVersion(cmakelist_42), "42" 134 ) 135 136 def testParseLLVMMajorVersionInvalid(self): 137 invalid_cmakelist = "invalid cmakelist.txt contents" 138 self.assertIsNone( 139 get_llvm_hash.ParseLLVMMajorVersion(invalid_cmakelist) 140 ) 141 142 @mock.patch.object(get_llvm_hash, "GetAndUpdateLLVMProjectInLLVMTools") 143 @mock.patch.object(subprocess_helpers, "CheckCommand") 144 def testGetLLVMMajorVersionWithOldPath( 145 self, 146 _mock_check_command, 147 mock_update_project, 148 ): 149 src_dir = self.make_tempdir() 150 mock_update_project.return_value = str(src_dir) 151 152 cmakelists = Path(src_dir) / "llvm" / "CMakeLists.txt" 153 cmakelists.parent.mkdir(parents=True) 154 cmakelists.write_text( 155 textwrap.dedent( 156 """ 157 if(NOT DEFINED LLVM_VERSION_MAJOR) 158 set(LLVM_VERSION_MAJOR 12345) 159 endif() 160 """ 161 ), 162 encoding="utf-8", 163 ) 164 self.assertEqual(get_llvm_hash.GetLLVMMajorVersion(), "12345") 165 166 @mock.patch.object(get_llvm_hash, "GetAndUpdateLLVMProjectInLLVMTools") 167 @mock.patch.object(subprocess_helpers, "CheckCommand") 168 def testGetLLVMMajorVersionWithNewPath( 169 self, 170 _mock_check_command, 171 mock_update_project, 172 ): 173 src_dir = self.make_tempdir() 174 mock_update_project.return_value = str(src_dir) 175 176 old_cmakelists = Path(src_dir) / "llvm" / "CMakeLists.txt" 177 old_cmakelists.parent.mkdir(parents=True) 178 old_cmakelists.write_text( 179 textwrap.dedent( 180 """ 181 Some text 182 that has 183 nothing to do with 184 LLVM_VERSION_MAJOR 185 """ 186 ), 187 encoding="utf-8", 188 ) 189 190 new_cmakelists = ( 191 Path(src_dir) / "cmake" / "Modules" / "LLVMVersion.cmake" 192 ) 193 new_cmakelists.parent.mkdir(parents=True) 194 new_cmakelists.write_text( 195 textwrap.dedent( 196 """ 197 if(NOT DEFINED LLVM_VERSION_MAJOR) 198 set(LLVM_VERSION_MAJOR 5432) 199 endif() 200 """ 201 ), 202 encoding="utf-8", 203 ) 204 205 self.assertEqual(get_llvm_hash.GetLLVMMajorVersion(), "5432") 206 207 def testGetLLVMNextHash(self): 208 self.assertEqual( 209 get_llvm_hash.LLVMHash().GetCrOSLLVMNextHash(), 210 llvm_next.LLVM_NEXT_HASH, 211 ) 212 213 214if __name__ == "__main__": 215 unittest.main() 216