Coverage for gws-app/gws/lib/cli/_test.py: 100%

133 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-16 22:59 +0200

1"""Tests for the cli module.""" 

2 

3from unittest import mock 

4import pytest 

5 

6import gws 

7import gws.lib.cli as cli 

8import gws.test.util as u 

9 

10 

11def test_cprint(capsys): 

12 cli.SCRIPT_NAME = '' 

13 

14 # Test with color 

15 with mock.patch('sys.stdout.isatty', return_value=True): 

16 cli.cprint('red', 'test message') 

17 captured = capsys.readouterr() 

18 assert '\x1b[31mtest message\x1b[0m' in captured.out 

19 

20 # Test without color (non-tty) 

21 with mock.patch('sys.stdout.isatty', return_value=False): 

22 cli.cprint('red', 'test message') 

23 captured = capsys.readouterr() 

24 assert 'test message' in captured.out 

25 assert '\x1b[31m' not in captured.out 

26 

27 # Test with script name 

28 cli.SCRIPT_NAME = 'testscript' 

29 cli.cprint('blue', 'test message') 

30 captured = capsys.readouterr() 

31 assert '[testscript] test message' in captured.out 

32 cli.SCRIPT_NAME = '' # Reset for other tests 

33 

34 

35def test_error_warning_info(capsys): 

36 cli.error('error message') 

37 captured = capsys.readouterr() 

38 assert 'error message' in captured.out 

39 

40 cli.warning('warning message') 

41 captured = capsys.readouterr() 

42 assert 'warning message' in captured.out 

43 

44 cli.info('info message') 

45 captured = capsys.readouterr() 

46 assert 'info message' in captured.out 

47 

48 

49def test_fatal(capsys): 

50 with u.raises(SystemExit) as excinfo: 

51 cli.fatal('fatal message') 

52 assert excinfo.value.code == 1 

53 captured = capsys.readouterr() 

54 assert 'fatal message' in captured.out 

55 

56 

57def test_run(capsys): 

58 # Test successful command 

59 with mock.patch('subprocess.run') as mock_run: 

60 mock_run.return_value.returncode = 0 

61 cli.run('echo test') 

62 mock_run.assert_called_once() 

63 captured = capsys.readouterr() 

64 assert '> echo test' in captured.out 

65 

66 # Test command as list 

67 with mock.patch('subprocess.run') as mock_run: 

68 mock_run.return_value.returncode = 0 

69 cli.run(['echo', 'test']) 

70 mock_run.assert_called_once() 

71 captured = capsys.readouterr() 

72 assert '> echo test' in captured.out 

73 

74 # Test failing command 

75 with mock.patch('subprocess.run') as mock_run: 

76 mock_run.return_value.returncode = 1 

77 with pytest.raises(SystemExit): 

78 cli.run('false') 

79 captured = capsys.readouterr() 

80 assert 'COMMAND FAILED' in captured.out 

81 

82 

83def test_exec(): 

84 # Test successful command 

85 result = cli.exec('echo "hello world"') 

86 assert result == "hello world" 

87 

88 # Test failing command 

89 with mock.patch('subprocess.run') as mock_run: 

90 mock_run.side_effect = Exception("Command failed") 

91 result = cli.exec('invalid command') 

92 assert 'FAILED' in result 

93 

94 

95def test_find_dirs(tmp_path): 

96 # Create test directory structure 

97 (tmp_path / "dir1").mkdir() 

98 (tmp_path / "dir2").mkdir() 

99 (tmp_path / ".hidden_dir").mkdir() 

100 (tmp_path / "file1").write_text("test") 

101 

102 # Test finding directories 

103 dirs = list(cli.find_dirs(tmp_path)) 

104 assert len(dirs) == 2 

105 assert str(tmp_path / "dir1") in dirs 

106 assert str(tmp_path / "dir2") in dirs 

107 assert str(tmp_path / ".hidden_dir") not in dirs 

108 

109 # Test with non-existent directory 

110 dirs = list(cli.find_dirs(tmp_path / "nonexistent")) 

111 assert len(dirs) == 0 

112 

113 

114def test_find_files(tmp_path): 

115 # Create test directory structure 

116 (tmp_path / "dir1").mkdir() 

117 (tmp_path / "dir1" / "file1.txt").write_text("test") 

118 (tmp_path / "dir1" / "file2.py").write_text("test") 

119 (tmp_path / "dir2").mkdir() 

120 (tmp_path / "dir2" / "file3.txt").write_text("test") 

121 (tmp_path / ".hidden_dir").mkdir() 

122 (tmp_path / ".hidden_dir" / "file4.txt").write_text("test") 

123 (tmp_path / "file5.txt").write_text("test") 

124 

125 # Test finding all files 

126 files = list(cli.find_files(tmp_path)) 

127 assert len(files) == 4 

128 assert str(tmp_path / "dir1" / "file1.txt") in files 

129 assert str(tmp_path / "dir1" / "file2.py") in files 

130 assert str(tmp_path / "dir2" / "file3.txt") in files 

131 assert str(tmp_path / "file5.txt") in files 

132 

133 # Test with pattern 

134 files = list(cli.find_files(tmp_path, pattern=r'\.txt$')) 

