[[[ In cmdline tests, use subprocess.Popen for executing commands to allow support for exit-code checks on both Windows and posix systems. This makes the test suite require Python version >= 2.4. * subversion/tests/cmdline/svntest/main.py (global): Import subprocess instead of popen2. Remove variable "platform_with_popen3_class". (_quote_arg): Removed, since commands + args to open_pipe are now lists. (open_pipe): Replace "mode" character parameter with a "binary_mode" boolean value. Use subprocess.Popen for spawning the child process. (wait_on_pipe): Interpret the return value of wait() according to subprocess.Popen semantics. (spawn_process, copy_repos): When calling open_pipe, pass the command as a list and binary_mode as a boolean. (run_svn2): New, like run_svn but with a "binary_mode" boolean param. * subversion/tests/cmdline/svntest/actions.py (run_and_verify_svnlook, run_and_verify_svnlook2, run_and_verify_svnadmin, run_and_verify_svnadmin2, run_and_verify_svnversion, run_and_verify_svnversion2, run_and_verify_svn_match_any, run_and_verify_svn_match_any2): Remove the caveat comment stating that exit-code checks are skipped for some platforms. (run_and_verify_svn): Remove the caveat comment stating that exit-code checks are skipped for some platforms. Pass binary_mode=0 to run_and_verify_svn2. (run_and_verify_svn2): Remove the caveat comment stating that exit-code checks are skipped for some platforms. Add a binary_mode boolean parameter. Replace call to run_svn with run_svn2. * subversion/tests/cmdline/import_tests.py (import_eol_style): Call run_and_verify_svn2 with binary_mode=1. * subversion/tests/cmdline/cat_tests.py, subversion/tests/cmdline/lock_tests.py, subversion/tests/cmdline/stat_tests.py: Pass binary_mode parameter to run_and_verify_svn2. ]]] Index: subversion/tests/cmdline/svntest/main.py =================================================================== --- subversion/tests/cmdline/svntest/main.py (revision 30475) +++ subversion/tests/cmdline/svntest/main.py (working copy) @@ -25,6 +25,7 @@ import traceback # for print_exc() import threading import Queue +from subprocess import Popen, PIPE import getopt try: @@ -96,12 +97,6 @@ _exe = '' _bat = '' -try: - from popen2 import Popen3 - platform_with_popen3_class = True -except ImportError: - platform_with_popen3_class = False - # The location of our mock svneditor script. if sys.platform == 'win32': svneditor_script = os.path.join(sys.path[0], 'svneditor.bat') @@ -318,39 +313,19 @@ # system: _safe_arg_re = re.compile(r'^[A-Za-z\d\.\_\/\-\:\@]+$') -def _quote_arg(arg): - """Quote ARG for a command line. +def open_pipe(command, binary_mode): + """Opens a subcommand pipe to COMMAND. If BINARY_MODE is true, linebreak + style will be preserved in output. - Simply surround every argument in double-quotes unless it contains - only universally harmless characters. - - WARNING: This function cannot handle arbitrary command-line - arguments. It can easily be confused by shell metacharacters. A - perfect job would be difficult and OS-dependent (see, for example, - http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp). - In other words, this function is just good enough for what we need - here.""" - - arg = str(arg) - if _safe_arg_re.match(arg): - return arg - else: - if os.name != 'nt': - arg = arg.replace('$', '\$') - return '"%s"' % (arg,) - -def open_pipe(command, mode): - """Opens a popen3 pipe to COMMAND in MODE. - Returns (infile, outfile, errfile, waiter); waiter should be passed to wait_on_pipe.""" - if platform_with_popen3_class: - kid = Popen3(command, True) - return kid.tochild, kid.fromchild, kid.childerr, (kid, command) - else: - inf, outf, errf = os.popen3(command, mode) - return inf, outf, errf, None + # (use universal newlines for text mode) + kid = Popen(command, shell=False, bufsize=-1, + universal_newlines=(not binary_mode), + stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) + return kid.stdin, kid.stdout, kid.stderr, (kid, command) + def wait_on_pipe(waiter, stdout_lines, stderr_lines): """Waits for KID (opened with open_pipe) to finish, dying if it does. Uses STDOUT_LINES and STDERR_LINES for error message @@ -360,9 +335,9 @@ kid, command = waiter - wait_code = kid.wait() + exit_code = kid.wait() - if os.WIFSIGNALED(wait_code): + if exit_code < 0: exit_signal = os.WTERMSIG(wait_code) if stdout_lines is not None: sys.stdout.write("".join(stdout_lines)) @@ -374,26 +349,21 @@ % (command, exit_signal)) raise SVNProcessTerminatedBySignal else: - exit_code = os.WEXITSTATUS(wait_code) if exit_code and verbose_mode: - sys.stderr.write("CMD: %s exited with %d\n" % (command, exit_code)) + sys.stderr.write("CMD: %s exited with %d\n" % + (' '.join(command), exit_code)) return exit_code # Run any binary, supplying input text, logging the command line -def spawn_process(command, binary_mode=0,stdin_lines=None, *varargs): - args = ' '.join(map(_quote_arg, varargs)) +def spawn_process(command, binary_mode=0, stdin_lines=None, *varargs): + cmd_argv = [command] + map(str, varargs) # Log the command line if verbose_mode and not command.endswith('.py'): - print 'CMD:', os.path.basename(command) + ' ' + args, + print 'CMD:', os.path.basename(command) + ' ' + ' '.join(cmd_argv[1:]), - if binary_mode: - mode = 'b' - else: - mode = 't' + infile, outfile, errfile, kid = open_pipe(cmd_argv, binary_mode) - infile, outfile, errfile, kid = open_pipe(command + ' ' + args, mode) - if stdin_lines: map(infile.write, stdin_lines) @@ -495,6 +465,12 @@ return run_command(svn_binary, error_expected, 0, *(_with_auth(_with_config_dir(varargs)))) +def run_svn2(error_expected, binary_mode, *varargs): + """Line run_svn, but allows user-specified enabling of binary mode for + output capture.""" + return run_command(svn_binary, error_expected, binary_mode, + *(_with_auth(_with_config_dir(varargs)))) + # For running svnadmin. Ignores the output. def run_svnadmin(*varargs): """Run svnadmin with VARARGS, returns exit code as int; stdout, stderr as @@ -633,20 +609,21 @@ # Do an svnadmin dump|svnadmin load cycle. Print a fake pipe command so that # the displayed CMDs can be run by hand create_repos(dst_path) - dump_args = ' dump "' + src_path + '"' - load_args = ' load "' + dst_path + '"' + dump_argv = [svnadmin_binary, 'dump', src_path] + load_argv = [svnadmin_binary, 'load', dst_path] if ignore_uuid: - load_args = load_args + " --ignore-uuid" + load_args = load_argv.append('--ignore-uuid') if verbose_mode: - print 'CMD:', os.path.basename(svnadmin_binary) + dump_args, \ - '|', os.path.basename(svnadmin_binary) + load_args, + print 'CMD:', os.path.basename(svnadmin_binary), \ + ' '.join(dump_argv[1:]), '|', os.path.basename(svnadmin_binary), \ + ' '.join(load_argv[1:]), + start = time.time() - dump_in, dump_out, dump_err, dump_kid = \ - open_pipe(svnadmin_binary + dump_args, 'b') - load_in, load_out, load_err, load_kid = \ - open_pipe(svnadmin_binary + load_args, 'b') + dump_in, dump_out, dump_err, dump_kid = open_pipe(dump_argv, True) + load_in, load_out, load_err, load_kid = open_pipe(load_argv, True) + stop = time.time() if verbose_mode: print '