Pytest-like test framework for LSL — drives sakura-lslc and sakura-slemu
Project description
sakura-lsltest
A pytest-style test framework for LSL — by Sakura Studios, IKE.
sakura-lsltest drives sakura-lslc
(the offline compiler) and
sakura-slemu (the headless
region emulator) so you can write fully automated unit & integration
tests against real LSL source — no viewer, no in-world prim, no
manual button-clicking.
import lsltest
ALICE = "22222222-2222-2222-2222-222222222222"
@lsltest.compile_ok("scripts/vendor.lsl")
def test_vendor_compiles(): pass
@lsltest.scene(
scripts=["scripts/vendor.lsl"],
owner=("11111111-...", "Shop", 100),
avatars=[(ALICE, "Alice", 50)],
fixtures=[("https://api/x", 200, '{"ok":true}')],
)
def test_alice_buys_apple(world):
world.touch(link=1, avatar=ALICE)
world.assert_dialog_open(ALICE)
world.dialog_reply(avatar=ALICE, button="Apple")
world.assert_hud(link=1, substring="Sold Apple")
world.assert_said(kind="region-to", channel=0, substring="thanks")
world.assert_balance(ALICE, 51)
$ lsltest run examples/
PASS test_basics.py::test_broken_rejected
PASS test_basics.py::test_hello_compiles
PASS test_vendor.py::test_alice_buys_apple
PASS test_vendor.py::test_bob_cancels
Result: 76 passing / 76 tests
What you can assert
| Concern | API |
|---|---|
| Compiles cleanly | @lsltest.compile_ok(path) |
| Fails to compile (with diagnostic) | @lsltest.compile_fails(path, diagnostic=...) |
| Compiles but warns | @lsltest.compile_warns(path, warning=...) |
| Touch / dialog / textbox / listen / IM | world.touch / dialog_reply / textbox_reply / listen |
| Player pays the object | world.money_in(avatar=, amount=) |
| External system calls llRequestURL endpoint | world.http_in(url=, method=, body=) |
| Attach / detach as HUD | world.attach / detach |
| HUD floating text | world.assert_hud(link=, substring=) |
| What was said (any channel) | world.assert_said(kind=, channel=, substring=) |
| Dialog open for an avatar | world.assert_dialog_open(avatar=) |
| Avatar's L$ balance | world.assert_balance(avatar, amount) / world.balance(avatar) |
| Arbitrary event predicate | world.first("dialog", to=ALICE) |
| Inspect entire event stream | world.events("hud"), world.chat() |
Install
From PyPI
pip install sakura-lsltest
(plus the two C binaries — lslc and slemu from the sibling repos,
either built locally with make or installed system-wide.)
Arch Linux (AUR)
yay -S sakura-lsltest # latest tagged release
yay -S sakura-lsltest-git # follow main
See packaging/aur/README.md for the
PKGBUILDs.
From source (editable)
git clone https://github.com/Sakura-Studios-IKE/sakura-lsltest.git
cd sakura-lsltest
pip install -e .
Run
lsltest run tests/ # discover and run all test_*.py
lsltest run tests/ -k vendor # filter by name substring
lsltest run tests/ -v # show failure details
lsltest run tests/ --keep # keep slemu artefacts for inspection
lsltest run tests/ --lslc ./lslc --slemu ./slemu
Inside Python, the framework is a regular library — you can also call
World(), Scene(), and the decorators from a pytest run, a Hypothesis
strategy, a Makefile, or a CI step.
How it works
- Compile. For each test, lsltest calls
lslc -c <path>and parses the result; failures bubble up as Python assertion errors with the exact diagnostic stderr. - Scene setup. It writes a
world.cfgfile describing your avatars, owners, groups, and HTTP fixtures, plus asession.cmdsfile listing the player actions you queued. - Drive slemu. It launches
slemu --json-events --config … --commands …with all your compiled.lslbc. Every event slemu emits (chat, HUD update, dialog opening, money transfer, link-message, HTTP request) becomes a parseable JSON line. - Verify. Any
world.assert_*(…)calls embed assertions into the command file; slemu evaluates them in line with the rest of the simulation. Any failures (and any Python-side post-run checks viaworld.balance(…)/world.events(…)/world.first(…)) raiseLsltestErrorwhich the runner reports with full context.
Because every assertion is observable through the JSON event stream, your tests are deterministic, reproducible, and CI-friendly — same artefacts, same answers, every run.
Project layout
sakura-lsltest/
├── README.md
├── LICENSE MIT
├── pyproject.toml
├── Makefile
├── src/lsltest/
│ ├── __init__.py public API
│ ├── core.py decorators, World, Scene, runner
│ └── __main__.py `python -m lsltest` / `lsltest` CLI
└── examples/
├── scripts/ example LSL sources
├── test_basics.py compile-only tests
└── test_vendor.py interactive scene tests
Status
sakura-lsltest is the test harness in Sakura Studios' five-tool
open-source LSL toolchain:
sakura-lslc— offline compiler.sakura-slemu— headless region emulator.sakura-lsltest(this repo) — automated test harness.sakura-lsldb— gdb-style CLI debugger.sakura-intellij-lsl— IntelliJ plugin.
Author / Attribution
Authored and maintained by Shiho Sakura (@ShihoSakura) on behalf of Sakura Studios, IKE.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sakura_lsltest-1.0.0.tar.gz.
File metadata
- Download URL: sakura_lsltest-1.0.0.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca548736ec62ba782dffff840346eb4721603b22b9f35ef69907e284c951c8fb
|
|
| MD5 |
ec2decfc1a99fcd6971cd225717c48c6
|
|
| BLAKE2b-256 |
9cdcd91c9a32411fad1ccbb3a9b5d43ef0e4f15d99e7749863117d1932a77616
|
Provenance
The following attestation bundles were made for sakura_lsltest-1.0.0.tar.gz:
Publisher:
ci.yml on Sakura-Studios-IKE/sakura-lsltest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sakura_lsltest-1.0.0.tar.gz -
Subject digest:
ca548736ec62ba782dffff840346eb4721603b22b9f35ef69907e284c951c8fb - Sigstore transparency entry: 1548520428
- Sigstore integration time:
-
Permalink:
Sakura-Studios-IKE/sakura-lsltest@1c6659afb70acf25605fee3fe73d0feba98d0c74 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/Sakura-Studios-IKE
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@1c6659afb70acf25605fee3fe73d0feba98d0c74 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sakura_lsltest-1.0.0-py3-none-any.whl.
File metadata
- Download URL: sakura_lsltest-1.0.0-py3-none-any.whl
- Upload date:
- Size: 11.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2f1a3a64543fefc792a1278d3180e13f42d3921dbdedb96a911240e232e0472
|
|
| MD5 |
3e468c5b2c8f2b01ece965af84176044
|
|
| BLAKE2b-256 |
f87050e6f1e967e76b94d6f7ecc08c540bf87318111c10c42ea70e33cbb0993c
|
Provenance
The following attestation bundles were made for sakura_lsltest-1.0.0-py3-none-any.whl:
Publisher:
ci.yml on Sakura-Studios-IKE/sakura-lsltest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sakura_lsltest-1.0.0-py3-none-any.whl -
Subject digest:
a2f1a3a64543fefc792a1278d3180e13f42d3921dbdedb96a911240e232e0472 - Sigstore transparency entry: 1548520495
- Sigstore integration time:
-
Permalink:
Sakura-Studios-IKE/sakura-lsltest@1c6659afb70acf25605fee3fe73d0feba98d0c74 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/Sakura-Studios-IKE
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@1c6659afb70acf25605fee3fe73d0feba98d0c74 -
Trigger Event:
push
-
Statement type: