use ass_editor::{
commands::*,
core::{EditorDocument, Position},
utils::search::{DocumentSearch, DocumentSearchImpl},
};
use std::time::{Duration, Instant};
fn generate_massive_script(events: usize) -> String {
let mut script = String::from(
r#"[Script Info]
Title: Massive Stress Test Script
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.601
PlayResX: 3840
PlayResY: 2160
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
"#,
);
for i in 0..100 {
script.push_str(&format!(
"Style: Style{i},Arial,{},&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1\n",
20 + (i % 20)
));
}
script.push_str("\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n");
for i in 0..events {
let hours = i / 3600;
let minutes = (i % 3600) / 60;
let seconds = i % 60;
let start_time = format!("{hours}:{minutes:02}:{seconds:02}.00");
let end_time = format!("{}:{:02}:{:02}.00", hours, minutes, seconds + 5);
let style = format!("Style{}", i % 100);
let text = match i % 10 {
0 => format!("Simple dialogue line number {i}"),
1 => format!("{{\\pos(1920,1080)}}Positioned text at line {i}"),
2 => format!("{{\\k20}}Ka{{\\k30}}ra{{\\k25}}o{{\\k35}}ke line {i}"),
3 => format!("{{\\fade(255,0,255,0,0,500,1000)}}Fading text {i}"),
4 => format!("{{\\move(100,100,3740,2060)}}Moving across screen {i}"),
5 => format!("{{\\be1\\blur5}}Blurred effect on line {i}"),
6 => format!("Multi\\Nline\\Ntext\\Nwith\\Nbreaks {i}"),
7 => format!("{{\\c&HFF0000&}}Red {{\\c&H00FF00&}}Green {{\\c&H0000FF&}}Blue {i}"),
8 => format!("{{\\fscx150\\fscy150}}Scaled larger text {i}"),
_ => format!("{{\\frz45}}Rotated diagonal text line {i}"),
};
script.push_str(&format!(
"Dialogue: 0,{start_time},{end_time},{style},,0,0,0,,{text}\n"
));
}
script
}
#[test]
#[ignore] fn stress_test_massive_document_parsing() {
let sizes = vec![10_000, 50_000, 100_000];
for size in sizes {
println!("\nTesting with {size} events...");
let script = generate_massive_script(size);
let script_len = script.len();
println!(
"Script size: {script_len} bytes ({:.2} MB)",
script_len as f64 / 1_048_576.0
);
let start = Instant::now();
let doc = EditorDocument::from_content(&script).unwrap();
let parse_time = start.elapsed();
println!("Parse time: {parse_time:?}");
assert!(parse_time < Duration::from_secs(5), "Parsing took too long");
assert_eq!(doc.text(), script);
doc.validate().unwrap();
}
}
#[test]
#[ignore]
fn stress_test_many_small_edits() {
let mut doc = EditorDocument::from_content(&generate_massive_script(10_000)).unwrap();
let num_edits = 1000;
println!("\nPerforming {num_edits} small edits...");
let start = Instant::now();
for i in 0..num_edits {
let pos = Position::new((i * 100) % doc.len());
doc.insert(pos, "X").unwrap();
}
let edit_time = start.elapsed();
println!("Total edit time: {edit_time:?}");
let avg_time = edit_time / num_edits as u32;
println!("Average per edit: {avg_time:?}");
assert!(edit_time < Duration::from_millis((num_edits * 2) as u64));
let undo_start = Instant::now();
let mut undo_count = 0;
while doc.can_undo() && undo_count < 100 {
doc.undo().unwrap();
undo_count += 1;
}
let undo_time = undo_start.elapsed();
println!("Undo {undo_count} operations: {undo_time:?}");
}
#[test]
#[ignore]
fn stress_test_search_performance() {
use ass_editor::utils::search::{SearchOptions, SearchScope};
let doc = EditorDocument::from_content(&generate_massive_script(50_000)).unwrap();
let mut search = DocumentSearchImpl::new();
search.build_index(&doc).unwrap();
let patterns = vec!["text", "line", "\\k", "pos", "a"];
for pattern in patterns {
println!("\nSearching for '{pattern}'...");
let options = SearchOptions {
case_sensitive: false,
whole_words: false,
use_regex: false,
scope: SearchScope::All,
max_results: 10_000,
};
let start = Instant::now();
let results = search.search(pattern, &options).unwrap();
let search_time = start.elapsed();
let result_count = results.len();
println!("Found {result_count} results in {search_time:?}");
assert!(search_time < Duration::from_millis(500));
}
}
#[test]
#[ignore]
fn stress_test_batch_operations() {
let mut doc = EditorDocument::from_content(&generate_massive_script(20_000)).unwrap();
let mut batch = BatchCommand::new("Massive batch operation".to_string());
for i in 0..100 {
match i % 5 {
0 => {
batch = batch.add_command(Box::new(
EditStyleCommand::new(format!("Style{}", i % 100))
.set_size(22 + (i % 10) as u32),
));
}
1 => {
batch = batch.add_command(Box::new(InsertTagCommand::new(
Position::new(i * 100),
format!("\\fade({},0)", i * 10),
)));
}
2 => {
let pos = Position::new((i * 1000) % doc.len());
batch = batch.add_command(Box::new(InsertTextCommand::new(pos, format!("[{i}]"))));
}
3 => {
batch = batch.add_command(Box::new(ApplyStyleCommand::new(
format!("Style{}", i % 50),
"Default".to_string(),
)));
}
_ => {
batch = batch.add_command(Box::new(TimingAdjustCommand::new(
vec![i, i + 1, i + 2],
100,
100,
)));
}
}
}
println!("\nExecuting batch with 100 commands...");
let start = Instant::now();
let result = batch.execute(&mut doc).unwrap();
let batch_time = start.elapsed();
println!("Batch execution time: {batch_time:?}");
assert!(result.success);
assert!(batch_time < Duration::from_secs(2));
let undo_start = Instant::now();
doc.undo().unwrap();
let undo_time = undo_start.elapsed();
println!("Batch undo time: {undo_time:?}");
assert!(undo_time < Duration::from_millis(500));
}
#[test]
#[ignore]
fn stress_test_memory_efficiency() {
let initial_script = generate_massive_script(5_000);
let initial_size = initial_script.len();
for i in 0..10 {
let mut doc = EditorDocument::from_content(&initial_script).unwrap();
for j in 0..100 {
let pos = Position::new((i * j) % doc.len());
doc.insert(pos, "TEST").unwrap();
}
for _ in 0..50 {
doc.undo().unwrap();
}
let final_size = doc.text().len();
assert!(final_size < initial_size * 2);
drop(doc);
}
}
#[test]
#[ignore]
fn stress_test_concurrent_operations() {
#[cfg(feature = "concurrency")]
{
use ass_editor::core::SyncDocument;
let doc = EditorDocument::from_content(&generate_massive_script(1_000)).unwrap();
let sync_doc = SyncDocument::new(doc);
println!("\nTesting SyncDocument operations...");
let start = Instant::now();
let text = sync_doc.text().unwrap();
assert!(!text.is_empty());
sync_doc.validate().unwrap();
for i in 0..50 {
let result = sync_doc.with_write(|doc| {
let pos = Position::new((i * 100).min(doc.len()));
let _ = doc.insert(pos, "X");
Ok(())
});
assert!(result.is_ok());
}
let sync_time = start.elapsed();
println!("Sync operations completed in {sync_time:?}");
}
}
#[test]
#[ignore]
#[cfg(feature = "formats")]
fn stress_test_format_conversions() {
use ass_editor::utils::formats::{ConversionOptions, FormatConverter, SubtitleFormat};
let doc = EditorDocument::from_content(&generate_massive_script(5_000)).unwrap();
let options = ConversionOptions::default();
println!("\nTesting format conversions on large document...");
let start = Instant::now();
let srt = FormatConverter::export(&doc, SubtitleFormat::SRT, &options).unwrap();
let srt_time = start.elapsed();
let srt_len = srt.len();
println!("SRT export ({srt_len} bytes): {srt_time:?}");
let start = Instant::now();
let vtt = FormatConverter::export(&doc, SubtitleFormat::WebVTT, &options).unwrap();
let vtt_time = start.elapsed();
let vtt_len = vtt.len();
println!("WebVTT export ({vtt_len} bytes): {vtt_time:?}");
let start = Instant::now();
let ass_from_srt = FormatConverter::import(&srt, Some(SubtitleFormat::SRT)).unwrap();
let import_time = start.elapsed();
println!("Import from SRT: {import_time:?}");
let _doc2 = EditorDocument::from_content(&ass_from_srt).unwrap();
}
#[test]
#[ignore]
fn stress_test_validation_performance() {
let mut doc = EditorDocument::from_content(&generate_massive_script(20_000)).unwrap();
println!("\nTesting validation performance...");
let start = Instant::now();
doc.validate().unwrap();
let basic_time = start.elapsed();
println!("Basic validation: {basic_time:?}");
let start = Instant::now();
let issues = doc.validate_comprehensive().unwrap();
let comprehensive_time = start.elapsed();
let issue_count = issues.issues.len();
println!("Comprehensive validation: {comprehensive_time:?}, {issue_count} issues found");
assert!(basic_time < Duration::from_millis(100));
assert!(comprehensive_time < Duration::from_secs(1));
}