Skip to main content

Embedded C test runner with cross-compilation support

Project description

vyperling

Python 3.11+ License: MIT PyPI version Build Status

Embedded C unit test runner with cross-compilation support.
A pip-installable replacement for Ceedling โ€” no Ruby dependencies, modern Python-first design.

Overview

vyperling (vpl for short) is a complete embedded C testing framework that:

  • ๐Ÿ” Auto-discovers test_*.c files in your test directory
  • ๐ŸŽฏ Generates CMock-style mocks from C headers using pycparser + Jinja2
  • ๐Ÿ”จ Cross-compiles for native (GCC) and embedded targets (ARM, MIPS, RISC-V, AVR) with parallel jobs
  • ๐Ÿ–ฅ๏ธ Executes natively or under emulation (QEMU, simavr)
  • ๐Ÿ“Š Parses Unity test output with rich terminal reporting
  • ๐Ÿ“ˆ Generates coverage reports (gcov) for native targets
  • ๐Ÿ“‹ Exports JUnit XML for CI/CD integration

Perfect for firmware development, embedded systems testing, and hardware validation workflows.

Quick Start

1. Install

pip install vyperling
# Alias 'vpl' is registered automatically
vpl --version

Development (editable install from source):

git clone https://github.com/ericsonjoseph/vyperling.git
cd vyperling
pip install -e .

2. Create a Project

vpl new myproject
cd myproject

This scaffolds:

  • forge.yml โ€” project configuration
  • src/ โ€” source files under test
  • test/ โ€” test files (test_*.c pattern)
  • mocks/ โ€” auto-generated mocks

3. Run Tests

# Test native target (default)
vpl test

# Cross-compile for MIPS32
vpl test --target mips32

# Run specific tests (pattern match)
vpl test -k uart

# Parallel compilation (4 jobs)
vpl test -j4

# Generate coverage report (native only)
vpl test --coverage

# Export JUnit XML for CI
vpl test --output junit

Commands Reference

All commands use vyperling or vpl interchangeably.

vpl test โ€” Discover, Compile, Run, Report

vpl test [OPTIONS]

OPTIONS:
  --target TEXT           Toolchain target (default: targets.default from forge.yml)
  -k, --filter PATTERN    Run only tests matching PATTERN
  -j, --jobs N            Parallel compile jobs (default: 1)
  --coverage              Enable gcov coverage (native target only)
  --output FORMAT         Export results: junit
  -v, --verbose           Print every compiler command
  --no-mock               Skip automatic mock generation

Example: Test UART module with coverage on 4 parallel jobs:

vpl test -k uart -j4 --coverage

vpl mock โ€” Generate Mocks from Headers

Generate CMock-style mocks from C header files:

vpl mock src/uart.h src/spi.h

# Or mock all headers in configured source dirs
vpl mock --all

# Use a specific toolchain's preprocessor
vpl mock --target arm-cortex-m4 src/uart.h

Output: mocks/mock_uart.{c,h} and mocks/mock_spi.{c,h}

vpl build โ€” Compile Only (No Execution)

vpl build [OPTIONS]

OPTIONS:
  --target TEXT           Toolchain target
  -j, --jobs N            Parallel compile jobs
  -v, --verbose           Print compiler commands
  --no-mock               Skip mock generation

Useful for checking compilation without running tests:

vpl build --target arm-cortex-m4 --verbose

vpl targets โ€” List Available Toolchains

vpl targets

Shows:

  • Built-in targets (native, mips32, mips32el, arm-cortex-m4, riscv32, avr)
  • Project-defined targets (from forge.yml)

vpl clean โ€” Remove Build Artifacts

vpl clean              # Remove all build directories
vpl clean --target mips32  # Remove only target's build dir

vpl --version / vpl --help

Show version or full command help.

Cross-Compilation Targets

Target Triplet Emulator Install (Debian/Ubuntu)
native (native) direct exec (included with GCC)
mips32 mips-linux-gnu QEMU gcc-mips-linux-gnu qemu-user
mips32el mipsel-linux-gnu QEMU gcc-mipsel-linux-gnu qemu-user
arm-cortex-m4 arm-none-eabi QEMU gcc-arm-none-eabi qemu-user
riscv32 riscv64-unknown-elf QEMU gcc-riscv64-unknown-elf qemu-user
avr avr simavr gcc-avr avr-libc simavr

Installation on Ubuntu/Debian:

# Native (GCC)
sudo apt install gcc build-essential

# MIPS cross-compile
sudo apt install gcc-mips-linux-gnu qemu-user

