mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
perf test: Add a test for the ilist script
Test launching ilist through perf script and perf list --tui, and drive the textual app headless: check the tree of PMUs and metrics, search for and select the software task-clock event checking its counters are shown, check a failed search shows an error and select a metric. Failing to open counters, for example for want of permissions, is tolerated as an error dialog is shown. Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers <irogers@google.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
ab8834953b
commit
ba5992c94a
181
tools/perf/tests/shell/test_ilist_python.sh
Executable file
181
tools/perf/tests/shell/test_ilist_python.sh
Executable file
|
|
@ -0,0 +1,181 @@
|
|||
#!/bin/bash
|
||||
# perf script ilist and perf list --tui test
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
set -e
|
||||
|
||||
shelldir=$(dirname "$0")
|
||||
# shellcheck source=lib/setup_python.sh
|
||||
. "${shelldir}"/lib/setup_python.sh
|
||||
|
||||
if ! "$PYTHON" -c 'import perf' > /dev/null 2>&1; then
|
||||
echo "Skipping test, perf python module not found"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! "$PYTHON" -c 'import textual' > /dev/null 2>&1; then
|
||||
echo "Skipping test, python textual library not found"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
err=0
|
||||
output=$(mktemp /tmp/__perf_ilist_test.output.XXXXX)
|
||||
|
||||
cleanup() {
|
||||
rm -f "${output}"
|
||||
trap - EXIT TERM INT
|
||||
}
|
||||
|
||||
trap_cleanup() {
|
||||
echo "Unexpected signal in ${FUNCNAME[1]}"
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
trap trap_cleanup EXIT TERM INT
|
||||
|
||||
test_plumbing() {
|
||||
echo "ilist plumbing test"
|
||||
# The script should be found and run by perf script, --help doesn't
|
||||
# need a terminal.
|
||||
if ! perf script ilist --help > "${output}" 2>&1; then
|
||||
echo "ilist plumbing test [Failed: script failed]"
|
||||
cat "${output}"
|
||||
err=1
|
||||
return
|
||||
fi
|
||||
if ! grep -q -- "--interval" "${output}"; then
|
||||
echo "ilist plumbing test [Failed: script not launched]"
|
||||
cat "${output}"
|
||||
err=1
|
||||
return
|
||||
fi
|
||||
echo "ilist plumbing test [Success]"
|
||||
}
|
||||
|
||||
test_list_tui() {
|
||||
echo "perf list --tui plumbing test"
|
||||
# Arguments after -- are passed to the script launched by perf list.
|
||||
if ! perf list --tui -- --help > "${output}" 2>&1; then
|
||||
echo "perf list --tui plumbing test [Failed: script failed]"
|
||||
cat "${output}"
|
||||
err=1
|
||||
return
|
||||
fi
|
||||
if ! grep -q -- "--interval" "${output}"; then
|
||||
echo "perf list --tui plumbing test [Failed: script not launched]"
|
||||
cat "${output}"
|
||||
err=1
|
||||
return
|
||||
fi
|
||||
echo "perf list --tui plumbing test [Success]"
|
||||
}
|
||||
|
||||
test_headless_ui() {
|
||||
echo "ilist headless UI test"
|
||||
# Drive the textual app without a terminal. Opening counters may fail
|
||||
# for want of permissions, in which case an error dialog is shown.
|
||||
if ! "$PYTHON" > "${output}" 2>&1 <<'EOF'
|
||||
import asyncio
|
||||
import ilist
|
||||
from textual.widgets import Label, Tree
|
||||
|
||||
async def dismiss_error(app, pilot) -> bool:
|
||||
"""Close the error dialog if shown, returning whether it was."""
|
||||
if not isinstance(app.screen, ilist.ErrorScreen):
|
||||
return False
|
||||
await pilot.click("#error")
|
||||
await pilot.pause()
|
||||
if isinstance(app.screen, ilist.ErrorScreen):
|
||||
raise RuntimeError("Error dialog not dismissed")
|
||||
return True
|
||||
|
||||
async def search(pilot, text: str) -> None:
|
||||
await pilot.press("s")
|
||||
await pilot.pause()
|
||||
await pilot.press(*text, "enter")
|
||||
await pilot.pause()
|
||||
|
||||
def find_leaf(node, cls):
|
||||
"""Find the first node whose data is of type cls."""
|
||||
if isinstance(node.data, cls):
|
||||
return node
|
||||
for child in node.children:
|
||||
found = find_leaf(child, cls)
|
||||
if found:
|
||||
return found
|
||||
return None
|
||||
|
||||
async def check_counting(app, pilot, name: str) -> None:
|
||||
"""Check the selected event is shown and counting, or that it failed to open."""
|
||||
await pilot.pause(0.5)
|
||||
if await dismiss_error(app, pilot):
|
||||
print(f"{name}: failed to open")
|
||||
return
|
||||
shown = str(app.query_one("#event_name", Label).render())
|
||||
if shown != name:
|
||||
raise RuntimeError(f"Selected {shown} rather than {name}")
|
||||
if not app.query(ilist.CounterSparkline) or not app.query("#counter_total"):
|
||||
raise RuntimeError(f"No counters shown for {name}")
|
||||
print(f"{name}: total {app.query_one('#counter_total', Label).render()}")
|
||||
|
||||
async def run() -> None:
|
||||
app = ilist.IListApp(0.1)
|
||||
async with app.run_test(size=(120, 40)) as pilot:
|
||||
await pilot.pause()
|
||||
tree = app.query_one("#root", Tree)
|
||||
top = [str(node.label) for node in tree.root.children]
|
||||
if top != ["PMUs", "Metrics"]:
|
||||
raise RuntimeError(f"Unexpected tree: {top}")
|
||||
pmus = [str(node.label) for node in tree.root.children[0].children]
|
||||
if "software" not in pmus:
|
||||
raise RuntimeError(f"No software PMU in: {pmus}")
|
||||
|
||||
# Search for a software event, which exists everywhere. The search
|
||||
# result is selected which opens the event.
|
||||
await search(pilot, "task-clock")
|
||||
found = app.cur_search_result
|
||||
if not found or not isinstance(found.data, ilist.PmuEvent) or \
|
||||
"task-clock" not in str(found.label):
|
||||
raise RuntimeError(f"Search didn't find task-clock: {found}")
|
||||
await check_counting(app, pilot, found.data.name())
|
||||
if not app.query_one("#active_search", Label).display:
|
||||
raise RuntimeError("Active search not shown")
|
||||
await pilot.press("n", "p", "down", "up", "c")
|
||||
await pilot.pause(0.2)
|
||||
await dismiss_error(app, pilot)
|
||||
|
||||
# Searching for something that doesn't exist shows an error.
|
||||
await search(pilot, "no-such-event-xyzzy")
|
||||
if not await dismiss_error(app, pilot):
|
||||
raise RuntimeError("No error for a failed search")
|
||||
|
||||
# Select a metric, if there are any.
|
||||
metric = find_leaf(tree.root, ilist.Metric)
|
||||
if metric:
|
||||
app.expand_and_select(metric)
|
||||
await check_counting(app, pilot, metric.data.name())
|
||||
print("headless UI ok")
|
||||
|
||||
asyncio.run(run())
|
||||
EOF
|
||||
then
|
||||
echo "ilist headless UI test [Failed: exception]"
|
||||
cat "${output}"
|
||||
err=1
|
||||
return
|
||||
fi
|
||||
if ! grep -q "headless UI ok" "${output}"; then
|
||||
echo "ilist headless UI test [Failed: unexpected output]"
|
||||
cat "${output}"
|
||||
err=1
|
||||
return
|
||||
fi
|
||||
echo "ilist headless UI test [Success]"
|
||||
}
|
||||
|
||||
test_plumbing
|
||||
test_list_tui
|
||||
test_headless_ui
|
||||
|
||||
cleanup
|
||||
exit $err
|
||||
Loading…
Reference in New Issue
Block a user