Add a defer async macro for testing ()

This commit is contained in:
Mees Delzenne 2024-04-19 20:33:23 +02:00 committed by GitHub
parent b9da56e99a
commit 02faf73fef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 175 additions and 35 deletions
core/src
kvs/tests
mac

View file

@ -258,41 +258,56 @@ async fn scan_paged() {
assert!(tx.put(Unknown, "test5", "5").await.is_ok());
tx.commit().await.unwrap();
// Create a readonly transaction
let mut tx = ds.transaction(Read, Optimistic).await.unwrap();
let val =
tx.scan_paged(ScanPage::from("test1".into().."test9".into()), u32::MAX).await.unwrap();
let val = val.values;
assert_eq!(val.len(), 5);
assert_eq!(val[0].0, b"test1");
assert_eq!(val[0].1, b"1");
assert_eq!(val[1].0, b"test2");
assert_eq!(val[1].1, b"2");
assert_eq!(val[2].0, b"test3");
assert_eq!(val[2].1, b"3");
assert_eq!(val[3].0, b"test4");
assert_eq!(val[3].1, b"4");
assert_eq!(val[4].0, b"test5");
assert_eq!(val[4].1, b"5");
tx.cancel().await.unwrap();
let tx = ds.transaction(Read, Optimistic).await.unwrap();
async_defer!(let tx = (tx) defer {
tx.cancel().await.unwrap();
} after {
let val =
tx.scan_paged(ScanPage::from("test1".into().."test9".into()), u32::MAX).await.unwrap();
let val = val.values;
assert_eq!(val.len(), 5);
assert_eq!(val[0].0, b"test1");
assert_eq!(val[0].1, b"1");
assert_eq!(val[1].0, b"test2");
assert_eq!(val[1].1, b"2");
assert_eq!(val[2].0, b"test3");
assert_eq!(val[2].1, b"3");
assert_eq!(val[3].0, b"test4");
assert_eq!(val[3].1, b"4");
assert_eq!(val[4].0, b"test5");
assert_eq!(val[4].1, b"5");
})
.await;
// Create a readonly transaction
let mut tx = ds.transaction(Read, Optimistic).await.unwrap();
let val =
tx.scan_paged(ScanPage::from("test2".into().."test4".into()), u32::MAX).await.unwrap();
let val = val.values;
assert_eq!(val.len(), 2);
assert_eq!(val[0].0, b"test2");
assert_eq!(val[0].1, b"2");
assert_eq!(val[1].0, b"test3");
assert_eq!(val[1].1, b"3");
tx.cancel().await.unwrap();
let tx = ds.transaction(Read, Optimistic).await.unwrap();
async_defer!(let tx = (tx) defer {
tx.cancel().await.unwrap();
} after {
let val =
tx.scan_paged(ScanPage::from("test2".into().."test4".into()), u32::MAX).await.unwrap();
let val = val.values;
assert_eq!(val.len(), 2);
assert_eq!(val[0].0, b"test2");
assert_eq!(val[0].1, b"2");
assert_eq!(val[1].0, b"test3");
assert_eq!(val[1].1, b"3");
})
.await;
// Create a readonly transaction
let mut tx = ds.transaction(Read, Optimistic).await.unwrap();
let val = tx.scan_paged(ScanPage::from("test1".into().."test9".into()), 2).await.unwrap();
let val = val.values;
assert_eq!(val.len(), 2);
assert_eq!(val[0].0, b"test1");
assert_eq!(val[0].1, b"1");
assert_eq!(val[1].0, b"test2");
assert_eq!(val[1].1, b"2");
tx.cancel().await.unwrap();
let tx = ds.transaction(Read, Optimistic).await.unwrap();
async_defer!(let tx = (tx) defer {
tx.cancel().await.unwrap();
} after {
let val =
tx.scan_paged(ScanPage::from("test2".into().."test4".into()), u32::MAX).await.unwrap();
let val = val.values;
assert_eq!(val.len(), 2);
assert_eq!(val[0].0, b"test2");
assert_eq!(val[0].1, b"2");
assert_eq!(val[1].0, b"test3");
assert_eq!(val[1].1, b"3");
})
.await;
}

View file

@ -75,3 +75,128 @@ macro_rules! lazy_env_parse_or_else {
})
};
}
#[cfg(test)]
#[macro_export]
macro_rules! async_defer{
(let $bind:ident = ($capture:expr) defer { $($d:tt)* } after { $($t:tt)* }) => {
async {
async_defer!(@captured);
async_defer!(@catch_unwind);
#[allow(unused_mut)]
let mut v = Some($capture);
#[allow(unused_mut)]
let mut $bind = Captured(&mut v);
let res = CatchUnwindFuture(async { $($t)* }).await;
#[allow(unused_variables,unused_mut)]
if let Some(mut $bind) = v.take(){
async { $($d)* }.await;
}
match res{
Ok(x) => x,
Err(e) => ::std::panic::resume_unwind(e)
}
}
};
(defer { $($d:tt)* } after { $($t:tt)* }) => {
async {
async_defer!(@catch_unwind);
let res = CatchUnwindFuture(async { $($t)* }).await;
#[allow(unused_variables)]
async { $($d)* }.await;
match res{
Ok(x) => x,
Err(e) => ::std::panic::resume_unwind(e)
}
}
};
(@captured) => {
// unwraps are save cause the value can only be taken by consuming captured.
pub struct Captured<'a,T>(&'a mut Option<T>);
impl<T> ::std::ops::Deref for Captured<'_,T>{
type Target = T;
fn deref(&self) -> &T{
self.0.as_ref().unwrap()
}
}
impl<T> ::std::ops::DerefMut for Captured<'_,T>{
fn deref_mut(&mut self) -> &mut T{
self.0.as_mut().unwrap()
}
}
impl<T> Captured<'_,T>{
#[allow(dead_code)]
pub fn take(self) -> T{
self.0.take().unwrap()
}
}
};
(@catch_unwind) => {
struct CatchUnwindFuture<F>(F);
impl<F,R> ::std::future::Future for CatchUnwindFuture<F>
where F: ::std::future::Future<Output = R>,
{
type Output = ::std::thread::Result<R>;
fn poll(self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context<'_>) -> ::std::task::Poll<Self::Output>{
let pin = unsafe{ self.map_unchecked_mut(|x| &mut x.0) };
match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(||{
pin.poll(cx)
})) {
Ok(x) => x.map(Ok),
Err(e) => ::std::task::Poll::Ready(Err(e))
}
}
}
};
}
#[cfg(test)]
mod test {
#[tokio::test]
async fn async_defer_basic() {
let mut counter = 0;
async_defer!(defer {
assert_eq!(counter,1);
} after {
assert_eq!(counter,0);
counter += 1;
})
.await;
async_defer!(let t = (()) defer {
panic!("shouldn't be called");
} after {
assert_eq!(counter,1);
counter += 1;
t.take();
})
.await;
}
#[tokio::test]
#[should_panic(expected = "this is should be the message of the panic")]
async fn async_defer_panic() {
let mut counter = 0;
async_defer!(defer {
// This should still execute
assert_eq!(counter,1);
panic!("this is should be the message of the panic")
} after {
assert_eq!(counter,0);
counter += 1;
panic!("this panic should be caught")
})
.await;
}
}