# ARM Cortex-M4 (bare-metal)
sudo apt install gcc-arm-none-eabi qemu-user

# RISC-V
sudo apt install gcc-riscv64-unknown-elf qemu-user

# AVR (Arduino)
sudo apt install gcc-avr avr-libc simavr

Custom Toolchains

Define custom targets in forge.yml:

project:
  name: myproject

toolchains:
  custom-arm:
    description: "Custom ARM GCC 12.2"
    cc: "arm-linux-gcc-12.2"
    ar: "arm-linux-ar-12.2"
    cflags: ["-mcpu=cortex-a7", "-mfloat-abi=hard"]
    emulator: qemu-arm-static
    sysroot: /path/to/sysroot

targets:
  default: native

Configuration (forge.yml)

Minimal required:

project:
  name: MyProject

Full example with all options:

project:
  name: my-firmware
  src_dirs:
    - src
    - lib/hal
  test_dir: test
  include_dirs:
    - src
    - lib/hal/include
  build_dir: build
  mock_dir: mocks

targets:
  default: native

compiler:
  extra_cflags:
    - -Wall
    - -Wextra
    - -pedantic
  defines:
    - DEBUG=1
    - VERSION=1.0.0

toolchains:
  custom-mcu:
    description: "STM32 Cross-Compile"
    cc: arm-none-eabi-gcc
    ar: arm-none-eabi-ar
    cflags:
      - -mcpu=cortex-m4
      - -mthumb
    emulator: qemu-arm-static

Project Layout

After vpl new myproject:

myproject/
โ”œโ”€โ”€ forge.yml              # Project configuration
โ”œโ”€โ”€ src/                   # Source files under test
โ”‚   โ”œโ”€โ”€ uart.h
โ”‚   โ”œโ”€โ”€ uart.c
โ”‚   โ””โ”€โ”€ spi.c
โ”œโ”€โ”€ test/                  # Unit tests
โ”‚   โ”œโ”€โ”€ test_uart.c
โ”‚   โ””โ”€โ”€ test_spi.c
โ””โ”€โ”€ mocks/                 # Auto-generated mocks (created by 'vpl mock')
    โ”œโ”€โ”€ mock_uart.h
    โ”œโ”€โ”€ mock_uart.c
    โ”œโ”€โ”€ mock_spi.h
    โ””โ”€โ”€ mock_spi.c

Test File Pattern

Tests use the test_*.c pattern. Each test file:

  • Includes unity.h (provided by vyperling)
  • Includes mocks via #include "mock_<dependency>.h"
  • Defines test cases with void test_<name>(void)

Example: test/test_uart.c

#include "unity.h"
#include "uart.h"
#include "mock_gpio.h"

void setUp(void) {
    // Called before each test
}

void tearDown(void) {
    // Called after each test
}

void test_uart_init_should_configure_pins(void) {
    gpio_init_Expect();
    uart_init();
    TEST_ASSERT_TRUE(1);
}

void test_uart_send_should_transmit_byte(void) {
    uart_send(0x42);
    TEST_ASSERT_EQUAL_INT(0x42, last_byte_sent);
}

Test Framework

vyperling uses:

  • Unity โ€” lightweight C assertion framework (ThrowTheSwitch)
  • CMock โ€” automated mocking for C functions (auto-generated via vpl mock)
  • pycparser โ€” C header parser for mock generation

Assertion Macros

Unity provides rich assertions:

// Basic checks
TEST_ASSERT_TRUE(condition)
TEST_ASSERT_FALSE(condition)
TEST_ASSERT_NULL(ptr)
TEST_ASSERT_NOT_NULL(ptr)

// Equality
TEST_ASSERT_EQUAL_INT(expected, actual)
TEST_ASSERT_EQUAL_UINT(expected, actual)
TEST_ASSERT_EQUAL_HEX(expected, actual)
TEST_ASSERT_EQUAL_STRING(expected, actual)

// Arrays
TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, len)
TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)

// Floating point
TEST_ASSERT_EQUAL_FLOAT(expected, actual, delta)

See Unity documentation for complete reference.

Architecture

vyperling's pipeline flows through 8 independent modules, connected by dataclasses:

TestUnit โ”€โ”€โ†’ CompileResult โ”€โ”€โ†’ RunResult
Module Responsibility
config.py Load forge.yml, resolve paths, manage project settings
toolchains.py Maintain toolchain registry (builtin + user-defined)
discoverer.py Glob test_*.c, match source files, emit TestUnit list
mockgen.py Parse headers with pycparser, generate mocks via Jinja2
compiler.py Invoke GCC with ThreadPoolExecutor, cache via .forge_deps.json
runner.py Execute binaries natively or under QEMU/simavr, parse Unity output
reporter.py Rich terminal tables + JUnit XML export
coverage.py Generate gcov reports (native-only, best-effort)

