1use std::{path::PathBuf, sync::OnceLock};
3
4use anyhow::Result;
5use libbpf_rs::skel::Skel;
6use libtest_mimic::{Arguments, Failed, Trial};
7
8use super::state::{linux, rust, BPFState};
9
10#[macro_export]
13macro_rules! create_test {
14 ($func: path) => {
15 libtest_mimic::Trial::test(stringify!($func), move || $func())
16 };
17}
18
19#[derive(PartialEq)]
23pub struct TestSystemState {
24 pub rust_state: BPFState,
26 pub linux_state: BPFState,
28 pub ground_truth: BPFState,
30}
31
32impl TestSystemState {
33 pub fn new(skel: &dyn Skel, pin_dir: &PathBuf, ground_truth_path: &str) -> Result<Self> {
36 let ground_truth = toml::from_str(&std::fs::read_to_string(ground_truth_path)?)?;
37 Ok(Self {
38 rust_state: rust::rust_state(skel, pin_dir, &ground_truth)?,
39 linux_state: linux::linux_state(skel, pin_dir, &ground_truth)?,
40 ground_truth,
41 })
42 }
43}
44
45pub trait TestSuite {
48 type CustomTestState;
49
50 fn system_state() -> &'static OnceLock<TestSystemState>;
53
54 fn custom_state() -> &'static OnceLock<Self::CustomTestState>;
57
58 fn tests() -> Vec<Trial>;
61
62 fn get_system_state() -> Result<&'static TestSystemState, Failed> {
64 match Self::system_state().get() {
65 Some(inner) => Ok(inner),
66 None => Err("You must initialize the static first".into()),
67 }
68 }
69
70 fn get_custom_state() -> Result<&'static Self::CustomTestState, Failed> {
72 match Self::custom_state().get() {
73 Some(inner) => Ok(inner),
74 None => Err("You must initialize the static first".into()),
75 }
76 }
77
78 fn check_args(final_args: TestSystemState) -> Result<(), Failed> {
81 let initial_args = Self::get_system_state()?;
82 if initial_args.ground_truth != final_args.ground_truth {
83 initial_args.ground_truth.diff(&final_args.ground_truth);
84 return Err("ground truth changed during testing".into());
85 }
86 if initial_args.linux_state != final_args.linux_state {
87 initial_args.linux_state.diff(&final_args.linux_state);
88 return Err("linux state changed during testing".into());
89 }
90 if initial_args.rust_state != final_args.rust_state {
91 initial_args.rust_state.diff(&final_args.rust_state);
92 return Err("rust state changed during testing".into());
93 }
94 Ok(())
95 }
96
97 fn run_tests(
105 args: &Arguments,
106 system_state: TestSystemState,
107 custom_state: Self::CustomTestState,
108 ) -> Result<(), Failed>
109 where
110 Self::CustomTestState: 'static,
111 {
112 if Self::system_state().set(system_state).is_err() {
113 return Err("Unable to initialize TestSystemState".into());
114 }
115
116 if Self::custom_state().set(custom_state).is_err() {
117 return Err("Unable to initialize CustomTestState".into());
118 }
119
120 let conclusion = libtest_mimic::run(args, Self::tests());
121
122 match conclusion.has_failed() {
123 true => Err("At least one test failed".into()),
124 false => Ok(()),
125 }
126 }
127}