aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_rawimage_rpc.py
diff options
context:
space:
mode:
authorblasty <peter@haxx.in>2026-08-21 12:14:46 +0200
committerblasty <peter@haxx.in>2026-08-21 12:15:15 +0200
commit02d02417800184fb76cd0245cdaa94c437aa4081 (patch)
tree7589e6e2e8426bb7370fc56eee52d1559c9d7e57 /tests/test_rawimage_rpc.py
parentadopt ruff: pinned formatter + import sorting, opt-in pre-commit hook (diff)
downloadida-tui-02d02417800184fb76cd0245cdaa94c437aa4081.tar.gz
ida-tui-02d02417800184fb76cd0245cdaa94c437aa4081.tar.xz
ida-tui-02d02417800184fb76cd0245cdaa94c437aa4081.zip
reformat: ruff format + import sort, mechanically (see ruff.toml)
No behavior. Listed in .git-blame-ignore-revs (next commit).
Diffstat (limited to 'tests/test_rawimage_rpc.py')
-rw-r--r--tests/test_rawimage_rpc.py196
1 files changed, 138 insertions, 58 deletions
diff --git a/tests/test_rawimage_rpc.py b/tests/test_rawimage_rpc.py
index d39ad35..a6e7661 100644
--- a/tests/test_rawimage_rpc.py
+++ b/tests/test_rawimage_rpc.py
@@ -36,7 +36,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from idatui.rpcclient import RpcClient, RpcError # noqa: E402
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-BLOB = os.path.join(REPO, "experiments", "fibonacci.bin") # real Thumb code
+BLOB = os.path.join(REPO, "experiments", "fibonacci.bin") # real Thumb code
PASS = FAIL = 0
@@ -52,11 +52,24 @@ def check(name, ok, detail=""):
def spawn_pane(target, processor, timeout=420):
- cmd = [sys.executable, "-m", "idatui.pane", "spawn", "--open", target,
- "--processor", processor, "--detached", "--size", "60%",
- "--timeout", str(timeout)]
- r = subprocess.run(cmd, capture_output=True, text=True,
- timeout=timeout + 60, cwd=REPO)
+ cmd = [
+ sys.executable,
+ "-m",
+ "idatui.pane",
+ "spawn",
+ "--open",
+ target,
+ "--processor",
+ processor,
+ "--detached",
+ "--size",
+ "60%",
+ "--timeout",
+ str(timeout),
+ ]
+ r = subprocess.run(
+ cmd, capture_output=True, text=True, timeout=timeout + 60, cwd=REPO
+ )
if not r.stdout.strip():
print(f" spawn produced no JSON: {r.stderr.strip()}", file=sys.stderr)
return None
@@ -64,9 +77,22 @@ def spawn_pane(target, processor, timeout=420):
def stop_pane(sock, timeout=60):
- subprocess.run([sys.executable, "-m", "idatui.pane", "stop", "--sock", sock,
- "--timeout", str(timeout)],
- capture_output=True, text=True, timeout=timeout + 10, cwd=REPO)
+ subprocess.run(
+ [
+ sys.executable,
+ "-m",
+ "idatui.pane",
+ "stop",
+ "--sock",
+ sock,
+ "--timeout",
+ str(timeout),
+ ],
+ capture_output=True,
+ text=True,
+ timeout=timeout + 10,
+ cwd=REPO,
+ )
def main() -> int:
@@ -94,34 +120,44 @@ def main() -> int:
# -- load options actually reached IDA --------------------------- #
# Wrong processor => the disassembly is nonsense or absent; ARMv7-A
# also means a 32-bit database, without which Hex-Rays refuses.
- check("spawn forwarded --processor", info.get("ok"),
- json.dumps(info))
+ check("spawn forwarded --processor", info.get("ok"), json.dumps(info))
with RpcClient(sock) as c:
st = c.call("state")
- check("pane is drivable", st.get("active") in
- ("listing", "decomp", "hex"), json.dumps(st)[:200])
+ check(
+ "pane is drivable",
+ st.get("active") in ("listing", "decomp", "hex"),
+ json.dumps(st)[:200],
+ )
# -- define ------------------------------------------------- #
# fibonacci.bin is Thumb at 0x0; as ARM it does not decode.
r = c.call("define", kind="thumb", target="0x0")
d = r.get("define", {})
check("define thumb ran", "define" in r, json.dumps(r)[:200])
- check("define thumb decoded instructions",
- "instruction" in d.get("status", ""), d.get("status", ""))
+ check(
+ "define thumb decoded instructions",
+ "instruction" in d.get("status", ""),
+ d.get("status", ""),
+ )
r = c.call("define", kind="func", target="0x0")
- check("define func created a function",
- "function" in r["define"]["status"]
- or "already" in r["define"]["status"],
- r["define"]["status"])
+ check(
+ "define func created a function",
+ "function" in r["define"]["status"]
+ or "already" in r["define"]["status"],
+ r["define"]["status"],
+ )
bad = None
try:
c.call("define", kind="nonsense")
except RpcError as e:
bad = str(e)
- check("define rejects an unknown kind", bad is not None
- and "unknown define kind" in bad, str(bad))
+ check(
+ "define rejects an unknown kind",
+ bad is not None and "unknown define kind" in bad,
+ str(bad),
+ )
# -- opfmt (how a literal is displayed) --------------------- #
# Thumb code is full of small immediates -- the thing 'o' exists
@@ -134,21 +170,31 @@ def main() -> int:
lit = None
for ln in seen:
m = re.match(r"([0-9A-F]{8})\s+(.*)", ln.get("text", ""))
- if not (m and re.search(r"#(0x[0-9A-Fa-f]{2,}|[1-9]\d+)\b",
- m.group(2))):
+ if not (
+ m and re.search(r"#(0x[0-9A-Fa-f]{2,}|[1-9]\d+)\b", m.group(2))
+ ):
continue
ea_s = "0x" + m.group(1)
- st = c.call("opfmt", mode="show", target=ea_s, delay_ms=0
- ).get("opfmt", {}).get("status", "")
+ st = (
+ c.call("opfmt", mode="show", target=ea_s, delay_ms=0)
+ .get("opfmt", {})
+ .get("status", "")
+ )
if "no literal" not in st:
lit = (ea_s, st)
break
- check("the blob has an immediate to reformat", lit is not None,
- json.dumps([ln.get("text") for ln in seen[:8]]))
+ check(
+ "the blob has an immediate to reformat",
+ lit is not None,
+ json.dumps([ln.get("text") for ln in seen[:8]]),
+ )
if lit is not None:
tgt, st0 = lit
- check("opfmt show reports the stops without editing",
- "[" in st0 and "dec" in st0, st0)
+ check(
+ "opfmt show reports the stops without editing",
+ "[" in st0 and "dec" in st0,
+ st0,
+ )
r = c.call("opfmt", mode="dec", target=tgt, delay_ms=0)
st1 = r.get("opfmt", {}).get("status", "")
check("opfmt sets a named format", "dec" in st1, st1)
@@ -156,16 +202,21 @@ def main() -> int:
st2 = r.get("opfmt", {}).get("status", "")
check("opfmt cycles on from there", "\u2192" in st2, st2)
r = c.call("opfmt", mode="default")
- check("opfmt hands the operand back to IDA",
- "default" in r.get("opfmt", {}).get("status", ""),
- r.get("opfmt", {}).get("status", ""))
+ check(
+ "opfmt hands the operand back to IDA",
+ "default" in r.get("opfmt", {}).get("status", ""),
+ r.get("opfmt", {}).get("status", ""),
+ )
badfmt = None
try:
c.call("opfmt", mode="roman")
except RpcError as e:
badfmt = str(e)
- check("opfmt rejects an unknown mode", badfmt is not None
- and "unknown opfmt mode" in badfmt, str(badfmt))
+ check(
+ "opfmt rejects an unknown mode",
+ badfmt is not None and "unknown opfmt mode" in badfmt,
+ str(badfmt),
+ )
# -- rename_many -------------------------------------------- #
fns = c.call("functions", limit=200)
@@ -177,28 +228,48 @@ def main() -> int:
# 'start' (not 'addr') on purpose: symbol files in the wild
# use it, and accepting only one spelling is how a bulk
# import silently renames nothing.
- json.dump([{"start": hex(ea), "name": "bulk_named_fn"},
- {"start": "0xdeadbe", "name": "nowhere"}], f)
+ json.dump(
+ [
+ {"start": hex(ea), "name": "bulk_named_fn"},
+ {"start": "0xdeadbe", "name": "nowhere"},
+ ],
+ f,
+ )
r = c.call("rename_many", file=symfile)
m = r.get("rename_many", {})
- check("rename_many applied the good entry", m.get("ok") == 1,
- json.dumps(m))
- check("rename_many reports the bad entry",
- m.get("failed") == 1 and m.get("errors"), json.dumps(m))
+ check(
+ "rename_many applied the good entry",
+ m.get("ok") == 1,
+ json.dumps(m),
+ )
+ check(
+ "rename_many reports the bad entry",
+ m.get("failed") == 1 and m.get("errors"),
+ json.dumps(m),
+ )
# The readback matters more than the return value: a driver
# trusts resolve/functions to decide what work is left.
- check("renamed symbol resolves",
- c.call("resolve", name="bulk_named_fn").get("ea") == ea,
- json.dumps(c.call("resolve", name="bulk_named_fn")))
+ check(
+ "renamed symbol resolves",
+ c.call("resolve", name="bulk_named_fn").get("ea") == ea,
+ json.dumps(c.call("resolve", name="bulk_named_fn")),
+ )
names = {f["name"] for f in c.call("functions", limit=200)}
- check("function table shows the new name",
- "bulk_named_fn" in names, str(sorted(names)[:10]))
+ check(
+ "function table shows the new name",
+ "bulk_named_fn" in names,
+ str(sorted(names)[:10]),
+ )
- r = c.call("rename_many", items=[{"addr": hex(ea),
- "name": "inline_named_fn"}])
- check("rename_many takes inline items",
- r["rename_many"]["ok"] == 1, json.dumps(r["rename_many"]))
+ r = c.call(
+ "rename_many", items=[{"addr": hex(ea), "name": "inline_named_fn"}]
+ )
+ check(
+ "rename_many takes inline items",
+ r["rename_many"]["ok"] == 1,
+ json.dumps(r["rename_many"]),
+ )
# -- the stale-pseudocode trap ------------------------------ #
# Hex-Rays caches per function and does not notice that a
@@ -208,22 +279,31 @@ def main() -> int:
# the function's own body cites it.)
before = c.call("pseudocode", target=hex(ea))
pc_before = json.dumps(before)
- r = c.call("rename_many", items=[{"addr": hex(ea),
- "name": "after_cache_fn"}])
+ r = c.call(
+ "rename_many", items=[{"addr": hex(ea), "name": "after_cache_fn"}]
+ )
pc_after = json.dumps(c.call("pseudocode", target=hex(ea)))
- check("pseudocode was cached before the rename",
- "inline_named_fn" in pc_before, pc_before[:200])
- check("rename_many invalidates the decompile cache",
- "after_cache_fn" in pc_after
- and "inline_named_fn" not in pc_after, pc_after[:300])
+ check(
+ "pseudocode was cached before the rename",
+ "inline_named_fn" in pc_before,
+ pc_before[:200],
+ )
+ check(
+ "rename_many invalidates the decompile cache",
+ "after_cache_fn" in pc_after and "inline_named_fn" not in pc_after,
+ pc_after[:300],
+ )
empty = None
try:
c.call("rename_many")
except RpcError as e:
empty = str(e)
- check("rename_many without items errors", empty is not None
- and "items" in empty, str(empty))
+ check(
+ "rename_many without items errors",
+ empty is not None and "items" in empty,
+ str(empty),
+ )
finally:
stop_pane(sock)