Skip to content

Commit 644549f

Browse files
Retain Os and Arch classes for stronger typing
1 parent 8176343 commit 644549f

File tree

5 files changed

+80
-67
lines changed

5 files changed

+80
-67
lines changed

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: 28 additions & 31 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):
@@ -137,11 +138,10 @@ class JRETestCase(TypedDict):
137138
testcase_jre = testcase["jre"]
138139
jres = [testcase_jre, self.other_jre]
139140
with self.subTest(jre=testcase), sq_api_utils.sq_api_mocker() as mocker:
140-
mocker.mock_analysis_jres(
141-
body=[sq_api_utils.jre_to_dict(jre) for jre in jres])
141+
mocker.mock_analysis_jres(body=[sq_api_utils.jre_to_dict(jre) for jre in jres])
142142
mocker.mock_analysis_jre_download(id=testcase_jre.id, body=testcase["bytes"], status=200)
143143

144-
provisioner = JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch())
144+
provisioner = JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value)
145145
jre_path = provisioner.provision()
146146

147147
cache_file = self.cache.get_file(testcase_jre.filename, testcase["checksum"])
@@ -161,7 +161,7 @@ def test_invalid_checksum(self, *args):
161161
mocker.mock_analysis_jres(body=[jre_dict])
162162
mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200)
163163

164-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
164+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
165165

166166
def test_retry_mechanism(self, *args):
167167
with sq_api_utils.sq_api_mocker() as mocker:
@@ -173,7 +173,7 @@ def test_retry_mechanism(self, *args):
173173
mocker.mock_analysis_jres(body=[jre_dict])
174174
mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200)
175175

176-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
176+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
177177

178178
cache_file = self.cache.get_file(self.zip_jre.filename, self.zip_checksum)
179179
self.assertTrue(cache_file.is_valid())
@@ -187,7 +187,7 @@ def test_already_cached(self, *args):
187187
with self.cache.get_file(self.zip_jre.filename, self.zip_checksum).open(mode="wb") as f:
188188
f.write(self.zip_bytes)
189189

190-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
190+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
191191

192192
cache_file = self.cache.get_file(self.zip_jre.filename, self.zip_checksum)
193193
self.assertTrue(cache_file.is_valid())
@@ -208,19 +208,18 @@ def test_file_already_exists_with_invalid_checksum(self, *args):
208208
cache_file.is_valid(), msg="Cache file should have invalid checksum before provisioner ran"
209209
)
210210

211-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
211+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
212212

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

215215
def test_no_jre_available(self, *args):
216216
with self.assertRaises(NoJreAvailableException), sq_api_utils.sq_api_mocker() as mocker:
217217
mocker.mock_analysis_jres(body=[])
218-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
218+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
219219

220220
def test_unzip_dir_already_exists(self, *args):
221221
with sq_api_utils.sq_api_mocker() as mocker:
222-
mocker.mock_analysis_jres(
223-
body=[sq_api_utils.jre_to_dict(self.zip_jre)])
222+
mocker.mock_analysis_jres(body=[sq_api_utils.jre_to_dict(self.zip_jre)])
224223
mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200)
225224

226225
unzip_dir = self.cache.get_file_path("jre.zip_unzip")
@@ -229,7 +228,7 @@ def test_unzip_dir_already_exists(self, *args):
229228
old_text_file = unzip_dir / "subdir/test.txt"
230229
old_text_file.write_text("test")
231230

232-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
231+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
233232

234233
self.assertTrue(unzip_dir.exists())
235234
self.assertTrue((unzip_dir / "readme.md").exists())
@@ -242,17 +241,15 @@ def test_unsupported_jre(self, *args):
242241
filename="jre.txt",
243242
sha256=self.zip_checksum,
244243
java_path="java",
245-
os="linux",
246-
arch="aarch64",
244+
os=Os.LINUX.value,
245+
arch=Arch.AARCH64.value,
247246
download_url=None,
248247
)
249248

250249
with self.assertRaises(UnsupportedArchiveFormat), sq_api_utils.sq_api_mocker() as mocker:
251-
mocker.mock_analysis_jres(
252-
body=[sq_api_utils.jre_to_dict(unsupported_archive_jre)])
250+
mocker.mock_analysis_jres(body=[sq_api_utils.jre_to_dict(unsupported_archive_jre)])
253251
mocker.mock_analysis_jre_download(id="unsupported", body=self.zip_bytes, status=200)
254-
255-
JREProvisioner(self.api, self.cache, utils.get_os(), utils.get_arch()).provision()
252+
JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision()
256253

257254

258255
class TestJREResolvedPath(unittest.TestCase):
@@ -279,13 +276,13 @@ def test(self):
279276
{
280277
SONAR_SCANNER_JAVA_EXE_PATH: "a/b",
281278
SONAR_SCANNER_SKIP_JRE_PROVISIONING: True,
282-
SONAR_SCANNER_OS: "windows",
279+
SONAR_SCANNER_OS: Os.WINDOWS.value,
283280
}
284281
)
285282

286283
self.assertEqual(config.sonar_scanner_java_exe_path, "a/b")
287284
self.assertTrue(config.sonar_scanner_skip_jre_provisioning)
288-
self.assertEqual(config.sonar_scanner_os, "windows")
285+
self.assertEqual(config.sonar_scanner_os, Os.WINDOWS.value)
289286

290287

291288
class TestJREResolver(unittest.TestCase):
@@ -339,7 +336,7 @@ class TestCaseDict(TypedDict):
339336
{
340337
"name": "if skip_jre_provisioning is True and java_home is not set return the default for windows",
341338
"config": JREResolverConfiguration(
342-
sonar_scanner_os="windows",
339+
sonar_scanner_os=Os.WINDOWS.value,
343340
sonar_scanner_skip_jre_provisioning=True,
344341
sonar_scanner_java_exe_path=None,
345342
),

0 commit comments

Comments
 (0)