Update test infrastructure and ae2cvs utility to latest. Fix runtest.py's swallowing...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 17 Aug 2005 19:00:39 +0000 (19:00 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 17 Aug 2005 19:00:39 +0000 (19:00 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1336 fdb21ef1-2011-0410-befe-b5e4ea1792b1

SConstruct
bin/ae2cvs
etc/TestCmd.py
etc/TestCommon.py
runtest.py
test/Fortran/F90PATH.py
test/Options.py
test/Options/PackageOption.py
test/Options/PathOption.py
test/Perforce/Perforce.py
test/RCS/diskcheck.py

index be557ef5a8da51a74b5d78273b8bfb38d35038b2..c4c99b354c34bb7a174eac6519f9e6d678596655 100644 (file)
@@ -247,6 +247,10 @@ def SCons_revision(target, source, env):
 revbuilder = Builder(action = Action(SCons_revision,
                                      varlist=['COPYRIGHT', 'VERSION']))
 
+# When copying local files from a Repository (Aegis),
+# just make copies, don't symlink them.
+SetOption('duplicate', 'copy')
+
 env = Environment(
                    ENV                 = ENV,
 
index fd40b414ad9c57c221925b26d1fa956f88184470..e7cb22b2fe76d7d783ae02b8dbb90b4b179a89d3 100644 (file)
@@ -1,12 +1,14 @@
 #! /usr/bin/env perl
+
+$revision = "src/ae2cvs.pl 0.04.D001 2005/08/14 15:13:36 knight";
+
+$copyright = "Copyright 2001, 2002, 2003, 2004, 2005 Steven Knight.";
+
 #
-# Copyright 2001, 2002, 2003 Steven Knight.  All rights reserved.  This program
-# is free software; you can redistribute and/or modify under the
-# same terms as Perl itself.
+# All rights reserved.  This program is free software; you can
+# redistribute and/or modify under the same terms as Perl itself.
 #
 
-$revision = "src/ae2cvs.pl 0.D002 2001/10/03 09:36:49 software";
-
 use strict;
 use File::Find;
 use File::Spec;
@@ -15,15 +17,24 @@ use Pod::Usage ();
 use vars qw( @add_list @args @cleanup @copy_list @libraries
             @mkdir_list @remove_list
             %seen_dir
-            $ae_copy $aedir $aedist $cnum $commit $common $cvsmod
+            $ae_copy $aedir $aedist
+            $cnum $comment $commit $common $copyright
+            $cvs_command $cvsmod $cvsroot
             $delta $description $exec $help $indent $infile
-            $proj $pwd $quiet
+            $proj $pwd $quiet $revision
             $summary $usedir $usepath );
 
 $aedist = 1;
+$cvsroot = undef;
 $exec = undef;
 $indent = "";
 
+sub version {
+       print "ae2cvs: $revision\n";
+       print "$copyright\n";
+       exit 0;
+}
+
 {
     use Getopt::Long;
 
@@ -33,6 +44,7 @@ $indent = "";
        "aedist" => sub { $aedist = 1 },
        "aegis" => sub { $aedist = 0 },
        "change=i" => \$cnum,
+       "d=s" => \$cvsroot,
        "file=s" => \$infile,
        "help|?" => \$help,
        "library=s" => \@libraries,
@@ -41,6 +53,7 @@ $indent = "";
        "project=s" => \$proj,
        "quiet" => \$quiet,
        "usedir=s" => \$usedir,
+       "v|version" => \&version,
        "x|execute" => sub { $exec++ if ! defined $exec || $exec != 0 },
        "X|EXECUTE" => sub { $exec = 2 if ! defined $exec || $exec != 0 },
     );
@@ -50,6 +63,8 @@ $indent = "";
     $exec = 0 if ! defined $exec;
 }
 
+$cvs_command = $cvsroot ? "cvs -d $cvsroot -Q" : "cvs -Q";
+
 #
 # Wrap up the $quiet logic in one place.
 #
@@ -115,6 +130,59 @@ sub filter {
     return $output;
 }
 
