tests: rework path_args and klippy fixtures
Initialize the temporary directory in a "session_args" fixture that is session scoped. Move the Klippy process to a session scope, restarting the process only when necessary. This combination speeds up tests as it reduces disk I/O and the overhead of starting the Klippy process for each test. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
81f2418d78
commit
55baad1ad0
|
@ -17,6 +17,8 @@ from fixtures import KlippyProcess, HttpClient, WebsocketClient
|
||||||
|
|
||||||
ASSETS = pathlib.Path(__file__).parent.joinpath("assets")
|
ASSETS = pathlib.Path(__file__).parent.joinpath("assets")
|
||||||
|
|
||||||
|
need_klippy_restart = pytest.StashKey[bool]()
|
||||||
|
|
||||||
def pytest_addoption(parser: pytest.Parser, pluginmanager):
|
def pytest_addoption(parser: pytest.Parser, pluginmanager):
|
||||||
parser.addoption("--klipper-path", action="store", dest="klipper_path")
|
parser.addoption("--klipper-path", action="store", dest="klipper_path")
|
||||||
parser.addoption("--klipper-exec", action="store", dest="klipper_exec")
|
parser.addoption("--klipper-exec", action="store", dest="klipper_exec")
|
||||||
|
@ -55,25 +57,17 @@ def event_loop() -> Iterator[asyncio.AbstractEventLoop]:
|
||||||
yield loop
|
yield loop
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
|
||||||
def path_args(request: pytest.FixtureRequest,
|
@pytest.fixture(scope="session")
|
||||||
ssl_certs: Dict[str, pathlib.Path]
|
def session_args(ssl_certs: Dict[str, pathlib.Path]
|
||||||
) -> Iterator[Dict[str, pathlib.Path]]:
|
) -> Iterator[Dict[str, pathlib.Path]]:
|
||||||
path_marker = request.node.get_closest_marker("run_paths")
|
mconf_asset = ASSETS.joinpath(f"moonraker/base_server.conf")
|
||||||
paths = {
|
secrets_asset = ASSETS.joinpath(f"moonraker/secrets.ini")
|
||||||
"moonraker_conf": "base_server.conf",
|
pcfg_asset = ASSETS.joinpath(f"klipper/base_printer.cfg")
|
||||||
"secrets": "secrets.ini",
|
|
||||||
"printer_cfg": "base_printer.cfg"
|
|
||||||
}
|
|
||||||
if path_marker is not None:
|
|
||||||
paths.update(path_marker.kwargs)
|
|
||||||
moon_cfg_path = ASSETS.joinpath(f"moonraker/{paths['moonraker_conf']}")
|
|
||||||
secrets_path = ASSETS.joinpath(f"moonraker/{paths['secrets']}")
|
|
||||||
pcfg_path = ASSETS.joinpath(f"klipper/{paths['printer_cfg']}")
|
|
||||||
with tempfile.TemporaryDirectory(prefix="moonraker-test") as tmpdir:
|
with tempfile.TemporaryDirectory(prefix="moonraker-test") as tmpdir:
|
||||||
tmp_path = pathlib.Path(tmpdir)
|
tmp_path = pathlib.Path(tmpdir)
|
||||||
secrets_dest = tmp_path.joinpath(paths['secrets'])
|
secrets_dest = tmp_path.joinpath("secrets.ini")
|
||||||
shutil.copy(secrets_path, secrets_dest)
|
shutil.copy(secrets_asset, secrets_dest)
|
||||||
cfg_path = tmp_path.joinpath("config")
|
cfg_path = tmp_path.joinpath("config")
|
||||||
cfg_path.mkdir()
|
cfg_path.mkdir()
|
||||||
log_path = tmp_path.joinpath("logs")
|
log_path = tmp_path.joinpath("logs")
|
||||||
|
@ -83,6 +77,7 @@ def path_args(request: pytest.FixtureRequest,
|
||||||
gcode_path = tmp_path.joinpath("gcode_files")
|
gcode_path = tmp_path.joinpath("gcode_files")
|
||||||
gcode_path.mkdir()
|
gcode_path.mkdir()
|
||||||
dest_paths = {
|
dest_paths = {
|
||||||
|
"temp_path": tmp_path,
|
||||||
"asset_path": ASSETS,
|
"asset_path": ASSETS,
|
||||||
"config_path": cfg_path,
|
"config_path": cfg_path,
|
||||||
"database_path": db_path,
|
"database_path": db_path,
|
||||||
|
@ -92,26 +87,22 @@ def path_args(request: pytest.FixtureRequest,
|
||||||
"klippy_uds_path": tmp_path.joinpath("klippy_uds"),
|
"klippy_uds_path": tmp_path.joinpath("klippy_uds"),
|
||||||
"klippy_pty_path": tmp_path.joinpath("klippy_pty"),
|
"klippy_pty_path": tmp_path.joinpath("klippy_pty"),
|
||||||
"klipper.dict": ASSETS.joinpath("klipper/klipper.dict"),
|
"klipper.dict": ASSETS.joinpath("klipper/klipper.dict"),
|
||||||
|
"mconf_asset": mconf_asset,
|
||||||
|
"pcfg_asset": pcfg_asset,
|
||||||
}
|
}
|
||||||
dest_paths.update(ssl_certs)
|
dest_paths.update(ssl_certs)
|
||||||
if "moonraker_log" in paths:
|
mconf_dest = cfg_path.joinpath("moonraker.conf")
|
||||||
dest_paths['moonraker.log'] = log_path.joinpath(
|
dest_paths["moonraker.conf"] = mconf_dest
|
||||||
paths["moonraker_log"])
|
interpolate_config(mconf_asset, mconf_dest, dest_paths)
|
||||||
moon_cfg_dest = cfg_path.joinpath("moonraker.conf")
|
|
||||||
interpolate_config(moon_cfg_path, moon_cfg_dest, dest_paths)
|
|
||||||
dest_paths['moonraker.conf'] = moon_cfg_dest
|
|
||||||
pcfg_dest = cfg_path.joinpath("printer.cfg")
|
pcfg_dest = cfg_path.joinpath("printer.cfg")
|
||||||
interpolate_config(pcfg_path, pcfg_dest, dest_paths)
|
dest_paths["printer.cfg"] = pcfg_dest
|
||||||
dest_paths['printer.cfg'] = pcfg_dest
|
interpolate_config(pcfg_asset, pcfg_dest, dest_paths)
|
||||||
if "moonraker_bkp" in paths:
|
|
||||||
bkp_source = ASSETS.joinpath("moonraker/base_server.conf")
|
|
||||||
bkp_dest = cfg_path.joinpath(paths["moonraker_bkp"])
|
|
||||||
interpolate_config(bkp_source, bkp_dest, dest_paths)
|
|
||||||
yield dest_paths
|
yield dest_paths
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="session")
|
||||||
def klippy(path_args: Dict[str, pathlib.Path],
|
def klippy_session(session_args: Dict[str, pathlib.Path],
|
||||||
pytestconfig: pytest.Config) -> Iterator[KlippyProcess]:
|
pytestconfig: pytest.Config) -> Iterator[KlippyProcess]:
|
||||||
|
pytestconfig.stash[need_klippy_restart] = False
|
||||||
kpath = pytestconfig.getoption('klipper_path', "~/klipper")
|
kpath = pytestconfig.getoption('klipper_path', "~/klipper")
|
||||||
kexec = pytestconfig.getoption('klipper_exec', None)
|
kexec = pytestconfig.getoption('klipper_exec', None)
|
||||||
if kexec is None:
|
if kexec is None:
|
||||||
|
@ -119,11 +110,81 @@ def klippy(path_args: Dict[str, pathlib.Path],
|
||||||
exec = pathlib.Path(kexec).expanduser()
|
exec = pathlib.Path(kexec).expanduser()
|
||||||
klipper_path = pathlib.Path(kpath).expanduser()
|
klipper_path = pathlib.Path(kpath).expanduser()
|
||||||
base_cmd = f"{exec} {klipper_path}/klippy/klippy.py "
|
base_cmd = f"{exec} {klipper_path}/klippy/klippy.py "
|
||||||
kproc = KlippyProcess(base_cmd, path_args)
|
kproc = KlippyProcess(base_cmd, session_args)
|
||||||
kproc.start()
|
kproc.start()
|
||||||
yield kproc
|
yield kproc
|
||||||
kproc.stop()
|
kproc.stop()
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def klippy(klippy_session: KlippyProcess,
|
||||||
|
pytestconfig: pytest.Config):
|
||||||
|
if pytestconfig.stash[need_klippy_restart]:
|
||||||
|
pytestconfig.stash[need_klippy_restart] = False
|
||||||
|
klippy_session.restart()
|
||||||
|
return klippy_session
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def path_args(request: pytest.FixtureRequest,
|
||||||
|
session_args: Dict[str, pathlib.Path],
|
||||||
|
pytestconfig: pytest.Config
|
||||||
|
) -> Iterator[Dict[str, pathlib.Path]]:
|
||||||
|
path_marker = request.node.get_closest_marker("run_paths")
|
||||||
|
paths: Dict[str, Any] = {
|
||||||
|
"moonraker_conf": "base_server.conf",
|
||||||
|
"secrets": "secrets.ini",
|
||||||
|
"printer_cfg": "base_printer.cfg",
|
||||||
|
"klippy_uds": None
|
||||||
|
}
|
||||||
|
if path_marker is not None:
|
||||||
|
paths.update(path_marker.kwargs)
|
||||||
|
tmp_path = session_args["temp_path"]
|
||||||
|
cfg_path = session_args["config_path"]
|
||||||
|
mconf_dest = session_args["moonraker.conf"]
|
||||||
|
mconf_asset = ASSETS.joinpath(f"moonraker/{paths['moonraker_conf']}")
|
||||||
|
pcfg_asset = ASSETS.joinpath(f"klipper/{paths['printer_cfg']}")
|
||||||
|
last_uds = session_args["klippy_uds_path"]
|
||||||
|
if paths["klippy_uds"] is not None:
|
||||||
|
tmp_uds = tmp_path.joinpath(paths["klippy_uds"])
|
||||||
|
session_args["klippy_uds_path"] = tmp_uds
|
||||||
|
if (
|
||||||
|
not mconf_asset.samefile(session_args["mconf_asset"]) or
|
||||||
|
paths["klippy_uds"] is not None
|
||||||
|
):
|
||||||
|
session_args['mconf_asset'] = mconf_asset
|
||||||
|
interpolate_config(mconf_asset, mconf_dest, session_args)
|
||||||
|
if not pcfg_asset.samefile(session_args["pcfg_asset"]):
|
||||||
|
pcfg_dest = session_args["printer.cfg"]
|
||||||
|
session_args["pcfg_asset"] = pcfg_asset
|
||||||
|
interpolate_config(pcfg_asset, pcfg_dest, session_args)
|
||||||
|
pytestconfig.stash[need_klippy_restart] = True
|
||||||
|
if paths["secrets"] != session_args["secrets_path"].name:
|
||||||
|
secrets_asset = ASSETS.joinpath(f"moonraker/{paths['secrets']}")
|
||||||
|
secrets_dest = tmp_path.joinpath(paths['secrets'])
|
||||||
|
shutil.copy(secrets_asset, secrets_dest)
|
||||||
|
session_args["secrets_path"] = secrets_dest
|
||||||
|
if "moonraker_log" in paths:
|
||||||
|
log_path = session_args["log_path"]
|
||||||
|
session_args['moonraker.log'] = log_path.joinpath(
|
||||||
|
paths["moonraker_log"])
|
||||||
|
bkp_dest: pathlib.Path = cfg_path.joinpath(".moonraker.conf.bkp")
|
||||||
|
if "moonraker_bkp" in paths:
|
||||||
|
bkp_source = ASSETS.joinpath("moonraker/base_server.conf")
|
||||||
|
bkp_dest = cfg_path.joinpath(paths["moonraker_bkp"])
|
||||||
|
interpolate_config(bkp_source, bkp_dest, session_args)
|
||||||
|
yield session_args
|
||||||
|
log = session_args.pop("moonraker.log", None)
|
||||||
|
if log is not None and log.is_file():
|
||||||
|
log.unlink()
|
||||||
|
if bkp_dest.is_file():
|
||||||
|
bkp_dest.unlink()
|
||||||
|
for item in session_args["database_path"].iterdir():
|
||||||
|
if item.is_file():
|
||||||
|
item.unlink()
|
||||||
|
session_args["klippy_uds_path"] = last_uds
|
||||||
|
if paths["klippy_uds"] is not None:
|
||||||
|
# restore the original uds path
|
||||||
|
interpolate_config(mconf_asset, mconf_dest, session_args)
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="class")
|
||||||
def base_server(path_args: Dict[str, pathlib.Path],
|
def base_server(path_args: Dict[str, pathlib.Path],
|
||||||
event_loop: asyncio.AbstractEventLoop
|
event_loop: asyncio.AbstractEventLoop
|
||||||
|
|
|
@ -476,7 +476,7 @@ class TestBackupConfig:
|
||||||
|
|
||||||
def test_backup_config_success(self, path_args: Dict[str, pathlib.Path]):
|
def test_backup_config_success(self, path_args: Dict[str, pathlib.Path]):
|
||||||
cfg_path = path_args["moonraker.conf"]
|
cfg_path = path_args["moonraker.conf"]
|
||||||
bkp_dest = cfg_path.parent.joinpath(".moonraker.conf.bkp")
|
bkp_dest = cfg_path.parent.joinpath(f".{cfg_path.name}.bkp")
|
||||||
if bkp_dest.exists():
|
if bkp_dest.exists():
|
||||||
pytest.fail("Backup Already Exists")
|
pytest.fail("Backup Already Exists")
|
||||||
confighelper.backup_config(str(cfg_path))
|
confighelper.backup_config(str(cfg_path))
|
||||||
|
@ -484,7 +484,7 @@ class TestBackupConfig:
|
||||||
|
|
||||||
def test_backup_skip(self, path_args: Dict[str, pathlib.Path]):
|
def test_backup_skip(self, path_args: Dict[str, pathlib.Path]):
|
||||||
cfg_path = path_args["moonraker.conf"]
|
cfg_path = path_args["moonraker.conf"]
|
||||||
bkp_dest = cfg_path.parent.joinpath(".moonraker.conf.bkp")
|
bkp_dest = cfg_path.parent.joinpath(f".{cfg_path.name}.bkp")
|
||||||
if not bkp_dest.exists():
|
if not bkp_dest.exists():
|
||||||
pytest.fail("Backup Not Present")
|
pytest.fail("Backup Not Present")
|
||||||
stat = bkp_dest.stat()
|
stat = bkp_dest.stat()
|
||||||
|
@ -493,6 +493,6 @@ class TestBackupConfig:
|
||||||
|
|
||||||
def test_find_backup(self, path_args: Dict[str, pathlib.Path]):
|
def test_find_backup(self, path_args: Dict[str, pathlib.Path]):
|
||||||
cfg_path = path_args["moonraker.conf"]
|
cfg_path = path_args["moonraker.conf"]
|
||||||
bkp_dest = cfg_path.parent.joinpath(".moonraker.conf.bkp")
|
bkp_dest = cfg_path.parent.joinpath(f".{cfg_path.name}.bkp")
|
||||||
bkp = confighelper.find_config_backup(str(cfg_path))
|
bkp = confighelper.find_config_backup(str(cfg_path))
|
||||||
assert bkp == str(bkp_dest)
|
assert bkp == str(bkp_dest)
|
||||||
|
|
|
@ -57,34 +57,21 @@ async def test_klippy_shutdown(ready_server: Server, klippy: KlippyProcess):
|
||||||
await asyncio.wait_for(fut, 2.)
|
await asyncio.wait_for(fut, 2.)
|
||||||
assert fut.result() == "shutdown"
|
assert fut.result() == "shutdown"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_klippy_disconnect(ready_server: Server, klippy: KlippyProcess):
|
|
||||||
evtloop = ready_server.get_event_loop()
|
|
||||||
fut = evtloop.create_future()
|
|
||||||
|
|
||||||
def on_disconnect():
|
|
||||||
if not fut.done():
|
|
||||||
fut.set_result("disconnect")
|
|
||||||
ready_server.register_event_handler("server:klippy_disconnect",
|
|
||||||
on_disconnect)
|
|
||||||
klippy.stop()
|
|
||||||
await asyncio.wait_for(fut, 2.)
|
|
||||||
assert fut.result() == "disconnect"
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_klippy_reconnect(ready_server: Server, klippy: KlippyProcess):
|
async def test_klippy_reconnect(ready_server: Server, klippy: KlippyProcess):
|
||||||
evtloop = ready_server.get_event_loop()
|
evtloop = ready_server.get_event_loop()
|
||||||
fut = evtloop.create_future()
|
futs = [evtloop.create_future() for _ in range(2)]
|
||||||
|
events = {
|
||||||
def on_reconnect():
|
"server:klippy_disconnect": lambda: futs[0].set_result("disconnect"),
|
||||||
if not fut.done():
|
"server:klippy_ready": lambda: futs[1].set_result("ready")
|
||||||
fut.set_result("test")
|
}
|
||||||
ready_server.register_event_handler("server:klippy_ready",
|
for name, func in events.items():
|
||||||
on_reconnect)
|
ready_server.register_event_handler(name, func)
|
||||||
klippy.send_gcode("RESTART")
|
klippy.restart()
|
||||||
await asyncio.wait_for(fut, 4.)
|
ret = await asyncio.wait_for(asyncio.gather(*futs), 6.)
|
||||||
assert fut.result() == "test"
|
assert ret == ["disconnect", "ready"]
|
||||||
|
|
||||||
|
@pytest.mark.run_paths(klippy_uds="fake_uds")
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_no_klippy_connection_error(full_server: Server):
|
async def test_no_klippy_connection_error(full_server: Server):
|
||||||
await full_server.start_server()
|
await full_server.start_server()
|
||||||
|
@ -152,6 +139,7 @@ async def test_wait_connect_fail(base_server: Server):
|
||||||
ret = await base_server.klippy_connection.wait_connected()
|
ret = await base_server.klippy_connection.wait_connected()
|
||||||
assert ret is False
|
assert ret is False
|
||||||
|
|
||||||
|
@pytest.mark.run_paths(klippy_uds="fake_uds")
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_no_uds(base_server: Server):
|
async def test_no_uds(base_server: Server):
|
||||||
attempts = [1, 2, 3]
|
attempts = [1, 2, 3]
|
||||||
|
@ -163,6 +151,7 @@ async def test_no_uds(base_server: Server):
|
||||||
ret = await base_server.klippy_connection._do_connect()
|
ret = await base_server.klippy_connection._do_connect()
|
||||||
assert ret is False
|
assert ret is False
|
||||||
|
|
||||||
|
@pytest.mark.run_paths(klippy_uds="fake_uds")
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_no_uds_access(base_server: Server,
|
async def test_no_uds_access(base_server: Server,
|
||||||
path_args: Dict[str, pathlib.Path]):
|
path_args: Dict[str, pathlib.Path]):
|
||||||
|
|
Loading…
Reference in New Issue