use std::fs;
use cvmfs::{
common::CvmfsResult, fetcher::Fetcher, file_system::CernvmFileSystem, repository::Repository,
};
use fuser::FileType;
use serial_test::serial;
const REPO_URL: &str = "http://cvmfs-stratum-one.cern.ch/opt/boss";
const CACHE_PATH: &str = "/tmp/cvmfs_test_cache";
const PINNED_REVISION: u32 = 293;
fn create_repo() -> Repository {
fs::create_dir_all(CACHE_PATH).expect("Failure creating the cache");
let fetcher = Fetcher::new(REPO_URL, CACHE_PATH, true).expect("Failure creating the fetcher");
let mut repo = Repository::new(fetcher).expect("Failure creating the repository");
repo.set_current_tag(PINNED_REVISION).expect("Failure setting pinned revision");
repo
}
#[test]
#[serial]
fn test_initialization() -> CvmfsResult<()> {
let repo = create_repo();
assert_eq!(0, repo.catalog_count());
assert_eq!("boss.cern.ch", repo.fqrn);
repo.retrieve_current_root_catalog()?;
assert_eq!(1, repo.catalog_count());
Ok(())
}
#[test]
#[serial]
fn test_revision_info() -> CvmfsResult<()> {
let repo = create_repo();
let revision = repo.get_revision_number()?;
assert!(revision > 0);
let root_hash = repo.get_root_hash()?;
assert!(!root_hash.is_empty());
let name = repo.get_name()?;
assert!(!name.is_empty());
let timestamp = repo.get_timestamp()?;
assert!(timestamp > 0);
Ok(())
}
#[test]
#[serial]
fn test_current_tag() -> CvmfsResult<()> {
let repo = create_repo();
let tag = repo.current_tag()?;
assert!(tag.revision > 0);
assert!(!tag.hash.is_empty());
assert!(tag.timestamp > 0);
Ok(())
}
#[test]
#[serial]
fn test_has_history() {
let repo = create_repo();
assert!(repo.has_history());
}
#[test]
#[serial]
fn test_retrieve_history() -> CvmfsResult<()> {
let repo = create_repo();
let history = repo.retrieve_history()?;
assert_eq!(history.schema, "1.0");
assert_eq!(history.fqrn, "boss.cern.ch");
Ok(())
}
#[test]
#[serial]
fn test_get_tag_by_revision() -> CvmfsResult<()> {
let repo = create_repo();
let current_rev = repo.get_revision_number()? as u32;
let tag = repo.get_tag(current_rev)?;
assert_eq!(tag.revision, current_rev as i32);
assert!(!tag.hash.is_empty());
Ok(())
}
#[test]
#[serial]
fn test_get_last_tag() -> CvmfsResult<()> {
let repo = create_repo();
let tag = repo.get_last_tag()?;
assert!(tag.revision > 0);
assert!(!tag.hash.is_empty());
Ok(())
}
#[test]
#[serial]
fn test_set_current_tag() -> CvmfsResult<()> {
let mut repo = create_repo();
let current_rev = repo.get_revision_number()? as u32;
repo.set_current_tag(current_rev)?;
let tag = repo.current_tag()?;
assert_eq!(tag.revision, current_rev as i32);
Ok(())
}
#[test]
#[serial]
fn test_history_get_tag_by_name() -> CvmfsResult<()> {
let repo = create_repo();
let history = repo.retrieve_history()?;
let tag = history.get_tag_by_name("trunk")?;
assert!(tag.is_some());
let tag = tag.unwrap();
assert_eq!(tag.name, "trunk");
Ok(())
}
#[test]
#[serial]
fn test_history_get_tag_by_revision() -> CvmfsResult<()> {
let repo = create_repo();
let history = repo.retrieve_history()?;
let current_rev = repo.current_tag()?.revision as u32;
let tag = history.get_tag_by_revision(current_rev)?;
assert!(tag.is_some());
assert_eq!(tag.unwrap().revision, current_rev as i32);
Ok(())
}
#[test]
#[serial]
fn test_history_get_tag_by_date() -> CvmfsResult<()> {
let repo = create_repo();
let history = repo.retrieve_history()?;
let tag = history.get_tag_by_date(0)?;
assert!(tag.is_some());
Ok(())
}
#[test]
#[serial]
fn test_history_get_nonexistent_name() -> CvmfsResult<()> {
let repo = create_repo();
let history = repo.retrieve_history()?;
let tag = history.get_tag_by_name("this_tag_does_not_exist_at_all")?;
assert!(tag.is_none());
Ok(())
}
#[test]
#[serial]
fn test_history_get_nonexistent_revision() -> CvmfsResult<()> {
let repo = create_repo();
let history = repo.retrieve_history()?;
let tag = history.get_tag_by_revision(999_999_999)?;
assert!(tag.is_none());
Ok(())
}
#[test]
#[serial]
fn test_retrieve_root_catalog() -> CvmfsResult<()> {
let repo = create_repo();
repo.retrieve_current_root_catalog()?;
let hash = repo.get_root_hash()?.to_string();
repo.with_catalog(&hash, |catalog| {
assert!(catalog.is_root());
assert!(catalog.revision > 0);
assert!(catalog.schema > 0.0);
Ok(())
})
}
#[test]
#[serial]
fn test_catalog_caching() -> CvmfsResult<()> {
let repo = create_repo();
let hash = repo.get_root_hash()?.to_string();
repo.with_catalog(&hash, |_| Ok(()))?;
assert_eq!(1, repo.catalog_count());
repo.with_catalog(&hash, |_| Ok(()))?;
assert_eq!(1, repo.catalog_count());
Ok(())
}
#[test]
#[serial]
fn test_catalog_has_nested() -> CvmfsResult<()> {
let repo = create_repo();
repo.retrieve_current_root_catalog()?;
let hash = repo.get_root_hash()?.to_string();
repo.with_catalog(&hash, |catalog| {
assert!(catalog.has_nested()?);
let count = catalog.nested_count()?;
assert!(count > 0);
Ok(())
})
}
#[test]
#[serial]
fn test_catalog_list_nested() -> CvmfsResult<()> {
let repo = create_repo();
repo.retrieve_current_root_catalog()?;
let hash = repo.get_root_hash()?.to_string();
repo.with_catalog(&hash, |catalog| {
let nested = catalog.list_nested()?;
assert!(!nested.is_empty());
for nested_ref in &nested {
assert!(!nested_ref.root_path.is_empty());
assert!(!nested_ref.catalog_hash.is_empty());
}
Ok(())
})
}
#[test]
#[serial]
fn test_retrieve_catalog_for_path() -> CvmfsResult<()> {
let repo = create_repo();
let root_hash = repo.retrieve_catalog_for_path("")?;
repo.with_catalog(&root_hash, |catalog| {
assert!(catalog.is_root());
Ok(())
})?;
let nested_hash = repo.retrieve_catalog_for_path("/slc4_ia32_gcc34")?;
repo.with_catalog(&nested_hash, |catalog| {
assert!(!catalog.is_root());
Ok(())
})
}
#[test]
#[serial]
fn test_statistics() -> CvmfsResult<()> {
let repo = create_repo();
repo.retrieve_current_root_catalog()?;
let stats = repo.get_statistics()?;
assert!(stats.dir > 0);
assert!(stats.regular > 0);
assert!(stats.file_size > 0);
Ok(())
}
#[test]
#[serial]
fn test_list_root_directory() -> CvmfsResult<()> {
let repo = create_repo();
let entries = repo.list_directory("/")?;
assert!(!entries.is_empty());
let names: Vec<&str> = entries.iter().map(|e| e.name.as_str()).collect();
assert!(names.contains(&"testfile"));
assert!(names.contains(&"database"));
assert!(names.contains(&"pacman-3.29"));
Ok(())
}
#[test]
#[serial]
fn test_list_subdirectory() -> CvmfsResult<()> {
let repo = create_repo();
let entries = repo.list_directory("/database")?;
assert!(!entries.is_empty());
let names: Vec<&str> = entries.iter().map(|e| e.name.as_str()).collect();
assert!(names.contains(&"offlinedb.db"));
assert!(names.contains(&"run.db"));
Ok(())
}
#[test]
#[serial]
fn test_list_nested_catalog_directory() -> CvmfsResult<()> {
let repo = create_repo();
let entries = repo.list_directory("/slc4_ia32_gcc34")?;
assert!(!entries.is_empty());
Ok(())
}
#[test]
#[serial]
fn test_list_nonexistent_directory() {
let repo = create_repo();
let result = repo.list_directory("/nonexistent_path_xyz").unwrap();
assert!(result.is_empty());
}
#[test]
#[serial]
fn test_lookup_root() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup("/")?;
assert!(entry.is_directory());
Ok(())
}
#[test]
#[serial]
fn test_lookup_file() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup("/testfile")?;
assert!(entry.is_file());
assert_eq!(entry.name, "testfile");
assert_eq!(entry.size, 50);
Ok(())
}
#[test]
#[serial]
fn test_lookup_directory() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup("/database")?;
assert!(entry.is_directory());
assert_eq!(entry.name, "database");
Ok(())
}
#[test]
#[serial]
fn test_lookup_symlink() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup("/pacman-3.29/setup.csh")?;
assert!(entry.is_symlink());
assert_eq!(entry.symlink.as_deref(), Some("scripts/initialize_setup.csh"));
Ok(())
}
#[test]
#[serial]
fn test_lookup_nested_catalog_entry() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup("/slc4_ia32_gcc34")?;
assert!(entry.is_directory());
assert!(entry.is_nested_catalog_mountpoint() || entry.is_nested_catalog_root());
Ok(())
}
#[test]
#[serial]
fn test_lookup_nonexistent() {
let repo = create_repo();
let result = repo.lookup("/this/does/not/exist");
assert!(result.is_err());
}
#[test]
#[serial]
fn test_get_file_regular() -> CvmfsResult<()> {
let repo = create_repo();
let file = repo.get_file("/testfile")?;
let mut buf = vec![0u8; file.file_size() as usize];
let n = file.read_at(0, &mut buf)?;
let contents = String::from_utf8_lossy(&buf[..n]);
assert_eq!(n, 50);
assert!(contents.contains("slc4_ia32_gcc34"));
Ok(())
}
#[test]
#[serial]
fn test_get_file_binary() -> CvmfsResult<()> {
let repo = create_repo();
let file = repo.get_file("/pacman-latest.tar.gz")?;
let mut header = [0u8; 2];
file.read_at(0, &mut header)?;
assert_eq!(header, [0x1f, 0x8b]); Ok(())
}
#[test]
#[serial]
fn test_get_file_not_a_file() {
let repo = create_repo();
let result = repo.get_file("/database");
assert!(result.is_err());
}
#[test]
#[serial]
fn test_get_file_nonexistent() {
let repo = create_repo();
let result = repo.get_file("/nonexistent_file");
assert!(result.is_err());
}
#[test]
#[serial]
fn test_chunked_file_read_first_bytes() -> CvmfsResult<()> {
let repo = create_repo();
let file = repo.get_file("/database/offlinedb.db")?;
let mut header = [0u8; 16];
file.read_at(0, &mut header)?;
assert_eq!(&header[..6], b"SQLite");
Ok(())
}
#[test]
#[serial]
fn test_chunked_file_seek_and_read() -> CvmfsResult<()> {
let repo = create_repo();
let file = repo.get_file("/database/offlinedb.db")?;
let mut buf1 = [0u8; 64];
file.read_at(0, &mut buf1)?;
let mut buf2 = [0u8; 64];
file.read_at(0, &mut buf2)?;
assert_eq!(buf1, buf2);
Ok(())
}
#[test]
#[serial]
fn test_chunked_file_cross_chunk_read() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup_with_chunks("/database/offlinedb.db")?;
assert!(entry.has_chunks());
assert!(!entry.chunks.is_empty());
let first_chunk_size = entry.chunks[0].size as u64;
let file = repo.get_file("/database/offlinedb.db")?;
let offset = first_chunk_size - 32;
let mut buf = [0u8; 64];
let n = file.read_at(offset, &mut buf)?;
assert_eq!(n, 64);
Ok(())
}
#[test]
#[serial]
fn test_fetcher_new_http() -> CvmfsResult<()> {
let fetcher = Fetcher::new(REPO_URL, CACHE_PATH, true)?;
assert!(fetcher.source.starts_with("http"));
Ok(())
}
#[test]
#[serial]
fn test_fetcher_new_local_dir() -> CvmfsResult<()> {
let tmp = format!("/tmp/cvmfs_test_fetcher_local_{}", std::process::id());
fs::create_dir_all(&tmp)?;
let fetcher = Fetcher::new(&tmp, CACHE_PATH, true)?;
assert!(fetcher.source.starts_with("file://"));
fs::remove_dir_all(&tmp).ok();
Ok(())
}
#[test]
#[serial]
fn test_fetcher_retrieve_raw_file() -> CvmfsResult<()> {
let fetcher = Fetcher::new(REPO_URL, CACHE_PATH, true)?;
let path = fetcher.retrieve_raw_file(".cvmfspublished")?;
assert!(fs::metadata(&path).is_ok());
let content = fs::read(&path)?;
assert!(!content.is_empty());
let text = String::from_utf8_lossy(&content);
assert!(text.contains("boss.cern.ch"));
Ok(())
}
#[test]
#[serial]
fn test_fetcher_retrieve_file_cached() -> CvmfsResult<()> {
let fetcher = Fetcher::new(REPO_URL, CACHE_PATH, true)?;
let path1 = fetcher.retrieve_file("data/08/ade4fa3cf6beb5023967c23f6e1f7d8b7754ec")?;
let path2 = fetcher.retrieve_file("data/08/ade4fa3cf6beb5023967c23f6e1f7d8b7754ec")?;
assert_eq!(path1, path2);
Ok(())
}
#[test]
#[serial]
fn test_directory_entry_file_attributes() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup("/testfile")?;
assert!(entry.is_file());
assert!(!entry.is_directory());
assert!(!entry.is_symlink());
assert!(!entry.has_chunks());
assert!(entry.content_hash.is_some());
assert!(entry.content_hash_string().is_some());
assert!(entry.mode > 0);
assert!(entry.mtime > 0);
assert_eq!(entry.uid, 313);
assert_eq!(entry.gid, 313);
assert!(entry.nlink() > 0);
Ok(())
}
#[test]
#[serial]
fn test_directory_entry_chunked_attributes() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup_with_chunks("/database/offlinedb.db")?;
assert!(entry.is_file());
assert!(entry.has_chunks());
assert!(!entry.chunks.is_empty());
assert!(entry.content_hash.is_none());
assert!(entry.content_hash_string().is_none());
for chunk in &entry.chunks {
assert!(!chunk.content_hash.is_empty());
assert!(chunk.size > 0);
}
Ok(())
}
#[test]
#[serial]
fn test_directory_entry_symlink_attributes() -> CvmfsResult<()> {
let repo = create_repo();
let entry = repo.lookup("/pacman-3.29/setup.csh")?;
assert!(entry.is_symlink());
assert!(!entry.is_directory());
assert!(entry.symlink.is_some());
Ok(())
}
#[test]
#[serial]
fn test_catalog_find_directory_entry() -> CvmfsResult<()> {
let repo = create_repo();
repo.retrieve_current_root_catalog()?;
let hash = repo.get_root_hash()?.to_string();
repo.with_catalog(&hash, |catalog| {
let entry = catalog.find_directory_entry("")?;
assert!(entry.is_directory());
Ok(())
})
}
#[test]
#[serial]
fn test_catalog_list_directory() -> CvmfsResult<()> {
let repo = create_repo();
repo.retrieve_current_root_catalog()?;
let hash = repo.get_root_hash()?.to_string();
repo.with_catalog(&hash, |catalog| {
let entries = catalog.list_directory("/")?;
assert!(!entries.is_empty());
Ok(())
})
}
#[test]
#[serial]
fn test_catalog_statistics() -> CvmfsResult<()> {
let repo = create_repo();
repo.retrieve_current_root_catalog()?;
let hash = repo.get_root_hash()?.to_string();
repo.with_catalog(&hash, |catalog| {
let stats = catalog.get_statistics()?;
assert!(stats.dir > 0);
assert!(stats.regular > 0);
assert!(stats.file_size > 0);
Ok(())
})
}
#[test]
#[serial]
fn test_certificate_verification_runs_on_init() {
let repo = create_repo();
assert!(!repo.fqrn.is_empty());
}
fn create_fs() -> CernvmFileSystem {
let repo = create_repo();
CernvmFileSystem::new(repo).expect("Failure creating the filesystem")
}
#[test]
#[serial]
fn test_fs_getattr_root() {
let fs = create_fs();
let (_, attr) = fs.do_lookup("/").unwrap();
assert_eq!(attr.kind, FileType::Directory);
assert!(attr.nlink >= 2);
}
#[test]
#[serial]
fn test_fs_getattr_file() {
let fs = create_fs();
let (_, attr) = fs.do_lookup("/testfile").unwrap();
assert_eq!(attr.kind, FileType::RegularFile);
assert_eq!(attr.size, 50);
assert!(attr.nlink > 0);
assert_eq!(attr.uid, 313);
assert_eq!(attr.gid, 313);
assert_eq!(attr.flags, 0);
assert_eq!(attr.rdev, 0);
}
#[test]
#[serial]
fn test_fs_getattr_directory() {
let fs = create_fs();
let (_, attr) = fs.do_lookup("/database").unwrap();
assert_eq!(attr.kind, FileType::Directory);
assert_eq!(attr.nlink, 2);
}
#[test]
#[serial]
fn test_fs_getattr_nonexistent() {
let fs = create_fs();
let result = fs.do_lookup("/nonexistent");
assert!(result.is_err());
}
#[test]
#[serial]
fn test_fs_getattr_symlink() {
let fs = create_fs();
let (_, attr) = fs.do_lookup("/pacman-3.29/setup.csh").unwrap();
assert_eq!(attr.kind, FileType::Symlink);
}
#[test]
#[serial]
fn test_fs_readlink() {
let fs = create_fs();
let target = fs.do_readlink("/pacman-3.29/setup.csh").unwrap();
assert_eq!(target, "scripts/initialize_setup.csh");
}
#[test]
#[serial]
fn test_fs_readlink_not_a_link() {
let fs = create_fs();
let result = fs.do_readlink("/testfile");
assert!(result.is_err());
}
#[test]
#[serial]
fn test_fs_open_file() {
let fs = create_fs();
let fh = fs.do_open("/testfile").unwrap();
assert!(fh > 0);
}
#[test]
#[serial]
fn test_fs_open_directory_fails() {
let fs = create_fs();
let result = fs.do_open("/database");
assert!(result.is_err());
}
#[test]
#[serial]
fn test_fs_release() {
let fs = create_fs();
let fh = fs.do_open("/testfile").unwrap();
let result = fs.do_release(fh);
assert!(result.is_ok());
}
#[test]
#[serial]
fn test_fs_release_nonexistent() {
let fs = create_fs();
let result = fs.do_release(999999);
assert!(result.is_err());
}
#[test]
#[serial]
fn test_fs_readdir() {
let fs = create_fs();
let entries = fs.do_readdir("/").unwrap();
assert!(!entries.is_empty());
let names: Vec<&str> = entries.iter().map(|(_, name)| name.as_str()).collect();
assert!(names.contains(&"testfile"));
assert!(names.contains(&"database"));
}
#[test]
#[serial]
fn test_fs_readdir_not_a_dir() {
let fs = create_fs();
let result = fs.do_readdir("/testfile");
assert!(result.is_err());
}
#[test]
#[serial]
fn test_fs_statfs() {
let fs = create_fs();
let (blocks, files) = fs.do_statfs().unwrap();
assert!(blocks > 0);
assert!(files > 0);
}
#[test]
#[serial]
fn test_fs_getxattr() {
let fs = create_fs();
let result = fs.do_getxattr("user.test");
assert!(result.is_err());
}
#[test]
#[serial]
fn test_fs_access_exists() {
let fs = create_fs();
let result = fs.do_access("/testfile");
assert!(result.is_ok());
}
#[test]
#[serial]
fn test_fs_access_nonexistent() {
let fs = create_fs();
let result = fs.do_access("/nonexistent");
assert!(result.is_err());
}