Add a defer async macro for testing (#3908)
This commit is contained in:
parent
b9da56e99a
commit
02faf73fef
2 changed files with 175 additions and 35 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue