From 00684d51abf632310b58e5ed56b0ed61d9eeeb3d Mon Sep 17 00:00:00 2001 From: Tim Harder Date: Thu, 4 Mar 2021 13:41:41 -0700 Subject: tests: move to repo root dir --- tests/restrictions/__init__.py | 0 tests/restrictions/test_boolean.py | 165 ++++++++++++++++ tests/restrictions/test_delegated.py | 40 ++++ tests/restrictions/test_packages.py | 198 +++++++++++++++++++ tests/restrictions/test_restriction.py | 100 ++++++++++ tests/restrictions/test_util.py | 32 ++++ tests/restrictions/test_values.py | 334 +++++++++++++++++++++++++++++++++ 7 files changed, 869 insertions(+) create mode 100644 tests/restrictions/__init__.py create mode 100644 tests/restrictions/test_boolean.py create mode 100644 tests/restrictions/test_delegated.py create mode 100644 tests/restrictions/test_packages.py create mode 100644 tests/restrictions/test_restriction.py create mode 100644 tests/restrictions/test_util.py create mode 100644 tests/restrictions/test_values.py (limited to 'tests/restrictions') diff --git a/tests/restrictions/__init__.py b/tests/restrictions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/restrictions/test_boolean.py b/tests/restrictions/test_boolean.py new file mode 100644 index 00000000..5f998f04 --- /dev/null +++ b/tests/restrictions/test_boolean.py @@ -0,0 +1,165 @@ +from snakeoil.test import TestCase + +from pkgcore.restrictions import boolean, restriction + +true = restriction.AlwaysBool(node_type='foo', negate=True) +false = restriction.AlwaysBool(node_type='foo', negate=False) + + +class AlwaysForcableBool(boolean.base): + + __slots__ = () + + def force_True(self, action, *args): + yield True + + match = force_False = force_True + + +class base: + + kls = None + + def test_invalid_restrictions(self): + self.assertRaises(TypeError, self.kls, 42, node_type='foo') + base = self.kls(node_type='foo') + self.assertRaises(TypeError, base.add_restriction, 42) + self.assertRaises(TypeError, base.add_restriction) + + def test_init_finalize(self): + final = self.kls(true, node_type='foo', finalize=True) + # since it becomes a tuple, throws a AttributeError + self.assertRaises(TypeError, final.add_restriction, false) + + final = self.kls(true, node_type='foo') + # since it becomes a tuple, throws a AttributeError + self.assertRaises(TypeError, final.add_restriction, false) + + def test_finalize(self): + base = self.kls(true, node_type='foo', finalize=False) + base.add_restriction(false) + base.finalize() + self.assertRaises(TypeError, base.add_restriction, true) + + def test_change_restrictions(self): + base = self.kls(true, false) + assert self.kls(false, true) == base.change_restrictions(false, true) + assert self.kls(false, true) != base.change_restrictions(false, true, negate=True) + assert self.kls(false, true, negate=True) == base.change_restrictions(false, true, negate=True) + + def test_add_restriction(self): + self.assertRaises(TypeError, + self.kls(true, finalize=True).add_restriction, false) + self.assertRaises(TypeError, + self.kls(node_type='foon').add_restriction, false) + k = self.kls(finalize=False) + k.add_restriction(false) + assert k.restrictions == [false] + + # TODO total_len? what does it do? + +class BaseTest(base, TestCase): + + kls = boolean.base + + def test_base(self): + base = self.kls(true, false, node_type='foo') + assert len(base) == 2 + assert list(base) == [true, false] + self.assertRaises(NotImplementedError, base.match, false) + # TODO is the signature for these correct? + self.assertRaises(NotImplementedError, base.force_False, false) + self.assertRaises(NotImplementedError, base.force_True, false) + self.assertIdentical(base[1], false) + + +# TODO these tests are way too limited +class AndRestrictionTest(base, TestCase): + + kls = boolean.AndRestriction + + def test_match(self): + assert self.kls(true, true, node_type='foo').match(None) + assert not self.kls(false, true, true, node_type='foo').match(None) + assert not self.kls(true, false, true, node_type='foo').match(None) + + def test_negate_match(self): + assert self.kls(false, true, node_type='foo', negate=True).match(None) + assert self.kls(true, false, node_type='foo', negate=True).match(None) + assert self.kls(false, false, node_type='foo', negate=True).match(None) + assert not self.kls(true, true, node_type='foo', negate=True).match(None) + + def test_dnf_solutions(self): + assert self.kls(true, true).dnf_solutions() == [[true, true]] + assert self.kls(self.kls(true, true), true).dnf_solutions() == [[true, true, true]] + assert (list(map(set, self.kls( + true, true, + boolean.OrRestriction(false, true)).dnf_solutions())) == + [set([true, true, false]), set([true, true, true])]) + assert self.kls().dnf_solutions() == [[]] + + def test_cnf_solutions(self): + assert self.kls(true, true).cnf_solutions() == [[true], [true]] + assert self.kls(self.kls(true, true), true).cnf_solutions() == [[true], [true], [true]] + assert (list(self.kls( + true, true, + boolean.OrRestriction(false, true)).cnf_solutions()) == + list([[true], [true], [false, true]])) + assert self.kls().cnf_solutions() == [] + + +class OrRestrictionTest(base, TestCase): + + kls = boolean.OrRestriction + + def test_match(self): + assert self.kls(true, true, node_type='foo').match(None) + assert self.kls(false, true, false, node_type='foo').match(None) + assert self.kls(true, false, false, node_type='foo').match(None) + assert self.kls(false, false, true, node_type='foo').match(None) + assert not self.kls(false, false, node_type='foo').match(None) + + def test_negate_match(self): + for x in ((true, false), (false, true), (true, true)): + assert not self.kls(node_type='foo', negate=True, *x).match(None) + assert self.kls(false, false, node_type='foo', negate=True).match(None) + + def test_dnf_solutions(self): + assert self.kls(true, true).dnf_solutions() == [[true], [true]] + assert (list(map(set, self.kls( + true, true, + boolean.AndRestriction(false, true)).dnf_solutions())) == + list(map(set, [[true], [true], [false, true]]))) + assert self.kls(self.kls(true, false), true).dnf_solutions() == [[true], [false], [true]] + assert self.kls().dnf_solutions() == [[]] + + def test_cnf_solutions(self): + assert self.kls(true, true).cnf_solutions() == [[true, true]] + assert ([set(x) for x in self.kls( + true, true, + boolean.AndRestriction(false, true)).cnf_solutions()] == + [set(x) for x in [[true, false], [true, true]]]) + + assert ([set(x) for x in self.kls(self.kls( + true, true, + boolean.AndRestriction(false, true))).cnf_solutions()] == + [set(x) for x in [[true, false], [true, true]]]) + + assert (set(self.kls( + self.kls(true, false), + true).cnf_solutions()[0]) == + set([true, false, true])) + + assert self.kls().cnf_solutions() == [] + + +class JustOneRestrictionTest(base, TestCase): + + kls = boolean.JustOneRestriction + + def test_match(self): + assert self.kls(true, false, node_type='foo').match(None) + assert self.kls(false, true, false, node_type='foo').match(None) + assert not self.kls(false, false, node_type='foo').match(None) + assert not self.kls(true, false, true, node_type='foo').match(None) + assert not self.kls(true, true, true, node_type='foo').match(None) diff --git a/tests/restrictions/test_delegated.py b/tests/restrictions/test_delegated.py new file mode 100644 index 00000000..e392707e --- /dev/null +++ b/tests/restrictions/test_delegated.py @@ -0,0 +1,40 @@ +from pkgcore.restrictions.delegated import delegate +from pkgcore.test import TestRestriction + + +class Test_delegate(TestRestriction): + + kls = delegate + + def test_it(self): + self.assertRaises(TypeError, self.kls, None, None) + y = True + l = [] + def f(x, mode): + l.append(mode) + if mode == 'force_False': + return not y + return y + + for negated in (False, True): + def assertIt(got, expected): + self.assertEqual( + got, expected, + msg=f"got={got!r}, expected={expected!r}, negate={negated!r}") + y = True + l[:] = [] + o = self.kls(f, negate=negated) + self.assertMatches(o, [None], negated=negated) + + y = False + self.assertNotMatches(o, [None], negated=negated) + + if negated: + assertIt(l, ['match', 'force_False', 'force_True', + 'match', 'force_False', 'force_True']) + else: + assertIt(l, ['match', 'force_True', 'force_False', + 'match', 'force_True', 'force_False']) + + def test_caching(self): + self.assertFalse(self.kls.inst_caching, False) diff --git a/tests/restrictions/test_packages.py b/tests/restrictions/test_packages.py new file mode 100644 index 00000000..b716073e --- /dev/null +++ b/tests/restrictions/test_packages.py @@ -0,0 +1,198 @@ +from snakeoil.mappings import AttrAccessible + +from pkgcore import log +from pkgcore.restrictions import packages, values +from pkgcore.test import (TestCase, TestRestriction, callback_logger, + malleable_obj, silence_logging) + + +class AlwaysSelfIntersect(values.base): + def intersect(self, other): + return self + + __hash__ = object.__hash__ + + +class TestPackageRestriction(TestRestriction): + + if packages.PackageRestriction is packages.PackageRestriction: + kls = packages.PackageRestriction + else: + class kls(packages.PackageRestriction, + packages.PackageRestriction_mixin): + __slots__ = () + __inst_caching__ = packages.PackageRestriction.__inst_caching__ + + kls = staticmethod(kls) + + @silence_logging(log.logging.root) + def test_matching(self): + strexact = values.StrExactMatch + + args = malleable_obj(category="foon", package="dar") + self.assertMatches(self.kls("category", strexact("foon")), args) + self.assertMatches(self.kls("package", strexact("dar")), args) + self.assertNotMatches(self.kls("package", strexact("dar"), negate=True), + args) + self.assertNotMatches(self.kls("package", strexact("foon")), args) + + self.assertMatches(self.kls("package", strexact("foon"), negate=True), + args) + excepts = [] + # no msg should be thrown, it wasn't an unexpected exception + + log.logging.root.addHandler(callback_logger(excepts.append)) + self.assertNotMatches(self.kls("foon", AlwaysSelfIntersect), args) + self.assertFalse(excepts) + + self.assertMatches(self.kls("foon", AlwaysSelfIntersect, negate=True), + args) + self.assertFalse(excepts) + + class foo: + def __getattr__(self, attr): + if attr.startswith("exc"): + raise exceptions_d.get(attr[4:], None)() + raise AttributeError("monkey lover") + + exceptions_d = {"KeyboardInterrupt":KeyboardInterrupt, + "RuntimeError":RuntimeError, "SystemExit":SystemExit} + + for mode in ("match", "force_True", "force_False"): + excepts[:] = [] + self.assertRaises(AttributeError, + getattr(self.kls("foon", AlwaysSelfIntersect), mode), + foo()) + self.assertEqual( + len(excepts), 1, + msg=f"expected one exception, got {excepts!r}") + + # ensure various exceptions are passed through + for k in (KeyboardInterrupt, RuntimeError, SystemExit): + self.assertRaises( + k, + getattr(self.kls(f"exc_{k.__name__}", AlwaysSelfIntersect), mode), + foo()) + + # check that it only does string comparison in exception catching. + class foo: + def __cmp__(self, other): + raise TypeError + + def __getattr__(self, attr): + raise AttributeError(self, attr) + + self.assertFalse(self.kls("foon", AlwaysSelfIntersect).match(foo())) + + def test_attr(self): + self.assertEqual(self.kls('val', values.AlwaysTrue).attr, + 'val') + self.assertEqual(self.kls('val.dar', values.AlwaysTrue).attr, + 'val.dar') + self.assertEqual(self.kls('val', values.AlwaysTrue).attrs, + ('val',)) + self.assertEqual(self.kls('val.dar', values.AlwaysTrue).attrs, + ('val.dar',)) + + def test_eq(self): + self.assertEqual( + self.kls('one', values.AlwaysTrue), + self.kls('one', values.AlwaysTrue)) + self.assertNotEqual( + self.kls('one', values.AlwaysTrue), + self.kls('one', values.AlwaysTrue, negate=True)) + self.assertNotEqual( + self.kls('one', values.AlwaysTrue), + self.kls('two', values.AlwaysTrue)) + self.assertNotEqual( + self.kls('one', values.AlwaysTrue, negate=True), + self.kls('one', values.AlwaysFalse, negate=True)) + + def test_hash(self): + inst = self.kls('one.dar', AlwaysSelfIntersect()) + hash(inst) + + +class values_callback(values.base): + + __slots__ = ("callback",) + + def __init__(self, callback): + object.__setattr__(self, 'callback', callback) + + def match(self, val): + return self.callback((None, val)) + + def force_True(self, pkg, attr, val): + return self.callback((True, pkg, attr, val)) + + def force_False(self, pkg, attr, val): + return self.callback((False, pkg, attr, val)) + + +class TestPackageRestrictionMulti(TestCase): + + if packages.PackageRestriction is packages.PackageRestriction: + kls = packages.PackageRestrictionMulti + else: + class kls(packages.PackageRestrictionMulti, + packages.PackageRestrictionMulti_mixin): + __slots__ = () + __inst_caching__ = packages.PackageRestrictionMulti.__inst_caching__ + + kls = staticmethod(kls) + + def test_attr(self): + o = self.kls(("asdf.far", "repo"), values.AlwaysTrue) + self.assertEqual(o.attrs, ("asdf.far", "repo")) + self.assertEqual(o.attr, None) + + def test_values(self): + l = [] + def f(*args): + self.assertLen(args, 1) + l.append(args[0]) + return True + + o = self.kls(("asdf.far", "repo"), values_callback(f)) + + pkg = AttrAccessible() + o.match(pkg) + self.assertFalse(l) + + pkg['repo'] = 1 + o.match(pkg) + self.assertFalse(l) + + pkg['asdf'] = AttrAccessible(far=2) + o.match(pkg) + self.assertEqual(l, [(None, [2,1],)]) + + l[:] = [] + o.force_True(pkg) + self.assertEqual(l, [(True, pkg, ('asdf.far', 'repo'), [2,1],)]) + + l[:] = [] + o.force_False(pkg) + self.assertEqual(l, [(False, pkg, ('asdf.far', 'repo'), [2,1],)]) + + +class ConditionalTest(TestCase): + + def test_eq(self): + p = (packages.PackageRestriction('one', values.AlwaysTrue),) + p2 = (packages.PackageRestriction('one', values.AlwaysFalse),) + v = values.AlwaysTrue + v2 = values.AlwaysFalse + self.assertEqual( + packages.Conditional('use', v, p), + packages.Conditional('use', v, p)) + self.assertNotEqual( + packages.Conditional('use', v2, p), + packages.Conditional('use', v, p)) + self.assertNotEqual( + packages.Conditional('use', v, p), + packages.Conditional('use', v, p2)) + self.assertNotEqual( + packages.Conditional('use1', v, p), + packages.Conditional('use', v, p)) diff --git a/tests/restrictions/test_restriction.py b/tests/restrictions/test_restriction.py new file mode 100644 index 00000000..03305bc7 --- /dev/null +++ b/tests/restrictions/test_restriction.py @@ -0,0 +1,100 @@ +from functools import partial + +from pkgcore.restrictions import restriction +from pkgcore.test import TestRestriction + + +class SillyBool(restriction.base): + """Extra stupid version of AlwaysBool to test base.force_{True,False}.""" + + __slots__ = ('negate',) + + def __init__(self, negate=False): + object.__setattr__(self, 'negate', negate) + + def match(self, *args, **kwargs): + return not self.negate + + +class BaseTest(TestRestriction): + + bool_kls = SillyBool + + def test_base(self): + base = restriction.base() + self.assertEqual(len(base), 1) + # Just check repr and str do not raise + self.assertTrue(str(base)) + self.assertTrue(repr(base)) + self.assertTrue(hash(base)) + self.assertRaises(NotImplementedError, base.match) + + def test_it(self): + true = self.bool_kls(negate=False) + false = self.bool_kls(negate=True) + args = [None] + + self.assertMatch(true, args[0]) + self.assertForceTrue(true, args) + self.assertNotForceFalse(true, args) + + self.assertNotMatch(false, args[0]) + self.assertNotForceTrue(false, args) + self.assertForceFalse(false, args) + + +class AlwaysBoolTest(TestRestriction): + + bool_kls = partial(restriction.AlwaysBool, 'foo') + + def test_true(self): + true_r = self.bool_kls(True) + false_r = self.bool_kls(False) + self.assertMatch(true_r, false_r) + self.assertForceTrue(true_r, false_r) + self.assertNotForceFalse(true_r, false_r) + + self.assertNotMatch(false_r, true_r) + self.assertNotForceTrue(false_r, true_r) + self.assertForceFalse(false_r, true_r) + + self.assertEqual(str(true_r), "always 'True'") + self.assertEqual(str(false_r), "always 'False'") + self.assertNotEqual(hash(true_r), hash(false_r)) + self.assertEqual(hash(true_r), + hash(self.bool_kls(True))) + self.assertEqual(hash(false_r), + hash(self.bool_kls(False))) + self.assertEqual(true_r, self.bool_kls(True)) + self.assertEqual(false_r, self.bool_kls(False)) + self.assertNotEqual(true_r, false_r) + + +class NoneMatch(restriction.base): + + """Only matches None.""" + + __slots__ = () + + def match(self, val): + return val is None + + def __repr__(self): + return '' + + def __str__(self): + return 'NoneMatch' + + +class AnyMatchTest(TestRestriction): + + def test_basic(self): + for negate in (False, True): + inst = restriction.AnyMatch(NoneMatch(), 'spork', negate=negate) + self.assertMatch(inst, ['spork', None], negated=negate) + self.assertNotMatch(inst, ['spork'], negated=negate) + self.assertNotMatch(inst, (), negated=negate) + + # just test these do not traceback + self.assertTrue(repr(inst)) + self.assertTrue(str(inst)) diff --git a/tests/restrictions/test_util.py b/tests/restrictions/test_util.py new file mode 100644 index 00000000..5e125d7e --- /dev/null +++ b/tests/restrictions/test_util.py @@ -0,0 +1,32 @@ +from snakeoil.test import TestCase + +from pkgcore.restrictions import packages, util, values + + +class Test_collect_package_restrictions(TestCase): + + def test_collect_all(self): + prs = [packages.PackageRestriction("category", values.AlwaysTrue)] * 10 + self.assertEqual( + list(util.collect_package_restrictions(packages.AndRestriction( + packages.OrRestriction(), packages.AndRestriction(), + *prs))), + prs) + + def test_collect_specific(self): + prs = {} + for x in ("category", "package", "version", "iuse"): + prs[x] = packages.PackageRestriction(x, values.AlwaysTrue) + + r = packages.AndRestriction( + packages.OrRestriction(*prs.values()), packages.AlwaysTrue) + for k, v in prs.items(): + self.assertEqual( + list(util.collect_package_restrictions(r, attrs=[k])), + [v]) + r = packages.AndRestriction(packages.OrRestriction( + *prs.values()), *prs.values()) + for k, v in prs.items(): + self.assertEqual( + list(util.collect_package_restrictions(r, attrs=[k])), + [v] * 2) diff --git a/tests/restrictions/test_values.py b/tests/restrictions/test_values.py new file mode 100644 index 00000000..2571f45d --- /dev/null +++ b/tests/restrictions/test_values.py @@ -0,0 +1,334 @@ +from snakeoil.test import TestCase + +from pkgcore.restrictions import values +from pkgcore.test import TestRestriction + + +class SillyBool(values.base): + """Extra stupid version of AlwaysBool to test base.force_{True,False}.""" + def __init__(self, negate=False): + object.__setattr__(self, "negate", negate) + + def match(self, something): + return not self.negate + + +class BaseTest(TestRestriction): + + def test_force(self): + self.assertMatches(SillyBool(negate=False), None, [None]*3) + self.assertNotMatches(SillyBool(negate=True), None, [None]*3) + + +class GetAttrTest(TestRestriction): + + """Test bits of GetAttrRestriction that differ from PackageRestriction.""" + + def test_force(self): + # TODO we could do with both more tests and a more powerful + # force_* implementation. This just tests if the function + # takes the right number of arguments. + + # TODO test negate handling + succeeds = values.GetAttrRestriction('test', values.AlwaysTrue) + fails = values.GetAttrRestriction('test', values.AlwaysFalse) + + class Dummy: + test = True + + dummy = Dummy() + + class FakePackage: + """XXX this is vastly too minimal.""" + value = dummy + + pkg = FakePackage() + + args = [pkg, 'value', dummy] + self.assertForceTrue(succeeds, args) + self.assertNotForceFalse(succeeds, args) + self.assertNotForceTrue(fails, args) + self.assertForceFalse(fails, args) + + +class StrRegexTest(TestRestriction): + + kls = values.StrRegex + + def test_match(self): + for negated in (False, True): + self.assertMatches(self.kls('foo.*r', match=True, + negate=negated), + 'foobar', [None, None, 'foobar'], negated=negated) + self.assertNotMatches(self.kls('foo.*r', match=True, + negate=negated), + 'afoobar', [None, None, 'afoobar'], negated=negated) + + def test_search(self): + for negated in (False, True): + self.assertMatches(self.kls('foo.*r', negate=negated), + 'asdfoobar', [None, None, 'asdfoobar'], negated=negated) + self.assertNotMatches(self.kls('foo.*r', negate=negated), + 'afobar', [None, None, 'afobar'], negated=negated) + + def test_case_sensitivity(self): + self.assertNotMatches(self.kls('foo'), 'FoO', ['FOo']*3) + self.assertMatches(self.kls('foo', False), 'FoO', ['fOo']*3) + + def test_str(self): + assert 'search spork' == str(self.kls('spork')) + assert 'not search spork' == str(self.kls('spork', negate=True)) + assert 'match spork' == str(self.kls('spork', match=True)) + assert 'not match spork' == str(self.kls('spork', match=True, negate=True)) + + def test_repr(self): + for restr, string in [ + (self.kls('spork'), "