use mdtablefix::process::WRAP_COLS;
use proptest::prelude::*;
#[path = "support/idempotence_generators.rs"]
mod idempotence_generators;
#[path = "support/idempotence_harness.rs"]
mod idempotence_harness;
#[path = "support/layout_normalization.rs"]
mod layout_normalization;
#[path = "support/idempotence_reachability.rs"]
mod idempotence_reachability;
use idempotence_generators::{document_strategy, overlong_code_span_block_strategy};
use idempotence_harness::{
BREAK_SPELLINGS,
SWEEP_DOCUMENTS,
flags_for,
format_twice,
proptest_config,
sample,
};
const BRACKET_SEAM_HEAD_WIDTH: usize = WRAP_COLS - 1;
fn bracket_reference_seam_strategy() -> impl Strategy<Value = String> {
(proptest::collection::vec("[a-z]{2,6}", 1..=6), "[0-9]{1,2}").prop_map(|(words, digits)| {
let mut head = format!("{} **bold**`code`", words.join(" "));
while head.len() + 5 <= BRACKET_SEAM_HEAD_WIDTH {
head.push_str(" aaaa");
}
if head.len() < BRACKET_SEAM_HEAD_WIDTH {
head.push(' ');
head.push_str(&"a".repeat(BRACKET_SEAM_HEAD_WIDTH - head.len()));
}
format!("{head}\n[{digits}]")
})
}
fn code_emphasis_table_strategy() -> impl Strategy<Value = String> {
let words = || proptest::collection::vec("[a-z]{2,8}", 1..=3).prop_map(|words| words.join(" "));
(
words(),
words(),
words(),
prop_oneof![Just("*"), Just("_")],
words(),
words(),
)
.prop_map(|(header, row, prefix, marker, code, suffix)| {
format!(
"| {header} | Notes |\n| --- | ----- |\n| {row} | {prefix} \
{marker}`{code}`{marker} {suffix} |\n"
)
})
}
proptest! {
#![proptest_config(proptest_config())]
#[test]
fn cli_formatting_reaches_a_fixed_point(
document in document_strategy(),
mask in 0u16..=255u16,
) {
let flags = flags_for(mask);
let (once, twice) = format_twice(&document, &flags);
prop_assert_eq!(
&twice,
&once,
"formatting is not a fixed point for flags {:?}\ninput:\n{}\npass 1:\n{}\npass 2:\n{}",
flags,
document,
once,
twice,
);
}
#[test]
fn code_emphasis_table_repair_reaches_a_fixed_point(
document in code_emphasis_table_strategy(),
wrap in any::<bool>(),
ellipsis in any::<bool>(),
) {
let mut flags = vec!["--code-emphasis"];
if wrap {
flags.push("--wrap");
}
if ellipsis {
flags.push("--ellipsis");
}
let (once, twice) = format_twice(&document, &flags);
prop_assert_eq!(
&twice,
&once,
"code-emphasis table formatting is not a fixed point for flags {:?}\\ninput:\\n{}\\npass 1:\\n{}\\npass 2:\\n{}",
flags,
document,
once,
twice,
);
}
}
#[test]
fn wrapped_list_continuation_keeps_outdented_underline_stable() {
let document = concat!(
" 1. aaaaaaaa aaaaa aaaaaa aaaaaaa vyv lkklcbhp yxabwf ncm he pkdqzfkd loeyhsa cs\n",
"smvburhk xpurxf vtbb wggfap hn on uuqti xv icqdjk\n",
"-----\n",
);
let flags = [
"--wrap",
"--ellipsis",
"--footnotes",
"--code-emphasis",
"--headings",
];
let (once, twice) = format_twice(document, &flags);
assert_eq!(twice, once);
assert!(once.lines().any(|line| line == "-----"));
}
#[test]
fn generated_corpus_produces_changed_and_unchanged_documents() {
let documents = sample(&document_strategy(), SWEEP_DOCUMENTS);
let mut changed = 0_usize;
let mut unchanged = 0_usize;
for document in &documents {
let (once, twice) = format_twice(document, &flags_for(u16::MAX));
assert_eq!(twice, once, "generated document drifted: {document:?}");
if once == *document {
unchanged += 1;
} else {
changed += 1;
}
}
assert!(changed > 0, "no generated document was reformatted");
assert!(unchanged > 0, "no generated document was already formatted");
}
#[test]
fn generated_corpus_reaches_both_defect_classes() {
let breaks = sample(&proptest::sample::select(BREAK_SPELLINGS), SWEEP_DOCUMENTS);
let mut covered = std::collections::BTreeSet::new();
for break_line in &breaks {
let input: Vec<String> = ["alpha", break_line, "beta"]
.iter()
.map(|line| (*line).to_string())
.collect();
let once = mdtablefix::wrap::wrap_text(&input, 80);
assert!(
once.iter().any(|line| line == break_line),
"wrap absorbed the {break_line:?} break: {once:?}",
);
covered.insert(*break_line);
}
assert_eq!(
covered.len(),
BREAK_SPELLINGS.len(),
"the generator did not reach every break spelling",
);
let class_b = sample(&overlong_code_span_block_strategy(), SWEEP_DOCUMENTS);
let mut spans = 0_usize;
for block in &class_b {
let mut block_lines = block.lines();
let Some(first_line) = block_lines.next() else {
continue;
};
if !first_line.contains("(`") || block_lines.next().is_none() || first_line.len() <= 80 {
continue;
}
spans += 1;
let document = format!("{block}\n");
let (once, twice) = format_twice(&document, &flags_for(1));
assert_eq!(
twice, once,
"the class B shape is not a fixed point: {document:?}",
);
}
assert!(
spans > 0,
"the generator never produced a prefixed line with a code span and a continuation",
);
}
#[test]
fn generated_corpus_reaches_the_bracket_reference_seam() {
let seams = sample(&bracket_reference_seam_strategy(), SWEEP_DOCUMENTS);
let mut reached = 0_usize;
for seam in &seams {
let mut lines = seam.lines();
let Some(head) = lines.next() else {
continue;
};
let Some(reference) = lines.next() else {
continue;
};
if head.len() != BRACKET_SEAM_HEAD_WIDTH || !reference.starts_with('[') {
continue;
}
reached += 1;
let document = format!("{seam}\n");
let (once, twice) = format_twice(&document, &flags_for(1));
assert_eq!(
twice, once,
"the bracket reference seam is not a fixed point: {document:?}",
);
assert!(
once.lines().any(|line| line.trim() == reference),
"expected {reference:?} to survive as a line of {once:?}",
);
assert!(
once.lines().all(|line| !line.ends_with('[')),
"opening bracket was stranded in {once:?}",
);
}
assert!(
reached > 0,
"the generator never produced a paragraph that fills the line before a bracket reference",
);
}