#2 Add tests for the version module
Merged 4 years ago by jjames. Opened 4 years ago by defolos.
defolos/opam2rpm add_tests_for_version  into  master

file added
+4
@@ -0,0 +1,4 @@ 

+ [run]

+ branch = True

+ [tool:pytest]

+ addopts = --cov=opam2rpm --cov-report html

file modified
+3
@@ -1,2 +1,5 @@ 

  /build

  /opam2rpm/__pycache__

+ /htmlcov

+ .coverage

+ opam2rpm.egg-info

@@ -0,0 +1,5 @@ 

+ -r requirements.txt

+ 

+ pytest

+ pytest-cov

+ mypy

file modified
+55 -27
@@ -30,22 +30,45 @@ 

  

  To use this class, create a Version object from the upstream version number:

  

-     >>> v = Version('v1.2.3')

+ >>> v = Version('v1.2.3')

  

  Version objects can be compared as though they were numbers:

  

-     >>> w = Version('v1.2.3b')

-     >>> x = Version('v1.2.3~2')

-     >>> v < w

-     True

-     >>> v < x

-     False

+ >>> w = Version('v1.2.3b')

+ >>> x = Version('v1.2.3~2')

+ >>> v < w

+ True

+ >>> v < x

+ False

+ 

  """

+ from __future__ import annotations

  

+ from enum import IntEnum

  from itertools import zip_longest

+ from typing import List, Tuple, Literal, Optional

  import re

  

- def char_class(char):

+ 

+ class CharClass(IntEnum):

+     """Character classes inside a version string"""

+     TILDE = 0

+     EMPTY = 1

+     LETTER = 2

+     REST = 3

+ 

+ 

+ def str_to_int(number_or_not: Optional[str],

+                fallback: int=0) -> int:

+     if number_or_not is None:

+         return fallback

+     try:

+         return int(number_or_not)

+     except ValueError:

+         return fallback

+ 

+ 

+ def char_class(char: Optional[str]) -> CharClass:

      """Determine which class this character belongs to:

      0: tilde

      1: empty
@@ -53,12 +76,13 @@ 

      3: anything else

      """

      if char == '~':

-         return 0

-     if char == '':

-         return 1

+         return CharClass.TILDE

+     if char == '' or char is None:

+         return CharClass.EMPTY

      if char.isalpha():

-         return 2

-     return 3

+         return CharClass.LETTER

+     return CharClass.REST

+ 

  

  class Version:

      """Representation of a package version.
@@ -67,9 +91,9 @@ 

  

      __slots__ = ['elems']

  

-     def __init__(self, verstring):

+     def __init__(self, verstring: str) -> None:

          """Initialize a version object from a version string."""

-         self.elems = []

+         self.elems: List[str] = []

          while verstring:

              digits = re.search(r'\d+', verstring)

              if digits:
@@ -81,9 +105,9 @@ 

                  verstring = verstring[end:]

              else:

                  self.elems.append(verstring)

-                 verstring = None

+                 break

  

-     def compare(self, other):

+     def compare(self, other: Version) -> Literal[1, -1, 0]:

          """Compare two versions.

  

          Returns a negative value, zero, or a positive value as the first
@@ -91,19 +115,19 @@ 

          respectively.

          """

          digit = False

-         comparison = 0

-         for el1, el2 in zip_longest(self.elems, other.elems, fillvalue=''):

+         comparison: Literal[1, -1, 0] = 0

+         for el1, el2 in zip_longest(self.elems, other.elems):

              if digit:

-                 if int(el1) < int(el2):

+                 int_el1, int_el2 = str_to_int(el1), str_to_int(el2)

+                 if int_el1 < int_el2:

                      comparison = -1

                      break

-                 if int(el1) > int(el2):

+                 if int_el1 > int_el2:

                      comparison = 1

                      break

              else:

-                 for ch1, ch2 in zip_longest(el1, el2, fillvalue=''):

-                     cl1 = char_class(ch1)

-                     cl2 = char_class(ch2)

+                 for ch1, ch2 in zip_longest(el1 or '', el2 or ''):

