#![cfg(all(feature = "fs", target_os = "linux"))]
use xpct::{be_err, be_ok, expect, match_pattern, pattern};
use crate::{
Connection, CreateOptions, Error, FilesystemId,
lock::{LockHandler, LockResult, LockType},
};
fn fresh_handler() -> LockHandler {
LockHandler::new(FilesystemId::new())
}
#[test]
fn shared_lock_blocks_exclusive() -> crate::Result<()> {
let handler = fresh_handler();
let shared = expect!(handler.acquire_litebox_lock(LockType::Read)?)
.to(match_pattern(pattern!(LockResult::Acquired(_))))
.into_inner();
expect!(handler.acquire_litebox_lock(LockType::Write)?)
.to(match_pattern(pattern!(LockResult::Locked)));
drop(shared);
expect!(handler.acquire_litebox_lock(LockType::Write)?)
.to(match_pattern(pattern!(LockResult::Acquired(_))));
Ok(())
}
#[test]
fn exclusive_lock_blocks_shared() -> crate::Result<()> {
let handler = fresh_handler();
let exclusive = expect!(handler.acquire_litebox_lock(LockType::Write)?)
.to(match_pattern(pattern!(LockResult::Acquired(_))))
.into_inner();
expect!(handler.acquire_litebox_lock(LockType::Read)?)
.to(match_pattern(pattern!(LockResult::Locked)));
drop(exclusive);
expect!(handler.acquire_litebox_lock(LockType::Read)?)
.to(match_pattern(pattern!(LockResult::Acquired(_))));
Ok(())
}
#[test]
fn shared_locks_compose() -> crate::Result<()> {
let handler = fresh_handler();
let first = expect!(handler.acquire_litebox_lock(LockType::Read)?)
.to(match_pattern(pattern!(LockResult::Acquired(_))))
.into_inner();
let second = expect!(handler.acquire_litebox_lock(LockType::Read)?)
.to(match_pattern(pattern!(LockResult::Acquired(_))))
.into_inner();
drop(first);
drop(second);
Ok(())
}
#[test]
fn try_upgrade_succeeds_with_no_other_holder() -> crate::Result<()> {
let handler = fresh_handler();
let shared = match handler.acquire_litebox_lock(LockType::Read)? {
LockResult::Acquired(handle) => handle,
LockResult::Locked => panic!("expected to acquire shared lock"),
};
let upgraded = expect!(shared.try_upgrade()?).to(be_ok()).into_inner();
expect!(handler.acquire_litebox_lock(LockType::Read)?)
.to(match_pattern(pattern!(LockResult::Locked)));
drop(upgraded);
Ok(())
}
#[test]
fn try_upgrade_fails_when_contended() -> crate::Result<()> {
let handler = fresh_handler();
let shared_a = match handler.acquire_litebox_lock(LockType::Read)? {
LockResult::Acquired(handle) => handle,
LockResult::Locked => panic!("expected to acquire shared lock A"),
};
let shared_b = match handler.acquire_litebox_lock(LockType::Read)? {
LockResult::Acquired(handle) => handle,
LockResult::Locked => panic!("expected to acquire shared lock B"),
};
let still_shared = expect!(shared_a.try_upgrade()?).to(be_err()).into_inner();
expect!(handler.acquire_litebox_lock(LockType::Write)?)
.to(match_pattern(pattern!(LockResult::Locked)));
drop(still_shared);
drop(shared_b);
Ok(())
}
#[test]
fn connection_open_fails_when_exclusive_lock_held() -> crate::Result<()> {
let temp_dir = tempfile::tempdir().unwrap();
let path = temp_dir.path().join("test.litebox");
drop(Connection::create_new(&path, &CreateOptions::new())?);
let uuid: FilesystemId = {
let raw = rusqlite::Connection::open(&path)?;
let uuid_str: String = raw.query_row(
"SELECT value FROM liteboxfs_settings WHERE key = 'uuid'",
[],
|row| row.get(0),
)?;
uuid_str.parse().expect("expected a valid UUID")
};
let handler = LockHandler::new(uuid);
let _exclusive = match handler.acquire_litebox_lock(LockType::Write)? {
LockResult::Acquired(handle) => handle,
LockResult::Locked => panic!("expected to acquire exclusive lock"),
};
expect!(Connection::open(&path, &Default::default()))
.to(be_err())
.to(match_pattern(pattern!(Error::LiteboxLocked)));
Ok(())
}
#[test]
fn in_memory_connection_does_not_take_litebox_lock() -> crate::Result<()> {
let _a = Connection::open_in_memory(&CreateOptions::new())?;
let _b = Connection::open_in_memory(&CreateOptions::new())?;
let handler = fresh_handler();
let _exclusive = match handler.acquire_litebox_lock(LockType::Write)? {
LockResult::Acquired(handle) => handle,
LockResult::Locked => panic!("expected to acquire exclusive lock"),
};
let _c = Connection::open_in_memory(&CreateOptions::new())?;
Ok(())
}