#![cfg(all(
feature = "net",
feature = "dataforts",
feature = "netdb",
feature = "redex-disk"
))]
use std::fs;
use std::path::PathBuf;
use net::ffi::transport::{
NET_ERR_DIR_INVALID_MANIFEST, NET_ERR_DIR_IO, NET_ERR_DIR_PATH_INVALID,
NET_ERR_TRANSFER_ALL_PEERS_FAILED, NET_ERR_TRANSFER_BACKEND, NET_ERR_TRANSFER_CANCELLED,
NET_ERR_TRANSFER_ENGINE_NOT_INSTALLED, NET_ERR_TRANSFER_HASH_MISMATCH,
NET_ERR_TRANSFER_INVALID_ARGUMENT, NET_ERR_TRANSFER_NOT_FOUND, NET_ERR_TRANSFER_NULL_POINTER,
NET_ERR_TRANSFER_PANIC, NET_ERR_TRANSFER_SHUTTING_DOWN, NET_TRANSPORT_OK,
};
fn header_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("include")
.join("net_transport.h")
}
fn is_transport_code(name: &str) -> bool {
name == "NET_TRANSPORT_OK"
|| name.starts_with("NET_ERR_TRANSFER_")
|| name.starts_with("NET_ERR_DIR_")
}
fn parse_header_transport_defines() -> Vec<(String, i32)> {
let header = fs::read_to_string(header_path())
.unwrap_or_else(|e| panic!("read {}: {e}", header_path().display()));
let mut out = Vec::new();
for line in header.lines() {
let trimmed = line.trim_start();
let Some(rest) = trimmed.strip_prefix("#define") else {
continue;
};
let rest = rest.trim();
let mut parts = rest.split_whitespace();
let Some(name) = parts.next() else { continue };
if !is_transport_code(name) {
continue;
}
let Some(value_str) = parts.next() else {
panic!("malformed #define for {name}: missing value");
};
let value_str = value_str
.split("/*")
.next()
.unwrap_or(value_str)
.split("//")
.next()
.unwrap_or(value_str)
.trim();
let value: i32 = value_str
.parse()
.unwrap_or_else(|e| panic!("non-integer value for {name}: {value_str:?} ({e})"));
out.push((name.to_string(), value));
}
out
}
#[test]
fn every_transport_error_define_matches_rust_constant() {
let pairs = parse_header_transport_defines();
assert!(
!pairs.is_empty(),
"header at {} produced no transport defines",
header_path().display(),
);
let expected: &[(&str, i32)] = &[
("NET_TRANSPORT_OK", NET_TRANSPORT_OK),
("NET_ERR_TRANSFER_NOT_FOUND", NET_ERR_TRANSFER_NOT_FOUND),
(
"NET_ERR_TRANSFER_HASH_MISMATCH",
NET_ERR_TRANSFER_HASH_MISMATCH,
),
(
"NET_ERR_TRANSFER_ALL_PEERS_FAILED",
NET_ERR_TRANSFER_ALL_PEERS_FAILED,
),
("NET_ERR_TRANSFER_CANCELLED", NET_ERR_TRANSFER_CANCELLED),
(
"NET_ERR_TRANSFER_NULL_POINTER",
NET_ERR_TRANSFER_NULL_POINTER,
),
(
"NET_ERR_TRANSFER_SHUTTING_DOWN",
NET_ERR_TRANSFER_SHUTTING_DOWN,
),
(
"NET_ERR_TRANSFER_ENGINE_NOT_INSTALLED",
NET_ERR_TRANSFER_ENGINE_NOT_INSTALLED,
),
("NET_ERR_TRANSFER_BACKEND", NET_ERR_TRANSFER_BACKEND),
("NET_ERR_TRANSFER_PANIC", NET_ERR_TRANSFER_PANIC),
(
"NET_ERR_TRANSFER_INVALID_ARGUMENT",
NET_ERR_TRANSFER_INVALID_ARGUMENT,
),
("NET_ERR_DIR_INVALID_MANIFEST", NET_ERR_DIR_INVALID_MANIFEST),
("NET_ERR_DIR_PATH_INVALID", NET_ERR_DIR_PATH_INVALID),
("NET_ERR_DIR_IO", NET_ERR_DIR_IO),
];
for (name, header_value) in &pairs {
let expected_value = expected
.iter()
.find(|(n, _)| *n == name)
.map(|(_, v)| *v)
.unwrap_or_else(|| {
panic!(
"header has unknown #define `{name}` = {header_value} (Rust constant missing or test allow-list not updated)"
)
});
assert_eq!(
*header_value, expected_value,
"header `#define {name} {header_value}` disagrees with Rust constant ({expected_value})",
);
}
let header_names: Vec<&str> = pairs.iter().map(|(n, _)| n.as_str()).collect();
for (name, _) in expected {
assert!(
header_names.contains(name),
"Rust constant `{name}` is not declared in `include/net_transport.h`. Add the `#define` or remove the constant.",
);
}
let mut codes: Vec<i32> = expected.iter().map(|(_, c)| *c).collect();
codes.sort_unstable();
codes.dedup();
assert_eq!(codes.len(), expected.len(), "duplicate transport code");
}