diff options
| author | Alice Rogers <alice.mei.rogers@gmail.com> | 2026-10-02 11:26:20 -0700 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2026-10-03 11:44:45 +0200 |
| commit | 1dc462fc214907671600172280c2e79ef9fe6fcf (patch) | |
| tree | 30d9683a86bb61e73fbec2e8a28a9e0a9621d254 /tools/perf/tests/shell/test_ttimechart_python.sh | |
| parent | e7a72d6919804e5e5a10e13d71e259acbc7510bc (diff) | |
perf test: Add a test for perf timechart --tui
Test launching ttimechart through 'perf timechart --tui', the error when
there are no scheduler events, the --dump output with the -P, -T and -p
options, and drive the textual app headless exercising the views and
key bindings.
Committer testing:
root@x2:/home/acme/git/perf-tools-next# perf test "perf timechart --tui (ttimechart.py) test"
167: perf timechart --tui (ttimechart.py) test : Ok
=== Test Summary ===
Passed main tests : 1
Passed subtests : 0
Skipped tests : 0
Failed tests : 0
root@x2:/home/acme/git/perf-tools-next# perf test -vv "perf timechart --tui (ttimechart.py) test"
167: perf timechart --tui (ttimechart.py) test:
---- start ----
test child forked, pid 2833475
perf timechart --tui plumbing test
perf timechart --tui plumbing test [Success]
ttimechart no events test
ttimechart no events test [Success]
ttimechart dump test
ttimechart dump test [Success]
ttimechart headless UI test
ttimechart headless UI test [Success]
---- end(0) ----
167: perf timechart --tui (ttimechart.py) test : Ok
=== Test Summary ===
Passed main tests : 1
Passed subtests : 0
Skipped tests : 0
Failed tests : 0
root@x2:/home/acme/git/perf-tools-next#
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Alice Rogers <alice.mei.rogers@gmail.com>
Co-developed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/tests/shell/test_ttimechart_python.sh')
| -rwxr-xr-x | tools/perf/tests/shell/test_ttimechart_python.sh | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/tools/perf/tests/shell/test_ttimechart_python.sh b/tools/perf/tests/shell/test_ttimechart_python.sh new file mode 100755 index 000000000000..87b91ed8fc16 --- /dev/null +++ b/tools/perf/tests/shell/test_ttimechart_python.sh @@ -0,0 +1,237 @@ +#!/bin/bash +# perf timechart --tui (ttimechart.py) test +# SPDX-License-Identifier: GPL-2.0 + +set -e + +shelldir=$(dirname "$0") +# shellcheck source=lib/setup_python.sh +. "${shelldir}"/lib/setup_python.sh + +if ! perf check feature -q libtraceevent; then + echo "Skipping test, libtraceevent is disabled" + exit 2 +fi + +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 +perfdata= +output= +clockdata= + +cleanup() { + [ -n "${perfdata}" ] && rm -f "${perfdata}"* + [ -n "${clockdata}" ] && rm -f "${clockdata}"* + rm -f "${output}" + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +perfdata=$(mktemp /tmp/__perf_ttimechart_test.perf.data.XXXXX) +output=$(mktemp /tmp/__perf_ttimechart_test.output.XXXXX) +clockdata=$(mktemp /tmp/__perf_ttimechart_test.clock.data.XXXXX) + +workload() { + for _ in 1 2 3 4 5; do + sleep 0.01 + done +} + +record() { + # perf timechart record is system wide and so may need privileges, + # fall back to recording just the workload's scheduler events. + if perf timechart record -o "${perfdata}" -- \ + bash -c "$(declare -f workload); workload" > /dev/null 2>&1; then + return 0 + fi + if perf record -R -c 1 -e sched:sched_switch -e sched:sched_wakeup \ + -o "${perfdata}" -- bash -c "$(declare -f workload); workload" \ + > /dev/null 2>&1; then + return 0 + fi + return 1 +} + +test_plumbing() { + echo "perf timechart --tui plumbing test" + # A missing input file should be reported by the script launched + # through perf script, this doesn't need a terminal. + if perf timechart --tui -i "${perfdata}.missing" > "${output}" 2>&1; then + echo "perf timechart --tui plumbing test [Failed: missing file not an error]" + err=1 + return + fi + if ! grep -q "not found" "${output}"; then + echo "perf timechart --tui plumbing test [Failed: script not launched]" + cat "${output}" + err=1 + return + fi + echo "perf timechart --tui plumbing test [Success]" +} + +test_no_events() { + echo "ttimechart no events test" + if ! perf record -e task-clock -o "${clockdata}" -- true > /dev/null 2>&1; then + echo "ttimechart no events test [Skipped: perf record failed]" + return + fi + if perf script ttimechart -i "${clockdata}" --dump > "${output}" 2>&1; then + echo "ttimechart no events test [Failed: expected an error]" + err=1 + return + fi + if ! grep -q "no scheduler, power or I/O events" "${output}"; then + echo "ttimechart no events test [Failed: unexpected error message]" + cat "${output}" + err=1 + return + fi + # The same should be reported when reading the data from a pipe. + if perf record -e task-clock -o - -- true 2> /dev/null | \ + perf script ttimechart -i - --dump > "${output}" 2>&1; then + echo "ttimechart no events test [Failed: expected an error from a pipe]" + err=1 + return + fi + if ! grep -q "no scheduler, power or I/O events" "${output}"; then + echo "ttimechart no events test [Failed: unexpected error message from a pipe]" + cat "${output}" + err=1 + return + fi + echo "ttimechart no events test [Success]" +} + +test_dump() { + echo "ttimechart dump test" + if ! perf script ttimechart -i "${perfdata}" --dump > "${output}" 2>&1; then + echo "ttimechart dump test [Failed: script failed]" + cat "${output}" + err=1 + return + fi + if ! grep -q "^Duration: .*sched events: [1-9]" "${output}" || + ! grep -q "^CPU [0-9]*: busy" "${output}" || + ! grep -q "^Task .*Running .*Waiting .*Blocked" "${output}" || + ! grep -q "sleep" "${output}"; then + echo "ttimechart dump test [Failed: unexpected output]" + cat "${output}" + err=1 + return + fi + + # Tasks only shouldn't show CPUs, power only shouldn't show tasks. + perf script ttimechart -i "${perfdata}" --dump -T > "${output}" 2>&1 || true + if grep -q "^CPU [0-9]*: " "${output}" || + ! grep -q "^Task .*Running .*Waiting .*Blocked" "${output}"; then + echo "ttimechart dump test [Failed: -T output]" + cat "${output}" + err=1 + return + fi + perf script ttimechart -i "${perfdata}" --dump -P > "${output}" 2>&1 || true + if grep -q "^Task .*Running .*Waiting .*Blocked" "${output}" || + ! grep -q "^CPU [0-9]*: " "${output}"; then + echo "ttimechart dump test [Failed: -P output]" + cat "${output}" + err=1 + return + fi + + # Filtering on the workload shouldn't show other tasks. + perf script ttimechart -i "${perfdata}" --dump -p sleep > "${output}" 2>&1 || true + if ! grep -q "^sleep " "${output}" || grep -q "^bash " "${output}"; then + echo "ttimechart dump test [Failed: -p output]" + cat "${output}" + err=1 + return + fi + echo "ttimechart dump test [Success]" +} + +test_headless_ui() { + echo "ttimechart headless UI test" + # Drive the textual app without a terminal, exercising the views and + # key bindings. + if ! "$PYTHON" - "${perfdata}" > "${output}" 2>&1 <<'EOF' +import asyncio +import sys +import ttimechart as tc + +async def run() -> None: + # The app starts before the data is loaded in a background thread. + app = tc.TimechartApp(sys.argv[1], False, False, []) + async with app.run_test(size=(120, 40)) as pilot: + for _ in range(600): + if app.loaded: + break + await pilot.pause(0.1) + if not app.loaded: + raise RuntimeError("Timed out loading data") + await pilot.pause() + tabs = app.query_one(tc.TabbedContent) + for tab in ["cpus", "tasks", "summary"]: + tabs.active = tab + await pilot.pause() + await pilot.press("down", "plus", "plus", "right", "n", "p", "minus", + "shift+right", "shift+left", "s", "w", "0") + await pilot.pause() + tabs.active = "summary" + await pilot.pause() + await pilot.press("enter") + await pilot.pause() + details = str(app.query_one("#details", tc.Static).render()) + if "Cursor" not in details: + raise RuntimeError(f"Unexpected details: {details}") + print("headless UI ok") + +asyncio.run(run()) +EOF + then + echo "ttimechart headless UI test [Failed: exception]" + cat "${output}" + err=1 + return + fi + if ! grep -q "headless UI ok" "${output}"; then + echo "ttimechart headless UI test [Failed: unexpected output]" + cat "${output}" + err=1 + return + fi + echo "ttimechart headless UI test [Success]" +} + +test_plumbing +test_no_events + +if ! record; then + echo "Skipping remaining tests, failed to record scheduler events (permissions?)" + if [ $err -eq 0 ]; then + err=2 + fi + cleanup + exit $err +fi + +test_dump +test_headless_ui + +cleanup +exit $err |
