diff --git a/src/pysonar_scanner/api.py b/src/pysonar_scanner/api.py index 7d152289..45ec3046 100644 --- a/src/pysonar_scanner/api.py +++ b/src/pysonar_scanner/api.py @@ -32,7 +32,7 @@ Key, ) from pysonar_scanner.exceptions import MissingKeyException, SonarQubeApiException -from pysonar_scanner.utils import Arch, Os, remove_trailing_slash +from pysonar_scanner.utils import remove_trailing_slash, OsStr, ArchStr @dataclass(frozen=True) @@ -211,12 +211,9 @@ def download_analysis_engine(self, handle: typing.BinaryIO) -> None: except requests.RequestException as e: raise SonarQubeApiException("Error while fetching the analysis engine") from e - def get_analysis_jres(self, os: Optional[Os] = None, arch: Optional[Arch] = None) -> list[JRE]: + def get_analysis_jres(self, os: OsStr, arch: ArchStr) -> list[JRE]: try: - params = { - "os": os.value if os else None, - "arch": arch.value if arch else None, - } + params = {"os": os, "arch": arch} res = requests.get( f"{self.base_urls.api_base_url}/analysis/jres", auth=self.auth, diff --git a/src/pysonar_scanner/configuration/configuration_loader.py b/src/pysonar_scanner/configuration/configuration_loader.py index ce037066..bdcb90fd 100644 --- a/src/pysonar_scanner/configuration/configuration_loader.py +++ b/src/pysonar_scanner/configuration/configuration_loader.py @@ -23,7 +23,7 @@ from pysonar_scanner.configuration.pyproject_toml import TomlConfigurationLoader from pysonar_scanner.configuration.properties import SONAR_TOKEN, SONAR_PROJECT_BASE_DIR, Key from pysonar_scanner.configuration.properties import PROPERTIES -from pysonar_scanner.configuration import sonar_project_properties, environment_variables +from pysonar_scanner.configuration import sonar_project_properties, environment_variables, dynamic_defaults_loader from pysonar_scanner.exceptions import MissingKeyException @@ -38,6 +38,7 @@ def load() -> dict[Key, any]: # each property loader is required to return NO default values. # E.g. if no property has been set, an empty dict must be returned. # Default values should be set through the get_static_default_properties() method + cli_properties = CliConfigurationLoader.load() # CLI properties have a higher priority than properties file, # but we need to resolve them first to load the properties file @@ -48,6 +49,7 @@ def load() -> dict[Key, any]: toml_properties = TomlConfigurationLoader.load(toml_dir) resolved_properties = get_static_default_properties() + resolved_properties.update(dynamic_defaults_loader.load()) resolved_properties.update(toml_properties.project_properties) resolved_properties.update(sonar_project_properties.load(base_dir)) resolved_properties.update(toml_properties.sonar_properties) diff --git a/src/pysonar_scanner/configuration/dynamic_defaults_loader.py b/src/pysonar_scanner/configuration/dynamic_defaults_loader.py new file mode 100644 index 00000000..04fcbdf4 --- /dev/null +++ b/src/pysonar_scanner/configuration/dynamic_defaults_loader.py @@ -0,0 +1,36 @@ +# +# Sonar Scanner Python +# Copyright (C) 2011-2024 SonarSource SA. +# mailto:info AT sonarsource DOT com +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +import os +from typing import Dict + +from pysonar_scanner.configuration.properties import Key, SONAR_SCANNER_OS, SONAR_SCANNER_ARCH, SONAR_PROJECT_BASE_DIR +from pysonar_scanner import utils + + +def load() -> Dict[Key, any]: + """ + Load dynamically computed default properties + """ + properties = { + SONAR_SCANNER_OS: utils.get_os().value, + SONAR_SCANNER_ARCH: utils.get_arch().value, + SONAR_PROJECT_BASE_DIR: os.getcwd(), + } + return properties diff --git a/src/pysonar_scanner/configuration/properties.py b/src/pysonar_scanner/configuration/properties.py index c8ca1bed..67f6370d 100644 --- a/src/pysonar_scanner/configuration/properties.py +++ b/src/pysonar_scanner/configuration/properties.py @@ -178,7 +178,7 @@ def env_variable_name(self) -> str: ), Property( name=SONAR_USER_HOME, - default_value="~/.sonar", + default_value=None, cli_getter=lambda args: args.sonar_user_home ), Property( diff --git a/src/pysonar_scanner/jre.py b/src/pysonar_scanner/jre.py index 2cc1d9d4..1ec3fc88 100644 --- a/src/pysonar_scanner/jre.py +++ b/src/pysonar_scanner/jre.py @@ -53,9 +53,11 @@ def from_string(path: str) -> "JREResolvedPath": class JREProvisioner: - def __init__(self, api: SonarQubeApi, cache: Cache): + def __init__(self, api: SonarQubeApi, cache: Cache, sonar_scanner_os: str, sonar_scanner_arch: str): self.api = api self.cache = cache + self.sonar_scanner_os = sonar_scanner_os + self.sonar_scanner_arch = sonar_scanner_arch def provision(self) -> JREResolvedPath: jre, resolved_path = self.__attempt_provisioning_jre_with_retry() @@ -67,7 +69,7 @@ def __attempt_provisioning_jre_with_retry(self) -> tuple[JRE, pathlib.Path]: jre_and_resolved_path = self.__attempt_provisioning_jre() if jre_and_resolved_path is None: raise ChecksumException( - f"Failed to download and verify JRE for {utils.get_os().value} and {utils.get_arch().value}" + f"Failed to download and verify JRE for {self.sonar_scanner_os} and {self.sonar_scanner_arch}" ) return jre_and_resolved_path @@ -83,10 +85,10 @@ def __attempt_provisioning_jre(self) -> Optional[tuple[JRE, pathlib.Path]]: return (jre, jre_path) if jre_path is not None else None def __get_available_jre(self) -> JRE: - jres = self.api.get_analysis_jres(os=utils.get_os(), arch=utils.get_arch()) + jres = self.api.get_analysis_jres(os=self.sonar_scanner_os, arch=self.sonar_scanner_arch) if len(jres) == 0: raise NoJreAvailableException( - f"No JREs are available for {utils.get_os().value} and {utils.get_arch().value}" + f"No JREs are available for {self.sonar_scanner_os} and {self.sonar_scanner_arch}" ) return jres[0] diff --git a/src/pysonar_scanner/scannerengine.py b/src/pysonar_scanner/scannerengine.py index 34169b76..c980fc0d 100644 --- a/src/pysonar_scanner/scannerengine.py +++ b/src/pysonar_scanner/scannerengine.py @@ -31,6 +31,7 @@ from pysonar_scanner.api import SonarQubeApi from pysonar_scanner.cache import Cache, CacheFile +from pysonar_scanner.configuration.properties import SONAR_SCANNER_OS, SONAR_SCANNER_ARCH from pysonar_scanner.exceptions import ChecksumException, SQTooOldException from pysonar_scanner.jre import JREProvisioner, JREResolvedPath, JREResolver, JREResolverConfiguration from subprocess import Popen, PIPE @@ -175,6 +176,6 @@ def __version_check(self): ) def __resolve_jre(self, config: dict[str, any]) -> JREResolvedPath: - jre_provisionner = JREProvisioner(self.api, self.cache) + jre_provisionner = JREProvisioner(self.api, self.cache, config[SONAR_SCANNER_OS], config[SONAR_SCANNER_ARCH]) jre_resolver = JREResolver(JREResolverConfiguration.from_dict(config), jre_provisionner) return jre_resolver.resolve_jre() diff --git a/src/pysonar_scanner/utils.py b/src/pysonar_scanner/utils.py index 4bd7d115..96ffab34 100644 --- a/src/pysonar_scanner/utils.py +++ b/src/pysonar_scanner/utils.py @@ -23,6 +23,9 @@ import typing from enum import Enum +OsStr = typing.Literal["windows", "linux", "mac", "alpine", "other"] +ArchStr = typing.Literal["x64", "aarch64", "other"] + def remove_trailing_slash(url: str) -> str: return url.rstrip("/ ").lstrip() @@ -36,11 +39,11 @@ def calculate_checksum(filehandle: typing.BinaryIO) -> str: class Os(Enum): - WINDOWS = "windows" - LINUX = "linux" - MACOS = "mac" - ALPINE = "alpine" - OTHER = "other" + WINDOWS: OsStr = "windows" + LINUX: OsStr = "linux" + MACOS: OsStr = "mac" + ALPINE: OsStr = "alpine" + OTHER: OsStr = "other" def get_os() -> Os: @@ -70,9 +73,9 @@ def is_alpine() -> bool: class Arch(Enum): - X64 = "x64" - AARCH64 = "aarch64" - OTHER = "other" + X64: ArchStr = "x64" + AARCH64: ArchStr = "aarch64" + OTHER: ArchStr = "other" def get_arch() -> Arch: diff --git a/tests/sq_api_utils.py b/tests/sq_api_utils.py index 41b8c1b4..beced83d 100644 --- a/tests/sq_api_utils.py +++ b/tests/sq_api_utils.py @@ -79,9 +79,9 @@ def mock_analysis_engine_download(self, body: bytes = b"", status: int = 200) -> def mock_analysis_jres( self, body: Optional[list[dict]] = None, - os_matcher: Optional[str] = None, - arch_matcher: Optional[str] = None, status: int = 200, + os_matcher: Optional[str] = "linux", + arch_matcher: Optional[str] = "x64", ) -> responses.BaseResponse: return self.rsps.get( url=f"{self.api_url}/analysis/jres", diff --git a/tests/test_api.py b/tests/test_api.py index a19e7aae..9b952836 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -317,10 +317,17 @@ def test_get_analysis_jres(self): ), ] - with self.subTest("get_analysis_jres works"), sq_api_mocker() as mocker: + with self.subTest("get_analysis_jres works (linux)"), sq_api_mocker() as mocker: mocker.mock_analysis_jres([sq_api_utils.jre_to_dict(jre) for jre in expected_jres]) - actual_jres = self.sq.get_analysis_jres() + actual_jres = self.sq.get_analysis_jres(os="linux", arch="x64") + self.assertEqual(actual_jres, expected_jres) + + with self.subTest("get_analysis_jres works (windows)"), sq_api_mocker() as mocker: + mocker.mock_analysis_jres( + [sq_api_utils.jre_to_dict(jre) for jre in expected_jres], os_matcher="windows", arch_matcher="aarch64" + ) + actual_jres = self.sq.get_analysis_jres(os="windows", arch="aarch64") self.assertEqual(actual_jres, expected_jres) with ( @@ -329,7 +336,7 @@ def test_get_analysis_jres(self): self.assertRaises(SonarQubeApiException), ): mocker.mock_analysis_jres(status=404) - self.sq.get_analysis_jres() + self.sq.get_analysis_jres(os="linux", arch="x64") with ( self.subTest("get_analysis_jres returns error when keys are missing"), @@ -337,7 +344,7 @@ def test_get_analysis_jres(self): self.assertRaises(SonarQubeApiException), ): mocker.mock_analysis_jres([{"id": "jre1"}]) - self.sq.get_analysis_jres() + self.sq.get_analysis_jres(os="linux", arch="x64") def test_download_analysis_jre(self): jre_id = "jre1" diff --git a/tests/test_configuration_loader.py b/tests/test_configuration_loader.py index b5b493c2..fdcd3476 100644 --- a/tests/test_configuration_loader.py +++ b/tests/test_configuration_loader.py @@ -17,7 +17,7 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # - +import os from unittest.mock import patch import pyfakefs.fake_filesystem_unittest as pyfakefs @@ -26,6 +26,7 @@ from pysonar_scanner.configuration.properties import ( SONAR_PROJECT_KEY, SONAR_PROJECT_NAME, + SONAR_PROJECT_BASE_DIR, SONAR_SCANNER_APP, SONAR_SCANNER_APP_VERSION, SONAR_SCANNER_BOOTSTRAP_START_TIME, @@ -46,11 +47,17 @@ SONAR_PYTHON_VERSION, SONAR_HOST_URL, SONAR_SCANNER_JAVA_OPTS, + SONAR_SCANNER_ARCH, + SONAR_SCANNER_OS, ) -from pysonar_scanner.configuration.configuration_loader import ConfigurationLoader, SONAR_PROJECT_BASE_DIR +from pysonar_scanner.configuration.configuration_loader import ConfigurationLoader from pysonar_scanner.exceptions import MissingKeyException +from pysonar_scanner.utils import Arch, Os +# Mock utils.get_os and utils.get_arch at the module level +@patch("pysonar_scanner.utils.get_arch", return_value=Arch.X64) +@patch("pysonar_scanner.utils.get_os", return_value=Os.LINUX) class TestConfigurationLoader(pyfakefs.TestCase): def setUp(self): self.maxDiff = None @@ -59,7 +66,10 @@ def setUp(self): self.env_patcher.start() @patch("sys.argv", ["myscript.py", "--token", "myToken", "--sonar-project-key", "myProjectKey"]) - def test_defaults(self): + def test_defaults(self, mock_get_os, mock_get_arch): + custom_dir = "/my_analysis_directory" + self.fs.create_dir(custom_dir) + os.chdir(custom_dir) configuration = ConfigurationLoader.load() expected_configuration = { SONAR_TOKEN: "myToken", @@ -69,22 +79,40 @@ def test_defaults(self): SONAR_SCANNER_BOOTSTRAP_START_TIME: configuration[SONAR_SCANNER_BOOTSTRAP_START_TIME], SONAR_VERBOSE: False, SONAR_SCANNER_SKIP_JRE_PROVISIONING: False, - SONAR_USER_HOME: "~/.sonar", + SONAR_PROJECT_BASE_DIR: "/my_analysis_directory", SONAR_SCANNER_CONNECT_TIMEOUT: 5, SONAR_SCANNER_SOCKET_TIMEOUT: 60, SONAR_SCANNER_RESPONSE_TIMEOUT: 0, SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit", SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit", + SONAR_SCANNER_OS: Os.LINUX.value, + SONAR_SCANNER_ARCH: Arch.X64.value, } self.assertDictEqual(configuration, expected_configuration) @patch("pysonar_scanner.configuration.configuration_loader.get_static_default_properties", return_value={}) + @patch("pysonar_scanner.configuration.dynamic_defaults_loader.load", return_value={}) @patch("sys.argv", ["myscript.py"]) - def test_no_defaults_in_configuration_loaders(self, get_static_default_properties_mock): + def test_no_defaults_in_configuration_loaders( + self, get_static_default_properties_mock, mock_load, mock_get_os, mock_get_arch + ): config = ConfigurationLoader.load() self.assertDictEqual(config, {}) - def test_get_token(self): + @patch("pysonar_scanner.configuration.configuration_loader.get_static_default_properties", return_value={}) + @patch("sys.argv", ["myscript.py"]) + def test_dynamic_defaults_are_loaded(self, get_static_default_properties_mock, mock_get_os, mock_get_arch): + config = ConfigurationLoader.load() + self.assertDictEqual( + config, + { + SONAR_PROJECT_BASE_DIR: os.getcwd(), + SONAR_SCANNER_OS: Os.LINUX.value, + SONAR_SCANNER_ARCH: Arch.X64.value, + }, + ) + + def test_get_token(self, mock_get_os, mock_get_arch): with self.subTest("Token is present"): self.assertEqual(configuration_loader.get_token({SONAR_TOKEN: "myToken"}), "myToken") @@ -92,7 +120,7 @@ def test_get_token(self): configuration_loader.get_token({}) @patch("sys.argv", ["myscript.py", "--token", "myToken", "--sonar-project-key", "myProjectKey"]) - def test_load_sonar_project_properties(self): + def test_load_sonar_project_properties(self, mock_get_os, mock_get_arch): self.fs.create_file( "sonar-project.properties", @@ -117,12 +145,14 @@ def test_load_sonar_project_properties(self): SONAR_SCANNER_BOOTSTRAP_START_TIME: configuration[SONAR_SCANNER_BOOTSTRAP_START_TIME], SONAR_VERBOSE: False, SONAR_SCANNER_SKIP_JRE_PROVISIONING: False, - SONAR_USER_HOME: "~/.sonar", + SONAR_PROJECT_BASE_DIR: os.getcwd(), SONAR_SCANNER_CONNECT_TIMEOUT: 5, SONAR_SCANNER_SOCKET_TIMEOUT: 60, SONAR_SCANNER_RESPONSE_TIMEOUT: 0, SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit", SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit", + SONAR_SCANNER_OS: Os.LINUX.value, + SONAR_SCANNER_ARCH: Arch.X64.value, } self.assertDictEqual(configuration, expected_configuration) @@ -138,7 +168,7 @@ def test_load_sonar_project_properties(self): "custom/path", ], ) - def test_load_sonar_project_properties_from_custom_path(self): + def test_load_sonar_project_properties_from_custom_path(self, mock_get_os, mock_get_arch): self.fs.create_dir("custom/path") self.fs.create_file( "custom/path/sonar-project.properties", @@ -164,12 +194,13 @@ def test_load_sonar_project_properties_from_custom_path(self): SONAR_SCANNER_BOOTSTRAP_START_TIME: configuration[SONAR_SCANNER_BOOTSTRAP_START_TIME], SONAR_VERBOSE: False, SONAR_SCANNER_SKIP_JRE_PROVISIONING: False, - SONAR_USER_HOME: "~/.sonar", SONAR_SCANNER_CONNECT_TIMEOUT: 5, SONAR_SCANNER_SOCKET_TIMEOUT: 60, SONAR_SCANNER_RESPONSE_TIMEOUT: 0, SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit", SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit", + SONAR_SCANNER_OS: Os.LINUX.value, + SONAR_SCANNER_ARCH: Arch.X64.value, } self.assertDictEqual(configuration, expected_configuration) @@ -185,7 +216,7 @@ def test_load_sonar_project_properties_from_custom_path(self): "custom/path", ], ) - def test_load_pyproject_toml_from_base_dir(self): + def test_load_pyproject_toml_from_base_dir(self, mock_get_os, mock_get_arch): self.fs.create_dir("custom/path") self.fs.create_file( "custom/path/pyproject.toml", @@ -212,12 +243,13 @@ def test_load_pyproject_toml_from_base_dir(self): SONAR_SCANNER_BOOTSTRAP_START_TIME: configuration[SONAR_SCANNER_BOOTSTRAP_START_TIME], SONAR_VERBOSE: False, SONAR_SCANNER_SKIP_JRE_PROVISIONING: False, - SONAR_USER_HOME: "~/.sonar", SONAR_SCANNER_CONNECT_TIMEOUT: 5, SONAR_SCANNER_SOCKET_TIMEOUT: 60, SONAR_SCANNER_RESPONSE_TIMEOUT: 0, SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit", SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit", + SONAR_SCANNER_OS: Os.LINUX.value, + SONAR_SCANNER_ARCH: Arch.X64.value, } self.assertDictEqual(configuration, expected_configuration) @@ -233,7 +265,7 @@ def test_load_pyproject_toml_from_base_dir(self): "custom/path", ], ) - def test_load_pyproject_toml_from_toml_path(self): + def test_load_pyproject_toml_from_toml_path(self, mock_get_os, mock_get_arch): self.fs.create_dir("custom/path") self.fs.create_file( "custom/path/pyproject.toml", @@ -259,19 +291,21 @@ def test_load_pyproject_toml_from_toml_path(self): SONAR_SCANNER_BOOTSTRAP_START_TIME: configuration[SONAR_SCANNER_BOOTSTRAP_START_TIME], SONAR_VERBOSE: False, SONAR_SCANNER_SKIP_JRE_PROVISIONING: False, - SONAR_USER_HOME: "~/.sonar", + SONAR_PROJECT_BASE_DIR: os.getcwd(), SONAR_SCANNER_CONNECT_TIMEOUT: 5, SONAR_SCANNER_SOCKET_TIMEOUT: 60, SONAR_SCANNER_RESPONSE_TIMEOUT: 0, SONAR_SCANNER_KEYSTORE_PASSWORD: "changeit", SONAR_SCANNER_TRUSTSTORE_PASSWORD: "changeit", + SONAR_SCANNER_OS: Os.LINUX.value, + SONAR_SCANNER_ARCH: Arch.X64.value, TOML_PATH: "custom/path", } self.assertDictEqual(configuration, expected_configuration) @patch("sys.argv", ["myscript.py"]) @patch.dict("os.environ", {"SONAR_TOKEN": "TokenFromEnv", "SONAR_PROJECT_KEY": "KeyFromEnv"}, clear=True) - def test_load_from_env_variables_only(self): + def test_load_from_env_variables_only(self, mock_get_os, mock_get_arch): """Test that configuration can be loaded exclusively from environment variables""" configuration = ConfigurationLoader.load() @@ -303,7 +337,7 @@ def test_load_from_env_variables_only(self): }, clear=True, ) - def test_properties_priority(self): + def test_properties_priority(self, mock_get_os, mock_get_arch): """Test the priority order of different configuration sources: 1. CLI args (highest) 2. Environment variables @@ -375,3 +409,11 @@ def test_properties_priority(self): # CLI args have highest priority self.assertEqual(configuration[SONAR_PROJECT_KEY], "ProjectKeyFromCLI") self.assertEqual(configuration[SONAR_TOKEN], "myToken") # CLI overrides env var + + +# If you have test functions outside of classes, use patch as a decorator for each function +@patch("pysonar_scanner.utils.get_arch", return_value=Arch.X64.value) +@patch("pysonar_scanner.utils.get_os", return_value=Os.LINUX.value) +def test_standalone_function(mock_get_os, mock_get_arch): + # ...existing test code... + pass diff --git a/tests/test_jre.py b/tests/test_jre.py index 33acadde..77d013ba 100644 --- a/tests/test_jre.py +++ b/tests/test_jre.py @@ -34,13 +34,14 @@ ) from pysonar_scanner.exceptions import ChecksumException, NoJreAvailableException, UnsupportedArchiveFormat from pysonar_scanner.jre import JREProvisioner, JREResolvedPath, JREResolver, JREResolverConfiguration +from pysonar_scanner.utils import Os, Arch from tests import sq_api_utils import zipfile -@patch("pysonar_scanner.utils.get_os", return_value=utils.Os.LINUX) -@patch("pysonar_scanner.utils.get_arch", return_value=utils.Arch.AARCH64) +@patch("pysonar_scanner.utils.get_os", return_value=Os.LINUX) +@patch("pysonar_scanner.utils.get_arch", return_value=Arch.X64) class TestJREProvisioner(pyfakefs.TestCase): def setUp(self): self.setUpPyfakefs(allow_root_user=False) @@ -56,8 +57,8 @@ def setUp(self): filename="fake_jre.zip", sha256="fakechecksum", java_path="fake_java", - os=utils.Os.WINDOWS.value, - arch=utils.Arch.X64.value, + os=Os.WINDOWS.value, + arch="x64", download_url="http://example.com/fake_jre.zip", ) @@ -74,8 +75,8 @@ def __setup_zip_file(self): filename=self.zip_name, sha256=self.zip_checksum, java_path="java", - os="linux", - arch="aarch64", + os=Os.LINUX.value, + arch=Arch.AARCH64.value, download_url=None, ) @@ -94,8 +95,8 @@ def __setup_tar_file(self): filename=self.tar_gz_name, sha256=self.tar_gz_checksum, java_path="java", - os="linux", - arch="aarch64", + os=Os.LINUX.value, + arch=Arch.AARCH64.value, download_url=None, ) @@ -107,14 +108,14 @@ def __setup_tar_file(self): filename=self.tgz_name, sha256=self.tgz_checksum, java_path="java", - os="linux", - arch="aarch64", + os=Os.LINUX.value, + arch=Arch.AARCH64.value, download_url=None, ) def test_if_patching_worked(self, get_os_mock, get_arch_mock): - self.assertEqual(utils.get_os(), utils.Os.LINUX) - self.assertEqual(utils.get_arch(), utils.Arch.AARCH64) + self.assertEqual(utils.get_os(), Os.LINUX) + self.assertEqual(utils.get_arch(), Arch.X64) def test_successfully_downloading_jre(self, get_os_mock, get_arch_mock): class JRETestCase(TypedDict): @@ -137,12 +138,10 @@ class JRETestCase(TypedDict): testcase_jre = testcase["jre"] jres = [testcase_jre, self.other_jre] with self.subTest(jre=testcase), sq_api_utils.sq_api_mocker() as mocker: - mocker.mock_analysis_jres( - body=[sq_api_utils.jre_to_dict(jre) for jre in jres], os_matcher="linux", arch_matcher="aarch64" - ) + mocker.mock_analysis_jres(body=[sq_api_utils.jre_to_dict(jre) for jre in jres]) mocker.mock_analysis_jre_download(id=testcase_jre.id, body=testcase["bytes"], status=200) - provisioner = JREProvisioner(self.api, self.cache) + provisioner = JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value) jre_path = provisioner.provision() cache_file = self.cache.get_file(testcase_jre.filename, testcase["checksum"]) @@ -159,10 +158,10 @@ def test_invalid_checksum(self, *args): with self.assertRaises(ChecksumException), sq_api_utils.sq_api_mocker() as mocker: jre_dict = sq_api_utils.jre_to_dict(self.zip_jre) jre_dict["sha256"] = "invalid" - mocker.mock_analysis_jres(body=[jre_dict], os_matcher="linux", arch_matcher="aarch64") + mocker.mock_analysis_jres(body=[jre_dict]) mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200) - JREProvisioner(self.api, self.cache).provision() + JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision() def test_retry_mechanism(self, *args): with sq_api_utils.sq_api_mocker() as mocker: @@ -170,11 +169,11 @@ def test_retry_mechanism(self, *args): jre_dict_with_invalid_checksum["sha256"] = "invalid" jre_dict = sq_api_utils.jre_to_dict(self.zip_jre) - mocker.mock_analysis_jres(body=[jre_dict_with_invalid_checksum], os_matcher="linux", arch_matcher="aarch64") - mocker.mock_analysis_jres(body=[jre_dict], os_matcher="linux", arch_matcher="aarch64") + mocker.mock_analysis_jres(body=[jre_dict_with_invalid_checksum]) + mocker.mock_analysis_jres(body=[jre_dict]) mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200) - JREProvisioner(self.api, self.cache).provision() + JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision() cache_file = self.cache.get_file(self.zip_jre.filename, self.zip_checksum) self.assertTrue(cache_file.is_valid()) @@ -182,13 +181,13 @@ def test_retry_mechanism(self, *args): def test_already_cached(self, *args): with sq_api_utils.sq_api_mocker(assert_all_requests_are_fired=False) as mocker: jre_dict = sq_api_utils.jre_to_dict(self.zip_jre) - metadata_rsps = mocker.mock_analysis_jres(body=[jre_dict], os_matcher="linux", arch_matcher="aarch64") + metadata_rsps = mocker.mock_analysis_jres(body=[jre_dict]) download_rsps = mocker.mock_analysis_jre_download(id="zip_jre", status=500) with self.cache.get_file(self.zip_jre.filename, self.zip_checksum).open(mode="wb") as f: f.write(self.zip_bytes) - JREProvisioner(self.api, self.cache).provision() + JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision() cache_file = self.cache.get_file(self.zip_jre.filename, self.zip_checksum) self.assertTrue(cache_file.is_valid()) @@ -199,7 +198,7 @@ def test_already_cached(self, *args): def test_file_already_exists_with_invalid_checksum(self, *args): with sq_api_utils.sq_api_mocker() as mocker: jre_dict = sq_api_utils.jre_to_dict(self.zip_jre) - mocker.mock_analysis_jres(body=[jre_dict], os_matcher="linux", arch_matcher="aarch64") + mocker.mock_analysis_jres(body=[jre_dict]) mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200) with self.cache.get_file(self.zip_jre.filename, self.zip_checksum).open(mode="wb") as f: @@ -209,20 +208,18 @@ def test_file_already_exists_with_invalid_checksum(self, *args): cache_file.is_valid(), msg="Cache file should have invalid checksum before provisioner ran" ) - JREProvisioner(self.api, self.cache).provision() + JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision() self.assertTrue(cache_file.is_valid(), msg="Cache file should have valid checksum after provisioner ran") def test_no_jre_available(self, *args): with self.assertRaises(NoJreAvailableException), sq_api_utils.sq_api_mocker() as mocker: - mocker.mock_analysis_jres(body=[], os_matcher="linux", arch_matcher="aarch64") - JREProvisioner(self.api, self.cache).provision() + mocker.mock_analysis_jres(body=[]) + JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision() def test_unzip_dir_already_exists(self, *args): with sq_api_utils.sq_api_mocker() as mocker: - mocker.mock_analysis_jres( - body=[sq_api_utils.jre_to_dict(self.zip_jre)], os_matcher="linux", arch_matcher="aarch64" - ) + mocker.mock_analysis_jres(body=[sq_api_utils.jre_to_dict(self.zip_jre)]) mocker.mock_analysis_jre_download(id="zip_jre", body=self.zip_bytes, status=200) unzip_dir = self.cache.get_file_path("jre.zip_unzip") @@ -231,7 +228,7 @@ def test_unzip_dir_already_exists(self, *args): old_text_file = unzip_dir / "subdir/test.txt" old_text_file.write_text("test") - JREProvisioner(self.api, self.cache).provision() + JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision() self.assertTrue(unzip_dir.exists()) self.assertTrue((unzip_dir / "readme.md").exists()) @@ -244,18 +241,15 @@ def test_unsupported_jre(self, *args): filename="jre.txt", sha256=self.zip_checksum, java_path="java", - os="linux", - arch="aarch64", + os=Os.LINUX.value, + arch=Arch.AARCH64.value, download_url=None, ) with self.assertRaises(UnsupportedArchiveFormat), sq_api_utils.sq_api_mocker() as mocker: - mocker.mock_analysis_jres( - body=[sq_api_utils.jre_to_dict(unsupported_archive_jre)], os_matcher="linux", arch_matcher="aarch64" - ) + mocker.mock_analysis_jres(body=[sq_api_utils.jre_to_dict(unsupported_archive_jre)]) mocker.mock_analysis_jre_download(id="unsupported", body=self.zip_bytes, status=200) - - JREProvisioner(self.api, self.cache).provision() + JREProvisioner(self.api, self.cache, utils.get_os().value, utils.get_arch().value).provision() class TestJREResolvedPath(unittest.TestCase): @@ -282,13 +276,13 @@ def test(self): { SONAR_SCANNER_JAVA_EXE_PATH: "a/b", SONAR_SCANNER_SKIP_JRE_PROVISIONING: True, - SONAR_SCANNER_OS: "windows", + SONAR_SCANNER_OS: Os.WINDOWS.value, } ) self.assertEqual(config.sonar_scanner_java_exe_path, "a/b") self.assertTrue(config.sonar_scanner_skip_jre_provisioning) - self.assertEqual(config.sonar_scanner_os, "windows") + self.assertEqual(config.sonar_scanner_os, Os.WINDOWS.value) class TestJREResolver(unittest.TestCase): @@ -342,7 +336,7 @@ class TestCaseDict(TypedDict): { "name": "if skip_jre_provisioning is True and java_home is not set return the default for windows", "config": JREResolverConfiguration( - sonar_scanner_os="windows", + sonar_scanner_os=Os.WINDOWS.value, sonar_scanner_skip_jre_provisioning=True, sonar_scanner_java_exe_path=None, ), diff --git a/tests/test_scannerengine.py b/tests/test_scannerengine.py index e165fce7..a6badc82 100644 --- a/tests/test_scannerengine.py +++ b/tests/test_scannerengine.py @@ -147,6 +147,8 @@ def test_command_building(self, provision_mock, resolve_jre_mock, execute_mock): config = { "sonar.token": "myToken", "sonar.projectKey": "myProjectKey", + "sonar.scanner.os": "linux", + "sonar.scanner.arch": "x64", } expected_std_in = json.dumps( @@ -154,6 +156,8 @@ def test_command_building(self, provision_mock, resolve_jre_mock, execute_mock): "scannerProperties": [ {"key": "sonar.token", "value": "myToken"}, {"key": "sonar.projectKey", "value": "myProjectKey"}, + {"key": "sonar.scanner.os", "value": "linux"}, + {"key": "sonar.scanner.arch", "value": "x64"}, ] } )