Skip to content

Add a pure RegressionReport service - #307

Open
aliceinwire wants to merge 1 commit into
kernelci:mainfrom
aliceinwire:improvements
Open

Add a pure RegressionReport service#307
aliceinwire wants to merge 1 commit into
kernelci:mainfrom
aliceinwire:improvements

Conversation

@aliceinwire

Copy link
Copy Markdown
Member
  • Classifies regressions, fixes, unstable results, persistent failures, new results, and missing results.
  • Preserves duplicate executions using occurrence indexes rather than collapsing them into dictionary entries.
  • Uses origin, platform, architecture, compiler, configuration, path, and result type as the comparison identity.
  • Incorporates Dashboard tree-report classifications for history-aware regression, fixed, and unstable detection.

- Classifies regressions, fixes, unstable results, persistent failures, new results, and missing results.
- Preserves duplicate executions using occurrence indexes rather than collapsing them into dictionary entries.
- Uses origin, platform, architecture, compiler, configuration, path, and result type as the comparison identity.
- Incorporates Dashboard tree-report classifications for history-aware regression, fixed, and unstable detection.

Signed-off-by: Arisu Tachibana <arisu.tachibana@miraclelinux.com>
Comment thread kcidev/api.py
base_results, head_results = checkout(base), checkout(head)
# tree-report supplies history-aware unstable/regression decisions. If
# HEAD is not the newest checkout the raw transition remains useful.
history = self.get_tree_report(origin, branch, giturl)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so get_tree_report() uses default max_age_in_hours=None/min_age_in_hours=None. For me I get a error 400, can we pass a default in here (or real ints)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also probably worth wrapping this in a try/except: and set report.incomplete = True on failure. So history stays optional as the comment intends

Comment thread kcidev/api.py
base_results, head_results = checkout(base), checkout(head)
# tree-report supplies history-aware unstable/regression decisions. If
# HEAD is not the newest checkout the raw transition remains useful.
history = self.get_tree_report(origin, branch, giturl)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also probably worth wrapping this in a try/except: and set report.incomplete = True on failure. So history stays optional as the comment intends

Comment thread kcidev/libs/regression.py
new = str(after.get("status") or "UNKNOWN").upper()
if key in history["unstable"]:
return "unstable"
if key in history["regression"] or (old == "PASS" and new in FAIL_STATUSES):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so PASS→PASS and even FAIL→PASS (a fix) get classified as regression. History should only disambiguate maybe require new in FAIL_STATUSES before the history key wins. Same shape just below for fixed.

Comment thread kcidev/libs/regression.py
category = cls._classify(before, after, key, history)
if category is None:
continue
chosen = after or before

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this crashes for me on a falsy head entry (an empty {} execution becomes None -> AttributeError). after if after is not None else before fixes it.

Comment thread kcidev/libs/regression.py
old[_key(item, kind)].append(item)
for item in head_results.get(kind + "s", []):
new[_key(item, kind)].append(item)
for key in sorted(set(old) | set(new)):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate pairing is positional, so it's order-dependent: [PASS, FAIL] vs [FAIL, PASS] gives regression+fixed, while [PASS, FAIL] vs [PASS, FAIL] gives persistent_fail. mainline these dupes are usually the same board in different labs, and lab isn't in the identity, so sorting each bucket (or adding lab) would make it deterministic

Comment thread kcidev/libs/regression.py
return tuple(result_identity(item, kind).values())


def _tree_report_keys(tree_report, category):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The history identity built here ends up with kind="test" and origin="unknown", so it can never match boot or build results (and likely not tests either). Dropping kind and filling origin from the tree report would let history actually apply

)
if report["incomplete"]:
raise click.exceptions.Exit(2)
policies = {value.strip() for value in fail_on.split(",")}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--fail-on isn't validated. Could we check the parsed set against the category names and UsageError on unknowns?

if bool(base) != bool(head):
raise click.UsageError("provide both --base and --head, or neither")
if not base:
giturl, branch, latest = set_giturl_branch_commit(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This auto base/head resolution raises click.Abort. Not a KciDevError, we should also catch click.Abort

click.echo(json.dumps({"error": str(exc), "incomplete": True}))
raise click.exceptions.Exit(2) from exc
if json_output:
# This is deliberately the only stdout write in JSON mode.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your comment here "only stdout write in JSON mode". Threading error_verbose=False through get_build_issues/get_boot_issues (or redirecting to stderr) would fix it.

Comment thread kcidev/api.py
report = RegressionReport.compare(
base, head, base_results, head_results, history
)
if include_issues:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issues one request per regression and per persistent_fail; on a big commit that's thousands of sequential calls. Worth capping it or making include_issues opt-in.

Comment thread tests/test_regression.py
@@ -0,0 +1,88 @@
import json

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add coverage for the history branches, the gate command (exit codes and --fail-on parsing), and duplicate count mismatches (base 2 / head 1)? Those paths hold most of the issues above and are currently untested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants