diff --git a/britney.conf b/britney.conf index 6fbe035..e1e4105 100644 --- a/britney.conf +++ b/britney.conf @@ -94,3 +94,7 @@ ADT_SHARED_RESULTS_CACHE = ADT_SWIFT_URL = file:///srv/release.debian.org/britney/state/debci.json # Base URL for autopkgtest site, used for links in the excuses ADT_CI_URL = https://ci.debian.net/ + +# Autopkgtest results can be used to influence the aging +ADT_REGRESSION_PENALTY = 10 +ADT_SUCCESS_BOUNTY = 3 diff --git a/britney.py b/britney.py index 22980aa..0cc9703 100755 --- a/britney.py +++ b/britney.py @@ -518,11 +518,11 @@ class Britney(object): self.options.ignore_cruft == "0": self.options.ignore_cruft = False - self.policies.append(AgePolicy(self.options, self.suite_info, MINDAYS)) self.policies.append(RCBugPolicy(self.options, self.suite_info)) self.policies.append(PiupartsPolicy(self.options, self.suite_info)) if getattr(self.options, 'adt_enable') == 'yes': self.policies.append(AutopkgtestPolicy(self.options, self.suite_info)) + self.policies.append(AgePolicy(self.options, self.suite_info, MINDAYS)) for policy in self.policies: policy.register_hints(self._hint_parser) diff --git a/britney2/excuse.py b/britney2/excuse.py index 48b4c6c..2bb18df 100644 --- a/britney2/excuse.py +++ b/britney2/excuse.py @@ -88,6 +88,9 @@ class Excuse(object): self.old_binaries = defaultdict(set) self.policy_info = {} + self.bounty = {} + self.penalty = {} + def sortkey(self): if self.daysold == None: return (-1, self.name) @@ -294,3 +297,10 @@ class Excuse(object): excusedata["is-candidate"] = self.is_valid return excusedata + def add_bounty(self, policy, bounty): + """"adding bounty""" + self.bounty[policy] = bounty + + def add_penalty(self, policy, penalty): + """"adding penalty""" + self.penalty[policy] = penalty diff --git a/britney2/policies/autopkgtest.py b/britney2/policies/autopkgtest.py index 86f84bd..e8fdfae 100644 --- a/britney2/policies/autopkgtest.py +++ b/britney2/policies/autopkgtest.py @@ -268,6 +268,15 @@ class AutopkgtestPolicy(BasePolicy): verdict = PolicyVerdict.PASS_HINTED else: excuse.addreason('autopkgtest') + + if self.options.adt_success_bounty and verdict == PolicyVerdict.PASS: + excuse.add_bounty('autopkgtest', int(self.options.adt_success_bounty)) + if self.options.adt_regression_penalty and verdict == PolicyVerdict.REJECTED_PERMANENTLY: + excuse.add_penalty('autopkgtest', int(self.options.adt_regression_penalty)) + # In case we give penalties instead of blocking, we must pass in + # case of regression. + verdict = PolicyVerdict.PASS + return verdict # diff --git a/britney2/policies/policy.py b/britney2/policies/policy.py index 645a207..d088621 100644 --- a/britney2/policies/policy.py +++ b/britney2/policies/policy.py @@ -281,6 +281,12 @@ class AgePolicy(BasePolicy): days_old = self._date_now - self._dates[source_name][1] min_days = self._min_days[urgency] + for bounty in excuse.bounty.values(): + self.log('Applying bounty: %d days' % bounty) + min_days -= bounty + for penalty in excuse.penalty.values(): + self.log('Applying penalty: %d days' % penalty) + min_days += penalty age_info['age-requirement'] = min_days age_info['current-age'] = days_old diff --git a/tests/__init__.py b/tests/__init__.py index 7beb000..e0bef89 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -390,6 +390,9 @@ ADT_SHARED_RESULTS_CACHE = ADT_SWIFT_URL = http://localhost:18085 ADT_CI_URL = https://autopkgtest.ubuntu.com/ + +ADT_SUCCESS_BOUNTY = +ADT_REGRESSION_PENALTY = ''') assert os.path.exists(self.britney) diff --git a/tests/test_autopkgtest.py b/tests/test_autopkgtest.py index 6027820..2f96bba 100755 --- a/tests/test_autopkgtest.py +++ b/tests/test_autopkgtest.py @@ -2458,6 +2458,109 @@ class T(TestBase): # not expecting any failures to retrieve from swift self.assertNotIn('Failure', out, out) + def test_multi_rdepends_with_tests_mixed_penalty(self): + '''Bounty/penalty system instead of blocking + based on "Multiple reverse dependencies with tests (mixed results)"''' + + # Don't use policy verdics, but age packages appropriate + for line in fileinput.input(self.britney_conf, inplace=True): + if line.startswith('MINDAYS_MEDIUM'): + print('MINDAYS_MEDIUM = 13') + elif line.startswith('ADT_SUCCESS_BOUNTY'): + print('ADT_SUCCESS_BOUNTY = 6') + elif line.startswith('ADT_REGRESSION_PENALTY'): + print('ADT_REGRESSION_PENALTY = 27') + else: + sys.stdout.write(line) + + self.data.add_default_packages(green=False) + + # green has passed before on i386 only, therefore ALWAYSFAIL on amd64 + self.swift.set_results({'autopkgtest-testing': { + 'testing/i386/g/green/20150101_100000@': (0, 'green 1', tr('passedbefore/1')), + }}) + + # first run requests tests and marks them as pending + exc = self.do_test( + [('libgreen1', {'Version': '2', 'Source': 'green', 'Depends': 'libc6'}, 'autopkgtest')], + {'green': (False, {'green': {'amd64': 'RUNNING-ALWAYSFAIL', 'i386': 'RUNNING'}, + 'lightgreen': {'amd64': 'RUNNING-ALWAYSFAIL', 'i386': 'RUNNING-ALWAYSFAIL'}, + 'darkgreen': {'amd64': 'RUNNING-ALWAYSFAIL', 'i386': 'RUNNING-ALWAYSFAIL'}, + }) + }, + {'green': [('old-version', '1'), ('new-version', '2')]})[1] + + # while no autopkgtest results are known, default age applies + self.assertEqual(exc['green']['policy_info']['age']['age-requirement'], 13) + + # second run collects the results + self.swift.set_results({'autopkgtest-testing': { + 'testing/i386/d/darkgreen/20150101_100000@': (0, 'darkgreen 1', tr('green/2')), + 'testing/amd64/l/lightgreen/20150101_100100@': (0, 'lightgreen 1', tr('green/1')), + 'testing/amd64/l/lightgreen/20150101_100101@': (4, 'lightgreen 1', tr('green/2')), + 'testing/i386/g/green/20150101_100200@': (0, 'green 2', tr('green/2')), + 'testing/amd64/g/green/20150101_100201@': (4, 'green 2', tr('green/2')), + # unrelated results (wrong trigger), ignore this! + 'testing/amd64/d/darkgreen/20150101_100000@': (0, 'darkgreen 1', tr('green/1')), + 'testing/i386/l/lightgreen/20150101_100100@': (0, 'lightgreen 1', tr('blue/1')), + }}) + + res = self.do_test( + [], + {'green': (False, {'green/2': {'amd64': 'ALWAYSFAIL', 'i386': 'PASS'}, + 'lightgreen/1': {'amd64': 'REGRESSION', 'i386': 'RUNNING'}, + 'darkgreen/1': {'amd64': 'RUNNING', 'i386': 'PASS'}, + }) + }) + out = res[0] + exc = res[1] + + self.assertIn('Update Excuses generation completed', out) + # not expecting any failures to retrieve from swift + self.assertNotIn('Failure', out) + + # there should be some pending ones + self.assertEqual(self.pending_requests, + {'green/2': {'darkgreen': ['amd64'], 'lightgreen': ['i386']}}) + + # autopkgtest should not cause the package to be blocked + self.assertEqual(exc['green']['policy_info']['autopkgtest']['verdict'], 'PASS') + # instead, it should cause the age to sky-rocket + self.assertEqual(exc['green']['policy_info']['age']['age-requirement'], 40) + + def test_passing_package_receives_bounty(self): + '''Does not request a test for an uninstallable package''' + + # Don't use policy verdics, but age packages appropriate + for line in fileinput.input(self.britney_conf, inplace=True): + if line.startswith('MINDAYS_MEDIUM'): + print('MINDAYS_MEDIUM = 13') + elif line.startswith('ADT_SUCCESS_BOUNTY'): + print('ADT_SUCCESS_BOUNTY = 6') + elif line.startswith('ADT_REGRESSION_PENALTY'): + print('ADT_REGRESSION_PENALTY = 27') + else: + sys.stdout.write(line) + + self.data.add_default_packages(green=False) + + self.swift.set_results({'autopkgtest-testing': { + 'testing/i386/d/darkgreen/20150101_100000@': (0, 'darkgreen 1', tr('green/2')), + 'testing/amd64/d/darkgreen/20150101_100000@': (0, 'darkgreen 1', tr('green/2')), + 'testing/i386/l/lightgreen/20150101_100100@': (0, 'lightgreen 1', tr('green/2')), + 'testing/amd64/l/lightgreen/20150101_100100@': (0, 'lightgreen 1', tr('green/2')), + 'testing/i386/g/green/20150101_100200@': (0, 'green 2', tr('green/2')), + 'testing/amd64/g/green/20150101_100201@': (0, 'green 2', tr('green/2')), + }}) + + exc = self.do_test( + [('green', {'Version': '2'}, 'autopkgtest')], + {'green': (False, {})}, + {})[1] + + # it should cause the age to drop + self.assertEqual(exc['green']['policy_info']['age']['age-requirement'], 7) + if __name__ == '__main__': unittest.main()