+#
+# Parse a change description, in both 'aegis -l cd" and "aedist" formats.
+#
+# Returns an array containing the project name, the change number
+# (if any), the delta number (if any), the SUMMARY, the DESCRIPTION
+# and the lines describing the files in the change.
+#
+sub parse_change {
+    my $output = shift;
+
+    my ($p, $c, $d, $c_or_d, $sum, $desc, $filesection, @flines);
+
+    # The project name line comes after NAME in "aegis -l cd" format,
+    # and PROJECT in "aedist" format.  In both cases, the project name
+    # and the change/delta name are separated a comma.
+    ($p = $output) =~ s/(?:NAME|PROJECT)\n([^\n]*)\n.*/$1/ms;
+    ($p, $c_or_d) = (split(/,/, $p));
+
+    # In "aegis -l cd" format, the project name actually comes after
+    # the string "Project" and is itself enclosed in double quotes.
+    $p =~ s/Project "([^"]*)"/$1/;
+
+    # The change or delta string was the right-hand side of the comma.
+    # "aegis -l cd" format spells it "Change 123." or "Delta 123." while
+    # "aedist" format spells it "change 123."
+    if ($c_or_d =~ /\s*[Cc]hange (\d+).*/) { $c = $1 };
+    if ($c_or_d =~ /\s*[Dd]elta (\d+).*/) { $d = $1 };
+
+    # The SUMMARY line is always followed the DESCRIPTION section.
+    # It seems to always be a single line, but we grab everything in
+    # between just in case.
+    ($sum = $output) =~ s/.*\nSUMMARY\n//ms;
+    $sum =~ s/\nDESCRIPTION\n.*//ms;
+
+    # The DESCRIPTION section is followed ARCHITECTURE in "aegis -l cd"
+    # format and by CAUSE in "aedist" format.  Explicitly under it if the
+    # string is only "none," which means they didn't supply a description.
+    ($desc = $output) =~ s/.*\nDESCRIPTION\n//ms;
+    $desc =~ s/\n(ARCHITECTURE|CAUSE)\n.*//ms;
+    chomp($desc);
+    if ($desc eq "none" || $desc eq "none\n") { $desc = undef }
+
+    # The FILES section is followed by HISTORY in "aegis -l cd" format.
+    # It seems to be the last section in "aedist" format, but stripping
+    # a non-existent HISTORY section doesn't hurt.
+    ($filesection = $output) =~ s/.*\nFILES\n//ms;
+    $filesection =~ s/\nHISTORY\n.*//ms;
+
+    @flines = split(/\n/, $filesection);
+
+    ($p, $c, $d, $sum, $desc, \@flines)
+}
+
 #
 #
 #
@@ -144,15 +212,12 @@ if ($aedist) {
     }
 
     my $output = filter("aedist -l -unf", $contents);
+    my ($p, $c, $d, $s, $desc, $fl) = parse_change($output);
 
