use rumdl_lib::lint_context::LintContext;
use rumdl_lib::rule::Rule;
use rumdl_lib::rules::MD032BlanksAroundLists;
use std::time::Instant;
#[test]
fn test_deeply_nested_unordered_lists_performance() {
let content = generate_deeply_nested_unordered_lists(15, 5);
let start = Instant::now();
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let parsing_duration = start.elapsed();
assert!(
parsing_duration.as_millis() < 100,
"Parsing took {}ms, expected < 100ms",
parsing_duration.as_millis()
);
assert!(!ctx.list_blocks.is_empty(), "Should detect list blocks");
let rule = MD032BlanksAroundLists::default();
let rule_start = Instant::now();
let warnings = rule.check(&ctx).unwrap();
let rule_duration = rule_start.elapsed();
assert!(
rule_duration.as_millis() < 50,
"Rule checking took {}ms, expected < 50ms",
rule_duration.as_millis()
);
println!(
"Deeply nested unordered lists (15 levels): parse={}ms, rule={}ms, warnings={}",
parsing_duration.as_millis(),
rule_duration.as_millis(),
warnings.len()
);
}
#[test]
fn test_deeply_nested_ordered_lists_performance() {
let content = generate_deeply_nested_ordered_lists(12, 8);
let start = Instant::now();
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let parsing_duration = start.elapsed();
assert!(
parsing_duration.as_millis() < 150,
"Parsing took {}ms, expected < 150ms",
parsing_duration.as_millis()
);
let rule = MD032BlanksAroundLists::default();
let rule_start = Instant::now();
let warnings = rule.check(&ctx).unwrap();
let rule_duration = rule_start.elapsed();
assert!(
rule_duration.as_millis() < 75,
"Rule checking took {}ms, expected < 75ms",
rule_duration.as_millis()
);
println!(
"Deeply nested ordered lists (12 levels): parse={}ms, rule={}ms, warnings={}",
parsing_duration.as_millis(),
rule_duration.as_millis(),
warnings.len()
);
}
#[test]
fn test_mixed_deeply_nested_lists_performance() {
let content = generate_mixed_deeply_nested_lists(10, 6);
let start = Instant::now();
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let parsing_duration = start.elapsed();
assert!(
parsing_duration.as_millis() < 120,
"Parsing took {}ms, expected < 120ms",
parsing_duration.as_millis()
);
assert!(ctx.list_blocks.len() >= 2, "Should detect multiple list blocks");
let rule = MD032BlanksAroundLists::default();
let rule_start = Instant::now();
let warnings = rule.check(&ctx).unwrap();
let rule_duration = rule_start.elapsed();
assert!(
rule_duration.as_millis() < 60,
"Rule checking took {}ms, expected < 60ms",
rule_duration.as_millis()
);
println!(
"Mixed deeply nested lists (10 levels): parse={}ms, rule={}ms, warnings={}",
parsing_duration.as_millis(),
rule_duration.as_millis(),
warnings.len()
);
}
#[test]
fn test_extremely_wide_nested_lists_performance() {
let content = generate_wide_nested_lists(6, 50);
let start = Instant::now();
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let parsing_duration = start.elapsed();
assert!(
parsing_duration.as_millis() < 200,
"Parsing took {}ms, expected < 200ms",
parsing_duration.as_millis()
);
let rule = MD032BlanksAroundLists::default();
let rule_start = Instant::now();
let warnings = rule.check(&ctx).unwrap();
let rule_duration = rule_start.elapsed();
assert!(
rule_duration.as_millis() < 100,
"Rule checking took {}ms, expected < 100ms",
rule_duration.as_millis()
);
println!(
"Wide nested lists (6 levels, 50 items): parse={}ms, rule={}ms, warnings={}",
parsing_duration.as_millis(),
rule_duration.as_millis(),
warnings.len()
);
}
#[test]
fn test_pathological_nesting_with_content_performance() {
let content = generate_pathological_nested_content(8);
let start = Instant::now();
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let parsing_duration = start.elapsed();
assert!(
parsing_duration.as_millis() < 300,
"Parsing took {}ms, expected < 300ms",
parsing_duration.as_millis()
);
let rule = MD032BlanksAroundLists::default();
let rule_start = Instant::now();
let warnings = rule.check(&ctx).unwrap();
let rule_duration = rule_start.elapsed();
assert!(
rule_duration.as_millis() < 150,
"Rule checking took {}ms, expected < 150ms",
rule_duration.as_millis()
);
println!(
"Pathological nested content (8 levels): parse={}ms, rule={}ms, warnings={}",
parsing_duration.as_millis(),
rule_duration.as_millis(),
warnings.len()
);
}
#[test]
fn test_fix_performance_on_deeply_nested_lists() {
let content = generate_nested_lists_needing_fixes(10, 4);
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let rule = MD032BlanksAroundLists::default();
let warnings = rule.check(&ctx).unwrap();
assert!(!warnings.is_empty(), "Should have warnings to fix");
let fix_start = Instant::now();
let fixed_content = rule.fix(&ctx).unwrap();
let fix_duration = fix_start.elapsed();
assert!(
fix_duration.as_millis() < 100,
"Fix took {}ms, expected < 100ms",
fix_duration.as_millis()
);
assert_ne!(fixed_content, content, "Content should be modified by fix");
let fixed_ctx = LintContext::new(&fixed_content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let warnings_after_fix = rule.check(&fixed_ctx).unwrap();
assert!(
warnings_after_fix.len() < warnings.len(),
"Fix should reduce number of warnings"
);
println!(
"Fix on nested lists needing fixes (10 levels): fix={}ms, warnings_before={}, warnings_after={}",
fix_duration.as_millis(),
warnings.len(),
warnings_after_fix.len()
);
}
#[test]
fn test_memory_usage_with_extreme_nesting() {
let content = generate_memory_stress_lists(20, 3);
let start = Instant::now();
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let parsing_duration = start.elapsed();
assert!(
parsing_duration.as_millis() < 500,
"Memory stress test took {}ms, expected < 500ms",
parsing_duration.as_millis()
);
assert!(
!ctx.list_blocks.is_empty(),
"Should detect list blocks even with extreme nesting"
);
let rule = MD032BlanksAroundLists::default();
let rule_start = Instant::now();
let _warnings = rule.check(&ctx).unwrap(); let rule_duration = rule_start.elapsed();
assert!(
rule_duration.as_millis() < 200,
"Rule on memory stress test took {}ms, expected < 200ms",
rule_duration.as_millis()
);
println!(
"Memory stress test (20 levels): parse={}ms, rule={}ms",
parsing_duration.as_millis(),
rule_duration.as_millis()
);
}
fn generate_deeply_nested_unordered_lists(max_depth: usize, items_per_level: usize) -> String {
let mut content = String::new();
content.push_str("# Test Document\n\n");
for depth in 0..max_depth {
let indent = " ".repeat(depth);
for item in 1..=items_per_level {
content.push_str(&format!(
"{}* List item at depth {} number {}\n",
indent,
depth + 1,
item
));
if item == 1 {
content.push_str(&format!("{indent} Additional content for item {item}\n"));
}
}
if depth < max_depth - 1 {
content.push('\n');
}
}
content.push_str("\n\nEnd of document.\n");
content
}
fn generate_deeply_nested_ordered_lists(max_depth: usize, items_per_level: usize) -> String {
let mut content = String::new();
content.push_str("# Ordered Lists Test\n\n");
for depth in 0..max_depth {
let indent = " ".repeat(depth); for item in 1..=items_per_level {
content.push_str(&format!(
"{}{}. Ordered item at depth {} number {}\n",
indent,
item,
depth + 1,
item
));
if item == items_per_level / 2 {
content.push_str(&format!("{indent} Continuation paragraph for item {item}\n"));
}
}
if depth < max_depth - 1 {
content.push('\n');
}
}
content.push_str("\n\nEnd of ordered test.\n");
content
}
fn generate_mixed_deeply_nested_lists(max_depth: usize, items_per_level: usize) -> String {
let mut content = String::new();
content.push_str("# Mixed Lists Test\n\n");
for depth in 0..max_depth {
let indent = " ".repeat(depth);
let is_ordered = depth % 2 == 0;
for item in 1..=items_per_level {
if is_ordered {
content.push_str(&format!("{indent}{item}. Mixed ordered item {item}\n"));
} else {
content.push_str(&format!("{indent}- Mixed unordered item {item}\n"));
}
}
if depth < max_depth - 1 {
content.push('\n');
}
}
content.push_str("\n\nEnd of mixed test.\n");
content
}
fn generate_wide_nested_lists(max_depth: usize, items_per_level: usize) -> String {
let mut content = String::new();
content.push_str("# Wide Lists Test\n\n");
for depth in 0..max_depth {
let indent = " ".repeat(depth);
for item in 1..=items_per_level {
content.push_str(&format!("{}* Wide item {} at depth {}\n", indent, item, depth + 1));
if item % 10 == 0 {
content.push_str(&format!("{indent} Special content for item {item}\n"));
}
}
if depth < max_depth - 1 {
content.push('\n');
}
}
content.push_str("\n\nEnd of wide test.\n");
content
}
fn generate_pathological_nested_content(max_depth: usize) -> String {
let mut content = String::new();
content.push_str("# Pathological Content Test\n\n");
for depth in 0..max_depth {
let indent = " ".repeat(depth);
content.push_str(&format!("{}* Complex item at depth {}\n", indent, depth + 1));
content.push_str(&format!("{indent} \n")); content.push_str(&format!("{indent} > Blockquote within list\n"));
content.push_str(&format!("{indent} > Second line of quote\n"));
content.push_str(&format!("{indent} \n"));
content.push_str(&format!("{indent} ```code\n"));
content.push_str(&format!("{indent} Code block within list\n"));
content.push_str(&format!("{indent} ```\n"));
content.push_str(&format!("{indent} \n"));
content.push_str(&format!("{indent} Another paragraph in the list item.\n"));
if depth < max_depth - 1 {
content.push('\n');
}
}
content.push_str("\n\nEnd of pathological test.\n");
content
}
fn generate_nested_lists_needing_fixes(max_depth: usize, items_per_level: usize) -> String {
let mut content = String::new();
content.push_str("# Lists Needing Fixes\n");
for depth in 0..max_depth {
let indent = " ".repeat(depth);
for item in 1..=items_per_level {
content.push_str(&format!("{}* Item {} at depth {}\n", indent, item, depth + 1));
}
content.push_str(&format!(
"{}Paragraph immediately after list at depth {}\n",
" ".repeat(depth * 2),
depth + 1
));
if depth < max_depth - 1 {
content.push_str(&format!("{indent}* Next list starts immediately\n"));
}
}
content.push_str("Final paragraph.\n");
content
}
fn generate_memory_stress_lists(max_depth: usize, items_per_level: usize) -> String {
let mut content = String::new();
content.push_str("# Memory Stress Test\n\n");
for depth in 0..max_depth {
let indent = " ".repeat(depth);
for item in 1..=items_per_level {
content.push_str(&format!("{}* Deep item {} (depth {})\n", indent, item, depth + 1));
if depth % 3 == 0 {
content.push_str(&format!("{indent} **Bold text** in item {item}\n"));
}
if depth % 5 == 0 {
content.push_str(&format!("{indent} [Link text](https://example.com/{depth}/{item})\n"));
}
}
}
content.push_str("\n\nEnd of memory stress test.\n");
content
}