From 3c2dc0e033d1095417f3297f47c3cc42a0d747d1 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Mon, 10 Oct 2005 02:49:11 +0000 Subject: [PATCH] Fix on-disk file matching on case-insensitive systems. Various fixes for win32 portability. Refactor the --debug=time test. Refactor the Perforce test. Additional cleanup. git-svn-id: http://scons.tigris.org/svn/scons/trunk@1369 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- etc/TestCommon.py | 1 + src/engine/SCons/Node/FS.py | 4 +- src/engine/SCons/Node/FSTests.py | 4 + src/engine/SCons/Tool/msvs.py | 7 +- test/Configure/CONFIGURELOG.py | 6 +- test/Perforce/Perforce.py | 197 ++++++++++++++++++++----------- test/QT/up-to-date.py | 12 +- test/option--debug.py | 53 +-------- test/option/debug-time.py | 132 +++++++++++++++++++++ 9 files changed, 289 insertions(+), 127 deletions(-) create mode 100644 test/option/debug-time.py diff --git a/etc/TestCommon.py b/etc/TestCommon.py index af3c8a83..f2daf570 100644 --- a/etc/TestCommon.py +++ b/etc/TestCommon.py @@ -95,6 +95,7 @@ from TestCmd import * from TestCmd import __all__ __all__.extend([ 'TestCommon', + 'TestFailed', 'exe_suffix', 'obj_suffix', 'shobj_suffix', diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index ceec6965..810ede73 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -1432,10 +1432,10 @@ class Dir(Base): except OSError: pass else: - for entry in entries: + for entry in map(_my_normcase, entries): d[entry] = 1 self.on_disk_entries = d - return d.has_key(name) + return d.has_key(_my_normcase(name)) def srcdir_list(self): """__cacheable__""" diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 71fb3ef8..565384f7 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -1455,11 +1455,15 @@ class DirTestCase(_tempdirTestCase): test.subdir('d') test.write(['d', 'exists'], "d/exists\n") + test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n") d = self.fs.Dir('d') assert d.entry_exists_on_disk('exists') assert not d.entry_exists_on_disk('does_not_exist') + if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin": + assert d.entry_exists_on_disk('case-insensitive') + def test_srcdir_list(self): """Test the Dir.srcdir_list() method """ diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index 084f1d45..38c6dcb3 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -1395,6 +1395,8 @@ solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM', suffix = '$MSVSSOLUTIONSUFFIX', emitter = solutionEmitter) +default_MSVS_SConscript = None + def generate(env): """Add Builders and construction variables for Microsoft Visual Studio project files to an Environment.""" @@ -1416,7 +1418,10 @@ def generate(env): # shouldn't depend on anything in SCons.Script. env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript else: - env['MSVSSCONSCRIPT'] = env.File('SConstruct') + global default_MSVS_SConscript + if default_MSVS_SConscript is None: + default_MSVS_SConscript = env.File('SConstruct') + env['MSVSSCONSCRIPT'] = default_MSVS_SConscript env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, exec_script_main) env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}' diff --git a/test/Configure/CONFIGURELOG.py b/test/Configure/CONFIGURELOG.py index 3668cfaf..456fcc96 100644 --- a/test/Configure/CONFIGURELOG.py +++ b/test/Configure/CONFIGURELOG.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation +# __COPYRIGHT__ # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -22,7 +22,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "test/Configure.py 0.96.D308 2005/09/25 12:59:35 knight" +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Test that the configure context log file name can be specified by @@ -55,6 +55,6 @@ scons: Configure: (cached) yes """ -test.must_match('custom.logfile', expect) +test.must_match('custom.logfile', expect, mode='r') test.pass_test() diff --git a/test/Perforce/Perforce.py b/test/Perforce/Perforce.py index 5d41f8f0..d273cbbf 100644 --- a/test/Perforce/Perforce.py +++ b/test/Perforce/Perforce.py @@ -37,80 +37,141 @@ import string import TestSCons -test = TestSCons.TestSCons() - -p4 = test.where_is('p4') -if not p4: - test.skip_test("Could not find 'p4'; skipping test(s).\n") - -user = os.environ.get('USER') -if not user: - user = os.environ.get('USERNAME') -if not user: - user = os.environ.get('P4USER') - -host = socket.gethostname() - -# clean out everything -try: - test.run(program=p4, arguments='-p 1666 obliterate -y //testme/...') - test.run(program=p4, arguments='-p 1666 depot -d testme') -except TestSCons.TestFailed: - pass # it's okay if this fails...it will fail if the depot is clear already. +class TestPerforce(TestSCons.TestSCons): + def __init__(self, *args, **kw): + apply(TestSCons.TestSCons.__init__, (self,)+args, kw) + + self._p4prog = self.where_is('p4') + if not self._p4prog: + self.skip_test("Could not find 'p4'; skipping test(s).\n") + + self.host = socket.gethostname() + + self.user = os.environ.get('USER') + if not self.user: + self.user = os.environ.get('USERNAME') + if not self.user: + self.user = os.environ.get('P4USER') + + self.depot = 'testme' + + self.p4d = self.where_is('p4d') + if self.p4d: + self.p4portflags = ['-p', self.host + ':1777'] + self.subdir('depot', ['depot', 'testme']) + args = [self.p4d, '-q', '-d'] + \ + self.p4portflags + \ + ['-J', 'Journal', + '-L', 'Log', + '-r', self.workpath('depot')] + + # We don't use self.run() because the TestCmd logic will hang + # waiting for the daemon to exit, even when we pass it + # the -d option. + try: + spawnv = os.spawnv + except AttributeError: + os.system(string.join(args)) + else: + spawnv(os.P_NOWAIT, self.p4d, args) + self.sleep(2) + else: + self.p4portflags = ['-p', self.host + ':1666'] + try: + self.p4('obliterate -y //%s/...' % self.depot) + self.p4('depot -d %s' % self.depot) + except TestSCons.TestFailed: + # It's okay if this fails. It will fail if the depot + # is already clear. + pass + + self.portflag = string.join(self.p4portflags) + + def p4(self, *args, **kw): + try: + arguments = kw['arguments'] + except KeyError: + arguments = args[0] + args = args[1:] + kw['arguments'] = string.join(self.p4portflags + [arguments]) + kw['program'] = self._p4prog + return apply(self.run, args, kw) + + def substitute(self, s, **kw): + kw = kw.copy() + kw.update(self.__dict__) + return s % kw + + def cleanup(self, condition = None): + if self.p4d: + self.p4('admin stop') + self.p4d = None + + if TestSCons: + TestSCons.TestSCons.cleanup(self, condition) + +test = TestPerforce() # Set up a perforce depot for testing. -depotspec = """\ +depotspec = test.substitute("""\ # A Perforce Depot Specification. -Depot: testme +Depot: %(depot)s -Owner: %s +Owner: %(user)s -Date: 2003/02/19 17:21:41 +Date: 2003/02/19 17:21:41 Description: - A test depot. + A test depot. -Type: local +Type: local -Address: subdir +Address: subdir -Map: testme/... -""" % user +Map: %(depot)s/... +""") -test.run(program=p4, arguments='-p 1666 depot -i', stdin = depotspec) +test.p4(arguments='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. -Client: %s +Client: %(client)s -Owner: %s +Owner: %(user)s -Host: %s +Host: %(host)s Description: - Created by ccrain. + Created by %(user)s. -Root: %s +Root: %(root)s -Options: noallwrite noclobber nocompress unlocked nomodtime normdir +Options: noallwrite noclobber nocompress unlocked nomodtime normdir -LineEnd: local +LineEnd: local View: - %s //%s/... + //%(depot)s/%(subdir)s... //%(client)s/... """ -clientspec1 = clientspec % ("testclient1", user, host, test.workpath('import'), - "//testme/foo/...", "testclient1") -clientspec2 = clientspec % ("testclient2", user, host, test.workpath('work'), - "//testme/...", "testclient2") +clientspec1 = test.substitute(clientspec, + client = 'testclient1', + root = test.workpath('import'), + subdir = 'foo/', + ) + +clientspec2 = test.substitute(clientspec, + client = 'testclient2', + root = test.workpath('work'), + subdir = '', + ) test.subdir('import', ['import', 'sub'], 'work') -test.run(program=p4, arguments = '-p 1666 client -i', stdin=clientspec1) -test.run(program=p4, arguments = '-p 1666 client -i', stdin=clientspec2) +test.p4('client -i', stdin=clientspec1) +test.p4('client -i', stdin=clientspec2) test.write(['import', 'aaa.in'], "import/aaa.in\n") test.write(['import', 'bbb.in'], "import/bbb.in\n") @@ -133,36 +194,35 @@ os.environ["PWD"] = test.workpath('import') 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) +args = '-c testclient1 add -t binary %s' % string.join(paths) +test.p4(args, chdir='import') -changespec = """ -Change: new +changespec = test.substitute(""" +Change: new -Client: testclient1 +Client: testclient1 -User: %s +User: %(user)s -Status: new +Status: new Description: - A test check in + A test check in Files: - //testme/foo/aaa.in # add - //testme/foo/bbb.in # add - //testme/foo/ccc.in # add - //testme/foo/sub/SConscript # add - //testme/foo/sub/ddd.in # add - //testme/foo/sub/eee.in # add - //testme/foo/sub/fff.in # add -""" % user - -test.run(program=p4, - arguments='-p 1666 -c testclient1 submit -i', - stdin=changespec) - -test.write(['work', 'SConstruct'], """ + //%(depot)s/foo/aaa.in # add + //%(depot)s/foo/bbb.in # add + //%(depot)s/foo/ccc.in # add + //%(depot)s/foo/sub/SConscript # add + //%(depot)s/foo/sub/ddd.in # add + //%(depot)s/foo/sub/eee.in # add + //%(depot)s/foo/sub/fff.in # add +""") + +test.p4('-c testclient1 opened') +test.p4('-c testclient1 submit -i', stdin=changespec) + +SConstruct_contents = test.substitute(""" def cat(env, source, target): target = str(target[0]) source = map(str, source) @@ -171,7 +231,7 @@ def cat(env, source, target): f.write(open(src, "rb").read()) f.close() env = Environment(BUILDERS={'Cat':Builder(action=cat)}, - P4FLAGS='-p 1666 -c testclient2') + P4FLAGS='%(portflag)s -c testclient2') env.Cat('aaa.out', 'foo/aaa.in') env.Cat('bbb.out', 'foo/bbb.in') env.Cat('ccc.out', 'foo/ccc.in') @@ -180,6 +240,8 @@ env.SourceCode('.', env.Perforce()) SConscript('foo/sub/SConscript', 'env') """) +test.write(['work', 'SConstruct'], SConstruct_contents) + test.subdir(['work', 'foo']) test.write(['work', 'foo', 'bbb.in'], "work/foo/bbb.in\n") @@ -187,6 +249,7 @@ test.subdir(['work', 'foo', 'sub']) test.write(['work', 'foo', 'sub', 'eee.in'], "work/foo/sub/eee.in\n") test.run(chdir = 'work', arguments = '.') + test.fail_test(test.read(['work', 'all']) != "import/aaa.in\nwork/foo/bbb.in\nimport/ccc.in\n") test.fail_test(test.read(['work', 'foo', 'sub', 'all']) != "import/sub/ddd.in\nwork/foo/sub/eee.in\nimport/sub/fff.in\n") diff --git a/test/QT/up-to-date.py b/test/QT/up-to-date.py index 5ae7cc6b..46b9db80 100644 --- a/test/QT/up-to-date.py +++ b/test/QT/up-to-date.py @@ -33,10 +33,18 @@ up-to-date after a build. ca. September 2005.) """ +import os + import TestSCons +_obj = TestSCons._obj + test = TestSCons.TestSCons() +if not os.environ.get('QTDIR', None): + x ="External environment variable $QTDIR not set; skipping test(s).\n" + test.skip_test(x) + test.subdir('layer', ['layer', 'aclock'], ['layer', 'aclock', 'qt_bug']) @@ -121,10 +129,10 @@ test.write(['layer', 'aclock', 'qt_bug', 'my.cc'], """\ #include """) -test.run(arguments = 'layer/aclock/qt_bug/my.o', stderr=None) +test.run(arguments = 'layer/aclock/qt_bug/my'+_obj, stderr=None) test.up_to_date(options = '--debug=explain', - arguments = 'layer/aclock/qt_bug/my.o', + arguments = 'layer/aclock/qt_bug/my'+_obj, stderr=None) test.pass_test() diff --git a/test/option--debug.py b/test/option--debug.py index bc383e8d..ce0aed48 100644 --- a/test/option--debug.py +++ b/test/option--debug.py @@ -66,63 +66,12 @@ test.write('bar.h', """ """) ############################ -# test --debug=time +# test --debug=pdb test.run(arguments = "--debug=pdb", stdin = "n\ns\nq\n") test.fail_test(string.find(test.stdout(), "(Pdb)") == -1) test.fail_test(string.find(test.stdout(), "scons") == -1) -test.write('foo.c', r""" -#include "foo.h" - -int main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("f1.c\n"); - exit (0); -} -""") - -test.write('bar.c', """ -#include "bar.h" - -""") - -############################ -# test --debug=time - -def num(match, line): - return float(re.match(match, line).group(1)) - -start_time = time.time() -test.run(program=TestSCons.python, arguments='-c pass') -overhead = time.time() - start_time - -start_time = time.time() -test.run(arguments = "--debug=time .") -expected_total_time = time.time() - start_time - overhead -line = string.split(test.stdout(), '\n') - -cmdline = filter(lambda x: x[:23] == "Command execution time:", line) - -expected_command_time = num(r'Command execution time: (\d+\.\d+) seconds', cmdline[0]) -expected_command_time = expected_command_time + num(r'Command execution time: (\d+\.\d+) seconds', cmdline[1]) -expected_command_time = expected_command_time + num(r'Command execution time: (\d+\.\d+) seconds', cmdline[2]) - -totalline = filter(lambda x: x[:6] == "Total ", line) - -total_time = num(r'Total build time: (\d+\.\d+) seconds', totalline[0]) -sconscript_time = num(r'Total SConscript file execution time: (\d+\.\d+) seconds', totalline[1]) -scons_time = num(r'Total SCons execution time: (\d+\.\d+) seconds', totalline[2]) -command_time = num(r'Total command execution time: (\d+\.\d+) seconds', totalline[3]) - -def check(expected, actual, tolerance): - return abs((expected-actual)/actual) <= tolerance - -assert check(expected_command_time, command_time, 0.01) -assert check(total_time, sconscript_time+scons_time+command_time, 0.01) -assert check(total_time, expected_total_time, 0.1) - ############################ # test --debug=presub diff --git a/test/option/debug-time.py b/test/option/debug-time.py new file mode 100644 index 00000000..5766cff8 --- /dev/null +++ b/test/option/debug-time.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import TestSCons +import sys +import string +import re +import time + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx') +env.Program('foo', Split('foo.c bar.c')) +""") + +test.write('foo.c', r""" +#include "foo.h" +int main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("f1.c\n"); + exit (0); +} +""") + +test.write('bar.c', """ +#include "bar.h" +""") + +test.write('foo.h', """ +#ifndef FOO_H +#define FOO_H +#include "bar.h" +#endif +""") + +test.write('bar.h', """ +#ifndef BAR_H +#define BAR_H +#include "foo.h" +#endif +""") + +def num(match, line): + return float(re.search(match, line).group(1)) + +# Try to make things a little more equal by measuring Python overhead +# executing a minimal file, and reading the scons.py script itself from +# disk so that it's already been cached. +test.write('pass.py', "pass\n") +test.read(test.program) + +start_time = time.time() +test.run(program=TestSCons.python, arguments=test.workpath('pass.py')) +overhead = time.time() - start_time + +start_time = time.time() +test.run(arguments = "--debug=time .") +complete_time = time.time() - start_time + +expected_total_time = complete_time - overhead +lines = string.split(test.stdout(), '\n') + +expected_command_time = 0.0 +for cmdline in filter(lambda x: x[:23] == "Command execution time:", lines): + n = num(r'Command execution time: (\d+\.\d+) seconds', cmdline) + expected_command_time = expected_command_time + n + +stdout = test.stdout() + +total_time = num(r'Total build time: (\d+\.\d+) seconds', stdout) +sconscript_time = num(r'Total SConscript file execution time: (\d+\.\d+) seconds', stdout) +scons_time = num(r'Total SCons execution time: (\d+\.\d+) seconds', stdout) +command_time = num(r'Total command execution time: (\d+\.\d+) seconds', stdout) + +def within_tolerance(expected, actual, tolerance): + return abs((expected-actual)/actual) <= tolerance + +failures = [] + +if not within_tolerance(expected_command_time, command_time, 0.01): + failures.append("""\ +SCons reported a total command execution time of %s, +but command execution times really totalled %s, +outside of the 1%% tolerance. +""" % (command_time, expected_command_time)) + +added_times = sconscript_time+scons_time+command_time +if not within_tolerance(total_time, added_times, 0.01): + failures.append("""\ +SCons reported a total build time of %s, +but the various execution times actually totalled %s, +outside of the 1%% tolerance. +""" % (total_time, added_times)) + +if not within_tolerance(total_time, expected_total_time, 0.15): + failures.append("""\ +SCons reported total build time of %s, +but the actual measured build time was %s +(end-to-end time of %s less Python overhead of %s), +outside of the 15%% tolerance. +""" % (total_time, expected_total_time, complete_time, overhead)) + +if failures: + print string.join([test.stdout()] + failures, '\n') + test.fail_test(1) + +test.pass_test() -- 2.26.2