222 lines
6.7 KiB
Python
222 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Tests for the top-level Python run helper."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import builtins
|
|
import ctypes
|
|
import importlib
|
|
import sys
|
|
import types
|
|
|
|
|
|
class _Resources(ctypes.Structure):
|
|
_fields_ = [('window_host', ctypes.py_object)]
|
|
|
|
|
|
class _Raw(types.ModuleType):
|
|
def __init__(self):
|
|
super().__init__('datoviz.raw')
|
|
self.calls = []
|
|
self.app = object()
|
|
self.view = object()
|
|
self.window_host = object()
|
|
self.should_close = False
|
|
|
|
def dvz_app(self, scene):
|
|
self.calls.append(('app', scene))
|
|
return self.app
|
|
|
|
def dvz_app_with_resources(self, scene, config, resources):
|
|
self.calls.append(('app_with_resources', scene, config, resources._obj.window_host))
|
|
return self.app
|
|
|
|
def dvz_app_resources(self):
|
|
self.calls.append(('app_resources',))
|
|
return _Resources()
|
|
|
|
def dvz_view_window(self, app, figure, width, height, title):
|
|
self.calls.append(('view_window', app, figure, width, height, title))
|
|
return self.view
|
|
|
|
def dvz_app_run(self, app, frame_count):
|
|
self.calls.append(('app_run', app, frame_count))
|
|
|
|
def dvz_app_render_once(self, app):
|
|
self.calls.append(('app_render_once', app))
|
|
return 0
|
|
|
|
def dvz_app_reap_closed_views(self, app):
|
|
self.calls.append(('app_reap_closed_views', app))
|
|
return True
|
|
|
|
def dvz_view_request_frame(self, view):
|
|
self.calls.append(('view_request_frame', view))
|
|
return 0
|
|
|
|
def dvz_app_destroy(self, app):
|
|
self.calls.append(('app_destroy', app))
|
|
|
|
def dvz_scene_destroy(self, scene):
|
|
self.calls.append(('scene_destroy', scene))
|
|
|
|
def dvz_window_host(self):
|
|
self.calls.append(('window_host',))
|
|
return self.window_host
|
|
|
|
def dvz_window_host_poll(self, window_host):
|
|
self.calls.append(('window_host_poll', window_host))
|
|
|
|
def dvz_window_host_destroy(self, window_host):
|
|
self.calls.append(('window_host_destroy', window_host))
|
|
|
|
def dvz_window_should_close(self, window):
|
|
self.calls.append(('window_should_close', window))
|
|
return self.should_close
|
|
|
|
def dvz_app_should_exit(self, app):
|
|
self.calls.append(('app_should_exit', app))
|
|
return self.should_close
|
|
|
|
|
|
class TerminalInteractiveShell:
|
|
def __init__(self):
|
|
self.enabled = []
|
|
|
|
def enable_gui(self, name):
|
|
self.enabled.append(name)
|
|
|
|
|
|
class _NeverReadyContext:
|
|
def input_is_ready(self):
|
|
return False
|
|
|
|
|
|
def _install_raw(monkeypatch, datoviz):
|
|
raw = _Raw()
|
|
monkeypatch.setitem(sys.modules, 'datoviz.raw', raw)
|
|
monkeypatch.setattr(datoviz, 'raw', raw, raising=False)
|
|
return raw
|
|
|
|
|
|
def test_run_blocks_and_cleans_up_outside_ipython(monkeypatch):
|
|
import datoviz
|
|
|
|
raw = _install_raw(monkeypatch, datoviz)
|
|
monkeypatch.delattr(builtins, 'get_ipython', raising=False)
|
|
|
|
result = datoviz.run('scene', 'figure', 320, 240, 'Title')
|
|
|
|
assert result is None
|
|
assert ('app_run', raw.app, 0) in raw.calls
|
|
assert raw.calls[-1] == ('app_destroy', raw.app)
|
|
assert ('scene_destroy', 'scene') not in raw.calls
|
|
|
|
|
|
def test_run_returns_live_session_in_terminal_ipython(monkeypatch):
|
|
import datoviz
|
|
|
|
dvz_run = importlib.import_module('datoviz.run')
|
|
|
|
raw = _install_raw(monkeypatch, datoviz)
|
|
shell = TerminalInteractiveShell()
|
|
monkeypatch.setattr(builtins, 'get_ipython', lambda: shell, raising=False)
|
|
monkeypatch.setattr(dvz_run, '_enable_ipython_inputhook', lambda sh: sh is shell)
|
|
|
|
session = datoviz.run('scene', 'figure', 320, 240, 'Title')
|
|
|
|
assert session.running
|
|
assert ('app_run', raw.app, 0) not in raw.calls
|
|
assert ('app_with_resources', 'scene', None, raw.window_host) in raw.calls
|
|
assert ('view_request_frame', raw.view) in raw.calls
|
|
|
|
session.render_once()
|
|
assert raw.calls[-3:] == [
|
|
('window_host_poll', raw.window_host),
|
|
('app_should_exit', raw.app),
|
|
('app_render_once', raw.app),
|
|
]
|
|
|
|
session.close()
|
|
assert raw.calls[-2:] == [
|
|
('app_destroy', raw.app),
|
|
('window_host_destroy', raw.window_host),
|
|
]
|
|
assert ('scene_destroy', 'scene') not in raw.calls
|
|
|
|
|
|
def test_run_can_be_forced_blocking_in_terminal_ipython(monkeypatch):
|
|
import datoviz
|
|
|
|
raw = _install_raw(monkeypatch, datoviz)
|
|
monkeypatch.setattr(builtins, 'get_ipython', lambda: TerminalInteractiveShell(), raising=False)
|
|
|
|
result = datoviz.run('scene', 'figure', 320, 240, 'Title', blocking=True)
|
|
|
|
assert result is None
|
|
assert ('app_run', raw.app, 0) in raw.calls
|
|
assert ('scene_destroy', 'scene') not in raw.calls
|
|
|
|
|
|
def test_run_can_reopen_borrowed_scene_after_blocking_close(monkeypatch):
|
|
import datoviz
|
|
|
|
raw = _install_raw(monkeypatch, datoviz)
|
|
monkeypatch.delattr(builtins, 'get_ipython', raising=False)
|
|
|
|
datoviz.run('scene', 'figure', 320, 240, 'Title')
|
|
datoviz.run('scene', 'figure', 320, 240, 'Title')
|
|
|
|
assert [call[0] for call in raw.calls].count('app') == 2
|
|
assert [call[0] for call in raw.calls].count('app_run') == 2
|
|
assert [call[0] for call in raw.calls].count('app_destroy') == 2
|
|
assert ('scene_destroy', 'scene') not in raw.calls
|
|
|
|
|
|
def test_live_session_closes_when_native_window_requests_close(monkeypatch):
|
|
dvz_run = importlib.import_module('datoviz.run')
|
|
raw = _Raw()
|
|
session = dvz_run.RunSession(raw, 'scene', raw.app, raw.view, window_host=raw.window_host)
|
|
raw.should_close = True
|
|
|
|
assert session.render_once() is None
|
|
assert not session.running
|
|
assert raw.calls[-5:] == [
|
|
('window_host_poll', raw.window_host),
|
|
('app_should_exit', raw.app),
|
|
('app_reap_closed_views', raw.app),
|
|
('app_destroy', raw.app),
|
|
('window_host_destroy', raw.window_host),
|
|
]
|
|
assert ('scene_destroy', 'scene') not in raw.calls
|
|
|
|
|
|
def test_inputhook_reaps_and_returns_once_session_closes(monkeypatch):
|
|
dvz_run = importlib.import_module('datoviz.run')
|
|
raw = _Raw()
|
|
dvz_run._close_live_runs()
|
|
session = dvz_run.RunSession(
|
|
raw, 'scene', raw.app, raw.view, window_host=raw.window_host, fps=0
|
|
)
|
|
raw.should_close = True
|
|
|
|
monkeypatch.setattr(dvz_run.time, 'sleep', lambda seconds: None)
|
|
dvz_run._datoviz_inputhook(_NeverReadyContext())
|
|
|
|
assert session._closed
|
|
assert not session.running
|
|
assert raw.calls[-5:] == [
|
|
('window_host_poll', raw.window_host),
|
|
('app_should_exit', raw.app),
|
|
('app_reap_closed_views', raw.app),
|
|
('app_destroy', raw.app),
|
|
('window_host_destroy', raw.window_host),
|
|
]
|
|
|
|
|
|
def test_inputhook_returns_when_no_live_sessions():
|
|
dvz_run = importlib.import_module('datoviz.run')
|
|
|
|
dvz_run._close_live_runs()
|
|
dvz_run._datoviz_inputhook(_NeverReadyContext())
|