text: #!/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'."""
  
  import unittest
  import sys
  
  from semantic_version import base
  
  
  class TopLevelTestCase(unittest.TestCase):
      """Test module-level functions."""
  
      if sys.version_info[0] <= 2:
          import contextlib
          @contextlib.contextmanager
          def subTest(self, **kwargs):
              yield
  
      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:
              with self.subTest(a=a, b=b):
                  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.2-alpha'),
          ('>=0.1.1,!=0.2.0', '0.2.1'),
      )
  
      def test_match(self):
          for spec, version in self.matches:
              with self.subTest(spec=spec, version=version):
                  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:
              with self.subTest(version=version):
                  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:
              with self.subTest(version=version):
                  self.assertFalse(
                      base.validate(version),
                      "%r should not be a valid version" % (version,))
  
  
  class VersionTestCase(unittest.TestCase):
      if sys.version_info[0] <= 2:
          import contextlib
          @contextlib.contextmanager
          def subTest(self, **kwargs):
              yield
  
      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():
              with self.subTest(text=text):
                  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:
              with self.subTest(text=text):
                  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:
              with self.subTest(text=text):
                  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():
              with self.subTest(text=text):
                  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:
              with self.subTest(text=text):
                  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:
              with self.subTest(text=text):
                  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(sys.version_info[0] <= 2, "Comparisons don't raise TypeError in Python 2")
      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):
      if sys.version_info[0] <= 2:
          import contextlib
          @contextlib.contextmanager
          def subTest(self, **kwargs):
              yield
  
      invalids = [
          '<=0.1.1+build3',
          '<=0.1.1+',
          '>0.2.3-rc2+',
      ]
  
      def test_invalids(self):
          for invalid in self.invalids:
              with self.subTest(invalid=invalid):
                  with self.assertRaises(ValueError, msg="SpecItem(%r) should be invalid" % invalid):
                      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_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():
              with self.subTest(spec_text=spec_text):
                  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+build1'],
              ['0.0.1', '0.1.0-rc1', '0.2.0', '0.1.1', '0.1.0-rc1+build2'],
          ),
          '=0.1.0': (
              ['0.1.0', '0.1.0+build1'],
              ['0.0.1', '0.1.0-rc1', '0.2.0', '0.1.1', '0.1.0-rc1+build2'],
          ),
          '0.1.0': (
              ['0.1.0', '0.1.0+build1'],
              ['0.0.1', '0.1.0-rc1', '0.2.0', '0.1.1', '0.1.0-rc1+build2'],
          ),
          '==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.2': (
              ['0.1.2+build4', '0.1.2-alpha', '0.1.0'],
              ['0.2.3', '1.1.1', '0.1.3'],
          ),
          '<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+1'],
              ['0.2.3-rc3', '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+b1'],
              ['1.1.1', '1.1.2-alpha', '1.1.2-alpha+b1', '1.2.1', '2.1.0'],
          ),
          '^1.1.2': (
              ['1.1.3', '1.1.2+b1', '1.2.1'],
              ['1.1.1', '1.1.2-alpha', '1.1.2-alpha+b1', '2.1.0'],
          ),
          '^0.1.2': (
              ['0.1.2', '0.1.2+b1', '0.1.3'],
              ['0.1.2-alpha', '0.2.0', '1.1.2', '0.1.1'],
          ),
          '^0.0.2': (
              ['0.0.2', '0.0.2+abb'],
              ['0.0.2-alpha', '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.4.5-alpha', '1.5.0'],
          ),
          '~=1.4.0': (
              ['1.4.0', '1.4.10-alpha', '1.4.10'],
              ['1.3.6', '1.3.9', '1.4.0-alpha', '1.5.0'],
          ),
          '~=1.4': (
              ['1.4.0', '1.6.10-alpha', '1.6.10'],
              ['1.3.0', '1.4.0-alpha', '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:
                  with self.subTest(spec=spec_text, version=version_text):
                      version = base.Version(version_text)
                      self.assertTrue(spec.match(version), "%r should match %r" % (version, spec))
  
              for version_text in failing:
                  with self.subTest(spec=spec_text, excluded=version_text):
                      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):
      if sys.version_info[0] <= 2:
          import contextlib
          @contextlib.contextmanager
          def subTest(self, **kwargs):
              yield
  
      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:
                  with self.subTest(target=equivalent, sample=sample):
                      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):
      if sys.version_info[0] <= 2:
          import contextlib
          @contextlib.contextmanager
          def subTest(self, **kwargs):
              yield
  
          def assertCountEqual(self, a, b):
              import collections
              self.assertEqual(
                  collections.Counter(a),
                  collections.Counter(b),
              )
  
      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'],
          '=0.1.2': ['==0.1.2'],
          '>=0.1.2': ['>=0.1.2'],
          '^1.2.3': ['>=1.2.3', '<2.0.0'],
          '~=1.2.3': ['>=1.2.3', '<1.3.0'],
      }
  
      def test_parsing(self):
          for spec_list_text, specs in self.examples.items():
              with self.subTest(spec=spec_list_text):
                  spec_list = base.Spec(spec_list_text)
  
                  self.assertEqual(spec_list_text, str(spec_list))
                  self.assertNotEqual(spec_list_text, spec_list)
                  self.assertCountEqual(specs, [str(spec) for spec in 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():
              with self.subTest(spec=spec_list_texts):
                  spec_list = base.Spec(*spec_list_texts)
  
                  self.assertEqual(','.join(spec_list_texts), str(spec_list))
                  self.assertCountEqual(specs, [str(spec) for spec in spec_list])
                  self.assertEqual(spec_list, base.Spec(','.join(spec_list_texts)))
  
                  for spec_text in specs:
                      self.assertIn(str(base.SpecItem(spec_text)), repr(spec_list))
  
      matches = {
          # At least 0.1.1 excluding 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 with 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:
                  with self.subTest(spec=spec_list_text, matching=version_text):
                      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:
                  with self.subTest(spec=spec_list_text, excluded=version_text):
                      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:
              with self.subTest(spec=spec_list_text):
                  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()
  