+                     cl1, cl2 = char_class(ch1), char_class(ch2)

                      if cl1 < cl2:

                          comparison = -1

                          break
@@ -116,6 +140,10 @@ 

                      if ch1 > ch2:

                          comparison = 1

                          break

+                 # need to break out of the outer loop as well if we found a

+                 # valid comparison

+                 if comparison != 0:

+                     break

              digit = not digit

          return comparison

  
@@ -141,11 +169,11 @@ 

          return ''.join(self.elems)

  

      @staticmethod

-     def _to_fedora(elems):

+     def _to_fedora(elems: List[str]) -> Tuple[str, str]:

          """Convert elems to Fedora version and release numbers."""

          ver = str(elems.pop(0))

          while elems and \

-               (elems[0] == '.' or elems[0] == '_' or elems[0].startswith('~')):

+                 (elems[0] == '.' or elems[0] == '_' or elems[0].startswith('~')):

              dot = elems.pop(0)

              if dot == '_':

                  dot = '.'
@@ -156,7 +184,7 @@ 

                  ver += '.0'

          return ver, '0.1.'.join(elems) if elems else '1'

  

-     def to_fedora(self):

+     def to_fedora(self) -> Tuple[str, str]:

          """Convert this version number to Fedora version and release numbers."""

          if not self.elems:

              return '0', '0.1'

file added
+2
@@ -0,0 +1,2 @@ 

+ [pytest]

+ addopts = --doctest-modules --cov=opam2rpm --cov-report html --cov-report term

file modified
+1 -1
@@ -1,4 +1,4 @@ 

- BeautifulSoup

+ BeautifulSoup4

  jinja2

  pyparsing

  requests

@@ -0,0 +1,56 @@ 

+ from opam2rpm.version import Version

+ 

+ v_middle = Version("v1.2.3")

+ v_highest = Version("v1.2.3b")

+ v_lowest = Version('v1.2.3~2')

+ 

+ 

+ def test_version_greater():

+     assert(Version("v2.4b") > Version("v0.2.4~"))

+     assert(Version("v2.4b") > Version("v0.2.4"))

+     assert(Version("v1") > Version("v0.4"))

+ 

+     assert(Version("v2.4b") > Version("v2.4a"))

+ 

+     assert(v_highest > v_middle > v_lowest)

+ 

+ 

+ def test_version_equal():

+     assert(Version("v0.1c") == Version("v0.1c"))

+     assert(v_highest == v_highest)

+ 

+ 

+ def test_version_smaller():

+     assert(Version("v0.2.4~") < Version("v2.4b"))

+     assert(Version("v0.2.4") < Version("v2.4b"))

+     assert(Version("v0.4") < Version("v1"))

+ 

+     assert(Version("v2.4a") < Version("v2.4b"))

+ 

+     assert(v_lowest < v_middle < v_highest)

+ 

+ 

+ def test_version_not_equal():

+     assert(v_middle != v_highest)

+     assert(v_middle != v_lowest)

+     assert(v_lowest != v_highest)

+ 

+ 

+ def test_str():

+     for s in ["v", "v1.2", "v0.16~", "3.4.8a"]:

+         assert(str(Version(s)) == s)

+ 

+ 

+ class TestToFedora:

+     def test_from_empty(self):

+         assert(Version("").to_fedora() == ("0", "0.1"))

+ 

+     def _test_from_single_digit(self):

+         # FIXME: this does not look right:

+         assert(Version("b1.2").to_fedora() == ('0', 'b0.1.10.1..0.1.2'))

+ 

+     def test_general_case(self):

+         assert(Version("v0.1.2~").to_fedora() == ("0.1.2.0", "1"))

+         assert(Version("v0.1.2").to_fedora() == ("0.1.2", "1"))

+ 

+         assert(Version("v0.1.2_3").to_fedora() == ("0.1.2.3", "1"))

This pull request adds type hints, unit tests and a small bugfix to the version module

Wow, thanks a lot for this. Unit tests have been on my TODO list for a month now, so thanks for giving me a shove in the right direction.

Pull-Request has been merged by jjames

4 years ago