def can_test_on_upstream(self): change_states = {"POST", "MODIFIED"} # With these states, the change is in upstream if self.status not in {"POST", "MODIFIED", "ON_QA", "VERIFIED", "RELEASE_PENDING"}: return False history = self.get_history()["bugs"][0]["history"] changes = [] # We look for status changes in the history for event in history: for change in event["changes"]: if change["field_name"].lower() != "status": continue if change["added"] in change_states: changes.append(event["when"]) return event["when"] < appliance_build_datetime() else: return False
def pytest_runtest_setup(item): if not hasattr(item, "_bugzilla_bugs"): return skippers = set([]) xfailers = set([]) # We filter only bugs that are fixed in current release # Generic status-based skipping for bug in filter(lambda o: o is not None, map(lambda bug: item._bugzilla_bugs.get_bug(bug.id), item._bugzilla_bugs)): if bug.status in {"NEW", "ASSIGNED", "ON_DEV"}: skippers.add(bug.id) # POST/MODIFIED for upstream states = {"POST", "MODIFIED"} for bug in filter(lambda b: b.status in states, filter(lambda o: o is not None, map(lambda bug: item._bugzilla_bugs.get_bug(bug.id), item._bugzilla_bugs))): history = bug.get_history()["bugs"][0]["history"] changes = [] # We look for status changes in the history for event in history: for change in event["changes"]: if change["field_name"].lower() != "status": continue if change["added"] in states: changes.append(event["when"]) break if changes: if appliance_is_downstream(): # The possible fix is definitely not in the downstream build skippers.add(bug.id) continue # Given that the current bug state is what we want, we select the last change to the bug last_change = changes[-1] if last_change < appliance_build_datetime(): logger.info( "Decided to test {} on upstream, because the appliance was built " "after the bug {} status was modified".format(item.nodeid, bug.id) ) else: skippers.add(bug.id) # Custom skip/xfail handler global_env = dict( bugs=item._bugzilla_bugs, appliance_version=current_version(), appliance_downstream=appliance_is_downstream(), ) # We will now extend the env with fixtures, so they can be used in the guard functions # We will however add only those that are not in the global_env otherwise we could overwrite # our own stuff. for funcarg, value in item.callspec.params.iteritems(): if funcarg not in global_env: global_env[funcarg] = value for bug in set(map(lambda bug: item._bugzilla_bugs.get_bug(bug.id), item._bugzilla_bugs)): local_env = {"bug": bug} local_env.update(global_env) if item._skip_func(**local_env): skippers.add(bug.id) if item._xfail_func(**local_env): xfailers.add(bug.id) # Separate loop for unskipping for bug in set(map(lambda id: item._bugzilla_bugs.get_bug(id), skippers)): if bug.id not in item._unskip_dict: continue local_env = {"bug": bug} local_env.update(global_env) if item._unskip_dict[bug.id](**local_env): skippers.discard(bug.id) # We now have to resolve what to do with this test item # xfailing takes precedence over skipping (xfail is via custom function) if xfailers: item.add_marker( pytest.mark.xfail( "Xfailing due to these bugs: {}".format(", ".join(map(str, xfailers))))) elif skippers: bz_url = urlparse(item._bugzilla.url) pytest.skip("Skipping due to these bugs:\n{}".format( "\n".join([ "{}: {} ({}://{}/show_bug.cgi?id={})".format( bug.status, bug.summary, bz_url.scheme, bz_url.netloc, bug.id) for bug in set(map(lambda id: item._bugzilla_bugs.get_bug(id), skippers)) ]) )) else: logger.info("No action required by Bugzilla for {}. All good!".format(item.nodeid))