diff --git a/conf/packages/packages.server.dep_rewrite.example b/conf/packages/packages.server.dep_rewrite.example index 52b43b6c6..d2a6ed435 100644 --- a/conf/packages/packages.server.dep_rewrite.example +++ b/conf/packages/packages.server.dep_rewrite.example @@ -10,6 +10,8 @@ # AND or OR operators: ( app-foo/bar & foo-app/rab ) | app-nice/woot # Please take this into consideration when writing dep rewrites. +# 1. Regex-based dependency rewrite syntax. + # LINE CONSTRUCTION: # <::pkg::> <::dep_pattern::> <::dep_replace_1::> [<::dep_replace_2::> ...] # ::pkg:: = package containing dependency to match, or * for catch-all @@ -59,3 +61,25 @@ # Apply a dep_rewrite rule to any package being added: # * <::dep_pattern::> <::dep_replace_1::> [<::dep_replace_2::> ...] # In other words, set <::pkg::> to * + + +# 2. Token-based dependency rewrite syntax. + +# This syntax supports only modification to dependencies, not addition nor removal. +# rewrite <::pkg::> <::rule::> +# where <::pkg::> is matched the same was as explained above, +# and <::rule::> is: +# +# [] [] +# (order does not matter). +# +# To a dependency to be rewritten, ::dep_atom:: must match and ::use_to_match:: if present. +# Dependency will be rewritten as ::new_dep_atom::. +# If present in rule, ::use_to_remove:: will be removed if dependency contains it. +# +# ::dep_atom:: and ::new_dep_atom:: are category/package names, without version, slot etc. +# ::use_to_remove:: can be an asterisk (*) which means to remove all USE flags. +# +# EXAMPLE +# rewrite foo/bar from-dep=app-crypt/pinentry to-dep=app-crypt/pinentry-gtk2 if-dep-has-use=gtk drop-use=gtk +# will change foo/bar's dependency "app-crypt/pinentry[gtk,qt5]" to "app-crypt/pinentry-gtk2[qt5]". diff --git a/lib/entropy/dep.py b/lib/entropy/dep.py index fbbe1108e..1f6b5684f 100644 --- a/lib/entropy/dep.py +++ b/lib/entropy/dep.py @@ -3,8 +3,9 @@ """ @author: Fabio Erculiani + @author: Slawomir Nizio @contact: lxnay@sabayon.org - @copyright: Fabio Erculiani + @copyright: Fabio Erculiani, Slawomir Nizio @license: GPL-2 B{Entropy dependency functions module}. @@ -1241,3 +1242,140 @@ def expand_dependencies(dependencies, entropy_repository_list, pkg_deps.append((dep, dep_type)) return pkg_deps + +class WrongRewriteRuleError(Exception): + """ + Exception thrown when the new dep_rewrite mechanism encounters a badly + formatted rule. + """ + pass + + +class DependencyRewriter(object): + """ + Class that implements the new dependency rewrite schema. + It is simpler to use but less powerful than the regex based one, so the two + are complementary. + """ + _use_in_depstring_re = re.compile(r"\[([^\]]+)\]") + + def __init__(self, deps, rule): + self.deps = deps + self._rule = rule + self._rule_dict = self._get_rewrite_rules_dict(rule) + self.matched = None + self.changed = None + + self._dep_has_use, self._modify_use = self._setup_default_funcs() + + + @staticmethod + def _get_rewrite_rules_dict(rule): + known_rule_keys = set(["if-dep-has-use", "from-dep", "to-dep", "drop-use"]) + mandatory_keys = set(["from-dep", "to-dep"]) + + rule_dict = {} + for rule_word in rule.split(): + try: + op, arg = rule_word.split("=", 1) + except ValueError: + raise WrongRewriteRuleError("Badly formatted line with '%s'" % (rule_word,)) + rule_dict[op] = arg + + if op not in known_rule_keys: + raise WrongRewriteRuleError("Bad key '%s'" % (op,)) + + missing_keys = mandatory_keys - set(rule_dict.keys()) + if missing_keys: + raise WrongRewriteRuleError("Incomplete rewrite rule") + + return rule_dict + + + def _setup_default_funcs(self): + if 'if-dep-has-use' in self._rule_dict: + dep_has_use_func = lambda deps_: self._rule_dict['if-dep-has-use'] in deps_ + else: + dep_has_use_func = lambda _: True + + if 'drop-use' in self._rule_dict: + if self._rule_dict['drop-use'] == "*": + modify_use_func = lambda dep_postfix, use_flags_list, clean_use_flags_list: \ + DependencyRewriter._use_in_depstring_re.sub( + "", + dep_postfix, + count=1) + else: + modify_use_func = lambda dep_postfix, use_flags_list, clean_use_flags_list: \ + DependencyRewriter._use_in_depstring_re.sub( + self._filter_use(use_flags_list, clean_use_flags_list, self._rule_dict['drop-use']), + dep_postfix, + count=1) + else: + modify_use_func = lambda dep_postfix, _1, _2: dep_postfix + + return dep_has_use_func, modify_use_func + + + @staticmethod + def _filter_use(use_list, clean_use_list, use_to_remove): + filtered_use = [] + for orig_use, clean_use in zip(use_list, clean_use_list): + if clean_use != use_to_remove: + filtered_use.append(orig_use) + + if filtered_use: + result = "[%s]" % (",".join(filtered_use),) + else: + result = "" + + return result + + + def rewrite(self): + """ + Do a rewrite of dependencies and set attributes: self.deps (rewritten + dependencies), self.matched (any dependency matched its prerequisites) + and self.changed (at least one dependency was modifed). + + Value of these attributes is not defined before executing this method. + """ + matched = False + changed = False + + new_deps = [] + for dep in self.deps: + clean_dep = dep_getkey(dep) + dep_prefix, dep_postfix = dep.split(clean_dep, 1) + + use_flags_list, clean_use_flags_list = self._use_flags_from_dep(dep_postfix) + + if self._rule_dict['from-dep'] == clean_dep and self._dep_has_use(clean_use_flags_list): + new_dep = dep_prefix + self._rule_dict['to-dep'] + self._modify_use(dep_postfix, use_flags_list, clean_use_flags_list) + new_deps.append(new_dep) + matched = True + if dep != new_dep: + changed = True + else: + new_deps.append(dep) + + self.matched = matched + self.changed = changed + self.deps = new_deps + + + @staticmethod + def _use_flags_from_dep(depstring): + dep_use_match = DependencyRewriter._use_in_depstring_re.search(depstring) + usestring = dep_use_match.group(1) if dep_use_match else "" + dep_use = usestring.split(",") + clean_dep_use = [] + use_postfixes_to_strip = ("?", "(-)", "(+)") + for raw_dep in dep_use: + for use_postfix_to_strip in use_postfixes_to_strip: + if raw_dep.endswith(use_postfix_to_strip): + raw_dep = raw_dep[:-len(use_postfix_to_strip)] + clean_dep_use += [raw_dep] + + assert len(dep_use) == len(clean_dep_use) + return dep_use, clean_dep_use diff --git a/lib/entropy/server/interfaces/main.py b/lib/entropy/server/interfaces/main.py index 28fb49807..9633d9d7d 100644 --- a/lib/entropy/server/interfaces/main.py +++ b/lib/entropy/server/interfaces/main.py @@ -2,8 +2,9 @@ """ @author: Fabio Erculiani + @author: Slawomir Nizio @contact: lxnay@sabayon.org - @copyright: Fabio Erculiani + @copyright: Fabio Erculiani, Slawomir Nizio @license: GPL-2 B{Entropy Package Manager Server Main Interfaces}. @@ -984,16 +985,83 @@ class ServerSystemSettingsPlugin(SystemSettingsPlugin): if cached is not None: return cached - data = {} + data = [] rewrite_file = Server._get_conf_dep_rewrite_file() if not os.path.isfile(rewrite_file): return data rewrite_content = self.__generic_parser(rewrite_file) + def add_dep_handler(dep_pattern): + metadata = {'dep_pattern_str': dep_pattern, + 'replaces_str': _("added"), + 'action': "add", + 'add_what': dep_pattern} + return metadata + + def remove_dep_handler(dep_pattern, compiled_pattern): + metadata = {'dep_pattern_str': dep_pattern, + 'replaces_str': _("removed"), + 'action': "remove", + 'does_dep_match_func': lambda dep_string: compiled_pattern.match(dep_string) is not None, + 'replaces': []} + return metadata + + def replace_dep_handler(dep_pattern, compiled_pattern, replaces): + def _do(replace, dep_string): + new_dep_string, number_of_subs_made = \ + compiled_pattern.subn(replace, dep_string) + return new_dep_string, bool(number_of_subs_made) + + metadata = {'dep_pattern_str': dep_pattern, + 'replaces_str': "=> " + ', '.join(replaces), + 'action': "replace", + 'does_dep_match_func': lambda dep_string: compiled_pattern.match(dep_string) is not None, + 'replaces': replaces, + 'do_func': _do} + return metadata + + def new_replace_dep_handler(rule): + def _do(_unused, dep_string): + try: + # Should not actually happen as it's tested with does_dep_match_func. + rewriter = entropy.dep.DependencyRewriter([dep_string], rule) + except entropy.dep.WrongRewriteRuleError: + return dep_string, False + + rewriter.rewrite() + return rewriter.deps[0], rewriter.changed + + def _does_dep_match(dep_string): + try: + rewriter = entropy.dep.DependencyRewriter([dep_string], rule) + except entropy.dep.WrongRewriteRuleError: + return False + + rewriter.rewrite() + return rewriter.matched + + metadata = {'dep_pattern_str': "rewrite", + # not translated to match the keyword in configuration + 'replaces_str': "~> %s" % (rule,), + 'action': "replace", + 'does_dep_match_func': _does_dep_match, + 'replaces': [rule], + 'do_func': _do} + return metadata + for line in rewrite_content: params = line.split() if len(params) < 2: continue + + if params[0] == "rewrite": + if len(params) < 3: + continue + _unused, pkg_match, rule = line.split(None, 2) + metadata = new_replace_dep_handler(rule) + data.append((pkg_match, metadata)) + continue + pkg_match, pattern, replaces = params[0], params[1], params[2:] if pattern.startswith("++"): compiled_pattern = None @@ -1007,8 +1075,18 @@ class ServerSystemSettingsPlugin(SystemSettingsPlugin): except re.error: # invalid pattern continue + + if compiled_pattern is None: + # this means that user is asking to add dep_pattern + # as a dependency to package + metadata = add_dep_handler(pattern) + elif not replaces: + # this means that user is asking to remove dep_pattern + metadata = remove_dep_handler(pattern, compiled_pattern) + else: + metadata = replace_dep_handler(pattern, compiled_pattern, replaces) # use this key to make sure to not overwrite similar entries - data[(pkg_match, pattern)] = (compiled_pattern, replaces) + data.append((pkg_match, metadata)) self._mod_rewrite_data = data return data @@ -7357,7 +7435,7 @@ class Server(Client): rewrites_enabled = [] wildcard_rewrite = False - for dep_string_rewrite, dep_pattern in dep_rewrite: + for dep_string_rewrite, handler in dep_rewrite: # magic catch-all support if dep_string_rewrite == "*": pkg_id, rc = None, 0 @@ -7366,7 +7444,7 @@ class Server(Client): wildcard_rewrite = False pkg_id, rc = tmp_repo.atomMatch(dep_string_rewrite) if rc == 0: - rewrites_enabled.append((dep_string_rewrite, dep_pattern)) + rewrites_enabled.append((dep_string_rewrite, handler)) tmp_repo.close() if not rewrites_enabled: @@ -7384,22 +7462,11 @@ class Server(Client): level = "info", header = brown(" @@ ") ) - for dep_string_rewrite, dep_pattern in rewrites_enabled: - compiled_pattern, replaces = \ - dep_rewrite[(dep_string_rewrite, dep_pattern)] - if compiled_pattern is None: - # this means that user is asking to add dep_pattern - # as a dependency to package - replaces_str = _("added") - elif not replaces: - # this means that user is asking to remove dep_pattern - replaces_str = _("removed") - else: - replaces_str = "=> " + ', '.join(replaces) + for dep_string_rewrite, handler in rewrites_enabled: self.output( "%s %s" % ( - purple(dep_pattern), - replaces_str, + purple(handler['dep_pattern_str']), + handler['replaces_str'], ), importance = 1, level = "info", @@ -7457,17 +7524,19 @@ class Server(Client): for dep_string, dep_value, is_conflict in pkg_deps: - dep_string_matched = False + dep_changed = False matched_pattern = False - for key in rewrites_enabled: + for dep_string_rewrite, handler in rewrites_enabled: - dep_string_rewrite, dep_pattern = key - compiled_pattern, replaces = dep_rewrite[key] - if compiled_pattern is None: - # user is asking to add dep_pattern to dependency list + action = handler['action'] + + assert action in ("add", "remove", "replace") + + if action == "add": + # user is asking to add a dependency to dependency list dep_pattern_string, dep_pattern_type, conflict = \ - _extract_dep_add_from_dep_pattern(dep_pattern) + _extract_dep_add_from_dep_pattern(handler['add_what']) if conflict: conflicts.add(dep_pattern_string) else: @@ -7475,20 +7544,19 @@ class Server(Client): (dep_pattern_string, dep_pattern_type)) continue - if not compiled_pattern.match(dep_string): + if not handler['does_dep_match_func'](dep_string): # dep_string not matched, skipping continue matched_pattern = True - if not replaces: - # then it's a removal - dep_string_matched = True + if action == "remove": + dep_changed = True - for replace in replaces: - new_dep_string, number_of_subs_made = \ - compiled_pattern.subn(replace, dep_string) - if number_of_subs_made: - dep_string_matched = True + for replace in handler['replaces']: + new_dep_string, subst_made = \ + handler['do_func'](replace, dep_string) + if subst_made: + dep_changed = True if new_dep_string and (new_dep_string != "-"): if is_conflict: @@ -7529,13 +7597,13 @@ class Server(Client): header = darkred(" !!! ") ) - if dep_string_matched: + if dep_changed: if is_conflict: remove_conflicts.add(dep_string) else: remove_dependencies.add(dep_string) - elif (not dep_string_matched) and matched_pattern: + elif (not dep_changed) and matched_pattern: self.output( "%s: %s :: %s" % ( darkred(_("No dependency rewrite made for")), diff --git a/lib/tests/dep.py b/lib/tests/dep.py index bae8e12e1..f17894bd8 100644 --- a/lib/tests/dep.py +++ b/lib/tests/dep.py @@ -7,6 +7,7 @@ import unittest from entropy.const import const_convert_to_rawstring, const_convert_to_unicode from entropy.output import print_generic import tests._misc as _misc +import itertools import tempfile import subprocess import shutil @@ -285,6 +286,255 @@ class DepTest(unittest.TestCase): outcome = et.get_entropy_package_sha1(name) self.assertEqual(outcome, expected_outcome) + +class DepsRewriteTestsMixin: + def _validate(self): + rewriter = et.DependencyRewriter(self._deps, self._rule) + rewriter.rewrite() + self.assertEqual(rewriter.deps, self._expected_deps) + + +class MatchedChangedTestsMixin: + def _validate(self): + rewriter = et.DependencyRewriter(self._deps, self._rule) + rewriter.rewrite() + self.assertEqual(rewriter.matched, self._expected_matched) + self.assertEqual(rewriter.changed, self._expected_changed) + + +class DependencyRewriterTests(unittest.TestCase, DepsRewriteTestsMixin): + # Test for the new rewrite rules without regexes. + def test_dep_changed(self): + # inspired by: + # foo/bar (.*)app-text/poppler(.*)(\[.*\]) \1app-text/poppler-glib\2 + self._rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + self._deps = ["app-text/poppler"] + self._expected_deps = ["app-text/poppler-glib"] + self._validate() + + def _test_dep_changed_pkg_data_stays(self, dep_matches): + prefixes = ("", "=", ">=", "<", "~") + postfixes = ("", ":3", "[some]", "[some,use]", ":4[use]") + rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + + if dep_matches: + dep_base = "app-text/poppler" + expected_base = "app-text/poppler-glib" + else: + dep_base = "foobar" + expected_base = dep_base + + for prefix, postfix in itertools.product(prefixes, postfixes): + self._rule = rule + self._deps = [prefix + dep_base + postfix] + self._expected_deps = [prefix + expected_base + postfix] + self._validate() + + def test_dep_changed_pkg_data_stays(self): + self._test_dep_changed_pkg_data_stays(dep_matches=True) + + def test_dep_with_pkg_data_dep_not_matched(self): + self._test_dep_changed_pkg_data_stays(dep_matches=False) + + def test_dep_changed_version_stays(self): + self._rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + self._deps = ["=app-text/poppler-10"] + self._expected_deps = ["=app-text/poppler-glib-10"] + self._validate() + + def test_version_dep_not_matched(self): + self._rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + self._deps = ["=app-text/not-poppler-10"] + self._expected_deps = ["=app-text/not-poppler-10"] + self._validate() + + def test_version_and_data_matches(self): + self._rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + self._deps = ["=app-text/poppler-10:3[use]"] + self._expected_deps = ["=app-text/poppler-glib-10:3[use]"] + self._validate() + + def test_version_and_data_not_matches(self): + self._rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + self._deps = ["=app-text/not-poppler-10:3[use]"] + self._expected_deps = ["=app-text/not-poppler-10:3[use]"] + self._validate() + + def test_more_deps_one_changed(self): + self._rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + self._deps = ["d1", "app-text/poppler", "d2"] + self._expected_deps = ["d1", "app-text/poppler-glib", "d2"] + self._validate() + + def test_dep_not_changed_dep_not_matched(self): + self._rule = "from-dep=app-text/poppler-not to-dep=app-text/poppler-glib" + self._deps = ["app-text/poppler"] + self._expected_deps = ["app-text/poppler"] + self._validate() + + def test_more_deps_none_changed_dep_not_matched(self): + self._rule = "from-dep=app-text/poppler-not to-dep=app-text/poppler-glib" + self._deps = ["d1", "app-text/poppler", "d2"] + self._expected_deps = ["d1", "app-text/poppler", "d2"] + self._validate() + + def _test_with_if_has_use(self, dep_use, rule_use, dep_use_matches): + assert dep_use.startswith("[") or not dep_use + if dep_use_matches: + expected_dep_base = "x11-misc/lightdm-qt4" + else: + expected_dep_base = "x11-misc/lightdm" + + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt4 if-dep-has-use=%s" % (rule_use,) + self._deps = ["x11-misc/lightdm"+dep_use] + self._expected_deps = [expected_dep_base+dep_use] + self._validate() + + def test_with_if_has_use_matched(self): + self._test_with_if_has_use(dep_use="[qt4]", rule_use="qt4", dep_use_matches=True) + + def test_with_if_has_use_matched_complex(self): + use_list = ("[qt4,qt5]", "[qt4?,qt5]", "[qt4(-)]", "[qt4(+)]") + for use in use_list: + self._test_with_if_has_use(dep_use=use, rule_use="qt4", dep_use_matches=True) + + def test_with_if_has_use_not_matched_no_use(self): + self._test_with_if_has_use(dep_use="", rule_use="qt4", dep_use_matches=False) + + def test_with_if_has_use_not_matched_different_use(self): + self._test_with_if_has_use(dep_use="[not-qt4]", rule_use="qt4", dep_use_matches=False) + + def test_with_if_has_use_not_matched_different_use_complex(self): + self._test_with_if_has_use(dep_use="[qt4]", rule_use="not-qt4", dep_use_matches=False) + # There is no necessity to handle [-use(-)]. + self._test_with_if_has_use(dep_use="[-qt4]", rule_use="qt4", dep_use_matches=False) + use_list = ("[qt4,qt5]", "[qt4?,qt5]", "[qt4(-)]", "[qt4(+)]") + for use in use_list: + self._test_with_if_has_use(dep_use=use, rule_use="not-qt4", dep_use_matches=False) + + def test_with_if_has_use_and_drop_use(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt4 if-dep-has-use=qt4 drop-use=qt4" + self._deps = ["x11-misc/lightdm[qt4]"] + self._expected_deps = ["x11-misc/lightdm-qt4"] + self._validate() + + def test_with_if_has_use_and_drop_use_more_deps(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt4 if-dep-has-use=qt4 drop-use=qt4" + self._deps = ["x11-misc/lightdm[qt4,something-else]"] + self._expected_deps = ["x11-misc/lightdm-qt4[something-else]"] + self._validate() + + def test_with_if_has_use_and_drop_use_complex(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt4 if-dep-has-use=qt4 drop-use=qt4" + self._deps = ["x11-misc/lightdm[qt4(-),something-else(+)]"] + self._expected_deps = ["x11-misc/lightdm-qt4[something-else(+)]"] + self._validate() + + def test_with_if_has_use_and_drop_use_no_dropped_use_match(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt4 if-dep-has-use=qt4 drop-use=something" + self._deps = ["x11-misc/lightdm[qt4]"] + self._expected_deps = ["x11-misc/lightdm-qt4[qt4]"] + self._validate() + + def test_without_if_has_use_but_with_drop_use(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt5 drop-use=qt4" + self._deps = ["x11-misc/lightdm[qt4]"] + self._expected_deps = ["x11-misc/lightdm-qt5"] + self._validate() + + def test_with_drop_use_asterisk(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt5 drop-use=*" + self._deps = ["x11-misc/lightdm[qt4?,qt5]"] + self._expected_deps = ["x11-misc/lightdm-qt5"] + self._validate() + + def test_with_drop_use_asterisk_and_no_use_in_dep(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt5 drop-use=*" + self._deps = ["x11-misc/lightdm"] + self._expected_deps = ["x11-misc/lightdm-qt5"] + self._validate() + + def test_raises_on_unknown_key(self): + rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt4 haha=true" + deps = ["x11-misc/lightdm"] + + with self.assertRaisesRegexp(et.WrongRewriteRuleError, r"^Bad key 'haha'"): + et.DependencyRewriter(deps, rule) + + def test_raises_on_missing_key(self): + rule_parts = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt4".split() + deps = [] + for rule in rule_parts: + with self.assertRaisesRegexp(et.WrongRewriteRuleError, r"Incomplete rewrite rule"): + et.DependencyRewriter(deps, rule) + + def test_raises_on_key_with_no_assignment(self): + wrong_values = ("if-has-use", "drop-use", "haha") + for value in wrong_values: + rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt4 %s" % (value,) + deps = ["x11-misc/lightdm"] + with self.assertRaisesRegexp(et.WrongRewriteRuleError, + r"^Badly formatted line with '%s'" % (value,)): + et.DependencyRewriter(deps, rule) + + def test_with_empty_deps(self): + self._rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + self._deps = [] + self._expected_deps = [] + self._validate() + + +class DependencyRewriterBasicAttrsTests(unittest.TestCase, MatchedChangedTestsMixin): + def test_matched_and_changed(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt5 drop-use=qt4" + self._deps = ["x11-misc/lightdm[qt4]"] + self._expected_matched = True + self._expected_changed = True + self._validate() + + def test_not_matched_and_not_changed_dep_mismatch(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt5 drop-use=qt4" + self._deps = ["something-else/lightdm[qt4]"] + self._expected_matched = False + self._expected_changed = False + self._validate() + + def test_not_matched_and_not_changed_use_mismatch(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm-qt5 if-dep-has-use=qt4" + self._deps = ["x11-misc/lightdm[not-qt4]"] + self._expected_matched = False + self._expected_changed = False + self._validate() + + def test_matched_and_not_changed(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm if-dep-has-use=qt4" + self._deps = ["x11-misc/lightdm[qt4]"] + self._expected_matched = True + self._expected_changed = False + self._validate() + + def test_with_empty_deps(self): + self._rule = "from-dep=app-text/poppler to-dep=app-text/poppler-glib" + self._deps = [] + self._expected_matched = False + self._expected_changed = False + self._validate() + + def test_matched_and_changed_with_use_asterisk(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm drop-use=*" + self._deps = ["x11-misc/lightdm[gtk]"] + self._expected_matched = True + self._expected_changed = True + self._validate() + + def test_matched_and_not_changed_with_use_asterisk(self): + self._rule = "from-dep=x11-misc/lightdm to-dep=x11-misc/lightdm drop-use=*" + self._deps = ["x11-misc/lightdm"] + self._expected_matched = True + self._expected_changed = False + self._validate() + + if __name__ == '__main__': unittest.main() raise SystemExit(0)