Included pin subcommand with auditing and assignment
This commit is contained in:
@@ -1970,4 +1970,275 @@ fn test_monitor_sh_timestamps_work_in_watch_mode() {
|
||||
"monitor_filter should be defined and used in both watch and normal mode (found {} refs)",
|
||||
filter_count
|
||||
);
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Pin map data
|
||||
// ==========================================================================
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_exists_for_all_presets() {
|
||||
use anvil::board::presets;
|
||||
use anvil::board::pinmap;
|
||||
|
||||
// Every board preset (except nano-old) should have a pin map
|
||||
for preset in presets::PRESETS {
|
||||
let found = pinmap::find_pinmap_fuzzy(preset.name);
|
||||
assert!(
|
||||
found.is_some(),
|
||||
"Board preset '{}' has no pin map. Add one to pinmap.rs.",
|
||||
preset.name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_uno_basics() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let m = pinmap::find_pinmap("uno").unwrap();
|
||||
assert_eq!(m.total_digital, 14);
|
||||
assert_eq!(m.total_analog, 6);
|
||||
// Uno has 20 pins total (D0-D13 + A0-A5 mapped as D14-D19)
|
||||
assert_eq!(m.pins.len(), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_mega_basics() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let m = pinmap::find_pinmap("mega").unwrap();
|
||||
assert_eq!(m.total_digital, 54);
|
||||
assert_eq!(m.total_analog, 16);
|
||||
// Mega has 70 pins: D0-D53 + A0-A15
|
||||
assert_eq!(m.pins.len(), 70);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_resolve_alias_a0() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let uno = pinmap::find_pinmap("uno").unwrap();
|
||||
assert_eq!(pinmap::resolve_alias(uno, "A0"), Some(14));
|
||||
assert_eq!(pinmap::resolve_alias(uno, "a0"), Some(14));
|
||||
|
||||
let mega = pinmap::find_pinmap("mega").unwrap();
|
||||
assert_eq!(pinmap::resolve_alias(mega, "A0"), Some(54));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_spi_pins_differ_by_board() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let uno = pinmap::find_pinmap("uno").unwrap();
|
||||
let mega = pinmap::find_pinmap("mega").unwrap();
|
||||
|
||||
let uno_spi = uno.groups.iter().find(|g| g.name == "spi").unwrap();
|
||||
let mega_spi = mega.groups.iter().find(|g| g.name == "spi").unwrap();
|
||||
|
||||
let uno_sck = uno_spi.fixed_pins.iter().find(|p| p.0 == "sck").unwrap().1;
|
||||
let mega_sck = mega_spi.fixed_pins.iter().find(|p| p.0 == "sck").unwrap().1;
|
||||
|
||||
// Uno SCK=13, Mega SCK=52
|
||||
assert_ne!(uno_sck, mega_sck, "SPI SCK should differ between uno and mega");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_capabilities_filter() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let m = pinmap::find_pinmap("uno").unwrap();
|
||||
let pwm = pinmap::pins_with_capability(m, "pwm");
|
||||
// Uno has PWM on 3, 5, 6, 9, 10, 11
|
||||
assert_eq!(pwm.len(), 6);
|
||||
|
||||
let analog = pinmap::pins_with_capability(m, "analog");
|
||||
assert_eq!(analog.len(), 6); // A0-A5
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_board_capabilities_list() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let m = pinmap::find_pinmap("uno").unwrap();
|
||||
let caps = pinmap::board_capabilities(m);
|
||||
assert!(caps.contains(&"digital"));
|
||||
assert!(caps.contains(&"analog"));
|
||||
assert!(caps.contains(&"pwm"));
|
||||
assert!(caps.contains(&"spi"));
|
||||
assert!(caps.contains(&"i2c"));
|
||||
assert!(caps.contains(&"uart"));
|
||||
assert!(caps.contains(&"interrupt"));
|
||||
assert!(caps.contains(&"led"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_mega_has_four_uarts() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let m = pinmap::find_pinmap("mega").unwrap();
|
||||
let uart_count = m.groups.iter()
|
||||
.filter(|g| g.name.starts_with("uart"))
|
||||
.count();
|
||||
assert_eq!(uart_count, 4, "Mega should have uart, uart1, uart2, uart3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_leonardo_i2c_different_from_uno() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let uno = pinmap::find_pinmap("uno").unwrap();
|
||||
let leo = pinmap::find_pinmap("leonardo").unwrap();
|
||||
|
||||
let uno_i2c = uno.groups.iter().find(|g| g.name == "i2c").unwrap();
|
||||
let leo_i2c = leo.groups.iter().find(|g| g.name == "i2c").unwrap();
|
||||
|
||||
let uno_sda = uno_i2c.fixed_pins.iter().find(|p| p.0 == "sda").unwrap().1;
|
||||
let leo_sda = leo_i2c.fixed_pins.iter().find(|p| p.0 == "sda").unwrap().1;
|
||||
|
||||
// Uno SDA=18 (A4), Leonardo SDA=2
|
||||
assert_ne!(uno_sda, leo_sda, "I2C SDA should differ between uno and leonardo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_spi_has_user_selectable_cs() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
// All boards with SPI should require user to select CS
|
||||
for board in pinmap::available_boards() {
|
||||
let m = pinmap::find_pinmap(board).unwrap();
|
||||
if let Some(spi) = m.groups.iter().find(|g| g.name == "spi") {
|
||||
assert!(
|
||||
spi.user_selectable.contains(&"cs"),
|
||||
"Board '{}' SPI should have user-selectable CS pin",
|
||||
board
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinmap_nano_has_analog_only_pins() {
|
||||
use anvil::board::pinmap;
|
||||
|
||||
let m = pinmap::find_pinmap("nano").unwrap();
|
||||
let a6 = pinmap::get_pin(m, 20).unwrap(); // A6
|
||||
assert!(a6.capabilities.contains(&"analog"));
|
||||
assert!(!a6.capabilities.contains(&"digital"),
|
||||
"Nano A6 should be analog-only");
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Pin config in .anvil.toml
|
||||
// ==========================================================================
|
||||
|
||||
#[test]
|
||||
fn test_config_with_pins_roundtrips() {
|
||||
use anvil::project::config::{BoardPinConfig, PinAssignment, BusConfig};
|
||||
use std::collections::HashMap;
|
||||
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let mut config = ProjectConfig::new("pin_test");
|
||||
|
||||
// Add pin assignments
|
||||
let mut assignments = HashMap::new();
|
||||
assignments.insert("red_led".to_string(), PinAssignment {
|
||||
pin: 13,
|
||||
mode: "output".to_string(),
|
||||
});
|
||||
assignments.insert("limit_switch".to_string(), PinAssignment {
|
||||
pin: 7,
|
||||
mode: "input".to_string(),
|
||||
});
|
||||
|
||||
let mut buses = HashMap::new();
|
||||
let mut spi_pins = HashMap::new();
|
||||
spi_pins.insert("cs".to_string(), 10u8);
|
||||
buses.insert("spi".to_string(), BusConfig { user_pins: spi_pins });
|
||||
buses.insert("i2c".to_string(), BusConfig { user_pins: HashMap::new() });
|
||||
|
||||
config.pins.insert("uno".to_string(), BoardPinConfig {
|
||||
assignments,
|
||||
buses,
|
||||
});
|
||||
|
||||
config.save(tmp.path()).unwrap();
|
||||
let loaded = ProjectConfig::load(tmp.path()).unwrap();
|
||||
|
||||
let pc = loaded.pins.get("uno").unwrap();
|
||||
assert_eq!(pc.assignments.len(), 2);
|
||||
assert_eq!(pc.assignments["red_led"].pin, 13);
|
||||
assert_eq!(pc.assignments["red_led"].mode, "output");
|
||||
assert_eq!(pc.assignments["limit_switch"].pin, 7);
|
||||
assert_eq!(pc.buses.len(), 2);
|
||||
assert_eq!(*pc.buses["spi"].user_pins.get("cs").unwrap(), 10u8);
|
||||
assert!(pc.buses["i2c"].user_pins.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_without_pins_loads_empty() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let config = ProjectConfig::new("no_pins");
|
||||
config.save(tmp.path()).unwrap();
|
||||
|
||||
let loaded = ProjectConfig::load(tmp.path()).unwrap();
|
||||
assert!(loaded.pins.is_empty(), "Config without pins section should load with empty pins");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_pins_skip_serializing_when_empty() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let config = ProjectConfig::new("empty_pins");
|
||||
config.save(tmp.path()).unwrap();
|
||||
|
||||
let content = fs::read_to_string(tmp.path().join(CONFIG_FILENAME)).unwrap();
|
||||
assert!(
|
||||
!content.contains("[pins"),
|
||||
"Empty pins should not appear in serialized config"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pins_per_board_independence() {
|
||||
use anvil::project::config::{BoardPinConfig, PinAssignment};
|
||||
use std::collections::HashMap;
|
||||
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let mut config = ProjectConfig::new("multi_board_pins");
|
||||
config.boards.insert("mega".to_string(), BoardProfile {
|
||||
fqbn: "arduino:avr:mega:cpu=atmega2560".to_string(),
|
||||
baud: None,
|
||||
});
|
||||
|
||||
// Different pin numbers for same friendly name on different boards
|
||||
let mut uno_assigns = HashMap::new();
|
||||
uno_assigns.insert("status_led".to_string(), PinAssignment {
|
||||
pin: 13, mode: "output".to_string(),
|
||||
});
|
||||
|
||||
let mut mega_assigns = HashMap::new();
|
||||
mega_assigns.insert("status_led".to_string(), PinAssignment {
|
||||
pin: 13, mode: "output".to_string(),
|
||||
});
|
||||
mega_assigns.insert("extra_led".to_string(), PinAssignment {
|
||||
pin: 22, mode: "output".to_string(),
|
||||
});
|
||||
|
||||
config.pins.insert("uno".to_string(), BoardPinConfig {
|
||||
assignments: uno_assigns, buses: HashMap::new(),
|
||||
});
|
||||
config.pins.insert("mega".to_string(), BoardPinConfig {
|
||||
assignments: mega_assigns, buses: HashMap::new(),
|
||||
});
|
||||
|
||||
config.save(tmp.path()).unwrap();
|
||||
let loaded = ProjectConfig::load(tmp.path()).unwrap();
|
||||
|
||||
let uno_pins = loaded.pins.get("uno").unwrap();
|
||||
let mega_pins = loaded.pins.get("mega").unwrap();
|
||||
|
||||
assert_eq!(uno_pins.assignments.len(), 1);
|
||||
assert_eq!(mega_pins.assignments.len(), 2);
|
||||
assert!(mega_pins.assignments.contains_key("extra_led"));
|
||||
assert!(!uno_pins.assignments.contains_key("extra_led"));
|
||||
}
|
||||
Reference in New Issue
Block a user