use anvil::templates::{TemplateManager, TemplateContext}; use std::fs; use tempfile::TempDir; // ========================================================================== // Mock Arduino: template file content verification // ========================================================================== #[test] fn test_mock_arduino_header_has_core_api() { let tmp = TempDir::new().unwrap(); let ctx = TemplateContext { project_name: "mock_test".to_string(), anvil_version: "1.0.0".to_string(), board_name: "uno".to_string(), fqbn: "arduino:avr:uno".to_string(), baud: 115200, }; TemplateManager::extract("basic", tmp.path(), &ctx).unwrap(); let header = fs::read_to_string(tmp.path().join("test/mocks/mock_arduino.h")).unwrap(); // Core Arduino constants assert!(header.contains("#define INPUT"), "Should define INPUT"); assert!(header.contains("#define OUTPUT"), "Should define OUTPUT"); assert!(header.contains("#define HIGH"), "Should define HIGH"); assert!(header.contains("#define LOW"), "Should define LOW"); assert!(header.contains("#define LED_BUILTIN"), "Should define LED_BUILTIN"); assert!(header.contains("#define A0"), "Should define A0"); // Core Arduino functions (declarations use aligned whitespace) assert!(header.contains("pinMode("), "Should declare pinMode"); assert!(header.contains("digitalWrite("), "Should declare digitalWrite"); assert!(header.contains("digitalRead("), "Should declare digitalRead"); assert!(header.contains("analogRead("), "Should declare analogRead"); assert!(header.contains("analogWrite("), "Should declare analogWrite"); assert!(header.contains("millis()"), "Should declare millis"); assert!(header.contains("delay("), "Should declare delay"); // Serial class assert!(header.contains("class MockSerial"), "Should declare MockSerial"); assert!(header.contains("extern MockSerial Serial"), "Should declare global Serial"); // Test control API assert!(header.contains("mock_arduino_reset()"), "Should have reset"); assert!(header.contains("mock_arduino_advance_millis("), "Should have advance_millis"); assert!(header.contains("mock_arduino_set_digital("), "Should have set_digital"); assert!(header.contains("mock_arduino_set_analog("), "Should have set_analog"); // String class assert!(header.contains("class String"), "Should declare String class"); } #[test] fn test_mock_arduino_shims_exist() { let tmp = TempDir::new().unwrap(); let ctx = TemplateContext { project_name: "shim_test".to_string(), anvil_version: "1.0.0".to_string(), board_name: "uno".to_string(), fqbn: "arduino:avr:uno".to_string(), baud: 115200, }; TemplateManager::extract("basic", tmp.path(), &ctx).unwrap(); // Arduino.h shim should include mock_arduino.h let arduino_h = fs::read_to_string(tmp.path().join("test/mocks/Arduino.h")).unwrap(); assert!( arduino_h.contains("mock_arduino.h"), "Arduino.h shim should redirect to mock_arduino.h" ); // Wire.h shim should provide MockWire let wire_h = fs::read_to_string(tmp.path().join("test/mocks/Wire.h")).unwrap(); assert!(wire_h.contains("class MockWire"), "Wire.h should declare MockWire"); assert!(wire_h.contains("extern MockWire Wire"), "Wire.h should declare global Wire"); // SPI.h shim should provide MockSPI let spi_h = fs::read_to_string(tmp.path().join("test/mocks/SPI.h")).unwrap(); assert!(spi_h.contains("class MockSPI"), "SPI.h should declare MockSPI"); assert!(spi_h.contains("extern MockSPI SPI"), "SPI.h should declare global SPI"); assert!(spi_h.contains("SPI_MODE0"), "SPI.h should define SPI modes"); assert!(spi_h.contains("struct SPISettings"), "SPI.h should define SPISettings"); } #[test] fn test_mock_arduino_all_files_ascii() { let tmp = TempDir::new().unwrap(); let ctx = TemplateContext { project_name: "ascii_mock".to_string(), anvil_version: "1.0.0".to_string(), board_name: "uno".to_string(), fqbn: "arduino:avr:uno".to_string(), baud: 115200, }; TemplateManager::extract("basic", tmp.path(), &ctx).unwrap(); let mock_files = vec![ "test/mocks/mock_arduino.h", "test/mocks/mock_arduino.cpp", "test/mocks/Arduino.h", "test/mocks/Wire.h", "test/mocks/SPI.h", ]; for filename in &mock_files { let content = fs::read_to_string(tmp.path().join(filename)).unwrap(); for (line_num, line) in content.lines().enumerate() { for (col, ch) in line.chars().enumerate() { assert!( ch.is_ascii(), "Non-ASCII in {} at {}:{}: '{}' (U+{:04X})", filename, line_num + 1, col + 1, ch, ch as u32 ); } } } } #[test] fn test_cmake_links_mock_arduino() { let tmp = TempDir::new().unwrap(); let ctx = TemplateContext { project_name: "cmake_test".to_string(), anvil_version: "1.0.0".to_string(), board_name: "uno".to_string(), fqbn: "arduino:avr:uno".to_string(), baud: 115200, }; TemplateManager::extract("basic", tmp.path(), &ctx).unwrap(); let cmake = fs::read_to_string(tmp.path().join("test/CMakeLists.txt")).unwrap(); // Should define mock_arduino library assert!(cmake.contains("add_library(mock_arduino"), "Should define mock_arduino library"); assert!(cmake.contains("mock_arduino.cpp"), "Should compile mock_arduino.cpp"); // Both test targets should link mock_arduino assert!(cmake.contains("target_link_libraries(test_unit"), "Should have test_unit target"); assert!(cmake.contains("target_link_libraries(test_system"), "Should have test_system target"); // System test target assert!(cmake.contains("add_executable(test_system"), "Should build test_system"); assert!(cmake.contains("test_system.cpp"), "Should compile test_system.cpp"); // gtest discovery for both assert!(cmake.contains("gtest_discover_tests(test_unit)"), "Should discover unit tests"); assert!(cmake.contains("gtest_discover_tests(test_system)"), "Should discover system tests"); } #[test] fn test_system_test_template_uses_simhal() { let tmp = TempDir::new().unwrap(); let ctx = TemplateContext { project_name: "sys_test".to_string(), anvil_version: "1.0.0".to_string(), board_name: "uno".to_string(), fqbn: "arduino:avr:uno".to_string(), baud: 115200, }; TemplateManager::extract("basic", tmp.path(), &ctx).unwrap(); let system_test = fs::read_to_string(tmp.path().join("test/test_system.cpp")).unwrap(); // Should include mock_arduino and sim_hal assert!(system_test.contains("mock_arduino.h"), "Should include mock_arduino.h"); assert!(system_test.contains("sim_hal.h"), "Should include sim_hal.h"); assert!(system_test.contains("sys_test_app.h"), "Should reference project app header"); // Should use SimHal, not MockHal assert!(system_test.contains("SimHal"), "Should use SimHal for system tests"); // Should call mock_arduino_reset assert!(system_test.contains("mock_arduino_reset()"), "Should reset mock state in SetUp"); } #[test] fn test_root_test_scripts_exist_and_reference_test_dir() { let tmp = TempDir::new().unwrap(); let ctx = TemplateContext { project_name: "script_test".to_string(), anvil_version: "1.0.0".to_string(), board_name: "uno".to_string(), fqbn: "arduino:avr:uno".to_string(), baud: 115200, }; TemplateManager::extract("basic", tmp.path(), &ctx).unwrap(); // test.sh let test_sh = fs::read_to_string(tmp.path().join("test.sh")).unwrap(); assert!(test_sh.contains("TEST_DIR="), "test.sh should set TEST_DIR"); assert!(test_sh.contains("cmake"), "test.sh should invoke cmake"); assert!(test_sh.contains("ctest"), "test.sh should invoke ctest"); assert!(test_sh.contains("--unit"), "test.sh should support --unit flag"); assert!(test_sh.contains("--system"), "test.sh should support --system flag"); assert!(test_sh.contains("--clean"), "test.sh should support --clean flag"); // test.bat let test_bat = fs::read_to_string(tmp.path().join("test.bat")).unwrap(); assert!(test_bat.contains("TEST_DIR"), "test.bat should set TEST_DIR"); assert!(test_bat.contains("cmake"), "test.bat should invoke cmake"); assert!(test_bat.contains("ctest"), "test.bat should invoke ctest"); assert!(test_bat.contains("--unit"), "test.bat should support --unit flag"); assert!(test_bat.contains("--system"), "test.bat should support --system flag"); }