use mdbook_lint_core::{Document, Rule};
use mdbook_lint_rulesets::standard::{md049::MD049, md051::MD051};
use std::path::PathBuf;
use std::time::{Duration, Instant};
fn create_test_document(content: &str) -> Document {
Document::new(content.to_string(), PathBuf::from("test.md")).unwrap()
}
fn assert_completes_quickly<R: Rule>(rule: &R, document: &Document, max_duration: Duration) {
let start = Instant::now();
let result = rule.check(document);
let elapsed = start.elapsed();
assert!(
elapsed < max_duration,
"Rule {} took {:?} (max allowed: {:?})",
rule.id(),
elapsed,
max_duration
);
assert!(result.is_ok(), "Rule check failed: {:?}", result);
}
#[test]
fn test_md051_large_html_content_performance() {
let mut content = String::from("# Test Document\n\n");
for i in 0..100 {
let section = format!(
r##"<div id="section-{}" class="content">
<p id="para-{}-1">Content here</p>
<span id="span-{}-1">More content</span>
<a name="anchor-{}" href="#top">Link</a>
<h2 id="heading-{}">Subsection {}</h2>
<div id="nested-{}" data-value="test">
<p id="deep-{}-1">Nested paragraph</p>
<span id="deep-{}-2">Nested span</span>
</div>
</div>
"##,
i, i, i, i, i, i, i, i, i
);
content.push_str(§ion);
}
content.push_str("\n## Links\n\n");
for i in 0..50 {
let link1 = format!("- [Section {}](#section-{})\n", i, i);
let link2 = format!("- [Heading {}](#heading-{})\n", i, i);
content.push_str(&link1);
content.push_str(&link2);
}
let document = create_test_document(&content);
let rule = MD051::new();
assert_completes_quickly(&rule, &document, Duration::from_millis(500));
}
#[test]
fn test_md051_pathological_html_attributes() {
let mut content = String::from("# Document\n\n");
for i in 0..50 {
let html_block = format!(
r##"<div class="class-{}" data-test="value" id="element-{}" style="color: red" onclick="handleClick()" data-id="{}" title="Element {}">
<input type="text" name="field-{}" id="input-{}" value="test" placeholder="Enter text" required>
<a href="#anchor-{}" name="link-{}" id="anchor-{}" class="link">Link text</a>
</div>
"##,
i, i, i, i, i, i, i, i, i
);
content.push_str(&html_block);
}
let document = create_test_document(&content);
let rule = MD051::new();
assert_completes_quickly(&rule, &document, Duration::from_millis(200));
}
#[test]
fn test_md049_code_spans_with_underscore_asterisk_patterns() {
let content = r#"# Rust Methods
Use the following methods for overflow handling:
- Wrap in all modes with the `wrapping_*` methods, such as `wrapping_add`.
- Return the `None` value if there is overflow with the `checked_*` methods.
- Return the value and a boolean with the `overflowing_*` methods.
- Saturate at min/max values with the `saturating_*` methods.
You can also use patterns like `impl_*` or `trait_*` in macros.
The `*_mut` and `*_ref` patterns are common in Rust:
- `as_mut`, `as_ref`
- `get_mut`, `get_ref`
- `iter_mut`, `into_iter`
Some more examples: `Box<dyn Any + '_>`, `&'_ str`, `*const T`, `*mut T`.
And here's some actual *emphasized text* and _also emphasized_ text.
"#;
let document = create_test_document(content);
let rule = MD049::new();
assert_completes_quickly(&rule, &document, Duration::from_millis(100));
}
#[test]
fn test_md049_many_code_spans_performance() {
let mut content = String::from("# Document\n\n");
for i in 0..100 {
content.push_str(&format!(
"Use `function_{}` with `param_*` and `result_{}`. ",
i, i
));
content.push_str(&format!("The `check_{}` validates `input_*` patterns. ", i));
content.push_str(&format!("Call `wrapper_{}` for `*_ptr` handling.\n", i));
}
content.push_str("\nThis has *real emphasis* and _also this_.\n");
let document = create_test_document(&content);
let rule = MD049::new();
assert_completes_quickly(&rule, &document, Duration::from_millis(100));
}
#[test]
fn test_md049_nested_backticks_and_emphasis() {
let content = r#"# Complex Patterns
Here's a line with `code containing * and _` mixed with *real emphasis*.
Multiple backticks: ``usage: `command_*` `` and then _emphasis_.
Triple backticks inline: ```not a code block but `inline` ``` with *emphasis*.
Edge cases:
- `_underscore_start`
- `asterisk_end*`
- `*both*_mixed_*`
- `_*_*_*_` (pathological but valid)
And normal text with *proper emphasis* and _underscored emphasis_.
"#;
let document = create_test_document(content);
let rule = MD049::new();
assert_completes_quickly(&rule, &document, Duration::from_millis(100));
}
#[test]
fn test_combined_performance_stress_test() {
let mut content = String::from("# Combined Stress Test\n\n");
for i in 0..50 {
let html_section = format!(
r##"<section id="section-{}" class="main">
<h2 id="header-{}">Section {}</h2>
</section>
"##,
i, i, i
);
content.push_str(&html_section);
}
content.push_str("\n## Code Patterns\n\n");
for i in 0..50 {
content.push_str(&format!(
"Use `method_{}` with `pattern_*` and `suffix_{}`. ",
i, i
));
}
content.push_str("\n## Links\n\n");
for i in 0..25 {
let link = format!("- [Link to section {}](#section-{})\n", i, i);
content.push_str(&link);
}
let document = create_test_document(&content);
let md051 = MD051::new();
assert_completes_quickly(&md051, &document, Duration::from_millis(300));
let md049 = MD049::new();
assert_completes_quickly(&md049, &document, Duration::from_millis(100));
}