summaryrefslogtreecommitdiff
path: root/tools/perf/tests/shell/test_ilist_python.sh
blob: a235539c01767ee5ea98d8a167de7490093aa3d3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/bin/bash
# perf script ilist and perf list --tui test (no_mlock)
# 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