Arduino CLI build system with HAL-based test architecture

Build and upload tool (arduino-build.sh):
- Compile, upload, and monitor via arduino-cli
- Device discovery with USB ID identification (--devices)
- Persistent reconnecting serial monitor (--watch)
- Split compile/upload workflow (--verify, --upload-only)
- First-time setup wizard (--setup)
- Comprehensive --help with troubleshooting and RedBoard specs

Testable application architecture:
- Hardware abstraction layer (lib/hal/) decouples logic from Arduino API
- Google Mock HAL for unit tests (exact call verification)
- Simulated HAL for system tests (GPIO state, virtual clock, I2C devices)
- Example I2C temperature sensor simulator (TMP102)
- Host-side test suite via CMake + Google Test (21 tests)

Example sketch:
- blink/ -- LED blink with button-controlled speed, wired through HAL
This commit is contained in:
Eric Ratliff
2026-02-14 09:25:49 -06:00
committed by Eric Ratliff
commit 61f4659462
18 changed files with 2417 additions and 0 deletions

84
test/run_tests.sh Executable file
View File

@@ -0,0 +1,84 @@
#!/usr/bin/env bash
#
# run_tests.sh -- Build and run host-side unit and system tests
#
# Usage:
# ./test/run_tests.sh Build and run all tests
# ./test/run_tests.sh --clean Clean rebuild
# ./test/run_tests.sh --verbose Verbose test output
# ./test/run_tests.sh --filter X Run only tests matching X
#
# Prerequisites:
# cmake >= 3.14, g++ or clang++, git (for fetching gtest)
#
# First run will download Google Test (~30 seconds).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BUILD_DIR="$SCRIPT_DIR/build"
# Color output
if [[ -t 1 ]]; then
RED=$'\033[0;31m'; GRN=$'\033[0;32m'; CYN=$'\033[0;36m'
BLD=$'\033[1m'; RST=$'\033[0m'
else
RED=''; GRN=''; CYN=''; BLD=''; RST=''
fi
info() { echo -e "${CYN}[TEST]${RST} $*"; }
ok() { echo -e "${GRN}[PASS]${RST} $*"; }
die() { echo -e "${RED}[FAIL]${RST} $*" >&2; exit 1; }
# Parse args
DO_CLEAN=0
VERBOSE=""
FILTER=""
for arg in "$@"; do
case "$arg" in
--clean) DO_CLEAN=1 ;;
--verbose) VERBOSE="--verbose" ;;
--filter) : ;; # next arg is the pattern
-*) die "Unknown option: $arg" ;;
*) FILTER="$arg" ;;
esac
done
# Check prerequisites
command -v cmake &>/dev/null || die "cmake not found. Install: sudo apt install cmake"
command -v g++ &>/dev/null || command -v clang++ &>/dev/null || die "No C++ compiler found"
command -v git &>/dev/null || die "git not found (needed to fetch Google Test)"
# Clean if requested
if [[ $DO_CLEAN -eq 1 ]] && [[ -d "$BUILD_DIR" ]]; then
info "Cleaning build directory..."
rm -rf "$BUILD_DIR"
fi
# Configure
if [[ ! -f "$BUILD_DIR/CMakeCache.txt" ]]; then
info "Configuring (first run will fetch Google Test)..."
cmake -S "$SCRIPT_DIR" -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Debug
fi
# Build
info "Building tests..."
cmake --build "$BUILD_DIR" --parallel
# Run
echo ""
info "${BLD}Running tests...${RST}"
echo ""
CTEST_ARGS=("--test-dir" "$BUILD_DIR" "--output-on-failure")
[[ -n "$VERBOSE" ]] && CTEST_ARGS+=("--verbose")
[[ -n "$FILTER" ]] && CTEST_ARGS+=("-R" "$FILTER")
if ctest "${CTEST_ARGS[@]}"; then
echo ""
ok "${BLD}All tests passed.${RST}"
else
echo ""
die "Some tests failed."
fi