use noxu_log::{
FileManager, LogFileReader, LogManager, entry_type::LogEntryType,
provisional::Provisional,
};
use std::sync::Arc;
use tempfile::TempDir;
fn make_managers(dir: &TempDir) -> (Arc<FileManager>, LogManager) {
let fm =
Arc::new(FileManager::new(dir.path(), false, 10_000_000, 100).unwrap());
let lm = LogManager::new(Arc::clone(&fm), 3, 1_048_576, 4096);
(fm, lm)
}
fn make_managers_small_file(
dir: &TempDir,
max_file_size: u64,
) -> (Arc<FileManager>, LogManager) {
let fm = Arc::new(
FileManager::new(dir.path(), false, max_file_size, 100).unwrap(),
);
let lm = LogManager::new(Arc::clone(&fm), 3, 2_097_152, 4096);
(fm, lm)
}
#[test]
fn test_write_and_read_entry() {
let dir = TempDir::new().unwrap();
let (fm, lm) = make_managers(&dir);
let payload = b"noxu write-and-read test";
let lsn = lm
.log(LogEntryType::Trace, payload, Provisional::No, true, false)
.unwrap();
let (entry_type, read_payload) = lm.read_entry(lsn).unwrap();
assert_eq!(entry_type, LogEntryType::Trace);
assert_eq!(read_payload.as_slice(), payload);
drop(fm); }
#[test]
fn test_file_flip() {
let dir = TempDir::new().unwrap();
let max_size: u64 = 10_240;
let (fm, lm) = make_managers_small_file(&dir, max_size);
let payload = vec![0xABu8; 4096];
for _ in 0..4 {
lm.log(LogEntryType::Trace, &payload, Provisional::No, true, false)
.unwrap();
}
let files = fm.list_file_numbers().unwrap();
assert!(
files.len() >= 2,
"expected at least 2 log files, found {:?}",
files
);
}
#[test]
fn test_crc_validation_on_read() {
use std::fs::OpenOptions;
use std::io::{Seek, SeekFrom, Write};
let dir = TempDir::new().unwrap();
let (fm, lm) = make_managers(&dir);
let payload = b"data to corrupt";
let lsn = lm
.log(LogEntryType::Trace, payload, Provisional::No, true, false)
.unwrap();
lm.flush_sync().unwrap();
fm.clear_cache();
let payload_byte_offset = lsn.file_offset() as u64
+ noxu_log::entry_header::MIN_HEADER_SIZE as u64;
let file_path = dir.path().join(format!("{:08x}.ndb", lsn.file_number()));
{
let mut f = OpenOptions::new().write(true).open(&file_path).unwrap();
f.seek(SeekFrom::Start(payload_byte_offset)).unwrap();
let original = payload[0];
f.write_all(&[original ^ 0xFF]).unwrap();
f.flush().unwrap();
}
fm.clear_cache();
let mut reader =
LogFileReader::open(Arc::clone(&fm), lsn.file_number()).unwrap();
let result = reader.read_next_strict();
assert!(
result.is_err(),
"Expected a checksum error but got {:?}",
result.ok()
);
match result.unwrap_err() {
noxu_log::NoxuLogError::Checksum { .. } => {} other => panic!("Expected Checksum error, got {:?}", other),
}
}
#[test]
fn test_sequential_read() {
let dir = TempDir::new().unwrap();
let (fm, lm) = make_managers(&dir);
let n_entries = 10usize;
let mut lsns = Vec::with_capacity(n_entries);
for i in 0..n_entries {
let payload = format!("entry-{}", i);
let lsn = lm
.log(
LogEntryType::Trace,
payload.as_bytes(),
Provisional::No,
false,
false,
)
.unwrap();
lsns.push(lsn);
}
lm.flush_no_sync().unwrap();
let file_num = lsns[0].file_number();
let mut reader = LogFileReader::open(Arc::clone(&fm), file_num).unwrap();
let mut count = 0usize;
while let Some((_lsn, entry_type, _payload)) = reader.read_next() {
assert_eq!(entry_type, LogEntryType::Trace);
count += 1;
}
assert_eq!(
count, n_entries,
"expected {} entries, LogFileReader found {}",
n_entries, count
);
}
#[test]
fn test_sync_flushes_to_disk() {
let dir = TempDir::new().unwrap();
let (fm, lm) = make_managers(&dir);
let payload = b"durable data";
let lsn = lm
.log(LogEntryType::Trace, payload, Provisional::No, false, false)
.unwrap();
lm.flush_sync().unwrap();
fm.clear_cache();
drop(lm);
let mut reader =
LogFileReader::open(Arc::clone(&fm), lsn.file_number()).unwrap();
let result = reader.read_next();
assert!(result.is_some(), "entry must be on disk after sync");
let (_read_lsn, entry_type, read_payload) = result.unwrap();
assert_eq!(entry_type, LogEntryType::Trace);
assert_eq!(read_payload.as_slice(), payload);
}