use ass_editor::*;
use std::time::Instant;
#[test]
fn test_rapid_consecutive_operations() {
let mut doc = EditorDocument::new();
let start = Instant::now();
for i in 0..10000 {
if i % 2 == 0 {
doc.insert(Position::new(0), "x").unwrap();
} else {
doc.delete(Range::new(Position::new(0), Position::new(1)))
.unwrap();
}
}
let elapsed = start.elapsed();
println!("10000 operations took: {elapsed:?}");
assert!(doc.is_empty());
}
#[test]
fn test_nested_undo_redo_performance() {
let mut doc = EditorDocument::new();
for i in 0..100 {
doc.insert(Position::new(doc.len_bytes()), &format!("Line {i}\n"))
.unwrap();
}
let start = Instant::now();
let mut undo_count = 0;
while doc.undo().is_ok() {
undo_count += 1;
}
let mut redo_count = 0;
while doc.redo().is_ok() {
redo_count += 1;
}
let elapsed = start.elapsed();
println!("{undo_count} undo + {redo_count} redo operations took: {elapsed:?}");
assert!(doc.text().contains("Line"));
}
#[test]
fn test_pathological_line_lengths() {
let long_line = "a".repeat(100000);
let mut doc = EditorDocument::from_content(&long_line).unwrap();
doc.insert(Position::new(50000), "MIDDLE").unwrap();
assert!(doc.text().contains("MIDDLE"));
doc.delete(Range::new(Position::new(50000), Position::new(50006)))
.unwrap();
assert!(!doc.text().contains("MIDDLE"));
}
#[test]
fn test_many_small_lines() {
let mut content = String::new();
for i in 0..10000 {
content.push_str(&format!("{i}\n"));
}
let mut doc = EditorDocument::from_content(&content).unwrap();
assert_eq!(doc.len_lines(), 10001);
let mid_pos = Position::new(content.len() / 2);
doc.insert(mid_pos, "INSERTED").unwrap();
assert!(doc.text().contains("INSERTED"));
}
#[test]
fn test_fragmented_editing_pattern() {
let mut doc = EditorDocument::from_content("0123456789".repeat(100).as_str()).unwrap();
for i in (0..1000).step_by(10) {
let pos = Position::new(i);
doc.replace(Range::new(pos, pos.advance(1)), "X").unwrap();
}
let x_count = doc.text().matches('X').count();
assert_eq!(x_count, 100);
}
#[test]
fn test_memory_pressure_document_growth() {
let mut doc = EditorDocument::new();
for i in 0..1000 {
let chunk = format!("Chunk {i}: {}\n", "data".repeat(100));
doc.insert(Position::new(doc.len_bytes()), &chunk).unwrap();
if i % 100 == 99 {
let mut undo_count = 0;
for _ in 0..10 {
if doc.undo().is_ok() {
undo_count += 1;
} else {
break;
}
}
for _ in 0..undo_count {
if doc.redo().is_err() {
break;
}
}
}
}
let final_size = doc.len_bytes();
eprintln!("Final document size: {final_size} bytes");
assert!(final_size > 100000); }
#[test]
fn test_replace_performance_patterns() {
let mut doc = EditorDocument::from_content(&"test ".repeat(10000)).unwrap();
let start = Instant::now();
let mut offset = 0;
while let Some(pos) = doc.text()[offset..].find("test") {
let abs_pos = offset + pos;
doc.replace(
Range::new(Position::new(abs_pos), Position::new(abs_pos + 4)),
"replaced",
)
.unwrap();
offset = abs_pos + 8; }
let elapsed = start.elapsed();
println!("Replace-all operation took: {elapsed:?}");
assert!(!doc.text().contains("test"));
assert_eq!(doc.text().matches("replaced").count(), 10000);
}
#[test]
fn test_large_ass_script_handling() {
let mut content = String::from("[Script Info]\nTitle: Large Script\n\n[Events]\n");
content.push_str(
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n",
);
for i in 0..10000 {
let hours = i / 3600;
let minutes = (i % 3600) / 60;
let seconds = i % 60;
content.push_str(&format!(
"Dialogue: 0,{hours}:{minutes:02}:{seconds:02}.00,{hours}:{minutes:02}:{:02}.00,Default,,0,0,0,,Line {i} text\n",
seconds + 1
));
}
let start = Instant::now();
let doc = EditorDocument::from_content(&content).unwrap();
let parse_time = start.elapsed();
println!("Parsing 10000 dialogue lines took: {parse_time:?}");
assert!(doc.text().contains("Line 9999 text"));
}
#[test]
fn test_complex_ass_formatting_tags() {
let complex_line = r"Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,{\pos(192,108)\fad(200,200)\1c&H00FF00&\3c&H000000&\bord2\shad1\be1\fs24\fn Arial\i1\b1\u1\s1\frz30\frx15\fry20\fscx120\fscy80\fsp3\q2\a6\k100}Complex{\k50}Text{\r}Normal";
let mut doc =
EditorDocument::from_content(&format!("[Script Info]\n\n[Events]\n{complex_line}"))
.unwrap();
assert!(doc.text().contains(r"{\pos(192,108)"));
assert!(doc.text().contains(r"{\k50}"));
let tag_pos = doc.text().find(r"\fs24").unwrap();
doc.replace(
Range::new(Position::new(tag_pos + 3), Position::new(tag_pos + 5)),
"48",
)
.unwrap();
assert!(doc.text().contains(r"\fs48"));
}
#[test]
fn test_position_calculation_edge_cases() {
let content = "ASCII 中文 🎭 Émojis 👨👩👧👦\nLine2\r\nLine3";
let doc = EditorDocument::from_content(content).unwrap();
let positions = vec![
(0, 1, 1), (5, 1, 6), (6, 1, 7), (9, 1, 8), (12, 1, 9), (13, 1, 10), (17, 1, 11), ];
for (byte_offset, expected_line, expected_col) in positions {
let lc = doc
.position_to_line_column(Position::new(byte_offset))
.unwrap();
assert_eq!(lc.line, expected_line);
assert_eq!(lc.column, expected_col);
}
}
#[test]
fn test_boundary_crossing_operations() {
let mut doc = EditorDocument::from_content("Line1\nLine2\nLine3").unwrap();
let start = Position::new(5); let end = Position::new(11);
doc.delete(Range::new(start, end)).unwrap();
assert_eq!(doc.text(), "Line1\nLine3");
doc.insert(Position::new(6), "Line2\n").unwrap();
assert_eq!(doc.text(), "Line1\nLine2\nLine3");
}
#[test]
fn test_zero_width_operations() {
let mut doc = EditorDocument::from_content("Test").unwrap();
let before = doc.text();
doc.delete(Range::new(Position::new(2), Position::new(2)))
.unwrap();
assert_eq!(doc.text(), before);
doc.replace(Range::new(Position::new(2), Position::new(2)), "X")
.unwrap();
assert_eq!(doc.text(), "TeXst");
}
#[cfg(feature = "plugins")]
#[test]
fn test_extension_manager_under_load() {
let mut manager = ExtensionManager::new();
manager
.load_extension(Box::new(
ass_editor::extensions::builtin::syntax_highlight::SyntaxHighlightExtension::new(),
))
.unwrap();
manager
.load_extension(Box::new(
ass_editor::extensions::builtin::auto_complete::AutoCompleteExtension::new(),
))
.unwrap();
for i in 0..1000 {
manager.set_config(format!("key_{i}"), format!("value_{i}"));
}
let start = Instant::now();
for i in 0..1000 {
assert_eq!(
manager.get_config(&format!("key_{i}")).as_deref(),
Some(&format!("value_{i}")[..])
);
}
let elapsed = start.elapsed();
println!("1000 config lookups took: {elapsed:?}");
}
#[test]
fn test_error_recovery_stress() {
let mut doc = EditorDocument::from_content("Initial content").unwrap();
let mut success_count = 0;
let mut error_count = 0;
for i in 0..1000 {
let result = match i % 4 {
0 => doc.insert(Position::new(i % doc.len_bytes()), "x"),
1 => doc.delete(Range::new(Position::new(0), Position::new(1))),
2 => doc.insert(Position::new(999999), "invalid"), _ => doc.delete(Range::new(Position::new(999999), Position::new(1000000))), };
match result {
Ok(_) => success_count += 1,
Err(_) => error_count += 1,
}
}
assert!(success_count > 400 && success_count < 600);
assert!(error_count > 400 && error_count < 600);
assert!(!doc.text().is_empty());
}