106 lines
5.7 KiB
Python
106 lines
5.7 KiB
Python
import importlib.util
|
|
from pathlib import Path
|
|
import unittest
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).parents[1] / "vm"))
|
|
import tempfile
|
|
import json
|
|
from unittest.mock import patch, Mock
|
|
from types import SimpleNamespace
|
|
from contextlib import ExitStack
|
|
import io
|
|
|
|
spec = importlib.util.spec_from_file_location("desktop", Path(__file__).parents[1] / "vm/desktop.py")
|
|
desktop = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(desktop)
|
|
|
|
|
|
class ValidationTests(unittest.TestCase):
|
|
def test_settling_delay_precedes_final_capture_only_after_actions(self):
|
|
for actions in ([], [{"type": "click", "ymin": 400, "xmin": 400, "ymax": 600, "xmax": 600}]):
|
|
events = []
|
|
image = Mock(size=(1280, 800), width=1280, height=800)
|
|
def grab(**kwargs):
|
|
events.append("capture")
|
|
return image
|
|
with tempfile.TemporaryDirectory() as directory, ExitStack() as stack:
|
|
state = Path(directory)
|
|
(state / "session.json").write_text('{"DISPLAY": ":0"}')
|
|
stack.enter_context(patch.object(desktop, "STATE", state))
|
|
stack.enter_context(patch.object(sys, "argv", ["desktop.py"]))
|
|
stack.enter_context(patch.object(sys, "stdin", io.StringIO(json.dumps({"actions": actions, "coordinateSpace": "normalized_1000"}))))
|
|
stack.enter_context(patch.object(sys, "stdout", io.StringIO()))
|
|
stack.enter_context(patch.dict(sys.modules, {"PIL": SimpleNamespace(ImageGrab=SimpleNamespace(grab=grab))}))
|
|
stack.enter_context(patch("time.sleep", side_effect=lambda seconds: events.append(seconds)))
|
|
command = stack.enter_context(patch.object(desktop, "run", side_effect=lambda *args: events.append("action")))
|
|
stack.enter_context(patch.object(desktop.accessibility, "collect", return_value={"targets": [], "warning": None}))
|
|
stack.enter_context(patch.object(desktop.accessibility, "annotate", return_value=image))
|
|
desktop.main()
|
|
if actions:
|
|
self.assertEqual(events, ["capture", "action", 0.5, "capture"])
|
|
command.assert_called_once_with("xdotool", "mousemove", "--sync", "640", "400", "click", "1")
|
|
else:
|
|
self.assertEqual(events, ["capture", "capture"])
|
|
|
|
def test_session_save(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
state = Path(directory)
|
|
with patch.object(desktop, "STATE", state):
|
|
desktop.save_session({"DISPLAY": ":0"})
|
|
desktop.save_session({"DISPLAY": ":1"})
|
|
path = state / "session.json"
|
|
self.assertEqual(json.loads(path.read_text()), {"DISPLAY": ":1"})
|
|
self.assertEqual(path.stat().st_mode & 0o777, 0o600)
|
|
self.assertEqual(len(list(state.iterdir())), 1)
|
|
|
|
def test_autostart(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
with patch.object(Path, "home", return_value=Path(directory)):
|
|
desktop.install_autostart()
|
|
desktop.install_autostart()
|
|
entries = list((Path(directory) / ".config/autostart").iterdir())
|
|
self.assertEqual(len(entries), 1)
|
|
self.assertIn("--register-session\n", entries[0].read_text())
|
|
self.assertIn("OnlyShowIn=XFCE;", entries[0].read_text())
|
|
|
|
def test_normalized_coordinates(self):
|
|
for value, expected in [(0, (0, 0)), (500, (640, 400)), (1000, (1279, 799))]:
|
|
self.assertEqual(desktop.click_pixels({"x": value, "y": value}, 1280, 800, "normalized_1000"), expected)
|
|
self.assertEqual(desktop.click_pixels({"ymin": value, "xmin": value, "ymax": value, "xmax": value}, 1280, 800, "normalized_1000"), expected)
|
|
self.assertEqual(desktop.click_pixels({"ymin": 300, "xmin": 400, "ymax": 500, "xmax": 600}, 1280, 800, "normalized_1000"), (640, 320))
|
|
self.assertEqual(desktop.click_pixels({"x": 1000, "y": 1000}, 1, 1, "normalized_1000"), (0, 0))
|
|
self.assertEqual(desktop.click_pixels({"ymin": 0, "xmin": 0, "ymax": 1000, "xmax": 1000}, 1, 1, "normalized_1000"), (0, 0))
|
|
self.assertEqual(desktop.click_pixels({"x": 480, "y": 425}, 1280, 800, "pixels"), (480, 425))
|
|
self.assertEqual(desktop.click_pixels({"ymin": 400, "xmin": 460, "ymax": 450, "xmax": 500}, 1280, 800, "pixels"), (480, 425))
|
|
for value in (-1, 1001, 0.5, True):
|
|
with self.assertRaises(ValueError):
|
|
desktop.click_pixels({"x": value, "y": 0}, 1280, 800, "normalized_1000")
|
|
with self.assertRaises(ValueError):
|
|
desktop.click_pixels({"ymin": value, "xmin": 0, "ymax": 0, "xmax": 0}, 1280, 800, "normalized_1000")
|
|
|
|
def test_invalid_coordinate_space(self):
|
|
with self.assertRaises(ValueError):
|
|
desktop.validate({"actions": [], "coordinateSpace": "guess"}, 1280, 800)
|
|
|
|
def test_capture(self):
|
|
self.assertEqual(desktop.validate({"actions": []}, 100, 100), [])
|
|
|
|
def test_bounds(self):
|
|
for x in (-1, 100, True, "2"):
|
|
with self.assertRaises(ValueError):
|
|
desktop.validate({"actions": [{"type": "click", "x": x, "y": 0}]}, 100, 100)
|
|
with self.assertRaises(ValueError):
|
|
desktop.validate({"actions": [{"type": "click", "ymin": 0, "xmin": x, "ymax": 0, "xmax": 0}]}, 100, 100)
|
|
|
|
def test_invalid_keys(self):
|
|
with self.assertRaises(ValueError):
|
|
desktop.validate({"actions": [{"type": "keys", "keys": ["--repeat"]}]}, 100, 100)
|
|
|
|
def test_wait_budget(self):
|
|
with self.assertRaises(ValueError):
|
|
desktop.validate({"actions": [{"type": "wait", "milliseconds": 3000}] * 2}, 100, 100)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|