887
888
:param universal_newlines: Convert CRLF => LF
889
890
env_changes = kwargs.get('env_changes', {})
891
process = self.start_bzr_subprocess(args, env_changes=env_changes)
892
# We distinguish between retcode=None and retcode not passed.
893
supplied_retcode = kwargs.get('retcode', 0)
894
return self.finish_bzr_subprocess(process, retcode=supplied_retcode,
895
universal_newlines=kwargs.get('universal_newlines', False),
898
def start_bzr_subprocess(self, process_args, env_changes=None,
899
skip_if_plan_to_signal=False):
900
"""Start bzr in a subprocess for testing.
902
This starts a new Python interpreter and runs bzr in there.
903
This should only be used for tests that have a justifiable need for
904
this isolation: e.g. they are testing startup time, or signal
905
handling, or early startup code, etc. Subprocess code can't be
906
profiled or debugged so easily.
908
:param process_args: a list of arguments to pass to the bzr executable,
909
for example `['--version']`.
910
:param env_changes: A dictionary which lists changes to environment
911
variables. A value of None will unset the env variable.
912
The values must be strings. The change will only occur in the
913
child, so you don't need to fix the environment after running.
914
:param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
917
:returns: Popen object for the started process.
919
if skip_if_plan_to_signal:
920
if not getattr(os, 'kill', None):
921
raise TestSkipped("os.kill not available.")
923
if env_changes is None:
893
927
def cleanup_environment():
902
936
if not os.path.isfile(bzr_path):
903
937
# We are probably installed. Assume sys.argv is the right file
904
938
bzr_path = sys.argv[0]
908
941
# win32 subprocess doesn't support preexec_fn
909
942
# so we will avoid using it on all platforms, just to
910
943
# make sure the code path is used, and we don't break on win32
911
944
cleanup_environment()
912
process = Popen([sys.executable, bzr_path]+args,
945
process = Popen([sys.executable, bzr_path] + list(process_args),
913
946
stdout=PIPE, stderr=PIPE)
915
948
restore_environment()
917
out = process.stdout.read()
918
err = process.stderr.read()
920
if kwargs.get('universal_newlines', False):
951
def finish_bzr_subprocess(self, process, retcode=0, send_signal=None,
952
universal_newlines=False, process_args=None):
953
"""Finish the execution of process.
955
:param process: the Popen object returned from start_bzr_subprocess.
956
:param retcode: The status code that is expected. Defaults to 0. If
957
None is supplied, the status code is not checked.
958
:param send_signal: an optional signal to send to the process.
959
:param universal_newlines: Convert CRLF => LF
960
:returns: (stdout, stderr)
962
if send_signal is not None:
963
os.kill(process.pid, send_signal)
964
out, err = process.communicate()
966
if universal_newlines:
921
967
out = out.replace('\r\n', '\n')
922
968
err = err.replace('\r\n', '\n')
924
retcode = process.wait()
925
supplied_retcode = kwargs.get('retcode', 0)
926
if supplied_retcode is not None:
927
if supplied_retcode != retcode:
928
mutter('Output of bzr %s:\n%s', args, out)
929
mutter('Error for bzr %s:\n%s', args, err)
930
self.fail('Command bzr %s failed with retcode %s != %s'
931
% (args, supplied_retcode, retcode))
970
if retcode is not None and retcode != process.returncode:
971
if process_args is None:
972
process_args = "(unknown args)"
973
mutter('Output of bzr %s:\n%s', process_args, out)
974
mutter('Error for bzr %s:\n%s', process_args, err)
975
self.fail('Command bzr %s failed with retcode %s != %s'
976
% (process_args, retcode, process.returncode))
932
977
return [out, err]
934
979
def check_inventory_shape(self, inv, shape):