Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/results.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,29 @@ Example:
kci-dev results summary --giturl 'https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git' --branch master --commit d1486dca38afd08ca279ae94eb3a397f10737824
```

#### --history

Show commit history with test results for multiple recent commits in a table format. This displays historical data showing pass/fail/inconclusive counts for builds, boots, and tests across recent commits.

Example:

```sh
kci-dev results summary --giturl 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git' --branch master --history
```

Output:
```
+--------------+-------------------------------------+----------+-----------+-----------------+
| Commit | Name | Builds | Boots | Tests |
+==============+=====================================+==========+===========+=================+
| c4dce0c094a8 | spi-fix-v6.16-rc3-58-gc4dce0c094a89 | 47/1/0 | 100/0/47 | 8853/1677/3016 |
+--------------+-------------------------------------+----------+-----------+-----------------+
| 92ca6c498a5e | v6.16-rc3-57-g92ca6c498a5e6 | 48/0/0 | 130/1/53 | 8856/1456/3111 |
+--------------+-------------------------------------+----------+-----------+-----------------+
```

The format shows pass/fail/inconclusive counts with color coding (green for pass, red for fail, yellow for inconclusive).

### <a id="result-builds"></a>builds

List builds results.
Expand Down
14 changes: 14 additions & 0 deletions kcidev/libs/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ def dashboard_fetch_summary(origin, giturl, branch, commit, arch, use_json):
return dashboard_api_fetch(endpoint, params, use_json)


def dashboard_fetch_commits_history(origin, giturl, branch, commit, use_json):
"""Fetch commit history data from /commits endpoint"""
endpoint = f"tree/{commit}/commits"
params = {
"origin": origin,
"git_url": giturl,
"git_branch": branch,
}

logging.info(f"Fetching commit history for commit {commit} on {branch} branch")
logging.debug(f"Parameters: origin={origin}, git_url={giturl}")
return dashboard_api_fetch(endpoint, params, use_json)


def dashboard_fetch_builds(
origin, giturl, branch, commit, arch, tree, start_date, end_date, use_json
):
Expand Down
25 changes: 19 additions & 6 deletions kcidev/subcommands/results/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
dashboard_fetch_boots,
dashboard_fetch_build,
dashboard_fetch_builds,
dashboard_fetch_commits_history,
dashboard_fetch_summary,
dashboard_fetch_test,
dashboard_fetch_tests,
Expand All @@ -23,6 +24,7 @@
)
from kcidev.subcommands.results.parser import (
cmd_builds,
cmd_commits_history,
cmd_list_trees,
cmd_single_build,
cmd_single_test,
Expand Down Expand Up @@ -77,13 +79,24 @@ def results(ctx):

@results.command()
@common_options
def summary(origin, git_folder, giturl, branch, commit, latest, arch, tree, use_json):
@click.option("--history", is_flag=True, help="Show commit history data")
def summary(
origin, git_folder, giturl, branch, commit, latest, arch, tree, history, use_json
):
"""Display a summary of results."""
giturl, branch, commit = set_giturl_branch_commit(
origin, giturl, branch, commit, latest, git_folder
)
data = dashboard_fetch_summary(origin, giturl, branch, commit, arch, use_json)
cmd_summary(data, use_json)
if history:
# For history, always use latest commit
giturl, branch, commit = set_giturl_branch_commit(
origin, giturl, branch, commit, True, git_folder
)
data = dashboard_fetch_commits_history(origin, giturl, branch, commit, use_json)
cmd_commits_history(data, use_json)
else:
giturl, branch, commit = set_giturl_branch_commit(
origin, giturl, branch, commit, latest, git_folder
)
data = dashboard_fetch_summary(origin, giturl, branch, commit, arch, use_json)
cmd_summary(data, use_json)


@results.command()
Expand Down
96 changes: 96 additions & 0 deletions kcidev/subcommands/results/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,99 @@ def cmd_hardware_list(data, use_json):
kci_msg_cyan(hardware["platform"], nl=False)
kci_msg("")
kci_msg("")


def format_colored_summary(pass_count, fail_count, inconclusive_count):
"""Format pass/fail/inconclusive with colors like print_summary"""
import click

# Format with colors using click.style directly for table display
pass_str = (
click.style(str(pass_count), fg="green") if pass_count else str(pass_count)
)
fail_str = click.style(str(fail_count), fg="red") if fail_count else str(fail_count)
inconclusive_str = (
click.style(str(inconclusive_count), fg="yellow")
if inconclusive_count
else str(inconclusive_count)
)

return f"{pass_str}/{fail_str}/{inconclusive_str}"


def cmd_commits_history(data, use_json):
"""Display commit history data from /commits endpoint"""
logging.info("Displaying commit history")

if use_json:
kci_msg(json.dumps(data))
else:
# Handle both list and dict responses
if isinstance(data, list):
commits = data
else:
commits = data.get("commits", [])

if not commits:
kci_msg("No commits found")
return

# Format as table with shortened pass/fail/inconclusive format
from tabulate import tabulate

table_data = []

for commit in commits:
# Get summaries
builds = commit.get("builds", {})
boots = commit.get("boots", {})
tests = commit.get("tests", {})

# Calculate totals in same format as regular summary
builds_pass = builds.get("PASS", 0)
builds_fail = builds.get("FAIL", 0)
builds_inconclusive = (
builds.get("ERROR", 0)
+ builds.get("SKIP", 0)
+ builds.get("MISS", 0)
+ builds.get("DONE", 0)
+ builds.get("NULL", 0)
)

boots_pass = boots.get("pass", 0)
boots_fail = boots.get("fail", 0)
boots_inconclusive = (
boots.get("miss", 0)
+ boots.get("error", 0)
+ boots.get("null", 0)
+ boots.get("skip", 0)
+ boots.get("done", 0)
)

tests_pass = tests.get("pass", 0)
tests_fail = tests.get("fail", 0)
tests_inconclusive = (
tests.get("error", 0)
+ tests.get("skip", 0)
+ tests.get("miss", 0)
+ tests.get("done", 0)
+ tests.get("null", 0)
)

table_data.append(
[
commit.get("git_commit_hash", "unknown")[:12],
commit.get("git_commit_name", "unknown"),
format_colored_summary(
builds_pass, builds_fail, builds_inconclusive
),
format_colored_summary(boots_pass, boots_fail, boots_inconclusive),
format_colored_summary(tests_pass, tests_fail, tests_inconclusive),
]
)

headers = ["Commit", "Name", "Builds", "Boots", "Tests"]
# Use click.secho to preserve colors in the table
import click

click.secho(tabulate(table_data, headers=headers, tablefmt="grid"), color=True)
33 changes: 33 additions & 0 deletions tests/test_kcidev.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,39 @@ def test_main():
pass


def test_kcidev_results_summary_history_help():
"""Test the --history flag appears in results summary help"""
command = ["poetry", "run", "kci-dev", "results", "summary", "--help"]
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print("returncode: " + str(result.returncode))
print("#### stdout ####")
print(result.stdout)
print("#### stderr ####")
print(result.stderr)
assert result.returncode == 0
assert "--history" in result.stdout
assert "Show commit history data" in result.stdout


def test_kcidev_results_summary_history_import():
"""Test that history functionality can be imported without errors"""
from kcidev.libs.dashboard import dashboard_fetch_commits_history
from kcidev.subcommands.results.parser import (
cmd_commits_history,
format_colored_summary,
)

# Test that functions exist and are callable
assert callable(cmd_commits_history)
assert callable(format_colored_summary)
assert callable(dashboard_fetch_commits_history)

# Test format_colored_summary with sample data
result = format_colored_summary(10, 5, 2)
assert isinstance(result, str)
assert "/" in result # Should contain separators


def test_clean():
# clean enviroment
shutil.rmtree("my-new-repo/")