test_execute.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. import subprocess
  2. import pytest
  3. from flexmock import flexmock
  4. from borgmatic import execute as module
  5. @pytest.mark.parametrize(
  6. 'command,exit_code,borg_local_path,borg_exit_codes,expected_result',
  7. (
  8. (['grep'], 2, None, None, module.Exit_status.ERROR),
  9. (['grep'], 2, 'borg', None, module.Exit_status.ERROR),
  10. (['borg'], 2, 'borg', None, module.Exit_status.ERROR),
  11. (['borg1'], 2, 'borg1', None, module.Exit_status.ERROR),
  12. (['grep'], 1, None, None, module.Exit_status.ERROR),
  13. (['grep'], 1, 'borg', None, module.Exit_status.ERROR),
  14. (['borg'], 1, 'borg', None, module.Exit_status.WARNING),
  15. (['borg1'], 1, 'borg1', None, module.Exit_status.WARNING),
  16. (['grep'], 100, None, None, module.Exit_status.ERROR),
  17. (['grep'], 100, 'borg', None, module.Exit_status.ERROR),
  18. (['borg'], 100, 'borg', None, module.Exit_status.WARNING),
  19. (['borg1'], 100, 'borg1', None, module.Exit_status.WARNING),
  20. (['grep'], 0, None, None, module.Exit_status.SUCCESS),
  21. (['grep'], 0, 'borg', None, module.Exit_status.SUCCESS),
  22. (['borg'], 0, 'borg', None, module.Exit_status.SUCCESS),
  23. (['borg1'], 0, 'borg1', None, module.Exit_status.SUCCESS),
  24. # -9 exit code occurs when child process get SIGKILLed.
  25. (['grep'], -9, None, None, module.Exit_status.ERROR),
  26. (['grep'], -9, 'borg', None, module.Exit_status.ERROR),
  27. (['borg'], -9, 'borg', None, module.Exit_status.ERROR),
  28. (['borg1'], -9, 'borg1', None, module.Exit_status.ERROR),
  29. (['borg'], None, None, None, module.Exit_status.STILL_RUNNING),
  30. (['borg'], 1, 'borg', [], module.Exit_status.WARNING),
  31. (['borg'], 1, 'borg', [{}], module.Exit_status.WARNING),
  32. (['borg'], 1, 'borg', [{'code': 1}], module.Exit_status.WARNING),
  33. (['grep'], 1, 'borg', [{'code': 100, 'treat_as': 'error'}], module.Exit_status.ERROR),
  34. (['borg'], 1, 'borg', [{'code': 100, 'treat_as': 'error'}], module.Exit_status.WARNING),
  35. (['borg'], 1, 'borg', [{'code': 1, 'treat_as': 'error'}], module.Exit_status.ERROR),
  36. (['borg'], 2, 'borg', [{'code': 99, 'treat_as': 'warning'}], module.Exit_status.ERROR),
  37. (['borg'], 2, 'borg', [{'code': 2, 'treat_as': 'warning'}], module.Exit_status.WARNING),
  38. (['borg'], 100, 'borg', [{'code': 1, 'treat_as': 'error'}], module.Exit_status.WARNING),
  39. (['borg'], 100, 'borg', [{'code': 100, 'treat_as': 'error'}], module.Exit_status.ERROR),
  40. ),
  41. )
  42. def test_interpret_exit_code_respects_exit_code_and_borg_local_path(
  43. command,
  44. exit_code,
  45. borg_local_path,
  46. borg_exit_codes,
  47. expected_result,
  48. ):
  49. assert (
  50. module.interpret_exit_code(command, exit_code, borg_local_path, borg_exit_codes)
  51. is expected_result
  52. )
  53. def test_command_for_process_converts_sequence_command_to_string():
  54. process = flexmock(args=['foo', 'bar', 'baz'])
  55. assert module.command_for_process(process) == 'foo bar baz'
  56. def test_command_for_process_passes_through_string_command():
  57. process = flexmock(args='foo bar baz')
  58. assert module.command_for_process(process) == 'foo bar baz'
  59. def test_output_buffer_for_process_returns_stderr_when_stdout_excluded():
  60. stdout = flexmock()
  61. stderr = flexmock()
  62. process = flexmock(stdout=stdout, stderr=stderr)
  63. assert module.output_buffer_for_process(process, exclude_stdouts=[flexmock(), stdout]) == stderr
  64. def test_output_buffer_for_process_returns_stdout_when_not_excluded():
  65. stdout = flexmock()
  66. process = flexmock(stdout=stdout)
  67. assert (
  68. module.output_buffer_for_process(process, exclude_stdouts=[flexmock(), flexmock()])
  69. == stdout
  70. )
  71. def test_append_last_lines_under_max_line_count_appends():
  72. last_lines = ['last']
  73. flexmock(module.logger).should_receive('log').once()
  74. module.append_last_lines(
  75. last_lines,
  76. captured_output=flexmock(),
  77. line='line',
  78. output_log_level=flexmock(),
  79. )
  80. assert last_lines == ['last', 'line']
  81. def test_append_last_lines_over_max_line_count_trims_and_appends():
  82. original_last_lines = [str(number) for number in range(module.ERROR_OUTPUT_MAX_LINE_COUNT)]
  83. last_lines = list(original_last_lines)
  84. flexmock(module.logger).should_receive('log').once()
  85. module.append_last_lines(
  86. last_lines,
  87. captured_output=flexmock(),
  88. line='line',
  89. output_log_level=flexmock(),
  90. )
  91. assert last_lines == [*original_last_lines[1:], 'line']
  92. def test_append_last_lines_with_output_log_level_none_appends_captured_output():
  93. last_lines = ['last']
  94. captured_output = ['captured']
  95. flexmock(module.logger).should_receive('log').never()
  96. module.append_last_lines(
  97. last_lines,
  98. captured_output=captured_output,
  99. line='line',
  100. output_log_level=None,
  101. )
  102. assert captured_output == ['captured', 'line']
  103. def test_mask_command_secrets_masks_password_flag_value():
  104. assert module.mask_command_secrets(('cooldb', '--username', 'bob', '--password', 'pass')) == (
  105. 'cooldb',
  106. '--username',
  107. 'bob',
  108. '--password',
  109. '***',
  110. )
  111. def test_mask_command_secrets_passes_through_other_commands():
  112. assert module.mask_command_secrets(('cooldb', '--username', 'bob')) == (
  113. 'cooldb',
  114. '--username',
  115. 'bob',
  116. )
  117. @pytest.mark.parametrize(
  118. 'full_command,input_file,output_file,environment,expected_result',
  119. (
  120. (('foo', 'bar'), None, None, None, 'foo bar'),
  121. (('foo', 'bar'), flexmock(name='input'), None, None, 'foo bar < input'),
  122. (('foo', 'bar'), None, flexmock(name='output'), None, 'foo bar > output'),
  123. (
  124. ('A',) * module.MAX_LOGGED_COMMAND_LENGTH,
  125. None,
  126. None,
  127. None,
  128. 'A ' * (module.MAX_LOGGED_COMMAND_LENGTH // 2 - 2) + '...',
  129. ),
  130. (
  131. ('foo', 'bar'),
  132. flexmock(name='input'),
  133. flexmock(name='output'),
  134. None,
  135. 'foo bar < input > output',
  136. ),
  137. (
  138. ('foo', 'bar'),
  139. None,
  140. None,
  141. {'UNKNOWN': 'secret', 'OTHER': 'thing'},
  142. 'foo bar',
  143. ),
  144. (
  145. ('foo', 'bar'),
  146. None,
  147. None,
  148. {'PGTHING': 'secret', 'BORG_OTHER': 'thing'},
  149. 'PGTHING=*** BORG_OTHER=*** foo bar',
  150. ),
  151. ),
  152. )
  153. def test_log_command_logs_command_constructed_from_arguments(
  154. full_command,
  155. input_file,
  156. output_file,
  157. environment,
  158. expected_result,
  159. ):
  160. flexmock(module).should_receive('mask_command_secrets').replace_with(lambda command: command)
  161. flexmock(module.logger).should_receive('debug').with_args(expected_result).once()
  162. module.log_command(full_command, input_file, output_file, environment)
  163. def test_execute_command_calls_full_command():
  164. full_command = ['foo', 'bar']
  165. flexmock(module).should_receive('log_command')
  166. flexmock(module.subprocess).should_receive('Popen').with_args(
  167. full_command,
  168. stdin=None,
  169. stdout=module.subprocess.PIPE,
  170. stderr=module.subprocess.STDOUT,
  171. shell=False,
  172. env=None,
  173. cwd=None,
  174. close_fds=False,
  175. ).and_return(flexmock(stdout=None)).once()
  176. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  177. flexmock(module).should_receive('log_outputs')
  178. output = module.execute_command(full_command)
  179. assert output is None
  180. def test_execute_command_calls_full_command_with_output_file():
  181. full_command = ['foo', 'bar']
  182. output_file = flexmock(name='test')
  183. flexmock(module).should_receive('log_command')
  184. flexmock(module.subprocess).should_receive('Popen').with_args(
  185. full_command,
  186. stdin=None,
  187. stdout=output_file,
  188. stderr=module.subprocess.PIPE,
  189. shell=False,
  190. env=None,
  191. cwd=None,
  192. close_fds=False,
  193. ).and_return(flexmock(stderr=None)).once()
  194. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  195. flexmock(module).should_receive('log_outputs')
  196. output = module.execute_command(full_command, output_file=output_file)
  197. assert output is None
  198. def test_execute_command_calls_full_command_without_capturing_output():
  199. full_command = ['foo', 'bar']
  200. flexmock(module).should_receive('log_command')
  201. flexmock(module.subprocess).should_receive('Popen').with_args(
  202. full_command,
  203. stdin=None,
  204. stdout=None,
  205. stderr=None,
  206. shell=False,
  207. env=None,
  208. cwd=None,
  209. close_fds=False,
  210. ).and_return(flexmock(wait=lambda: 0)).once()
  211. flexmock(module).should_receive('interpret_exit_code').and_return(module.Exit_status.SUCCESS)
  212. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  213. flexmock(module).should_receive('log_outputs')
  214. output = module.execute_command(full_command, output_file=module.DO_NOT_CAPTURE)
  215. assert output is None
  216. def test_execute_command_calls_full_command_with_input_file():
  217. full_command = ['foo', 'bar']
  218. input_file = flexmock(name='test')
  219. flexmock(module).should_receive('log_command')
  220. flexmock(module.subprocess).should_receive('Popen').with_args(
  221. full_command,
  222. stdin=input_file,
  223. stdout=module.subprocess.PIPE,
  224. stderr=module.subprocess.STDOUT,
  225. shell=False,
  226. env=None,
  227. cwd=None,
  228. close_fds=False,
  229. ).and_return(flexmock(stdout=None)).once()
  230. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  231. flexmock(module).should_receive('log_outputs')
  232. output = module.execute_command(full_command, input_file=input_file)
  233. assert output is None
  234. def test_execute_command_calls_full_command_with_shell():
  235. full_command = ['foo', 'bar']
  236. flexmock(module).should_receive('log_command')
  237. flexmock(module.subprocess).should_receive('Popen').with_args(
  238. ' '.join(full_command),
  239. stdin=None,
  240. stdout=module.subprocess.PIPE,
  241. stderr=module.subprocess.STDOUT,
  242. shell=True,
  243. env=None,
  244. cwd=None,
  245. close_fds=False,
  246. ).and_return(flexmock(stdout=None)).once()
  247. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  248. flexmock(module).should_receive('log_outputs')
  249. output = module.execute_command(full_command, shell=True)
  250. assert output is None
  251. def test_execute_command_calls_full_command_with_environment():
  252. full_command = ['foo', 'bar']
  253. flexmock(module).should_receive('log_command')
  254. flexmock(module.subprocess).should_receive('Popen').with_args(
  255. full_command,
  256. stdin=None,
  257. stdout=module.subprocess.PIPE,
  258. stderr=module.subprocess.STDOUT,
  259. shell=False,
  260. env={'a': 'b'},
  261. cwd=None,
  262. close_fds=False,
  263. ).and_return(flexmock(stdout=None)).once()
  264. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  265. flexmock(module).should_receive('log_outputs')
  266. output = module.execute_command(full_command, environment={'a': 'b'})
  267. assert output is None
  268. def test_execute_command_calls_full_command_with_working_directory():
  269. full_command = ['foo', 'bar']
  270. flexmock(module).should_receive('log_command')
  271. flexmock(module.subprocess).should_receive('Popen').with_args(
  272. full_command,
  273. stdin=None,
  274. stdout=module.subprocess.PIPE,
  275. stderr=module.subprocess.STDOUT,
  276. shell=False,
  277. env=None,
  278. cwd='/working',
  279. close_fds=False,
  280. ).and_return(flexmock(stdout=None)).once()
  281. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  282. flexmock(module).should_receive('log_outputs')
  283. output = module.execute_command(full_command, working_directory='/working')
  284. assert output is None
  285. def test_execute_command_without_run_to_completion_returns_process():
  286. full_command = ['foo', 'bar']
  287. process = flexmock()
  288. flexmock(module).should_receive('log_command')
  289. flexmock(module.subprocess).should_receive('Popen').with_args(
  290. full_command,
  291. stdin=None,
  292. stdout=module.subprocess.PIPE,
  293. stderr=module.subprocess.STDOUT,
  294. shell=False,
  295. env=None,
  296. cwd=None,
  297. close_fds=False,
  298. ).and_return(process).once()
  299. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  300. flexmock(module).should_receive('log_outputs')
  301. assert module.execute_command(full_command, run_to_completion=False) == process
  302. def test_execute_command_and_capture_output_returns_stdout():
  303. full_command = ['foo', 'bar']
  304. expected_output = '[]'
  305. flexmock(module).should_receive('log_command')
  306. flexmock(module.subprocess).should_receive('check_output').with_args(
  307. full_command,
  308. stdin=None,
  309. stderr=None,
  310. shell=False,
  311. env=None,
  312. cwd=None,
  313. close_fds=False,
  314. ).and_return(flexmock(decode=lambda: expected_output)).once()
  315. output = module.execute_command_and_capture_output(full_command)
  316. assert output == expected_output
  317. def test_execute_command_and_capture_output_with_capture_stderr_returns_stderr():
  318. full_command = ['foo', 'bar']
  319. expected_output = '[]'
  320. flexmock(module).should_receive('log_command')
  321. flexmock(module.subprocess).should_receive('check_output').with_args(
  322. full_command,
  323. stdin=None,
  324. stderr=module.subprocess.STDOUT,
  325. shell=False,
  326. env=None,
  327. cwd=None,
  328. close_fds=False,
  329. ).and_return(flexmock(decode=lambda: expected_output)).once()
  330. output = module.execute_command_and_capture_output(full_command, capture_stderr=True)
  331. assert output == expected_output
  332. def test_execute_command_and_capture_output_returns_output_when_process_error_is_not_considered_an_error():
  333. full_command = ['foo', 'bar']
  334. expected_output = '[]'
  335. err_output = b'[]'
  336. flexmock(module).should_receive('log_command')
  337. flexmock(module.subprocess).should_receive('check_output').with_args(
  338. full_command,
  339. stdin=None,
  340. stderr=None,
  341. shell=False,
  342. env=None,
  343. cwd=None,
  344. close_fds=False,
  345. ).and_raise(subprocess.CalledProcessError(1, full_command, err_output)).once()
  346. flexmock(module).should_receive('interpret_exit_code').and_return(
  347. module.Exit_status.SUCCESS,
  348. ).once()
  349. output = module.execute_command_and_capture_output(full_command)
  350. assert output == expected_output
  351. def test_execute_command_and_capture_output_raises_when_command_errors():
  352. full_command = ['foo', 'bar']
  353. expected_output = '[]'
  354. flexmock(module).should_receive('log_command')
  355. flexmock(module.subprocess).should_receive('check_output').with_args(
  356. full_command,
  357. stdin=None,
  358. stderr=None,
  359. shell=False,
  360. env=None,
  361. cwd=None,
  362. close_fds=False,
  363. ).and_raise(subprocess.CalledProcessError(2, full_command, expected_output)).once()
  364. flexmock(module).should_receive('interpret_exit_code').and_return(
  365. module.Exit_status.ERROR,
  366. ).once()
  367. with pytest.raises(subprocess.CalledProcessError):
  368. module.execute_command_and_capture_output(full_command)
  369. def test_execute_command_and_capture_output_returns_output_with_shell():
  370. full_command = ['foo', 'bar']
  371. expected_output = '[]'
  372. flexmock(module).should_receive('log_command')
  373. flexmock(module.subprocess).should_receive('check_output').with_args(
  374. 'foo bar',
  375. stdin=None,
  376. stderr=None,
  377. shell=True,
  378. env=None,
  379. cwd=None,
  380. close_fds=False,
  381. ).and_return(flexmock(decode=lambda: expected_output)).once()
  382. output = module.execute_command_and_capture_output(full_command, shell=True)
  383. assert output == expected_output
  384. def test_execute_command_and_capture_output_returns_output_with_environment():
  385. full_command = ['foo', 'bar']
  386. expected_output = '[]'
  387. flexmock(module).should_receive('log_command')
  388. flexmock(module.subprocess).should_receive('check_output').with_args(
  389. full_command,
  390. stdin=None,
  391. stderr=None,
  392. shell=False,
  393. env={'a': 'b'},
  394. cwd=None,
  395. close_fds=False,
  396. ).and_return(flexmock(decode=lambda: expected_output)).once()
  397. output = module.execute_command_and_capture_output(
  398. full_command,
  399. shell=False,
  400. environment={'a': 'b'},
  401. )
  402. assert output == expected_output
  403. def test_execute_command_and_capture_output_returns_output_with_working_directory():
  404. full_command = ['foo', 'bar']
  405. expected_output = '[]'
  406. flexmock(module).should_receive('log_command')
  407. flexmock(module.subprocess).should_receive('check_output').with_args(
  408. full_command,
  409. stdin=None,
  410. stderr=None,
  411. shell=False,
  412. env=None,
  413. cwd='/working',
  414. close_fds=False,
  415. ).and_return(flexmock(decode=lambda: expected_output)).once()
  416. output = module.execute_command_and_capture_output(
  417. full_command,
  418. shell=False,
  419. working_directory='/working',
  420. )
  421. assert output == expected_output
  422. def test_execute_command_with_processes_calls_full_command():
  423. full_command = ['foo', 'bar']
  424. processes = (flexmock(),)
  425. flexmock(module).should_receive('log_command')
  426. flexmock(module.subprocess).should_receive('Popen').with_args(
  427. full_command,
  428. stdin=None,
  429. stdout=module.subprocess.PIPE,
  430. stderr=module.subprocess.STDOUT,
  431. shell=False,
  432. env=None,
  433. cwd=None,
  434. close_fds=False,
  435. ).and_return(flexmock(stdout=None)).once()
  436. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  437. flexmock(module).should_receive('log_outputs')
  438. output = module.execute_command_with_processes(full_command, processes)
  439. assert output is None
  440. def test_execute_command_with_processes_returns_output_with_output_log_level_none():
  441. full_command = ['foo', 'bar']
  442. processes = (flexmock(),)
  443. flexmock(module).should_receive('log_command')
  444. process = flexmock(stdout=None)
  445. flexmock(module.subprocess).should_receive('Popen').with_args(
  446. full_command,
  447. stdin=None,
  448. stdout=module.subprocess.PIPE,
  449. stderr=module.subprocess.STDOUT,
  450. shell=False,
  451. env=None,
  452. cwd=None,
  453. close_fds=False,
  454. ).and_return(process).once()
  455. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  456. flexmock(module).should_receive('log_outputs').and_return({process: 'out'})
  457. output = module.execute_command_with_processes(full_command, processes, output_log_level=None)
  458. assert output == 'out'
  459. def test_execute_command_with_processes_calls_full_command_with_output_file():
  460. full_command = ['foo', 'bar']
  461. processes = (flexmock(),)
  462. output_file = flexmock(name='test')
  463. flexmock(module).should_receive('log_command')
  464. flexmock(module.subprocess).should_receive('Popen').with_args(
  465. full_command,
  466. stdin=None,
  467. stdout=output_file,
  468. stderr=module.subprocess.PIPE,
  469. shell=False,
  470. env=None,
  471. cwd=None,
  472. close_fds=False,
  473. ).and_return(flexmock(stderr=None)).once()
  474. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  475. flexmock(module).should_receive('log_outputs')
  476. output = module.execute_command_with_processes(full_command, processes, output_file=output_file)
  477. assert output is None
  478. def test_execute_command_with_processes_calls_full_command_without_capturing_output():
  479. full_command = ['foo', 'bar']
  480. processes = (flexmock(),)
  481. flexmock(module).should_receive('log_command')
  482. flexmock(module.subprocess).should_receive('Popen').with_args(
  483. full_command,
  484. stdin=None,
  485. stdout=None,
  486. stderr=None,
  487. shell=False,
  488. env=None,
  489. cwd=None,
  490. close_fds=False,
  491. ).and_return(flexmock(wait=lambda: 0)).once()
  492. flexmock(module).should_receive('interpret_exit_code').and_return(module.Exit_status.SUCCESS)
  493. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  494. flexmock(module).should_receive('log_outputs')
  495. output = module.execute_command_with_processes(
  496. full_command,
  497. processes,
  498. output_file=module.DO_NOT_CAPTURE,
  499. )
  500. assert output is None
  501. def test_execute_command_with_processes_calls_full_command_with_input_file():
  502. full_command = ['foo', 'bar']
  503. processes = (flexmock(),)
  504. input_file = flexmock(name='test')
  505. flexmock(module).should_receive('log_command')
  506. flexmock(module.subprocess).should_receive('Popen').with_args(
  507. full_command,
  508. stdin=input_file,
  509. stdout=module.subprocess.PIPE,
  510. stderr=module.subprocess.STDOUT,
  511. shell=False,
  512. env=None,
  513. cwd=None,
  514. close_fds=False,
  515. ).and_return(flexmock(stdout=None)).once()
  516. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  517. flexmock(module).should_receive('log_outputs')
  518. output = module.execute_command_with_processes(full_command, processes, input_file=input_file)
  519. assert output is None
  520. def test_execute_command_with_processes_calls_full_command_with_shell():
  521. full_command = ['foo', 'bar']
  522. processes = (flexmock(),)
  523. flexmock(module).should_receive('log_command')
  524. flexmock(module.subprocess).should_receive('Popen').with_args(
  525. ' '.join(full_command),
  526. stdin=None,
  527. stdout=module.subprocess.PIPE,
  528. stderr=module.subprocess.STDOUT,
  529. shell=True,
  530. env=None,
  531. cwd=None,
  532. close_fds=False,
  533. ).and_return(flexmock(stdout=None)).once()
  534. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  535. flexmock(module).should_receive('log_outputs')
  536. output = module.execute_command_with_processes(full_command, processes, shell=True)
  537. assert output is None
  538. def test_execute_command_with_processes_calls_full_command_with_environment():
  539. full_command = ['foo', 'bar']
  540. processes = (flexmock(),)
  541. flexmock(module).should_receive('log_command')
  542. flexmock(module.subprocess).should_receive('Popen').with_args(
  543. full_command,
  544. stdin=None,
  545. stdout=module.subprocess.PIPE,
  546. stderr=module.subprocess.STDOUT,
  547. shell=False,
  548. env={'a': 'b'},
  549. cwd=None,
  550. close_fds=False,
  551. ).and_return(flexmock(stdout=None)).once()
  552. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  553. flexmock(module).should_receive('log_outputs')
  554. output = module.execute_command_with_processes(full_command, processes, environment={'a': 'b'})
  555. assert output is None
  556. def test_execute_command_with_processes_calls_full_command_with_working_directory():
  557. full_command = ['foo', 'bar']
  558. processes = (flexmock(),)
  559. flexmock(module).should_receive('log_command')
  560. flexmock(module.subprocess).should_receive('Popen').with_args(
  561. full_command,
  562. stdin=None,
  563. stdout=module.subprocess.PIPE,
  564. stderr=module.subprocess.STDOUT,
  565. shell=False,
  566. env=None,
  567. cwd='/working',
  568. close_fds=False,
  569. ).and_return(flexmock(stdout=None)).once()
  570. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  571. flexmock(module).should_receive('log_outputs')
  572. output = module.execute_command_with_processes(
  573. full_command,
  574. processes,
  575. working_directory='/working',
  576. )
  577. assert output is None
  578. def test_execute_command_with_processes_kills_processes_on_error():
  579. full_command = ['foo', 'bar']
  580. flexmock(module).should_receive('log_command')
  581. process = flexmock(stdout=flexmock(read=lambda count: None))
  582. process.should_receive('poll')
  583. process.should_receive('kill').once()
  584. processes = (process,)
  585. flexmock(module.subprocess).should_receive('Popen').with_args(
  586. full_command,
  587. stdin=None,
  588. stdout=module.subprocess.PIPE,
  589. stderr=module.subprocess.STDOUT,
  590. shell=False,
  591. env=None,
  592. cwd=None,
  593. close_fds=False,
  594. ).and_raise(subprocess.CalledProcessError(1, full_command, 'error')).once()
  595. flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
  596. flexmock(module).should_receive('log_outputs').never()
  597. with pytest.raises(subprocess.CalledProcessError):
  598. module.execute_command_with_processes(full_command, processes)