mirror of
https://github.com/kristoferssolo/tls-pq-bench.git
synced 2026-03-22 00:36:21 +00:00
test: add unit tests for config, errors, and records
This commit is contained in:
20
Cargo.lock
generated
20
Cargo.lock
generated
@@ -215,6 +215,12 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "claims"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bba18ee93d577a8428902687bcc2b6b45a56b1981a1f6d779731c86cc4c5db18"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.54"
|
version = "4.5.54"
|
||||||
@@ -275,6 +281,7 @@ name = "common"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo-husky",
|
"cargo-husky",
|
||||||
|
"claims",
|
||||||
"miette",
|
"miette",
|
||||||
"rcgen",
|
"rcgen",
|
||||||
"rustls",
|
"rustls",
|
||||||
@@ -769,6 +776,7 @@ dependencies = [
|
|||||||
name = "runner"
|
name = "runner"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"claims",
|
||||||
"clap",
|
"clap",
|
||||||
"common",
|
"common",
|
||||||
"miette",
|
"miette",
|
||||||
@@ -1164,9 +1172,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.9.11+spec-1.1.0"
|
version = "1.0.1+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
|
checksum = "bbe30f93627849fa362d4a602212d41bb237dc2bd0f8ba0b2ce785012e124220"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
@@ -1179,18 +1187,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.7.5+spec-1.1.0"
|
version = "1.0.0+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_parser"
|
name = "toml_parser"
|
||||||
version = "1.0.6+spec-1.1.0"
|
version = "1.0.8+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
|
checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|||||||
17
Cargo.toml
17
Cargo.toml
@@ -9,17 +9,13 @@ edition = "2024"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
common = { path = "common" }
|
||||||
|
|
||||||
aws-lc-rs = "1"
|
aws-lc-rs = "1"
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
cargo-husky = { version = "1", default-features = false, features = [
|
|
||||||
"user-hooks",
|
|
||||||
] }
|
|
||||||
claims = "0.8"
|
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
common = { path = "common" }
|
|
||||||
miette = { version = "7", features = ["fancy"] }
|
miette = { version = "7", features = ["fancy"] }
|
||||||
rcgen = "0.14"
|
rcgen = "0.14"
|
||||||
rstest = "0.26"
|
|
||||||
rustls = { version = "0.23", default-features = false, features = [
|
rustls = { version = "0.23", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"tls12",
|
"tls12",
|
||||||
@@ -29,15 +25,22 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
strum = { version = "0.27", features = ["derive"] }
|
strum = { version = "0.27", features = ["derive"] }
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
toml = "0.9"
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tokio-rustls = { version = "0.26", default-features = false, features = [
|
tokio-rustls = { version = "0.26", default-features = false, features = [
|
||||||
"tls12",
|
"tls12",
|
||||||
] }
|
] }
|
||||||
|
toml = "1.0"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||||
uuid = { version = "1", features = ["v4"] }
|
uuid = { version = "1", features = ["v4"] }
|
||||||
|
|
||||||
|
# dev
|
||||||
|
cargo-husky = { version = "1", default-features = false, features = [
|
||||||
|
"user-hooks",
|
||||||
|
] }
|
||||||
|
claims = "0.8"
|
||||||
|
rstest = "0.26"
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
nursery = "warn"
|
nursery = "warn"
|
||||||
pedantic = "warn"
|
pedantic = "warn"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ authors.workspace = true
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
claims.workspace = true
|
||||||
miette.workspace = true
|
miette.workspace = true
|
||||||
rcgen.workspace = true
|
rcgen.workspace = true
|
||||||
rustls.workspace = true
|
rustls.workspace = true
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Common types and utilities for the TLS benchmark harness.
|
//! Common types and utilities for the TLS benchmark harness
|
||||||
|
|
||||||
pub mod cert;
|
pub mod cert;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use strum::{Display, EnumString};
|
use strum::{Display, EnumString};
|
||||||
|
|
||||||
/// TLS key exchange mode.
|
/// TLS key exchange mode
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumString, Display)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumString, Display)]
|
||||||
#[strum(serialize_all = "lowercase")]
|
#[strum(serialize_all = "lowercase")]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
@@ -20,18 +20,18 @@ pub enum KeyExchangeMode {
|
|||||||
X25519Mlkem768,
|
X25519Mlkem768,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single benchmark measurement record, output as NDJSON.
|
/// A single benchmark measurement record, output as NDJSON
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct BenchRecord {
|
pub struct BenchRecord {
|
||||||
/// Iteration number (0-indexed, excludes warmup).
|
/// Iteration number (0-indexed, excludes warmup)
|
||||||
pub iteration: u64,
|
pub iteration: u64,
|
||||||
/// Key exchange mode used.
|
/// Key exchange mode used
|
||||||
pub mode: KeyExchangeMode,
|
pub mode: KeyExchangeMode,
|
||||||
/// Payload size in bytes.
|
/// Payload size in bytes
|
||||||
pub payload_bytes: u64,
|
pub payload_bytes: u64,
|
||||||
/// Handshake latency in nanoseconds.
|
/// Handshake latency in nanoseconds
|
||||||
pub handshake_ns: u64,
|
pub handshake_ns: u64,
|
||||||
/// Time-to-last-byte in nanoseconds (from connection start).
|
/// Time-to-last-byte in nanoseconds (from connection start)
|
||||||
pub ttlb_ns: u64,
|
pub ttlb_ns: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ impl BenchRecord {
|
|||||||
/// Serialize this record as a single NDJSON line (no trailing newline).
|
/// Serialize this record as a single NDJSON line (no trailing newline).
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns an error if serialization fails.
|
/// Returns an error if serialization fails
|
||||||
pub fn to_ndjson(&self) -> Result<String, serde_json::Error> {
|
pub fn to_ndjson(&self) -> Result<String, serde_json::Error> {
|
||||||
serde_json::to_string(self)
|
serde_json::to_string(self)
|
||||||
}
|
}
|
||||||
@@ -56,6 +56,9 @@ impl fmt::Display for BenchRecord {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use claims::{assert_err, assert_ok};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -68,21 +71,54 @@ mod tests {
|
|||||||
handshake_ns: 1_000_000,
|
handshake_ns: 1_000_000,
|
||||||
ttlb_ns: 2_000_000,
|
ttlb_ns: 2_000_000,
|
||||||
};
|
};
|
||||||
let json = record.to_ndjson().expect("serialization should succeed");
|
let json = assert_ok!(record.to_ndjson());
|
||||||
assert!(json.contains(r#""iteration":0"#));
|
assert!(json.contains(r#""iteration":0"#));
|
||||||
assert!(json.contains(r#""mode":"x25519""#));
|
assert!(json.contains(r#""mode":"x25519""#));
|
||||||
assert!(json.contains(r#""payload_bytes":1024"#));
|
assert!(json.contains(r#""payload_bytes":1024"#));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bench_record_roundtrip() {
|
||||||
|
let original = BenchRecord {
|
||||||
|
iteration: 42,
|
||||||
|
mode: KeyExchangeMode::X25519Mlkem768,
|
||||||
|
payload_bytes: 4096,
|
||||||
|
handshake_ns: 5_000_000,
|
||||||
|
ttlb_ns: 10_000_000,
|
||||||
|
};
|
||||||
|
let json = assert_ok!(original.to_ndjson());
|
||||||
|
let deserialized = assert_ok!(serde_json::from_str::<BenchRecord>(&json));
|
||||||
|
|
||||||
|
assert_eq!(original.iteration, deserialized.iteration);
|
||||||
|
assert_eq!(original.mode, deserialized.mode);
|
||||||
|
assert_eq!(original.payload_bytes, deserialized.payload_bytes);
|
||||||
|
assert_eq!(original.handshake_ns, deserialized.handshake_ns);
|
||||||
|
assert_eq!(original.ttlb_ns, deserialized.ttlb_ns);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn key_exchange_mode_from_str() {
|
fn key_exchange_mode_from_str() {
|
||||||
assert_eq!(
|
let mode = assert_ok!(KeyExchangeMode::from_str("x25519"));
|
||||||
KeyExchangeMode::from_str("x25519").expect("should parse"),
|
assert_eq!(mode, KeyExchangeMode::X25519);
|
||||||
KeyExchangeMode::X25519
|
|
||||||
);
|
let mode = assert_ok!(KeyExchangeMode::from_str("x25519mlkem768"));
|
||||||
assert_eq!(
|
assert_eq!(mode, KeyExchangeMode::X25519Mlkem768);
|
||||||
KeyExchangeMode::from_str("x25519mlkem768").expect("should parse"),
|
}
|
||||||
KeyExchangeMode::X25519Mlkem768
|
|
||||||
);
|
#[test]
|
||||||
|
fn key_exchange_mode_parse_error() {
|
||||||
|
assert_err!(KeyExchangeMode::from_str("invalid"));
|
||||||
|
assert_err!(KeyExchangeMode::from_str("x25519invalid"));
|
||||||
|
assert_err!(KeyExchangeMode::from_str(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn key_exchange_mode_serde() {
|
||||||
|
let json = r#"{"mode":"x25519mlkem768"}"#;
|
||||||
|
let value = assert_ok!(serde_json::from_str::<Value>(json));
|
||||||
|
let mode = assert_ok!(serde_json::from_value::<KeyExchangeMode>(
|
||||||
|
value["mode"].clone()
|
||||||
|
));
|
||||||
|
assert_eq!(mode, KeyExchangeMode::X25519Mlkem768);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ toml.workspace = true
|
|||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
claims.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -83,3 +83,156 @@ impl Config {
|
|||||||
.unwrap_or(KeyExchangeMode::X25519)
|
.unwrap_or(KeyExchangeMode::X25519)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use claims::assert_ok;
|
||||||
|
|
||||||
|
const VALID_CONFIG: &str = r#"
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "x25519"
|
||||||
|
payload = 1024
|
||||||
|
iters = 100
|
||||||
|
warmup = 10
|
||||||
|
concurrency = 1
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "x25519mlkem768"
|
||||||
|
payload = 4096
|
||||||
|
iters = 50
|
||||||
|
warmup = 5
|
||||||
|
concurrency = 4
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
fn get_config_from_str(toml: &str) -> Config {
|
||||||
|
assert_ok!(toml::from_str::<Config>(toml))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_single_benchmark() {
|
||||||
|
let toml = r#"
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "x25519"
|
||||||
|
payload = 1024
|
||||||
|
iters = 100
|
||||||
|
warmup = 10
|
||||||
|
concurrency = 1
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
"#;
|
||||||
|
let config = get_config_from_str(toml);
|
||||||
|
assert_eq!(config.benchmarks.len(), 1);
|
||||||
|
assert_eq!(config.benchmarks[0].mode, "x25519");
|
||||||
|
assert_eq!(config.benchmarks[0].payload, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_multiple_benchmarks() {
|
||||||
|
let config = get_config_from_str(VALID_CONFIG);
|
||||||
|
assert_eq!(config.benchmarks.len(), 2);
|
||||||
|
assert_eq!(config.benchmarks[0].mode, "x25519");
|
||||||
|
assert_eq!(config.benchmarks[1].mode, "x25519mlkem768");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_mode() {
|
||||||
|
let toml = r#"
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "invalid_mode"
|
||||||
|
payload = 1024
|
||||||
|
iters = 100
|
||||||
|
warmup = 10
|
||||||
|
concurrency = 1
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
"#;
|
||||||
|
let config = get_config_from_str(toml);
|
||||||
|
assert!(config.server_mode() == KeyExchangeMode::X25519); // fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn payload_zero_validation() {
|
||||||
|
let toml = r#"
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "x25519"
|
||||||
|
payload = 0
|
||||||
|
iters = 100
|
||||||
|
warmup = 10
|
||||||
|
concurrency = 1
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
"#;
|
||||||
|
let config = get_config_from_str(toml);
|
||||||
|
let result = validate_config(&config, toml, std::path::Path::new("test.toml"));
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iters_zero_validation() {
|
||||||
|
let toml = r#"
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "x25519"
|
||||||
|
payload = 1024
|
||||||
|
iters = 0
|
||||||
|
warmup = 10
|
||||||
|
concurrency = 1
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
"#;
|
||||||
|
let config = get_config_from_str(toml);
|
||||||
|
let result = validate_config(&config, toml, std::path::Path::new("test.toml"));
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn concurrency_zero_validation() {
|
||||||
|
let toml = r#"
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "x25519"
|
||||||
|
payload = 1024
|
||||||
|
iters = 100
|
||||||
|
warmup = 10
|
||||||
|
concurrency = 0
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
"#;
|
||||||
|
let config = get_config_from_str(toml);
|
||||||
|
let result = validate_config(&config, toml, std::path::Path::new("test.toml"));
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_benchmarks() {
|
||||||
|
let toml = "benchmarks = []";
|
||||||
|
let config = get_config_from_str(toml);
|
||||||
|
assert!(config.benchmarks.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn server_mode_fallback() {
|
||||||
|
let toml = r#"
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "x25519"
|
||||||
|
payload = 1024
|
||||||
|
iters = 100
|
||||||
|
warmup = 10
|
||||||
|
concurrency = 1
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
"#;
|
||||||
|
let config = get_config_from_str(toml);
|
||||||
|
assert_eq!(config.server_mode(), KeyExchangeMode::X25519);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn server_mode_mlkem() {
|
||||||
|
let toml = r#"
|
||||||
|
[[benchmarks]]
|
||||||
|
mode = "x25519mlkem768"
|
||||||
|
payload = 1024
|
||||||
|
iters = 100
|
||||||
|
warmup = 10
|
||||||
|
concurrency = 1
|
||||||
|
server = "127.0.0.1:4433"
|
||||||
|
"#;
|
||||||
|
let config = get_config_from_str(toml);
|
||||||
|
assert_eq!(config.server_mode(), KeyExchangeMode::X25519Mlkem768);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user