From adad7bbaadf30b5980cbc7d73d18fa870577542d Mon Sep 17 00:00:00 2001 From: Adam Samalik Date: Thu, 29 Jun 2023 18:25:13 +0200 Subject: [PATCH] re-import sources as agreed with the maintainer --- .gitignore | 4 +- tests/scripts/__init__.py | 0 tests/scripts/compat.py | 14 + tests/scripts/run_tests.sh | 3 + tests/scripts/test_base.py | 729 ++++++++++++++++++++++++++++++++++ tests/scripts/test_match.py | 175 ++++++++ tests/scripts/test_parsing.py | 99 +++++ tests/scripts/test_spec.py | 156 ++++++++ tests/tests.yml | 15 + 9 files changed, 1194 insertions(+), 1 deletion(-) create mode 100644 tests/scripts/__init__.py create mode 100644 tests/scripts/compat.py create mode 100644 tests/scripts/run_tests.sh create mode 100755 tests/scripts/test_base.py create mode 100755 tests/scripts/test_match.py create mode 100755 tests/scripts/test_parsing.py create mode 100644 tests/scripts/test_spec.py create mode 100644 tests/tests.yml diff --git a/.gitignore b/.gitignore index 5e058ba..8db3d96 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -SOURCES/semantic_version-2.6.0.tar.gz +/semantic_version-2.4.1.tar.gz +/semantic_version-2.4.2.tar.gz +/semantic_version-2.5.0.tar.gz /semantic_version-2.6.0.tar.gz diff --git a/tests/scripts/__init__.py b/tests/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/scripts/compat.py b/tests/scripts/compat.py new file mode 100644 index 0000000..f3ef508 --- /dev/null +++ b/tests/scripts/compat.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Copyright (c) The python-semanticversion project +# This code is distributed under the two-clause BSD License. + +import sys + +is_python2 = (sys.version_info[0] == 2) + + +try: # pragma: no cover + import unittest2 as unittest +except ImportError: # pragma: no cover + import unittest + diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh new file mode 100644 index 0000000..826e3a5 --- /dev/null +++ b/tests/scripts/run_tests.sh @@ -0,0 +1,3 @@ +#!/usr/bin/bash +set -eux +python3 -m nose . diff --git a/tests/scripts/test_base.py b/tests/scripts/test_base.py new file mode 100755 index 0000000..a1255c3 --- /dev/null +++ b/tests/scripts/test_base.py @@ -0,0 +1,729 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) The python-semanticversion project +# This code is distributed under the two-clause BSD License. + +"""Test the various functions from 'base'.""" + +from .compat import unittest, is_python2 + +from semantic_version import base + +class ComparisonTestCase(unittest.TestCase): + def test_identifier_cmp(self): + cases = [ + # Integers + ('1', '1', 0), + ('1', '2', -1), + ('11', '2', 1), + ('3333', '40', 1), + + # Text + ('aa', 'ab', -1), + ('aa', 'aa', 0), + ('ab', 'aa', 1), + ('aaa', 'ab', -1), + + # Mixed + ('10', '1a', -1), + ('1a', '10', 1), + ('ab1', '42', 1), + ] + + for a, b, expected in cases: + result = base.identifier_cmp(a, b) + self.assertEqual(expected, result, + "identifier_cmp(%r, %r) returned %d instead of %d" % ( + a, b, result, expected)) + + def test_identifier_list_cmp(self): + cases = [ + # Same length + (['1', '2', '3'], ['1', '2', '3'], 0), + (['1', '2', '3'], ['1', '3', '2'], -1), + (['1', '2', '4'], ['1', '2', '3'], 1), + + # Mixed lengths + (['1', 'a'], ['1', 'a', '0'], -1), + (['1', 'a', '0'], ['1', 'a'], 1), + (['1', 'b'], ['1', 'a', '1000'], 1), + ] + + for a, b, expected in cases: + result = base.identifier_list_cmp(a, b) + self.assertEqual(expected, result, + "identifier_list_cmp(%r, %r) returned %d instead of %d" % ( + a, b, result, expected)) + + +class TopLevelTestCase(unittest.TestCase): + """Test module-level functions.""" + + versions = ( + ('0.1.0', '0.1.1', -1), + ('0.1.1', '0.1.1', 0), + ('0.1.1', '0.1.0', 1), + ('0.1.0-alpha', '0.1.0', -1), + ('0.1.0-alpha+2', '0.1.0-alpha', NotImplemented), + ) + + def test_compare(self): + for a, b, expected in self.versions: + result = base.compare(a, b) + self.assertEqual(expected, result, + "compare(%r, %r) should be %r instead of %r" % (a, b, expected, result)) + + matches = ( + ('>=0.1.1', '0.1.2'), + ('>=0.1.1', '0.1.1'), + ('>=0.1.1', '0.1.1-alpha'), + ('>=0.1.1,!=0.2.0', '0.2.1'), + ) + + def test_match(self): + for spec, version in self.matches: + self.assertTrue(base.match(spec, version), + "%r should accept %r" % (spec, version)) + + valid_strings = ( + '1.0.0-alpha', + '1.0.0-alpha.1', + '1.0.0-beta.2', + '1.0.0-beta.11', + '1.0.0-rc.1', + '1.0.0-rc.1+build.1', + '1.0.0', + '1.0.0+0.3.7', + '1.3.7+build', + '1.3.7+build.2.b8f12d7', + '1.3.7+build.11.e0f985a', + '1.1.1', + '1.1.2', + '1.1.3-rc4.5', + '1.1.3-rc42.3-14-15.24+build.2012-04-13.223', + '1.1.3+build.2012-04-13.HUY.alpha-12.1', + ) + + def test_validate_valid(self): + for version in self.valid_strings: + self.assertTrue(base.validate(version), + "%r should be a valid version" % (version,)) + + invalid_strings = ( + '1', + 'v1', + '1.2.3.4', + '1.2', + '1.2a3', + '1.2.3a4', + 'v12.34.5', + '1.2.3+4+5', + ) + + def test_validate_invalid(self): + for version in self.invalid_strings: + self.assertFalse(base.validate(version), + "%r should not be a valid version" % (version,)) + + +class VersionTestCase(unittest.TestCase): + versions = { + '1.0.0-alpha': (1, 0, 0, ('alpha',), ()), + '1.0.0-alpha.1': (1, 0, 0, ('alpha', '1'), ()), + '1.0.0-beta.2': (1, 0, 0, ('beta', '2'), ()), + '1.0.0-beta.11': (1, 0, 0, ('beta', '11'), ()), + '1.0.0-rc.1': (1, 0, 0, ('rc', '1'), ()), + '1.0.0-rc.1+build.1': (1, 0, 0, ('rc', '1'), ('build', '1')), + '1.0.0': (1, 0, 0, (), ()), + '1.0.0+0.3.7': (1, 0, 0, (), ('0', '3', '7')), + '1.3.7+build': (1, 3, 7, (), ('build',)), + '1.3.7+build.2.b8f12d7': (1, 3, 7, (), ('build', '2', 'b8f12d7')), + '1.3.7+build.11.e0f985a': (1, 3, 7, (), ('build', '11', 'e0f985a')), + '1.1.1': (1, 1, 1, (), ()), + '1.1.2': (1, 1, 2, (), ()), + '1.1.3-rc4.5': (1, 1, 3, ('rc4', '5'), ()), + '1.1.3-rc42.3-14-15.24+build.2012-04-13.223': + (1, 1, 3, ('rc42', '3-14-15', '24'), ('build', '2012-04-13', '223')), + '1.1.3+build.2012-04-13.HUY.alpha-12.1': + (1, 1, 3, (), ('build', '2012-04-13', 'HUY', 'alpha-12', '1')), + } + + def test_parsing(self): + for text, expected_fields in self.versions.items(): + version = base.Version(text) + actual_fields = (version.major, version.minor, version.patch, + version.prerelease, version.build) + self.assertEqual(expected_fields, actual_fields) + + def test_str(self): + for text in self.versions: + version = base.Version(text) + self.assertEqual(text, str(version)) + self.assertEqual("Version('%s')" % text, repr(version)) + + def test_compare_to_self(self): + for text in self.versions: + self.assertEqual(base.Version(text), base.Version(text)) + self.assertNotEqual(text, base.Version(text)) + + partial_versions = { + '1.1': (1, 1, None, None, None), + '2': (2, None, None, None, None), + '1.0.0-alpha': (1, 0, 0, ('alpha',), None), + '1.0.0-alpha.1': (1, 0, 0, ('alpha', '1'), None), + '1.0.0-beta.2': (1, 0, 0, ('beta', '2'), None), + '1.0.0-beta.11': (1, 0, 0, ('beta', '11'), None), + '1.0.0-rc.1': (1, 0, 0, ('rc', '1'), None), + '1.0.0': (1, 0, 0, None, None), + '1.1.1': (1, 1, 1, None, None), + '1.1.2': (1, 1, 2, None, None), + '1.1.3-rc4.5': (1, 1, 3, ('rc4', '5'), None), + '1.0.0-': (1, 0, 0, (), None), + '1.0.0-rc.1+build.1': (1, 0, 0, ('rc', '1'), ('build', '1')), + '1.0.0+0.3.7': (1, 0, 0, (), ('0', '3', '7')), + '1.3.7+build': (1, 3, 7, (), ('build',)), + '1.3.7+build.2.b8f12d7': (1, 3, 7, (), ('build', '2', 'b8f12d7')), + '1.3.7+build.11.e0f985a': (1, 3, 7, (), ('build', '11', 'e0f985a')), + '1.1.3-rc42.3-14-15.24+build.2012-04-13.223': + (1, 1, 3, ('rc42', '3-14-15', '24'), ('build', '2012-04-13', '223')), + '1.1.3+build.2012-04-13.HUY.alpha-12.1': + (1, 1, 3, (), ('build', '2012-04-13', 'HUY', 'alpha-12', '1')), + } + + def test_parsing_partials(self): + for text, expected_fields in self.partial_versions.items(): + version = base.Version(text, partial=True) + actual_fields = (version.major, version.minor, version.patch, + version.prerelease, version.build) + self.assertEqual(expected_fields, actual_fields) + self.assertTrue(version.partial, "%r should have partial=True" % version) + + def test_str_partials(self): + for text in self.partial_versions: + version = base.Version(text, partial=True) + self.assertEqual(text, str(version)) + self.assertEqual("Version('%s', partial=True)" % text, repr(version)) + + def test_compare_partial_to_self(self): + for text in self.partial_versions: + self.assertEqual( + base.Version(text, partial=True), + base.Version(text, partial=True)) + self.assertNotEqual(text, base.Version(text, partial=True)) + + def test_hash(self): + self.assertEqual(1, + len(set([base.Version('0.1.0'), base.Version('0.1.0')]))) + + self.assertEqual(2, + len(set([base.Version('0.1.0'), base.Version('0.1.0', partial=True)]))) + + # A fully-defined 'partial' version isn't actually partial. + self.assertEqual(1, + len(set([ + base.Version('0.1.0-a1+34'), + base.Version('0.1.0-a1+34', partial=True) + ])) + ) + + @unittest.skipIf(is_python2, "Comparisons to other objects are broken in Py2.") + def test_invalid_comparisons(self): + v = base.Version('0.1.0') + with self.assertRaises(TypeError): + v < '0.1.0' + with self.assertRaises(TypeError): + v <= '0.1.0' + with self.assertRaises(TypeError): + v > '0.1.0' + with self.assertRaises(TypeError): + v >= '0.1.0' + + self.assertTrue(v != '0.1.0') + self.assertFalse(v == '0.1.0') + + def test_bump_clean_versions(self): + # We Test each property explicitly as the == comparator for versions + # does not distinguish between prerelease or builds for equality. + + v = base.Version('1.0.0+build') + v = v.next_major() + self.assertEqual(v.major, 2) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.0+build') + v = v.next_minor() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 1) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.0+build') + v = v.next_patch() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 1) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.1.0+build') + v = v.next_major() + self.assertEqual(v.major, 2) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.1.0+build') + v = v.next_minor() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 2) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.1.0+build') + v = v.next_patch() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 1) + self.assertEqual(v.patch, 1) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.1+build') + v = v.next_major() + self.assertEqual(v.major, 2) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.1+build') + v = v.next_minor() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 1) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.1+build') + v = v.next_patch() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 2) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + def test_bump_prerelease_versions(self): + # We Test each property explicitly as the == comparator for versions + # does not distinguish between prerelease or builds for equality. + + v = base.Version('1.0.0-pre+build') + v = v.next_major() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.0-pre+build') + v = v.next_minor() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.0-pre+build') + v = v.next_patch() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.1.0-pre+build') + v = v.next_major() + self.assertEqual(v.major, 2) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.1.0-pre+build') + v = v.next_minor() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 1) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.1.0-pre+build') + v = v.next_patch() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 1) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.1-pre+build') + v = v.next_major() + self.assertEqual(v.major, 2) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.1-pre+build') + v = v.next_minor() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 1) + self.assertEqual(v.patch, 0) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + v = base.Version('1.0.1-pre+build') + v = v.next_patch() + self.assertEqual(v.major, 1) + self.assertEqual(v.minor, 0) + self.assertEqual(v.patch, 1) + self.assertEqual(v.prerelease, ()) + self.assertEqual(v.build, ()) + + +class SpecItemTestCase(unittest.TestCase): + invalids = [ + '<=0.1.1+build3', + '<=0.1.1+', + '>0.2.3-rc2+', + ] + + def test_invalids(self): + for invalid in self.invalids: + with self.assertRaises(ValueError, msg="SpecItem(%r) should be invalid" % invalid): + _v = base.SpecItem(invalid) + + components = { + '==0.1.0': (base.SpecItem.KIND_EQUAL, 0, 1, 0, None, None), + '==0.1.2-rc3': (base.SpecItem.KIND_EQUAL, 0, 1, 2, ('rc3',), None), + '==0.1.2+build3.14': (base.SpecItem.KIND_EQUAL, 0, 1, 2, (), ('build3', '14')), + '<=0.1.1': (base.SpecItem.KIND_LTE, 0, 1, 1, None, None), + '<0.1.1': (base.SpecItem.KIND_LT, 0, 1, 1, None, None), + '<=0.1.1': (base.SpecItem.KIND_LTE, 0, 1, 1, None, None), + '!=0.1.1+': (base.SpecItem.KIND_NEQ, 0, 1, 1, (), ()), + '<=0.1.1-': (base.SpecItem.KIND_LTE, 0, 1, 1, (), None), + '>=0.2.3-rc2': (base.SpecItem.KIND_GTE, 0, 2, 3, ('rc2',), None), + '>=2.0.0': (base.SpecItem.KIND_GTE, 2, 0, 0, None, None), + '!=0.1.1+rc3': (base.SpecItem.KIND_NEQ, 0, 1, 1, (), ('rc3',)), + '!=0.3.0': (base.SpecItem.KIND_NEQ, 0, 3, 0, None, None), + '=0.3.0': (base.SpecItem.KIND_EQUAL, 0, 3, 0, None, None), + '0.3.0': (base.SpecItem.KIND_EQUAL, 0, 3, 0, None, None), + '~0.1.2': (base.SpecItem.KIND_TILDE, 0, 1, 2, None, None), + '^0.1.3': (base.SpecItem.KIND_CARET, 0, 1, 3, None, None), + } + + def test_components(self): + for spec_text, components in self.components.items(): + kind, major, minor, patch, prerelease, build = components + spec = base.SpecItem(spec_text) + + self.assertEqual(kind, spec.kind) + self.assertEqual(major, spec.spec.major) + self.assertEqual(minor, spec.spec.minor) + self.assertEqual(patch, spec.spec.patch) + self.assertEqual(prerelease, spec.spec.prerelease) + self.assertEqual(build, spec.spec.build) + + matches = { + '==0.1.0': ( + ['0.1.0', '0.1.0-rc1', '0.1.0+build1', '0.1.0-rc1+build2'], + ['0.0.1', '0.2.0', '0.1.1'], + ), + '=0.1.0': ( + ['0.1.0', '0.1.0-rc1', '0.1.0+build1', '0.1.0-rc1+build2'], + ['0.0.1', '0.2.0', '0.1.1'], + ), + '0.1.0': ( + ['0.1.0', '0.1.0-rc1', '0.1.0+build1', '0.1.0-rc1+build2'], + ['0.0.1', '0.2.0', '0.1.1'], + ), + '==0.1.2-rc3': ( + ['0.1.2-rc3+build1', '0.1.2-rc3+build4.5'], + ['0.1.2-rc4', '0.1.2', '0.1.3'], + ), + '==0.1.2+build3.14': ( + ['0.1.2+build3.14'], + ['0.1.2-rc+build3.14', '0.1.2+build3.15'], + ), + '<=0.1.1': ( + ['0.0.0', '0.1.1-alpha1', '0.1.1', '0.1.1+build2'], + ['0.1.2'], + ), + '<0.1.1': ( + ['0.1.0', '0.0.0'], + ['0.1.1', '0.1.1-zzz+999', '1.2.0', '0.1.1+build3'], + ), + '<=0.1.1': ( + ['0.1.1+build4', '0.1.1-alpha', '0.1.0'], + ['0.2.3', '1.1.1', '0.1.2'], + ), + '<0.1.1-': ( + ['0.1.0', '0.1.1-alpha', '0.1.1-alpha+4'], + ['0.2.0', '1.0.0', '0.1.1', '0.1.1+build1'], + ), + '>=0.2.3-rc2': ( + ['0.2.3-rc3', '0.2.3', '0.2.3+1', '0.2.3-rc2', '0.2.3-rc2+1'], + ['0.2.3-rc1', '0.2.2'], + ), + '==0.2.3+': ( + ['0.2.3'], + ['0.2.3+rc1', '0.2.4', '0.2.3-rc2'], + ), + '!=0.2.3-rc2+12': ( + ['0.2.3-rc3', '0.2.3', '0.2.3-rc2+1', '0.2.4', '0.2.3-rc3+12'], + ['0.2.3-rc2+12'], + ), + '==2.0.0+b1': ( + ['2.0.0+b1'], + ['2.1.1', '1.9.9', '1.9.9999', '2.0.0', '2.0.0-rc4'], + ), + '!=0.1.1': ( + ['0.1.2', '0.1.0', '1.4.2'], + ['0.1.1', '0.1.1-alpha', '0.1.1+b1'], + ), + '!=0.3.4-': ( + ['0.4.0', '1.3.0', '0.3.4-alpha', '0.3.4-alpha+b1'], + ['0.3.4', '0.3.4+b1'], + ), + '~1.1.2': ( + ['1.1.3', '1.1.2-alpha', '1.1.2-alpha+b1'], + ['1.1.1', '1.2.1', '2.1.0'], + ), + '^1.1.2': ( + ['1.1.3', '1.2.1', '1.1.2-alpha', '1.1.2-alpha+b1'], + ['1.1.1', '2.1.0'], + ), + '^0.1.2': ( + ['0.1.2', '0.1.2-alpha', '0.1.3'], + ['0.2.0', '1.1.2', '0.1.1'], + ), + '^0.0.2': ( + ['0.0.2', '0.0.2-alpha', '0.0.2+abb'], + ['0.1.0', '0.0.3', '1.0.0'], + ), + '~=1.4.5': ( + ['1.4.5', '1.4.10-alpha', '1.4.10'], + ['1.3.6', '1.4.4', '1.5.0'], + ), + '~=1.4.0': ( + ['1.4.0', '1.4.10-alpha', '1.4.10'], + ['1.3.6', '1.3.9', '1.5.0'], + ), + '~=1.4': ( + ['1.4.0', '1.6.10-alpha', '1.6.10'], + ['1.3.0', '2.0.0'], + ), + } + + def test_matches(self): + for spec_text, versions in self.matches.items(): + spec = base.SpecItem(spec_text) + matching, failing = versions + + for version_text in matching: + version = base.Version(version_text) + self.assertTrue(spec.match(version), "%r should match %r" % (version, spec)) + + for version_text in failing: + version = base.Version(version_text) + self.assertFalse(spec.match(version), + "%r should not match %r" % (version, spec)) + + def test_equality(self): + spec1 = base.SpecItem('==0.1.0') + spec2 = base.SpecItem('==0.1.0') + self.assertEqual(spec1, spec2) + self.assertFalse(spec1 == '==0.1.0') + + def test_to_string(self): + spec = base.SpecItem('==0.1.0') + self.assertEqual('==0.1.0', str(spec)) + self.assertEqual(base.SpecItem.KIND_EQUAL, spec.kind) + + def test_hash(self): + self.assertEqual(1, + len(set([base.SpecItem('==0.1.0'), base.SpecItem('==0.1.0')]))) + + +class CoerceTestCase(unittest.TestCase): + examples = { + # Dict of target: [list of equivalents] + '0.0.0': ('0', '0.0', '0.0.0', '0.0.0+', '0-'), + '0.1.0': ('0.1', '0.1+', '0.1-', '0.1.0'), + '0.1.0+2': ('0.1.0+2', '0.1.0.2'), + '0.1.0+2.3.4': ('0.1.0+2.3.4', '0.1.0+2+3+4', '0.1.0.2+3+4'), + '0.1.0+2-3.4': ('0.1.0+2-3.4', '0.1.0+2-3+4', '0.1.0.2-3+4', '0.1.0.2_3+4'), + '0.1.0-a2.3': ('0.1.0-a2.3', '0.1.0a2.3', '0.1.0_a2.3'), + '0.1.0-a2.3+4.5-6': ('0.1.0-a2.3+4.5-6', '0.1.0a2.3+4.5-6', '0.1.0a2.3+4.5_6', '0.1.0a2.3+4+5/6'), + } + + def test_coerce(self): + for equivalent, samples in self.examples.items(): + target = base.Version(equivalent) + for sample in samples: + v_sample = base.Version.coerce(sample) + self.assertEqual(target, v_sample) + + def test_invalid(self): + self.assertRaises(ValueError, base.Version.coerce, 'v1') + + +class SpecTestCase(unittest.TestCase): + examples = { + '>=0.1.1,<0.1.2': ['>=0.1.1', '<0.1.2'], + '>=0.1.0,!=0.1.3-rc1,<0.1.3': ['>=0.1.0', '!=0.1.3-rc1', '<0.1.3'], + } + + def test_parsing(self): + for spec_list_text, specs in self.examples.items(): + spec_list = base.Spec(spec_list_text) + + self.assertEqual(spec_list_text, str(spec_list)) + self.assertNotEqual(spec_list_text, spec_list) + self.assertEqual(specs, [str(spec) for spec in spec_list]) + + for spec_text in specs: + self.assertTrue(repr(base.SpecItem(spec_text)) in repr(spec_list)) + + split_examples = { + ('>=0.1.1', '<0.1.2', '!=0.1.1+build1'): ['>=0.1.1', '<0.1.2', '!=0.1.1+build1'], + ('>=0.1.0', '!=0.1.3-rc1,<0.1.3'): ['>=0.1.0', '!=0.1.3-rc1', '<0.1.3'], + } + + def test_parsing_split(self): + for spec_list_texts, specs in self.split_examples.items(): + spec_list = base.Spec(*spec_list_texts) + + self.assertEqual(','.join(spec_list_texts), str(spec_list)) + self.assertEqual(specs, [str(spec) for spec in spec_list]) + self.assertEqual(spec_list, base.Spec(','.join(spec_list_texts))) + + for spec_text in specs: + self.assertTrue(repr(base.SpecItem(spec_text)) in repr(spec_list)) + + matches = { + # At least 0.1.1 including pre-releases, less than 0.1.2 excluding pre-releases + '>=0.1.1,<0.1.2': ( + ['0.1.1', '0.1.1+4', '0.1.1-alpha'], + ['0.1.2-alpha', '0.1.2', '1.3.4'], + ), + # At least 0.1.0 without pre-releases, less than 0.1.4 excluding pre-releases, + # neither 0.1.3-rc1 nor any build of that version, + # not 0.1.0+b3 precisely + '>=0.1.0-,!=0.1.3-rc1,!=0.1.0+b3,<0.1.4': ( + ['0.1.1', '0.1.0+b4', '0.1.2', '0.1.3-rc2'], + ['0.0.1', '0.1.0+b3', '0.1.4', '0.1.4-alpha', '0.1.3-rc1+4', + '0.1.0-alpha', '0.2.2', '0.1.4-rc1'], + ), + } + + def test_matches(self): + for spec_list_text, versions in self.matches.items(): + spec_list = base.Spec(spec_list_text) + matching, failing = versions + + for version_text in matching: + version = base.Version(version_text) + self.assertTrue(version in spec_list, + "%r should be in %r" % (version, spec_list)) + self.assertTrue(spec_list.match(version), + "%r should match %r" % (version, spec_list)) + + for version_text in failing: + version = base.Version(version_text) + self.assertFalse(version in spec_list, + "%r should not be in %r" % (version, spec_list)) + self.assertFalse(spec_list.match(version), + "%r should not match %r" % (version, spec_list)) + + def test_equality(self): + for spec_list_text in self.examples: + slist1 = base.Spec(spec_list_text) + slist2 = base.Spec(spec_list_text) + self.assertEqual(slist1, slist2) + self.assertFalse(slist1 == spec_list_text) + + def test_filter_empty(self): + s = base.Spec('>=0.1.1') + res = tuple(s.filter(())) + self.assertEqual((), res) + + def test_filter_incompatible(self): + s = base.Spec('>=0.1.1,!=0.1.4') + res = tuple(s.filter([ + base.Version('0.1.0'), + base.Version('0.1.4'), + base.Version('0.1.4-alpha'), + ])) + self.assertEqual((), res) + + def test_filter_compatible(self): + s = base.Spec('>=0.1.1,!=0.1.4,<0.2.0') + res = tuple(s.filter([ + base.Version('0.1.0'), + base.Version('0.1.1'), + base.Version('0.1.5'), + base.Version('0.1.4-alpha'), + base.Version('0.1.2'), + base.Version('0.2.0-rc1'), + base.Version('3.14.15'), + ])) + + expected = ( + base.Version('0.1.1'), + base.Version('0.1.5'), + base.Version('0.1.2'), + ) + + self.assertEqual(expected, res) + + def test_select_empty(self): + s = base.Spec('>=0.1.1') + self.assertIsNone(s.select(())) + + def test_select_incompatible(self): + s = base.Spec('>=0.1.1,!=0.1.4') + res = s.select([ + base.Version('0.1.0'), + base.Version('0.1.4'), + base.Version('0.1.4-alpha'), + ]) + self.assertIsNone(res) + + def test_select_compatible(self): + s = base.Spec('>=0.1.1,!=0.1.4,<0.2.0') + res = s.select([ + base.Version('0.1.0'), + base.Version('0.1.1'), + base.Version('0.1.5'), + base.Version('0.1.4-alpha'), + base.Version('0.1.2'), + base.Version('0.2.0-rc1'), + base.Version('3.14.15'), + ]) + + self.assertEqual(base.Version('0.1.5'), res) + + def test_contains(self): + self.assertFalse('ii' in base.Spec('>=0.1.1')) + + def test_hash(self): + self.assertEqual(1, + len(set([base.Spec('>=0.1.1'), base.Spec('>=0.1.1')]))) + + +if __name__ == '__main__': # pragma: no cover + unittest.main() diff --git a/tests/scripts/test_match.py b/tests/scripts/test_match.py new file mode 100755 index 0000000..4d1a96f --- /dev/null +++ b/tests/scripts/test_match.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) The python-semanticversion project +# This code is distributed under the two-clause BSD License. + +import unittest + +import semantic_version + + +class MatchTestCase(unittest.TestCase): + invalid_specs = [ + '', + '!0.1', + '<=0.1.4a', + '>0.1.1.1', + '<0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + ] + + valid_specs = [ + '*', + '==0.1.0', + '=0.1.0', + '0.1.0', + '<=0.1.1', + '<0.1', + '1', + '>0.1.2-rc1', + '>=0.1.2-rc1.3.4', + '==0.1.2+build42-12.2012-01-01.12h23', + '!=0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + '^0.1.2', + '~0.1.2', + '~=0.1.2', + ] + + matches = { + '*': [ + '0.1.1', + '0.1.1+build4.5', + '0.1.2-rc1', + '0.1.2-rc1.3', + '0.1.2-rc1.3.4', + '0.1.2+build42-12.2012-01-01.12h23', + '0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + '0.2.0', + '1.0.0', + ], + '==0.1.2': [ + '0.1.2-rc1', + '0.1.2-rc1.3.4', + '0.1.2+build42-12.2012-01-01.12h23', + '0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + ], + '=0.1.2': [ + '0.1.2-rc1', + '0.1.2-rc1.3.4', + '0.1.2+build42-12.2012-01-01.12h23', + '0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + ], + '0.1.2': [ + '0.1.2-rc1', + '0.1.2-rc1.3.4', + '0.1.2+build42-12.2012-01-01.12h23', + '0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + ], + '<=0.1.2': [ + '0.1.1', + '0.1.2-rc1', + '0.1.2-rc1.3.4', + '0.1.2', + '0.1.2+build4', + ], + '!=0.1.2+': [ + '0.1.2+1', + '0.1.2-rc1', + ], + '!=0.1.2-': [ + '0.1.1', + '0.1.2-rc1', + ], + '!=0.1.2+345': [ + '0.1.1', + '0.1.2-rc1+345', + '0.1.2+346', + '0.2.3+345', + ], + '>=0.1.1': [ + '0.1.1', + '0.1.1+build4.5', + '0.1.2-rc1.3', + '0.2.0', + '1.0.0', + ], + '>0.1.1': [ + '0.1.2+build4.5', + '0.1.2-rc1.3', + '0.2.0', + '1.0.0', + ], + '<0.1.1-': [ + '0.1.1-alpha', + '0.1.1-rc4', + '0.1.0+12.3', + ], + '^0.1.2': [ + '0.1.2', + '0.1.2+build4.5', + '0.1.3-rc1.3', + '0.1.4', + ], + '~0.1.2': [ + '0.1.2', + '0.1.2+build4.5', + '0.1.3-rc1.3', + ], + '~=1.4.5': ( + '1.4.5', + '1.4.10-alpha', + '1.4.10', + ), + '~=1.4': [ + '1.4.0', + '1.6.10-alpha', + '1.6.10', + ], + } + + def test_invalid(self): + for invalid in self.invalid_specs: + with self.assertRaises(ValueError, msg="Spec(%r) should be invalid" % invalid): + semantic_version.Spec(invalid) + + def test_simple(self): + for valid in self.valid_specs: + spec = semantic_version.SpecItem(valid) + normalized = str(spec) + self.assertEqual(spec, semantic_version.SpecItem(normalized)) + + def test_match(self): + for spec_txt, versions in self.matches.items(): + spec = semantic_version.Spec(spec_txt) + self.assertNotEqual(spec, spec_txt) + for version_txt in versions: + version = semantic_version.Version(version_txt) + self.assertTrue(spec.match(version), "%r does not match %r" % (version, spec)) + self.assertTrue(semantic_version.match(spec_txt, version_txt)) + self.assertTrue(version in spec, "%r not in %r" % (version, spec)) + + def test_contains(self): + spec = semantic_version.Spec('<=0.1.1') + self.assertFalse('0.1.0' in spec, "0.1.0 should not be in %r" % spec) + + version = semantic_version.Version('0.1.1+4.2') + self.assertTrue(version in spec, "%r should be in %r" % (version, spec)) + + version = semantic_version.Version('0.1.1-rc1+4.2') + self.assertTrue(version in spec, "%r should be in %r" % (version, spec)) + + def test_prerelease_check(self): + strict_spec = semantic_version.Spec('>=0.1.1-') + lax_spec = semantic_version.Spec('>=0.1.1') + version = semantic_version.Version('0.1.1-rc1+4.2') + self.assertTrue(version in lax_spec, "%r should be in %r" % (version, lax_spec)) + self.assertFalse(version in strict_spec, "%r should not be in %r" % (version, strict_spec)) + + def test_build_check(self): + spec = semantic_version.Spec('<=0.1.1-rc1') + version = semantic_version.Version('0.1.1-rc1+4.2') + self.assertTrue(version in spec, "%r should be in %r" % (version, spec)) + + +if __name__ == '__main__': # pragma: no cover + unittest.main() + diff --git a/tests/scripts/test_parsing.py b/tests/scripts/test_parsing.py new file mode 100755 index 0000000..8fd22da --- /dev/null +++ b/tests/scripts/test_parsing.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) The python-semanticversion project +# This code is distributed under the two-clause BSD License. + +import itertools +import unittest + +import semantic_version + + +class ParsingTestCase(unittest.TestCase): + invalids = [ + None, + '', + '0', + '0.1', + '0.1.4a', + '0.1.1.1', + '0.1.2-rc23,1', + ] + + valids = [ + '0.1.1', + '0.1.2-rc1', + '0.1.2-rc1.3.4', + '0.1.2+build42-12.2012-01-01.12h23', + '0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + ] + + def test_invalid(self): + for invalid in self.invalids: + self.assertRaises(ValueError, semantic_version.Version, invalid) + + def test_simple(self): + for valid in self.valids: + version = semantic_version.Version(valid) + self.assertEqual(valid, str(version)) + + +class ComparisonTestCase(unittest.TestCase): + order = [ + '1.0.0-alpha', + '1.0.0-alpha.1', + '1.0.0-beta.2', + '1.0.0-beta.11', + '1.0.0-rc.1', + '1.0.0', + '1.3.7+build', + ] + + def test_comparisons(self): + for i, first in enumerate(self.order): + first_ver = semantic_version.Version(first) + for j, second in enumerate(self.order): + second_ver = semantic_version.Version(second) + if i < j: + self.assertTrue(first_ver < second_ver, '%r !< %r' % (first_ver, second_ver)) + elif i == j: + self.assertTrue(first_ver == second_ver, '%r != %r' % (first_ver, second_ver)) + else: + self.assertTrue(first_ver > second_ver, '%r !> %r' % (first_ver, second_ver)) + + cmp_res = -1 if i < j else (1 if i > j else 0) + self.assertEqual(cmp_res, semantic_version.compare(first, second)) + + unordered = [ + [ + '1.0.0-rc.1', + '1.0.0-rc.1+build.1', + ], + [ + '1.0.0', + '1.0.0+0.3.7', + ], + [ + '1.3.7', + '1.3.7+build', + '1.3.7+build.2.b8f12d7', + '1.3.7+build.11.e0f985a', + ], + ] + + def test_unordered(self): + for group in self.unordered: + for a, b in itertools.combinations(group, 2): + v1 = semantic_version.Version(a) + v2 = semantic_version.Version(b) + self.assertTrue(v1 == v1, "%r != %r" % (v1, v1)) + self.assertFalse(v1 != v1, "%r != %r" % (v1, v1)) + self.assertFalse(v1 == v2, "%r == %r" % (v1, v2)) + self.assertTrue(v1 != v2, "%r !!= %r" % (v1, v2)) + self.assertFalse(v1 < v2, "%r !< %r" % (v1, v2)) + self.assertFalse(v1 <= v2, "%r !<= %r" % (v1, v2)) + self.assertFalse(v2 > v1, "%r !> %r" % (v2, v1)) + self.assertFalse(v2 >= v1, "%r !>= %r" % (v2, v1)) + +if __name__ == '__main__': # pragma: no cover + unittest.main() diff --git a/tests/scripts/test_spec.py b/tests/scripts/test_spec.py new file mode 100644 index 0000000..43c9d6a --- /dev/null +++ b/tests/scripts/test_spec.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) The python-semanticversion project +# This code is distributed under the two-clause BSD License. + +"""Test conformance to the specs.""" + +from .compat import unittest, is_python2 + +import semantic_version + +# shortcut +Version = semantic_version.Version + + +class FormatTests(unittest.TestCase): + """Tests proper version validation.""" + + def test_major_minor_patch(self): + ### SPEC: + # A normal version number MUST take the form X.Y.Z + + with self.assertRaises(ValueError): + Version('1') + with self.assertRaises(ValueError): + Version('1.1') + # Doesn't raise + Version('1.2.3') + with self.assertRaises(ValueError): + Version('1.2.3.4') + + ### SPEC: + # Where X, Y, and Z are non-negative integers, + + with self.assertRaises(ValueError): + Version('1.2.A') + with self.assertRaises(ValueError): + Version('1.-2.3') + # Valid + v = Version('1.2.3') + self.assertEqual(1, v.major) + self.assertEqual(2, v.minor) + self.assertEqual(3, v.patch) + + + ### Spec: + # And MUST NOT contain leading zeroes + with self.assertRaises(ValueError): + Version('1.2.01') + with self.assertRaises(ValueError): + Version('1.02.1') + with self.assertRaises(ValueError): + Version('01.2.1') + # Valid + v = Version('0.0.0') + self.assertEqual(0, v.major) + self.assertEqual(0, v.minor) + self.assertEqual(0, v.patch) + + def test_prerelease(self): + ### SPEC: + # A pre-release version MAY be denoted by appending a hyphen and a + # series of dot separated identifiers immediately following the patch + # version. + + with self.assertRaises(ValueError): + Version('1.2.3 -23') + # Valid + v = Version('1.2.3-23') + self.assertEqual(('23',), v.prerelease) + + ### SPEC: + # Identifiers MUST comprise only ASCII alphanumerics and hyphen. + # Identifiers MUST NOT be empty + with self.assertRaises(ValueError): + Version('1.2.3-a,') + with self.assertRaises(ValueError): + Version('1.2.3-..') + + ### SPEC: + # Numeric identifiers MUST NOT include leading zeroes. + + with self.assertRaises(ValueError): + Version('1.2.3-a0.01') + with self.assertRaises(ValueError): + Version('1.2.3-00') + # Valid + v = Version('1.2.3-0a.0.000zz') + self.assertEqual(('0a', '0', '000zz'), v.prerelease) + + def test_build(self): + ### SPEC: + # Build metadata MAY be denoted by appending a plus sign and a series of + # dot separated identifiers immediately following the patch or + # pre-release version + + v = Version('1.2.3') + self.assertEqual((), v.build) + with self.assertRaises(ValueError): + Version('1.2.3 +4') + + ### SPEC: + # Identifiers MUST comprise only ASCII alphanumerics and hyphen. + # Identifiers MUST NOT be empty + with self.assertRaises(ValueError): + Version('1.2.3+a,') + with self.assertRaises(ValueError): + Version('1.2.3+..') + + # Leading zeroes allowed + v = Version('1.2.3+0.0a.01') + self.assertEqual(('0', '0a', '01'), v.build) + + def test_precedence(self): + ### SPEC: + # Precedence is determined by the first difference when comparing from + # left to right as follows: Major, minor, and patch versions are always + # compared numerically. + # Example: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1 + self.assertLess(Version('1.0.0'), Version('2.0.0')) + self.assertLess(Version('2.0.0'), Version('2.1.0')) + self.assertLess(Version('2.1.0'), Version('2.1.1')) + + ### SPEC: + # When major, minor, and patch are equal, a pre-release version has + # lower precedence than a normal version. + # Example: 1.0.0-alpha < 1.0.0 + self.assertLess(Version('1.0.0-alpha'), Version('1.0.0')) + + ### SPEC: + # Precedence for two pre-release versions with the same major, minor, + # and patch version MUST be determined by comparing each dot separated + # identifier from left to right until a difference is found as follows: + # identifiers consisting of only digits are compared numerically + self.assertLess(Version('1.0.0-1'), Version('1.0.0-2')) + + # and identifiers with letters or hyphens are compared lexically in + # ASCII sort order. + self.assertLess(Version('1.0.0-aa'), Version('1.0.0-ab')) + + # Numeric identifiers always have lower precedence than + # non-numeric identifiers. + self.assertLess(Version('1.0.0-9'), Version('1.0.0-a')) + + # A larger set of pre-release fields has a higher precedence than a + # smaller set, if all of the preceding identifiers are equal. + self.assertLess(Version('1.0.0-a.b.c'), Version('1.0.0-a.b.c.0')) + + # Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0. + self.assertLess(Version('1.0.0-alpha'), Version('1.0.0-alpha.1')) + self.assertLess(Version('1.0.0-alpha.1'), Version('1.0.0-alpha.beta')) + self.assertLess(Version('1.0.0-alpha.beta'), Version('1.0.0-beta')) + self.assertLess(Version('1.0.0-beta'), Version('1.0.0-beta.2')) + self.assertLess(Version('1.0.0-beta.2'), Version('1.0.0-beta.11')) + self.assertLess(Version('1.0.0-beta.11'), Version('1.0.0-rc.1')) + self.assertLess(Version('1.0.0-rc.1'), Version('1.0.0')) diff --git a/tests/tests.yml b/tests/tests.yml new file mode 100644 index 0000000..8cc6565 --- /dev/null +++ b/tests/tests.yml @@ -0,0 +1,15 @@ +--- +# Run a simple test +- hosts: localhost + roles: + - role: standard-test-basic + tags: + - classic + + required_packages: + - python3-nose + + tests: + - simple: + dir: scripts + run: ./run_tests.sh