use crate::context::{Context, Environment};
use crate::error::Result;
use crate::service::Permit;
use crate::store::RawStore;
use std::path::{Path, PathBuf};
use std::sync::Arc;
#[cfg_attr(
not(windows),
allow(
dead_code,
reason = "only Windows reads an access list; every system's tests read these rules"
)
)]
pub(crate) mod acl;
pub(crate) mod administered;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(any(test, feature = "test-support"))]
pub(crate) mod memory;
#[cfg(any(windows, test))]
pub(crate) mod owner;
pub(crate) mod program;
#[cfg_attr(
not(windows),
allow(
dead_code,
reason = "only Windows replaces a file this way; every system's tests read these rules"
)
)]
pub(crate) mod replace;
#[cfg_attr(
not(windows),
allow(
dead_code,
reason = "only Windows schedules a task; every system's tests read these rules"
)
)]
pub(crate) mod task;
pub(crate) mod token;
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
#[cfg(target_os = "linux")]
use linux as os;
#[cfg(target_os = "macos")]
use macos as os;
#[cfg(windows)]
use windows as os;
pub(crate) use administered::{Administered, Hive};
pub(crate) use os::{fs, proc, user};
#[cfg(all(windows, any(test, feature = "test-support")))]
pub(crate) use windows::credman as windows_credman;
#[cfg(all(windows, test))]
pub(crate) use windows::seal::Dpapi;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Os {
MacOs,
Linux,
Windows,
}
pub const OS: Os = os::OS;
pub(crate) const SECURITY: &str = "/usr/bin/security";
impl Os {
pub(crate) fn secrets_tool(self) -> Option<&'static str> {
match self {
Os::MacOs => Some(SECURITY),
Os::Linux | Os::Windows => None,
}
}
pub(crate) fn make_private_commands(
self,
kind: Kind,
paths: &[&str],
sid: Option<&str>,
) -> Vec<String> {
match self {
Os::MacOs | Os::Linux => {
let mode = match kind {
Kind::Directory => "700",
Kind::File => "600",
Kind::Any => "go-rwx",
};
vec![format!("chmod {mode} {}", paths.join(" "))]
}
Os::Windows => match (kind, sid) {
(Kind::Directory | Kind::File, Some(sid)) => paths
.iter()
.flat_map(|path| acl::icacls(kind == Kind::Directory, path, sid))
.collect(),
_ => Vec::new(),
},
}
}
pub(crate) fn remove_file_command(self, path: &Path) -> String {
match self {
Os::MacOs | Os::Linux => {
format!("rm '{}'", path.display().to_string().replace('\'', r"'\''"))
}
Os::Windows => {
let mut quoted = String::new();
for c in path.display().to_string().chars() {
if matches!(c, '\'' | '\u{2018}' | '\u{2019}' | '\u{201a}' | '\u{201b}') {
quoted.push(c);
}
quoted.push(c);
}
format!("Remove-Item -LiteralPath '{quoted}'")
}
}
}
pub(crate) fn guarded_folders(self) -> &'static [&'static str] {
match self {
Os::MacOs => &[
"Desktop",
"Documents",
"Downloads",
"Library/Mobile Documents",
"Library/CloudStorage",
],
Os::Linux | Os::Windows => &[],
}
}
pub(crate) fn package_bins(self, env: &Environment) -> Vec<PathBuf> {
match self {
Os::MacOs => vec!["/opt/homebrew/bin".into(), "/usr/local/bin".into()],
Os::Linux => Vec::new(),
Os::Windows => [
("APPDATA", &["npm"][..]),
("LOCALAPPDATA", &["Microsoft", "WinGet", "Links"]),
("USERPROFILE", &["scoop", "shims"]),
]
.into_iter()
.filter_map(|(variable, below)| {
let mut place = env.named_folder(variable)?;
place.extend(below);
Some(place)
})
.collect(),
}
}
pub(crate) fn same_path(self, a: &Path, b: &Path) -> bool {
match self {
Os::MacOs | Os::Linux => a == b,
Os::Windows => same_path_in_any_case(a, b),
}
}
pub(crate) fn variable_key(self, name: &std::ffi::OsStr) -> std::ffi::OsString {
match self {
Os::MacOs | Os::Linux => name.to_os_string(),
Os::Windows => name
.to_string_lossy()
.chars()
.map(upper_one_for_one)
.collect::<String>()
.into(),
}
}
pub(crate) fn user_variable(self) -> &'static str {
match self {
Os::MacOs | Os::Linux => "USER",
Os::Windows => "USERNAME",
}
}
pub fn home_variable(self) -> &'static str {
match self {
Os::MacOs | Os::Linux => "HOME",
Os::Windows => "USERPROFILE",
}
}
pub(crate) fn default_pitboard_home(self, home: &Path) -> PathBuf {
match self {
Os::MacOs | Os::Linux => home.join(".pitboard"),
Os::Windows => home.join("AppData").join("Local").join("Pitboard"),
}
}
pub(crate) fn app_command_line(self, app: &Path) -> Option<PathBuf> {
match self {
Os::MacOs => (app.extension() == Some("app".as_ref()))
.then(|| app.join("Contents/Helpers/pitboard")),
Os::Linux => None,
Os::Windows => app
.file_name()
.filter(|name| name.eq_ignore_ascii_case("Pitboard.exe"))
.and_then(|_| app.parent())
.map(|install| install.join("bin").join("pitboard.exe")),
}
}
}
pub fn same_path_in_any_case(a: &Path, b: &Path) -> bool {
let (mut a, mut b) = (a.components(), b.components());
loop {
match (a.next(), b.next()) {
(None, None) => return true,
(Some(one), Some(other))
if same_name_in_any_case(one.as_os_str(), other.as_os_str()) => {}
_ => return false,
}
}
}
pub fn path_key_in_any_case(path: &str) -> String {
let path = path.replace('/', r"\");
let path = match path.get(..8) {
Some(verbatim) if verbatim.eq_ignore_ascii_case(r"\\?\UNC\") => {
format!(r"\\{}", &path[8..])
}
_ => path.strip_prefix(r"\\?\").unwrap_or(&path).to_owned(),
};
let upper = |name: &str| name.chars().map(upper_one_for_one).collect::<String>();
let drive = path
.as_bytes()
.get(..2)
.filter(|head| head[0].is_ascii_alphabetic() && head[1] == b':')
.map(|head| char::from(head[0].to_ascii_uppercase()));
let (root, rest) = if let Some(unc) = path.strip_prefix(r"\\") {
let mut parts = unc.splitn(3, '\\');
let (server, share) = (parts.next().unwrap_or(""), parts.next().unwrap_or(""));
(
format!(r"\\{}\{}\", upper(server), upper(share)),
parts.next().unwrap_or(""),
)
} else if let Some(drive) = drive {
match path[2..].strip_prefix('\\') {
Some(rest) => (format!(r"{drive}:\"), rest),
None => (format!("{drive}:"), &path[2..]),
}
} else if let Some(rest) = path.strip_prefix('\\') {
(r"\".to_owned(), rest)
} else {
(String::new(), path.as_str())
};
let rooted = root.ends_with('\\');
let mut names: Vec<&str> = Vec::new();
for name in rest.split('\\') {
match name {
"" | "." => {}
".." if names.last().is_some_and(|last| *last != "..") => {
names.pop();
}
".." if rooted => {}
name => names.push(name),
}
}
let names: Vec<String> = names.into_iter().map(upper).collect();
root + &names.join(r"\")
}
pub(crate) fn without_verbatim_prefix(path: PathBuf) -> PathBuf {
let Some(text) = path.to_str() else {
return path;
};
if let Some(unc) = text.strip_prefix(r"\\?\UNC\") {
return PathBuf::from(format!(r"\\{unc}"));
}
match text.strip_prefix(r"\\?\") {
Some(rest) if drive_absolute(rest) => PathBuf::from(rest),
_ => path,
}
}
fn drive_absolute(path: &str) -> bool {
matches!(path.as_bytes(), [drive, b':', b'\\' | b'/', ..] if drive.is_ascii_alphabetic())
}
#[cfg(any(test, feature = "test-support"))]
pub(crate) fn claude_desktop_on_windows(roaming: &[PathBuf], local: &[PathBuf]) -> Vec<PathBuf> {
let roaming = roaming
.iter()
.flat_map(|dir| ["Claude", "Claude-3p"].map(|name| dir.join(name)));
roaming
.chain(local.iter().map(|dir| dir.join("AnthropicClaude")))
.collect()
}
#[cfg(any(test, feature = "test-support"))]
pub(crate) fn pitboard_app_on_windows(local: &[PathBuf]) -> Vec<PathBuf> {
local
.iter()
.flat_map(|dir| ["Pitboard App", "Pitboard App Debug"].map(|name| dir.join(name)))
.collect()
}
#[cfg(any(test, feature = "test-support"))]
pub(crate) fn real_login_places(
started_in: Option<&Context>,
) -> std::result::Result<Vec<PathBuf>, String> {
let home = user::accounts_own_home().ok_or("this system names no home for this account")?;
let pitboard = user::accounts_own_pitboard_home()
.ok_or("this system names no Pitboard folder for this account")?;
let own = Context::new(home).with_pitboard_home(pitboard);
let (roaming, local) = user::app_data_folders(started_in.is_some());
Ok(std::iter::once(&own)
.chain(started_in)
.flat_map(|ctx| {
let mut places = vec![
crate::provider::claude::paths::config_dir(ctx),
crate::provider::claude::paths::config_file(ctx),
crate::provider::codex::paths::home(ctx),
ctx.pitboard_home.clone(),
ctx.default_pitboard_home(),
];
if ctx.os != Os::Windows {
places.extend([
ctx.home.join("Library/Application Support/Claude"),
ctx.home.join("Library/Application Support/Claude-3p"),
PathBuf::from("/Applications/Claude.app"),
]);
}
places
})
.chain(claude_desktop_on_windows(&roaming, &local))
.chain(pitboard_app_on_windows(&local))
.filter(|place| place.is_absolute())
.collect())
}
#[cfg(all(windows, any(test, feature = "test-support")))]
pub(crate) use windows::lasting_environment;
#[cfg(all(not(windows), any(test, feature = "test-support")))]
pub(crate) fn lasting_environment(_name: &str) -> Vec<String> {
Vec::new()
}
#[cfg(any(test, feature = "test-support"))]
pub(crate) fn lies_in(path: &Path, place: &Path) -> bool {
let path = std::path::absolute(path).unwrap_or_else(|_| path.to_path_buf());
let (real, real_place) = (resolved(&path, LINKS), resolved(place, LINKS));
near(&path, place)
|| near(&real, &real_place)
|| meets(&real, &real_place)
|| meets(&real_place, &real)
}
#[cfg(any(test, feature = "test-support"))]
const LINKS: u8 = 32;
#[cfg(any(test, feature = "test-support"))]
fn near(a: &Path, b: &Path) -> bool {
begins(a, b) || begins(b, a)
}
#[cfg(any(test, feature = "test-support"))]
fn begins(path: &Path, with: &Path) -> bool {
let with = without_verbatim_prefix(with.to_path_buf());
without_verbatim_prefix(path.to_path_buf())
.ancestors()
.any(|above| same_path_in_any_case(above, &with))
}
#[cfg(any(test, feature = "test-support"))]
fn meets(a: &Path, b: &Path) -> bool {
let Some((deepest, below_b)) = b
.ancestors()
.find_map(|above| Some((fs::file_id(above)?, b.strip_prefix(above).ok()?)))
else {
return false;
};
a.ancestors().any(|above| {
fs::file_id(above) == Some(deepest)
&& a.strip_prefix(above)
.is_ok_and(|below_a| near(below_a, below_b))
})
}
#[cfg(any(test, feature = "test-support"))]
fn resolved(path: &Path, links: u8) -> PathBuf {
for found in path.ancestors() {
let Ok(rest) = path.strip_prefix(found) else {
continue;
};
if let Ok(real) = std::fs::canonicalize(found) {
return beyond(real, rest);
}
let link = std::fs::symlink_metadata(found).is_ok_and(|it| it.file_type().is_symlink());
if let (true, Some(left), Ok(to)) = (link, links.checked_sub(1), std::fs::read_link(found))
{
let to = match found.parent() {
Some(parent) => parent.join(to),
None => to,
};
return resolved(&beyond(to, rest), left);
}
}
path.to_path_buf()
}
#[cfg(any(test, feature = "test-support"))]
fn beyond(mut at: PathBuf, rest: &Path) -> PathBuf {
for part in rest.components() {
match part {
std::path::Component::ParentDir => {
at.pop();
}
std::path::Component::CurDir => {}
part => at.push(part),
}
}
at
}
fn same_name_in_any_case(a: &std::ffi::OsStr, b: &std::ffi::OsStr) -> bool {
let (a, b) = (a.to_string_lossy(), b.to_string_lossy());
a.chars()
.map(upper_one_for_one)
.eq(b.chars().map(upper_one_for_one))
}
pub(crate) fn upper_one_for_one(c: char) -> char {
if u32::from(c) > 0xFFFF {
return c;
}
let mut upper = c.to_uppercase();
match (upper.next(), upper.next()) {
(Some(one), None) => one,
_ => c,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
windows,
allow(
dead_code,
reason = "only a login shell is late, and Windows has none: its `Path` is read from \
the registry"
)
)]
pub(crate) enum LoginPath {
Said(String),
Unknown,
Late,
}
pub(crate) fn login_path(env: &Environment) -> LoginPath {
os::login_path(env)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Access {
pub(crate) shared: bool,
pub(crate) described: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Private {
Yes,
Shared,
NotOwn,
Link,
NotADirectory,
Missing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Kind {
Directory,
File,
Any,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Elevation {
Normal,
Elevated { why: &'static str },
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Floor {
Met,
Below { build: u32 },
Unknown,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct WindowsSettings {
pub(crate) wsl: Vec<String>,
pub(crate) proxy: Option<String>,
pub(crate) admin_approval: Option<bool>,
}
pub(crate) const WINDOWS_FLOOR: u32 = 26100;
#[cfg(any(windows, test))]
pub(crate) fn windows_floor(version: Option<(u32, u32, u32)>) -> Floor {
match version {
None => Floor::Unknown,
Some((major, minor, build)) if (major, minor, build) >= (10, 0, WINDOWS_FLOOR) => {
Floor::Met
}
Some((_, _, build)) => Floor::Below { build },
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Process {
pub(crate) pid: u32,
pub(crate) path: PathBuf,
pub(crate) package: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Listed {
pub pid: u32,
pub parent: u32,
pub path: PathBuf,
pub package: Option<String>,
pub placed: bool,
}
pub(crate) fn named(os: Os, all: Vec<Listed>, program: &str) -> Option<Vec<Process>> {
let files: Vec<String> = match os {
Os::MacOs | Os::Linux => vec![program.to_owned()],
Os::Windows => {
let mut files = vec![format!("{program}.exe")];
if program == "codex" {
files.push("codex-x86_64-pc-windows-msvc.exe".to_owned());
files.push("codex-aarch64-pc-windows-msvc.exe".to_owned());
}
files
}
};
let runs = |name: &std::ffi::OsStr| match os {
Os::MacOs | Os::Linux => files.iter().any(|file| name == file.as_str()),
Os::Windows => files
.iter()
.any(|file| same_name_in_any_case(name, std::ffi::OsStr::new(file))),
};
let found: Vec<Listed> = all
.into_iter()
.filter(|p| p.path.file_name().is_some_and(runs))
.collect();
if found.iter().any(|p| !p.placed) {
return None;
}
Some(
found
.into_iter()
.map(|p| Process {
pid: p.pid,
path: p.path,
package: p.package,
})
.collect(),
)
}
pub struct Jar {
pub version: i64,
pub compatible: i64,
pub cookies: Vec<Cookie>,
}
pub struct Cookie {
pub host: String,
pub name: String,
pub path: String,
pub stored: Vec<u8>,
pub expires_utc: i64,
}
impl std::fmt::Debug for Cookie {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Cookie")
.field("host", &self.host)
.field("name", &self.name)
.field("path", &self.path)
.field("stored", &format_args!("{} bytes", self.stored.len()))
.field("expires_utc", &self.expires_utc)
.finish()
}
}
#[derive(Debug)]
pub enum JarError {
Absent,
Unsettled(String),
Unknown(String),
Unreadable(String),
Unsupported,
}
#[cfg(any(test, feature = "test-support"))]
pub(crate) use os::write_jar;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Ran {
pub exit: Option<i32>,
pub stdout: String,
pub timed_out: bool,
}
pub(crate) trait Host: Send + Sync + std::fmt::Debug {
fn foreign_secrets(&self, ctx: &Context, account: &str) -> Option<Box<dyn RawStore>>;
fn file(&self, path: PathBuf) -> Box<dyn RawStore>;
fn vault(&self, ctx: &Context) -> Box<dyn RawStore>;
fn vault_is_shared(&self) -> bool;
fn listing(&self) -> std::result::Result<Vec<Listed>, String>;
fn processes(&self, program: &str) -> Option<Vec<Process>> {
named(OS, self.listing().ok()?, program)
}
fn cookies(
&self,
jar: &Path,
hosts: &[&str],
names: &[&str],
) -> std::result::Result<Jar, JarError>;
fn app_version(&self, bundle: &Path) -> Option<String>;
fn app_id(&self, bundle: &Path) -> Option<String>;
fn scheduler(&self) -> Option<&dyn Scheduler>;
fn elevation(&self, ctx: &Context) -> Elevation;
fn floor(&self) -> Floor;
fn administered_file(&self, path: &Path) -> Administered {
if administered::READ_BY_REAL_HOSTS {
administered::read_text(path)
} else {
Administered::Unset
}
}
fn managed_preference(&self, domain: &str, key: &str) -> Administered;
fn administered_folder(&self, dir: &Path) -> Vec<PathBuf> {
if administered::READ_BY_REAL_HOSTS {
administered::listed(dir)
} else {
Vec::new()
}
}
fn policy(&self, _hive: Hive, _key: &str, _value: &str) -> Administered {
Administered::Unset
}
fn program_data(&self) -> Option<PathBuf> {
None
}
fn credential_names(&self, _prefix: &str) -> std::result::Result<Vec<String>, String> {
Ok(Vec::new())
}
fn user_environment(&self, _name: &str) -> Administered {
Administered::Unset
}
fn machine_environment(&self, _name: &str) -> Administered {
Administered::Unset
}
fn credential_held(
&self,
_ctx: &Context,
_target: &str,
) -> std::result::Result<bool, crate::store::Error> {
Ok(false)
}
fn windows_settings(&self) -> WindowsSettings {
WindowsSettings::default()
}
fn kernel_is_wsl(&self) -> bool {
false
}
fn on_a_windows_drive(&self, _path: &Path) -> bool {
false
}
}
pub(crate) trait Scheduler: Send + Sync + std::fmt::Debug {
fn location(&self, ctx: &Context) -> crate::schedule::Location;
fn installed(&self, ctx: &Context) -> bool;
fn program(&self, ctx: &Context) -> Option<PathBuf>;
fn environment(&self, ctx: &Context) -> Vec<(String, String)>;
fn put(
&self,
ctx: &Context,
permit: Permit,
program: &Path,
environment: &[(String, String)],
) -> Result<()>;
fn remove(&self, ctx: &Context, permit: Permit) -> Result<bool>;
fn started_this_process(&self, said: Option<&str>) -> bool;
fn read_by_its_runs(&self, _ctx: &Context) -> Vec<(String, String)> {
Vec::new()
}
fn task(&self, _ctx: &Context) -> Option<task::Task> {
None
}
}
pub fn awake() -> std::time::Duration {
os::awake()
}
#[cfg(any(windows, test))]
pub(crate) fn from_interrupt_time(units: u64) -> std::time::Duration {
std::time::Duration::from_secs(units / 10_000_000)
+ std::time::Duration::from_nanos(units % 10_000_000 * 100)
}
pub(crate) fn current() -> Arc<dyn Host> {
os::host()
}
pub(crate) fn current_program() -> std::io::Result<PathBuf> {
os::current_program()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_crates_lints_keep_every_rule_of_the_workspaces_and_refuse_stds_creates() {
let read = |path: &str| {
std::fs::read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join(path)).expect(path)
};
let rules = |text: &str| -> Vec<String> {
text.lines()
.filter(|line| line.trim_start().starts_with("{ path"))
.map(str::to_owned)
.collect()
};
let own = rules(&read("clippy.toml"));
for rule in rules(&read("../../clippy.toml")) {
assert!(own.contains(&rule), "{rule}");
}
for create in [
"std::fs::write",
"std::fs::OpenOptions::create",
"std::fs::create_dir_all",
] {
let named = format!("{{ path = \"{create}\",");
assert!(own.iter().any(|rule| rule.contains(&named)), "{create}");
}
}
#[test]
fn a_store_of_secrets_is_offered_only_where_there_is_one() {
let ctx = Context::for_unit_test();
let offered = ctx.host().foreign_secrets(&ctx, "someone");
assert_eq!(
offered.map(|k| k.kind()),
match OS {
Os::MacOs => Some(crate::store::Backend::Keychain),
Os::Linux => None,
Os::Windows => Some(crate::store::Backend::CredentialManager),
}
);
assert_eq!(
ctx.host().file(PathBuf::from("/nowhere/at/all")).kind(),
crate::store::Backend::File
);
}
fn listed(pid: u32, path: &str) -> Listed {
Listed {
pid,
parent: 1,
path: PathBuf::from(path),
package: None,
placed: true,
}
}
#[test]
fn a_program_is_named_by_its_file_as_each_system_names_it() {
let all = || {
vec![
listed(
1,
"C:/Users/a/.codex/packages/standalone/current/bin/Codex.EXE",
),
listed(2, "C:/n/vendor/codex-resources/codex-command-runner.exe"),
listed(3, "C:/n/vendor/bin/codex-code-mode-host.exe"),
listed(4, "C:/n/vendor/codex-path/rg.exe"),
listed(5, "/usr/local/bin/codex"),
listed(6, "C:/n/codex"),
listed(
7,
"C:/Users/a/AppData/Local/Microsoft/WinGet/Packages/OpenAI.Codex_x/codex-x86_64-pc-windows-msvc.exe",
),
listed(
8,
"C:/Users/a/AppData/Local/Microsoft/WinGet/Packages/OpenAI.Codex_x/codex-aarch64-pc-windows-msvc.exe",
),
]
};
let pids = |os: Os| {
named(os, all(), "codex").map(|found| found.iter().map(|p| p.pid).collect::<Vec<_>>())
};
assert_eq!(pids(Os::Windows), Some(vec![1, 7, 8]));
assert_eq!(pids(Os::MacOs), Some(vec![5, 6]));
assert_eq!(pids(Os::Linux), Some(vec![5, 6]));
}
#[test]
fn a_process_that_cannot_be_placed_makes_the_answer_unknown() {
let unplaced = |name: &str| Listed {
placed: false,
..listed(7, name)
};
let placed = listed(1, "C:/x/codex.exe");
assert_eq!(
named(
Os::Windows,
vec![placed.clone(), unplaced("codex.exe")],
"codex"
),
None
);
assert_eq!(
named(Os::Windows, vec![placed, unplaced("svchost.exe")], "codex")
.map(|found| found.len()),
Some(1),
"unless it is not the program"
);
}
#[test]
fn paths_are_compared_exactly_on_macos_and_linux() {
for os in [Os::MacOs, Os::Linux] {
let same = |a: &str, b: &str| os.same_path(Path::new(a), Path::new(b));
assert!(same("ChatGPT.app", "ChatGPT.app"), "{os:?}");
assert!(
same("/Applications/ChatGPT.app/", "/Applications//ChatGPT.app"),
"{os:?}"
);
assert!(!same("chatgpt.app", "ChatGPT.app"), "{os:?}");
assert!(!same("ChatGPT.app", "ChatGPT"), "{os:?}");
}
}
#[test]
fn paths_are_compared_in_any_case_on_windows() {
let same = |a: &str, b: &str| Os::Windows.same_path(Path::new(a), Path::new(b));
assert!(same("Codex.exe", "codex.EXE"));
assert!(same("/Programs/OpenAI/Codex/", "/programs//openai/CODEX"));
assert!(same(r"C:\Users\Dana\Codex.exe", r"c:\users\dana\codex.exe"));
assert!(same("Éclair", "éCLAIR"));
assert!(!same("e\u{301}clair", "\u{e9}clair"), "not normalised");
assert!(!same("Straße", "STRASSE"), "ß is upper case as it is");
assert!(!same("Codex.exe", "Codex"));
assert!(!same("/a/b", "/a/b/c"));
}
#[test]
fn a_verbatim_path_is_spelled_as_libuv_and_codex_spell_it() {
let plain = |path: &str| without_verbatim_prefix(PathBuf::from(path));
assert_eq!(
plain(r"\\?\C:\Users\X\.codex"),
PathBuf::from(r"C:\Users\X\.codex")
);
assert_eq!(
plain(r"\\?\UNC\server\share\x"),
PathBuf::from(r"\\server\share\x")
);
for kept in [
r"C:\Users\X",
r"\\?\Volume{0}\x",
r"\\server\share",
"/Users/x",
] {
assert_eq!(plain(kept), PathBuf::from(kept), "{kept}");
}
}
#[test]
fn a_windows_path_has_one_key_however_it_is_spelled() {
let key = path_key_in_any_case;
for spelling in [
r"C:\Users\X\AppData\Local\Pitboard",
r"c:\users\x\appdata\local\pitboard\",
r"\\?\C:\Users\X\AppData\Local\Pitboard",
"C:/Users/X/AppData/Local/Pitboard",
r"C:\Users\X\.\AppData\Local\..\Local\Pitboard",
r"C:\Users\\X\AppData\Local\Pitboard",
] {
assert_eq!(
key(spelling),
r"C:\USERS\X\APPDATA\LOCAL\PITBOARD",
"{spelling}"
);
}
assert_eq!(key(r"\\server\share\Pitboard"), r"\\SERVER\SHARE\PITBOARD");
assert_eq!(
key(r"\\?\UNC\server\share\Pitboard"),
r"\\SERVER\SHARE\PITBOARD"
);
assert_eq!(key(r"C:\..\Pitboard"), r"C:\PITBOARD");
assert_eq!(key(r"C:\"), r"C:\");
assert_eq!(key(r"C:\Users\Straße"), r"C:\USERS\STRAßE");
assert_ne!(key("C:\\Users\\e\u{301}"), key("C:\\Users\\\u{e9}"));
assert_ne!(
key(r"C:\Users\XAVIER~1\AppData\Local\Pitboard"),
key(r"C:\Users\Xavier Dana\AppData\Local\Pitboard"),
"no short name is expanded"
);
}
#[test]
fn a_path_lies_in_a_place_in_any_case_and_past_a_folder_not_made_yet() {
let root = std::env::temp_dir().join(format!("pitboard-lies-in-{}", std::process::id()));
let place = root.join("Library/Application Support");
let lies = |path: &str| lies_in(&root.join(path), &place);
assert!(lies("Library/Application Support/x"));
assert!(lies("library/APPLICATION SUPPORT/x"), "in any case");
assert!(lies("LIBRARY"), "it holds it");
assert!(lies("never-made/../Library/Application Support/x"));
assert!(!lies("Library/Application Supportx"));
assert!(!lies("never-made/Library/Application Support"));
assert!(!lies("Library/../elsewhere/Application Support"));
assert!(!root.exists());
}
#[test]
fn claude_desktops_windows_folders_are_refused_in_any_spelling() {
let (roaming, local) = (
PathBuf::from("/p/AppData/Roaming"),
PathBuf::from("/p/AppData/Local"),
);
let places =
claude_desktop_on_windows(std::slice::from_ref(&roaming), std::slice::from_ref(&local));
let refused = |path: PathBuf| places.iter().any(|place| lies_in(&path, place));
for path in [
roaming.join("Claude"),
roaming.join("claude").join("Cookies"),
roaming.join("CLAUDE-3P"),
local.join("AnthropicClaude").join("app-1.0"),
roaming.clone(),
] {
assert!(refused(path.clone()), "{}", path.display());
}
for path in [
roaming.join("Claudette"),
local.join("Claude"),
local.join("Pitboard"),
] {
assert!(!refused(path.clone()), "{}", path.display());
}
}
#[test]
fn the_windows_apps_folders_are_refused_in_any_spelling() {
let local = PathBuf::from("/p/AppData/Local");
let places = pitboard_app_on_windows(std::slice::from_ref(&local));
let refused = |path: PathBuf| places.iter().any(|place| lies_in(&path, place));
for path in [
local.join("Pitboard App").join("WebView2"),
local.join("pitboard app debug").join("x"),
local.join("PITBOARD APP"),
local.clone(),
] {
assert!(refused(path.clone()), "{}", path.display());
}
for path in [
local.join("Pitboard Apps"),
local.join("Pitboard"),
local.join("Pitboard").join("App"),
local.join("Pitboard App Debugger"),
] {
assert!(!refused(path.clone()), "{}", path.display());
}
}
#[test]
#[cfg(windows)]
fn a_verbatim_path_lies_where_its_plain_spelling_does() {
let lies = |path: &str, place: &str| lies_in(Path::new(path), Path::new(place));
assert!(lies(
r"\\?\C:\Users\Dana\Library\x",
r"c:\users\dana\library"
));
assert!(lies(
r"C:\Users\Dana\Library\x",
r"\\?\c:\users\dana\library"
));
assert!(!lies(
r"\\?\D:\Users\Dana\Library\x",
r"C:\Users\Dana\Library"
));
}
#[test]
fn a_scheduler_is_offered_where_pitboard_writes_for_one() {
assert!(current().scheduler().is_some());
}
#[test]
fn the_host_reads_sudo_from_the_context_and_root_from_the_process() {
let under = |value: &str| {
let env: Environment = [("HOME", "/Users/x"), ("SUDO_UID", value)]
.into_iter()
.collect();
let ctx = Context::for_command_line(&env);
ctx.host().elevation(&ctx)
};
assert_eq!(
under("501"),
match OS {
Os::MacOs | Os::Linux => Elevation::Elevated { why: "with sudo" },
Os::Windows => user::elevation(false),
}
);
let ctx = Context::for_unit_test();
assert_eq!(
ctx.host().elevation(&ctx),
user::elevation(false),
"withheld from a unit test, so only root is read"
);
assert_eq!(under(""), user::elevation(false));
}
#[test]
fn time_awake_moves_with_this_process_while_it_is_awake() {
let (awake_at, instant_at) = (awake(), std::time::Instant::now());
std::thread::sleep(std::time::Duration::from_millis(50));
let (passed, measured) = (awake() - awake_at, instant_at.elapsed());
assert!(
passed > measured / 2 && passed < measured * 2,
"{passed:?} against {measured:?}"
);
}
#[test]
fn interrupt_time_is_in_hundreds_of_nanoseconds() {
use std::time::Duration;
assert_eq!(from_interrupt_time(10_000_000), Duration::from_secs(1));
assert_eq!(from_interrupt_time(1), Duration::from_nanos(100));
assert_eq!(
from_interrupt_time(36_000_000_015),
Duration::new(3600, 1_500)
);
}
#[test]
fn the_floor_is_windows_11_24h2_with_server_2025_counted() {
for met in [(10, 0, 26100), (10, 0, 26200), (10, 0, 27000), (11, 0, 100)] {
assert_eq!(windows_floor(Some(met)), Floor::Met, "{met:?}");
}
for (version, build) in [
((10, 0, 26099), 26099),
((10, 0, 22631), 22631),
((10, 0, 19045), 19045),
((6, 3, 9600), 9600),
] {
assert_eq!(
windows_floor(Some(version)),
Floor::Below { build },
"{version:?}"
);
}
assert_eq!(windows_floor(None), Floor::Unknown);
assert_eq!(WINDOWS_FLOOR, 26100);
}
#[test]
fn the_floor_is_met_on_the_systems_the_tests_run_on() {
assert_eq!(current().floor(), Floor::Met);
}
}