Files
core/test/rust/native/io/mod.rs
T
2026-11-03 03:32:07 +00:00

130 lines
4.3 KiB
Rust

use super::{Directory, fixture};
use dever::compiler::{build, project::Project};
use dever_runtime::io::File;
mod stream;
#[test]
fn file_aliases_share_cursor_and_close_without_truncating_existing_data() {
let directory = Directory::new();
let path = directory.0.join("file");
let name = path.to_str().unwrap();
let file = File::create(name).unwrap();
file.write(b"abcde").unwrap();
assert!(File::create(name).is_err());
assert_eq!(std::fs::read(&path).unwrap(), b"abcde");
file.close().unwrap();
let file = File::open(name).unwrap();
let alias = file.clone();
assert_eq!(file.read(2).unwrap().unwrap(), b"ab");
assert_eq!(alias.read(2).unwrap().unwrap(), b"cd");
for limit in [0, -1, i64::MAX] {
assert!(alias.read(limit).is_err());
}
assert_eq!(file.read(20).unwrap().unwrap(), b"e");
assert_eq!(file.read(2).unwrap(), None);
alias.close().unwrap();
assert_eq!(file.read(2).unwrap_err(), "file is closed");
assert_eq!(file.write(b"").unwrap_err(), "file is closed");
assert_eq!(file.close().unwrap_err(), "file is closed");
}
#[cfg(target_os = "linux")]
#[test]
fn last_file_alias_releases_descriptor_on_success_and_error() {
let directory = Directory::new();
let path = directory.0.join("drop");
std::fs::write(&path, b"test").unwrap();
let open_count = || {
std::fs::read_dir("/proc/self/fd")
.unwrap()
.filter_map(Result::ok)
.filter(|entry| std::fs::read_link(entry.path()).ok().as_ref() == Some(&path))
.count()
};
for _ in 0..32 {
let alias = {
let file = File::open(path.to_str().unwrap()).unwrap();
let alias = file.clone();
assert_eq!(open_count(), 1);
alias
};
assert_eq!(open_count(), 1);
drop(alias);
assert_eq!(open_count(), 0);
let failure = || -> Result<(), dever_runtime::io::Error> {
let file = File::open(path.to_str().unwrap())?;
let alias = file.clone();
alias.read(0)?;
Ok(())
};
assert!(failure().is_err());
assert_eq!(open_count(), 0);
}
}
#[test]
fn native_file_values_preserve_aliases_and_nominal_failures() {
let directory = Directory::new();
std::fs::write(directory.0.join("input"), b"abcde").unwrap();
let project = Project::load(&fixture().with_file_name("io.dever")).unwrap();
let program = project.check().unwrap();
for (entry, expected) in [
("verify", None),
("missing", Some("OpenResult.Failed")),
("invalid", Some("ReadResult.Failed")),
] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let output = std::process::Command::new(binary)
.current_dir(&directory.0)
.output()
.unwrap();
if let Some(message) = expected {
assert!(!output.status.success());
assert!(output.stdout.is_empty());
let error = String::from_utf8(output.stderr).unwrap();
assert!(
error.contains(message) && error.contains("lib/dever/io.dever:"),
"{error}"
);
} else {
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
assert_eq!(output.stdout, b"hello\ntrue\n");
assert_eq!(
std::fs::read(directory.0.join("created")).unwrap(),
b"hello"
);
}
}
#[cfg(target_os = "linux")]
{
let binary = build::build(
&program,
&project.sources,
"print",
&directory.0.join("print"),
)
.unwrap();
let output = std::process::Command::new(binary)
.stdout(
std::fs::OpenOptions::new()
.write(true)
.open("/dev/full")
.unwrap(),
)
.output()
.unwrap();
assert!(!output.status.success());
let error = String::from_utf8(output.stderr).unwrap();
assert!(
error.contains("standard output write failed") && error.contains("lib/dever/io.dever:"),
"{error}"
);
}
}