use proptest::prelude::*;
mod grid_tests {
use super::*;
use dotmax::BrailleGrid;
proptest! {
#[test]
fn grid_new_never_panics_for_valid_dimensions(
w in 1usize..1000,
h in 1usize..1000
) {
let result = BrailleGrid::new(w, h);
prop_assert!(result.is_ok());
let grid = result.unwrap();
prop_assert_eq!(grid.width(), w);
prop_assert_eq!(grid.height(), h);
}
#[test]
fn grid_new_zero_width_returns_error(h in 1usize..100) {
let result = BrailleGrid::new(0, h);
prop_assert!(result.is_err());
}
#[test]
fn grid_new_zero_height_returns_error(w in 1usize..100) {
let result = BrailleGrid::new(w, 0);
prop_assert!(result.is_err());
}
#[test]
fn set_dot_inbounds_never_panics(
w in 1usize..100,
h in 1usize..100,
) {
let mut grid = BrailleGrid::new(w, h).unwrap();
let max_x = w * 2; let max_y = h * 4;
prop_assert!(grid.set_dot(0, 0).is_ok());
if max_x > 1 && max_y > 1 {
prop_assert!(grid.set_dot(max_x - 1, 0).is_ok());
prop_assert!(grid.set_dot(0, max_y - 1).is_ok());
prop_assert!(grid.set_dot(max_x - 1, max_y - 1).is_ok());
}
}
#[test]
fn set_dot_outofbounds_returns_error(
w in 1usize..50,
h in 1usize..50,
) {
let mut grid = BrailleGrid::new(w, h).unwrap();
let max_x = w * 2;
let max_y = h * 4;
let result = grid.set_dot(max_x, 0);
prop_assert!(result.is_err());
let result = grid.set_dot(0, max_y);
prop_assert!(result.is_err());
let result = grid.set_dot(max_x + 10, max_y + 10);
prop_assert!(result.is_err());
}
#[test]
fn clear_maintains_dimensions(
w in 1usize..100,
h in 1usize..100,
) {
let mut grid = BrailleGrid::new(w, h).unwrap();
if w > 0 && h > 0 {
let _ = grid.set_dot(0, 0);
}
grid.clear();
prop_assert_eq!(grid.width(), w);
prop_assert_eq!(grid.height(), h);
}
#[test]
fn to_unicode_grid_produces_valid_braille(
w in 1usize..50,
h in 1usize..50,
) {
let grid = BrailleGrid::new(w, h).unwrap();
let unicode = grid.to_unicode_grid();
prop_assert_eq!(unicode.len(), h);
for row in &unicode {
prop_assert_eq!(row.len(), w);
for &ch in row {
let code = ch as u32;
prop_assert!(
(0x2800..=0x28FF).contains(&code),
"Character {:?} (U+{:04X}) is not a braille character",
ch, code
);
}
}
}
#[test]
fn dot_dimensions_consistent(
w in 1usize..100,
h in 1usize..100,
) {
let grid = BrailleGrid::new(w, h).unwrap();
prop_assert_eq!(grid.dot_width(), w * 2);
prop_assert_eq!(grid.dot_height(), h * 4);
}
#[test]
fn resize_preserves_overlapping_content(
old_w in 5usize..50,
old_h in 5usize..50,
new_w in 5usize..50,
new_h in 5usize..50,
) {
let mut grid = BrailleGrid::new(old_w, old_h).unwrap();
grid.set_dot(0, 0).unwrap();
grid.resize(new_w, new_h).unwrap();
prop_assert_eq!(grid.width(), new_w);
prop_assert_eq!(grid.height(), new_h);
}
}
}
mod color_tests {
use super::*;
use dotmax::Color;
use dotmax::color::{rgb_to_ansi256, rgb_to_ansi16};
proptest! {
#[test]
fn rgb_creation_valid_for_all_u8(
r in 0u8..=255,
g in 0u8..=255,
b in 0u8..=255,
) {
let color = Color::rgb(r, g, b);
prop_assert_eq!(color.r, r);
prop_assert_eq!(color.g, g);
prop_assert_eq!(color.b, b);
}
#[test]
fn ansi256_conversion_produces_valid_code(
r in 0u8..=255,
g in 0u8..=255,
b in 0u8..=255,
) {
let ansi = rgb_to_ansi256(r, g, b);
let _ = ansi; prop_assert!(true);
}
#[test]
fn ansi16_conversion_produces_valid_code(
r in 0u8..=255,
g in 0u8..=255,
b in 0u8..=255,
) {
let ansi = rgb_to_ansi16(r, g, b);
prop_assert!(ansi < 16, "ANSI-16 code {} is out of range", ansi);
}
#[test]
fn black_color_consistent(_dummy in 0..1) {
let black = Color::black();
prop_assert_eq!(black.r, 0);
prop_assert_eq!(black.g, 0);
prop_assert_eq!(black.b, 0);
}
#[test]
fn white_color_consistent(_dummy in 0..1) {
let white = Color::white();
prop_assert_eq!(white.r, 255);
prop_assert_eq!(white.g, 255);
prop_assert_eq!(white.b, 255);
}
}
}
mod color_scheme_tests {
use super::*;
use dotmax::color::{ColorScheme, ColorSchemeBuilder};
use dotmax::Color;
proptest! {
#[test]
fn color_scheme_builder_produces_valid_schemes(
num_colors in 2usize..10,
) {
let mut builder = ColorSchemeBuilder::new("Test");
for i in 0..num_colors {
let intensity = i as f32 / (num_colors - 1) as f32;
let value = (intensity * 255.0) as u8;
builder = builder.add_color(intensity, Color::rgb(value, value, value));
}
let result = builder.build();
prop_assert!(result.is_ok());
}
#[test]
fn color_scheme_sample_valid_for_all_intensities(
intensity in 0.0f32..=1.0,
) {
let scheme = ColorScheme::grayscale();
let color = scheme.sample(intensity);
let _ = color.r;
let _ = color.g;
let _ = color.b;
prop_assert!(true);
}
#[test]
fn grayscale_interpolation_monotonic(
i1 in 0.0f32..0.5,
i2 in 0.5f32..1.0,
) {
let scheme = ColorScheme::grayscale();
let c1 = scheme.sample(i1);
let c2 = scheme.sample(i2);
let brightness1 = (c1.r as u16 + c1.g as u16 + c1.b as u16) / 3;
let brightness2 = (c2.r as u16 + c2.g as u16 + c2.b as u16) / 3;
prop_assert!(
brightness2 >= brightness1.saturating_sub(5),
"Higher intensity {} should produce brighter color than lower intensity {}: {} vs {}",
i2, i1, brightness2, brightness1
);
}
}
}
mod primitive_tests {
use super::*;
use dotmax::{BrailleGrid, primitives::{draw_line, draw_circle, shapes::draw_rectangle}};
proptest! {
#[test]
fn line_includes_endpoints(
w in 20usize..100,
h in 20usize..100,
x0 in 5i32..15,
y0 in 5i32..15,
x1 in 5i32..15,
y1 in 5i32..15,
) {
let mut grid = BrailleGrid::new(w, h).unwrap();
draw_line(&mut grid, x0, y0, x1, y1).unwrap();
let unicode = grid.to_unicode_grid();
prop_assert!(!unicode.is_empty());
}
#[test]
fn circle_never_panics(
w in 50usize..100,
h in 50usize..100,
cx in 20i32..80,
cy in 20i32..80,
r in 0u32..30,
) {
let mut grid = BrailleGrid::new(w, h).unwrap();
let result = draw_circle(&mut grid, cx, cy, r);
prop_assert!(result.is_ok());
}
#[test]
fn circle_symmetry(
size in 30usize..60,
radius in 5u32..15,
) {
let mut grid = BrailleGrid::new(size, size).unwrap();
let center = (size / 2) as i32 * 2;
draw_circle(&mut grid, center, center, radius).unwrap();
let unicode = grid.to_unicode_grid();
prop_assert!(!unicode.is_empty());
}
#[test]
fn rectangle_never_panics(
w in 50usize..100,
h in 30usize..60,
x in 5i32..20,
y in 5i32..20,
rect_w in 10u32..50,
rect_h in 10u32..30,
) {
let mut grid = BrailleGrid::new(w, h).unwrap();
let result = draw_rectangle(&mut grid, x, y, rect_w, rect_h);
prop_assert!(result.is_ok());
}
#[test]
fn clipped_drawing_doesnt_panic(
w in 20usize..50,
h in 20usize..50,
) {
let mut grid = BrailleGrid::new(w, h).unwrap();
let result = draw_line(&mut grid, -10, -10, 1000, 1000);
prop_assert!(result.is_ok());
let result = draw_circle(&mut grid, 0, 0, 100);
prop_assert!(result.is_ok());
}
}
}
mod animation_tests {
use super::*;
use dotmax::animation::{FrameBuffer, FrameTimer};
proptest! {
#[test]
fn frame_buffer_creation_valid(
w in 1usize..100,
h in 1usize..100,
) {
let buffer = FrameBuffer::new(w, h);
prop_assert_eq!(buffer.width(), w);
prop_assert_eq!(buffer.height(), h);
}
#[test]
fn frame_buffer_swap_maintains_dimensions(
w in 1usize..50,
h in 1usize..50,
) {
let mut buffer = FrameBuffer::new(w, h);
for _ in 0..5 {
buffer.swap_buffers();
prop_assert_eq!(buffer.width(), w);
prop_assert_eq!(buffer.height(), h);
}
}
#[test]
fn frame_timer_valid_fps(
fps in 1u32..120,
) {
let timer = FrameTimer::new(fps);
prop_assert_eq!(timer.target_fps(), fps);
}
}
}
mod density_tests {
use super::*;
use dotmax::density::DensitySet;
proptest! {
#[test]
fn density_map_valid_for_all_intensities(
intensity in 0.0f32..=1.0,
) {
let density = DensitySet::ascii();
let ch = density.map(intensity);
prop_assert!(ch.is_ascii() || ch as u32 > 127);
}
#[test]
fn density_map_boundary_values(_dummy in 0..1) {
let density = DensitySet::ascii();
let _ = density.map(0.0);
let _ = density.map(1.0);
prop_assert!(true);
}
#[test]
fn custom_density_set_creation(
num_chars in 2usize..20,
) {
let chars: Vec<char> = (0..num_chars)
.map(|i| char::from_u32(32 + i as u32).unwrap_or(' '))
.collect();
let result = DensitySet::new("Custom".to_string(), chars.clone());
prop_assert!(result.is_ok());
let density = result.unwrap();
prop_assert_eq!(density.characters.len(), num_chars);
}
}
}