Files
AskaEth 5f8382565e
CI / fmt (rustfmt --check) (push) Waiting to run
CI / clippy (ubuntu-latest) (push) Waiting to run
CI / clippy (windows-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run
CI / build release (ubuntu-latest) (push) Waiting to run
CI / build release (windows-latest) (push) Waiting to run
Guards / core 零 UI 守门 (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Unlucky Launcher v0.1.0
单提交快照:main 只保留已发布版本的代码树,开发史在 dev。
2026-11-01 20:02:49 +08:00

50 lines
1.6 KiB
Rust

//! 原子写:覆盖语义、父目录创建、不留临时文件、可选读。
use unlucky_core::platform::atomic::{read_optional, tmp_path, write_atomic};
use crate::common::{dir_entries, TempDir};
#[test]
fn writes_and_replaces_in_place() {
let temp = TempDir::new("atomic");
let path = temp.child("config/launcher.json");
write_atomic(&path, b"{\"a\":1}").expect("首次写");
assert_eq!(temp.read("config/launcher.json"), "{\"a\":1}");
write_atomic(&path, b"{\"a\":2}").expect("覆盖写");
assert_eq!(temp.read("config/launcher.json"), "{\"a\":2}");
// 目录里只该有目标文件,临时文件必须被改名掉。
assert_eq!(dir_entries(&temp.child("config")), vec!["launcher.json"]);
}
#[test]
fn read_optional_distinguishes_missing_from_failure() {
let temp = TempDir::new("atomic-read");
let path = temp.child("nope.json");
assert!(read_optional(&path).expect("读缺失文件").is_none());
temp.write("here.json", "内容");
let bytes = read_optional(&temp.child("here.json"))
.expect("读文件")
.expect("有内容");
assert_eq!(bytes, "内容".as_bytes());
}
#[test]
fn tmp_name_stays_in_same_directory() {
let temp = TempDir::new("atomic-tmp");
let path = temp.child("config/profiles.json");
let tmp = tmp_path(&path);
assert_eq!(
tmp.parent(),
path.parent(),
"临时文件必须同目录(rename 才原子)"
);
assert_ne!(tmp, path);
let first = tmp_path(&path);
let second = tmp_path(&path);
assert_ne!(first, second, "并发写要能拿到不同的临时名");
}