Valheim.Testing.NativeSmoke
0.1.0
dotnet tool install --global Valheim.Testing.NativeSmoke --version 0.1.0
dotnet new tool-manifest
dotnet tool install --local Valheim.Testing.NativeSmoke --version 0.1.0
#tool dotnet:?package=Valheim.Testing.NativeSmoke&version=0.1.0
nuke :add-package Valheim.Testing.NativeSmoke --version 0.1.0
Valheim.Testing.NativeSmoke
API reference. This package is the valheim-test .NET tool; its supported surface is the commands below, not a .NET library.
valheim-test checks that a mod loads in the real game. Each run stages a disposable loader profile of a Steam install, keeps private evidence and removes what it made; the install is only read. New to the tool? Start with Your first server load check.
Install
dotnet tool install Valheim.Testing.NativeSmoke --version 0.1.0-rc.1 --global
The tool needs the .NET 10 SDK. If a Homebrew .NET on macOS cannot start the tool, set DOTNET_ROOT to the directory dotnet --info reports.
Commands
| Command | Does |
|---|---|
start |
Loads a client-side mod in an owned client that hosts a disposable world |
server-load |
Loads a server-side mod on an owned dedicated server; one clean client joins |
server-load-ab |
Runs server-load with and without one mod, to isolate a load interaction |
server-load-phases |
Runs ordered server-only fixture bakes |
build |
Builds a mod against the selected install's publicized assemblies, privately |
init |
Creates an editable test project that reruns a start or server-load run |
finish |
Asks a held run to finish and clean up |
cli |
Sends one strictly pinned command to a running owned client or server |
env |
Lists this machine's environments, checks it, and clears what runs left |
session check |
Checks a session file and, with --hosts, its hosts |
detach |
Experimental: a held Windows dedicated server that outlives the terminal |
steam-runtime capture |
Pins SteamCMD's macOS server libraries for a Mac server profile |
valheim-test help lists every command with its usage.
Exit codes
| Code | start, server-load, server-load-ab |
env, session check |
cli |
|---|---|---|---|
| 0 | Passed | Nothing to refuse or recover | ValheimCLI accepted the command |
| 1 | The run failed | Not used | Not accepted, or the screenshot incomplete |
| 2 | Usage error (not server-load-ab) |
Usage error | Usage error |
| 3 | Refused, or the outcome is unknown | Something refused or left to recover | Refused; no command sent |
server-load-ab reports a usage error, --hold and --preflight-only included, as REFUSED: without a usage line, and exits 3. build, init and finish exit 0 when done, 2 on a usage error and 3 when refused.
Run options
start, server-load, server-load-ab and build share these options. A command refuses an option it does not take as Unknown option. "Repeat": give it more than once.
Mod inputs
| Option | Taken by | Does |
|---|---|---|
--mod DLL |
start, server-load, server-load-ab | A mod to load. Repeat for a set. Without it, start and server-load pick the current project's one built plugin |
--project MOD.csproj |
start, server-load, build | Builds the mod privately first; leave out --mod and --build-inputs |
--build-inputs FILE |
start, server-load | The build-inputs.json from build; the run refuses a changed mod or reference |
--dependency NAME=DLL |
start, server-load, build | A third-party compile reference for --project. Repeat |
--search-root DIR\|DLL |
all four | A folder or exact DLL to search for dependencies. Repeat |
--optional-reference ASSEMBLY |
start, server-load, server-load-ab | Confirms a reference that is deliberately absent. Repeat |
--scenario TEST.dll, --scenario-project TEST.csproj |
start, server-load | A mod-owned scenario; one or the other (scenarios) |
--output NEW_DIR |
all four | A new folder outside the mod project. Default: a new folder under valheim-test-runs/ in ValheimTesting's data folder |
The dependency resolver chooses files and writes dependencies.lock.json. It never downloads a mod.
Machine and installs
| Option | Taken by | Does |
|---|---|---|
--inventory FILE |
all four | Uses a file's environments instead of this machine |
--game DIR |
start, build | The client install to use |
--client-env NAME |
all four | The client environment to use from the inventory |
--server DIR |
server-load, server-load-ab, build | The dedicated-server install to use |
--server-env NAME |
server-load, server-load-ab, build | The server environment to use from the inventory |
--client DIR |
server-load, server-load-ab | The clean client's install |
--client-architecture x64\|arm64 |
start, server-load, server-load-ab | The macOS client slice (Apple Silicon) |
--join HOST:PORT |
server-load, server-load-ab | The address a client on another machine joins |
Loader and ValheimCLI
| Option | Taken by | Does |
|---|---|---|
--loader-package FILE |
server-load, server-load-ab, build | A reviewed loader for the server's profile |
--client-loader-package FILE |
all four | A reviewed loader for the client's profile |
--cli-manifest FILE --cli-files DIR |
start, server-load, server-load-ab | A ValheimCLI core and packs from one build |
--steam-runtime-package FILE |
server-load | A Mac server's pinned SteamCMD libraries |
Timing, logs and holds
| Option | Taken by | Does |
|---|---|---|
--join-seconds 10..900 |
start, server-load, server-load-ab | The deadline to enter the world. Default 180 |
--server-startup-seconds 1..3600 |
server-load, server-load-ab | The deadline for the server to become ready. Default 300 |
--expected-log-error EXACT_HEADER --expected-log-reason REASON |
start, server-load, server-load-ab | Admits one known BepInEx error by its whole header line, with a reason |
--hold |
start, server-load | Keeps a passing run's game open (hold) |
start
valheim-test start --mod /path/to/MyMod.dll
start loads a client-side mod in an owned Windows or macOS client that hosts the packaged world with the packaged character. It prints run ID:, a log-scan line and a PASS or FAIL result line.
| Option | Does |
|---|---|
--source COMMIT |
Records the mod build's source commit |
--compare-mod DLL --compare-source COMMIT |
Runs a second build of the first mod against the same world, as arms before and after |
Both compared builds must declare the same plugin identities. The client needs its desktop session and a running Steam client. Through SSH on Windows, start launches the client in the signed-in desktop through a scheduled task; on macOS, run it from the unlocked console.
server-load
valheim-test server-load --mod /path/to/ServerMod.dll
server-load prints the server and client it chose, writes campaign.json and runs the read-only host preflight before anything is copied. Then one clean client without the mod joins the owned server.
| Option | Does |
|---|---|
--server-only |
Runs no client. Prints SERVER_LOAD_PASS instead of SERVER_JOIN_PASS |
--preflight-only |
Stops after PREFLIGHT PASSED |
--config FILE |
A config file for the server. Repeat |
--plugin-file FILE, --plugin-dir DIR |
A mod's assets beside its DLL; DLLs are refused. Repeat |
--adapter DLL |
A built test adapter, instead of building the bundled one |
--adapter-project ADAPTER.csproj |
Builds a mod's own adapter; needs --session-capability and --session-token-variable |
--adapter-property NAME={mod}\|{cli} |
Maps an adapter project property to the pinned mod or ValheimCLI DLL. Repeat |
--session-capability OWNER/session --session-token-variable NAME |
The custom adapter's capability and token variable |
--world-fixture DIR |
Loads a pinned world fixture instead of the packaged world |
--bake-fixture NEW_DIR |
Saves the world as a new fixture; see Bake a fixture |
--assert-command TEXT --assert-line PREFIX |
One server command whose reply proves the mod is ready; needs --server-only |
--before-save-command TEXT --before-save-line PREFIX |
One change before a bake saves |
The result line is SERVER_JOIN_PASS, SERVER_LOAD_PASS or SERVER_LOAD_FAIL, with the evidence's size and path. The server must be on this machine. Building the bundled adapter needs NuGet.org; offline, pass --adapter DLL.
server-load-ab
valheim-test server-load-ab \
--mod /path/to/FirstMod.dll --mod /path/to/SecondMod.dll \
--remove-mod /path/to/SecondMod.dll \
--output /path/to/new-run
--remove-mod DLL names one of at least two --mod DLLs; --output is required. It refuses --hold, --preflight-only, --project, --build-inputs, --dependency, --scenario, --scenario-project, --steam-runtime-package, --world-fixture and the adapter-project, bake and assertion options. The full set runs in before/ and the set without that mod in after/. The result line is SERVER_MODSET_AB_PASS or SERVER_MODSET_AB_FAIL.
server-load-phases
valheim-test server-load-phases --plan FILE --output NEW_DIR
Both paths must be absolute. The plan and its output are on Bake a fixture.
build
valheim-test build --project /path/to/MyMod/MyMod.csproj
build publicizes the selected install's assemblies into a new private folder and builds the mod against them, never into a game.
| Option | Does |
|---|---|
--role client\|server |
Builds against the client (default, as start selects it) or the dedicated server (as server-load selects it) |
With --role server it takes --server, --server-env and --loader-package; otherwise --game, --client-env and --client-loader-package. It prints Built mod: and Build inputs:. Pass both to a run with --mod DLL --build-inputs FILE.
It sets VALHEIM_INSTALL, VALHEIM_MANAGED, ValheimPath, ValheimManaged, BEPINEX_CORE, BepInExCore, PUBLICIZED_PATH, PublicizedAssembliesPath and CopyOutputDLLPath (a private folder), and empties CopyOutputDLLPath2 and CopyOutputDLLPath3. Guard any other copy into a game first. Give a missing third-party reference with --dependency or --search-root.
Your own scenario
A mod's test project can hold one public scenario that runs on the prepared game:
| Interface | Runs on | Example |
|---|---|---|
IOneShotServerScenario |
server-load; RequiresClient false needs --server-only |
MetadataScenario.cs |
IOneShotHostedScenario |
start |
HostedWorldScenario.cs |
IOneShotScenarioPreflight |
Either; ValidateInputs() runs before anything is staged |
context.RequireInputs<T>() reads its result |
One command builds the mod, the scenario and its adapter, then runs them:
valheim-test server-load --server-only \
--project /path/to/MyMod/MyMod.csproj \
--scenario-project /path/to/MyMod.SystemTests/MyMod.SystemTests.csproj \
--adapter-project /path/to/MyMod.TestAdapter/MyMod.TestAdapter.csproj \
--adapter-property 'MyModDll={mod}' \
--session-capability mymod.testing/session \
--session-token-variable MYMOD_TEST_SESSION_TOKEN
A scenario owns its assertions, so it refuses --hold, --bake-fixture and --assert-command.
init
valheim-test init --output NEW_DIR
valheim-test init server --output NEW_DIR
init creates an editable consumer project for a start run's regression.json, or with server for a server-load run's campaign.json. It pins the Valheim.Testing.Game this tool carries, and with server also Valheim.Testing.GameSessions, and restores from NuGet.org. A tool built from source refuses, because NuGet.org does not serve its version; --candidate-feed LOCAL_PACKAGE_DIR is for maintainers (CONTRIBUTING).
hold and finish
valheim-test server-load --mod /path/to/ServerMod.dll --hold
valheim-test finish --run ID
--hold keeps the owned game running after the checks pass; a failing run is never held. It prints HELD run with the run ID and the cli commands that reach the game. finish --run ID, or Ctrl+C on the runner, stops and cleans up. If a held game exits, the run fails.
cli
valheim-test cli --evidence RUN_EVIDENCE --phase world --command TEXT
| Option | Does |
|---|---|
--evidence RUN_EVIDENCE |
The evidence folder holding the role's lease |
--role client\|server |
Needed only when the folder holds both leases |
--phase menu\|world |
Which published pins to verify first |
--expect-strict FILE |
Other strict pins, in place of --phase |
--command TEXT |
One game command; cli_screenshot takes only a size, 1 to 4 |
--timeout-seconds 1..15 |
The command deadline. Default 5 |
| Run | Evidence folder |
|---|---|
start |
RUN/evidence/smoke |
server-load, the server |
RUN/evidence |
server-load, the joined client |
RUN/evidence/client |
It checks the exact process, the port's owner and the strict pins, records the command in owned-cli-command-*.jsonl, sends it once and never retries.
env
| Command | Does |
|---|---|
env list [--inventory FILE] [--json] |
Prints each environment, detected: and NOT FOUND: lines. No verdict |
env preflight [--inventory FILE] [--json] |
Checks this machine before a one-off run. Prints ELIGIBLE or REFUSED |
env status [--inventory FILE] [--json] |
Lists what earlier runs left, from their journals. Changes nothing |
env recover --run ID [--inventory FILE] [--json] |
Removes what it can prove the run left, except what it kept on purpose |
env recover --run ID --launch-gone |
Settles a run whose only unprovable items are launches you confirmed are gone |
env teardown --run ID [--inventory FILE] [--json] |
As recover, and also removes what the run kept on purpose |
env teardown --copy PATH [--inventory FILE] |
Removes one copy made before runs journalled their copies |
env teardown --run ID --machine-gone [--inventory FILE] |
Releases the Steam leases of a run whose machine is gone for good |
env preflight checks this machine only (its refusals). env status prints each run that left something with its state:
| State | Means |
|---|---|
LIVE |
Its runner still runs; nothing is touched |
UNKNOWN |
It never journalled its end and its runner cannot be checked from here |
RECOVERABLE |
It left items that are provably its own; env recover --run ID |
UNRECOVERABLE |
Something cannot be proven gone or its own |
KEPT |
It ended and kept a runtime or lease on purpose; env teardown --run ID |
The last line is N runs ended and left nothing. Nothing was changed. env status exits 0 only when every run ended and left nothing; a KEPT run, an unjournalled copy or an unreadable host exits 3. env recover that keeps a runtime on purpose names env teardown --run ID and exits 3. Debug a native run says when to use each.
session check
valheim-test session check SESSION [--hosts] [--json]
session check reports every problem with a session.json's inputs and actor assignment without contacting a host, then ELIGIBLE or REFUSED lines. --hosts adds the read-only host checks (installs, loaders, ports, free space, conflicting games, journals, signed-in Steam accounts) and prints READY when they pass. Sessions: GameSessions.
detach
valheim-test detach server-load --server 'C:\Games\Valheim dedicated server' --mod 'C:\Mods\MyMod.dll' --server-only --output 'C:\ValheimTesting\runs\inspection-1'
valheim-test detach status
valheim-test detach finish --run ID
Experimental, Windows only. detach server-load starts a server-only held run under Task Scheduler and returns once it is held; it needs a signed-in desktop session and absolute paths. detach status exits 0 when nothing is left to recover, a live run included. Recover an interrupted start with env recover --run ID on the same machine.
steam-runtime capture
valheim-test steam-runtime capture --steamcmd DIR --output NEW_FILE
macOS only. It writes a reviewed manifest of SteamCMD's native server libraries. Pass it to server-load as --steam-runtime-package FILE for a Mac dedicated server profile (Steam runtime package).
What every run does
| Behaviour | Rule |
|---|---|
| Profiles | Each actor runs from a disposable loader profile; the install is only read (profiles) |
| Loader | Windows and Linux: the pinned BepInExPack_Valheim from loader-dependency.json, or a named loader package; never the install's. macOS: its loader package, or the install's native loader |
| ValheimCLI | --cli-manifest and --cli-files, else VALHEIMCLI_BUNDLE, else the bundle pinned in the tool. The run prints which |
| Output | Must be new and outside every install and the mod project; a refusal writes REFUSED.txt with the reason |
| Journal | Every copy, character, process, lease and lock is journalled before it is made, for env status |
| User files | The client's recent-servers list is moved aside before launch and put back after |
| Log scan | Logs are scanned at teardown (log scan); one summary line per arm |
| Environment variable | Does |
|---|---|
VALHEIMCLI_BUNDLE |
A folder with one ValheimCLI manifest and its DLLs |
VALHEIM_TESTING_KEEP_RUNTIME=1 |
Keeps the owned runtime; env status then reports the run as KEPT |
VALHEIM_TESTING_SOURCE_HASH_AFTER_RUN=1 |
Re-hashes the whole source install after the run, not only sizes and times |
What a run writes
| File | Holds |
|---|---|
dependencies.lock.json |
Every resolved file and dependency decision |
campaign.json, plan.json, client-plan.json |
server-load's derived inputs; plan.json holds the server password |
regression.json, environments.json |
start's derived inputs |
evidence/ |
result.json, junit.xml and the kept logs (where each log is) |
result.json reports each step with its phase, four states and the Toolkit that ran (evidence). Keep the output private: it holds local paths, a server password, account identifiers and a copy of your recent-servers list.
Limits
- A pass proves the plugins loaded and the game entered a world, not the mod's gameplay.
server-loadkeeps its server on this machine. A server on another machine runs as a session.clireaches only games a run on this machine owns.- The packaged world and character come from one game version; recheck them after a game update. Run one client with the packaged character at a time.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 63 | 10/14/2026 |
| 0.1.0-rc.1 | 38 | 10/14/2026 |
| 0.1.0-preview.31 | 57 | 10/12/2026 |
| 0.1.0-preview.26 | 62 | 10/12/2026 |
| 0.1.0-preview.23 | 60 | 10/11/2026 |
| 0.1.0-preview.20 | 55 | 10/10/2026 |
| 0.1.0-preview.16 | 63 | 10/8/2026 |
| 0.1.0-preview.15 | 54 | 10/8/2026 |
| 0.1.0-preview.14 | 58 | 10/8/2026 |
| 0.1.0-preview.8 | 67 | 10/8/2026 |
| 0.1.0-preview.7 | 54 | 10/7/2026 |
| 0.1.0-preview.6 | 54 | 10/6/2026 |
| 0.1.0-preview.5 | 54 | 10/6/2026 |
| 0.1.0-preview.4 | 56 | 10/6/2026 |
| 0.1.0-preview.3 | 69 | 10/4/2026 |
| 0.1.0-preview.2 | 68 | 10/2/2026 |
| 0.1.0-preview.1 | 58 | 10/2/2026 |