summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/Actions/function.py56
-rw-r--r--test/Configure/build-fail.py90
-rw-r--r--test/Configure/implicit-cache.py103
-rw-r--r--test/KeyboardInterrupt.py46
-rw-r--r--test/Value.py18
-rw-r--r--test/explain/basic.py10
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())