summaryrefslogtreecommitdiffstats
path: root/python/mozperftest/mozperftest/fzf/fzf.py
blob: af9594db3f72bb18c81fe307f3280ff11c9f097e (plain)
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
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import json
import os
import subprocess
import sys
from distutils.spawn import find_executable
from pathlib import Path

from mach.util import get_state_dir
from mozterm import Terminal

HERE = Path(__file__).parent.resolve()
SRC_ROOT = (HERE / ".." / ".." / ".." / "..").resolve()
PREVIEW_SCRIPT = HERE / "preview.py"
FZF_HEADER = """
Please select a performance test to execute.
{shortcuts}
""".strip()

fzf_shortcuts = {
    "ctrl-t": "toggle-all",
    "alt-bspace": "beginning-of-line+kill-line",
    "?": "toggle-preview",
}

fzf_header_shortcuts = [
    ("select", "tab"),
    ("accept", "enter"),
    ("cancel", "ctrl-c"),
    ("cursor-up", "up"),
    ("cursor-down", "down"),
]


def run_fzf(cmd, tasks):
    env = dict(os.environ)
    env.update(
        {"PYTHONPATH": os.pathsep.join([p for p in sys.path if "requests" in p])}
    )
    proc = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stdin=subprocess.PIPE,
        env=env,
        universal_newlines=True,
    )
    out = proc.communicate("\n".join(tasks))[0].splitlines()
    selected = []
    query = None
    if out:
        query = out[0]
        selected = out[1:]
    return query, selected


def format_header():
    terminal = Terminal()
    shortcuts = []
    for action, key in fzf_header_shortcuts:
        shortcuts.append(
            "{t.white}{action}{t.normal}: {t.yellow}<{key}>{t.normal}".format(
                t=terminal, action=action, key=key
            )
        )
    return FZF_HEADER.format(shortcuts=", ".join(shortcuts), t=terminal)


def select(test_objects):
    mozbuild_dir = Path(Path.home(), ".mozbuild")
    os.makedirs(str(mozbuild_dir), exist_ok=True)
    cache_file = Path(mozbuild_dir, ".perftestfuzzy")

    with cache_file.open("w") as f:
        f.write(json.dumps(test_objects))

    def _display(task):
        from mozperftest.script import ScriptInfo

        path = Path(task["path"])
        script_info = ScriptInfo(str(path))
        flavor = script_info.script_type.name
        if flavor == "browsertime":
            flavor = "bt"
        tags = script_info.get("tags", [])

        location = str(path.parent).replace(str(SRC_ROOT), "").strip("/")
        if len(tags) > 0:
            return f"[{flavor}][{','.join(tags)}] {path.name} in {location}"
        return f"[{flavor}] {path.name} in {location}"

    candidate_tasks = [_display(t) for t in test_objects]

    fzf_bin = find_executable(
        "fzf", str(Path(get_state_dir(), "fzf", "bin"))
    ) or find_executable("fzf")
    if not fzf_bin:
        raise AssertionError("Unable to find fzf")

    key_shortcuts = [k + ":" + v for k, v in fzf_shortcuts.items()]

    base_cmd = [
        fzf_bin,
        "-m",
        "--bind",
        ",".join(key_shortcuts),
        "--header",
        format_header(),
        "--preview-window=right:50%",
        "--print-query",
        "--preview",
        sys.executable + ' {} -t "{{+f}}"'.format(str(PREVIEW_SCRIPT)),
    ]
    query_str, tasks = run_fzf(base_cmd, sorted(candidate_tasks))
    return tasks