window-deco-test.sh 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. #!/usr/bin/env bash
  2. # Automated window/decoration/theme test against a running mjdevc.
  3. # Usage: compositor/scripts/window-deco-test.sh [logfile]
  4. set -u
  5. SOCK="${MJDEV_SOCK:-${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/mjdev-compositor.sock}"
  6. LOG="${1:-/tmp/mjdev-window-deco-test.log}"
  7. exec > >(tee -a "$LOG") 2>&1
  8. echo "== mjdev window/deco test $(date -Is) =="
  9. python3 - "$SOCK" <<'PY'
  10. import json, socket, sys, time
  11. SOCK = sys.argv[1]
  12. def wait_sock(timeout=90):
  13. import os
  14. for _ in range(timeout):
  15. if os.path.exists(SOCK):
  16. return
  17. time.sleep(1)
  18. print(f"FAIL: no socket at {SOCK}")
  19. sys.exit(1)
  20. def ipc(cmd):
  21. s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  22. s.settimeout(5)
  23. s.connect(SOCK)
  24. s.sendall((json.dumps(cmd) + "\n").encode())
  25. s.shutdown(socket.SHUT_WR)
  26. data = b""
  27. while True:
  28. try:
  29. chunk = s.recv(65536)
  30. if not chunk:
  31. break
  32. data += chunk
  33. except socket.timeout:
  34. break
  35. s.close()
  36. if not data:
  37. return {}
  38. return json.loads(data.decode().split("\n")[0])
  39. wait_sock()
  40. print(f"socket: {SOCK}")
  41. wins = ipc({"cmd": "list-windows"}).get("windows", [])
  42. print("--- list-windows ---")
  43. for w in wins:
  44. print(f" id={w['id']} app={w.get('app_id')} {w['x']},{w['y']} {w['width']}x{w['height']}")
  45. calc = next((w for w in wins if "calculator" in (w.get("app_id") or "").lower()), None)
  46. if calc is None:
  47. print("FAIL: org.gnome.Calculator not listed (start with --startup gnome-calculator)")
  48. sys.exit(1)
  49. fail = 0
  50. if calc["y"] < 0:
  51. print(f"FAIL: negative Y={calc['y']}")
  52. fail = 1
  53. else:
  54. print(f"OK: geometry y={calc['y']} size={calc['width']}x{calc['height']}")
  55. cid = calc["id"]
  56. x, y, w, h = calc["x"], calc["y"], calc["width"], calc["height"]
  57. r = ipc({"cmd": "set-decoration-theme", "bg_r": 180, "bg_g": 40, "bg_b": 40,
  58. "fg_r": 255, "fg_g": 255, "fg_b": 255,
  59. "icon_bg_r": 220, "icon_bg_g": 80, "icon_bg_b": 80,
  60. "icon_fg_r": 255, "icon_fg_g": 200, "icon_fg_b": 200})
  61. if not r.get("ok"):
  62. print(f"FAIL: set-decoration-theme: {r}")
  63. fail = 1
  64. else:
  65. print("OK: set-decoration-theme")
  66. # unmaximize if needed so titlebar close target is predictable
  67. if calc.get("maximized"):
  68. ipc({"cmd": "maximize", "id": cid, "maximized": False})
  69. time.sleep(0.4)
  70. calc = ipc({"cmd": "list-windows"}).get("windows", [])[0]
  71. x, y, w, h = calc["x"], calc["y"], calc["width"], calc["height"]
  72. ipc({"cmd": "activate", "id": cid})
  73. close_x = x + w - 18
  74. tb_y = y + 18
  75. ipc({"cmd": "click", "x": close_x, "y": tb_y})
  76. time.sleep(0.5)
  77. still = any(w["id"] == cid for w in ipc({"cmd": "list-windows"}).get("windows", []))
  78. if still:
  79. print("FAIL: titlebar close click did not close window")
  80. fail = 1
  81. else:
  82. print("OK: titlebar close via IPC click")
  83. sys.exit(fail)
  84. PY
  85. echo "exit=$?"