Compare commits
7 Commits
v0.2.1
...
3fb982b057
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fb982b057 | |||
| 53553dc1fa | |||
| 717727bf5a | |||
| d97d00c593 | |||
| 2b0c4b5ee0 | |||
| 59e63e1338 | |||
| de32a524da |
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).
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ This element is of the following form:
|
|||||||
- let:
|
- let:
|
||||||
name: Let Item
|
name: Let Item
|
||||||
values:
|
values:
|
||||||
key1: value1
|
- key1: value1
|
||||||
key2: value2
|
- key2: value2
|
||||||
key3: <| $(variable)[$(loop_index)] |>
|
- key3: <| $(variable)[$(loop_index)] |>
|
||||||
|
|
||||||
The ``let`` element is used to set values in the global directory.
|
The ``let`` element is used to set values in the global directory.
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ The parameter file can be specified in the `.tum` file root:
|
|||||||
:caption: configuration files definition in the main `.tum` test file
|
:caption: configuration files definition in the main `.tum` test file
|
||||||
|
|
||||||
config_file:
|
config_file:
|
||||||
config1.yaml
|
- config1.yaml
|
||||||
config2.yaml
|
- config2.yaml
|
||||||
|
|
||||||
main:
|
main:
|
||||||
name: Test example
|
name: Test example
|
||||||
|
|||||||
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.1
|
0.2.2
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ class TestItemConsoleReadUntil(TestItemConsoleAction):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
cons = self.get_console()
|
cons = self.get_console()
|
||||||
ru = self._prms.expanse(self._read_until)
|
ru = self._prms.expanse(self._read_until)
|
||||||
read_timeout = int(self._prms.getParam("timeout", default=-1, processed=True))
|
read_timeout = float(self._prms.getParam("timeout", default=-1, processed=True))
|
||||||
mute = self._prms.getParam("mute", default=False, processed=True)
|
mute = self._prms.getParam("mute", default=False, processed=True)
|
||||||
if read_timeout < 0:
|
if read_timeout < 0:
|
||||||
read_timeout = None
|
read_timeout = None
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -51,14 +51,18 @@ class TestFileManager:
|
|||||||
w.disconnect_signals()
|
w.disconnect_signals()
|
||||||
# Snapshot user-selected checkboxes and fold state so they survive a
|
# Snapshot user-selected checkboxes and fold state so they survive a
|
||||||
# reload of the same file (same logic as session-restore through prefs).
|
# reload of the same file (same logic as session-restore through prefs).
|
||||||
|
# checkList works only if show_checkboxes is True
|
||||||
previous_check_list = w.treeTests.getCheckList()
|
previous_check_list = w.treeTests.getCheckList()
|
||||||
previous_fold_list = w.treeTests.getFoldList()
|
previous_fold_list = w.treeTests.getFoldList()
|
||||||
previous_count = w.treeTests.getItemCount()
|
previous_count = w.treeTests.getItemCount()
|
||||||
self.clear_process()
|
self.clear_process()
|
||||||
if self.load(file_name) and w.test_service is not None:
|
if self.load(file_name) and \
|
||||||
if w.treeTests.getItemCount() == previous_count:
|
w.test_service is not None and \
|
||||||
w.treeTests.restoreCheckList(previous_check_list, w.test_service)
|
w.treeTests.getItemCount() == previous_count:
|
||||||
|
if prefs.settings.show_checkboxes :
|
||||||
|
w.treeTests.restoreCheckList(previous_check_list, w.test_service)
|
||||||
w.treeTests.restoreFoldList(previous_fold_list)
|
w.treeTests.restoreFoldList(previous_fold_list)
|
||||||
|
|
||||||
w.reconnect_signals()
|
w.reconnect_signals()
|
||||||
|
|
||||||
def _make_progress(self, w):
|
def _make_progress(self, w):
|
||||||
|
|||||||
@@ -84,7 +84,18 @@
|
|||||||
- read_until: {expected: HelloConsole, timeout: 1, mute: true}
|
- read_until: {expected: HelloConsole, timeout: 1, mute: true}
|
||||||
|
|
||||||
- console:
|
- console:
|
||||||
name: Console read_until muted
|
name: Console read_until float timeout
|
||||||
|
console_name: term
|
||||||
|
key: $(test)_PASS
|
||||||
|
steps:
|
||||||
|
- writeln: echo "HelloConsole"
|
||||||
|
{% if os == "Windows" %}
|
||||||
|
- read_until: {expected: echo "HelloConsole", timeout: 0.2}
|
||||||
|
{% endif %}
|
||||||
|
- read_until: {expected: HelloConsole, timeout: 0.2}
|
||||||
|
|
||||||
|
- console:
|
||||||
|
name: Console read_until process result
|
||||||
console_name: term
|
console_name: term
|
||||||
key: $(test)_PASS
|
key: $(test)_PASS
|
||||||
steps:
|
steps:
|
||||||
@@ -94,6 +105,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
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
- let:
|
- let:
|
||||||
name: Let it be
|
name: Let it be
|
||||||
values:
|
values:
|
||||||
it: $(loop_param)
|
- it: $(loop_param)
|
||||||
be: <| $(loop_param) == $(it) |>
|
- be: <| $(loop_param) == $(it) |>
|
||||||
|
|
||||||
- loop:
|
- loop:
|
||||||
name: Cycle iterating on list
|
name: Cycle iterating on list
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
- let:
|
- let:
|
||||||
name: lua_func test constants,
|
name: lua_func test constants,
|
||||||
values:
|
values:
|
||||||
lua_func test parameter: test parameter lua_func
|
- lua_func test parameter: test parameter lua_func
|
||||||
|
|
||||||
- lua_func:
|
- lua_func:
|
||||||
name: fail lua_func
|
name: fail lua_func
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
- let:
|
- let:
|
||||||
name: py_func test constants,
|
name: py_func test constants,
|
||||||
values:
|
values:
|
||||||
py_func test parameter: test parameter
|
- py_func test parameter: test parameter
|
||||||
|
|
||||||
- py_func:
|
- py_func:
|
||||||
name: pass py_func
|
name: pass py_func
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
main:
|
|
||||||
name: run sub-test (always fail)
|
|
||||||
steps:
|
|
||||||
- check:
|
|
||||||
name: fail
|
|
||||||
values:
|
|
||||||
- false
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
main:
|
|
||||||
name: run sub-test (always pass)
|
|
||||||
steps:
|
|
||||||
- check:
|
|
||||||
name: pass
|
|
||||||
values:
|
|
||||||
- true
|
|
||||||
@@ -31,7 +31,11 @@ main:
|
|||||||
|
|
||||||
{% for item in items %}
|
{% for item in items %}
|
||||||
# item test
|
# item test
|
||||||
- let: {name: {{ item }} test constants, values: {test: {{ item }}, test_path: items/$(test)}}
|
- let:
|
||||||
|
name: {{ item }} test constants
|
||||||
|
values:
|
||||||
|
- test: {{ item }}
|
||||||
|
- test_path: items/$(test)
|
||||||
- group:
|
- group:
|
||||||
name: {{ item }} test
|
name: {{ item }} test
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
Reference in New Issue
Block a user