-    my $filesection;
-    if (! defined $proj) {
-       ($proj = $output) =~ s/PROJECT\n([^\n]*)\n.*/$1/ms;
-    }
-    ($summary = $output) =~ s/.*\nSUMMARY\n([^\n]*)\n.*/$1/ms;
-    ($description = $output) =~ s/.*\nDESCRIPTION\n([^\n]*)\nCAUSE\n.*/$1/ms;
-    ($filesection = $output) =~ s/.*\nFILES\n//ms;
-    @filelines = split(/\n/, $filesection);
+    $proj = $p if ! defined $proj;
+    $summary = $s;
+    $description = $desc;
+    @filelines = @$fl;
 
     if (! $exec) {
        printit qq(MYTMP="/tmp/ae2cvs-ae.\$\$"\n),
@@ -181,9 +246,10 @@ if ($aedist) {
     }
 
     $ae_copy = sub {
-       my $dest = shift;
-       my $source = File::Spec->catfile($aedir, "src", $dest);
-       execute(qq(cp $source $dest));
+       foreach my $dest (@_) {
+           my $source = File::Spec->catfile($aedir, "src", $dest);
+           execute(qq(cp $source $dest));
+       }
     }
 } else {
     $cnum = $ENV{AEGIS_CHANGE} if ! defined $cnum;
@@ -192,24 +258,21 @@ if ($aedist) {
     $common = "-lib " . join(" -lib ", @libraries) if @libraries;
     $common = "$common -proj $proj" if $proj;
 
-    foreach (`aegis -l ph -unf $common`) {
-       chomp;
-       if (/^(\d+) .{24} $cnum\s*(.*)/) {
-           $delta = $1;
-           $summary = $2;
-           last;
-       }
-    }
+    my $output = `aegis -l cd $cnum -unf $common`;
+    my ($p, $c, $d, $s, $desc, $fl) = parse_change($output);
+
+    $delta = $d;
+    $summary = $s;
+    $description = $desc;
+    @filelines = @$fl;
+
     if (! $delta) {
-       print STDERR "ae2cvs:  No change $cnum for project $proj.\n";
-       exit 1;
+        print STDERR "ae2cvs:  No delta number, exiting.\n";
+        exit 1;
     }
 
-    @filelines = `aegis -l cf -unf -c $cnum $common`;
-
     $ae_copy = sub {
-       my $file = shift;
-       execute(qq(aegis -cp -ind -delta $delta $common $file));
+       execute(qq(aegis -cp -ind -delta $delta $common @_));
     }
 }
 
@@ -229,7 +292,7 @@ if (! File::Spec->file_name_is_absolute($usepath)) {
 if (! -d File::Spec->catfile($usedir, "CVS")) {
     $cvsmod = (split(/\./, $proj))[0] if ! defined $cvsmod;
 
-    execute(qq(cvs -Q co $cvsmod));
+    execute(qq($cvs_command co $cvsmod));
 
     _chdir($cvsmod);
 
@@ -288,7 +351,7 @@ if (@mkdir_list) {
            $indent = "  ";
        }
        _mkdir($_);
-       execute(qq(cvs -Q add $_));
+       execute(qq($cvs_command add $_));
        if (! $exec) {
            $indent = "";
            printit qq(fi\n);
@@ -300,21 +363,21 @@ if (@mkdir_list) {
 }
 
 # Copy in any files in the change, before we try to "cvs add" them.
-for (@copy_list) {
-    $ae_copy->($_);
-}
+$ae_copy->(@copy_list) if @copy_list;
 
 if (@add_list) {
-    execute(qq(cvs -Q add @add_list));
+    execute(qq($cvs_command add @add_list));
 }
 
 if (@remove_list) {
     execute(qq(rm -f @remove_list));
-    execute(qq(cvs -Q remove @remove_list));
+    execute(qq($cvs_command remove @remove_list));
 }
 
 # Last, commit the whole bunch.
-$commit = qq(cvs -Q commit -m "$summary" .);
+$comment = $summary;
+$comment .= "\n" . $description if $description;
+$commit = qq($cvs_command commit -m '$comment' .);
 if ($exec == 1) {
     printit qq(# Execute the following to commit the changes:\n),
            qq(# $commit\n);
@@ -352,12 +415,13 @@ ae2cvs - convert an Aegis change set to CVS commands
 
 =head1 SYNOPSIS
 
-ae2cvs [-aedist|-aegis] [-c change] [-f file] [-l lib]
-       [-m module] [-n] [-p proj] [-q] [-u dir] [-x] [-X]
+ae2cvs [-aedist|-aegis] [-c change] [-d cvs_root] [-f file] [-l lib]
+       [-m module] [-n] [-p proj] [-q] [-u dir] [-v] [-x] [-X]
 
        -aedist         use aedist format from input (default)
        -aegis          query aegis repository directly
        -c change       change number
+       -d cvs_root     CVS root directory
        -f file         read aedist from file ('-' == stdin)
        -l lib          Aegis library directory
        -m module       CVS module
@@ -365,6 +429,7 @@ ae2cvs [-aedist|-aegis] [-c change] [-f file] [-l lib]
        -p proj         project name
        -q              quiet, don't print commands
        -u dir          use dir for CVS checkin
+       -v              print version string and exit
        -x              execute the commands, but don't commit;
                        two or more -x options commit changes
        -X              execute the commands and commit changes
@@ -408,6 +473,14 @@ Specify the Aegis change number to be used.
 The value of the C<AEGIS_CHANGE> environment variable
 is used by default.
 
+=item -d cvsroot
+
+Specify the CVS root directory to be used.
+This option is passed explicitly to each executed C<cvs> command.
+The default behavior is to omit any C<-d> options
+and let the executed C<cvs> commands use the
+C<CVSROOT> environment variable as they normally would.
+
 =item -f file
 
 Reads the aedist change set from the specified C<file>,
@@ -447,6 +520,10 @@ Use the already checked-out CVS tree that exists at C<dir>
 for the checkins and commits.
 The default is to use a separately-created temporary directory.
 
+=item -v
+
+Print the version string and exit.
+
 =item -x
 
 Execute the commands to bring the CVS repository up to date,
@@ -489,16 +566,13 @@ the time, though, so this needs more investigation.
 
 =head1 TODO
 
-Add support for the CVS -d option to allow use of a specified
-CVS repository.
-
 Add an explicit test for using ae2cvs in the Aegis
 integrate_pass_notify_command field to support fully keeping a
 repository in sync automatically.
 
 =head1 COPYRIGHT
 
-Copyright 2001, 2002, 2003 Steven Knight.
+Copyright 2001, 2002, 2003, 2004, 2005 Steven Knight.
 
 =head1 SEE ALSO
 
index e12aa4c53a3d5ae08785402b6211c0497ba85d64..79ec8dc4d2c2796bc0b0f0682b6647bf74042aad 100644 (file)
@@ -175,8 +175,8 @@ version.
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
 __author__ = "Steven Knight <knight at baldmt dot com>"
-__revision__ = "TestCmd.py 0.13.D002 2004/11/20 08:34:16 knight"
-__version__ = "0.13"
+__revision__ = "TestCmd.py 0.15.D001 2005/08/16 17:14:33 knight"
+__version__ = "0.15"
 
 import os
 import os.path
@@ -241,14 +241,21 @@ _Cleanup = []
 
 def _clean():
     global _Cleanup
-    list = _Cleanup[:]
-    _Cleanup = []
-    list.reverse()
-    for test in list:
+    cleanlist = filter(None, _Cleanup)
+    del _Cleanup[:]
+    cleanlist.reverse()
+    for test in cleanlist:
         test.cleanup()
 
 sys.exitfunc = _clean
 
+class Collector:
+    def __init__(self, top):
+        self.entries = [top]
+    def __call__(self, arg, dirname, names):
+        pathjoin = lambda n, d=dirname: os.path.join(d, n)
+        self.entries.extend(map(pathjoin, names))
+
 def _caller(tblist, skip):
     string = ""
     arr = []
@@ -417,7 +424,7 @@ else:
             if os.path.isfile(f):
                 try:
                     st = os.stat(f)
-                except:
+                except OSError:
                     continue
                 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
                     return f
@@ -634,25 +641,27 @@ class TestCmd:
             if self.verbose:
                 sys.stderr.write("chdir(" + chdir + ")\n")
             os.chdir(chdir)
-        cmd = None
         if program:
             if not os.path.isabs(program):
                 program = os.path.join(self._cwd, program)
-            cmd = escape_cmd(program)
-            if interpreter:
-                cmd = interpreter + " " + cmd
         else:
-            cmd = escape_cmd(self.program)
-            if self.interpreter:
-                cmd =  self.interpreter + " " + cmd
+            program = self.program
+            if not interpreter:
+                interpreter = self.interpreter
+        cmd = [program]
+        if interpreter:
+            cmd = [interpreter] + cmd
         if arguments:
-            cmd = cmd + " " + arguments
+            if type(arguments) == type(''):
+                arguments = string.split(arguments)
+            cmd.extend(arguments)
+        cmd_string = string.join(cmd, ' ')
         if self.verbose:
-            sys.stderr.write(cmd + "\n")
+            sys.stderr.write(cmd_string + "\n")
         try:
             p = popen2.Popen3(cmd, 1)
         except AttributeError:
-            (tochild, fromchild, childerr) = os.popen3(cmd)
+            (tochild, fromchild, childerr) = os.popen3(cmd_string)
             if stdin:
                 if is_List(stdin):
                     for line in stdin:
@@ -748,7 +757,7 @@ class TestCmd:
             new = os.path.join(self.workdir, sub)
             try:
                 os.mkdir(new)
-            except:
+            except OSError:
                 pass
             else:
                 count = count + 1
@@ -837,37 +846,124 @@ class TestCmd:
         """
         return apply(os.path.join, (self.workdir,) + tuple(args))
 
-    def writable(self, top, write):
+    def readable(self, top, read=1):
+        """Make the specified directory tree readable (read == 1)
+        or not (read == None).
+        """
+
+        if read:
+            def do_chmod(fname):
+                try: st = os.stat(fname)
+                except OSError: pass
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0400))
+        else:
+            def do_chmod(fname):
+                try: st = os.stat(fname)
+                except OSError: pass
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0400))
+
+        if os.path.isfile(top):
+            # If it's a file, that's easy, just chmod it.
+            do_chmod(top)
+        elif read:
+            # It's a directory and we're trying to turn on read
+            # permission, so it's also pretty easy, just chmod the
+            # directory and then chmod every entry on our walk down the
+            # tree.  Because os.path.walk() is top-down, we'll enable
+            # read permission on any directories that have it disabled
+            # before os.path.walk() tries to list their contents.
+            do_chmod(top)
+
+            def chmod_entries(arg, dirname, names, do_chmod=do_chmod):
+                pathnames = map(lambda n, d=dirname: os.path.join(d, n),
+                                names)
+                map(lambda p, do=do_chmod: do(p), pathnames)
+
+            os.path.walk(top, chmod_entries, None)
+        else:
+            # It's a directory and we're trying to turn off read
+            # permission, which means we have to chmod the directoreis
+            # in the tree bottom-up, lest disabling read permission from
+            # the top down get in the way of being able to get at lower
+            # parts of the tree.  But os.path.walk() visits things top
+            # down, so we just use an object to collect a list of all
+            # of the entries in the tree, reverse the list, and then
+            # chmod the reversed (bottom-up) list.
+            col = Collector(top)
+            os.path.walk(top, col, None)
+            col.entries.reverse()
+            map(lambda d, do=do_chmod: do(d), col.entries)
+
+    def writable(self, top, write=1):
         """Make the specified directory tree writable (write == 1)
         or not (write == None).
         """
 
-        def _walk_chmod(arg, dirname, names):
-            st = os.stat(dirname)
-            os.chmod(dirname, arg(st[stat.ST_MODE]))
-            for name in names:
-                n = os.path.join(dirname, name)
-                st = os.stat(n)
-                os.chmod(n, arg(st[stat.ST_MODE]))
+        if write:
+            def do_chmod(fname):
+                try: st = os.stat(fname)
+                except OSError: pass
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0200))
+        else:
+            def do_chmod(fname):
+                try: st = os.stat(fname)
+                except OSError: pass
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0200))
 
-        def _mode_writable(mode):
-            return stat.S_IMODE(mode|0200)
+        if os.path.isfile(top):
+            do_chmod(top)
+        else:
+            col = Collector(top)
+            os.path.walk(top, col, None)
+            map(lambda d, do=do_chmod: do(d), col.entries)
 
-        def _mode_non_writable(mode):
-            return stat.S_IMODE(mode&~0200)
+    def executable(self, top, execute=1):
+        """Make the specified directory tree executable (execute == 1)
+        or not (execute == None).
+        """
 
-        if write:
-            f = _mode_writable
+        if execute:
+            def do_chmod(fname):
+                try: st = os.stat(fname)
+                except OSError: pass
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0100))
         else:
-            f = _mode_non_writable
+            def do_chmod(fname):
+                try: st = os.stat(fname)
+                except OSError: pass
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0100))
+
         if os.path.isfile(top):
-            st = os.stat(top)
-            os.chmod(top, f(st[stat.ST_MODE]))
+            # If it's a file, that's easy, just chmod it.
+            do_chmod(top)
+        elif execute:
+            # It's a directory and we're trying to turn on execute
+            # permission, so it's also pretty easy, just chmod the
+            # directory and then chmod every entry on our walk down the
+            # tree.  Because os.path.walk() is top-down, we'll enable
+            # execute permission on any directories that have it disabled
+            # before os.path.walk() tries to list their contents.
+            do_chmod(top)
+
+            def chmod_entries(arg, dirname, names, do_chmod=do_chmod):
+                pathnames = map(lambda n, d=dirname: os.path.join(d, n),
+                                names)
+                map(lambda p, do=do_chmod: do(p), pathnames)
+
+            os.path.walk(top, chmod_entries, None)
         else:
-            try:
-                os.path.walk(top, _walk_chmod, f)
-            except:
-                pass # ignore any problems changing modes
+            # It's a directory and we're trying to turn off execute
+            # permission, which means we have to chmod the directories
+            # in the tree bottom-up, lest disabling execute permission from
+            # the top down get in the way of being able to get at lower
+            # parts of the tree.  But os.path.walk() visits things top
+            # down, so we just use an object to collect a list of all
+            # of the entries in the tree, reverse the list, and then
+            # chmod the reversed (bottom-up) list.
+            col = Collector(top)
+            os.path.walk(top, col, None)
+            col.entries.reverse()
+            map(lambda d, do=do_chmod: do(d), col.entries)
 
     def write(self, file, content, mode = 'wb'):
         """Writes the specified content text (second argument) to the
index ae57f0cff2dac338c5520e92671ca2c93520638f..af3c8a833df6aed59ba32c749e46c0db0ea83d6d 100644 (file)
@@ -80,8 +80,8 @@ The TestCommon module also provides the following variables
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
 __author__ = "Steven Knight <knight at baldmt dot com>"
-__revision__ = "TestCommon.py 0.13.D001 2004/11/20 08:30:40 knight"
-__version__ = "0.13"
+__revision__ = "TestCommon.py 0.14.D001 2005/08/15 23:02:35 knight"
+__version__ = "0.14"
 
 import os
 import os.path
@@ -262,6 +262,8 @@ class TestCommon(TestCmd):
         file_contents = self.read(file, mode)
         try:
             self.fail_test(not self.match(file_contents, expect))
+        except KeyboardInterrupt:
+            raise
         except:
             print "Unexpected contents of `%s'" % file
             print "EXPECTED contents ======"
@@ -338,6 +340,8 @@ class TestCommon(TestCmd):
             match = self.match
         try:
             apply(TestCmd.run, [self], kw)
+        except KeyboardInterrupt:
+            raise
         except:
             print "STDOUT ============"
             print self.stdout()
index 7f364793fe28ec00889cfeced798afa9f5123832..b409ce559626f0a2792b8e6fd643125e749b2aa1 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# __COPYRIGHT__
+# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation
 #
 # runtest.py - wrapper script for running SCons tests
 #
@@ -206,6 +206,31 @@ else:
 
 sp.append(cwd)
 
+#
+_ws = re.compile('\s')
+
+def escape(s):
+    if _ws.search(s):
+        s = '"' + s + '"'
+    return s
+
+# Set up lowest-common-denominator spawning of a process on both Windows
+# and non-Windows systems that works all the way back to Python 1.5.2.
+try:
+    os.spawnv
+except AttributeError:
+    def spawn_it(command_args):
+        pid = os.fork()
+        if pid == 0:
+            os.execv(command_args[0], command_args)
+        else:
+            pid, status = os.waitpid(pid, 0)
+            return status >> 8
+else:
+    def spawn_it(command_args):
+       command_args = map(escape, command_args)
+        return os.spawnv(os.P_WAIT, command_args[0], command_args)
+
 class Base:
     def __init__(self, path, spe=None):
         self.path = path
@@ -220,9 +245,7 @@ class Base:
 
 class SystemExecutor(Base):
     def execute(self):
-        s = os.system(self.command)
-        if s >= 256:
-            s = s / 256
+        s = spawn_it(self.command_args)
         self.status = s
         if s < 0 or s > 2:
             sys.stdout.write("Unexpected exit status %d\n" % s)
@@ -232,7 +255,7 @@ try:
 except AttributeError:
     class PopenExecutor(Base):
         def execute(self):
-            (tochild, fromchild, childerr) = os.popen3(self.command)
+            (tochild, fromchild, childerr) = os.popen3(self.command_str)
             tochild.close()
             self.stdout = fromchild.read()
             self.stderr = childerr.read()
@@ -243,7 +266,7 @@ except AttributeError:
 else:
     class PopenExecutor(Base):
         def execute(self):
-            p = popen2.Popen3(self.command, 1)
+            p = popen2.Popen3(self.command_str, 1)
             p.tochild.close()
             self.stdout = p.fromchild.read()
             self.stderr = p.childerr.read()
@@ -264,7 +287,7 @@ class XML(PopenExecutor):
     def write(self, f):
         f.write('    <test>\n')
         f.write('      <file_name>%s</file_name>\n' % self.path)
-        f.write('      <command_line>%s</command_line>\n' % self.command)
+        f.write('      <command_line>%s</command_line>\n' % self.command_str)
         f.write('      <exit_status>%s</exit_status>\n' % self.status)
         f.write('      <stdout>%s</stdout>\n' % self.stdout)
         f.write('      <stderr>%s</stderr>\n' % self.stderr)
@@ -468,17 +491,14 @@ class Unbuffered:
 
 sys.stdout = Unbuffered(sys.stdout)
 
-_ws = re.compile('\s')
-
-def escape(s):
-    if _ws.search(s):
-        s = '"' + s + '"'
-    return s
-
 for t in tests:
-    t.command = string.join(map(escape, [python, debug, t.abspath]), " ")
+    t.command_args = [python]
+    if debug:
+        t.command_args.append(debug)
+    t.command_args.append(t.abspath)
+    t.command_str = string.join(map(escape, t.command_args), " ")
     if printcommand:
-        sys.stdout.write(t.command + "\n")
+        sys.stdout.write(t.command_str + "\n")
     t.execute()
 
 passed = filter(lambda t: t.status == 0, tests)
index 87ddda135a3e6c675b3c18a03f49a7b10c4a481a..f1fac46936e2009378ecfc2599394bba90e2489c 100644 (file)
@@ -26,6 +26,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import os
 import os.path
+import string
 import sys
 import TestSCons
 
index ecd9aa4ef3055187777b26f3ee6dcb125798451c..bd827d1eb9186fcba65e629cba63cca3138005a5 100644 (file)
@@ -114,19 +114,19 @@ def check(expect):
 test.run()
 check(['0', '1', cc, string.strip(ccflags + ' -g'), 'v', 'v'])
 
-test.run(arguments='"RELEASE_BUILD=1"')
+test.run(arguments='RELEASE_BUILD=1')
 check(['1', '1', cc, string.strip(ccflags + ' -O -g'), 'v', 'v'])
 
-test.run(arguments='"RELEASE_BUILD=1" "DEBUG_BUILD=0"')
+test.run(arguments='RELEASE_BUILD=1 DEBUG_BUILD=0')
 check(['1', '0', cc, string.strip(ccflags + ' -O'), 'v', 'v'])
 
-test.run(arguments='"CC=not_a_c_compiler"')
+test.run(arguments='CC=not_a_c_compiler')
 check(['0', '1', 'not_a_c_compiler', string.strip(ccflags + ' -g'), 'v', 'v'])
 
-test.run(arguments='"UNDECLARED=foo"')
+test.run(arguments='UNDECLARED=foo')
 check(['0', '1', cc, string.strip(ccflags + ' -g'), 'v', 'v'])
 
-test.run(arguments='"CCFLAGS=--taco"')
+test.run(arguments='CCFLAGS=--taco')
 check(['0', '1', cc, string.strip(ccflags + ' -g'), 'v', 'v'])
 
 test.write('custom.py', """
@@ -137,7 +137,7 @@ RELEASE_BUILD=1
 test.run()
 check(['1', '0', cc, string.strip(ccflags + ' -O'), 'v', 'v'])
 
-test.run(arguments='"DEBUG_BUILD=1"')
+test.run(arguments='DEBUG_BUILD=1')
 check(['1', '1', cc, string.strip(ccflags + ' -O -g'), 'v', 'v'])
 
 test.run(arguments='-h',
@@ -217,7 +217,7 @@ check(['1','0'])
 checkSave('options.saved', { 'RELEASE_BUILD':1, 'DEBUG_BUILD':0})
 
 # Override with command line arguments
-test.run(arguments='"DEBUG_BUILD=3"')
+test.run(arguments='DEBUG_BUILD=3')
 check(['1','3'])
 checkSave('options.saved', {'RELEASE_BUILD':1, 'DEBUG_BUILD':3})
 
@@ -265,12 +265,12 @@ check(['0','1'])
 checkSave('options.saved', {})
 
 # Now specify one option the same as default and make sure it doesn't write out
-test.run(arguments='"DEBUG_BUILD=1"')
+test.run(arguments='DEBUG_BUILD=1')
 check(['0','1'])
 checkSave('options.saved', {})
 
 # Now specify same option non-default and make sure only it is written out
-test.run(arguments='"DEBUG_BUILD=0" "LISTOPTION_TEST=a,b"')
+test.run(arguments='DEBUG_BUILD=0 LISTOPTION_TEST=a,b')
 check(['0','0'])
 checkSave('options.saved',{'DEBUG_BUILD':0, 'LISTOPTION_TEST':'a,b'})
 
index ca8944d2b9b1384f5deb51d8c788ebb81d5b415a..cc520f78654cc4f341dd1362496e82cfc7a78cb5 100644 (file)
@@ -62,7 +62,7 @@ test.run()
 check(['1'])
 test.run(arguments='x11=no'); check(['0'])
 test.run(arguments='x11=0'); check(['0'])
-test.run(arguments='"x11=%s"' % test.workpath()); check([test.workpath()])
+test.run(arguments=['x11=%s' % test.workpath()]); check([test.workpath()])
 
 test.run(arguments='x11=/non/existing/path/',
          stderr = """
index 55f95c53c138bf0b80e001579bc08ff0ca556d97..d15f39bc57709b4718b002136133aa0322472f4e 100644 (file)
@@ -74,27 +74,27 @@ check([qtpath, os.path.join('$qtdir', 'lib'), libpath])
 
 qtpath = os.path.join(workpath, 'qt')
 libpath = os.path.join(qtpath, 'lib')
-test.run(arguments='"qtdir=%s"' % qtpath)
+test.run(arguments=['qtdir=%s' % qtpath])
 check([qtpath, os.path.join('$qtdir', 'lib'), libpath])
 
 qtpath = workpath
 libpath = os.path.join(qtpath, 'nolib')
-test.run(arguments='"qt_libraries=%s"' % libpath)
+test.run(arguments=['qt_libraries=%s' % libpath])
 check([qtpath, libpath, libpath])
 
 qtpath = os.path.join(workpath, 'qt')
 libpath = os.path.join(workpath, 'nolib')
-test.run(arguments='"qtdir=%s" "qt_libraries=%s"' % (qtpath, libpath))
+test.run(arguments=['qtdir=%s' % qtpath, 'qt_libraries=%s' % libpath])
 check([qtpath, libpath, libpath])
 
 qtpath = os.path.join(workpath, 'non', 'existing', 'path')
-test.run(arguments='"qtdir=%s"' % qtpath,
+test.run(arguments=['qtdir=%s' % qtpath],
          stderr = """
 scons: *** Path for option qtdir does not exist: %s
 File "SConstruct", line 12, in ?
 """ % qtpath, status=2)
 
-test.run(arguments='"qt_libraries=%s"' % qtpath,
+test.run(arguments=['qt_libraries=%s' % qtpath],
          stderr = """
 scons: *** Path for option qt_libraries does not exist: %s
 File "SConstruct", line 12, in ?
@@ -132,16 +132,16 @@ Default(env.Alias('dummy', None))
 test.run()
 check([default_subdir])
 
-test.run(arguments='"X=%s"' % existing_file)
+test.run(arguments=['X=%s' % existing_file])
 check([existing_file])
 
-test.run(arguments='"X=%s"' % non_existing_file)
+test.run(arguments=['X=%s' % non_existing_file])
 check([non_existing_file])
 
-test.run(arguments='"X=%s"' % existing_subdir)
+test.run(arguments=['X=%s' % existing_subdir])
 check([existing_subdir])
 
-test.run(arguments='"X=%s"' % non_existing_subdir)
+test.run(arguments=['X=%s' % non_existing_subdir])
 check([non_existing_subdir])
 
 test.must_not_exist(non_existing_file)
@@ -173,17 +173,17 @@ test.write(default_file, "default_file\n")
 test.run()
 check([default_file])
 
-test.run(arguments='"X=%s"' % existing_subdir,
+test.run(arguments=['X=%s' % existing_subdir],
          status=2,
          stderr="""
 scons: *** File path for option X is a directory: %s
 File "SConstruct", line 6, in ?
 """ % existing_subdir)
 
-test.run(arguments='"X=%s"' % existing_file)
+test.run(arguments=['X=%s' % existing_file])
 check([existing_file])
 
-test.run(arguments='"X=%s"' % non_existing_file,
+test.run(arguments=['X=%s' % non_existing_file],
          status=2,
          stderr="""
 scons: *** File path for option X does not exist: %s
@@ -216,17 +216,17 @@ test.subdir(default_subdir)
 test.run()
 check([default_subdir])
 
-test.run(arguments='"X=%s"' % existing_file,
+test.run(arguments=['X=%s' % existing_file],
          status=2,
          stderr="""
 scons: *** Directory path for option X is a file: %s
 File "SConstruct", line 6, in ?
 """ % existing_file)
 
-test.run(arguments='"X=%s"' % existing_subdir)
+test.run(arguments=['X=%s' % existing_subdir])
 check([existing_subdir])
 
-test.run(arguments='"X=%s"' % non_existing_subdir,
+test.run(arguments=['X=%s' % non_existing_subdir],
          status=2,
          stderr="""
 scons: *** Directory path for option X does not exist: %s
@@ -251,17 +251,17 @@ Default(env.Alias('dummy', None))
 test.run()
 check([default_subdir])
 
-test.run(arguments='"X=%s"' % existing_file,
+test.run(arguments=['X=%s' % existing_file],
          status=2,
          stderr="""
 scons: *** Path for option X is a file, not a directory: %s
 File "SConstruct", line 6, in ?
 """ % existing_file)
 
-test.run(arguments='"X=%s"' % existing_subdir)
+test.run(arguments=['X=%s' % existing_subdir])
 check([existing_subdir])
 
-test.run(arguments='"X=%s"' % non_existing_subdir)
+test.run(arguments=['X=%s' % non_existing_subdir])
 check([non_existing_subdir])
 
 test.must_exist(non_existing_subdir)
index 891acc3c570f3aa4840882d96f08457dfa8fb394..5d41f8f0807d1c3b1cd1f539c6dd75c4ec1d0ea4 100644 (file)
@@ -59,7 +59,8 @@ except TestSCons.TestFailed:
     pass # it's okay if this fails...it will fail if the depot is clear already.
 
 # Set up a perforce depot for testing.
-test.write("depotspec","""# A Perforce Depot Specification.
+depotspec = """\
+# A Perforce Depot Specification.
 Depot: testme
 
 Owner: %s
@@ -74,13 +75,14 @@ Type:       local
 Address:       subdir
 
 Map:   testme/...
-""" % user)
+""" % user
 
-test.run(program=p4, arguments='-p 1666 depot -i < depotspec')
+test.run(program=p4, arguments='-p 1666 depot -i', stdin = depotspec)
 
 # Now set up 2 clients, one to check in some files, and one to
 # do the building.
-clientspec = """# A Perforce Client Specification.
+clientspec = """\
+# A Perforce Client Specification.
 Client:        %s
 
 Owner: %s
@@ -104,13 +106,11 @@ clientspec1 = clientspec % ("testclient1", user, host, test.workpath('import'),
                             "//testme/foo/...", "testclient1")
 clientspec2 = clientspec % ("testclient2", user, host, test.workpath('work'),
                             "//testme/...", "testclient2")
-test.write("testclient1", clientspec1)
-test.write("testclient2", clientspec2)
 
 test.subdir('import', ['import', 'sub'], 'work')
 
-test.run(program=p4, arguments = '-p 1666 client -i < testclient1')
-test.run(program=p4, arguments = '-p 1666 client -i < testclient2')
+test.run(program=p4, arguments = '-p 1666 client -i', stdin=clientspec1)
+test.run(program=p4, arguments = '-p 1666 client -i', stdin=clientspec2)
 
 test.write(['import', 'aaa.in'], "import/aaa.in\n")
 test.write(['import', 'bbb.in'], "import/bbb.in\n")
@@ -130,11 +130,13 @@ test.write(['import', 'sub', 'fff.in'], "import/sub/fff.in\n")
 
 # Perforce uses the PWD environment variable in preference to the actual cwd
 os.environ["PWD"] = test.workpath('import')
-paths = map(os.path.normpath, [ 'sub/ddd.in', 'sub/eee.in', 'sub/fff.in', 'sub/SConscript' ])
-args = '-p 1666 -c testclient1 add -t binary *.in %s' % string.join(paths)
+paths = [ 'aaa.in', 'bbb.in', 'ccc.in',
+          'sub/ddd.in', 'sub/eee.in', 'sub/fff.in', 'sub/SConscript' ]
+paths = map(os.path.normpath, paths)
+args = '-p 1666 -c testclient1 add -t binary %s' % string.join(paths)
 test.run(program=p4, chdir='import', arguments=args)
 
-test.write('changespec', """
+changespec = """
 Change:        new
 
 Client:        testclient1
@@ -154,9 +156,11 @@ Files:
        //testme/foo/sub/ddd.in # add
        //testme/foo/sub/eee.in # add
        //testme/foo/sub/fff.in # add
-""" % user)
+""" % user
 
-test.run(program=p4, arguments='-p 1666 -c testclient1 submit -i < changespec')
+test.run(program=p4,
+         arguments='-p 1666 -c testclient1 submit -i',
+         stdin=changespec)
 
 test.write(['work', 'SConstruct'], """
 def cat(env, source, target):
index 4a93edf7c869704b955747a4ceb553dfa16a7b21..8af2545d13c2e349f24f45a46a487e3e426f4aac 100644 (file)
@@ -117,10 +117,10 @@ test.write(['sub', 'eee.in'], "checked-out sub/eee.in\n")
 
 expect = """\
 
-scons: warning: Ignoring missing SConscript 'sub/SConscript'
+scons: warning: Ignoring missing SConscript '%s'
 File "SConstruct", line 23, in ?
 scons: *** Source `aaa.in' not found, needed by target `aaa.out'.  Stop.
-"""
+""" % os.path.join('sub', 'SConscript')
 
 test.run(status=2, stderr=expect)