test_execute.py 14 KB

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