UdonHost
Tsvrc.UdonTestKit.UdonHost runs UdonSharp behaviours in a Play Mode test as their compiled Udon
programs, the way VRChat runs them, instead of as their C# classes. Disposing it destroys the
GameObjects it created. UdonTestBase gives every test one, and
Run your behaviours as Udon walks through using it.
using var udon = new UdonHost();
UdonBehaviour door = udon.Create<Door>("Door");
door.SendCustomEvent(nameof(Door.Open));
Assert.That(door.GetProgramVariable(nameof(Door.IsOpen)), Is.EqualTo(true));
A behaviour that a test adds with AddComponent runs as plain C#. One created with UdonHost runs
the program UdonSharp compiled from it, so the test sees what Udon does with that code, including
Udon halting the behaviour when it throws.
Creating behaviours
Create<T>(string name, bool active = true) creates a GameObject named name with an
UdonBehaviour that runs the compiled program of T, an UdonSharpBehaviour, and returns that
UdonBehaviour. The test reaches the program through it, by the names of the methods and fields
of T: SendCustomEvent, SetProgramVariable and GetProgramVariable.
With active: false, the GameObject starts inactive, and the behaviour starts once the test
activates it. Udon runs the Start of a program on a later frame, so a test that checks what
Start did yields until UdonBehaviour.HasDoneStart is true.
Create throws an InvalidOperationException that says what to do in two cases:
Thas no program asset. A script made with Assets > Create > U# Script gets one along with it. For a script without one, the error readsDoor has no UdonSharp program asset. Create one with Assets > Create > VRChat > Udon > Udon C# Program Asset and set its Source Script.Tis in an assembly that player builds leave out. The compile UdonSharp runs for a world upload reads only player assemblies, and a program asset whose script is in any other assembly makes that compile log an error and compile no program at all. The error names the assembly and points to Assets > Create > U# Assembly Definition.
The second case covers the Doubles assembly that
TestAssemblyCreator creates, which compiles only for tests. Test
doubles that run as Udon go in the UdonDoubles assembly it creates for them, as in
Check the calls Udon code makes.
What the kit works around
explains the upload compile.
Disposing
Dispose() destroys every GameObject that Create made, skipping any the test destroyed already.
using var udon = new UdonHost(); is all the cleanup a test needs.
Reading the calls of an UdonCallLog
UdonHost.Calls(UdonBehaviour log) is static and returns the calls an
UdonCallLog recorded, in the order they arrived. Pass the UdonBehaviour that
Create<UdonCallLog> returned. It returns a new array on each call, so a test that changes it
leaves the log as it was.
Expecting an Udon exception
UdonHost.ExpectUdonException(Action act) is static and a coroutine: yield it from a
[UnityTest]. It runs act, which makes an Udon behaviour throw, and expects the error Udon logs
as it halts the behaviour:
yield return UdonHost.ExpectUdonException(() => door.SendCustomEvent(nameof(Door.Unlock)));
UdonSharp logs a copy of that error on a later frame, which would fail the next test.
ExpectUdonException waits up to 10 frames for the copy and expects it too if it comes.
UdonSharp doesn't always log the copy, as
What the kit works around
explains.
When act doesn't make Udon throw, the error it expects never comes, and Unity Test Framework fails
the test. Like any coroutine, it does nothing until the test yields it.
Edge cases
- Use it in Play Mode tests. A behaviour created with it doesn't need ClientSim, so start a
ClientSimSessiononly when the behaviour needs players. - Udon runs the events a program delays with
SendCustomEventDelayedFramesandSendCustomEventDelayedSecondson a later frame, once the delay has passed, never during the call that delays them. A test waits for them in a[UnityTest], as in Run your behaviours as Udon. - It runs each program as UdonSharp last compiled it, normally the compile for the Editor, which
keeps the code inside
#if UNITY_EDITOR. To check the compile for upload, useUdonSharpUploadCompile.