# SPDX-License-Identifier: Apache-2.0
# Copyright Contributors to the Rez Project


from rez.vendor.version.version import Version, AlphanumericVersionToken, \
    VersionRange, reverse_sort_key, _ReversedComparable
from rez.vendor.version.requirement import Requirement, RequirementList
from rez.vendor.version.util import VersionError
import random
import textwrap
import unittest



def _print(txt=''):
    # uncomment for verbose output
    #print txt
    pass


class TestVersionSchema(unittest.TestCase):
    make_token = AlphanumericVersionToken

    def __init__(self, fn):
        unittest.TestCase.__init__(self, fn)

    def _test_strict_weak_ordering(self, a, b):
        self.assertTrue(a == a)
        self.assertTrue(b == b)

        e       = (a == b)
        ne      = (a != b)
        lt      = (a < b)
        lte     = (a <= b)
        gt      = (a > b)
        gte     = (a >= b)

        _print('\n' + textwrap.dedent(
               """
               '%s' <op> '%s'
               ==:  %s
               !=:  %s
               <:   %s
               <=:  %s
               >:   %s
               >=:  %s
               """).strip() % (a, b, e, ne, lt, lte, gt, gte))

        self.assertTrue(e != ne)
        if e:
            self.assertTrue(not lt)
            self.assertTrue(not gt)
            self.assertTrue(lte)
            self.assertTrue(gte)
        else:
            self.assertTrue(lt != gt)
            self.assertTrue(lte != gte)
            self.assertTrue(lt == lte)
            self.assertTrue(gt == gte)

        if not isinstance(a, _ReversedComparable):
            self._test_strict_weak_ordering(reverse_sort_key(a),
                                            reverse_sort_key(b))

    def _test_ordered(self, items):
        def _test(fn, items_, op_str):
            for i, a in enumerate(items_):
                for b in items_[i+1:]:
                    _print("'%s' %s '%s'" % (a, op_str, b))
                    self.assertTrue(fn(a, b))

        _test(lambda a, b: a < b, items, '<')
        _test(lambda a, b: a <= b, items, '<=')
        _test(lambda a, b: a != b, items, '!=')
        _test(lambda a, b: a > b, list(reversed(items)), '>')
        _test(lambda a, b: a >= b, list(reversed(items)), '>=')
        _test(lambda a, b: a != b, list(reversed(items)), '!=')

    def _create_random_token(self):
        s = self.make_token.create_random_token_string()
        return self.make_token(s)

    def _create_random_version(self):
        ver_str = '.'.join(self.make_token.create_random_token_string()
                           for i in range(random.randint(0, 6)))
        return Version(ver_str, make_token=self.make_token)

    def test_misc(self):
        self.assertEqual(Version("1.2.12").as_tuple(), ("1", "2", "12"))

    def test_token_strict_weak_ordering(self):
        # test equal tokens
        tok = self._create_random_token()
        self._test_strict_weak_ordering(tok, tok)

        # test random tokens
        for i in range(100):
            tok1 = self._create_random_token()
            tok2 = self._create_random_token()
            self._test_strict_weak_ordering(tok1, tok2)

    def test_version_strict_weak_ordering(self):
        # test equal versions
        ver = self._create_random_version()
        self._test_strict_weak_ordering(ver, ver)

        # test random versions
        for i in range(100):
            ver1 = self._create_random_version()
            ver2 = self._create_random_version()
            self._test_strict_weak_ordering(ver1, ver2)

    def test_token_comparisons(self):
        def _lt(a, b):
            _print("'%s' < '%s'" % (a, b))
            self.assertTrue(self.make_token(a) < self.make_token(b))
            self.assertTrue(Version(a) < Version(b))

        _print()
        _lt("3", "4")
        _lt("01", "1")
        _lt("beta", "1")
        _lt("alpha3", "alpha4")
        _lt("alpha", "alpha3")
        _lt("gamma33", "33gamma")

    def test_version_comparisons(self):
        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(Version(a) == Version(b))

        _print()
        _eq("", "")
        _eq("1", "1")
        _eq("1.2", "1-2")
        _eq("1.2-3", "1-2.3")

        ascending = ["",
                     "0.0.0",
                     "1",
                     "2",
                     "2.alpha1",
                     "2.alpha2",
                     "2.beta",
                     "2.0",
                     "2.0.8.8",
                     "2.1",
                     "2.1.0"]
        self._test_ordered([Version(x) for x in ascending])

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)

        # test behaviour in sets
        a = Version("1.0")
        b = Version("1.0")
        c = Version("1.0alpha")
        d = Version("2.0.0")

        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d]) - set([a]), set([c, d]))
        _eq2(set([b, c]) | set([c, d]), set([b, c, d]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_version_range(self):
        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)

            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))

            a_ = a.replace('.', '-')
            a_ = a_.replace("--", "..")
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))

            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)

            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)

            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)

            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)

            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)

        def _and(a, b, c):
            _print("'%s' & '%s' == '%s'" % (a, b, c))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            c_range = None if c is None else VersionRange(c)
            self.assertTrue(a_range & b_range == c_range)
            self.assertTrue(b_range & a_range == c_range)

            a_or_b = a_range | b_range
            a_and_b = a_range & b_range
            a_sub_b = a_range - b_range
            b_sub_a = b_range - a_range
            ranges = [a_and_b, a_sub_b, b_sub_a]
            ranges = [x for x in ranges if x]
            self.assertTrue(ranges[0].union(ranges[1:]) == a_or_b)

        def _inv(a, b):
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(~a_range == b_range)
            self.assertTrue(~b_range == a_range)
            self.assertTrue(a_range | b_range == VersionRange())
            self.assertTrue(a_range & b_range is None)

        # simple cases
        _print()
        _eq("", "")
        _eq("1", "1")
        _eq("1.0.0", "1.0.0")
        _eq("3+<3_", "3")
        _eq("_+<__", "_")
        _eq("1.2+<=2.0", "1.2..2.0")
        _eq("10+,<20", "10+<20")
        _eq("1+<1.0", "1+<1.0")
        _eq(">=2", "2+")
        _eq(">=1.21.1,<1.23", ">=1.21.1<1.23")
        _eq(">1.21.1,<1.23", ">1.21.1<1.23")
        _eq(">1.21.1<1.23", ">1.21.1<1.23")
        _eq(">1.21.1,<=1.23", ">1.21.1<=1.23")

        # Reverse order which is a syntax pip packages use more often now.
        # Only allowed when separated by a comma.
        _eq("<1.23,>=1.21.1", ">=1.21.1<1.23")
        _eq("<1.23,>1.21.1", ">1.21.1<1.23")

        # optimised cases
        _eq("3|3", "3")
        _eq("3|1", "1|3")
        _eq("5|3|1", "1|3|5")
        _eq("1|1_", "1+<1__")
        _eq("1|1_|1__", "1+,<1___")
        _eq("|", "")
        _eq("||", "||||||||")
        _eq("1|1_+", "1+")
        _eq("<1|1", "<1_")
        _eq("1+<3|3+<5", "1+<5")
        _eq(">4<6|1+<3", "1+<3|>4,<6")
        _eq("4+<6|1+<3|", "")
        _eq("4|2+", "2+")
        _eq("3|<5", "<5")
        _eq("<3|>3", ">3|<3")
        _eq("3+|<3", "")
        _eq("3+|<4", "")
        _eq("2+<=6|3+<5", "2..6")
        _eq("3+,<5|2+<=6", "2+<=6")
        _eq("2|2+", "2+")
        _eq("2|2.1+", "2+")
        _eq("2|<2.1", "<2_")
        _eq("3..3", "==3")
        _eq(">=3,<=3", "==3")

        # AND'ing
        _and("3", "3", "3")
        _and("1", "==1", "==1")
        _and("", "==1", "==1")
        _and("3", "4", None)
        _and("<3", "5+", None)
        _and("4+<6", "6+<8", None)
        _and("2+", "<=4", "2..4")
        _and("1", "1.0", "1.0")
        _and("4..6", "6+<8", "==6")

        # inverse
        _inv("3+", "<3")
        _inv("<=3", ">3")
        _inv("3.5", "<3.5|3.5_+")
        self.assertTrue(~VersionRange() is None)

        # odd (but valid) cases
        _eq(">", ">")       # greater than the empty version
        _eq("+", "")        # greater or equal to empty version (is all vers)
        _eq(">=", "")       # equivalent to above
        _eq("<=", "==")     # less or equal to empty version (is only empty)
        _eq("..", "==")     # from empty version to empty version
        _eq("+<=", "==")    # equivalent to above

        invalid_range = [
            "4+<2",         # lower bound greater than upper
            ">3<3",         # both greater and less than same version
            ">3<=3",        # greater and less or equal to same version
            "3+<3"          # greater and equal to, and less than, same version
        ]

        for s in invalid_range:
            self.assertRaises(VersionError, VersionRange, s)

        invalid_syntax = [
            "<",            # less than the empty version
            "><",           # both greater and less than empty version
            ">3>4",         # both are lower bounds
            "<3<4",         # both are upper bounds
            "<4>3",         # upper bound before lower without comma
            ",<4",          # leading comma
            "4+,",          # trailing comma
            "1>=",          # pre-lower-op in post
            "+1",           # post-lower-op in pre
            "4<",           # pre-upper-op in post
            "1+<2<3"        # more than two bounds
        ]

        for s in invalid_syntax:
            self.assertRaises(VersionError, VersionRange, s)

        # test simple logic
        self.assertTrue(VersionRange("").is_any())
        self.assertTrue(VersionRange("2+<4").bounded())
        self.assertTrue(VersionRange("2+").lower_bounded())
        self.assertTrue(not VersionRange("2+").upper_bounded())
        self.assertTrue(not VersionRange("2+").bounded())
        self.assertTrue(VersionRange("<2").upper_bounded())
        self.assertTrue(not VersionRange("<2").lower_bounded())
        self.assertTrue(not VersionRange("<2").bounded())

        # test range from version(s)
        v = Version("3")
        self.assertTrue(VersionRange.from_version(v, "eq") == VersionRange("==3"))
        self.assertTrue(VersionRange.from_version(v, "gt") == VersionRange(">3"))
        self.assertTrue(VersionRange.from_version(v, "gte") == VersionRange("3+"))
        self.assertTrue(VersionRange.from_version(v, "lt") == VersionRange("<3"))
        self.assertTrue(VersionRange.from_version(v, "lte") == VersionRange("<=3"))

        range1 = VersionRange.from_version(Version("2"), "gte")
        range2 = VersionRange.from_version(Version("4"), "lte")
        _eq(str(range1 & range2), "2..4")

        v2 = Version("6.0")
        v3 = Version("4")
        self.assertTrue(VersionRange.from_versions([v, v2, v3])
                        == VersionRange("==3|==4|==6.0"))

        # test behaviour in sets
        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)

        a = VersionRange("1+<=2.5")
        b = VersionRange("1..2.5")
        c = VersionRange(">=5")
        d = VersionRange(">6.1.0")
        e = VersionRange("3.2")

        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d, e]) - set([a]), set([c, d, e]))
        _eq2(set([b, c, e]) | set([c, d]), set([b, c, d, e]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_containment(self):
        # basic containment
        self.assertTrue(Version("3") in VersionRange("3+"))
        self.assertTrue(Version("5") in VersionRange("3..5"))
        self.assertTrue(Version("5_") not in VersionRange("3..5"))
        self.assertTrue(Version("3.0.0") in VersionRange("3+"))
        self.assertTrue(Version("3.0.0") not in VersionRange("3.1+"))
        self.assertTrue(Version("3") in VersionRange("<1|5|6|8|7|3|60+"))
        self.assertTrue(Version("3") in VersionRange("<1|5|6|8|7|==3|60+"))
        self.assertTrue(VersionRange("2.1+<4") in VersionRange("<4"))
        self.assertTrue(VersionRange("2.1..4") not in VersionRange("<4"))
        self.assertTrue(VersionRange("3") in VersionRange("3"))
        self.assertTrue(VersionRange("==3") in VersionRange("3"))
        self.assertTrue(VersionRange("3.5+<3_") in VersionRange("3"))
        self.assertTrue(VersionRange("3") not in VersionRange("4+<6"))
        self.assertTrue(VersionRange("3+<10") not in VersionRange("4+<6"))

        # iterating over sorted version list
        numbers = [2, 3, 5, 10, 11, 13, 14]
        versions = [Version(str(x)) for x in numbers]
        rev_versions = list(reversed(versions))
        composite_range = VersionRange.from_versions(versions)

        entries = [(VersionRange(""), 7),
                   (VersionRange("0+"), 7),
                   (VersionRange("5+"), 5),
                   (VersionRange("6+"), 4),
                   (VersionRange("50+"), 0),
                   (VersionRange(">5"), 4),
                   (VersionRange("5"), 1),
                   (VersionRange("6"), 0),
                   (VersionRange("<5"), 2),
                   (VersionRange("<6"), 3),
                   (VersionRange("<50"), 7),
                   (VersionRange("<=5"), 3),
                   (VersionRange("<1"), 0),
                   (VersionRange("2|9+"), 5),
                   (VersionRange("3+<6|12+<13.5"), 3),
                   (VersionRange("<1|20+"), 0),
                   (VersionRange(">0<20"), 7)]

        for range_, count in entries:
            # brute-force containment tests
            matches = set(x for x in versions if x in range_)
            self.assertEqual(len(matches), count)

            # more optimal containment tests
            def _test_it(it):
                matches_ = set(version for contains, version in it if contains)
                self.assertEqual(matches_, matches)

            _test_it(range_.iter_intersect_test(versions))
            _test_it(range_.iter_intersect_test(rev_versions, descending=True))

            # throw in an intersection test
            self.assertEqual(composite_range.intersects(range_), (count != 0))
            int_range = composite_range & range_
            versions_ = [] if int_range is None else int_range.to_versions()
            self.assertEqual(set(versions_), matches)

            # throw in a superset test as well
            self.assertEqual(range_.issuperset(composite_range), (count == 7))
            if count:
                self.assertTrue(composite_range.issuperset(int_range))

    def test_requirement_list(self):
        def _eq(reqs, expected_reqs):
            _print("requirements(%s) == requirements(%s)"
                   % (' '.join(reqs), ' '.join(expected_reqs)))
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print("result: %s" % str(reqlist))

            exp_reqs_ = [Requirement(x) for x in expected_reqs]
            self.assertTrue(reqlist.requirements == exp_reqs_)

            exp_names = set(x.name for x in exp_reqs_ if not x.conflict)
            self.assertTrue(reqlist.names == exp_names)

            exp_confl_names = set(x.name for x in exp_reqs_ if x.conflict)
            self.assertTrue(reqlist.conflict_names == exp_confl_names)

        def _confl(reqs, a, b):
            _print("requirements(%s) == %s <--!--> %s" % (' '.join(reqs), a, b))
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print("result: %s" % str(reqlist))

            a_req = Requirement(a)
            b_req = Requirement(b)
            self.assertTrue(reqlist.conflict == (a_req, b_req))

        _print()
        _eq(["foo"],
            ["foo"])
        _eq(["foo", "bah"],
            ["foo", "bah"])
        _eq(["bah", "foo"],
            ["bah", "foo"])
        _eq(["foo-4+", "foo-4.5"],
            ["foo-4.5"])
        _eq(["bah-2.4", "foo", "bah-2.4.1+"],
            ["bah-2.4.1+<2.4_", "foo"])
        _eq(["foo-2+", "!foo-4+"],
            ["foo-2+<4"])
        _eq(["!bah-1", "!bah-3"],
            ["!bah-1|3"])
        _eq(["!bah-5", "foo-2.3", "!bah-5.6+"],
            ["!bah-5+", "foo-2.3"])
        _eq(["~bah-4", "foo", "bah<4.2"],
            ["bah-4+<4.2", "foo"])
        _eq(["~bah", "!foo", "bah<4.2"],
            ["bah<4.2", "!foo"])
        _eq(["~bah-3+", "~bah-5"],
            ["~bah-5"])

        _confl(["foo-1", "foo-2"],
               "foo-1", "foo-2")
        _confl(["foo-2", "foo-1"],
               "foo-2", "foo-1")
        _confl(["foo", "~bah-5+", "bah-2"],
               "~bah-5+", "bah-2")
        _confl(["foo", "~bah-5+", "bah-7..12", "bah-2"],
               "bah-7..12", "bah-2")


if __name__ == '__main__':
    unittest.main()

73c200c5510c931c872eec2c5ffdd360f6b4f0b0
<mask token>


class TestVersionSchema(unittest.TestCase):
    <mask token>

    def __init__(self, fn):
        unittest.TestCase.__init__(self, fn)

    def _test_strict_weak_ordering(self, a, b):
        self.assertTrue(a == a)
        self.assertTrue(b == b)
        e = a == b
        ne = a != b
        lt = a < b
        lte = a <= b
        gt = a > b
        gte = a >= b
        _print('\n' + textwrap.dedent(
            """
               '%s' <op> '%s'
               ==:  %s
               !=:  %s
               <:   %s
               <=:  %s
               >:   %s
               >=:  %s
               """
            ).strip() % (a, b, e, ne, lt, lte, gt, gte))
        self.assertTrue(e != ne)
        if e:
            self.assertTrue(not lt)
            self.assertTrue(not gt)
            self.assertTrue(lte)
            self.assertTrue(gte)
        else:
            self.assertTrue(lt != gt)
            self.assertTrue(lte != gte)
            self.assertTrue(lt == lte)
            self.assertTrue(gt == gte)
        if not isinstance(a, _ReversedComparable):
            self._test_strict_weak_ordering(reverse_sort_key(a),
                reverse_sort_key(b))
    <mask token>
    <mask token>
    <mask token>

    def test_misc(self):
        self.assertEqual(Version('1.2.12').as_tuple(), ('1', '2', '12'))

    def test_token_strict_weak_ordering(self):
        tok = self._create_random_token()
        self._test_strict_weak_ordering(tok, tok)
        for i in range(100):
            tok1 = self._create_random_token()
            tok2 = self._create_random_token()
            self._test_strict_weak_ordering(tok1, tok2)

    def test_version_strict_weak_ordering(self):
        ver = self._create_random_version()
        self._test_strict_weak_ordering(ver, ver)
        for i in range(100):
            ver1 = self._create_random_version()
            ver2 = self._create_random_version()
            self._test_strict_weak_ordering(ver1, ver2)
    <mask token>

    def test_version_comparisons(self):

        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(Version(a) == Version(b))
        _print()
        _eq('', '')
        _eq('1', '1')
        _eq('1.2', '1-2')
        _eq('1.2-3', '1-2.3')
        ascending = ['', '0.0.0', '1', '2', '2.alpha1', '2.alpha2',
            '2.beta', '2.0', '2.0.8.8', '2.1', '2.1.0']
        self._test_ordered([Version(x) for x in ascending])

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)
        a = Version('1.0')
        b = Version('1.0')
        c = Version('1.0alpha')
        d = Version('2.0.0')
        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d]) - set([a]), set([c, d]))
        _eq2(set([b, c]) | set([c, d]), set([b, c, d]))
        _eq2(set([b, c]) & set([c, d]), set([c]))
    <mask token>

    def test_containment(self):
        self.assertTrue(Version('3') in VersionRange('3+'))
        self.assertTrue(Version('5') in VersionRange('3..5'))
        self.assertTrue(Version('5_') not in VersionRange('3..5'))
        self.assertTrue(Version('3.0.0') in VersionRange('3+'))
        self.assertTrue(Version('3.0.0') not in VersionRange('3.1+'))
        self.assertTrue(Version('3') in VersionRange('<1|5|6|8|7|3|60+'))
        self.assertTrue(Version('3') in VersionRange('<1|5|6|8|7|==3|60+'))
        self.assertTrue(VersionRange('2.1+<4') in VersionRange('<4'))
        self.assertTrue(VersionRange('2.1..4') not in VersionRange('<4'))
        self.assertTrue(VersionRange('3') in VersionRange('3'))
        self.assertTrue(VersionRange('==3') in VersionRange('3'))
        self.assertTrue(VersionRange('3.5+<3_') in VersionRange('3'))
        self.assertTrue(VersionRange('3') not in VersionRange('4+<6'))
        self.assertTrue(VersionRange('3+<10') not in VersionRange('4+<6'))
        numbers = [2, 3, 5, 10, 11, 13, 14]
        versions = [Version(str(x)) for x in numbers]
        rev_versions = list(reversed(versions))
        composite_range = VersionRange.from_versions(versions)
        entries = [(VersionRange(''), 7), (VersionRange('0+'), 7), (
            VersionRange('5+'), 5), (VersionRange('6+'), 4), (VersionRange(
            '50+'), 0), (VersionRange('>5'), 4), (VersionRange('5'), 1), (
            VersionRange('6'), 0), (VersionRange('<5'), 2), (VersionRange(
            '<6'), 3), (VersionRange('<50'), 7), (VersionRange('<=5'), 3),
            (VersionRange('<1'), 0), (VersionRange('2|9+'), 5), (
            VersionRange('3+<6|12+<13.5'), 3), (VersionRange('<1|20+'), 0),
            (VersionRange('>0<20'), 7)]
        for range_, count in entries:
            matches = set(x for x in versions if x in range_)
            self.assertEqual(len(matches), count)

            def _test_it(it):
                matches_ = set(version for contains, version in it if contains)
                self.assertEqual(matches_, matches)
            _test_it(range_.iter_intersect_test(versions))
            _test_it(range_.iter_intersect_test(rev_versions, descending=True))
            self.assertEqual(composite_range.intersects(range_), count != 0)
            int_range = composite_range & range_
            versions_ = [] if int_range is None else int_range.to_versions()
            self.assertEqual(set(versions_), matches)
            self.assertEqual(range_.issuperset(composite_range), count == 7)
            if count:
                self.assertTrue(composite_range.issuperset(int_range))

    def test_requirement_list(self):

        def _eq(reqs, expected_reqs):
            _print('requirements(%s) == requirements(%s)' % (' '.join(reqs),
                ' '.join(expected_reqs)))
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print('result: %s' % str(reqlist))
            exp_reqs_ = [Requirement(x) for x in expected_reqs]
            self.assertTrue(reqlist.requirements == exp_reqs_)
            exp_names = set(x.name for x in exp_reqs_ if not x.conflict)
            self.assertTrue(reqlist.names == exp_names)
            exp_confl_names = set(x.name for x in exp_reqs_ if x.conflict)
            self.assertTrue(reqlist.conflict_names == exp_confl_names)

        def _confl(reqs, a, b):
            _print('requirements(%s) == %s <--!--> %s' % (' '.join(reqs), a, b)
                )
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print('result: %s' % str(reqlist))
            a_req = Requirement(a)
            b_req = Requirement(b)
            self.assertTrue(reqlist.conflict == (a_req, b_req))
        _print()
        _eq(['foo'], ['foo'])
        _eq(['foo', 'bah'], ['foo', 'bah'])
        _eq(['bah', 'foo'], ['bah', 'foo'])
        _eq(['foo-4+', 'foo-4.5'], ['foo-4.5'])
        _eq(['bah-2.4', 'foo', 'bah-2.4.1+'], ['bah-2.4.1+<2.4_', 'foo'])
        _eq(['foo-2+', '!foo-4+'], ['foo-2+<4'])
        _eq(['!bah-1', '!bah-3'], ['!bah-1|3'])
        _eq(['!bah-5', 'foo-2.3', '!bah-5.6+'], ['!bah-5+', 'foo-2.3'])
        _eq(['~bah-4', 'foo', 'bah<4.2'], ['bah-4+<4.2', 'foo'])
        _eq(['~bah', '!foo', 'bah<4.2'], ['bah<4.2', '!foo'])
        _eq(['~bah-3+', '~bah-5'], ['~bah-5'])
        _confl(['foo-1', 'foo-2'], 'foo-1', 'foo-2')
        _confl(['foo-2', 'foo-1'], 'foo-2', 'foo-1')
        _confl(['foo', '~bah-5+', 'bah-2'], '~bah-5+', 'bah-2')
        _confl(['foo', '~bah-5+', 'bah-7..12', 'bah-2'], 'bah-7..12', 'bah-2')


