mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Ian Rogers <irogers@google.com>
To: Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@redhat.com>,
	 Arnaldo Carvalho de Melo <acme@kernel.org>,
	Namhyung Kim <namhyung@kernel.org>, Jiri Olsa <jolsa@kernel.org>,
	 Ian Rogers <irogers@google.com>,
	Adrian Hunter <adrian.hunter@intel.com>,
	 James Clark <james.clark@linaro.org>,
	Thomas Falcon <thomas.falcon@intel.com>,
	 Alice Rogers <alice.mei.rogers@gmail.com>,
	Changbin Du <changbin.du@huawei.com>,
	 Tengda Wu <wutengda@huaweicloud.com>, tanze <tanze@kylinos.cn>,
	 Athira Rajeev <atrajeev@linux.ibm.com>,
	Dapeng Mi <dapeng1.mi@linux.intel.com>,
	 linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org
Subject: [PATCH v1 07/13] perf test: Add a test for the ilist script
Date: Fri,  2 Oct 2026 11:26:16 -0700	[thread overview]
Message-ID: <20261002182624.3259797-8-irogers@google.com> (raw)
In-Reply-To: <20261002182624.3259797-1-irogers@google.com>

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>
---
 tools/perf/tests/shell/test_ilist_python.sh | 181 ++++++++++++++++++++
 1 file changed, 181 insertions(+)
 create mode 100755 tools/perf/tests/shell/test_ilist_python.sh

diff --git a/tools/perf/tests/shell/test_ilist_python.sh b/tools/perf/tests/shell/test_ilist_python.sh
new file mode 100755
index 000000000000..83048474710d
--- /dev/null
+++ b/tools/perf/tests/shell/test_ilist_python.sh
@@ -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
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


  parent reply	other threads:[~2026-10-02 18:26 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-02 18:26 [PATCH v1 00/13] perf timechart/list/treport: Interactive Textual TUIs and perf python session improvements Ian Rogers
2026-10-02 18:26 ` [PATCH v1 01/13] perf session: Don't flush remaining events once processing is done Ian Rogers
2026-10-02 18:26 ` [PATCH v1 02/13] perf python: Quietly stop processing events when a callback raises Ian Rogers
2026-10-02 18:26 ` [PATCH v1 03/13] perf python: Lazily copy events and samples from process_events Ian Rogers
2026-10-02 18:26 ` [PATCH v1 04/13] perf python: Lazily resolve sample callchains Ian Rogers
2026-10-02 18:26 ` [PATCH v1 05/13] perf python: Release the GIL while processing session events Ian Rogers
2026-10-02 18:26 ` [PATCH v1 06/13] perf list: Add a --tui option to launch ilist Ian Rogers
2026-10-02 18:26 ` Ian Rogers [this message]
2026-10-02 18:26 ` [PATCH v1 08/13] perf treport: Show the profile while it loads Ian Rogers
2026-10-03  9:31   ` Arnaldo Carvalho de Melo
2026-10-02 18:26 ` [PATCH v1 09/13] perf test: Add a test for the treport script Ian Rogers
2026-10-03  9:35   ` Arnaldo Carvalho de Melo
2026-10-02 18:26 ` [PATCH v1 10/13] perf timechart: Add an interactive --tui mode Ian Rogers
2026-10-02 18:26 ` [PATCH v1 11/13] perf test: Add a test for perf timechart --tui Ian Rogers
2026-10-02 18:26 ` [PATCH v1 12/13] perf timechart: Add a --live mode to the TUI Ian Rogers
2026-10-02 18:26 ` [PATCH v1 13/13] perf test: Test perf timechart --live Ian Rogers
2026-10-03  9:10 ` [PATCH v1 00/13] perf timechart/list/treport: Interactive Textual TUIs and perf python session improvements Arnaldo Carvalho de Melo
2026-10-03  9:12   ` Arnaldo Carvalho de Melo
2026-10-03  9:15     ` Arnaldo Carvalho de Melo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261002182624.3259797-8-irogers@google.com \
    --to=irogers@google.com \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=alice.mei.rogers@gmail.com \
    --cc=atrajeev@linux.ibm.com \
    --cc=changbin.du@huawei.com \
    --cc=dapeng1.mi@linux.intel.com \
    --cc=james.clark@linaro.org \
    --cc=jolsa@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=peterz@infradead.org \
    --cc=tanze@kylinos.cn \
    --cc=thomas.falcon@intel.com \
    --cc=wutengda@huaweicloud.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®