Compare commits
7 Commits
perf/load-
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9dae210f7f | |||
| d97d00c593 | |||
| 2b0c4b5ee0 | |||
| 59e63e1338 | |||
| de32a524da | |||
| 2515213b14 | |||
| 0376b77494 |
16
README.md
16
README.md
@@ -129,6 +129,22 @@ A VSCode / VSCodium client extension (`testium_assist`) wraps `testium lsp`;
|
|||||||
the schema is built from testium itself, so new item types and parameters
|
the schema is built from testium itself, so new item types and parameters
|
||||||
appear in the editor on the next testium upgrade with no client change.
|
appear in the editor on the next testium upgrade with no client change.
|
||||||
|
|
||||||
|
It is published on [Open VSX](https://open-vsx.org/extension/testium/testium-assist),
|
||||||
|
so in **VSCodium, Cursor, Windsurf, Theia and code-server** it installs from the
|
||||||
|
Extensions view (search `testium-assist`) or with
|
||||||
|
`codium --install-extension testium.testium-assist`.
|
||||||
|
|
||||||
|
**Microsoft VSCode** does not list Open VSX extensions, so install the `.vsix`
|
||||||
|
by hand — download it from the Open VSX page above, then *Extensions → ⋯ →
|
||||||
|
Install from VSIX…* or:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
code --install-extension testium-assist-0.1.0.vsix
|
||||||
|
```
|
||||||
|
|
||||||
|
The extension runs `testium lsp`, so `testium` must be on the `PATH` (otherwise
|
||||||
|
point the `testium.serverPath` setting at the binary/AppImage).
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### `wl_proxy_marshal_flags` symbol error
|
### `wl_proxy_marshal_flags` symbol error
|
||||||
|
|||||||
@@ -67,3 +67,36 @@ dependencies:
|
|||||||
:caption: enable the language server for a wheel / source install
|
:caption: enable the language server for a wheel / source install
|
||||||
|
|
||||||
pip install 'testium[lsp]'
|
pip install 'testium[lsp]'
|
||||||
|
|
||||||
|
Installing the VSCode / VSCodium extension
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The *testium_assist* client extension is published on `Open VSX
|
||||||
|
<https://open-vsx.org/extension/testium/testium-assist>`_, the registry used by
|
||||||
|
VSCodium, Cursor, Windsurf, Eclipse Theia and code-server. In those editors,
|
||||||
|
open the Extensions view and search ``testium-assist``, or install it from the
|
||||||
|
command line:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
:caption: install in VSCodium and other Open VSX editors
|
||||||
|
|
||||||
|
codium --install-extension testium.testium-assist
|
||||||
|
|
||||||
|
Microsoft *VSCode* uses a different marketplace that does not list Open VSX
|
||||||
|
extensions, so install the packaged ``.vsix`` by hand. Download it from the
|
||||||
|
Open VSX page linked above, then either choose *Extensions* → *⋯* →
|
||||||
|
*Install from VSIX…* in the UI, or run:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
:caption: install the .vsix in Microsoft VSCode
|
||||||
|
|
||||||
|
code --install-extension testium-assist-0.1.0.vsix
|
||||||
|
|
||||||
|
The extension launches ``testium lsp``, so the ``testium`` command must be on
|
||||||
|
the ``PATH``. If *testium* is installed elsewhere — a specific binary or an
|
||||||
|
AppImage — point the ``testium.serverPath`` setting at it instead.
|
||||||
|
|
||||||
|
Once installed, open a ``.tum`` file: completion of item types, hover
|
||||||
|
documentation and the outline view become available. If nothing happens, check
|
||||||
|
that no ``files.associations`` entry forces ``*.tum`` to another language (it
|
||||||
|
must stay the ``tum`` language the extension provides).
|
||||||
|
|||||||
Binary file not shown.
@@ -1,3 +1,9 @@
|
|||||||
|
version 0.2.2
|
||||||
|
==============
|
||||||
|
- Flatpak sandbox issue fixed for term console. Now a term console is
|
||||||
|
exactly like a host console.
|
||||||
|
- Persistence fix of dialogs in case of flatpak.
|
||||||
|
|
||||||
version 0.2.1
|
version 0.2.1
|
||||||
==============
|
==============
|
||||||
- Faster test loading, especially for large tests built from jinja
|
- Faster test loading, especially for large tests built from jinja
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.2
|
0.2.2
|
||||||
|
|||||||
@@ -11,6 +11,16 @@ sys.path.append(os.path.abspath(ourpath.parent))
|
|||||||
import interpreter.utils.constants as cst
|
import interpreter.utils.constants as cst
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# Force UTF-8 on stdout/stderr so the runner's output survives a legacy
|
||||||
|
# console code page (Windows cp1252 can't encode box-drawing/accented
|
||||||
|
# chars). Only the stream encoders change; the locale default used for
|
||||||
|
# config files is untouched.
|
||||||
|
for _stream in (sys.stdout, sys.stderr):
|
||||||
|
try:
|
||||||
|
_stream.reconfigure(encoding="utf-8")
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
pass # no stdout (frozen GUI) or non-reconfigurable stream
|
||||||
|
|
||||||
# Subcommand dispatch (must run *before* argparse so neither 'schema' nor
|
# Subcommand dispatch (must run *before* argparse so neither 'schema' nor
|
||||||
# 'lsp' has to share the GUI/batch flag surface). The subcommands also
|
# 'lsp' has to share the GUI/batch flag surface). The subcommands also
|
||||||
# skip the multiprocessing 'spawn' setup which is only meaningful for the
|
# skip the multiprocessing 'spawn' setup which is only meaningful for the
|
||||||
|
|||||||
@@ -81,9 +81,13 @@ class TermConsole(Console):
|
|||||||
bufsize=0)
|
bufsize=0)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.term = pexpect.spawn( shell_cmd,
|
# In Flatpak this returns a `flatpak-spawn --host` wrapper so the
|
||||||
echo=False,
|
# console behaves like a host shell (matching py_func / lua_func /
|
||||||
cwd=self.ppath)
|
# run); elsewhere it's the chosen command unchanged.
|
||||||
|
from interpreter.utils import bins
|
||||||
|
argv = bins.host_console_command(shell_cmd, self.ppath)
|
||||||
|
self.term = pexpect.spawn(argv[0], args=argv[1:],
|
||||||
|
echo=False, cwd=self.ppath)
|
||||||
|
|
||||||
self.q = BytesStore()
|
self.q = BytesStore()
|
||||||
self.t = threading.Thread(target=self.enqueue_output)
|
self.t = threading.Thread(target=self.enqueue_output)
|
||||||
|
|||||||
@@ -221,6 +221,11 @@ def main(args, conn=None):
|
|||||||
|
|
||||||
if conn:
|
if conn:
|
||||||
settings.setValue(SettingsLastChoices, result)
|
settings.setValue(SettingsLastChoices, result)
|
||||||
|
# Flush before sending: the parent terminates this subprocess as soon
|
||||||
|
# as it reads the result, so the QSettings destructor never runs and
|
||||||
|
# the write would race the kill (lost under Flatpak — see the
|
||||||
|
# tested-references dialog for the full rationale).
|
||||||
|
settings.sync()
|
||||||
conn.send([result, success])
|
conn.send([result, success])
|
||||||
conn.close()
|
conn.close()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -76,6 +76,12 @@ def main(args, conn=None):
|
|||||||
|
|
||||||
if conn:
|
if conn:
|
||||||
settings.setValue(SettingsLastReference, result)
|
settings.setValue(SettingsLastReference, result)
|
||||||
|
# Flush to disk *before* handing the result back: as soon as the parent
|
||||||
|
# receives it on the pipe it terminates this subprocess (SIGTERM, no
|
||||||
|
# handler), so the QSettings destructor never runs. Without sync() the
|
||||||
|
# write races the kill and is lost — reliably so under Flatpak, where
|
||||||
|
# the .conf is atomically renamed on the slower ~/.var/app overlay.
|
||||||
|
settings.sync()
|
||||||
conn.send([result, success])
|
conn.send([result, success])
|
||||||
conn.close()
|
conn.close()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class ReportExportHTML(rpe.ReportExport):
|
|||||||
self.prepareFile()
|
self.prepareFile()
|
||||||
self.create_base()
|
self.create_base()
|
||||||
self.process_tests()
|
self.process_tests()
|
||||||
with open(self._file_name, 'w') as f:
|
with open(self._file_name, 'w', encoding="utf-8") as f:
|
||||||
f.write(lxml.html.tostring(self.root, pretty_print=True).decode())
|
f.write(lxml.html.tostring(self.root, pretty_print=True).decode())
|
||||||
|
|
||||||
def testsIterate(self, row):
|
def testsIterate(self, row):
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class ReportExportJUnit(rpe.ReportExport):
|
|||||||
|
|
||||||
ts = TestSuite(repname, test_cases=self.test_cases,
|
ts = TestSuite(repname, test_cases=self.test_cases,
|
||||||
hostname=tm.gd('host_ip'))
|
hostname=tm.gd('host_ip'))
|
||||||
with open(self._file_name, 'w') as f:
|
with open(self._file_name, 'w', encoding="utf-8") as f:
|
||||||
TestSuite.to_file(f, [ts])
|
TestSuite.to_file(f, [ts])
|
||||||
|
|
||||||
def testsIterate(self, row):
|
def testsIterate(self, row):
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ Public API
|
|||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -177,6 +178,27 @@ def flatpak_host_spawn(interp_bin, cmd_args, host_cwd, extra_env=None):
|
|||||||
return spawn
|
return spawn
|
||||||
|
|
||||||
|
|
||||||
|
def host_console_command(shell_cmd, cwd):
|
||||||
|
"""Build the argv to start *shell_cmd* as an ordinary interactive console.
|
||||||
|
|
||||||
|
*shell_cmd* is the command the caller chose (a string — shell-split — or
|
||||||
|
an argv list); the choice is preserved verbatim.
|
||||||
|
|
||||||
|
Outside Flatpak the command is returned unchanged. Inside Flatpak a bare
|
||||||
|
spawn would run in the sandbox under the runtime python3, so a host venv
|
||||||
|
(``/path/venv/bin/python3 -m mod``) can't see its pip deps. We simply run
|
||||||
|
it on the host with ``flatpak-spawn --host`` so it behaves like any other
|
||||||
|
terminal: flatpak-spawn passes the current environment through unchanged
|
||||||
|
and the shell (sourced venv, profile, …) sets things up as the user wants.
|
||||||
|
No env forwarding or scrubbing — the launcher's leaked PYTHONPATH points at
|
||||||
|
/app paths absent on the host, so it's inert there.
|
||||||
|
"""
|
||||||
|
argv = shlex.split(shell_cmd) if isinstance(shell_cmd, str) else list(shell_cmd)
|
||||||
|
if not _in_flatpak():
|
||||||
|
return argv
|
||||||
|
return ["flatpak-spawn", "--host", f"--directory={cwd}", *argv]
|
||||||
|
|
||||||
|
|
||||||
def _which_host_flatpak(name):
|
def _which_host_flatpak(name):
|
||||||
"""Resolve a binary name (or absolute path) on the host via flatpak-spawn.
|
"""Resolve a binary name (or absolute path) on the host via flatpak-spawn.
|
||||||
|
|
||||||
|
|||||||
95
src/testium/main_win/desktop_integration.py
Normal file
95
src/testium/main_win/desktop_integration.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
"""Install a desktop entry + icon under the user's data dir so desktop shells
|
||||||
|
show the testium icon in the task bar / dock.
|
||||||
|
|
||||||
|
On a native Wayland session GNOME takes a window's task-bar icon from the
|
||||||
|
``.desktop`` file whose name (or ``StartupWMClass``) matches the window
|
||||||
|
``app_id`` — ``QGuiApplication.setWindowIcon`` is ignored there. The portable
|
||||||
|
channels (source checkout, PyInstaller binary, AppImage) install no system
|
||||||
|
desktop file, so we drop an idempotent one in ``~/.local/share``. The window
|
||||||
|
``app_id`` is set to ``testium`` (see ``QApplication.setDesktopFileName`` in
|
||||||
|
``testium_win``), which is exactly this file's base name.
|
||||||
|
|
||||||
|
Flatpak ships its own ``org.testium.Testium.desktop`` and keeps its own app id,
|
||||||
|
so the caller skips this integration there.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
from PySide6.QtGui import QPixmap
|
||||||
|
|
||||||
|
# Must match QApplication.setDesktopFileName(...) for the GUI, and is used as
|
||||||
|
# both the desktop-file base name and the StartupWMClass.
|
||||||
|
APP_ID = "testium"
|
||||||
|
|
||||||
|
|
||||||
|
def _launch_command():
|
||||||
|
"""Best-effort Exec= for the menu entry. Not needed for icon matching, but
|
||||||
|
makes the entry actually launchable when possible."""
|
||||||
|
appimage = os.environ.get("APPIMAGE")
|
||||||
|
if appimage:
|
||||||
|
return f'"{appimage}"'
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
return f'"{os.path.abspath(sys.executable)}"'
|
||||||
|
argv0 = os.path.abspath(sys.argv[0]) if sys.argv and sys.argv[0] else ""
|
||||||
|
if argv0 and os.path.exists(argv0):
|
||||||
|
return f'"{os.path.abspath(sys.executable)}" "{argv0}"'
|
||||||
|
return f'"{os.path.abspath(sys.executable)}" -m testium'
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_desktop_entry():
|
||||||
|
"""Create (or refresh) ~/.local/share icon + desktop entry. Best-effort:
|
||||||
|
any failure is swallowed so it can never take the GUI down.
|
||||||
|
|
||||||
|
Freedesktop-only: a no-op off Linux (Windows / macOS use the window icon)."""
|
||||||
|
if not sys.platform.startswith("linux"):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
data_home = os.environ.get("XDG_DATA_HOME") or os.path.join(
|
||||||
|
os.path.expanduser("~"), ".local", "share"
|
||||||
|
)
|
||||||
|
icon_dir = os.path.join(data_home, "icons", "hicolor", "256x256", "apps")
|
||||||
|
app_dir = os.path.join(data_home, "applications")
|
||||||
|
icon_path = os.path.join(icon_dir, f"{APP_ID}.png")
|
||||||
|
desktop_path = os.path.join(app_dir, f"{APP_ID}.desktop")
|
||||||
|
|
||||||
|
os.makedirs(icon_dir, exist_ok=True)
|
||||||
|
os.makedirs(app_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Icon: render the bundled Qt resource to a PNG once. Requires a live
|
||||||
|
# QGuiApplication (the caller creates it before calling us).
|
||||||
|
if not os.path.isfile(icon_path):
|
||||||
|
pixmap = QPixmap(u":/black/testium_logo.png")
|
||||||
|
if not pixmap.isNull():
|
||||||
|
pixmap = pixmap.scaled(
|
||||||
|
256, 256,
|
||||||
|
Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
|
Qt.TransformationMode.SmoothTransformation,
|
||||||
|
)
|
||||||
|
pixmap.save(icon_path, "PNG")
|
||||||
|
|
||||||
|
# Absolute Icon= path so the shell resolves it without an icon-cache
|
||||||
|
# refresh; StartupWMClass lets X11 / XWayland match too.
|
||||||
|
desktop = (
|
||||||
|
"[Desktop Entry]\n"
|
||||||
|
"Type=Application\n"
|
||||||
|
"Name=Testium\n"
|
||||||
|
"Comment=Test sequencer\n"
|
||||||
|
f"Icon={icon_path}\n"
|
||||||
|
f"Exec={_launch_command()} %f\n"
|
||||||
|
"Terminal=false\n"
|
||||||
|
f"StartupWMClass={APP_ID}\n"
|
||||||
|
"Categories=Utility;Development;\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write only when missing or changed, to avoid needless menu churn.
|
||||||
|
current = None
|
||||||
|
if os.path.isfile(desktop_path):
|
||||||
|
with open(desktop_path, "r") as fh:
|
||||||
|
current = fh.read()
|
||||||
|
if current != desktop:
|
||||||
|
with open(desktop_path, "w") as fh:
|
||||||
|
fh.write(desktop)
|
||||||
|
except Exception:
|
||||||
|
# Desktop integration is a nicety, never a hard requirement.
|
||||||
|
pass
|
||||||
@@ -678,6 +678,24 @@ def MainWin(
|
|||||||
debug=False,
|
debug=False,
|
||||||
):
|
):
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
# Application identity so desktop shells (GNOME, ...) show the testium
|
||||||
|
# icon in the task bar / dock instead of a generic one. On Wayland this
|
||||||
|
# sets the surface app_id; on X11/XWayland it sets WM_CLASS, so the window
|
||||||
|
# stops inheriting the launcher's class (e.g. "python3" under the AppImage,
|
||||||
|
# which is what GNOME was keying the wrong icon off) and the window icon
|
||||||
|
# below is used as the fallback. In Flatpak the id must be the Flatpak app
|
||||||
|
# id so it matches the installed desktop file.
|
||||||
|
app.setApplicationName("Testium")
|
||||||
|
app.setApplicationDisplayName("Testium")
|
||||||
|
app.setDesktopFileName(os.environ.get("FLATPAK_ID", "testium"))
|
||||||
|
app.setWindowIcon(QIcon(u":/black/testium_logo.png"))
|
||||||
|
# On native Wayland the task-bar icon comes from an installed desktop file
|
||||||
|
# matched to the app_id, not from setWindowIcon(). Flatpak ships its own;
|
||||||
|
# for the other Linux channels drop an idempotent one under ~/.local/share.
|
||||||
|
# Windows / macOS use the window icon set above, so this is Linux-only.
|
||||||
|
if sys.platform.startswith("linux") and not os.environ.get("FLATPAK_ID"):
|
||||||
|
from main_win.desktop_integration import ensure_desktop_entry
|
||||||
|
ensure_desktop_entry()
|
||||||
ui = MainWindow(
|
ui = MainWindow(
|
||||||
test_file,
|
test_file,
|
||||||
config_files,
|
config_files,
|
||||||
|
|||||||
@@ -94,6 +94,17 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
- read_until: {expected: endOfCmd, timeout: 1, process_result: "'Hello' in r'''$(result)''' and 'PASS' in r'''$(result)''' "}
|
- read_until: {expected: endOfCmd, timeout: 1, process_result: "'Hello' in r'''$(result)''' and 'PASS' in r'''$(result)''' "}
|
||||||
|
|
||||||
|
{% if os == "Linux" %}
|
||||||
|
- console:
|
||||||
|
name: Console runs on host (not the Flatpak sandbox)
|
||||||
|
doc: Regression guard for the 0.2.1 Flatpak bug (term console spawned inside the sandbox instead of on the host). /.flatpak-info exists only inside the sandbox, so the host-only marker is emitted (and matched by read_until) ONLY when the shell really runs on the host. On a broken Flatpak the marker never appears, read_until times out and the item FAILS. The marker is built at runtime ($M) so it is never present in the command line itself. Passes on every other channel.
|
||||||
|
console_name: term
|
||||||
|
key: $(test)_PASS
|
||||||
|
steps:
|
||||||
|
- writeln: 'test -e /.flatpak-info && M=SANDBOX || M=HOST; echo "console_host_check_$M"'
|
||||||
|
- read_until: {expected: console_host_check_HOST, timeout: 5}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
- console:
|
- console:
|
||||||
name: Console closure
|
name: Console closure
|
||||||
execute_on_stop: true
|
execute_on_stop: true
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
console_name: jrpces
|
console_name: jrpces
|
||||||
key: $(test)_PASS
|
key: $(test)_PASS
|
||||||
steps:
|
steps:
|
||||||
- writeln: python3 {{include_directory}}/jrpc_echo_server.py -c {{include_directory}}/jrpces.ini
|
- writeln: '"$(python_bin)" {{include_directory}}/jrpc_echo_server.py -c {{include_directory}}/jrpces.ini'
|
||||||
- read_until: {expected: ready, timeout: 5}
|
- read_until: {expected: ready, timeout: 5}
|
||||||
|
|
||||||
- console:
|
- console:
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ def exec():
|
|||||||
junit_report = report.replace(".sqlite", f"-{test}.xml")
|
junit_report = report.replace(".sqlite", f"-{test}.xml")
|
||||||
print(junit_report)
|
print(junit_report)
|
||||||
_prepare_file_to_save(junit_report)
|
_prepare_file_to_save(junit_report)
|
||||||
with open(junit_report, "w") as f:
|
with open(junit_report, "w", encoding="utf-8") as f:
|
||||||
f.write(TestSuite.to_xml_string([ts]))
|
f.write(TestSuite.to_xml_string([ts]))
|
||||||
|
|
||||||
# cleanup
|
# cleanup
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ REM Reports are stamped with the mode so successive runs don't clobber each othe
|
|||||||
|
|
||||||
SET "TAIL=-b -d "python_bin=%VENV_PYTHON%" -d "validation_report_file=validation-%MODE%" -- "%SCRIPT_DIR%\main.tum"%EXTRA%"
|
SET "TAIL=-b -d "python_bin=%VENV_PYTHON%" -d "validation_report_file=validation-%MODE%" -- "%SCRIPT_DIR%\main.tum"%EXTRA%"
|
||||||
|
|
||||||
|
REM The report-exporter plugin (items\report_plugin) is a pip entry-point
|
||||||
|
REM package. It must live in the *testium* environment, so it is installed into
|
||||||
|
REM the source/wheel venvs below. A frozen PyInstaller binary cannot see
|
||||||
|
REM externally-installed plugins, so report_plugin is expected to be skipped
|
||||||
|
REM there (same as Linux pyinstaller mode).
|
||||||
|
SET "FAKE_EXPORTER=%SCRIPT_DIR%\fake_exporter"
|
||||||
|
|
||||||
REM ---------- per-mode launcher ----------------------------------------------
|
REM ---------- per-mode launcher ----------------------------------------------
|
||||||
|
|
||||||
echo -- validation mode: %MODE%
|
echo -- validation mode: %MODE%
|
||||||
@@ -100,8 +107,25 @@ echo ERROR: unknown --mode '%MODE%'. Expected: source ^| wheel ^| pyinstaller.
|
|||||||
exit /b 1
|
exit /b 1
|
||||||
|
|
||||||
:MODE_SOURCE
|
:MODE_SOURCE
|
||||||
call "%PROJECT_DIR%\run.bat" %TAIL%
|
REM Run testium from src\ in a dedicated venv set up here. We do NOT delegate to
|
||||||
exit /b %ERRORLEVEL%
|
REM the project's run.bat: that one launches the GUI and does not forward its
|
||||||
|
REM arguments, so the suite would never run head-less.
|
||||||
|
SET "TESTIUM_VENV=%PROJECT_DIR%\test\tmp\testium_venv"
|
||||||
|
IF NOT EXIST "%TESTIUM_VENV%" (
|
||||||
|
echo Creating testium venv at %TESTIUM_VENV%
|
||||||
|
%PYTHON_EXE% -m venv "%TESTIUM_VENV%"
|
||||||
|
IF !ERRORLEVEL! NEQ 0 (
|
||||||
|
echo ERROR while creating the testium venv.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
call "%TESTIUM_VENV%\Scripts\pip" install --quiet --upgrade pip
|
||||||
|
call "%TESTIUM_VENV%\Scripts\pip" install --quiet -r "%PROJECT_DIR%\src\requirements.txt"
|
||||||
|
REM language-server extra so `testium lsp` works from source (lsp_check.py)
|
||||||
|
call "%TESTIUM_VENV%\Scripts\pip" install --quiet "pygls>=1.3"
|
||||||
|
)
|
||||||
|
call "%TESTIUM_VENV%\Scripts\pip" install --quiet -e "%FAKE_EXPORTER%"
|
||||||
|
SET CMD="%TESTIUM_VENV%\Scripts\python.exe" "%PROJECT_DIR%\src\testium"
|
||||||
|
GOTO LAUNCH
|
||||||
|
|
||||||
:MODE_WHEEL
|
:MODE_WHEEL
|
||||||
SET "WHEEL=%PROJECT_DIR%\dist\testium-%VERSION%-py3-none-any.whl"
|
SET "WHEEL=%PROJECT_DIR%\dist\testium-%VERSION%-py3-none-any.whl"
|
||||||
@@ -115,10 +139,13 @@ IF NOT EXIST "%WHEEL_VENV%" (
|
|||||||
echo Creating wheel venv at %WHEEL_VENV%
|
echo Creating wheel venv at %WHEEL_VENV%
|
||||||
%PYTHON_EXE% -m venv --system-site-packages "%WHEEL_VENV%"
|
%PYTHON_EXE% -m venv --system-site-packages "%WHEEL_VENV%"
|
||||||
call "%WHEEL_VENV%\Scripts\pip" install --quiet --upgrade pip
|
call "%WHEEL_VENV%\Scripts\pip" install --quiet --upgrade pip
|
||||||
call "%WHEEL_VENV%\Scripts\pip" install --quiet "%WHEEL%"
|
REM install with the [lsp] extra so the wheel channel is validated in its
|
||||||
|
REM language-server-capable form (pulls pygls), matching `pip install testium[lsp]`.
|
||||||
|
call "%WHEEL_VENV%\Scripts\pip" install --quiet "%WHEEL%[lsp]"
|
||||||
)
|
)
|
||||||
"%WHEEL_VENV%\Scripts\python.exe" -m testium %TAIL%
|
call "%WHEEL_VENV%\Scripts\pip" install --quiet -e "%FAKE_EXPORTER%"
|
||||||
exit /b %ERRORLEVEL%
|
SET CMD="%WHEEL_VENV%\Scripts\python.exe" -m testium
|
||||||
|
GOTO LAUNCH
|
||||||
|
|
||||||
:MODE_PYI
|
:MODE_PYI
|
||||||
SET "PYI_BIN=%PROJECT_DIR%\dist\testium-%VERSION%.exe"
|
SET "PYI_BIN=%PROJECT_DIR%\dist\testium-%VERSION%.exe"
|
||||||
@@ -127,5 +154,22 @@ IF NOT EXIST "%PYI_BIN%" (
|
|||||||
echo ERROR: PyInstaller binary not found in %PROJECT_DIR%\dist -- run build_all.sh first.
|
echo ERROR: PyInstaller binary not found in %PROJECT_DIR%\dist -- run build_all.sh first.
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
"%PYI_BIN%" %TAIL%
|
SET CMD="%PYI_BIN%"
|
||||||
|
GOTO LAUNCH
|
||||||
|
|
||||||
|
REM ---------- launch ----------------------------------------------------------
|
||||||
|
|
||||||
|
:LAUNCH
|
||||||
|
echo -- launch: %CMD%
|
||||||
|
|
||||||
|
REM LSP check (this exact channel): `schema` must keep its nested actions and
|
||||||
|
REM `lsp` must answer initialize. Mirrors run.sh; aborts the run on failure.
|
||||||
|
echo -- LSP check (%MODE%)
|
||||||
|
"%VENV_PYTHON%" "%SCRIPT_DIR%\lsp_check.py" %CMD%
|
||||||
|
IF !ERRORLEVEL! NEQ 0 (
|
||||||
|
echo ERROR: LSP check failed for mode %MODE%.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%CMD% %TAIL%
|
||||||
exit /b %ERRORLEVEL%
|
exit /b %ERRORLEVEL%
|
||||||
|
|||||||
Reference in New Issue
Block a user