mirror of
https://github.com/cjfranko/NTP-Timeturner.git
synced 2025-11-08 18:32:02 +00:00
test: add tests for ensure_config function
Co-authored-by: aider (gemini/gemini-2.5-pro-preview-05-06) <aider@aider.chat>
This commit is contained in:
parent
3cbe95bd6a
commit
a124aae424
1 changed files with 127 additions and 81 deletions
208
src/main.rs
208
src/main.rs
|
|
@ -1,81 +1,127 @@
|
||||||
// src/main.rs
|
// src/main.rs
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod sync_logic;
|
mod sync_logic;
|
||||||
mod serial_input;
|
mod serial_input;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use crate::config::watch_config;
|
use crate::config::watch_config;
|
||||||
use crate::sync_logic::LtcState;
|
use crate::sync_logic::LtcState;
|
||||||
use crate::serial_input::start_serial_thread;
|
use crate::serial_input::start_serial_thread;
|
||||||
use crate::ui::start_ui;
|
use crate::ui::start_ui;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::{Arc, Mutex, mpsc},
|
sync::{Arc, Mutex, mpsc},
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Embed the default config.json at compile time.
|
/// Embed the default config.json at compile time.
|
||||||
const DEFAULT_CONFIG: &str = include_str!("../config.json");
|
const DEFAULT_CONFIG: &str = include_str!("../config.json");
|
||||||
|
|
||||||
/// If no `config.json` exists alongside the binary, write out the default.
|
/// If no `config.json` exists alongside the binary, write out the default.
|
||||||
fn ensure_config() {
|
fn ensure_config() {
|
||||||
let p = Path::new("config.json");
|
let p = Path::new("config.json");
|
||||||
if !p.exists() {
|
if !p.exists() {
|
||||||
fs::write(p, DEFAULT_CONFIG)
|
fs::write(p, DEFAULT_CONFIG)
|
||||||
.expect("Failed to write default config.json");
|
.expect("Failed to write default config.json");
|
||||||
eprintln!("⚙️ Emitted default config.json");
|
eprintln!("⚙️ Emitted default config.json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// 🔄 Ensure there's always a config.json present
|
// 🔄 Ensure there's always a config.json present
|
||||||
ensure_config();
|
ensure_config();
|
||||||
|
|
||||||
// 1️⃣ Start watching config.json for changes
|
// 1️⃣ Start watching config.json for changes
|
||||||
let hw_offset = watch_config("config.json");
|
let hw_offset = watch_config("config.json");
|
||||||
println!("🔧 Watching config.json (hardware_offset_ms)...");
|
println!("🔧 Watching config.json (hardware_offset_ms)...");
|
||||||
|
|
||||||
// 2️⃣ Channel for raw LTC frames
|
// 2️⃣ Channel for raw LTC frames
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
println!("✅ Channel created");
|
println!("✅ Channel created");
|
||||||
|
|
||||||
// 3️⃣ Shared state for UI and serial reader
|
// 3️⃣ Shared state for UI and serial reader
|
||||||
let ltc_state = Arc::new(Mutex::new(LtcState::new()));
|
let ltc_state = Arc::new(Mutex::new(LtcState::new()));
|
||||||
println!("✅ State initialised");
|
println!("✅ State initialised");
|
||||||
|
|
||||||
// 4️⃣ Spawn the serial reader thread (no offset here)
|
// 4️⃣ Spawn the serial reader thread (no offset here)
|
||||||
{
|
{
|
||||||
let tx_clone = tx.clone();
|
let tx_clone = tx.clone();
|
||||||
let state_clone = ltc_state.clone();
|
let state_clone = ltc_state.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
println!("🚀 Serial thread launched");
|
println!("🚀 Serial thread launched");
|
||||||
start_serial_thread(
|
start_serial_thread(
|
||||||
"/dev/ttyACM0",
|
"/dev/ttyACM0",
|
||||||
115200,
|
115200,
|
||||||
tx_clone,
|
tx_clone,
|
||||||
state_clone,
|
state_clone,
|
||||||
0, // ignored in serial path
|
0, // ignored in serial path
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5️⃣ Spawn the UI renderer thread, passing the live offset Arc
|
// 5️⃣ Spawn the UI renderer thread, passing the live offset Arc
|
||||||
{
|
{
|
||||||
let ui_state = ltc_state.clone();
|
let ui_state = ltc_state.clone();
|
||||||
let offset_clone = hw_offset.clone();
|
let offset_clone = hw_offset.clone();
|
||||||
let port = "/dev/ttyACM0".to_string();
|
let port = "/dev/ttyACM0".to_string();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
println!("🖥️ UI thread launched");
|
println!("🖥️ UI thread launched");
|
||||||
start_ui(ui_state, port, offset_clone);
|
start_ui(ui_state, port, offset_clone);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6️⃣ Keep main thread alive
|
// 6️⃣ Keep main thread alive
|
||||||
println!("📡 Main thread entering loop...");
|
println!("📡 Main thread entering loop...");
|
||||||
for _frame in rx {
|
for _frame in rx {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// RAII guard to ensure config file is cleaned up after test.
|
||||||
|
struct ConfigGuard;
|
||||||
|
|
||||||
|
impl Drop for ConfigGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = fs::remove_file("config.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ensure_config() {
|
||||||
|
let _guard = ConfigGuard; // Cleanup when _guard goes out of scope.
|
||||||
|
|
||||||
|
// --- Test 1: File creation ---
|
||||||
|
// Pre-condition: config.json does not exist.
|
||||||
|
let _ = fs::remove_file("config.json");
|
||||||
|
|
||||||
|
ensure_config();
|
||||||
|
|
||||||
|
// Post-condition: config.json exists and has default content.
|
||||||
|
let p = Path::new("config.json");
|
||||||
|
assert!(p.exists(), "config.json should have been created");
|
||||||
|
let contents = fs::read_to_string(p).expect("Failed to read created config.json");
|
||||||
|
assert_eq!(contents, DEFAULT_CONFIG, "config.json content should match default");
|
||||||
|
|
||||||
|
// --- Test 2: File is not overwritten ---
|
||||||
|
// Pre-condition: config.json exists with different content.
|
||||||
|
let custom_content = "{\"hardware_offset_ms\": 999}";
|
||||||
|
fs::write("config.json", custom_content)
|
||||||
|
.expect("Failed to write custom config.json for test");
|
||||||
|
|
||||||
|
ensure_config();
|
||||||
|
|
||||||
|
// Post-condition: config.json still has the custom content.
|
||||||
|
let contents_after = fs::read_to_string("config.json")
|
||||||
|
.expect("Failed to read config.json after second ensure_config call");
|
||||||
|
assert_eq!(contents_after, custom_content, "config.json should not be overwritten");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue