diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 5b1eedeea..52f069596 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -19,7 +19,7 @@ from configparser import ConfigParser from datetime import datetime from datetime import timedelta from hashlib import sha256 -from io import StringIO +from io import BytesIO, StringIO from unittest.mock import patch import msgpack @@ -61,7 +61,7 @@ from . import key src_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) -def exec_cmd(*args, archiver=None, fork=False, exe=None, input=b'', **kw): +def exec_cmd(*args, archiver=None, fork=False, exe=None, input=b'', binary_output=False, **kw): if fork: try: if exe is None: @@ -78,12 +78,18 @@ def exec_cmd(*args, archiver=None, fork=False, exe=None, input=b'', **kw): except SystemExit as e: # possibly raised by argparse output = '' ret = e.code - return ret, os.fsdecode(output) + if binary_output: + return ret, output + else: + return ret, os.fsdecode(output) else: stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr try: sys.stdin = StringIO(input.decode()) - sys.stdout = sys.stderr = output = StringIO() + sys.stdin.buffer = BytesIO(input) + output = BytesIO() + # Always use utf-8 here, to simply .decode() below + output_text = sys.stdout = sys.stderr = io.TextIOWrapper(output, encoding='utf-8') if archiver is None: archiver = Archiver() archiver.prerun_checks = lambda *args: None @@ -95,9 +101,11 @@ def exec_cmd(*args, archiver=None, fork=False, exe=None, input=b'', **kw): # actions that abort early (eg. --help) where given. Catch this and return # the error code as-if we invoked a Borg binary. except SystemExit as e: - return e.code, output.getvalue() + output_text.flush() + return e.code, output.getvalue() if binary_output else output.getvalue().decode() ret = archiver.run(args) - return ret, output.getvalue() + output_text.flush() + return ret, output.getvalue() if binary_output else output.getvalue().decode() finally: sys.stdin, sys.stdout, sys.stderr = stdin, stdout, stderr @@ -927,6 +935,18 @@ class ArchiverTestCase(ArchiverTestCaseBase): os.mkdir('input/cache3') os.link('input/cache1/%s' % CACHE_TAG_NAME, 'input/cache3/%s' % CACHE_TAG_NAME) + def test_create_stdin(self): + self.cmd('init', '--encryption=repokey', self.repository_location) + input_data = b'\x00foo\n\nbar\n \n' + self.cmd('create', self.repository_location + '::test', '-', input=input_data) + item = json.loads(self.cmd('list', '--json-lines', self.repository_location + '::test')) + assert item['uid'] == 0 + assert item['gid'] == 0 + assert item['size'] == len(input_data) + assert item['path'] == 'stdin' + extracted_data = self.cmd('extract', '--stdout', self.repository_location + '::test', binary_output=True) + assert extracted_data == input_data + def test_create_without_root(self): """test create without a root""" self.cmd('init', '--encryption=repokey', self.repository_location)