Files
tbamud/doc/testing.md
Copilot ac94839eb5 Add Unity-based unit test infrastructure (Phase 1 — 117 tests) (#177)
Introduced new system for unit tests based on the Unity framework.

Added tests for some of the simple functions in four different files.
2026-04-23 01:17:48 +02:00

4.4 KiB
Raw Blame History

tbaMUD Unit Testing

Updated 2026-04

Overview

tbaMUD has a C unit-test suite built on the Unity framework. Tests live in the tests/ directory alongside the vendored Unity source.

tests/
  Makefile.in           Autoconf template; processed by configure
  test_stubs.c          Weak-symbol stubs that satisfy mud headers
  unity_to_junit.py     Converts Unity output to JUnit XML
  test_class.c          Tests for src/class.c
  test_interpreter.c    Tests for src/interpreter.c
  test_random.c         Tests for src/random.c
  test_utils.c          Tests for src/utils.c
  vendor/unity/         Vendored Unity test framework

Prerequisites

Requirement Notes
C compiler (gcc or clang) Same compiler used to build the mud
GNU make Any POSIX-compatible make works
Python 3 Required only for JUnit XML conversion (unity_to_junit.py)
autoconf / configure Already needed to build the mud

Running the tests

Run ./configure from the repository root first (only needed once):

./configure

Then build and run all tests from the tests/ directory:

cd tests
make test

make test performs the following steps for each test binary:

  1. Compiles the test binary (if not already up to date).
  2. Runs the binary and captures stdout/stderr to test-results/<name>.out.
  3. Measures wall-clock elapsed time.
  4. Converts the Unity output to JUnit XML via unity_to_junit.py, writing test-results/<name>.xml.
  5. Prints [PASS] <name> or [FAIL] <name> and exits non-zero if any binary failed.

To build the test binaries without running them:

cd tests
make

To remove all test binaries and result files:

cd tests
make clean

Test suites

Binary Source under test Test file
test_utils src/utils.c, src/random.c test_utils.c
test_random src/random.c, rand_number/dice in src/utils.c test_random.c
test_interpreter src/interpreter.c test_interpreter.c
test_class src/class.c test_class.c

Writing a new test

Adding a test case to an existing suite

  1. Open the relevant test_<name>.c file.
  2. Write a function with the signature void test_my_feature(void).
  3. Use Unity assertion macros such as TEST_ASSERT_EQUAL_INT, TEST_ASSERT_NULL, TEST_ASSERT_TRUE, etc.
  4. Register the function in the main() block:
    RUN_TEST(test_my_feature);
    

Example:

void test_str_cmp_equal_strings(void)
{
    TEST_ASSERT_EQUAL_INT(0, str_cmp("hello", "hello"));
}

Creating a new test suite

  1. Create tests/test_<module>.c. Copy the boilerplate from an existing suite: include unity.h, define setUp/tearDown (may be empty), write test functions, and provide a main() that calls UNITY_BEGIN(), RUN_TEST(...) for each function, and return UNITY_END();.

  2. Add the binary to tests/Makefile.in:

    • Add the name to the TESTS variable.
    • Add a build rule:
      test_<module>: $(UNITY_SRC) $(STUBS_SRC) $(UTILS_SRC) \
                     $(SRCDIR)/<module>.c test_<module>.c
          $(COMPILE) -o $@ $^ $(LIBS)
      
  3. Re-run ./configure from the repository root to regenerate tests/Makefile from the updated tests/Makefile.in.

Stubs

Many mud source files reference global variables and functions that are only meaningful at runtime (e.g. descriptor_list, log()). test_stubs.c provides zero-initialised definitions and __attribute__((weak)) stub implementations for these symbols so that test binaries link without pulling in the full mud.

If a new test requires a function not yet stubbed, add a weak stub to test_stubs.c:

__attribute__((weak)) void my_function(void) { /* no-op */ }

JUnit XML output and CI

unity_to_junit.py reads Unity's line-oriented output on stdin and writes a JUnit-compatible XML file. It accepts an optional third argument with the elapsed wall-clock time in seconds (provided by the make test target):

usage: unity_to_junit.py <suite_name> <output.xml> [elapsed_seconds]

The GitHub Actions workflow (.github/workflows/build.yml) runs make test on every push and pull request against master. After the tests finish the dorny/test-reporter action reads tests/test-results/*.xml and publishes a formatted report as a GitHub Check with pass/fail counts and per-suite execution times.