Files
core/test/rust/compiler/net.rs
T
2026-11-03 03:32:07 +00:00

149 lines
4.8 KiB
Rust

use dever::{
compiler::{build, check, model::Program},
syntax::{diagnostic::Diagnostic, source::Source},
};
fn linked(text: &str) -> Result<Program, Diagnostic> {
let inputs = [
("main", text),
("dever.io", include_str!("../../../lib/dever/io.dever")),
("dever.time", include_str!("../../../lib/dever/time.dever")),
("dever.task", include_str!("../../../lib/dever/task.dever")),
(
"dever.net",
include_str!("../../../lib/dever/net/socket.dever"),
),
(
"dever.net",
include_str!("../../../lib/dever/net/listener.dever"),
),
];
let modules = inputs
.iter()
.enumerate()
.map(|(index, (_, text))| {
dever::syntax::parse(&Source::new(
index,
if index == 0 {
"main.dever".into()
} else {
format!("source{index}.dever")
},
(*text).into(),
))
})
.collect::<Result<Vec<_>, _>>()?;
check::check(
modules,
&inputs
.iter()
.map(|(package, _)| (*package).into())
.collect::<Vec<_>>(),
)
}
#[test]
fn network_effects_and_runtime_features_match_the_actual_entry() {
for (source, suspends, schedules, net) in [
("main() (answer: Int) { answer = 42 }", false, false, false),
(
"main() (socket: dever.system.Socket?) { socket = null }",
false,
false,
true,
),
(
"main(socket: dever.system.Socket) () { dever.net.close(socket) }",
false,
false,
true,
),
(
"main(listener: dever.system.Listener) (port: Int) { port = dever.net.port(listener) }",
false,
false,
true,
),
(
"main() (socket: dever.system.Socket) { socket = dever.net.connectTimeout(\"127.0.0.1\", 80, 1000) }",
true,
false,
true,
),
(
"main() (socket: dever.system.Socket) { socket = dever.net.connect(\"localhost\", 80) }",
true,
true,
true,
),
(
"main() (listener: dever.system.Listener) { listener = dever.net.listen(\"127.0.0.1\", 0) }",
true,
true,
true,
),
] {
let program = linked(source).unwrap();
let main = program
.functions
.iter()
.position(|function| function.signature.name == "main")
.unwrap();
let effects = program.functions[main].signature.effects;
assert_eq!(effects.suspend, suspends, "{source}");
assert_eq!(effects.schedule, schedules, "{source}");
assert_eq!(
build::runtime_features(&program, &program.reachable([main])).contains(&"net"),
net,
"{source}"
);
}
}
#[test]
fn network_resources_obey_visibility_equality_and_effect_boundaries() {
for (source, code) in [
("main() () { value = dever.system.tcpPort(null) }", "C006"),
(
"main(socket: dever.system.Socket) (answer: Bool) { answer = socket == socket }",
"C005",
),
(
"main(values: List<dever.system.Listener?>) (answer: Bool) { answer = values == values }",
"C005",
),
(
"main(listener: dever.system.Listener) (port: Int) pure { port = dever.net.port(listener) }",
"C013",
),
("main(value: dever.net.SystemReadResult) () {}", "C006"),
(
"main(socket: dever.system.Socket) () { value = dever.net.read(socket, true) }",
"C005",
),
(
"main(socket: dever.system.Socket) () { value = dever.net.read(socket, 1)\n dever.io.println(\"blocking\") }",
"C013",
),
] {
assert_eq!(linked(source).unwrap_err().code, code, "{source}");
}
}
#[test]
fn lazy_network_streams_do_not_claim_unread_errors_but_captured_failures_remain_obligations() {
for source in [
"main(socket: dever.system.Socket) () { close(dever.net.chunks(socket, 1)) }",
"main(listener: dever.system.Listener) () { close(dever.net.connections(listener)) }",
] {
assert!(linked(source).is_ok(), "{source}");
}
for source in [
"main(socket: dever.system.Socket) () { lost = result(dever.net.read(socket, 1)) }",
"main(socket: dever.system.Socket) () { lost = result(dever.net.chunks(socket, 1)) }",
"main(socket: dever.system.Socket) () { lost = result(dever.net.close(socket)) }",
] {
assert_eq!(linked(source).unwrap_err().code, "C012", "{source}");
}
}