1+ import argparse
2+ import os
3+ from unittest .mock import patch , MagicMock
4+
5+ import pytest
6+ import pyvips
7+
8+ from image_slicer .cli import main
9+
10+
11+ @pytest .fixture (scope = "module" )
12+ def test_image_path (tmpdir_factory ):
13+ """
14+ Creates a temporary black PNG image for testing.
15+ """
16+ path = str (tmpdir_factory .mktemp ("data" ).join ("test_image.png" ))
17+ image = pyvips .Image .black (100 , 85 , bands = 1 )
18+ image .write_to_file (path )
19+ return path
20+
21+
22+ def test_main_with_grid_arguments (test_image_path , tmp_path ):
23+ """
24+ Tests the CLI main function with grid arguments.
25+ """
26+ output_dir = str (tmp_path / "output_cli_grid" )
27+
28+ with patch ('sys.argv' , ['imslice' , test_image_path , output_dir , '--grid' , '2' , '3' ]):
29+ main ()
30+
31+ files = os .listdir (output_dir )
32+ assert len (files ) == 6 # 2*3 = 6 tiles
33+
34+
35+ def test_main_with_number_of_tiles_argument (test_image_path , tmp_path ):
36+ """
37+ Tests the CLI main function with number of tiles argument.
38+ """
39+ output_dir = str (tmp_path / "output_cli_tiles" )
40+
41+ with patch ('sys.argv' , ['imslice' , test_image_path , output_dir , '--number-of-tiles' , '4' ]):
42+ main ()
43+
44+ files = os .listdir (output_dir )
45+ assert len (files ) == 4
46+
47+
48+ def test_main_with_tile_size_arguments (test_image_path , tmp_path ):
49+ """
50+ Tests the CLI main function with tile size arguments.
51+ """
52+ output_dir = str (tmp_path / "output_cli_size" )
53+
54+ with patch ('sys.argv' , ['imslice' , test_image_path , output_dir , '--tile-size' , '50' , '40' ]):
55+ main ()
56+
57+ files = os .listdir (output_dir )
58+ # 100/50 = 2 cols, 85/40 = 3 rows (rounded up) = 2*3 = 6 tiles
59+ assert len (files ) == 6
60+
61+
62+ def test_main_with_custom_format (test_image_path , tmp_path ):
63+ """
64+ Tests the CLI main function with custom naming format.
65+ """
66+ output_dir = str (tmp_path / "output_cli_format" )
67+
68+ with patch ('sys.argv' , ['imslice' , test_image_path , output_dir , '--grid' , '2' , '2' ,
69+ '--format' , 'custom_{row}_{col}.jpg' ]):
70+ main ()
71+
72+ files = os .listdir (output_dir )
73+ assert 'custom_0_0.jpg' in files
74+ assert 'custom_1_1.jpg' in files
75+ assert len (files ) == 4
76+
77+
78+ def test_main_with_short_arguments (test_image_path , tmp_path ):
79+ """
80+ Tests the CLI main function with short argument forms.
81+ """
82+ output_dir = str (tmp_path / "output_cli_short" )
83+
84+ with patch ('sys.argv' , ['imslice' , test_image_path , output_dir , '-g' , '3' , '2' ]):
85+ main ()
86+
87+ files = os .listdir (output_dir )
88+ assert len (files ) == 6 # 3*2 = 6 tiles
89+
90+
91+ def test_main_number_of_tiles_short_form (test_image_path , tmp_path ):
92+ """
93+ Tests the CLI main function with short form of number-of-tiles.
94+ """
95+ output_dir = str (tmp_path / "output_cli_n" )
96+
97+ with patch ('sys.argv' , ['imslice' , test_image_path , output_dir , '-n' , '9' ]):
98+ main ()
99+
100+ files = os .listdir (output_dir )
101+ assert len (files ) == 9
102+
103+
104+ def test_main_tile_size_short_form (test_image_path , tmp_path ):
105+ """
106+ Tests the CLI main function with short form of tile-size.
107+ """
108+ output_dir = str (tmp_path / "output_cli_t" )
109+
110+ with patch ('sys.argv' , ['imslice' , test_image_path , output_dir , '-t' , '25' , '30' ]):
111+ main ()
112+
113+ files = os .listdir (output_dir )
114+ # 100/25 = 4 cols, 85/30 = 3 rows (rounded up) = 4*3 = 12 tiles
115+ assert len (files ) == 12
116+
117+
118+ def test_main_custom_format_short_form (test_image_path , tmp_path ):
119+ """
120+ Tests the CLI main function with short form of format.
121+ """
122+ output_dir = str (tmp_path / "output_cli_f" )
123+
124+ with patch ('sys.argv' , ['imslice' , test_image_path , output_dir , '-g' , '2' , '2' ,
125+ '-f' , 'piece_{row}_{col}.png' ]):
126+ main ()
127+
128+ files = os .listdir (output_dir )
129+ assert 'piece_0_0.png' in files
130+ assert 'piece_1_1.png' in files
131+
132+
133+ def test_main_missing_required_argument ():
134+ """
135+ Tests that the CLI raises SystemExit when required mutually exclusive group is missing.
136+ """
137+ with patch ('sys.argv' , ['imslice' , 'source.png' , 'output_dir' ]):
138+ with pytest .raises (SystemExit ):
139+ main ()
140+
141+
142+ def test_main_conflicting_arguments ():
143+ """
144+ Tests that the CLI raises SystemExit when mutually exclusive arguments are provided.
145+ """
146+ with patch ('sys.argv' , ['imslice' , 'source.png' , 'output_dir' ,
147+ '--grid' , '2' , '2' , '--number-of-tiles' , '4' ]):
148+ with pytest .raises (SystemExit ):
149+ main ()
150+
151+
152+ def test_main_invalid_image_path (tmp_path ):
153+ """
154+ Tests that the CLI handles invalid image paths gracefully.
155+ """
156+ output_dir = str (tmp_path / "output_cli_invalid" )
157+
158+ with patch ('sys.argv' , ['imslice' , 'nonexistent.png' , output_dir , '--grid' , '2' , '2' ]):
159+ with pytest .raises (pyvips .Error ):
160+ main ()
161+
162+
163+ def test_main_module_execution (test_image_path , tmp_path ):
164+ """
165+ Tests the __main__ execution path of the CLI module.
166+ """
167+ import subprocess
168+ import sys
169+
170+ output_dir = str (tmp_path / "output_main_module" )
171+
172+ # Execute the CLI module directly via Python
173+ result = subprocess .run ([
174+ sys .executable , '-m' , 'image_slicer.cli' ,
175+ test_image_path , output_dir , '--grid' , '2' , '2'
176+ ], cwd = '/Users/sam/image_slicer' , env = {'PYTHONPATH' : 'src' },
177+ capture_output = True , text = True )
178+
179+ # Check that it executed successfully
180+ assert result .returncode == 0
181+
182+ # Check that files were created
183+ files = os .listdir (output_dir )
184+ assert len (files ) == 4 # 2*2 = 4 tiles
0 commit comments