From 7a0e639da8d372e1960dc3dd1cd0620424a228d1 Mon Sep 17 00:00:00 2001 From: Corey Oordt Date: Tue, 26 Mar 2024 13:31:10 -0500 Subject: [PATCH] Added CalVer function and formatting - Version parts now have a `calver_format` attribute for CalVer parts. --- bumpversion/cli.py | 2 +- bumpversion/versioning/functions.py | 41 ++++ bumpversion/versioning/models.py | 32 ++- pyproject.toml | 2 + tests/fixtures/basic_cfg_expected.txt | 12 +- tests/fixtures/basic_cfg_expected.yaml | 4 + tests/fixtures/basic_cfg_expected_full.json | 4 + tests/test_versioning/test_functions.py | 67 ++++++- tests/test_versioning/test_models_version.py | 189 +++++++++++++++--- .../test_models_versioncomponent.py | 133 ++++++++++-- 10 files changed, 434 insertions(+), 52 deletions(-) diff --git a/bumpversion/cli.py b/bumpversion/cli.py index 8798fef5..2e054b7f 100644 --- a/bumpversion/cli.py +++ b/bumpversion/cli.py @@ -26,7 +26,7 @@ context_settings={ "help_option_names": ["-h", "--help"], }, - add_help_option=False, + add_help_option=True, ) @click.version_option(version=__version__) @click.pass_context diff --git a/bumpversion/versioning/functions.py b/bumpversion/versioning/functions.py index 7a9cff5c..d2e4c4df 100644 --- a/bumpversion/versioning/functions.py +++ b/bumpversion/versioning/functions.py @@ -1,9 +1,36 @@ """Generators for version parts.""" +import datetime import re from typing import List, Optional, Union +def get_datetime_info(current_dt: datetime.datetime) -> dict: + """Return the full structure of the given datetime for formatting.""" + return { + "YYYY": current_dt.strftime("%Y"), + "YY": current_dt.strftime("%y").lstrip("0") or "0", + "0Y": current_dt.strftime("%y"), + "MMM": current_dt.strftime("%b"), + "MM": str(current_dt.month), + "0M": current_dt.strftime("%m"), + "DD": str(current_dt.day), + "0D": current_dt.strftime("%d"), + "JJJ": current_dt.strftime("%j").lstrip("0"), + "00J": current_dt.strftime("%j"), + "Q": str((current_dt.month - 1) // 3 + 1), + "WW": current_dt.strftime("%W").lstrip("0") or "0", + "0W": current_dt.strftime("%W"), + "UU": current_dt.strftime("%U").lstrip("0") or "0", + "0U": current_dt.strftime("%U"), + "VV": current_dt.strftime("%V").lstrip("0") or "0", + "0V": current_dt.strftime("%V"), + "GGGG": current_dt.strftime("%G"), + "GG": current_dt.strftime("%G")[2:].lstrip("0") or "0", + "0G": current_dt.strftime("%G")[2:], + } + + class PartFunction: """Base class for a version part function.""" @@ -35,6 +62,20 @@ def bump(self, value: Optional[str] = None) -> str: return value or self.optional_value +class CalVerFunction(PartFunction): + """This is a class that provides a CalVer function for version parts.""" + + def __init__(self, calver_format: str): + self.independent = False + self.calver_format = calver_format + self.first_value = self.bump() + self.optional_value = "There isn't an optional value for CalVer." + + def bump(self, value: Optional[str] = None) -> str: + """Return the optional value.""" + return self.calver_format.format(**get_datetime_info(datetime.datetime.now())) + + class NumericFunction(PartFunction): """ This is a class that provides a numeric function for version parts. diff --git a/bumpversion/versioning/models.py b/bumpversion/versioning/models.py index a37d3d99..6171a06b 100644 --- a/bumpversion/versioning/models.py +++ b/bumpversion/versioning/models.py @@ -9,7 +9,7 @@ from bumpversion.exceptions import InvalidVersionPartError from bumpversion.utils import key_val_string -from bumpversion.versioning.functions import NumericFunction, PartFunction, ValuesFunction +from bumpversion.versioning.functions import CalVerFunction, NumericFunction, PartFunction, ValuesFunction class VersionComponent: @@ -26,6 +26,7 @@ def __init__( optional_value: Optional[str] = None, first_value: Union[str, int, None] = None, independent: bool = False, + calver_format: Optional[str] = None, source: Optional[str] = None, value: Union[str, int, None] = None, ): @@ -33,11 +34,15 @@ def __init__( self.func: Optional[PartFunction] = None self.independent = independent self.source = source + self.calver_format = calver_format if values: str_values = [str(v) for v in values] str_optional_value = str(optional_value) if optional_value is not None else None str_first_value = str(first_value) if first_value is not None else None self.func = ValuesFunction(str_values, str_optional_value, str_first_value) + elif calver_format: + self.func = CalVerFunction(calver_format) + self._value = self._value or self.func.first_value else: self.func = NumericFunction(optional_value, first_value or "0") @@ -53,6 +58,7 @@ def copy(self) -> "VersionComponent": optional_value=self.func.optional_value, first_value=self.func.first_value, independent=self.independent, + calver_format=self.calver_format, source=self.source, value=self._value, ) @@ -101,13 +107,28 @@ class VersionComponentSpec(BaseModel): This is used to read in the configuration from the bumpversion config file. """ - values: Optional[list] = None # Optional. Numeric is used if missing or no items in list + values: Optional[list] = None + """The possible values for the component. If it and `calver_format` is None, the component is numeric.""" + optional_value: Optional[str] = None # Optional. - # Defaults to first value. 0 in the case of numeric. Empty string means nothing is optional. - first_value: Union[str, int, None] = None # Optional. Defaults to first value in values + """The value that is optional to include in the version. + + - Defaults to first value in values or 0 in the case of numeric. + - Empty string means nothing is optional. + - CalVer components ignore this.""" + + first_value: Union[str, int, None] = None + """The first value to increment from.""" + independent: bool = False + """Is the component independent of the other components?""" + + calver_format: Optional[str] = None + """The format string for a CalVer component.""" + # source: Optional[str] = None # Name of environment variable or context variable to use as the source for value - depends_on: Optional[str] = None # The name of the component this component depends on + depends_on: Optional[str] = None + """The name of the component this component depends on.""" def create_component(self, value: Union[str, int, None] = None) -> VersionComponent: """Generate a version component from the configuration.""" @@ -116,6 +137,7 @@ def create_component(self, value: Union[str, int, None] = None) -> VersionCompon optional_value=self.optional_value, first_value=self.first_value, independent=self.independent, + calver_format=self.calver_format, # source=self.source, value=value, ) diff --git a/pyproject.toml b/pyproject.toml index bc465578..dfe1080c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,10 +76,12 @@ docs = [ ] test = [ "coverage", + "freezegun", "pre-commit", "pytest-cov", "pytest", "pytest-mock", + "pytest-sugar", ] [tool.setuptools.dynamic] diff --git a/tests/fixtures/basic_cfg_expected.txt b/tests/fixtures/basic_cfg_expected.txt index 4ef4dc51..9867f9f4 100644 --- a/tests/fixtures/basic_cfg_expected.txt +++ b/tests/fixtures/basic_cfg_expected.txt @@ -41,22 +41,26 @@ 'included_paths': [], 'message': 'Bump version: {current_version} → {new_version}', 'parse': '(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)(\\-(?P[a-z]+))?', - 'parts': {'major': {'depends_on': None, + 'parts': {'major': {'calver_format': None, + 'depends_on': None, 'first_value': None, 'independent': False, 'optional_value': None, 'values': None}, - 'minor': {'depends_on': None, + 'minor': {'calver_format': None, + 'depends_on': None, 'first_value': None, 'independent': False, 'optional_value': None, 'values': None}, - 'patch': {'depends_on': None, + 'patch': {'calver_format': None, + 'depends_on': None, 'first_value': None, 'independent': False, 'optional_value': None, 'values': None}, - 'release': {'depends_on': None, + 'release': {'calver_format': None, + 'depends_on': None, 'first_value': None, 'independent': False, 'optional_value': 'gamma', diff --git a/tests/fixtures/basic_cfg_expected.yaml b/tests/fixtures/basic_cfg_expected.yaml index ef6ba4be..5d7fb01e 100644 --- a/tests/fixtures/basic_cfg_expected.yaml +++ b/tests/fixtures/basic_cfg_expected.yaml @@ -49,24 +49,28 @@ message: "Bump version: {current_version} → {new_version}" parse: "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)(\\-(?P[a-z]+))?" parts: major: + calver_format: null depends_on: null first_value: null independent: false optional_value: null values: null minor: + calver_format: null depends_on: null first_value: null independent: false optional_value: null values: null patch: + calver_format: null depends_on: null first_value: null independent: false optional_value: null values: null release: + calver_format: null depends_on: null first_value: null independent: false diff --git a/tests/fixtures/basic_cfg_expected_full.json b/tests/fixtures/basic_cfg_expected_full.json index c1e3a4c7..24cb4893 100644 --- a/tests/fixtures/basic_cfg_expected_full.json +++ b/tests/fixtures/basic_cfg_expected_full.json @@ -58,6 +58,7 @@ "parse": "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)(\\-(?P[a-z]+))?", "parts": { "major": { + "calver_format": null, "depends_on": null, "first_value": null, "independent": false, @@ -65,6 +66,7 @@ "values": null }, "minor": { + "calver_format": null, "depends_on": null, "first_value": null, "independent": false, @@ -72,6 +74,7 @@ "values": null }, "patch": { + "calver_format": null, "depends_on": null, "first_value": null, "independent": false, @@ -79,6 +82,7 @@ "values": null }, "release": { + "calver_format": null, "depends_on": null, "first_value": null, "independent": false, diff --git a/tests/test_versioning/test_functions.py b/tests/test_versioning/test_functions.py index d561d49c..93eda2e9 100644 --- a/tests/test_versioning/test_functions.py +++ b/tests/test_versioning/test_functions.py @@ -1,7 +1,7 @@ import pytest from pytest import param - -from bumpversion.versioning.functions import NumericFunction, ValuesFunction, IndependentFunction +from freezegun import freeze_time +from bumpversion.versioning.functions import NumericFunction, ValuesFunction, IndependentFunction, CalVerFunction # NumericFunction @@ -145,3 +145,66 @@ def test_bump_with_value_returns_value(self): def test_bump_with_no_value_returns_initial_value(self): func = IndependentFunction("1") assert func.bump() == "1" + + +class TestCalVerFunction: + """The calver function manages incrementing and resetting calver version parts.""" + + @freeze_time("2020-05-01") + def test_creation_sets_first_value_and_optional_value(self): + func = CalVerFunction("{YYYY}.{MM}") + assert func.optional_value == "There isn't an optional value for CalVer." + assert func.first_value == "2020.5" + assert func.calver_format == "{YYYY}.{MM}" + + @freeze_time("2020-05-01") + def test_bump_with_value_ignores_value(self): + func = CalVerFunction("{YYYY}.{MM}.{DD}") + assert func.bump("123456") == "2020.5.1" + + @pytest.mark.parametrize( + ["calver", "expected"], + [ + param("{YYYY}", "2002", id="{YYYY}"), + param("{YY}", "2", id="{YY}"), + param("{0Y}", "02", id="{0Y}"), + param("{MMM}", "May", id="{MMM}"), + param("{MM}", "5", id="{MM}"), + param("{0M}", "05", id="{0M}"), + param("{DD}", "1", id="{DD}"), + param("{0D}", "01", id="{0D}"), + param("{JJJ}", "121", id="{JJJ}"), + param("{00J}", "121", id="{00J}"), + param("{Q}", "2", id="{Q}"), + param("{WW}", "17", id="{WW}"), + param("{0W}", "17", id="{0W}"), + param("{UU}", "17", id="{UU}"), + param("{0U}", "17", id="{0U}"), + param("{VV}", "18", id="{VV}"), + param("{0V}", "18", id="{0V}"), + param("{GGGG}", "2002", id="{GGGG}"), + param("{GG}", "2", id="{GG}"), + param("{0G}", "02", id="{0G}"), + ], + ) + @freeze_time("2002-05-01") + def test_calver_formatting_renders_correctly(self, calver: str, expected: str): + """Test that the calver is formatted correctly.""" + func = CalVerFunction(calver) + assert func.bump() == expected + + @pytest.mark.parametrize( + ["calver", "expected"], + [ + param("{YYYY}", "2000", id="{YYYY}"), + param("{YY}", "0", id="{YY}"), + param("{0Y}", "00", id="{0Y}"), + param("{GGGG}", "1999", id="{GGGG}"), + param("{GG}", "99", id="{GG}"), + param("{0G}", "99", id="{0G}"), + ], + ) + @freeze_time("2000-01-01") + def test_century_years_return_zeros(self, calver: str, expected: str): + func = CalVerFunction(calver) + assert func.bump() == expected diff --git a/tests/test_versioning/test_models_version.py b/tests/test_versioning/test_models_version.py index 381729d1..e30ea56a 100644 --- a/tests/test_versioning/test_models_version.py +++ b/tests/test_versioning/test_models_version.py @@ -1,5 +1,7 @@ """Tests for the Version class.""" +from freezegun import freeze_time + from bumpversion.versioning.models import VersionComponentSpec from bumpversion.versioning.models import VersionSpec import pytest @@ -7,7 +9,7 @@ @pytest.fixture -def version_spec(): +def semver_version_spec(): """Return a version spec.""" config = { "major": VersionComponentSpec(), @@ -19,13 +21,25 @@ def version_spec(): return VersionSpec(config) -class TestVersion: - """Test how the Version model should behave.""" +@pytest.fixture +def calver_version_spec(): + """Return a calver version spec.""" + config = { + "release": VersionComponentSpec(calver_format="{YYYY}.{MM}.{DD}"), + "patch": VersionComponentSpec(), + "build": VersionComponentSpec(optional_value="0", independent=True), + } + + return VersionSpec(config) + + +class TestSemVerVersion: + """Test how a SemVer-style Version model should behave.""" - def test_version_acts_like_a_dict(self, version_spec: VersionSpec): + def test_acts_like_a_dict(self, semver_version_spec: VersionSpec): """You can get version components by name.""" # Arrange - version = version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) + version = semver_version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) # Assert assert version["major"].value == "1" @@ -36,32 +50,32 @@ def test_version_acts_like_a_dict(self, version_spec: VersionSpec): with pytest.raises(KeyError): version["invalid"] - def test_version_has_a_length(self, version_spec: VersionSpec): + def test_length_is_number_of_parts(self, semver_version_spec: VersionSpec): # Arrange - version = version_spec.create_version({"major": "1"}) + version = semver_version_spec.create_version({"major": "1"}) # Assert assert len(version) == 4 - def test_version_is_iterable(self, version_spec: VersionSpec): + def test_is_iterable(self, semver_version_spec: VersionSpec): # Arrange - version = version_spec.create_version({"major": "1"}) + version = semver_version_spec.create_version({"major": "1"}) # Assert assert list(version) == ["major", "minor", "patch", "build"] - def test_version_has_a_string_representation(self, version_spec: VersionSpec): + def test_has_a_string_representation(self, semver_version_spec: VersionSpec): # Arrange - version = version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) + version = semver_version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) # Assert assert repr(version) == "" - def test_version_has_an_equality_comparison(self, version_spec: VersionSpec): + def test_has_an_equality_comparison(self, semver_version_spec: VersionSpec): # Arrange - version1 = version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) - version2 = version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) - version3 = version_spec.create_version({"major": "1", "minor": "2", "patch": "4"}) + version1 = semver_version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) + version2 = semver_version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) + version3 = semver_version_spec.create_version({"major": "1", "minor": "2", "patch": "4"}) # Assert assert version1 == version2 @@ -70,10 +84,10 @@ def test_version_has_an_equality_comparison(self, version_spec: VersionSpec): class TestBump: """Tests of the bump method.""" - def test_bump_does_not_change_original_version(self, version_spec: VersionSpec): + def test_does_not_change_original_version(self, semver_version_spec: VersionSpec): """Bumping a version does not change the original version.""" # Arrange - version1 = version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) + version1 = semver_version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) # Act version2 = version1.bump("patch") @@ -82,10 +96,10 @@ def test_bump_does_not_change_original_version(self, version_spec: VersionSpec): assert version1["patch"].value == "3" assert version2["patch"].value == "4" - def test_bump_returns_a_new_version(self, version_spec: VersionSpec): + def test_returns_a_new_version(self, semver_version_spec: VersionSpec): """Bumping a version returns a new version.""" # Arrange - version1 = version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) + version1 = semver_version_spec.create_version({"major": "1", "minor": "2", "patch": "3"}) # Act version2 = version1.bump("patch") @@ -93,10 +107,10 @@ def test_bump_returns_a_new_version(self, version_spec: VersionSpec): # Assert assert version1 is not version2 - def test_bump_changes_component_and_dependents(self, version_spec: VersionSpec): + def test_changes_component_and_dependents(self, semver_version_spec: VersionSpec): """Bumping a version bumps the specified component and changes its dependents.""" # Arrange - version1 = version_spec.create_version({"major": "1", "minor": "2", "patch": "3", "build": "4"}) + version1 = semver_version_spec.create_version({"major": "1", "minor": "2", "patch": "3", "build": "4"}) # Act patch_version = version1.bump("patch") @@ -130,10 +144,139 @@ class TestRequiredComponents: param({"major": "0", "minor": "0", "patch": "3", "build": ""}, ["patch"], id="patch"), ], ) - def test_returns_required_component_names(self, version_spec: VersionSpec, values: dict, expected: list): + def test_returns_required_component_names( + self, semver_version_spec: VersionSpec, values: dict, expected: list + ): + """The required_keys function returns all required keys.""" + # Arrange + version = semver_version_spec.create_version(values) + + # Act + required_components = version.required_components() + + # Assert + assert required_components == expected + + +class TestCalVerVersion: + """Test how a CalVer-style Version model should behave.""" + + @freeze_time("2020-05-01") + def test_acts_like_a_dict(self, calver_version_spec: VersionSpec): + """You can get version components by name.""" + # Arrange + version = calver_version_spec.create_version({"release": "2020.4.1", "patch": "3", "build": "10"}) + + # Assert + assert version["release"].value == "2020.4.1" + assert version["patch"].value == "3" + assert version["build"].value == "10" + + with pytest.raises(KeyError): + version["invalid"] + + def test_length_is_number_of_parts(self, calver_version_spec: VersionSpec): + # Arrange + version = calver_version_spec.create_version({"release": "2020.4.1"}) + + # Assert + assert len(version) == 3 + + def test_version_is_iterable(self, calver_version_spec: VersionSpec): + # Arrange + version = calver_version_spec.create_version({"release": "2020.4.1"}) + + # Assert + assert list(version) == ["release", "patch", "build"] + + def test_has_a_string_representation(self, calver_version_spec: VersionSpec): + # Arrange + version = calver_version_spec.create_version({"release": "2020.4.1", "patch": "3"}) + + # Assert + assert repr(version) == "" + + def test_version_has_an_equality_comparison(self, calver_version_spec: VersionSpec): + # Arrange + version1 = calver_version_spec.create_version({"release": "2020.4.1", "patch": "3"}) + version2 = calver_version_spec.create_version({"release": "2020.4.1", "patch": "3"}) + version3 = calver_version_spec.create_version({"release": "2020.5.1"}) + + # Assert + assert version1 == version2 + assert version1 != version3 + + class TestBump: + """Tests of the bump method.""" + + @freeze_time("2020-05-01") + def test_bump_does_not_change_original_version(self, calver_version_spec: VersionSpec): + """Bumping a version does not change the original version.""" + # Arrange + version1 = calver_version_spec.create_version({"release": "2020.4.1", "patch": "3", "build": "10"}) + + # Act + version2 = version1.bump("patch") + + # Assert + assert version1["patch"].value == "3" + assert version1["release"].value == "2020.4.1" + assert version2["patch"].value == "4" + assert version2["release"].value == "2020.4.1" + + @freeze_time("2020-05-01") + def test_bump_returns_a_new_version(self, calver_version_spec: VersionSpec): + """Bumping a version returns a new version.""" + # Arrange + version1 = calver_version_spec.create_version({"release": "2020.4.1", "patch": "3"}) + + # Act + version2 = version1.bump("patch") + + # Assert + assert version1 is not version2 + + @freeze_time("2020-05-01") + def test_bump_changes_component_and_dependents(self, calver_version_spec: VersionSpec): + """Bumping a version bumps the specified component and changes its dependents.""" + # Arrange + version1 = calver_version_spec.create_version({"release": "2020.4.1", "patch": "3", "build": "4"}) + + # Act + patch_version = version1.bump("patch") + release_version = version1.bump("release") + build_version = version1.bump("build") + + # Assert + patch_version_str = ".".join([item.value for item in patch_version.components.values()]) + release_version_str = ".".join([item.value for item in release_version.components.values()]) + build_version_str = ".".join([item.value for item in build_version.components.values()]) + assert patch_version_str == "2020.4.1.4.4" + assert release_version_str == "2020.5.1.0.4" + assert build_version_str == "2020.4.1.3.5" + + class TestRequiredComponents: + """Tests of the required_keys function.""" + + @pytest.mark.parametrize( + ["values", "expected"], + [ + param({"release": "2020.4.1", "patch": "3"}, ["release", "patch"], id="release-patch"), + param( + {"release": "2020.4.1", "build": "4"}, + ["release", "build"], + id="release-build", + ), + param({"release": "2020.4.1"}, ["release"], id="release"), + ], + ) + @freeze_time("2020-05-01") + def test_returns_required_component_names( + self, calver_version_spec: VersionSpec, values: dict, expected: list + ): """The required_keys function returns all required keys.""" # Arrange - version = version_spec.create_version(values) + version = calver_version_spec.create_version(values) # Act required_components = version.required_components() diff --git a/tests/test_versioning/test_models_versioncomponent.py b/tests/test_versioning/test_models_versioncomponent.py index b100d521..67510661 100644 --- a/tests/test_versioning/test_models_versioncomponent.py +++ b/tests/test_versioning/test_models_versioncomponent.py @@ -1,18 +1,19 @@ """Tests of the VersionPart model.""" +from freezegun import freeze_time + from bumpversion.versioning.models import VersionComponentSpec -from bumpversion.versioning.functions import ValuesFunction, NumericFunction +from bumpversion.versioning.functions import ValuesFunction, NumericFunction, CalVerFunction import pytest +params = { + "numeric": [{"optional_value": "0", "first_value": "0"}], + "values": [{"values": ["alpha", "beta", "gamma"]}], + "calver": [{"calver_format": "{YYYY}.{MM}.{DD}"}], +} + -@pytest.fixture( - params=[ - {"optional_value": "0", "first_value": "0"}, - {"first_value": "1"}, - {"values": ["alpha", "beta", "gamma"]}, - {"values": ["alpha", "gamma"]}, - ] -) +@pytest.fixture def version_component_config(request): """Return a three-part and a two-part version part configuration.""" return VersionComponentSpec(**request.param) @@ -20,45 +21,143 @@ def version_component_config(request): class TestVersionComponent: class TestCreation: - def test_none_value_uses_optional_value(self, version_component_config): + @pytest.mark.parametrize( + ["version_component_config"], + [ + params["numeric"], + params["values"], + ], + ids=["numeric", "values"], + indirect=True, + ) + def test_none_value_uses_optional_value(self, version_component_config: VersionComponentSpec): + """When passing in no value, the optional value is used.""" vp = version_component_config.create_component() assert vp.value == vp.func.optional_value assert vp._value is None + @pytest.mark.parametrize( + ["version_component_config"], + [params["calver"]], + ids=["calver"], + indirect=True, + ) + @freeze_time("2020-05-01") + def test_none_value_uses_current_date(self, version_component_config: VersionComponentSpec): + """When passing in no value, the current date is used for CalVer components.""" + vp = version_component_config.create_component() + assert vp.value == "2020.5.1" + assert vp._value == "2020.5.1" + def test_config_with_values_selects_values_function(self): values = ["0", "1", "2"] vp = VersionComponentSpec(values=values).create_component() assert isinstance(vp.func, ValuesFunction) - def test_config_without_values_selects_numeric_function(self): + def test_config_with_calver_selects_calver_function(self): + vp = VersionComponentSpec(calver_format="{YYYY}.{MM}").create_component() + assert isinstance(vp.func, CalVerFunction) + + def test_config_defaults_to_numeric_function(self): vp = VersionComponentSpec().create_component() assert isinstance(vp.func, NumericFunction) + @pytest.mark.parametrize( + ["version_component_config"], + [ + params["numeric"], + params["values"], + params["calver"], + ], + ids=["numeric", "values", "calver"], + indirect=True, + ) def test_copy_returns_new_version_part(self, version_component_config): vp = version_component_config.create_component(version_component_config.first_value) vc = vp.copy() assert vp.value == vc.value assert id(vp) != id(vc) - def test_bump_increments_value(self, version_component_config): + @pytest.mark.parametrize( + ["version_component_config"], + [params["numeric"]], + ids=["numeric"], + indirect=True, + ) + def test_bump_increments_numeric_value(self, version_component_config): vp = version_component_config.create_component(version_component_config.first_value) vc = vp.bump() - if version_component_config.values: - assert vc.value == str(version_component_config.values[1]) - else: - assert vc.value == str(int(version_component_config.first_value) + 1) + assert vc.value == str(int(version_component_config.first_value) + 1) + @pytest.mark.parametrize( + ["version_component_config"], + [params["values"]], + ids=["values"], + indirect=True, + ) + def test_bump_selects_next_value(self, version_component_config): + vp = version_component_config.create_component(version_component_config.first_value) + vc = vp.bump() + assert vc.value == str(version_component_config.values[1]) + + @pytest.mark.parametrize( + ["version_component_config"], + [ + params["numeric"], + params["values"], + params["calver"], + ], + ids=["numeric", "values", "calver"], + indirect=True, + ) def test_non_first_value_is_not_optional(self, version_component_config): assert not version_component_config.create_component(version_component_config.first_value).bump().is_optional + @pytest.mark.parametrize( + ["version_component_config"], + [params["calver"]], + ids=["calver"], + indirect=True, + ) + def test_calver_is_not_optional(self, version_component_config): + vc = version_component_config.create_component(version_component_config.first_value) + assert not vc.is_optional + + @pytest.mark.parametrize( + ["version_component_config"], + [params["numeric"], params["values"]], + ids=["numeric", "values"], + indirect=True, + ) def test_first_value_is_optional(self, version_component_config): - assert version_component_config.create_component(version_component_config.first_value).is_optional + vc = version_component_config.create_component(version_component_config.first_value) + assert vc.is_optional + @pytest.mark.parametrize( + ["version_component_config"], + [ + params["numeric"], + params["values"], + params["calver"], + ], + ids=["numeric", "values", "calver"], + indirect=True, + ) def test_versionparts_with_same_settings_are_equal(self, version_component_config): version1 = version_component_config.create_component(version_component_config.first_value) version2 = version_component_config.create_component(version_component_config.first_value) assert version1 == version2 + @pytest.mark.parametrize( + ["version_component_config"], + [ + params["numeric"], + params["values"], + params["calver"], + ], + ids=["numeric", "values", "calver"], + indirect=True, + ) def test_null_resets_value_to_first_value(self, version_component_config): version1 = version_component_config.create_component(version_component_config.first_value) version2 = version1.bump().null()