//! The spool cannot grow without bound while the daemon is away. //! //! The hook appends one record per prompt and the daemon consumes the spool //! each tick. When the daemon is not running nothing consumes it, so the file //! grew forever and the daemon then read the whole thing into memory on //! recovery. The hook caps it at the source, which is the only place that //! actually bounds growth. use std::path::PathBuf; use std::process::Command; fn hook_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../shell-hooks/signald-hooks.zsh") .canonicalize() .expect("hook script") } /// Source the real hook, drive `precmd` past a small cap, and check the file /// was truncated rather than left to grow. #[test] fn the_hook_truncates_a_spool_past_the_cap() { let dir = std::env::temp_dir().join(format!("signald-spoolcap-{}", std::process::id())); let _ = std::fs::remove_dir_all(&dir); std::fs::create_dir_all(&dir).unwrap(); let spool = dir.join("terminal.spool"); let script = format!( r#" export SIGNALD_SPOOL={spool} export SIGNALD_SPOOL_MAX_BYTES=200 source {hook} for i in {{1..40}}; do _signald_precmd; done wc -c < $SIGNALD_SPOOL "#, spool = spool.display(), hook = hook_path().display(), ); let out = Command::new("zsh") .arg("-c") .arg(&script) .output() .expect("zsh runs the hook"); assert!( out.status.success(), "hook failed: {}", String::from_utf8_lossy(&out.stderr) ); let size: u64 = String::from_utf8_lossy(&out.stdout) .trim() .parse() .expect("wc prints a size"); assert!(size <= 200, "spool grew to {size} bytes past a 200-byte cap"); assert!(size > 0, "truncation must not stop the current record landing"); let _ = std::fs::remove_dir_all(&dir); }