diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/Actions/function.py | 56 | ||||
-rw-r--r-- | test/Configure/build-fail.py | 90 | ||||
-rw-r--r-- | test/Configure/implicit-cache.py | 103 | ||||
-rw-r--r-- | test/KeyboardInterrupt.py | 46 | ||||
-rw-r--r-- | test/Value.py | 18 | ||||
-rw-r--r-- | test/explain/basic.py | 10 |
6 files changed, 294 insertions, 29 deletions
diff --git a/test/Actions/function.py b/test/Actions/function.py index 5e755a8..4fc0dc4 100644 --- a/test/Actions/function.py +++ b/test/Actions/function.py @@ -60,7 +60,33 @@ optEnv = Environment(options=options, tools=[]) r = re.compile(optEnv['regexp']) -toto = \ +withClosure = \ +r''' +def toto(header='%(header)s', trailer='%(trailer)s'): + xxx = %(closure_cell_value)s + def writeDeps(target, source, env, b=%(b)s, r=r %(extraarg)s , + header=header, trailer=trailer): + """+'"""%(docstring)s"""'+""" + def foo(b=b): + return %(nestedfuncexp)s + f = open(str(target[0]),'wb') + f.write(header) + for d in env['ENVDEPS']: + f.write(d+'%(separator)s') + f.write(trailer+'\\n') + f.write(str(foo())+'\\n') + f.write(r.match('aaaa').group(1)+'\\n') + %(extracode)s + try: + f.write(str(xarg)+'\\n') + except NameError: + pass + f.close() + + return writeDeps +''' + +NoClosure = \ r''' def toto(header='%(header)s', trailer='%(trailer)s'): xxx = %(closure_cell_value)s @@ -86,7 +112,17 @@ def toto(header='%(header)s', trailer='%(trailer)s'): return writeDeps ''' -exec( toto % optEnv ) +try: + # Check that lexical closure are supported + def a(): + x = 0 + def b(): + return x + return b + a().func_closure[0].cell_contents + exec( withClosure % optEnv ) +except (AttributeError, TypeError): + exec( NoClosure % optEnv ) genHeaderBld = SCons.Builder.Builder( action = SCons.Action.Action( @@ -122,15 +158,25 @@ scons: `.' is up to date. scons: done building targets. """ -def runtest( arguments, expectedOutFile, expectedRebuild=True): +import sys +if sys.version[:3] == '2.1': + expectedStderr = """\ +%s:79: SyntaxWarning: local name 'x' in 'a' shadows use of 'x' as global in nested scope 'b' + def a(): +""" % test.workpath('SConstruct') +else: + expectedStderr = "" + +def runtest(arguments, expectedOutFile, expectedRebuild=True, stderr=expectedStderr): test.run(arguments=arguments, - stdout=expectedRebuild and rebuildstr or nobuildstr) + stdout=expectedRebuild and rebuildstr or nobuildstr, + stderr=expectedStderr) test.must_match('Out.gen.h', expectedOutFile) # Should not be rebuild when ran a second time with the same # arguments. - test.run(arguments = arguments, stdout=nobuildstr) + test.run(arguments = arguments, stdout=nobuildstr, stderr=expectedStderr) test.must_match('Out.gen.h', expectedOutFile) diff --git a/test/Configure/build-fail.py b/test/Configure/build-fail.py new file mode 100644 index 0000000..2facb81 --- /dev/null +++ b/test/Configure/build-fail.py @@ -0,0 +1,90 @@ +#!/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__" + +""" +Verify that Configure tests work even after an earlier test fails. + +This was broken in 0.98.3 because we'd mark the /usr/bin/g++ compiler +as having failed (because it was on the candidates list as the implicit +command dependency for both the object file and executable generated +for the configuration test) and then avoid trying to rebuild anything +else that used the "failed" Node. + +Thanks to Ben Webb for the test case. +""" + +import os + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.subdir('a', 'b') + +a_boost_hpp = os.path.join('..', 'a', 'boost.hpp') +b_boost_hpp = os.path.join('..', 'b', 'boost.hpp') + +test.write('SConstruct', """\ +import os +def _check(context): + for dir in ['a', 'b']: + inc = os.path.join('..', dir, 'boost.hpp') + result = context.TryRun(''' + #include "%s" + + int main() { return 0; } + ''' % inc, '.cpp')[0] + if result: + import sys + sys.stdout.write('%s: ' % inc) + break + context.Result(result) + return result +env = Environment() +conf = env.Configure(custom_tests={'CheckBoost':_check}) +conf.CheckBoost() +conf.Finish() +""") + +test.write(['b', 'boost.hpp'], """#define FILE "b/boost.hpp"\n""") + +expect = test.wrap_stdout(read_str = "%s: yes\n" % b_boost_hpp, + build_str = "scons: `.' is up to date.\n") + +test.run(arguments='--config=force', stdout=expect) + +expect = test.wrap_stdout(read_str = "%s: yes\n" % a_boost_hpp, + build_str = "scons: `.' is up to date.\n") + +test.write(['a', 'boost.hpp'], """#define FILE "a/boost.hpp"\n""") + +test.run(arguments='--config=force', stdout=expect) + +test.run() + +test.pass_test() diff --git a/test/Configure/implicit-cache.py b/test/Configure/implicit-cache.py new file mode 100644 index 0000000..58ae7c9 --- /dev/null +++ b/test/Configure/implicit-cache.py @@ -0,0 +1,103 @@ +#!/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__" + +""" +Verify that use of --implicit-cache with the Python Value Nodes +used by the Configure subsystem generate the same .sconsign file +and don't cause it to grow without limit. + +This was reported as issue 2033 in the tigris.org bug tracker, by the +Ardour project. Prior to 0.98.4, the Value implementation would actually +return the repr() of its value as the str(). This was done because +it made saving a Value in a file and reading it back in kind of work, +because a print a string Value into a file (for example) would in fact +put quotes around it and be assignable in that file. + +The problem is that this would get stored in a .sconsign file as its +repr(), with the specific problem being that Values with embedded newlines +would get stored as strings containing backslash+n digraphs *and* the +quotes at beginning and end of the string:: + + '\n#include <math.h>\n\n': {<.sconsign info>} + +Then, when we read that back in from the .sconsign file, we would store +that repr() as a string Value itself, escaping the backslashes and +including the quotes, so when we stored it the second time it would end +up looking like: + + "'\\n#include <math.h>\\n\\n'": {<.sconsign info>} + +Every time that we would read this value and store it again (because +something else changed in the .sconf_temp directory), the string would +get longer and longer until it blew out the users's memory. +""" + +import TestSConsign + +test = TestSConsign.TestSConsign() + +test.write('SConstruct', """ +env = Environment(CPPPATH=['.']) +conf = Configure(env) +conf.CheckHeader( 'math.h' ) +if ARGUMENTS.get('USE_FOO'): + conf.CheckHeader( 'foo.h' ) +env = conf.Finish() +""") + +test.write('foo.h', "#define FOO 1\n") + +# First run: Have the configure subsystem only look for math.h, and +# squirrel away the .sconsign info for the conftest_0.c file that's +# generated from the Python Value Node that we're using for our test. + +test.run(arguments = '.') + +test.run_sconsign('-d .sconf_temp -e conftest_0.c --raw .sconsign.dblite') +old_sconsign_dblite = test.stdout() + +# Second run: Have the configure subsystem also look for foo.h, so +# that there's a change in the .sconf_temp directory that will cause its +# .sconsign information to get rewritten from disk. Squirrel away the +# .sconsign info for the conftest_0.c file. The now-fixed bug would show +# up because the entry would change with the additional string-escaping +# described above. The now-correct behavior is that the re-stored value +# for conftest_0.c doesn't change. + +test.run(arguments = '--implicit-cache USE_FOO=1 .') + +test.run_sconsign('-d .sconf_temp -e conftest_0.c --raw .sconsign.dblite') +new_sconsign_dblite = test.stdout() + +if old_sconsign_dblite != new_sconsign_dblite: + print ".sconsign.dblite did not match:" + print "FIRST RUN ==========" + print old_sconsign_dblite + print "SECOND RUN ==========" + print new_sconsign_dblite + test.fail_test() + +test.pass_test() diff --git a/test/KeyboardInterrupt.py b/test/KeyboardInterrupt.py index 25239b5..5a06119 100644 --- a/test/KeyboardInterrupt.py +++ b/test/KeyboardInterrupt.py @@ -28,15 +28,10 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Verify that we handle keyboard interrupts (CTRL-C) correctly. """ -import os - import TestSCons test = TestSCons.TestSCons() -if 'killpg' not in dir(os) or 'setpgrp' not in dir(os): - test.skip_test("This Python version does not support killing process groups; skipping test.\n") - test.write('toto.c', r""" void foo() {} @@ -44,20 +39,47 @@ void foo() test.write('SConstruct', r""" import os +import sys import signal -# Make sure that SCons is a process group leader. -os.setpgrp() +if 'killpg' not in dir(os) or 'setpgrp' not in dir(os) or sys.platform == 'cygwin': + # The platform does not support process group. Therefore, we + # directly invoked the SIGINT handler to simulate a + # KeyboardInterrupt. This hack is necessary because there is no + # easy way to get access to the current Job/Taskmaster object. + # + # Note that this way of performing the test is not as good as + # using killpg because the Taskmaster is stopped synchronously. In + # addition, the SCons subprocesses (or forked children before the + # exec() of the subprocess) are never killed. This therefore + # exercise less SCons functionality. + # + # FIXME: There seems to be a bug on Cygwin where the compiler + # process hangs after sending the SIGINT signal to the process + # group. It is probably a bug in cygwin1.dll, or maybe in the + # Python 'C' code or the Python subprocess module. We therefore do + # not use 'killpg' on Cygwin. + def explode(env, target, source): + handler = signal.getsignal(signal.SIGINT) + handler(signal.SIGINT, None) + return 0 +else: + # The platform does support process group so we use killpg to send + # a SIGINT to everyone. + + # Make sure that SCons is a process group leader. + os.setpgrp() + + def explode(env, target, source): + os.killpg(0, signal.SIGINT) -all = [] -def explode(env, target, source): - os.killpg(0, signal.SIGINT) +all = [] for i in xrange(40): - all += Object('toto%5d' % i, 'toto.c') + all.extend(Object('toto%5d' % i, 'toto.c')) -all+= Command( 'broken', 'toto.c', explode) +all.extend(Command( 'broken', 'toto.c', explode)) Default( Alias('all', all)) """ diff --git a/test/Value.py b/test/Value.py index 72d499e..01647e3 100644 --- a/test/Value.py +++ b/test/Value.py @@ -91,12 +91,12 @@ for source_signature in ['MD5', 'timestamp-newer']: test.run(arguments='-c') test.run() - out7 = """create_value(["'my value'"], ["f3.out"])""" - out8 = """create_value_file(["f5.out"], ["'my value'"])""" + out7 = """create_value(['my value'], ["f3.out"])""" + out8 = """create_value_file(["f5.out"], ['my value'])""" - out1 = """create(["f1.out"], ["'/usr/local'"])""" - out2 = """create(["f2.out"], ["10"])""" - out3 = """create\\(\\["f3.out"\\], \\["<.*.Custom instance at """ + out1 = """create(["f1.out"], ['/usr/local'])""" + out2 = """create(["f2.out"], [10])""" + out3 = """create\\(\\["f3.out"\\], \\[<.*.Custom instance at """ #" <- unconfuses emacs syntax highlighting test.fail_test(string.find(test.stdout(), out1) == -1) @@ -114,9 +114,9 @@ for source_signature in ['MD5', 'timestamp-newer']: test.up_to_date(arguments='.') test.run(arguments='prefix=/usr') - out4 = """create(["f1.out"], ["'/usr'"])""" - out5 = """create(["f2.out"], ["4"])""" - out6 = """create\\(\\["f3.out"\\], \\["<.*.Custom instance at """ + out4 = """create(["f1.out"], ['/usr'])""" + out5 = """create(["f2.out"], [4])""" + out6 = """create\\(\\["f3.out"\\], \\[<.*.Custom instance at """ #" <- unconfuses emacs syntax highlighting test.fail_test(string.find(test.stdout(), out4) == -1) test.fail_test(string.find(test.stdout(), out5) == -1) @@ -132,7 +132,7 @@ for source_signature in ['MD5', 'timestamp-newer']: test.unlink('f3.out') test.run(arguments='prefix=/var') - out4 = """create(["f1.out"], ["'/var'"])""" + out4 = """create(["f1.out"], ['/var'])""" test.fail_test(string.find(test.stdout(), out4) == -1) test.fail_test(string.find(test.stdout(), out5) != -1) diff --git a/test/explain/basic.py b/test/explain/basic.py index e1e3aec..e1198d5 100644 --- a/test/explain/basic.py +++ b/test/explain/basic.py @@ -39,9 +39,10 @@ test = TestSCons.TestSCons() test.subdir(['src'], ['src', 'subdir']) -subdir_file8 = os.path.join('subdir', 'file8') subdir_file7 = os.path.join('subdir', 'file7') subdir_file7_in = os.path.join('subdir', 'file7.in') +subdir_file8 = os.path.join('subdir', 'file8') +subdir_file9 = os.path.join('subdir', 'file9') cat_py = test.workpath('cat.py') inc_aaa = test.workpath('inc', 'aaa') @@ -122,6 +123,7 @@ file6 = env.Cat('file6', 'file6.in') AlwaysBuild(file6) env.Cat('subdir/file7', 'subdir/file7.in') env.OneCat('subdir/file8', ['subdir/file7.in', env.Value(%(test_value)s)] ) +env.OneCat('subdir/file9', ['subdir/file7.in', env.Value(7)] ) """ % valueDict ) test_value = '"first"' @@ -195,6 +197,8 @@ scons: building `%(subdir_file7)s' because it doesn't exist %(_python_)s %(cat_py)s %(subdir_file7)s %(subdir_file7_in)s scons: building `%(subdir_file8)s' because it doesn't exist %(_python_)s %(cat_py)s %(subdir_file8)s %(subdir_file7_in)s +scons: building `%(subdir_file9)s' because it doesn't exist +%(_python_)s %(cat_py)s %(subdir_file9)s %(subdir_file7_in)s """ % locals()) test.run(chdir='src', arguments=args, stdout=expect) @@ -248,8 +252,8 @@ scons: rebuilding `file5' because `%(inc_bbb_k)s' changed scons: rebuilding `file6' because AlwaysBuild() is specified %(_python_)s %(cat_py)s file6 file6.in scons: rebuilding `%(subdir_file8)s' because: - `"'first'"' is no longer a dependency - `'second'' is a new dependency + `first' is no longer a dependency + `second' is a new dependency %(_python_)s %(cat_py)s %(subdir_file8)s %(subdir_file7_in)s """ % locals()) |