From 44f4af4b259b88ff5c96bc9d16fb4d64e3762959 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 9 Dec 2024 23:09:55 +0100 Subject: [PATCH] refactor: Replace git tag fetching with micropython_versions function Signed-off-by: Jos Verlinde --- src/stubber/commands/switch_cmd.py | 5 +- src/stubber/utils/config.py | 6 +- tests/commandline/stubber_cli_test.py | 74 +++++++++++++++++------- tests/publish/data/all_packages_test.db | Bin 0 -> 86016 bytes 4 files changed, 56 insertions(+), 29 deletions(-) create mode 100644 tests/publish/data/all_packages_test.db diff --git a/src/stubber/commands/switch_cmd.py b/src/stubber/commands/switch_cmd.py index 8638b534..67a225ff 100644 --- a/src/stubber/commands/switch_cmd.py +++ b/src/stubber/commands/switch_cmd.py @@ -7,10 +7,9 @@ import rich_click as click -import mpflash.basicgit as git from stubber.utils.config import CONFIG from stubber.utils.repos import fetch_repos, repo_paths -from mpflash.versions import SET_PREVIEW, V_PREVIEW +from mpflash.versions import SET_PREVIEW, V_PREVIEW, micropython_versions from .cli import stubber_cli @@ -22,7 +21,7 @@ # get version list from Git tags in the repo that is provided on the command line try: - VERSION_LIST = git.get_tags("micropython/micropython", minver="v1.9.3") + [ + VERSION_LIST = micropython_versions( minver="v1.9.3") + [ V_PREVIEW, "latest", "stable", diff --git a/src/stubber/utils/config.py b/src/stubber/utils/config.py index 1286aa3b..b3446649 100644 --- a/src/stubber/utils/config.py +++ b/src/stubber/utils/config.py @@ -4,12 +4,10 @@ from typing import List from mpflash.logger import log +from mpflash.versions import V_PREVIEW, micropython_versions from typedconfig.config import Config, key, section from typedconfig.source import EnvironmentConfigSource -import mpflash.basicgit as git -from mpflash.versions import V_PREVIEW - from .typed_config_toml import TomlConfigSource @@ -94,7 +92,7 @@ def post_read_hook(self) -> dict: # read the versions from the git tags all_versions = [] try: - all_versions = git.get_tags("micropython/micropython", minver="v1.17") + all_versions = micropython_versions(minver="v1.17") except Exception as e: log.warning(f"Could not read micropython versions from git: {e}") all_versions = ["1.19", "1.19.1", "1.20.0", "1.21.0"] diff --git a/tests/commandline/stubber_cli_test.py b/tests/commandline/stubber_cli_test.py index 5eb22c56..e3c48096 100644 --- a/tests/commandline/stubber_cli_test.py +++ b/tests/commandline/stubber_cli_test.py @@ -2,11 +2,11 @@ from typing import List import pytest -from click.testing import CliRunner -from pytest_mock import MockerFixture # module under test : import stubber.stubber as stubber +from click.testing import CliRunner +from pytest_mock import MockerFixture from stubber.commands.switch_cmd import VERSION_LIST # mark all tests @@ -99,11 +99,19 @@ def test_cmd_switch(mocker: MockerFixture, params: List[str]): mocker.patch("stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0) m_fetch = mocker.patch("stubber.commands.clone_cmd.git.fetch", autospec=True, return_value=0) - m_switch_branch = mocker.patch("stubber.commands.clone_cmd.git.switch_branch", autospec=True, return_value=0) - m_switch_tag = mocker.patch("stubber.commands.clone_cmd.git.switch_tag", autospec=True, return_value=0) - mocker.patch("stubber.commands.clone_cmd.git.get_local_tag", autospec=True, return_value="v1.42") + m_switch_branch = mocker.patch( + "stubber.commands.clone_cmd.git.switch_branch", autospec=True, return_value=0 + ) + m_switch_tag = mocker.patch( + "stubber.commands.clone_cmd.git.switch_tag", autospec=True, return_value=0 + ) + mocker.patch( + "stubber.commands.clone_cmd.git.get_local_tag", autospec=True, return_value="v1.42" + ) - m_match = mocker.patch("stubber.utils.repos.match_lib_with_mpy", autospec=True) # Moved to other module + m_match = mocker.patch( + "stubber.utils.repos.match_lib_with_mpy", autospec=True + ) # Moved to other module mocker.patch("stubber.commands.clone_cmd.Path.exists", return_value=True) result = runner.invoke(stubber.stubber_cli, params) @@ -118,12 +126,14 @@ def test_cmd_switch(mocker: MockerFixture, params: List[str]): # core m_match.assert_called_once() - if "latest" in params or "preview" in params: - m_switch_branch.assert_called_once() - m_switch_tag.assert_not_called() - else: - m_switch_branch.assert_not_called() - m_switch_tag.assert_called_once() + assert m_switch_branch.call_count + m_switch_tag.call_count == 1 + + # if "latest" in params or "preview" in params: + # m_switch_branch.assert_called_once() + # m_switch_tag.assert_not_called() + # else: + # m_switch_branch.assert_not_called() + # m_switch_tag.assert_called_once() @pytest.mark.parametrize("version", VERSION_LIST) @@ -134,9 +144,15 @@ def test_cmd_switch_version(mocker: MockerFixture, version: str): m_clone = mocker.patch("stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0) m_fetch = mocker.patch("stubber.commands.clone_cmd.git.fetch", autospec=True, return_value=0) - m_switch = mocker.patch("stubber.commands.clone_cmd.git.switch_branch", autospec=True, return_value=0) - m_checkout = mocker.patch("stubber.commands.clone_cmd.git.checkout_tag", autospec=True, return_value=0) - m_get_l_tag = mocker.patch("stubber.commands.clone_cmd.git.get_local_tag", autospec=True, return_value="v1.42") + m_switch = mocker.patch( + "stubber.commands.clone_cmd.git.switch_branch", autospec=True, return_value=0 + ) + m_checkout = mocker.patch( + "stubber.commands.clone_cmd.git.checkout_tag", autospec=True, return_value=0 + ) + m_get_l_tag = mocker.patch( + "stubber.commands.clone_cmd.git.get_local_tag", autospec=True, return_value="v1.42" + ) m_match = mocker.patch("stubber.utils.repos.match_lib_with_mpy", autospec=True) @@ -177,7 +193,9 @@ def test_cmd_stub(mocker: MockerFixture): m_generate.assert_called_once_with(Path(".")) m_postprocessing.assert_called_once() - m_postprocessing.assert_called_once_with([Path(".")], stubgen=False, black=True, autoflake=False) + m_postprocessing.assert_called_once_with( + [Path(".")], stubgen=False, black=True, autoflake=False + ) assert result.exit_code == 0 @@ -189,13 +207,17 @@ def test_cmd_get_frozen(mocker: MockerFixture, tmp_path: Path): # check basic command line sanity check runner = CliRunner() - m_get_local_tag = mocker.patch("mpflash.basicgit.get_local_tag", autospec=True, return_value="v1.42") + m_get_local_tag = mocker.patch( + "mpflash.basicgit.get_local_tag", autospec=True, return_value="v1.42" + ) m_freeze_any = mocker.patch("stubber.commands.get_frozen_cmd.freeze_any", autospec=True) m_post = mocker.patch("stubber.utils.do_post_processing", autospec=True) # fake run - need to ensure that there is a destination folder - result = runner.invoke(stubber.stubber_cli, ["get-frozen", "--stub-folder", tmp_path.as_posix()]) + result = runner.invoke( + stubber.stubber_cli, ["get-frozen", "--stub-folder", tmp_path.as_posix()] + ) assert result.exit_code == 0 # FIXME : test fails in CI m_freeze_any.assert_called_once() @@ -249,13 +271,17 @@ def test_cmd_get_docstubs(mocker: MockerFixture, tmp_path: Path): # check basic command line sanity check runner = CliRunner() - m_get_l_tag = mocker.patch("mpflash.basicgit.get_local_tag", autospec=True, return_value="v1.42") + m_get_l_tag = mocker.patch( + "mpflash.basicgit.get_local_tag", autospec=True, return_value="v1.42" + ) # from stubber.commands.get_docstubs import generate_from_rst m_generate = mocker.patch("stubber.commands.get_docstubs_cmd.generate_from_rst", autospec=True) # fake run - result = runner.invoke(stubber.stubber_cli, ["get-docstubs", "--stub-folder", tmp_path.as_posix()]) + result = runner.invoke( + stubber.stubber_cli, ["get-docstubs", "--stub-folder", tmp_path.as_posix()] + ) assert result.exit_code == 0 # process is called assert m_generate.call_count == 1 @@ -277,7 +303,9 @@ def test_cmd_get_docstubs(mocker: MockerFixture, tmp_path: Path): def test_cmd_merge(mocker: MockerFixture, cmdline: List[str]): runner = CliRunner() # from stubber.commands.clone import git - m_merge_docstubs = mocker.patch("stubber.commands.merge_cmd.merge_all_docstubs", autospec=True, return_value={}) + m_merge_docstubs = mocker.patch( + "stubber.commands.merge_cmd.merge_all_docstubs", autospec=True, return_value={} + ) result = runner.invoke(stubber.stubber_cli, cmdline) assert result.exit_code == 0 m_merge_docstubs.assert_called_once() @@ -297,7 +325,9 @@ def test_cmd_merge(mocker: MockerFixture, cmdline: List[str]): def test_cmd_publish(mocker: MockerFixture, cmdline: List[str]): runner = CliRunner() # from stubber.commands.clone import git - m_publish_multiple = mocker.patch("stubber.commands.publish_cmd.publish_multiple", autospec=True, return_value={}) + m_publish_multiple = mocker.patch( + "stubber.commands.publish_cmd.publish_multiple", autospec=True, return_value={} + ) result = runner.invoke(stubber.stubber_cli, cmdline) assert result.exit_code == 0 m_publish_multiple.assert_called_once() diff --git a/tests/publish/data/all_packages_test.db b/tests/publish/data/all_packages_test.db new file mode 100644 index 0000000000000000000000000000000000000000..c937e44d08cc3165f4c2b46ead1fe45052a26827 GIT binary patch literal 86016 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 JvSd|^|35+M{uBTJ literal 0 HcmV?d00001