Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions .github/workflows/lua-lazy-mutation-property.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
name: Lua Lazy Mutation Property Stress

on:
schedule:
- cron: "17 3 * * 0"
workflow_dispatch:
inputs:
cases:
description: "Override QJSON_MUT_PROP_CASES (default: 4000)"
required: false
steps:
description: "Override QJSON_MUT_PROP_STEPS (default: 200)"
required: false
seed:
description: "Override QJSON_MUT_PROP_SEED (random if empty)"
required: false

env:
CARGO_TERM_COLOR: always

jobs:
lua-mutation-property:
name: Lazy mutation property suite
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false

- name: Install Rust (stable)
run: |
rustup toolchain install stable --profile minimal --no-self-update
rustup default stable

- name: Cache cargo registry & target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: mut-prop-${{ runner.os }}-${{ hashFiles('Cargo.toml', 'Cargo.lock', 'Makefile') }}
restore-keys: |
mut-prop-${{ runner.os }}-

- name: Install LuaJIT
uses: leafo/gh-actions-lua@v13
with:
luaVersion: "luajit-2.1.0-beta3"

- name: Install LuaRocks
run: |
sudo apt-get update
sudo apt-get install -y lua5.1 liblua5.1-0-dev luarocks

- name: Build cdylib (release)
run: cargo build --release

- name: Install Lua dependencies
run: |
# Ubuntu LuaRocks targets Lua 5.1 by default; LuaJIT is ABI-compatible.
sudo luarocks install busted
sudo luarocks install lua-cjson

- name: Resolve LuaJIT executable
id: luajit
run: |
set -euo pipefail
for candidate in luajit luajit-2.1.0-beta3 lua; do
if command -v "$candidate" >/dev/null 2>&1; then
lua_bin="$(command -v "$candidate")"
if "$lua_bin" -e 'assert(jit, "LuaJIT required")' >/dev/null 2>&1; then
echo "path=$lua_bin" >> "$GITHUB_OUTPUT"
"$lua_bin" -e 'print(jit.version)'
exit 0
fi
fi
done
echo "LuaJIT executable not found" >&2
exit 1

- name: Resolve stress parameters
id: params
env:
DISPATCH_CASES: ${{ github.event.inputs.cases }}
DISPATCH_STEPS: ${{ github.event.inputs.steps }}
DISPATCH_SEED: ${{ github.event.inputs.seed }}
run: |
set -euo pipefail
cases="${DISPATCH_CASES:-}"
steps="${DISPATCH_STEPS:-}"
seed="${DISPATCH_SEED:-}"

if [ -z "$cases" ]; then
cases=4000
fi
if [ -z "$steps" ]; then
steps=200
fi
if [ -z "$seed" ]; then
seed="$(python3 - <<'PY'
import secrets
print(secrets.randbelow(2**31 - 1) + 1)
PY
)"
fi

require_positive_int() {
name="$1"
value="$2"
if ! [[ "$value" =~ ^[1-9][0-9]*$ ]]; then
echo "$name must be a positive integer, got '$value'" >&2
exit 1
fi
}

require_positive_int cases "$cases"
require_positive_int steps "$steps"
require_positive_int seed "$seed"

echo "cases=$cases" >> "$GITHUB_OUTPUT"
echo "steps=$steps" >> "$GITHUB_OUTPUT"
echo "seed=$seed" >> "$GITHUB_OUTPUT"

- name: Run lazy mutation property suite
env:
LUAJIT: ${{ steps.luajit.outputs.path }}
QJSON_MUT_PROP_CASES: ${{ steps.params.outputs.cases }}
QJSON_MUT_PROP_STEPS: ${{ steps.params.outputs.steps }}
QJSON_MUT_PROP_SEED: ${{ steps.params.outputs.seed }}
run: |
echo "QJSON_MUT_PROP_CASES=$QJSON_MUT_PROP_CASES"
echo "QJSON_MUT_PROP_STEPS=$QJSON_MUT_PROP_STEPS"
echo "QJSON_MUT_PROP_SEED=$QJSON_MUT_PROP_SEED"
make lua-mutation-property-test
18 changes: 18 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,21 @@ The property suite generates valid JSON containers, runs
`decode -> materialize -> encode -> decode -> materialize`, checks structural
equality, and probes the encoder max-depth boundary around 1000 nested
containers.

