Skip to main content

zerodds_conformance/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! Conformance-Test-Vector-Runner.
5//!
6//! Crate `zerodds-conformance`. Safety classification: **STANDARD**.
7//!
8//! Pro externer Conformance-Suite ein Modul, das die Spec-Test-
9//! Vektoren als Konstanten haelt und gegen die Production-
10//! Implementations laufen laesst. Damit ist die Konformitaet im
11//! Workspace nachweisbar ohne externe CI-Tool-Setup-Abhaengigkeit
12//! — externe Tools (Autobahn-Server, h2spec) bleiben optional in
13//! `live-interop`-Job.
14//!
15//! # Module
16//!
17//! * [`autobahn_ws`] — RFC 6455 + RFC 7692 (WebSocket).
18//! * [`oasis_mqtt`] — OASIS MQTT-5.0 Spec §3 + §4.
19//! * [`h2spec_grpc`] — RFC 7540/7541 (HTTP/2 + HPACK) + gRPC-interop.
20//! * [`coap_plugtest`] — IETF-Plugtest (RFC 7252 + 7641 + 7959).
21//! * [`dds_xml_xvendor`] — DDS-XML 1.0 Cross-Vendor + W3C-XSD.
22
23#![no_std]
24#![forbid(unsafe_code)]
25#![warn(missing_docs)]
26
27extern crate alloc;
28
29#[cfg(feature = "std")]
30extern crate std;
31
32pub mod autobahn_ws;
33pub mod coap_plugtest;
34pub mod dds_xml_xvendor;
35pub mod h2spec_grpc;
36pub mod oasis_mqtt;
37
38/// Ergebnis eines einzelnen Conformance-Test-Cases.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum CaseResult {
41    /// Test bestanden.
42    Pass,
43    /// Test fehlgeschlagen, mit Begruendung.
44    Fail(alloc::string::String),
45    /// Test geskipped (z.B. Spec optional + Feature-Flag aus).
46    Skip(&'static str),
47}
48
49impl CaseResult {
50    /// `true` wenn `Pass` oder `Skip`.
51    #[must_use]
52    pub fn is_acceptable(&self) -> bool {
53        !matches!(self, Self::Fail(_))
54    }
55}
56
57/// Ein einzelner Test-Case (Name + Run-Funktion).
58pub struct TestCase {
59    /// Name (typisch Spec-§-Referenz).
60    pub name: &'static str,
61    /// Run-Funktion.
62    pub run: fn() -> CaseResult,
63}
64
65/// Liefert ein Reporting-Tupel `(passed, skipped, failed)`.
66#[must_use]
67pub fn run_suite(suite: &[TestCase]) -> (usize, usize, usize) {
68    let mut pass = 0usize;
69    let mut skip = 0usize;
70    let mut fail = 0usize;
71    for c in suite {
72        match (c.run)() {
73            CaseResult::Pass => pass += 1,
74            CaseResult::Skip(_) => skip += 1,
75            CaseResult::Fail(_) => fail += 1,
76        }
77    }
78    (pass, skip, fail)
79}
80
81#[cfg(test)]
82#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn case_result_pass_acceptable() {
88        assert!(CaseResult::Pass.is_acceptable());
89        assert!(CaseResult::Skip("foo").is_acceptable());
90        assert!(!CaseResult::Fail("bar".into()).is_acceptable());
91    }
92
93    #[test]
94    fn run_suite_reports_counts() {
95        fn p() -> CaseResult {
96            CaseResult::Pass
97        }
98        fn s() -> CaseResult {
99            CaseResult::Skip("x")
100        }
101        fn f() -> CaseResult {
102            CaseResult::Fail("y".into())
103        }
104        let suite = [
105            TestCase { name: "a", run: p },
106            TestCase { name: "b", run: s },
107            TestCase { name: "c", run: f },
108        ];
109        let (p, s, f) = run_suite(&suite);
110        assert_eq!((p, s, f), (1, 1, 1));
111    }
112}