use crate::{AssertThat, Mode, tracking::AssertionTracking};
use core::fmt::Debug;
use tokio::sync::RwLock;
#[allow(clippy::return_self_not_must_use)]
#[cfg_attr(feature = "fluent", assertr_derive::fluent_aliases)]
pub trait TokioRwLockAssertions<T: Debug> {
#[cfg_attr(feature = "fluent", fluent_alias("not_be_locked"))]
fn is_not_locked(self) -> Self;
fn is_free(self) -> Self
where
Self: Sized,
{
self.is_not_locked()
}
fn is_read_locked(self) -> Self;
fn is_write_locked(self) -> Self;
}
impl<T: Debug, M: Mode> TokioRwLockAssertions<T> for AssertThat<'_, RwLock<T>, M> {
#[track_caller]
fn is_not_locked(self) -> Self
where
T: Debug,
{
self.track_assertion();
if self.actual().try_write().is_err() {
if self.actual().try_read().is_err() {
self.fail(format_args!(
"Actual: {actual:?}\n\nwas expected to not be read- or write-locked, but it is!\n\nIt is currently write-locked!\n",
actual = self.actual(),
));
} else {
self.fail(format_args!(
"Actual: {actual:?}\n\nwas expected to not be read- or write-locked, but it is!\n\nIt is currently read-locked!\n",
actual = self.actual(),
));
}
}
self
}
#[track_caller]
fn is_read_locked(self) -> Self
where
T: Debug,
{
self.track_assertion();
if self.actual().try_write().is_ok() {
self.fail(format_args!(
"Actual: {actual:?}\n\nwas expected to be read-locked, but it is not!\n\nIt is not locked at all!\n",
actual = self.actual(),
));
} else {
if self.actual().try_read().is_err() {
self.fail(format_args!(
"Actual: {actual:?}\n\nwas expected to be read-locked, but it is not!\n\nIt is currently write-locked!\n",
actual = self.actual(),
));
}
}
self
}
#[track_caller]
fn is_write_locked(self) -> Self
where
T: Debug,
{
self.track_assertion();
if self.actual().try_write().is_ok() {
self.fail(format_args!(
"Actual: {actual:?}\n\nwas expected to be write-locked, but it is not!\n",
actual = self.actual(),
));
} else {
if self.actual().try_read().is_ok() {
self.fail(format_args!(
"Actual: {actual:?}\n\nwas expected to be write-locked, but it is not!\n\nIt is currently read-locked!\n",
actual = self.actual(),
));
}
}
self
}
}
#[cfg(test)]
mod tests {
mod is_not_locked {
use crate::prelude::*;
use indoc::formatdoc;
use tokio::sync::RwLock;
#[test]
fn succeeds_when_not_locked() {
let rw_lock = RwLock::new(42);
assert_that!(rw_lock).is_not_locked();
}
#[tokio::test]
async fn panics_when_write_locked() {
let rw_lock = RwLock::new(42);
let rw_lock_write_guard = rw_lock.write().await;
assert_that_panic_by(|| assert_that!(&rw_lock).with_location(false).is_not_locked())
.has_type::<String>()
.is_equal_to(formatdoc! {r#"
-------- assertr --------
Actual: RwLock {{ data: <locked> }}
was expected to not be read- or write-locked, but it is!
It is currently write-locked!
-------- assertr --------
"#});
drop(rw_lock_write_guard);
}
#[tokio::test]
async fn panics_when_read_locked() {
let rw_lock = RwLock::new(42);
let rw_lock_read_guard = rw_lock.read().await;
assert_that_panic_by(|| assert_that!(&rw_lock).with_location(false).is_not_locked())
.has_type::<String>()
.is_equal_to(formatdoc! {r#"
-------- assertr --------
Actual: RwLock {{ data: 42 }}
was expected to not be read- or write-locked, but it is!
It is currently read-locked!
-------- assertr --------
"#});
drop(rw_lock_read_guard);
}
}
mod is_read_locked {
use crate::prelude::*;
use indoc::formatdoc;
use tokio::sync::RwLock;
#[tokio::test]
async fn succeeds_when_read_locked() {
let rw_lock = RwLock::new(42);
let rw_lock_read_guard = rw_lock.read().await;
assert_that!(&rw_lock).is_read_locked();
drop(rw_lock_read_guard);
}
#[tokio::test]
async fn panics_when_write_locked() {
let rw_lock = RwLock::new(42);
let rw_lock_write_guard = rw_lock.write().await;
assert_that_panic_by(|| assert_that!(&rw_lock).with_location(false).is_read_locked())
.has_type::<String>()
.is_equal_to(formatdoc! {r#"
-------- assertr --------
Actual: RwLock {{ data: <locked> }}
was expected to be read-locked, but it is not!
It is currently write-locked!
-------- assertr --------
"#});
drop(rw_lock_write_guard);
}
#[test]
fn panics_when_not_locked_at_all() {
let rw_lock = RwLock::new(42);
assert_that_panic_by(|| assert_that!(rw_lock).with_location(false).is_read_locked())
.has_type::<String>()
.is_equal_to(formatdoc! {r#"
-------- assertr --------
Actual: RwLock {{ data: 42 }}
was expected to be read-locked, but it is not!
It is not locked at all!
-------- assertr --------
"#});
}
}
mod is_write_locked {
use crate::prelude::*;
use indoc::formatdoc;
use tokio::sync::RwLock;
#[tokio::test]
async fn succeeds_when_write_locked() {
let rw_lock = RwLock::new(42);
let rw_lock_write_guard = rw_lock.write().await;
assert_that!(&rw_lock).is_write_locked();
drop(rw_lock_write_guard);
}
#[tokio::test]
async fn panics_when_read_locked() {
let rw_lock = RwLock::new(42);
let rw_lock_read_guard = rw_lock.read().await;
assert_that_panic_by(|| {
assert_that!(&rw_lock)
.with_location(false)
.is_write_locked()
})
.has_type::<String>()
.is_equal_to(formatdoc! {r#"
-------- assertr --------
Actual: RwLock {{ data: 42 }}
was expected to be write-locked, but it is not!
It is currently read-locked!
-------- assertr --------
"#});
drop(rw_lock_read_guard);
}
#[test]
fn panics_when_not_write_locked() {
let rw_lock = RwLock::new(42);
assert_that_panic_by(|| assert_that!(rw_lock).with_location(false).is_write_locked())
.has_type::<String>()
.is_equal_to(formatdoc! {r#"
-------- assertr --------
Actual: RwLock {{ data: 42 }}
was expected to be write-locked, but it is not!
-------- assertr --------
"#});
}
}
}