## Lua lazy mutation property tests

Lazy mutation path coverage is in `tests/lua/lazy_mutation_property_spec.lua`
and uses deterministic defaults in the Makefile so PR runs are reproducible:

```sh
make lua-mutation-property-test
```

Stress this focused suite locally by overriding case/step count and seed:

```sh
make lua-mutation-property-test \
QJSON_MUT_PROP_CASES=4000 \
QJSON_MUT_PROP_STEPS=200 \
QJSON_MUT_PROP_SEED=12345
```
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ OPENRESTY_RESTY := $(OPENRESTY)/bin/resty
LUAJIT ?= $(shell if [ -x "$(OPENRESTY_LUAJIT)" ]; then echo "$(OPENRESTY_LUAJIT)"; else command -v luajit 2>/dev/null || echo luajit; fi)
RESTY ?= $(shell if [ -x "$(OPENRESTY_RESTY)" ]; then echo "$(OPENRESTY_RESTY)"; else command -v resty 2>/dev/null || echo resty; fi)
LUA_PATH ?= ./lua/?.lua;$(OPENRESTY)/lualib/?.lua;$(OPENRESTY)/lualib/?/init.lua;;
LUA_CPATH ?= ./vendor/lua-cjson/?.so;./target/release/lib?.so;./?.so;$(OPENRESTY)/lualib/?.so;/usr/local/lib/lua/5.1/?.so;$(OPENRESTY)/luajit/lib/lua/5.1/?.so
LUA_CPATH ?= ./vendor/lua-cjson/?.so;./target/release/lib?.dylib;./target/release/lib?.so;./?.so;$(OPENRESTY)/lualib/?.so;/usr/local/lib/lua/5.1/?.so;$(OPENRESTY)/luajit/lib/lua/5.1/?.so

LUAJIT_PREFIX ?= $(shell dirname $$(dirname $$(command -v $(LUAJIT) 2>/dev/null || echo $(OPENRESTY_LUAJIT))))
LUAJIT_INC ?= $(LUAJIT_PREFIX)/include/luajit-2.1
QJSON_PROP_CASES ?= 200
QJSON_PROP_SEED ?= 760076
QJSON_MUT_PROP_CASES ?= 200
QJSON_MUT_PROP_SEED ?= 104104
QJSON_MUT_PROP_STEPS ?= 24

LIB_DIR := $(CURDIR)/target/release
ifeq ($(shell uname),Darwin)
Expand All @@ -19,7 +22,7 @@ else
LUA_ENV := LD_LIBRARY_PATH=$(LIB_DIR) LUA_PATH='$(LUA_PATH)' LUA_CPATH='$(LUA_CPATH)'
endif

.PHONY: help build test lua-property-test lint lua-lint bench clean
.PHONY: help build test lua-property-test lua-mutation-property-test lint lua-lint bench clean

help: ## Show this help
@# FS uses [^#]* (not .*) so a description containing `##` isn't truncated.
Expand All @@ -37,6 +40,11 @@ lua-property-test: build ## Run deterministic Lua encode/materialize property te
QJSON_PROP_CASES=$(QJSON_PROP_CASES) QJSON_PROP_SEED=$(QJSON_PROP_SEED) \
$(LUA_ENV) busted --lua=$(LUAJIT) tests/lua/encode_property_spec.lua --lpath='./lua/?.lua'

lua-mutation-property-test: build ## Run deterministic Lua lazy-mutation property tests
QJSON_MUT_PROP_CASES=$(QJSON_MUT_PROP_CASES) QJSON_MUT_PROP_SEED=$(QJSON_MUT_PROP_SEED) \
QJSON_MUT_PROP_STEPS=$(QJSON_MUT_PROP_STEPS) \
$(LUA_ENV) busted --lua=$(LUAJIT) tests/lua/lazy_mutation_property_spec.lua --lpath='./lua/?.lua'

lint: ## Run clippy with -D warnings
cargo clippy --release --all-targets -- -D warnings

Expand Down
Loading
Loading