<mask token>

<mask token>


class TestVersionSchema(unittest.TestCase):
    <mask token>

    def __init__(self, fn):
        unittest.TestCase.__init__(self, fn)

    def _test_strict_weak_ordering(self, a, b):
        self.assertTrue(a == a)
        self.assertTrue(b == b)
        e = a == b
        ne = a != b
        lt = a < b
        lte = a <= b
        gt = a > b
        gte = a >= b
        _print('\n' + textwrap.dedent(
            """
               '%s' <op> '%s'
               ==:  %s
               !=:  %s
               <:   %s
               <=:  %s
               >:   %s
               >=:  %s
               """
            ).strip() % (a, b, e, ne, lt, lte, gt, gte))
        self.assertTrue(e != ne)
        if e:
            self.assertTrue(not lt)
            self.assertTrue(not gt)
            self.assertTrue(lte)
            self.assertTrue(gte)
        else:
            self.assertTrue(lt != gt)
            self.assertTrue(lte != gte)
            self.assertTrue(lt == lte)
            self.assertTrue(gt == gte)
        if not isinstance(a, _ReversedComparable):
            self._test_strict_weak_ordering(reverse_sort_key(a),
                reverse_sort_key(b))

    def _test_ordered(self, items):

        def _test(fn, items_, op_str):
            for i, a in enumerate(items_):
                for b in items_[i + 1:]:
                    _print("'%s' %s '%s'" % (a, op_str, b))
                    self.assertTrue(fn(a, b))
        _test(lambda a, b: a < b, items, '<')
        _test(lambda a, b: a <= b, items, '<=')
        _test(lambda a, b: a != b, items, '!=')
        _test(lambda a, b: a > b, list(reversed(items)), '>')
        _test(lambda a, b: a >= b, list(reversed(items)), '>=')
        _test(lambda a, b: a != b, list(reversed(items)), '!=')
    <mask token>
    <mask token>

    def test_misc(self):
        self.assertEqual(Version('1.2.12').as_tuple(), ('1', '2', '12'))

    def test_token_strict_weak_ordering(self):
        tok = self._create_random_token()
        self._test_strict_weak_ordering(tok, tok)
        for i in range(100):
            tok1 = self._create_random_token()
            tok2 = self._create_random_token()
            self._test_strict_weak_ordering(tok1, tok2)

    def test_version_strict_weak_ordering(self):
        ver = self._create_random_version()
        self._test_strict_weak_ordering(ver, ver)
        for i in range(100):
            ver1 = self._create_random_version()
            ver2 = self._create_random_version()
            self._test_strict_weak_ordering(ver1, ver2)
    <mask token>

    def test_version_comparisons(self):

        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(Version(a) == Version(b))
        _print()
        _eq('', '')
        _eq('1', '1')
        _eq('1.2', '1-2')
        _eq('1.2-3', '1-2.3')
        ascending = ['', '0.0.0', '1', '2', '2.alpha1', '2.alpha2',
            '2.beta', '2.0', '2.0.8.8', '2.1', '2.1.0']
        self._test_ordered([Version(x) for x in ascending])

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)
        a = Version('1.0')
        b = Version('1.0')
        c = Version('1.0alpha')
        d = Version('2.0.0')
        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d]) - set([a]), set([c, d]))
        _eq2(set([b, c]) | set([c, d]), set([b, c, d]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_version_range(self):

        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))
            a_ = a.replace('.', '-')
            a_ = a_.replace('--', '..')
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))
            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)
            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)
            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)
            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)
            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)

        def _and(a, b, c):
            _print("'%s' & '%s' == '%s'" % (a, b, c))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            c_range = None if c is None else VersionRange(c)
            self.assertTrue(a_range & b_range == c_range)
            self.assertTrue(b_range & a_range == c_range)
            a_or_b = a_range | b_range
            a_and_b = a_range & b_range
            a_sub_b = a_range - b_range
            b_sub_a = b_range - a_range
            ranges = [a_and_b, a_sub_b, b_sub_a]
            ranges = [x for x in ranges if x]
            self.assertTrue(ranges[0].union(ranges[1:]) == a_or_b)

        def _inv(a, b):
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(~a_range == b_range)
            self.assertTrue(~b_range == a_range)
            self.assertTrue(a_range | b_range == VersionRange())
            self.assertTrue(a_range & b_range is None)
        _print()
        _eq('', '')
        _eq('1', '1')
        _eq('1.0.0', '1.0.0')
        _eq('3+<3_', '3')
        _eq('_+<__', '_')
        _eq('1.2+<=2.0', '1.2..2.0')
        _eq('10+,<20', '10+<20')
        _eq('1+<1.0', '1+<1.0')
        _eq('>=2', '2+')
        _eq('>=1.21.1,<1.23', '>=1.21.1<1.23')
        _eq('>1.21.1,<1.23', '>1.21.1<1.23')
        _eq('>1.21.1<1.23', '>1.21.1<1.23')
        _eq('>1.21.1,<=1.23', '>1.21.1<=1.23')
        _eq('<1.23,>=1.21.1', '>=1.21.1<1.23')
        _eq('<1.23,>1.21.1', '>1.21.1<1.23')
        _eq('3|3', '3')
        _eq('3|1', '1|3')
        _eq('5|3|1', '1|3|5')
        _eq('1|1_', '1+<1__')
        _eq('1|1_|1__', '1+,<1___')
        _eq('|', '')
        _eq('||', '||||||||')
        _eq('1|1_+', '1+')
        _eq('<1|1', '<1_')
        _eq('1+<3|3+<5', '1+<5')
        _eq('>4<6|1+<3', '1+<3|>4,<6')
        _eq('4+<6|1+<3|', '')
        _eq('4|2+', '2+')
        _eq('3|<5', '<5')
        _eq('<3|>3', '>3|<3')
        _eq('3+|<3', '')
        _eq('3+|<4', '')
        _eq('2+<=6|3+<5', '2..6')
        _eq('3+,<5|2+<=6', '2+<=6')
        _eq('2|2+', '2+')
        _eq('2|2.1+', '2+')
        _eq('2|<2.1', '<2_')
        _eq('3..3', '==3')
        _eq('>=3,<=3', '==3')
        _and('3', '3', '3')
        _and('1', '==1', '==1')
        _and('', '==1', '==1')
        _and('3', '4', None)
        _and('<3', '5+', None)
        _and('4+<6', '6+<8', None)
        _and('2+', '<=4', '2..4')
        _and('1', '1.0', '1.0')
        _and('4..6', '6+<8', '==6')
        _inv('3+', '<3')
        _inv('<=3', '>3')
        _inv('3.5', '<3.5|3.5_+')
        self.assertTrue(~VersionRange() is None)
        _eq('>', '>')
        _eq('+', '')
        _eq('>=', '')
        _eq('<=', '==')
        _eq('..', '==')
        _eq('+<=', '==')
        invalid_range = ['4+<2', '>3<3', '>3<=3', '3+<3']
        for s in invalid_range:
            self.assertRaises(VersionError, VersionRange, s)
        invalid_syntax = ['<', '><', '>3>4', '<3<4', '<4>3', ',<4', '4+,',
            '1>=', '+1', '4<', '1+<2<3']
        for s in invalid_syntax:
            self.assertRaises(VersionError, VersionRange, s)
        self.assertTrue(VersionRange('').is_any())
        self.assertTrue(VersionRange('2+<4').bounded())
        self.assertTrue(VersionRange('2+').lower_bounded())
        self.assertTrue(not VersionRange('2+').upper_bounded())
        self.assertTrue(not VersionRange('2+').bounded())
        self.assertTrue(VersionRange('<2').upper_bounded())
        self.assertTrue(not VersionRange('<2').lower_bounded())
        self.assertTrue(not VersionRange('<2').bounded())
        v = Version('3')
        self.assertTrue(VersionRange.from_version(v, 'eq') == VersionRange(
            '==3'))
        self.assertTrue(VersionRange.from_version(v, 'gt') == VersionRange(
            '>3'))
        self.assertTrue(VersionRange.from_version(v, 'gte') == VersionRange
            ('3+'))
        self.assertTrue(VersionRange.from_version(v, 'lt') == VersionRange(
            '<3'))
        self.assertTrue(VersionRange.from_version(v, 'lte') == VersionRange
            ('<=3'))
        range1 = VersionRange.from_version(Version('2'), 'gte')
        range2 = VersionRange.from_version(Version('4'), 'lte')
        _eq(str(range1 & range2), '2..4')
        v2 = Version('6.0')
        v3 = Version('4')
        self.assertTrue(VersionRange.from_versions([v, v2, v3]) ==
            VersionRange('==3|==4|==6.0'))

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)
        a = VersionRange('1+<=2.5')
        b = VersionRange('1..2.5')
        c = VersionRange('>=5')
        d = VersionRange('>6.1.0')
        e = VersionRange('3.2')
        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d, e]) - set([a]), set([c, d, e]))
        _eq2(set([b, c, e]) | set([c, d]), set([b, c, d, e]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_containment(self):
        self.assertTrue(Version('3') in VersionRange('3+'))
        self.assertTrue(Version('5') in VersionRange('3..5'))
        self.assertTrue(Version('5_') not in VersionRange('3..5'))
        self.assertTrue(Version('3.0.0') in VersionRange('3+'))
        self.assertTrue(Version('3.0.0') not in VersionRange('3.1+'))
        self.assertTrue(Version('3') in VersionRange('<1|5|6|8|7|3|60+'))
        self.assertTrue(Version('3') in VersionRange('<1|5|6|8|7|==3|60+'))
        self.assertTrue(VersionRange('2.1+<4') in VersionRange('<4'))
        self.assertTrue(VersionRange('2.1..4') not in VersionRange('<4'))
        self.assertTrue(VersionRange('3') in VersionRange('3'))
        self.assertTrue(VersionRange('==3') in VersionRange('3'))
        self.assertTrue(VersionRange('3.5+<3_') in VersionRange('3'))
        self.assertTrue(VersionRange('3') not in VersionRange('4+<6'))
        self.assertTrue(VersionRange('3+<10') not in VersionRange('4+<6'))
        numbers = [2, 3, 5, 10, 11, 13, 14]
        versions = [Version(str(x)) for x in numbers]
        rev_versions = list(reversed(versions))
        composite_range = VersionRange.from_versions(versions)
        entries = [(VersionRange(''), 7), (VersionRange('0+'), 7), (
            VersionRange('5+'), 5), (VersionRange('6+'), 4), (VersionRange(
            '50+'), 0), (VersionRange('>5'), 4), (VersionRange('5'), 1), (
            VersionRange('6'), 0), (VersionRange('<5'), 2), (VersionRange(
            '<6'), 3), (VersionRange('<50'), 7), (VersionRange('<=5'), 3),
            (VersionRange('<1'), 0), (VersionRange('2|9+'), 5), (
            VersionRange('3+<6|12+<13.5'), 3), (VersionRange('<1|20+'), 0),
            (VersionRange('>0<20'), 7)]
        for range_, count in entries:
            matches = set(x for x in versions if x in range_)
            self.assertEqual(len(matches), count)

            def _test_it(it):
                matches_ = set(version for contains, version in it if contains)
                self.assertEqual(matches_, matches)
            _test_it(range_.iter_intersect_test(versions))
            _test_it(range_.iter_intersect_test(rev_versions, descending=True))
            self.assertEqual(composite_range.intersects(range_), count != 0)
            int_range = composite_range & range_
            versions_ = [] if int_range is None else int_range.to_versions()
            self.assertEqual(set(versions_), matches)
            self.assertEqual(range_.issuperset(composite_range), count == 7)
            if count:
                self.assertTrue(composite_range.issuperset(int_range))

    def test_requirement_list(self):

        def _eq(reqs, expected_reqs):
            _print('requirements(%s) == requirements(%s)' % (' '.join(reqs),
                ' '.join(expected_reqs)))
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print('result: %s' % str(reqlist))
            exp_reqs_ = [Requirement(x) for x in expected_reqs]
            self.assertTrue(reqlist.requirements == exp_reqs_)
            exp_names = set(x.name for x in exp_reqs_ if not x.conflict)
            self.assertTrue(reqlist.names == exp_names)
            exp_confl_names = set(x.name for x in exp_reqs_ if x.conflict)
            self.assertTrue(reqlist.conflict_names == exp_confl_names)

        def _confl(reqs, a, b):
            _print('requirements(%s) == %s <--!--> %s' % (' '.join(reqs), a, b)
                )
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print('result: %s' % str(reqlist))
            a_req = Requirement(a)
            b_req = Requirement(b)
            self.assertTrue(reqlist.conflict == (a_req, b_req))
        _print()
        _eq(['foo'], ['foo'])
        _eq(['foo', 'bah'], ['foo', 'bah'])
        _eq(['bah', 'foo'], ['bah', 'foo'])
        _eq(['foo-4+', 'foo-4.5'], ['foo-4.5'])
        _eq(['bah-2.4', 'foo', 'bah-2.4.1+'], ['bah-2.4.1+<2.4_', 'foo'])
        _eq(['foo-2+', '!foo-4+'], ['foo-2+<4'])
        _eq(['!bah-1', '!bah-3'], ['!bah-1|3'])
        _eq(['!bah-5', 'foo-2.3', '!bah-5.6+'], ['!bah-5+', 'foo-2.3'])
        _eq(['~bah-4', 'foo', 'bah<4.2'], ['bah-4+<4.2', 'foo'])
        _eq(['~bah', '!foo', 'bah<4.2'], ['bah<4.2', '!foo'])
        _eq(['~bah-3+', '~bah-5'], ['~bah-5'])
        _confl(['foo-1', 'foo-2'], 'foo-1', 'foo-2')
        _confl(['foo-2', 'foo-1'], 'foo-2', 'foo-1')
        _confl(['foo', '~bah-5+', 'bah-2'], '~bah-5+', 'bah-2')
        _confl(['foo', '~bah-5+', 'bah-7..12', 'bah-2'], 'bah-7..12', 'bah-2')


<mask token>

<mask token>


class TestVersionSchema(unittest.TestCase):
    <mask token>

    def __init__(self, fn):
        unittest.TestCase.__init__(self, fn)

    def _test_strict_weak_ordering(self, a, b):
        self.assertTrue(a == a)
        self.assertTrue(b == b)
        e = a == b
        ne = a != b
        lt = a < b
        lte = a <= b
        gt = a > b
        gte = a >= b
        _print('\n' + textwrap.dedent(
            """
               '%s' <op> '%s'
               ==:  %s
               !=:  %s
               <:   %s
               <=:  %s
               >:   %s
               >=:  %s
               """
            ).strip() % (a, b, e, ne, lt, lte, gt, gte))
        self.assertTrue(e != ne)
        if e:
            self.assertTrue(not lt)
            self.assertTrue(not gt)
            self.assertTrue(lte)
            self.assertTrue(gte)
        else:
            self.assertTrue(lt != gt)
            self.assertTrue(lte != gte)
            self.assertTrue(lt == lte)
            self.assertTrue(gt == gte)
        if not isinstance(a, _ReversedComparable):
            self._test_strict_weak_ordering(reverse_sort_key(a),
                reverse_sort_key(b))

    def _test_ordered(self, items):

        def _test(fn, items_, op_str):
            for i, a in enumerate(items_):
                for b in items_[i + 1:]:
                    _print("'%s' %s '%s'" % (a, op_str, b))
                    self.assertTrue(fn(a, b))
        _test(lambda a, b: a < b, items, '<')
        _test(lambda a, b: a <= b, items, '<=')
        _test(lambda a, b: a != b, items, '!=')
        _test(lambda a, b: a > b, list(reversed(items)), '>')
        _test(lambda a, b: a >= b, list(reversed(items)), '>=')
        _test(lambda a, b: a != b, list(reversed(items)), '!=')
    <mask token>

    def _create_random_version(self):
        ver_str = '.'.join(self.make_token.create_random_token_string() for
            i in range(random.randint(0, 6)))
        return Version(ver_str, make_token=self.make_token)

    def test_misc(self):
        self.assertEqual(Version('1.2.12').as_tuple(), ('1', '2', '12'))

    def test_token_strict_weak_ordering(self):
        tok = self._create_random_token()
        self._test_strict_weak_ordering(tok, tok)
        for i in range(100):
            tok1 = self._create_random_token()
            tok2 = self._create_random_token()
            self._test_strict_weak_ordering(tok1, tok2)

    def test_version_strict_weak_ordering(self):
        ver = self._create_random_version()
        self._test_strict_weak_ordering(ver, ver)
        for i in range(100):
            ver1 = self._create_random_version()
            ver2 = self._create_random_version()
            self._test_strict_weak_ordering(ver1, ver2)
    <mask token>

    def test_version_comparisons(self):

        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(Version(a) == Version(b))
        _print()
        _eq('', '')
        _eq('1', '1')
        _eq('1.2', '1-2')
        _eq('1.2-3', '1-2.3')
        ascending = ['', '0.0.0', '1', '2', '2.alpha1', '2.alpha2',
            '2.beta', '2.0', '2.0.8.8', '2.1', '2.1.0']
        self._test_ordered([Version(x) for x in ascending])

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)
        a = Version('1.0')
        b = Version('1.0')
        c = Version('1.0alpha')
        d = Version('2.0.0')
        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d]) - set([a]), set([c, d]))
        _eq2(set([b, c]) | set([c, d]), set([b, c, d]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_version_range(self):

        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))
            a_ = a.replace('.', '-')
            a_ = a_.replace('--', '..')
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))
            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)
            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)
            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)
            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)
            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)

        def _and(a, b, c):
            _print("'%s' & '%s' == '%s'" % (a, b, c))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            c_range = None if c is None else VersionRange(c)
            self.assertTrue(a_range & b_range == c_range)
            self.assertTrue(b_range & a_range == c_range)
            a_or_b = a_range | b_range
            a_and_b = a_range & b_range
            a_sub_b = a_range - b_range
            b_sub_a = b_range - a_range
            ranges = [a_and_b, a_sub_b, b_sub_a]
            ranges = [x for x in ranges if x]
            self.assertTrue(ranges[0].union(ranges[1:]) == a_or_b)

        def _inv(a, b):
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(~a_range == b_range)
            self.assertTrue(~b_range == a_range)
            self.assertTrue(a_range | b_range == VersionRange())
            self.assertTrue(a_range & b_range is None)
        _print()
        _eq('', '')
        _eq('1', '1')
        _eq('1.0.0', '1.0.0')
        _eq('3+<3_', '3')
        _eq('_+<__', '_')
        _eq('1.2+<=2.0', '1.2..2.0')
        _eq('10+,<20', '10+<20')
        _eq('1+<1.0', '1+<1.0')
        _eq('>=2', '2+')
        _eq('>=1.21.1,<1.23', '>=1.21.1<1.23')
        _eq('>1.21.1,<1.23', '>1.21.1<1.23')
        _eq('>1.21.1<1.23', '>1.21.1<1.23')
        _eq('>1.21.1,<=1.23', '>1.21.1<=1.23')
        _eq('<1.23,>=1.21.1', '>=1.21.1<1.23')
        _eq('<1.23,>1.21.1', '>1.21.1<1.23')
        _eq('3|3', '3')
        _eq('3|1', '1|3')
        _eq('5|3|1', '1|3|5')
        _eq('1|1_', '1+<1__')
        _eq('1|1_|1__', '1+,<1___')
        _eq('|', '')
        _eq('||', '||||||||')
        _eq('1|1_+', '1+')
        _eq('<1|1', '<1_')
        _eq('1+<3|3+<5', '1+<5')
        _eq('>4<6|1+<3', '1+<3|>4,<6')
        _eq('4+<6|1+<3|', '')
        _eq('4|2+', '2+')
        _eq('3|<5', '<5')
        _eq('<3|>3', '>3|<3')
        _eq('3+|<3', '')
        _eq('3+|<4', '')
        _eq('2+<=6|3+<5', '2..6')
        _eq('3+,<5|2+<=6', '2+<=6')
        _eq('2|2+', '2+')
        _eq('2|2.1+', '2+')
        _eq('2|<2.1', '<2_')
        _eq('3..3', '==3')
        _eq('>=3,<=3', '==3')
        _and('3', '3', '3')
        _and('1', '==1', '==1')
        _and('', '==1', '==1')
        _and('3', '4', None)
        _and('<3', '5+', None)
        _and('4+<6', '6+<8', None)
        _and('2+', '<=4', '2..4')
        _and('1', '1.0', '1.0')
        _and('4..6', '6+<8', '==6')
        _inv('3+', '<3')
        _inv('<=3', '>3')
        _inv('3.5', '<3.5|3.5_+')
        self.assertTrue(~VersionRange() is None)
        _eq('>', '>')
        _eq('+', '')
        _eq('>=', '')
        _eq('<=', '==')
        _eq('..', '==')
        _eq('+<=', '==')
        invalid_range = ['4+<2', '>3<3', '>3<=3', '3+<3']
        for s in invalid_range:
            self.assertRaises(VersionError, VersionRange, s)
        invalid_syntax = ['<', '><', '>3>4', '<3<4', '<4>3', ',<4', '4+,',
            '1>=', '+1', '4<', '1+<2<3']
        for s in invalid_syntax:
            self.assertRaises(VersionError, VersionRange, s)
        self.assertTrue(VersionRange('').is_any())
        self.assertTrue(VersionRange('2+<4').bounded())
        self.assertTrue(VersionRange('2+').lower_bounded())
        self.assertTrue(not VersionRange('2+').upper_bounded())
        self.assertTrue(not VersionRange('2+').bounded())
        self.assertTrue(VersionRange('<2').upper_bounded())
        self.assertTrue(not VersionRange('<2').lower_bounded())
        self.assertTrue(not VersionRange('<2').bounded())
        v = Version('3')
        self.assertTrue(VersionRange.from_version(v, 'eq') == VersionRange(
            '==3'))
        self.assertTrue(VersionRange.from_version(v, 'gt') == VersionRange(
            '>3'))
        self.assertTrue(VersionRange.from_version(v, 'gte') == VersionRange
            ('3+'))
        self.assertTrue(VersionRange.from_version(v, 'lt') == VersionRange(
            '<3'))
        self.assertTrue(VersionRange.from_version(v, 'lte') == VersionRange
            ('<=3'))
        range1 = VersionRange.from_version(Version('2'), 'gte')
        range2 = VersionRange.from_version(Version('4'), 'lte')
        _eq(str(range1 & range2), '2..4')
        v2 = Version('6.0')
        v3 = Version('4')
        self.assertTrue(VersionRange.from_versions([v, v2, v3]) ==
            VersionRange('==3|==4|==6.0'))

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)
        a = VersionRange('1+<=2.5')
        b = VersionRange('1..2.5')
        c = VersionRange('>=5')
        d = VersionRange('>6.1.0')
        e = VersionRange('3.2')
        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d, e]) - set([a]), set([c, d, e]))
        _eq2(set([b, c, e]) | set([c, d]), set([b, c, d, e]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_containment(self):
        self.assertTrue(Version('3') in VersionRange('3+'))
        self.assertTrue(Version('5') in VersionRange('3..5'))
        self.assertTrue(Version('5_') not in VersionRange('3..5'))
        self.assertTrue(Version('3.0.0') in VersionRange('3+'))
        self.assertTrue(Version('3.0.0') not in VersionRange('3.1+'))
        self.assertTrue(Version('3') in VersionRange('<1|5|6|8|7|3|60+'))
        self.assertTrue(Version('3') in VersionRange('<1|5|6|8|7|==3|60+'))
        self.assertTrue(VersionRange('2.1+<4') in VersionRange('<4'))
        self.assertTrue(VersionRange('2.1..4') not in VersionRange('<4'))
        self.assertTrue(VersionRange('3') in VersionRange('3'))
        self.assertTrue(VersionRange('==3') in VersionRange('3'))
        self.assertTrue(VersionRange('3.5+<3_') in VersionRange('3'))
        self.assertTrue(VersionRange('3') not in VersionRange('4+<6'))
        self.assertTrue(VersionRange('3+<10') not in VersionRange('4+<6'))
        numbers = [2, 3, 5, 10, 11, 13, 14]
        versions = [Version(str(x)) for x in numbers]
        rev_versions = list(reversed(versions))
        composite_range = VersionRange.from_versions(versions)
        entries = [(VersionRange(''), 7), (VersionRange('0+'), 7), (
            VersionRange('5+'), 5), (VersionRange('6+'), 4), (VersionRange(
            '50+'), 0), (VersionRange('>5'), 4), (VersionRange('5'), 1), (
            VersionRange('6'), 0), (VersionRange('<5'), 2), (VersionRange(
            '<6'), 3), (VersionRange('<50'), 7), (VersionRange('<=5'), 3),
            (VersionRange('<1'), 0), (VersionRange('2|9+'), 5), (
            VersionRange('3+<6|12+<13.5'), 3), (VersionRange('<1|20+'), 0),
            (VersionRange('>0<20'), 7)]
        for range_, count in entries:
            matches = set(x for x in versions if x in range_)
            self.assertEqual(len(matches), count)

            def _test_it(it):
                matches_ = set(version for contains, version in it if contains)
                self.assertEqual(matches_, matches)
            _test_it(range_.iter_intersect_test(versions))
            _test_it(range_.iter_intersect_test(rev_versions, descending=True))
            self.assertEqual(composite_range.intersects(range_), count != 0)
            int_range = composite_range & range_
            versions_ = [] if int_range is None else int_range.to_versions()
            self.assertEqual(set(versions_), matches)
            self.assertEqual(range_.issuperset(composite_range), count == 7)
            if count:
                self.assertTrue(composite_range.issuperset(int_range))

    def test_requirement_list(self):

        def _eq(reqs, expected_reqs):
            _print('requirements(%s) == requirements(%s)' % (' '.join(reqs),
                ' '.join(expected_reqs)))
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print('result: %s' % str(reqlist))
            exp_reqs_ = [Requirement(x) for x in expected_reqs]
            self.assertTrue(reqlist.requirements == exp_reqs_)
            exp_names = set(x.name for x in exp_reqs_ if not x.conflict)
            self.assertTrue(reqlist.names == exp_names)
            exp_confl_names = set(x.name for x in exp_reqs_ if x.conflict)
            self.assertTrue(reqlist.conflict_names == exp_confl_names)

        def _confl(reqs, a, b):
            _print('requirements(%s) == %s <--!--> %s' % (' '.join(reqs), a, b)
                )
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print('result: %s' % str(reqlist))
            a_req = Requirement(a)
            b_req = Requirement(b)
            self.assertTrue(reqlist.conflict == (a_req, b_req))
        _print()
        _eq(['foo'], ['foo'])
        _eq(['foo', 'bah'], ['foo', 'bah'])
        _eq(['bah', 'foo'], ['bah', 'foo'])
        _eq(['foo-4+', 'foo-4.5'], ['foo-4.5'])
        _eq(['bah-2.4', 'foo', 'bah-2.4.1+'], ['bah-2.4.1+<2.4_', 'foo'])
        _eq(['foo-2+', '!foo-4+'], ['foo-2+<4'])
        _eq(['!bah-1', '!bah-3'], ['!bah-1|3'])
        _eq(['!bah-5', 'foo-2.3', '!bah-5.6+'], ['!bah-5+', 'foo-2.3'])
        _eq(['~bah-4', 'foo', 'bah<4.2'], ['bah-4+<4.2', 'foo'])
        _eq(['~bah', '!foo', 'bah<4.2'], ['bah<4.2', '!foo'])
        _eq(['~bah-3+', '~bah-5'], ['~bah-5'])
        _confl(['foo-1', 'foo-2'], 'foo-1', 'foo-2')
        _confl(['foo-2', 'foo-1'], 'foo-2', 'foo-1')
        _confl(['foo', '~bah-5+', 'bah-2'], '~bah-5+', 'bah-2')
        _confl(['foo', '~bah-5+', 'bah-7..12', 'bah-2'], 'bah-7..12', 'bah-2')


<mask token>

from rez.vendor.version.version import Version, AlphanumericVersionToken, VersionRange, reverse_sort_key, _ReversedComparable
from rez.vendor.version.requirement import Requirement, RequirementList
from rez.vendor.version.util import VersionError
import random
import textwrap
import unittest


def _print(txt=''):
    pass


class TestVersionSchema(unittest.TestCase):
    make_token = AlphanumericVersionToken

    def __init__(self, fn):
        unittest.TestCase.__init__(self, fn)

    def _test_strict_weak_ordering(self, a, b):
        self.assertTrue(a == a)
        self.assertTrue(b == b)
        e = a == b
        ne = a != b
        lt = a < b
        lte = a <= b
        gt = a > b
        gte = a >= b
        _print('\n' + textwrap.dedent(
            """
               '%s' <op> '%s'
               ==:  %s
               !=:  %s
               <:   %s
               <=:  %s
               >:   %s
               >=:  %s
               """
            ).strip() % (a, b, e, ne, lt, lte, gt, gte))
        self.assertTrue(e != ne)
        if e:
            self.assertTrue(not lt)
            self.assertTrue(not gt)
            self.assertTrue(lte)
            self.assertTrue(gte)
        else:
            self.assertTrue(lt != gt)
            self.assertTrue(lte != gte)
            self.assertTrue(lt == lte)
            self.assertTrue(gt == gte)
        if not isinstance(a, _ReversedComparable):
            self._test_strict_weak_ordering(reverse_sort_key(a),
                reverse_sort_key(b))

    def _test_ordered(self, items):

        def _test(fn, items_, op_str):
            for i, a in enumerate(items_):
                for b in items_[i + 1:]:
                    _print("'%s' %s '%s'" % (a, op_str, b))
                    self.assertTrue(fn(a, b))
        _test(lambda a, b: a < b, items, '<')
        _test(lambda a, b: a <= b, items, '<=')
        _test(lambda a, b: a != b, items, '!=')
        _test(lambda a, b: a > b, list(reversed(items)), '>')
        _test(lambda a, b: a >= b, list(reversed(items)), '>=')
        _test(lambda a, b: a != b, list(reversed(items)), '!=')

    def _create_random_token(self):
        s = self.make_token.create_random_token_string()
        return self.make_token(s)

    def _create_random_version(self):
        ver_str = '.'.join(self.make_token.create_random_token_string() for
            i in range(random.randint(0, 6)))
        return Version(ver_str, make_token=self.make_token)

    def test_misc(self):
        self.assertEqual(Version('1.2.12').as_tuple(), ('1', '2', '12'))

    def test_token_strict_weak_ordering(self):
        tok = self._create_random_token()
        self._test_strict_weak_ordering(tok, tok)
        for i in range(100):
            tok1 = self._create_random_token()
            tok2 = self._create_random_token()
            self._test_strict_weak_ordering(tok1, tok2)

    def test_version_strict_weak_ordering(self):
        ver = self._create_random_version()
        self._test_strict_weak_ordering(ver, ver)
        for i in range(100):
            ver1 = self._create_random_version()
            ver2 = self._create_random_version()
            self._test_strict_weak_ordering(ver1, ver2)

    def test_token_comparisons(self):

        def _lt(a, b):
            _print("'%s' < '%s'" % (a, b))
            self.assertTrue(self.make_token(a) < self.make_token(b))
            self.assertTrue(Version(a) < Version(b))
        _print()
        _lt('3', '4')
        _lt('01', '1')
        _lt('beta', '1')
        _lt('alpha3', 'alpha4')
        _lt('alpha', 'alpha3')
        _lt('gamma33', '33gamma')

    def test_version_comparisons(self):

        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(Version(a) == Version(b))
        _print()
        _eq('', '')
        _eq('1', '1')
        _eq('1.2', '1-2')
        _eq('1.2-3', '1-2.3')
        ascending = ['', '0.0.0', '1', '2', '2.alpha1', '2.alpha2',
            '2.beta', '2.0', '2.0.8.8', '2.1', '2.1.0']
        self._test_ordered([Version(x) for x in ascending])

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)
        a = Version('1.0')
        b = Version('1.0')
        c = Version('1.0alpha')
        d = Version('2.0.0')
        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d]) - set([a]), set([c, d]))
        _eq2(set([b, c]) | set([c, d]), set([b, c, d]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_version_range(self):

        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))
            a_ = a.replace('.', '-')
            a_ = a_.replace('--', '..')
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))
            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)
            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)
            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)
            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)
            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)

        def _and(a, b, c):
            _print("'%s' & '%s' == '%s'" % (a, b, c))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            c_range = None if c is None else VersionRange(c)
            self.assertTrue(a_range & b_range == c_range)
            self.assertTrue(b_range & a_range == c_range)
            a_or_b = a_range | b_range
            a_and_b = a_range & b_range
            a_sub_b = a_range - b_range
            b_sub_a = b_range - a_range
            ranges = [a_and_b, a_sub_b, b_sub_a]
            ranges = [x for x in ranges if x]
            self.assertTrue(ranges[0].union(ranges[1:]) == a_or_b)

        def _inv(a, b):
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(~a_range == b_range)
            self.assertTrue(~b_range == a_range)
            self.assertTrue(a_range | b_range == VersionRange())
            self.assertTrue(a_range & b_range is None)
        _print()
        _eq('', '')
        _eq('1', '1')
        _eq('1.0.0', '1.0.0')
        _eq('3+<3_', '3')
        _eq('_+<__', '_')
        _eq('1.2+<=2.0', '1.2..2.0')
        _eq('10+,<20', '10+<20')
        _eq('1+<1.0', '1+<1.0')
        _eq('>=2', '2+')
        _eq('>=1.21.1,<1.23', '>=1.21.1<1.23')
        _eq('>1.21.1,<1.23', '>1.21.1<1.23')
        _eq('>1.21.1<1.23', '>1.21.1<1.23')
        _eq('>1.21.1,<=1.23', '>1.21.1<=1.23')
        _eq('<1.23,>=1.21.1', '>=1.21.1<1.23')
        _eq('<1.23,>1.21.1', '>1.21.1<1.23')
        _eq('3|3', '3')
        _eq('3|1', '1|3')
        _eq('5|3|1', '1|3|5')
        _eq('1|1_', '1+<1__')
        _eq('1|1_|1__', '1+,<1___')
        _eq('|', '')
        _eq('||', '||||||||')
        _eq('1|1_+', '1+')
        _eq('<1|1', '<1_')
        _eq('1+<3|3+<5', '1+<5')
        _eq('>4<6|1+<3', '1+<3|>4,<6')
        _eq('4+<6|1+<3|', '')
        _eq('4|2+', '2+')
        _eq('3|<5', '<5')
        _eq('<3|>3', '>3|<3')
        _eq('3+|<3', '')
        _eq('3+|<4', '')
        _eq('2+<=6|3+<5', '2..6')
        _eq('3+,<5|2+<=6', '2+<=6')
        _eq('2|2+', '2+')
        _eq('2|2.1+', '2+')
        _eq('2|<2.1', '<2_')
        _eq('3..3', '==3')
        _eq('>=3,<=3', '==3')
        _and('3', '3', '3')
        _and('1', '==1', '==1')
        _and('', '==1', '==1')
        _and('3', '4', None)
        _and('<3', '5+', None)
        _and('4+<6', '6+<8', None)
        _and('2+', '<=4', '2..4')
        _and('1', '1.0', '1.0')
        _and('4..6', '6+<8', '==6')
        _inv('3+', '<3')
        _inv('<=3', '>3')
        _inv('3.5', '<3.5|3.5_+')
        self.assertTrue(~VersionRange() is None)
        _eq('>', '>')
        _eq('+', '')
        _eq('>=', '')
        _eq('<=', '==')
        _eq('..', '==')
        _eq('+<=', '==')
        invalid_range = ['4+<2', '>3<3', '>3<=3', '3+<3']
        for s in invalid_range:
            self.assertRaises(VersionError, VersionRange, s)
        invalid_syntax = ['<', '><', '>3>4', '<3<4', '<4>3', ',<4', '4+,',
            '1>=', '+1', '4<', '1+<2<3']
        for s in invalid_syntax:
            self.assertRaises(VersionError, VersionRange, s)
        self.assertTrue(VersionRange('').is_any())
        self.assertTrue(VersionRange('2+<4').bounded())
        self.assertTrue(VersionRange('2+').lower_bounded())
        self.assertTrue(not VersionRange('2+').upper_bounded())
        self.assertTrue(not VersionRange('2+').bounded())
        self.assertTrue(VersionRange('<2').upper_bounded())
        self.assertTrue(not VersionRange('<2').lower_bounded())
        self.assertTrue(not VersionRange('<2').bounded())
        v = Version('3')
        self.assertTrue(VersionRange.from_version(v, 'eq') == VersionRange(
            '==3'))
        self.assertTrue(VersionRange.from_version(v, 'gt') == VersionRange(
            '>3'))
        self.assertTrue(VersionRange.from_version(v, 'gte') == VersionRange
            ('3+'))
        self.assertTrue(VersionRange.from_version(v, 'lt') == VersionRange(
            '<3'))
        self.assertTrue(VersionRange.from_version(v, 'lte') == VersionRange
            ('<=3'))
        range1 = VersionRange.from_version(Version('2'), 'gte')
        range2 = VersionRange.from_version(Version('4'), 'lte')
        _eq(str(range1 & range2), '2..4')
        v2 = Version('6.0')
        v3 = Version('4')
        self.assertTrue(VersionRange.from_versions([v, v2, v3]) ==
            VersionRange('==3|==4|==6.0'))

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)
        a = VersionRange('1+<=2.5')
        b = VersionRange('1..2.5')
        c = VersionRange('>=5')
        d = VersionRange('>6.1.0')
        e = VersionRange('3.2')
        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d, e]) - set([a]), set([c, d, e]))
        _eq2(set([b, c, e]) | set([c, d]), set([b, c, d, e]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_containment(self):
        self.assertTrue(Version('3') in VersionRange('3+'))
        self.assertTrue(Version('5') in VersionRange('3..5'))
        self.assertTrue(Version('5_') not in VersionRange('3..5'))
        self.assertTrue(Version('3.0.0') in VersionRange('3+'))
        self.assertTrue(Version('3.0.0') not in VersionRange('3.1+'))
        self.assertTrue(Version('3') in VersionRange('<1|5|6|8|7|3|60+'))
        self.assertTrue(Version('3') in VersionRange('<1|5|6|8|7|==3|60+'))
        self.assertTrue(VersionRange('2.1+<4') in VersionRange('<4'))
        self.assertTrue(VersionRange('2.1..4') not in VersionRange('<4'))
        self.assertTrue(VersionRange('3') in VersionRange('3'))
        self.assertTrue(VersionRange('==3') in VersionRange('3'))
        self.assertTrue(VersionRange('3.5+<3_') in VersionRange('3'))
        self.assertTrue(VersionRange('3') not in VersionRange('4+<6'))
        self.assertTrue(VersionRange('3+<10') not in VersionRange('4+<6'))
        numbers = [2, 3, 5, 10, 11, 13, 14]
        versions = [Version(str(x)) for x in numbers]
        rev_versions = list(reversed(versions))
        composite_range = VersionRange.from_versions(versions)
        entries = [(VersionRange(''), 7), (VersionRange('0+'), 7), (
            VersionRange('5+'), 5), (VersionRange('6+'), 4), (VersionRange(
            '50+'), 0), (VersionRange('>5'), 4), (VersionRange('5'), 1), (
            VersionRange('6'), 0), (VersionRange('<5'), 2), (VersionRange(
            '<6'), 3), (VersionRange('<50'), 7), (VersionRange('<=5'), 3),
            (VersionRange('<1'), 0), (VersionRange('2|9+'), 5), (
            VersionRange('3+<6|12+<13.5'), 3), (VersionRange('<1|20+'), 0),
            (VersionRange('>0<20'), 7)]
        for range_, count in entries:
            matches = set(x for x in versions if x in range_)
            self.assertEqual(len(matches), count)

            def _test_it(it):
                matches_ = set(version for contains, version in it if contains)
                self.assertEqual(matches_, matches)
            _test_it(range_.iter_intersect_test(versions))
            _test_it(range_.iter_intersect_test(rev_versions, descending=True))
            self.assertEqual(composite_range.intersects(range_), count != 0)
            int_range = composite_range & range_
            versions_ = [] if int_range is None else int_range.to_versions()
            self.assertEqual(set(versions_), matches)
            self.assertEqual(range_.issuperset(composite_range), count == 7)
            if count:
                self.assertTrue(composite_range.issuperset(int_range))

    def test_requirement_list(self):

        def _eq(reqs, expected_reqs):
            _print('requirements(%s) == requirements(%s)' % (' '.join(reqs),
                ' '.join(expected_reqs)))
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print('result: %s' % str(reqlist))
            exp_reqs_ = [Requirement(x) for x in expected_reqs]
            self.assertTrue(reqlist.requirements == exp_reqs_)
            exp_names = set(x.name for x in exp_reqs_ if not x.conflict)
            self.assertTrue(reqlist.names == exp_names)
            exp_confl_names = set(x.name for x in exp_reqs_ if x.conflict)
            self.assertTrue(reqlist.conflict_names == exp_confl_names)

        def _confl(reqs, a, b):
            _print('requirements(%s) == %s <--!--> %s' % (' '.join(reqs), a, b)
                )
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print('result: %s' % str(reqlist))
            a_req = Requirement(a)
            b_req = Requirement(b)
            self.assertTrue(reqlist.conflict == (a_req, b_req))
        _print()
        _eq(['foo'], ['foo'])
        _eq(['foo', 'bah'], ['foo', 'bah'])
        _eq(['bah', 'foo'], ['bah', 'foo'])
        _eq(['foo-4+', 'foo-4.5'], ['foo-4.5'])
        _eq(['bah-2.4', 'foo', 'bah-2.4.1+'], ['bah-2.4.1+<2.4_', 'foo'])
        _eq(['foo-2+', '!foo-4+'], ['foo-2+<4'])
        _eq(['!bah-1', '!bah-3'], ['!bah-1|3'])
        _eq(['!bah-5', 'foo-2.3', '!bah-5.6+'], ['!bah-5+', 'foo-2.3'])
        _eq(['~bah-4', 'foo', 'bah<4.2'], ['bah-4+<4.2', 'foo'])
        _eq(['~bah', '!foo', 'bah<4.2'], ['bah<4.2', '!foo'])
        _eq(['~bah-3+', '~bah-5'], ['~bah-5'])
        _confl(['foo-1', 'foo-2'], 'foo-1', 'foo-2')
        _confl(['foo-2', 'foo-1'], 'foo-2', 'foo-1')
        _confl(['foo', '~bah-5+', 'bah-2'], '~bah-5+', 'bah-2')
        _confl(['foo', '~bah-5+', 'bah-7..12', 'bah-2'], 'bah-7..12', 'bah-2')


if __name__ == '__main__':
    unittest.main()

# SPDX-License-Identifier: Apache-2.0
# Copyright Contributors to the Rez Project


from rez.vendor.version.version import Version, AlphanumericVersionToken, \
    VersionRange, reverse_sort_key, _ReversedComparable
from rez.vendor.version.requirement import Requirement, RequirementList
from rez.vendor.version.util import VersionError
import random
import textwrap
import unittest



def _print(txt=''):
    # uncomment for verbose output
    #print txt
    pass


class TestVersionSchema(unittest.TestCase):
    make_token = AlphanumericVersionToken

    def __init__(self, fn):
        unittest.TestCase.__init__(self, fn)

    def _test_strict_weak_ordering(self, a, b):
        self.assertTrue(a == a)
        self.assertTrue(b == b)

        e       = (a == b)
        ne      = (a != b)
        lt      = (a < b)
        lte     = (a <= b)
        gt      = (a > b)
        gte     = (a >= b)

        _print('\n' + textwrap.dedent(
               """
               '%s' <op> '%s'
               ==:  %s
               !=:  %s
               <:   %s
               <=:  %s
               >:   %s
               >=:  %s
               """).strip() % (a, b, e, ne, lt, lte, gt, gte))

        self.assertTrue(e != ne)
        if e:
            self.assertTrue(not lt)
            self.assertTrue(not gt)
            self.assertTrue(lte)
            self.assertTrue(gte)
        else:
            self.assertTrue(lt != gt)
            self.assertTrue(lte != gte)
            self.assertTrue(lt == lte)
            self.assertTrue(gt == gte)

        if not isinstance(a, _ReversedComparable):
            self._test_strict_weak_ordering(reverse_sort_key(a),
                                            reverse_sort_key(b))

    def _test_ordered(self, items):
        def _test(fn, items_, op_str):
            for i, a in enumerate(items_):
                for b in items_[i+1:]:
                    _print("'%s' %s '%s'" % (a, op_str, b))
                    self.assertTrue(fn(a, b))

        _test(lambda a, b: a < b, items, '<')
        _test(lambda a, b: a <= b, items, '<=')
        _test(lambda a, b: a != b, items, '!=')
        _test(lambda a, b: a > b, list(reversed(items)), '>')
        _test(lambda a, b: a >= b, list(reversed(items)), '>=')
        _test(lambda a, b: a != b, list(reversed(items)), '!=')

    def _create_random_token(self):
        s = self.make_token.create_random_token_string()
        return self.make_token(s)

    def _create_random_version(self):
        ver_str = '.'.join(self.make_token.create_random_token_string()
                           for i in range(random.randint(0, 6)))
        return Version(ver_str, make_token=self.make_token)

    def test_misc(self):
        self.assertEqual(Version("1.2.12").as_tuple(), ("1", "2", "12"))

    def test_token_strict_weak_ordering(self):
        # test equal tokens
        tok = self._create_random_token()
        self._test_strict_weak_ordering(tok, tok)

        # test random tokens
        for i in range(100):
            tok1 = self._create_random_token()
            tok2 = self._create_random_token()
            self._test_strict_weak_ordering(tok1, tok2)

    def test_version_strict_weak_ordering(self):
        # test equal versions
        ver = self._create_random_version()
        self._test_strict_weak_ordering(ver, ver)

        # test random versions
        for i in range(100):
            ver1 = self._create_random_version()
            ver2 = self._create_random_version()
            self._test_strict_weak_ordering(ver1, ver2)

    def test_token_comparisons(self):
        def _lt(a, b):
            _print("'%s' < '%s'" % (a, b))
            self.assertTrue(self.make_token(a) < self.make_token(b))
            self.assertTrue(Version(a) < Version(b))

        _print()
        _lt("3", "4")
        _lt("01", "1")
        _lt("beta", "1")
        _lt("alpha3", "alpha4")
        _lt("alpha", "alpha3")
        _lt("gamma33", "33gamma")

    def test_version_comparisons(self):
        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(Version(a) == Version(b))

        _print()
        _eq("", "")
        _eq("1", "1")
        _eq("1.2", "1-2")
        _eq("1.2-3", "1-2.3")

        ascending = ["",
                     "0.0.0",
                     "1",
                     "2",
                     "2.alpha1",
                     "2.alpha2",
                     "2.beta",
                     "2.0",
                     "2.0.8.8",
                     "2.1",
                     "2.1.0"]
        self._test_ordered([Version(x) for x in ascending])

        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)

        # test behaviour in sets
        a = Version("1.0")
        b = Version("1.0")
        c = Version("1.0alpha")
        d = Version("2.0.0")

        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d]) - set([a]), set([c, d]))
        _eq2(set([b, c]) | set([c, d]), set([b, c, d]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_version_range(self):
        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)

            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))

            a_ = a.replace('.', '-')
            a_ = a_.replace("--", "..")
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))

            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)

            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)

            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)

            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)

            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)

        def _and(a, b, c):
            _print("'%s' & '%s' == '%s'" % (a, b, c))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            c_range = None if c is None else VersionRange(c)
            self.assertTrue(a_range & b_range == c_range)
            self.assertTrue(b_range & a_range == c_range)

            a_or_b = a_range | b_range
            a_and_b = a_range & b_range
            a_sub_b = a_range - b_range
            b_sub_a = b_range - a_range
            ranges = [a_and_b, a_sub_b, b_sub_a]
            ranges = [x for x in ranges if x]
            self.assertTrue(ranges[0].union(ranges[1:]) == a_or_b)

        def _inv(a, b):
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(~a_range == b_range)
            self.assertTrue(~b_range == a_range)
            self.assertTrue(a_range | b_range == VersionRange())
            self.assertTrue(a_range & b_range is None)

        # simple cases
        _print()
        _eq("", "")
        _eq("1", "1")
        _eq("1.0.0", "1.0.0")
        _eq("3+<3_", "3")
        _eq("_+<__", "_")
        _eq("1.2+<=2.0", "1.2..2.0")
        _eq("10+,<20", "10+<20")
        _eq("1+<1.0", "1+<1.0")
        _eq(">=2", "2+")
        _eq(">=1.21.1,<1.23", ">=1.21.1<1.23")
        _eq(">1.21.1,<1.23", ">1.21.1<1.23")
        _eq(">1.21.1<1.23", ">1.21.1<1.23")
        _eq(">1.21.1,<=1.23", ">1.21.1<=1.23")

        # Reverse order which is a syntax pip packages use more often now.
        # Only allowed when separated by a comma.
        _eq("<1.23,>=1.21.1", ">=1.21.1<1.23")
        _eq("<1.23,>1.21.1", ">1.21.1<1.23")

        # optimised cases
        _eq("3|3", "3")
        _eq("3|1", "1|3")
        _eq("5|3|1", "1|3|5")
        _eq("1|1_", "1+<1__")
        _eq("1|1_|1__", "1+,<1___")
        _eq("|", "")
        _eq("||", "||||||||")
        _eq("1|1_+", "1+")
        _eq("<1|1", "<1_")
        _eq("1+<3|3+<5", "1+<5")
        _eq(">4<6|1+<3", "1+<3|>4,<6")
        _eq("4+<6|1+<3|", "")
        _eq("4|2+", "2+")
        _eq("3|<5", "<5")
        _eq("<3|>3", ">3|<3")
        _eq("3+|<3", "")
        _eq("3+|<4", "")
        _eq("2+<=6|3+<5", "2..6")
        _eq("3+,<5|2+<=6", "2+<=6")
        _eq("2|2+", "2+")
        _eq("2|2.1+", "2+")
        _eq("2|<2.1", "<2_")
        _eq("3..3", "==3")
        _eq(">=3,<=3", "==3")

        # AND'ing
        _and("3", "3", "3")
        _and("1", "==1", "==1")
        _and("", "==1", "==1")
        _and("3", "4", None)
        _and("<3", "5+", None)
        _and("4+<6", "6+<8", None)
        _and("2+", "<=4", "2..4")
        _and("1", "1.0", "1.0")
        _and("4..6", "6+<8", "==6")

        # inverse
        _inv("3+", "<3")
        _inv("<=3", ">3")
        _inv("3.5", "<3.5|3.5_+")
        self.assertTrue(~VersionRange() is None)

        # odd (but valid) cases
        _eq(">", ">")       # greater than the empty version
        _eq("+", "")        # greater or equal to empty version (is all vers)
        _eq(">=", "")       # equivalent to above
        _eq("<=", "==")     # less or equal to empty version (is only empty)
        _eq("..", "==")     # from empty version to empty version
        _eq("+<=", "==")    # equivalent to above

        invalid_range = [
            "4+<2",         # lower bound greater than upper
            ">3<3",         # both greater and less than same version
            ">3<=3",        # greater and less or equal to same version
            "3+<3"          # greater and equal to, and less than, same version
        ]

        for s in invalid_range:
            self.assertRaises(VersionError, VersionRange, s)

        invalid_syntax = [
            "<",            # less than the empty version
            "><",           # both greater and less than empty version
            ">3>4",         # both are lower bounds
            "<3<4",         # both are upper bounds
            "<4>3",         # upper bound before lower without comma
            ",<4",          # leading comma
            "4+,",          # trailing comma
            "1>=",          # pre-lower-op in post
            "+1",           # post-lower-op in pre
            "4<",           # pre-upper-op in post
            "1+<2<3"        # more than two bounds
        ]

        for s in invalid_syntax:
            self.assertRaises(VersionError, VersionRange, s)

        # test simple logic
        self.assertTrue(VersionRange("").is_any())
        self.assertTrue(VersionRange("2+<4").bounded())
        self.assertTrue(VersionRange("2+").lower_bounded())
        self.assertTrue(not VersionRange("2+").upper_bounded())
        self.assertTrue(not VersionRange("2+").bounded())
        self.assertTrue(VersionRange("<2").upper_bounded())
        self.assertTrue(not VersionRange("<2").lower_bounded())
        self.assertTrue(not VersionRange("<2").bounded())

        # test range from version(s)
        v = Version("3")
        self.assertTrue(VersionRange.from_version(v, "eq") == VersionRange("==3"))
        self.assertTrue(VersionRange.from_version(v, "gt") == VersionRange(">3"))
        self.assertTrue(VersionRange.from_version(v, "gte") == VersionRange("3+"))
        self.assertTrue(VersionRange.from_version(v, "lt") == VersionRange("<3"))
        self.assertTrue(VersionRange.from_version(v, "lte") == VersionRange("<=3"))

        range1 = VersionRange.from_version(Version("2"), "gte")
        range2 = VersionRange.from_version(Version("4"), "lte")
        _eq(str(range1 & range2), "2..4")

        v2 = Version("6.0")
        v3 = Version("4")
        self.assertTrue(VersionRange.from_versions([v, v2, v3])
                        == VersionRange("==3|==4|==6.0"))

        # test behaviour in sets
        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)

        a = VersionRange("1+<=2.5")
        b = VersionRange("1..2.5")
        c = VersionRange(">=5")
        d = VersionRange(">6.1.0")
        e = VersionRange("3.2")

        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d, e]) - set([a]), set([c, d, e]))
        _eq2(set([b, c, e]) | set([c, d]), set([b, c, d, e]))
        _eq2(set([b, c]) & set([c, d]), set([c]))

    def test_containment(self):
        # basic containment
        self.assertTrue(Version("3") in VersionRange("3+"))
        self.assertTrue(Version("5") in VersionRange("3..5"))
        self.assertTrue(Version("5_") not in VersionRange("3..5"))
        self.assertTrue(Version("3.0.0") in VersionRange("3+"))
        self.assertTrue(Version("3.0.0") not in VersionRange("3.1+"))
        self.assertTrue(Version("3") in VersionRange("<1|5|6|8|7|3|60+"))
        self.assertTrue(Version("3") in VersionRange("<1|5|6|8|7|==3|60+"))
        self.assertTrue(VersionRange("2.1+<4") in VersionRange("<4"))
        self.assertTrue(VersionRange("2.1..4") not in VersionRange("<4"))
        self.assertTrue(VersionRange("3") in VersionRange("3"))
        self.assertTrue(VersionRange("==3") in VersionRange("3"))
        self.assertTrue(VersionRange("3.5+<3_") in VersionRange("3"))
        self.assertTrue(VersionRange("3") not in VersionRange("4+<6"))
        self.assertTrue(VersionRange("3+<10") not in VersionRange("4+<6"))

        # iterating over sorted version list
        numbers = [2, 3, 5, 10, 11, 13, 14]
        versions = [Version(str(x)) for x in numbers]
        rev_versions = list(reversed(versions))
        composite_range = VersionRange.from_versions(versions)

        entries = [(VersionRange(""), 7),
                   (VersionRange("0+"), 7),
                   (VersionRange("5+"), 5),
                   (VersionRange("6+"), 4),
                   (VersionRange("50+"), 0),
                   (VersionRange(">5"), 4),
                   (VersionRange("5"), 1),
                   (VersionRange("6"), 0),
                   (VersionRange("<5"), 2),
                   (VersionRange("<6"), 3),
                   (VersionRange("<50"), 7),
                   (VersionRange("<=5"), 3),
                   (VersionRange("<1"), 0),
                   (VersionRange("2|9+"), 5),
                   (VersionRange("3+<6|12+<13.5"), 3),
                   (VersionRange("<1|20+"), 0),
                   (VersionRange(">0<20"), 7)]

        for range_, count in entries:
            # brute-force containment tests
            matches = set(x for x in versions if x in range_)
            self.assertEqual(len(matches), count)

            # more optimal containment tests
            def _test_it(it):
                matches_ = set(version for contains, version in it if contains)
                self.assertEqual(matches_, matches)

            _test_it(range_.iter_intersect_test(versions))
            _test_it(range_.iter_intersect_test(rev_versions, descending=True))

            # throw in an intersection test
            self.assertEqual(composite_range.intersects(range_), (count != 0))
            int_range = composite_range & range_
            versions_ = [] if int_range is None else int_range.to_versions()
            self.assertEqual(set(versions_), matches)

            # throw in a superset test as well
            self.assertEqual(range_.issuperset(composite_range), (count == 7))
            if count:
                self.assertTrue(composite_range.issuperset(int_range))

    def test_requirement_list(self):
        def _eq(reqs, expected_reqs):
            _print("requirements(%s) == requirements(%s)"
                   % (' '.join(reqs), ' '.join(expected_reqs)))
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print("result: %s" % str(reqlist))

            exp_reqs_ = [Requirement(x) for x in expected_reqs]
            self.assertTrue(reqlist.requirements == exp_reqs_)

            exp_names = set(x.name for x in exp_reqs_ if not x.conflict)
            self.assertTrue(reqlist.names == exp_names)

            exp_confl_names = set(x.name for x in exp_reqs_ if x.conflict)
            self.assertTrue(reqlist.conflict_names == exp_confl_names)

        def _confl(reqs, a, b):
            _print("requirements(%s) == %s <--!--> %s" % (' '.join(reqs), a, b))
            reqs_ = [Requirement(x) for x in reqs]
            reqlist = RequirementList(reqs_)
            _print("result: %s" % str(reqlist))

            a_req = Requirement(a)
            b_req = Requirement(b)
            self.assertTrue(reqlist.conflict == (a_req, b_req))

        _print()
        _eq(["foo"],
            ["foo"])
        _eq(["foo", "bah"],
            ["foo", "bah"])
        _eq(["bah", "foo"],
            ["bah", "foo"])
        _eq(["foo-4+", "foo-4.5"],
            ["foo-4.5"])
        _eq(["bah-2.4", "foo", "bah-2.4.1+"],
            ["bah-2.4.1+<2.4_", "foo"])
        _eq(["foo-2+", "!foo-4+"],
            ["foo-2+<4"])
        _eq(["!bah-1", "!bah-3"],
            ["!bah-1|3"])
        _eq(["!bah-5", "foo-2.3", "!bah-5.6+"],
            ["!bah-5+", "foo-2.3"])
        _eq(["~bah-4", "foo", "bah<4.2"],
            ["bah-4+<4.2", "foo"])
        _eq(["~bah", "!foo", "bah<4.2"],
            ["bah<4.2", "!foo"])
        _eq(["~bah-3+", "~bah-5"],
            ["~bah-5"])

        _confl(["foo-1", "foo-2"],
               "foo-1", "foo-2")
        _confl(["foo-2", "foo-1"],
               "foo-2", "foo-1")
        _confl(["foo", "~bah-5+", "bah-2"],
               "~bah-5+", "bah-2")
        _confl(["foo", "~bah-5+", "bah-7..12", "bah-2"],
               "bah-7..12", "bah-2")


if __name__ == '__main__':
    unittest.main()
