Refactor code structure for improved readability and maintainability
mandatory-regression-gate / deterministic-tests (push) Has been cancelled
mandatory-regression-gate / protected-performance-path (push) Has been cancelled

This commit is contained in:
Your Name
2026-07-23 15:34:05 +03:00
parent 593522e83f
commit 8447b2d796
21 changed files with 3439 additions and 4 deletions
+241
View File
@@ -0,0 +1,241 @@
use hcie_protocol::tools::{TextAlignment, TextOrientation, TextEffect};
use hcie_text::TextRenderer;
/// Integration tests for hcie-text text rendering.
///
/// Covers: font loading, font management, rasterization baseline,
/// error handling for missing fonts, fallback behavior,
/// create_text_layer and refresh_text_layer.
// ---------------------------------------------------------------------------
// Helper: load a built-in font for testing
// ---------------------------------------------------------------------------
fn setup_renderer() -> (TextRenderer, bool) {
let mut renderer = TextRenderer::new();
let font_data = include_bytes!("../tests/fixtures/Inter-Regular.ttf");
let ok = renderer.load_font("Inter", font_data).is_ok();
(renderer, ok)
}
// ---------------------------------------------------------------------------
// Font management
// ---------------------------------------------------------------------------
#[test]
fn new_renderer_has_no_fonts() {
let renderer = TextRenderer::new();
assert!(renderer.get_fonts().is_empty(), "new renderer should have no fonts");
}
#[test]
fn load_font_succeeds_with_valid_data() {
let mut renderer = TextRenderer::new();
// embed a small TTF for testing — must exist at this path
let font_data = include_bytes!("../tests/fixtures/Inter-Regular.ttf");
let result = renderer.load_font("Inter", font_data);
assert!(result.is_ok(), "loading a valid TTF should succeed: {:?}", result.err());
}
#[test]
fn load_font_fails_with_invalid_data() {
let mut renderer = TextRenderer::new();
let result = renderer.load_font("Invalid", &[0u8, 1, 2, 3]);
assert!(result.is_err(), "loading garbage data as a font should fail");
}
#[test]
fn has_font_returns_true_after_loading() {
let mut renderer = TextRenderer::new();
let font_data = include_bytes!("../tests/fixtures/Inter-Regular.ttf");
if renderer.load_font("Inter", font_data).is_ok() {
assert!(renderer.has_font("Inter"));
}
}
#[test]
fn has_font_returns_false_for_unloaded() {
let renderer = TextRenderer::new();
assert!(!renderer.has_font("NonexistentFont"));
}
#[test]
fn get_fonts_returns_loaded_names() {
let mut renderer = TextRenderer::new();
let font_data = include_bytes!("../tests/fixtures/Inter-Regular.ttf");
if renderer.load_font("MyFont", font_data).is_ok() {
let fonts = renderer.get_fonts();
assert!(fonts.contains(&"MyFont".to_string()));
}
}
// ---------------------------------------------------------------------------
// Text rasterization (baseline — if font is available)
// ---------------------------------------------------------------------------
#[test]
fn rasterize_text_returns_valid_output() {
let (mut renderer, font_ok) = setup_renderer();
if !font_ok {
eprintln!("Skipping: test font not available");
return;
}
let result = renderer.rasterize_text(
"Hello",
"Inter",
24.0,
[0, 0, 0, 255], // black
0.0, 0.0, // x, y
0.0, // angle
TextAlignment::Left,
TextOrientation::Horizontal,
&[], // no effects
true, // anti-alias
);
assert!(result.is_ok(), "rasterize should succeed: {:?}", result.err());
let (pixels, w, h, _ox, _oy, _uw, _uh) = result.unwrap();
assert!(w > 0, "rasterized width should be > 0");
assert!(h > 0, "rasterized height should be > 0");
assert_eq!(pixels.len(), (w * h * 4) as usize);
}
#[test]
fn rasterize_empty_string_produces_no_pixels_or_minimal() {
let (mut renderer, font_ok) = setup_renderer();
if !font_ok { return; }
let result = renderer.rasterize_text(
"", "Inter", 24.0, [0, 0, 0, 255],
0.0, 0.0, 0.0, TextAlignment::Left,
TextOrientation::Horizontal, &[], true,
);
// Empty string may return an error or a 0-size buffer; either is acceptable
match result {
Ok((pixels, w, h, _, _, _, _)) => {
assert!(w == 0 || h == 0 || pixels.is_empty(),
"empty string should produce trivial output: {}x{}", w, h);
}
Err(_) => { /* empty string may legitimately fail */ }
}
}
#[test]
fn rasterize_returns_different_output_for_different_sizes() {
let (mut renderer, font_ok) = setup_renderer();
if !font_ok { return; }
let small = renderer.rasterize_text(
"A", "Inter", 12.0, [0, 0, 0, 255],
0.0, 0.0, 0.0, TextAlignment::Left,
TextOrientation::Horizontal, &[], true,
).unwrap();
let large = renderer.rasterize_text(
"A", "Inter", 48.0, [0, 0, 0, 255],
0.0, 0.0, 0.0, TextAlignment::Left,
TextOrientation::Horizontal, &[], true,
).unwrap();
// Larger font size should produce taller output
assert!(large.2 >= small.2, "larger font should produce taller output");
}
// ---------------------------------------------------------------------------
// Color handling
// ---------------------------------------------------------------------------
#[test]
fn rasterize_text_applies_color() {
let (mut renderer, font_ok) = setup_renderer();
if !font_ok { return; }
let result = renderer.rasterize_text(
"X", "Inter", 24.0, [255, 0, 0, 255], // red
0.0, 0.0, 0.0, TextAlignment::Left,
TextOrientation::Horizontal, &[], true,
);
if let Ok((pixels, w, h, _, _, _, _)) = result {
if w > 0 && h > 0 {
// At least some pixels should have non-zero red channel
let has_red = pixels.iter().step_by(4).any(|&r| r > 0);
assert!(has_red, "text rasterized in red should have non-zero R channel");
}
}
}
// ---------------------------------------------------------------------------
// create_text_layer
// ---------------------------------------------------------------------------
#[test]
fn create_text_layer_produces_valid_layer() {
let (mut renderer, font_ok) = setup_renderer();
if !font_ok { return; }
let result = renderer.create_text_layer(
"Hello", "Inter", 24.0, [0, 0, 0, 255],
10.0, 20.0, 0.0, TextAlignment::Left,
TextOrientation::Horizontal, &[],
);
assert!(result.is_ok(), "create_text_layer should succeed: {:?}", result.err());
let layer = result.unwrap();
assert!(layer.width > 0);
assert!(layer.height > 0);
assert!(layer.pixels.len() >= 4);
}
#[test]
fn create_text_layer_fails_for_missing_font() {
let mut renderer = TextRenderer::new();
let result = renderer.create_text_layer(
"Hello", "NonExistentFont", 24.0, [0, 0, 0, 255],
0.0, 0.0, 0.0, TextAlignment::Left,
TextOrientation::Horizontal, &[],
);
assert!(result.is_err(), "should fail when font is not loaded");
}
// ---------------------------------------------------------------------------
// Error handling
// ---------------------------------------------------------------------------
#[test]
fn rasterize_fails_for_missing_font() {
let mut renderer = TextRenderer::new();
let result = renderer.rasterize_text(
"Hi", "AbsentFont", 12.0, [0; 4],
0.0, 0.0, 0.0, TextAlignment::Left,
TextOrientation::Horizontal, &[], true,
);
assert!(result.is_err(), "rasterize with missing font should fail");
}
// ---------------------------------------------------------------------------
// refresh_text_layer
// ---------------------------------------------------------------------------
#[test]
fn refresh_text_layer_does_not_panic() {
let (mut renderer, font_ok) = setup_renderer();
if !font_ok { return; }
let mut layer = match renderer.create_text_layer(
"Test", "Inter", 16.0, [0, 0, 0, 255],
0.0, 0.0, 0.0, TextAlignment::Left,
TextOrientation::Horizontal, &[],
) {
Ok(l) => l,
Err(e) => {
eprintln!("Skipping refresh test: cannot create layer: {}", e);
return;
}
};
let result = renderer.refresh_text_layer(&mut layer);
assert!(result.is_ok() || result.is_err());
// Either outcome is valid depending on the layer's state
}