11import numpy as np
2+ import pytest
3+ from numpy .typing import NDArray
24
35import imgviz
46
@@ -11,3 +13,86 @@ def test_tile() -> None:
1113
1214 assert tiled .shape == (25 , 75 , 3 )
1315 assert tiled .dtype == np .uint8
16+
17+
18+ @pytest .fixture
19+ def images () -> list [NDArray [np .uint8 ]]:
20+ return [np .full ((10 , 12 , 3 ), i * 10 , dtype = np .uint8 ) for i in range (4 )]
21+
22+
23+ def test_tile_auto_shape (images : list [NDArray [np .uint8 ]]) -> None :
24+ tiled = imgviz .tile (images )
25+
26+ assert tiled .shape == (20 , 24 , 3 )
27+ assert tiled .dtype == np .uint8
28+
29+
30+ def test_tile_row_only (images : list [NDArray [np .uint8 ]]) -> None :
31+ tiled = imgviz .tile (images , row = 1 )
32+
33+ assert tiled .shape == (10 , 48 , 3 )
34+
35+
36+ def test_tile_col_only (images : list [NDArray [np .uint8 ]]) -> None :
37+ tiled = imgviz .tile (images , col = 1 )
38+
39+ assert tiled .shape == (40 , 12 , 3 )
40+
41+
42+ def test_tile_draws_border (images : list [NDArray [np .uint8 ]]) -> None :
43+ tiled = imgviz .tile (images , row = 2 , col = 2 , border = (255 , 0 , 0 ), border_width = 3 )
44+
45+ assert tiled .shape == (23 , 27 , 3 )
46+ assert (tiled [10 :13 ] == (255 , 0 , 0 )).all ()
47+ assert (tiled [:, 12 :15 ] == (255 , 0 , 0 )).all ()
48+ assert (tiled [0 :10 , 0 :12 ] == 0 ).all () # first image cell is left intact
49+
50+
51+ def test_tile_promotes_gray_to_rgb () -> None :
52+ gray = [np .full ((10 , 12 ), 5 , dtype = np .uint8 ) for _ in range (3 )]
53+
54+ tiled = imgviz .tile (gray , row = 1 , col = 3 )
55+
56+ assert tiled .shape == (10 , 36 , 3 )
57+ assert tiled .dtype == np .uint8
58+ assert (tiled == 5 ).all () # gray value is broadcast across rgb channels
59+
60+
61+ def test_tile_promotes_rgb_to_rgba () -> None :
62+ rgba = np .full ((10 , 12 , 4 ), 5 , dtype = np .uint8 )
63+ rgb = np .full ((10 , 12 , 3 ), 5 , dtype = np .uint8 )
64+
65+ tiled = imgviz .tile ([rgba , rgb ], row = 1 , col = 2 )
66+
67+ assert tiled .shape == (10 , 24 , 4 )
68+ assert (tiled [:, :12 , 3 ] == 5 ).all () # already-rgba tile keeps its alpha
69+ assert (tiled [:, 12 :, 3 ] == 255 ).all () # rgb tile gains an opaque alpha
70+
71+
72+ def test_tile_pads_short_grid () -> None :
73+ tiled = imgviz .tile ([np .full ((10 , 12 , 3 ), 7 , dtype = np .uint8 )], row = 1 , col = 2 , cval = 0 )
74+
75+ assert tiled .shape == (10 , 24 , 3 )
76+ assert (tiled [:, :12 ] == 7 ).all () # image cell is written faithfully
77+ assert (tiled [:, 12 :] == 0 ).all ()
78+
79+
80+ @pytest .mark .parametrize (("row" , "col" ), [(0 , None ), (None , 0 ), (- 1 , None ), (None , - 1 )])
81+ def test_tile_rejects_non_positive_row_col (
82+ images : list [NDArray [np .uint8 ]], row : int | None , col : int | None
83+ ) -> None :
84+ with pytest .raises (ValueError , match = "must be positive" ):
85+ imgviz .tile (images , row = row , col = col )
86+
87+
88+ @pytest .mark .parametrize ("kwargs" , [{"col" : "2" }, {"row" : "2" }])
89+ def test_tile_rejects_non_int_dimension (
90+ images : list [NDArray [np .uint8 ]], kwargs : dict [str , str ]
91+ ) -> None :
92+ with pytest .raises (TypeError , match = "must be int" ):
93+ imgviz .tile (images , ** kwargs ) # type: ignore[arg-type]
94+
95+
96+ def test_tile_rejects_non_uint8 () -> None :
97+ with pytest .raises (ValueError , match = "image dtype must be np.uint8" ):
98+ imgviz .tile ([np .zeros ((4 , 4 , 3 ), dtype = np .float32 )], row = 1 , col = 1 )
0 commit comments