Key invariant: Each module emits structured output (dataclass) that the next consumes โ€” no hidden state.

Development

Install for Development

git clone https://github.com/ericsonjoseph/vyperling.git
cd vyperling
pip install -e .

Run Tests

# Full test suite (coverage on by default)
pytest

# Single test file
pytest tests/test_mockgen.py

# Single test by name
pytest -k test_cross_compile

# Disable coverage
pytest --no-cov

Project Structure

  • vyperling/ โ€” main library
    • cli.py โ€” Click entry point (commands: test, build, mock, clean, targets, new)
    • config.py โ€” forge.yml loader and config validation
    • toolchains.py โ€” Toolchain dataclass + registry
    • discoverer.py โ€” test file discovery
    • mockgen.py โ€” C header parsing + mock generation
    • compiler.py โ€” GCC invocation with caching and parallel jobs
    • runner.py โ€” test execution and Unity output parsing
    • reporter.py โ€” rich terminal output + JUnit export
    • coverage.py โ€” gcov/gcovr pipeline
    • scaffold.py โ€” project template generation
    • errors.py โ€” exception hierarchy
    • unity.py โ€” Unity C framework accessor
    • c/ โ€” vendored C assets (Unity v2.6.1 + forge_mock)
    • templates/ โ€” Jinja2 templates for mocks and scaffolding
  • tests/ โ€” comprehensive Python test suite
  • DEVELOPMENT_PLAN.md โ€” implementation checklist (all 17 steps โœ…)

Dependencies

Runtime:

  • click>=8.0 โ€” CLI framework
  • rich>=13.0 โ€” terminal formatting
  • pyyaml>=6.0 โ€” YAML config parsing
  • pycparser>=2.21 โ€” C header parsing
  • jinja2>=3.0 โ€” template engine
  • gcovr>=7.0 โ€” coverage reporting

Development:

  • pytest>=7.0 โ€” testing
  • pytest-cov>=4.0 โ€” coverage measurement

Known Limitations (v0.0.1)

  • Mock generation: Skips variadic functions, function-pointer params, and incomplete struct-by-value params (warns during generation)
  • Coverage: Native target only; requires GCC with -fprofile-arcs -ftest-coverage support
  • Emulation: Timeout-based (default 30s per test binary)

Roadmap

Phase Items
v0.1 โœ… Core pipeline (discover โ†’ mock โ†’ compile โ†’ run โ†’ report)
v0.2 ๐Ÿ“‹ Enhanced mock generation (variadic support, callbacks)
v0.3 ๐Ÿ“‹ CI/CD integration templates (GitHub Actions, GitLab CI)
v0.4 ๐Ÿ“‹ IDE integration (VS Code extension)

Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Write tests for your changes
  4. Run the full test suite (pytest)
  5. Commit with conventional messages
  6. Push and open a pull request

For major changes, please open an issue first to discuss.

License

MIT โ€” see LICENSE file for details.

Credits

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vyperling-0.0.1.tar.gz (59.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vyperling-0.0.1-py3-none-any.whl (64.9 kB view details)

Uploaded Python 3

File details

Details for the file vyperling-0.0.1.tar.gz.

File metadata

  • Download URL: vyperling-0.0.1.tar.gz
  • Upload date:
  • Size: 59.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.13 Linux/6.17.0-1015-azure

File hashes

Hashes for vyperling-0.0.1.tar.gz
Algorithm Hash digest
SHA256 0e5553cea2598a302790c600d1571e6a7c3ec8c5dd36e37511a1fa0961bdebaf
MD5 b0ab0e6c6848e48b50132a6517445741
BLAKE2b-256 fbfc610f7f05bbd18e960e0b07d24a71a8c38776ed6bed3940960b2a0b48b32a

See more details on using hashes here.

File details

Details for the file vyperling-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: vyperling-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 64.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.13 Linux/6.17.0-1015-azure

File hashes

Hashes for vyperling-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b5a9e73a16feb53423385699e3a8e5153bd66bcd577ea44ab6b08dcce64fcb77
MD5 8043838800e2bc4e1a3998a9f9c79f0d
BLAKE2b-256 6333c62359721afa1dd730b9e54c3d437969851b3b656e6b6fbe42045b033a4a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page