1#![allow(clippy::type_complexity)]
6#![forbid(unsafe_code)]
28
29use serde::{Deserialize, Serialize};
30use std::fmt;
31use std::future::Future;
32use std::io::{self, SeekFrom};
33use std::net::SocketAddr;
34use std::path::Path;
35use std::pin::Pin;
36use std::time::Duration;
37
38pub mod atp_security;
39pub mod bench;
40pub mod h1_expect_continue_conformance;
41pub mod h1_request_building_conformance;
42pub mod h1_response_building_conformance;
43pub mod h2_connect_method_conformance;
44pub mod h2_continuation_conformance;
45pub mod h2_data_end_stream_conformance;
46pub mod h2_enable_push_conformance;
47pub mod h2_goaway_conformance;
48pub mod h2_ping_conformance;
49pub mod h2_priority_conformance;
50pub mod h2_settings_conformance;
51pub mod hpack_conformance;
52pub mod hpack_encoder_conformance;
53pub mod kafka_record_batch_v2;
54pub mod lean_coverage_matrix;
55pub mod lean_frontier;
56pub mod logging;
57pub mod mysql_conformance;
58pub mod otlp_wire_format;
59pub mod raptorq_rfc6330;
60#[path = "../raptorq_rfc6330/reporting/src/mod.rs"]
61pub mod raptorq_rfc6330_reporting;
62pub mod reference_registry;
63pub mod report;
64pub mod rfc6330_fixtures;
65pub mod rfc6330_tests;
66pub mod runner;
67pub mod tests;
68pub mod traceability;
69
70pub use atp_security::{
71 AtpSecurityContract, atp_security_conformance_tests, atp_security_coverage_matrix,
72};
73pub use bench::{
74 BenchAllocSnapshot, BenchAllocStats, BenchCategory, BenchComparisonResult,
75 BenchComparisonSummary, BenchConfig, BenchOutput, BenchRunResult, BenchRunSummary, BenchRunner,
76 BenchThresholds, Benchmark, Comparison, ComparisonConfidence, RegressionCheck,
77 RegressionConfig, RegressionMetric, Stats, StatsError, default_benchmarks,
78 run_benchmark_comparison,
79};
80pub use h1_expect_continue_conformance::{
81 ExpectContinueComplianceReport, ExpectContinueComplianceSummary, ExpectContinueConformanceCase,
82 ExpectContinueConformanceTester, ExpectContinueTestResult, ExpectContinueTestVerdict,
83};
84pub use h1_request_building_conformance::{
85 RequestBuilderOp, RequestBuildingComplianceReport, RequestBuildingComplianceSummary,
86 RequestBuildingConformanceCase, RequestBuildingConformanceTester, RequestBuildingTestResult,
87 RequestBuildingTestVerdict,
88};
89pub use h1_response_building_conformance::{
90 ResponseBuilderOp, ResponseBuildingComplianceReport, ResponseBuildingComplianceSummary,
91 ResponseBuildingConformanceCase, ResponseBuildingConformanceTester, ResponseBuildingTestResult,
92 ResponseBuildingTestVerdict,
93};
94pub use h2_connect_method_conformance::{
95 ConnectMethodComplianceReport, ConnectMethodComplianceSummary, ConnectMethodConformanceCase,
96 ConnectMethodConformanceTester, ConnectMethodTestResult, ConnectMethodTestVerdict,
97 ConnectRequest,
98};
99pub use h2_continuation_conformance::{
100 ContinuationComplianceReport, ContinuationComplianceSummary, ContinuationConformanceCase,
101 ContinuationConformanceTester, ContinuationTestResult, ContinuationTestVerdict,
102 ExpectedOutcome as ContinuationExpectedOutcome, FrameSequence,
103 RequirementLevel as ContinuationRequirementLevel, TestFrame, TestFrameResult,
104};
105pub use h2_data_end_stream_conformance::{
106 DataEndStreamComplianceReport, DataEndStreamComplianceSummary, DataEndStreamConformanceCase,
107 DataEndStreamConformanceResult, DataEndStreamConformanceTester, DataEndStreamConnectionState,
108 DataEndStreamTestVerdict, RequirementLevel as DataEndStreamRequirementLevel,
109 SerializableDataFrame, StreamEndStreamState,
110};
111pub use h2_enable_push_conformance::{
112 EnablePushComplianceReport, EnablePushComplianceSummary, EnablePushConformanceCase,
113 EnablePushConformanceTester, EnablePushTestResult, EnablePushTestVerdict, TestRequest,
114};
115pub use h2_goaway_conformance::{
116 GoAwayComplianceReport, GoAwayComplianceSummary, GoAwayConformanceCase,
117 GoAwayConformanceResult, GoAwayConformanceTester, GoAwayConnectionState, GoAwayTestVerdict,
118 RequirementLevel as GoAwayRequirementLevel, SerializableGoAwayFrame,
119};
120pub use h2_ping_conformance::{
121 PingComplianceReport, PingComplianceSummary, PingConformanceCase, PingConformanceResult,
122 PingConformanceTester, PingConnectionState, PingTestVerdict, PingTiming,
123 RequirementLevel as PingRequirementLevel, SerializablePingFrame,
124};
125pub use h2_priority_conformance::{
126 PriorityComplianceReport, PriorityComplianceSummary, PriorityConformanceCase,
127 PriorityConformanceResult, PriorityConformanceTester, PriorityTestVerdict,
128 RequirementLevel as PriorityRequirementLevel, StreamPriorityState,
129};
130pub use h2_settings_conformance::{
131 ComplianceReport as SettingsComplianceReport, ComplianceSummary as SettingsComplianceSummary,
132 ConformanceResult as SettingsConformanceResult, ExpectedOutcome, Setting,
133 SettingsConformanceCase, SettingsConformanceTester, SettingsFrame, SettingsSnapshot,
134 TestVerdict as SettingsTestVerdict,
135};
136pub use hpack_conformance::{
137 ComplianceReport as HpackComplianceReport, ComplianceSummary as HpackComplianceSummary,
138 ConformanceResult as HpackConformanceResult, ExpectedOutcome as HpackExpectedOutcome,
139 HpackConformanceCase, HpackConformanceTester, RequirementLevel as HpackRequirementLevel,
140 TestVerdict as HpackTestVerdict,
141};
142pub use hpack_encoder_conformance::{
143 EncoderTestVerdict, HpackEncoderComplianceReport, HpackEncoderComplianceSummary,
144 HpackEncoderConformanceCase, HpackEncoderConformanceTester, HpackEncoderTestResult,
145};
146pub use kafka_record_batch_v2::{
147 ConformanceTestResult, Header, KafkaConformanceHarness, RecordAttribute, RecordBatchV2,
148 RecordV2, TimestampType,
149};
150pub use lean_coverage_matrix::{
151 BlockerCode, CoverageBlocker, CoverageEvidence, CoverageRow, CoverageRowType, CoverageStatus,
152 LEAN_COVERAGE_SCHEMA_VERSION, LeanCoverageMatrix,
153};
154pub use lean_frontier::{
155 LEAN_FRONTIER_SCHEMA_VERSION, LeanDiagnosticSeverity, LeanFrontierBucket,
156 LeanFrontierDiagnostic, LeanFrontierReport, extract_frontier_report,
157};
158pub use logging::{
159 ConformanceTestLogger, LogCollector, LogConfig, LogEntry, LogLevel, TestEvent, TestEventKind,
160};
161pub use reference_registry::{
162 ReferenceRegistryError, ReferenceRegistryGuardFailure, ReferenceRegistryGuardReport,
163 ReferenceSurfaceRegistry, ReferenceSurfaceRow, ReferenceVerdictAdmission,
164 RuntimeConformanceVerdict, SOURCE_CONFORMANCE_REGISTRY_CONTRACT,
165};
166pub use report::{render_console_summary, write_json_report};
167pub use runner::{
168 ComparisonResult, ComparisonStatus, ComparisonSummary, RunConfig, RunSummary, SingleRunResult,
169 SuiteResult, SuiteTestResult, TestRunner, compare_results, run_comparison,
170 run_conformance_suite,
171};
172pub use traceability::{
173 CiReport, CoverageStats, ScanWarning, SpecRequirement, TraceabilityEntry, TraceabilityMatrix,
174 TraceabilityMatrixBuilder, TraceabilityScan, TraceabilityScanError, requirements_from_entries,
175 scan_conformance_attributes,
176};
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
184pub enum RequirementLevel {
185 Must,
187 Should,
189 May,
191}
192
193impl fmt::Display for RequirementLevel {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 match self {
196 RequirementLevel::Must => write!(f, "MUST"),
197 RequirementLevel::Should => write!(f, "SHOULD"),
198 RequirementLevel::May => write!(f, "MAY"),
199 }
200 }
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct TestResult {
210 pub passed: bool,
212 pub message: Option<String>,
214 pub checkpoints: Vec<Checkpoint>,
216 pub duration_ms: Option<u64>,
218}
219
220impl TestResult {
221 pub fn passed() -> Self {
223 Self {
224 passed: true,
225 message: None,
226 checkpoints: Vec::new(),
227 duration_ms: None,
228 }
229 }
230
231 pub fn failed(message: impl Into<String>) -> Self {
233 Self {
234 passed: false,
235 message: Some(message.into()),
236 checkpoints: Vec::new(),
237 duration_ms: None,
238 }
239 }
240
241 pub fn with_checkpoint(mut self, checkpoint: Checkpoint) -> Self {
243 self.checkpoints.push(checkpoint);
244 self
245 }
246
247 pub fn with_duration(mut self, duration_ms: u64) -> Self {
249 self.duration_ms = Some(duration_ms);
250 self
251 }
252}
253
254#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
256pub struct Checkpoint {
257 pub name: String,
259 pub data: serde_json::Value,
261}
262
263impl Checkpoint {
264 pub fn new(name: impl Into<String>, data: serde_json::Value) -> Self {
266 Self {
267 name: name.into(),
268 data,
269 }
270 }
271}
272
273pub fn checkpoint(name: &str, data: serde_json::Value) {
275 let _ = Checkpoint::new(name, data.clone());
276 crate::logging::record_checkpoint(name, data);
277}
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
285pub enum TestCategory {
286 Spawn,
288 Channels,
290 IO,
292 Sync,
294 Time,
296 Cancel,
298 Security,
300}
301
302impl fmt::Display for TestCategory {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 match self {
305 TestCategory::Spawn => write!(f, "spawn"),
306 TestCategory::Channels => write!(f, "channels"),
307 TestCategory::IO => write!(f, "io"),
308 TestCategory::Sync => write!(f, "sync"),
309 TestCategory::Time => write!(f, "time"),
310 TestCategory::Cancel => write!(f, "cancel"),
311 TestCategory::Security => write!(f, "security"),
312 }
313 }
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct TestMeta {
323 pub id: String,
325 pub name: String,
327 pub description: String,
329 pub category: TestCategory,
331 pub tags: Vec<String>,
333 pub expected: String,
335}
336
337pub trait RuntimeInterface: Sized {
346 type JoinHandle<T: Send + 'static>: Future<Output = T> + Send + 'static;
349
350 type MpscSender<T: Send + 'static>: MpscSender<T> + 'static;
352
353 type MpscReceiver<T: Send + 'static>: MpscReceiver<T> + 'static;
355
356 type OneshotSender<T: Send + 'static>: OneshotSender<T> + 'static;
358
359 type OneshotReceiver<T: Send + 'static>: Future<Output = Result<T, OneshotRecvError>>
361 + Send
362 + 'static;
363
364 type BroadcastSender<T: Send + Clone + 'static>: BroadcastSender<T> + 'static;
366
367 type BroadcastReceiver<T: Send + Clone + 'static>: BroadcastReceiver<T> + 'static;
369
370 type WatchSender<T: Send + Sync + 'static>: WatchSender<T> + 'static;
372
373 type WatchReceiver<T: Send + Sync + Clone + 'static>: WatchReceiver<T> + 'static;
375
376 type File: AsyncFile + 'static;
378
379 type TcpListener: TcpListener<Stream = Self::TcpStream> + 'static;
381
382 type TcpStream: TcpStream + 'static;
384
385 type UdpSocket: UdpSocket + 'static;
387
388 fn spawn<F>(&self, future: F) -> Self::JoinHandle<F::Output>
391 where
392 F: Future + Send + 'static,
393 F::Output: Send + 'static;
394
395 fn block_on<F: Future>(&self, future: F) -> F::Output;
398
399 fn bench_alloc_snapshot(&self) -> Option<crate::bench::runner::BenchAllocSnapshot> {
401 None
402 }
403
404 fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
407
408 fn timeout<'a, F: Future + Send + 'a>(
410 &'a self,
411 duration: Duration,
412 future: F,
413 ) -> Pin<Box<dyn Future<Output = Result<F::Output, TimeoutError>> + Send + 'a>>
414 where
415 F::Output: Send;
416
417 fn mpsc_channel<T: Send + 'static>(
420 &self,
421 capacity: usize,
422 ) -> (Self::MpscSender<T>, Self::MpscReceiver<T>);
423
424 fn oneshot_channel<T: Send + 'static>(
426 &self,
427 ) -> (Self::OneshotSender<T>, Self::OneshotReceiver<T>);
428
429 fn broadcast_channel<T: Send + Clone + 'static>(
431 &self,
432 capacity: usize,
433 ) -> (Self::BroadcastSender<T>, Self::BroadcastReceiver<T>);
434
435 fn watch_channel<T: Send + Sync + Clone + 'static>(
437 &self,
438 initial: T,
439 ) -> (Self::WatchSender<T>, Self::WatchReceiver<T>);
440
441 fn file_create<'a>(
444 &'a self,
445 path: &'a Path,
446 ) -> Pin<Box<dyn Future<Output = io::Result<Self::File>> + Send + 'a>>;
447
448 fn file_open<'a>(
450 &'a self,
451 path: &'a Path,
452 ) -> Pin<Box<dyn Future<Output = io::Result<Self::File>> + Send + 'a>>;
453
454 fn tcp_listen<'a>(
457 &'a self,
458 addr: &'a str,
459 ) -> Pin<Box<dyn Future<Output = io::Result<Self::TcpListener>> + Send + 'a>>;
460
461 fn tcp_connect<'a>(
463 &'a self,
464 addr: SocketAddr,
465 ) -> Pin<Box<dyn Future<Output = io::Result<Self::TcpStream>> + Send + 'a>>;
466
467 fn udp_bind<'a>(
469 &'a self,
470 addr: &'a str,
471 ) -> Pin<Box<dyn Future<Output = io::Result<Self::UdpSocket>> + Send + 'a>>;
472}
473
474#[derive(Debug, Clone, Copy, PartialEq, Eq)]
480pub struct OneshotRecvError;
481
482impl fmt::Display for OneshotRecvError {
483 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484 write!(f, "oneshot channel sender dropped")
485 }
486}
487
488impl std::error::Error for OneshotRecvError {}
489
490pub trait MpscSender<T: Send>: Clone + Send + Sync {
492 fn send(&self, value: T) -> Pin<Box<dyn Future<Output = Result<(), T>> + Send + '_>>;
494}
495
496pub trait MpscReceiver<T: Send>: Send {
498 fn recv(&mut self) -> Pin<Box<dyn Future<Output = Option<T>> + Send + '_>>;
500}
501
502pub trait OneshotSender<T: Send>: Send {
504 fn send(self, value: T) -> Result<(), T>;
506}
507
508#[derive(Debug, Clone, Copy, PartialEq, Eq)]
510pub enum BroadcastRecvError {
511 Lagged(u64),
513 Closed,
515}
516
517impl fmt::Display for BroadcastRecvError {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 match self {
520 Self::Lagged(n) => write!(f, "receiver lagged by {n} messages"),
521 Self::Closed => write!(f, "broadcast channel closed"),
522 }
523 }
524}
525
526impl std::error::Error for BroadcastRecvError {}
527
528pub trait BroadcastSender<T: Send + Clone>: Clone + Send + Sync {
530 fn send(&self, value: T) -> Result<usize, T>;
532
533 fn subscribe(&self) -> Box<dyn BroadcastReceiver<T>>;
535}
536
537pub trait BroadcastReceiver<T: Send + Clone>: Send {
539 fn recv(&mut self) -> Pin<Box<dyn Future<Output = Result<T, BroadcastRecvError>> + Send + '_>>;
541}
542
543#[derive(Debug, Clone, Copy, PartialEq, Eq)]
545pub struct WatchRecvError;
546
547impl fmt::Display for WatchRecvError {
548 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549 write!(f, "watch channel closed")
550 }
551}
552
553impl std::error::Error for WatchRecvError {}
554
555pub trait WatchSender<T: Send + Sync>: Send + Sync {
557 fn send(&self, value: T) -> Result<(), T>;
559}
560
561pub trait WatchReceiver<T: Send + Sync>: Clone + Send + Sync {
563 fn changed(&mut self) -> Pin<Box<dyn Future<Output = Result<(), WatchRecvError>> + Send + '_>>;
565
566 fn borrow_and_clone(&self) -> T;
568}
569
570pub trait AsyncFile: Send {
576 fn write_all<'a>(
578 &'a mut self,
579 buf: &'a [u8],
580 ) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'a>>;
581
582 fn read_exact<'a>(
584 &'a mut self,
585 buf: &'a mut [u8],
586 ) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'a>>;
587
588 fn read_to_end<'a>(
590 &'a mut self,
591 buf: &'a mut Vec<u8>,
592 ) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'a>>;
593
594 fn seek<'a>(
596 &'a mut self,
597 pos: SeekFrom,
598 ) -> Pin<Box<dyn Future<Output = io::Result<u64>> + Send + 'a>>;
599
600 fn sync_all(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + '_>>;
602
603 fn shutdown(&mut self) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + '_>>;
605}
606
607#[derive(Debug, Clone, Copy, PartialEq, Eq)]
613pub struct TimeoutError;
614
615impl fmt::Display for TimeoutError {
616 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
617 write!(f, "operation timed out")
618 }
619}
620
621impl std::error::Error for TimeoutError {}
622
623pub trait TcpListener: Send {
625 type Stream: TcpStream;
627
628 fn local_addr(&self) -> io::Result<SocketAddr>;
630
631 fn accept(
633 &mut self,
634 ) -> Pin<Box<dyn Future<Output = io::Result<(Self::Stream, SocketAddr)>> + Send + '_>>;
635}
636
637pub trait TcpStream: Send {
639 fn read<'a>(
641 &'a mut self,
642 buf: &'a mut [u8],
643 ) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'a>>;
644
645 fn read_exact<'a>(
647 &'a mut self,
648 buf: &'a mut [u8],
649 ) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'a>>;
650
651 fn write_all<'a>(
653 &'a mut self,
654 buf: &'a [u8],
655 ) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'a>>;
656
657 fn shutdown(&mut self) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + '_>>;
659}
660
661pub trait UdpSocket: Send {
663 fn local_addr(&self) -> io::Result<SocketAddr>;
665
666 fn send_to<'a>(
668 &'a self,
669 buf: &'a [u8],
670 addr: SocketAddr,
671 ) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'a>>;
672
673 fn recv_from<'a>(
675 &'a self,
676 buf: &'a mut [u8],
677 ) -> Pin<Box<dyn Future<Output = io::Result<(usize, SocketAddr)>> + Send + 'a>>;
678}
679
680pub struct ConformanceTest<RT: RuntimeInterface> {
686 pub meta: TestMeta,
688 pub test_fn: fn(&RT) -> TestResult,
690}
691
692impl<RT: RuntimeInterface> ConformanceTest<RT> {
693 pub const fn new(meta: TestMeta, test_fn: fn(&RT) -> TestResult) -> Self {
695 Self { meta, test_fn }
696 }
697
698 pub fn run(&self, runtime: &RT) -> TestResult {
700 (self.test_fn)(runtime)
701 }
702}
703
704#[macro_export]
725macro_rules! conformance_test {
726 (
727 id: $id:literal,
728 name: $name:literal,
729 description: $desc:literal,
730 category: $cat:expr,
731 tags: [$($tag:literal),* $(,)?],
732 expected: $expected:literal,
733 test: |$rt:ident| $body:expr
734 ) => {
735 {
736 fn test_fn<RT: $crate::RuntimeInterface>($rt: &RT) -> $crate::TestResult {
737 $body
738 }
739
740 $crate::ConformanceTest::new(
741 $crate::TestMeta {
742 id: $id.to_string(),
743 name: $name.to_string(),
744 description: $desc.to_string(),
745 category: $cat,
746 tags: vec![$($tag.to_string()),*],
747 expected: $expected.to_string(),
748 },
749 test_fn,
750 )
751 }
752 };
753}