135 assert len(files) == 3 

136 assert str(tmp_path / "dir1" / "file1.txt") in files 

137 assert str(tmp_path / "dir2" / "file3.txt") in files 

138 assert str(tmp_path / "file5.txt") in files 

139 assert str(tmp_path / "dir1" / "file2.py") not in files 

140 

141 # Test without recursion 

142 files = list(cli.find_files(tmp_path, deep=False)) 

143 assert len(files) == 1 

144 assert str(tmp_path / "file5.txt") in files 

145 

146 # Test with non-existent directory 

147 files = list(cli.find_files(tmp_path / "nonexistent")) 

148 assert len(files) == 0 

149 

150 

151def test_read_write_file(tmp_path): 

152 test_file = tmp_path / "test.txt" 

153 

154 # Test writing to file 

155 cli.write_file(str(test_file), "test content") 

156 assert test_file.read_text() == "test content" 

157 

158 # Test reading from file 

159 content = cli.read_file(str(test_file)) 

160 assert content == "test content" 

161 

162 

163def test_parse_args(): 

164 # Test basic arguments 

165 args = cli.parse_args(["script.py", "arg1", "arg2"]) 

166 assert args[0] == "script.py" 

167 assert args[1] == "arg1" 

168 assert args[2] == "arg2" 

169 

170 # Test options 

171 args = cli.parse_args(["script.py", "-o", "value", "--long", "longval"]) 

172 assert args["o"] == "value" 

173 assert args["long"] == "longval" 

174 

175 # Test flags 

176 args = cli.parse_args(["script.py", "-f", "--flag"]) 

177 assert args["f"] is True 

178 assert args["flag"] is True 

179 

180 # Test rest arguments 

181 args = cli.parse_args(["script.py", "-", "rest1", "rest2"]) 

182 assert args["_rest"] == ["rest1", "rest2"] 

183 

184 

185def test_main(): 

186 # Test help flag 

187 with mock.patch('sys.argv', ['script.py', '-h']), \ 

188 mock.patch('sys.exit') as mock_exit, \ 

189 mock.patch('builtins.print') as mock_print: 

190 cli.main("test", lambda x: 0, "Usage: test") 

191 mock_print.assert_called() 

192 mock_exit.assert_called_with(0) 

193 

194 # Test normal execution 

195 with mock.patch('sys.argv', ['script.py', 'arg']), \ 

196 mock.patch('sys.exit') as mock_exit: 

197 cli.main("test", lambda x: 42, "Usage: test") 

198 mock_exit.assert_called_with(42) 

199 

200 # Test exception handling 

201 

202 def failing_func(args): 

203 raise Exception("Test exception") 

204 

205 with mock.patch('sys.argv', ['script.py', 'arg']), \ 

206 mock.patch('sys.exit') as mock_exit, \ 

207 mock.patch('gws.lib.cli.error') as mock_error: 

208 cli.main("test", failing_func, "Usage: test") 

209 mock_error.assert_called() 

210 

211 

212# def test_text_table(): 

213# # Test with list of dicts 

214# data = [ 

215# {"name": "Alice", "age": 30}, 

216# {"name": "Bob", "age": 25} 

217# ] 

218# 

219# # With header 

220# table = cli.text_table(data, header=["name", "age"]) 

221# assert "name | age" in table 

222# assert "Alice | 30" in table 

223# 

224# # With auto header 

225# table = cli.text_table(data, header="auto") 

226# assert "name | age" in table 

227# assert "Alice | 30" in table 

228# 

229# # Without header 

230# table = cli.text_table(data, header=None) 

231# assert "name | age" not in table 

232# assert "Alice | 30" in table 

233# 

234# # Test with list of lists 

235# data = [ 

236# ["Alice", 30], 

237# ["Bob", 25] 

238# ] 

239# 

240# table = cli.text_table(data, header=["name", "age"]) 

241# assert "name | age" in table 

242# assert "Alice | 30" in table 

243# 

244# # Test with empty data 

245# assert cli.text_table([]) == "" 

246 

247 

248# def test_progress_indicator(capsys): 

249# # Test initialization and basic logging 

250# with mock.patch('time.time', side_effect=[0, 

251# 1.5]): # Mock time to get consistent results 

252# with cli.ProgressIndicator("Test", total=100) as progress: 

253# captured = capsys.readouterr() 

254# assert "START" in captured.out 

255# assert "100" in captured.out 

256 

257# # Test update 

258# progress.update(10) 

259# captured = capsys.readouterr() 

260# assert captured.out.strip() == "[test] Test: 10%" 

261 

262# # Test update reaching threshold 

263# progress.update(40) 

264# captured = capsys.readouterr() 

265# assert "50%" in captured.out 

266 

267# # Test end message 

268# captured = capsys.readouterr() 

269# assert "END" in captured.out 

270 

271# # Test without  

272# with cli.ProgressIndicator("NoTotal") as progress: 

273# captured = capsys.readouterr() 

274# assert "START" in captured.out 

275 

276# # Updates should do nothing 

277# progress.update(50) 

278# captured = capsys.readouterr() 

279# assert captured.out.strip() == ""