Skip to content

Commit f2e583d

Browse files
Retain Os and Arch classes for stronger typing
1 parent 1848685 commit f2e583d

5 files changed

Lines changed: 96 additions & 69 deletions

File tree

src/pysonar_scanner/configuration/dynamic_defaults_loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ def load() -> Dict[Key, any]:
2929
Load dynamically computed default properties
3030
"""
3131
properties = {
32-
SONAR_SCANNER_OS: utils.get_os(),
33-
SONAR_SCANNER_ARCH: utils.get_arch(),
32+
SONAR_SCANNER_OS: utils.get_os().value,
33+
SONAR_SCANNER_ARCH: utils.get_arch().value,
3434
SONAR_PROJECT_BASE_DIR: os.getcwd(),
3535
}
3636
return properties

src/pysonar_scanner/utils.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import pathlib
2222
import platform
2323
import typing
24+
from enum import Enum
2425

2526
OsStr = typing.Literal["windows", "linux", "mac", "alpine", "other"]
2627
ArchStr = typing.Literal["x86_64", "aarch64", "x86"]
@@ -37,7 +38,15 @@ def calculate_checksum(filehandle: typing.BinaryIO) -> str:
3738
return sha256_hash.hexdigest()
3839

3940

40-
def get_os() -> OsStr:
41+
class Os(Enum):
42+
WINDOWS: OsStr = "windows"
43+
LINUX: OsStr = "linux"
44+
MACOS: OsStr = "mac"
45+
ALPINE: OsStr = "alpine"
46+
OTHER: OsStr = "other"
47+
48+
49+
def get_os() -> Os:
4150
def is_alpine() -> bool:
4251
try:
4352
os_release = pathlib.Path("/etc/os-release")
@@ -52,24 +61,30 @@ def is_alpine() -> bool:
5261

5362
os_name = platform.system()
5463
if os_name == "Windows":
55-
return "windows"
64+
return Os.WINDOWS
5665
elif os_name == "Darwin":
57-
return "mac"
66+
return Os.MACOS
5867
elif os_name == "Linux":
5968
if is_alpine():
60-
return "alpine"
69+
return Os.ALPINE
6170
else:
62-
return "linux"
63-
return "other"
71+
return Os.LINUX
72+
return Os.OTHER
73+
74+
75+
class Arch(Enum):
76+
X64: ArchStr = "x64"
77+
AARCH64: ArchStr = "aarch64"
78+
OTHER: ArchStr = "other"
6479

6580

66-
def get_arch() -> ArchStr:
81+
def get_arch() -> Arch:
6782
machine = platform.machine().lower()
6883
if machine in ["amd64", "x86_64"]:
69-
return "x64"
84+
return Arch.X64
7085
elif machine == "arm64":
71-
return "aarch64"
72-
return "other"
86+
return Arch.AARCH64
87+
return Arch.OTHER
7388

7489

7590
def filter_none_values(dictionary: dict) -> dict:

tests/test_configuration_loader.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@
5151
)
5252
from pysonar_scanner.configuration.configuration_loader import ConfigurationLoader, SONAR_PROJECT_BASE_DIR
5353
from pysonar_scanner.exceptions import MissingKeyException
54+
from pysonar_scanner.utils import Arch, Os
5455

5556

5657
# Mock utils.get_os and utils.get_arch at the module level
57-
@patch("pysonar_scanner.utils.get_arch", return_value="x64")
58-
@patch("pysonar_scanner.utils.get_os", return_value="linux")
58+
@patch("pysonar_scanner.utils.get_arch", return_value=Arch.X64)
59+
@patch("pysonar_scanner.utils.get_os", return_value=Os.LINUX)
5960
class TestConfigurationLoader(pyfakefs.TestCase):
6061
def setUp(self):
6162
self.maxDiff = None
@@ -80,8 +81,8 @@ def test_defaults(self, mock_get_os, mock_get_arch):
8081
SONAR_SCANNER_RESPONSE_TIMEOUT: 0,
8182
SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit",
8283
SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit",
83-
SONAR_SCANNER_OS: "linux",
84-
SONAR_SCANNER_ARCH: "x64",
84+
SONAR_SCANNER_OS: Os.LINUX.value,
85+
SONAR_SCANNER_ARCH: Arch.X64.value,
8586
}
8687
self.assertDictEqual(configuration, expected_configuration)
8788

@@ -102,8 +103,8 @@ def test_dynamic_defaults_are_loaded(self, get_static_default_properties_mock, m
102103
config,
103104
{
104105
SONAR_PROJECT_BASE_DIR: "/",
105-
SONAR_SCANNER_OS: "linux",
106-
SONAR_SCANNER_ARCH: "x64",
106+
SONAR_SCANNER_OS: Os.LINUX.value,
107+
SONAR_SCANNER_ARCH: Arch.X64.value,
107108
},
108109
)
109110

@@ -146,8 +147,8 @@ def test_load_sonar_project_properties(self, mock_get_os, mock_get_arch):
146147
SONAR_SCANNER_RESPONSE_TIMEOUT: 0,
147148
SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit",
148149
SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit",
149-
SONAR_SCANNER_OS: "linux",
150-
SONAR_SCANNER_ARCH: "x64",
150+
SONAR_SCANNER_OS: Os.LINUX.value,
151+
SONAR_SCANNER_ARCH: Arch.X64.value,
151152
}
152153
self.assertDictEqual(configuration, expected_configuration)
153154

@@ -194,8 +195,8 @@ def test_load_sonar_project_properties_from_custom_path(self, mock_get_os, mock_
194195
SONAR_SCANNER_RESPONSE_TIMEOUT: 0,
195196
SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit",
196197
SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit",
197-
SONAR_SCANNER_OS: "linux",
198-
SONAR_SCANNER_ARCH: "x64",
198+
SONAR_SCANNER_OS: Os.LINUX.value,
199+
SONAR_SCANNER_ARCH: Arch.X64.value,
199200
}
200201
self.assertDictEqual(configuration, expected_configuration)
201202

@@ -243,8 +244,8 @@ def test_load_pyproject_toml_from_base_dir(self, mock_get_os, mock_get_arch):
243244
SONAR_SCANNER_RESPONSE_TIMEOUT: 0,
244245
SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit",
245246
SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit",
246-
SONAR_SCANNER_OS: "linux",
247-
SONAR_SCANNER_ARCH: "x64",
247+
SONAR_SCANNER_OS: Os.LINUX.value,
248+
SONAR_SCANNER_ARCH: Arch.X64.value,
248249
}
249250
self.assertDictEqual(configuration, expected_configuration)
250251

@@ -292,8 +293,8 @@ def test_load_pyproject_toml_from_toml_path(self, mock_get_os, mock_get_arch):
292293
SONAR_SCANNER_RESPONSE_TIMEOUT: 0,
293294
SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit",
294295
SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit",
295-
SONAR_SCANNER_OS: "linux",
296-
SONAR_SCANNER_ARCH: "x64",
296+
SONAR_SCANNER_OS: Os.LINUX.value,
297+
SONAR_SCANNER_ARCH: Arch.X64.value,
297298
TOML_PATH: "custom/path",
298299
}
299300
self.assertDictEqual(configuration, expected_configuration)
@@ -407,8 +408,8 @@ def test_properties_priority(self, mock_get_os, mock_get_arch):
407408

408409

409410
# If you have test functions outside of classes, use patch as a decorator for each function
410-
@patch("pysonar_scanner.utils.get_arch", return_value="x64")
411-
@patch("pysonar_scanner.utils.get_os", return_value="linux")
411+
@patch("pysonar_scanner.utils.get_arch", return_value=Arch.X64.value)
412+
@patch("pysonar_scanner.utils.get_os", return_value=Os.LINUX.value)
412413
def test_standalone_function(mock_get_os, mock_get_arch):
413414
# ...existing test code...
414415
pass

tests/test_jre.py

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@
3434
)
3535
from pysonar_scanner.exceptions import ChecksumException, NoJreAvailableException, UnsupportedArchiveFormat
3636
from pysonar_scanner.jre import JREProvisioner, JREResolvedPath, JREResolver, JREResolverConfiguration
37+
from pysonar_scanner.utils import Os, Arch
3738
from tests import sq_api_utils
3839

3940
import zipfile
4041

4142

42-
@patch("pysonar_scanner.utils.get_os", return_value="linux")
43-
@patch("pysonar_scanner.utils.get_arch", return_value="aarch64")
43+
@patch("pysonar_scanner.utils.get_os", return_value=Os.LINUX)
44+
@patch("pysonar_scanner.utils.get_arch", return_value=Arch.AARCH64)
4445
class TestJREProvisioner(pyfakefs.TestCase):
4546
def setUp(self):
4647
self.setUpPyfakefs(allow_root_user=False)
@@ -56,7 +57,7 @@ def setUp(self):
5657
filename="fake_jre.zip",
5758
sha256="fakechecksum",
5859
java_path="fake_java",
59-
os="windows",
60+
os=Os.WINDOWS.value,
6061
arch="x64",
6162
download_url="http://example.com/fake_jre.zip",
6263
)
@@ -74,8 +75,8 @@ def __setup_zip_file(self):
7475
filename=self.zip_name,
7576
sha256=self.zip_checksum,
7677
java_path="java",
77-
os="linux",
78-
arch="aarch64",
78+
os=Os.LINUX.value,
79+
arch=Arch.AARCH64.value,
7980
download_url=None,
8081
)
8182

@@ -94,8 +95,8 @@ def __setup_tar_file(self):
9495
filename=self.tar_gz_name,
9596
sha256=self.tar_gz_checksum,
9697
java_path="java",
97-
os="linux",
98-
arch="aarch64",
98+
os=Os.LINUX.value,
99+
arch=Arch.AARCH64.value,
99100
download_url=None,
100101
)
101102

@@ -107,14 +108,14 @@ def __setup_tar_file(self):
107108
filename=self.tgz_name,
108109
sha256=self.tgz_checksum,
109110
java_path="java",
110-
os="linux",
111-
arch="aarch64",
111+
os=Os.LINUX.value,
112+
arch=Arch.AARCH64.value,
112113
download_url=None,
113114
)
114115

115116
def test_if_patching_worked(self, get_os_mock, get_arch_mock):
116-
self.assertEqual(utils.get_os(), "linux")
117-
self.assertEqual(utils.get_arch(), "aarch64")
117+
self.assertEqual(utils.get_os(), Os.LINUX)
118+
self.assertEqual(utils.get_arch(), Arch.AARCH64)
118119

119120
def test_successfully_downloading_jre(self, get_os_mock, get_arch_mock):
120121
class JRETestCase(TypedDict):
@@ -138,11 +139,13 @@ class JRETestCase(TypedDict):
138139
jres = [testcase_jre, self.other_jre]
139140
with self.subTest(jre=testcase), sq_api_utils.sq_api_mocker() as mocker:
140141
mocker.mock_analysis_jres(
141-
body=[sq_api_utils.jre_to_dict(jre) for jre in jres], os_matcher="linux", arch_matcher="aarch64"
142+
body=[sq_api_utils.jre_to_dict(jre) for jre in jres],
143+
os_matcher=Os.LINUX.value,
144+
arch_matcher=Arch.AARCH64.value,
142145
)
143146
mocker.mock_analysis_jre_download(id=testcase_jre.id, body=testcase["bytes"], status=200)
144147

145-
provisioner = JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch())
148+
provisioner = JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value)
146149
jre_path = provisioner.provision()
147150

148151
cache_file = self.cache.get_file(testcase_jre.filename, testcase["checksum"])
@@ -159,36 +162,40 @@ def test_invalid_checksum(self, *args):
159162
with self.assertRaises(ChecksumException), sq_api_utils.sq_api_mocker() as mocker:
160163
jre_dict = sq_api_utils.jre_to_dict(self.zip_jre)
161164
jre_dict["sha256"] = "invalid"
162-
mocker.mock_analysis_jres(body=[jre_dict], os_matcher="linux", arch_matcher="aarch64")
165+
mocker.mock_analysis_jres(body=[jre_dict], os_matcher=Os.LINUX.value, arch_matcher=Arch.AARCH64.value)
163166
mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200)
164167

165-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
168+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
166169

167170
def test_retry_mechanism(self, *args):
168171
with sq_api_utils.sq_api_mocker() as mocker:
169172
jre_dict_with_invalid_checksum = sq_api_utils.jre_to_dict(self.zip_jre)
170173
jre_dict_with_invalid_checksum["sha256"] = "invalid"
171174

172175
jre_dict = sq_api_utils.jre_to_dict(self.zip_jre)
173-
mocker.mock_analysis_jres(body=[jre_dict_with_invalid_checksum], os_matcher="linux", arch_matcher="aarch64")
174-
mocker.mock_analysis_jres(body=[jre_dict], os_matcher="linux", arch_matcher="aarch64")
176+
mocker.mock_analysis_jres(
177+
body=[jre_dict_with_invalid_checksum], os_matcher=Os.LINUX.value, arch_matcher=Arch.AARCH64.value
178+
)
179+
mocker.mock_analysis_jres(body=[jre_dict], os_matcher=Os.LINUX.value, arch_matcher=Arch.AARCH64.value)
175180
mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200)
176181

177-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
182+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
178183

179184
cache_file = self.cache.get_file(self.zip_jre.filename, self.zip_checksum)
180185
self.assertTrue(cache_file.is_valid())
181186

182187
def test_already_cached(self, *args):
183188
with sq_api_utils.sq_api_mocker(assert_all_requests_are_fired=False) as mocker:
184189
jre_dict = sq_api_utils.jre_to_dict(self.zip_jre)
185-
metadata_rsps = mocker.mock_analysis_jres(body=[jre_dict], os_matcher="linux", arch_matcher="aarch64")
190+
metadata_rsps = mocker.mock_analysis_jres(
191+
body=[jre_dict], os_matcher=Os.LINUX.value, arch_matcher=Arch.AARCH64.value
192+
)
186193
download_rsps = mocker.mock_analysis_jre_download(id="zip_jre", status=500)
187194

188195
with self.cache.get_file(self.zip_jre.filename, self.zip_checksum).open(mode="wb") as f:
189196
f.write(self.zip_bytes)
190197

191-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
198+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
192199

193200
cache_file = self.cache.get_file(self.zip_jre.filename, self.zip_checksum)
194201
self.assertTrue(cache_file.is_valid())
@@ -199,7 +206,7 @@ def test_already_cached(self, *args):
199206
def test_file_already_exists_with_invalid_checksum(self, *args):
200207
with sq_api_utils.sq_api_mocker() as mocker:
201208
jre_dict = sq_api_utils.jre_to_dict(self.zip_jre)
202-
mocker.mock_analysis_jres(body=[jre_dict], os_matcher="linux", arch_matcher="aarch64")
209+
mocker.mock_analysis_jres(body=[jre_dict], os_matcher=Os.LINUX.value, arch_matcher=Arch.AARCH64.value)
203210
mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200)
204211

205212
with self.cache.get_file(self.zip_jre.filename, self.zip_checksum).open(mode="wb") as f:
@@ -209,19 +216,21 @@ def test_file_already_exists_with_invalid_checksum(self, *args):
209216
cache_file.is_valid(), msg="Cache file should have invalid checksum before provisioner ran"
210217
)
211218

212-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
219+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
213220

214221
self.assertTrue(cache_file.is_valid(), msg="Cache file should have valid checksum after provisioner ran")
215222

216223
def test_no_jre_available(self, *args):
217224
with self.assertRaises(NoJreAvailableException), sq_api_utils.sq_api_mocker() as mocker:
218-
mocker.mock_analysis_jres(body=[], os_matcher="linux", arch_matcher="aarch64")
219-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
225+
mocker.mock_analysis_jres(body=[], os_matcher=Os.LINUX.value, arch_matcher=Arch.AARCH64.value)
226+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
220227

221228
def test_unzip_dir_already_exists(self, *args):
222229
with sq_api_utils.sq_api_mocker() as mocker:
223230
mocker.mock_analysis_jres(
224-
body=[sq_api_utils.jre_to_dict(self.zip_jre)], os_matcher="linux", arch_matcher="aarch64"
231+
body=[sq_api_utils.jre_to_dict(self.zip_jre)],
232+
os_matcher=Os.LINUX.value,
233+
arch_matcher=Arch.AARCH64.value,
225234
)
226235
mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200)
227236

@@ -231,7 +240,7 @@ def test_unzip_dir_already_exists(self, *args):
231240
old_text_file = unzip_dir / "subdir/test.txt"
232241
old_text_file.write_text("test")
233242

234-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
243+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
235244

236245
self.assertTrue(unzip_dir.exists())
237246
self.assertTrue((unzip_dir / "readme.md").exists())
@@ -244,18 +253,20 @@ def test_unsupported_jre(self, *args):
244253
filename="jre.txt",
245254
sha256=self.zip_checksum,
246255
java_path="java",
247-
os="linux",
248-
arch="aarch64",
256+
os=Os.LINUX.value,
257+
arch=Arch.AARCH64.value,
249258
download_url=None,
250259
)
251260

252261
with self.assertRaises(UnsupportedArchiveFormat), sq_api_utils.sq_api_mocker() as mocker:
253262
mocker.mock_analysis_jres(
254-
body=[sq_api_utils.jre_to_dict(unsupported_archive_jre)], os_matcher="linux", arch_matcher="aarch64"
263+
body=[sq_api_utils.jre_to_dict(unsupported_archive_jre)],
264+
os_matcher=Os.LINUX.value,
265+
arch_matcher=Arch.AARCH64.value,
255266
)
256267
mocker.mock_analysis_jre_download(id="unsupported", body=self.zip_bytes, status=200)
257268

258-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
269+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
259270

260271

261272
class TestJREResolvedPath(unittest.TestCase):
@@ -282,13 +293,13 @@ def test(self):
282293
{
283294
SONAR_SCANNER_JAVA_EXE_PATH: "a/b",
284295
SONAR_SCANNER_SKIP_JRE_PROVISIONING: True,
285-
SONAR_SCANNER_OS: "windows",
296+
SONAR_SCANNER_OS: Os.WINDOWS.value,
286297
}
287298
)
288299

289300
self.assertEqual(config.sonar_scanner_java_exe_path, "a/b")
290301
self.assertTrue(config.sonar_scanner_skip_jre_provisioning)
291-
self.assertEqual(config.sonar_scanner_os, "windows")
302+
self.assertEqual(config.sonar_scanner_os, Os.WINDOWS.value)
292303

293304

294305
class TestJREResolver(unittest.TestCase):
@@ -342,7 +353,7 @@ class TestCaseDict(TypedDict):
342353
{
343354
"name": "if skip_jre_provisioning is True and java_home is not set return the default for windows",
344355
"config": JREResolverConfiguration(
345-
sonar_scanner_os="windows",
356+
sonar_scanner_os=Os.WINDOWS.value,
346357
sonar_scanner_skip_jre_provisioning=True,
347358
sonar_scanner_java_exe_path=None,
348359
),

0 commit comments

Comments
 (0)