@@ -272,6 +272,85 @@ impl<'a> ColorRenderer<'a> {
272272 }
273273}
274274
275+ #[ cfg( test) ]
276+ mod tests {
277+ use super :: * ;
278+
279+ fn load_test_font ( ) -> Vec < u8 > {
280+ std:: fs:: read ( "test-data/Nabla-subset.ttf" ) . expect ( "missing test font" )
281+ }
282+
283+ #[ test]
284+ fn colrv1_render_produces_non_empty_image ( ) {
285+ let data = load_test_font ( ) ;
286+ let dfont = DFont :: new ( & data) ;
287+ let mut renderer = ColorRenderer :: new ( & dfont, 32.0 , None , None ) ;
288+ let ( buffer, img) = renderer
289+ . render_string ( "hello" )
290+ . expect ( "render_string returned None" ) ;
291+
292+ assert ! ( !buffer. is_empty( ) , "serialized buffer should not be empty" ) ;
293+ assert ! ( img. width( ) > 0 && img. height( ) > 0 , "image has zero size" ) ;
294+
295+ let non_zero = img. pixels ( ) . filter ( |p| p. 0 [ 0 ] > 0 ) . count ( ) ;
296+ assert ! ( non_zero > 0 , "image is completely blank" ) ;
297+ }
298+
299+ #[ test]
300+ fn colrv1_glyph_cache_is_reused ( ) {
301+ let data = load_test_font ( ) ;
302+ let dfont = DFont :: new ( & data) ;
303+ let mut renderer = ColorRenderer :: new ( & dfont, 32.0 , None , None ) ;
304+
305+ // "ll" shares the same glyph; after rendering, the cache should contain it
306+ renderer. render_string ( "hello" ) . unwrap ( ) ;
307+ let cache_size_after_hello = renderer. cache . len ( ) ;
308+
309+ // "lo" reuses 'l' and 'o' which are already cached
310+ renderer. render_string ( "lo" ) . unwrap ( ) ;
311+ let cache_size_after_lo = renderer. cache . len ( ) ;
312+
313+ assert_eq ! (
314+ cache_size_after_hello, cache_size_after_lo,
315+ "cache grew when all glyphs should already have been cached"
316+ ) ;
317+ }
318+
319+ #[ test]
320+ fn colrv1_cached_tiles_contain_color ( ) {
321+ let data = load_test_font ( ) ;
322+ let dfont = DFont :: new ( & data) ;
323+ let mut renderer = ColorRenderer :: new ( & dfont, 32.0 , None , None ) ;
324+
325+ renderer. render_string ( "hello" ) . unwrap ( ) ;
326+
327+ // At least one cached tile should have pixels where the RGB channels
328+ // differ from each other, proving we're rendering actual color, not
329+ // just grayscale alpha.
330+ let has_color = renderer. cache . values ( ) . any ( |tile| {
331+ tile. pixmap . pixels ( ) . iter ( ) . any ( |px| {
332+ let ( r, g, b) = ( px. red ( ) , px. green ( ) , px. blue ( ) ) ;
333+ px. alpha ( ) > 0 && !( r == g && g == b)
334+ } )
335+ } ) ;
336+ assert ! ( has_color, "no color pixels found in cached glyph tiles" ) ;
337+ }
338+
339+ #[ test]
340+ fn colrv1_same_font_has_zero_diff ( ) {
341+ let data = load_test_font ( ) ;
342+ let dfont = DFont :: new ( & data) ;
343+ let mut renderer_a = ColorRenderer :: new ( & dfont, 32.0 , None , None ) ;
344+ let mut renderer_b = ColorRenderer :: new ( & dfont, 32.0 , None , None ) ;
345+
346+ let ( _, img_a) = renderer_a. render_string ( "world" ) . unwrap ( ) ;
347+ let ( _, img_b) = renderer_b. render_string ( "world" ) . unwrap ( ) ;
348+
349+ let diff = crate :: render:: utils:: count_differences ( img_a, img_b, 0 ) ;
350+ assert_eq ! ( diff, 0 , "same font should produce identical images" ) ;
351+ }
352+ }
353+
275354/// Read the first CPAL palette from a font.
276355fn read_cpal_palette ( font : & skrifa:: FontRef ) -> Vec < PaletteColor > {
277356 let cpal = match font. cpal ( ) {
0 commit comments