lua-cc-tools
A set of tools for bundling and testing Lua programs written for
CCTweaked / ComputerCraft Minecraft mods.
Contains a bundled version of SquidDev's Copy Cat.
Tools
ccbundle
ccbundle
is a tool that can be used to bundle multiple Lua files together.
It works by tracing all require
function calls and inlining required files.
Options:
--entry
— entry Lua file;--output
— output file to write;--project
— (optional) project file.
When present, --project
option makes all other options ignored and
thus not mandatory.
ccpack
ccpack
is a tool for bundling folders into one JSON file for
later usage in ccrun
command.
Options:
--folder
— folder to bundle;--output
— output JSON image file to write.
ccunpack
ccunpack
does the reverse operation of ccpack
: it unpacks all
files from a certain JSON image into the output directory. Existing
files are always overridden, so be careful with this tool and always use
version control systems (e.g. git
) to track changes in your code.
Options:
--fs
— JSON image file to read;--output
— target directory to extract files to.
ccrun
ccrun
is a tool for running Copy Cat in
a clean environment with a specific file system image.
This command uses Playwright to run Copy Cat.
Options:
--port
— port to listen to;--watch
— (optional) automatically reload Copy Cat when
file system image changes (only compatible with --fs
option);--fs
— (optional) JSON file system image prepared by
ccimage
command;--folder
— (optional) run Copy Cat in certain real file
system folder, meaning pack it into a temporary JSON image on start
and unpack back when the program is done running.
Although --fs
and --folder
options are marked as optional, you really
should choose one.
cctest
cctest
is a tool for running automated tests written with a built-in
testing framework. This command works similar to ccrun
, but it
automatically runs all tests and presents the output in a readable format
in the host terminal.
Options:
--project
— project file;--testsGrep
— (optional) Lua string.match
pattern for
target tests (runs all tests by default);--suitsGrep
— (optional) Lua string.match
pattern for
target suites (scans all suites by default).
To be able to use this tool, you need to create a
project file and configure in a
certain way
Project files
Series of Lua files can be grouped into projects, described by JSON
files in a specific format:
{
"entry": "path/to/entry.lua",
"output": "dist/path/to/bundle.lua",
"references": {
"other-project": "path/to/other/project.json"
}
}
In this example:
entry: string
— path to the main entry file, the same as
you'd pass as --entry
option to ccbundle;output: string
— path to the destination bundled file, the
same as you'd pass as --output
option to ccbundle;references: Object<string, string>
— (optional) a map of
dependant projects that need to be build alongside the main project.
Usage with cctest
In order to be able to use cctest, another section needs to
be defined:
{
"testMatch": [
"**/*.test.lua"
]
}
In this example:
testMatch: string[]
— array of
glob-like patterns matching
your project's tests.
Please note that you can't use references
and testMatch
in the same
project file.
Testing framework
This toolset also includes a testing framework for ComputerCraft
applications. It is automatically loaded by cctest.
If you already know Jest, this framework may seem
familiar.
Basics
Every test written with this framework should belong to a test suite.
A test suite is a series of tests with one name, described in a single
file.
Example:
describe("Math", function()
beforeEach(function()
print("I run before each test")
end)
test("add", function()
expect(5 + 7).toBe(12)
end)
test("subtract", function()
expect(5 - 7).toBe(1)
end)
end)
describe(sSuiteName, fSuite)
Defines a test suite. This function is available in every test file.
Arguments:
sSuiteName: string
— name of the suite;fSuite: function
— function that implements the suite,
meaning it contains all the test()
calls that define individual
tests.
Returns: nil
.
test(sTestName, fTest)
Defines a test. This function is available in every suite environment.
Arguments:
sTestName: string
— name of the test;fTest: function
— function containing actual code of the test.
Returns: nil
.
expect(aValue)
Creates an assertion. This function is available in every test.
Arguments:
aValue: any
— value to assert.
Returns: assertion table.
beforeAll, beforeEach, afterAll, afterEach
Series of helpers for running certain code on various stages of
running tests.
Arguments:
fCode: function
— function to call.
Returns: nil
.
cctools
This global table is designed to contain less common helper methods
so that they don't pollute the global environment.
cctools.fn
Creates a mock function that stores all arguments with which it was
called.
Arguments:
fImpl: function
— (optional) internal implementation,
the function to be called by the mock function.
Returns: table
.
The table returned can be called as a normal function. It also has
several fields:
calls: table
— contains a 2-dimensional list of arguments
that were ever passed to the mock;clear()
— clears the list of calls.
Assertion tables
This testing framework supports behavior-driven
expect-style interface for
assertions in tests that is exposed through expect
function.
This function returns a table with a set of assertion methods that
are used to validate the given value.
Example:
expect(2).toBeTruthy()
expect("fork").toBe("knife")
A standard assertion table has the following methods:
toBe(bValue)
— checks if expected and given values
are the same (expected == bValue
);toEqual(bValue)
— checks if expected and given values
are equal, meaning expected == bValue
for primitives and deep
comparison for tables;toBeTruthy()
— checks if expected value would count
as true
in an if (expected)
check;toBeFalsy()
— checks if expected value would count
as false
in an if (expected)
check;toBeNil()
— checks if expected value is nil.
In addition to that, a standard assertion table also has the following
chaining combinators:
toNot
— returns an inverted assertion table, meaning that
all it's assertions fail if the original assertion is true.
A practical example with toNot
would look like this:
expect(true).toBeTruthy()
expect(true).toNot.toBeFalsy()
Contributing
When contributing to this repository, please first discuss the change
you wish to make via issue, email, or any other method with the
owners of this repository before making a change.
Any participation is welcome!