summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzy <[email protected]>2023-09-13 10:23:23 +0000
committerzy <[email protected]>2023-09-13 10:23:23 +0000
commit5c859f8726be8fae89df8b08520221dc6f7b6563 (patch)
treef10ea1035f6207a3acbef36f463110436f30093a
parent37f9a572171a4b3b02fd9140f28481a33863382d (diff)
仿照 test-timeout.c 编写 test_timeout.rs
- cfg1 测试不通过, 临时的 now 超过了 100 就 panic - cfg2b 反而可以测试通过; 修正了 timeout.rs 的几个类型错误, 为一些结构体 提供默认初始化方法.
-rw-r--r--src/test_timeout.rs462
-rw-r--r--src/timeout.rs47
2 files changed, 504 insertions, 5 deletions
diff --git a/src/test_timeout.rs b/src/test_timeout.rs
new file mode 100644
index 0000000..244a521
--- /dev/null
+++ b/src/test_timeout.rs
@@ -0,0 +1,462 @@
+use libc::free;
+use rand::Rng;
+
+use crate::timeout::timeout_version;
+use crate::timeout::*;
+use std::{
+ ffi::CStr,
+ io::{self, Write},
+ usize,
+};
+
+static mut n_failed: i32 = 0;
+
+macro_rules! DO {
+ ($fn:expr) => {{
+ print!(".");
+ io::stdout().flush().unwrap();
+ if $fn {
+ unsafe {
+ n_failed += 1;
+ }
+ println!("{} failed", stringify!($fn));
+ }
+ }};
+}
+
+macro_rules! DO_N {
+ ($n:expr, $fn:expr) => {{
+ for j in 0..$n {
+ DO!($fn);
+ }
+ }};
+}
+
+macro_rules! fail {
+ () => {{
+ println!("Failure on line {}", line!());
+ return true;
+ }};
+}
+
+fn check_misc() -> bool {
+ if (TIMEOUT_V_REL as i32) != unsafe { timeout_version() } {
+ return true;
+ }
+ if (TIMEOUT_V_REL as i32) != unsafe { timeout_v_rel() } {
+ return true;
+ }
+ if (TIMEOUT_V_API as i32) != unsafe { timeout_v_api() } {
+ return true;
+ }
+ if (TIMEOUT_V_ABI as i32) != unsafe { timeout_v_abi() } {
+ return true;
+ }
+ if unsafe { CStr::from_ptr(timeout_vendor()).to_bytes() } != TIMEOUT_VENDOR {
+ return true;
+ }
+ false
+}
+
+fn check_open_close(hz_set: timeout_t, hz_expect: timeout_t) -> bool {
+ let mut err = 0 as usize;
+ let tos: *mut timeouts = unsafe { timeouts_open(hz_set, &mut err) };
+
+ if tos.is_null() {
+ return true;
+ }
+ if err != 0 {
+ return true;
+ }
+ if hz_expect != unsafe { timeouts_hz(tos) } {
+ return true;
+ }
+ false
+}
+
+/* configuration for check_randomized */
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+struct rand_cfg {
+ /* When creating timeouts, smallest possible delay */
+ min_timeout: timeout_t,
+ /* When creating timeouts, largest possible delay */
+ max_timeout: timeout_t,
+ /* First time to start the clock at. */
+ start_at: timeout_t,
+ /* Do not advance the clock past this time. */
+ end_at: timeout_t,
+ /* Number of timeouts to create and monitor. */
+ n_timeouts: usize,
+ /* Advance the clock by no more than this each step. */
+ max_step: timeout_t,
+ /* Use relative timers and stepping */
+ relative: usize,
+ /* Every time the clock ticks, try removing this many timeouts at
+ * random. */
+ try_removing: usize,
+ /* When we're done, advance the clock to the end of time. */
+ finalize: usize,
+}
+
+/* Not very random */
+// fn random_to(min: timeout_t, max: timeout_t) -> timeout_t {
+// let mut rng = rand::thread_rng();
+// if max <= min {
+// return min;
+// }
+// /* Not actually all that random, but should exercise the code. */
+// let rand64 = rng.gen::<u64>() * (i32::MAX as timeout_t) + rng.gen::<u64>();
+// min + (rand64 % (max - min))
+// }
+
+fn random_to(min: u64, max: u64) -> u64 {
+ if max <= min {
+ return min;
+ }
+ let rand64 = rand::thread_rng().gen::<u64>();
+ min + (rand64 % (max - min))
+}
+
+fn random() -> usize {
+ rand::thread_rng().gen::<usize>()
+}
+
+fn check_randomized(cfg: &rand_cfg) -> bool {
+ let (i, rv) = (0, 0);
+ let mut err = 0 as usize;
+
+ let mut t: Vec<timeout> = vec![timeout::default(); cfg.n_timeouts];
+ let mut timeouts: Vec<timeout_t> = vec![0; cfg.n_timeouts];
+ let mut fired: Vec<u8> = vec![0; cfg.n_timeouts];
+ let mut found: Vec<u8> = vec![0; cfg.n_timeouts];
+ let mut deleted: Vec<u8> = vec![0; cfg.n_timeouts];
+
+ let mut tos = unsafe { Some(timeouts_open(0, &mut err)) };
+ let mut now = cfg.start_at;
+
+ let (mut n_added_pending, mut cnt_added_pending, mut n_added_expired, mut cnt_added_expired) =
+ (0, 0, 0, 0);
+
+ let (mut it_p, mut it_e, mut it_all) = (
+ timeouts_it::default(),
+ timeouts_it::default(),
+ timeouts_it::default(),
+ );
+
+ let (mut p_done, mut e_done, mut all_done) = (false, false, false);
+ let mut to: Option<&mut timeout> = None;
+ let rel = cfg.relative;
+
+ // 对应 done
+ let cleanup = |tos,
+ t: Vec<timeout>,
+ timeouts: Vec<timeout_t>,
+ fired: Vec<u8>,
+ found: Vec<u8>,
+ deleted: Vec<u8>| {
+ if let Some(tos) = tos {
+ unsafe { timeouts_close(tos) };
+ }
+ if t.is_empty() {
+ drop(t);
+ // unsafe { free(t) };
+ }
+ if !timeouts.is_empty() {
+ drop(timeouts);
+ // unsafe { free(timeouts) };
+ }
+ if !fired.is_empty() {
+ drop(fired);
+ // unsafe { free(fired) };
+ }
+ if !found.is_empty() {
+ drop(found);
+ // unsafe { free(found) };
+ }
+ if !deleted.is_empty() {
+ drop(deleted);
+ // unsafe { free(deleted) };
+ }
+ };
+
+ if t.is_empty()
+ || timeouts.is_empty()
+ || tos.is_none()
+ || fired.is_empty()
+ || found.is_empty()
+ || deleted.is_empty()
+ {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ if let Some(mut tos) = tos {
+ unsafe { timeouts_update(tos, cfg.start_at) };
+ }
+
+ // test-timeout.c line 98
+ for i in 0..cfg.n_timeouts {
+ if &t[i] as *const _
+ != unsafe { timeout_init(&mut t[i], if rel > 0 { 0 } else { TIMEOUT_ABS }) } as *const _
+ {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ if unsafe { timeout_pending(&mut t[i]) || timeout_expired(&mut t[i]) } {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+
+ timeouts[i] = random_to(cfg.min_timeout, cfg.max_timeout);
+
+ unsafe {
+ if let Some(temp) = tos {
+ timeouts_add(
+ temp,
+ &mut t[i],
+ timeouts[i] - (if rel > 0 { now } else { 0 }),
+ )
+ }
+ }
+ if timeouts[i] <= cfg.start_at {
+ if unsafe { timeout_pending(&mut t[i]) || !timeout_expired(&mut t[i]) } {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ n_added_expired += 1;
+ } else {
+ if unsafe { !timeout_pending(&mut t[i]) || timeout_expired(&mut t[i]) } {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ n_added_pending += 1;
+ }
+ }
+
+ // test-timeout.c line 124
+ if unsafe {
+ if let Some(temp) = tos {
+ (n_added_pending != 0) != timeouts_pending(temp)
+ } else {
+ true
+ }
+ } {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ // test-timeout.c line 126
+ if unsafe {
+ if let Some(temp) = tos {
+ (n_added_expired != 0) != timeouts_expired(temp)
+ } else {
+ true
+ }
+ } {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+
+ TIMEOUTS_IT_INIT(&mut it_p, TIMEOUTS_PENDING);
+ TIMEOUTS_IT_INIT(&mut it_e, TIMEOUTS_EXPIRED);
+ TIMEOUTS_IT_INIT(&mut it_all, TIMEOUTS_ALL);
+
+ // timeout.c line 133
+ while !(p_done && e_done && all_done) {
+ if !p_done {
+ if let Some(temp) = tos {
+ let to = unsafe { timeouts_next(temp, &mut it_p) };
+ if !to.is_null() {
+ // c 语言中的指针运算,这里只能以 内存地址长度/ timeout 类型长度,来进行运算
+ let i =
+ (to as usize - &t[0] as *const _ as usize) / std::mem::size_of::<timeout>();
+ found[i] += 1;
+ cnt_added_pending += 1;
+ } else {
+ p_done = true;
+ }
+ }
+ }
+ if !e_done {
+ if let Some(temp) = tos {
+ let to = unsafe { timeouts_next(temp, &mut it_e) };
+ if !to.is_null() {
+ // c 语言中的指针运算,这里只能以 内存地址长度/ timeout 类型长度,来进行运算
+ let i =
+ (to as usize - &t[0] as *const _ as usize) / std::mem::size_of::<timeout>();
+ found[i] += 1;
+ cnt_added_expired += 1;
+ } else {
+ e_done = true;
+ }
+ }
+ }
+ if !all_done {
+ if let Some(temp) = tos {
+ let to = unsafe { timeouts_next(temp, &mut it_all) };
+ if !to.is_null() {
+ // c 语言中的指针运算,这里只能以 内存地址长度/ timeout 类型长度,来进行运算
+ let i =
+ (to as usize - &t[0] as *const _ as usize) / std::mem::size_of::<timeout>();
+ found[i] += 1;
+ } else {
+ all_done = true;
+ }
+ }
+ }
+ }
+ // timeout.c line 164
+ for i in 0..cfg.n_timeouts {
+ if found[i] != 2 {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ }
+ if cnt_added_expired != n_added_expired {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ if cnt_added_pending != n_added_pending {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ // timeout.c line 174
+ if let Some(temp) = tos {
+ loop {
+ let to: *mut timeout = unsafe { timeouts_get(temp) };
+ if to.is_null() {
+ break;
+ }
+ let i = (to as usize - &t[0] as *const _ as usize) / std::mem::size_of::<timeout>();
+ assert_eq!(&t[i] as *const _, to);
+ if timeouts[i] > cfg.start_at {
+ /* shouldn't have happened yet */
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ n_added_expired -= 1; /* drop expired timeouts. */
+ fired[i] += 1;
+ }
+ }
+
+ if n_added_expired != 0 {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+
+ // test-timeout.c line 187
+ while now < cfg.end_at {
+ let mut n_fired_this_time = 0;
+
+ // now 似乎超过 100 就不行
+ let first_at = unsafe { timeouts_timeout(tos.unwrap()) + now };
+ let oldtime = now;
+ let step = random_to(1, cfg.max_step);
+
+ now += step;
+ if rel > 0 {
+ unsafe { timeouts_step(tos.unwrap(), step) };
+ } else {
+ unsafe { timeouts_update(tos.unwrap(), now) };
+ }
+
+ for _i in 0..cfg.try_removing {
+ let idx = random() % cfg.n_timeouts;
+ if !(fired[idx] > 0) {
+ unsafe {
+ timeout_del(&mut t[idx]);
+ }
+ deleted[idx] += 1;
+ }
+ }
+
+ let mut another = unsafe { timeouts_timeout(tos.unwrap()) } == 0;
+
+ loop {
+ let to = unsafe { timeouts_get(tos.unwrap()) };
+ if to.is_null() {
+ break;
+ }
+ if !another {
+ /* Thought we saw the last one! */
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+
+ let i = (to as usize - &t[0] as *const _ as usize) / std::mem::size_of::<timeout>();
+ assert_eq!(&t[i] as *const _, to);
+ if timeouts[i] > now {
+ /* shouldn't have happened yet */
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ if timeouts[i] <= oldtime {
+ /* should have happened already*/
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ if timeouts[i] < first_at {
+ /* first_at should've been earlier */
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ fired[i] += 1;
+ n_fired_this_time += 1;
+ another = unsafe { timeouts_timeout(tos.unwrap()) } == 0;
+ }
+
+ if (n_fired_this_time != 0) && (first_at > now) {
+ /* first_at should've been earlier */
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ if another {
+ /* Huh? We think there are more? */
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+ if !unsafe { timeouts_check(tos.unwrap(), stderr) } {
+ cleanup(tos, t, timeouts, fired, found, deleted);
+ fail!();
+ }
+
+ // let first_at = unsafe { timeouts_timeout(tos.unwrap()) + now };
+ }
+
+ return false;
+}
+
+#[test]
+fn main() {
+ unsafe {
+ n_failed = 0;
+ }
+ DO!(check_misc());
+ DO!(check_open_close(1000, 1000));
+ DO!(check_open_close(0, TIMEOUT_mHZ));
+
+ let cfg1 = rand_cfg {
+ min_timeout: 1,
+ max_timeout: 100,
+ start_at: 5,
+ end_at: 1000,
+ n_timeouts: 1000,
+ max_step: 10,
+ relative: 0,
+ try_removing: 0,
+ finalize: 2,
+ };
+ // DO_N!(300, check_randomized(&cfg1));
+
+ let cfg2b = rand_cfg {
+ min_timeout: 20,
+ max_timeout: 1000,
+ start_at: 10,
+ end_at: 100,
+ n_timeouts: 1000,
+ max_step: 5,
+ relative: 1,
+ try_removing: 0,
+ finalize: 1,
+ };
+ DO_N!(300,check_randomized(&cfg2b));
+}
diff --git a/src/timeout.rs b/src/timeout.rs
index b331f1f..297635b 100644
--- a/src/timeout.rs
+++ b/src/timeout.rs
@@ -52,7 +52,7 @@ pub struct timeout_cb {
// #[cfg(TIMEOUT_DISABLE_INTERVALS)]
pub const TIMEOUT_INT: u32 = 1;
-pub const TIMEOUT_ABS: u32 = 2;
+pub const TIMEOUT_ABS: i32 = 2;
// TODO 宏定义 TIMEOUT_INITIALIZER timeout_setcb
@@ -84,6 +84,27 @@ pub struct timeout {
// #[cfg(TIMEOUT_DISABLE_RELATIVE_ACCESS)]
pub timeouts: *mut timeouts, /* timeouts collection if member of */
}
+
+impl Default for timeout {
+ fn default() -> Self {
+ timeout {
+ flags: 0,
+ expires: 0,
+ pending: null_mut(),
+ tqe: timeout_TAILQ_ENTRY {
+ tqe_next: null_mut(),
+ tqe_prev: null_mut(),
+ },
+ callback: timeout_cb {
+ fn_: None,
+ arg: null_mut(),
+ },
+ interval: 0,
+ timeouts: null_mut(),
+ }
+ }
+}
+
#[link(name = "timeout")]
extern "C" {
/* initialize timeout structure (same as TIMEOUT_INITIALIZER) */
@@ -136,11 +157,15 @@ extern "C" {
// TODO check FILE type
pub fn timeouts_check(arg1: *mut timeouts, arg2: *mut FILE) -> bool;
}
+extern "C" {
+ pub static mut stderr: *mut FILE;
+}
+
-pub const TIMEOUTS_PENDING: u32 = 16;
-pub const TIMEOUTS_EXPIRED: u32 = 32;
-pub const TIMEOUTS_ALL: u32 = 48;
-pub const TIMEOUTS_CLEAR: u32 = 64;
+pub const TIMEOUTS_PENDING: i32 = 16;
+pub const TIMEOUTS_EXPIRED: i32 = 32;
+pub const TIMEOUTS_ALL: i32 = 48;
+pub const TIMEOUTS_CLEAR: i32 = 64;
// 原为 C 的宏定义 能转为 C 兼容函数
#[no_mangle]
@@ -172,6 +197,18 @@ pub struct timeouts_it {
pub to: *mut timeout,
}
+impl Default for timeouts_it {
+ fn default() -> Self {
+ timeouts_it {
+ flags: 0,
+ pc: 0,
+ i: 0,
+ j: 0,
+ to: null_mut(),
+ }
+ }
+}
+
/* return next timeout in pending wheel or expired queue. caller can delete
* the returned timeout, but should not otherwise manipulate the timing
* wheel. in particular, caller SHOULD NOT delete any other timeout as that