use proptest::prelude::*;
use streamdown_parser::Parser;
use streamdown_render::Renderer;
fn markdown_string() -> impl Strategy<Value = String> {
prop::string::string_regex(r"[\x20-\x7E\n\t]*").unwrap()
}
fn text_line() -> impl Strategy<Value = String> {
prop::string::string_regex(r"[\x20-\x7E]{0,200}").unwrap()
}
fn heading() -> impl Strategy<Value = String> {
(1..=6usize, text_line()).prop_map(|(level, text)| format!("{} {}", "#".repeat(level), text))
}
fn code_block() -> impl Strategy<Value = String> {
(text_line(), prop::collection::vec(text_line(), 0..10)).prop_map(|(lang, lines)| {
let lang = if lang.is_empty() {
String::new()
} else {
lang.split_whitespace().next().unwrap_or("").to_string()
};
format!("```{}\n{}\n```", lang, lines.join("\n"))
})
}
fn list() -> impl Strategy<Value = String> {
prop::collection::vec(text_line(), 1..10).prop_map(|items| {
items
.iter()
.map(|item| format!("- {}", item))
.collect::<Vec<_>>()
.join("\n")
})
}
proptest! {
#[test]
fn parser_never_panics(input in markdown_string()) {
let mut parser = Parser::new();
for line in input.lines() {
let _ = parser.parse_line(line);
}
}
#[test]
fn parser_handles_text(line in text_line()) {
let mut parser = Parser::new();
let _events = parser.parse_line(&line);
if !line.trim().is_empty() {
}
}
#[test]
fn parser_handles_headings(h in heading()) {
let mut parser = Parser::new();
let _events = parser.parse_line(&h);
}
#[test]
fn parser_handles_code_blocks(code in code_block()) {
let mut parser = Parser::new();
for line in code.lines() {
let _ = parser.parse_line(line);
}
}
#[test]
fn parser_handles_lists(list in list()) {
let mut parser = Parser::new();
for line in list.lines() {
let _ = parser.parse_line(line);
}
}
}
proptest! {
#[test]
fn renderer_never_panics(input in markdown_string()) {
let mut output = Vec::new();
let mut parser = Parser::new();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut renderer = Renderer::new(&mut output, 80);
for line in input.lines() {
for event in parser.parse_line(line) {
let _ = renderer.render_event(&event);
}
}
}));
prop_assert!(result.is_ok(), "Renderer panicked on input");
}
#[test]
fn renderer_produces_valid_utf8(input in markdown_string()) {
let mut output = Vec::new();
let mut parser = Parser::new();
{
let mut renderer = Renderer::new(&mut output, 80);
for line in input.lines() {
for event in parser.parse_line(line) {
let _ = renderer.render_event(&event);
}
}
}
let result = String::from_utf8(output);
prop_assert!(result.is_ok(), "Renderer produced invalid UTF-8");
}
#[test]
fn renderer_handles_widths(input in text_line(), width in 20..200usize) {
let mut output = Vec::new();
let mut parser = Parser::new();
{
let mut renderer = Renderer::new(&mut output, width);
for event in parser.parse_line(&input) {
let _ = renderer.render_event(&event);
}
}
let _result = String::from_utf8(output).unwrap();
}
}
proptest! {
#[test]
fn visible_length_never_panics(input in markdown_string()) {
let _ = streamdown_ansi::utils::visible_length(&input);
}
#[test]
fn visible_never_panics(input in markdown_string()) {
let _ = streamdown_ansi::utils::visible(&input);
}
#[test]
fn visible_shorter_or_equal(input in markdown_string()) {
let visible = streamdown_ansi::utils::visible(&input);
let _vis_len = streamdown_ansi::utils::visible_length(&visible);
}
}
proptest! {
#[test]
fn latex_never_panics(input in markdown_string()) {
let _ = streamdown_plugin::latex::latex_to_unicode(&input);
}
#[test]
fn plugin_manager_never_panics(input in text_line()) {
use streamdown_config::ComputedStyle;
use streamdown_core::state::ParseState;
use streamdown_plugin::PluginManager;
let mut manager = PluginManager::with_builtins();
let state = ParseState::new();
let style = ComputedStyle::default();
let _ = manager.process_line(&input, &state, &style);
}
}
proptest! {
#[test]
fn hsv_produces_valid_rgb(h in 0.0f64..1.0, s in 0.0f64..1.0, v in 0.0f64..1.0) {
use streamdown_ansi::color::hsv_to_rgb;
let (r, g, b) = hsv_to_rgb(h, s, v);
let _ = (r, g, b);
}
#[test]
fn hex2rgb_handles_valid_hex(r in 0u8..=255, g in 0u8..=255, b in 0u8..=255) {
use streamdown_ansi::color::hex2rgb;
let hex = format!("#{:02x}{:02x}{:02x}", r, g, b);
let result = hex2rgb(&hex);
prop_assert!(result.is_some());
let (pr, pg, pb) = result.unwrap();
prop_assert_eq!(pr, r);
prop_assert_eq!(pg, g);
prop_assert_eq!(pb, b);
}
}