use crate::sync::Barrier;
use std::time::*;
use anyhow::Result;
use rusqlite::TransactionState;
use self::test;
use super::*;
use crate::testing::*;
pub(crate) fn check_concurrency(
f: impl Fn() -> anyhow::Result<()> + Send + Sync + 'static,
#[allow(unused_variables)] iterations_hint: usize,
) -> anyhow::Result<()> {
#[cfg(loom)]
{
loom::model(move || f().unwrap());
Ok(())
}
#[cfg(shuttle)]
{
shuttle::check_random(move || f().unwrap(), iterations_hint);
Ok(())
}
#[cfg(all(not(loom), not(shuttle)))]
f()
}
#[test]
fn test_to_usize_io() -> Result<()> {
assert_eq!(convert_int_io::<_, u32>(u32::MAX as u64)?, u32::MAX);
if let Err(err) = convert_int_io::<_, u32>(u32::MAX as u64 + 1) {
assert_eq!(err.kind(), TO_USIZE_IO_ERROR_KIND);
assert!(err
.get_ref()
.unwrap()
.downcast_ref::<TryFromIntError>()
.is_none());
assert_eq!(err.get_ref().unwrap().to_string(), TO_USIZE_IO_ERR_PAYLOAD);
} else {
panic!("expected failure")
}
assert_eq!(
convert_int_io::<_, u64>(u32::MAX as usize)?,
u32::MAX as u64
);
Ok(())
}
#[test]
fn test_inc_array() {
let inc_and_ret = |arr: &[u8]| {
let mut arr = arr.to_vec();
if inc_big_endian_array(&mut arr[..]) {
Some(arr)
} else {
None
}
};
assert_eq!(inc_and_ret(&[0]), Some(vec![1]));
assert_eq!(inc_and_ret(&[]), None);
assert_eq!(inc_and_ret(&[0xff]), None);
assert_eq!(inc_and_ret(&[0xfe, 0xff]), Some(vec![0xff, 0]));
}
#[test]
#[cfg(not(miri))]
fn test_replace_keys() -> Result<()> {
check_concurrency(
|| {
let tempdir = test_tempdir("test_replace_keys")?;
let handle = Handle::new(tempdir.path.clone())?;
let a = "a".as_bytes().to_vec();
let b = "b".as_bytes().to_vec();
let block_size: usize = handle.block_size().try_into()?;
let a_value = readable_repeated_bytes(1, block_size);
let b_value = readable_repeated_bytes(2, block_size);
let b_read = b_value.as_slice();
handle.single_write_from(a.clone(), a_value.as_slice())?;
handle.single_write_from(b.clone(), b_read)?;
handle.single_write_from(b.clone(), b_read)?;
assert_repeated_bytes_values_eq(
handle.read_single(&a).unwrap().unwrap().new_reader(),
a_value.as_slice(),
);
let dir = handle.dir.clone();
let values_punched = handle.get_value_puncher_done();
drop(handle);
values_punched.wait();
let entries = dir.walk_dir()?;
let values_files: Vec<_> = entries
.iter()
.filter(|entry| entry.entry_type == walk::EntryType::ValuesFile)
.collect();
let mut allocated_space = 0;
for value_file in values_files {
let mut file = File::open(&value_file.path)?;
for region in seekhole::Iter::new(&mut file) {
let region = region?;
if matches!(region.region_type, seekhole::RegionType::Data) {
allocated_space += region.length();
}
}
}
assert!(
[2].map(|num_blocks| num_blocks * block_size as seekhole::RegionOffset)
.contains(&allocated_space),
"block_size={}, allocated_space={}",
block_size,
allocated_space
);
Ok(())
},
100,
)
}
#[test]
#[cfg(not(miri))]
fn punch_value_before_snapshot_cloned() -> anyhow::Result<()> {
check_concurrency(
|| {
let tempdir = test_tempdir("punch_value_before_snapshot_cloned")?;
let handle = Handle::new(tempdir.path.clone())?;
let key = "a".as_bytes().to_vec();
let first_value = readable_repeated_bytes(1, handle.block_size() as usize);
let second_value = readable_repeated_bytes(2, handle.block_size() as usize);
let reader_handle = Handle::new(tempdir.path.clone())?;
let stop = Instant::now() + Duration::from_secs(1);
while Instant::now() < stop {
handle.single_write_from(key.clone(), first_value.as_slice())?;
let write_barrier = Barrier::new(2);
thread_scope(|scope| {
let reader_scope = scope.spawn(|| -> anyhow::Result<()> {
let mut reader = reader_handle.read()?;
let value = reader.add(&key).unwrap().unwrap();
write_barrier.wait();
let snapshot = reader.begin().unwrap();
let value = snapshot.value(value);
assert_repeated_bytes_values_eq(value.new_reader(), first_value.as_slice());
Ok(())
});
write_barrier.wait();
handle
.single_write_from(key.clone(), second_value.as_slice())
.unwrap();
reader_scope.join().unwrap()
})?;
}
Ok(())
},
1,
)
}
#[test]
#[cfg(not(miri))]
fn test_torrent_storage_benchmark() -> anyhow::Result<()> {
use testing::torrent_storage::*;
BENCHMARK_OPTS.build()?.run()
}
#[test]
fn test_sqlite_update_same_value_txn_state() -> Result<()> {
let mut conn = rusqlite::Connection::open_in_memory()?;
conn.execute_batch(
r"
create table a(b);
--insert into a values (1);
",
)?;
let tx = conn.transaction()?;
assert_eq!(tx.transaction_state(None)?, TransactionState::None);
let changed = tx.execute("update a set b=1", [])?;
assert_eq!(changed, 0);
assert_eq!(tx.transaction_state(None)?, TransactionState::Write);
Ok(())
}