_testium_launch_cmd() returns the right entry point per mode (AppImage,
Flatpak, PyInstaller, source/wheel). Fixes PermissionError on read-only
__main__.py inside the AppImage squashfs mount.
_which() probes host dirs only in Flatpak (/run/host/usr/bin) and AppImage
(/usr/bin); apply_host_libs prepares env for host subprocesses (prepend host
libs in Flatpak, strip $APPDIR pollution + PYTHONHOME in AppImage); user
override resolved via _which() for bare names.
stdout/stderr of the subprocesses were going to DEVNULL — early-startup
errors (lua require failures, exceptions before stdio_redir kicks in)
were lost.
New helper proc_drain.drain_to_log spawns a daemon thread per pipe that
print()s each line through stdio_redir, so it reaches the log + live
output. Used by py_process and lua_process with [py_func]/[lua_func]
prefixes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
get_testium_version() used pkg_resources (deprecated, slow to import)
and a narrow catch on git.InvalidGitRepositoryError; any other git
exception fell through to the outer except and returned "unknown".
- Use importlib.metadata.version("testium") to read the wheel
version that setuptools bakes from src/VERSION at build time. Works
out of any source checkout — pip-installed copies report
"<x.y> (wheel release)" instead of "unknown".
- Source-checkout path: tried first when prefs.git_supported, broadly
catches Exception so a missing repo / detached worktree / etc. no
longer hides the wheel-metadata fallback.
- PyInstaller path: graceful "unknown (binary release)" if the bundled
VERSION file is unreadable, instead of an unhandled exception.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- README.md: pruned developer-oriented sections (Sphinx setup, Qt
Creator workflow, VSCode debugging, release procedure, AppImage
Wayland note) and replaced them with a user-facing layout: pre-built
releases pointer, quick start, manual install, troubleshooting,
licence.
- CONTRIBUTING.md: absorbed the developer content (debugging in VSCode,
Qt GUI regen, Sphinx build, validation suite — batch + GUI variants,
cross-distrib check, release procedure).
- doc/quick_start.md: 5-minute path from install to a passing test,
in batch mode and in the GUI.
- doc/tutorial.md: guided walk-through against a small calc.py
module — check, py_func, expected_result, $(...) expansion, group,
let, condition, report (with the mkdir reminder), context_id.
- CLAUDE.md: subprocess API contract, bins.py, report-exporter
plugin section, packaging matrix (wheel / PyInstaller / Flatpak /
.deb work-in-progress), refreshed recent-fixes list. README/CLAUDE
validation command no longer carries the spurious "-l" flag (which
is GUI-only and a no-op in batch).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Added some api accessible from python and lua sub_processes. Now the tests only access to py_func.tm instead of direct api.testium module access.
Corrected some f"xxx" to allow working with old python (bookworm).
Changed param.yaml of the test to allow lua to work in all situations.
Various other small fixes for frozen app, wheel.
Tested in all situations, and OK. Ready for tag !
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Drop the now-obsolete src/lib and src/py_func data entries (those
paths no longer exist)
- Add src/testium/py_func and src/testium/runtime as bundle-root data
dirs: the py_func subprocess is launched with the *host* Python
(not the frozen interpreter), so it needs the source files on disk
at cwd=subproc_path() to find py_func/__main__.py and import from
runtime.*
- Hidden imports updated: libs.* → api.*, plus py_func.* explicitly
declared so PyInstaller pulls them into the bundle even though
they are loaded as data
Smoke-tested: built binary runs `testium -b`, py_func subprocess works.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move src/lib/ → src/testium/runtime/ (internal plumbing)
Move src/testium/libs/ → src/testium/api/ (public SDK for test scripts)
Move src/py_func/ → src/testium/py_func/ (Python subprocess)
Move src/lua_func/ → src/testium/lua_func/ (Lua subprocess data)
The package now ships as a single coherent unit instead of four sibling
top-level packages (testium, lib, py_func, lua_func) — pip install
gives a clean site-packages/testium/ with no namespace pollution; .lua
files travel with the wheel via package_data; the wheel installs
cleanly and `testium -b` runs end-to-end including py_func subprocesses
and entry-point exporter plugins.
Naming:
- runtime/ (internal, no API guarantees) clearer than lib/
- api/ (public SDK consumed as `import api.testium as tm`) clearer than libs/
Imports updated en masse: from lib. → from runtime. and from libs. →
from api., plus the importlib.import_module("libs.*") strings in
test_item_console.py and test_item_runtime_plot.py. Test/example
scripts (helper_lib.py, parallel.py, post_execution.py) and the
fake_exporter test suite migrated too.
paths.py: subproc_path() now returns testium_path() — both point at
the testium package directory since the subprocesses live inside.
pyproject.toml: removed exclude=["lua_func", "py_func"] (no longer
needed), added package-data for testium.lua_func/*.lua, removed the
license classifier (PEP 639 conflict with license expression).
Subprocess isolation contract: py_func/ and lua_func/ may only import
runtime/ and their own modules — never interpreter/, main_win/, api/,
or testium/. Enforced by test/validation/items/isolation/ which runs a
py_func that statically scans subprocess source files for forbidden
imports. The contract holds today; the test prevents future drift.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the hardcoded if/elif in Export.exec() with a dict registry.
Built-in formats (text, json, junit, html) are registered as lazy
loaders; missing optional deps (junit_xml, lxml) print a clear message
with a pip install hint instead of raising. Entry-points
(group "testium.exporters") are discovered at import time — installed
plugins are auto-detected with no extra config.
An unknown or unavailable format prints an info line and skips the
export; the test run is not interrupted.
Validation:
- New testium-fake-exporter package under test/validation/fake_exporter/
installed automatically by scripts/build_env.sh on venv creation.
It registers fake_format via entry-points and exports the tests
table to CSV — a real, useful exporter that exercises the plugin
contract end-to-end (entry-point discovery, dispatch, SQLite query).
- New dedicated items/report_plugin/ test exercises both the
unknown-format skip path and the fake_format plugin path, with a
py_func check (file_check.py) on the produced CSV. Runs once per
validation suite.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merge the two "Recent fixes" sections into one (branches are gone),
add parallel_branch icon, F1 panel, test-tree state, unittest rename,
run item rename, licence. Fix SUCCESS/FAILURE → PASS/FAIL in run item.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add LICENSE (full EUPL-1.2 text + project copyright), CONTRIBUTING.md
with the inbound = outbound rule, and declare the licence in
pyproject.toml and README.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reload (re-loading the same .tum file) was rebuilding the GUI tree
from scratch, resetting every checkbox and unfolding everything.
Snapshot the user's selection and fold state via the existing
getCheckList/restoreCheckList and getFoldList/restoreFoldList methods
(already used for session persistence through prefs), so a same-file
reload keeps both as well. A change in the total item count (file
edited between loads) skips the restore.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A parallel branch now displays a single-arrow icon (parallel_branch.png)
distinct from the parallel container's three-arrow icon, making the
tree hierarchy easier to read at a glance.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The 'branches' list shown in the F1 panel previously contained each
branch's full 'steps', duplicating what is already visible in the test
tree. Strip 'steps' inside each branch dict while keeping 'branches'
itself so per-branch attributes (name, wait_for, condition, ...) stay
visible.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The parallel item runs branches concurrently with sync:all or sync:any
policy and optional per-branch wait_for synchronization. Each branch
runs in its own daemon thread and produces a clean per-item entry in
the SQLite report; the live output is prefixed [<branch_name>] so
concurrent branches stay readable.
Supporting changes:
- StdoutProxy (lib/stdout_redirect.py): thread-aware sys.stdout/stderr
with per-thread capture buffers and per-branch live-output prefix.
Adds writeln() for Python 3.14 unittest compatibility.
- TestItemContainer: shared base extracted from Group/Cycle for the
sequential children execution pattern.
- TestItemSleep: interruptible loop polling _is_stopped so sync:any
can cancel slow branches quickly.
- TestReport: thread-safe SQLite (check_same_thread=False + lock).
Also drops the unused -m/--terminal mode and its module.
Validation: 11 scenarios in test/validation/items/parallel covering
sync:all/any, no_fail, wait_for + timeout, conditions, multi-branch,
nested parallel, parallel inside loop, real branch failure.
Documentation: new parallel_test_item.rst added to the manual.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Clarify result semantics (SUCCESS on launch, not on sub-test result),
batch vs GUI mode behaviour, and clean up attribute descriptions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- run item: rename tum_fime→tum, remove stdout=PIPE (deadlock with
spawn), support batch mode (-b), SUCCESS on any completed subprocess
regardless of sub-test result
- batch.py: fix control("loaded") deadlock via daemon thread + Event +
is_alive() polling; fix premature finish on gd_update messages;
propagate success flag from finished message; guard control("close")
- process.py: include success flag in send_finished message
- py_process/lua_process: add stdout/stderr=DEVNULL to Popen
- test_run.py: fix finished detection ("id" in m and m["id"] is None)
- testium_win.py: track run_exit_code, SIGABRT handler, clean exit
- __init__.py: sys.exit with batch success flag
- Add run item validation tests and CLAUDE.md documentation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each dialog test item now accepts an optional auto_result parameter
(ok/cancel/yes/no) and auto_value for text dialogs. When set, the dialog
window opens, stays visible 2 seconds, then closes automatically with the
specified result — allowing the validation suite to run without manual
interaction.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allows any test item to store its result (or PASS/FAIL status when result
is None) into a named global variable, available to subsequent items via
$(variable_name). store_result runs after expected_result but before
no_fail so the real outcome is always captured.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without expected_result, a False process_result value does not fail the
test. Adding expected_result: True makes the comparison fail as intended.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the external jrpces binary dependency with a self-contained
Python script. The server supports TCP (newline-delimited JSON, port 4321)
and UDP (port 4323), handles JSON-RPC 1.0 and 2.0, and implements:
- echo(*args) -> [args, {}]
- unknown methods -> error {code: -32000, message: "function not found"}
test.tum is updated to launch jrpc_echo_server.py via python3 and wait
for the "ready" readiness message before running tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dialog subprocesses were forked from TestProcess, inheriting its
multiprocessing Queue objects and their process-shared POSIX semaphores
(_wlock). If a fork happened while the feeder thread held _wlock, the
child exited without releasing it, permanently blocking the feeder
thread on the next wacquire() and stalling Python's atexit _finalize_join
— causing test_proc.join() (no timeout) to hang the app for ~15 seconds.
Fix: use multiprocessing.get_context('spawn') for dialog subprocesses so
they start with a clean interpreter and inherit no semaphores or Queue
state. Also add a terminate/kill fallback timeout to test_proc.join() as
a safety net, and fix the missing return in JsonRpcConnection.is_alive().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add dialog_env.py service: forces QT_QPA_PLATFORM=xcb on Linux so Qt
doesn't crash under Wayland in spawned subprocesses
- Use QMessageBox instance (instead of static methods) for msg/question
dialogs so WindowStaysOnTopHint can be set, making them visible
- Add TestItemDialogBase with _run_dialog/_run_dialog_with_result/_cleanup_process,
removing duplicated subprocess launch/poll/terminate logic from all 7 dialog items
- Reduce terminate() join timeout from 2s to 0.2s across all dialog items
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add item_load_context() context manager to tum_except.py that enriches
ETUMSyntaxError with the item type, name, and parent path instead of
replacing the original message with a vague 'missing or wrong parameter'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- process.py: stop context_id engines in the inner finally block, before
restore_gd() wipes _py_func_contexts/_lua_func_contexts from the global
dict — engines were previously orphaned after every test run
- py_func/tm.py: add user-facing docstrings to gd/setgd/delgd; remove
internal JSON-serialization details from the docs
- helper_lib.rst: auto-generate global variable helpers from py_func.tm
(the actual subprocess API) instead of globdict
- conf.py: add src/ to Sphinx sys.path so py_func.tm is importable
- py_func_test_item.rst: simplify context sharing section, remove
JSON-serializable/non-serializable distinction for end users
- Regenerated PDF manual
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>