113 lines
3.5 KiB
Rust
113 lines
3.5 KiB
Rust
// Defensive coverage for the image-upload boundary. The GLES path requires a
|
||
// live GL context (out of scope for these CPU-only tests) so the assertions
|
||
// below exercise the software backend, which shares the same length-vs-
|
||
// dimensions check before delegating to tiny-skia. Both paths:
|
||
//
|
||
// * refuse the upload when `data.len() != width * height * 4`,
|
||
// * refuse zero-area uploads (`width == 0` or `height == 0`),
|
||
// * never panic — they log to stderr and return without drawing.
|
||
|
||
use std::sync::Arc;
|
||
|
||
use ltk::core::{ RenderOptions, UiSurface };
|
||
use ltk::{ column, img_widget, Color, Element };
|
||
|
||
fn render_image( rgba: Arc<Vec<u8>>, w: u32, h: u32 ) -> UiSurface<()>
|
||
{
|
||
let mut surface = UiSurface::<()>::new( 64, 64 );
|
||
let view: Element<()> = column()
|
||
.padding( 0.0 )
|
||
.push( img_widget( rgba, w, h ) )
|
||
.into();
|
||
let _ = surface.render(
|
||
&view,
|
||
RenderOptions::full_canvas( 64, 64 ).background( Color::BLACK ),
|
||
);
|
||
surface
|
||
}
|
||
|
||
#[ test ]
|
||
fn declared_size_matching_buffer_renders_without_panic()
|
||
{
|
||
// Sanity baseline: a 4×4 image with the expected 64-byte buffer must
|
||
// render through the path that the validation tests below exercise.
|
||
let bytes = Arc::new( vec![ 0xFFu8; 4 * 4 * 4 ] );
|
||
let _ = render_image( bytes, 4, 4 );
|
||
}
|
||
|
||
#[ test ]
|
||
fn shorter_buffer_is_rejected_without_panic()
|
||
{
|
||
// 10×10 declared but only 16 bytes provided. Without the boundary
|
||
// check tiny-skia would walk past the slice end inside its premul
|
||
// loop. With the check the renderer logs once and skips the draw.
|
||
let bytes = Arc::new( vec![ 0u8; 16 ] );
|
||
let _ = render_image( bytes, 10, 10 );
|
||
}
|
||
|
||
#[ test ]
|
||
fn longer_buffer_is_rejected_without_panic()
|
||
{
|
||
// 2×2 declared, 256 bytes provided. The buffer is larger than the
|
||
// driver would read, but the size mismatch still fails the strict
|
||
// equality and the draw is refused.
|
||
let bytes = Arc::new( vec![ 0u8; 256 ] );
|
||
let _ = render_image( bytes, 2, 2 );
|
||
}
|
||
|
||
#[ test ]
|
||
fn zero_width_image_does_not_blit()
|
||
{
|
||
// IntSize::from_wh rejects zero dimensions on tiny-skia, but the
|
||
// boundary check fires earlier for a uniform error path on both
|
||
// backends.
|
||
let bytes = Arc::new( vec![] );
|
||
let _ = render_image( bytes, 0, 10 );
|
||
}
|
||
|
||
#[ test ]
|
||
fn zero_height_image_does_not_blit()
|
||
{
|
||
let bytes = Arc::new( vec![] );
|
||
let _ = render_image( bytes, 10, 0 );
|
||
}
|
||
|
||
#[ test ]
|
||
fn non_multiple_of_four_buffer_is_rejected()
|
||
{
|
||
// 1×1 should be 4 bytes; 3 bytes is shorter and not a multiple of 4 —
|
||
// every byte counts.
|
||
let bytes = Arc::new( vec![ 0xFF, 0xFF, 0xFF ] );
|
||
let _ = render_image( bytes, 1, 1 );
|
||
}
|
||
|
||
#[ test ]
|
||
fn dimensions_overflow_usize_are_rejected_without_panic()
|
||
{
|
||
// `u32::MAX × u32::MAX × 4` overflows. The boundary check uses
|
||
// `saturating_mul` (or i64-promotion in the GLES path) so the
|
||
// expected length never wraps to a small number that happens to
|
||
// match a real buffer length. The provided buffer is small, so the
|
||
// equality fails and the draw is refused.
|
||
let bytes = Arc::new( vec![ 0u8; 16 ] );
|
||
let _ = render_image( bytes, u32::MAX, u32::MAX );
|
||
}
|
||
|
||
#[ test ]
|
||
fn renderer_recovers_after_a_rejected_upload()
|
||
{
|
||
// A frame with a malformed image must not poison the surface. The
|
||
// next render with a valid view succeeds.
|
||
let bad = Arc::new( vec![ 0u8; 5 ] );
|
||
let mut s = render_image( bad, 4, 4 );
|
||
|
||
let view: Element<()> = column().padding( 0.0 ).into();
|
||
let out = s.render(
|
||
&view,
|
||
RenderOptions::full_canvas( 64, 64 ).background( Color::rgb( 0.0, 1.0, 0.0 ) ),
|
||
);
|
||
// Tree shape change → full redraw, but the important check is that
|
||
// render() returned at all.
|
||
let _ = out;
|
||
}
|