macro_rules! define_csaf_test {
(
$struct_name:ident, $validator_name:ident,
id: $id:literal,
doc_type: $doc_type:ty,
version: $version:literal,
cases: [$(($case_name:ident, $case_num:literal, $path:literal, $display:literal)),* $(,)?]
) => {
#[derive(Debug, Clone, Copy)]
pub struct $struct_name<V>(std::marker::PhantomData<V>);
impl<V> $struct_name<V> {
pub const ID: &'static str = $id;
pub const fn new() -> Self {
Self(std::marker::PhantomData)
}
pub fn id(&self) -> &'static str {
Self::ID
}
}
impl<
V: crate::test_validation::TestValidator<
crate::csaf::raw::RawDocument<$doc_type>,
> + Default,
> $struct_name<V> {
pub fn validate(
&self,
doc: &crate::csaf::raw::RawDocument<$doc_type>,
) -> Result<(), Vec<crate::validation::ValidationError>> {
let validator = V::default();
validator.validate(doc)
}
}
#[cfg(test)]
impl<
V: crate::test_validation::TestValidator<
crate::csaf::raw::RawDocument<$doc_type>,
> + Default,
> $struct_name<V> {
pub fn expect(
&self,
$($case_name: Result<(), Vec<crate::validation::ValidationError>>),*
) {
let test_cases = vec![
$({
let content = std::fs::read_to_string($path)
.unwrap_or_else(|e| panic!(
"Failed to load {} (case {}): {}",
$display, $case_num, e
));
let json: serde_json::Value = serde_json::from_str(&content)
.unwrap_or_else(|e| panic!(
"Failed to parse {} (case {}): {}",
$display, $case_num, e
));
let doc: crate::csaf::raw::RawDocument<$doc_type> =
crate::csaf::raw::RawDocument::new(json);
($case_num, doc, $case_name)
}),*
];
let validator = V::default();
for (case_num, doc, expected) in test_cases {
let actual = validator.validate(&doc);
crate::test_result_comparison::compare_test_results(
&actual,
&expected,
$version,
Self::ID,
case_num,
)
.unwrap_or_else(|e| panic!("{}", e));
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct $validator_name;
};
}
pub(crate) use define_csaf_test;
macro_rules! define_test_cases_aggregate {
(
const_name: $const_name:ident,
mandatory: [$(($m_inst:ident, $m_struct:ident, $m_validator:ident)),* $(,)?],
recommended: [$(($r_inst:ident, $r_struct:ident, $r_validator:ident)),* $(,)?],
informative: [$(($i_inst:ident, $i_struct:ident, $i_validator:ident)),* $(,)?]
) => {
#[derive(Debug, Clone, Copy)]
pub struct TestCases {
$(pub $m_inst: $m_struct<$m_validator>,)*
$(pub $r_inst: $r_struct<$r_validator>,)*
$(pub $i_inst: $i_struct<$i_validator>,)*
}
impl TestCases {
pub const fn new() -> Self {
Self {
$($m_inst: $m_struct::new(),)*
$($r_inst: $r_struct::new(),)*
$($i_inst: $i_struct::new(),)*
}
}
pub fn basic(&self) -> Vec<&'static str> {
self.mandatory()
}
pub fn extended(&self) -> Vec<&'static str> {
let mut tests = self.basic();
tests.extend(self.recommended());
tests
}
pub fn full(&self) -> Vec<&'static str> {
let mut tests = self.extended();
tests.extend(self.informative());
tests
}
pub fn mandatory(&self) -> Vec<&'static str> {
vec![$(self.$m_inst.id()),*]
}
pub fn recommended(&self) -> Vec<&'static str> {
vec![$(self.$r_inst.id()),*]
}
pub fn informative(&self) -> Vec<&'static str> {
vec![$(self.$i_inst.id()),*]
}
}
pub const $const_name: TestCases = TestCases::new();
pub fn mandatory_tests() -> Vec<&'static str> {
vec![$($const_name.$m_inst.id()),*]
}
pub fn recommended_tests() -> Vec<&'static str> {
vec![$($const_name.$r_inst.id()),*]
}
pub fn informative_tests() -> Vec<&'static str> {
vec![$($const_name.$i_inst.id()),*]
}
};
}
pub(crate) use define_test_cases_aggregate;