From 236b4e3e42133966de467be4f6f12e7965c5eff3 Mon Sep 17 00:00:00 2001 From: "weizhen.zt" Date: Tue, 7 Nov 2023 16:55:51 +0800 Subject: [PATCH 1/7] utils: add some unit test cases Signed-off-by: weizhen.zt --- utils/src/compact.rs | 16 ++ utils/src/compress/lz4_standard.rs | 13 ++ utils/src/compress/mod.rs | 89 ++++++++++- utils/src/compress/zlib_random.rs | 14 ++ utils/src/crypt.rs | 118 ++++++++++++++ utils/src/digest.rs | 50 ++++++ utils/src/filemap.rs | 30 ++++ utils/src/lib.rs | 7 + utils/src/metrics.rs | 240 +++++++++++++++++++++++++++++ utils/src/mpmc.rs | 13 ++ utils/src/trace.rs | 36 +++++ utils/src/verity.rs | 23 +++ 12 files changed, 648 insertions(+), 1 deletion(-) diff --git a/utils/src/compact.rs b/utils/src/compact.rs index c0cca48415d..d6c276bd4f9 100644 --- a/utils/src/compact.rs +++ b/utils/src/compact.rs @@ -38,3 +38,19 @@ pub fn minor_dev(dev: u64) -> u64 { dev & 0xffffff } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + #[cfg(target_os = "linux")] + fn test_dev() { + let major: u64 = 0xffff_ffff_ffff_abcd; + let minor: u64 = 0xffff_ffff_abcd_ffff; + let dev = nix::sys::stat::makedev(major, minor); + assert_eq!(major_dev(dev), 0xffff_abcd); + assert_eq!(minor_dev(dev), 0xabcd_ffff); + } +} diff --git a/utils/src/compress/lz4_standard.rs b/utils/src/compress/lz4_standard.rs index 7cd30ab5e67..723962f22a3 100644 --- a/utils/src/compress/lz4_standard.rs +++ b/utils/src/compress/lz4_standard.rs @@ -58,3 +58,16 @@ pub(super) fn lz4_decompress(src: &[u8], dst: &mut [u8]) -> Result { Ok(dec_bytes as usize) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_input() { + let mut big_buf = vec![0x0u8; u32::MAX as usize]; + let mock_comperessed = vec![0x0u8; 32]; + assert!(lz4_compress(&big_buf).is_err()); + assert!(lz4_decompress(&mock_comperessed, big_buf.as_mut_slice()).is_err()); + } +} diff --git a/utils/src/compress/mod.rs b/utils/src/compress/mod.rs index fd19ca811f7..1570054681e 100644 --- a/utils/src/compress/mod.rs +++ b/utils/src/compress/mod.rs @@ -282,9 +282,38 @@ mod tests { 0x1u8, 0x2u8, 0x3u8, 0x4u8, 0x1u8, 0x2u8, 0x3u8, 0x4u8, 0x1u8, 0x2u8, 0x3u8, 0x4u8, 0x1u8, 0x2u8, 0x3u8, 0x4u8, ]; + let mut dst = [0x0u8; 16]; let (compressed, _) = compress(&buf, Algorithm::None).unwrap(); - assert_eq!(buf.to_vec(), compressed.to_vec()); + let _len = decompress(&buf, &mut dst, Algorithm::None).unwrap(); + assert_eq!(dst.to_vec(), compressed.to_vec()); + } + + #[test] + fn test_compress_algorithm_ztsd() { + let buf = vec![0x2u8; 4097]; + let mut decompressed = vec![0; buf.len()]; + let (compressed, _) = compress(&buf, Algorithm::Zstd).unwrap(); + let sz = decompress(&compressed, decompressed.as_mut_slice(), Algorithm::Zstd).unwrap(); + assert_eq!(sz, 4097); + assert_eq!(buf, decompressed); + } + + #[test] + fn test_compress_algorithm_lz4() { + let buf = [ + 0x1u8, 0x2u8, 0x3u8, 0x4u8, 0x1u8, 0x2u8, 0x3u8, 0x4u8, 0x1u8, 0x2u8, 0x3u8, 0x4u8, + 0x1u8, 0x2u8, 0x3u8, 0x4u8, + ]; + let mut decompressed = vec![0; buf.len()]; + let (compressed, _) = compress(&buf, Algorithm::Lz4Block).unwrap(); + let _len = decompress( + &compressed, + decompressed.as_mut_slice(), + Algorithm::Lz4Block, + ) + .unwrap(); + assert_eq!(decompressed.to_vec(), buf.to_vec()); } #[test] @@ -505,5 +534,63 @@ mod tests { assert_eq!(&String::from_utf8_lossy(&buf[0..6]), "zlib.\n"); let ret = decoder.read(&mut buf).unwrap(); assert_eq!(ret, 0); + print!( + "{:?}, {:?}, {:?}, {:?},", + Algorithm::GZip, + Algorithm::Lz4Block, + Algorithm::Zstd, + Algorithm::None + ) + } + + #[test] + fn test_algorithm_from() { + assert_eq!(Algorithm::from_str("none").unwrap(), Algorithm::None); + assert_eq!( + Algorithm::from_str("lz4_block").unwrap(), + Algorithm::Lz4Block + ); + assert_eq!(Algorithm::from_str("gzip").unwrap(), Algorithm::GZip); + assert_eq!(Algorithm::from_str("zstd").unwrap(), Algorithm::Zstd); + assert!(Algorithm::from_str("foo").is_err()); + assert_eq!( + Algorithm::try_from(Algorithm::None as u32).unwrap(), + Algorithm::None + ); + assert_eq!( + Algorithm::try_from(Algorithm::Lz4Block as u32).unwrap(), + Algorithm::Lz4Block + ); + assert_eq!( + Algorithm::try_from(Algorithm::GZip as u32).unwrap(), + Algorithm::GZip + ); + assert_eq!( + Algorithm::try_from(Algorithm::Zstd as u32).unwrap(), + Algorithm::Zstd + ); + assert!(Algorithm::try_from(u32::MAX).is_err()); + + assert_eq!( + Algorithm::try_from(Algorithm::None as u64).unwrap(), + Algorithm::None + ); + assert_eq!( + Algorithm::try_from(Algorithm::Lz4Block as u64).unwrap(), + Algorithm::Lz4Block + ); + assert_eq!( + Algorithm::try_from(Algorithm::GZip as u64).unwrap(), + Algorithm::GZip + ); + assert_eq!( + Algorithm::try_from(Algorithm::Zstd as u64).unwrap(), + Algorithm::Zstd + ); + assert!(Algorithm::try_from(u64::MAX).is_err()); + assert!(Algorithm::None.is_none()); + assert!(!Algorithm::Lz4Block.is_none()); + assert!(!Algorithm::GZip.is_none()); + assert!(!Algorithm::Zstd.is_none()); } } diff --git a/utils/src/compress/zlib_random.rs b/utils/src/compress/zlib_random.rs index 2f8091ccf21..59d9e3c1646 100644 --- a/utils/src/compress/zlib_random.rs +++ b/utils/src/compress/zlib_random.rs @@ -887,4 +887,18 @@ mod tests { decoder.uncompress(ctx, None, &c_buf, &mut d_buf).unwrap(); } } + + #[test] + fn test_zran_reader() { + let root_dir = &std::env::var("CARGO_MANIFEST_DIR").expect("$CARGO_MANIFEST_DIR"); + let path = PathBuf::from(root_dir).join("../tests/texture/zran/zran-two-streams.tar.gz"); + let file = OpenOptions::new().read(true).open(&path).unwrap(); + + let reader = ZranReader::new(file).unwrap(); + assert_eq!(reader.get_data_size(), 0); + + let buf = vec![0x0u8; 32]; + reader.set_initial_data(&buf); + assert_eq!(reader.get_data_size(), 32); + } } diff --git a/utils/src/crypt.rs b/utils/src/crypt.rs index 225898e4bfb..a5646ce61d3 100644 --- a/utils/src/crypt.rs +++ b/utils/src/crypt.rs @@ -688,4 +688,122 @@ mod tests { assert_eq!(buf2[0], 0x5a); assert_eq!(buf2[16], 0xa5); } + + #[test] + fn test_attribute() { + let none = Algorithm::None.new_cipher().unwrap(); + let aes128xts = Algorithm::Aes128Xts.new_cipher().unwrap(); + let aes256xts = Algorithm::Aes256Xts.new_cipher().unwrap(); + let aes256gcm = Algorithm::Aes256Gcm.new_cipher().unwrap(); + + assert!(!Algorithm::None.is_encryption_enabled()); + assert!(Algorithm::Aes128Xts.is_encryption_enabled()); + assert!(Algorithm::Aes256Xts.is_encryption_enabled()); + assert!(Algorithm::Aes256Gcm.is_encryption_enabled()); + + assert!(!Algorithm::None.is_aead()); + assert!(!Algorithm::Aes128Xts.is_aead()); + assert!(!Algorithm::Aes256Xts.is_aead()); + assert!(Algorithm::Aes256Gcm.is_aead()); + + assert_eq!(Algorithm::None.tag_size(), 0); + assert_eq!(Algorithm::Aes128Xts.tag_size(), 0); + assert_eq!(Algorithm::Aes256Xts.tag_size(), 0); + assert_eq!(Algorithm::Aes256Gcm.tag_size(), 12); + + assert_eq!(Algorithm::None.key_length(), 0); + assert_eq!(Algorithm::Aes128Xts.key_length(), AES_128_XTS_KEY_LENGTH); + assert_eq!(Algorithm::Aes256Xts.key_length(), AES_256_XTS_KEY_LENGTH); + assert_eq!(Algorithm::Aes256Gcm.key_length(), AES_256_GCM_KEY_LENGTH); + + print!("{}", Algorithm::Aes128Xts); + assert!(Algorithm::from_str("none").is_ok()); + assert!(Algorithm::from_str("aes128xts").is_ok()); + assert!(Algorithm::from_str("aes256xts").is_ok()); + assert!(Algorithm::from_str("aes256gcm").is_ok()); + assert!(Algorithm::from_str("non-exist").is_err()); + + assert!(Algorithm::try_from(Algorithm::None as u32).is_ok()); + assert!(Algorithm::try_from(Algorithm::Aes128Xts as u32).is_ok()); + assert!(Algorithm::try_from(Algorithm::Aes256Xts as u32).is_ok()); + assert!(Algorithm::try_from(Algorithm::Aes256Gcm as u32).is_ok()); + assert!(Algorithm::try_from(u32::MAX).is_err()); + + assert!(Algorithm::try_from(Algorithm::None as u64).is_ok()); + assert!(Algorithm::try_from(Algorithm::Aes128Xts as u64).is_ok()); + assert!(Algorithm::try_from(Algorithm::Aes256Xts as u64).is_ok()); + assert!(Algorithm::try_from(Algorithm::Aes256Gcm as u64).is_ok()); + assert!(Algorithm::try_from(u64::MAX).is_err()); + + println!("{:?},{:?},{:?},{:?}", none, aes128xts, aes256xts, aes256gcm); + } + + #[test] + fn test_crypt_with_context() { + let error_key = [0xcu8, 64]; + let symmetry_key = [0xcu8, 32]; + let mut key = [0xcu8; 32]; + key[31] = 0xa; + let iv = [0u8; 16]; + let data = b"11111111111111111"; + // create with mismatch key length and algo + assert!( + CipherContext::new(error_key.to_vec(), iv.to_vec(), true, Algorithm::Aes128Xts) + .is_err() + ); + // create wtih symmetry key + assert!(CipherContext::new( + symmetry_key.to_vec(), + iv.to_vec(), + true, + Algorithm::Aes128Xts + ) + .is_err()); + + // test context is none + let ctx = + CipherContext::new(key.to_vec(), iv.to_vec(), false, Algorithm::Aes128Xts).unwrap(); + let obj = Arc::new(Algorithm::Aes128Xts.new_cipher().unwrap()); + assert!(encrypt_with_context(data, &obj, &None, true).is_err()); + assert!(decrypt_with_context(b"somedata", &obj, &None, true).is_err()); + + // test encrypted is false + let no_change = encrypt_with_context(data, &obj, &Some(ctx.clone()), false).unwrap(); + assert_eq!(no_change.clone().into_owned(), data); + let bind = no_change.into_owned(); + let plain_text_no_change = + decrypt_with_context(&bind, &obj, &Some(ctx.clone()), false).unwrap(); + assert_eq!(plain_text_no_change.into_owned(), data); + + // test normal encrypt and decrypt + let encrypt_text = encrypt_with_context(data, &obj, &Some(ctx.clone()), true).unwrap(); + let bind = encrypt_text.into_owned(); + let plain_text = decrypt_with_context(&bind, &obj, &Some(ctx), true).unwrap(); + assert_eq!(&plain_text.into_owned(), data); + } + + fn test_gen_key(convergent_encryption: bool) { + let mut key = [0xcu8; 32]; + key[31] = 0xa; + let iv = [0u8; 16]; + let data = b"11111111111111111"; + let ctx = CipherContext::new( + key.to_vec(), + iv.to_vec(), + convergent_encryption, + Algorithm::Aes128Xts, + ) + .unwrap(); + let obj = Arc::new(Algorithm::Aes128Xts.new_cipher().unwrap()); + let (gen_key, gen_iv) = ctx.generate_cipher_meta(&key); + let ciphertext = obj.encrypt(gen_key, Some(&gen_iv), data).unwrap(); + let plaintext = obj.decrypt(gen_key, Some(&gen_iv), &ciphertext).unwrap(); + assert_eq!(&plaintext, data); + } + + #[test] + fn test_generate_cipher_meta() { + test_gen_key(true); + test_gen_key(false); + } } diff --git a/utils/src/digest.rs b/utils/src/digest.rs index 7e8ab74593a..99d28d0ffed 100644 --- a/utils/src/digest.rs +++ b/utils/src/digest.rs @@ -273,4 +273,54 @@ mod test { b"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592" ); } + + #[test] + fn test_try_from() { + assert!(Algorithm::try_from(Algorithm::Sha256 as u32).is_ok()); + assert!(Algorithm::try_from(Algorithm::Blake3 as u32).is_ok()); + assert!(Algorithm::try_from(0xffff_abcd as u32).is_err()); + + assert!(Algorithm::try_from(Algorithm::Sha256 as u64).is_ok()); + assert!(Algorithm::try_from(Algorithm::Blake3 as u64).is_ok()); + assert!(Algorithm::try_from(0xffff_abcd as u64).is_err()); + } + + #[test] + fn test_spec_hasher_new() { + let text = b"The quick brown fox jumps "; + let text2 = b"over the lazy dog"; + + let mut hasher: blake3::Hasher = blake3::Hasher::new(); + hasher.digest_update(text); + hasher.digest_update(text2); + let blake3 = hasher.digest_finalize(); + let str: String = blake3.into(); + assert_eq!( + str.as_bytes(), + b"2f1514181aadccd913abd94cfa592701a5686ab23f8df1dff1b74710febc6d4a" + ); + + let mut hasher = RafsDigestHasher::Sha256(Sha256::new()); + hasher.digest_update(text); + hasher.digest_update(text2); + let sha256 = hasher.digest_finalize(); + let str: String = sha256.into(); + assert_eq!( + str.as_bytes(), + b"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592" + ); + } + + #[test] + fn test_rafs_digest_try_from() { + let text = b"The quick brown fox jumps over the lazy dog"; + + let d1 = RafsDigest::from_buf(text, Algorithm::Blake3); + let d2 = RafsDigest::try_from(d1.data).unwrap(); + let s1: String = d1.into(); + let s2: String = d2.into(); + print!("{:?}", d1); + assert_eq!(s1, s2); + print!("{:?}, {:?}", Algorithm::Blake3, Algorithm::Sha256); + } } diff --git a/utils/src/filemap.rs b/utils/src/filemap.rs index 0d92ae0ea5a..0ef4553f331 100644 --- a/utils/src/filemap.rs +++ b/utils/src/filemap.rs @@ -213,6 +213,8 @@ pub fn clone_file(fd: RawFd) -> Result { #[cfg(test)] mod tests { + use vmm_sys_util::tempfile::TempFile; + use super::*; use std::fs::OpenOptions; use std::path::PathBuf; @@ -245,4 +247,32 @@ mod tests { let map = FileMapState::default(); drop(map); } + + #[test] + fn test_file_map_error() { + let temp = TempFile::new().unwrap(); + let file = OpenOptions::new() + .read(true) + .write(false) + .open(temp.as_path()) + .unwrap(); + assert!(FileMapState::new(file, 0, 4096, true).is_err()); + + let temp = TempFile::new().unwrap(); + let file = OpenOptions::new() + .read(true) + .write(false) + .open(temp.as_path()) + .unwrap(); + let mut map = FileMapState::new(file, 0, 4096, false).unwrap(); + assert!(map.get_slice::(0, usize::MAX).is_err()); + assert!(map.get_slice::(usize::MAX, 1).is_err()); + assert!(map.get_slice::(4096, 4096).is_err()); + assert!(map.get_slice::(0, 128).is_ok()); + + assert!(map.get_slice_mut::(0, usize::MAX).is_err()); + assert!(map.get_slice_mut::(usize::MAX, 1).is_err()); + assert!(map.get_slice_mut::(4096, 4096).is_err()); + assert!(map.get_slice_mut::(0, 128).is_ok()); + } } diff --git a/utils/src/lib.rs b/utils/src/lib.rs index f5d2cb44246..4785e36a9f4 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -164,4 +164,11 @@ mod tests { // fail to convert u64 to u32 assert_eq!(try_round_up_4k::(u64::MAX - 4096), None); } + + #[test] + fn test_round_up_uszie() { + assert_eq!(round_up_usize(10, 8), 16); + assert_eq!(round_up_usize(100, 8), 104); + assert_eq!(round_up_usize(1000, 8), 1000); + } } diff --git a/utils/src/metrics.rs b/utils/src/metrics.rs index f5321d98aa3..ddfdb41c084 100644 --- a/utils/src/metrics.rs +++ b/utils/src/metrics.rs @@ -848,4 +848,244 @@ mod tests { g.fop_update(StatsFop::Read, 2015520, true); assert_eq!(g.block_count_read[3].count(), 2); } + + #[test] + fn test_latency_millis_range_index() { + assert_eq!(latency_millis_range_index(0), 0); + assert_eq!(latency_millis_range_index(1), 0); + assert_eq!(latency_millis_range_index(10), 1); + assert_eq!(latency_millis_range_index(20), 1); + assert_eq!(latency_millis_range_index(40), 2); + assert_eq!(latency_millis_range_index(80), 3); + assert_eq!(latency_millis_range_index(160), 4); + assert_eq!(latency_millis_range_index(320), 4); + assert_eq!(latency_millis_range_index(640), 5); + assert_eq!(latency_millis_range_index(1280), 6); + assert_eq!(latency_millis_range_index(2560), 7); + } + + #[test] + fn test_latency_micros_range_index() { + assert_eq!(latency_micros_range_index(100), 0); + assert_eq!(latency_micros_range_index(500), 1); + assert_eq!(latency_micros_range_index(10_000), 2); + assert_eq!(latency_micros_range_index(30_000), 3); + assert_eq!(latency_micros_range_index(100_000), 4); + assert_eq!(latency_micros_range_index(1_000_000), 5); + assert_eq!(latency_micros_range_index(1_500_000), 6); + assert_eq!(latency_micros_range_index(3_000_000), 7); + } + + #[test] + fn test_inode_stats() { + let stat = InodeIoStats::default(); + stat.stats_fop_inc(StatsFop::Read); + stat.stats_fop_inc(StatsFop::Open); + assert_eq!(stat.fop_hits[StatsFop::Read as usize].count(), 1); + assert_eq!(stat.total_fops.count(), 2); + + stat.stats_cumulative(StatsFop::Open, 1000); + stat.stats_cumulative(StatsFop::Read, 4000); + stat.stats_cumulative(StatsFop::Read, 5000); + + assert_eq!(stat.block_count_read[0].count(), 0); + assert_eq!(stat.block_count_read[1].count(), 1); + assert_eq!(stat.block_count_read[2].count(), 1); + } + + #[test] + fn test_access_pattern() { + let ap = AccessPattern::default(); + ap.record_access_time(); + assert_ne!(ap.first_access_time_secs.load(Ordering::Relaxed), 0); + assert_ne!(ap.first_access_time_nanos.load(Ordering::Relaxed), 0); + } + + #[test] + fn test_file_stats_update() { + let f = FsIoStats::default(); + let node1: Inode = 1; + let node2: Inode = 2; + let node3: Inode = 3; + + f.new_file_counter(node1); + f.new_file_counter(node2); + assert!(f.access_patterns.read().unwrap().is_empty()); + assert!(f.file_counters.read().unwrap().is_empty()); + + f.access_pattern_enabled.store(true, Ordering::Relaxed); + f.files_account_enabled.store(true, Ordering::Relaxed); + f.record_latest_read_files_enabled + .store(true, Ordering::Relaxed); + f.new_file_counter(node1); + f.new_file_counter(node2); + f.file_stats_update(node1, StatsFop::Read, 4000, true); + f.file_stats_update(node1, StatsFop::Read, 5000, true); + f.file_stats_update(node1, StatsFop::Open, 0, true); + f.file_stats_update(node3, StatsFop::Open, 0, true); + assert_eq!( + f.access_patterns + .read() + .unwrap() + .get(&node1) + .unwrap() + .nr_read + .count(), + 2 + ); + assert_eq!( + f.file_counters + .read() + .unwrap() + .get(&node1) + .unwrap() + .fop_hits[StatsFop::Read as usize] + .count(), + 2 + ); + assert!(f.recent_read_files.is_set(node1 as u64)); + } + + #[test] + fn test_fop_update() { + let f = FsIoStats::default(); + assert_eq!(f.nr_opens.count(), 0); + f.fop_update(StatsFop::Open, 0, true); + assert_eq!(f.nr_opens.count(), 1); + f.fop_update(StatsFop::Release, 0, true); + assert_eq!(f.nr_opens.count(), 0); + f.fop_update(StatsFop::Opendir, 0, true); + assert_eq!(f.fop_errors[StatsFop::Opendir as usize].count(), 0); + f.fop_update(StatsFop::Opendir, 0, false); + assert_eq!(f.fop_errors[StatsFop::Opendir as usize].count(), 1); + } + + #[test] + fn test_latecny() { + let f = FsIoStats::default(); + assert_eq!(f.latency_start(), None); + f.measure_latency.store(true, Ordering::Relaxed); + let s = f.latency_start().unwrap(); + let d = Duration::new(2, 0); + f.latency_end(&s.checked_sub(d), StatsFop::Read); + + assert_eq!( + f.read_latency_dist[latency_micros_range_index(2 * 1000 * 1000)].count(), + 1 + ); + assert_eq!( + f.fop_cumulative_latency_total[StatsFop::Read as usize].count(), + saturating_duration_micros(&d) + ); + } + + #[test] + fn test_fs_io_stats_new_and_export() { + let id0: Option = Some("id-0".to_string()); + let id1: Option = Some("id-1".to_string()); + let none: Option = None; + + let _f1 = FsIoStats::new("id-0"); + assert!(export_files_stats(&id0, true).is_ok()); + assert!(export_files_stats(&none, true).is_ok()); + assert!(export_global_stats(&id0).is_ok()); + assert!(export_global_stats(&id1).is_err()); + assert!(export_global_stats(&none).is_ok()); + + let _f2 = FsIoStats::new("id-1"); + assert!(export_files_stats(&none, false).is_err()); + assert!(export_files_stats(&id0, true).is_ok()); + assert!(export_files_stats(&id0, false).is_ok()); + assert!(export_global_stats(&none).is_err()); + assert!(export_files_access_pattern(&id0).is_ok()); + assert!(export_files_access_pattern(&none).is_err()); + + let ios = FsIoStats::default(); + assert!(ios.export_files_access_patterns().is_ok()); + assert!(ios.export_files_stats().is_ok()); + assert!(ios.export_fs_stats().is_ok()); + ios.export_latest_read_files(); + + test_fop_record(); + } + + fn test_fop_record() { + let ios = FsIoStats::new("0"); + let mut recorder = FopRecorder::settle(StatsFop::Read, 0, &ios); + assert!(!recorder.success); + assert_eq!(recorder.size, 0); + + recorder.mark_success(10); + assert!(recorder.success); + assert_eq!(recorder.size, 10); + drop(recorder); + } + + #[test] + fn test_saturating_duration() { + assert_eq!( + saturating_duration_millis(&Duration::from_millis(1234)), + 1234 + ); + assert_eq!( + saturating_duration_micros(&Duration::from_millis(888)), + 888_000 + ); + assert_eq!( + saturating_duration_micros(&Duration::from_millis(1888)), + 1_888_000 + ); + } + + #[test] + fn test_blob_cache_metric() { + let m1: Arc = BlobcacheMetrics::new("id", "path"); + { + let metrics = BLOBCACHE_METRICS.read().unwrap(); + assert_eq!(metrics.len(), 1); + } + assert!(m1.export_metrics().is_ok()); + assert!(m1.release().is_ok()); + { + let metrics = BLOBCACHE_METRICS.read().unwrap(); + assert_eq!(metrics.len(), 0); + } + + let now = SystemTime::now(); + let prev = now.checked_sub(Duration::new(10, 0)).unwrap(); + m1.calculate_prefetch_metrics(prev); + assert_eq!(m1.prefetch_cumulative_time_millis.count(), 10_000); + assert_eq!( + m1.prefetch_end_time_secs.count(), + now.duration_since(SystemTime::UNIX_EPOCH) + .expect("No error") + .as_secs() + ); + + let id0: Option = Some("id-0".to_string()); + let none: Option = None; + BlobcacheMetrics::new("id-0", "t0"); + assert!(export_blobcache_metrics(&id0).is_ok()); + assert!(export_blobcache_metrics(&none).is_ok()); + BlobcacheMetrics::new("id-1", "t1"); + assert!(export_blobcache_metrics(&none).is_err()); + assert!(export_events().is_ok()); + } + + #[test] + fn test_backend_metric() { + let id0: Option = Some("id-0".to_string()); + let id1: Option = Some("id-1".to_string()); + let none: Option = None; + let b0 = BackendMetrics::new("id-0", "t0"); + assert!(export_backend_metrics(&id0).is_ok()); + assert!(export_backend_metrics(&id1).is_err()); + assert!(export_backend_metrics(&none).is_ok()); + let b1 = BackendMetrics::new("id-1", "t1"); + assert!(export_backend_metrics(&id0).is_ok()); + assert!(export_backend_metrics(&id1).is_ok()); + assert!(export_backend_metrics(&none).is_err()); + assert!(b0.release().is_ok()); + assert!(b1.release().is_ok()); + } } diff --git a/utils/src/mpmc.rs b/utils/src/mpmc.rs index 5261275f324..ba5da1eafba 100644 --- a/utils/src/mpmc.rs +++ b/utils/src/mpmc.rs @@ -156,4 +156,17 @@ mod tests { t.join().unwrap(); } + + #[test] + fn test_default_channel_send_and_recv() { + let channel = Channel::default(); + + channel.send(0x1u32).unwrap(); + channel.send(0x2u32).unwrap(); + assert_eq!(channel.try_recv().unwrap(), 0x1); + assert_eq!(channel.try_recv().unwrap(), 0x2); + + channel.close(); + channel.send(2u32).unwrap_err(); + } } diff --git a/utils/src/trace.rs b/utils/src/trace.rs index f4348ca1d45..d083cc99e0b 100644 --- a/utils/src/trace.rs +++ b/utils/src/trace.rs @@ -261,6 +261,8 @@ macro_rules! event_tracer { #[cfg(test)] pub mod tests { + use crate::trace::TimingTracerClass; + use super::{EventTracerClass, TraceClass}; use std::thread; @@ -303,4 +305,38 @@ pub mod tests { assert_eq!(map["registered_events"]["event_1"].as_u64(), Some(600)); assert_eq!(map["registered_events"]["event_2"].as_u64(), Some(900)); } + + #[test] + fn test_timing_trace() { + register_tracer!(TraceClass::Timing, TimingTracerClass); + let f = || (); + + let t1 = thread::Builder::new() + .spawn(move || { + for i in 0..100 { + timing_tracer!({ f() }, format!("t1.{}", i).as_str()); + } + }) + .unwrap(); + + let t2 = thread::Builder::new() + .spawn(move || { + for i in 0..100 { + timing_tracer!({ f() }, format!("t2.{}", i).as_str()); + } + }) + .unwrap(); + let t3 = thread::Builder::new() + .spawn(move || { + for i in 0..100 { + timing_tracer!({ f() }, format!("t3.{}", i).as_str()); + } + }) + .unwrap(); + + t1.join().unwrap(); + t2.join().unwrap(); + t3.join().unwrap(); + assert_eq!(timing_tracer!().unwrap().records.lock().unwrap().len(), 300); + } } diff --git a/utils/src/verity.rs b/utils/src/verity.rs index e7c877c463c..92c321391b1 100644 --- a/utils/src/verity.rs +++ b/utils/src/verity.rs @@ -431,4 +431,27 @@ mod tests { assert_ne!(root_digest, digest); assert_eq!(generator.mkl_tree.max_levels, 2); } + + #[test] + fn test_merkle_tree_digest_algo() { + let mkl = MerkleTree::new(4096, 1, Algorithm::Sha256); + assert_eq!(mkl.digest_algorithm(), Algorithm::Sha256); + } + + #[test] + fn test_verity_generator_error() { + let file = TempFile::new().unwrap(); + assert!(VerityGenerator::new(file.into_file(), u64::MAX, u32::MAX).is_err()); + + let file = TempFile::new().unwrap(); + let mut generator = VerityGenerator::new(file.into_file(), 0, 4097).unwrap(); + assert!(generator.set_digest(1, 0, &[1u8; 64]).is_err()); + } + + #[test] + fn test_verity_initialize() { + let file = TempFile::new().unwrap(); + let mut generator = VerityGenerator::new(file.into_file(), 0, 4097).unwrap(); + assert!(generator.initialize().is_ok()); + } } From b365b97533fcb21d2fd58a0589d49d88fb1fcd4d Mon Sep 17 00:00:00 2001 From: "weizhen.zt" Date: Tue, 7 Nov 2023 16:56:08 +0800 Subject: [PATCH 2/7] storage: add some unit test cases Signed-off-by: weizhen.zt --- Cargo.lock | 1 + storage/Cargo.toml | 1 + storage/src/cache/dummycache.rs | 225 +++++++++++++++++++++++++++ storage/src/cache/fscache/mod.rs | 87 +++++++++++ storage/src/cache/state/mod.rs | 48 ++++++ storage/src/cache/state/range_map.rs | 22 +++ 6 files changed, 384 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 4163f342468..7aac4dfff92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1324,6 +1324,7 @@ dependencies = [ "tar", "time", "tokio", + "toml", "url", "vm-memory", "vmm-sys-util", diff --git a/storage/Cargo.toml b/storage/Cargo.toml index a47ff12d547..3983058eab3 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -35,6 +35,7 @@ url = { version = "2.1.1", optional = true } vm-memory = "0.10" fuse-backend-rs = "^0.10.3" gpt = { version = "3.0.0", optional = true } +toml = "0.5" nydus-api = { version = "0.3", path = "../api" } nydus-utils = { version = "0.4", path = "../utils", features = ["encryption", "zran"] } diff --git a/storage/src/cache/dummycache.rs b/storage/src/cache/dummycache.rs index 2e554b6cbc0..7a0465f36e2 100644 --- a/storage/src/cache/dummycache.rs +++ b/storage/src/cache/dummycache.rs @@ -245,3 +245,228 @@ impl Drop for DummyCacheMgr { self.destroy(); } } + +#[cfg(test)] +mod tests { + use std::fs::OpenOptions; + + use nydus_api::ConfigV2; + use nydus_utils::metrics::BackendMetrics; + use vmm_sys_util::tempdir::TempDir; + + use crate::{ + cache::state::IndexedChunkMap, + device::{BlobIoChunk, BlobIoRange}, + meta::tests::DummyBlobReader, + test::{MockBackend, MockChunkInfo}, + }; + + use super::*; + + #[test] + fn test_dummy_cache() { + let info = BlobInfo::new( + 0, + "blob-0".to_string(), + 800, + 0, + 8, + 100, + BlobFeatures::empty(), + ); + let dir = TempDir::new().unwrap(); + let blob_path = dir + .as_path() + .join("blob-0") + .as_os_str() + .to_str() + .unwrap() + .to_string(); + let chunkmap = IndexedChunkMap::new(blob_path.as_str(), 100, true).unwrap(); + let chunkmap_unuse = IndexedChunkMap::new(blob_path.as_str(), 100, true).unwrap(); + + let f = OpenOptions::new() + .truncate(true) + .create(true) + .write(true) + .read(true) + .open(blob_path.as_str()) + .unwrap(); + assert!(f.set_len(800).is_ok()); + let reader: Arc = Arc::new(DummyBlobReader { + metrics: BackendMetrics::new("dummy", "localfs"), + file: f, + }); + let cache = DummyCache { + blob_id: "0".to_string(), + blob_info: Arc::new(info.clone()), + chunk_map: Arc::new(chunkmap), + reader: reader.clone(), + compressor: compress::Algorithm::None, + digester: digest::Algorithm::Blake3, + is_legacy_stargz: false, + need_validation: false, + }; + + let cache_unuse = DummyCache { + blob_id: "1".to_string(), + blob_info: Arc::new(info.clone()), + chunk_map: Arc::new(chunkmap_unuse), + reader, + compressor: compress::Algorithm::None, + digester: digest::Algorithm::Blake3, + is_legacy_stargz: false, + need_validation: false, + }; + + assert!(cache.get_legacy_stargz_size(0, 100).is_ok()); + assert!(!cache.is_zran()); + assert!(!cache.is_batch()); + assert!(cache.get_blob_object().is_none()); + assert!(cache.prefetch_range(&BlobIoRange::default()).is_err()); + assert_eq!(cache.blob_id, "0"); + assert_eq!(cache.blob_uncompressed_size().unwrap(), 800); + assert_eq!(cache.blob_compressed_size().unwrap(), 0); + assert_eq!(cache.blob_compressor(), compress::Algorithm::None); + assert_eq!(cache.blob_cipher(), Algorithm::None); + match cache.blob_cipher_object().as_ref() { + Cipher::None => {} + _ => panic!(), + } + assert!(cache.blob_cipher_context().is_none()); + assert_eq!(cache.blob_digester(), digest::Algorithm::Blake3); + assert!(!cache.is_legacy_stargz()); + assert!(!cache.need_validation()); + let _r = cache.reader(); + let _m = cache.get_chunk_map(); + assert!(cache.get_chunk_info(0).is_none()); + + assert!(cache.start_prefetch().is_ok()); + let reqs = BlobPrefetchRequest { + blob_id: "blob-0".to_string(), + offset: 0, + len: 10, + }; + let iovec_arr: &[BlobIoDesc] = &[]; + let reqs = &[reqs]; + + assert!(cache + .prefetch(Arc::new(cache_unuse), reqs, iovec_arr) + .is_err()); + assert!(cache.stop_prefetch().is_ok()); + let mut iovec = BlobIoVec::new(Arc::new(info.clone())); + let chunk: Arc = Arc::new(MockChunkInfo { + block_id: Default::default(), + blob_index: 0, + flags: Default::default(), + compress_size: 0, + uncompress_size: 800, + compress_offset: 0, + uncompress_offset: 0, + file_offset: 0, + index: 0, + reserved: 0, + }); + iovec.push(BlobIoDesc::new( + Arc::new(info.clone()), + BlobIoChunk::from(chunk.clone()), + 0, + 10, + true, + )); + + let mut dst_buf1 = vec![0x0u8; 800]; + let volatile_slice_1 = + unsafe { FileVolatileSlice::from_raw_ptr(dst_buf1.as_mut_ptr(), dst_buf1.len()) }; + let bufs: &[FileVolatileSlice] = &[volatile_slice_1]; + assert_eq!(cache.read(&mut iovec, bufs).unwrap(), 800); + + let chunk2: Arc = Arc::new(MockChunkInfo { + block_id: Default::default(), + blob_index: 0, + flags: Default::default(), + compress_size: 0, + uncompress_size: 100, + compress_offset: 0, + uncompress_offset: 0, + file_offset: 0, + index: 0, + reserved: 0, + }); + + let chunk3: Arc = Arc::new(MockChunkInfo { + block_id: Default::default(), + blob_index: 0, + flags: Default::default(), + compress_size: 0, + uncompress_size: 100, + compress_offset: 100, + uncompress_offset: 0, + file_offset: 0, + index: 0, + reserved: 0, + }); + + let mut iovec = BlobIoVec::new(Arc::new(info.clone())); + + iovec.push(BlobIoDesc::new( + Arc::new(info.clone()), + BlobIoChunk::from(chunk2.clone()), + 0, + 100, + true, + )); + + iovec.push(BlobIoDesc::new( + Arc::new(info), + BlobIoChunk::from(chunk3.clone()), + 100, + 100, + true, + )); + + let mut dst_buf2 = vec![0x0u8; 100]; + let mut dst_buf3 = vec![0x0u8; 100]; + let volatile_slice_2 = + unsafe { FileVolatileSlice::from_raw_ptr(dst_buf2.as_mut_ptr(), dst_buf2.len()) }; + + let volatile_slice_3 = + unsafe { FileVolatileSlice::from_raw_ptr(dst_buf3.as_mut_ptr(), dst_buf3.len()) }; + let bufs: &[FileVolatileSlice] = &[volatile_slice_2, volatile_slice_3]; + assert_eq!(cache.read(&mut iovec, bufs).unwrap(), 200); + } + + #[test] + fn test_dummy_cache_mgr() { + let content = r#"version=2 + id = "my_id" + metadata_path = "meta_path" + [backend] + type = "localfs" + [backend.localfs] + blob_file = "/tmp/nydus.blob.data" + dir = "/tmp" + alt_dirs = ["/var/nydus/cache"] + [cache] + type = "filecache" + compressed = true + validate = true + [cache.filecache] + work_dir = "/tmp" + "#; + + let cfg: ConfigV2 = toml::from_str(content).unwrap(); + let backend = MockBackend { + metrics: BackendMetrics::new("dummy", "localfs"), + }; + let mgr = + DummyCacheMgr::new(cfg.get_cache_config().unwrap(), Arc::new(backend), false).unwrap(); + assert!(mgr.init().is_ok()); + assert!(!mgr.gc(Some("blob-0"))); + let _bak = mgr.backend(); + mgr.check_stat(); + mgr.destroy(); + assert!(mgr.closed.load(Ordering::Acquire)); + drop(mgr); + } +} diff --git a/storage/src/cache/fscache/mod.rs b/storage/src/cache/fscache/mod.rs index cf624f4f427..95aef041cac 100644 --- a/storage/src/cache/fscache/mod.rs +++ b/storage/src/cache/fscache/mod.rs @@ -366,3 +366,90 @@ impl FileCacheEntry { } } } + +#[cfg(test)] +mod tests { + use std::{fs::OpenOptions, path::PathBuf}; + + use nydus_api::ConfigV2; + use nydus_utils::{compress, metrics::BackendMetrics}; + + use crate::{factory::ASYNC_RUNTIME, test::MockBackend}; + + use super::*; + + #[test] + fn test_fs_cache_mgr() { + let content = r#"version=2 + id = "my_id" + metadata_path = "meta_path" + [backend] + type = "localfs" + [backend.localfs] + blob_file = "/tmp/nydus.blob.data" + dir = "/tmp" + alt_dirs = ["/var/nydus/cache"] + [cache] + type = "fscache" + compressed = false + validate = true + [cache.fscache] + work_dir = "/tmp" + "#; + + let cfg: ConfigV2 = toml::from_str(content).unwrap(); + let backend = MockBackend { + metrics: BackendMetrics::new("dummy", "localfs"), + }; + + let mut mgr: FsCacheMgr = FsCacheMgr::new( + cfg.get_cache_config().unwrap(), + Arc::new(backend), + ASYNC_RUNTIME.clone(), + &cfg.id, + ) + .unwrap(); + assert!(mgr.init().is_ok()); + mgr.work_dir = "../tests/texture/zran/".to_string(); + + let root_dir = &std::env::var("CARGO_MANIFEST_DIR").expect("$CARGO_MANIFEST_DIR"); + let path = PathBuf::from(root_dir).join("../tests/texture/zran/233c72f2b6b698c07021c4da367cfe2dff4f049efbaa885ca0ff760ea297865a"); + + let features = BlobFeatures::ALIGNED + | BlobFeatures::INLINED_FS_META + | BlobFeatures::CHUNK_INFO_V2 + | BlobFeatures::ZRAN; + + let mut blob_info = BlobInfo::new( + 0, + "233c72f2b6b698c07021c4da367cfe2dff4f049efbaa885ca0ff760ea297865a".to_string(), + 0x16c6000, + 9839040, + RAFS_DEFAULT_CHUNK_SIZE as u32, + 0xa3, + features, + ); + + blob_info.set_blob_meta_info(0, 0xa1290, 0xa1290, compress::Algorithm::None as u32); + + let f1: File = OpenOptions::new() + .truncate(true) + .create(true) + .write(true) + .read(true) + .open(path.as_os_str()) + .unwrap(); + f1.set_len(800).unwrap(); + + blob_info.set_fscache_file(Some(Arc::new(f1.try_clone().unwrap()))); + + assert!(mgr.get_blob_cache(&Arc::new(blob_info.clone())).is_ok()); + assert!(mgr.gc(Some( + "233c72f2b6b698c07021c4da367cfe2dff4f049efbaa885ca0ff760ea297865a" + ))); + mgr.check_stat(); + let _backend = mgr.backend(); + mgr.destroy(); + drop(mgr); + } +} diff --git a/storage/src/cache/state/mod.rs b/storage/src/cache/state/mod.rs index 731a41bfb4a..e02559de93e 100644 --- a/storage/src/cache/state/mod.rs +++ b/storage/src/cache/state/mod.rs @@ -161,3 +161,51 @@ pub trait ChunkIndexGetter { /// Get the chunk's id/key for state tracking. fn get_index(chunk: &dyn BlobChunkInfo) -> Self::Index; } + +#[cfg(test)] +mod tests { + use crate::test::MockChunkInfo; + + use super::*; + + impl RangeMap for NoopChunkMap { + type I = u32; + } + + #[test] + fn test_trait_default_impl() { + let m = NoopChunkMap::new(false); + let chunk_info = MockChunkInfo { + index: 128, + ..Default::default() + }; + + assert!(m.is_pending(&chunk_info).is_ok()); + assert!(!m.is_pending(&chunk_info).unwrap()); + assert!(!m.is_range_all_ready()); + assert!(m.is_range_ready(0, 1).is_err()); + assert!(m.check_range_ready_and_mark_pending(0, 1).is_err()); + assert!(m.set_range_ready_and_clear_pending(0, 1).is_err()); + m.clear_range_pending(0, 1); + assert!(m.wait_for_range_ready(0, 1).is_err()); + assert!(m.as_range_map().is_none()); + assert!(!m.is_persist()); + assert!(!m.is_ready(&chunk_info).unwrap()); + } + + #[test] + #[should_panic] + fn test_check_ready_and_mark_pending_default_impl() { + let chunk_info = MockChunkInfo::default(); + let m = NoopChunkMap::new(false); + m.check_ready_and_mark_pending(&chunk_info).unwrap(); + } + + #[test] + #[should_panic] + fn test_set_ready_and_clear_pending_default_impl() { + let chunk_info = MockChunkInfo::default(); + let m = NoopChunkMap::new(false); + m.set_ready_and_clear_pending(&chunk_info).unwrap(); + } +} diff --git a/storage/src/cache/state/range_map.rs b/storage/src/cache/state/range_map.rs index 7d4cda2f42a..131bc15eeae 100644 --- a/storage/src/cache/state/range_map.rs +++ b/storage/src/cache/state/range_map.rs @@ -192,4 +192,26 @@ mod tests { } } } + + #[test] + fn test_range_map_state() { + let dir = TempDir::new().unwrap(); + let blob_path = dir.as_path().join("blob-1"); + let blob_path = blob_path.as_os_str().to_str().unwrap().to_string(); + let range_count = 100; + + let map = BlobRangeMap::new(&blob_path, range_count, 0).unwrap(); + assert_eq!( + map.check_range_ready_and_mark_pending(1, 10) + .unwrap() + .unwrap() + .len(), + 10 + ); + assert!(map.set_range_ready_and_clear_pending(1, 10).is_ok()); + assert!(map + .check_range_ready_and_mark_pending(1, 10) + .unwrap() + .is_none()); + } } From 929ba4ef68909cd20b0acff644f255d68a9c906c Mon Sep 17 00:00:00 2001 From: "weizhen.zt" Date: Tue, 7 Nov 2023 16:56:21 +0800 Subject: [PATCH 3/7] rafs: add some unit test cases Signed-off-by: weizhen.zt --- rafs/src/fs.rs | 46 +++++ rafs/src/metadata/cached_v5.rs | 120 ++++++++++- rafs/src/metadata/chunk.rs | 215 +++++++++++++++++++ rafs/src/metadata/direct_v6.rs | 37 ++++ rafs/src/metadata/inode.rs | 366 +++++++++++++++++++++++++++++++++ rafs/src/metadata/layout/v6.rs | 293 ++++++++++++++++++++++++++ rafs/src/metadata/md_v5.rs | 19 ++ rafs/src/metadata/mod.rs | 141 +++++++++++++ rafs/src/metadata/noop.rs | 53 +++++ rafs/src/mock/mock_chunk.rs | 40 ++++ rafs/src/mock/mock_inode.rs | 94 +++++++++ rafs/src/mock/mock_super.rs | 92 +++++++++ 12 files changed, 1514 insertions(+), 2 deletions(-) diff --git a/rafs/src/fs.rs b/rafs/src/fs.rs index 1d1627c94f7..23f435733b6 100644 --- a/rafs/src/fs.rs +++ b/rafs/src/fs.rs @@ -1003,3 +1003,49 @@ pub(crate) mod tests { } } } + +#[cfg(test)] +mod tests { + use nydus_utils::metrics::FsIoStats; + + use super::*; + #[test] + fn test_rafs() { + let rafs = Rafs { + id: "foo".into(), + device: BlobDevice::default(), + ios: FsIoStats::default().into(), + sb: Arc::new(RafsSuper::default()), + initialized: false, + digest_validate: false, + fs_prefetch: false, + prefetch_all: false, + xattr_enabled: false, + amplify_io: 0, + i_uid: 0, + i_gid: 0, + i_time: 0, + }; + assert_eq!(rafs.id(), "foo"); + assert!(!rafs.xattr_supported()); + let ent = rafs.negative_entry(); + assert_eq!(ent.inode, 0); + assert_eq!(ent.generation, 0); + assert_eq!(ent.attr_flags, 0); + rafs.init(FsOptions::ASYNC_DIO).unwrap(); + rafs.open(&Context::default(), Inode::default(), 0, 0) + .unwrap(); + rafs.release( + &Context::default(), + Inode::default(), + 0, + Handle::default(), + false, + false, + Some(0), + ) + .unwrap(); + rafs.statfs(&Context::default(), Inode::default()).unwrap(); + rafs.destroy(); + } +} diff --git a/rafs/src/metadata/cached_v5.rs b/rafs/src/metadata/cached_v5.rs index 828fc1abb9f..db38d80a497 100644 --- a/rafs/src/metadata/cached_v5.rs +++ b/rafs/src/metadata/cached_v5.rs @@ -797,15 +797,24 @@ mod cached_tests { use std::sync::Arc; use nydus_storage::device::{BlobDevice, BlobFeatures}; + use nydus_utils::digest::{Algorithm, RafsDigest}; use nydus_utils::ByteSize; + use storage::device::v5::BlobV5ChunkInfo; + use storage::device::{BlobChunkFlags, BlobChunkInfo}; use crate::metadata::cached_v5::{CachedInodeV5, CachedSuperBlockV5}; + use crate::metadata::inode::RafsInodeFlags; use crate::metadata::layout::v5::{ rafsv5_align, RafsV5BlobTable, RafsV5ChunkInfo, RafsV5Inode, RafsV5InodeWrapper, }; use crate::metadata::layout::{RafsXAttrs, RAFS_V5_ROOT_INODE}; - use crate::metadata::{RafsInode, RafsStore, RafsSuperMeta}; - use crate::{BufWriter, RafsInodeExt, RafsIoReader}; + use crate::metadata::{ + RafsInode, RafsInodeWalkAction, RafsStore, RafsSuperBlock, RafsSuperInodes, RafsSuperMeta, + }; + use crate::{BufWriter, RafsInodeExt, RafsIoRead, RafsIoReader}; + use vmm_sys_util::tempfile::TempFile; + + use super::CachedChunkInfoV5; #[test] fn test_load_inode() { @@ -1061,4 +1070,111 @@ mod cached_tests { assert_eq!(sb.max_inode, 4); assert_eq!(sb.s_inodes.len(), 3); } + + fn get_streams() -> (Box, BufWriter) { + let temp = TempFile::new().unwrap(); + let w = OpenOptions::new() + .read(true) + .write(true) + .open(temp.as_path()) + .unwrap(); + let r = OpenOptions::new() + .read(true) + .write(false) + .open(temp.as_path()) + .unwrap(); + let writer: BufWriter = BufWriter::new(w); + let reader: Box = Box::new(r); + (reader, writer) + } + + #[test] + fn test_cached_super_block_v5() { + let digest = RafsDigest::from_buf("foobar".as_bytes(), Algorithm::Blake3); + let meta = RafsSuperMeta::default(); + let mut node = CachedInodeV5 { + i_ino: 0, + ..CachedInodeV5::default() + }; + node.i_mode |= libc::S_IFDIR as u32; + node.i_child_idx = 2; + node.i_flags = RafsInodeFlags::SYMLINK; + node.i_name = OsStr::new("foo").into(); + node.i_digest = digest; + let mut child_node = CachedInodeV5::default(); + child_node.i_mode |= libc::S_IFDIR as u32; + child_node.i_ino = 1; + child_node.i_name = OsStr::new("bar").into(); + let mut blk = CachedSuperBlockV5::new(meta, false); + let (r, _w) = get_streams(); + let mut r = r as RafsIoReader; + assert!(blk.load_all_inodes(&mut r).is_ok()); + assert_eq!(blk.get_max_ino(), RAFS_V5_ROOT_INODE); + assert!(blk.get_inode(0, false).is_err()); + assert!(blk.get_extended_inode(0, false).is_err()); + + blk.s_inodes.insert(0, Arc::new(node.clone())); + assert!(blk.get_inode(0, false).is_ok()); + assert!(blk.get_extended_inode(0, false).is_ok()); + + blk.destroy(); + assert!(blk.s_inodes.is_empty()); + let blobs = blk.get_blob_extra_infos(); + assert!(blobs.unwrap().is_empty()); + assert_eq!(blk.root_ino(), RAFS_V5_ROOT_INODE); + + node.add_child(Arc::new(child_node)); + assert_eq!(node.i_child.len(), 1); + + let mut descendants = Vec::>::new(); + node.collect_descendants_inodes(&mut descendants).unwrap(); + assert!(node.collect_descendants_inodes(&mut descendants).is_ok()); + assert_eq!(node.get_entry().inode, node.ino()); + assert_eq!(node.get_xattr(OsStr::new("foobar")).unwrap(), None); + assert!(!node.is_blkdev()); + assert!(!node.is_chrdev()); + assert!(!node.is_sock()); + assert!(!node.is_fifo()); + assert_eq!(node.get_symlink_size(), 0); + + node.i_child_cnt = 1; + let mut found = false; + node.walk_children_inodes(0, &mut |_node, _child_name, child_ino, _offset| { + if child_ino == 1 { + found = true; + Ok(RafsInodeWalkAction::Break) + } else { + Ok(RafsInodeWalkAction::Continue) + } + }) + .unwrap(); + assert!(found); + let rafsinode = node.as_inode(); + assert!(rafsinode.get_child_by_name(OsStr::new("bar")).is_ok()); + assert!(rafsinode.get_child_by_index(0).is_ok()); + assert!(rafsinode.get_child_by_index(1).is_err()); + assert_eq!(rafsinode.get_child_index().unwrap(), 2); + + assert_eq!(node.name(), "foo"); + assert_eq!(node.get_name_size(), "foo".len() as u16); + assert_eq!(node.flags(), RafsInodeFlags::SYMLINK.bits()); + assert_eq!(node.get_digest(), digest); + } + + #[test] + fn test_cached_chunk_info_v5() { + let mut info = CachedChunkInfoV5::new(); + info.index = 1024; + info.blob_index = 1; + info.flags = BlobChunkFlags::COMPRESSED; + + assert_eq!(info.index(), 1024 as u32); + assert!(info.is_compressed()); + assert!(!info.is_encrypted()); + let info = info.as_base(); + + assert_eq!(info.blob_index(), 1 as u32); + assert!(info.is_compressed()); + assert!(!info.is_encrypted()); + } } diff --git a/rafs/src/metadata/chunk.rs b/rafs/src/metadata/chunk.rs index 5e8a3446d37..9321fb2bfda 100644 --- a/rafs/src/metadata/chunk.rs +++ b/rafs/src/metadata/chunk.rs @@ -421,3 +421,218 @@ fn to_rafs_v5_chunk_info(cki: &dyn BlobV5ChunkInfo) -> RafsV5ChunkInfo { reserved: 0u32, } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::MockChunkInfo; + use nydus_utils::digest; + + fn test_chunk_wrapper(mut wrapper: ChunkWrapper) { + let dig = RafsDigest::from_buf([0xc; 32].as_slice(), digest::Algorithm::Blake3); + wrapper.set_id(dig); + assert_eq!(wrapper.id().to_owned(), dig); + wrapper.set_blob_index(1024); + assert_eq!(wrapper.blob_index(), 1024); + wrapper.set_compressed_offset(1024); + assert_eq!(wrapper.compressed_offset(), 1024); + wrapper.set_compressed_size(1024); + assert_eq!(wrapper.compressed_size(), 1024); + wrapper.set_uncompressed_offset(1024); + assert_eq!(wrapper.uncompressed_offset(), 1024); + wrapper.set_uncompressed_size(1024); + assert_eq!(wrapper.uncompressed_size(), 1024); + wrapper.set_index(1024); + assert_eq!(wrapper.index(), 1024); + wrapper.set_file_offset(1024); + assert_eq!(wrapper.file_offset(), 1024); + wrapper.set_compressed(true); + assert!(wrapper.is_compressed()); + wrapper.set_batch(true); + assert!(wrapper.is_batch()); + wrapper + .set_chunk_info(2048, 2048, 2048, 2048, 2048, 2048, 2048, true, true) + .unwrap(); + assert_eq!(wrapper.blob_index(), 2048); + assert_eq!(wrapper.compressed_offset(), 2048); + assert_eq!(wrapper.compressed_size(), 2048); + assert_eq!(wrapper.uncompressed_offset(), 2048); + assert_eq!(wrapper.uncompressed_size(), 2048); + assert_eq!(wrapper.file_offset(), 2048); + assert!(wrapper.is_compressed()); + } + + #[test] + fn test_chunk_wrapper_v5() { + let wrapper = ChunkWrapper::new(RafsVersion::V5); + test_chunk_wrapper(wrapper); + let wrapper = ChunkWrapper::Ref(Arc::new(CachedChunkInfoV5::default())); + test_chunk_wrapper(wrapper); + } + + #[test] + fn test_chunk_wrapper_v6() { + let wrapper = ChunkWrapper::new(RafsVersion::V6); + test_chunk_wrapper(wrapper); + let wrapper = ChunkWrapper::Ref(Arc::new(TarfsChunkInfoV6::new(0, 0, 0, 0))); + test_chunk_wrapper(wrapper); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref() { + let wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + assert_eq!(wrapper.id().to_owned(), RafsDigest::default()); + assert_eq!(wrapper.blob_index(), 0); + assert_eq!(wrapper.compressed_offset(), 0); + assert_eq!(wrapper.compressed_size(), 0); + assert_eq!(wrapper.uncompressed_offset(), 0); + assert_eq!(wrapper.uncompressed_size(), 0); + assert_eq!(wrapper.index(), 0); + assert_eq!(wrapper.file_offset(), 0); + assert!(!wrapper.is_compressed()); + assert!(!wrapper.is_batch()); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_id() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + let dig = RafsDigest::from_buf([0xc; 32].as_slice(), digest::Algorithm::Blake3); + wrapper.set_id(dig); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_blob_index() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_blob_index(1024); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_compressed_offset() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_compressed_offset(2048); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_uncompressed_size() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_uncompressed_size(1024); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_uncompressed_offset() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_uncompressed_offset(1024); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_compressed_size() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_compressed_size(2048); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_index() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_index(2048); + } + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_file_offset() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_file_offset(1024); + } + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_compressed() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_compressed(true); + } + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_batch() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.set_batch(true); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_set_chunk_info() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper + .set_chunk_info(2048, 2048, 2048, 2048, 2048, 2048, 2048, true, true) + .unwrap(); + } + + #[test] + #[should_panic] + fn test_chunk_wrapper_ref_ensure_owned() { + let mut wrapper = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + wrapper.ensure_owned(); + } + + fn test_copy_from(mut w1: ChunkWrapper, w2: ChunkWrapper) { + w1.copy_from(&w2); + assert_eq!(w1.blob_index(), w2.blob_index()); + assert_eq!(w1.compressed_offset(), w2.compressed_offset()); + assert_eq!(w1.compressed_size(), w2.compressed_size()); + assert_eq!(w1.uncompressed_offset(), w2.uncompressed_offset()); + assert_eq!(w1.uncompressed_size(), w2.uncompressed_size()); + } + + #[test] + fn test_chunk_wrapper_copy_from() { + let wrapper_v6 = ChunkWrapper::Ref(Arc::new(TarfsChunkInfoV6::new(0, 1, 128, 256))); + let wrapper_v5 = ChunkWrapper::Ref(Arc::new(CachedChunkInfoV5::new())); + test_copy_from(wrapper_v5.clone(), wrapper_v5.clone()); + test_copy_from(wrapper_v5.clone(), wrapper_v6.clone()); + test_copy_from(wrapper_v6.clone(), wrapper_v5); + test_copy_from(wrapper_v6.clone(), wrapper_v6); + } + + #[test] + #[should_panic] + fn test_ref_copy1() { + let wrapper_ref = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + test_copy_from(wrapper_ref.clone(), wrapper_ref); + } + + #[test] + #[should_panic] + fn test_ref_copy2() { + let wrapper_ref = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + let wrapper_v5 = ChunkWrapper::Ref(Arc::new(CachedChunkInfoV5::default())); + test_copy_from(wrapper_ref, wrapper_v5); + } + + #[test] + #[should_panic] + fn test_ref_copy3() { + let wrapper_ref = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + let wrapper_v6 = ChunkWrapper::Ref(Arc::new(TarfsChunkInfoV6::new(0, 0, 0, 0))); + test_copy_from(wrapper_ref, wrapper_v6); + } + + #[test] + #[should_panic] + fn test_ref_copy4() { + let wrapper_ref = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + let wrapper_v6 = ChunkWrapper::Ref(Arc::new(TarfsChunkInfoV6::new(0, 0, 0, 0))); + test_copy_from(wrapper_v6, wrapper_ref); + } + + #[test] + #[should_panic] + fn test_ref_copy5() { + let wrapper_ref = ChunkWrapper::Ref(Arc::new(MockChunkInfo::default())); + let wrapper_v5 = ChunkWrapper::Ref(Arc::new(CachedChunkInfoV5::default())); + test_copy_from(wrapper_v5, wrapper_ref); + } +} diff --git a/rafs/src/metadata/direct_v6.rs b/rafs/src/metadata/direct_v6.rs index 1803475a368..0c8086bc76b 100644 --- a/rafs/src/metadata/direct_v6.rs +++ b/rafs/src/metadata/direct_v6.rs @@ -1565,3 +1565,40 @@ impl BlobV5ChunkInfo for TarfsChunkInfoV6 { self } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_direct_mapping_state() { + let mut meta = RafsSuperMeta::default(); + meta.flags |= RafsSuperFlags::TARTFS_MODE; + let state = DirectMappingState::new(&meta); + assert!(state.is_tarfs()); + assert_eq!(state.block_size(), EROFS_BLOCK_SIZE_512); + + meta.flags &= !RafsSuperFlags::TARTFS_MODE; + let state = DirectMappingState::new(&meta); + assert!(!state.is_tarfs()); + assert_eq!(state.block_size(), EROFS_BLOCK_SIZE_4096); + } + + #[test] + fn test_tarfs_chunk_info_v6() { + let info1 = TarfsChunkInfoV6::new(0x0000_0001, 0x0000_0002, 0x0000_0004, 0x0000_0008); + let _info2 = TarfsChunkInfoV6::from_chunk_addr(&RafsV6InodeChunkAddr::default(), 0); + assert_eq!(info1.chunk_id().to_owned(), TARFS_DIGEST); + assert_eq!(info1.id(), 0x0000_0002); + assert_eq!(info1.blob_index(), 0x0000_0001); + assert_eq!(info1.compressed_offset(), 0x0000_0004); + assert_eq!(info1.uncompressed_offset(), 0x0000_0004); + assert_eq!(info1.compressed_size(), 0x0000_0008); + assert_eq!(info1.uncompressed_size(), 0x0000_0008); + assert!(!info1.is_compressed()); + assert!(!info1.is_encrypted()); + assert_eq!(info1.index(), 0x0000_0002); + assert_eq!(info1.file_offset(), 0x0000_0000); + assert_eq!(info1.flags(), BlobChunkFlags::empty()); + } +} diff --git a/rafs/src/metadata/inode.rs b/rafs/src/metadata/inode.rs index 780401a9102..19332284818 100644 --- a/rafs/src/metadata/inode.rs +++ b/rafs/src/metadata/inode.rs @@ -758,3 +758,369 @@ impl Default for RafsInodeFlags { RafsInodeFlags::empty() } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + metadata::{direct_v5::DirectSuperBlockV5, RafsSuperMeta}, + mock::MockInode, + }; + + #[test] + fn test_inode_wrapper() { + let mut wrapper_v5 = InodeWrapper::new(RafsVersion::V5); + let mut wrapper_v6 = InodeWrapper::new(RafsVersion::V6); + let mut wrapper_cache_v5 = + InodeWrapper::from_inode_info(Arc::new(CachedInodeV5::default())); + let wrapper_ondisk_v5 = InodeWrapper::from_inode_info(Arc::new(OndiskInodeWrapperV5 { + mapping: DirectSuperBlockV5::new(&RafsSuperMeta::default(), false), + offset: 0, + })); + + assert!(wrapper_v5.is_v5()); + assert!(!wrapper_v6.is_v5()); + assert!(wrapper_cache_v5.is_v5()); + assert!(wrapper_ondisk_v5.is_v5()); + assert!(!wrapper_v5.is_v6()); + assert!(wrapper_v6.is_v6()); + assert!(!wrapper_cache_v5.is_v6()); + assert!(!wrapper_ondisk_v5.is_v6()); + assert_eq!(wrapper_v5.inode_size(), 128); + + wrapper_v5.set_mode(0x0000_0001); + wrapper_v6.set_mode(0x0000_0002); + assert_eq!(wrapper_v5.mode(), 0x0000_0001); + assert_eq!(wrapper_v6.mode(), 0x0000_0002); + + assert!(!wrapper_v5.is_hardlink()); + assert!(!wrapper_v6.is_hardlink()); + assert!(!wrapper_cache_v5.is_hardlink()); + assert!(!wrapper_v5.is_symlink()); + assert!(!wrapper_v6.is_symlink()); + assert!(!wrapper_cache_v5.is_symlink()); + assert!(!wrapper_v5.is_chrdev()); + assert!(!wrapper_v6.is_chrdev()); + assert!(!wrapper_cache_v5.is_chrdev()); + assert!(!wrapper_v5.is_blkdev()); + assert!(!wrapper_v6.is_blkdev()); + assert!(!wrapper_v5.is_fifo()); + assert!(!wrapper_v6.is_fifo()); + assert!(!wrapper_v5.is_sock()); + assert!(!wrapper_v6.is_sock()); + assert!(!wrapper_cache_v5.is_sock()); + assert!(!wrapper_v5.has_hardlink()); + assert!(!wrapper_v6.has_hardlink()); + wrapper_v5.set_has_hardlink(true); + wrapper_v6.set_has_hardlink(true); + assert!(wrapper_v5.has_hardlink()); + assert!(wrapper_v6.has_hardlink()); + wrapper_v5.set_has_hardlink(false); + wrapper_v6.set_has_hardlink(false); + assert!(!wrapper_v5.has_hardlink()); + assert!(!wrapper_v6.has_hardlink()); + assert!(!wrapper_v5.has_xattr()); + assert!(!wrapper_v6.has_xattr()); + assert!(!wrapper_cache_v5.has_xattr()); + wrapper_v5.set_has_xattr(true); + wrapper_v6.set_has_xattr(true); + assert!(wrapper_v5.has_xattr()); + assert!(wrapper_v6.has_xattr()); + wrapper_v5.set_has_xattr(false); + wrapper_v6.set_has_xattr(false); + assert!(!wrapper_v5.has_xattr()); + assert!(!wrapper_v6.has_xattr()); + wrapper_v5.set_ino(0x0000_0001); + wrapper_v6.set_ino(0x0000_0002); + assert_eq!(wrapper_v5.ino(), 0x0000_0001); + assert_eq!(wrapper_v6.ino(), 0x0000_0002); + wrapper_v5.set_parent(0x0000_0004); + assert_eq!(wrapper_v5.parent(), 0x0000_0004); + assert_eq!(wrapper_cache_v5.size(), 0); + wrapper_v5.set_uid(0x0000_0001); + wrapper_v6.set_uid(0x0000_0002); + assert_eq!(wrapper_v5.uid(), 0x0000_0001); + assert_eq!(wrapper_v6.uid(), 0x0000_0002); + wrapper_v5.set_gid(0x0000_0001); + wrapper_v6.set_gid(0x0000_0002); + assert_eq!(wrapper_v5.gid(), 0x0000_0001); + assert_eq!(wrapper_v6.gid(), 0x0000_0002); + wrapper_v5.set_mtime(0x0000_0004); + wrapper_v6.set_mtime(0x0000_0008); + assert_eq!(wrapper_v5.mtime(), 0x0000_0004); + assert_eq!(wrapper_v6.mtime(), 0x0000_0008); + assert_eq!(wrapper_cache_v5.mtime(), 0x0000_0000); + wrapper_v5.set_mtime_nsec(0x0000_0004); + wrapper_v6.set_mtime_nsec(0x0000_0008); + assert_eq!(wrapper_v5.mtime_nsec(), 0x0000_0004); + assert_eq!(wrapper_v6.mtime_nsec(), 0x0000_0008); + assert_eq!(wrapper_cache_v5.mtime_nsec(), 0x0000_0000); + wrapper_v5.set_blocks(0x0000_0010); + wrapper_v6.set_blocks(0x0000_0020); + assert_eq!(wrapper_v5.blocks(), 0x0000_0010); + assert_eq!(wrapper_v6.blocks(), 0x0000_0020); + assert_eq!(wrapper_cache_v5.blocks(), 0x0000_0000); + wrapper_v5.set_rdev(0x0000_0010); + wrapper_v6.set_rdev(0x0000_0020); + assert_eq!(wrapper_v5.rdev(), 0x0000_0010); + assert_eq!(wrapper_v6.rdev(), 0x0000_0020); + assert_eq!(wrapper_cache_v5.rdev(), 0x0000_0000); + wrapper_v5.set_projid(0x0000_0100); + wrapper_v6.set_projid(0x0000_0200); + wrapper_v5.set_nlink(0x0000_0010); + wrapper_v6.set_nlink(0x0000_0020); + assert_eq!(wrapper_v5.nlink(), 0x0000_0010); + assert_eq!(wrapper_v6.nlink(), 0x0000_0020); + assert_eq!(wrapper_cache_v5.nlink(), 0x0000_0000); + wrapper_v5.set_name_size(0x0000_0010); + wrapper_v6.set_name_size(0x0000_0020); + assert_eq!(wrapper_v5.name_size(), 0x0000_0010); + assert_eq!(wrapper_v6.name_size(), 0x0000_0020); + assert_eq!(wrapper_cache_v5.name_size(), 0x0000_0000); + wrapper_v5.set_symlink_size(0x0000_0010); + wrapper_v6.set_symlink_size(0x0000_0020); + assert_eq!(wrapper_v5.symlink_size(), 0x0000_0010); + assert_eq!(wrapper_v6.symlink_size(), 0x0000_0020); + assert_eq!(wrapper_cache_v5.symlink_size(), 0x0000_0000); + wrapper_v5.set_child_index(0x0000_0010); + wrapper_v6.set_child_index(0x0000_0020); + wrapper_cache_v5.set_child_index(0x0000_0008); + assert_eq!(wrapper_v5.child_index(), 0x0000_0010); + assert_eq!(wrapper_v6.child_index(), u32::MAX); + assert_eq!(wrapper_cache_v5.child_index(), 0x0000_0008); + wrapper_v5.set_child_count(0x0000_0010); + wrapper_v6.set_child_count(0x0000_0020); + assert_eq!(wrapper_v5.child_count(), 0x0000_0010); + assert_eq!(wrapper_v6.child_count(), 0x0000_0020); + assert_eq!(wrapper_cache_v5.child_count(), 0x0000_0000); + wrapper_v5.create_chunk(); + wrapper_v6.create_chunk(); + } + + #[test] + #[should_panic] + fn test_inode_size_v6() { + let wrapper_v6 = InodeWrapper::new(RafsVersion::V6); + wrapper_v6.inode_size(); + } + + #[test] + #[should_panic] + fn test_inode_size_ref() { + let wrapper_cache_v5 = InodeWrapper::from_inode_info(Arc::new(CachedInodeV5::default())); + wrapper_cache_v5.inode_size(); + } + + #[test] + #[should_panic] + fn test_set_mode_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_mode(0x0000_0001); + } + + #[test] + #[should_panic] + fn test_is_blk_dev_ref() { + let wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.is_blkdev(); + } + + #[test] + #[should_panic] + fn test_is_fifo_ref() { + let wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.is_fifo(); + } + + #[test] + #[should_panic] + fn test_has_hardlink_ref() { + let wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.has_hardlink(); + } + + #[test] + #[should_panic] + fn test_set_has_hardlink_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_has_hardlink(true); + } + + #[test] + #[should_panic] + fn test_set_has_xattr_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_has_xattr(true); + } + + #[test] + #[should_panic] + fn test_set_ino_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_ino(Inode::default()); + } + + #[test] + #[should_panic] + fn test_set_parent_v6() { + let mut wrapper_v6 = InodeWrapper::new(RafsVersion::V6); + wrapper_v6.set_parent(Inode::default()); + } + + #[test] + #[should_panic] + fn test_set_parent_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_parent(Inode::default()); + } + + #[test] + #[should_panic] + fn test_get_parent_v6() { + let wrapper_v6 = InodeWrapper::new(RafsVersion::V6); + wrapper_v6.parent(); + } + + #[test] + #[should_panic] + fn test_get_parent_ref() { + let wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.parent(); + } + + #[test] + #[should_panic] + fn test_set_size_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_size(0x0000_0001); + } + + #[test] + #[should_panic] + fn test_set_uid_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_uid(0x0000_0000_0001); + } + + #[test] + #[should_panic] + fn test_set_gid_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_gid(0x0000_0001); + } + + #[test] + #[should_panic] + fn test_set_mtime_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_mtime(0x0000_0001); + } + + #[test] + #[should_panic] + fn test_set_mtime_nsec_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_mtime_nsec(0x0000_0001); + } + + #[test] + #[should_panic] + fn test_set_blocks_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_blocks(0x0000_0001); + } + + #[test] + #[should_panic] + fn test_set_rdev_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_rdev(0x0000_0001); + } + + #[test] + #[should_panic] + fn test_set_projid_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_projid(0x0000_0001); + } + + #[test] + #[should_panic] + fn test_set_digest_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_digest(RafsDigest::default()); + } + + #[test] + #[should_panic] + fn test_get_digest_v6() { + let wrapper_v6 = InodeWrapper::new(RafsVersion::V6); + wrapper_v6.digest(); + } + + #[test] + #[should_panic] + fn test_set_namesize_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_name_size(0x0000_0000); + } + + #[test] + #[should_panic] + fn test_set_symlink_size_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_symlink_size(0x0000_0000); + } + + #[test] + #[should_panic] + fn test_set_child_count_ref() { + let mut wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.set_child_count(0x0000_0000); + } + + #[test] + #[should_panic] + fn test_create_chunk_ref() { + let wrapper_mock = InodeWrapper::from_inode_info(Arc::new(MockInode::default())); + wrapper_mock.create_chunk(); + } + + #[test] + fn test_rafs_v6_inode() { + let mut inode = RafsV6Inode { + i_ino: 0x0000_0000, + i_uid: 0x0000_0001, + i_gid: 0x0000_0002, + i_projid: 0x0000_0003, + i_mode: 0x0000_0000, + i_size: 0x0000_0005, + i_blocks: 0x0000_0006, + i_flags: RafsInodeFlags::default(), + i_nlink: 0x0000_0007, + i_child_count: 0x0000_0008, + i_name_size: 0x0000_0010, + i_symlink_size: 0x0000_0011, + i_rdev: 0x0000_0012, + i_mtime_nsec: 0x0000_0013, + i_mtime: 0x0000_0014, + }; + + inode.set_name_size(0x0000_0001); + inode.set_symlink_size(0x0000_0002); + + assert_eq!(inode.i_name_size, 0x0000_0001); + assert_eq!(inode.i_symlink_size, 0x0000_0002); + assert_eq!(inode.uidgid(), (0x0000_0001, 0x0000_0002)); + assert_eq!(inode.mtime(), (0x0000_0014 as u64, 0x0000_0013)); + assert_eq!(inode.mode(), 0x0000_0000); + assert!(!inode.is_chrdev()); + assert!(!inode.is_blkdev()); + assert!(!inode.is_fifo()); + assert!(!inode.is_sock()); + assert!(!inode.is_hardlink()); + assert!(!inode.has_hardlink()); + assert!(!inode.has_xattr()); + assert!(!inode.has_hole()); + } +} diff --git a/rafs/src/metadata/layout/v6.rs b/rafs/src/metadata/layout/v6.rs index a5fba6f9b16..32f9be5f265 100644 --- a/rafs/src/metadata/layout/v6.rs +++ b/rafs/src/metadata/layout/v6.rs @@ -2193,6 +2193,7 @@ impl RafsV6PrefetchTable { #[cfg(test)] mod tests { use super::*; + use crate::metadata::RafsVersion; use crate::{BufWriter, RafsIoRead}; use std::fs::OpenOptions; use std::io::Write; @@ -2512,4 +2513,296 @@ mod tests { addr.set_block_addr(179); assert_eq!(addr.block_addr(), 179); } + + #[test] + fn test_rsfs_v6_super_block() { + let mut blk = RafsV6SuperBlock::new(); + assert!(blk.validate(0).is_err()); + + blk.set_inos(10); + blk.set_blocks(100); + blk.set_root_nid(1000); + assert_eq!(blk.s_inos, 10); + assert_eq!(blk.s_blocks, 100); + assert_eq!(blk.s_root_nid, 1000); + + blk.set_block_bits(EROFS_BLOCK_BITS_9); + blk.set_meta_addr(1024 * 1024); + assert_eq!( + blk.s_meta_blkaddr, + (1024 * 1024) / EROFS_BLOCK_SIZE_512 as u32 + ); + + blk.set_block_bits(EROFS_BLOCK_BITS_12); + blk.set_meta_addr(1024 * 1024); + assert_eq!( + blk.s_meta_blkaddr, + (1024 * 1024) / EROFS_BLOCK_SIZE_4096 as u32 + ); + } + + #[test] + fn test_rafs_v6_super_block_ext() { + let mut ext = RafsV6SuperBlockExt::new(); + ext.set_compressor(compress::Algorithm::GZip); + ext.set_has_xattr(); + ext.set_explicit_uidgid(); + ext.set_inlined_chunk_digest(); + ext.set_tarfs_mode(); + ext.set_digester(digest::Algorithm::Blake3); + ext.set_chunk_table(1024, 1024); + ext.set_cipher(crypt::Algorithm::Aes128Xts); + + assert_ne!(ext.s_flags & RafsSuperFlags::COMPRESSION_GZIP.bits(), 0); + assert_ne!(ext.s_flags & RafsSuperFlags::HAS_XATTR.bits(), 0); + assert_ne!(ext.s_flags & RafsSuperFlags::EXPLICIT_UID_GID.bits(), 0); + assert_ne!(ext.s_flags & RafsSuperFlags::INLINED_CHUNK_DIGEST.bits(), 0); + assert_ne!(ext.s_flags & RafsSuperFlags::TARTFS_MODE.bits(), 0); + assert_ne!(ext.s_flags & RafsSuperFlags::HASH_BLAKE3.bits(), 0); + assert_eq!(ext.chunk_table_size(), 1024); + assert_eq!(ext.chunk_table_offset(), 1024); + assert_ne!( + ext.s_flags & RafsSuperFlags::ENCRYPTION_ASE_128_XTS.bits(), + 0 + ); + } + + #[test] + fn test_rafs_v6_inode_compact() { + let mut cpt = RafsV6InodeCompact::new(); + cpt.set_size(1024); + cpt.set_ino(10); + cpt.set_nlink(2048); + cpt.set_mode(1); + cpt.set_u(8); + cpt.set_uidgid(1, 1); + cpt.set_mtime(1, 1000); + cpt.set_rdev(20); + cpt.set_xattr_inline_count(10); + cpt.set_data_layout(1); + assert_eq!(cpt.format().to_le(), 2); + assert_eq!(cpt.mode(), 1); + assert_eq!(cpt.size(), 1024); + assert_eq!(cpt.union(), 8); + assert_eq!(cpt.ino(), 10); + assert_eq!(cpt.ugid(), (1, 1)); + assert_eq!(cpt.mtime_s_ns(), (0, 0)); + assert_eq!(cpt.nlink(), 2048); + assert_eq!(cpt.rdev(), 0); + assert_eq!(cpt.xattr_inline_count(), 10); + } + + #[test] + fn test_rafs_v6_inode_extended_inode() { + let mut ext = RafsV6InodeExtended::new(); + ext.set_size(1024); + ext.set_ino(1024); + ext.set_nlink(1024); + ext.set_mode(1024); + ext.set_u(1024); + ext.set_rdev(1024); + ext.set_xattr_inline_count(1024); + + assert_eq!(ext.format(), 1); + assert_eq!(ext.mode(), 1024); + assert_eq!(ext.size(), 1024); + assert_eq!(ext.union(), 1024); + assert_eq!(ext.ino(), 1024); + assert_eq!(ext.ugid(), (0, 0)); + assert_eq!(ext.mtime_s_ns(), (0, 0)); + assert_eq!(ext.nlink(), 1024); + assert_eq!(ext.rdev(), 1024); + assert_eq!(ext.xattr_inline_count(), 1024); + } + + #[test] + fn test_v6_inode() { + let i = new_v6_inode( + &InodeWrapper::new(RafsVersion::V6), + EROFS_INODE_FLAT_INLINE, + 1024, + true, + ); + assert_eq!(i.ino(), 0); + assert_eq!(i.size(), 0); + assert_eq!(i.mtime_s_ns(), (0, 0)); + assert_eq!(i.nlink(), 0); + } + + #[test] + fn test_rafs_v6_dirent() { + let mut dir = RafsV6Dirent::new(0, 1024, EROFS_FILE_TYPE::EROFS_FT_BLKDEV as u8); + dir.set_name_offset(2048); + assert_eq!(dir.e_nameoff, 2048); + + assert_eq!( + RafsV6Dirent::file_type(libc::S_IFREG), + EROFS_FILE_TYPE::EROFS_FT_REG_FILE as u8 + ); + assert_eq!( + RafsV6Dirent::file_type(libc::S_IFDIR), + EROFS_FILE_TYPE::EROFS_FT_DIR as u8 + ); + assert_eq!( + RafsV6Dirent::file_type(libc::S_IFCHR), + EROFS_FILE_TYPE::EROFS_FT_CHRDEV as u8 + ); + assert_eq!( + RafsV6Dirent::file_type(libc::S_IFBLK), + EROFS_FILE_TYPE::EROFS_FT_BLKDEV as u8 + ); + assert_eq!( + RafsV6Dirent::file_type(libc::S_IFIFO), + EROFS_FILE_TYPE::EROFS_FT_FIFO as u8 + ); + assert_eq!( + RafsV6Dirent::file_type(libc::S_IFSOCK), + EROFS_FILE_TYPE::EROFS_FT_SOCK as u8 + ); + assert_eq!( + RafsV6Dirent::file_type(libc::S_IFLNK), + EROFS_FILE_TYPE::EROFS_FT_SYMLINK as u8 + ); + } + + #[test] + fn test_rafs_v6_inode_chunk_header() { + let hdr = RafsV6InodeChunkHeader::new(0x1000_0000, EROFS_BLOCK_SIZE_4096); + let val = hdr.to_u32(); + let newhdr = RafsV6InodeChunkHeader::from_u32(val); + assert_eq!(newhdr.format, hdr.format); + assert_eq!(newhdr.reserved, hdr.reserved); + } + #[test] + fn test_align_offset() { + assert_eq!(align_offset(1099, 8), 1104); + assert_eq!(align_offset(1099, 16), 1104); + assert_eq!(align_offset(1099, 32), 1120); + } + #[test] + fn test_calculate_nid() { + assert_eq!(calculate_nid(1024, 512), 16); + assert_eq!(calculate_nid(1024, 768), 8); + assert_eq!(calculate_nid(2048, 768), 40); + } + + #[test] + fn test_rafs_v6_blob() { + let mut blob = RafsV6Blob { + cipher_algo: crypt::Algorithm::Aes256Gcm as u32, + ..RafsV6Blob::default() + }; + assert!(blob.to_blob_info().is_err()); + + blob.blob_id = [0x1u8; BLOB_SHA256_LEN]; + blob.blob_meta_digest = [0xcu8; 32]; + blob.blob_meta_digest[31] = 0xau8; + + blob.cipher_algo = crypt::Algorithm::Aes128Xts as u32; + let info: BlobInfo = blob.to_blob_info().unwrap(); + RafsV6Blob::from_blob_info(&info).unwrap(); + assert!(RafsV6Blob::from_blob_info(&info).is_ok()); + + blob.cipher_algo = crypt::Algorithm::None as u32; + let info: BlobInfo = blob.to_blob_info().unwrap(); + RafsV6Blob::from_blob_info(&info).unwrap(); + assert!(RafsV6Blob::from_blob_info(&info).is_ok()); + } + + #[test] + fn test_rafs_v6_blob_table() { + let mut table = RafsV6BlobTable::new(); + assert_eq!(table.size(), 0); + table.add( + "0".to_string(), + 0, + 0, + 1024, + 10, + 0, + 0, + RafsSuperFlags { bits: 0 }, + [0; 32], + [0; 32], + 0, + 0, + BlobCompressionContextHeader::default(), + Arc::new(crypt::Algorithm::Aes128Xts.new_cipher().unwrap()), + Some(CipherContext::default()), + ); + assert_eq!(table.size(), size_of::()); + assert!(table.get(0).is_ok()); + assert!(table.get(1).is_err()); + } + + fn get_streams() -> (Box, BufWriter) { + let temp = TempFile::new().unwrap(); + let w = OpenOptions::new() + .read(true) + .write(true) + .open(temp.as_path()) + .unwrap(); + let r = OpenOptions::new() + .read(true) + .write(false) + .open(temp.as_path()) + .unwrap(); + let writer: BufWriter = BufWriter::new(w); + let reader: Box = Box::new(r); + (reader, writer) + } + + #[test] + fn test_rafs_v6_blob_table_store() { + let mut table = RafsV6BlobTable::new(); + table.add( + "0".to_string(), + 0, + 0, + 1024, + 10, + 0, + 0, + RafsSuperFlags { bits: 0 }, + [0; 32], + [0; 32], + 0, + 0, + BlobCompressionContextHeader::default(), + Arc::new(crypt::Algorithm::Aes128Xts.new_cipher().unwrap()), + Some(CipherContext::default()), + ); + + let (_reader, mut writer) = get_streams(); + table.store(&mut writer).unwrap(); + writer.flush().unwrap(); + } + + #[test] + fn test_rafs_v6_xattr_entry() { + let ent = RafsV6XattrEntry::new(); + assert_eq!(ent.name_index(), 0); + assert_eq!(ent.name_len(), 0); + assert_eq!(ent.value_size(), 0); + } + + #[test] + fn test_rafs_prefetch_table() { + let mut table = RafsV6PrefetchTable::new(); + assert_eq!(table.size(), 0); + assert_eq!(table.len(), 0); + assert!(table.is_empty()); + table.add_entry(0); + table.add_entry(1); + assert_eq!(table.len(), 2); + assert!(!table.is_empty()); + + let (mut reader, mut writer) = get_streams(); + table.store(&mut writer).unwrap(); + writer.flush().unwrap(); + table.inodes.clear(); + assert_eq!(table.len(), 0); + assert!(table.load_prefetch_table_from(&mut reader, 0, 2).is_ok()); + assert_eq!(table.len(), 2); + } } diff --git a/rafs/src/metadata/md_v5.rs b/rafs/src/metadata/md_v5.rs index 039dcd05811..fd1362b2731 100644 --- a/rafs/src/metadata/md_v5.rs +++ b/rafs/src/metadata/md_v5.rs @@ -258,5 +258,24 @@ impl BlobChunkInfo for V5IoChunk { #[cfg(test)] mod tests { + use super::*; // TODO: add unit test cases for RafsSuper::{try_load_v5, amplify_io} + #[test] + fn test_v5_io_chunk() { + let info = V5IoChunk { + block_id: RafsDigest::default().into(), + blob_index: 2, + index: 3, + compressed_offset: 1024, + uncompressed_offset: 2048, + compressed_size: 10, + uncompressed_size: 20, + flags: BlobChunkFlags::BATCH, + }; + + assert_eq!(info.chunk_id(), &RafsDigest::default()); + assert_eq!(info.id(), 3); + assert!(!info.is_compressed()); + assert!(!info.is_encrypted()); + } } diff --git a/rafs/src/metadata/mod.rs b/rafs/src/metadata/mod.rs index 88ee2cd1837..0c12fc3dd88 100644 --- a/rafs/src/metadata/mod.rs +++ b/rafs/src/metadata/mod.rs @@ -1173,4 +1173,145 @@ mod tests { digest::Algorithm::Blake3 ); } + + #[test] + fn test_rafs_crypt_from() { + assert_eq!( + crypt::Algorithm::from(RafsSuperFlags::ENCRYPTION_ASE_128_XTS), + crypt::Algorithm::Aes128Xts + ); + assert_eq!( + crypt::Algorithm::from(RafsSuperFlags::empty()), + crypt::Algorithm::None + ); + } + + #[test] + fn test_rafs_super_meta() { + let mut meta = RafsSuperMeta::default(); + assert!(!meta.has_xattr()); + assert!(!meta.has_inlined_chunk_digest()); + assert_eq!(meta.get_compressor(), compress::Algorithm::None); + assert_eq!(meta.get_digester(), digest::Algorithm::Blake3); + assert_eq!(meta.get_cipher(), crypt::Algorithm::None); + + meta.version = RAFS_SUPER_VERSION_V6; + meta.flags |= RafsSuperFlags::INLINED_CHUNK_DIGEST; + meta.flags |= RafsSuperFlags::HASH_SHA256; + meta.flags |= RafsSuperFlags::COMPRESSION_GZIP; + meta.flags |= RafsSuperFlags::ENCRYPTION_ASE_128_XTS; + + assert!(meta.has_inlined_chunk_digest()); + assert_eq!(meta.get_compressor(), compress::Algorithm::GZip); + assert_eq!(meta.get_digester(), digest::Algorithm::Sha256); + assert_eq!(meta.get_cipher(), crypt::Algorithm::Aes128Xts); + + meta.version = RAFS_SUPER_VERSION_V5; + assert_eq!(meta.get_compressor(), compress::Algorithm::GZip); + assert_eq!(meta.get_digester(), digest::Algorithm::Sha256); + assert_eq!(meta.get_cipher(), crypt::Algorithm::None); + + let cfg = meta.get_config(); + assert!(cfg.check_compatibility(&meta).is_ok()); + } + + #[test] + fn test_rafs_super_new() { + let cfg = RafsConfigV2 { + mode: "direct".into(), + ..RafsConfigV2::default() + }; + let mut rs = RafsSuper::new(&cfg).unwrap(); + rs.destroy(); + } + + fn get_meta( + chunk_size: u32, + explice_uidgid: bool, + tartfs_mode: bool, + hash: RafsSuperFlags, + comp: RafsSuperFlags, + crypt: RafsSuperFlags, + version: u32, + ) -> RafsSuperMeta { + let mut meta = RafsSuperMeta { + chunk_size, + ..Default::default() + }; + if explice_uidgid { + meta.flags |= RafsSuperFlags::EXPLICIT_UID_GID; + } + if tartfs_mode { + meta.flags |= RafsSuperFlags::TARTFS_MODE; + } + meta.flags |= hash; + meta.flags |= comp; + meta.flags |= crypt; + meta.version = version; + meta + } + + #[test] + fn test_rafs_super_config_check_compatibility_fail() { + let meta1 = get_meta( + 1024 as u32, + true, + true, + RafsSuperFlags::HASH_BLAKE3, + RafsSuperFlags::COMPRESSION_GZIP, + RafsSuperFlags::ENCRYPTION_ASE_128_XTS, + RAFS_SUPER_VERSION_V5, + ); + let meta2 = get_meta( + 2048 as u32, + true, + true, + RafsSuperFlags::HASH_BLAKE3, + RafsSuperFlags::COMPRESSION_GZIP, + RafsSuperFlags::ENCRYPTION_ASE_128_XTS, + RAFS_SUPER_VERSION_V5, + ); + let meta3 = get_meta( + 1024 as u32, + false, + true, + RafsSuperFlags::HASH_BLAKE3, + RafsSuperFlags::COMPRESSION_GZIP, + RafsSuperFlags::ENCRYPTION_ASE_128_XTS, + RAFS_SUPER_VERSION_V5, + ); + let meta4 = get_meta( + 1024 as u32, + true, + false, + RafsSuperFlags::HASH_BLAKE3, + RafsSuperFlags::COMPRESSION_GZIP, + RafsSuperFlags::ENCRYPTION_ASE_128_XTS, + RAFS_SUPER_VERSION_V5, + ); + let meta5 = get_meta( + 1024 as u32, + true, + true, + RafsSuperFlags::HASH_SHA256, + RafsSuperFlags::COMPRESSION_GZIP, + RafsSuperFlags::ENCRYPTION_ASE_128_XTS, + RAFS_SUPER_VERSION_V5, + ); + let meta6 = get_meta( + 1024 as u32, + true, + true, + RafsSuperFlags::HASH_BLAKE3, + RafsSuperFlags::COMPRESSION_GZIP, + RafsSuperFlags::ENCRYPTION_NONE, + RAFS_SUPER_VERSION_V6, + ); + + assert!(meta1.get_config().check_compatibility(&meta2).is_err()); + assert!(meta1.get_config().check_compatibility(&meta3).is_err()); + assert!(meta1.get_config().check_compatibility(&meta4).is_err()); + assert!(meta1.get_config().check_compatibility(&meta5).is_err()); + assert!(meta1.get_config().check_compatibility(&meta6).is_err()); + } } diff --git a/rafs/src/metadata/noop.rs b/rafs/src/metadata/noop.rs index f67970b71f2..2c16a289b2a 100644 --- a/rafs/src/metadata/noop.rs +++ b/rafs/src/metadata/noop.rs @@ -66,3 +66,56 @@ impl RafsSuperBlock for NoopSuperBlock { unimplemented!("used by RAFS v6 only") } } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + #[should_panic] + fn test_get_max_ino() { + let blk = NoopSuperBlock::new(); + blk.get_max_ino(); + } + + #[test] + #[should_panic] + fn test_get_inode() { + let blk = NoopSuperBlock::new(); + blk.get_inode(Inode::default(), false).unwrap(); + } + + #[test] + #[should_panic] + fn test_get_extended_inode() { + let blk = NoopSuperBlock::new(); + blk.get_extended_inode(Inode::default(), false).unwrap(); + } + + #[test] + #[should_panic] + fn test_root_ino() { + let blk = NoopSuperBlock::new(); + blk.root_ino(); + } + + #[test] + #[should_panic] + fn test_get_chunk_info() { + let blk = NoopSuperBlock::new(); + blk.get_chunk_info(0).unwrap(); + } + + #[test] + #[should_panic] + fn test_set_blob_device() { + let blk = NoopSuperBlock::new(); + blk.set_blob_device(BlobDevice::default()); + } + + #[test] + fn test_noop_super_block() { + let mut blk = NoopSuperBlock::new(); + assert!(blk.get_blob_infos().is_empty()); + blk.destroy(); + } +} diff --git a/rafs/src/mock/mock_chunk.rs b/rafs/src/mock/mock_chunk.rs index 9a01340ffd9..0472cf9c029 100644 --- a/rafs/src/mock/mock_chunk.rs +++ b/rafs/src/mock/mock_chunk.rs @@ -94,3 +94,43 @@ impl BlobV5ChunkInfo for MockChunkInfo { self } } + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use nydus_utils::digest::{Algorithm, RafsDigest}; + use storage::device::{v5::BlobV5ChunkInfo, BlobChunkFlags, BlobChunkInfo}; + + use super::MockChunkInfo; + + #[test] + fn test_mock_chunk_info() { + let mut info = MockChunkInfo::mock(0, 1024, 512, 2048, 512); + let digest = RafsDigest::from_buf("foobar".as_bytes(), Algorithm::Blake3); + info.c_block_id = Arc::new(digest); + info.c_blob_index = 1; + info.c_flags = BlobChunkFlags::COMPRESSED; + info.c_index = 2; + + let base = info.as_base(); + let any = info.as_any(); + let rev = any.downcast_ref::().unwrap(); + + assert_eq!(info.chunk_id().data, digest.data); + assert_eq!(info.id(), 2); + assert_eq!(base.id(), rev.id()); + assert!(info.is_compressed()); + assert!(!info.is_encrypted()); + assert_eq!(info.blob_index(), 1); + assert_eq!(info.flags(), BlobChunkFlags::COMPRESSED); + assert_eq!(info.compressed_offset(), 1024); + assert_eq!(info.compressed_size(), 512); + assert_eq!(info.compressed_end(), 1024 + 512); + + assert_eq!(info.uncompressed_offset(), 2048); + assert_eq!(info.uncompressed_size(), 512); + assert_eq!(info.uncompressed_end(), 2048 + 512); + assert_eq!(info.file_offset(), 0); + } +} diff --git a/rafs/src/mock/mock_inode.rs b/rafs/src/mock/mock_inode.rs index 6c62180a150..b96a8d36ba5 100644 --- a/rafs/src/mock/mock_inode.rs +++ b/rafs/src/mock/mock_inode.rs @@ -308,3 +308,97 @@ impl RafsV5InodeOps for MockInode { false } } + +#[cfg(test)] +mod tests { + use nydus_utils::digest::Algorithm; + + use crate::metadata::layout::RAFS_V5_ROOT_INODE; + + use super::*; + + #[test] + fn test_mock_node() { + let size = 20; + let mut chunks = Vec::>::new(); + let digest = RafsDigest::from_buf("foobar".as_bytes(), Algorithm::Blake3); + let info = MockChunkInfo::mock(0, 1024, 1024, 2048, 1024); + + chunks.push(Arc::new(info.clone())); + chunks.push(Arc::new(info)); + + let mut node = MockInode::mock(13, size, chunks); + node.i_flags = RafsInodeFlags::XATTR; + node.i_mode = libc::S_IFDIR; + node.i_name = "foo".into(); + node.i_digest = digest; + node.i_parent = RAFS_V5_ROOT_INODE; + let mut child_node1 = MockInode::mock(14, size, Vec::>::new()); + child_node1.i_name = OsStr::new("child1").into(); + child_node1.i_size = 10; + let mut child_node2 = MockInode::mock(15, size, Vec::>::new()); + child_node2.i_name = OsStr::new("child2").into(); + child_node1.i_size = 20; + + node.i_child.push(Arc::new(child_node1)); + node.i_child.push(Arc::new(child_node2)); + node.i_child_cnt = 2; + node.i_child_idx = 2; + + node.i_xattr.insert("attr1".into(), "bar1".into()); + node.i_xattr.insert("attr2".into(), "bar2".into()); + node.i_xattr.insert("attr3".into(), "bar3".into()); + + node.i_data.push(Arc::new(MockChunkInfo::default())); + + assert!(node.validate(0, 0).is_ok()); + assert_eq!(node.ino(), 13); + assert_eq!(node.size(), 20); + assert_eq!(node.rdev(), 0); + assert_eq!(node.projid(), 0); + assert_eq!(node.name(), "foo"); + assert_eq!(node.flags(), RafsInodeFlags::XATTR.bits()); + assert_eq!(node.get_digest(), digest); + assert_eq!(node.get_name_size(), "foo".len() as u16); + assert!(node.get_chunk_info(0).is_ok()); + assert!(node.get_chunk_info_v5(0).is_ok()); + assert_eq!(node.parent(), RAFS_V5_ROOT_INODE); + assert!(node.get_blob_by_index(0).is_ok()); + assert_eq!(node.get_chunk_size(), CHUNK_SIZE); + assert!(!node.has_hole()); + + let ent = node.get_entry(); + assert_eq!(ent.inode, node.ino()); + assert_eq!(ent.attr_timeout, node.i_meta.attr_timeout); + assert_eq!(ent.entry_timeout, node.i_meta.entry_timeout); + assert_eq!(ent.attr, node.get_attr().into()); + + assert!(node.get_symlink().is_err()); + assert_eq!(node.get_symlink_size(), 0 as u16); + + assert!(node.get_child_by_name(OsStr::new("child1")).is_ok()); + assert!(node.get_child_by_index(0).is_ok()); + assert!(node.get_child_by_index(1).is_ok()); + assert_eq!(node.get_child_count(), 2 as u32); + assert_eq!(node.get_child_index().unwrap(), 2 as u32); + assert_eq!(node.get_chunk_count(), 2 as u32); + assert!(node.has_xattr()); + assert_eq!( + node.get_xattr(OsStr::new("attr2")).unwrap().unwrap(), + "bar2".as_bytes() + ); + assert_eq!(node.get_xattrs().unwrap().len(), 3); + + assert!(!node.is_blkdev()); + assert!(!node.is_chrdev()); + assert!(!node.is_sock()); + assert!(!node.is_fifo()); + assert!(node.is_dir()); + assert!(!node.is_symlink()); + assert!(!node.is_reg()); + assert!(!node.is_hardlink()); + let mut inodes = Vec::>::new(); + node.collect_descendants_inodes(&mut inodes).unwrap(); + assert_eq!(inodes.len(), 2); + } +} diff --git a/rafs/src/mock/mock_super.rs b/rafs/src/mock/mock_super.rs index 101d62fcad9..50652ddf676 100644 --- a/rafs/src/mock/mock_super.rs +++ b/rafs/src/mock/mock_super.rs @@ -74,3 +74,95 @@ impl RafsSuperBlock for MockSuperBlock { unimplemented!() } } + +#[cfg(test)] +mod tests { + use std::fs::OpenOptions; + + use vmm_sys_util::tempfile::TempFile; + + use crate::{mock::MockChunkInfo, RafsIoRead}; + + use super::*; + + #[test] + fn test_mock_super_block() { + let chunks = Vec::>::new(); + let node1 = MockInode::mock(0, 20, chunks.clone()); + let node2 = MockInode::mock(1, 20, chunks); + let mut blk = MockSuperBlock::new(); + blk.inodes.insert(node1.ino(), Arc::new(node1)); + blk.inodes.insert(node2.ino(), Arc::new(node2)); + assert!(blk.get_inode(0, false).is_ok()); + assert!(blk.get_inode(1, false).is_ok()); + assert!(blk.get_inode(2, false).is_err()); + + assert!(blk.get_extended_inode(0, false).is_ok()); + assert!(blk.get_extended_inode(1, false).is_ok()); + assert!(blk.get_extended_inode(2, false).is_err()); + } + #[test] + #[should_panic] + fn test_get_max_ino() { + let blk = MockSuperBlock::new(); + blk.get_max_ino(); + } + + fn get_reader() -> Box { + let temp = TempFile::new().unwrap(); + let r = OpenOptions::new() + .read(true) + .write(false) + .open(temp.as_path()) + .unwrap(); + let reader: Box = Box::new(r); + reader + } + + #[test] + #[should_panic] + fn test_load() { + let mut blk = MockSuperBlock::new(); + let mut reader: Box = get_reader(); + blk.load(&mut reader).unwrap(); + } + + #[test] + #[should_panic] + fn test_update() { + let blk = MockSuperBlock::new(); + let mut reader: Box = get_reader(); + blk.update(&mut reader).unwrap(); + } + + #[test] + #[should_panic] + fn test_rootino() { + let blk = MockSuperBlock::new(); + blk.root_ino(); + } + #[test] + #[should_panic] + fn test_get_chunk_info() { + let blk = MockSuperBlock::new(); + blk.get_chunk_info(0).unwrap(); + } + + #[test] + #[should_panic] + fn test_set_blob_device() { + let blk = MockSuperBlock::new(); + blk.set_blob_device(BlobDevice::default()); + } + + #[test] + fn test_mock_super_block_func() { + let mut blk = MockSuperBlock::new(); + assert!(blk.get_inode(0, true).is_err()); + assert!(blk.get_extended_inode(0, true).is_err()); + blk.inodes.insert(0, Arc::new(MockInode::default())); + assert!(blk.get_inode(0, true).is_ok()); + assert!(blk.get_extended_inode(0, true).is_ok()); + blk.destroy(); + } +} From e3ead1a0252ed59639cac0a172b749a332105ce8 Mon Sep 17 00:00:00 2001 From: "weizhen.zt" Date: Tue, 7 Nov 2023 16:56:30 +0800 Subject: [PATCH 4/7] api: add some unit test cases Signed-off-by: weizhen.zt --- api/src/config.rs | 420 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 420 insertions(+) diff --git a/api/src/config.rs b/api/src/config.rs index 589e4519cf2..32c7541b117 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -2178,4 +2178,424 @@ mod tests { let auth = registry.auth.unwrap(); assert_eq!(auth, test_auth); } + + #[test] + fn test_config2_error() { + let content_bad_version = r#"version=3 + "#; + let cfg: ConfigV2 = toml::from_str(content_bad_version).unwrap(); + assert!(!cfg.validate()); + let cfg = ConfigV2::new("id"); + assert!(cfg.get_backend_config().is_err()); + assert!(cfg.get_cache_config().is_err()); + assert!(cfg.get_rafs_config().is_err()); + assert!(cfg.get_cache_working_directory().is_err()); + + let content = r#"version=2 + [cache] + type = "filecache" + [cache.filecache] + work_dir = "/tmp" + "#; + let cfg: ConfigV2 = toml::from_str(content).unwrap(); + assert_eq!(cfg.get_cache_working_directory().unwrap(), "/tmp"); + + let content = r#"version=2 + [cache] + type = "fscache" + [cache.fscache] + work_dir = "./foo" + "#; + let cfg: ConfigV2 = toml::from_str(content).unwrap(); + assert_eq!(cfg.get_cache_working_directory().unwrap(), "./foo"); + + let content = r#"version=2 + [cache] + type = "bar" + "#; + let cfg: ConfigV2 = toml::from_str(content).unwrap(); + assert!(cfg.get_cache_working_directory().is_err()); + + let content = r#" + foo-bar-xxxx + "#; + assert!(toml::from_str::(content).is_err()); + } + + #[test] + fn test_backend_config_valid() { + let mut cfg = BackendConfigV2 { + backend_type: "localdisk".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + cfg.localdisk = Some(LocalDiskConfig { + device_path: "".to_string(), + disable_gpt: true, + }); + assert!(!cfg.validate()); + + let cfg = BackendConfigV2 { + backend_type: "localfs".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + + let cfg = BackendConfigV2 { + backend_type: "oss".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + + let cfg = BackendConfigV2 { + backend_type: "s3".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + + let cfg = BackendConfigV2 { + backend_type: "register".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + + let cfg = BackendConfigV2 { + backend_type: "http-proxy".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + + let cfg = BackendConfigV2 { + backend_type: "foobar".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + } + + fn get_config(backend_type: &str) { + let mut cfg: BackendConfigV2 = BackendConfigV2::default(); + assert!(cfg.get_localdisk_config().is_err()); + + cfg.backend_type = backend_type.to_string(); + assert!(cfg.get_localdisk_config().is_err()); + } + + #[test] + fn test_get_confg() { + get_config("localdisk"); + get_config("localfs"); + get_config("oss"); + get_config("s3"); + get_config("register"); + get_config("http-proxy"); + } + + #[test] + fn test_cache_config_valid() { + let cfg = CacheConfigV2 { + cache_type: "blobcache".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + + let cfg = CacheConfigV2 { + cache_type: "fscache".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + + let cfg = CacheConfigV2 { + cache_type: "dummycache".to_string(), + ..Default::default() + }; + assert!(cfg.validate()); + + let cfg = CacheConfigV2 { + cache_type: "foobar".to_string(), + ..Default::default() + }; + assert!(!cfg.validate()); + } + + #[test] + fn test_get_fscache_config() { + let mut cfg = CacheConfigV2::default(); + assert!(cfg.get_fscache_config().is_err()); + cfg.cache_type = "fscache".to_string(); + assert!(cfg.get_fscache_config().is_err()); + } + + #[test] + fn test_fscache_get_work_dir() { + let mut cfg = FsCacheConfig::default(); + assert!(cfg.get_work_dir().is_err()); + cfg.work_dir = ".".to_string(); + assert!(cfg.get_work_dir().is_ok()); + cfg.work_dir = "foobar".to_string(); + let res = cfg.get_work_dir().is_ok(); + fs::remove_dir_all("foobar").unwrap(); + assert!(res); + } + + #[test] + fn test_default_mirror_config() { + let cfg = MirrorConfig::default(); + assert_eq!(cfg.host, ""); + assert_eq!(cfg.health_check_interval, 5); + assert_eq!(cfg.failure_limit, 5); + assert_eq!(cfg.ping_url, ""); + } + + #[test] + fn test_config_v2_from_file() { + let content = r#"version=2 + [cache] + type = "filecache" + [cache.filecache] + work_dir = "/tmp" + "#; + if fs::write("test_config_v2_from_file.cfg", content).is_ok() { + let res = ConfigV2::from_file("test_config_v2_from_file.cfg").is_ok(); + fs::remove_file("test_config_v2_from_file.cfg").unwrap(); + assert!(res); + } else { + assert!(ConfigV2::from_file("test_config_v2_from_file.cfg").is_err()); + } + } + + #[test] + fn test_blob_cache_entry_v2_from_file() { + let content = r#"version=2 + id = "my_id" + metadata_path = "meta_path" + [backend] + type = "localfs" + [backend.localfs] + blob_file = "/tmp/nydus.blob.data" + dir = "/tmp" + alt_dirs = ["/var/nydus/cache"] + [cache] + type = "filecache" + compressed = true + validate = true + [cache.filecache] + work_dir = "/tmp" + "#; + if fs::write("test_blob_cache_entry_v2_from_file.cfg", content).is_ok() { + let res = + BlobCacheEntryConfigV2::from_file("test_blob_cache_entry_v2_from_file.cfg").is_ok(); + fs::remove_file("test_blob_cache_entry_v2_from_file.cfg").unwrap(); + assert!(res); + } else { + assert!(ConfigV2::from_file("test_blob_cache_entry_v2_from_file.cfg").is_err()); + } + } + + #[test] + fn test_blob_cache_valid() { + let err_version_content = r#"version=1"#; + + let config: BlobCacheEntryConfigV2 = toml::from_str(err_version_content).unwrap(); + assert!(!config.validate()); + + let content = r#"version=2 + id = "my_id" + metadata_path = "meta_path" + [backend] + type = "localfs" + [backend.localfs] + blob_file = "/tmp/nydus.blob.data" + dir = "/tmp" + alt_dirs = ["/var/nydus/cache"] + [cache] + type = "filecache" + compressed = true + validate = true + [cache.filecache] + work_dir = "/tmp" + "#; + + let config: BlobCacheEntryConfigV2 = toml::from_str(content).unwrap(); + assert!(config.validate()); + } + + #[test] + fn test_blob_from_str() { + let content = r#"version=2 + id = "my_id" + metadata_path = "meta_path" + [backend] + type = "localfs" + [backend.localfs] + blob_file = "/tmp/nydus.blob.data" + dir = "/tmp" + alt_dirs = ["/var/nydus/cache"] + [cache] + type = "filecache" + compressed = true + validate = true + [cache.filecache] + work_dir = "/tmp" + "#; + + let config: BlobCacheEntryConfigV2 = BlobCacheEntryConfigV2::from_str(content).unwrap(); + assert_eq!(config.version, 2); + assert_eq!(config.id, "my_id"); + assert_eq!(config.backend.localfs.unwrap().dir, "/tmp"); + assert_eq!(config.cache.file_cache.unwrap().work_dir, "/tmp"); + let content = r#" + { + "version": 2, + "id": "my_id", + "backend": { + "type": "localfs", + "localfs": { + "dir": "/tmp" + } + } + } + "#; + let config: BlobCacheEntryConfigV2 = BlobCacheEntryConfigV2::from_str(content).unwrap(); + + assert_eq!(config.version, 2); + assert_eq!(config.id, "my_id"); + assert_eq!(config.backend.localfs.unwrap().dir, "/tmp"); + + let content = r#"foobar"#; + assert!(BlobCacheEntryConfigV2::from_str(content).is_err()); + } + + #[test] + fn test_blob_cache_entry_from_file() { + let content = r#"{ + "type": "bootstrap", + "id": "blob1", + "config": { + "id": "cache1", + "backend_type": "localfs", + "backend_config": {}, + "cache_type": "fscache", + "cache_config": {}, + "metadata_path": "/tmp/metadata1" + }, + "domain_id": "domain1" + }"#; + if fs::write("test_blob_cache_entry_from_file.cfg", content).is_ok() { + let res = BlobCacheEntry::from_file("test_blob_cache_entry_from_file.cfg").is_ok(); + fs::remove_file("test_blob_cache_entry_from_file.cfg").unwrap(); + assert!(res); + } else { + assert!(ConfigV2::from_file("test_blob_cache_entry_from_file.cfg").is_err()); + } + } + + #[test] + fn test_blob_cache_entry_valid() { + let content = r#"{ + "type": "bootstrap", + "id": "blob1", + "config": { + "id": "cache1", + "backend_type": "localfs", + "backend_config": {}, + "cache_type": "fscache", + "cache_config": {}, + "metadata_path": "/tmp/metadata1" + }, + "domain_id": "domain1" + }"#; + let mut cfg = BlobCacheEntry::from_str(content).unwrap(); + cfg.blob_type = "foobar".to_string(); + assert!(!cfg.validate()); + + let content = r#"{ + "type": "bootstrap", + "id": "blob1", + "domain_id": "domain1" + }"#; + let cfg = BlobCacheEntry::from_str(content).unwrap(); + assert!(cfg.validate()); + } + + #[test] + fn test_blob_cache_entry_from_str() { + let content = r#"{ + "type": "bootstrap", + "id": "blob1", + "config": { + "id": "cache1", + "backend_type": "localfs", + "backend_config": {}, + "cache_type": "fscache", + "cache_config": {}, + "metadata_path": "/tmp/metadata1" + }, + "domain_id": "domain1" + }"#; + assert!(BlobCacheEntry::from_str(content).is_ok()); + let content = r#"{ + "type": "foobar", + "id": "blob1", + "config": { + "id": "cache1", + "backend_type": "foobar", + "backend_config": {}, + "cache_type": "foobar", + "cache_config": {}, + "metadata_path": "/tmp/metadata1" + }, + "domain_id": "domain1" + }"#; + assert!(BlobCacheEntry::from_str(content).is_err()); + + let content = r#"foobar"#; + assert!(BlobCacheEntry::from_str(content).is_err()); + } + + #[test] + fn test_default_value() { + assert!(default_true()); + assert_eq!(default_failure_limit(), 5); + assert_eq!(default_prefetch_batch_size(), 1024 * 1024); + assert_eq!(default_prefetch_threads(), 8); + } + + #[test] + fn test_bckend_config_try_from() { + let config = BackendConfig { + backend_type: "localdisk".to_string(), + backend_config: serde_json::to_value(LocalDiskConfig::default()).unwrap(), + }; + assert!(BackendConfigV2::try_from(&config).is_ok()); + + let config = BackendConfig { + backend_type: "localfs".to_string(), + backend_config: serde_json::to_value(LocalFsConfig::default()).unwrap(), + }; + assert!(BackendConfigV2::try_from(&config).is_ok()); + + let config = BackendConfig { + backend_type: "oss".to_string(), + backend_config: serde_json::to_value(OssConfig::default()).unwrap(), + }; + assert!(BackendConfigV2::try_from(&config).is_ok()); + + let config = BackendConfig { + backend_type: "s3".to_string(), + backend_config: serde_json::to_value(S3Config::default()).unwrap(), + }; + assert!(BackendConfigV2::try_from(&config).is_ok()); + + let config = BackendConfig { + backend_type: "registry".to_string(), + backend_config: serde_json::to_value(RegistryConfig::default()).unwrap(), + }; + assert!(BackendConfigV2::try_from(&config).is_ok()); + + let config = BackendConfig { + backend_type: "foobar".to_string(), + backend_config: serde_json::to_value(LocalDiskConfig::default()).unwrap(), + }; + assert!(BackendConfigV2::try_from(&config).is_err()); + } } From 512ecfb3cd86a05c772b7d30bbc44287960611e2 Mon Sep 17 00:00:00 2001 From: "weizhen.zt" Date: Tue, 7 Nov 2023 16:56:43 +0800 Subject: [PATCH 5/7] builder: add some unit test cases Signed-off-by: weizhen.zt --- builder/src/core/overlay.rs | 140 ++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/builder/src/core/overlay.rs b/builder/src/core/overlay.rs index 21dfe1169f6..e8797a5928c 100644 --- a/builder/src/core/overlay.rs +++ b/builder/src/core/overlay.rs @@ -209,3 +209,143 @@ impl Node { None } } + +#[cfg(test)] +mod tests { + use nydus_rafs::metadata::{inode::InodeWrapper, layout::v5::RafsV5Inode}; + + use crate::core::node::NodeInfo; + + use super::*; + + #[test] + fn test_white_spec_from_str() { + let spec = WhiteoutSpec::default(); + assert!(matches!(spec, WhiteoutSpec::Oci)); + + assert!(WhiteoutSpec::from_str("oci").is_ok()); + assert!(WhiteoutSpec::from_str("overlayfs").is_ok()); + assert!(WhiteoutSpec::from_str("none").is_ok()); + assert!(WhiteoutSpec::from_str("foo").is_err()); + } + + #[test] + fn test_white_type_removal_check() { + let t1 = WhiteoutType::OciOpaque; + let t2 = WhiteoutType::OciRemoval; + let t3 = WhiteoutType::OverlayFsOpaque; + let t4 = WhiteoutType::OverlayFsRemoval; + assert!(!t1.is_removal()); + assert!(t2.is_removal()); + assert!(!t3.is_removal()); + assert!(t4.is_removal()); + } + + #[test] + fn test_overlay_low_layer_check() { + let t1 = Overlay::Lower; + let t2 = Overlay::UpperAddition; + let t3 = Overlay::UpperModification; + + assert!(t1.is_lower_layer()); + assert!(!t2.is_lower_layer()); + assert!(!t3.is_lower_layer()); + } + + #[test] + fn test_node() { + let mut inode = InodeWrapper::V5(RafsV5Inode::default()); + inode.set_mode(libc::S_IFCHR); + let node = Node::new(inode, NodeInfo::default(), 0); + assert!(!node.is_overlayfs_whiteout(WhiteoutSpec::None)); + assert!(node.is_overlayfs_whiteout(WhiteoutSpec::Overlayfs)); + assert_eq!( + node.whiteout_type(WhiteoutSpec::Overlayfs).unwrap(), + WhiteoutType::OverlayFsRemoval + ); + + let mut inode = InodeWrapper::V5(RafsV5Inode::default()); + let mut info: NodeInfo = NodeInfo::default(); + assert!(info + .xattrs + .add(OVERLAYFS_WHITEOUT_OPAQUE.into(), "y".into()) + .is_ok()); + inode.set_mode(libc::S_IFDIR); + let node = Node::new(inode, info, 0); + assert!(!node.is_overlayfs_opaque(WhiteoutSpec::None)); + assert!(node.is_overlayfs_opaque(WhiteoutSpec::Overlayfs)); + assert_eq!( + node.whiteout_type(WhiteoutSpec::Overlayfs).unwrap(), + WhiteoutType::OverlayFsOpaque + ); + + let mut inode = InodeWrapper::V5(RafsV5Inode::default()); + let mut info = NodeInfo::default(); + assert!(info + .xattrs + .add(OVERLAYFS_WHITEOUT_OPAQUE.into(), "n".into()) + .is_ok()); + inode.set_mode(libc::S_IFDIR); + let node = Node::new(inode, info, 0); + assert!(!node.is_overlayfs_opaque(WhiteoutSpec::None)); + assert!(!node.is_overlayfs_opaque(WhiteoutSpec::Overlayfs)); + + let mut inode = InodeWrapper::V5(RafsV5Inode::default()); + let mut info = NodeInfo::default(); + assert!(info + .xattrs + .add(OVERLAYFS_WHITEOUT_OPAQUE.into(), "y".into()) + .is_ok()); + inode.set_mode(libc::S_IFCHR); + let node = Node::new(inode, info, 0); + assert!(!node.is_overlayfs_opaque(WhiteoutSpec::None)); + assert!(!node.is_overlayfs_opaque(WhiteoutSpec::Overlayfs)); + + let mut inode = InodeWrapper::V5(RafsV5Inode::default()); + let mut info = NodeInfo::default(); + assert!(info + .xattrs + .add(OVERLAYFS_WHITEOUT_OPAQUE.into(), "n".into()) + .is_ok()); + inode.set_mode(libc::S_IFDIR); + let node = Node::new(inode, info, 0); + assert!(!node.is_overlayfs_opaque(WhiteoutSpec::None)); + assert!(!node.is_overlayfs_opaque(WhiteoutSpec::Overlayfs)); + + let inode = InodeWrapper::V5(RafsV5Inode::default()); + let info = NodeInfo::default(); + let mut node = Node::new(inode, info, 0); + + assert_eq!(node.whiteout_type(WhiteoutSpec::None), None); + assert_eq!(node.whiteout_type(WhiteoutSpec::Oci), None); + assert_eq!(node.whiteout_type(WhiteoutSpec::Overlayfs), None); + + node.overlay = Overlay::Lower; + assert_eq!(node.whiteout_type(WhiteoutSpec::Overlayfs), None); + + let inode = InodeWrapper::V5(RafsV5Inode::default()); + let mut info = NodeInfo::default(); + let name = OCISPEC_WHITEOUT_PREFIX.to_string() + "foo"; + info.target_vec.push(name.clone().into()); + let node = Node::new(inode, info, 0); + assert_eq!( + node.whiteout_type(WhiteoutSpec::Oci).unwrap(), + WhiteoutType::OciRemoval + ); + assert_eq!(node.origin_name(WhiteoutType::OciRemoval).unwrap(), "foo"); + assert_eq!(node.origin_name(WhiteoutType::OciOpaque), None); + assert_eq!( + node.origin_name(WhiteoutType::OverlayFsRemoval).unwrap(), + OsStr::new(&name) + ); + + let inode = InodeWrapper::V5(RafsV5Inode::default()); + let mut info = NodeInfo::default(); + info.target_vec.push(OCISPEC_WHITEOUT_OPAQUE.into()); + let node = Node::new(inode, info, 0); + assert_eq!( + node.whiteout_type(WhiteoutSpec::Oci).unwrap(), + WhiteoutType::OciOpaque + ); + } +} From 3a46793c9747b47f3711b1cf4760e04ec7446fee Mon Sep 17 00:00:00 2001 From: "weizhen.zt" Date: Wed, 8 Nov 2023 06:02:12 +0000 Subject: [PATCH 6/7] storage: move toml to dev-dependencies Signed-off-by: weizhen.zt --- storage/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/Cargo.toml b/storage/Cargo.toml index 3983058eab3..0d75e9e8084 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -35,7 +35,6 @@ url = { version = "2.1.1", optional = true } vm-memory = "0.10" fuse-backend-rs = "^0.10.3" gpt = { version = "3.0.0", optional = true } -toml = "0.5" nydus-api = { version = "0.3", path = "../api" } nydus-utils = { version = "0.4", path = "../utils", features = ["encryption", "zran"] } @@ -44,6 +43,7 @@ nydus-utils = { version = "0.4", path = "../utils", features = ["encryption", "z vmm-sys-util = "0.11" tar = "0.4.40" regex = "1.7.0" +toml = "0.5" [features] backend-localdisk = [] From c0350c01f2358b3b24fd7235f63c923d6540cafa Mon Sep 17 00:00:00 2001 From: "weizhen.zt" Date: Wed, 8 Nov 2023 14:31:09 +0800 Subject: [PATCH 7/7] utils: bugfix for unit test case. Signed-off-by: weizhen.zt --- utils/src/metrics.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/utils/src/metrics.rs b/utils/src/metrics.rs index ddfdb41c084..5810b542e7c 100644 --- a/utils/src/metrics.rs +++ b/utils/src/metrics.rs @@ -966,16 +966,18 @@ mod tests { assert_eq!(f.latency_start(), None); f.measure_latency.store(true, Ordering::Relaxed); let s = f.latency_start().unwrap(); - let d = Duration::new(2, 0); + let d = Duration::new(1, 500_000_000); + /* because of the timer resolution, the elapsed maybe greater than 1.5s gentlely*/ f.latency_end(&s.checked_sub(d), StatsFop::Read); - assert_eq!( - f.read_latency_dist[latency_micros_range_index(2 * 1000 * 1000)].count(), + f.read_latency_dist[latency_micros_range_index(1_500_000)].count(), 1 ); - assert_eq!( - f.fop_cumulative_latency_total[StatsFop::Read as usize].count(), - saturating_duration_micros(&d) + /* we think if the latency delta error no more 1ms, the test is successful. */ + assert!( + f.fop_cumulative_latency_total[StatsFop::Read as usize].count() + - saturating_duration_micros(&d) + <= 1000 ); }