Add mock Arduino for x86_64 host-side testing

Complete Arduino API mock (mock_arduino.h/cpp) enabling application
code to compile and run on PC without hardware. Includes MockSerial,
String class, GPIO/analog/timing/interrupt mocks with state tracking
and test control API.

- Arduino.h, Wire.h, SPI.h shims intercept includes in test builds
- System test template (test_system.cpp) using SimHal
- CMakeLists.txt builds mock_arduino as static lib, links both suites
- Root test.sh/test.bat with --unit/--system/--clean/--verbose flags
- test.bat auto-detects MSVC via vswhere + vcvarsall.bat
- Doctor reports nuanced compiler status (on PATH vs installed)
- Refresh pulls mock infrastructure into existing projects
- 15 tests passing: 7 unit (MockHal) + 8 system (SimHal)
This commit is contained in:
Eric Ratliff
2026-02-20 08:21:11 -06:00
parent 1ae136530f
commit aa1e9d5043
13 changed files with 1831 additions and 16 deletions

View File

@@ -13,6 +13,7 @@ pub struct SystemHealth {
pub dialout_ok: bool,
pub cmake_ok: bool,
pub cpp_compiler_ok: bool,
pub cpp_on_path: bool,
pub git_ok: bool,
pub ports_found: usize,
}
@@ -99,7 +100,14 @@ pub fn check_system_health() -> SystemHealth {
let cmake_ok = which::which("cmake").is_ok();
// C++ compiler (optional -- for host tests)
let cpp_compiler_ok = has_cpp_compiler();
let cpp_on_path = which::which("g++").is_ok()
|| which::which("clang++").is_ok()
|| which::which("cl").is_ok();
let cpp_compiler_ok = if cpp_on_path {
true
} else {
has_cpp_compiler()
};
// git
let git_ok = which::which("git").is_ok();
@@ -115,6 +123,7 @@ pub fn check_system_health() -> SystemHealth {
dialout_ok,
cmake_ok,
cpp_compiler_ok,
cpp_on_path,
git_ok,
ports_found,
}
@@ -130,6 +139,20 @@ fn has_cpp_compiler() -> bool {
if which::which("cl").is_ok() {
return true;
}
// cl.exe may be installed via VS Build Tools but not on PATH.
// Check via vswhere.exe (ships with VS installer).
if let Ok(output) = std::process::Command::new(
r"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe",
)
.args(["-latest", "-property", "installationPath"])
.output()
{
let path = String::from_utf8_lossy(&output.stdout);
let vcvarsall = format!("{}\\VC\\Auxiliary\\Build\\vcvarsall.bat", path.trim());
if std::path::Path::new(&vcvarsall).exists() {
return true;
}
}
}
false
}
@@ -217,8 +240,14 @@ fn print_diagnostics(health: &SystemHealth) {
}
// C++ compiler
if health.cpp_compiler_ok {
if health.cpp_on_path {
println!(" {} C++ compiler", "ok".green());
} else if health.cpp_compiler_ok {
println!(
" {} C++ compiler {}",
"ok".green(),
"(MSVC installed; test.bat will configure automatically)".bright_black()
);
} else {
println!(
" {} C++ compiler {}",
@@ -655,16 +684,16 @@ fn fix_spec_cmake(pm: Option<&str>) -> FixSpec {
fn fix_spec_cpp(pm: Option<&str>) -> FixSpec {
match pm {
Some("winget") => FixSpec::Auto {
prompt: "Install Visual Studio Build Tools via winget?",
program: "winget",
args: &["install", "--id", "Microsoft.VisualStudio.2022.BuildTools", "-e"],
},
Some("choco") => FixSpec::Auto {
prompt: "Install MinGW g++ via Chocolatey?",
prompt: "Install MinGW g++ via Chocolatey? (recommended -- lands on PATH)",
program: "choco",
args: &["install", "mingw", "-y"],
},
Some("winget") => FixSpec::Auto {
prompt: "Install Visual Studio Build Tools via winget? (test.bat will find it automatically)",
program: "winget",
args: &["install", "--id", "Microsoft.VisualStudio.2022.BuildTools", "-e"],
},
Some("brew") => FixSpec::Manual {
message: "run: xcode-select --install",
},
@@ -751,7 +780,7 @@ fn hint_cmake() -> &'static str {
fn hint_cpp_compiler() -> &'static str {
if cfg!(target_os = "windows") {
"install: winget install Microsoft.VisualStudio.2022.BuildTools (or MinGW g++)"
"install: choco install mingw (or open Developer Command Prompt for MSVC)"
} else if cfg!(target_os = "macos") {
"install: xcode-select --install"
} else {

View File

@@ -16,10 +16,20 @@ const REFRESHABLE_FILES: &[&str] = &[
"upload.bat",
"monitor.sh",
"monitor.bat",
"test.sh",
"test.bat",
"_detect_port.ps1",
"_monitor_filter.ps1",
"test/run_tests.sh",
"test/run_tests.bat",
"test/CMakeLists.txt",
"test/mocks/mock_arduino.h",
"test/mocks/mock_arduino.cpp",
"test/mocks/Arduino.h",
"test/mocks/Wire.h",
"test/mocks/SPI.h",
"test/mocks/mock_hal.h",
"test/mocks/sim_hal.h",
];
pub fn run_refresh(project_dir: Option<&str>, force: bool) -> Result<()> {