Mock objects for generating a more useful codex and for use testing Dual Universe scripts offline.
The mock files are commented to match the codex as much as possible. In the case that the codex does not acurately or fully describe the Lua API the mocks should follow actual API behavior instead of the codex. To generate a browsable documentation file (once the project is cloned/set up) run the following in the base directory:
ldoc .
Output can be found at doc/index.html
. Note that the documentation won't show the package prefix (dumocks.
) in file names, but it's still needed to load modules.
An already compiled and uploaded copy of the documentation can be found on this project's github pages with and without documentation for mock methods:
The easiest way to install this project is using luarocks:
luarocks install du-mocks
This will install all dependencies and place the dumocks
modules on your lua path, allowing you to import modules like this:
local mockWarpDrive = require("dumocks.WarpDriveUnit")
Version numbers will follow the game version for major/minor numbers (0.24.x), while the patch number (x.x.1) will represent the du-mocks revision number for the Dual Universe minor release, not the Dual Universe patch number. If DU is patched without changing the Lua api a new du-mocks version may not be pushed immediately.
Alternately, you can clone the repository and simply place the project src directory on your lua package path and load modules with the dumocks.
prefix, like so:
package.path = package.path .. ";../du-mocks/src/?.lua"
local mockWarpDrive = require("dumocks.WarpDriveUnit")
Or confiuring the path in bash:
export LUA_PATH="../du-mocks/src/?.lua;;$LUA_PATH"
lua ./test/TestMyModule.lua
Note: If you get an error similar to module 'dumocks.Element' not found
on loading a module besides Element, your path is probably pointing to the inside of the dumocks
package instead of one level above it.
Luarocks can be used to install all dependencies: luarocks install --only-deps du-mocks-scm-0.rockspec
-
ldoc: For producing nice to read documentation.
-
luaunit: For automated testing.
-
luacov: For tracking code coverage when running all tests. Can be removed from
runTests.sh
if not desired. To view results using luacov-html (which is a separate package) simply runluacov -r html
after running tests and openluacov-html/index.html
.
The unit tests on the mocks themselves provide an example of how to use the mocks. In short, the mock must be imported, then instantiated (and configured), then bundled into a closure for use by the script you are testing:
local mockConstruct = require("dumocks.Construct")
local mock = mockConstruct:new()
mock.name = "My Construct"
local construct = mock:mockGetClosure()
local name = construct.getName()
assert(name == "My Construct")
For a more complex and real-world example, my factory inventory display has tests that use multiple elements: https://github.com/1337joe/du-factory-inventory/blob/main/test/collector/TestCollectorUnit.lua
Testing render script works a little differently: because render script is run in a sandbox with more limited Lua functionality the render script mock provides an environment to run a script in instead of wrapping itself in a closure like other mocks.
Inline Render Script
local rs = require("dumocks.RenderScript")
local renderScript = rs:new()
local environment = renderScript:mockGetEnvironment()
-- Set screen input
renderScript.input = "Test"
-- switch to the sandboxed environment
local oldEnv = _ENV
local _ENV = environment
logMessage(getInput())
-- renderscript can be called here
setOutput("test")
-- restore the old environment
_ENV = oldEnv
-- Print the screen output
print(renderScript.output)
External File Render Script
local rs = require("dumocks.RenderScript")
local renderScript = rs:new()
local environment = renderScript:mockGetEnvironment()
local script = assert(loadfile("path/to/my/renderscript.lua", "t", environment))
-- Set screen input (read with readInput())
renderScript.input = "Test"
script()
-- Print the screen output (written with setOutput())
print(renderScript.output)
-- Print the SVG output of the script
print(renderScript:mockGenerateSvg())
ScreenUnit
local msu = require("dumocks.ScreenUnit")
local screenUnit = msu:new()
local closure = screenUnit:mockGetClosure()
local script = [[
assert(getInput() == "test input")
local xRes, yRes = getResolution()
setOutput(tostring(xRes))
]]
closure.setScriptInput("test input")
closure.setRenderScript(script)
local renderScript, environment = mock:mockDoRenderScript()
-- Print the output returned to the ScreenUnit
print(closure.getScriptOutput())
-- Print the SVG output of the script
print(renderScript:mockGenerateSvg())
Renderscript screen output can be exported as an SVG image by calling renderScript.mockGenerateSvg()
(see the external file sample code above).
More fleshed out examples of this as well as an HTML header for including most of the in-game fonts are in TestRenderScript.lua. The output of this test script from the latest main
build can be found here.
Unit tests are provided to validate the funcionality and demonstrate usage of the mocks. The tests depend on lua modules luaunit
and luacov
for the unit test framework and code coverage, respectively. To run all tests run the following script from the repository base directory:
./test/runTests.sh
Luaunit arguments may be passed in, such as -o junit
to produce junit-style xml result files (though the junit file path is hardcoded to output to test/results/
).
Individual test files are executable and can be run the project root.
All unit tests include characterization tests that can be run in-game to validate expected behavior as well as on the relevant mock object to verify the mock behaves as the game does. These are aided by an extraction tool that can parse out the code blocks and build a document that can be pasted in-game to a control module. To run this tool, execute (replacing TestFile
with the appropriate test file path/name):
./test/bundleCharacterizationTest.lua TestFile
This will print the result out in the console (for piping to xclip -selection c
or clip.exe
, depending on your platform), or an output file can be specified as a second argument to the program.
Blocks of code to be extracted should be surrounded by comment blocks with the following format:
---------------
-- copy from here to slot1.onStatusChanged(status): *
---------------
<CODE GOES HERE>
---------------
-- copy to here to slot1.onStatusChanged(status): *
---------------
- The start and end blocks should match on method signatures.
slot1
indicates what slot should receive the code (other options besides a numbered slot arelibrary
,system
, andunit
).onStatusChanged(status)
must match the method signature of the handler you want to create. If in doubt create one in-game and export the configuration to clipboard, then paste in notepad to examine it.- Arguments to be passed in follow the method signature (optionally indictaed by a colon), and should be separated by spaces or commas in the case of multiple arguments.
- Full documentation matching or exceeding the Codex.
- Document widget data format/contents.
- Implementation to allow each method to be used for testing.
- Unit testing for each method.
- A characterization (game-behavior) test that can be run in-game and using the mock to validate behavior matches.
- Test verifies only expected methods exist and calls element-inherited methods.
- Element definitions for the in-game elements that the mock applies to.
- ⬜ = Missing
- 🔜 = In Progress
- ✔️ = Completed
- ➖ = N/A
- U = Untested
Unit | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
Construct | ✔️ ➖ | 🔜 | ⬜ | ⬜ ✔️ | ➖ |
Library | ✔️ ➖ | ✔️ | ✔️ | ➖ ✔️ | ➖ |
Player | ✔️ ➖ | 🔜 | ⬜ | 🔜 ✔️ | ➖ |
System | ✔️ ✔️ | 🔜 | 🔜 | 🔜 ✔️ | ➖ |
Element | ✔️ ➖ | 🔜 | 🔜 | ➖ ➖ | ➖ |
AdjustorUnit | ✔️ ✔️ | 🔜 | ⬜ | ⬜ ✔️ | ✔️ |
AirfoilUnit | ✔️ ✔️ | 🔜 | ⬜ | ⬜ ✔️ | ✔️ |
AntiGravityGeneratorUnit | ✔️ ✔️ | ✔️ | ✔️ | 🔜 🔜 | ✔️ |
BaseShieldGeneratorUnit | ✔️ ⬜ | ⬜ | ⬜ | ⬜ ⬜ | ⬜ |
BrakeUnit | ✔️ ✔️ | 🔜 | ⬜ | ⬜ ✔️ | ✔️ |
ContainerUnit | ✔️ ✔️ | ✔️ | ✔️ | ✔️ ✔️ | 🔜 |
ControlUnit | ✔️ ⬜ | 🔜 | 🔜 | 🔜 ✔️ | ✔️ |
CoreUnit | ✔️ ✔️ | 🔜 | 🔜 | 🔜 ✔️ | ✔️ |
CounterUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
DatabankUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
DetectionZoneUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
DoorUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | 🔜 |
EmitterUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
EngineUnit | ✔️ ✔️ | 🔜 | ⬜ | ⬜ ✔️ | 🔜 |
FireworksUnit | ✔️ ➖ | 🔜 | 🔜 | 🔜 ✔️ | ✔️ |
ForceFieldUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
GyroUnit | ✔️ ✔️ | ✔️ | 🔜 | 🔜 ✔️ | ✔️ |
IndustryUnit | ✔️ ➖ | 🔜 | 🔜 | 🔜 ✔️ | ✔️ |
LandingGearUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
LaserDetectorUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
LaserEmitterUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
LightUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
ManualButtonUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
ManualSwitchUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
MiningUnit | ✔️ ➖ | 🔜 | 🔜 | 🔜 ✔️ | ✔️ |
PlasmaExtractorUnit | ✔️ ⬜ | ⬜ | ⬜ | ⬜ ⬜ | 🔜 |
PressureTileUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
RadarUnit | ✔️ ⬜ | ✔️ | 🔜 | 🔜 ✔️ | 🔜 |
ReceiverUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
RenderScript | ✔️ ➖ | 🔜 | 🔜 | 🔜 ✔️ | ➖ |
ScreenUnit | ✔️ ➖ | ✔️ | ✔️ | 🔜 ✔️ | ✔️ |
ShieldGeneratorUnit | ✔️ ✔️ | 🔜 | 🔜 | 🔜 ✔️ | 🔜 |
SurfaceEngineUnit | ✔️ ✔️ | 🔜 | ⬜ | ✔️ ✔️ | 🔜 |
TelemeterUnit | ✔️ ➖ | 🔜 | 🔜 | 🔜 ✔️ | ✔️ |
TransponderUnit | ✔️ ➖ | ✔️ | ✔️ | ✔️ ✔️ | ✔️ |
WarpDriveUnit | ✔️ ✔️ | ✔️ | 🔜 | ✔️ 🔜 | ✔️ |
WeaponUnit | ✔️ ✔️ | 🔜 | 🔜 | 🔜 ✔️ | 🔜 |
Many mocks are incomplete (until I code something that needs them for testing), but documentation should be up to date. If you encounter a function that's not documented here (or where my documentation doesn't match the function) either send me a message or file a GitHub Issue (or fork the project, fix it, and send me a pull request).
Discord: 1337joe#6186
In-Game: W3asel
My game/coding time is often limited so I can't promise a quick response.