test_execute.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. import subprocess
  2. import pytest
  3. from flexmock import flexmock
  4. from borgmatic import execute as module
  5. @pytest.mark.parametrize(
  6. 'exit_code,error_on_warnings,expected_result',
  7. (
  8. (2, True, True),
  9. (2, False, True),
  10. (1, True, True),
  11. (1, False, False),
  12. (0, True, False),
  13. (0, False, False),
  14. ),
  15. )
  16. def test_exit_code_indicates_error_respects_exit_code_and_error_on_warnings(
  17. exit_code, error_on_warnings, expected_result
  18. ):
  19. assert (
  20. module.exit_code_indicates_error(exit_code, error_on_warnings=error_on_warnings)
  21. is expected_result
  22. )
  23. def test_command_for_process_converts_sequence_command_to_string():
  24. process = flexmock(args=['foo', 'bar', 'baz'])
  25. assert module.command_for_process(process) == 'foo bar baz'
  26. def test_command_for_process_passes_through_string_command():
  27. process = flexmock(args='foo bar baz')
  28. assert module.command_for_process(process) == 'foo bar baz'
  29. def test_output_buffer_for_process_returns_stderr_when_stdout_excluded():
  30. stdout = flexmock()
  31. stderr = flexmock()
  32. process = flexmock(stdout=stdout, stderr=stderr)
  33. assert module.output_buffer_for_process(process, exclude_stdouts=[flexmock(), stdout]) == stderr
  34. def test_output_buffer_for_process_returns_stdout_when_not_excluded():
  35. stdout = flexmock()
  36. process = flexmock(stdout=stdout)
  37. assert (
  38. module.output_buffer_for_process(process, exclude_stdouts=[flexmock(), flexmock()])
  39. == stdout
  40. )
  41. def test_execute_command_calls_full_command():
  42. full_command = ['foo', 'bar']
  43. flexmock(module.os, environ={'a': 'b'})
  44. flexmock(module.subprocess).should_receive('Popen').with_args(
  45. full_command,
  46. stdin=None,
  47. stdout=module.subprocess.PIPE,
  48. stderr=module.subprocess.STDOUT,
  49. shell=False,
  50. env=None,
  51. cwd=None,
  52. ).and_return(flexmock(stdout=None)).once()
  53. flexmock(module).should_receive('log_outputs')
  54. output = module.execute_command(full_command)
  55. assert output is None
  56. def test_execute_command_calls_full_command_with_output_file():
  57. full_command = ['foo', 'bar']
  58. output_file = flexmock(name='test')
  59. flexmock(module.os, environ={'a': 'b'})
  60. flexmock(module.subprocess).should_receive('Popen').with_args(
  61. full_command,
  62. stdin=None,
  63. stdout=output_file,
  64. stderr=module.subprocess.PIPE,
  65. shell=False,
  66. env=None,
  67. cwd=None,
  68. ).and_return(flexmock(stderr=None)).once()
  69. flexmock(module).should_receive('log_outputs')
  70. output = module.execute_command(full_command, output_file=output_file)
  71. assert output is None
  72. def test_execute_command_calls_full_command_without_capturing_output():
  73. full_command = ['foo', 'bar']
  74. flexmock(module.os, environ={'a': 'b'})
  75. flexmock(module.subprocess).should_receive('Popen').with_args(
  76. full_command, stdin=None, stdout=None, stderr=None, shell=False, env=None, cwd=None
  77. ).and_return(flexmock(wait=lambda: 0)).once()
  78. flexmock(module).should_receive('exit_code_indicates_error').and_return(False)
  79. flexmock(module).should_receive('log_outputs')
  80. output = module.execute_command(full_command, output_file=module.DO_NOT_CAPTURE)
  81. assert output is None
  82. def test_execute_command_calls_full_command_with_input_file():
  83. full_command = ['foo', 'bar']
  84. input_file = flexmock(name='test')
  85. flexmock(module.os, environ={'a': 'b'})
  86. flexmock(module.subprocess).should_receive('Popen').with_args(
  87. full_command,
  88. stdin=input_file,
  89. stdout=module.subprocess.PIPE,
  90. stderr=module.subprocess.STDOUT,
  91. shell=False,
  92. env=None,
  93. cwd=None,
  94. ).and_return(flexmock(stdout=None)).once()
  95. flexmock(module).should_receive('log_outputs')
  96. output = module.execute_command(full_command, input_file=input_file)
  97. assert output is None
  98. def test_execute_command_calls_full_command_with_shell():
  99. full_command = ['foo', 'bar']
  100. flexmock(module.os, environ={'a': 'b'})
  101. flexmock(module.subprocess).should_receive('Popen').with_args(
  102. ' '.join(full_command),
  103. stdin=None,
  104. stdout=module.subprocess.PIPE,
  105. stderr=module.subprocess.STDOUT,
  106. shell=True,
  107. env=None,
  108. cwd=None,
  109. ).and_return(flexmock(stdout=None)).once()
  110. flexmock(module).should_receive('log_outputs')
  111. output = module.execute_command(full_command, shell=True)
  112. assert output is None
  113. def test_execute_command_calls_full_command_with_extra_environment():
  114. full_command = ['foo', 'bar']
  115. flexmock(module.os, environ={'a': 'b'})
  116. flexmock(module.subprocess).should_receive('Popen').with_args(
  117. full_command,
  118. stdin=None,
  119. stdout=module.subprocess.PIPE,
  120. stderr=module.subprocess.STDOUT,
  121. shell=False,
  122. env={'a': 'b', 'c': 'd'},
  123. cwd=None,
  124. ).and_return(flexmock(stdout=None)).once()
  125. flexmock(module).should_receive('log_outputs')
  126. output = module.execute_command(full_command, extra_environment={'c': 'd'})
  127. assert output is None
  128. def test_execute_command_calls_full_command_with_working_directory():
  129. full_command = ['foo', 'bar']
  130. flexmock(module.os, environ={'a': 'b'})
  131. flexmock(module.subprocess).should_receive('Popen').with_args(
  132. full_command,
  133. stdin=None,
  134. stdout=module.subprocess.PIPE,
  135. stderr=module.subprocess.STDOUT,
  136. shell=False,
  137. env=None,
  138. cwd='/working',
  139. ).and_return(flexmock(stdout=None)).once()
  140. flexmock(module).should_receive('log_outputs')
  141. output = module.execute_command(full_command, working_directory='/working')
  142. assert output is None
  143. def test_execute_command_without_run_to_completion_returns_process():
  144. full_command = ['foo', 'bar']
  145. process = flexmock()
  146. flexmock(module.os, environ={'a': 'b'})
  147. flexmock(module.subprocess).should_receive('Popen').with_args(
  148. full_command,
  149. stdin=None,
  150. stdout=module.subprocess.PIPE,
  151. stderr=module.subprocess.STDOUT,
  152. shell=False,
  153. env=None,
  154. cwd=None,
  155. ).and_return(process).once()
  156. flexmock(module).should_receive('log_outputs')
  157. assert module.execute_command(full_command, run_to_completion=False) == process
  158. def test_execute_command_captures_output():
  159. full_command = ['foo', 'bar']
  160. expected_output = '[]'
  161. flexmock(module.os, environ={'a': 'b'})
  162. flexmock(module.subprocess).should_receive('check_output').with_args(
  163. full_command, shell=False, env=None, cwd=None
  164. ).and_return(flexmock(decode=lambda: expected_output)).once()
  165. output = module.execute_command(full_command, output_log_level=None)
  166. assert output == expected_output
  167. def test_execute_command_captures_output_with_shell():
  168. full_command = ['foo', 'bar']
  169. expected_output = '[]'
  170. flexmock(module.os, environ={'a': 'b'})
  171. flexmock(module.subprocess).should_receive('check_output').with_args(
  172. 'foo bar', shell=True, env=None, cwd=None
  173. ).and_return(flexmock(decode=lambda: expected_output)).once()
  174. output = module.execute_command(full_command, output_log_level=None, shell=True)
  175. assert output == expected_output
  176. def test_execute_command_captures_output_with_extra_environment():
  177. full_command = ['foo', 'bar']
  178. expected_output = '[]'
  179. flexmock(module.os, environ={'a': 'b'})
  180. flexmock(module.subprocess).should_receive('check_output').with_args(
  181. full_command, shell=False, env={'a': 'b', 'c': 'd'}, cwd=None
  182. ).and_return(flexmock(decode=lambda: expected_output)).once()
  183. output = module.execute_command(
  184. full_command, output_log_level=None, shell=False, extra_environment={'c': 'd'}
  185. )
  186. assert output == expected_output
  187. def test_execute_command_captures_output_with_working_directory():
  188. full_command = ['foo', 'bar']
  189. expected_output = '[]'
  190. flexmock(module.os, environ={'a': 'b'})
  191. flexmock(module.subprocess).should_receive('check_output').with_args(
  192. full_command, shell=False, env=None, cwd='/working'
  193. ).and_return(flexmock(decode=lambda: expected_output)).once()
  194. output = module.execute_command(
  195. full_command, output_log_level=None, shell=False, working_directory='/working'
  196. )
  197. assert output == expected_output
  198. def test_execute_command_with_processes_calls_full_command():
  199. full_command = ['foo', 'bar']
  200. processes = (flexmock(),)
  201. flexmock(module.os, environ={'a': 'b'})
  202. flexmock(module.subprocess).should_receive('Popen').with_args(
  203. full_command,
  204. stdin=None,
  205. stdout=module.subprocess.PIPE,
  206. stderr=module.subprocess.STDOUT,
  207. shell=False,
  208. env=None,
  209. cwd=None,
  210. ).and_return(flexmock(stdout=None)).once()
  211. flexmock(module).should_receive('log_outputs')
  212. output = module.execute_command_with_processes(full_command, processes)
  213. assert output is None
  214. def test_execute_command_with_processes_calls_full_command_with_output_file():
  215. full_command = ['foo', 'bar']
  216. processes = (flexmock(),)
  217. output_file = flexmock(name='test')
  218. flexmock(module.os, environ={'a': 'b'})
  219. flexmock(module.subprocess).should_receive('Popen').with_args(
  220. full_command,
  221. stdin=None,
  222. stdout=output_file,
  223. stderr=module.subprocess.PIPE,
  224. shell=False,
  225. env=None,
  226. cwd=None,
  227. ).and_return(flexmock(stderr=None)).once()
  228. flexmock(module).should_receive('log_outputs')
  229. output = module.execute_command_with_processes(full_command, processes, output_file=output_file)
  230. assert output is None
  231. def test_execute_command_with_processes_calls_full_command_without_capturing_output():
  232. full_command = ['foo', 'bar']
  233. processes = (flexmock(),)
  234. flexmock(module.os, environ={'a': 'b'})
  235. flexmock(module.subprocess).should_receive('Popen').with_args(
  236. full_command, stdin=None, stdout=None, stderr=None, shell=False, env=None, cwd=None
  237. ).and_return(flexmock(wait=lambda: 0)).once()
  238. flexmock(module).should_receive('exit_code_indicates_error').and_return(False)
  239. flexmock(module).should_receive('log_outputs')
  240. output = module.execute_command_with_processes(
  241. full_command, processes, output_file=module.DO_NOT_CAPTURE
  242. )
  243. assert output is None
  244. def test_execute_command_with_processes_calls_full_command_with_input_file():
  245. full_command = ['foo', 'bar']
  246. processes = (flexmock(),)
  247. input_file = flexmock(name='test')
  248. flexmock(module.os, environ={'a': 'b'})
  249. flexmock(module.subprocess).should_receive('Popen').with_args(
  250. full_command,
  251. stdin=input_file,
  252. stdout=module.subprocess.PIPE,
  253. stderr=module.subprocess.STDOUT,
  254. shell=False,
  255. env=None,
  256. cwd=None,
  257. ).and_return(flexmock(stdout=None)).once()
  258. flexmock(module).should_receive('log_outputs')
  259. output = module.execute_command_with_processes(full_command, processes, input_file=input_file)
  260. assert output is None
  261. def test_execute_command_with_processes_calls_full_command_with_shell():
  262. full_command = ['foo', 'bar']
  263. processes = (flexmock(),)
  264. flexmock(module.os, environ={'a': 'b'})
  265. flexmock(module.subprocess).should_receive('Popen').with_args(
  266. ' '.join(full_command),
  267. stdin=None,
  268. stdout=module.subprocess.PIPE,
  269. stderr=module.subprocess.STDOUT,
  270. shell=True,
  271. env=None,
  272. cwd=None,
  273. ).and_return(flexmock(stdout=None)).once()
  274. flexmock(module).should_receive('log_outputs')
  275. output = module.execute_command_with_processes(full_command, processes, shell=True)
  276. assert output is None
  277. def test_execute_command_with_processes_calls_full_command_with_extra_environment():
  278. full_command = ['foo', 'bar']
  279. processes = (flexmock(),)
  280. flexmock(module.os, environ={'a': 'b'})
  281. flexmock(module.subprocess).should_receive('Popen').with_args(
  282. full_command,
  283. stdin=None,
  284. stdout=module.subprocess.PIPE,
  285. stderr=module.subprocess.STDOUT,
  286. shell=False,
  287. env={'a': 'b', 'c': 'd'},
  288. cwd=None,
  289. ).and_return(flexmock(stdout=None)).once()
  290. flexmock(module).should_receive('log_outputs')
  291. output = module.execute_command_with_processes(
  292. full_command, processes, extra_environment={'c': 'd'}
  293. )
  294. assert output is None
  295. def test_execute_command_with_processes_calls_full_command_with_working_directory():
  296. full_command = ['foo', 'bar']
  297. processes = (flexmock(),)
  298. flexmock(module.os, environ={'a': 'b'})
  299. flexmock(module.subprocess).should_receive('Popen').with_args(
  300. full_command,
  301. stdin=None,
  302. stdout=module.subprocess.PIPE,
  303. stderr=module.subprocess.STDOUT,
  304. shell=False,
  305. env=None,
  306. cwd='/working',
  307. ).and_return(flexmock(stdout=None)).once()
  308. flexmock(module).should_receive('log_outputs')
  309. output = module.execute_command_with_processes(
  310. full_command, processes, working_directory='/working'
  311. )
  312. assert output is None
  313. def test_execute_command_with_processes_kills_processes_on_error():
  314. full_command = ['foo', 'bar']
  315. process = flexmock(stdout=flexmock(read=lambda count: None))
  316. process.should_receive('poll')
  317. process.should_receive('kill').once()
  318. processes = (process,)
  319. flexmock(module.os, environ={'a': 'b'})
  320. flexmock(module.subprocess).should_receive('Popen').with_args(
  321. full_command,
  322. stdin=None,
  323. stdout=module.subprocess.PIPE,
  324. stderr=module.subprocess.STDOUT,
  325. shell=False,
  326. env=None,
  327. cwd=None,
  328. ).and_raise(subprocess.CalledProcessError(1, full_command, 'error')).once()
  329. flexmock(module).should_receive('log_outputs').never()
  330. with pytest.raises(subprocess.CalledProcessError):
  331. module.execute_command_with_processes(full_command, processes)