1# Copyright 2024 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from __future__ import annotations 6 7import os 8import pathlib 9import unittest 10from unittest import mock 11 12import crossbench.path as pth 13from crossbench import plt 14from crossbench.plt import PLATFORM 15from crossbench.plt.bin import (Binary, BinaryNotFoundError, LinuxBinary, 16 MacOsBinary, PosixBinary, WinBinary) 17from tests import test_helper 18from tests.crossbench.base import CrossbenchFakeFsTestCase 19from tests.crossbench.mock_helper import (LinuxMockPlatform, MacOsMockPlatform, 20 WinMockPlatform) 21 22 23class BinaryTestCase(CrossbenchFakeFsTestCase): 24 25 def setUp(self) -> None: 26 super().setUp() 27 self._all_mock_platforms = ( 28 LinuxMockPlatform(), 29 MacOsMockPlatform(), 30 WinMockPlatform(), 31 # TODO: add adb testing 32 ) 33 self._all_platforms = (PLATFORM,) + self._all_mock_platforms 34 35 def all_mock_platforms(self): 36 yield from self._all_mock_platforms 37 38 def all_platforms(self): 39 yield from self._all_platforms 40 41 def create_binary_path(self, path: pth.AnyPathLike) -> pth.LocalPath: 42 result = pth.LocalPath(path) 43 self.fs.create_file(result, st_size=100) 44 return result 45 46 def test_create_without_binary(self): 47 with self.assertRaises(ValueError): 48 Binary(name="test") 49 with self.assertRaises(ValueError): 50 Binary(name="test", posix="") 51 52 def test_new_windows_binary_invalid(self): 53 with self.assertRaises(ValueError): 54 WinBinary("custom") 55 with self.assertRaises(ValueError): 56 WinBinary(pth.AnyPath("custom")) 57 with self.assertRaises(ValueError): 58 WinBinary(pth.AnyPath("foo/bar/custom.py")) 59 60 def test_new_windows_binary(self): 61 binary = WinBinary("crossbench_mock_binary.exe") 62 self.assertEqual(binary.name, "crossbench_mock_binary.exe") 63 platform = WinMockPlatform() 64 path = platform.local_path("C:/Users/user-name/AppData/Local/Programs/" 65 "crossbench/crossbench_mock_binary.exe") 66 with self.assertRaises(ValueError): 67 with platform.override_binary(binary, path): 68 self.assertEqual(binary.resolve(platform), path) 69 70 self.fs.create_file(path, st_size=100) 71 with mock.patch("shutil.which", return_value=path) as cm: 72 with platform.override_binary(binary, path): 73 self.assertEqual(binary.resolve(platform), path) 74 self.assertEqual(binary.resolve_cached(platform), path) 75 cm.assert_called_once_with(os.fspath(path)) 76 77 # Still cached 78 self.assertEqual(binary.resolve_cached(platform), path) 79 with self.assertRaises(BinaryNotFoundError): 80 self.assertEqual(binary.resolve(platform), path) 81 82 binary.resolve_cached.cache_clear() 83 with self.assertRaises(BinaryNotFoundError): 84 self.assertEqual(binary.resolve(platform), path) 85 with self.assertRaises(BinaryNotFoundError): 86 self.assertEqual(binary.resolve_cached(platform), path) 87 88 def test_basic_accessor(self): 89 binary = Binary("test", default="foo/bar/test") 90 self.assertEqual(binary.name, "test") 91 92 def test_basic_accessor_multiple(self): 93 binary = Binary("test", default=("foo/bar/test1", "foo/bar/test2")) 94 self.assertEqual(binary.name, "test") 95 96 def test_unknown_binary(self): 97 binary = Binary("crossbench_mock_binary", default="crossbench_mock_binary") 98 for platform in self.all_platforms(): 99 with self.assertRaises(BinaryNotFoundError): 100 binary.resolve(platform) 101 102 def test_known_binary_default(self): 103 for platform in self.all_mock_platforms(): 104 with self.subTest(platform=platform): 105 default = pth.AnyPath("foo/bar/default/crossbench_mock_binary") 106 result = default 107 if platform.is_win: 108 result = pth.AnyPath("foo/bar/default/crossbench_mock_binary.exe") 109 binary = Binary("crossbench_mock_binary", default=default) 110 self.assertEqual(binary.platform_path(platform), (pth.AnyPath(result),)) 111 with self.assertRaises(BinaryNotFoundError): 112 binary.resolve(platform) 113 with self.assertRaises(BinaryNotFoundError): 114 binary.resolve_cached(platform) 115 self.fs.create_file(result, st_size=100) 116 self.assertEqual(pth.AnyPath(binary.resolve(platform)), result) 117 self.assertEqual(pth.AnyPath(binary.resolve_cached(platform)), result) 118 self.fs.remove(result) 119 120 def test_known_binary_default_multiple(self): 121 for platform in self.all_mock_platforms(): 122 with self.subTest(platform=platform): 123 default_miss = pth.AnyPath("foo/bar/default/fake") 124 default = pth.AnyPath("foo/bar/default/crossbench_mock_binary") 125 result = default 126 if platform.is_win: 127 default_miss = pth.AnyPath("foo/bar/default/fake.exe") 128 result = pth.AnyPath("foo/bar/default/crossbench_mock_binary.exe") 129 binary = Binary( 130 "crossbench_mock_binary", default=(default_miss, default)) 131 self.assertEqual( 132 binary.platform_path(platform), ( 133 pth.AnyPath(default_miss), 134 pth.AnyPath(result), 135 )) 136 with self.assertRaises(BinaryNotFoundError): 137 binary.resolve(platform) 138 with self.assertRaises(BinaryNotFoundError): 139 binary.resolve_cached(platform) 140 self.fs.create_file(result, st_size=100) 141 self.assertEqual(pth.AnyPath(binary.resolve(platform)), result) 142 self.assertEqual(pth.AnyPath(binary.resolve_cached(platform)), result) 143 self.fs.remove(result) 144 145 @unittest.skipUnless(plt.PLATFORM.is_posix, "Only supported on posix") 146 def test_known_binary_linux(self): 147 result = self.create_binary_path( 148 pth.AnyPosixPath("foo/bar/default/crossbench_mock_binary")) 149 binary = Binary("crossbench_mock_binary", linux=result) 150 self.validate_known_binary_linux(result, binary) 151 binary = LinuxBinary(result) 152 self.validate_known_binary_linux(result, binary) 153 154 def validate_known_binary_linux(self, result, binary): 155 result = pth.AnyPosixPath(result) 156 platform = LinuxMockPlatform() 157 self.assertEqual(str(binary.resolve(platform)), str(result)) 158 self.assertEqual(str(binary.resolve_cached(platform)), str(result)) 159 160 for platform in self.all_mock_platforms(): 161 if platform.is_linux: 162 continue 163 self.assertEqual(binary.platform_path(platform), ()) 164 with self.assertRaises(BinaryNotFoundError): 165 binary.resolve(platform) 166 with self.assertRaises(BinaryNotFoundError): 167 binary.resolve_cached(platform) 168 169 @unittest.skipUnless(plt.PLATFORM.is_posix, "Only supported on posix") 170 def test_known_binary_macos(self): 171 result = self.create_binary_path("foo/bar/default/crossbench_mock_binary") 172 binary = Binary("crossbench_mock_binary", macos=result) 173 self.validate_known_binary_macos(result, binary) 174 binary = MacOsBinary(result) 175 self.validate_known_binary_macos(result, binary) 176 177 def validate_known_binary_macos(self, result, binary): 178 platform = MacOsMockPlatform() 179 self.assertEqual(binary.resolve(platform), result) 180 self.assertEqual(binary.resolve_cached(platform), result) 181 182 for platform in self.all_mock_platforms(): 183 if platform.is_macos: 184 continue 185 self.assertEqual(binary.platform_path(platform), ()) 186 with self.assertRaises(BinaryNotFoundError): 187 binary.resolve(platform) 188 with self.assertRaises(BinaryNotFoundError): 189 binary.resolve_cached(platform) 190 191 @unittest.skipUnless(plt.PLATFORM.is_posix, "Only supported on posix") 192 def test_known_binary_posix(self): 193 result = self.create_binary_path("foo/bar/default/crossbench_mock_binary") 194 binary = Binary("crossbench_mock_binary", posix=result) 195 self.validate_known_binary_posix(result, binary) 196 binary = PosixBinary(result) 197 self.validate_known_binary_posix(result, binary) 198 199 def validate_known_binary_posix(self, result, binary): 200 for platform in self.all_mock_platforms(): 201 if not platform.is_posix: 202 continue 203 self.assertEqual(binary.resolve(platform), result) 204 self.assertEqual(binary.resolve_cached(platform), result) 205 206 for platform in self.all_mock_platforms(): 207 if platform.is_posix: 208 continue 209 self.assertEqual(binary.platform_path(platform), ()) 210 with self.assertRaises(BinaryNotFoundError): 211 binary.resolve(platform) 212 with self.assertRaises(BinaryNotFoundError): 213 binary.resolve_cached(platform) 214 215 def test_known_binary_win(self): 216 result = self.create_binary_path( 217 "foo/bar/default/crossbench_mock_binary.exe") 218 result = pathlib.PureWindowsPath(result) 219 binary = Binary("crossbench_mock_binary", win=result) 220 self.validate_known_binary_win(result, binary) 221 binary = WinBinary(result) 222 self.validate_known_binary_win(result, binary) 223 224 def validate_known_binary_win(self, result, binary): 225 platform = WinMockPlatform() 226 self.assertEqual(binary.resolve(platform), result) 227 self.assertEqual(binary.resolve_cached(platform), result) 228 229 for platform in self.all_mock_platforms(): 230 if platform.is_win: 231 continue 232 self.assertEqual(binary.platform_path(platform), ()) 233 with self.assertRaises(BinaryNotFoundError): 234 binary.resolve(platform) 235 with self.assertRaises(BinaryNotFoundError): 236 binary.resolve_cached(platform) 237 238 239if __name__ == "__main__": 240 test_helper.run_pytest(__file__) 241