Feature/fix tests (#561)

fixed some tests
This commit is contained in:
euzu
2026-02-01 14:45:33 +01:00
committed by GitHub
parent 6d22383930
commit 360c75dae7
14 changed files with 140 additions and 131 deletions
+4 -6
View File
@@ -166,8 +166,8 @@ mod tests {
fn create_test_file(name: &str) -> ScannedMediaFile {
ScannedMediaFile {
path: PathBuf::from(format!("/test/{}", name)),
file_path: format!("/test/{}", name),
path: PathBuf::from(format!("/test/{name}")),
file_path: format!("/test/{name}"),
file_name: name.to_string(),
extension: "mkv".to_string(),
size_bytes: 1024,
@@ -186,8 +186,7 @@ mod tests {
let resolver = MetadataResolver::from_config(&config, client, MetadataStorage::new(PathBuf::from("/tmp")));
let file = create_test_file("The.Matrix.1999.1080p.mkv");
let metadata = match MediaClassifier::classify(&file) {
MediaClassification::Movie { metadata, .. } => metadata,
MediaClassification::Series { metadata, .. } => metadata,
MediaClassification::Movie { metadata, .. } | MediaClassification::Series { metadata, .. } => metadata,
};
let group = MediaGroup::Movie { file, metadata: Box::new(metadata) };
@@ -215,8 +214,7 @@ mod tests {
let resolver = MetadataResolver::from_config(&config, client, MetadataStorage::new(PathBuf::from("/tmp")));
let file = create_test_file("343jfkjh4789dkjfh934z3.Movie.mkv");
let metadata = match MediaClassifier::classify(&file) {
MediaClassification::Movie { metadata, .. } => metadata,
MediaClassification::Series { metadata, .. } => metadata,
MediaClassification::Movie { metadata, .. } | MediaClassification::Series { metadata, .. } => metadata,
};
let group = MediaGroup::Movie { file, metadata: Box::new(metadata) };
+1 -1
View File
@@ -311,7 +311,7 @@ mod tests {
let entry = MetadataCacheEntry::new(
"/test/movie.mp4".to_string(),
1024,
1234567890,
1_234_567_890,
MediaMetadata::Movie(MovieMetadata {
title: "Test Movie".to_string(),
year: Some(2020),
+3 -3
View File
@@ -281,8 +281,8 @@ mod tests {
assert_eq!(movie.year, Some(1999));
assert_eq!(movie.imdb_id, Some("tt0133093".to_string()));
assert_eq!(movie.tmdb_id, Some(603));
assert_eq!(movie.genres.as_ref().map(|g| g.len()).unwrap_or_default(), 2);
assert_eq!(movie.directors.as_ref().map(|g| g.len()).unwrap_or_default(), 2);
assert_eq!(movie.genres.as_ref().map(Vec::len).unwrap_or_default(), 2);
assert_eq!(movie.directors.as_ref().map(Vec::len).unwrap_or_default(), 2);
} else {
panic!("Expected movie metadata");
}
@@ -316,7 +316,7 @@ mod tests {
assert_eq!(series.imdb_id, Some("tt0903747".to_string()));
assert_eq!(series.tmdb_id, Some(1396));
assert_eq!(series.tvdb_id, Some(81189));
assert_eq!(series.genres.as_ref().map(|g| g.len()).unwrap_or_default(), 3);
assert_eq!(series.genres.as_ref().map(Vec::len).unwrap_or_default(), 3);
assert_eq!(series.status, Some("Ended".to_string()));
} else {
panic!("Expected series metadata");
+15 -14
View File
@@ -281,28 +281,29 @@ async fn resolve_template<'a>(app_config: &'a Arc<AppConfig>, http_client: &'a r
#[cfg(test)]
mod tests {
use arc_swap::ArcSwap;
use arc_swap::{ArcSwap, ArcSwapOption};
use crate::model::ProcessingStats;
use super::*;
use shared::model::{ConfigPaths};
use crate::utils::FileLockManager;
fn create_app_config() -> Arc<AppConfig> {
Arc::new(AppConfig {
config: Arc::new(Default::default()),
sources: Arc::new(Default::default()),
hdhomerun: Arc::new(Default::default()),
api_proxy: Arc::new(Default::default()),
file_locks: Arc::new(Default::default()),
config: Arc::new(ArcSwap::default()),
sources: Arc::new(ArcSwap::default()),
hdhomerun: Arc::new(ArcSwapOption::default()),
api_proxy: Arc::new(ArcSwapOption::default()),
file_locks: Arc::new(FileLockManager::default()),
paths: Arc::new(ArcSwap::from_pointee(ConfigPaths {
config_path: "".to_string(),
config_file_path: "".to_string(),
sources_file_path: "".to_string(),
config_path: String::new(),
config_file_path: String::new(),
sources_file_path: String::new(),
mapping_file_path: None,
mapping_files_used: None,
api_proxy_file_path: "".to_string(),
api_proxy_file_path: String::new(),
custom_stream_response_path: None,
})),
custom_stream_response: Arc::new(Default::default()),
custom_stream_response: Arc::new(ArcSwapOption::default()),
access_token_secret: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],
encrypt_secret: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
})
@@ -459,7 +460,7 @@ mod tests {
let app_cfg = create_app_config();
let client = reqwest::Client::new();
let template = r#"
let template = r"
*🔄 Playlist Update Report*
{{#each stats}}
@@ -484,10 +485,10 @@ mod tests {
{{/if}}
_Timestamp: {{timestamp}}_
"#;
";
let output = render_template(&app_cfg, &client, Some(template), &content).await;
println!("Telegram Output:\n{}", output);
println!("Telegram Output:\n{output}");
assert!(output.contains("🔄 Playlist Update Report"));
assert!(output.contains("Telegram Input"));
+6 -8
View File
@@ -617,7 +617,6 @@ mod tests {
use crate::model::{EpgSmartMatchConfig, PersistedEpgSource, TVGuide};
use crate::processing::parser::xmltv::normalize_channel_name;
use std::collections::HashSet;
use std::io;
use std::path::PathBuf;
#[test]
@@ -636,9 +635,9 @@ mod tests {
}
#[ignore]
#[ignore = "requires a local XMLTV fixture under /tmp"]
#[test]
fn parse_test() -> io::Result<()> {
fn parse_test() {
let run_test = async move || {
//let file_path = PathBuf::from("/tmp/epg.xml.gz");
let file_path = PathBuf::from("/tmp/invalid_epg.xml");
@@ -652,19 +651,18 @@ mod tests {
let channel_ids = HashSet::from([342u32.intern()]);
match tv_guide.filter(&mut id_cache).await {
None => assert!(false, "No epg filtered"),
None => panic!("No epg filtered"),
Some(epgs) => {
for epg in epgs {
assert_eq!(epg.children.len(), channel_ids.len() * 2, "Epg size does not match")
assert_eq!(epg.children.len(), channel_ids.len() * 2, "Epg size does not match");
}
}
}
}
};
let _result = tokio::runtime::Runtime::new()
tokio::runtime::Runtime::new()
.unwrap()
.block_on(run_test());
Ok(())
}
#[test]
@@ -725,4 +723,4 @@ mod tests {
println!("{}", metaphone.encode(&normalize_channel_name("BU | ODISEA ᵁᴴᴰ ³⁸⁴⁰ᴾ", &epg_smart_cfg)));
println!("{}", metaphone.encode(&normalize_channel_name("BG | ODISEA ᵁᴴᴰ ³⁸⁴⁰ᴾ", &epg_smart_cfg)));
}
}
}
+7 -9
View File
@@ -387,11 +387,10 @@ mod tests {
let file_content = fs::read_to_string("/tmp/series-info.json").expect("Unable to read file");
match serde_json::from_str::<XtreamSeriesInfo>(&file_content) {
Ok(series_info) => {
println!("{:#?}", series_info);
assert!(true);
println!("{series_info:#?}");
}
Err(err) => {
assert!(false, "Failed to parse json file: {err}");
panic!("Failed to parse json file: {err}");
}
}
}
@@ -402,14 +401,13 @@ mod tests {
if fs::exists("/tmp/vod_streams.json").unwrap_or(false) {
let reader = Box::pin(async_file_reader(tokio::fs::File::open("/tmp/vod_streams.json").await?));
match map_to_xtream_streams(XtreamCluster::Video, reader, &"test".intern()).await {
Ok(_streams) => {
println!("{:?}", _streams.get(1));
println!("{:?}", _streams.get(100));
println!("{:?}", _streams.get(200));
assert!(true);
Ok(streams) => {
println!("{:?}", streams.get(1));
println!("{:?}", streams.get(100));
println!("{:?}", streams.get(200));
}
Err(err) => {
assert!(false, "Failed to parse json file: {err}");
panic!("Failed to parse json file: {err}");
}
};
}
+8 -8
View File
@@ -288,7 +288,7 @@ mod tests {
.map(|(i, (name, quality))| PlaylistItem {
header: PlaylistItemHeader {
title: format!("Chanel {name} [{quality}]").into(),
source_ordinal: i as u32,
source_ordinal: u32::try_from(i).unwrap(),
..Default::default()
},
})
@@ -313,10 +313,10 @@ mod tests {
let ord =
playlist_comparator(channel_sort.sequence.as_ref(), channel_sort.order, va, vb);
if ord != Ordering::Equal {
ord
} else {
if ord == Ordering::Equal {
a.header.source_ordinal.cmp(&b.header.source_ordinal)
} else {
ord
}
});
@@ -376,7 +376,7 @@ mod tests {
.map(|(i, name)| PlaylistItem {
header: PlaylistItemHeader {
title: name.to_string().into(),
source_ordinal: i as u32,
source_ordinal: u32::try_from(i).unwrap(),
..Default::default()
},
})
@@ -407,10 +407,10 @@ mod tests {
&b.header.title,
);
if ord != Ordering::Equal {
ord
} else {
if ord == Ordering::Equal {
a.header.source_ordinal.cmp(&b.header.source_ordinal)
} else {
ord
}
});
+36 -36
View File
@@ -4146,7 +4146,7 @@ mod tests {
id: record.id,
data: format!("{content} {}", record.id + 9000),
};
tree_update.update(&i, new_record).map_err(|e| e.to_io())?;
tree_update.update(&i, new_record).map_err(BPlusTreeError::to_io)?;
} else {
panic!("{content} {i} not found");
}
@@ -4256,7 +4256,7 @@ mod tests {
assert_eq!(update.len().expect("Update len failed"), test_size as usize);
// Update existing key - length should stay same
update.update(&1, Record { id: 1, data: "updated".to_string() }).map_err(|e| e.to_io())?;
update.update(&1, Record { id: 1, data: "updated".to_string() }).map_err(BPlusTreeError::to_io)?;
assert_eq!(update.len().expect("Update len failed after update"), test_size as usize);
// Insert new key - length should increase
@@ -4313,7 +4313,7 @@ mod tests {
let mut tree_update = BPlusTreeUpdate::<u32, Record>::try_new(&filepath)?;
for i in 0u32..100 {
if i % 2 == 0 {
tree_update.update(&i, Record { id: i, data: format!("UpdatedContent {i}") }).map_err(|e| e.to_io())?;
tree_update.update(&i, Record { id: i, data: format!("UpdatedContent {i}") }).map_err(BPlusTreeError::to_io)?;
}
}
@@ -4357,7 +4357,7 @@ mod tests {
let padding: String = generate_random_string(400);
for i in 0u32..10 {
// Each record has unique data to prevent compression
tree.insert(i, Record { id: i, data: format!("{}{}", padding, i) });
tree.insert(i, Record { id: i, data: format!("{padding}{i}") });
}
tree.store(&filepath)?;
@@ -4367,7 +4367,7 @@ mod tests {
let mut tree_update = BPlusTreeUpdate::<u32, Record>::try_new(&filepath)?;
let same_size_padding: String = generate_random_string(400);
for i in 0u32..10 {
tree_update.update(&i, Record { id: i, data: format!("{}{}", same_size_padding, i) }).map_err(|e| e.to_io())?;
tree_update.update(&i, Record { id: i, data: format!("{same_size_padding}{i}") }).map_err(BPlusTreeError::to_io)?;
}
let size_after_same_update = std::fs::metadata(&filepath)?.len();
@@ -4388,7 +4388,7 @@ mod tests {
let mut tree_update = BPlusTreeUpdate::<u32, Record>::try_new(&filepath)?;
let smaller_padding: String = generate_random_string(200);
for i in 0u32..10 {
tree_update.update(&i, Record { id: i, data: format!("{}{}", smaller_padding, i) }).map_err(|e| e.to_io())?;
tree_update.update(&i, Record { id: i, data: format!("{smaller_padding}{i}") }).map_err(BPlusTreeError::to_io)?;
}
let size_after_smaller_update = std::fs::metadata(&filepath)?.len();
@@ -4398,7 +4398,7 @@ mod tests {
// 5000 chars is much larger than the original ~400 byte allocation
let larger_padding: String = generate_random_string(5000);
for i in 0u32..1 {
tree_update.update(&i, Record { id: i, data: format!("{}{}", larger_padding, i) }).map_err(|e| e.to_io())?;
tree_update.update(&i, Record { id: i, data: format!("{larger_padding}{i}") }).map_err(BPlusTreeError::to_io)?;
}
let size_after_larger_update = std::fs::metadata(&filepath)?.len();
@@ -4444,7 +4444,7 @@ mod tests {
// 2. Multiple Updates (COW)
for i in (0..test_size).step_by(10) {
tree_update.update(&i, Record { id: i, data: format!("UpdatedContent {i}") }).map_err(|e| e.to_io())?;
tree_update.update(&i, Record { id: i, data: format!("UpdatedContent {i}") }).map_err(BPlusTreeError::to_io)?;
}
// 3. Verify Query Integrity (Must return NEW values)
@@ -4497,7 +4497,7 @@ mod tests {
assert_eq!(tree_update.query_le(&5).unwrap().unwrap().id, 0);
// 2. COW Update
tree_update.update(&10, Record { id: 10, data: "NewVal".to_string() }).map_err(|e| e.to_io())?;
tree_update.update(&10, Record { id: 10, data: "NewVal".to_string() }).map_err(BPlusTreeError::to_io)?;
// 3. Verify LE returns the LATEST value
let val = tree_update.query_le(&15).expect("Query failed").expect("Should find LE key after COW update");
@@ -4574,7 +4574,7 @@ mod tests {
.map(|(k, v)| (k, v))
.collect();
tree_update.update_batch(&update_refs).map_err(|e| e.to_io())?;
tree_update.update_batch(&update_refs).map_err(BPlusTreeError::to_io)?;
drop(tree_update);
// Verify all updates
@@ -4612,7 +4612,7 @@ mod tests {
// Test empty batch - should be no-op
let empty_batch: Vec<(&u32, &Record)> = vec![];
let result = tree_update.update_batch(&empty_batch).map_err(|e| e.to_io())?;
let result = tree_update.update_batch(&empty_batch).map_err(BPlusTreeError::to_io)?;
assert_eq!(result, initial_root, "Empty batch should not change root offset");
@@ -4656,7 +4656,7 @@ mod tests {
.collect();
// Perform batch update
tree_update.update_batch(&update_refs).map_err(|e| e.to_io())?;
tree_update.update_batch(&update_refs).map_err(BPlusTreeError::to_io)?;
drop(tree_update);
// Verify all updates via iterator
@@ -4704,7 +4704,7 @@ mod tests {
.map(|(k, v)| (k, v))
.collect();
tree_update.update_batch(&update_refs).map_err(|e| e.to_io())?;
tree_update.update_batch(&update_refs).map_err(BPlusTreeError::to_io)?;
let size_before_compact = std::fs::metadata(&filepath)?.len();
@@ -4756,9 +4756,9 @@ mod tests {
// Verify all data is present in the NEW file
let mut tree_check = BPlusTreeQuery::<u32, Record>::try_new(&filepath)?;
assert!(tree_check.query(&1).map_err(|e| e.to_io())?.is_some(), "Should have key 1");
assert!(tree_check.query(&2).map_err(|e| e.to_io())?.is_some(), "Should have key 2");
assert!(tree_check.query(&3).map_err(|e| e.to_io())?.is_some(), "Should have key 3 - if missing, file handle wasn't updated");
assert!(tree_check.query(&1).map_err(BPlusTreeError::to_io)?.is_some(), "Should have key 1");
assert!(tree_check.query(&2).map_err(BPlusTreeError::to_io)?.is_some(), "Should have key 2");
assert!(tree_check.query(&3).map_err(BPlusTreeError::to_io)?.is_some(), "Should have key 3 - if missing, file handle wasn't updated");
Ok(())
}
@@ -4935,10 +4935,11 @@ mod tests {
// Expected size without packing:
// 1000 items * 4096 bytes/block = 4,096,000 bytes (~4MB)
// Plus internal nodes
let unpacked_size_estimate = count as u64 * super::PAGE_SIZE_USIZE as u64;
let unpacked_size_estimate =
u64::from(count) * u64::try_from(super::PAGE_SIZE_USIZE).unwrap();
println!("File size with packing: {} bytes", file_size);
println!("Estimated unpacked size: {} bytes", unpacked_size_estimate);
println!("File size with packing: {file_size} bytes");
println!("Estimated unpacked size: {unpacked_size_estimate} bytes");
// We expect significant savings.
// 1000 items * ~60 bytes / 4096 bytes/block ~= 15 blocks
@@ -5008,7 +5009,7 @@ mod tests {
let val1 = "A".repeat(10000);
let val2 = "B".repeat(10000);
let updates = vec![
let updates = [
(1, val1.clone()),
(2, val2.clone()),
];
@@ -5072,7 +5073,7 @@ mod tests {
let mut tree_update = BPlusTreeUpdate::<u32, String>::try_new(&filepath)?;
// Batch contains same key multiple times
let updates = vec![
let updates = [
(1, "First".to_string()),
(1, "Second".to_string()),
(2, "Two".to_string()),
@@ -5117,7 +5118,7 @@ mod tests {
let size_before = std::fs::metadata(&filepath)?.len();
// Each individual update writes a full path, increasing file size significantly.
assert!(size_before > count as u64 * 4000);
assert!(size_before > u64::from(count) * 4000);
// Now Compact
tree_update.compact(&filepath)?;
@@ -5127,7 +5128,7 @@ mod tests {
// 200 items * 100 bytes = 20KB payload.
// Should pack into ~5-6 blocks (4KB each).
println!("Size before: {}, Size after: {}", size_before, size_after);
println!("Size before: {size_before}, Size after: {size_after}");
assert!(size_after < size_before / 10, "Compaction should pack values");
assert!(size_after < 100 * 1024, "File should be small"); // < 100KB
@@ -5208,7 +5209,7 @@ mod tests {
let keys: Vec<u32> = (0..count).collect();
let value_info: Vec<ValueInfo> = (0..count)
.map(|i| ValueInfo {
mode: ValueStorageMode::Packed(i as u64 * 4096, (i % 16) as u16),
mode: ValueStorageMode::Packed(u64::from(i) * 4096, (i % 16) as u16),
length: 100,
cache: Mutex::new(None),
})
@@ -5277,7 +5278,7 @@ mod tests {
// File size should have grown slightly (promoted values written at EOF)
// but not dramatically since most values are still packed
println!("Size initial: {}, Size after: {}", size_initial, size_after);
println!("Size initial: {size_initial}, Size after: {size_after}");
assert!(size_after >= size_initial, "File should not shrink");
// Verify all data is correct
@@ -5303,7 +5304,7 @@ mod tests {
let mut tree = BPlusTree::<u32, String>::new();
for i in 0..10 {
tree.insert(i, format!("value_{}", i));
tree.insert(i, format!("value_{i}"));
}
tree.store(&filepath)?;
@@ -5312,12 +5313,12 @@ mod tests {
// Test None policy - should not error
update.flush_policy = super::FlushPolicy::None;
for i in 0..5 {
update.update(&i, format!("new_{}", i)).map_err(super::BPlusTreeError::to_io)?;
update.update(&i, format!("new_{i}")).map_err(super::BPlusTreeError::to_io)?;
}
// Verify values within same session
for i in 0..5 {
assert_eq!(update.query(&i).map_err(super::BPlusTreeError::to_io)?.unwrap(), format!("new_{}", i));
assert_eq!(update.query(&i).map_err(super::BPlusTreeError::to_io)?.unwrap(), format!("new_{i}"));
}
// Test Batch policy
@@ -5424,8 +5425,8 @@ mod tests {
let page = SlottedPage::new(&mut data, PageType::Leaf).expect("Init failed");
assert_eq!(page.header.page_type, PageType::Leaf);
assert_eq!(page.header.cell_count, 0);
assert_eq!(page.header.free_start, PAGE_HEADER_SIZE as u16);
assert_eq!(page.header.free_end, PAGE_SIZE as u16);
assert_eq!(page.header.free_start, PAGE_HEADER_SIZE);
assert_eq!(page.header.free_end, PAGE_SIZE);
assert_eq!(page.free_space(), PAGE_SIZE_USIZE - PAGE_HEADER_SIZE_USIZE);
}
@@ -5439,11 +5440,11 @@ mod tests {
// Insert length-prefixed for test realism
let mut cell1 = Vec::new();
cell1.extend_from_slice(&(val1.len() as u32).to_le_bytes());
cell1.extend_from_slice(&u32::try_from(val1.len()).unwrap().to_le_bytes());
cell1.extend_from_slice(val1);
let mut cell2 = Vec::new();
cell2.extend_from_slice(&(val2.len() as u32).to_le_bytes());
cell2.extend_from_slice(&u32::try_from(val2.len()).unwrap().to_le_bytes());
cell2.extend_from_slice(val2);
page.insert_at_index(0, &cell1).unwrap();
@@ -5465,7 +5466,7 @@ mod tests {
let payload = vec![0xAAu8; 500];
let mut cell = Vec::new();
cell.extend_from_slice(&(payload.len() as u32).to_le_bytes());
cell.extend_from_slice(&u32::try_from(payload.len()).unwrap().to_le_bytes());
cell.extend_from_slice(&payload);
for i in 0..6 {
@@ -5496,7 +5497,7 @@ mod tests {
// Case 1: Split single item page -> Should return None (no-op)
let val = b"item";
let mut cell = Vec::new();
cell.extend_from_slice(&(val.len() as u32).to_le_bytes());
cell.extend_from_slice(&u32::try_from(val.len()).unwrap().to_le_bytes());
cell.extend_from_slice(val);
page.insert_at_index(0, &cell).unwrap();
@@ -5506,7 +5507,7 @@ mod tests {
assert_eq!(page.header.cell_count, 1); // Original page untouched
}
Ok(Some(_)) => panic!("Split of single item should result in None"),
Err(e) => panic!("Split of single item should result in no-op, not error: {:?}", e),
Err(e) => panic!("Split of single item should result in no-op, not error: {e:?}"),
}
}
@@ -5535,4 +5536,3 @@ mod tests {
Ok(())
}
}
+33 -21
View File
@@ -1,6 +1,7 @@
use super::bplustree::{BPlusTree, BPlusTreeUpdate, BPlusTreeQuery};
use rand::prelude::*;
use rand::distr::Alphanumeric;
use std::io::Write;
use std::time::Instant;
use tempfile::NamedTempFile;
@@ -15,12 +16,17 @@ fn random_string(len: usize) -> String {
}
#[test]
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::too_many_lines
)]
fn stress_test_bplustree() {
let temp_file = NamedTempFile::new().unwrap();
let filepath = temp_file.path().to_path_buf();
let log_path = std::path::Path::new("/tmp/stress_results.txt");
let mut log_file = std::fs::File::create(log_path).unwrap();
use std::io::Write;
// Config
let num_items = 500_000;
@@ -29,7 +35,7 @@ fn stress_test_bplustree() {
let large_val_len = 500; // Larger than packed limit (256)
writeln!(log_file, "=== B+Tree Stress Test & Performance Analysis ===").unwrap();
writeln!(log_file, "Dataset: {} items", num_items).unwrap();
writeln!(log_file, "Dataset: {num_items} items").unwrap();
// ----------------------------------------------------------------
// Phase 1: Batch Insert (Sequential Keys)
@@ -50,7 +56,7 @@ fn stress_test_bplustree() {
tree.insert(k, v);
}
let insert_duration = start.elapsed();
writeln!(log_file, "Insert Time: {:.2?}", insert_duration).unwrap();
writeln!(log_file, "Insert Time: {insert_duration:.2?}").unwrap();
writeln!(log_file, "Throughput: {:.0} ops/sec", num_items as f64 / insert_duration.as_secs_f64()).unwrap();
// Prepare query keys
@@ -61,13 +67,13 @@ fn stress_test_bplustree() {
// ----------------------------------------------------------------
// Phase 1b: Memory-Only Random Query (Before storing)
// ----------------------------------------------------------------
writeln!(log_file, "\n[Phase 1b] Memory-Only Random Query ({} items)...", query_count).unwrap();
writeln!(log_file, "\n[Phase 1b] Memory-Only Random Query ({query_count} items)...").unwrap();
let start = Instant::now();
for k in query_subset_mem {
let _ = tree.query(k);
}
let duration = start.elapsed();
writeln!(log_file, "Time: {:.2?}", duration).unwrap();
writeln!(log_file, "Time: {duration:.2?}").unwrap();
writeln!(log_file, "Throughput: {:.0} ops/sec", query_count as f64 / duration.as_secs_f64()).unwrap();
// ----------------------------------------------------------------
@@ -78,21 +84,21 @@ fn stress_test_bplustree() {
tree.store(&filepath).unwrap();
drop(tree);
let duration = start.elapsed();
writeln!(log_file, "Write Time: {:.2?}", duration).unwrap();
writeln!(log_file, "Write Time: {duration:.2?}").unwrap();
let size_phase1 = std::fs::metadata(&filepath).unwrap().len();
writeln!(log_file, "File Size: {:.2} MB", size_phase1 as f64 / 1024.0 / 1024.0).unwrap();
// ----------------------------------------------------------------
// Phase 2: Random Query (Disk-based)
// ----------------------------------------------------------------
writeln!(log_file, "\n[Phase 2] Random Query Disk-based ({} items)...", query_count).unwrap();
writeln!(log_file, "\n[Phase 2] Random Query Disk-based ({query_count} items)...").unwrap();
let mut query = BPlusTreeQuery::<u32, String>::try_new(&filepath).unwrap();
let start = Instant::now();
for k in query_subset_mem {
let _ = query.query_zero_copy(k).unwrap();
}
let duration = start.elapsed();
writeln!(log_file, "Time: {:.2?}", duration).unwrap();
writeln!(log_file, "Time: {duration:.2?}").unwrap();
writeln!(log_file, "Throughput: {:.0} ops/sec", query_count as f64 / duration.as_secs_f64()).unwrap();
// ----------------------------------------------------------------
@@ -105,11 +111,11 @@ fn stress_test_bplustree() {
.map(|&k| (k, random_string(small_val_len)))
.collect();
let update_refs: Vec<(&u32, &String)> = updates.iter().map(|(k,v)| (k,v)).collect();
let mut updater = BPlusTreeUpdate::<u32, String>::try_new(&filepath).unwrap();
let mut tree_updater = BPlusTreeUpdate::<u32, String>::try_new(&filepath).unwrap();
let start = Instant::now();
updater.update_batch(&update_refs).unwrap();
tree_updater.update_batch(&update_refs).unwrap();
let duration = start.elapsed();
writeln!(log_file, "Time: {:.2?}", duration).unwrap();
writeln!(log_file, "Time: {duration:.2?}").unwrap();
writeln!(log_file, "Throughput: {:.0} ops/sec", update_count as f64 / duration.as_secs_f64()).unwrap();
let size_phase3 = std::fs::metadata(&filepath).unwrap().len();
writeln!(log_file, "File Size: {:.2} MB", size_phase3 as f64 / 1024.0 / 1024.0).unwrap();
@@ -123,26 +129,32 @@ fn stress_test_bplustree() {
.collect();
let update_refs_prom: Vec<(&u32, &String)> = updates_prom.iter().map(|(k,v)| (k,v)).collect();
let start = Instant::now();
updater.update_batch(&update_refs_prom).unwrap();
tree_updater.update_batch(&update_refs_prom).unwrap();
let duration = start.elapsed();
writeln!(log_file, "Time: {:.2?}", duration).unwrap();
writeln!(log_file, "Time: {duration:.2?}").unwrap();
writeln!(log_file, "Throughput: {:.0} ops/sec", update_count as f64 / duration.as_secs_f64()).unwrap();
let size_phase4 = std::fs::metadata(&filepath).unwrap().len();
writeln!(log_file, "File Size: {:.2} MB", size_phase4 as f64 / 1024.0 / 1024.0).unwrap();
drop(updater);
drop(tree_updater);
// ----------------------------------------------------------------
// Phase 5: Compaction
// ----------------------------------------------------------------
writeln!(log_file, "\n[Phase 5] Compaction...").unwrap();
let mut updater = BPlusTreeUpdate::<u32, String>::try_new(&filepath).unwrap();
let mut tree_updater = BPlusTreeUpdate::<u32, String>::try_new(&filepath).unwrap();
let start = Instant::now();
updater.compact(&filepath).unwrap();
tree_updater.compact(&filepath).unwrap();
let duration = start.elapsed();
writeln!(log_file, "Time: {:.2?}", duration).unwrap();
writeln!(log_file, "Time: {duration:.2?}").unwrap();
let size_phase5 = std::fs::metadata(&filepath).unwrap().len();
writeln!(log_file, "File Size: {:.2} MB (Reduction: {:.2} MB)", size_phase5 as f64 / 1024.0 / 1024.0, (size_phase4 as i64 - size_phase5 as i64) as f64 / 1024.0 / 1024.0).unwrap();
drop(updater);
writeln!(
log_file,
"File Size: {:.2} MB (Reduction: {:.2} MB)",
size_phase5 as f64 / 1024.0 / 1024.0,
(size_phase4 as i64 - size_phase5 as i64) as f64 / 1024.0 / 1024.0
)
.unwrap();
drop(tree_updater);
// ----------------------------------------------------------------
// Phase 6: Full Tree Load and In-Memory Query
@@ -151,13 +163,13 @@ fn stress_test_bplustree() {
let start = Instant::now();
let tree_mem = BPlusTree::<u32, String>::load(&filepath).unwrap();
let load_duration = start.elapsed();
writeln!(log_file, "Load Time: {:.2?}", load_duration).unwrap();
writeln!(log_file, "Load Time: {load_duration:.2?}").unwrap();
let start = Instant::now();
for k in query_subset_mem {
let _ = tree_mem.query(k);
}
let query_duration = start.elapsed();
writeln!(log_file, "In-Memory Query Time: {:.2?}", query_duration).unwrap();
writeln!(log_file, "In-Memory Query Time: {query_duration:.2?}").unwrap();
writeln!(log_file, "Throughput: {:.0} ops/sec", query_count as f64 / query_duration.as_secs_f64()).unwrap();
}
+3 -3
View File
@@ -664,7 +664,7 @@ mod tests {
assert_eq!(offset, 100);
assert_eq!(length, 50);
}
_ => panic!("Expected Single location"),
ValueLocation::Packed { .. } => panic!("Expected Single location"),
}
assert_eq!(entries[1].sort_key, "banana".to_string());
@@ -675,7 +675,7 @@ mod tests {
assert_eq!(index, 3);
assert_eq!(length, 75);
}
_ => panic!("Expected Packed location"),
ValueLocation::Single { .. } => panic!("Expected Packed location"),
}
assert_eq!(entries[2].sort_key, "cherry".to_string());
@@ -685,7 +685,7 @@ mod tests {
assert_eq!(offset, 300);
assert_eq!(length, 100);
}
_ => panic!("Expected Single location"),
ValueLocation::Packed { .. } => panic!("Expected Single location"),
}
}
+17 -17
View File
@@ -117,47 +117,47 @@ mod tests {
#[test]
fn test_parse_timeshift() {
assert!(matches!(parse_timeshift(Some(&String::from("2"))), EpgTimeShift::Fixed(120)));
assert!(matches!(parse_timeshift(Some(&String::from("-1:30"))), EpgTimeShift::Fixed(-90)));
assert!(matches!(parse_timeshift(Some(&String::from("+0:15"))), EpgTimeShift::Fixed(15)));
assert!(matches!(parse_timeshift(Some(&String::from("1:45"))), EpgTimeShift::Fixed(105)));
assert!(matches!(parse_timeshift(Some(&String::from(":45"))), EpgTimeShift::Fixed(45)));
assert!(matches!(parse_timeshift(Some(&String::from("-:45"))), EpgTimeShift::Fixed(-45)));
assert!(matches!(parse_timeshift(Some(&String::from("0:30"))), EpgTimeShift::Fixed(30)));
assert!(matches!(parse_timeshift(Some(&String::from(":3"))), EpgTimeShift::Fixed(3)));
assert!(matches!(parse_timeshift(Some(&String::from("2:"))), EpgTimeShift::Fixed(120)));
assert!(matches!(parse_timeshift(Some(&String::from("+2:00"))), EpgTimeShift::Fixed(120)));
assert!(matches!(parse_timeshift(Some(&String::from("-0:10"))), EpgTimeShift::Fixed(-10)));
assert!(matches!(parse_timeshift(Some(&String::from("invalid"))), EpgTimeShift::None));
assert!(matches!(parse_timeshift(Some(&String::from("+abc"))), EpgTimeShift::None));
assert!(matches!(parse_timeshift(Some(&String::new())), EpgTimeShift::None));
assert!(matches!(parse_timeshift(Some("-1:30")), EpgTimeShift::Fixed(-90)));
assert!(matches!(parse_timeshift(Some("+0:15")), EpgTimeShift::Fixed(15)));
assert!(matches!(parse_timeshift(Some("1:45")), EpgTimeShift::Fixed(105)));
assert!(matches!(parse_timeshift(Some(":45")), EpgTimeShift::Fixed(45)));
assert!(matches!(parse_timeshift(Some("-:45")), EpgTimeShift::Fixed(-45)));
assert!(matches!(parse_timeshift(Some("0:30")), EpgTimeShift::Fixed(30)));
assert!(matches!(parse_timeshift(Some(":3")), EpgTimeShift::Fixed(3)));
assert!(matches!(parse_timeshift(Some("2:")), EpgTimeShift::Fixed(120)));
assert!(matches!(parse_timeshift(Some("+2:00")), EpgTimeShift::Fixed(120)));
assert!(matches!(parse_timeshift(Some("-0:10")), EpgTimeShift::Fixed(-10)));
assert!(matches!(parse_timeshift(Some("invalid")), EpgTimeShift::None));
assert!(matches!(parse_timeshift(Some("+abc")), EpgTimeShift::None));
assert!(matches!(parse_timeshift(Some("")), EpgTimeShift::None));
assert!(matches!(parse_timeshift(None), EpgTimeShift::None));
}
#[test]
fn test_parse_timezone() {
// Check timezone parsing creates the correct variant
let amterdam = parse_timeshift(Some(&"Europe/Amsterdam".to_string()));
let amterdam = parse_timeshift(Some("Europe/Amsterdam"));
if let EpgTimeShift::TimeZone(tz) = amterdam {
assert_eq!(tz.name(), "Europe/Amsterdam");
} else {
panic!("Expected TimeZone for Europe/Amsterdam");
}
let new_york = parse_timeshift(Some(&"America/New_York".to_string()));
let new_york = parse_timeshift(Some("America/New_York"));
if let EpgTimeShift::TimeZone(tz) = new_york {
assert_eq!(tz.name(), "America/New_York");
} else {
panic!("Expected TimeZone for America/New_York");
}
let tokyo = parse_timeshift(Some(&"Asia/Tokyo".to_string()));
let tokyo = parse_timeshift(Some("Asia/Tokyo"));
if let EpgTimeShift::TimeZone(tz) = tokyo {
assert_eq!(tz.name(), "Asia/Tokyo");
} else {
panic!("Expected TimeZone for Asia/Tokyo");
}
let utc = parse_timeshift(Some(&"UTC".to_string()));
let utc = parse_timeshift(Some("UTC"));
if let EpgTimeShift::TimeZone(tz) = utc {
assert_eq!(tz.name(), "UTC");
} else {
@@ -3,6 +3,7 @@ use std::str::FromStr;
use yew::prelude::*;
use crate::app::components::{NameStep, Panel, PlaylistAssistantContext, TypeStep};
#[allow(dead_code)]
enum PlaylistAssistantStep {
Name,
Type,
@@ -58,4 +59,4 @@ pub fn PlaylistAssistant() -> Html {
</div>
</ContextProvider<PlaylistAssistantContext >>
}
}
}
@@ -1,6 +1,7 @@
use yew::UseStateHandle;
#[derive(Clone, PartialEq)]
#[allow(dead_code)]
pub struct PlaylistAssistantContext {
pub custom_class: UseStateHandle<String>,
}
}
+3 -3
View File
@@ -718,7 +718,7 @@ mod tests {
match get_filter(flt, None) {
Ok(filter) => {
assert_eq!(format!("{filter}"), flt);
let channels = vec![
let channels = [
create_mock_pli("24/7: Cars", "FR Channels"),
create_mock_pli("24/7: Cars", "US Channels"),
create_mock_pli("Entertainment", "US Channels"),
@@ -768,7 +768,7 @@ mod tests {
match get_filter(flt, None) {
Ok(filter) => {
assert_eq!(format!("{filter}"), flt);
let channels = vec![
let channels = [
create_mock_pli("NA", "GA"),
create_mock_pli("NB", "GB"),
create_mock_pli("NA", "GB"),
@@ -833,7 +833,7 @@ mod tests {
match get_filter(flt, None) {
Ok(filter) => {
assert_eq!(format!("{filter}"), flt);
let channels = vec![
let channels = [
create_mock_pli("24/7: Cars", "FR Channels"),
create_mock_pli("24/7: Cars", "US Channels"),
create_mock_pli("Entertainment", "US Channels"),