use std::fs::File;
use std::fs::{self};
use std::io::Read;
use std::io::Write;
use std::io::{self};
use std::path::Path;
use std::path::PathBuf;
use filetime::set_file_times;
use filetime::FileTime;
use tempfile::TempDir;
use ritecache::DiskCacheError;
use ritecache::LruDiskCache;
struct TestFixture {
pub tempdir: TempDir,
}
fn create_file<T: AsRef<Path>, F: FnOnce(File) -> io::Result<()>>(
dir: &Path,
path: T,
fill_contents: F,
) -> io::Result<PathBuf> {
let b = dir.join(path);
fs::create_dir_all(b.parent().unwrap())?;
let f = fs::File::create(&b)?;
fill_contents(f)?;
b.canonicalize()
}
fn set_mtime_back<T: AsRef<Path>>(path: T, seconds: usize) {
let m = fs::metadata(path.as_ref()).unwrap();
let t = FileTime::from_last_modification_time(&m);
let t = FileTime::from_unix_time(t.unix_seconds() - seconds as i64, t.nanoseconds());
set_file_times(path, t, t).unwrap();
}
fn read_all<R: Read>(r: &mut R) -> io::Result<Vec<u8>> {
let mut v = vec![];
r.read_to_end(&mut v)?;
Ok(v)
}
impl TestFixture {
pub fn new() -> TestFixture {
TestFixture {
tempdir: tempfile::Builder::new().prefix("lru-disk-cache-test").tempdir().unwrap(),
}
}
pub fn tmp(&self) -> &Path {
self.tempdir.path()
}
pub fn create_file<T: AsRef<Path>>(&self, path: T, size: usize) -> PathBuf {
create_file(self.tempdir.path(), path, |mut f| f.write_all(&vec![0; size])).unwrap()
}
}
#[test]
fn test_empty_dir() {
let f = TestFixture::new();
LruDiskCache::new(f.tmp(), 1024).unwrap();
}
#[test]
fn test_missing_root() {
let f = TestFixture::new();
LruDiskCache::new(f.tmp().join("not-here"), 1024).unwrap();
}
#[test]
fn test_some_existing_files() {
let f = TestFixture::new();
f.create_file("file1", 10);
f.create_file("file2", 10);
let c = LruDiskCache::new(f.tmp(), 20).unwrap();
assert_eq!(c.size(), 20);
assert_eq!(c.len(), 2);
}
#[test]
fn test_existing_file_too_large() {
let f = TestFixture::new();
set_mtime_back(f.create_file("file1", 10), 10);
set_mtime_back(f.create_file("file2", 10), 5);
let c = LruDiskCache::new(f.tmp(), 15).unwrap();
assert_eq!(c.size(), 10);
assert_eq!(c.len(), 1);
assert!(!c.contains_key("file1"));
assert!(c.contains_key("file2"));
}
#[test]
fn test_existing_files_lru_mtime() {
let f = TestFixture::new();
set_mtime_back(f.create_file("file1", 10), 5);
set_mtime_back(f.create_file("file2", 10), 10);
let mut c = LruDiskCache::new(f.tmp(), 25).unwrap();
assert_eq!(c.size(), 20);
c.insert_bytes("file3", &[0; 10]).unwrap();
assert_eq!(c.size(), 20);
assert!(!c.contains_key("file2"));
assert!(c.contains_key("file1"));
}
#[test]
fn test_insert_bytes() {
let f = TestFixture::new();
let mut c = LruDiskCache::new(f.tmp(), 25).unwrap();
c.insert_bytes("a/b/c", &[0; 10]).unwrap();
assert!(c.contains_key("a/b/c"));
c.insert_bytes("a/b/d", &[0; 10]).unwrap();
assert_eq!(c.size(), 20);
c.insert_bytes("x/y/z", &[0; 10]).unwrap();
assert_eq!(c.size(), 20);
assert!(!c.contains_key("a/b/c"));
assert!(!f.tmp().join("a/b/c").exists());
}
#[test]
fn test_insert_bytes_exact() {
let f = TestFixture::new();
let mut c = LruDiskCache::new(f.tmp(), 20).unwrap();
c.insert_bytes("file1", &[1; 10]).unwrap();
c.insert_bytes("file2", &[2; 10]).unwrap();
assert_eq!(c.size(), 20);
c.insert_bytes("file3", &[3; 10]).unwrap();
assert_eq!(c.size(), 20);
assert!(!c.contains_key("file1"));
}
#[test]
fn test_add_get_lru() {
let f = TestFixture::new();
{
let mut c = LruDiskCache::new(f.tmp(), 25).unwrap();
c.insert_bytes("file1", &[1; 10]).unwrap();
c.insert_bytes("file2", &[2; 10]).unwrap();
assert_eq!(read_all(&mut c.get("file1").unwrap()).unwrap(), vec![1u8; 10]);
c.insert_bytes("file3", &[3; 10]).unwrap();
assert_eq!(c.size(), 20);
assert!(!c.contains_key("file2"));
}
set_mtime_back(f.tmp().join("file1"), 5);
set_mtime_back(f.tmp().join("file3"), 5);
{
let mut c = LruDiskCache::new(f.tmp(), 25).unwrap();
c.get("file1").unwrap();
}
{
let mut c = LruDiskCache::new(f.tmp(), 25).unwrap();
assert!(c.contains_key("file1"));
assert!(c.contains_key("file3"));
assert_eq!(c.size(), 20);
c.insert_bytes("file4", &[4; 10]).unwrap();
assert_eq!(c.size(), 20);
assert!(!c.contains_key("file3"));
assert!(c.contains_key("file1"));
}
}
#[test]
fn test_insert_bytes_too_large() {
let f = TestFixture::new();
let mut c = LruDiskCache::new(f.tmp(), 1).unwrap();
match c.insert_bytes("a/b/c", &[0; 2]) {
Err(DiskCacheError::FileTooLarge) => {}
x => panic!("Unexpected result: {:?}", x),
}
}
#[test]
fn test_insert_file() {
let f = TestFixture::new();
let p1 = f.create_file("file1", 10);
let p2 = f.create_file("file2", 10);
let p3 = f.create_file("file3", 10);
let mut c = LruDiskCache::new(f.tmp().join("cache"), 25).unwrap();
c.insert_file("file1", &p1).unwrap();
assert_eq!(c.len(), 1);
c.insert_file("file2", &p2).unwrap();
assert_eq!(c.len(), 2);
assert_eq!(read_all(&mut c.get("file1").unwrap()).unwrap(), vec![0u8; 10]);
c.insert_file("file3", &p3).unwrap();
assert_eq!(c.len(), 2);
assert_eq!(c.size(), 20);
assert!(!c.contains_key("file2"));
assert!(!p1.exists());
assert!(!p2.exists());
assert!(!p3.exists());
}
#[test]
fn test_remove() {
let f = TestFixture::new();
let p1 = f.create_file("file1", 10);
let p2 = f.create_file("file2", 10);
let p3 = f.create_file("file3", 10);
let mut c = LruDiskCache::new(f.tmp().join("cache"), 25).unwrap();
c.insert_file("file1", &p1).unwrap();
c.insert_file("file2", &p2).unwrap();
c.remove("file1").unwrap();
c.insert_file("file3", &p3).unwrap();
assert_eq!(c.len(), 2);
assert_eq!(c.size(), 20);
assert!(!c.contains_key("file1"));
assert!(!f.tmp().join("cache").join("file1").exists());
assert!(f.tmp().join("cache").join("file2").exists());
assert!(f.tmp().join("cache").join("file3").exists());
assert!(!p1.exists());
assert!(!p2.exists());
assert!(!p3.exists());
let p4 = f.create_file("file1", 10);
c.insert_file("file1", &p4).unwrap();
assert_eq!(c.len(), 2);
assert!(c.contains_key("file1"));
assert!(!c.contains_key("file2"));
assert!(!f.tmp().join("cache").join("file2").exists());
assert!(!p4.exists());
}