test_execute.py 14 KB

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