From c9fb8328ee7e5c5e2d1349613af6515bd3c980b5 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 17 Nov 2017 18:52:55 -0500 Subject: fix bad syntax --- src/engine/SCons/EnvironmentValues.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/SCons/EnvironmentValues.py b/src/engine/SCons/EnvironmentValues.py index e2efccb..d94bf3a 100644 --- a/src/engine/SCons/EnvironmentValues.py +++ b/src/engine/SCons/EnvironmentValues.py @@ -84,6 +84,7 @@ class EnvironmentValue(object): """ parts = [] for c in self.value: + pass -- cgit v0.12 From 7ef3bf28c05eaed128db86855183685e9d7bbd96 Mon Sep 17 00:00:00 2001 From: Gabriel Russell Date: Tue, 3 Oct 2017 12:50:09 -0400 Subject: update mtime on cache retrieval --- src/engine/SCons/CacheDir.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py index ac91c85..ab80808 100644 --- a/src/engine/SCons/CacheDir.py +++ b/src/engine/SCons/CacheDir.py @@ -56,6 +56,10 @@ def CacheRetrieveFunc(target, source, env): fs.symlink(fs.readlink(cachefile), t.get_internal_path()) else: env.copy_from_cache(cachefile, t.get_internal_path()) + try: + os.utime(cachefile, None) + except OSError: + pass st = fs.stat(cachefile) fs.chmod(t.get_internal_path(), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) return 0 -- cgit v0.12 From a2e35978f159dc4f131451213c2a8c78d4b5835c Mon Sep 17 00:00:00 2001 From: Gabriel Russell Date: Wed, 13 Dec 2017 17:03:00 -0500 Subject: test for readonly cache --- test/CacheDir/readonly-cache.py | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100755 test/CacheDir/readonly-cache.py diff --git a/test/CacheDir/readonly-cache.py b/test/CacheDir/readonly-cache.py new file mode 100755 index 0000000..05c46e6 --- /dev/null +++ b/test/CacheDir/readonly-cache.py @@ -0,0 +1,65 @@ +#!/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 accessing cache works even if it's read-only. +""" + +import os +import TestSCons + +test = TestSCons.TestSCons() + +test.write(['SConstruct'], """\ +CacheDir('cache') +Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) +""") + +test.write('file.in', "file.in\n") + +test.run(arguments = '--debug=explain --cache-debug=- .') + +test.unlink('file.out') + +test.run(arguments = '--debug=explain --cache-debug=- .') + +test.unlink('file.out') + +for root, dirs, files in os.walk("cache",topdown=False): + for file in files: + os.chmod(os.path.join(root,file),0o444) + for dir in dirs: + os.chmod(os.path.join(root,dir),0o555) + +test.run(arguments = '--debug=explain --cache-debug=- .') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From d77af43a417184ef00337ac86705bbba7e61f08f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 17 Jan 2018 23:35:26 -0500 Subject: add another packaging build dir to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4050468..1807b29 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ __pycache__/ build/** bootstrap/** .idea/ +src/build/** # Translations -- cgit v0.12 From e25d4155b872845af6df188efdf50a660f235572 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 17 Jan 2018 23:37:06 -0500 Subject: Added find_program_path() to SCons.Tool. Logic was in mingw. Use this to allow tools to search for normal SCOns paths, and then specify alternative default install paths for such tools. This should resolve clang failing on appveyor on windows --- src/engine/SCons/Tool/__init__.py | 29 +++++++++++++++++++++++++++++ src/engine/SCons/Tool/clang.py | 7 +++++++ src/engine/SCons/Tool/clangxx.py | 11 ++++++++++- src/engine/SCons/Tool/mingw.py | 31 +++++++------------------------ 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 42f84e1..0b340c0 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -1247,6 +1247,35 @@ def tool_list(platform, env): return [x for x in tools if x] + +def find_program_path(env, key_program, default_paths=[]): + """ + Find the location of key_program and then return the path it was located at. + Checking the default install locations. + Mainly for windows where tools aren't all installed in /usr/bin,etc + :param env: Current Environment() + :param key_program: Program we're using to locate the directory to add to PATH. + """ + # First search in the SCons path + path=env.WhereIs(key_program) + if (path): + return path + # then the OS path: + path=SCons.Util.WhereIs(key_program) + if (path): + return path + + # If that doesn't work try default location for mingw + save_path = env['ENV']['PATH'] + for p in default_paths: + env.AppendENVPath('PATH',p) + path = env.WhereIs(key_program) + if not path: + env['ENV']['PATH']=save_path + return path + + + # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index 177e6b2..a6bae43 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -52,6 +52,13 @@ def generate(env): """Add Builders and construction variables for clang to an Environment.""" SCons.Tool.cc.generate(env) + if env['PLATFORM'] == 'win32': + # Ensure that we have a proper path for clang + clang = SCons.Tool.find_program_path(env, compilers[0], default_paths=[r'C:\Program Files\LLVM\bin',]) + if clang: + clang_bin_dir = os.path.dirname(clang) + env.AppendENVPath('PATH', clang_bin_dir) + env['CC'] = env.Detect(compilers) or 'clang' if env['PLATFORM'] in ['cygwin', 'win32']: env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') diff --git a/src/engine/SCons/Tool/clangxx.py b/src/engine/SCons/Tool/clangxx.py index dd501af..63f1b0d 100644 --- a/src/engine/SCons/Tool/clangxx.py +++ b/src/engine/SCons/Tool/clangxx.py @@ -66,13 +66,22 @@ def generate(env): env['SHOBJSUFFIX'] = '.pic.o' elif env['PLATFORM'] == 'sunos': env['SHOBJSUFFIX'] = '.pic.o' + elif env['PLATFORM'] == 'win32': + # Ensure that we have a proper path for clang++ + clangxx = SCons.Tool.find_program_path(env, compilers[0], default_paths=[r'C:\Program Files\LLVM\bin',]) + if clangxx: + clangxx_bin_dir = os.path.dirname(clangxx) + env.AppendENVPath('PATH', clangxx_bin_dir) + # determine compiler version if env['CXX']: pipe = SCons.Action._subproc(env, [env['CXX'], '--version'], stdin='devnull', stderr='devnull', stdout=subprocess.PIPE) - if pipe.wait() != 0: return + if pipe.wait() != 0: + return + # clang -dumpversion is of no use line = pipe.stdout.readline() if sys.version_info[0] > 2: diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index 778db3c..4121902 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -42,26 +42,6 @@ import SCons.Defaults import SCons.Tool import SCons.Util -# This is what we search for to find mingw: -key_program = 'mingw32-gcc' - -def find(env): - # First search in the SCons path - path=env.WhereIs(key_program) - if (path): - return path - # then the OS path: - path=SCons.Util.WhereIs(key_program) - if (path): - return path - - # If that doesn't work try default location for mingw - save_path=env['ENV']['PATH'] - env.AppendENVPath('PATH',r'c:\MinGW\bin') - path =env.WhereIs(key_program) - if not path: - env['ENV']['PATH']=save_path - return path def shlib_generator(target, source, env, for_signature): cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) @@ -126,12 +106,15 @@ res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', source_scanner=SCons.Tool.SourceFileScanner) SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) +# This is what we search for to find mingw: +key_program = 'mingw32-gcc' + + def generate(env): - mingw = find(env) + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=[r'c:\MinGW\bin',]) if mingw: - dir = os.path.dirname(mingw) - env.PrependENVPath('PATH', dir ) - + mingw_bin_dir = os.path.dirname(mingw) + env.AppendENVPath('PATH', mingw_bin_dir) # Most of mingw is the same as gcc and friends... gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas', 'gfortran', 'm4'] -- cgit v0.12 From a4ca5012e89b136ffbc0d7fb201976551bf63227 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 17 Jan 2018 23:42:48 -0500 Subject: Add info on changes --- src/CHANGES.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 42b44d7..df25dac 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -22,6 +22,13 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Updated Jar builder to flatten source list which could contain embedded lists - Removed some magic numbers from jar.py on behalf of Mats Wichmann (mats@linux.com) + From William Deegan: + - Updated logic for mingw and clang on win32 to search default tool install paths if not + found in normal SCons PATH. If the user specifies PATH or tool specific paths they + will be used and the default paths below will be ignored. + - Default path for clang/clangxx : C:\Program Files\LLVM\bin + - Default path for mingw : c:\MinGW\bin + RELEASE 3.0.1 - Mon, 12 Nov 2017 15:31:33 -0700 From Daniel Moody: -- cgit v0.12 From 28d93e43689e9e91f1a648c522bf6fd11bb28f90 Mon Sep 17 00:00:00 2001 From: Gabriel Russell Date: Tue, 30 Jan 2018 14:45:41 -0500 Subject: test for mtime update --- test/CacheDir/readonly-cache.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/CacheDir/readonly-cache.py b/test/CacheDir/readonly-cache.py index 05c46e6..db3b3ec 100755 --- a/test/CacheDir/readonly-cache.py +++ b/test/CacheDir/readonly-cache.py @@ -28,8 +28,11 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Verify accessing cache works even if it's read-only. """ +import glob import os import TestSCons +import time +from stat import * test = TestSCons.TestSCons() @@ -42,10 +45,22 @@ test.write('file.in', "file.in\n") test.run(arguments = '--debug=explain --cache-debug=- .') +cachefile = glob.glob("cache/??/*")[0] + +time0 = os.stat(cachefile).st_mtime + +time.sleep(.1) + test.unlink('file.out') test.run(arguments = '--debug=explain --cache-debug=- .') +time1 = os.stat(cachefile).st_mtime + +# make sure that mtime has been updated on cache use +if time1 <= time0: + test.fail_test() + test.unlink('file.out') for root, dirs, files in os.walk("cache",topdown=False): -- cgit v0.12 From 0ec795c27d1311b0ef815165395a43dd85ec3eec Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 31 Jan 2018 14:12:08 -0800 Subject: Remove SCons/Options from Manifest for packaging. --- src/engine/MANIFEST.in | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 2df278e..3f883be 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -18,7 +18,6 @@ SCons/Node/__init__.py SCons/Node/Alias.py SCons/Node/FS.py SCons/Node/Python.py -SCons/Options/*.py SCons/PathList.py SCons/Platform/__init__.py SCons/Platform/aix.py -- cgit v0.12 From d9842caf097ffc90144a1f7683aa7b27ff955507 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 1 Feb 2018 09:02:24 -0800 Subject: Remove SCons.Options from setup.py --- src/setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/setup.py b/src/setup.py index 100e367..442315c 100644 --- a/src/setup.py +++ b/src/setup.py @@ -416,7 +416,6 @@ arguments = { 'packages': ["SCons", "SCons.compat", "SCons.Node", - "SCons.Options", "SCons.Platform", "SCons.Scanner", "SCons.Script", -- cgit v0.12 From 486e13a3760e52d3a877fcb720320a0115b9630d Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sat, 10 Feb 2018 17:39:57 -0500 Subject: pickle protocal was causing issues in some cases --- src/engine/SCons/compat/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py index 0b6c016..c053edf 100644 --- a/src/engine/SCons/compat/__init__.py +++ b/src/engine/SCons/compat/__init__.py @@ -98,7 +98,7 @@ import pickle # Was pickle.HIGHEST_PROTOCOL # Changed to 2 so py3.5+'s pickle will be compatible with py2.7. -PICKLE_PROTOCOL = 2 +PICKLE_PROTOCOL = -2 # TODO: FIXME # In 3.x, 'profile' automatically loads the fast version if available. -- cgit v0.12 From ad792d38e8f3cabf7da8aa990d1c29c6b79bb41f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 10 Feb 2018 19:30:55 -0800 Subject: Remove obsolete HOWTO for sub releases. --- HOWTO/subrelease.txt | 114 --------------------------------------------------- 1 file changed, 114 deletions(-) delete mode 100644 HOWTO/subrelease.txt diff --git a/HOWTO/subrelease.txt b/HOWTO/subrelease.txt deleted file mode 100644 index 06b757a..0000000 --- a/HOWTO/subrelease.txt +++ /dev/null @@ -1,114 +0,0 @@ -__COPYRIGHT__ - -This document covers how to prepare subreleases of SCons--that is, -releases numbered with X.Y.Z format, such as 0.94.1, 1.0.1, etc. - -If you need to prepare a release (X.Y, such as 0.93, 1.0, etc.), then -see the document HOWTO/release.txt. - -Things to do to release a new X.Y.Z version of SCons: - - BEFORE STARTING THE SUB-BRANCH: - - Update the user's guide on the parent - - sh bin/docdiff - - sh bin/docupdate - - START THE NEW SUB-BRANCH FOR SUBRELEASE - - aenbr -p scons.0.{94} {1} - - aenc -p scons.0.{94}.{1} - - Call it something like, - "Prepare a new sub-release for XYZ." - Cause = internal_enhancement. - Exempt it from all tests (*_exempt = true). - - ae_p scons.0.{94}.{1} - - aedb 100 - - aecd - - # Change the hard-coded package version numbers - # in the following files. - aecp README - vi README - - aecp SConstruct - vi SConstruct - - aecp QMTest/TestSCons.py - vi QMTest/TestSCons.py - - # Read through and update the README files if necessary - [optional] aecp README - [optional] vi README - - [optional] aecp src/README.txt - [optional] vi src/README.txt - - # Prepare src/CHANGES.txt - aecp src/CHANGES.txt - vi src/CHANGES.txt - - change the release line to reflect - the new subrelease - - date -R the new subrelease - - add an explanatory not after the subrelease line: - - NOTE: This is a pre-release of 0.{95} - for testing purposes. When 0.{95} is - released, all these changes will show - up as 0.95 changes. - - # Prepare src/RELEASE.txt - aecp src/RELEASE.txt - vi src/RELEASE.txt - - date -R the release only if necessary - - Read through and edit appropriately. - - Can probably keep most of the existing text - - Add any new known problems - - # Prepare debian/changelog - aecp debian/changelog - vi debian/changelog - - add the new subrelease - - date -R the new subrelease - - # Now build and prepare the release itself. - aeb - - aet -reg - - aed - - aede - - etc. - - - - Make the relevant packages available for by-hand pickup directly - off the web site: - - scp scons-0.{94}.{1}.tar.gz stevenknight@scons.sourceforge.net:/home/groups/s/sc/scons/htdocs - scp scons-0.{94}.{1}.zip stevenknight@scons.sourceforge.net:/home/groups/s/sc/scons/htdocs - - - Test downloading from the web site. - - - - Announce to scons-dev@scons.org -- cgit v0.12 From 014b0bf4a0ba87a3a7ec1ebce2f9ae81ae8e5d86 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sat, 10 Feb 2018 23:40:58 -0500 Subject: Updated CHANGES.txt --- src/CHANGES.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index d2587e3..de06e84 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,6 +7,10 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE + From Daniel Moody: + - Made a small change to scons main __init__.py to set the pickling protocal + to choose the latest which fixed 2 failing test varient dir test from Interactive + From Ray Donnelly: - Fix the PATH created by scons.bat (and other .bat files) to provide a normalized PATH. Some pythons in the 3.6 series are no longer able to handle paths which -- cgit v0.12 From 900b35ab28b344b6b5597f3fdefd34de5db10d69 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 29 Dec 2017 02:39:59 -0500 Subject: fix for issue 3042, Jar method was not respecting variant dirs when using os.path to detect files or dirs --- src/engine/SCons/Tool/jar.py | 22 +++++++++++++++------- test/Java/JAR.py | 7 ++++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index b31ccb9..cb7c994 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -189,13 +189,21 @@ def Jar(env, target = None, source = [], *args, **kw): # found a dir so get the class files out of it target_nodes.extend(dir_to_class(s)) else: - if os.path.isfile(s): - # found a file that exists on the FS, make sure its a class file - target_nodes.extend(file_to_class(s)) - elif os.path.isdir(s): - # found a dir so get the class files out of it - target_nodes.extend(dir_to_class(s)) - elif s[-len(java_suffix):] == java_suffix or s[-len(java_class_suffix):] == java_class_suffix: + try: + # source is string try to convert it to file + target_nodes.extend(file_to_class(env.fs.File(s))) + continue + except: + pass + + try: + # source is string try to covnert it to dir + target_nodes.extend(dir_to_class(env.fs.Dir(s))) + continue + except: + pass + + if s[-len(java_suffix):] == java_suffix or s[-len(java_class_suffix):] == java_class_suffix: # found a file that may not exists and is only a string # so add it after converting it to a class file target_nodes.extend(file_to_class(s)) diff --git a/test/Java/JAR.py b/test/Java/JAR.py index da2e72e..d62696c 100644 --- a/test/Java/JAR.py +++ b/test/Java/JAR.py @@ -337,6 +337,7 @@ test.must_exist(['testdir2', 'barTest', 'com', 'javasource', 'JavaFile3.class']) # make some directories to test in test.subdir('listOfLists', + ['manifest_dir'], ['listOfLists', 'src'], ['listOfLists', 'src', 'com'], ['listOfLists', 'src', 'com', 'javasource'], @@ -346,14 +347,14 @@ test.subdir('listOfLists', test.write(['listOfLists', 'SConstruct'], """ foo = Environment() foo.VariantDir('build', 'src', duplicate=0) +foo.VariantDir('test', '../manifest_dir', duplicate=0) sourceFiles = ["src/com/javasource/JavaFile1.java", "src/com/javasource/JavaFile2.java", "src/com/javasource/JavaFile3.java",] list_of_class_files = foo.Java('build', source=sourceFiles) resources = ['build/com/resource/resource1.txt', 'build/com/resource/resource2.txt'] for resource in resources: foo.Command(resource, list_of_class_files, Copy(resource, resource.replace('build','src'))) -foo.Command('build/MANIFEST.mf', list_of_class_files, Copy('build/MANIFEST.mf', 'MANIFEST.mf')) contents = [list_of_class_files, resources] -foo.Jar(target = 'lists', source = contents + ['build/MANIFEST.mf'], JARCHDIR='build') +foo.Jar(target = 'lists', source = contents + ['test/MANIFEST.mf'], JARCHDIR='build') foo.Command("listsTest", [], Mkdir("listsTest") ) foo.Command('listsTest/src/com/javasource/JavaFile3.java', 'lists.jar', foo['JAR'] + ' xvf ../lists.jar', chdir='listsTest') """) @@ -394,7 +395,7 @@ public class JavaFile3 } """) -test.write(['listOfLists', 'MANIFEST.mf'], +test.write(['manifest_dir','MANIFEST.mf'], """Manifest-Version: 1.0 MyManifestTest: Test """) -- cgit v0.12 From b7dda62fbfface49432fd05d9c7ff507a3b381b3 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sun, 11 Feb 2018 13:39:19 -0500 Subject: removed code that is not used and added warning for unusual case --- src/engine/SCons/Tool/jar.py | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index cb7c994..5e3711a 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -163,19 +163,6 @@ def Jar(env, target = None, source = [], *args, **kw): else: return dir_targets - # In the case that we are passed just string to a node which is directory - # but does not exist, we need to check all the current targets to see if - # that directory is going to exist so we can add it as a source to Jar builder - def get_all_targets(env, node='.'): - def get_all_targets_iter(env, node): - if node.has_builder(): - yield node - for kid in node.all_children(): - for kid in get_all_targets(env, kid): - yield kid - node = env.arg2nodes(node, env.fs.Entry)[0] - return list(get_all_targets_iter(env, node)) - # loop through the sources and handle each accordingly # the goal here is to get all the source files into a class # file or a directory that contains class files @@ -203,21 +190,8 @@ def Jar(env, target = None, source = [], *args, **kw): except: pass - if s[-len(java_suffix):] == java_suffix or s[-len(java_class_suffix):] == java_class_suffix: - # found a file that may not exists and is only a string - # so add it after converting it to a class file - target_nodes.extend(file_to_class(s)) - else: - # found a swig file so add it after converting it to class files - if(os.path.splitext(str(s))[1] == ".i"): - target_nodes.extend(env.JavaClassFile(source = s, *args, **kw)) - else: - # The final else case handles anything not caught above and makes - # sure other nodes that are sources for this jar get add as - # a source to the JarFile builder - for node in get_all_targets(env): - if(s == str(node)): - target_nodes.append(node) + SCons.Warnings.Warning("File: " + str(s) + " could not be identified as File or Directory, skipping.") + # at this point all our sources have been converted to classes or directories of class # so pass it to the Jar builder return env.JarFile(target = target, source = target_nodes, *args, **kw) -- cgit v0.12 From 3d65c6d31d19ab9d439413b89efa083c6d887542 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sun, 11 Feb 2018 17:20:36 -0500 Subject: forcing passing of python 3.5 and 3.6 tests for successful build in travis CI --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2b6049a..bdb3baa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,6 @@ install: # so allow failure so the coverage stage can be reached with python 2 matrix: allow_failures: - - python: 3.5 - - python: 3.6 - python: pypy -- cgit v0.12 From da0a0c9a2de08fcfb5b0a0b4df30d21c4c786045 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sun, 11 Feb 2018 17:22:41 -0500 Subject: updated comments no functional change --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bdb3baa..0e02b41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,7 @@ language: python install: - ./.travis/install.sh -# python 3 is not fulling passing at this time -# so allow failure so the coverage stage can be reached with python 2 +# pypy is not passing, but allow failures for coverage stage to be reached matrix: allow_failures: - python: pypy -- cgit v0.12 From 090107e5282cdbff7481a52467b9abcba5a9652d Mon Sep 17 00:00:00 2001 From: Jonathon Reinhart Date: Fri, 9 Feb 2018 23:49:36 -0500 Subject: Replace all instances of `int main()` with `int main(void)` --- bin/SConsExamples.py | 2 +- src/engine/SCons/Conftest.py | 20 ++++++++++---------- src/engine/SCons/SConfTests.py | 8 ++++---- src/engine/SCons/Scanner/CTests.py | 14 +++++++------- src/engine/SCons/Scanner/IDLTests.py | 6 +++--- test/CacheDir/up-to-date-q.py | 2 +- test/Case.py | 2 +- test/Configure/VariantDir-SConscript.py | 2 +- test/Configure/VariantDir.py | 2 +- test/Configure/VariantDir2.py | 2 +- test/Configure/basic.py | 2 +- test/Configure/build-fail.py | 2 +- test/Configure/clean.py | 2 +- test/Configure/custom-tests.py | 6 +++--- test/Configure/help.py | 2 +- test/FindSourceFiles.py | 2 +- test/Interactive/added-include.py | 4 ++-- test/Interactive/implicit-VariantDir.py | 4 ++-- test/LINK/VersionedLib-VariantDir.py | 2 +- test/LINK/VersionedLib-j2.py | 2 +- test/LINK/VersionedLib-subdir.py | 2 +- test/Libs/Library.py | 2 +- test/MSVC/pch-basics.py | 2 +- test/MSVC/pch-spaces-subdir.py | 2 +- test/MSVS/CPPPATH-Dirs.py | 2 +- test/QT/CPPPATH-appended.py | 2 +- test/QT/CPPPATH.py | 2 +- test/QT/QTFLAGS.py | 4 ++-- test/QT/empty-env.py | 2 +- test/QT/manual.py | 2 +- test/QT/moc-from-header.py | 2 +- test/QT/reentrant.py | 2 +- test/RPATH.py | 2 +- test/YACC/live.py | 4 ++-- 34 files changed, 60 insertions(+), 60 deletions(-) diff --git a/bin/SConsExamples.py b/bin/SConsExamples.py index 722e50a..a2ed570 100644 --- a/bin/SConsExamples.py +++ b/bin/SConsExamples.py @@ -39,7 +39,7 @@ # env.Program('foo') # # -# int main() { printf("foo.c\n"); } +# int main(void) { printf("foo.c\n"); } # # # diff --git a/src/engine/SCons/Conftest.py b/src/engine/SCons/Conftest.py index abaf00d..84aa992 100644 --- a/src/engine/SCons/Conftest.py +++ b/src/engine/SCons/Conftest.py @@ -136,7 +136,7 @@ def CheckBuilder(context, text = None, language = None): if not text: text = """ -int main() { +int main(void) { return 0; } """ @@ -157,7 +157,7 @@ def CheckCC(context): """ context.Display("Checking whether the C compiler works... ") text = """ -int main() +int main(void) { return 0; } @@ -177,7 +177,7 @@ def CheckSHCC(context): """ context.Display("Checking whether the (shared) C compiler works... ") text = """ -int foo() +int foo(void) { return 0; } @@ -197,7 +197,7 @@ def CheckCXX(context): """ context.Display("Checking whether the C++ compiler works... ") text = """ -int main() +int main(void) { return 0; } @@ -217,7 +217,7 @@ def CheckSHCXX(context): """ context.Display("Checking whether the (shared) C++ compiler works... ") text = """ -int main() +int main(void) { return 0; } @@ -290,7 +290,7 @@ char %s();""" % function_name #include %(hdr)s -int main() { +int main(void) { #if defined (__stub_%(name)s) || defined (__stub___%(name)s) fail fail fail #else @@ -400,7 +400,7 @@ def CheckType(context, type_name, fallback = None, %(include)s %(header)s -int main() { +int main(void) { if ((%(name)s *) 0) return 0; if (sizeof (%(name)s)) @@ -465,7 +465,7 @@ def CheckTypeSize(context, type_name, header = None, language = None, expect = N src = src + r""" typedef %s scons_check_type; -int main() +int main(void) { static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)]; test_array[0] = 0; @@ -498,7 +498,7 @@ int main() src = src + """ #include #include -int main() { +int main(void) { printf("%d", (int)sizeof(""" + type_name + """)); return 0; } @@ -560,7 +560,7 @@ def CheckDeclaration(context, symbol, includes = None, language = None): context.Display('Checking whether %s is declared... ' % symbol) src = src + r""" -int main() +int main(void) { #ifndef %s (void) %s; diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index f4c6f89..65c4b5f 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -115,9 +115,9 @@ class SConfTestCase(unittest.TestCase): def checks(self, sconf, TryFuncString): TryFunc = self.SConf.SConfBase.__dict__[TryFuncString] - res1 = TryFunc( sconf, "int main() { return 0; }\n", ".c" ) + res1 = TryFunc( sconf, "int main(void) { return 0; }\n", ".c" ) res2 = TryFunc( sconf, - '#include "no_std_header.h"\nint main() {return 0; }\n', + '#include "no_std_header.h"\nint main(void) {return 0; }\n', '.c' ) return (res1,res2) @@ -254,7 +254,7 @@ class SConfTestCase(unittest.TestCase): def checks(sconf): prog = """ #include -int main() { +int main(void) { printf( "Hello" ); return 0; } @@ -755,7 +755,7 @@ int main() { prog = """ #include -int main() { +int main(void) { printf( "Hello" ); return 0; } diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py index 9c7df12..c31d373 100644 --- a/src/engine/SCons/Scanner/CTests.py +++ b/src/engine/SCons/Scanner/CTests.py @@ -48,7 +48,7 @@ test.write('f1.cpp',""" #include \"f1.h\" #include -int main() +int main(void) { return 0; } @@ -60,7 +60,7 @@ test.write('f2.cpp',""" #include \"f1.h\" #import -int main() +int main(void) { return 0; } @@ -79,7 +79,7 @@ test.write('f3.cpp',""" const char* x = "#include " -int main() +int main(void) { return 0; } @@ -113,7 +113,7 @@ test.write('fa.cpp',""" #include \"fa.h\" #include -int main() +int main(void) { return 0; } @@ -135,7 +135,7 @@ test.write(['work', 'src', 'fff.c'], """ #include #include -int main() +int main(void) { return 0; } @@ -144,7 +144,7 @@ int main() test.write([ 'work', 'src', 'aaa.c'], """ #include "bbb.h" -int main() +int main(void) { return 0; } @@ -155,7 +155,7 @@ test.write([ 'work', 'src', 'bbb.h'], "\n") test.write([ 'repository', 'src', 'ccc.c'], """ #include "ddd.h" -int main() +int main(void) { return 0; } diff --git a/src/engine/SCons/Scanner/IDLTests.py b/src/engine/SCons/Scanner/IDLTests.py index 227799e..7b53801 100644 --- a/src/engine/SCons/Scanner/IDLTests.py +++ b/src/engine/SCons/Scanner/IDLTests.py @@ -159,7 +159,7 @@ test.write(['work', 'src', 'fff.c'], """ #include #include -int main() +int main(void) { return 0; } @@ -168,7 +168,7 @@ int main() test.write([ 'work', 'src', 'aaa.c'], """ #include "bbb.idl" -int main() +int main(void) { return 0; } @@ -179,7 +179,7 @@ test.write([ 'work', 'src', 'bbb.idl'], "\n") test.write([ 'repository', 'src', 'ccc.c'], """ #include "ddd.idl" -int main() +int main(void) { return 0; } diff --git a/test/CacheDir/up-to-date-q.py b/test/CacheDir/up-to-date-q.py index 275b127..f0e3962 100644 --- a/test/CacheDir/up-to-date-q.py +++ b/test/CacheDir/up-to-date-q.py @@ -55,7 +55,7 @@ test = TestSCons.TestSCons() test.subdir('cache', 'alpha', 'beta') foo_c = """ -int main(){ return 0; } +int main(void){ return 0; } """ sconstruct = """ diff --git a/test/Case.py b/test/Case.py index fb6821d..193da54 100644 --- a/test/Case.py +++ b/test/Case.py @@ -43,7 +43,7 @@ test.write('main.c', """\ void foo(); void bar(); -int main() { +int main(void) { foo(); bar(); exit (0); diff --git a/test/Configure/VariantDir-SConscript.py b/test/Configure/VariantDir-SConscript.py index c82778a..deb7b8f 100644 --- a/test/Configure/VariantDir-SConscript.py +++ b/test/Configure/VariantDir-SConscript.py @@ -96,7 +96,7 @@ test.write(['sub', 'TestProgram.c'], """\ #include "TestProgram.h" #include -int main() { +int main(void) { printf( "Hello\\n" ); } """) diff --git a/test/Configure/VariantDir.py b/test/Configure/VariantDir.py index 01df276..23a4b14 100644 --- a/test/Configure/VariantDir.py +++ b/test/Configure/VariantDir.py @@ -63,7 +63,7 @@ env.Program( 'TestProgram', 'TestProgram.c' ) test.write('TestProgram.c', """\ #include -int main() { +int main(void) { printf( "Hello\\n" ); } """) diff --git a/test/Configure/VariantDir2.py b/test/Configure/VariantDir2.py index 62b832f..ffe6525 100644 --- a/test/Configure/VariantDir2.py +++ b/test/Configure/VariantDir2.py @@ -41,7 +41,7 @@ SConscript('SConscript', variant_dir='build', src='.') test.write('SConscript', """\ env = Environment() config = env.Configure(conf_dir='sconf', log_file='config.log') -config.TryRun("int main() {}", ".c") +config.TryRun("int main(void) {}", ".c") config.Finish() """) diff --git a/test/Configure/basic.py b/test/Configure/basic.py index 4344941..4038a45 100644 --- a/test/Configure/basic.py +++ b/test/Configure/basic.py @@ -61,7 +61,7 @@ env.Program( 'TestProgram', 'TestProgram.c' ) test.write('TestProgram.c', """\ #include -int main() { +int main(void) { printf( "Hello\\n" ); } """) diff --git a/test/Configure/build-fail.py b/test/Configure/build-fail.py index 74609f6..218cf18 100644 --- a/test/Configure/build-fail.py +++ b/test/Configure/build-fail.py @@ -58,7 +58,7 @@ def _check(context): result = context.TryRun(''' #include "%s" - int main() { return 0; } + int main(void) { return 0; } ''' % inc, '.cpp')[0] if result: import sys diff --git a/test/Configure/clean.py b/test/Configure/clean.py index bb15165..d7a5dc7 100644 --- a/test/Configure/clean.py +++ b/test/Configure/clean.py @@ -53,7 +53,7 @@ env.Program( 'TestProgram', 'TestProgram.c' ) test.write('TestProgram.c', """\ #include -int main() { +int main(void) { printf( "Hello\\n" ); } """) diff --git a/test/Configure/custom-tests.py b/test/Configure/custom-tests.py index 7bb2366..6362e25 100644 --- a/test/Configure/custom-tests.py +++ b/test/Configure/custom-tests.py @@ -41,12 +41,12 @@ CR = test.CR # cached rebuild (up to date) NCF = test.NCF # non-cached build failure CF = test.CF # cached build failure -compileOK = '#include \\nint main() {printf("Hello");return 0;}' +compileOK = '#include \\nint main(void) {printf("Hello");return 0;}' compileFAIL = "syntax error" linkOK = compileOK -linkFAIL = "void myFunc(); int main() { myFunc(); }" +linkFAIL = "void myFunc(); int main(void) { myFunc(); }" runOK = compileOK -runFAIL = "int main() { return 1; }" +runFAIL = "int main(void) { return 1; }" test.write('pyAct.py', """\ from __future__ import print_function diff --git a/test/Configure/help.py b/test/Configure/help.py index f42088b..32f74da 100644 --- a/test/Configure/help.py +++ b/test/Configure/help.py @@ -53,7 +53,7 @@ env.Program( 'TestProgram', 'TestProgram.c' ) test.write('TestProgram.c', """\ #include -int main() { +int main(void) { printf( "Hello\\n" ); } """) diff --git a/test/FindSourceFiles.py b/test/FindSourceFiles.py index 88b9d7e..62b906c 100644 --- a/test/FindSourceFiles.py +++ b/test/FindSourceFiles.py @@ -63,7 +63,7 @@ foo_c = env.Substfile('foo.c.in', SUBST_DICT = {'__A__' : '0' }) foo = env.Program(foo_c) """) -test.write('src/foo.c.in', """ int main() { return __A__;} +test.write('src/foo.c.in', """ int main(void) { return __A__;} """) test.run(arguments = 'package') diff --git a/test/Interactive/added-include.py b/test/Interactive/added-include.py index 86a473d..e65cb14 100644 --- a/test/Interactive/added-include.py +++ b/test/Interactive/added-include.py @@ -41,7 +41,7 @@ test.write('foo.h.in', """ test.write('foo.c', """ #include -int main() +int main(void) { printf("foo.c\\n"); return 0; @@ -81,7 +81,7 @@ test.write('foo.c', """ #include -int main() +int main(void) { printf("%s\\n", FOO_STRING); return 0; diff --git a/test/Interactive/implicit-VariantDir.py b/test/Interactive/implicit-VariantDir.py index 5ef4583..fc52080 100644 --- a/test/Interactive/implicit-VariantDir.py +++ b/test/Interactive/implicit-VariantDir.py @@ -74,7 +74,7 @@ test.write(['src', 'foo.c'], """ #define FOO_PRINT_STRING "Hello from foo.c" -int main() +int main(void) { printf(FOO_PRINT_STRING "\\n"); return 0; @@ -115,7 +115,7 @@ test.write(['src', 'foo.c'], """ #include "foo.h" #include -int main() +int main(void) { printf(FOO_PRINT_STRING "\\n"); return 0; diff --git a/test/LINK/VersionedLib-VariantDir.py b/test/LINK/VersionedLib-VariantDir.py index 0350c6e..340fee0 100644 --- a/test/LINK/VersionedLib-VariantDir.py +++ b/test/LINK/VersionedLib-VariantDir.py @@ -65,7 +65,7 @@ test.write(['src','bin','main.c'], """ __declspec(dllimport) #endif int foo(); -int main() +int main(void) { return foo(); } diff --git a/test/LINK/VersionedLib-j2.py b/test/LINK/VersionedLib-j2.py index 4646a37..ed3b7ff 100644 --- a/test/LINK/VersionedLib-j2.py +++ b/test/LINK/VersionedLib-j2.py @@ -57,7 +57,7 @@ test.write('main.c', """ __declspec(dllimport) #endif int foo(); -int main() { return foo(); } +int main(void) { return foo(); } """) test.write('SConstruct', """ diff --git a/test/LINK/VersionedLib-subdir.py b/test/LINK/VersionedLib-subdir.py index 91f3011..2271a54 100644 --- a/test/LINK/VersionedLib-subdir.py +++ b/test/LINK/VersionedLib-subdir.py @@ -58,7 +58,7 @@ test.write('main.c', """ __declspec(dllimport) #endif int foo(); -int main() +int main(void) { return foo(); } diff --git a/test/Libs/Library.py b/test/Libs/Library.py index b603926..50fe68b 100644 --- a/test/Libs/Library.py +++ b/test/Libs/Library.py @@ -152,7 +152,7 @@ void nrd() { test.write('uses-nrd.c', r""" void nrd(); -int main() { +int main(void) { nrd(); return 0; } diff --git a/test/MSVC/pch-basics.py b/test/MSVC/pch-basics.py index 45735ed..ebee0da 100644 --- a/test/MSVC/pch-basics.py +++ b/test/MSVC/pch-basics.py @@ -39,7 +39,7 @@ test.skip_if_not_msvc() test.write('Main.cpp', """\ #include "Precompiled.h" -int main() +int main(void) { return testf(); } diff --git a/test/MSVC/pch-spaces-subdir.py b/test/MSVC/pch-spaces-subdir.py index 3a65b44..65595fc 100644 --- a/test/MSVC/pch-spaces-subdir.py +++ b/test/MSVC/pch-spaces-subdir.py @@ -39,7 +39,7 @@ test.skip_if_not_msvc() test.write('Main.cpp', """\ #include "Precompiled.h" -int main() +int main(void) { return testf(); } diff --git a/test/MSVS/CPPPATH-Dirs.py b/test/MSVS/CPPPATH-Dirs.py index 45ec846..53c7a8b 100644 --- a/test/MSVS/CPPPATH-Dirs.py +++ b/test/MSVS/CPPPATH-Dirs.py @@ -68,7 +68,7 @@ test.write('SConstruct', SConscript_contents) test.write('main.cpp', """\ #include -int main() { +int main(void) { printf("hello, world!\\n"); } """) diff --git a/test/QT/CPPPATH-appended.py b/test/QT/CPPPATH-appended.py index 2780921..98baad8 100644 --- a/test/QT/CPPPATH-appended.py +++ b/test/QT/CPPPATH-appended.py @@ -57,7 +57,7 @@ env.Program(target = 'aaa', source = 'aaa.cpp') test.write(['sub', 'aaa.cpp'], r""" #include "aaa.h" -int main() { aaa(); return 0; } +int main(void) { aaa(); return 0; } """) test.write(['sub', 'aaa.h'], r""" diff --git a/test/QT/CPPPATH.py b/test/QT/CPPPATH.py index 4ea42bd..5de41d9 100644 --- a/test/QT/CPPPATH.py +++ b/test/QT/CPPPATH.py @@ -47,7 +47,7 @@ env.Program(target = 'aaa', source = 'aaa.cpp', CPPPATH=['$CPPPATH', './local_in test.write('aaa.cpp', r""" #include "aaa.h" -int main() { aaa(); return 0; } +int main(void) { aaa(); return 0; } """) test.write('aaa.h', r""" diff --git a/test/QT/QTFLAGS.py b/test/QT/QTFLAGS.py index 8d266ad..61a1d87 100644 --- a/test/QT/QTFLAGS.py +++ b/test/QT/QTFLAGS.py @@ -127,7 +127,7 @@ test.write(['work1', 'main.cpp'], """ #include "uic-another_ui_file.hpp" void mocFromCpp(); -int main() { +int main(void) { mocFromH(); mocFromCpp(); an_ui_file(); @@ -189,7 +189,7 @@ env2.Program('main.cpp') """ % {'QTDIR':QT}) test.write(['work2', 'main.cpp'], """ -int main() { return 0; } +int main(void) { return 0; } """) # Ignore stderr, because if Qt is not installed, diff --git a/test/QT/empty-env.py b/test/QT/empty-env.py index 77547e7..b7867e5 100644 --- a/test/QT/empty-env.py +++ b/test/QT/empty-env.py @@ -46,7 +46,7 @@ env.Program('main', 'main.cpp', CPPDEFINES=['FOO'], LIBS=[]) test.write('main.cpp', r""" #include "foo6.h" -int main() { foo6(); return 0; } +int main(void) { foo6(); return 0; } """) test.write(['qt', 'include', 'foo6.h'], """\ diff --git a/test/QT/manual.py b/test/QT/manual.py index 1f140ae..c5cf74e 100644 --- a/test/QT/manual.py +++ b/test/QT/manual.py @@ -119,7 +119,7 @@ test.write('main.cpp', r""" #include "eee.h" #include "uic_fff.hpp" -int main() { +int main(void) { aaa(); bbb(); ccc(); ddd(); eee(); fff(); return 0; } """) diff --git a/test/QT/moc-from-header.py b/test/QT/moc-from-header.py index 41b1b3d..4151cba 100644 --- a/test/QT/moc-from-header.py +++ b/test/QT/moc-from-header.py @@ -61,7 +61,7 @@ if env['PLATFORM'] == 'darwin': test.write('aaa.cpp', r""" #include "aaa.h" -int main() { aaa(); return 0; } +int main(void) { aaa(); return 0; } """) test.write('aaa.h', r""" diff --git a/test/QT/reentrant.py b/test/QT/reentrant.py index be464b9..af19af9 100644 --- a/test/QT/reentrant.py +++ b/test/QT/reentrant.py @@ -56,7 +56,7 @@ env.Program('main', 'main.cpp', CPPDEFINES=['FOO'], LIBS=[]) test.write('main.cpp', r""" #include "foo5.h" -int main() { foo5(); return 0; } +int main(void) { foo5(); return 0; } """) test.run() diff --git a/test/RPATH.py b/test/RPATH.py index 4e13bb1..754abfe 100644 --- a/test/RPATH.py +++ b/test/RPATH.py @@ -46,7 +46,7 @@ Program('foo', 'foo.c', LIBS='bar', LIBPATH='bar', RPATH='bar') test.write('foo.c', """\ #include -int main() { +int main(void) { void bar(); bar(); exit (0); diff --git a/test/YACC/live.py b/test/YACC/live.py index 253a387..6dd08f7 100644 --- a/test/YACC/live.py +++ b/test/YACC/live.py @@ -60,7 +60,7 @@ extern int yyparse(); int yyerror(char *s); int yylex(); -int main() +int main(void) { return yyparse(); } @@ -108,7 +108,7 @@ file_hpp = 'file.hpp' test.write("hello.cpp", """\ #include "%(file_hpp)s" -int main() +int main(void) { } """ % locals()) -- cgit v0.12 From dc52eb2d432cef90be3c8fee69f927c4aa5b0cfb Mon Sep 17 00:00:00 2001 From: Jonathon Reinhart Date: Sun, 11 Feb 2018 18:15:51 -0500 Subject: Update CHANGES.txt --- src/CHANGES.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index d2587e3..2fc06fe 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -37,6 +37,12 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Updated manpage scons.xml to fix a nested list problem - Updated doc terminionly: use prepend instead of append as appropriate + From Jonathon Reinhart: + - Replace all instances of `int main()` in C code with `int main(void)`. + Specifically, this fixes the test cases use by Configure.CheckCC() which + would fail when using -Wstrict-prototypes. + + RELEASE 3.0.1 - Mon, 12 Nov 2017 15:31:33 -0700 From Daniel Moody: -- cgit v0.12 From 5af439a4cab5a55b43fce75118eccbd45bf82fe5 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sun, 11 Feb 2018 21:02:30 -0500 Subject: used pickle.HIGHEST_PROTOCAL instead of -2 for better readability --- src/engine/SCons/compat/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py index c053edf..7ab94c5 100644 --- a/src/engine/SCons/compat/__init__.py +++ b/src/engine/SCons/compat/__init__.py @@ -98,7 +98,7 @@ import pickle # Was pickle.HIGHEST_PROTOCOL # Changed to 2 so py3.5+'s pickle will be compatible with py2.7. -PICKLE_PROTOCOL = -2 +PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL # TODO: FIXME # In 3.x, 'profile' automatically loads the fast version if available. -- cgit v0.12 From 0589244884a657553258addd579fe0f592238b96 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 12 Feb 2018 08:45:52 -0500 Subject: updated changes to make note that increasing the pickling protocal will cause issues when reading sconsigns with the previous pickle protocal --- src/CHANGES.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index de06e84..62c6aa7 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,9 +8,10 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Daniel Moody: - - Made a small change to scons main __init__.py to set the pickling protocal - to choose the latest which fixed 2 failing test varient dir test from Interactive - + - Set the pickling protocal back to highest which was causing issues + with variant dir tests. This will cause issues if reading sconsigns + pickled with the previous lower protocal. + From Ray Donnelly: - Fix the PATH created by scons.bat (and other .bat files) to provide a normalized PATH. Some pythons in the 3.6 series are no longer able to handle paths which -- cgit v0.12 From 4685be01723dd4ef4917217e8aff25e92a55f1a5 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 14 Feb 2018 11:44:59 -0500 Subject: Update default paths for clang compilers. This should hopefully fix some appveyor failures. --- src/engine/SCons/Tool/clang.py | 5 ++++- src/engine/SCons/Tool/clang/__init__.py | 16 ++++++++++++++++ src/engine/SCons/Tool/clangxx.py | 4 +++- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/engine/SCons/Tool/clang/__init__.py diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index a6bae43..aba5598 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -45,6 +45,8 @@ import sys import SCons.Util import SCons.Tool.cc +from SCons.Tool.clang import get_clang_install_dirs + compilers = ['clang'] @@ -54,7 +56,7 @@ def generate(env): if env['PLATFORM'] == 'win32': # Ensure that we have a proper path for clang - clang = SCons.Tool.find_program_path(env, compilers[0], default_paths=[r'C:\Program Files\LLVM\bin',]) + clang = SCons.Tool.find_program_path(env, compilers[0], default_paths=get_clang_install_dirs(env['PLATFORM'])) if clang: clang_bin_dir = os.path.dirname(clang) env.AppendENVPath('PATH', clang_bin_dir) @@ -64,6 +66,7 @@ def generate(env): env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') else: env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') + # determine compiler version if env['CC']: #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], diff --git a/src/engine/SCons/Tool/clang/__init__.py b/src/engine/SCons/Tool/clang/__init__.py new file mode 100644 index 0000000..775ba80 --- /dev/null +++ b/src/engine/SCons/Tool/clang/__init__.py @@ -0,0 +1,16 @@ +""" +Common routines and data for clang tools +""" + +clang_win32_dirs = [ + r'C:\Program Files\LLVM\bin', + r'C:\cygwin64\bin', + r'C:\msys64', + r'C:\cygwin\bin', + r'C:\msys', +] + +def get_clang_install_dirs(platform): + if platform == 'win32': + return clang_win32_dirs + else return [] \ No newline at end of file diff --git a/src/engine/SCons/Tool/clangxx.py b/src/engine/SCons/Tool/clangxx.py index 63f1b0d..2bf4cf4 100644 --- a/src/engine/SCons/Tool/clangxx.py +++ b/src/engine/SCons/Tool/clangxx.py @@ -46,6 +46,8 @@ import sys import SCons.Tool import SCons.Util import SCons.Tool.cxx +from SCons.Tool.clang import get_clang_install_dirs + compilers = ['clang++'] @@ -68,7 +70,7 @@ def generate(env): env['SHOBJSUFFIX'] = '.pic.o' elif env['PLATFORM'] == 'win32': # Ensure that we have a proper path for clang++ - clangxx = SCons.Tool.find_program_path(env, compilers[0], default_paths=[r'C:\Program Files\LLVM\bin',]) + clangxx = SCons.Tool.find_program_path(env, compilers[0], default_paths=get_clang_install_dirs(env['PLATFORM'])) if clangxx: clangxx_bin_dir = os.path.dirname(clangxx) env.AppendENVPath('PATH', clangxx_bin_dir) -- cgit v0.12 From 1bdaf47dc61efefe469885a67876fdee46586f4a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 19 Feb 2018 10:47:25 -0500 Subject: Remove duplicate example. Likely caused by bad merge Fixes #2983 --- .../examples/builderswriting_MY_EMITTER_1.xml | 3 +- doc/generated/examples/caching_ex-random_1.xml | 6 +-- doc/generated/examples/troubleshoot_explain1_3.xml | 2 +- doc/generated/variables.gen | 59 ++++++++++++---------- doc/user/builders-writing.xml | 20 -------- 5 files changed, 38 insertions(+), 52 deletions(-) diff --git a/doc/generated/examples/builderswriting_MY_EMITTER_1.xml b/doc/generated/examples/builderswriting_MY_EMITTER_1.xml index 440b105..881fee7 100644 --- a/doc/generated/examples/builderswriting_MY_EMITTER_1.xml +++ b/doc/generated/examples/builderswriting_MY_EMITTER_1.xml @@ -1,5 +1,6 @@ % scons -Q my_command file1.input modify1.in > file1.foo -my_command file2.input modify2.in > file2.foo +sh: my_command: command not found +scons: *** [file1.foo] Error 127 diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index 81cbc5d..6a0337b 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -1,9 +1,9 @@ % scons -Q -cc -o f5.o -c f5.c -cc -o f4.o -c f4.c +cc -o f2.o -c f2.c cc -o f1.o -c f1.c +cc -o f5.o -c f5.c cc -o f3.o -c f3.c -cc -o f2.o -c f2.c +cc -o f4.o -c f4.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml index 32adb2f..e481f76 100644 --- a/doc/generated/examples/troubleshoot_explain1_3.xml +++ b/doc/generated/examples/troubleshoot_explain1_3.xml @@ -3,5 +3,5 @@ cp file.in file.oout scons: warning: Cannot find target file.out after building -File "/Users/bdbaddog/devel/scons/git/as_scons/bootstrap/src/script/scons.py", line 201, in <module> +File "/Users/bdbaddog/devel/scons/git/scons-bugfixes/bootstrap/src/script/scons.py", line 201, in <module> diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index b86690a..2961b91 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -480,7 +480,8 @@ An automatically-generated construction variable containing the C preprocessor command-line options to define values. The value of $_CPPDEFFLAGS is created -by appending $CPPDEFPREFIX and $CPPDEFSUFFIX +by respectively prepending and appending +$CPPDEFPREFIX and $CPPDEFSUFFIX to the beginning and end of each definition in $CPPDEFINES. @@ -503,7 +504,8 @@ If $CPPDEFINES is a strin the values of the $CPPDEFPREFIX and $CPPDEFSUFFIX construction variables -will be added to the beginning and end. +will be respectively prepended and appended to the beginning and end +of each definition in $CPPDEFINES. @@ -517,7 +519,7 @@ If $CPPDEFINES is a list, the values of the $CPPDEFPREFIX and $CPPDEFSUFFIX construction variables -will be appended to the beginning and end +will be respectively prepended and appended to the beginning and end of each element in the list. If any element is a list or tuple, then the first item is the name being @@ -535,7 +537,7 @@ If $CPPDEFINES is a dicti the values of the $CPPDEFPREFIX and $CPPDEFSUFFIX construction variables -will be appended to the beginning and end +will be respectively prepended and appended to the beginning and end of each item from the dictionary. The key of each dictionary item is a name being defined @@ -563,7 +565,7 @@ env = Environment(CPPDEFINES={'B':2, 'A':None}) The prefix used to specify preprocessor definitions on the C compiler command line. -This will be appended to the beginning of each definition +This will be prepended to the beginning of each definition in the $CPPDEFINES construction variable when the $_CPPDEFFLAGS variable is automatically generated. @@ -619,7 +621,7 @@ An automatically-generated construction variable containing the C preprocessor command-line options for specifying directories to be searched for include files. The value of $_CPPINCFLAGS is created -by appending $INCPREFIX and $INCSUFFIX +by respectively prepending and appending $INCPREFIX and $INCSUFFIX to the beginning and end of each directory in $CPPPATH. @@ -661,7 +663,7 @@ through the automatically-generated $_CPPINCFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the value of the $INCPREFIX and $INCSUFFIX construction variables to the beginning and end @@ -2602,7 +2604,8 @@ containing the Fortran compiler command-line options for specifying directories to be searched for include files and module files. The value of $_FORTRANINCFLAGS is created -by prepending/appending $INCPREFIX and $INCSUFFIX +by respectively prepending and appending +$INCPREFIX and $INCSUFFIX to the beginning and end of each directory in $FORTRANPATH. @@ -2625,7 +2628,7 @@ for module files, as well. The prefix used to specify a module directory on the Fortran compiler command line. -This will be appended to the beginning of the directory +This will be prepended to the beginning of the directory in the $FORTRANMODDIR construction variables when the $_FORTRANMODFLAG variables is automatically generated. @@ -2637,7 +2640,7 @@ when the The suffix used to specify a module directory on the Fortran compiler command line. -This will be appended to the beginning of the directory +This will be appended to the end of the directory in the $FORTRANMODDIR construction variables when the $_FORTRANMODFLAG variables is automatically generated. @@ -2653,8 +2656,8 @@ for specifying the directory location where the Fortran compiler should place any module files that happen to get generated during compilation. The value of $_FORTRANMODFLAG is created -by prepending/appending $FORTRANMODDIRPREFIX and -$FORTRANMODDIRSUFFIX +by respectively prepending and appending +$FORTRANMODDIRPREFIX and $FORTRANMODDIRSUFFIX to the beginning and end of the directory in $FORTRANMODDIR. @@ -2727,7 +2730,7 @@ through the automatically-generated $_FORTRANINCFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the values of the $INCPREFIX and $INCSUFFIX construction variables to the beginning and end @@ -3087,7 +3090,7 @@ env = Environment(IMPLICIT_COMMAND_DEPENDENCIES = 0) The prefix used to specify an include directory on the C compiler command line. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $CPPPATH and $FORTRANPATH construction variables when the $_CPPINCFLAGS and $_FORTRANINCFLAGS variables are automatically generated. @@ -3698,7 +3701,7 @@ An automatically-generated construction variable containing the linker command-line options for specifying directories to be searched for library. The value of $_LIBDIRFLAGS is created -by appending $LIBDIRPREFIX and $LIBDIRSUFFIX +by respectively prepending and appending $LIBDIRPREFIX and $LIBDIRSUFFIX to the beginning and end of each directory in $LIBPATH. @@ -3709,7 +3712,7 @@ of each directory in $LIBPATH The prefix used to specify a library directory on the linker command line. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $LIBPATH construction variable when the $_LIBDIRFLAGS variable is automatically generated. @@ -3742,7 +3745,7 @@ An automatically-generated construction variable containing the linker command-line options for specifying libraries to be linked with the resulting target. The value of $_LIBFLAGS is created -by appending $LIBLINKPREFIX and $LIBLINKSUFFIX +by respectively prepending and appending $LIBLINKPREFIX and $LIBLINKSUFFIX to the beginning and end of each filename in $LIBS. @@ -3753,7 +3756,7 @@ of each filename in $LIBS The prefix used to specify a library to link on the linker command line. -This will be appended to the beginning of each library +This will be prepended to the beginning of each library in the $LIBS construction variable when the $_LIBFLAGS variable is automatically generated. @@ -3807,7 +3810,7 @@ through the automatically-generated $_LIBDIRFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the values of the $LIBDIRPREFIX and $LIBDIRSUFFIX construction variables to the beginning and end @@ -3863,7 +3866,7 @@ through the automatically-generated $_LIBFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the values of the $LIBLINKPREFIX and $LIBLINKSUFFIX construction variables to the beginning and end @@ -5601,7 +5604,8 @@ containing the command-line options for specifying directories to be searched by the resource compiler. The value of $RCINCFLAGS is created -by appending $RCINCPREFIX and $RCINCSUFFIX +by respectively prepending and appending +$RCINCPREFIX and $RCINCSUFFIX to the beginning and end of each directory in $CPPPATH. @@ -5613,7 +5617,7 @@ of each directory in $CPPPATH The prefix (flag) used to specify an include directory on the resource compiler command line. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $CPPPATH construction variable when the $RCINCFLAGS variable is expanded. @@ -5735,7 +5739,7 @@ An automatically-generated construction variable containing the rpath flags to be used when linking a program with shared libraries. The value of $_RPATH is created -by appending $RPATHPREFIX and $RPATHSUFFIX +by respectively prepending $RPATHPREFIX and appending $RPATHSUFFIX to the beginning and end of each directory in $RPATH. @@ -5763,7 +5767,7 @@ path, you must make it absolute yourself. The prefix used to specify a directory to be searched for shared libraries when running programs. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $RPATH construction variable when the $_RPATH variable is automatically generated. @@ -6935,7 +6939,8 @@ An automatically-generated construction variable containing the SWIG command-line options for specifying directories to be searched for included files. The value of $_SWIGINCFLAGS is created -by appending $SWIGINCPREFIX and $SWIGINCSUFFIX +by respectively prepending and appending +$SWIGINCPREFIX and $SWIGINCSUFFIX to the beginning and end of each directory in $SWIGPATH. @@ -6946,7 +6951,7 @@ of each directory in $SWIGPATH The prefix used to specify an include directory on the SWIG command line. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $SWIGPATH construction variable when the $_SWIGINCFLAGS variable is automatically generated. @@ -7020,7 +7025,7 @@ through the automatically-generated $_SWIGINCFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the values of the $SWIGINCPREFIX and $SWIGINCSUFFIX construction variables to the beginning and end diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index 07f2dec..f740a79 100644 --- a/doc/user/builders-writing.xml +++ b/doc/user/builders-writing.xml @@ -826,9 +826,6 @@ env2 = Environment(BUILDERS = {'Foo' : bld}, MY_EMITTER = modify2) env1.Foo('file1') env2.Foo('file2') -import os -env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() -env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() file1.input @@ -848,23 +845,6 @@ cat - -bld = Builder(action = 'my_command $SOURCES > $TARGET', - suffix = '.foo', - src_suffix = '.input', - emitter = '$MY_EMITTER') -def modify1(target, source, env): - return target, source + ['modify1.in'] -def modify2(target, source, env): - return target, source + ['modify2.in'] -env1 = Environment(BUILDERS = {'Foo' : bld}, - MY_EMITTER = modify1) -env2 = Environment(BUILDERS = {'Foo' : bld}, - MY_EMITTER = modify2) -env1.Foo('file1') -env2.Foo('file2') - - In this example, the modify1.in -- cgit v0.12 From ad01d6c0c6d6171163d2d8129e097357ebecf5da Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 19 Feb 2018 09:25:27 -0800 Subject: Fix running my_command example script in example --- .../examples/builderswriting_MY_EMITTER_1.xml | 5 ++--- doc/generated/examples/caching_ex-random_1.xml | 6 +++--- doc/generated/examples/environments_ex3_1.xml | 22 +++++++++++++++++--- doc/generated/examples/java_jar1_1.xml | 2 +- doc/generated/examples/troubleshoot_Dump_2.xml | 2 +- doc/generated/examples/troubleshoot_explain1_3.xml | 2 +- doc/generated/tools.gen | 12 +++++------ doc/generated/tools.mod | 4 ++-- doc/generated/variables.gen | 24 +++++++++++----------- doc/generated/variables.mod | 4 ++-- doc/user/builders-writing.xml | 2 +- 11 files changed, 50 insertions(+), 35 deletions(-) diff --git a/doc/generated/examples/builderswriting_MY_EMITTER_1.xml b/doc/generated/examples/builderswriting_MY_EMITTER_1.xml index 881fee7..0c17d0e 100644 --- a/doc/generated/examples/builderswriting_MY_EMITTER_1.xml +++ b/doc/generated/examples/builderswriting_MY_EMITTER_1.xml @@ -1,6 +1,5 @@ % scons -Q -my_command file1.input modify1.in > file1.foo -sh: my_command: command not found -scons: *** [file1.foo] Error 127 +./my_command file1.input modify1.in > file1.foo +./my_command file2.input modify2.in > file2.foo diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index 6a0337b..19b02f5 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -1,9 +1,9 @@ % scons -Q -cc -o f2.o -c f2.c -cc -o f1.o -c f1.c -cc -o f5.o -c f5.c cc -o f3.o -c f3.c +cc -o f5.o -c f5.c +cc -o f2.o -c f2.c cc -o f4.o -c f4.c +cc -o f1.o -c f1.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/environments_ex3_1.xml b/doc/generated/examples/environments_ex3_1.xml index 3262302..12b6ae0 100644 --- a/doc/generated/examples/environments_ex3_1.xml +++ b/doc/generated/examples/environments_ex3_1.xml @@ -1,6 +1,22 @@ % scons -Q - -scons: *** Two environments with different actions were specified for the same target: foo.o -File "/home/my/project/SConstruct", line 6, in <module> +UnicodeDecodeError: 'utf8' codec can't decode byte 0xc2 in position 547: invalid continuation byte: + File "/home/my/project/SConstruct", line 6: + dbg.Program('foo', 'foo.c') + File "bootstrap/src/engine/SCons/Environment.py", line 260: + return MethodWrapper.__call__(self, target, source, *args, **kw) + File "bootstrap/src/engine/SCons/Environment.py", line 224: + return self.method(*nargs, **kwargs) + File "bootstrap/src/engine/SCons/Builder.py", line 635: + return self._execute(env, target, source, OverrideWarner(kw), ekw) + File "bootstrap/src/engine/SCons/Builder.py", line 541: + source = self.src_builder_sources(env, source, overwarn) + File "bootstrap/src/engine/SCons/Builder.py", line 748: + tlist = bld._execute(env, None, [s], overwarn) + File "bootstrap/src/engine/SCons/Builder.py", line 557: + _node_errors(self, env, tlist, slist) + File "bootstrap/src/engine/SCons/Builder.py", line 303: + msg = "Two environments with different actions were specified for the same target: %s\n(action 1: %s)\n(action 2: %s)" % (t,t_contents.decode('utf-8'),contents.decode('utf-8')) + File "/home/bdbaddog/tools/python-2.7.13/lib/python2.7/encodings/utf_8.py", line 16: + return codecs.utf_8_decode(input, errors, True) diff --git a/doc/generated/examples/java_jar1_1.xml b/doc/generated/examples/java_jar1_1.xml index de93227..daa3d1a 100644 --- a/doc/generated/examples/java_jar1_1.xml +++ b/doc/generated/examples/java_jar1_1.xml @@ -1,5 +1,5 @@ % scons -Q javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java -jar cf test.jar classes +scons: *** [test.jar] Source `classes.class' not found, needed by target `test.jar'. diff --git a/doc/generated/examples/troubleshoot_Dump_2.xml b/doc/generated/examples/troubleshoot_Dump_2.xml index 08c6f04..a621422 100644 --- a/doc/generated/examples/troubleshoot_Dump_2.xml +++ b/doc/generated/examples/troubleshoot_Dump_2.xml @@ -79,7 +79,7 @@ scons: Reading SConscript files ... 'SHCXX': '$CXX', 'SHCXXCOM': '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM","$SHCXXCOMSTR")}', 'SHCXXFLAGS': ['$CXXFLAGS'], - 'SHELL': 'command', + 'SHELL': None, 'SHLIBPREFIX': '', 'SHLIBSUFFIX': '.dll', 'SHOBJPREFIX': '$OBJPREFIX', diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml index e481f76..064fdcb 100644 --- a/doc/generated/examples/troubleshoot_explain1_3.xml +++ b/doc/generated/examples/troubleshoot_explain1_3.xml @@ -3,5 +3,5 @@ cp file.in file.oout scons: warning: Cannot find target file.out after building -File "/Users/bdbaddog/devel/scons/git/scons-bugfixes/bootstrap/src/script/scons.py", line 201, in <module> +File "/home/bdbaddog/scons/git/scons/bootstrap/src/script/scons.py", line 201, in <module> diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen index 26b7e22..f858aa4 100644 --- a/doc/generated/tools.gen +++ b/doc/generated/tools.gen @@ -778,19 +778,19 @@ Sets construction variables for the Sets: &cv-link-AS;, &cv-link-ASCOM;, &cv-link-ASFLAGS;, &cv-link-ASPPCOM;, &cv-link-ASPPFLAGS;.Uses: &cv-link-ASCOMSTR;, &cv-link-ASPPCOMSTR;. - - packaging + + Packaging -A framework for building binary and source packages. +Sets construction variables for the Package Builder. - - Packaging + + packaging -Sets construction variables for the Package Builder. +A framework for building binary and source packages. diff --git a/doc/generated/tools.mod b/doc/generated/tools.mod index f9bc1d7..1209d74 100644 --- a/doc/generated/tools.mod +++ b/doc/generated/tools.mod @@ -78,8 +78,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. mwcc"> mwld"> nasm"> -packaging"> Packaging"> +packaging"> pdf"> pdflatex"> pdftex"> @@ -186,8 +186,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. mwcc"> mwld"> nasm"> -packaging"> Packaging"> +packaging"> pdf"> pdflatex"> pdftex"> diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 2961b91..a756d35 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -3209,7 +3209,7 @@ The command line used to call the Java archive tool. The string displayed when the Java archive tool is called -If this is not set, then $JARCOM (the command line) is displayed. +If this is not set, then $JARCOM (the command line) is displayed. @@ -3219,7 +3219,7 @@ env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") The string displayed when the Java archive tool is called -If this is not set, then $JARCOM (the command line) is displayed. +If this is not set, then $JARCOM (the command line) is displayed. @@ -6614,6 +6614,16 @@ Example + + SHLIBVERSIONFLAGS + + +Extra flags added to $SHLINKCOM when building versioned +SharedLibrary. These flags are only used when $SHLIBVERSION is +set. + + + _SHLIBVERSIONFLAGS @@ -6627,16 +6637,6 @@ and some extra dynamically generated options (such as - - SHLIBVERSIONFLAGS - - -Extra flags added to $SHLINKCOM when building versioned -SharedLibrary. These flags are only used when $SHLIBVERSION is -set. - - - SHLINK diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod index 460724e..6ecf6c9 100644 --- a/doc/generated/variables.mod +++ b/doc/generated/variables.mod @@ -496,8 +496,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $_SHLIBSONAME"> $SHLIBSUFFIX"> $SHLIBVERSION"> -$_SHLIBVERSIONFLAGS"> $SHLIBVERSIONFLAGS"> +$_SHLIBVERSIONFLAGS"> $SHLINK"> $SHLINKCOM"> $SHLINKCOMSTR"> @@ -1125,8 +1125,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $_SHLIBSONAME"> $SHLIBSUFFIX"> $SHLIBVERSION"> -$_SHLIBVERSIONFLAGS"> $SHLIBVERSIONFLAGS"> +$_SHLIBVERSIONFLAGS"> $SHLINK"> $SHLINKCOM"> $SHLINKCOMSTR"> diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index f740a79..ce95128 100644 --- a/doc/user/builders-writing.xml +++ b/doc/user/builders-writing.xml @@ -812,7 +812,7 @@ env.Foo('file') -bld = Builder(action = 'my_command $SOURCES > $TARGET', +bld = Builder(action = './my_command $SOURCES > $TARGET', suffix = '.foo', src_suffix = '.input', emitter = '$MY_EMITTER') -- cgit v0.12 From 32df95374e7ccb78ba677b5f287a437d12c0df4b Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 19 Feb 2018 16:09:09 -0500 Subject: Fix HOWTO/README. Remove vestiges of Option(). [ci skip] --- HOWTO/README | 5 ----- src/engine/SCons/Script/__init__.py | 2 -- 2 files changed, 7 deletions(-) diff --git a/HOWTO/README b/HOWTO/README index 951c019..e20df73 100644 --- a/HOWTO/README +++ b/HOWTO/README @@ -22,8 +22,3 @@ new-tool.txt release.txt Steps to go through when releasing a new version of SCons. -subrelease.txt - Steps to go through when releasing a new subsidiary version - of SCons--for example, 0.95.1 after we've released 0.95. - So far, we've only done this to get some early testing on major - refactorings. diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index a05c541..89fc061 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -280,8 +280,6 @@ sconscript_reading = 0 def Variables(files=[], args=ARGUMENTS): return SCons.Variables.Variables(files, args) -def Options(files=[], args=ARGUMENTS): - return SCons.Options.Options(files, args) # The list of global functions to add to the SConscript name space # that end up calling corresponding methods or Builders in the -- cgit v0.12 From a1a453e5046dbb1151eba2ca605d08b5d5820e81 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 20 Feb 2018 10:01:26 -0500 Subject: Update README.rst. Was missing correct info on python 3.5+ support. [ci skip] --- README.rst | 52 +++++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/README.rst b/README.rst index 8eb5af1..d63c8a4 100644 --- a/README.rst +++ b/README.rst @@ -72,9 +72,8 @@ version at the SCons download page: Execution Requirements ====================== -Running SCons requires Python version 2.7 or later (Python 3 is not -yet supported). There should be no other dependencies or requirements -to run SCons. +Running SCons requires Python version 2.7.* and Python 3.5 or higher. +There should be no other dependencies or requirements to run SCons. The default SCons configuration assumes use of the Microsoft Visual C++ compiler suite on WIN32 systems, and assumes a C compiler named 'cc', a C++ @@ -105,13 +104,11 @@ populate the build/scons/ subdirectory. You would do this as follows on a Linux or UNIX system (using sh or a derivative like bash or ksh):: $ setenv MYSCONS=`pwd`/src - $ setenv SCONS_LIB_DIR=$MYSCONS/engine $ python $MYSCONS/script/scons.py [arguments] Or on Windows:: C:\scons>set MYSCONS=%cd%\src - C:\scons>set SCONS_LIB_DIR=%MYSCONS%\engine C:\scons>python %MYSCONS%\script\scons.py [arguments] An alternative approach is to skip the above and use:: @@ -181,7 +178,7 @@ Or on Windows:: By default, the above commands will do the following: -- Install the version-numbered "scons-3.0.0" and "sconsign-3.0.0" scripts in +- Install the version-numbered "scons-3.1.0" and "sconsign-3.1.0" scripts in the default system script directory (/usr/bin or C:\\Python\*\\Scripts, for example). This can be disabled by specifying the "--no-version-script" option on the command line. @@ -193,23 +190,23 @@ By default, the above commands will do the following: before making it the default on your system. On UNIX or Linux systems, you can have the "scons" and "sconsign" scripts be - hard links or symbolic links to the "scons-3.0.0" and "sconsign-3.0.0" + hard links or symbolic links to the "scons-3.1.0" and "sconsign-3.1.0" scripts by specifying the "--hardlink-scons" or "--symlink-scons" options on the command line. -- Install "scons-3.0.0.bat" and "scons.bat" wrapper scripts in the Python +- Install "scons-3.1.0.bat" and "scons.bat" wrapper scripts in the Python prefix directory on Windows (C:\\Python\*, for example). This can be disabled by specifying the "--no-install-bat" option on the command line. On UNIX or Linux systems, the "--install-bat" option may be specified to - have "scons-3.0.0.bat" and "scons.bat" files installed in the default system + have "scons-3.1.0.bat" and "scons.bat" files installed in the default system script directory, which is useful if you want to install SCons in a shared file system directory that can be used to execute SCons from both UNIX/Linux and Windows systems. - Install the SCons build engine (a Python module) in an appropriate - version-numbered SCons library directory (/usr/lib/scons-3.0.0 or - C:\\Python\*\\scons-3.0.0, for example). See below for more options related to + version-numbered SCons library directory (/usr/lib/scons-3.1.0 or + C:\\Python\*\\scons-3.1.0, for example). See below for more options related to installing the build engine library. - Install the troff-format man pages in an appropriate directory on UNIX or @@ -487,7 +484,7 @@ running all of "runtest.py -a". Building Packages ================= -We use SCons (version 3.0.0 or later) to build its own packages. If you +We use SCons (version 3.1.0 or later) to build its own packages. If you already have an appropriate version of SCons installed on your system, you can build everything by simply running it:: @@ -502,9 +499,7 @@ about `Executing SCons Without Installing`_):: Depending on the utilities installed on your system, any or all of the following packages will be built:: - build/dist/scons-3.0.0-1.noarch.rpm - build/dist/scons-3.0.0-1.src.rpm - build/dist/scons-3.0.0.linux-i686.tar.gz + build/dist/scons-3.1.0.linux-i686.tar.gz build/dist/scons-3.1.0.alpha.yyyymmdd.tar.gz build/dist/scons-3.1.0.alpha.yyyymmdd.win32.exe build/dist/scons-3.1.0.alpha.yyyymmdd.zip @@ -513,7 +508,6 @@ following packages will be built:: build/dist/scons-local-3.1.0.alpha.yyyymmdd.zip build/dist/scons-src-3.1.0.alpha.yyyymmdd.tar.gz build/dist/scons-src-3.1.0.alpha.yyyymmdd.zip - build/dist/scons_3.0.0-1_all.deb The SConstruct file is supposed to be smart enough to avoid trying to build packages for which you don't have the proper utilities installed. For @@ -537,10 +531,6 @@ system, it should not try to install it.) The runtest.py script supports a -p option that will run the specified tests (individually or collectively via the -a option) against the unpacked build/test-/\* subdirectory:: - $ python runtest.py -p deb - - $ python runtest.py -p rpm - $ python runtest.py -p local-tar-gz $ python runtest.py -p local-zip @@ -690,18 +680,22 @@ in the LICENSE file. Reporting Bugs ============== -Please report bugs by following the detailed instructions on our Bug -Submission page: +The SCons project welcomes bug reports and feature requests. - http://scons.tigris.org/bug-submission.html +Please make sure you send email with the problem or feature request to the SCons user's mailing list, +which you can join via the link below: -You can also send mail to the SCons developers' mailing list: + http://two.pairlist.net/mailman/listinfo/scons-users - scons-dev@scons.org +Once you have discussed your issue on the users mailing list and the community has confirmed that +it is either a new bug or a duplicate of an existing bug, then please follow the instructions the c +ommunity provides to file a new bug or to add yourself to the CC list for an existing bug + +You can explore the list of existing bugs, which may include workarounds for the problem you've +run into on GitHub Issues: + + https://github.com/SCons/scons/issues -But even if you send email to the mailing list please make sure that you ALSO -submit a bug report to the project page bug tracker, because bug reports in -email often get overlooked in the general flood of messages. Mailing Lists @@ -773,5 +767,5 @@ many contributors, including but not at all limited to: \... and many others. -Copyright (c) 2001 - 2017 The SCons Foundation +Copyright (c) 2001 - 2018 The SCons Foundation -- cgit v0.12 From 796d4f35a68e72d11c0cf3f3bae6c20332aee5fe Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 20 Feb 2018 11:31:30 -0500 Subject: changed the way JobTests.py checks for tests running in parallel to fix github issue 3102 --- src/engine/SCons/JobTests.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/engine/SCons/JobTests.py b/src/engine/SCons/JobTests.py index 6ae8e92..36dceb3 100644 --- a/src/engine/SCons/JobTests.py +++ b/src/engine/SCons/JobTests.py @@ -87,6 +87,7 @@ class Task(object): self.taskmaster = taskmaster self.was_executed = 0 self.was_prepared = 0 + def prepare(self): self.was_prepared = 1 @@ -104,9 +105,19 @@ class Task(object): self.taskmaster.guard.acquire() self.taskmaster.begin_list.append(self.i) self.taskmaster.guard.release() - + + # while task is executing, represent this in the parallel_list + # and then turn it off + self.taskmaster.parallel_list[self.i] = 1 self._do_something() - + self.taskmaster.parallel_list[self.i] = 0 + + # check if task was executing while another was also executing + for j in range(1, self.taskmaster.num_tasks): + if(self.taskmaster.parallel_list[j+1] == 1): + self.taskmaster.found_parallel = True + break + self.was_executed = 1 self.taskmaster.guard.acquire() @@ -115,7 +126,7 @@ class Task(object): def executed(self): self.taskmaster.num_executed = self.taskmaster.num_executed + 1 - + self.taskmaster.test_case.failUnless(self.was_prepared, "the task wasn't prepared") self.taskmaster.test_case.failUnless(self.was_executed, @@ -132,13 +143,16 @@ class Task(object): def postprocess(self): self.taskmaster.num_postprocessed = self.taskmaster.num_postprocessed + 1 + def exception_set(self): + pass + class RandomTask(Task): def _do_something(self): # do something that will take some random amount of time: - for i in range(random.randrange(0, num_sines, 1)): + for i in range(random.randrange(0, 1000 + num_sines, 1)): x = math.sin(i) - time.sleep(0.01) - + time.sleep(0.1) + class ExceptionTask(object): """A dummy task class for testing purposes.""" @@ -190,7 +204,10 @@ class Taskmaster(object): self.num_executed = 0 self.num_failed = 0 self.num_postprocessed = 0 + self.parallel_list = [0] * (n+1) + self.found_parallel = False self.Task = Task + # 'guard' guards 'task_begin_list' and 'task_end_list' try: import threading @@ -222,12 +239,7 @@ class Taskmaster(object): def tasks_were_serial(self): "analyze the task order to see if they were serial" - serial = 1 # assume the tasks were serial - for i in range(num_tasks): - serial = serial and (self.begin_list[i] - == self.end_list[i] - == (i + 1)) - return serial + return not self.found_parallel def exception_set(self): pass @@ -562,4 +574,4 @@ if __name__ == "__main__": # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: +# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file -- cgit v0.12 From 8f45abe8808744685ea5d6384ae6368b32d9f19d Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 20 Feb 2018 12:06:24 -0500 Subject: removed JobTests.py workaround from travis --- .travis.yml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0e02b41..9d8b96d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,12 +14,7 @@ jobs: include: - &test_job stage: Test - script: - # WORKAROUND: attempt to retry JobTests.py if it fails and then continue if it passes, if it fails ten times - # then it is a real failure not related to intermittent travis failures - - n=0; while [[ $n -lt 10 ]]; do python runtest.py src/engine/SCons/JobTests.py && break; n=$((n+1)); done; if [ "$n" -gt "9" ]; then false; fi - - echo "src/engine/SCons/JobTests.py" > exclude_jobtest - - python runtest.py -a --exclude-list exclude_jobtest || if [[ $? == 2 ]]; then true; else false; fi + script: python runtest.py -a || if [[ $? == 2 ]]; then true; else false; fi before_script: skip after_success: skip python: 2.7 @@ -71,14 +66,8 @@ jobs: - echo "parallel = True" >> .coveragerc - printf "omit =\n\t*Tests.py\n\tsrc/test_*\n\tsrc/setup.py\n\n" >> .coveragerc - echo "[path] = $PWD" >> .coveragerc - # Not including this workaround in the coverage report, because it will result - # in constantly changing coverage reports depending on the number of times - # the JobTests.py had to run to pass - # TODO: figure out how to cover JobTests.py - # - n=0; while [[ $n -lt 10 ]]; do coverage run --rcfile=$PWD/.coveragerc runtest.py src/engine/SCons/JobTests.py && break; n=$((n+1)); done; if [ "$n" -gt "9" ]; then false; fi - # exclude JobTest.py becuase we already ran that - - echo "src/engine/SCons/JobTests.py" > exclude_jobtest - - python runtest.py -l -a --exclude-list exclude_jobtest > all_tests + # get a list of all the tests to split them up + - python runtest.py -l -a > all_tests - let "start = ($(wc -l < all_tests) / ${TOTAL_BUILD_JOBS}) * (${BUILD_JOB_NUM} - 1)"; true; - let "end = ($(wc -l < all_tests) / ${TOTAL_BUILD_JOBS}) * ${BUILD_JOB_NUM}" - if (( ${BUILD_JOB_NUM} == ${TOTAL_BUILD_JOBS} )); then end=$(wc -l < all_tests); fi -- cgit v0.12 From f620c01ef13e0ecc3a7f2086c7c4ecef9e560725 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 20 Feb 2018 12:17:46 -0500 Subject: whitespace clean up, no functional changes --- src/engine/SCons/JobTests.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/JobTests.py b/src/engine/SCons/JobTests.py index 36dceb3..cc5e3c7 100644 --- a/src/engine/SCons/JobTests.py +++ b/src/engine/SCons/JobTests.py @@ -87,7 +87,6 @@ class Task(object): self.taskmaster = taskmaster self.was_executed = 0 self.was_prepared = 0 - def prepare(self): self.was_prepared = 1 @@ -105,19 +104,19 @@ class Task(object): self.taskmaster.guard.acquire() self.taskmaster.begin_list.append(self.i) self.taskmaster.guard.release() - + # while task is executing, represent this in the parallel_list - # and then turn it off + # and then turn it off self.taskmaster.parallel_list[self.i] = 1 self._do_something() self.taskmaster.parallel_list[self.i] = 0 - + # check if task was executing while another was also executing for j in range(1, self.taskmaster.num_tasks): if(self.taskmaster.parallel_list[j+1] == 1): self.taskmaster.found_parallel = True break - + self.was_executed = 1 self.taskmaster.guard.acquire() @@ -126,7 +125,7 @@ class Task(object): def executed(self): self.taskmaster.num_executed = self.taskmaster.num_executed + 1 - + self.taskmaster.test_case.failUnless(self.was_prepared, "the task wasn't prepared") self.taskmaster.test_case.failUnless(self.was_executed, @@ -152,7 +151,7 @@ class RandomTask(Task): for i in range(random.randrange(0, 1000 + num_sines, 1)): x = math.sin(i) time.sleep(0.1) - + class ExceptionTask(object): """A dummy task class for testing purposes.""" @@ -574,4 +573,4 @@ if __name__ == "__main__": # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From fd6d431634dc461cca47e11d4a89cc1737220fe2 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 20 Feb 2018 15:23:20 -0500 Subject: reduced number of calculations and sleep time to improve test speed --- src/engine/SCons/JobTests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/SCons/JobTests.py b/src/engine/SCons/JobTests.py index cc5e3c7..39918db 100644 --- a/src/engine/SCons/JobTests.py +++ b/src/engine/SCons/JobTests.py @@ -52,7 +52,7 @@ def get_cpu_nums(): return 1 # Default # a large number -num_sines = 10000 +num_sines = 500 # how many parallel jobs to perform for the test num_jobs = get_cpu_nums()*2 @@ -148,9 +148,9 @@ class Task(object): class RandomTask(Task): def _do_something(self): # do something that will take some random amount of time: - for i in range(random.randrange(0, 1000 + num_sines, 1)): + for i in range(random.randrange(0, 100 + num_sines, 1)): x = math.sin(i) - time.sleep(0.1) + time.sleep(0.01) class ExceptionTask(object): """A dummy task class for testing purposes.""" @@ -282,7 +282,7 @@ class ParallelTestCase(unittest.TestCase): class SleepTask(Task): def _do_something(self): - time.sleep(0.1) + time.sleep(0.01) global SaveThreadPool SaveThreadPool = SCons.Job.ThreadPool @@ -292,7 +292,7 @@ class ParallelTestCase(unittest.TestCase): ThreadPoolCallList.append('put(%s)' % task.i) return SaveThreadPool.put(self, task) def get(self): - time.sleep(0.5) + time.sleep(0.05) result = SaveThreadPool.get(self) ThreadPoolCallList.append('get(%s)' % result[0].i) return result -- cgit v0.12 From a3d092855944aef928c5c3d6cba23908dbaa17f5 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 17 Mar 2018 13:50:50 -0400 Subject: Initial creation of issue template. --- .github/issue_template.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/issue_template.md diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 0000000..6af45d1 --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,10 @@ +# Please bring your issue to the SCons users mailing list before filing an issue here +# See: http://scons.org/bugs.html + +# If the issue is confirmed to be a bug please include the following information +* Version of SCons +* Version of Python +* How you installed SCons +* What Platform are you on? (Linux/Windows and which version) +* How to reproduce your issue? Please include a small self contained reproducer. Likely a SConstruct should do for most issues. + -- cgit v0.12 From 0a16004dfdafb829780f150a82784f0537755f57 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 17 Mar 2018 16:13:13 -0400 Subject: Update issue_template.md --- .github/issue_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index 6af45d1..3298b73 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -7,4 +7,4 @@ * How you installed SCons * What Platform are you on? (Linux/Windows and which version) * How to reproduce your issue? Please include a small self contained reproducer. Likely a SConstruct should do for most issues. - +* Link to SCons Users thread discussing your issue. -- cgit v0.12 From e7b15e2288afa13cab09943bb2a5fa723cfa789e Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 20 Mar 2018 16:46:30 -0400 Subject: Update issue_template.md --- .github/issue_template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/issue_template.md b/.github/issue_template.md index 3298b73..9f632b0 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -4,6 +4,7 @@ # If the issue is confirmed to be a bug please include the following information * Version of SCons * Version of Python +* Which python distribution if applicable (python.org, cygwin, anaconda, macports, brew,etc) * How you installed SCons * What Platform are you on? (Linux/Windows and which version) * How to reproduce your issue? Please include a small self contained reproducer. Likely a SConstruct should do for most issues. -- cgit v0.12 From 0458fbeaddae9f4bb1e977ccd642a201c7b46c5f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 24 Mar 2018 12:42:09 -0700 Subject: fix #3106 MSVC handling MSVC_BATCH with targetdir which requires escaping. --- src/CHANGES.txt | 2 ++ src/engine/SCons/Tool/msvc.py | 5 ++++- test/MSVC/MSVC_BATCH-spaces-targetdir.py | 11 +++++++++++ test/MSVC/MSVC_BATCH-spaces-targetdir/SConstruct | 8 ++++++++ test/MSVC/MSVC_BATCH-spaces-targetdir/src/a.c | 15 +++++++++++++++ test/MSVC/MSVC_BATCH-spaces-targetdir/src/b.c | 5 +++++ test/MSVC/MSVC_BATCH-spaces-targetdir/src/c.c | 5 +++++ test/MSVC/batch.py | 7 ++++--- 8 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 test/MSVC/MSVC_BATCH-spaces-targetdir.py create mode 100644 test/MSVC/MSVC_BATCH-spaces-targetdir/SConstruct create mode 100644 test/MSVC/MSVC_BATCH-spaces-targetdir/src/a.c create mode 100644 test/MSVC/MSVC_BATCH-spaces-targetdir/src/b.c create mode 100644 test/MSVC/MSVC_BATCH-spaces-targetdir/src/c.c diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 954af11..1cdc943 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -25,6 +25,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From William Deegan: - Remove long deprecated SCons.Options code and tests. This removes BoolOption,EnumOption, ListOption,PackageOption, and PathOption which have been replaced by *Variable() many years ago. + - Fix issue # 3106 MSVC if using MSVC_BATCH and target dir had a space would fail due to quirk in + MSVC's handling of escaped targetdirs when batch compiling. From Andrew Featherstone - Removed unused --warn options from the man page and source code. diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index e1b0536..f078c62 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -195,7 +195,10 @@ def msvc_output_flag(target, source, env, for_signature): # that the test(s) for this can be run on non-Windows systems # without having a hard-coded backslash mess up command-line # argument parsing. - return '/Fo${TARGET.dir}' + os.sep + # Adding double os.sep's as if the TARGET.dir has a space or otherwise + # needs to be quoted they are needed per MSVC's odd behavior + # See: https://github.com/SCons/scons/issues/3106 + return '/Fo${TARGET.dir}' + os.sep*2 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR", batch_key=msvc_batch_key, diff --git a/test/MSVC/MSVC_BATCH-spaces-targetdir.py b/test/MSVC/MSVC_BATCH-spaces-targetdir.py new file mode 100644 index 0000000..298e10e --- /dev/null +++ b/test/MSVC/MSVC_BATCH-spaces-targetdir.py @@ -0,0 +1,11 @@ +import TestSCons + + + +test = TestSCons.TestSCons() + +test.skip_if_not_msvc() + + +test.dir_fixture('MSVC_BATCH-spaces-targetdir') +test.run() \ No newline at end of file diff --git a/test/MSVC/MSVC_BATCH-spaces-targetdir/SConstruct b/test/MSVC/MSVC_BATCH-spaces-targetdir/SConstruct new file mode 100644 index 0000000..da8002b --- /dev/null +++ b/test/MSVC/MSVC_BATCH-spaces-targetdir/SConstruct @@ -0,0 +1,8 @@ +import os.path + +env=Environment(MSVC_BATCH=True) + +td='tar ge tdir' +VariantDir(td,'src') +env.Program(os.path.join(td,'test_program'), + [os.path.join(td,a) for a in ['a.c','b.c','c.c']]) diff --git a/test/MSVC/MSVC_BATCH-spaces-targetdir/src/a.c b/test/MSVC/MSVC_BATCH-spaces-targetdir/src/a.c new file mode 100644 index 0000000..1741de8 --- /dev/null +++ b/test/MSVC/MSVC_BATCH-spaces-targetdir/src/a.c @@ -0,0 +1,15 @@ +#include + +extern void myfuncb(); +extern void myfuncc(); + + +void myfunca() { + printf("myfunca\n"); +} + +int main(int argc, char *argv[]) { + myfunca(); + myfuncb(); + myfuncc(); +} \ No newline at end of file diff --git a/test/MSVC/MSVC_BATCH-spaces-targetdir/src/b.c b/test/MSVC/MSVC_BATCH-spaces-targetdir/src/b.c new file mode 100644 index 0000000..e03c5d0 --- /dev/null +++ b/test/MSVC/MSVC_BATCH-spaces-targetdir/src/b.c @@ -0,0 +1,5 @@ +#include + +void myfuncb() { + printf("myfuncb\n"); +} \ No newline at end of file diff --git a/test/MSVC/MSVC_BATCH-spaces-targetdir/src/c.c b/test/MSVC/MSVC_BATCH-spaces-targetdir/src/c.c new file mode 100644 index 0000000..1c262d3 --- /dev/null +++ b/test/MSVC/MSVC_BATCH-spaces-targetdir/src/c.c @@ -0,0 +1,5 @@ +#include + +void myfuncc() { + printf("myfuncc\n"); +} \ No newline at end of file diff --git a/test/MSVC/batch.py b/test/MSVC/batch.py index 8b7945b..af5b43e 100644 --- a/test/MSVC/batch.py +++ b/test/MSVC/batch.py @@ -72,6 +72,7 @@ for infile in sys.argv[2:]: """) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) cccom = r'%(_python_)s fake_cl.py $_MSVC_OUTPUT_FLAG $CHANGED_SOURCES' linkcom = r'%(_python_)s fake_link.py ${TARGET.windows} $SOURCES' env = Environment(tools=['msvc', 'mslink'], @@ -96,7 +97,7 @@ test.run(arguments = 'MSVC_BATCH=1 .') test.must_match('prog.exe', "prog.c\nf1.c\nf2.c\n", mode='r') test.must_match('fake_cl.log', """\ -/Fo. prog.c f1.c f2.c +/Fo.\\ prog.c f1.c f2.c """, mode='r') test.up_to_date(options = 'MSVC_BATCH=1', arguments = '.') @@ -109,8 +110,8 @@ test.run(arguments = 'MSVC_BATCH=1 .') test.must_match('prog.exe', "prog.c\nf1.c 2\nf2.c\n", mode='r') test.must_match('fake_cl.log', """\ -/Fo. prog.c f1.c f2.c -/Fo. f1.c +/Fo.\\ prog.c f1.c f2.c +/Fo.\\ f1.c """, mode='r') test.up_to_date(options = 'MSVC_BATCH=1', arguments = '.') -- cgit v0.12 From b42fd7c0e76ca252ce778e5258be1e770561704d Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 24 Mar 2018 17:23:50 -0400 Subject: Fix test so use os.sep instead of hardcoded backslash for MSVC_BATCH=true /Fo argument --- test/MSVC/batch.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/MSVC/batch.py b/test/MSVC/batch.py index af5b43e..0d3063f 100644 --- a/test/MSVC/batch.py +++ b/test/MSVC/batch.py @@ -32,6 +32,7 @@ explicit suffix settings so that the test should work when run on any platform. """ +import os import TestSCons test = TestSCons.TestSCons() @@ -97,8 +98,8 @@ test.run(arguments = 'MSVC_BATCH=1 .') test.must_match('prog.exe', "prog.c\nf1.c\nf2.c\n", mode='r') test.must_match('fake_cl.log', """\ -/Fo.\\ prog.c f1.c f2.c -""", mode='r') +/Fo.%s prog.c f1.c f2.c +"""%os.sep, mode='r') test.up_to_date(options = 'MSVC_BATCH=1', arguments = '.') @@ -110,9 +111,9 @@ test.run(arguments = 'MSVC_BATCH=1 .') test.must_match('prog.exe', "prog.c\nf1.c 2\nf2.c\n", mode='r') test.must_match('fake_cl.log', """\ -/Fo.\\ prog.c f1.c f2.c -/Fo.\\ f1.c -""", mode='r') +/Fo.%s prog.c f1.c f2.c +/Fo.%s f1.c +"""%(os.sep, os.sep), mode='r') test.up_to_date(options = 'MSVC_BATCH=1', arguments = '.') -- cgit v0.12 From 3ddce3f72dec6d9e4cc0381a1c00164264bef39d Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 24 Mar 2018 20:16:14 -0400 Subject: Reenable parallel builds with Pypy --- src/CHANGES.txt | 1 + src/engine/SCons/Script/Main.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 954af11..64c0893 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -25,6 +25,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From William Deegan: - Remove long deprecated SCons.Options code and tests. This removes BoolOption,EnumOption, ListOption,PackageOption, and PathOption which have been replaced by *Variable() many years ago. + - Re-Enable parallel SCons (-j) when running via Pypy From Andrew Featherstone - Removed unused --warn options from the man page and source code. diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index c810634..f111d35 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -48,6 +48,7 @@ import sys import time import traceback import sysconfig +import platform import SCons.CacheDir import SCons.Debug @@ -1253,7 +1254,8 @@ def _build_targets(fs, options, targets, target_top): BuildTask.options = options - python_has_threads = sysconfig.get_config_var('WITH_THREAD') + is_pypy = platform.python_implementation() == 'PyPy' + python_has_threads = sysconfig.get_config_var('WITH_THREAD') or is_pypy # to check if python configured with threads. global num_jobs num_jobs = options.num_jobs -- cgit v0.12 From 0cefc2bf199dd56d16b752042f9569a420b6ddab Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 25 Mar 2018 12:53:56 -0400 Subject: rename clang command package to clangCommon as it was breaking imports test. --- src/engine/SCons/Tool/clang.py | 2 +- src/engine/SCons/Tool/clang/__init__.py | 16 ---------------- src/engine/SCons/Tool/clangCommon/__init__.py | 17 +++++++++++++++++ src/engine/SCons/Tool/clangxx.py | 2 +- 4 files changed, 19 insertions(+), 18 deletions(-) delete mode 100644 src/engine/SCons/Tool/clang/__init__.py create mode 100644 src/engine/SCons/Tool/clangCommon/__init__.py diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index aba5598..c51e753 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -45,7 +45,7 @@ import sys import SCons.Util import SCons.Tool.cc -from SCons.Tool.clang import get_clang_install_dirs +from SCons.Tool.clangCommon import get_clang_install_dirs compilers = ['clang'] diff --git a/src/engine/SCons/Tool/clang/__init__.py b/src/engine/SCons/Tool/clang/__init__.py deleted file mode 100644 index 775ba80..0000000 --- a/src/engine/SCons/Tool/clang/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Common routines and data for clang tools -""" - -clang_win32_dirs = [ - r'C:\Program Files\LLVM\bin', - r'C:\cygwin64\bin', - r'C:\msys64', - r'C:\cygwin\bin', - r'C:\msys', -] - -def get_clang_install_dirs(platform): - if platform == 'win32': - return clang_win32_dirs - else return [] \ No newline at end of file diff --git a/src/engine/SCons/Tool/clangCommon/__init__.py b/src/engine/SCons/Tool/clangCommon/__init__.py new file mode 100644 index 0000000..37efbf6 --- /dev/null +++ b/src/engine/SCons/Tool/clangCommon/__init__.py @@ -0,0 +1,17 @@ +""" +Common routines and data for clang tools +""" + +clang_win32_dirs = [ + r'C:\Program Files\LLVM\bin', + r'C:\cygwin64\bin', + r'C:\msys64', + r'C:\cygwin\bin', + r'C:\msys', +] + +def get_clang_install_dirs(platform): + if platform == 'win32': + return clang_win32_dirs + else: + return [] \ No newline at end of file diff --git a/src/engine/SCons/Tool/clangxx.py b/src/engine/SCons/Tool/clangxx.py index 2bf4cf4..7194d9e 100644 --- a/src/engine/SCons/Tool/clangxx.py +++ b/src/engine/SCons/Tool/clangxx.py @@ -46,7 +46,7 @@ import sys import SCons.Tool import SCons.Util import SCons.Tool.cxx -from SCons.Tool.clang import get_clang_install_dirs +from SCons.Tool.clangCommon import get_clang_install_dirs compilers = ['clang++'] -- cgit v0.12 From 14d20880efb6029420ff7c9fd55da4ee26893098 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 25 Mar 2018 13:41:06 -0400 Subject: Fix clang and mingw tests --- src/engine/SCons/Tool/mingw.py | 7 ++++++- test/import.py | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index 4121902..de493f0 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -156,7 +156,12 @@ def generate(env): env['PROGSUFFIX'] = '.exe' def exists(env): - return find(env) + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=[r'c:\MinGW\bin',]) + if mingw: + mingw_bin_dir = os.path.dirname(mingw) + env.AppendENVPath('PATH', mingw_bin_dir) + + return mingw # Local Variables: # tab-width:4 diff --git a/test/import.py b/test/import.py index b6e5a8d..11fee9c 100644 --- a/test/import.py +++ b/test/import.py @@ -75,6 +75,8 @@ ignore = ('__init__.py', '386asm.py', 'linkloc.py', # Directory of common stuff for MSVC and MSVS 'MSCommon', + # clang common + "clangCommon", # Sun pkgchk and pkginfo common stuff 'sun_pkg.py', # RPM utilities -- cgit v0.12 From 23f357c1addfe7edbccbf0b804b7b29c12b5049c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 25 Mar 2018 13:51:42 -0400 Subject: added more default mingw paths per comments on pull request --- src/engine/SCons/Tool/mingw.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index de493f0..f2e9442 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -43,6 +43,14 @@ import SCons.Tool import SCons.Util +mingw_paths = [ + r'c:\MinGW\bin', + r'C:\cygwin64\bin', + r'C:\msys64', + r'C:\cygwin\bin', + r'C:\msys', +] + def shlib_generator(target, source, env, for_signature): cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) @@ -111,7 +119,7 @@ key_program = 'mingw32-gcc' def generate(env): - mingw = SCons.Tool.find_program_path(env, key_program, default_paths=[r'c:\MinGW\bin',]) + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=mingw_paths) if mingw: mingw_bin_dir = os.path.dirname(mingw) env.AppendENVPath('PATH', mingw_bin_dir) @@ -156,7 +164,7 @@ def generate(env): env['PROGSUFFIX'] = '.exe' def exists(env): - mingw = SCons.Tool.find_program_path(env, key_program, default_paths=[r'c:\MinGW\bin',]) + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=mingw_paths) if mingw: mingw_bin_dir = os.path.dirname(mingw) env.AppendENVPath('PATH', mingw_bin_dir) -- cgit v0.12 From 1724fba161d4123f092ba3f850418d78f58d4bad Mon Sep 17 00:00:00 2001 From: grbd Date: Sun, 1 Apr 2018 15:26:19 +0100 Subject: Also search for SConstruct.py, to make VStudio debugging easier --- src/engine/SCons/Script/Main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index f111d35..6bdd8fa 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -623,7 +623,7 @@ def _SConstruct_exists(dirname='', repositories=[], filelist=None): current directory. """ if not filelist: - filelist = ['SConstruct', 'Sconstruct', 'sconstruct'] + filelist = ['SConstruct', 'Sconstruct', 'sconstruct', 'SConstruct.py'] for file in filelist: sfile = os.path.join(dirname, file) if os.path.isfile(sfile): -- cgit v0.12 From c9384f44e9a47e3e2e028667905889fe9206b579 Mon Sep 17 00:00:00 2001 From: grbd Date: Sun, 1 Apr 2018 15:52:14 +0100 Subject: update to changes file --- src/CHANGES.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 64c0893..72dc513 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,6 +7,10 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE + From Richard West: + - Add SConstruct.py to the search path for the root SConstruct file. + Allows easier debugging within Visual Studio + From Daniel Moody: - Set the pickling protocal back to highest which was causing issues with variant dir tests. This will cause issues if reading sconsigns -- cgit v0.12 From ea55726884e48b2b02b1a094dcc38612dd02ad10 Mon Sep 17 00:00:00 2001 From: grbd Date: Sun, 1 Apr 2018 23:11:23 +0100 Subject: Added test for the use of SConstruct.py --- test/SConstruct.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/SConstruct.py b/test/SConstruct.py index cebdf04..62d383d 100644 --- a/test/SConstruct.py +++ b/test/SConstruct.py @@ -39,6 +39,15 @@ scons: \*\*\* No SConstruct file found. wpath = test.workpath() +test.write('SConstruct.py', """ +import os +print("SConstruct.py "+os.getcwd()) +""") + +test.run(arguments = ".", + stdout = test.wrap_stdout(read_str = 'SConstruct.py %s\n' % wpath, + build_str = "scons: `.' is up to date.\n")) + test.write('sconstruct', """ import os print("sconstruct "+os.getcwd()) -- cgit v0.12 From d5a21f6b86636a84d8664364e87102775bbb7a70 Mon Sep 17 00:00:00 2001 From: grbd Date: Mon, 2 Apr 2018 12:59:03 +0100 Subject: updates to docs for SConstruct.py --- doc/design/native.xml | 2 +- doc/man/scons.xml | 14 +++++++++----- doc/scons.mod | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/design/native.xml b/doc/design/native.xml index cd4edaf..25f0d95 100644 --- a/doc/design/native.xml +++ b/doc/design/native.xml @@ -64,7 +64,7 @@ By default, the &SCons; utility searches for a file named - &SConstruct;, &Sconstruct; or &sconstruct; (in that order) in the + &SConstruct;, &Sconstruct;, &sconstruct; or &SConstruct.py; (in that order) in the current directory, and reads its configuration from the first file found. A command-line option exists to read a different file name. diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 03a3478..57c5191 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -105,8 +105,9 @@ rebuild them. searches for a file named SConstruct, Sconstruct, -or sconstruct +or +SConstruct.py (in that order) in the current directory and reads its configuration from the first file found. An alternate file name may be @@ -635,8 +636,9 @@ yet have any results in the cache. before searching for the SConstruct, Sconstruct, -or sconstruct +or +SConstruct.py file, or doing anything else. Multiple @@ -649,8 +651,9 @@ equivalent to except that it will search for SConstruct, Sconstruct, -or sconstruct +or +SConstruct.py in the specified directory.) @@ -1658,9 +1661,10 @@ scons --tree=all,prune,status target Walks up the directory structure until an SConstruct , -Sconstruct -or +Sconstruct , sconstruct +or +SConstruct.py file is found, and uses that as the top of the directory tree. If no targets are specified on the command line, diff --git a/doc/scons.mod b/doc/scons.mod index 448a212..5fa8e13 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -137,6 +137,7 @@ SConstruct"> Sconstruct"> sconstruct"> +SConstruct.py"> .sconsign"> src"> -- cgit v0.12 From cc52bda4954ae1327e7d98cfb4418c64a55056ec Mon Sep 17 00:00:00 2001 From: grbd Date: Mon, 2 Apr 2018 20:14:24 +0100 Subject: Added sconstruct.py lowercase --- doc/design/native.xml | 2 +- doc/man/scons.xml | 20 ++++++++++++-------- doc/scons.mod | 1 + src/CHANGES.txt | 2 +- src/engine/SCons/Script/Main.py | 2 +- test/SConstruct.py | 9 +++++++++ 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/doc/design/native.xml b/doc/design/native.xml index 25f0d95..1329817 100644 --- a/doc/design/native.xml +++ b/doc/design/native.xml @@ -64,7 +64,7 @@ By default, the &SCons; utility searches for a file named - &SConstruct;, &Sconstruct;, &sconstruct; or &SConstruct.py; (in that order) in the + &SConstruct;, &Sconstruct;, &sconstruct;, &SConstruct.py or &sconstruct.py; (in that order) in the current directory, and reads its configuration from the first file found. A command-line option exists to read a different file name. diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 57c5191..cd6541b 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -105,9 +105,10 @@ rebuild them. searches for a file named SConstruct, Sconstruct, -sconstruct -or +sconstruct, SConstruct.py +or +sconstruct.py (in that order) in the current directory and reads its configuration from the first file found. An alternate file name may be @@ -636,9 +637,10 @@ yet have any results in the cache. before searching for the SConstruct, Sconstruct, -sconstruct -or +sconstruct, SConstruct.py +or +sconstruct.py file, or doing anything else. Multiple @@ -651,9 +653,10 @@ equivalent to except that it will search for SConstruct, Sconstruct, -sconstruct -or +sconstruct, SConstruct.py +or +sconstruct.py in the specified directory.) @@ -1662,9 +1665,10 @@ scons --tree=all,prune,status target Walks up the directory structure until an SConstruct , Sconstruct , -sconstruct -or +sconstruct , SConstruct.py +or +sconstruct.py file is found, and uses that as the top of the directory tree. If no targets are specified on the command line, diff --git a/doc/scons.mod b/doc/scons.mod index 5fa8e13..1d207c9 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -138,6 +138,7 @@ Sconstruct"> sconstruct"> SConstruct.py"> +sconstruct.py"> .sconsign"> src"> diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 72dc513..b7aa9dd 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,7 +8,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Richard West: - - Add SConstruct.py to the search path for the root SConstruct file. + - Add SConstruct.py and sconstruct.py to the search path for the root SConstruct file. Allows easier debugging within Visual Studio From Daniel Moody: diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 6bdd8fa..18c493c 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -623,7 +623,7 @@ def _SConstruct_exists(dirname='', repositories=[], filelist=None): current directory. """ if not filelist: - filelist = ['SConstruct', 'Sconstruct', 'sconstruct', 'SConstruct.py'] + filelist = ['SConstruct', 'Sconstruct', 'sconstruct', 'SConstruct.py', 'sconstruct.py'] for file in filelist: sfile = os.path.join(dirname, file) if os.path.isfile(sfile): diff --git a/test/SConstruct.py b/test/SConstruct.py index 62d383d..3cf1c02 100644 --- a/test/SConstruct.py +++ b/test/SConstruct.py @@ -39,6 +39,15 @@ scons: \*\*\* No SConstruct file found. wpath = test.workpath() +test.write('sconstruct.py', """ +import os +print("sconstruct.py "+os.getcwd()) +""") + +test.run(arguments = ".", + stdout = test.wrap_stdout(read_str = 'sconstruct.py %s\n' % wpath, + build_str = "scons: `.' is up to date.\n")) + test.write('SConstruct.py', """ import os print("SConstruct.py "+os.getcwd()) -- cgit v0.12 From 5e5cccde062fe18290048ab4adb4b2270fdf6c94 Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Thu, 29 Mar 2018 15:40:06 +0200 Subject: Fix EnvironmentError printing bug EnvironmentErrors does not need to have the attributes strerror or errno. This commit makes the code robust for such situation. One example of such EnvironmentError is CacheDir.py: raise SCons.Errors.EnvironmentError(msg) Signed-off-by: Fredrik Medley --- src/CHANGES.txt | 5 +++++ src/engine/SCons/Errors.py | 11 +++++------ src/engine/SCons/ErrorsTests.py | 25 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 64c0893..4d6d191 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -48,6 +48,11 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE Specifically, this fixes the test cases use by Configure.CheckCC() which would fail when using -Wstrict-prototypes. + From Fredrik Medley: + - Fix exception when printing of EnviromentError messages. + Specifically, this fixes error reporting of the race condition when + initializing the cache which error previously was hidden. + RELEASE 3.0.1 - Mon, 12 Nov 2017 15:31:33 -0700 diff --git a/src/engine/SCons/Errors.py b/src/engine/SCons/Errors.py index dae20b2..3746d5d 100644 --- a/src/engine/SCons/Errors.py +++ b/src/engine/SCons/Errors.py @@ -190,14 +190,13 @@ def convert_to_BuildError(status, exc_info=None): # error, which might be different from the target being built # (for example, failure to create the directory in which the # target file will appear). - try: - filename = status.filename - except AttributeError: - filename = None + filename = getattr(status, 'filename', None) + strerror = getattr(status, 'strerror', str(status)) + errno = getattr(status, 'errno', 2) buildError = BuildError( - errstr=status.strerror, - status=status.errno, + errstr=strerror, + status=errno, exitstatus=2, filename=filename, exc_info=exc_info) diff --git a/src/engine/SCons/ErrorsTests.py b/src/engine/SCons/ErrorsTests.py index c38158d..7819580 100644 --- a/src/engine/SCons/ErrorsTests.py +++ b/src/engine/SCons/ErrorsTests.py @@ -23,6 +23,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import errno +import os import sys import unittest @@ -100,6 +102,29 @@ class ErrorsTestCase(unittest.TestCase): except SCons.Errors.ExplicitExit as e: assert e.node == "node" + def test_convert_EnvironmentError_to_BuildError(self): + """Test the convert_to_BuildError function on EnvironmentError + exceptions. + """ + ee = SCons.Errors.EnvironmentError("test env error") + be = SCons.Errors.convert_to_BuildError(ee) + assert be.errstr == "test env error" + assert be.status == 2 + assert be.exitstatus == 2 + assert be.filename is None + + def test_convert_OSError_to_BuildError(self): + """Test the convert_to_BuildError function on OSError + exceptions. + """ + ose = OSError(7, 'test oserror') + be = SCons.Errors.convert_to_BuildError(ose) + assert be.errstr == 'test oserror' + assert be.status == 7 + assert be.exitstatus == 2 + assert be.filename is None + + if __name__ == "__main__": suite = unittest.makeSuite(ErrorsTestCase, 'test_') TestUnit.run(suite) -- cgit v0.12 From 5fc33d190ea7ebdba97703d8ff4ec3f3b49a46f9 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sat, 23 Dec 2017 23:24:32 -0500 Subject: updated the msvs 10 test to check and make sure the cl.exe is there as well. --- test/MSVS/vs-10.0-exec.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/MSVS/vs-10.0-exec.py b/test/MSVS/vs-10.0-exec.py index 1a4b59a..090bde8 100644 --- a/test/MSVS/vs-10.0-exec.py +++ b/test/MSVS/vs-10.0-exec.py @@ -55,11 +55,15 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -print("os.environ.update(%%s)" %% repr(env['ENV'])) +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) -exec(test.stdout()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) +exec(test.stdout()) test.subdir('sub dir') -- cgit v0.12 From 7d6468b7bf017ec8b0a412342b15d2a4d940c89d Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 9 Apr 2018 15:34:24 -0500 Subject: updated yacc tool to add default paths, updated to test to use the tool specifically and know when to skip on windows. --- src/engine/SCons/Tool/yacc.py | 9 +++++++++ test/YACC/YACC.py | 8 ++++---- test/YACC/live-check-output-cleaned.py | 2 +- test/YACC/live.py | 12 +++++++++--- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index 648433b..be65104 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -34,6 +34,7 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path +import sys import SCons.Defaults import SCons.Tool @@ -113,6 +114,14 @@ def generate(env): cxx_file.add_action('.yy', YaccAction) cxx_file.add_emitter('.yy', yyEmitter) + if(sys.platform == 'win32'): + bison = SCons.Tool.find_program_path(env, 'bison', default_paths=['C:\\cygwin64\\bin', 'C:\\cygwin\\bin', 'C:\\msys', 'C:\\msys64' ]) + if bison: + bison_bin_dir = os.path.dirname(bison) + env.AppendENVPath('PATH', bison_bin_dir) + else: + SCons.Warnings.Warning('yacc tool requested, but bison binary not found in ENV PATH') + env['YACC'] = env.Detect('bison') or 'yacc' env['YACCFLAGS'] = SCons.Util.CLVar('') env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' diff --git a/test/YACC/YACC.py b/test/YACC/YACC.py index 3fc1f7c..b27c2a7 100644 --- a/test/YACC/YACC.py +++ b/test/YACC/YACC.py @@ -53,10 +53,10 @@ env.CFile(target = 'ddd', source = 'ddd.ym') test.run(arguments = '.', stderr = None) -test.must_match('aaa.c', "aaa.y\nmyyacc.py\n") -test.must_match('bbb.c', "bbb.yacc\nmyyacc.py\n") -test.must_match('ccc.cc', "ccc.yacc\nmyyacc.py\n") -test.must_match('ddd.m', "ddd.yacc\nmyyacc.py\n") +test.must_match('aaa.c', "aaa.y" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('bbb.c', "bbb.yacc" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('ccc.cc', "ccc.yacc" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('ddd.m', "ddd.yacc" + os.linesep + "myyacc.py" + os.linesep) diff --git a/test/YACC/live-check-output-cleaned.py b/test/YACC/live-check-output-cleaned.py index a6240c9..8329b94 100644 --- a/test/YACC/live-check-output-cleaned.py +++ b/test/YACC/live-check-output-cleaned.py @@ -40,7 +40,7 @@ if not yacc: test.skip_test('No yacc or bison found; skipping test.\n') test.write('SConstruct', """ -foo = Environment(YACCFLAGS='-v -d') +foo = Environment(YACCFLAGS='-v -d', tools = ['default', 'yacc']) foo.CFile(source = 'foo.y') """ % locals()) diff --git a/test/YACC/live.py b/test/YACC/live.py index 6dd08f7..35f6c37 100644 --- a/test/YACC/live.py +++ b/test/YACC/live.py @@ -29,6 +29,8 @@ Test YACC and YACCFLAGS with a live yacc compiler. """ import TestSCons +import sys +import os _exe = TestSCons._exe _python_ = TestSCons._python_ @@ -40,12 +42,16 @@ yacc = test.where_is('yacc') or test.where_is('bison') if not yacc: test.skip_test('No yacc or bison found; skipping test.\n') +if sys.platform == 'win32': + if not test.where_is('gcc'): + test.skip_test('No gcc found on windows; skipping test.\n') + test.file_fixture('wrapper.py') test.write('SConstruct', """ -foo = Environment(YACCFLAGS='-d') +foo = Environment(YACCFLAGS='-d', tools = ['default', 'yacc']) yacc = foo.Dictionary('YACC') -bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc) +bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc, tools = ['default', 'yacc']) foo.Program(target = 'foo', source = 'foo.y') bar.Program(target = 'bar', source = 'bar.y') foo.Program(target = 'hello', source = ['hello.cpp']) @@ -152,7 +158,7 @@ test.run(arguments = 'bar' + _exe) test.up_to_date(arguments = 'bar' + _exe) -test.must_match(test.workpath('wrapper.out'), "wrapper.py\n") +test.must_match(test.workpath('wrapper.out'), "wrapper.py" + os.linesep) test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n") -- cgit v0.12 From e8ee5e89e0c301324aaa3b78a7a2e8c8919d53e8 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 10 Apr 2018 08:03:43 -0500 Subject: removed binary read and write so newlines are crossplatform and updated path for appveyor script --- .appveyor.yml | 2 +- test/YACC/YACC-fixture/myyacc.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index a9d2dfc..a735070 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,7 +2,7 @@ image: Visual Studio 2017 shallow_clone: true install: - - "set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" + - "set PATH=%PYTHON%;%PYTHON%\\Scripts;C:\\cygwin64\\bin;C:\\msys64;%PATH%" - python --version - pip install lxml - pip install pypiwin32 diff --git a/test/YACC/YACC-fixture/myyacc.py b/test/YACC/YACC-fixture/myyacc.py index c2e1abf..756c98f 100644 --- a/test/YACC/YACC-fixture/myyacc.py +++ b/test/YACC/YACC-fixture/myyacc.py @@ -4,10 +4,10 @@ cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:', []) output = None opt_string = '' for opt, arg in cmd_opts: - if opt == '-o': output = open(arg, 'wb') + if opt == '-o': output = open(arg, 'w') else: opt_string = opt_string + ' ' + opt for a in args: - contents = open(a, 'rb').read() - output.write(contents.replace(b'YACC', b'myyacc.py')) + contents = open(a, 'r').read() + output.write(contents.replace('YACC', 'myyacc.py')) output.close() sys.exit(0) -- cgit v0.12 From 5f3e6792dbaa74b3c384f9dcbf6ff71e984ba766 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 10 Apr 2018 11:17:40 -0500 Subject: removed unnecessary parans --- src/engine/SCons/Tool/yacc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index be65104..a43b3b3 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -114,7 +114,7 @@ def generate(env): cxx_file.add_action('.yy', YaccAction) cxx_file.add_emitter('.yy', yyEmitter) - if(sys.platform == 'win32'): + if sys.platform == 'win32': bison = SCons.Tool.find_program_path(env, 'bison', default_paths=['C:\\cygwin64\\bin', 'C:\\cygwin\\bin', 'C:\\msys', 'C:\\msys64' ]) if bison: bison_bin_dir = os.path.dirname(bison) -- cgit v0.12 From f526583e514cb1cbcfd44cd26b95d4025b3ba7f8 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 10 Apr 2018 12:40:36 -0500 Subject: moved default paths for cywing and mingw to their own platform modules. --- src/engine/SCons/Platform/cygwin.py | 9 +++++++++ src/engine/SCons/Platform/mingw.py | 39 +++++++++++++++++++++++++++++++++++++ src/engine/SCons/Tool/yacc.py | 4 +++- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/engine/SCons/Platform/mingw.py diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py index 8b4669c..f6c5086 100644 --- a/src/engine/SCons/Platform/cygwin.py +++ b/src/engine/SCons/Platform/cygwin.py @@ -32,9 +32,18 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import sys + from . import posix from SCons.Platform import TempFileMunge +CYGWIN_DEFAULT_PATHS = [] +if sys.platform == 'win32': + CYGWIN_DEFAULT_PATHS = [ + r'C:\cygwin64\bin', + r'C:\cygwin\bin' + ] + def generate(env): posix.generate(env) diff --git a/src/engine/SCons/Platform/mingw.py b/src/engine/SCons/Platform/mingw.py new file mode 100644 index 0000000..73633d7 --- /dev/null +++ b/src/engine/SCons/Platform/mingw.py @@ -0,0 +1,39 @@ +"""SCons.Platform.mingw + +Platform-specific initialization for the MinGW system. + +""" + +# +# __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 sys + +MINGW_DEFAULT_PATHS = [] +if sys.platform == 'win32': + MINGW_DEFAULT_PATHS = [ + r'C:\msys64', + r'C:\msys' + ] \ No newline at end of file diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index a43b3b3..cd9b9a8 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -39,6 +39,8 @@ import sys import SCons.Defaults import SCons.Tool import SCons.Util +from SCons.Platform.mingw import MINGW_DEFAULT_PATHS +from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") @@ -115,7 +117,7 @@ def generate(env): cxx_file.add_emitter('.yy', yyEmitter) if sys.platform == 'win32': - bison = SCons.Tool.find_program_path(env, 'bison', default_paths=['C:\\cygwin64\\bin', 'C:\\cygwin\\bin', 'C:\\msys', 'C:\\msys64' ]) + bison = SCons.Tool.find_program_path(env, 'bison', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) if bison: bison_bin_dir = os.path.dirname(bison) env.AppendENVPath('PATH', bison_bin_dir) -- cgit v0.12 From f8b479702bb0b5c81c147438f9ce91b5b6b460f2 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 10 Apr 2018 16:08:32 -0500 Subject: updated CHANGES.txt --- src/CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 43efa9e..76487f6 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,6 +8,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Daniel Moody: + - Add common location for default paths for cygwin and mingw in Platform modules + - Updated YACC tool to work on windows with Cygwin/MinGW setups - Set the pickling protocal back to highest which was causing issues with variant dir tests. This will cause issues if reading sconsigns pickled with the previous lower protocal. -- cgit v0.12 From e650cc43946737b9d950049ae9964064ea4bf33f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 10 Apr 2018 19:27:45 -0700 Subject: Fix for issue 3013 (and duplicate 3018). From bitbucket PR: https://bitbucket.org/scons/scons/pull-requests/303 --- src/CHANGES.txt | 3 ++ src/engine/SCons/Taskmaster.py | 9 ++++ test/SideEffect/Issues/3013/files/SConscript | 5 +++ test/SideEffect/Issues/3013/files/SConstruct | 21 ++++++++++ test/SideEffect/Issues/3013/files/test.cpp | 2 + .../Issues/3013/sideffect_with_variantdir.py | 49 ++++++++++++++++++++++ 6 files changed, 89 insertions(+) create mode 100644 test/SideEffect/Issues/3013/files/SConscript create mode 100644 test/SideEffect/Issues/3013/files/SConstruct create mode 100644 test/SideEffect/Issues/3013/files/test.cpp create mode 100644 test/SideEffect/Issues/3013/sideffect_with_variantdir.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 64c0893..d084feb 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,6 +7,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE + From Bernard Blackham: + - Fixed handling of side-effects in task master (fixes #3013). + From Daniel Moody: - Set the pickling protocal back to highest which was causing issues with variant dir tests. This will cause issues if reading sconsigns diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 17507a6..1522ca2 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -470,14 +470,23 @@ class Task(object): pending_children.discard(t) for p in t.waiting_parents: parents[p] = parents.get(p, 0) + 1 + t.waiting_parents = set() for t in targets: if t.side_effects is not None: for s in t.side_effects: if s.get_state() == NODE_EXECUTING: s.set_state(NODE_NO_STATE) + + # The side-effects may have been transferred to + # NODE_NO_STATE by executed_with{,out}_callbacks, but was + # not taken out of the waiting parents/pending children + # data structures. Check for that now. + if s.get_state() == NODE_NO_STATE and s.waiting_parents: + pending_children.discard(s) for p in s.waiting_parents: parents[p] = parents.get(p, 0) + 1 + s.waiting_parents = set() for p in s.waiting_s_e: if p.ref_count == 0: self.tm.candidates.append(p) diff --git a/test/SideEffect/Issues/3013/files/SConscript b/test/SideEffect/Issues/3013/files/SConscript new file mode 100644 index 0000000..27da7bb --- /dev/null +++ b/test/SideEffect/Issues/3013/files/SConscript @@ -0,0 +1,5 @@ +Import('env') + +primary = env.make_file('output', 'test.cpp') +this_causes_problems = env.SideEffect('output_side_effect', 'output') + diff --git a/test/SideEffect/Issues/3013/files/SConstruct b/test/SideEffect/Issues/3013/files/SConstruct new file mode 100644 index 0000000..d01a4b7 --- /dev/null +++ b/test/SideEffect/Issues/3013/files/SConstruct @@ -0,0 +1,21 @@ +env = Environment() + +def make_file(target, source, env): + with open(str(target[0]), 'w') as f: + f.write('gobldygook') + with open(str(target[0]) + '_side_effect', 'w') as side_effect: + side_effect.write('anything') + +env.Append( + BUILDERS={'make_file': Builder(action=Action(make_file))} +) + +env.objdir = 'build' + +SConscript( + 'SConscript', + variant_dir=env.objdir, + exports={'env':env}, + duplicate=0 +) + diff --git a/test/SideEffect/Issues/3013/files/test.cpp b/test/SideEffect/Issues/3013/files/test.cpp new file mode 100644 index 0000000..27424b0 --- /dev/null +++ b/test/SideEffect/Issues/3013/files/test.cpp @@ -0,0 +1,2 @@ +void some_function() {} + diff --git a/test/SideEffect/Issues/3013/sideffect_with_variantdir.py b/test/SideEffect/Issues/3013/sideffect_with_variantdir.py new file mode 100644 index 0000000..9ca3eca --- /dev/null +++ b/test/SideEffect/Issues/3013/sideffect_with_variantdir.py @@ -0,0 +1,49 @@ +#!/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__" + +""" +Test materials for Github Issue 3013 submitted by Stefan Ross: +https://github.com/SCons/scons/issues/3013 +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.dir_fixture('files') + +test.run( + arguments = '-j2' +) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: + -- cgit v0.12 From 56b55490167e165b70dd591da13468cdace315b8 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Thu, 12 Apr 2018 15:06:44 -0500 Subject: updated gettext tools to work on windows cygwin and mingw environments. --- src/engine/SCons/Tool/GettextCommon.py | 2 +- src/engine/SCons/Tool/gettext_tool.py | 12 ++++++++++++ src/engine/SCons/Tool/msgfmt.py | 14 ++++++++++++++ src/engine/SCons/Tool/msginit.py | 14 ++++++++++++++ src/engine/SCons/Tool/msgmerge.py | 15 ++++++++++++++- src/engine/SCons/Tool/xgettext.py | 14 +++++++++++++- 6 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/engine/SCons/Tool/GettextCommon.py b/src/engine/SCons/Tool/GettextCommon.py index 3b840a6..2d37def 100644 --- a/src/engine/SCons/Tool/GettextCommon.py +++ b/src/engine/SCons/Tool/GettextCommon.py @@ -447,7 +447,7 @@ def _detect_msgfmt(env): """ Detects *msgmfmt(1)* program. """ if 'MSGFMT' in env: return env['MSGFMT'] - msgfmt = env.Detect('msgfmt'); + msgfmt = env.Detect('msgfmt') if msgfmt: return msgfmt raise SCons.Errors.StopError(MsgfmtNotFound, "Could not detect msgfmt") diff --git a/src/engine/SCons/Tool/gettext_tool.py b/src/engine/SCons/Tool/gettext_tool.py index 6031e49..41bd70b 100644 --- a/src/engine/SCons/Tool/gettext_tool.py +++ b/src/engine/SCons/Tool/gettext_tool.py @@ -27,10 +27,22 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" ############################################################################# def generate(env,**kw): + import sys + import os import SCons.Tool + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + from SCons.Tool.GettextCommon \ import _translate, tool_list for t in tool_list(env['PLATFORM'], env): + if sys.platform == 'win32': + tool = SCons.Tool.find_program_path(env, t, default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if tool: + tool_bin_dir = os.path.dirname(tool) + env.AppendENVPath('PATH', tool_bin_dir) + else: + SCons.Warnings.Warning(t + ' tool requested, but binary not found in ENV PATH') env.Tool(t) env.AddMethod(_translate, 'Translate') ############################################################################# diff --git a/src/engine/SCons/Tool/msgfmt.py b/src/engine/SCons/Tool/msgfmt.py index 4fe6afd..3c9dde7 100644 --- a/src/engine/SCons/Tool/msgfmt.py +++ b/src/engine/SCons/Tool/msgfmt.py @@ -75,8 +75,22 @@ def _create_mo_file_builder(env, **kw): ############################################################################# def generate(env,**kw): """ Generate `msgfmt` tool """ + import sys + import os import SCons.Util + import SCons.Tool from SCons.Tool.GettextCommon import _detect_msgfmt + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + msgfmt = SCons.Tool.find_program_path(env, 'msgfmt', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if msgfmt: + msgfmt_bin_dir = os.path.dirname(msgfmt) + env.AppendENVPath('PATH', msgfmt_bin_dir) + else: + SCons.Warnings.Warning('msgfmt tool requested, but binary not found in ENV PATH') + try: env['MSGFMT'] = _detect_msgfmt(env) except: diff --git a/src/engine/SCons/Tool/msginit.py b/src/engine/SCons/Tool/msginit.py index 39f460d..9e056e7 100644 --- a/src/engine/SCons/Tool/msginit.py +++ b/src/engine/SCons/Tool/msginit.py @@ -77,8 +77,22 @@ def _POInitBuilderWrapper(env, target=None, source=_null, **kw): ############################################################################# def generate(env,**kw): """ Generate the `msginit` tool """ + import sys + import os import SCons.Util + import SCons.Tool from SCons.Tool.GettextCommon import _detect_msginit + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + msginit = SCons.Tool.find_program_path(env, 'msginit', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if msginit: + msginit_bin_dir = os.path.dirname(msginit) + env.AppendENVPath('PATH', msginit_bin_dir) + else: + SCons.Warnings.Warning('msginit tool requested, but binary not found in ENV PATH') + try: env['MSGINIT'] = _detect_msginit(env) except: diff --git a/src/engine/SCons/Tool/msgmerge.py b/src/engine/SCons/Tool/msgmerge.py index 11d7b48..76661d2 100644 --- a/src/engine/SCons/Tool/msgmerge.py +++ b/src/engine/SCons/Tool/msgmerge.py @@ -68,8 +68,21 @@ def _POUpdateBuilderWrapper(env, target=None, source=_null, **kw): ############################################################################# def generate(env,**kw): - """ Generate the `xgettext` tool """ + """ Generate the `msgmerge` tool """ + import sys + import os + import SCons.Tool from SCons.Tool.GettextCommon import _detect_msgmerge + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + msgmerge = SCons.Tool.find_program_path(env, 'msgmerge', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if msgmerge: + msgmerge_bin_dir = os.path.dirname(msgmerge) + env.AppendENVPath('PATH', msgmerge_bin_dir) + else: + SCons.Warnings.Warning('msgmerge tool requested, but binary not found in ENV PATH') try: env['MSGMERGE'] = _detect_msgmerge(env) except: diff --git a/src/engine/SCons/Tool/xgettext.py b/src/engine/SCons/Tool/xgettext.py index 2c0ce40..e4a17ca 100644 --- a/src/engine/SCons/Tool/xgettext.py +++ b/src/engine/SCons/Tool/xgettext.py @@ -288,9 +288,21 @@ def _POTUpdateBuilder(env, **kw): ############################################################################# def generate(env, **kw): """ Generate `xgettext` tool """ + import sys + import os import SCons.Util + import SCons.Tool from SCons.Tool.GettextCommon import RPaths, _detect_xgettext - + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + xgettext = SCons.Tool.find_program_path(env, 'xgettext', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if xgettext: + xgettext_bin_dir = os.path.dirname(xgettext) + env.AppendENVPath('PATH', xgettext_bin_dir) + else: + SCons.Warnings.Warning('xgettext tool requested, but binary not found in ENV PATH') try: env['XGETTEXT'] = _detect_xgettext(env) except: -- cgit v0.12 From 6135c8f37e53ef8aae352893c413d0676d586798 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 13 Apr 2018 11:08:56 -0500 Subject: updated CHANGES.txt --- src/CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 76487f6..dccf61e 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,6 +8,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Daniel Moody: + - Updated gettext tools to setup default paths for windows with Cygwin/MinGW setups - Add common location for default paths for cygwin and mingw in Platform modules - Updated YACC tool to work on windows with Cygwin/MinGW setups - Set the pickling protocal back to highest which was causing issues -- cgit v0.12 From d2ac0761b978215d2006c4bf892bfb8b2a42172d Mon Sep 17 00:00:00 2001 From: grbd Date: Sun, 15 Apr 2018 12:15:48 +0100 Subject: Added in support for Sconstruct.py --- doc/design/native.xml | 5 +++-- doc/man/scons.xml | 4 ++++ doc/scons.mod | 1 + src/CHANGES.txt | 2 +- src/engine/SCons/Script/Main.py | 2 +- test/SConstruct.py | 9 +++++++++ 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/design/native.xml b/doc/design/native.xml index 1329817..97d5831 100644 --- a/doc/design/native.xml +++ b/doc/design/native.xml @@ -64,8 +64,9 @@ By default, the &SCons; utility searches for a file named - &SConstruct;, &Sconstruct;, &sconstruct;, &SConstruct.py or &sconstruct.py; (in that order) in the - current directory, and reads its configuration from the first file + &SConstruct;, &Sconstruct;, &sconstruct;, &SConstruct.py, &Sconstruct.py + or &sconstruct.py; (in that order) in the current directory, + and reads its configuration from the first file found. A command-line option exists to read a different file name. diff --git a/doc/man/scons.xml b/doc/man/scons.xml index cd6541b..abbed4f 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -107,6 +107,7 @@ searches for a file named Sconstruct, sconstruct, SConstruct.py +Sconstruct.py or sconstruct.py (in that order) in the current directory and reads its @@ -639,6 +640,7 @@ before searching for the Sconstruct, sconstruct, SConstruct.py +Sconstruct.py or sconstruct.py file, or doing anything @@ -655,6 +657,7 @@ except that it will search for Sconstruct, sconstruct, SConstruct.py +Sconstruct.py or sconstruct.py in the specified directory.) @@ -1667,6 +1670,7 @@ scons --tree=all,prune,status target Sconstruct , sconstruct , SConstruct.py +Sconstruct.py or sconstruct.py file is found, and uses that diff --git a/doc/scons.mod b/doc/scons.mod index 1d207c9..974ec02 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -138,6 +138,7 @@ Sconstruct"> sconstruct"> SConstruct.py"> +Sconstruct.py"> sconstruct.py"> .sconsign"> src"> diff --git a/src/CHANGES.txt b/src/CHANGES.txt index be4e0dd..093c628 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,7 +8,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Richard West: - - Add SConstruct.py and sconstruct.py to the search path for the root SConstruct file. + - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. Allows easier debugging within Visual Studio From Bernard Blackham: diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 18c493c..923ef1e 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -623,7 +623,7 @@ def _SConstruct_exists(dirname='', repositories=[], filelist=None): current directory. """ if not filelist: - filelist = ['SConstruct', 'Sconstruct', 'sconstruct', 'SConstruct.py', 'sconstruct.py'] + filelist = ['SConstruct', 'Sconstruct', 'sconstruct', 'SConstruct.py', 'Sconstruct.py', 'sconstruct.py'] for file in filelist: sfile = os.path.join(dirname, file) if os.path.isfile(sfile): diff --git a/test/SConstruct.py b/test/SConstruct.py index 3cf1c02..8bc8617 100644 --- a/test/SConstruct.py +++ b/test/SConstruct.py @@ -48,6 +48,15 @@ test.run(arguments = ".", stdout = test.wrap_stdout(read_str = 'sconstruct.py %s\n' % wpath, build_str = "scons: `.' is up to date.\n")) +test.write('Sconstruct.py', """ +import os +print("sconstruct.py "+os.getcwd()) +""") + +test.run(arguments = ".", + stdout = test.wrap_stdout(read_str = 'sconstruct.py %s\n' % wpath, + build_str = "scons: `.' is up to date.\n")) + test.write('SConstruct.py', """ import os print("SConstruct.py "+os.getcwd()) -- cgit v0.12 From 0e38d6dc4916b33c2f13d76d18ed6e0bf18f316b Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 23 Apr 2018 14:01:33 -0600 Subject: Fix invalid xml entities in docs Signed-off-by: Mats Wichmann --- doc/design/native.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/design/native.xml b/doc/design/native.xml index 97d5831..0ea4074 100644 --- a/doc/design/native.xml +++ b/doc/design/native.xml @@ -64,7 +64,7 @@ By default, the &SCons; utility searches for a file named - &SConstruct;, &Sconstruct;, &sconstruct;, &SConstruct.py, &Sconstruct.py + &SConstruct;, &Sconstruct;, &sconstruct;, &SConstruct.py;, &Sconstruct.py; or &sconstruct.py; (in that order) in the current directory, and reads its configuration from the first file found. A command-line option exists to read a -- cgit v0.12 From 617cf7099997ada8986a4811d6dda09aeb86b16a Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 23 Apr 2018 14:14:42 -0600 Subject: Update CHANGES.txt as well --- src/CHANGES.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index d13fdec..4c05773 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,6 +7,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE + From Mats Wichmann: + - xml validity fixes from SConstruct.py change + From Richard West: - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. Allows easier debugging within Visual Studio @@ -50,7 +53,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Mats Wichmann: - Updated manpage scons.xml to fix a nested list problem - - Updated doc terminionly: use prepend instead of append as appropriate + - Updated doc terminiology: use prepend instead of append as appropriate From Jonathon Reinhart: - Replace all instances of `int main()` in C code with `int main(void)`. -- cgit v0.12 From 9ec7ee42da85d5a25fac73c13f668e6ccf7087a2 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 23 Apr 2018 13:48:11 -0600 Subject: Fix wiki references to point to gihub wiki Signed-off-by: Mats Wichmann --- QMTest/test-framework.rst | 2 +- bin/xmlagenda.py | 6 ++++-- src/CHANGES.txt | 5 ++--- src/engine/SCons/Tool/docbook/docs/manual.xml | 2 +- test/Docbook/basedir/htmlchunked/image/manual.xml | 2 +- test/Docbook/basedir/htmlhelp/image/manual.xml | 2 +- test/Docbook/basic/epub/image/manual.xml | 2 +- test/Docbook/basic/html/image/manual.xml | 2 +- test/Docbook/basic/htmlchunked/image/manual.xml | 2 +- test/Docbook/basic/htmlhelp/image/manual.xml | 2 +- test/Docbook/rootname/htmlchunked/image/manual.xml | 2 +- test/Docbook/rootname/htmlhelp/image/manual.xml | 2 +- testing/buildbot.yml | 2 +- www/project_tools.html | 2 +- 14 files changed, 18 insertions(+), 17 deletions(-) diff --git a/QMTest/test-framework.rst b/QMTest/test-framework.rst index 844d99b..d51e5f0 100644 --- a/QMTest/test-framework.rst +++ b/QMTest/test-framework.rst @@ -346,7 +346,7 @@ SCons, it is ``test/packaging/sandbox-test/sandbox-test.py``. For even more examples you should check out one of the external Tools, e.g. the *Qt4* Tool at https://bitbucket.org/dirkbaechle/scons_qt4. Also visit the SCons -Tools Index at http://www.scons.org/wiki/ToolsIndex for a complete +Tools Index at https://github.com/SCons/scons/wiki/ToolsIndex for a complete list of available Tools, though not all may have tests yet. How to convert old tests diff --git a/bin/xmlagenda.py b/bin/xmlagenda.py index fcfe53e..7091ee5 100755 --- a/bin/xmlagenda.py +++ b/bin/xmlagenda.py @@ -1,7 +1,9 @@ #!/usr/bin/env python -# Query the scons.tigris.org database for the issues of interest. -# The typical triage query is found on http://www.scons.org/wiki/BugParty +# Query gihub issue tracker for the issues of interest. +# The typical triage query is found on +# https://github.com/scons/scons/wiki/BugParty +# FIXME: this needs reworking for github, and wiki needs updating # Download the issues from Issuezilla as XML; this creates a file # named 'issues.xml'. Run this script in the dir containing diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 4c05773..119d122 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,9 +7,6 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - From Mats Wichmann: - - xml validity fixes from SConstruct.py change - From Richard West: - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. Allows easier debugging within Visual Studio @@ -54,6 +51,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Mats Wichmann: - Updated manpage scons.xml to fix a nested list problem - Updated doc terminiology: use prepend instead of append as appropriate + - xml validity fixes from SConstruct.py change + - update wiki links to new github location From Jonathon Reinhart: - Replace all instances of `int main()` in C code with `int main(void)`. diff --git a/src/engine/SCons/Tool/docbook/docs/manual.xml b/src/engine/SCons/Tool/docbook/docs/manual.xml index c129753..64ee925 100644 --- a/src/engine/SCons/Tool/docbook/docs/manual.xml +++ b/src/engine/SCons/Tool/docbook/docs/manual.xml @@ -67,7 +67,7 @@ package's docbook folder to the SCons User's Guide, chap. 19.7 "Where to put your custom Builders and Tools" and -the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. +the SCons Tools Wiki page at http://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/test/Docbook/basedir/htmlchunked/image/manual.xml b/test/Docbook/basedir/htmlchunked/image/manual.xml index ca12e0e..067c76e 100644 --- a/test/Docbook/basedir/htmlchunked/image/manual.xml +++ b/test/Docbook/basedir/htmlchunked/image/manual.xml @@ -72,7 +72,7 @@ the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. + url="https://github.com/SCons/scons/wiki/ToolsIndex">https://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/test/Docbook/basedir/htmlhelp/image/manual.xml b/test/Docbook/basedir/htmlhelp/image/manual.xml index ca12e0e..067c76e 100644 --- a/test/Docbook/basedir/htmlhelp/image/manual.xml +++ b/test/Docbook/basedir/htmlhelp/image/manual.xml @@ -72,7 +72,7 @@ the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. + url="https://github.com/SCons/scons/wiki/ToolsIndex">https://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/test/Docbook/basic/epub/image/manual.xml b/test/Docbook/basic/epub/image/manual.xml index ca12e0e..067c76e 100644 --- a/test/Docbook/basic/epub/image/manual.xml +++ b/test/Docbook/basic/epub/image/manual.xml @@ -72,7 +72,7 @@ the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. + url="https://github.com/SCons/scons/wiki/ToolsIndex">https://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/test/Docbook/basic/html/image/manual.xml b/test/Docbook/basic/html/image/manual.xml index ca12e0e..067c76e 100644 --- a/test/Docbook/basic/html/image/manual.xml +++ b/test/Docbook/basic/html/image/manual.xml @@ -72,7 +72,7 @@ the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. + url="https://github.com/SCons/scons/wiki/ToolsIndex">https://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/test/Docbook/basic/htmlchunked/image/manual.xml b/test/Docbook/basic/htmlchunked/image/manual.xml index ca12e0e..067c76e 100644 --- a/test/Docbook/basic/htmlchunked/image/manual.xml +++ b/test/Docbook/basic/htmlchunked/image/manual.xml @@ -72,7 +72,7 @@ the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. + url="https://github.com/SCons/scons/wiki/ToolsIndex">https://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/test/Docbook/basic/htmlhelp/image/manual.xml b/test/Docbook/basic/htmlhelp/image/manual.xml index ca12e0e..067c76e 100644 --- a/test/Docbook/basic/htmlhelp/image/manual.xml +++ b/test/Docbook/basic/htmlhelp/image/manual.xml @@ -72,7 +72,7 @@ the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. + url="https://github.com/SCons/scons/wiki/ToolsIndex">https://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/test/Docbook/rootname/htmlchunked/image/manual.xml b/test/Docbook/rootname/htmlchunked/image/manual.xml index ca12e0e..067c76e 100644 --- a/test/Docbook/rootname/htmlchunked/image/manual.xml +++ b/test/Docbook/rootname/htmlchunked/image/manual.xml @@ -72,7 +72,7 @@ the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. + url="https://github.com/SCons/scons/wiki/ToolsIndex">https://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/test/Docbook/rootname/htmlhelp/image/manual.xml b/test/Docbook/rootname/htmlhelp/image/manual.xml index ca12e0e..067c76e 100644 --- a/test/Docbook/rootname/htmlhelp/image/manual.xml +++ b/test/Docbook/rootname/htmlhelp/image/manual.xml @@ -72,7 +72,7 @@ the SCons Tools Wiki page at http://scons.org/wiki/ToolsIndex. + url="https://github.com/SCons/scons/wiki/ToolsIndex">https://github.com/SCons/scons/wiki/ToolsIndex. diff --git a/testing/buildbot.yml b/testing/buildbot.yml index 81c2143..7e6bb1a 100644 --- a/testing/buildbot.yml +++ b/testing/buildbot.yml @@ -9,7 +9,7 @@ # ansible-playbook -i hosts buildbot.yml -e 'botuser=sconsy' # # Tested with Ansible 1.5.0, based on -# http://scons.org/wiki/InstallingBuildbotSlaves +# https://github.com/SCons/scons/wiki/InstallingBuildbotSlaves # Send questions to: # # anatoly techtonik diff --git a/www/project_tools.html b/www/project_tools.html index ee731a4..cf80234 100644 --- a/www/project_tools.html +++ b/www/project_tools.html @@ -12,7 +12,7 @@ The main changes are: * Link to our Roadmap page. -->
  • SCons Home Page
  • -
  • SCons Wiki
  • +
  • SCons Wiki
  • -- cgit v0.12 From 649ea1bfa22f40b4ea4c5da7b8210b1efe2062b4 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 23 Apr 2018 12:38:07 -0600 Subject: Change bug references from tigris -> github Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + src/engine/SCons/Platform/win32.py | 2 +- src/engine/SCons/Tool/JavaCommon.py | 2 +- src/engine/SCons/Tool/msvc.py | 2 +- test/CPPDEFINES/append.py | 6 +++--- test/CPPDEFINES/pkg-config.py | 4 ++-- test/D/Issues/2994/Common/D_changed_DFLAGS_not_rebuilding.py | 4 ++-- test/Depends/no-Builder.py | 2 +- test/ExecuteInvalidateCache.py | 2 +- test/Fortran/USE-MODULE-CASEINSENS.py | 2 +- test/Java/Java-1.4.py | 2 +- test/Java/Java-1.5.py | 2 +- test/Java/Java-1.6.py | 2 +- test/Java/swig-dependencies.py | 2 +- test/Libs/SharedLibrary-update-deps.py | 2 +- test/MSVC/PCH-source.py | 2 +- test/Scanner/Dir.py | 2 +- test/VariantDir/include-subdir.py | 2 +- test/option/tree-lib.py | 2 +- 19 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 119d122..ee4d7bb 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -53,6 +53,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Updated doc terminiology: use prepend instead of append as appropriate - xml validity fixes from SConstruct.py change - update wiki links to new github location + - update bug links to new github location From Jonathon Reinhart: - Replace all instances of `int main()` in C code with `int main(void)`. diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index 0e9e01f..5c877b5 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -132,7 +132,7 @@ try: # Without this, python can randomly crash while using -jN. # See the python bug at http://bugs.python.org/issue6476 # and SCons issue at - # http://scons.tigris.org/issues/show_bug.cgi?id=2449 + # https://github.com/SCons/scons/issues/2449 def spawnve(mode, file, args, env): spawn_lock.acquire() try: diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 8b13f9f..dfb9e33 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -245,7 +245,7 @@ if java_parsing: # If that's an inner class which is declared in a method, it # requires an index prepended to the class-name, e.g. # 'Foo$1Inner' - # http://scons.tigris.org/issues/show_bug.cgi?id=2087 + # https://github.com/SCons/scons/issues/2087 if self.outer_state.localClasses and \ self.outer_state.stackBrackets[-1] > \ self.outer_state.stackBrackets[-2]+1: diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index f078c62..1412cf7 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -112,7 +112,7 @@ def object_emitter(target, source, env, parent_emitter): # # See issue #2505 for a discussion of what to do if it turns # out this assumption causes trouble in the wild: - # http://scons.tigris.org/issues/show_bug.cgi?id=2505 + # https://github.com/SCons/scons/issues/2505 if 'PCH' in env: pch = env['PCH'] if str(target[0]) != SCons.Util.splitext(str(pch))[0] + '.obj': diff --git a/test/CPPDEFINES/append.py b/test/CPPDEFINES/append.py index 7973f22..874fceb 100644 --- a/test/CPPDEFINES/append.py +++ b/test/CPPDEFINES/append.py @@ -43,7 +43,7 @@ env_1738_2.Append(CPPDEFINES={'value' : '1'}) print(env_1738_2.subst('$_CPPDEFFLAGS')) #env_1738_2.Object('test_1738_2', 'main.c') -# http://scons.tigris.org/issues/show_bug.cgi?id=2300 +# https://github.com/SCons/scons/issues/2300 env_2300_1 = Environment(CPPDEFINES = 'foo', CPPDEFPREFIX='-D') env_2300_1.Append(CPPDEFINES='bar') print(env_2300_1.subst('$_CPPDEFFLAGS')) @@ -52,8 +52,8 @@ env_2300_2 = Environment(CPPDEFINES = ['foo'], CPPDEFPREFIX='-D') # note the lis env_2300_2.Append(CPPDEFINES='bar') print(env_2300_2.subst('$_CPPDEFFLAGS')) -# http://scons.tigris.org/issues/show_bug.cgi?id=1152 -# http://scons.tigris.org/issues/show_bug.cgi?id=2900 +# https://github.com/SCons/scons/issues/1152 +# https://github.com/SCons/scons/issues/2900 # Python3 dicts dont preserve order. Hence we supply subclass of OrderedDict # whose __str__ and __repr__ act like a normal dict. from collections import OrderedDict diff --git a/test/CPPDEFINES/pkg-config.py b/test/CPPDEFINES/pkg-config.py index 42f38b6..4096934 100644 --- a/test/CPPDEFINES/pkg-config.py +++ b/test/CPPDEFINES/pkg-config.py @@ -70,7 +70,7 @@ class OrderedPrintingDict(OrderedDict): def __semi_deepcopy__(self): return self.copy() """ + """ -# http://scons.tigris.org/issues/show_bug.cgi?id=2671 +# https://github.com/SCons/scons/issues/2671 # Passing test cases env_1 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST']) env_1.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags bug') @@ -89,7 +89,7 @@ env_4 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None) env_4.MergeFlags('-DSOMETHING -DVARIABLE=2') print(env_4.subst('$_CPPDEFFLAGS')) -# http://scons.tigris.org/issues/show_bug.cgi?id=1738 +# https://github.com/SCons/scons/issues/1738 env_1738_1 = Environment(tools=['default']) env_1738_1.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags --libs bug') env_1738_1.Append(CPPDEFINES={'value' : '1'}) diff --git a/test/D/Issues/2994/Common/D_changed_DFLAGS_not_rebuilding.py b/test/D/Issues/2994/Common/D_changed_DFLAGS_not_rebuilding.py index 7e69f50..07b1366 100644 --- a/test/D/Issues/2994/Common/D_changed_DFLAGS_not_rebuilding.py +++ b/test/D/Issues/2994/Common/D_changed_DFLAGS_not_rebuilding.py @@ -1,6 +1,6 @@ """ -Test to check for issue reported in tigris bug 2994 -http://scons.tigris.org/issues/show_bug.cgi?id=2994 +Test to check for issue reported in github issue 2994 +https://github.com/SCons/scons/issues/2994 """ # diff --git a/test/Depends/no-Builder.py b/test/Depends/no-Builder.py index 48ab724..f55fcf9 100644 --- a/test/Depends/no-Builder.py +++ b/test/Depends/no-Builder.py @@ -40,7 +40,7 @@ file2 = File('file2') env.Depends(file1, [[file2, 'file3']]) # Verify that a "hidden" file created by another action causes the # action to run when an explicit Dependency is specified. -# See http://scons.tigris.org/issues/show_bug.cgi?id=2647 +# https://github.com/SCons/scons/issues/2647 env.Depends('hidden', 'file4.out') env.Command('file4.out', 'file4.in', [Copy('$TARGET', '$SOURCE'), Touch('hidden')]) diff --git a/test/ExecuteInvalidateCache.py b/test/ExecuteInvalidateCache.py index acc7701..f6ed391 100644 --- a/test/ExecuteInvalidateCache.py +++ b/test/ExecuteInvalidateCache.py @@ -27,7 +27,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Test the Execute() functions clears the memoized values of affected target Nodes when used with Delete(). Derived from -http://scons.tigris.org/issues/show_bug.cgi?id=1307 +https://github.com/SCons/scons/issues/1307 """ import TestSCons diff --git a/test/Fortran/USE-MODULE-CASEINSENS.py b/test/Fortran/USE-MODULE-CASEINSENS.py index 44c03fe..4dd115b 100644 --- a/test/Fortran/USE-MODULE-CASEINSENS.py +++ b/test/Fortran/USE-MODULE-CASEINSENS.py @@ -27,7 +27,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" # This test tests whether a file that defines a module "a" and # then uses it with a different case ("A") works. Pre-2.0, this # gave a spurious dependency cycle error. -# See http://scons.tigris.org/issues/show_bug.cgi?id=2574 +# See https://github.com/SCons/scons/issues/2574 import TestSCons diff --git a/test/Java/Java-1.4.py b/test/Java/Java-1.4.py index 4076783..8bbefaf 100644 --- a/test/Java/Java-1.4.py +++ b/test/Java/Java-1.4.py @@ -267,7 +267,7 @@ class Foo { } """) # Test private inner class instantiation, courtesy Tilo Prutz: -# http://scons.tigris.org/issues/show_bug.cgi?id=1594 +# https://github.com/SCons/scons/issues/1594 test.write(['src6', 'TestSCons.java'], """\ class test { diff --git a/test/Java/Java-1.5.py b/test/Java/Java-1.5.py index 6659a16..58513e2 100644 --- a/test/Java/Java-1.5.py +++ b/test/Java/Java-1.5.py @@ -267,7 +267,7 @@ class Foo { } """) # Test private inner class instantiation, courtesy Tilo Prutz: -# http://scons.tigris.org/issues/show_bug.cgi?id=1594 +# https://github.com/SCons/scons/issues/1594 test.write(['src6', 'TestSCons.java'], """\ class test { diff --git a/test/Java/Java-1.6.py b/test/Java/Java-1.6.py index be46919..04a9155 100644 --- a/test/Java/Java-1.6.py +++ b/test/Java/Java-1.6.py @@ -267,7 +267,7 @@ class Foo { } """) # Test private inner class instantiation, courtesy Tilo Prutz: -# http://scons.tigris.org/issues/show_bug.cgi?id=1594 +# https://github.com/SCons/scons/issues/1594 test.write(['src6', 'TestSCons.java'], """\ class test { diff --git a/test/Java/swig-dependencies.py b/test/Java/swig-dependencies.py index c72c44a..bd7a576 100644 --- a/test/Java/swig-dependencies.py +++ b/test/Java/swig-dependencies.py @@ -130,7 +130,7 @@ except: # Bug ticket reported also this seems work fine when running outsite # the test framework test.skip_test('Throwing no result for this test because of bug ' + - 'related here: http://scons.tigris.org/issues/show_bug.cgi?id=2907\n') + 'related here: https://github.com/SCons/scons/issues/2907\n') pass #test.must_exist(['java', 'classes', 'foopack', 'foopack.class']) #test.must_exist(['java', 'classes', 'foopack', 'foopackJNI.class']) diff --git a/test/Libs/SharedLibrary-update-deps.py b/test/Libs/SharedLibrary-update-deps.py index 076e3ad..3abce83 100644 --- a/test/Libs/SharedLibrary-update-deps.py +++ b/test/Libs/SharedLibrary-update-deps.py @@ -26,7 +26,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Test that SharedLibrary() updates when a different lib is linked, even if it has the same md5. -This is http://scons.tigris.org/issues/show_bug.cgi?id=2903 +This is https://github.com/SCons/scons/issues/2903 """ import sys diff --git a/test/MSVC/PCH-source.py b/test/MSVC/PCH-source.py index 6015fec..ccab66d 100644 --- a/test/MSVC/PCH-source.py +++ b/test/MSVC/PCH-source.py @@ -28,7 +28,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Test use of pre-compiled headers when the source .cpp file shows up in both the env.PCH() and the env.Program() source list. -Issue 2505: http://scons.tigris.org/issues/show_bug.cgi?id=2505 +Issue 2505: http://github.com/SCons/scons/issues/2505 """ import TestSCons diff --git a/test/Scanner/Dir.py b/test/Scanner/Dir.py index 86b80e9..fa4f6c9 100644 --- a/test/Scanner/Dir.py +++ b/test/Scanner/Dir.py @@ -27,7 +27,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Verify that a simple scanner that returns Dir nodes works correctly. -Submitted as http://scons.tigris.org/issues/show_bug.cgi?id=2534 +Submitted as https://github.com/SCons/scons/issues/2534 """ import TestSCons diff --git a/test/VariantDir/include-subdir.py b/test/VariantDir/include-subdir.py index d616bba..fe26c0a 100644 --- a/test/VariantDir/include-subdir.py +++ b/test/VariantDir/include-subdir.py @@ -32,7 +32,7 @@ we have to make sure that the file gets copied to the variant dir. (This was not the case for 0.98.5 and earlier) Test case supplied by Jared Grubb, based on a minimal example supplied -by Ali Tofigh, filed as http://scons.tigris.org/issues/show_bug.cgi?id=2121 +by Ali Tofigh, filed as https://github.com/SCons/scons/issues/2121 """ import TestSCons diff --git a/test/option/tree-lib.py b/test/option/tree-lib.py index 2dc5fb0..4725d42 100644 --- a/test/option/tree-lib.py +++ b/test/option/tree-lib.py @@ -30,7 +30,7 @@ the dependency on the library. (On earlier versions of the Microsoft toolchain this wouldn't show up unless the library already existed on disk.) -Issue 1363: http://scons.tigris.org/issues/show_bug.cgi?id=1363 +Issue 1363: https://github.com/SCons/scons/issues/1363 """ import TestSCons -- cgit v0.12 From f095eb14013f1f3e21dbb213b4e43694ecbf091d Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 29 Apr 2018 21:10:43 -0400 Subject: move test files from QMTest to testing/framework. QMtest hasn't been used in quite some time --- QMTest/README.txt | 50 - QMTest/SConscript | 61 - QMTest/TestCmd.py | 1907 ------------- QMTest/TestCmdTests.py | 3419 ----------------------- QMTest/TestCommon.py | 748 ----- QMTest/TestCommonTests.py | 2340 ---------------- QMTest/TestRuntest.py | 167 -- QMTest/TestSCons.py | 1642 ----------- QMTest/TestSConsMSVS.py | 1177 -------- QMTest/TestSCons_time.py | 331 --- QMTest/TestSConsign.py | 90 - QMTest/test-framework.rst | 430 --- SConstruct | 14 +- bin/SConsExamples.py | 4 +- bin/time-scons.py | 2 +- bin/update-release-info.py | 10 +- runtest.py | 8 +- src/CHANGES.txt | 2 + src/test_strings.py | 10 +- test/D/Support/executablesSearch.py | 2 +- test/README | 4 +- test/update-release-info/update-release-info.py | 2 +- testing/framework/README.txt | 50 + testing/framework/SConscript | 61 + testing/framework/TestCmd.py | 1907 +++++++++++++ testing/framework/TestCmdTests.py | 3419 +++++++++++++++++++++++ testing/framework/TestCommon.py | 748 +++++ testing/framework/TestCommonTests.py | 2340 ++++++++++++++++ testing/framework/TestRuntest.py | 167 ++ testing/framework/TestSCons.py | 1642 +++++++++++ testing/framework/TestSConsMSVS.py | 1177 ++++++++ testing/framework/TestSCons_time.py | 331 +++ testing/framework/TestSConsign.py | 90 + testing/framework/test-framework.rst | 427 +++ timings/README.txt | 4 +- 35 files changed, 12389 insertions(+), 12394 deletions(-) delete mode 100644 QMTest/README.txt delete mode 100644 QMTest/SConscript delete mode 100644 QMTest/TestCmd.py delete mode 100644 QMTest/TestCmdTests.py delete mode 100644 QMTest/TestCommon.py delete mode 100644 QMTest/TestCommonTests.py delete mode 100644 QMTest/TestRuntest.py delete mode 100644 QMTest/TestSCons.py delete mode 100644 QMTest/TestSConsMSVS.py delete mode 100644 QMTest/TestSCons_time.py delete mode 100644 QMTest/TestSConsign.py delete mode 100644 QMTest/test-framework.rst create mode 100644 testing/framework/README.txt create mode 100644 testing/framework/SConscript create mode 100644 testing/framework/TestCmd.py create mode 100644 testing/framework/TestCmdTests.py create mode 100644 testing/framework/TestCommon.py create mode 100644 testing/framework/TestCommonTests.py create mode 100644 testing/framework/TestRuntest.py create mode 100644 testing/framework/TestSCons.py create mode 100644 testing/framework/TestSConsMSVS.py create mode 100644 testing/framework/TestSCons_time.py create mode 100644 testing/framework/TestSConsign.py create mode 100644 testing/framework/test-framework.rst diff --git a/QMTest/README.txt b/QMTest/README.txt deleted file mode 100644 index 7750bc7..0000000 --- a/QMTest/README.txt +++ /dev/null @@ -1,50 +0,0 @@ -This directory contains testing infrastructure. Note that not all of -the pieces here are local to SCons. - - README.txt - - What you're looking at right now. - - SConscript - - Configuration for our packaging build, to copy the necessary - parts of the infrastructure into a build directory. - - TestCmd.py - TestCmdTests.py - TestCommon.py - TestCommonTests.py - - The TestCmd infrastructure for testing external commands. - These are for generic command testing, are used by some - other projects, and are developed separately from SCons. - (They're developed by SK, but still...) - - We've captured the unit tests (Test*Tests.py) for these files - along with the actual modules themselves to make it a little - easier to hack on them for our purposes. Note, however, - that any SCons-specific functionality should be implemented - in one of the - - TestRuntest.py - - Test infrastructure for our runtest.py script. - - TestSCons.py - - Test infrastructure for SCons itself. - - TestSConsMSVS.py - - Test infrastructure for SCons' Visual Studio support. - - TestSCons_time.py - - Test infrastructure for the scons-time.py script. - - TestSConsign.py - - Test infrastructure for the sconsign.py script. - -__COPYRIGHT__ -__FILE__ __REVISION__ __DATE__ __DEVELOPER__ diff --git a/QMTest/SConscript b/QMTest/SConscript deleted file mode 100644 index 1db7301..0000000 --- a/QMTest/SConscript +++ /dev/null @@ -1,61 +0,0 @@ -# -# SConscript file for external packages we need. -# - -# -# __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. -# - -import os.path - -Import('build_dir', 'env') - -files = [ - 'TestCmd.py', - 'TestCommon.py', - 'TestRuntest.py', - 'TestSCons.py', - 'TestSConsign.py', - 'TestSCons_time.py', -] - -def copy(target, source, env): - t = str(target[0]) - s = str(source[0]) - c = open(s, 'r').read() - # Note: We construct the __ VERSION __ substitution string at - # run-time so it doesn't get replaced when this file gets copied - # into the tree for packaging. - c = c.replace('__' + 'VERSION' + '__', env['VERSION']) - open(t, 'w').write(c) - -for file in files: - # Guarantee that real copies of these files always exist in - # build/QMTest. If there's a symlink there, then this is an Aegis - # build and we blow them away now so that they'll get "built" later. - p = os.path.join(build_dir, 'QMTest', file) - if os.path.islink(p): - os.unlink(p) - if not os.path.isabs(p): - p = '#' + p - env.Command(p, file, copy) - Local(p) diff --git a/QMTest/TestCmd.py b/QMTest/TestCmd.py deleted file mode 100644 index 0aab9a8..0000000 --- a/QMTest/TestCmd.py +++ /dev/null @@ -1,1907 +0,0 @@ -""" -TestCmd.py: a testing framework for commands and scripts. - -The TestCmd module provides a framework for portable automated testing -of executable commands and scripts (in any language, not just Python), -especially commands and scripts that require file system interaction. - -In addition to running tests and evaluating conditions, the TestCmd -module manages and cleans up one or more temporary workspace -directories, and provides methods for creating files and directories in -those workspace directories from in-line data, here-documents), allowing -tests to be completely self-contained. - -A TestCmd environment object is created via the usual invocation: - - import TestCmd - test = TestCmd.TestCmd() - -There are a bunch of keyword arguments available at instantiation: - - test = TestCmd.TestCmd(description = 'string', - program = 'program_or_script_to_test', - interpreter = 'script_interpreter', - workdir = 'prefix', - subdir = 'subdir', - verbose = Boolean, - match = default_match_function, - match_stdout = default_match_stdout_function, - match_stderr = default_match_stderr_function, - diff = default_diff_stderr_function, - diff_stdout = default_diff_stdout_function, - diff_stderr = default_diff_stderr_function, - combine = Boolean) - -There are a bunch of methods that let you do different things: - - test.verbose_set(1) - - test.description_set('string') - - test.program_set('program_or_script_to_test') - - test.interpreter_set('script_interpreter') - test.interpreter_set(['script_interpreter', 'arg']) - - test.workdir_set('prefix') - test.workdir_set('') - - test.workpath('file') - test.workpath('subdir', 'file') - - test.subdir('subdir', ...) - - test.rmdir('subdir', ...) - - test.write('file', "contents\n") - test.write(['subdir', 'file'], "contents\n") - - test.read('file') - test.read(['subdir', 'file']) - test.read('file', mode) - test.read(['subdir', 'file'], mode) - - test.writable('dir', 1) - test.writable('dir', None) - - test.preserve(condition, ...) - - test.cleanup(condition) - - test.command_args(program = 'program_or_script_to_run', - interpreter = 'script_interpreter', - arguments = 'arguments to pass to program') - - test.run(program = 'program_or_script_to_run', - interpreter = 'script_interpreter', - arguments = 'arguments to pass to program', - chdir = 'directory_to_chdir_to', - stdin = 'input to feed to the program\n') - universal_newlines = True) - - p = test.start(program = 'program_or_script_to_run', - interpreter = 'script_interpreter', - arguments = 'arguments to pass to program', - universal_newlines = None) - - test.finish(self, p) - - test.pass_test() - test.pass_test(condition) - test.pass_test(condition, function) - - test.fail_test() - test.fail_test(condition) - test.fail_test(condition, function) - test.fail_test(condition, function, skip) - test.fail_test(condition, function, skip, message) - - test.no_result() - test.no_result(condition) - test.no_result(condition, function) - test.no_result(condition, function, skip) - - test.stdout() - test.stdout(run) - - test.stderr() - test.stderr(run) - - test.symlink(target, link) - - test.banner(string) - test.banner(string, width) - - test.diff(actual, expected) - - test.diff_stderr(actual, expected) - - test.diff_stdout(actual, expected) - - test.match(actual, expected) - - test.match_stderr(actual, expected) - - test.match_stdout(actual, expected) - - test.set_match_function(match, stdout, stderr) - - test.match_exact("actual 1\nactual 2\n", "expected 1\nexpected 2\n") - test.match_exact(["actual 1\n", "actual 2\n"], - ["expected 1\n", "expected 2\n"]) - test.match_caseinsensitive("Actual 1\nACTUAL 2\n", "expected 1\nEXPECTED 2\n") - - test.match_re("actual 1\nactual 2\n", regex_string) - test.match_re(["actual 1\n", "actual 2\n"], list_of_regexes) - - test.match_re_dotall("actual 1\nactual 2\n", regex_string) - test.match_re_dotall(["actual 1\n", "actual 2\n"], list_of_regexes) - - test.tempdir() - test.tempdir('temporary-directory') - - test.sleep() - test.sleep(seconds) - - test.where_is('foo') - test.where_is('foo', 'PATH1:PATH2') - test.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') - - test.unlink('file') - test.unlink('subdir', 'file') - -The TestCmd module provides pass_test(), fail_test(), and no_result() -unbound functions that report test results for use with the Aegis change -management system. These methods terminate the test immediately, -reporting PASSED, FAILED, or NO RESULT respectively, and exiting with -status 0 (success), 1 or 2 respectively. This allows for a distinction -between an actual failed test and a test that could not be properly -evaluated because of an external condition (such as a full file system -or incorrect permissions). - - import TestCmd - - TestCmd.pass_test() - TestCmd.pass_test(condition) - TestCmd.pass_test(condition, function) - - TestCmd.fail_test() - TestCmd.fail_test(condition) - TestCmd.fail_test(condition, function) - TestCmd.fail_test(condition, function, skip) - TestCmd.fail_test(condition, function, skip, message) - - TestCmd.no_result() - TestCmd.no_result(condition) - TestCmd.no_result(condition, function) - TestCmd.no_result(condition, function, skip) - -The TestCmd module also provides unbound global functions that handle -matching in the same way as the match_*() methods described above. - - import TestCmd - - test = TestCmd.TestCmd(match = TestCmd.match_exact) - - test = TestCmd.TestCmd(match = TestCmd.match_caseinsensitive) - - test = TestCmd.TestCmd(match = TestCmd.match_re) - - test = TestCmd.TestCmd(match = TestCmd.match_re_dotall) - -These functions are also available as static methods: - - import TestCmd - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_exact) - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_caseinsensitive) - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re) - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re_dotall) - -These static methods can be accessed by a string naming the method: - - import TestCmd - - test = TestCmd.TestCmd(match = 'match_exact') - - test = TestCmd.TestCmd(match = 'match_caseinsensitive') - - test = TestCmd.TestCmd(match = 'match_re') - - test = TestCmd.TestCmd(match = 'match_re_dotall') - -The TestCmd module provides unbound global functions that can be used -for the "diff" argument to TestCmd.TestCmd instantiation: - - import TestCmd - - test = TestCmd.TestCmd(match = TestCmd.match_re, - diff = TestCmd.diff_re) - - test = TestCmd.TestCmd(diff = TestCmd.simple_diff) - - test = TestCmd.TestCmd(diff = TestCmd.context_diff) - - test = TestCmd.TestCmd(diff = TestCmd.unified_diff) - -These functions are also available as static methods: - - import TestCmd - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re, - diff = TestCmd.TestCmd.diff_re) - - test = TestCmd.TestCmd(diff = TestCmd.TestCmd.simple_diff) - - test = TestCmd.TestCmd(diff = TestCmd.TestCmd.context_diff) - - test = TestCmd.TestCmd(diff = TestCmd.TestCmd.unified_diff) - -These static methods can be accessed by a string naming the method: - - import TestCmd - - test = TestCmd.TestCmd(match = 'match_re', diff = 'diff_re') - - test = TestCmd.TestCmd(diff = 'simple_diff') - - test = TestCmd.TestCmd(diff = 'context_diff') - - test = TestCmd.TestCmd(diff = 'unified_diff') - -The "diff" argument can also be used with standard difflib functions: - - import difflib - - test = TestCmd.TestCmd(diff = difflib.context_diff) - - test = TestCmd.TestCmd(diff = difflib.unified_diff) - -Lastly, the where_is() method also exists in an unbound function -version. - - import TestCmd - - TestCmd.where_is('foo') - TestCmd.where_is('foo', 'PATH1:PATH2') - TestCmd.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') -""" - -# Copyright 2000-2010 Steven Knight -# This module is free software, and you may redistribute it and/or modify -# it under the same terms as Python itself, so long as this copyright message -# and disclaimer are retained in their original form. -# -# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -# -# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -from __future__ import division, print_function - -__author__ = "Steven Knight " -__revision__ = "TestCmd.py 1.3.D001 2010/06/03 12:58:27 knight" -__version__ = "1.3" - -import atexit -import difflib -import errno -import os -import re -import shutil -import signal -import stat -import sys -import tempfile -import threading -import time -import traceback -import types - - -IS_PY3 = sys.version_info[0] == 3 -IS_WINDOWS = sys.platform == 'win32' - - -class null(object): - pass - - -_Null = null() - -try: - from collections import UserList, UserString -except ImportError: - # no 'collections' module or no UserFoo in collections - exec('from UserList import UserList') - exec('from UserString import UserString') - -__all__ = [ - 'diff_re', - 'fail_test', - 'no_result', - 'pass_test', - 'match_exact', - 'match_caseinsensitive', - 'match_re', - 'match_re_dotall', - 'python', - '_python_', - 'TestCmd', - 'to_bytes', - 'to_str', -] - - -def is_List(e): - return isinstance(e, (list, UserList)) - - -def to_bytes(s): - if isinstance(s, bytes) or bytes is str: - return s - return bytes(s, 'utf-8') - - -def to_str(s): - if bytes is str or is_String(s): - return s - return str(s, 'utf-8') - - -try: - eval('unicode') -except NameError: - def is_String(e): - return isinstance(e, (str, UserString)) -else: - def is_String(e): - return isinstance(e, (str, unicode, UserString)) - -tempfile.template = 'testcmd.' -if os.name in ('posix', 'nt'): - tempfile.template = 'testcmd.' + str(os.getpid()) + '.' -else: - tempfile.template = 'testcmd.' - -re_space = re.compile('\s') - - -def _caller(tblist, skip): - string = "" - arr = [] - for file, line, name, text in tblist: - if file[-10:] == "TestCmd.py": - break - arr = [(file, line, name, text)] + arr - atfrom = "at" - for file, line, name, text in arr[skip:]: - if name in ("?", ""): - name = "" - else: - name = " (" + name + ")" - string = string + ("%s line %d of %s%s\n" % (atfrom, line, file, name)) - atfrom = "\tfrom" - return string - - -def fail_test(self=None, condition=1, function=None, skip=0, message=None): - """Cause the test to fail. - - By default, the fail_test() method reports that the test FAILED - and exits with a status of 1. If a condition argument is supplied, - the test fails only if the condition is true. - """ - if not condition: - return - if not function is None: - function() - of = "" - desc = "" - sep = " " - if not self is None: - if self.program: - of = " of " + self.program - sep = "\n\t" - if self.description: - desc = " [" + self.description + "]" - sep = "\n\t" - - at = _caller(traceback.extract_stack(), skip) - if message: - msg = "\t%s\n" % message - else: - msg = "" - sys.stderr.write("FAILED test" + of + desc + sep + at + msg) - - sys.exit(1) - - -def no_result(self=None, condition=1, function=None, skip=0): - """Causes a test to exit with no valid result. - - By default, the no_result() method reports NO RESULT for the test - and exits with a status of 2. If a condition argument is supplied, - the test fails only if the condition is true. - """ - if not condition: - return - if not function is None: - function() - of = "" - desc = "" - sep = " " - if not self is None: - if self.program: - of = " of " + self.program - sep = "\n\t" - if self.description: - desc = " [" + self.description + "]" - sep = "\n\t" - - at = _caller(traceback.extract_stack(), skip) - sys.stderr.write("NO RESULT for test" + of + desc + sep + at) - - sys.exit(2) - - -def pass_test(self=None, condition=1, function=None): - """Causes a test to pass. - - By default, the pass_test() method reports PASSED for the test - and exits with a status of 0. If a condition argument is supplied, - the test passes only if the condition is true. - """ - if not condition: - return - if not function is None: - function() - sys.stderr.write("PASSED\n") - sys.exit(0) - - -def match_exact(lines=None, matches=None, newline=os.sep): - """ - """ - - if isinstance(lines, bytes) or bytes is str: - newline = to_bytes(newline) - - if not is_List(lines): - lines = lines.split(newline) - if not is_List(matches): - matches = matches.split(newline) - if len(lines) != len(matches): - return - for i in range(len(lines)): - if lines[i] != matches[i]: - return - return 1 - - -def match_caseinsensitive(lines=None, matches=None): - """ - """ - if not is_List(lines): - lines = lines.split("\n") - if not is_List(matches): - matches = matches.split("\n") - if len(lines) != len(matches): - return - for i in range(len(lines)): - if lines[i].lower() != matches[i].lower(): - return - return 1 - - -def match_re(lines=None, res=None): - """ - """ - if not is_List(lines): - # CRs mess up matching (Windows) so split carefully - lines = re.split('\r?\n', lines) - if not is_List(res): - res = res.split("\n") - if len(lines) != len(res): - print("match_re: expected %d lines, found %d" % (len(res), len(lines))) - return - for i in range(len(lines)): - s = "^" + res[i] + "$" - try: - expr = re.compile(s) - except re.error as e: - msg = "Regular expression error in %s: %s" - raise re.error(msg % (repr(s), e.args[0])) - if not expr.search(lines[i]): - print("match_re: mismatch at line %d:\n search re='%s'\n line='%s'" % ( - i, s, lines[i])) - return - return 1 - - -def match_re_dotall(lines=None, res=None): - """ - """ - if not isinstance(lines, str): - lines = "\n".join(lines) - if not isinstance(res, str): - res = "\n".join(res) - s = "^" + res + "$" - try: - expr = re.compile(s, re.DOTALL) - except re.error as e: - msg = "Regular expression error in %s: %s" - raise re.error(msg % (repr(s), e.args[0])) - return expr.match(lines) - - -def simple_diff(a, b, fromfile='', tofile='', - fromfiledate='', tofiledate='', n=3, lineterm='\n'): - """ - A function with the same calling signature as difflib.context_diff - (diff -c) and difflib.unified_diff (diff -u) but which prints - output like the simple, unadorned 'diff" command. - """ - a = [to_str(q) for q in a] - b = [to_str(q) for q in b] - sm = difflib.SequenceMatcher(None, a, b) - - def comma(x1, x2): - return x1 + 1 == x2 and str(x2) or '%s,%s' % (x1 + 1, x2) - result = [] - for op, a1, a2, b1, b2 in sm.get_opcodes(): - if op == 'delete': - result.append("%sd%d" % (comma(a1, a2), b1)) - result.extend(['< ' + l for l in a[a1:a2]]) - elif op == 'insert': - result.append("%da%s" % (a1, comma(b1, b2))) - result.extend(['> ' + l for l in b[b1:b2]]) - elif op == 'replace': - result.append("%sc%s" % (comma(a1, a2), comma(b1, b2))) - result.extend(['< ' + l for l in a[a1:a2]]) - result.append('---') - result.extend(['> ' + l for l in b[b1:b2]]) - return result - - -def diff_re(a, b, fromfile='', tofile='', - fromfiledate='', tofiledate='', n=3, lineterm='\n'): - """ - A simple "diff" of two sets of lines when the expected lines - are regular expressions. This is a really dumb thing that - just compares each line in turn, so it doesn't look for - chunks of matching lines and the like--but at least it lets - you know exactly which line first didn't compare correctl... - """ - result = [] - diff = len(a) - len(b) - if diff < 0: - a = a + [''] * (-diff) - elif diff > 0: - b = b + [''] * diff - i = 0 - for aline, bline in zip(a, b): - s = "^" + aline + "$" - try: - expr = re.compile(s) - except re.error as e: - msg = "Regular expression error in %s: %s" - raise re.error(msg % (repr(s), e.args[0])) - if not expr.search(bline): - result.append("%sc%s" % (i + 1, i + 1)) - result.append('< ' + repr(a[i])) - result.append('---') - result.append('> ' + repr(b[i])) - i = i + 1 - return result - - -if os.name == 'posix': - def escape(arg): - "escape shell special characters" - slash = '\\' - special = '"$' - arg = arg.replace(slash, slash + slash) - for c in special: - arg = arg.replace(c, slash + c) - if re_space.search(arg): - arg = '"' + arg + '"' - return arg -else: - # Windows does not allow special characters in file names - # anyway, so no need for an escape function, we will just quote - # the arg. - def escape(arg): - if re_space.search(arg): - arg = '"' + arg + '"' - return arg - -if os.name == 'java': - python = os.path.join(sys.prefix, 'jython') -else: - python = os.environ.get('python_executable', sys.executable) -_python_ = escape(python) - -if sys.platform == 'win32': - - default_sleep_seconds = 2 - - def where_is(file, path=None, pathext=None): - if path is None: - path = os.environ['PATH'] - if is_String(path): - path = path.split(os.pathsep) - if pathext is None: - pathext = os.environ['PATHEXT'] - if is_String(pathext): - pathext = pathext.split(os.pathsep) - for ext in pathext: - if ext.lower() == file[-len(ext):].lower(): - pathext = [''] - break - for dir in path: - f = os.path.join(dir, file) - for ext in pathext: - fext = f + ext - if os.path.isfile(fext): - return fext - return None - -else: - - def where_is(file, path=None, pathext=None): - if path is None: - path = os.environ['PATH'] - if is_String(path): - path = path.split(os.pathsep) - for dir in path: - f = os.path.join(dir, file) - if os.path.isfile(f): - try: - st = os.stat(f) - except OSError: - continue - if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: - return f - return None - - default_sleep_seconds = 1 - - -import subprocess - -try: - subprocess.Popen.terminate -except AttributeError: - if sys.platform == 'win32': - import win32process - - def terminate(self): - win32process.TerminateProcess(self._handle, 1) - else: - def terminate(self): - os.kill(self.pid, signal.SIGTERM) - method = types.MethodType(terminate, None, subprocess.Popen) - setattr(subprocess.Popen, 'terminate', method) - - -# From Josiah Carlson, -# ASPN : Python Cookbook : Module to allow Asynchronous subprocess use on Windows and Posix platforms -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 - -PIPE = subprocess.PIPE - -if sys.platform == 'win32': # and subprocess.mswindows: - try: - from win32file import ReadFile, WriteFile - from win32pipe import PeekNamedPipe - except ImportError: - # If PyWin32 is not available, try ctypes instead - # XXX These replicate _just_enough_ PyWin32 behaviour for our purposes - import ctypes - from ctypes.wintypes import DWORD - - def ReadFile(hFile, bufSize, ol=None): - assert ol is None - lpBuffer = ctypes.create_string_buffer(bufSize) - bytesRead = DWORD() - bErr = ctypes.windll.kernel32.ReadFile( - hFile, lpBuffer, bufSize, ctypes.byref(bytesRead), ol) - if not bErr: - raise ctypes.WinError() - return (0, ctypes.string_at(lpBuffer, bytesRead.value)) - - def WriteFile(hFile, data, ol=None): - assert ol is None - bytesWritten = DWORD() - bErr = ctypes.windll.kernel32.WriteFile( - hFile, data, len(data), ctypes.byref(bytesWritten), ol) - if not bErr: - raise ctypes.WinError() - return (0, bytesWritten.value) - - def PeekNamedPipe(hPipe, size): - assert size == 0 - bytesAvail = DWORD() - bErr = ctypes.windll.kernel32.PeekNamedPipe( - hPipe, None, size, None, ctypes.byref(bytesAvail), None) - if not bErr: - raise ctypes.WinError() - return ("", bytesAvail.value, None) - import msvcrt -else: - import select - import fcntl - - try: - fcntl.F_GETFL - except AttributeError: - fcntl.F_GETFL = 3 - - try: - fcntl.F_SETFL - except AttributeError: - fcntl.F_SETFL = 4 - - -class Popen(subprocess.Popen): - def recv(self, maxsize=None): - return self._recv('stdout', maxsize) - - def recv_err(self, maxsize=None): - return self._recv('stderr', maxsize) - - def send_recv(self, input='', maxsize=None): - return self.send(input), self.recv(maxsize), self.recv_err(maxsize) - - def get_conn_maxsize(self, which, maxsize): - if maxsize is None: - maxsize = 1024 - elif maxsize < 1: - maxsize = 1 - return getattr(self, which), maxsize - - def _close(self, which): - getattr(self, which).close() - setattr(self, which, None) - - if sys.platform == 'win32': # and subprocess.mswindows: - def send(self, input): - input = to_bytes(input) - if not self.stdin: - return None - - try: - x = msvcrt.get_osfhandle(self.stdin.fileno()) - (errCode, written) = WriteFile(x, input) - except ValueError: - return self._close('stdin') - except (subprocess.pywintypes.error, Exception) as why: - if why.args[0] in (109, errno.ESHUTDOWN): - return self._close('stdin') - raise - - return written - - def _recv(self, which, maxsize): - conn, maxsize = self.get_conn_maxsize(which, maxsize) - if conn is None: - return None - - try: - x = msvcrt.get_osfhandle(conn.fileno()) - (read, nAvail, nMessage) = PeekNamedPipe(x, 0) - if maxsize < nAvail: - nAvail = maxsize - if nAvail > 0: - (errCode, read) = ReadFile(x, nAvail, None) - except ValueError: - return self._close(which) - except (subprocess.pywintypes.error, Exception) as why: - if why.args[0] in (109, errno.ESHUTDOWN): - return self._close(which) - raise - - # if self.universal_newlines: - # read = self._translate_newlines(read) - return read - - else: - def send(self, input): - if not self.stdin: - return None - - if not select.select([], [self.stdin], [], 0)[1]: - return 0 - - try: - written = os.write(self.stdin.fileno(), - bytearray(input, 'utf-8')) - except OSError as why: - if why.args[0] == errno.EPIPE: # broken pipe - return self._close('stdin') - raise - - return written - - def _recv(self, which, maxsize): - conn, maxsize = self.get_conn_maxsize(which, maxsize) - if conn is None: - return None - - try: - flags = fcntl.fcntl(conn, fcntl.F_GETFL) - except TypeError: - flags = None - else: - if not conn.closed: - fcntl.fcntl(conn, fcntl.F_SETFL, flags | os.O_NONBLOCK) - - try: - if not select.select([conn], [], [], 0)[0]: - return '' - - r = conn.read(maxsize) - if not r: - return self._close(which) - - # if self.universal_newlines: - # r = self._translate_newlines(r) - return r - finally: - if not conn.closed and not flags is None: - fcntl.fcntl(conn, fcntl.F_SETFL, flags) - - -disconnect_message = "Other end disconnected!" - - -def recv_some(p, t=.1, e=1, tr=5, stderr=0): - if tr < 1: - tr = 1 - x = time.time() + t - y = [] - r = '' - pr = p.recv - if stderr: - pr = p.recv_err - while time.time() < x or r: - r = pr() - if r is None: - if e: - raise Exception(disconnect_message) - else: - break - elif r: - y.append(r) - else: - time.sleep(max((x - time.time()) / tr, 0)) - return ''.join(y) - - -def send_all(p, data): - while len(data): - sent = p.send(data) - if sent is None: - raise Exception(disconnect_message) - data = memoryview(data)[sent:] - - -_Cleanup = [] - - -def _clean(): - global _Cleanup - cleanlist = [c for c in _Cleanup if c] - del _Cleanup[:] - cleanlist.reverse() - for test in cleanlist: - test.cleanup() - - -atexit.register(_clean) - - -class TestCmd(object): - """Class TestCmd - """ - - def __init__(self, description=None, - program=None, - interpreter=None, - workdir=None, - subdir=None, - verbose=None, - match=None, - match_stdout=None, - match_stderr=None, - diff=None, - diff_stdout=None, - diff_stderr=None, - combine=0, - universal_newlines=True, - timeout=None): - self.external = os.environ.get('SCONS_EXTERNAL_TEST', 0) - self._cwd = os.getcwd() - self.description_set(description) - self.program_set(program) - self.interpreter_set(interpreter) - if verbose is None: - try: - verbose = max(0, int(os.environ.get('TESTCMD_VERBOSE', 0))) - except ValueError: - verbose = 0 - self.verbose_set(verbose) - self.combine = combine - self.universal_newlines = universal_newlines - self.process = None - self.set_timeout(timeout) - self.set_match_function(match, match_stdout, match_stderr) - self.set_diff_function(diff, diff_stdout, diff_stderr) - self._dirlist = [] - self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0} - preserve_value = os.environ.get('PRESERVE', False) - if preserve_value not in [0, '0', 'False']: - self._preserve['pass_test'] = os.environ['PRESERVE'] - self._preserve['fail_test'] = os.environ['PRESERVE'] - self._preserve['no_result'] = os.environ['PRESERVE'] - else: - try: - self._preserve['pass_test'] = os.environ['PRESERVE_PASS'] - except KeyError: - pass - try: - self._preserve['fail_test'] = os.environ['PRESERVE_FAIL'] - except KeyError: - pass - try: - self._preserve['no_result'] = os.environ['PRESERVE_NO_RESULT'] - except KeyError: - pass - self._stdout = [] - self._stderr = [] - self.status = None - self.condition = 'no_result' - self.workdir_set(workdir) - self.subdir(subdir) - self.fixture_dirs = [] - - def __del__(self): - self.cleanup() - - def __repr__(self): - return "%x" % id(self) - - banner_char = '=' - banner_width = 80 - - def banner(self, s, width=None): - if width is None: - width = self.banner_width - return s + self.banner_char * (width - len(s)) - - escape = staticmethod(escape) - - def canonicalize(self, path): - if is_List(path): - path = os.path.join(*tuple(path)) - if not os.path.isabs(path): - path = os.path.join(self.workdir, path) - return path - - def chmod(self, path, mode): - """Changes permissions on the specified file or directory - path name.""" - path = self.canonicalize(path) - os.chmod(path, mode) - - def cleanup(self, condition=None): - """Removes any temporary working directories for the specified - TestCmd environment. If the environment variable PRESERVE was - set when the TestCmd environment was created, temporary working - directories are not removed. If any of the environment variables - PRESERVE_PASS, PRESERVE_FAIL, or PRESERVE_NO_RESULT were set - when the TestCmd environment was created, then temporary working - directories are not removed if the test passed, failed, or had - no result, respectively. Temporary working directories are also - preserved for conditions specified via the preserve method. - - Typically, this method is not called directly, but is used when - the script exits to clean up temporary working directories as - appropriate for the exit status. - """ - if not self._dirlist: - return - os.chdir(self._cwd) - self.workdir = None - if condition is None: - condition = self.condition - if self._preserve[condition]: - for dir in self._dirlist: - print(u"Preserved directory " + dir + "\n") - else: - list = self._dirlist[:] - list.reverse() - for dir in list: - self.writable(dir, 1) - shutil.rmtree(dir, ignore_errors=1) - self._dirlist = [] - - global _Cleanup - if self in _Cleanup: - _Cleanup.remove(self) - - def command_args(self, program=None, - interpreter=None, - arguments=None): - if not self.external: - if program: - if isinstance(program, str) and not os.path.isabs(program): - program = os.path.join(self._cwd, program) - else: - program = self.program - if not interpreter: - interpreter = self.interpreter - else: - if not program: - program = self.program - if not interpreter: - interpreter = self.interpreter - if not isinstance(program, (list, tuple)): - program = [program] - cmd = list(program) - if interpreter: - if not isinstance(interpreter, (list, tuple)): - interpreter = [interpreter] - cmd = list(interpreter) + cmd - if arguments: - if isinstance(arguments, str): - arguments = arguments.split() - cmd.extend(arguments) - return cmd - - def description_set(self, description): - """Set the description of the functionality being tested. - """ - self.description = description - - def set_diff_function(self, diff=_Null, stdout=_Null, stderr=_Null): - """Sets the specified diff functions. - """ - if diff is not _Null: - self._diff_function = diff - if stdout is not _Null: - self._diff_stdout_function = stdout - if stderr is not _Null: - self._diff_stderr_function = stderr - - def diff(self, a, b, name=None, diff_function=None, *args, **kw): - if diff_function is None: - try: - diff_function = getattr(self, self._diff_function) - except TypeError: - diff_function = self._diff_function - if diff_function is None: - diff_function = self.simple_diff - if name is not None: - print(self.banner(name)) - args = (a.splitlines(), b.splitlines()) + args - for line in diff_function(*args, **kw): - print(line) - - def diff_stderr(self, a, b, *args, **kw): - """Compare actual and expected file contents. - """ - try: - diff_stderr_function = getattr(self, self._diff_stderr_function) - except TypeError: - diff_stderr_function = self._diff_stderr_function - return self.diff(a, b, diff_function=diff_stderr_function, *args, **kw) - - def diff_stdout(self, a, b, *args, **kw): - """Compare actual and expected file contents. - """ - try: - diff_stdout_function = getattr(self, self._diff_stdout_function) - except TypeError: - diff_stdout_function = self._diff_stdout_function - return self.diff(a, b, diff_function=diff_stdout_function, *args, **kw) - - simple_diff = staticmethod(simple_diff) - - diff_re = staticmethod(diff_re) - - context_diff = staticmethod(difflib.context_diff) - - unified_diff = staticmethod(difflib.unified_diff) - - def fail_test(self, condition=1, function=None, skip=0, message=None): - """Cause the test to fail. - """ - if not condition: - return - self.condition = 'fail_test' - fail_test(self=self, - condition=condition, - function=function, - skip=skip, - message=message) - - def interpreter_set(self, interpreter): - """Set the program to be used to interpret the program - under test as a script. - """ - self.interpreter = interpreter - - def set_match_function(self, match=_Null, stdout=_Null, stderr=_Null): - """Sets the specified match functions. - """ - if match is not _Null: - self._match_function = match - if stdout is not _Null: - self._match_stdout_function = stdout - if stderr is not _Null: - self._match_stderr_function = stderr - - def match(self, lines, matches): - """Compare actual and expected file contents. - """ - try: - match_function = getattr(self, self._match_function) - except TypeError: - match_function = self._match_function - if match_function is None: - # Default is regular expression matches. - match_function = self.match_re - return match_function(lines, matches) - - def match_stderr(self, lines, matches): - """Compare actual and expected file contents. - """ - try: - match_stderr_function = getattr(self, self._match_stderr_function) - except TypeError: - match_stderr_function = self._match_stderr_function - if match_stderr_function is None: - # Default is to use whatever match= is set to. - match_stderr_function = self.match - return match_stderr_function(lines, matches) - - def match_stdout(self, lines, matches): - """Compare actual and expected file contents. - """ - try: - match_stdout_function = getattr(self, self._match_stdout_function) - except TypeError: - match_stdout_function = self._match_stdout_function - if match_stdout_function is None: - # Default is to use whatever match= is set to. - match_stdout_function = self.match - return match_stdout_function(lines, matches) - - match_exact = staticmethod(match_exact) - - match_caseinsensitive = staticmethod(match_caseinsensitive) - - match_re = staticmethod(match_re) - - match_re_dotall = staticmethod(match_re_dotall) - - def no_result(self, condition=1, function=None, skip=0): - """Report that the test could not be run. - """ - if not condition: - return - self.condition = 'no_result' - no_result(self=self, - condition=condition, - function=function, - skip=skip) - - def pass_test(self, condition=1, function=None): - """Cause the test to pass. - """ - if not condition: - return - self.condition = 'pass_test' - pass_test(self=self, condition=condition, function=function) - - def preserve(self, *conditions): - """Arrange for the temporary working directories for the - specified TestCmd environment to be preserved for one or more - conditions. If no conditions are specified, arranges for - the temporary working directories to be preserved for all - conditions. - """ - if conditions is (): - conditions = ('pass_test', 'fail_test', 'no_result') - for cond in conditions: - self._preserve[cond] = 1 - - def program_set(self, program): - """Set the executable program or script to be tested. - """ - if not self.external: - if program and not os.path.isabs(program): - program = os.path.join(self._cwd, program) - self.program = program - - def read(self, file, mode='rb', newline=None): - """Reads and returns the contents of the specified file name. - The file name may be a list, in which case the elements are - concatenated with the os.path.join() method. The file is - assumed to be under the temporary working directory unless it - is an absolute path name. The I/O mode for the file may - be specified; it must begin with an 'r'. The default is - 'rb' (binary read). - """ - file = self.canonicalize(file) - if mode[0] != 'r': - raise ValueError("mode must begin with 'r'") - if IS_PY3 and 'b' not in mode: - return open(file, mode, newline=newline).read() - else: - return open(file, mode).read() - - def rmdir(self, dir): - """Removes the specified dir name. - The dir name may be a list, in which case the elements are - concatenated with the os.path.join() method. The dir is - assumed to be under the temporary working directory unless it - is an absolute path name. - The dir must be empty. - """ - dir = self.canonicalize(dir) - os.rmdir(dir) - - def _timeout(self): - self.process.terminate() - self.timer.cancel() - self.timer = None - - def set_timeout(self, timeout): - self.timeout = timeout - self.timer = None - - def parse_path(self, path, suppress_current=False): - """Return a list with the single path components of path. - """ - head, tail = os.path.split(path) - result = [] - if not tail: - if head == path: - return [head] - else: - result.append(tail) - head, tail = os.path.split(head) - while head and tail: - result.append(tail) - head, tail = os.path.split(head) - result.append(head or tail) - result.reverse() - - return result - - def dir_fixture(self, srcdir, dstdir=None): - """Copies the contents of the specified folder srcdir from - the directory of the called script, to the current - working directory. - The srcdir name may be a list, in which case the elements are - concatenated with the os.path.join() method. The dstdir is - assumed to be under the temporary working directory, it gets - created automatically, if it does not already exist. - """ - - if srcdir and self.fixture_dirs and not os.path.isabs(srcdir): - for dir in self.fixture_dirs: - spath = os.path.join(dir, srcdir) - if os.path.isdir(spath): - break - else: - spath = srcdir - - if dstdir: - dstdir = self.canonicalize(dstdir) - else: - dstdir = '.' - - if dstdir != '.' and not os.path.exists(dstdir): - dstlist = self.parse_path(dstdir) - if len(dstlist) > 0 and dstlist[0] == ".": - dstlist = dstlist[1:] - for idx in range(len(dstlist)): - self.subdir(dstlist[:idx + 1]) - - if dstdir and self.workdir: - dstdir = os.path.join(self.workdir, dstdir) - - for entry in os.listdir(spath): - epath = os.path.join(spath, entry) - dpath = os.path.join(dstdir, entry) - if os.path.isdir(epath): - # Copy the subfolder - shutil.copytree(epath, dpath) - else: - shutil.copy(epath, dpath) - - def file_fixture(self, srcfile, dstfile=None): - """Copies the file srcfile from the directory of - the called script, to the current working directory. - The dstfile is assumed to be under the temporary working - directory unless it is an absolute path name. - If dstfile is specified its target directory gets created - automatically, if it does not already exist. - """ - srcpath, srctail = os.path.split(srcfile) - - if srcpath and (not self.fixture_dirs or os.path.isabs(srcpath)): - spath = srcfile - else: - for dir in self.fixture_dirs: - spath = os.path.join(dir, srcfile) - if os.path.isfile(spath): - break - - if not dstfile: - if srctail: - dpath = os.path.join(self.workdir, srctail) - else: - return - else: - dstpath, dsttail = os.path.split(dstfile) - if dstpath: - if not os.path.exists(os.path.join(self.workdir, dstpath)): - dstlist = self.parse_path(dstpath) - if len(dstlist) > 0 and dstlist[0] == ".": - dstlist = dstlist[1:] - for idx in range(len(dstlist)): - self.subdir(dstlist[:idx + 1]) - - dpath = os.path.join(self.workdir, dstfile) - shutil.copy(spath, dpath) - - def start(self, program=None, - interpreter=None, - arguments=None, - universal_newlines=None, - timeout=_Null, - **kw): - """ - Starts a program or script for the test environment. - - The specified program will have the original directory - prepended unless it is enclosed in a [list]. - """ - cmd = self.command_args(program, interpreter, arguments) - if self.verbose: - cmd_string = ' '.join([self.escape(c) for c in cmd]) - sys.stderr.write(cmd_string + "\n") - if universal_newlines is None: - universal_newlines = self.universal_newlines - - # On Windows, if we make stdin a pipe when we plan to send - # no input, and the test program exits before - # Popen calls msvcrt.open_osfhandle, that call will fail. - # So don't use a pipe for stdin if we don't need one. - stdin = kw.get('stdin', None) - if stdin is not None: - stdin = subprocess.PIPE - - combine = kw.get('combine', self.combine) - if combine: - stderr_value = subprocess.STDOUT - else: - stderr_value = subprocess.PIPE - - if timeout is _Null: - timeout = self.timeout - if timeout: - self.timer = threading.Timer(float(timeout), self._timeout) - self.timer.start() - - if IS_PY3 and sys.platform == 'win32': - # Set this otherwist stdout/stderr pipes default to - # windows default locale cp1252 which will throw exception - # if using non-ascii characters. - # For example test/Install/non-ascii-name.py - os.environ['PYTHONIOENCODING'] = 'utf-8' - - # It seems that all pythons up to py3.6 still set text mode if you set encoding. - # TODO: File enhancement request on python to propagate universal_newlines even - # if encoding is set.hg c - p = Popen(cmd, - stdin=stdin, - stdout=subprocess.PIPE, - stderr=stderr_value, - env=os.environ, - universal_newlines=False) - - self.process = p - return p - - @staticmethod - def fix_binary_stream(stream): - """ - Handle stdout/stderr from popen when we specify universal_newlines = False. - This will read from the pipes in binary mode, not decode the output, - and not convert line endings to \n. - We do this because in py3 (3.5) with universal_newlines=True, it will - choose the default system locale to decode the output, and this breaks unicode - output. Specifically breaking test/option--tree.py which outputs a unicode char. - - py 3.6 allows us to pass an encoding param to popen thus not requiring the decode - nor end of line handling, because we propagate universal_newlines as specified. - - TODO: Do we need to pass universal newlines into this function? - """ - - if not stream: - return stream - # TODO: Run full tests on both platforms and see if this fixes failures - # It seems that py3.6 still sets text mode if you set encoding. - elif sys.version_info[0] == 3:# TODO and sys.version_info[1] < 6: - stream = stream.decode('utf-8') - stream = stream.replace('\r\n', '\n') - elif sys.version_info[0] == 2: - stream = stream.replace('\r\n', '\n') - - return stream - - - def finish(self, popen=None, **kw): - """ - Finishes and waits for the process being run under control of - the specified popen argument, recording the exit status, - standard output and error output. - """ - if popen is None: - popen = self.process - stdout, stderr = popen.communicate() - - stdout = self.fix_binary_stream(stdout) - stderr = self.fix_binary_stream(stderr) - - if self.timer: - self.timer.cancel() - self.timer = None - self.status = popen.returncode - self.process = None - self._stdout.append(stdout or '') - self._stderr.append(stderr or '') - - def run(self, program=None, - interpreter=None, - arguments=None, - chdir=None, - stdin=None, - universal_newlines=None, - timeout=_Null): - """Runs a test of the program or script for the test - environment. Standard output and error output are saved for - future retrieval via the stdout() and stderr() methods. - - The specified program will have the original directory - prepended unless it is enclosed in a [list]. - """ - if self.external: - if not program: - program = self.program - if not interpreter: - interpreter = self.interpreter - - if universal_newlines is None: - universal_newlines = self.universal_newlines - - if chdir: - oldcwd = os.getcwd() - if not os.path.isabs(chdir): - chdir = os.path.join(self.workpath(chdir)) - if self.verbose: - sys.stderr.write("chdir(" + chdir + ")\n") - os.chdir(chdir) - p = self.start(program=program, - interpreter=interpreter, - arguments=arguments, - universal_newlines=universal_newlines, - timeout=timeout, - stdin=stdin) - if is_List(stdin): - stdin = ''.join(stdin) - - if stdin and IS_PY3:# and sys.version_info[1] < 6: - stdin = to_bytes(stdin) - - # TODO(sgk): figure out how to re-use the logic in the .finish() - # method above. Just calling it from here causes problems with - # subclasses that redefine .finish(). We could abstract this - # into Yet Another common method called both here and by .finish(), - # but that seems ill-thought-out. - stdout, stderr = p.communicate(input=stdin) - if self.timer: - self.timer.cancel() - self.timer = None - self.status = p.returncode - self.process = None - - stdout = self.fix_binary_stream(stdout) - stderr = self.fix_binary_stream(stderr) - - - self._stdout.append(stdout or '') - self._stderr.append(stderr or '') - - if chdir: - os.chdir(oldcwd) - if self.verbose >= 2: - write = sys.stdout.write - write('============ STATUS: %d\n' % self.status) - out = self.stdout() - if out or self.verbose >= 3: - write('============ BEGIN STDOUT (len=%d):\n' % len(out)) - write(out) - write('============ END STDOUT\n') - err = self.stderr() - if err or self.verbose >= 3: - write('============ BEGIN STDERR (len=%d)\n' % len(err)) - write(err) - write('============ END STDERR\n') - - def sleep(self, seconds=default_sleep_seconds): - """Sleeps at least the specified number of seconds. If no - number is specified, sleeps at least the minimum number of - seconds necessary to advance file time stamps on the current - system. Sleeping more seconds is all right. - """ - time.sleep(seconds) - - def stderr(self, run=None): - """Returns the error output from the specified run number. - If there is no specified run number, then returns the error - output of the last run. If the run number is less than zero, - then returns the error output from that many runs back from the - current run. - """ - if not run: - run = len(self._stderr) - elif run < 0: - run = len(self._stderr) + run - run = run - 1 - return self._stderr[run] - - def stdout(self, run=None): - """Returns the standard output from the specified run number. - If there is no specified run number, then returns the standard - output of the last run. If the run number is less than zero, - then returns the standard output from that many runs back from - the current run. - """ - if not run: - run = len(self._stdout) - elif run < 0: - run = len(self._stdout) + run - run = run - 1 - return self._stdout[run] - - def subdir(self, *subdirs): - """Create new subdirectories under the temporary working - directory, one for each argument. An argument may be a list, - in which case the list elements are concatenated using the - os.path.join() method. Subdirectories multiple levels deep - must be created using a separate argument for each level: - - test.subdir('sub', ['sub', 'dir'], ['sub', 'dir', 'ectory']) - - Returns the number of subdirectories actually created. - """ - count = 0 - for sub in subdirs: - if sub is None: - continue - if is_List(sub): - sub = os.path.join(*tuple(sub)) - new = os.path.join(self.workdir, sub) - try: - os.mkdir(new) - except OSError: - pass - else: - count = count + 1 - return count - - def symlink(self, target, link): - """Creates a symlink to the specified target. - The link name may be a list, in which case the elements are - concatenated with the os.path.join() method. The link is - assumed to be under the temporary working directory unless it - is an absolute path name. The target is *not* assumed to be - under the temporary working directory. - """ - if sys.platform == 'win32': - # Skip this on windows as we're not enabling it due to - # it requiring user permissions which aren't always present - # and we don't have a good way to detect those permissions yet. - return - link = self.canonicalize(link) - try: - os.symlink(target, link) - except AttributeError: - pass # Windows has no symlink - - def tempdir(self, path=None): - """Creates a temporary directory. - A unique directory name is generated if no path name is specified. - The directory is created, and will be removed when the TestCmd - object is destroyed. - """ - if path is None: - try: - path = tempfile.mktemp(prefix=tempfile.template) - except TypeError: - path = tempfile.mktemp() - os.mkdir(path) - - # Symlinks in the path will report things - # differently from os.getcwd(), so chdir there - # and back to fetch the canonical path. - cwd = os.getcwd() - try: - os.chdir(path) - path = os.getcwd() - finally: - os.chdir(cwd) - - # Uppercase the drive letter since the case of drive - # letters is pretty much random on win32: - drive, rest = os.path.splitdrive(path) - if drive: - path = drive.upper() + rest - - # - self._dirlist.append(path) - - global _Cleanup - if self not in _Cleanup: - _Cleanup.append(self) - - return path - - def touch(self, path, mtime=None): - """Updates the modification time on the specified file or - directory path name. The default is to update to the - current time if no explicit modification time is specified. - """ - path = self.canonicalize(path) - atime = os.path.getatime(path) - if mtime is None: - mtime = time.time() - os.utime(path, (atime, mtime)) - - def unlink(self, file): - """Unlinks the specified file name. - The file name may be a list, in which case the elements are - concatenated with the os.path.join() method. The file is - assumed to be under the temporary working directory unless it - is an absolute path name. - """ - file = self.canonicalize(file) - os.unlink(file) - - def verbose_set(self, verbose): - """Set the verbose level. - """ - self.verbose = verbose - - def where_is(self, file, path=None, pathext=None): - """Find an executable file. - """ - if is_List(file): - file = os.path.join(*tuple(file)) - if not os.path.isabs(file): - file = where_is(file, path, pathext) - return file - - def workdir_set(self, path): - """Creates a temporary working directory with the specified - path name. If the path is a null string (''), a unique - directory name is created. - """ - if (path != None): - if path == '': - path = None - path = self.tempdir(path) - self.workdir = path - - def workpath(self, *args): - """Returns the absolute path name to a subdirectory or file - within the current temporary working directory. Concatenates - the temporary working directory name with the specified - arguments using the os.path.join() method. - """ - return os.path.join(self.workdir, *tuple(args)) - - def readable(self, top, read=1): - """Make the specified directory tree readable (read == 1) - or not (read == None). - - This method has no effect on Windows systems, which use a - completely different mechanism to control file readability. - """ - - if sys.platform == 'win32': - return - - 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] | stat.S_IREAD)) - else: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~stat.S_IREAD)) - - 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. - do_chmod(top) - for dirpath, dirnames, filenames in os.walk(top): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - else: - # It's a directory and we're trying to turn off read - # permission, which means we have to chmod the directories - # 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. - for dirpath, dirnames, filenames in os.walk(top, topdown=0): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - do_chmod(top) - - def writable(self, top, write=1): - """Make the specified directory tree writable (write == 1) - or not (write == None). - """ - - if sys.platform == 'win32': - - if write: - def do_chmod(fname): - try: - os.chmod(fname, stat.S_IWRITE) - except OSError: - pass - else: - def do_chmod(fname): - try: - os.chmod(fname, stat.S_IREAD) - except OSError: - pass - - else: - - 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] | 0o200)) - else: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~0o200)) - - if os.path.isfile(top): - do_chmod(top) - else: - do_chmod(top) - for dirpath, dirnames, filenames in os.walk(top, topdown=0): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - - def executable(self, top, execute=1): - """Make the specified directory tree executable (execute == 1) - or not (execute == None). - - This method has no effect on Windows systems, which use a - completely different mechanism to control file executability. - """ - - if sys.platform == 'win32': - return - - 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] | stat.S_IEXEC)) - else: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~stat.S_IEXEC)) - - if os.path.isfile(top): - # 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. - do_chmod(top) - for dirpath, dirnames, filenames in os.walk(top): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - else: - # 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. - for dirpath, dirnames, filenames in os.walk(top, topdown=0): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - do_chmod(top) - - def write(self, file, content, mode='wb'): - """Writes the specified content text (second argument) to the - specified file name (first argument). The file name may be - a list, in which case the elements are concatenated with the - os.path.join() method. The file is created under the temporary - working directory. Any subdirectories in the path must already - exist. The I/O mode for the file may be specified; it must - begin with a 'w'. The default is 'wb' (binary write). - """ - file = self.canonicalize(file) - if mode[0] != 'w': - raise ValueError("mode must begin with 'w'") - with open(file, mode) as f: - try: - f.write(content) - except TypeError as e: - # python 3 default strings are not bytes, but unicode - f.write(bytes(content, 'utf-8')) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestCmdTests.py b/QMTest/TestCmdTests.py deleted file mode 100644 index b9226fd..0000000 --- a/QMTest/TestCmdTests.py +++ /dev/null @@ -1,3419 +0,0 @@ -#!/usr/bin/env python -""" -TestCmdTests.py: Unit tests for the TestCmd.py module. - -Copyright 2000-2010 Steven Knight -This module is free software, and you may redistribute it and/or modify -it under the same terms as Python itself, so long as this copyright message -and disclaimer are retained in their original form. - -IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -""" - -__author__ = "Steven Knight " -__revision__ = "TestCmdTests.py 1.3.D001 2010/06/03 12:58:27 knight" - -import os -import shutil -import signal -import stat -from StringIO import StringIO -import sys -import tempfile -import time -import types -import unittest -from UserList import UserList - - -# Strip the current directory so we get the right TestCmd.py module. -sys.path = sys.path[1:] - -import TestCmd - -def _is_readable(path): - # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IREAD - -def _is_writable(path): - # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IWRITE - -def _is_executable(path): - # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IEXEC - -def _clear_dict(dict, *keys): - for key in keys: - try: - dict[key] = '' # del dict[key] - except KeyError: - pass - -import subprocess - -try: - subprocess.Popen.terminate -except AttributeError: - if sys.platform == 'win32': - import win32process - def terminate(self): - win32process.TerminateProcess(self._handle, 1) - else: - def terminate(self): - os.kill(self.pid, signal.SIGTERM) - method = types.MethodType(terminate, None, subprocess.Popen) - setattr(subprocess.Popen, 'terminate', method) - -class ExitError(Exception): - pass - -class TestCmdTestCase(unittest.TestCase): - """Base class for TestCmd test cases, with fixture and utility methods.""" - - def setUp(self): - self.orig_cwd = os.getcwd() - - def tearDown(self): - os.chdir(self.orig_cwd) - - def setup_run_scripts(self): - class T: - pass - - t = T() - - t.script = 'script' - t.scriptx = 'scriptx.bat' - t.script1 = 'script_1.txt' - t.scriptout = 'scriptout' - t.scripterr = 'scripterr' - fmt = "import os, sys; cwd = os.getcwd(); " + \ - "sys.stdout.write('%s: STDOUT: %%s: %%s\\n' %% (cwd, sys.argv[1:])); " + \ - "sys.stderr.write('%s: STDERR: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" - fmtout = "import os, sys; cwd = os.getcwd(); " + \ - "sys.stdout.write('%s: STDOUT: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" - fmterr = "import os, sys; cwd = os.getcwd(); " + \ - "sys.stderr.write('%s: STDERR: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" - text = fmt % (t.script, t.script) - textx = fmt % (t.scriptx, t.scriptx) - if sys.platform == 'win32': - textx = textx.replace('%', '%%') - textx = '@python -c "%s"' % textx + ' %1 %2 %3 %4 %5 %6 %7 %8 %9\n' - else: - textx = '#! /usr/bin/env python\n' + textx + '\n' - text1 = 'A first line to be ignored!\n' + fmt % (t.script1, t.script1) - textout = fmtout % (t.scriptout) - texterr = fmterr % (t.scripterr) - - run_env = TestCmd.TestCmd(workdir = '') - run_env.subdir('sub dir') - t.run_env = run_env - - t.sub_dir = run_env.workpath('sub dir') - t.script_path = run_env.workpath('sub dir', t.script) - t.scriptx_path = run_env.workpath('sub dir', t.scriptx) - t.script1_path = run_env.workpath('sub dir', t.script1) - t.scriptout_path = run_env.workpath('sub dir', t.scriptout) - t.scripterr_path = run_env.workpath('sub dir', t.scripterr) - - run_env.write(t.script_path, text) - run_env.write(t.scriptx_path, textx) - run_env.write(t.script1_path, text1) - run_env.write(t.scriptout_path, textout) - run_env.write(t.scripterr_path, texterr) - - os.chmod(t.script_path, 0o644) # XXX UNIX-specific - os.chmod(t.scriptx_path, 0o755) # XXX UNIX-specific - os.chmod(t.script1_path, 0o644) # XXX UNIX-specific - os.chmod(t.scriptout_path, 0o644) # XXX UNIX-specific - os.chmod(t.scripterr_path, 0o644) # XXX UNIX-specific - - t.orig_cwd = os.getcwd() - - t.workdir = run_env.workpath('sub dir') - os.chdir(t.workdir) - - return t - - def translate_newlines(self, data): - data = data.replace("\r\n", "\n") - return data - - def call_python(self, input, python=None): - if python is None: - python = sys.executable - p = subprocess.Popen(python, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE, - stdout=subprocess.PIPE) - stdout, stderr = p.communicate(input) - stdout = self.translate_newlines(stdout) - stderr = self.translate_newlines(stderr) - return stdout, stderr, p.returncode - - def popen_python(self, input, status=0, stdout="", stderr="", python=None): - if python is None: - python = sys.executable - _stdout, _stderr, _status = self.call_python(input, python) - _stdout = self.translate_newlines(_stdout) - _stderr = self.translate_newlines(_stderr) - assert _status == status, \ - "status = %s, expected %s\n" % (str(_status), str(status)) + \ - "STDOUT ===================\n" + _stdout + \ - "STDERR ===================\n" + _stderr - assert _stdout == stdout, \ - "Expected STDOUT ==========\n" + stdout + \ - "Actual STDOUT ============\n" + _stdout + \ - "STDERR ===================\n" + _stderr - assert _stderr == stderr, \ - "Expected STDERR ==========\n" + stderr + \ - "Actual STDERR ============\n" + _stderr - - def run_match(self, content, *args): - expect = "%s: %s: %s: %s\n" % args - content = self.translate_newlines(content) - assert content == expect, \ - "Expected %s ==========\n" % args[1] + expect + \ - "Actual %s ============\n" % args[1] + content - - - -class __init__TestCase(TestCmdTestCase): - def test_init(self): - """Test init()""" - test = TestCmd.TestCmd() - test = TestCmd.TestCmd(description = 'test') - test = TestCmd.TestCmd(description = 'test', program = 'foo') - test = TestCmd.TestCmd(description = 'test', - program = 'foo', - universal_newlines=None) - - - -class basename_TestCase(TestCmdTestCase): - def test_basename(self): - """Test basename() [XXX TO BE WRITTEN]""" - assert 1 == 1 - - - -class cleanup_TestCase(TestCmdTestCase): - def test_cleanup(self): - """Test cleanup()""" - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - test.write('file1', "Test file #1\n") - test.cleanup() - assert not os.path.exists(wdir) - - def test_writable(self): - """Test cleanup() when the directory isn't writable""" - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - test.write('file2', "Test file #2\n") - os.chmod(test.workpath('file2'), 0o400) - os.chmod(wdir, 0o500) - test.cleanup() - assert not os.path.exists(wdir) - - def test_shutil(self): - """Test cleanup() when used with shutil""" - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - os.chdir(wdir) - - import shutil - save_rmtree = shutil.rmtree - def my_rmtree(dir, ignore_errors=0, wdir=wdir, _rmtree=save_rmtree): - assert os.getcwd() != wdir - return _rmtree(dir, ignore_errors=ignore_errors) - try: - shutil.rmtree = my_rmtree - test.cleanup() - finally: - shutil.rmtree = save_rmtree - - def test_atexit(self): - """Test cleanup() when atexit is used""" - self.popen_python("""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import atexit -def my_exitfunc(): - print("my_exitfunc()") -atexit.register(my_exitfunc) -import TestCmd -result = TestCmd.TestCmd(workdir = '') -sys.exit(0) -""" % self.orig_cwd, stdout='my_exitfunc()\n') - - def test_exitfunc(self): - """Test cleanup() when sys.exitfunc is set""" - self.popen_python("""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -def my_exitfunc(): - print("my_exitfunc()") -sys.exitfunc = my_exitfunc -import TestCmd -result = TestCmd.TestCmd(workdir = '') -sys.exit(0) -""" % self.orig_cwd, stdout='my_exitfunc()\n') - - - -class chmod_TestCase(TestCmdTestCase): - def test_chmod(self): - """Test chmod()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'sub') - - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_sub_file2 = os.path.join(test.workdir, 'sub', 'file2') - - open(wdir_file1, 'w').write("") - open(wdir_sub_file2, 'w').write("") - - if sys.platform == 'win32': - - test.chmod(wdir_file1, stat.S_IREAD) - test.chmod(['sub', 'file2'], stat.S_IWRITE) - - file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) - assert file1_mode == 0o444, '0%o' % file1_mode - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) - assert file2_mode == 0o666, '0%o' % file2_mode - - test.chmod('file1', stat.S_IWRITE) - test.chmod(wdir_sub_file2, stat.S_IREAD) - - file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) - assert file1_mode == 0o666, '0%o' % file1_mode - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) - assert file2_mode == 0o444, '0%o' % file2_mode - - else: - - test.chmod(wdir_file1, 0o700) - test.chmod(['sub', 'file2'], 0o760) - - file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) - assert file1_mode == 0o700, '0%o' % file1_mode - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) - assert file2_mode == 0o760, '0%o' % file2_mode - - test.chmod('file1', 0o765) - test.chmod(wdir_sub_file2, 0o567) - - file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) - assert file1_mode == 0o765, '0%o' % file1_mode - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) - assert file2_mode == 0o567, '0%o' % file2_mode - - - -class combine_TestCase(TestCmdTestCase): - def test_combine(self): - """Test combining stdout and stderr""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run1', """import sys -sys.stdout.write("run1 STDOUT %s\\n" % sys.argv[1:]) -sys.stdout.write("run1 STDOUT second line\\n") -sys.stderr.write("run1 STDERR %s\\n" % sys.argv[1:]) -sys.stderr.write("run1 STDERR second line\\n") -sys.stdout.write("run1 STDOUT third line\\n") -sys.stderr.write("run1 STDERR third line\\n") -""") - run_env.write('run2', """import sys -sys.stdout.write("run2 STDOUT %s\\n" % sys.argv[1:]) -sys.stdout.write("run2 STDOUT second line\\n") -sys.stderr.write("run2 STDERR %s\\n" % sys.argv[1:]) -sys.stderr.write("run2 STDERR second line\\n") -sys.stdout.write("run2 STDOUT third line\\n") -sys.stderr.write("run2 STDERR third line\\n") -""") - cwd = os.getcwd() - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - try: - test = TestCmd.TestCmd(interpreter = 'python', - workdir = '', - combine = 1) - try: - output = test.stdout() - except IndexError: - pass - else: - raise IndexError("got unexpected output:\n\t`%s'\n" % output) - - # The underlying system subprocess implementations can combine - # stdout and stderr in different orders, so we accomodate both. - - test.program_set('run1') - test.run(arguments = 'foo bar') - stdout_lines = """\ -run1 STDOUT ['foo', 'bar'] -run1 STDOUT second line -run1 STDOUT third line -""" - stderr_lines = """\ -run1 STDERR ['foo', 'bar'] -run1 STDERR second line -run1 STDERR third line -""" - foo_bar_expect = (stdout_lines + stderr_lines, - stderr_lines + stdout_lines) - - test.program_set('run2') - test.run(arguments = 'snafu') - stdout_lines = """\ -run2 STDOUT ['snafu'] -run2 STDOUT second line -run2 STDOUT third line -""" - stderr_lines = """\ -run2 STDERR ['snafu'] -run2 STDERR second line -run2 STDERR third line -""" - snafu_expect = (stdout_lines + stderr_lines, - stderr_lines + stdout_lines) - - # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL - output = test.stdout() - output = self.translate_newlines(output) - assert output in snafu_expect, output - error = test.stderr() - assert error == '', error - - output = test.stdout(run = -1) - output = self.translate_newlines(output) - assert output in foo_bar_expect, output - error = test.stderr(-1) - assert error == '', error - finally: - os.chdir(cwd) - - - -class description_TestCase(TestCmdTestCase): - def test_description(self): - """Test description()""" - test = TestCmd.TestCmd() - assert test.description is None, 'initialized description?' - test = TestCmd.TestCmd(description = 'test') - assert test.description == 'test', 'uninitialized description' - test.description_set('foo') - assert test.description == 'foo', 'did not set description' - - - -class diff_TestCase(TestCmdTestCase): - def test_diff_re(self): - """Test diff_re()""" - result = TestCmd.diff_re(["abcde"], ["abcde"]) - assert result == [], result - result = TestCmd.diff_re(["a.*e"], ["abcde"]) - assert result == [], result - result = TestCmd.diff_re(["a.*e"], ["xxx"]) - assert result == ['1c1', "< 'a.*e'", '---', "> 'xxx'"], result - - def test_diff_custom_function(self): - """Test diff() using a custom function""" - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def my_diff(a, b): - return [ - '*****', - a, - '*****', - b, - '*****', - ] -test = TestCmd.TestCmd(diff = my_diff) -test.diff("a\\nb1\\nc\\n", "a\\nb2\\nc\\n", "STDOUT") -sys.exit(0) -""" % self.orig_cwd, - stdout = """\ -STDOUT========================================================================== -***** -['a', 'b1', 'c'] -***** -['a', 'b2', 'c'] -***** -""") - - def test_diff_string(self): - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff = 'diff_re') -test.diff("a\\nb1\\nc\\n", "a\\nb2\\nc\\n", 'STDOUT') -sys.exit(0) -""" % self.orig_cwd, - stdout = """\ -STDOUT========================================================================== -2c2 -< 'b1' ---- -> 'b2' -""") - - def test_error(self): - """Test handling a compilation error in TestCmd.diff_re()""" - script_input = """import sys -sys.path = ['%s'] + sys.path -import TestCmd -assert TestCmd.diff_re(["a.*(e"], ["abcde"]) -sys.exit(0) -""" % self.orig_cwd - stdout, stderr, status = self.call_python(script_input) - assert status == 1, status - expect1 = "Regular expression error in '^a.*(e$': missing )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" - assert (stderr.find(expect1) != -1 or - stderr.find(expect2) != -1), repr(stderr) - - def test_simple_diff_static_method(self): - """Test calling the TestCmd.TestCmd.simple_diff() static method""" - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -result = TestCmd.TestCmd.simple_diff(['a', 'b', 'c', 'e', 'f1'], - ['a', 'c', 'd', 'e', 'f2']) -expect = ['2d1', '< b', '3a3', '> d', '5c5', '< f1', '---', '> f2'] -assert result == expect, result -sys.exit(0) -""" % self.orig_cwd) - - def test_context_diff_static_method(self): - """Test calling the TestCmd.TestCmd.context_diff() static method""" - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -result = TestCmd.TestCmd.context_diff(['a\\n', 'b\\n', 'c\\n', 'e\\n', 'f1\\n'], - ['a\\n', 'c\\n', 'd\\n', 'e\\n', 'f2\\n']) -result = list(result) -expect = [ - '*** \\n', - '--- \\n', - '***************\\n', - '*** 1,5 ****\\n', - ' a\\n', - '- b\\n', - ' c\\n', - ' e\\n', - '! f1\\n', - '--- 1,5 ----\\n', - ' a\\n', - ' c\\n', - '+ d\\n', - ' e\\n', - '! f2\\n', -] -assert result == expect, result -sys.exit(0) -""" % self.orig_cwd) - - def test_unified_diff_static_method(self): - """Test calling the TestCmd.TestCmd.unified_diff() static method""" - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -result = TestCmd.TestCmd.unified_diff(['a\\n', 'b\\n', 'c\\n', 'e\\n', 'f1\\n'], - ['a\\n', 'c\\n', 'd\\n', 'e\\n', 'f2\\n']) -result = list(result) -expect = [ - '--- \\n', - '+++ \\n', - '@@ -1,5 +1,5 @@\\n', - ' a\\n', - '-b\\n', - ' c\\n', - '+d\\n', - ' e\\n', - '-f1\\n', - '+f2\\n' -] -assert result == expect, result -sys.exit(0) -""" % self.orig_cwd) - - def test_diff_re_static_method(self): - """Test calling the TestCmd.TestCmd.diff_re() static method""" - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -result = TestCmd.TestCmd.diff_re(['a', 'b', 'c', '.', 'f1'], - ['a', 'c', 'd', 'e', 'f2']) -expect = [ - '2c2', - "< 'b'", - '---', - "> 'c'", - '3c3', - "< 'c'", - '---', - "> 'd'", - '5c5', - "< 'f1'", - '---', - "> 'f2'" -] -assert result == expect, result -sys.exit(0) -""" % self.orig_cwd) - - - -class diff_stderr_TestCase(TestCmdTestCase): - def test_diff_stderr_default(self): - """Test diff_stderr() default behavior""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd() -test.diff_stderr('a\nb1\nc\n', 'a\nb2\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -2c2 -< b1 ---- -> b2 -""") - - def test_diff_stderr_not_affecting_diff_stdout(self): - """Test diff_stderr() not affecting diff_stdout() behavior""" - self.popen_python(r"""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stderr='diff_re') -print("diff_stderr:") -test.diff_stderr('a\nb.\nc\n', 'a\nbb\nc\n') -print("diff_stdout:") -test.diff_stdout('a\nb.\nc\n', 'a\nbb\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -diff_stderr: -diff_stdout: -2c2 -< b. ---- -> bb -""") - - def test_diff_stderr_custom_function(self): - """Test diff_stderr() using a custom function""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def my_diff(a, b): - return ["a:"] + a + ["b:"] + b -test = TestCmd.TestCmd(diff_stderr=my_diff) -test.diff_stderr('abc', 'def') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -a: -abc -b: -def -""") - - def test_diff_stderr_TestCmd_function(self): - """Test diff_stderr() using a TestCmd function""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stderr = TestCmd.diff_re) -test.diff_stderr('a\n.\n', 'b\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -1c1 -< 'a' ---- -> 'b' -""") - - def test_diff_stderr_static_method(self): - """Test diff_stderr() using a static method""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stderr=TestCmd.TestCmd.diff_re) -test.diff_stderr('a\n.\n', 'b\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -1c1 -< 'a' ---- -> 'b' -""") - - def test_diff_stderr_string(self): - """Test diff_stderr() using a string to fetch the diff method""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stderr='diff_re') -test.diff_stderr('a\n.\n', 'b\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -1c1 -< 'a' ---- -> 'b' -""") - - - -class diff_stdout_TestCase(TestCmdTestCase): - def test_diff_stdout_default(self): - """Test diff_stdout() default behavior""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd() -test.diff_stdout('a\nb1\nc\n', 'a\nb2\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -2c2 -< b1 ---- -> b2 -""") - - def test_diff_stdout_not_affecting_diff_stderr(self): - """Test diff_stdout() not affecting diff_stderr() behavior""" - self.popen_python(r"""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stdout='diff_re') -print("diff_stdout:") -test.diff_stdout('a\nb.\nc\n', 'a\nbb\nc\n') -print("diff_stderr:") -test.diff_stderr('a\nb.\nc\n', 'a\nbb\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -diff_stdout: -diff_stderr: -2c2 -< b. ---- -> bb -""") - - def test_diff_stdout_custom_function(self): - """Test diff_stdout() using a custom function""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def my_diff(a, b): - return ["a:"] + a + ["b:"] + b -test = TestCmd.TestCmd(diff_stdout=my_diff) -test.diff_stdout('abc', 'def') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -a: -abc -b: -def -""") - - def test_diff_stdout_TestCmd_function(self): - """Test diff_stdout() using a TestCmd function""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stdout = TestCmd.diff_re) -test.diff_stdout('a\n.\n', 'b\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -1c1 -< 'a' ---- -> 'b' -""") - - def test_diff_stdout_static_method(self): - """Test diff_stdout() using a static method""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stdout=TestCmd.TestCmd.diff_re) -test.diff_stdout('a\n.\n', 'b\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -1c1 -< 'a' ---- -> 'b' -""") - - def test_diff_stdout_string(self): - """Test diff_stdout() using a string to fetch the diff method""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stdout='diff_re') -test.diff_stdout('a\n.\n', 'b\nc\n') -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -1c1 -< 'a' ---- -> 'b' -""") - - - -class exit_TestCase(TestCmdTestCase): - def test_exit(self): - """Test exit()""" - def _test_it(cwd, tempdir, condition, preserved): - close_true = {'pass_test': 1, 'fail_test': 0, 'no_result': 0} - exit_status = {'pass_test': 0, 'fail_test': 1, 'no_result': 2} - result_string = {'pass_test': "PASSED\n", - 'fail_test': "FAILED test at line 5 of \n", - 'no_result': "NO RESULT for test at line 5 of \n"} - global ExitError - input = """import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(workdir = '%s') -test.%s() -""" % (cwd, tempdir, condition) - stdout, stderr, status = self.call_python(input, python="python") - if close_true[condition]: - unexpected = (status != 0) - else: - unexpected = (status == 0) - if unexpected: - msg = "Unexpected exit status from python: %s\n" - raise ExitError(msg % status + stdout + stderr) - if status != exit_status[condition]: - msg = "Expected exit status %d, got %d\n" - raise ExitError(msg % (exit_status[condition], status)) - if stderr != result_string[condition]: - msg = "Expected error output:\n%sGot error output:\n%s" - raise ExitError(msg % (result_string[condition], stderr)) - if preserved: - if not os.path.exists(tempdir): - msg = "Working directory %s was mistakenly removed\n" - raise ExitError(msg % tempdir + stdout) - else: - if os.path.exists(tempdir): - msg = "Working directory %s was mistakenly preserved\n" - raise ExitError(msg % tempdir + stdout) - - run_env = TestCmd.TestCmd(workdir = '') - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - try: - cwd = self.orig_cwd - _clear_dict(os.environ, 'PRESERVE', 'PRESERVE_PASS', 'PRESERVE_FAIL', 'PRESERVE_NO_RESULT') - _test_it(cwd, 'dir01', 'pass_test', 0) - _test_it(cwd, 'dir02', 'fail_test', 0) - _test_it(cwd, 'dir03', 'no_result', 0) - os.environ['PRESERVE'] = '1' - _test_it(cwd, 'dir04', 'pass_test', 1) - _test_it(cwd, 'dir05', 'fail_test', 1) - _test_it(cwd, 'dir06', 'no_result', 1) - os.environ['PRESERVE'] = '' # del os.environ['PRESERVE'] - os.environ['PRESERVE_PASS'] = '1' - _test_it(cwd, 'dir07', 'pass_test', 1) - _test_it(cwd, 'dir08', 'fail_test', 0) - _test_it(cwd, 'dir09', 'no_result', 0) - os.environ['PRESERVE_PASS'] = '' # del os.environ['PRESERVE_PASS'] - os.environ['PRESERVE_FAIL'] = '1' - _test_it(cwd, 'dir10', 'pass_test', 0) - _test_it(cwd, 'dir11', 'fail_test', 1) - _test_it(cwd, 'dir12', 'no_result', 0) - os.environ['PRESERVE_FAIL'] = '' # del os.environ['PRESERVE_FAIL'] - os.environ['PRESERVE_NO_RESULT'] = '1' - _test_it(cwd, 'dir13', 'pass_test', 0) - _test_it(cwd, 'dir14', 'fail_test', 0) - _test_it(cwd, 'dir15', 'no_result', 1) - os.environ['PRESERVE_NO_RESULT'] = '' # del os.environ['PRESERVE_NO_RESULT'] - finally: - _clear_dict(os.environ, 'PRESERVE', 'PRESERVE_PASS', 'PRESERVE_FAIL', 'PRESERVE_NO_RESULT') - - - -class fail_test_TestCase(TestCmdTestCase): - def test_fail_test(self): - """Test fail_test()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """import sys -sys.stdout.write("run: STDOUT\\n") -sys.stderr.write("run: STDERR\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -TestCmd.fail_test(condition = 1) -""" % self.orig_cwd, status = 1, stderr = "FAILED test at line 4 of \n") - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') -test.run() -test.fail_test(condition = (test.status == 0)) -""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 6 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(program = 'run', interpreter = 'python', description = 'xyzzy', workdir = '') -test.run() -test.fail_test(condition = (test.status == 0)) -""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s [xyzzy]\n\tat line 6 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') -test.run() -def xxx(): - sys.stderr.write("printed on failure\\n") -test.fail_test(condition = (test.status == 0), function = xxx) -""" % self.orig_cwd, status = 1, stderr = "printed on failure\nFAILED test of %s\n\tat line 8 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def test1(self): - self.run() - self.fail_test(condition = (self.status == 0)) -def test2(self): - test1(self) -test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')) -""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 6 of (test1)\n\tfrom line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def test1(self): - self.run() - self.fail_test(condition = (self.status == 0), skip = 1) -def test2(self): - test1(self) -test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')) -""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) - - - -class interpreter_TestCase(TestCmdTestCase): - def test_interpreter(self): - """Test interpreter()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """import sys -sys.stdout.write("run: STDOUT\\n") -sys.stderr.write("run: STDERR\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(program = 'run', workdir = '') - test.interpreter_set('foo') - assert test.interpreter == 'foo', 'did not set interpreter' - test.interpreter_set('python') - assert test.interpreter == 'python', 'did not set interpreter' - test.run() - - - -class match_TestCase(TestCmdTestCase): - def test_match_default(self): - """Test match() default behavior""" - test = TestCmd.TestCmd() - assert test.match("abcde\n", "a.*e\n") - assert test.match("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match(lines, regexes) - - def test_match_custom_function(self): - """Test match() using a custom function""" - def match_length(lines, matches): - return len(lines) == len(matches) - test = TestCmd.TestCmd(match=match_length) - assert not test.match("123\n", "1\n") - assert test.match("123\n", "111\n") - assert not test.match("123\n123\n", "1\n1\n") - assert test.match("123\n123\n", "111\n111\n") - lines = ["123\n", "123\n"] - regexes = ["1\n", "1\n"] - assert test.match(lines, regexes) # due to equal numbers of lines - - def test_match_TestCmd_function(self): - """Test match() using a TestCmd function""" - test = TestCmd.TestCmd(match = TestCmd.match_exact) - assert not test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match(lines, regexes) - assert test.match(lines, lines) - - def test_match_static_method(self): - """Test match() using a static method""" - test = TestCmd.TestCmd(match=TestCmd.TestCmd.match_exact) - assert not test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match(lines, regexes) - assert test.match(lines, lines) - - def test_match_string(self): - """Test match() using a string to fetch the match method""" - test = TestCmd.TestCmd(match='match_exact') - assert not test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match(lines, regexes) - assert test.match(lines, lines) - - - -class match_exact_TestCase(TestCmdTestCase): - def test_match_exact_function(self): - """Test calling the TestCmd.match_exact() function""" - assert not TestCmd.match_exact("abcde\\n", "a.*e\\n") - assert TestCmd.match_exact("abcde\\n", "abcde\\n") - - def test_match_exact_instance_method(self): - """Test calling the TestCmd.TestCmd().match_exact() instance method""" - test = TestCmd.TestCmd() - assert not test.match_exact("abcde\\n", "a.*e\\n") - assert test.match_exact("abcde\\n", "abcde\\n") - - def test_match_exact_static_method(self): - """Test calling the TestCmd.TestCmd.match_exact() static method""" - assert not TestCmd.TestCmd.match_exact("abcde\\n", "a.*e\\n") - assert TestCmd.TestCmd.match_exact("abcde\\n", "abcde\\n") - - def test_evaluation(self): - """Test match_exact() evaluation""" - test = TestCmd.TestCmd() - assert not test.match_exact("abcde\n", "a.*e\n") - assert test.match_exact("abcde\n", "abcde\n") - assert not test.match_exact(["12345\n", "abcde\n"], ["1[0-9]*5\n", "a.*e\n"]) - assert test.match_exact(["12345\n", "abcde\n"], ["12345\n", "abcde\n"]) - assert not test.match_exact(UserList(["12345\n", "abcde\n"]), - ["1[0-9]*5\n", "a.*e\n"]) - assert test.match_exact(UserList(["12345\n", "abcde\n"]), - ["12345\n", "abcde\n"]) - assert not test.match_exact(["12345\n", "abcde\n"], - UserList(["1[0-9]*5\n", "a.*e\n"])) - assert test.match_exact(["12345\n", "abcde\n"], - UserList(["12345\n", "abcde\n"])) - assert not test.match_exact("12345\nabcde\n", "1[0-9]*5\na.*e\n") - assert test.match_exact("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_exact(lines, regexes) - assert test.match_exact(lines, lines) - - - -class match_re_dotall_TestCase(TestCmdTestCase): - def test_match_re_dotall_function(self): - """Test calling the TestCmd.match_re_dotall() function""" - assert TestCmd.match_re_dotall("abcde\nfghij\n", "a.*j\n") - - def test_match_re_dotall_instance_method(self): - """Test calling the TestCmd.TestCmd().match_re_dotall() instance method""" - test = TestCmd.TestCmd() - test.match_re_dotall("abcde\\nfghij\\n", "a.*j\\n") - - def test_match_re_dotall_static_method(self): - """Test calling the TestCmd.TestCmd.match_re_dotall() static method""" - assert TestCmd.TestCmd.match_re_dotall("abcde\nfghij\n", "a.*j\n") - - def test_error(self): - """Test handling a compilation error in TestCmd.match_re_dotall()""" - run_env = TestCmd.TestCmd(workdir = '') - cwd = os.getcwd() - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - try: - script_input = """import sys -sys.path = ['%s'] + sys.path -import TestCmd -assert TestCmd.match_re_dotall("abcde", "a.*(e") -sys.exit(0) -""" % cwd - stdout, stderr, status = self.call_python(script_input) - assert status == 1, status - expect1 = "Regular expression error in '^a.*(e$': missing )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" - assert (stderr.find(expect1) != -1 or - stderr.find(expect2) != -1), repr(stderr) - finally: - os.chdir(cwd) - - def test_evaluation(self): - """Test match_re_dotall() evaluation""" - test = TestCmd.TestCmd() - assert test.match_re_dotall("abcde\nfghij\n", "a.*e\nf.*j\n") - assert test.match_re_dotall("abcde\nfghij\n", "a[^j]*j\n") - assert test.match_re_dotall("abcde\nfghij\n", "abcde\nfghij\n") - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["1[0-9]*5\n", "a.*e\n", "f.*j\n"]) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["1.*j\n"]) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["12345\n", "abcde\n", "fghij\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["1[0-9]*5\n", "a.*e\n", "f.*j\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["1.*j\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["12345\n", "abcde\n", "fghij\n"]) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["1[0-9]*5\n", - "a.*e\n", - "f.*j\n"])) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["1.*j\n"])) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["12345\n", - "abcde\n", - "fghij\n"])) - assert test.match_re_dotall("12345\nabcde\nfghij\n", - "1[0-9]*5\na.*e\nf.*j\n") - assert test.match_re_dotall("12345\nabcde\nfghij\n", "1.*j\n") - assert test.match_re_dotall("12345\nabcde\nfghij\n", - "12345\nabcde\nfghij\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_re_dotall(lines, regexes) - assert test.match_re_dotall(lines, lines) - - - -class match_re_TestCase(TestCmdTestCase): - def test_match_re_function(self): - """Test calling the TestCmd.match_re() function""" - assert TestCmd.match_re("abcde\n", "a.*e\n") - - def test_match_re_instance_method(self): - """Test calling the TestCmd.TestCmd().match_re() instance method""" - test = TestCmd.TestCmd() - assert test.match_re("abcde\n", "a.*e\n") - - def test_match_re_static_method(self): - """Test calling the TestCmd.TestCmd.match_re() static method""" - assert TestCmd.TestCmd.match_re("abcde\n", "a.*e\n") - - def test_error(self): - """Test handling a compilation error in TestCmd.match_re()""" - run_env = TestCmd.TestCmd(workdir = '') - cwd = os.getcwd() - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - try: - script_input = """import sys -sys.path = ['%s'] + sys.path -import TestCmd -assert TestCmd.match_re("abcde\\n", "a.*(e\\n") -sys.exit(0) -""" % cwd - stdout, stderr, status = self.call_python(script_input) - assert status == 1, status - expect1 = "Regular expression error in '^a.*(e$': missing )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" - assert (stderr.find(expect1) != -1 or - stderr.find(expect2) != -1), repr(stderr) - finally: - os.chdir(cwd) - - def test_evaluation(self): - """Test match_re() evaluation""" - test = TestCmd.TestCmd() - assert test.match_re("abcde\n", "a.*e\n") - assert test.match_re("abcde\n", "abcde\n") - assert test.match_re(["12345\n", "abcde\n"], ["1[0-9]*5\n", "a.*e\n"]) - assert test.match_re(["12345\n", "abcde\n"], ["12345\n", "abcde\n"]) - assert test.match_re(UserList(["12345\n", "abcde\n"]), - ["1[0-9]*5\n", "a.*e\n"]) - assert test.match_re(UserList(["12345\n", "abcde\n"]), - ["12345\n", "abcde\n"]) - assert test.match_re(["12345\n", "abcde\n"], - UserList(["1[0-9]*5\n", "a.*e\n"])) - assert test.match_re(["12345\n", "abcde\n"], - UserList(["12345\n", "abcde\n"])) - assert test.match_re("12345\nabcde\n", "1[0-9]*5\na.*e\n") - assert test.match_re("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_re(lines, regexes) - assert test.match_re(lines, lines) - - - -class match_stderr_TestCase(TestCmdTestCase): - def test_match_stderr_default(self): - """Test match_stderr() default behavior""" - test = TestCmd.TestCmd() - assert test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_stderr(lines, regexes) - - def test_match_stderr_not_affecting_match_stdout(self): - """Test match_stderr() not affecting match_stdout() behavior""" - test = TestCmd.TestCmd(match_stderr=TestCmd.TestCmd.match_exact) - - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") - assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stderr(lines, regexes) - assert test.match_stderr(lines, lines) - - assert test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_stdout(lines, regexes) - - def test_match_stderr_custom_function(self): - """Test match_stderr() using a custom function""" - def match_length(lines, matches): - return len(lines) == len(matches) - test = TestCmd.TestCmd(match_stderr=match_length) - assert not test.match_stderr("123\n", "1\n") - assert test.match_stderr("123\n", "111\n") - assert not test.match_stderr("123\n123\n", "1\n1\n") - assert test.match_stderr("123\n123\n", "111\n111\n") - lines = ["123\n", "123\n"] - regexes = ["1\n", "1\n"] - assert test.match_stderr(lines, regexes) # equal numbers of lines - - def test_match_stderr_TestCmd_function(self): - """Test match_stderr() using a TestCmd function""" - test = TestCmd.TestCmd(match_stderr = TestCmd.match_exact) - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stderr(lines, regexes) - assert test.match_stderr(lines, lines) - - def test_match_stderr_static_method(self): - """Test match_stderr() using a static method""" - test = TestCmd.TestCmd(match_stderr=TestCmd.TestCmd.match_exact) - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stderr(lines, regexes) - assert test.match_stderr(lines, lines) - - def test_match_stderr_string(self): - """Test match_stderr() using a string to fetch the match method""" - test = TestCmd.TestCmd(match_stderr='match_exact') - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stderr(lines, regexes) - assert test.match_stderr(lines, lines) - - - -class match_stdout_TestCase(TestCmdTestCase): - def test_match_stdout_default(self): - """Test match_stdout() default behavior""" - test = TestCmd.TestCmd() - assert test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_stdout(lines, regexes) - - def test_match_stdout_not_affecting_match_stderr(self): - """Test match_stdout() not affecting match_stderr() behavior""" - test = TestCmd.TestCmd(match_stdout=TestCmd.TestCmd.match_exact) - - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") - assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stdout(lines, regexes) - assert test.match_stdout(lines, lines) - - assert test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_stderr(lines, regexes) - - def test_match_stdout_custom_function(self): - """Test match_stdout() using a custom function""" - def match_length(lines, matches): - return len(lines) == len(matches) - test = TestCmd.TestCmd(match_stdout=match_length) - assert not test.match_stdout("123\n", "1\n") - assert test.match_stdout("123\n", "111\n") - assert not test.match_stdout("123\n123\n", "1\n1\n") - assert test.match_stdout("123\n123\n", "111\n111\n") - lines = ["123\n", "123\n"] - regexes = ["1\n", "1\n"] - assert test.match_stdout(lines, regexes) # equal numbers of lines - - def test_match_stdout_TestCmd_function(self): - """Test match_stdout() using a TestCmd function""" - test = TestCmd.TestCmd(match_stdout = TestCmd.match_exact) - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stdout(lines, regexes) - assert test.match_stdout(lines, lines) - - def test_match_stdout_static_method(self): - """Test match_stdout() using a static method""" - test = TestCmd.TestCmd(match_stdout=TestCmd.TestCmd.match_exact) - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stdout(lines, regexes) - assert test.match_stdout(lines, lines) - - def test_match_stdout_string(self): - """Test match_stdout() using a string to fetch the match method""" - test = TestCmd.TestCmd(match_stdout='match_exact') - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stdout(lines, regexes) - assert test.match_stdout(lines, lines) - - - -class no_result_TestCase(TestCmdTestCase): - def test_no_result(self): - """Test no_result()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """import sys -sys.stdout.write("run: STDOUT\\n") -sys.stderr.write("run: STDERR\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -TestCmd.no_result(condition = 1) -""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test at line 4 of \n") - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') -test.run() -test.no_result(condition = (test.status == 0)) -""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 6 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(program = 'run', interpreter = 'python', description = 'xyzzy', workdir = '') -test.run() -test.no_result(condition = (test.status == 0)) -""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s [xyzzy]\n\tat line 6 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') -test.run() -def xxx(): - sys.stderr.write("printed on no result\\n") -test.no_result(condition = (test.status == 0), function = xxx) -""" % self.orig_cwd, status = 2, stderr = "printed on no result\nNO RESULT for test of %s\n\tat line 8 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def test1(self): - self.run() - self.no_result(condition = (self.status == 0)) -def test2(self): - test1(self) -test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')) -""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 6 of (test1)\n\tfrom line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def test1(self): - self.run() - self.no_result(condition = (self.status == 0), skip = 1) -def test2(self): - test1(self) -test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')) -""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) - - - -class pass_test_TestCase(TestCmdTestCase): - def test_pass_test(self): - """Test pass_test()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """import sys -sys.stdout.write("run: STDOUT\\n") -sys.stderr.write("run: STDERR\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -TestCmd.pass_test(condition = 1) -""" % self.orig_cwd, stderr = "PASSED\n") - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') -test.run() -test.pass_test(condition = (test.status == 0)) -""" % self.orig_cwd, stderr = "PASSED\n") - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') -test.run() -def brag(): - sys.stderr.write("printed on success\\n") -test.pass_test(condition = (test.status == 0), function = brag) -""" % self.orig_cwd, stderr = "printed on success\nPASSED\n") - - # TODO(sgk): SHOULD ALSO TEST FAILURE CONDITIONS - - - -class preserve_TestCase(TestCmdTestCase): - def test_preserve(self): - """Test preserve()""" - def cleanup_test(test, cond=None, stdout=""): - io = StringIO() - save = sys.stdout - sys.stdout = io - try: - if cond: - test.cleanup(cond) - else: - test.cleanup() - o = io.getvalue() - assert o == stdout, "o = `%s', stdout = `%s'" % (o, stdout) - finally: - sys.stdout = save - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.write('file1', "Test file #1\n") - #test.cleanup() - cleanup_test(test, ) - assert not os.path.exists(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.write('file2', "Test file #2\n") - test.preserve('pass_test') - cleanup_test(test, 'pass_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'fail_test') - assert not os.path.exists(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.write('file3', "Test file #3\n") - test.preserve('fail_test') - cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'pass_test') - assert not os.path.exists(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.write('file4', "Test file #4\n") - test.preserve('fail_test', 'no_result') - cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'no_result', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'pass_test') - assert not os.path.exists(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.preserve() - cleanup_test(test, 'pass_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'no_result', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - - -class program_TestCase(TestCmdTestCase): - def test_program(self): - """Test program()""" - test = TestCmd.TestCmd() - assert test.program is None, 'initialized program?' - test = TestCmd.TestCmd(program = 'test') - assert test.program == os.path.join(os.getcwd(), 'test'), 'uninitialized program' - test.program_set('foo') - assert test.program == os.path.join(os.getcwd(), 'foo'), 'did not set program' - - - -class read_TestCase(TestCmdTestCase): - def test_read(self): - """Test read()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_file2 = os.path.join(test.workdir, 'file2') - wdir_foo_file3 = os.path.join(test.workdir, 'foo', 'file3') - wdir_file4 = os.path.join(test.workdir, 'file4') - wdir_file5 = os.path.join(test.workdir, 'file5') - - open(wdir_file1, 'wb').write("") - open(wdir_file2, 'wb').write("Test\nfile\n#2.\n") - open(wdir_foo_file3, 'wb').write("Test\nfile\n#3.\n") - open(wdir_file4, 'wb').write("Test\nfile\n#4.\n") - open(wdir_file5, 'wb').write("Test\r\nfile\r\n#5.\r\n") - - try: - contents = test.read('no_file') - except IOError: # expect "No such file or directory" - pass - except: - raise - - try: - test.read(test.workpath('file_x'), mode = 'w') - except ValueError: # expect "mode must begin with 'r' - pass - except: - raise - - def _file_matches(file, contents, expected): - assert contents == expected, \ - "Expected contents of " + str(file) + "==========\n" + \ - expected + \ - "Actual contents of " + str(file) + "============\n" + \ - contents - - _file_matches(wdir_file1, test.read('file1'), "") - _file_matches(wdir_file2, test.read('file2'), "Test\nfile\n#2.\n") - _file_matches(wdir_foo_file3, test.read(['foo', 'file3']), - "Test\nfile\n#3.\n") - _file_matches(wdir_foo_file3, - test.read(UserList(['foo', 'file3'])), - "Test\nfile\n#3.\n") - _file_matches(wdir_file4, test.read('file4', mode = 'r'), - "Test\nfile\n#4.\n") - _file_matches(wdir_file5, test.read('file5', mode = 'rb'), - "Test\r\nfile\r\n#5.\r\n") - - - -class rmdir_TestCase(TestCmdTestCase): - def test_rmdir(self): - """Test rmdir()""" - test = TestCmd.TestCmd(workdir = '') - - try: - test.rmdir(['no', 'such', 'dir']) - except EnvironmentError: - pass - else: - raise Exception("did not catch expected EnvironmentError") - - test.subdir(['sub'], - ['sub', 'dir'], - ['sub', 'dir', 'one']) - - s = test.workpath('sub') - s_d = test.workpath('sub', 'dir') - s_d_o = test.workpath('sub', 'dir', 'one') - - try: - test.rmdir(['sub']) - except EnvironmentError: - pass - else: - raise Exception("did not catch expected EnvironmentError") - - assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o - - try: - test.rmdir(['sub']) - except EnvironmentError: - pass - else: - raise Exception("did not catch expected EnvironmentError") - - assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o - - test.rmdir(['sub', 'dir', 'one']) - - assert not os.path.exists(s_d_o), "%s exists?" % s_d_o - assert os.path.isdir(s_d), "%s is gone?" % s_d - - test.rmdir(['sub', 'dir']) - - assert not os.path.exists(s_d), "%s exists?" % s_d - assert os.path.isdir(s), "%s is gone?" % s - - test.rmdir('sub') - - assert not os.path.exists(s), "%s exists?" % s - - - -class run_TestCase(TestCmdTestCase): - def test_run(self): - """Test run()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - test.run() - self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, - repr([])) - self.run_match(test.stderr(), t.script, "STDERR", t.workdir, - repr([])) - - test.run(arguments = 'arg1 arg2 arg3') - self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, - repr(['arg1', 'arg2', 'arg3'])) - self.run_match(test.stderr(), t.script, "STDERR", t.workdir, - repr(['arg1', 'arg2', 'arg3'])) - - test.run(program = t.scriptx, arguments = 'foo') - self.run_match(test.stdout(), t.scriptx, "STDOUT", t.workdir, - repr(['foo'])) - self.run_match(test.stderr(), t.scriptx, "STDERR", t.workdir, - repr(['foo'])) - - test.run(chdir = os.curdir, arguments = 'x y z') - self.run_match(test.stdout(), t.script, "STDOUT", test.workdir, - repr(['x', 'y', 'z'])) - self.run_match(test.stderr(), t.script, "STDERR", test.workdir, - repr(['x', 'y', 'z'])) - - test.run(chdir = 'script_subdir') - script_subdir = test.workpath('script_subdir') - self.run_match(test.stdout(), t.script, "STDOUT", script_subdir, - repr([])) - self.run_match(test.stderr(), t.script, "STDERR", script_subdir, - repr([])) - - test.run(program = t.script1, interpreter = ['python', '-x']) - self.run_match(test.stdout(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(test.stderr(), t.script1, "STDERR", t.workdir, - repr([])) - - try: - test.run(chdir = 'no_subdir') - except OSError: - pass - - test.run(program = 'no_script', interpreter = 'python') - assert test.status != None, test.status - - try: - test.run(program = 'no_script', interpreter = 'no_interpreter') - except OSError: - # Python versions that use subprocess throw an OSError - # exception when they try to execute something that - # isn't there. - pass - else: - # Python versions that use os.popen3() or the Popen3 - # class run things through the shell, which just returns - # a non-zero exit status. - assert test.status != None, test.status - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - subdir = 't.scriptx_subdir') - - testx.run() - self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, - repr([])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, - repr([])) - - testx.run(arguments = 'foo bar') - self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, - repr(['foo', 'bar'])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, - repr(['foo', 'bar'])) - - testx.run(program = t.script, interpreter = 'python', arguments = 'bar') - self.run_match(testx.stdout(), t.script, "STDOUT", t.workdir, - repr(['bar'])) - self.run_match(testx.stderr(), t.script, "STDERR", t.workdir, - repr(['bar'])) - - testx.run(chdir = os.curdir, arguments = 'baz') - self.run_match(testx.stdout(), t.scriptx, "STDOUT", testx.workdir, - repr(['baz'])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", testx.workdir, - repr(['baz'])) - - testx.run(chdir = 't.scriptx_subdir') - t.scriptx_subdir = testx.workpath('t.scriptx_subdir') - self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.scriptx_subdir, - repr([])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", t.scriptx_subdir, - repr([])) - - testx.run(program = t.script1, interpreter = ('python', '-x')) - self.run_match(testx.stdout(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(testx.stderr(), t.script1, "STDERR", t.workdir, - repr([])) - - s = os.path.join('.', t.scriptx) - testx.run(program = [s]) - self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, - repr([])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, - repr([])) - - try: - testx.run(chdir = 'no_subdir') - except OSError: - pass - - try: - testx.run(program = 'no_program') - except OSError: - # Python versions that use subprocess throw an OSError - # exception when they try to execute something that - # isn't there. - pass - else: - # Python versions that use os.popen3() or the Popen3 - # class run things through the shell, which just returns - # a non-zero exit status. - assert test.status != None - - test1 = TestCmd.TestCmd(program = t.script1, - interpreter = ['python', '-x'], - workdir = '') - - test1.run() - self.run_match(test1.stdout(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(test1.stderr(), t.script1, "STDERR", t.workdir, - repr([])) - - finally: - os.chdir(t.orig_cwd) - - def test_run_subclass(self): - """Test run() through a subclass with different signatures""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - - class MyTestCmdSubclass(TestCmd.TestCmd): - def start(self, additional_argument=None, **kw): - return TestCmd.TestCmd.start(self, **kw) - - try: - test = MyTestCmdSubclass(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - test.run() - self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, - repr([])) - self.run_match(test.stderr(), t.script, "STDERR", t.workdir, - repr([])) - finally: - os.chdir(t.orig_cwd) - - -class run_verbose_TestCase(TestCmdTestCase): - def test_run_verbose(self): - """Test the run() method's verbose attribute""" - - # Prepare our "source directory." - t = self.setup_run_scripts() - - save_stdout = sys.stderr - save_stderr = sys.stderr - - try: - # Test calling TestCmd() with an explicit verbose = 1. - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - verbose = 1) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - assert expect == e, (expect, e) - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - verbose = 1) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - assert expect == e, (expect, e) - - # Test calling TestCmd() with an explicit verbose = 2. - - outerr_fmt = """\ -============ STATUS: 0 -============ BEGIN STDOUT (len=%s): -%s============ END STDOUT -============ BEGIN STDERR (len=%s) -%s============ END STDERR -""" - - out_fmt = """\ -============ STATUS: 0 -============ BEGIN STDOUT (len=%s): -%s============ END STDOUT -""" - - err_fmt = """\ -============ STATUS: 0 -============ BEGIN STDERR (len=%s) -%s============ END STDERR -""" - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - verbose = 2) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - - line_fmt = "script: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - verbose = 2) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - - line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) - - # Test calling TestCmd() with an explicit verbose = 3. - - test = TestCmd.TestCmd(program = t.scriptout, - interpreter = 'python', - workdir = '', - verbose = 2) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - - line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - expect = out_fmt % (len(stdout_line), stdout_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) - assert e == expect, (e, expect) - - test = TestCmd.TestCmd(program = t.scriptout, - interpreter = 'python', - workdir = '', - verbose = 3) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - - line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - '0', '') - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) - assert e == expect, (e, expect) - - # Test letting TestCmd() pick up verbose = 2 from the environment. - - os.environ['TESTCMD_VERBOSE'] = '2' - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '') - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - - line_fmt = "script: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '') - - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - - line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) - - # Test letting TestCmd() pick up verbose = 1 from the environment. - - os.environ['TESTCMD_VERBOSE'] = '1' - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - verbose = 1) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - assert expect == e, (expect, e) - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - verbose = 1) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - assert expect == e, (expect, e) - - finally: - sys.stdout = save_stdout - sys.stderr = save_stderr - os.chdir(t.orig_cwd) - os.environ['TESTCMD_VERBOSE'] = '' - - - -class set_diff_function_TestCase(TestCmdTestCase): - def test_set_diff_function(self): - """Test set_diff_function()""" - self.popen_python(r"""import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd() -test.diff("a\n", "a\n") -test.set_diff_function('diff_re') -test.diff(".\n", "a\n") -sys.exit(0) -""" % self.orig_cwd) - - def test_set_diff_function_stdout(self): - """Test set_diff_function(): stdout""" - self.popen_python("""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd() -print("diff:") -test.diff("a\\n", "a\\n") -print("diff_stdout:") -test.diff_stdout("a\\n", "a\\n") -test.set_diff_function(stdout='diff_re') -print("diff:") -test.diff(".\\n", "a\\n") -print("diff_stdout:") -test.diff_stdout(".\\n", "a\\n") -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -diff: -diff_stdout: -diff: -1c1 -< . ---- -> a -diff_stdout: -""") - - def test_set_diff_function_stderr(self): - """Test set_diff_function(): stderr """ - self.popen_python("""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd() -print("diff:") -test.diff("a\\n", "a\\n") -print("diff_stderr:") -test.diff_stderr("a\\n", "a\\n") -test.set_diff_function(stderr='diff_re') -print("diff:") -test.diff(".\\n", "a\\n") -print("diff_stderr:") -test.diff_stderr(".\\n", "a\\n") -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -diff: -diff_stderr: -diff: -1c1 -< . ---- -> a -diff_stderr: -""") - - - -class set_match_function_TestCase(TestCmdTestCase): - def test_set_match_function(self): - """Test set_match_function()""" - test = TestCmd.TestCmd() - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - - test.set_match_function('match_exact') - - assert not test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - - def test_set_match_function_stdout(self): - """Test set_match_function(): stdout """ - test = TestCmd.TestCmd() - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - - test.set_match_function(stdout='match_exact') - - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - - def test_set_match_function_stderr(self): - """Test set_match_function(): stderr """ - test = TestCmd.TestCmd() - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - - test.set_match_function(stderr='match_exact') - - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - - - -class sleep_TestCase(TestCmdTestCase): - def test_sleep(self): - """Test sleep()""" - test = TestCmd.TestCmd() - - start = time.time() - test.sleep() - end = time.time() - diff = end - start - assert diff > 0.9, "only slept %f seconds (start %f, end %f), not default" % (diff, start, end) - - start = time.time() - test.sleep(3) - end = time.time() - diff = end - start - assert diff > 2.9, "only slept %f seconds (start %f, end %f), not 3" % (diff, start, end) - - - -class stderr_TestCase(TestCmdTestCase): - def test_stderr(self): - """Test stderr()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run1', """import sys -sys.stdout.write("run1 STDOUT %s\\n" % sys.argv[1:]) -sys.stdout.write("run1 STDOUT second line\\n") -sys.stderr.write("run1 STDERR %s\\n" % sys.argv[1:]) -sys.stderr.write("run1 STDERR second line\\n") -""") - run_env.write('run2', """import sys -sys.stdout.write("run2 STDOUT %s\\n" % sys.argv[1:]) -sys.stdout.write("run2 STDOUT second line\\n") -sys.stderr.write("run2 STDERR %s\\n" % sys.argv[1:]) -sys.stderr.write("run2 STDERR second line\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(interpreter = 'python', workdir = '') - try: - output = test.stderr() - except IndexError: - pass - else: - raise IndexError("got unexpected output:\n" + output) - test.program_set('run1') - test.run(arguments = 'foo bar') - test.program_set('run2') - test.run(arguments = 'snafu') - # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL - output = test.stderr() - assert output == "run2 STDERR ['snafu']\nrun2 STDERR second line\n", output - output = test.stderr(run = -1) - assert output == "run1 STDERR ['foo', 'bar']\nrun1 STDERR second line\n", output - - - -class command_args_TestCase(TestCmdTestCase): - def test_command_args(self): - """Test command_args()""" - run_env = TestCmd.TestCmd(workdir = '') - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(workdir = '') - - r = test.command_args('prog') - expect = [run_env.workpath('prog')] - assert r == expect, (expect, r) - - r = test.command_args(test.workpath('new_prog')) - expect = [test.workpath('new_prog')] - assert r == expect, (expect, r) - - r = test.command_args('prog', 'python') - expect = ['python', run_env.workpath('prog')] - assert r == expect, (expect, r) - - r = test.command_args('prog', 'python', 'arg1 arg2') - expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2'] - assert r == expect, (expect, r) - - test.program_set('default_prog') - default_prog = run_env.workpath('default_prog') - - r = test.command_args() - expect = [default_prog] - assert r == expect, (expect, r) - - r = test.command_args(interpreter='PYTHON') - expect = ['PYTHON', default_prog] - assert r == expect, (expect, r) - - r = test.command_args(interpreter='PYTHON', arguments='arg3 arg4') - expect = ['PYTHON', default_prog, 'arg3', 'arg4'] - assert r == expect, (expect, r) - - test.interpreter_set('default_python') - - r = test.command_args() - expect = ['default_python', default_prog] - assert r == expect, (expect, r) - - r = test.command_args(arguments='arg5 arg6') - expect = ['default_python', default_prog, 'arg5', 'arg6'] - assert r == expect, (expect, r) - - r = test.command_args('new_prog_1') - expect = [run_env.workpath('new_prog_1')] - assert r == expect, (expect, r) - - r = test.command_args(program='new_prog_2') - expect = [run_env.workpath('new_prog_2')] - assert r == expect, (expect, r) - - - -class start_TestCase(TestCmdTestCase): - def setup_run_scripts(self): - t = TestCmdTestCase.setup_run_scripts(self) - t.recv_script = 'script_recv' - t.recv_script_path = t.run_env.workpath(t.sub_dir, t.recv_script) - t.recv_out_path = t.run_env.workpath('script_recv.out') - text = """\ -import os -import sys - -class Unbuffered: - def __init__(self, file): - self.file = file - def write(self, arg): - self.file.write(arg) - self.file.flush() - def __getattr__(self, attr): - return getattr(self.file, attr) - -sys.stdout = Unbuffered(sys.stdout) -sys.stderr = Unbuffered(sys.stderr) - -sys.stdout.write('script_recv: STDOUT\\n') -sys.stderr.write('script_recv: STDERR\\n') -logfp = open(r'%s', 'wb') -while 1: - line = sys.stdin.readline() - if not line: - break - logfp.write('script_recv: ' + line) - sys.stdout.write('script_recv: STDOUT: ' + line) - sys.stderr.write('script_recv: STDERR: ' + line) -logfp.close() - """ % t.recv_out_path - t.run_env.write(t.recv_script_path, text) - os.chmod(t.recv_script_path, 0o644) # XXX UNIX-specific - return t - - def test_start(self): - """Test start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - p = test.start() - self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, - repr([])) - p.wait() - - p = test.start(arguments='arg1 arg2 arg3') - self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, - repr(['arg1', 'arg2', 'arg3'])) - self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, - repr(['arg1', 'arg2', 'arg3'])) - p.wait() - - p = test.start(program=t.scriptx, arguments='foo') - self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, - repr(['foo'])) - self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, - repr(['foo'])) - p.wait() - - p = test.start(program=t.script1, interpreter=['python', '-x']) - self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, - repr([])) - p.wait() - - p = test.start(program='no_script', interpreter='python') - status = p.wait() - assert status != None, status - - try: - p = test.start(program='no_script', interpreter='no_interpreter') - except OSError: - # Python versions that use subprocess throw an OSError - # exception when they try to execute something that - # isn't there. - pass - else: - status = p.wait() - # Python versions that use os.popen3() or the Popen3 - # class run things through the shell, which just returns - # a non-zero exit status. - assert status != None, status - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - subdir = 't.scriptx_subdir') - - p = testx.start() - self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, - repr([])) - p.wait() - - p = testx.start(arguments='foo bar') - self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, - repr(['foo', 'bar'])) - self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, - repr(['foo', 'bar'])) - p.wait() - - p = testx.start(program=t.script, interpreter='python', arguments='bar') - self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, - repr(['bar'])) - self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, - repr(['bar'])) - p.wait() - - p = testx.start(program=t.script1, interpreter=('python', '-x')) - self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, - repr([])) - p.wait() - - s = os.path.join('.', t.scriptx) - p = testx.start(program=[s]) - self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, - repr([])) - p.wait() - - try: - testx.start(program='no_program') - except OSError: - # Python versions that use subprocess throw an OSError - # exception when they try to execute something that - # isn't there. - pass - else: - # Python versions that use os.popen3() or the Popen3 - # class run things through the shell, which just dies - # trying to execute the non-existent program before - # we can wait() for it. - try: - p = p.wait() - except OSError: - pass - - test1 = TestCmd.TestCmd(program = t.script1, - interpreter = ['python', '-x'], - workdir = '') - - p = test1.start() - self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, - repr([])) - p.wait() - - finally: - os.chdir(t.orig_cwd) - - def test_finish(self): - """Test finish()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - - test = TestCmd.TestCmd(program = t.recv_script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - test.start(stdin=1) - test.finish() - expect_stdout = """\ -script_recv: STDOUT -""" - expect_stderr = """\ -script_recv: STDERR -""" - stdout = test.stdout() - assert stdout == expect_stdout, stdout - stderr = test.stderr() - assert stderr == expect_stderr, stderr - - p = test.start(stdin=1) - p.send('input\n') - test.finish(p) - expect_stdout = """\ -script_recv: STDOUT -script_recv: STDOUT: input -""" - expect_stderr = """\ -script_recv: STDERR -script_recv: STDERR: input -""" - stdout = test.stdout() - assert stdout == expect_stdout, stdout - stderr = test.stderr() - assert stderr == expect_stderr, stderr - - p = test.start(combine=1, stdin=1) - p.send('input\n') - test.finish(p) - expect_stdout = """\ -script_recv: STDOUT -script_recv: STDERR -script_recv: STDOUT: input -script_recv: STDERR: input -""" - expect_stderr = "" - stdout = test.stdout() - assert stdout == expect_stdout, stdout - stderr = test.stderr() - assert stderr == expect_stderr, stderr - - finally: - os.chdir(t.orig_cwd) - - def test_recv(self): - """Test the recv() method of objects returned by start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - p = test.start() - stdout = p.recv() - while stdout == '': - import time - time.sleep(1) - stdout = p.recv() - self.run_match(stdout, t.script, "STDOUT", t.workdir, - repr([])) - p.wait() - - finally: - os.chdir(t.orig_cwd) - - def test_recv_err(self): - """Test the recv_err() method of objects returned by start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - p = test.start() - stderr = p.recv_err() - while stderr == '': - import time - time.sleep(1) - stderr = p.recv_err() - self.run_match(stderr, t.script, "STDERR", t.workdir, - repr([])) - p.wait() - - - finally: - os.chdir(t.orig_cwd) - - def test_send(self): - """Test the send() method of objects returned by start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - - test = TestCmd.TestCmd(program = t.recv_script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - p = test.start(stdin=1) - input = 'stdin.write() input to the receive script\n' - p.stdin.write(input) - p.stdin.close() - p.wait() - result = open(t.recv_out_path, 'rb').read() - expect = 'script_recv: ' + input - assert result == expect, repr(result) - - p = test.start(stdin=1) - input = 'send() input to the receive script\n' - p.send(input) - p.stdin.close() - p.wait() - result = open(t.recv_out_path, 'rb').read() - expect = 'script_recv: ' + input - assert result == expect, repr(result) - - finally: - os.chdir(t.orig_cwd) - - # TODO(sgk): figure out how to eliminate the race conditions here. - def __FLAKY__test_send_recv(self): - """Test the send_recv() method of objects returned by start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - - test = TestCmd.TestCmd(program = t.recv_script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - def do_send_recv(p, input): - send, stdout, stderr = p.send_recv(input) - stdout = self.translate_newlines(stdout) - stderr = self.translate_newlines(stderr) - return send, stdout, stderr - - p = test.start(stdin=1) - input = 'input to the receive script\n' - send, stdout, stderr = do_send_recv(p, input) - # Buffering issues and a race condition prevent this from - # being completely deterministic, so check for both null - # output and the first write() on each stream. - assert stdout in ("", "script_recv: STDOUT\n"), stdout - assert stderr in ("", "script_recv: STDERR\n"), stderr - send, stdout, stderr = do_send_recv(p, input) - assert stdout in ("", "script_recv: STDOUT\n"), stdout - assert stderr in ("", "script_recv: STDERR\n"), stderr - p.stdin.close() - stdout = self.translate_newlines(p.recv()) - stderr = self.translate_newlines(p.recv_err()) - assert stdout in ("", "script_recv: STDOUT\n"), stdout - assert stderr in ("", "script_recv: STDERR\n"), stderr - p.wait() - stdout = self.translate_newlines(p.recv()) - stderr = self.translate_newlines(p.recv_err()) - expect_stdout = """\ -script_recv: STDOUT -script_recv: STDOUT: input to the receive script -script_recv: STDOUT: input to the receive script -""" - expect_stderr = """\ -script_recv: STDERR -script_recv: STDERR: input to the receive script -script_recv: STDERR: input to the receive script -""" - assert stdout == expect_stdout, stdout - assert stderr == expect_stderr, stderr - result = open(t.recv_out_path, 'rb').read() - expect = ('script_recv: ' + input) * 2 - assert result == expect, (result, stdout, stderr) - - finally: - os.chdir(t.orig_cwd) - - - -class stdin_TestCase(TestCmdTestCase): - def test_stdin(self): - """Test stdin()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """from __future__ import print_function -import fileinput -for line in fileinput.input(): - print('Y'.join(line[:-1].split('X'))) -""") - run_env.write('input', "X on X this X line X\n") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') - test.run(arguments = 'input') - assert test.stdout() == "Y on Y this Y line Y\n" - test.run(stdin = "X is X here X tooX\n") - assert test.stdout() == "Y is Y here Y tooY\n" - test.run(stdin = """X here X -X there X -""") - assert test.stdout() == "Y here Y\nY there Y\n" - test.run(stdin = ["X line X\n", "X another X\n"]) - assert test.stdout() == "Y line Y\nY another Y\n" - - - -class stdout_TestCase(TestCmdTestCase): - def test_stdout(self): - """Test stdout()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run1', """import sys -sys.stdout.write("run1 STDOUT %s\\n" % sys.argv[1:]) -sys.stdout.write("run1 STDOUT second line\\n") -sys.stderr.write("run1 STDERR %s\\n" % sys.argv[1:]) -sys.stderr.write("run1 STDERR second line\\n") -""") - run_env.write('run2', """import sys -sys.stdout.write("run2 STDOUT %s\\n" % sys.argv[1:]) -sys.stdout.write("run2 STDOUT second line\\n") -sys.stderr.write("run2 STDERR %s\\n" % sys.argv[1:]) -sys.stderr.write("run2 STDERR second line\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(interpreter = 'python', workdir = '') - try: - output = test.stdout() - except IndexError: - pass - else: - raise IndexError("got unexpected output:\n\t`%s'\n" % output) - test.program_set('run1') - test.run(arguments = 'foo bar') - test.program_set('run2') - test.run(arguments = 'snafu') - # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL - output = test.stdout() - assert output == "run2 STDOUT ['snafu']\nrun2 STDOUT second line\n", output - output = test.stdout(run = -1) - assert output == "run1 STDOUT ['foo', 'bar']\nrun1 STDOUT second line\n", output - - - -class subdir_TestCase(TestCmdTestCase): - def test_subdir(self): - """Test subdir()""" - test = TestCmd.TestCmd(workdir = '', subdir = ['no', 'such', 'subdir']) - assert not os.path.exists(test.workpath('no')) - - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - assert test.subdir('bar') == 1 - assert test.subdir(['foo', 'succeed']) == 1 - if os.name != "nt": - os.chmod(test.workpath('foo'), 0o500) - assert test.subdir(['foo', 'fail']) == 0 - assert test.subdir(['sub', 'dir', 'ectory'], 'sub') == 1 - assert test.subdir('one', - UserList(['one', 'two']), - ['one', 'two', 'three']) == 3 - assert os.path.isdir(test.workpath('foo')) - assert os.path.isdir(test.workpath('bar')) - assert os.path.isdir(test.workpath('foo', 'succeed')) - if os.name != "nt": - assert not os.path.exists(test.workpath('foo', 'fail')) - assert os.path.isdir(test.workpath('sub')) - assert not os.path.exists(test.workpath('sub', 'dir')) - assert not os.path.exists(test.workpath('sub', 'dir', 'ectory')) - assert os.path.isdir(test.workpath('one', 'two', 'three')) - - - -class symlink_TestCase(TestCmdTestCase): - def test_symlink(self): - """Test symlink()""" - try: os.symlink - except AttributeError: return - - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_target1 = os.path.join(test.workdir, 'target1') - wdir_foo_file2 = os.path.join(test.workdir, 'foo', 'file2') - wdir_target2 = os.path.join(test.workdir, 'target2') - wdir_foo_target2 = os.path.join(test.workdir, 'foo', 'target2') - - test.symlink('target1', 'file1') - assert os.path.islink(wdir_file1) - assert not os.path.exists(wdir_file1) - open(wdir_target1, 'w').write("") - assert os.path.exists(wdir_file1) - - test.symlink('target2', ['foo', 'file2']) - assert os.path.islink(wdir_foo_file2) - assert not os.path.exists(wdir_foo_file2) - open(wdir_target2, 'w').write("") - assert not os.path.exists(wdir_foo_file2) - open(wdir_foo_target2, 'w').write("") - assert os.path.exists(wdir_foo_file2) - - - -class tempdir_TestCase(TestCmdTestCase): - def setUp(self): - TestCmdTestCase.setUp(self) - self._tempdir = tempfile.mktemp() - os.mkdir(self._tempdir) - os.chdir(self._tempdir) - - def tearDown(self): - TestCmdTestCase.tearDown(self) - os.rmdir(self._tempdir) - - def test_tempdir(self): - """Test tempdir()""" - test = TestCmd.TestCmd() - tdir1 = test.tempdir() - assert os.path.isdir(tdir1) - test.workdir_set(None) - test.cleanup() - assert not os.path.exists(tdir1) - - test = TestCmd.TestCmd() - tdir2 = test.tempdir('temp') - assert os.path.isdir(tdir2) - tdir3 = test.tempdir() - assert os.path.isdir(tdir3) - test.workdir_set(None) - test.cleanup() - assert not os.path.exists(tdir2) - assert not os.path.exists(tdir3) - - -timeout_script = """\ -import sys -import time -seconds = int(sys.argv[1]) -sys.stdout.write('sleeping %s\\n' % seconds) -sys.stdout.flush() -time.sleep(seconds) -sys.stdout.write('slept %s\\n' % seconds) -sys.stdout.flush() -sys.exit(0) -""" - -class timeout_TestCase(TestCmdTestCase): - def test_initialization(self): - """Test initialization timeout""" - test = TestCmd.TestCmd(workdir='', timeout=2) - test.write('sleep.py', timeout_script) - - test.run([sys.executable, test.workpath('sleep.py'), '4']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 4\n', test.stdout() - - test.run([sys.executable, test.workpath('sleep.py'), '4']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 4\n', test.stdout() - - def test_cancellation(self): - """Test timer cancellation after firing""" - test = TestCmd.TestCmd(workdir='', timeout=4) - test.write('sleep.py', timeout_script) - - test.run([sys.executable, test.workpath('sleep.py'), '6']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 6\n', test.stdout() - - test.run([sys.executable, test.workpath('sleep.py'), '2']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 2\nslept 2\n', test.stdout() - - test.run([sys.executable, test.workpath('sleep.py'), '6']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 6\n', test.stdout() - - def test_run(self): - """Test run() timeout""" - test = TestCmd.TestCmd(workdir='', timeout=8) - test.write('sleep.py', timeout_script) - - test.run([sys.executable, test.workpath('sleep.py'), '2'], - timeout=4) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 2\nslept 2\n', test.stdout() - - test.run([sys.executable, test.workpath('sleep.py'), '6'], - timeout=4) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 6\n', test.stdout() - - def test_set_timeout(self): - """Test set_timeout()""" - test = TestCmd.TestCmd(workdir='', timeout=2) - test.write('sleep.py', timeout_script) - - test.run([sys.executable, test.workpath('sleep.py'), '4']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 4\n', test.stdout() - - test.set_timeout(None) - - test.run([sys.executable, test.workpath('sleep.py'), '4']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 4\nslept 4\n', test.stdout() - - test.set_timeout(6) - - test.run([sys.executable, test.workpath('sleep.py'), '4']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 4\nslept 4\n', test.stdout() - - test.run([sys.executable, test.workpath('sleep.py'), '8']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 8\n', test.stdout() - - - -class unlink_TestCase(TestCmdTestCase): - def test_unlink(self): - """Test unlink()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_file2 = os.path.join(test.workdir, 'file2') - wdir_foo_file3a = os.path.join(test.workdir, 'foo', 'file3a') - wdir_foo_file3b = os.path.join(test.workdir, 'foo', 'file3b') - wdir_foo_file4 = os.path.join(test.workdir, 'foo', 'file4') - wdir_file5 = os.path.join(test.workdir, 'file5') - - open(wdir_file1, 'w').write("") - open(wdir_file2, 'w').write("") - open(wdir_foo_file3a, 'w').write("") - open(wdir_foo_file3b, 'w').write("") - open(wdir_foo_file4, 'w').write("") - open(wdir_file5, 'w').write("") - - try: - contents = test.unlink('no_file') - except OSError: # expect "No such file or directory" - pass - except: - raise - - test.unlink("file1") - assert not os.path.exists(wdir_file1) - - test.unlink(wdir_file2) - assert not os.path.exists(wdir_file2) - - test.unlink(['foo', 'file3a']) - assert not os.path.exists(wdir_foo_file3a) - - test.unlink(UserList(['foo', 'file3b'])) - assert not os.path.exists(wdir_foo_file3b) - - test.unlink([test.workdir, 'foo', 'file4']) - assert not os.path.exists(wdir_foo_file4) - - # Make it so we can't unlink file5. - # For UNIX, remove write permission from the dir and the file. - # For Windows, open the file. - os.chmod(test.workdir, 0o500) - os.chmod(wdir_file5, 0o400) - f = open(wdir_file5, 'r') - - try: - try: - test.unlink('file5') - except OSError: # expect "Permission denied" - pass - except: - raise - finally: - os.chmod(test.workdir, 0o700) - os.chmod(wdir_file5, 0o600) - f.close() - - - -class touch_TestCase(TestCmdTestCase): - def test_touch(self): - """Test touch()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'sub') - - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_sub_file2 = os.path.join(test.workdir, 'sub', 'file2') - - open(wdir_file1, 'w').write("") - open(wdir_sub_file2, 'w').write("") - - file1_old_time = os.path.getmtime(wdir_file1) - file2_old_time = os.path.getmtime(wdir_sub_file2) - - test.sleep() - - test.touch(wdir_file1) - - file1_new_time = os.path.getmtime(wdir_file1) - assert file1_new_time > file1_old_time - - test.touch('file1', file1_old_time) - - result = os.path.getmtime(wdir_file1) - # Sub-second granularity of file systems may still vary. - # On Windows, the two times may be off by a microsecond. - assert int(result) == int(file1_old_time), (result, file1_old_time) - - test.touch(['sub', 'file2']) - - file2_new_time = os.path.getmtime(wdir_sub_file2) - assert file2_new_time > file2_old_time - - - -class verbose_TestCase(TestCmdTestCase): - def test_verbose(self): - """Test verbose()""" - test = TestCmd.TestCmd() - assert test.verbose == 0, 'verbose already initialized?' - test = TestCmd.TestCmd(verbose = 1) - assert test.verbose == 1, 'did not initialize verbose' - test.verbose = 2 - assert test.verbose == 2, 'did not set verbose' - - - -class workdir_TestCase(TestCmdTestCase): - def test_workdir(self): - """Test workdir()""" - run_env = TestCmd.TestCmd(workdir = '') - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd() - assert test.workdir is None - - test = TestCmd.TestCmd(workdir = None) - assert test.workdir is None - - test = TestCmd.TestCmd(workdir = '') - assert test.workdir != None - assert os.path.isdir(test.workdir) - - test = TestCmd.TestCmd(workdir = 'dir') - assert test.workdir != None - assert os.path.isdir(test.workdir) - - no_such_subdir = os.path.join('no', 'such', 'subdir') - try: - test = TestCmd.TestCmd(workdir = no_such_subdir) - except OSError: # expect "No such file or directory" - pass - except: - raise - - test = TestCmd.TestCmd(workdir = 'foo') - workdir_foo = test.workdir - assert workdir_foo != None - - test.workdir_set('bar') - workdir_bar = test.workdir - assert workdir_bar != None - - try: - test.workdir_set(no_such_subdir) - except OSError: - pass # expect "No such file or directory" - except: - raise - assert workdir_bar == test.workdir - - assert os.path.isdir(workdir_foo) - assert os.path.isdir(workdir_bar) - - - -class workdirs_TestCase(TestCmdTestCase): - def test_workdirs(self): - """Test workdirs()""" - test = TestCmd.TestCmd() - assert test.workdir is None - test.workdir_set('') - wdir1 = test.workdir - test.workdir_set('') - wdir2 = test.workdir - assert os.path.isdir(wdir1) - assert os.path.isdir(wdir2) - test.cleanup() - assert not os.path.exists(wdir1) - assert not os.path.exists(wdir2) - - - -class workpath_TestCase(TestCmdTestCase): - def test_workpath(self): - """Test workpath()""" - test = TestCmd.TestCmd() - assert test.workdir is None - - test = TestCmd.TestCmd(workdir = '') - wpath = test.workpath('foo', 'bar') - assert wpath == os.path.join(test.workdir, 'foo', 'bar') - - - -class readable_TestCase(TestCmdTestCase): - def test_readable(self): - """Test readable()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - test.write('file1', "Test file #1\n") - test.write(['foo', 'file2'], "Test file #2\n") - - try: symlink = os.symlink - except AttributeError: pass - else: symlink('no_such_file', test.workpath('dangling_symlink')) - - test.readable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_readable(test.workdir) - assert not _is_readable(test.workpath('file1')) - assert not _is_readable(test.workpath('foo')) - assert not _is_readable(test.workpath('foo', 'file2')) - - test.readable(test.workdir, 1) - assert _is_readable(test.workdir) - assert _is_readable(test.workpath('file1')) - assert _is_readable(test.workpath('foo')) - assert _is_readable(test.workpath('foo', 'file2')) - - test.readable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_readable(test.workdir) - assert not _is_readable(test.workpath('file1')) - assert not _is_readable(test.workpath('foo')) - assert not _is_readable(test.workpath('foo', 'file2')) - - test.readable(test.workpath('file1'), 1) - assert _is_readable(test.workpath('file1')) - - test.readable(test.workpath('file1'), 0) - assert not _is_readable(test.workpath('file1')) - - test.readable(test.workdir, 1) - - - -class writable_TestCase(TestCmdTestCase): - def test_writable(self): - """Test writable()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - test.write('file1', "Test file #1\n") - test.write(['foo', 'file2'], "Test file #2\n") - - try: symlink = os.symlink - except AttributeError: pass - else: symlink('no_such_file', test.workpath('dangling_symlink')) - - test.writable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_writable(test.workdir) - assert not _is_writable(test.workpath('file1')) - assert not _is_writable(test.workpath('foo')) - assert not _is_writable(test.workpath('foo', 'file2')) - - test.writable(test.workdir, 1) - assert _is_writable(test.workdir) - assert _is_writable(test.workpath('file1')) - assert _is_writable(test.workpath('foo')) - assert _is_writable(test.workpath('foo', 'file2')) - - test.writable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_writable(test.workdir) - assert not _is_writable(test.workpath('file1')) - assert not _is_writable(test.workpath('foo')) - assert not _is_writable(test.workpath('foo', 'file2')) - - test.writable(test.workpath('file1'), 1) - assert _is_writable(test.workpath('file1')) - - test.writable(test.workpath('file1'), 0) - assert not _is_writable(test.workpath('file1')) - - - -class executable_TestCase(TestCmdTestCase): - def test_executable(self): - """Test executable()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - test.write('file1', "Test file #1\n") - test.write(['foo', 'file2'], "Test file #2\n") - - try: symlink = os.symlink - except AttributeError: pass - else: symlink('no_such_file', test.workpath('dangling_symlink')) - - def make_executable(fname): - st = os.stat(fname) - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0o100)) - - def make_non_executable(fname): - st = os.stat(fname) - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0o100)) - - test.executable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_executable(test.workdir) - make_executable(test.workdir) - assert not _is_executable(test.workpath('file1')) - assert not _is_executable(test.workpath('foo')) - make_executable(test.workpath('foo')) - assert not _is_executable(test.workpath('foo', 'file2')) - make_non_executable(test.workpath('foo')) - make_non_executable(test.workdir) - - test.executable(test.workdir, 1) - assert _is_executable(test.workdir) - assert _is_executable(test.workpath('file1')) - assert _is_executable(test.workpath('foo')) - assert _is_executable(test.workpath('foo', 'file2')) - - test.executable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_executable(test.workdir) - make_executable(test.workdir) - assert not _is_executable(test.workpath('file1')) - assert not _is_executable(test.workpath('foo')) - make_executable(test.workpath('foo')) - assert not _is_executable(test.workpath('foo', 'file2')) - - test.executable(test.workpath('file1'), 1) - assert _is_executable(test.workpath('file1')) - - test.executable(test.workpath('file1'), 0) - assert not _is_executable(test.workpath('file1')) - - test.executable(test.workdir, 1) - - - -class write_TestCase(TestCmdTestCase): - def test_write(self): - """Test write()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - test.write('file1', "Test file #1\n") - test.write(['foo', 'file2'], "Test file #2\n") - try: - test.write(['bar', 'file3'], "Test file #3 (should not get created)\n") - except IOError: # expect "No such file or directory" - pass - except: - raise - test.write(test.workpath('file4'), "Test file #4.\n") - test.write(test.workpath('foo', 'file5'), "Test file #5.\n") - try: - test.write(test.workpath('bar', 'file6'), "Test file #6 (should not get created)\n") - except IOError: # expect "No such file or directory" - pass - except: - raise - - try: - test.write('file7', "Test file #8.\n", mode = 'r') - except ValueError: # expect "mode must begin with 'w' - pass - except: - raise - - test.write('file8', "Test file #8.\n", mode = 'w') - test.write('file9', "Test file #9.\r\n", mode = 'wb') - - if os.name != "nt": - os.chmod(test.workdir, 0o500) - try: - test.write('file10', "Test file #10 (should not get created).\n") - except IOError: # expect "Permission denied" - pass - except: - raise - - assert os.path.isdir(test.workpath('foo')) - assert not os.path.exists(test.workpath('bar')) - assert os.path.isfile(test.workpath('file1')) - assert os.path.isfile(test.workpath('foo', 'file2')) - assert not os.path.exists(test.workpath('bar', 'file3')) - assert os.path.isfile(test.workpath('file4')) - assert os.path.isfile(test.workpath('foo', 'file5')) - assert not os.path.exists(test.workpath('bar', 'file6')) - assert not os.path.exists(test.workpath('file7')) - assert os.path.isfile(test.workpath('file8')) - assert os.path.isfile(test.workpath('file9')) - if os.name != "nt": - assert not os.path.exists(test.workpath('file10')) - - assert open(test.workpath('file8'), 'r').read() == "Test file #8.\n" - assert open(test.workpath('file9'), 'rb').read() == "Test file #9.\r\n" - - - -class variables_TestCase(TestCmdTestCase): - def test_variables(self): - """Test global variables""" - run_env = TestCmd.TestCmd(workdir = '') - - variables = [ - 'fail_test', - 'no_result', - 'pass_test', - 'match_exact', - 'match_re', - 'match_re_dotall', - 'python', - '_python_', - 'TestCmd', - ] - - script = "from __future__ import print_function\n" + \ - "import TestCmd\n" + \ - '\n'.join([ "print(TestCmd.%s\n)" % v for v in variables ]) - run_env.run(program=sys.executable, stdin=script) - stderr = run_env.stderr() - assert stderr == "", stderr - - script = "from __future__ import print_function\n" + \ - "from TestCmd import *\n" + \ - '\n'.join([ "print(%s)" % v for v in variables ]) - run_env.run(program=sys.executable, stdin=script) - stderr = run_env.stderr() - assert stderr == "", stderr - - - -if __name__ == "__main__": - tclasses = [ - __init__TestCase, - basename_TestCase, - cleanup_TestCase, - chmod_TestCase, - combine_TestCase, - command_args_TestCase, - description_TestCase, - diff_TestCase, - diff_stderr_TestCase, - diff_stdout_TestCase, - exit_TestCase, - fail_test_TestCase, - interpreter_TestCase, - match_TestCase, - match_exact_TestCase, - match_re_dotall_TestCase, - match_re_TestCase, - match_stderr_TestCase, - match_stdout_TestCase, - no_result_TestCase, - pass_test_TestCase, - preserve_TestCase, - program_TestCase, - read_TestCase, - rmdir_TestCase, - run_TestCase, - run_verbose_TestCase, - set_diff_function_TestCase, - set_match_function_TestCase, - sleep_TestCase, - start_TestCase, - stderr_TestCase, - stdin_TestCase, - stdout_TestCase, - subdir_TestCase, - symlink_TestCase, - tempdir_TestCase, - timeout_TestCase, - unlink_TestCase, - touch_TestCase, - verbose_TestCase, - workdir_TestCase, - workdirs_TestCase, - workpath_TestCase, - writable_TestCase, - write_TestCase, - variables_TestCase, - ] - if sys.platform != 'win32': - tclasses.extend([ - executable_TestCase, - readable_TestCase, - ]) - suite = unittest.TestSuite() - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests([ tclass(n) for n in names ]) - if not unittest.TextTestRunner().run(suite).wasSuccessful(): - sys.exit(1) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestCommon.py b/QMTest/TestCommon.py deleted file mode 100644 index 47a149b..0000000 --- a/QMTest/TestCommon.py +++ /dev/null @@ -1,748 +0,0 @@ -""" -TestCommon.py: a testing framework for commands and scripts - with commonly useful error handling - -The TestCommon module provides a simple, high-level interface for writing -tests of executable commands and scripts, especially commands and scripts -that interact with the file system. All methods throw exceptions and -exit on failure, with useful error messages. This makes a number of -explicit checks unnecessary, making the test scripts themselves simpler -to write and easier to read. - -The TestCommon class is a subclass of the TestCmd class. In essence, -TestCommon is a wrapper that handles common TestCmd error conditions in -useful ways. You can use TestCommon directly, or subclass it for your -program and add additional (or override) methods to tailor it to your -program's specific needs. Alternatively, the TestCommon class serves -as a useful example of how to define your own TestCmd subclass. - -As a subclass of TestCmd, TestCommon provides access to all of the -variables and methods from the TestCmd module. Consequently, you can -use any variable or method documented in the TestCmd module without -having to explicitly import TestCmd. - -A TestCommon environment object is created via the usual invocation: - - import TestCommon - test = TestCommon.TestCommon() - -You can use all of the TestCmd keyword arguments when instantiating a -TestCommon object; see the TestCmd documentation for details. - -Here is an overview of the methods and keyword arguments that are -provided by the TestCommon class: - - test.must_be_writable('file1', ['file2', ...]) - - test.must_contain('file', 'required text\n') - - test.must_contain_all(output, input, ['title', find]) - - test.must_contain_all_lines(output, lines, ['title', find]) - - test.must_contain_any_line(output, lines, ['title', find]) - - test.must_contain_exactly_lines(output, lines, ['title', find]) - - test.must_exist('file1', ['file2', ...]) - - test.must_match('file', "expected contents\n") - - test.must_not_be_writable('file1', ['file2', ...]) - - test.must_not_contain('file', 'banned text\n') - - test.must_not_contain_any_line(output, lines, ['title', find]) - - test.must_not_exist('file1', ['file2', ...]) - - test.run(options = "options to be prepended to arguments", - stdout = "expected standard output from the program", - stderr = "expected error output from the program", - status = expected_status, - match = match_function) - -The TestCommon module also provides the following variables - - TestCommon.python - TestCommon._python_ - TestCommon.exe_suffix - TestCommon.obj_suffix - TestCommon.shobj_prefix - TestCommon.shobj_suffix - TestCommon.lib_prefix - TestCommon.lib_suffix - TestCommon.dll_prefix - TestCommon.dll_suffix - -""" - -# Copyright 2000-2010 Steven Knight -# This module is free software, and you may redistribute it and/or modify -# it under the same terms as Python itself, so long as this copyright message -# and disclaimer are retained in their original form. -# -# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -# -# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - -from __future__ import print_function - -__author__ = "Steven Knight " -__revision__ = "TestCommon.py 1.3.D001 2010/06/03 12:58:27 knight" -__version__ = "1.3" - -import copy -import os -import stat -import sys -import glob - -try: - from collections import UserList -except ImportError: - # no 'collections' module or no UserList in collections - exec('from UserList import UserList') - -from TestCmd import * -from TestCmd import __all__ - -__all__.extend([ 'TestCommon', - 'exe_suffix', - 'obj_suffix', - 'shobj_prefix', - 'shobj_suffix', - 'lib_prefix', - 'lib_suffix', - 'dll_prefix', - 'dll_suffix', - ]) - -# Variables that describe the prefixes and suffixes on this system. -if sys.platform == 'win32': - exe_suffix = '.exe' - obj_suffix = '.obj' - shobj_suffix = '.obj' - shobj_prefix = '' - lib_prefix = '' - lib_suffix = '.lib' - dll_prefix = '' - dll_suffix = '.dll' -elif sys.platform == 'cygwin': - exe_suffix = '.exe' - obj_suffix = '.o' - shobj_suffix = '.os' - shobj_prefix = '' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'cyg' - dll_suffix = '.dll' -elif sys.platform.find('irix') != -1: - exe_suffix = '' - obj_suffix = '.o' - shobj_suffix = '.o' - shobj_prefix = '' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'lib' - dll_suffix = '.so' -elif sys.platform.find('darwin') != -1: - exe_suffix = '' - obj_suffix = '.o' - shobj_suffix = '.os' - shobj_prefix = '' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'lib' - dll_suffix = '.dylib' -elif sys.platform.find('sunos') != -1: - exe_suffix = '' - obj_suffix = '.o' - shobj_suffix = '.o' - shobj_prefix = 'so_' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'lib' - dll_suffix = '.so' -else: - exe_suffix = '' - obj_suffix = '.o' - shobj_suffix = '.os' - shobj_prefix = '' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'lib' - dll_suffix = '.so' - -def is_List(e): - return isinstance(e, (list, UserList)) - -def is_Tuple(e): - return isinstance(e, tuple) - -def is_Sequence(e): - return (not hasattr(e, "strip") and - hasattr(e, "__getitem__") or - hasattr(e, "__iter__")) - -def is_writable(f): - mode = os.stat(f)[stat.ST_MODE] - return mode & stat.S_IWUSR - -def separate_files(flist): - existing = [] - missing = [] - for f in flist: - if os.path.exists(f): - existing.append(f) - else: - missing.append(f) - return existing, missing - -if os.name == 'posix': - def _failed(self, status = 0): - if self.status is None or status is None: - return None - return _status(self) != status - def _status(self): - return self.status -elif os.name == 'nt': - def _failed(self, status = 0): - return not (self.status is None or status is None) and \ - self.status != status - def _status(self): - return self.status - -class TestCommon(TestCmd): - - # Additional methods from the Perl Test::Cmd::Common module - # that we may wish to add in the future: - # - # $test->subdir('subdir', ...); - # - # $test->copy('src_file', 'dst_file'); - - def __init__(self, **kw): - """Initialize a new TestCommon instance. This involves just - calling the base class initialization, and then changing directory - to the workdir. - """ - TestCmd.__init__(self, **kw) - os.chdir(self.workdir) - - def options_arguments(self, options, arguments): - """Merges the "options" keyword argument with the arguments.""" - if options: - if arguments is None: - return options - if isinstance(options, str): - options = [options] - if isinstance(arguments, str): - arguments = [arguments] - arguments = ' '.join(options + arguments) - return arguments - - def must_be_writable(self, *files): - """Ensures that the specified file(s) exist and are writable. - An individual file can be specified as a list of directory names, - in which case the pathname will be constructed by concatenating - them. Exits FAILED if any of the files does not exist or is - not writable. - """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing, missing = separate_files(files) - unwritable = [x for x in existing if not is_writable(x)] - if missing: - print("Missing files: `%s'" % "', `".join(missing)) - if unwritable: - print("Unwritable files: `%s'" % "', `".join(unwritable)) - self.fail_test(missing + unwritable) - - def must_contain(self, file, required, mode = 'rb', find = None): - """Ensures that the specified file contains the required text. - """ - file_contents = self.read(file, mode) - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - contains = find(file_contents, required) - if not contains: - print("File `%s' does not contain required string." % file) - print(self.banner('Required string ')) - print(required) - print(self.banner('%s contents ' % file)) - print(file_contents) - self.fail_test(not contains) - - def must_contain_all(self, output, input, title=None, find=None): - """Ensures that the specified output string (first argument) - contains all of the specified input as a block (second argument). - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. - """ - if find is None: - def find(o, i): - try: - return o.index(i) - except ValueError: - return None - - if is_List(output): - output = os.newline.join(output) - - if find(output, input) is None: - if title is None: - title = 'output' - print('Missing expected input from {}:'.format(title)) - print(input) - print(self.banner(title + ' ')) - print(output) - self.fail_test() - - def must_contain_all_lines(self, output, lines, title=None, find=None): - """Ensures that the specified output string (first argument) - contains all of the specified lines (second argument). - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. - """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - missing = [] - if is_List(output): - output = '\n'.join(output) - - for line in lines: - if find(output, line) is None: - missing.append(line) - - if missing: - if title is None: - title = 'output' - sys.stdout.write("Missing expected lines from %s:\n" % title) - for line in missing: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner(title + ' ') + '\n') - sys.stdout.write(output) - self.fail_test() - - def must_contain_any_line(self, output, lines, title=None, find=None): - """Ensures that the specified output string (first argument) - contains at least one of the specified lines (second argument). - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. - """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - for line in lines: - if find(output, line) is not None: - return - - if title is None: - title = 'output' - sys.stdout.write("Missing any expected line from %s:\n" % title) - for line in lines: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner(title + ' ') + '\n') - sys.stdout.write(output) - self.fail_test() - - def must_contain_exactly_lines(self, output, expect, title=None, find=None): - """Ensures that the specified output string (first argument) - contains all of the lines in the expected string (second argument) - with none left over. - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. The function must return the index - of the found line in the output, or None if the line is not found. - """ - out = output.splitlines() - if is_List(expect): - exp = [ e.rstrip('\n') for e in expect ] - else: - exp = expect.splitlines() - if sorted(out) == sorted(exp): - # early out for exact match - return - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - missing = [] - for line in exp: - found = find(out, line) - if found is None: - missing.append(line) - else: - out.pop(found) - - if not missing and not out: - # all lines were matched - return - - if title is None: - title = 'output' - if missing: - sys.stdout.write("Missing expected lines from %s:\n" % title) - for line in missing: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner('Missing %s ' % title) + '\n') - if out: - sys.stdout.write("Extra unexpected lines from %s:\n" % title) - for line in out: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner('Extra %s ' % title) + '\n') - sys.stdout.flush() - self.fail_test() - - def must_contain_lines(self, lines, output, title=None, find = None): - # Deprecated; retain for backwards compatibility. - return self.must_contain_all_lines(output, lines, title, find) - - def must_exist(self, *files): - """Ensures that the specified file(s) must exist. An individual - file be specified as a list of directory names, in which case the - pathname will be constructed by concatenating them. Exits FAILED - if any of the files does not exist. - """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - missing = [x for x in files if not os.path.exists(x) and not os.path.islink(x) ] - if missing: - print("Missing files: `%s'" % "', `".join(missing)) - self.fail_test(missing) - - def must_exist_one_of(self, files): - """Ensures that at least one of the specified file(s) exists. - The filenames can be given as a list, where each entry may be - a single path string, or a tuple of folder names and the final - filename that get concatenated. - Supports wildcard names like 'foo-1.2.3-*.rpm'. - Exits FAILED if none of the files exists. - """ - missing = [] - for x in files: - if is_List(x) or is_Tuple(x): - xpath = os.path.join(*x) - else: - xpath = is_Sequence(x) and os.path.join(x) or x - if glob.glob(xpath): - return - missing.append(xpath) - print("Missing one of: `%s'" % "', `".join(missing)) - self.fail_test(missing) - - def must_match(self, file, expect, mode = 'rb', match=None, message=None, newline=None): - """Matches the contents of the specified file (first argument) - against the expected contents (second argument). The expected - contents are a list of lines or a string which will be split - on newlines. - """ - file_contents = self.read(file, mode, newline) - if not match: - match = self.match - try: - self.fail_test(not match(to_str(file_contents), to_str(expect)), message=message) - except KeyboardInterrupt: - raise - except: - print("Unexpected contents of `%s'" % file) - self.diff(expect, file_contents, 'contents ') - raise - - def must_not_contain(self, file, banned, mode = 'rb', find = None): - """Ensures that the specified file doesn't contain the banned text. - """ - file_contents = self.read(file, mode) - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - contains = find(file_contents, banned) - if contains: - print("File `%s' contains banned string." % file) - print(self.banner('Banned string ')) - print(banned) - print(self.banner('%s contents ' % file)) - print(file_contents) - self.fail_test(contains) - - def must_not_contain_any_line(self, output, lines, title=None, find=None): - """Ensures that the specified output string (first argument) - does not contain any of the specified lines (second argument). - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. - """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - unexpected = [] - for line in lines: - if find(output, line) is not None: - unexpected.append(line) - - if unexpected: - if title is None: - title = 'output' - sys.stdout.write("Unexpected lines in %s:\n" % title) - for line in unexpected: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner(title + ' ') + '\n') - sys.stdout.write(output) - self.fail_test() - - def must_not_contain_lines(self, lines, output, title=None, find=None): - return self.must_not_contain_any_line(output, lines, title, find) - - def must_not_exist(self, *files): - """Ensures that the specified file(s) must not exist. - An individual file be specified as a list of directory names, in - which case the pathname will be constructed by concatenating them. - Exits FAILED if any of the files exists. - """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing = [x for x in files if os.path.exists(x) or os.path.islink(x)] - if existing: - print("Unexpected files exist: `%s'" % "', `".join(existing)) - self.fail_test(existing) - - def must_not_exist_any_of(self, files): - """Ensures that none of the specified file(s) exists. - The filenames can be given as a list, where each entry may be - a single path string, or a tuple of folder names and the final - filename that get concatenated. - Supports wildcard names like 'foo-1.2.3-*.rpm'. - Exits FAILED if any of the files exists. - """ - existing = [] - for x in files: - if is_List(x) or is_Tuple(x): - xpath = os.path.join(*x) - else: - xpath = is_Sequence(x) and os.path.join(x) or x - if glob.glob(xpath): - existing.append(xpath) - if existing: - print("Unexpected files exist: `%s'" % "', `".join(existing)) - self.fail_test(existing) - - def must_not_be_writable(self, *files): - """Ensures that the specified file(s) exist and are not writable. - An individual file can be specified as a list of directory names, - in which case the pathname will be constructed by concatenating - them. Exits FAILED if any of the files does not exist or is - writable. - """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing, missing = separate_files(files) - writable = [file for file in existing if is_writable(file)] - if missing: - print("Missing files: `%s'" % "', `".join(missing)) - if writable: - print("Writable files: `%s'" % "', `".join(writable)) - self.fail_test(missing + writable) - - def _complete(self, actual_stdout, expected_stdout, - actual_stderr, expected_stderr, status, match): - """ - Post-processes running a subcommand, checking for failure - status and displaying output appropriately. - """ - if _failed(self, status): - expect = '' - if status != 0: - expect = " (expected %s)" % str(status) - print("%s returned %s%s" % (self.program, _status(self), expect)) - print(self.banner('STDOUT ')) - print(actual_stdout) - print(self.banner('STDERR ')) - print(actual_stderr) - self.fail_test() - if (expected_stdout is not None - and not match(actual_stdout, expected_stdout)): - self.diff(expected_stdout, actual_stdout, 'STDOUT ') - if actual_stderr: - print(self.banner('STDERR ')) - print(actual_stderr) - self.fail_test() - if (expected_stderr is not None - and not match(actual_stderr, expected_stderr)): - print(self.banner('STDOUT ')) - print(actual_stdout) - self.diff(expected_stderr, actual_stderr, 'STDERR ') - self.fail_test() - - def start(self, program = None, - interpreter = None, - options = None, - arguments = None, - universal_newlines = None, - **kw): - """ - Starts a program or script for the test environment, handling - any exceptions. - """ - arguments = self.options_arguments(options, arguments) - try: - return TestCmd.start(self, program, interpreter, arguments, - universal_newlines, **kw) - except KeyboardInterrupt: - raise - except Exception as e: - print(self.banner('STDOUT ')) - try: - print(self.stdout()) - except IndexError: - pass - print(self.banner('STDERR ')) - try: - print(self.stderr()) - except IndexError: - pass - cmd_args = self.command_args(program, interpreter, arguments) - sys.stderr.write('Exception trying to execute: %s\n' % cmd_args) - raise e - - def finish(self, popen, stdout = None, stderr = '', status = 0, **kw): - """ - Finishes and waits for the process being run under control of - the specified popen argument. Additional arguments are similar - to those of the run() method: - - stdout The expected standard output from - the command. A value of None means - don't test standard output. - - stderr The expected error output from - the command. A value of None means - don't test error output. - - status The expected exit status from the - command. A value of None means don't - test exit status. - """ - TestCmd.finish(self, popen, **kw) - match = kw.get('match', self.match) - self._complete(self.stdout(), stdout, - self.stderr(), stderr, status, match) - - def run(self, options = None, arguments = None, - stdout = None, stderr = '', status = 0, **kw): - """Runs the program under test, checking that the test succeeded. - - The parameters are the same as the base TestCmd.run() method, - with the addition of: - - options Extra options that get prepended to the beginning - of the arguments. - - stdout The expected standard output from - the command. A value of None means - don't test standard output. - - stderr The expected error output from - the command. A value of None means - don't test error output. - - status The expected exit status from the - command. A value of None means don't - test exit status. - - By default, this expects a successful exit (status = 0), does - not test standard output (stdout = None), and expects that error - output is empty (stderr = ""). - """ - kw['arguments'] = self.options_arguments(options, arguments) - try: - match = kw['match'] - del kw['match'] - except KeyError: - match = self.match - TestCmd.run(self, **kw) - self._complete(self.stdout(), stdout, - self.stderr(), stderr, status, match) - - def skip_test(self, message="Skipping test.\n"): - """Skips a test. - - Proper test-skipping behavior is dependent on the external - TESTCOMMON_PASS_SKIPS environment variable. If set, we treat - the skip as a PASS (exit 0), and otherwise treat it as NO RESULT. - In either case, we print the specified message as an indication - that the substance of the test was skipped. - - (This was originally added to support development under Aegis. - Technically, skipping a test is a NO RESULT, but Aegis would - treat that as a test failure and prevent the change from going to - the next step. Since we ddn't want to force anyone using Aegis - to have to install absolutely every tool used by the tests, we - would actually report to Aegis that a skipped test has PASSED - so that the workflow isn't held up.) - """ - if message: - sys.stdout.write(message) - sys.stdout.flush() - pass_skips = os.environ.get('TESTCOMMON_PASS_SKIPS') - if pass_skips in [None, 0, '0']: - # skip=1 means skip this function when showing where this - # result came from. They only care about the line where the - # script called test.skip_test(), not the line number where - # we call test.no_result(). - self.no_result(skip=1) - else: - # We're under the development directory for this change, - # so this is an Aegis invocation; pass the test (exit 0). - self.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestCommonTests.py b/QMTest/TestCommonTests.py deleted file mode 100644 index 7949cb8..0000000 --- a/QMTest/TestCommonTests.py +++ /dev/null @@ -1,2340 +0,0 @@ -#!/usr/bin/env python -""" -TestCommonTests.py: Unit tests for the TestCommon.py module. - -Copyright 2000-2010 Steven Knight -This module is free software, and you may redistribute it and/or modify -it under the same terms as Python itself, so long as this copyright message -and disclaimer are retained in their original form. - -IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -""" - -__author__ = "Steven Knight " -__revision__ = "TestCommonTests.py 1.3.D001 2010/06/03 12:58:27 knight" - -import difflib -import os -import re -import signal -import stat -import sys -import unittest - -# Strip the current directory so we get the right TestCommon.py module. -sys.path = sys.path[1:] - -import TestCmd -import TestCommon - -def lstrip(s): - lines = [ _.expandtabs() for _ in s.split('\n') ] - if lines[0] == '': - lines = lines[1:] - spaces = len(re.match('^( *).*', lines[0]).group(1)) - if spaces: - lines = [ l[spaces:] for l in lines ] - return '\n'.join(lines) - -if sys.version[:3] == '1.5': - expected_newline = '\\012' -else: - expected_newline = '\\n' - -def assert_display(expect, result, error=None): - try: - expect = expect.pattern - except AttributeError: - pass - result = [ - '\n', - ('*'*80) + '\n', - expect, - ('*'*80) + '\n', - result, - ('*'*80) + '\n', - ] - if error: - result.append(error) - return ''.join(result) - - -class TestCommonTestCase(unittest.TestCase): - """Base class for TestCommon test cases, fixture and utility methods.""" - create_run_env = True - - def setUp(self): - self.orig_cwd = os.getcwd() - if self.create_run_env: - self.run_env = TestCmd.TestCmd(workdir = '') - - def tearDown(self): - os.chdir(self.orig_cwd) - - def set_up_execution_scripts(self): - run_env = self.run_env - - run_env.subdir('sub dir') - - self.python = sys.executable - - self.pass_script = run_env.workpath('sub dir', 'pass') - self.fail_script = run_env.workpath('sub dir', 'fail') - self.stdout_script = run_env.workpath('sub dir', 'stdout') - self.stderr_script = run_env.workpath('sub dir', 'stderr') - self.signal_script = run_env.workpath('sub dir', 'signal') - self.stdin_script = run_env.workpath('sub dir', 'stdin') - - preamble = "import sys" - stdout = "; sys.stdout.write(r'%s: STDOUT: ' + repr(sys.argv[1:]) + '\\n')" - stderr = "; sys.stderr.write(r'%s: STDERR: ' + repr(sys.argv[1:]) + '\\n')" - exit0 = "; sys.exit(0)" - exit1 = "; sys.exit(1)" - if sys.platform == 'win32': - wrapper = '@python -c "%s" %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9\n' - else: - wrapper = '#! /usr/bin/env python\n%s\n' - wrapper = '#! /usr/bin/env python\n%s\n' - - pass_body = preamble + stdout % self.pass_script + exit0 - fail_body = preamble + stdout % self.fail_script + exit1 - stderr_body = preamble + stderr % self.stderr_script + exit0 - - run_env.write(self.pass_script, wrapper % pass_body) - run_env.write(self.fail_script, wrapper % fail_body) - run_env.write(self.stderr_script, wrapper % stderr_body) - - signal_body = lstrip("""\ - import os - import signal - os.kill(os.getpid(), signal.SIGTERM) - """) - - run_env.write(self.signal_script, wrapper % signal_body) - - stdin_body = lstrip("""\ - import sys - input = sys.stdin.read()[:-1] - sys.stdout.write(r'%s: STDOUT: ' + repr(input) + '\\n') - sys.stderr.write(r'%s: STDERR: ' + repr(input) + '\\n') - """ % (self.stdin_script, self.stdin_script)) - - run_env.write(self.stdin_script, wrapper % stdin_body) - - def run_execution_test(self, script, expect_stdout, expect_stderr): - self.set_up_execution_scripts() - - run_env = self.run_env - - os.chdir(run_env.workpath('sub dir')) - - # Everything before this prepared our "source directory." - # Now do the real test. - script = script % self.__dict__ - run_env.run(program=sys.executable, stdin=script) - - stdout = run_env.stdout() - stderr = run_env.stderr() - - expect_stdout = expect_stdout % self.__dict__ - assert stdout == expect_stdout, assert_display(expect_stdout, - stdout, - stderr) - - try: - match = expect_stderr.match - except AttributeError: - expect_stderr = expect_stderr % self.__dict__ - assert stderr == expect_stderr, assert_display(expect_stderr, - stderr) - else: - assert expect_stderr.match(stderr), assert_display(expect_stderr, - stderr) - - -class __init__TestCase(TestCommonTestCase): - def test___init__(self): - """Test initialization""" - run_env = self.run_env - - os.chdir(run_env.workdir) - script = lstrip("""\ - from __future__ import print_function - from TestCommon import TestCommon - tc = TestCommon(workdir='') - import os - print(os.getcwd()) - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout()[:-1] - assert stdout != run_env.workdir, stdout - stderr = run_env.stderr() - assert stderr == "", stderr - - -class banner_TestCase(TestCommonTestCase): - create_run_env = False - def test_banner(self): - """Test banner()""" - tc = TestCommon.TestCommon(workdir='') - - b = tc.banner('xyzzy ') - assert b == "xyzzy ==========================================================================", b - - tc.banner_width = 10 - - b = tc.banner('xyzzy ') - assert b == "xyzzy ====", b - - b = tc.banner('xyzzy ', 20) - assert b == "xyzzy ==============", b - - tc.banner_char = '-' - - b = tc.banner('xyzzy ') - assert b == "xyzzy ----", b - -class must_be_writable_TestCase(TestCommonTestCase): - def test_file_does_not_exists(self): - """Test must_be_writable(): file does not exist""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Missing files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_writable_file_exists(self): - """Test must_be_writable(): writable file exists""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode | stat.S_IWUSR) - tc.must_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_non_writable_file_exists(self): - """Test must_be_writable(): non-writable file exists""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode & ~stat.S_IWUSR) - tc.must_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Unwritable files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_file_specified_as_list(self): - """Test must_be_writable(): file specified as list""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - f1 = tc.workpath('sub', 'file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode | stat.S_IWUSR) - tc.must_be_writable(['sub', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - -class must_contain_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_contain(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 contents\\n") - tc.must_contain('file1', "1 c") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_missing(self): - """Test must_contain(): file missing""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_contain('file1', "1 c\\n") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr.find("No such file or directory:") != -1, stderr - - def test_failure(self): - """Test must_contain(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 does not match\\n") - tc.must_contain('file1', "1 c") - tc.run() - """) - expect = lstrip("""\ - File `file1' does not contain required string. - Required string ================================================================ - 1 c - file1 contents ================================================================= - file1 does not match - - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == expect, repr(stdout) - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_mode(self): - """Test must_contain(): mode""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 contents\\n", mode='w') - tc.must_contain('file1', "1 c", mode='r') - tc.write('file2', "file2 contents\\n", mode='wb') - tc.must_contain('file2', "2 c", mode='rb') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_contain_all_lines_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_contain_all_lines(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_all_lines(output, lines) - - test.must_contain_all_lines(output, ['www\\n']) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_contain_all_lines(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_all_lines(output, lines) - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - output ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_find(self): - """Test must_contain_all_lines(): find""" - run_env = self.run_env - - script = lstrip(""" - import re - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'x.*', - '.*y', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - def re_search(output, line): - return re.compile(line, re.S).search(output) - test.must_contain_all_lines(output, lines, find=re_search) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_title(self): - """Test must_contain_all_lines(): title""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_all_lines(output, lines, title='STDERR') - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from STDERR: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - STDERR ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_contain_any_line_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_contain_any_line(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'aaa\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_any_line(output, lines) - - test.must_contain_any_line(output, ['www\\n']) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_contain_any_line(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_any_line(output, lines) - - test.pass_test() - """) - - expect = lstrip("""\ - Missing any expected line from output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - output ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_find(self): - """Test must_contain_any_line(): find""" - run_env = self.run_env - - script = lstrip(""" - import re - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'aaa', - '.*y', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - def re_search(output, line): - return re.compile(line, re.S).search(output) - test.must_contain_any_line(output, lines, find=re_search) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_title(self): - """Test must_contain_any_line(): title""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_any_line(output, lines, title='STDOUT') - - test.pass_test() - """) - - expect = lstrip("""\ - Missing any expected line from STDOUT: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - STDOUT ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_contain_exactly_lines_TestCase(TestCommonTestCase): - def test_success_list(self): - """Test must_contain_exactly_lines(): success (input list)""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'yyy\\n', - 'xxx\\n', - 'zzz', - 'www\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_exactly_lines(output, lines) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_success_string(self): - """Test must_contain_exactly_lines(): success (input string)""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = '''\\ - yyy - xxx - zzz - www - ''' - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_exactly_lines(output, lines) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_contain_exactly_lines(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_exactly_lines(output, lines) - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from output: - 'xxx' - 'yyy' - Missing output ================================================================= - Extra unexpected lines from output: - 'www' - 'zzz' - Extra output =================================================================== - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_find(self): - """Test must_contain_exactly_lines(): find""" - run_env = self.run_env - - script = lstrip(""" - import re - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'zzz', - '.*y', - 'xxx', - 'www', - ] - - output = '''\\\ - www - xxx - yyy - zzz - ''' - - def re_search(output, line): - pattern = re.compile(line, re.S) - index = 0 - for o in output: - if pattern.search(o): - return index - index +=1 - return None - test.must_contain_exactly_lines(output, lines, find=re_search) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_title(self): - """Test must_contain_exactly_lines(): title""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_exactly_lines(output, lines, title='STDOUT') - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from STDOUT: - 'xxx' - 'yyy' - Missing STDOUT ================================================================= - Extra unexpected lines from STDOUT: - 'www' - 'zzz' - Extra STDOUT =================================================================== - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_contain_lines_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_contain_lines(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_lines(lines, output) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_contain_lines(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_lines(lines, output) - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - output ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_exist_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_exist(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_exist('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_exist(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_exist('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Missing files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_file_specified_as_list(self): - """Test must_exist(): file specified as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_exist(['sub', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_broken_link(self) : - """Test must_exist(): exists but it is a broken link""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.symlink('badtarget', "brokenlink") - tc.must_exist('brokenlink') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - -class must_exist_one_of_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_exist_one_of(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_exist_one_of(['file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_exist_one_of(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_exist_one_of(['file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Missing one of: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_files_specified_as_list(self): - """Test must_exist_one_of(): files specified as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_exist_one_of(['file2', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_files_specified_with_wildcards(self): - """Test must_exist_one_of(): files specified with wildcards""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file7', "file7\\n") - tc.must_exist_one_of(['file?']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_given_as_list(self): - """Test must_exist_one_of(): file given as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_exist_one_of(['file2', - ['sub', 'file1']]) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_given_as_sequence(self): - """Test must_exist_one_of(): file given as sequence""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_exist_one_of(['file2', - ('sub', 'file1')]) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - -class must_match_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_match(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_match('file1', "file1\\n") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_does_not_exists(self): - """Test must_match(): file does not exist""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_match('file1', "file1\\n") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr.find("No such file or directory:") != -1, stderr - - def test_failure(self): - """Test must_match(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 does not match\\n") - tc.must_match('file1', "file1\\n") - tc.run() - """) - - expect = lstrip("""\ - Unexpected contents of `file1' - contents ======================================================================= - 1c1 - < file1 - --- - > file1 does not match - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == expect, stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_mode(self): - """Test must_match(): mode""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n", mode='w') - tc.must_match('file1', "file1\\n", mode='r') - tc.write('file2', "file2\\n", mode='wb') - tc.must_match('file2', "file2\\n", mode='rb') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_not_be_writable_TestCase(TestCommonTestCase): - def test_file_does_not_exists(self): - """Test must_not_be_writable(): file does not exist""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Missing files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_writable_file_exists(self): - """Test must_not_be_writable(): writable file exists""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode | stat.S_IWUSR) - tc.must_not_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Writable files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_non_writable_file_exists(self): - """Test must_not_be_writable(): non-writable file exists""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode & ~stat.S_IWUSR) - tc.must_not_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_specified_as_list(self): - """Test must_not_be_writable(): file specified as list""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - f1 = tc.workpath('sub', 'file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode & ~stat.S_IWUSR) - tc.must_not_be_writable(['sub', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_not_contain_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_not_contain(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 contents\\n") - tc.must_not_contain('file1', "1 does not contain c") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_does_not_exist(self): - """Test must_not_contain(): file does not exist""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_contain('file1', "1 c\\n") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr.find("No such file or directory:") != -1, stderr - - def test_failure(self): - """Test must_not_contain(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 does contain contents\\n") - tc.must_not_contain('file1', "1 does contain c") - tc.run() - """) - expect = lstrip("""\ - File `file1' contains banned string. - Banned string ================================================================== - 1 does contain c - file1 contents ================================================================= - file1 does contain contents - - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == expect, repr(stdout) - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_mode(self): - """Test must_not_contain(): mode""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 contents\\n", mode='w') - tc.must_not_contain('file1', "1 does not contain c", mode='r') - tc.write('file2', "file2 contents\\n", mode='wb') - tc.must_not_contain('file2', "2 does not contain c", mode='rb') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_not_contain_any_line_TestCase(TestCommonTestCase): - def test_failure(self): - """Test must_not_contain_any_line(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - 'www\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_not_contain_any_line(output, lines) - - test.pass_test() - """) - - expect = lstrip("""\ - Unexpected lines in output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - 'www%(expected_newline)s' - output ========================================================================= - www - xxx - yyy - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_find(self): - """Test must_not_contain_any_line(): find""" - run_env = self.run_env - - script = lstrip(""" - import re - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'x.*' - '.*y' - ] - - output = '''\\ - www - zzz - ''' - - def re_search(output, line): - return re.compile(line, re.S).search(output) - test.must_not_contain_any_line(output, lines, find=re_search) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_success(self): - """Test must_not_contain_any_line(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n' - 'yyy\\n' - ] - - output = '''\\ - www - zzz - ''' - - test.must_not_contain_any_line(output, lines) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_title(self): - """Test must_not_contain_any_line(): title""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_not_contain_any_line(output, lines, title='XYZZY') - - test.pass_test() - """) - - expect = lstrip("""\ - Unexpected lines in XYZZY: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - XYZZY ========================================================================== - www - xxx - yyy - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_not_contain_lines_TestCase(TestCommonTestCase): - def test_failure(self): - """Test must_not_contain_lines(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_not_contain_lines(lines, output) - - test.pass_test() - """) - - expect = lstrip("""\ - Unexpected lines in output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - output ========================================================================= - www - xxx - yyy - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_success(self): - """Test must_not_contain_lines(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n' - 'yyy\\n' - ] - - output = '''\\ - www - zzz - ''' - - test.must_not_contain_lines(lines, output) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_not_exist_TestCase(TestCommonTestCase): - def test_failure(self): - """Test must_not_exist(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_not_exist('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Unexpected files exist: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_success(self): - """Test must_not_exist(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_exist('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_specified_as_list(self): - """Test must_not_exist(): file specified as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.must_not_exist(['sub', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_existing_broken_link(self): - """Test must_not_exist(): exists but it is a broken link""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.symlink('badtarget', 'brokenlink') - tc.must_not_exist('brokenlink') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Unexpected files exist: `brokenlink'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - -class must_not_exist_any_of_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_not_exist_any_of(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_exist_any_of(['file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_not_exist_any_of(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_not_exist_any_of(['file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Unexpected files exist: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_files_specified_as_list(self): - """Test must_not_exist_any_of(): files specified as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_exist_any_of(['file2', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_files_specified_with_wildcards(self): - """Test must_not_exist_any_of(): files specified with wildcards""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file7', "file7\\n") - tc.must_not_exist_any_of(['files?']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_given_as_list(self): - """Test must_not_exist_any_of(): file given as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_not_exist_any_of(['file2', - ['sub', 'files*']]) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_given_as_sequence(self): - """Test must_not_exist_any_of(): file given as sequence""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_not_exist_any_of(['file2', - ('sub', 'files?')]) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - -class run_TestCase(TestCommonTestCase): - def test_argument_handling(self): - """Test run(): argument handling""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(arguments = "arg1 arg2 arg3", - stdout = r"%(pass_script)s: STDOUT: ['arg1', 'arg2', 'arg3']" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_default_pass(self): - """Test run(): default arguments, script passes""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(pass_script)s', - interpreter=r'%(python)s', - workdir='') - tc.run() - """) - - self.run_execution_test(script, "", "") - - def test_default_fail(self): - """Test run(): default arguments, script fails""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(fail_script)s', - interpreter='%(python)s', - workdir='') - tc.run() - """) - - expect_stdout = lstrip("""\ - %(fail_script)s returned 1 - STDOUT ========================================================================= - %(fail_script)s: STDOUT: [] - - STDERR ========================================================================= - - """) - - expect_stderr = lstrip("""\ - FAILED test of .*fail - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_default_stderr(self): - """Test run(): default arguments, error output""" - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir='') - tc.run() - """) - - expect_stdout = lstrip("""\ - STDOUT ========================================================================= - - STDERR ========================================================================= - 0a1 - > %(stderr_script)s: STDERR: [] - """) - - expect_stderr = lstrip("""\ - FAILED test of .*stderr - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_exception_handling(self): - """Test run(): exception handling""" - script = lstrip("""\ - import TestCmd - from TestCommon import TestCommon - def raise_exception(*args, **kw): - raise TypeError("forced TypeError") - TestCmd.TestCmd.start = raise_exception - tc = TestCommon(program='%(pass_script)s', - interpreter='%(python)s', - workdir='') - tc.run() - """) - - expect_stdout = lstrip("""\ - STDOUT ========================================================================= - STDERR ========================================================================= - """) - - expect_stderr = lstrip("""\ - Exception trying to execute: \\[%s, '[^']*pass'\\] - Traceback \\((innermost|most recent call) last\\): - File "", line \\d+, in (\\?|) - File "[^"]+TestCommon.py", line \\d+, in run - TestCmd.run\\(self, \\*\\*kw\\) - File "[^"]+TestCmd.py", line \\d+, in run - .* - File "[^"]+TestCommon.py", line \\d+, in start - raise e - TypeError: forced TypeError - """ % re.escape(repr(sys.executable))) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_ignore_stderr(self): - """Test run(): ignore stderr""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir='') - tc.run(stderr = None) - """) - - self.run_execution_test(script, "", "") - - def test_match_function_stdout(self): - """Test run(): explicit match function, stdout""" - - script = lstrip("""\ - def my_match_exact(actual, expect): return actual == expect - from TestCommon import TestCommon, match_re_dotall - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_re_dotall) - tc.run(arguments = "arg1 arg2 arg3", - stdout = r"%(pass_script)s: STDOUT: ['arg1', 'arg2', 'arg3']" + "\\n", - match = my_match_exact) - """) - - self.run_execution_test(script, "", "") - - def test_match_function_stderr(self): - """Test run(): explicit match function, stderr""" - - script = lstrip("""\ - def my_match_exact(actual, expect): return actual == expect - from TestCommon import TestCommon, match_re_dotall - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir="", - match=match_re_dotall) - tc.run(arguments = "arg1 arg2 arg3", - stderr = r"%(stderr_script)s: STDERR: ['arg1', 'arg2', 'arg3']" + "\\n", - match = my_match_exact) - """) - - self.run_execution_test(script, "", "") - - def test_matched_status_fails(self): - """Test run(): matched status, script fails""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(fail_script)s', - interpreter='%(python)s', - workdir='') - tc.run(status = 1) - """) - - self.run_execution_test(script, "", "") - - def test_matched_stdout(self): - """Test run(): matched stdout""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(stdout = r"%(pass_script)s: STDOUT: []" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_matched_stderr(self): - """Test run(): matched stderr""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(stderr = r"%(stderr_script)s: STDERR: []" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_mismatched_status_pass(self): - """Test run(): mismatched status, script passes""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir='') - tc.run(status = 1) - """) - - expect_stdout = lstrip("""\ - %(pass_script)s returned 0 (expected 1) - STDOUT ========================================================================= - %(pass_script)s: STDOUT: [] - - STDERR ========================================================================= - - """) - - expect_stderr = lstrip("""\ - FAILED test of .*pass - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_mismatched_status_fail(self): - """Test run(): mismatched status, script fails""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(fail_script)s', - interpreter='%(python)s', - workdir='') - tc.run(status = 2) - """) - - expect_stdout = lstrip("""\ - %(fail_script)s returned 1 (expected 2) - STDOUT ========================================================================= - %(fail_script)s: STDOUT: [] - - STDERR ========================================================================= - - """) - - expect_stderr = lstrip("""\ - FAILED test of .*fail - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_mismatched_stdout(self): - """Test run(): mismatched stdout""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir='') - tc.run(stdout = "Not found\\n") - """) - - expect_stdout = lstrip("""\ - STDOUT ========================================================================= - 1c1 - < Not found - --- - > %(pass_script)s: STDOUT: [] - """) - - expect_stderr = lstrip("""\ - FAILED test of .*pass - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_mismatched_stderr(self): - """Test run(): mismatched stderr""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir='') - tc.run(stderr = "Not found\\n") - """) - - expect_stdout = lstrip("""\ - STDOUT ========================================================================= - - STDERR ========================================================================= - 1c1 - < Not found - --- - > %(stderr_script)s: STDERR: [] - """) - - expect_stderr = lstrip("""\ - FAILED test of .*stderr - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_option_handling(self): - """Test run(): option handling""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(options = "opt1 opt2 opt3", - stdout = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3']" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_options_plus_arguments(self): - """Test run(): option handling with arguments""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(options = "opt1 opt2 opt3", - arguments = "arg1 arg2 arg3", - stdout = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3', 'arg1', 'arg2', 'arg3']" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_signal_handling(self): - """Test run(): signal handling""" - - try: - os.kill - except AttributeError: - sys.stderr.write('can not test, no os.kill ... ') - return - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(signal_script)s', - interpreter='%(python)s', - workdir='') - tc.run() - """) - - self.SIGTERM = signal.SIGTERM - - # Script returns the signal value as a negative number. - expect_stdout = lstrip("""\ - %(signal_script)s returned -%(SIGTERM)s - STDOUT ========================================================================= - - STDERR ========================================================================= - - """) - - expect_stderr = lstrip("""\ - FAILED test of .*signal - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_stdin(self): - """Test run(): stdin handling""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(stdin_script)s', - interpreter='%(python)s', - workdir='', - match=match_exact) - expect_stdout = r"%(stdin_script)s: STDOUT: 'input'" + "\\n" - expect_stderr = r"%(stdin_script)s: STDERR: 'input'" + "\\n" - tc.run(stdin="input\\n", stdout = expect_stdout, stderr = expect_stderr) - """) - - expect_stdout = lstrip("""\ - %(pass_script)s returned 0 (expected 1) - STDOUT ========================================================================= - %(pass_script)s: STDOUT: [] - - STDERR ========================================================================= - - """) - - self.run_execution_test(script, "", "") - - - -class start_TestCase(TestCommonTestCase): - def test_option_handling(self): - """Test start(): option handling""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - p = tc.start(options = "opt1 opt2 opt3") - expect = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3']" + "\\n" - tc.finish(p, stdout = expect) - """) - - self.run_execution_test(script, "", "") - - def test_options_plus_arguments(self): - """Test start(): option handling with arguments""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - p = tc.start(options = "opt1 opt2 opt3", - arguments = "arg1 arg2 arg3") - expect = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3', 'arg1', 'arg2', 'arg3']" + "\\n" - tc.finish(p, stdout = expect) - """) - - self.run_execution_test(script, "", "") - - - -class skip_test_TestCase(TestCommonTestCase): - def test_skip_test(self): - """Test skip_test()""" - run_env = self.run_env - - script = lstrip("""\ - import TestCommon - test = TestCommon.TestCommon(workdir='') - test.skip_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Skipping test.\n", stdout - stderr = run_env.stderr() - expect = [ - "NO RESULT for test at line 3 of \n", - "NO RESULT for test at line 3 of ()\n", - ] - assert stderr in expect, repr(stderr) - - script = lstrip("""\ - import TestCommon - test = TestCommon.TestCommon(workdir='') - test.skip_test("skipping test because I said so\\n") - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "skipping test because I said so\n", stdout - stderr = run_env.stderr() - expect = [ - "NO RESULT for test at line 3 of \n", - "NO RESULT for test at line 3 of ()\n", - ] - assert stderr in expect, repr(stderr) - - import os - os.environ['TESTCOMMON_PASS_SKIPS'] = '1' - - try: - script = lstrip("""\ - import TestCommon - test = TestCommon.TestCommon(workdir='') - test.skip_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Skipping test.\n", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - finally: - del os.environ['TESTCOMMON_PASS_SKIPS'] - - - -class variables_TestCase(TestCommonTestCase): - def test_variables(self): - """Test global variables""" - run_env = self.run_env - - variables = [ - 'fail_test', - 'no_result', - 'pass_test', - 'match_exact', - 'match_re', - 'match_re_dotall', - 'python', - '_python_', - 'TestCmd', - - 'TestCommon', - 'exe_suffix', - 'obj_suffix', - 'shobj_prefix', - 'shobj_suffix', - 'lib_prefix', - 'lib_suffix', - 'dll_prefix', - 'dll_suffix', - ] - - script = "from __future__ import print_function" + \ - "import TestCommon\n" + \ - '\n'.join([ "print(TestCommon.%s)\n" % v for v in variables ]) - run_env.run(program=sys.executable, stdin=script) - stderr = run_env.stderr() - assert stderr == "", stderr - - script = "from __future__ import print_function" + \ - "from TestCommon import *\n" + \ - '\n'.join([ "print(%s)" % v for v in variables ]) - run_env.run(program=sys.executable, stdin=script) - stderr = run_env.stderr() - assert stderr == "", stderr - - - -if __name__ == "__main__": - tclasses = [ - __init__TestCase, - banner_TestCase, - must_be_writable_TestCase, - must_contain_TestCase, - must_contain_all_lines_TestCase, - must_contain_any_line_TestCase, - must_contain_exactly_lines_TestCase, - must_contain_lines_TestCase, - must_exist_TestCase, - must_exist_one_of_TestCase, - must_match_TestCase, - must_not_be_writable_TestCase, - must_not_contain_TestCase, - must_not_contain_any_line_TestCase, - must_not_contain_lines_TestCase, - must_not_exist_TestCase, - must_not_exist_any_of_TestCase, - run_TestCase, - start_TestCase, - skip_test_TestCase, - variables_TestCase, - ] - suite = unittest.TestSuite() - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests([ tclass(n) for n in names ]) - if not unittest.TextTestRunner().run(suite).wasSuccessful(): - sys.exit(1) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestRuntest.py b/QMTest/TestRuntest.py deleted file mode 100644 index 68563da..0000000 --- a/QMTest/TestRuntest.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -TestRuntest.py: a testing framework for the runtest.py command used to -invoke SCons tests. - -A TestRuntest environment object is created via the usual invocation: - - test = TestRuntest() - -TestRuntest is a subclass of TestCommon, which is in turn is a subclass -of TestCmd), and hence has available all of the methods and attributes -from those classes, as well as any overridden or additional methods or -attributes defined in this subclass. -""" - -# __COPYRIGHT__ - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os -import os.path -import re -import shutil -import sys - -from TestCommon import * -from TestCommon import __all__ - -__all__.extend([ 'TestRuntest', - 'pythonstring', - ]) - -if re.search('\s', python): - pythonstring = _python_ -else: - pythonstring = python -pythonstring = pythonstring.replace('\\', '\\\\') - - -failing_test_template = """\ -import sys -sys.stdout.write('FAILING TEST STDOUT\\n') -sys.stderr.write('FAILING TEST STDERR\\n') -sys.exit(1) -""" - -no_result_test_template = """\ -import sys -sys.stdout.write('NO RESULT TEST STDOUT\\n') -sys.stderr.write('NO RESULT TEST STDERR\\n') -sys.exit(2) -""" - -passing_test_template = """\ -import sys -sys.stdout.write('PASSING TEST STDOUT\\n') -sys.stderr.write('PASSING TEST STDERR\\n') -sys.exit(0) -""" - -fake_scons_py = """ -__version__ = '1.2.3' -__build__ = 'D123' -__buildsys__ = 'fake_system' -__date__ = 'Jan 1 1970' -__developer__ = 'Anonymous' -""" - -fake___init___py = """ -__version__ = '4.5.6' -__build__ = 'D456' -__buildsys__ = 'another_fake_system' -__date__ = 'Dec 31 1999' -__developer__ = 'John Doe' -""" - -class TestRuntest(TestCommon): - """Class for testing the runtest.py script. - - This provides a common place for initializing Runtest tests, - eliminating the need to begin every test with the same repeated - initializations. - """ - - def __init__(self, **kw): - """Initialize a Runtest testing object. - - If they're not overridden by keyword arguments, this - initializes the object with the following default values: - - program = 'runtest.py' - interpreter = ['python', '-tt'] - match = match_exact - workdir = '' - - The workdir value means that, by default, a temporary - workspace directory is created for a TestRuntest environment. - The superclass TestCommon.__init__() will change directory (chdir) - to the workspace directory, so an explicit "chdir = '.'" on all - of the run() method calls is not necessary. This initialization - also copies the runtest.py and QMTest/ subdirectory tree to the - temporary directory, duplicating how this test infrastructure - appears in a normal workspace. - """ - if 'program' not in kw: - kw['program'] = 'runtest.py' - if 'interpreter' not in kw: - kw['interpreter'] = [python, '-tt'] - if 'match' not in kw: - kw['match'] = match_exact - if 'workdir' not in kw: - kw['workdir'] = '' - - try: - things_to_copy = kw['things_to_copy'] - except KeyError: - things_to_copy = [ - 'runtest.py', - 'QMTest', - ] - else: - del kw['things_to_copy'] - - orig_cwd = os.getcwd() - TestCommon.__init__(self, **kw) - - dirs = [os.environ.get('SCONS_RUNTEST_DIR', orig_cwd)] - - for thing in things_to_copy: - for dir in dirs: - t = os.path.join(dir, thing) - if os.path.exists(t): - if os.path.isdir(t): - copy_func = shutil.copytree - else: - copy_func = shutil.copyfile - copy_func(t, self.workpath(thing)) - break - - self.program_set(self.workpath(kw['program'])) - - os.environ['PYTHONPATH'] = '' - - def write_fake_scons_source_tree(self): - os.mkdir('src') - os.mkdir('src/script') - self.write('src/script/scons.py', fake_scons_py) - - os.mkdir('src/engine') - os.mkdir('src/engine/SCons') - self.write('src/engine/SCons/__init__.py', fake___init___py) - os.mkdir('src/engine/SCons/Script') - self.write('src/engine/SCons/Script/__init__.py', fake___init___py) - - def write_failing_test(self, name): - self.write(name, failing_test_template) - - def write_no_result_test(self, name): - self.write(name, no_result_test_template) - - def write_passing_test(self, name): - self.write(name, passing_test_template) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py deleted file mode 100644 index ef907d8..0000000 --- a/QMTest/TestSCons.py +++ /dev/null @@ -1,1642 +0,0 @@ -""" -TestSCons.py: a testing framework for the SCons software construction -tool. - -A TestSCons environment object is created via the usual invocation: - - test = TestSCons() - -TestScons is a subclass of TestCommon, which in turn is a subclass -of TestCmd), and hence has available all of the methods and attributes -from those classes, as well as any overridden or additional methods or -attributes defined in this subclass. -""" - -# __COPYRIGHT__ -from __future__ import division, print_function - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os -import re -import shutil -import sys -import time -import subprocess - -from TestCommon import * -from TestCommon import __all__ - -from TestCmd import Popen -from TestCmd import PIPE - -# Some tests which verify that SCons has been packaged properly need to -# look for specific version file names. Replicating the version number -# here provides some independent verification that what we packaged -# conforms to what we expect. - -default_version = '3.1.0.alpha.yyyymmdd' - -python_version_unsupported = (2, 6, 0) -python_version_deprecated = (2, 7, 0) - -# In the checked-in source, the value of SConsVersion in the following -# line must remain "__ VERSION __" (without the spaces) so the built -# version in build/QMTest/TestSCons.py contains the actual version -# string of the packages that have been built. -SConsVersion = '__VERSION__' -if SConsVersion == '__' + 'VERSION' + '__': - SConsVersion = default_version - -__all__.extend([ - 'TestSCons', - 'machine', - 'python', - '_exe', - '_obj', - '_shobj', - 'shobj_', - 'lib_', - '_lib', - 'dll_', - '_dll' - ]) - -machine_map = { - 'i686' : 'i386', - 'i586' : 'i386', - 'i486' : 'i386', -} - -try: - uname = os.uname -except AttributeError: - # Windows doesn't have a uname() function. We could use something like - # sys.platform as a fallback, but that's not really a "machine," so - # just leave it as None. - machine = None -else: - machine = uname()[4] - machine = machine_map.get(machine, machine) - -_exe = exe_suffix -_obj = obj_suffix -_shobj = shobj_suffix -shobj_ = shobj_prefix -_lib = lib_suffix -lib_ = lib_prefix -_dll = dll_suffix -dll_ = dll_prefix - - -if sys.platform == 'cygwin': - # On Cygwin, os.path.normcase() lies, so just report back the - # fact that the underlying Win32 OS is case-insensitive. - def case_sensitive_suffixes(s1, s2): - return 0 -else: - def case_sensitive_suffixes(s1, s2): - return (os.path.normcase(s1) != os.path.normcase(s2)) - - -file_expr = r"""File "[^"]*", line \d+, in [^\n]+ -""" - -# re.escape escapes too much. -def re_escape(str): - for c in '\\.[]()*+?': # Not an exhaustive list. - str = str.replace(c, '\\' + c) - return str - -# -# Helper functions that we use as a replacement to the default re.match -# when searching for special strings in stdout/stderr. -# -def search_re(out, l): - """ Search the regular expression 'l' in the output 'out' - and return the start index when successful. - """ - m = re.search(l, out) - if m: - return m.start() - - return None - -def search_re_in_list(out, l): - """ Search the regular expression 'l' in each line of - the given string list 'out' and return the line's index - when successful. - """ - for idx, o in enumerate(out): - m = re.search(l, o) - if m: - return idx - - return None - -# -# Helpers for handling Python version numbers -# -def python_version_string(): - return sys.version.split()[0] - -def python_minor_version_string(): - return sys.version[:3] - -def unsupported_python_version(version=sys.version_info): - return version < python_version_unsupported - -def deprecated_python_version(version=sys.version_info): - return version < python_version_deprecated - -if deprecated_python_version(): - msg = r""" -scons: warning: Support for pre-2.7.0 Python version (%s) is deprecated. - If this will cause hardship, contact scons-dev@scons.org -""" - - deprecated_python_expr = re_escape(msg % python_version_string()) + file_expr - del msg -else: - deprecated_python_expr = "" - - -def initialize_sconsflags(ignore_python_version): - """ - Add the --warn=no-python-version option to SCONSFLAGS for every - command so test scripts don't have to filter out Python version - deprecation warnings. - Same for --warn=no-visual-c-missing. - """ - save_sconsflags = os.environ.get('SCONSFLAGS') - if save_sconsflags: - sconsflags = [save_sconsflags] - else: - sconsflags = [] - if ignore_python_version and deprecated_python_version(): - sconsflags.append('--warn=no-python-version') - # Provide a way to suppress or provide alternate flags for - # TestSCons purposes by setting TESTSCONS_SCONSFLAGS. - # (The intended use case is to set it to null when running - # timing tests of earlier versions of SCons which don't - # support the --warn=no-visual-c-missing warning.) - visual_c = os.environ.get('TESTSCONS_SCONSFLAGS', - '--warn=no-visual-c-missing') - if visual_c: - sconsflags.append(visual_c) - os.environ['SCONSFLAGS'] = ' '.join(sconsflags) - return save_sconsflags - -def restore_sconsflags(sconsflags): - if sconsflags is None: - del os.environ['SCONSFLAGS'] - else: - os.environ['SCONSFLAGS'] = sconsflags - - -class TestSCons(TestCommon): - """Class for testing SCons. - - This provides a common place for initializing SCons tests, - eliminating the need to begin every test with the same repeated - initializations. - """ - - scons_version = SConsVersion - javac_is_gcj = False - - def __init__(self, **kw): - """Initialize an SCons testing object. - - If they're not overridden by keyword arguments, this - initializes the object with the following default values: - - program = 'scons' if it exists, - else 'scons.py' - interpreter = 'python' - match = match_exact - workdir = '' - - The workdir value means that, by default, a temporary workspace - directory is created for a TestSCons environment. In addition, - this method changes directory (chdir) to the workspace directory, - so an explicit "chdir = '.'" on all of the run() method calls - is not necessary. - """ - self.orig_cwd = os.getcwd() - self.external = os.environ.get('SCONS_EXTERNAL_TEST', 0) - - if not self.external: - try: - script_dir = os.environ['SCONS_SCRIPT_DIR'] - except KeyError: - pass - else: - os.chdir(script_dir) - if 'program' not in kw: - kw['program'] = os.environ.get('SCONS') - if not kw['program']: - if not self.external: - if os.path.exists('scons'): - kw['program'] = 'scons' - else: - kw['program'] = 'scons.py' - else: - kw['program'] = 'scons' - kw['interpreter'] = '' - elif not self.external and not os.path.isabs(kw['program']): - kw['program'] = os.path.join(self.orig_cwd, kw['program']) - if 'interpreter' not in kw and not os.environ.get('SCONS_EXEC'): - kw['interpreter'] = [python, '-tt'] - if 'match' not in kw: - kw['match'] = match_exact - if 'workdir' not in kw: - kw['workdir'] = '' - - # Term causing test failures due to bogus readline init - # control character output on FC8 - # TERM can cause test failures due to control chars in prompts etc. - os.environ['TERM'] = 'dumb' - - self.ignore_python_version = kw.get('ignore_python_version', 1) - if kw.get('ignore_python_version', -1) != -1: - del kw['ignore_python_version'] - - TestCommon.__init__(self, **kw) - - if not self.external: - import SCons.Node.FS - if SCons.Node.FS.default_fs is None: - SCons.Node.FS.default_fs = SCons.Node.FS.FS() - - try: - self.fixture_dirs = (os.environ['FIXTURE_DIRS']).split(os.pathsep) - except KeyError: - pass - - def Environment(self, ENV=None, *args, **kw): - """ - Return a construction Environment that optionally overrides - the default external environment with the specified ENV. - """ - if not self.external: - import SCons.Environment - import SCons.Errors - if not ENV is None: - kw['ENV'] = ENV - try: - return SCons.Environment.Environment(*args, **kw) - except (SCons.Errors.UserError, SCons.Errors.InternalError): - return None - - return None - - def detect(self, var, prog=None, ENV=None, norm=None): - """ - Detect a program named 'prog' by first checking the construction - variable named 'var' and finally searching the path used by - SCons. If either method fails to detect the program, then false - is returned, otherwise the full path to prog is returned. If - prog is None, then the value of the environment variable will be - used as prog. - """ - env = self.Environment(ENV) - if env: - v = env.subst('$'+var) - if not v: - return None - if prog is None: - prog = v - if v != prog: - return None - result = env.WhereIs(prog) - if norm and os.sep != '/': - result = result.replace(os.sep, '/') - return result - - return self.where_is(prog) - - def detect_tool(self, tool, prog=None, ENV=None): - """ - Given a tool (i.e., tool specification that would be passed - to the "tools=" parameter of Environment()) and a program that - corresponds to that tool, return true if and only if we can find - that tool using Environment.Detect(). - - By default, prog is set to the value passed into the tools parameter. - """ - - if not prog: - prog = tool - env = self.Environment(ENV, tools=[tool]) - if env is None: - return None - return env.Detect([prog]) - - def where_is(self, prog, path=None): - """ - Given a program, search for it in the specified external PATH, - or in the actual external PATH if none is specified. - """ - if path is None: - path = os.environ['PATH'] - if self.external: - if isinstance(prog, str): - prog = [prog] - import stat - paths = path.split(os.pathsep) - for p in prog: - for d in paths: - f = os.path.join(d, p) - if os.path.isfile(f): - try: - st = os.stat(f) - except OSError: - # os.stat() raises OSError, not IOError if the file - # doesn't exist, so in this case we let IOError get - # raised so as to not mask possibly serious disk or - # network issues. - continue - if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: - return os.path.normpath(f) - else: - import SCons.Environment - env = SCons.Environment.Environment() - return env.WhereIs(prog, path) - - return None - - def wrap_stdout(self, build_str = "", read_str = "", error = 0, cleaning = 0): - """Wraps standard output string(s) in the normal - "Reading ... done" and "Building ... done" strings - """ - cap,lc = [ ('Build','build'), - ('Clean','clean') ][cleaning] - if error: - term = "scons: %sing terminated because of errors.\n" % lc - else: - term = "scons: done %sing targets.\n" % lc - return "scons: Reading SConscript files ...\n" + \ - read_str + \ - "scons: done reading SConscript files.\n" + \ - "scons: %sing targets ...\n" % cap + \ - build_str + \ - term - - def run(self, *args, **kw): - """ - Set up SCONSFLAGS for every command so test scripts don't need - to worry about unexpected warnings in their output. - """ - sconsflags = initialize_sconsflags(self.ignore_python_version) - try: - TestCommon.run(self, *args, **kw) - finally: - restore_sconsflags(sconsflags) - -# Modifying the options should work and ought to be simpler, but this -# class is used for more than just running 'scons' itself. If there's -# an automated way of determining whether it's running 'scons' or -# something else, this code should be resurected. -# options = kw.get('options') -# if options: -# options = [options] -# else: -# options = [] -# if self.ignore_python_version and deprecated_python_version(): -# options.append('--warn=no-python-version') -# # Provide a way to suppress or provide alternate flags for -# # TestSCons purposes by setting TESTSCONS_SCONSFLAGS. -# # (The intended use case is to set it to null when running -# # timing tests of earlier versions of SCons which don't -# # support the --warn=no-visual-c-missing warning.) -# visual_c = os.environ.get('TESTSCONS_SCONSFLAGS', -# '--warn=no-visual-c-missing') -# if visual_c: -# options.append(visual_c) -# kw['options'] = ' '.join(options) -# TestCommon.run(self, *args, **kw) - - def up_to_date(self, arguments = '.', read_str = "", **kw): - s = "" - for arg in arguments.split(): - s = s + "scons: `%s' is up to date.\n" % arg - kw['arguments'] = arguments - stdout = self.wrap_stdout(read_str = read_str, build_str = s) - # Append '.*' so that timing output that comes after the - # up-to-date output is okay. - kw['stdout'] = re.escape(stdout) + '.*' - kw['match'] = self.match_re_dotall - self.run(**kw) - - def not_up_to_date(self, arguments = '.', **kw): - """Asserts that none of the targets listed in arguments is - up to date, but does not make any assumptions on other targets. - This function is most useful in conjunction with the -n option. - """ - s = "" - for arg in arguments.split(): - s = s + "(?!scons: `%s' is up to date.)" % re.escape(arg) - s = '('+s+'[^\n]*\n)*' - kw['arguments'] = arguments - stdout = re.escape(self.wrap_stdout(build_str='ARGUMENTSGOHERE')) - kw['stdout'] = stdout.replace('ARGUMENTSGOHERE', s) - kw['match'] = self.match_re_dotall - self.run(**kw) - - def option_not_yet_implemented(self, option, arguments=None, **kw): - """ - Verifies expected behavior for options that are not yet implemented: - a warning message, and exit status 1. - """ - msg = "Warning: the %s option is not yet implemented\n" % option - kw['stderr'] = msg - if arguments: - # If it's a long option and the argument string begins with '=', - # it's of the form --foo=bar and needs no separating space. - if option[:2] == '--' and arguments[0] == '=': - kw['arguments'] = option + arguments - else: - kw['arguments'] = option + ' ' + arguments - return self.run(**kw) - - def deprecated_wrap(self, msg): - """ - Calculate the pattern that matches a deprecation warning. - """ - return '\nscons: warning: ' + re_escape(msg) + '\n' + file_expr - - def deprecated_fatal(self, warn, msg): - """ - Determines if the warning has turned into a fatal error. If so, - passes the test, as any remaining runs are now moot. - - This method expects a SConscript to be present that will causes - the warning. The method writes a SConstruct that calls the - SConsscript and looks to see what type of result occurs. - - The pattern that matches the warning is returned. - - TODO: Actually detect that it's now an error. We don't have any - cases yet, so there's no way to test it. - """ - self.write('SConstruct', """if True: - WARN = ARGUMENTS.get('WARN') - if WARN: SetOption('warn', WARN) - SConscript('SConscript') - """) - - def err_out(): - # TODO calculate stderr for fatal error - return re_escape('put something here') - - # no option, should get one of nothing, warning, or error - warning = self.deprecated_wrap(msg) - self.run(arguments = '.', stderr = None) - stderr = self.stderr() - if stderr: - # most common case done first - if match_re_dotall(stderr, warning): - # expected output - pass - elif match_re_dotall(stderr, err_out()): - # now a fatal error; skip the rest of the tests - self.pass_test() - else: - # test failed; have to do this by hand... - print(self.banner('STDOUT ')) - print(self.stdout()) - print(self.diff(warning, stderr, 'STDERR ')) - self.fail_test() - - return warning - - def deprecated_warning(self, warn, msg): - """ - Verifies the expected behavior occurs for deprecation warnings. - This method expects a SConscript to be present that will causes - the warning. The method writes a SConstruct and exercises various - combinations of command-line options and SetOption parameters to - validate that it performs correctly. - - The pattern that matches the warning is returned. - """ - warning = self.deprecated_fatal(warn, msg) - - def RunPair(option, expected): - # run the same test with the option on the command line and - # then with the option passed via SetOption(). - self.run(options = '--warn=' + option, - arguments = '.', - stderr = expected, - match = match_re_dotall) - self.run(options = 'WARN=' + option, - arguments = '.', - stderr = expected, - match = match_re_dotall) - - # all warnings off, should get no output - RunPair('no-deprecated', '') - - # warning enabled, should get expected output - RunPair(warn, warning) - - # warning disabled, should get either nothing or mandatory message - expect = """()|(Can not disable mandataory warning: 'no-%s'\n\n%s)""" % (warn, warning) - RunPair('no-' + warn, expect) - - return warning - - def diff_substr(self, expect, actual, prelen=20, postlen=40): - i = 0 - for x, y in zip(expect, actual): - if x != y: - return "Actual did not match expect at char %d:\n" \ - " Expect: %s\n" \ - " Actual: %s\n" \ - % (i, repr(expect[i-prelen:i+postlen]), - repr(actual[i-prelen:i+postlen])) - i = i + 1 - return "Actual matched the expected output???" - - def python_file_line(self, file, line): - """ - Returns a Python error line for output comparisons. - - The exec of the traceback line gives us the correct format for - this version of Python. - - File "", line 1, - - We stick the requested file name and line number in the right - places, abstracting out the version difference. - """ - # This routine used to use traceback to get the proper format - # that doesn't work well with py3. And the format of the - # traceback seems to be stable, so let's just format - # an appropriate string - # - #exec('import traceback; x = traceback.format_stack()[-1]') - # import traceback - # x = traceback.format_stack() - # x = # XXX: .lstrip() - # x = x.replace('', file) - # x = x.replace('line 1,', 'line %s,' % line) - # x="\n".join(x) - x='File "%s", line %s, in \n'%(file,line) - return x - - def normalize_ps(self, s): - s = re.sub(r'(Creation|Mod)Date: .*', - r'\1Date XXXX', s) - s = re.sub(r'%DVIPSSource:\s+TeX output\s.*', - r'%DVIPSSource: TeX output XXXX', s) - s = re.sub(r'/(BaseFont|FontName) /[A-Z0-9]{6}', - r'/\1 /XXXXXX', s) - s = re.sub(r'BeginFont: [A-Z0-9]{6}', - r'BeginFont: XXXXXX', s) - - return s - - @staticmethod - def to_bytes_re_sub(pattern, repl, str, count=0, flags=0): - """ - Wrapper around re.sub to change pattern and repl to bytes to work with - both python 2 & 3 - """ - pattern = to_bytes(pattern) - repl = to_bytes(repl) - return re.sub(pattern, repl, str, count, flags) - - def normalize_pdf(self, s): - s = self.to_bytes_re_sub(r'/(Creation|Mod)Date \(D:[^)]*\)', - r'/\1Date (D:XXXX)', s) - s = self.to_bytes_re_sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]', - r'/ID [ ]', s) - s = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', - r'/\1 /XXXXXX', s) - s = self.to_bytes_re_sub(r'/Length \d+ *\n/Filter /FlateDecode\n', - r'/Length XXXX\n/Filter /FlateDecode\n', s) - - try: - import zlib - except ImportError: - pass - else: - begin_marker = to_bytes('/FlateDecode\n>>\nstream\n') - end_marker = to_bytes('endstream\nendobj') - - encoded = [] - b = s.find(begin_marker, 0) - while b != -1: - b = b + len(begin_marker) - e = s.find(end_marker, b) - encoded.append((b, e)) - b = s.find(begin_marker, e + len(end_marker)) - - x = 0 - r = [] - for b, e in encoded: - r.append(s[x:b]) - d = zlib.decompress(s[b:e]) - d = self.to_bytes_re_sub(r'%%CreationDate: [^\n]*\n', - r'%%CreationDate: 1970 Jan 01 00:00:00\n', d) - d = self.to_bytes_re_sub(r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d', - r'%DVIPSSource: TeX output 1970.01.01:0000', d) - d = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', - r'/\1 /XXXXXX', d) - r.append(d) - x = e - r.append(s[x:]) - s = to_bytes('').join(r) - - return s - - def paths(self,patterns): - import glob - result = [] - for p in patterns: - result.extend(sorted(glob.glob(p))) - return result - - def unlink_sconsignfile(self,name='.sconsign.dblite'): - """ - Delete sconsign file. - Note on python it seems to append .p3 to the file name so we take care of that - Parameters - ---------- - name - expected name of sconsign file - - Returns - ------- - None - """ - if sys.version_info[0] == 3: - name += '.p3' - self.unlink(name) - - def java_ENV(self, version=None): - """ - Initialize with a default external environment that uses a local - Java SDK in preference to whatever's found in the default PATH. - """ - if not self.external: - try: - return self._java_env[version]['ENV'] - except AttributeError: - self._java_env = {} - except KeyError: - pass - - import SCons.Environment - env = SCons.Environment.Environment() - self._java_env[version] = env - - - if version: - patterns = [ - '/usr/java/jdk%s*/bin' % version, - '/usr/lib/jvm/*-%s*/bin' % version, - '/usr/local/j2sdk%s*/bin' % version, - ] - java_path = self.paths(patterns) + [env['ENV']['PATH']] - else: - patterns = [ - '/usr/java/latest/bin', - '/usr/lib/jvm/*/bin', - '/usr/local/j2sdk*/bin', - ] - java_path = self.paths(patterns) + [env['ENV']['PATH']] - - env['ENV']['PATH'] = os.pathsep.join(java_path) - return env['ENV'] - - return None - - def java_where_includes(self,version=None): - """ - Return java include paths compiling java jni code - """ - import sys - - result = [] - if sys.platform[:6] == 'darwin': - java_home = self.java_where_java_home(version) - jni_path = os.path.join(java_home,'include','jni.h') - if os.path.exists(jni_path): - result.append(os.path.dirname(jni_path)) - - if not version: - version='' - jni_dirs = ['/System/Library/Frameworks/JavaVM.framework/Headers/jni.h', - '/usr/lib/jvm/default-java/include/jni.h', - '/usr/lib/jvm/java-*-oracle/include/jni.h'] - else: - jni_dirs = ['/System/Library/Frameworks/JavaVM.framework/Versions/%s*/Headers/jni.h'%version] - jni_dirs.extend(['/usr/lib/jvm/java-*-sun-%s*/include/jni.h'%version, - '/usr/lib/jvm/java-%s*-openjdk*/include/jni.h'%version, - '/usr/java/jdk%s*/include/jni.h'%version]) - dirs = self.paths(jni_dirs) - if not dirs: - return None - d=os.path.dirname(self.paths(jni_dirs)[0]) - result.append(d) - - if sys.platform == 'win32': - result.append(os.path.join(d,'win32')) - elif sys.platform.startswith('linux'): - result.append(os.path.join(d,'linux')) - return result - - def java_where_java_home(self, version=None): - if sys.platform[:6] == 'darwin': - # osx 10.11, 10.12 - home_tool = '/usr/libexec/java_home' - java_home = False - if os.path.exists(home_tool): - java_home = subprocess.check_output(home_tool).strip() - java_home = java_home.decode() - - if version is None: - if java_home: - return java_home - else: - homes = ['/System/Library/Frameworks/JavaVM.framework/Home', - # osx 10.10 - '/System/Library/Frameworks/JavaVM.framework/Versions/Current/Home'] - for home in homes: - if os.path.exists(home): - return home - - else: - if java_home.find('jdk%s'%version) != -1: - return java_home - else: - home = '/System/Library/Frameworks/JavaVM.framework/Versions/%s/Home' % version - if not os.path.exists(home): - # This works on OSX 10.10 - home = '/System/Library/Frameworks/JavaVM.framework/Versions/Current/' - else: - jar = self.java_where_jar(version) - home = os.path.normpath('%s/..'%jar) - if os.path.isdir(home): - return home - print("Could not determine JAVA_HOME: %s is not a directory" % home) - self.fail_test() - - def java_mac_check(self, where_java_bin, java_bin_name): - # on Mac there is a place holder java installed to start the java install process - # so we need to check the output in this case, more info here: - # http://anas.pk/2015/09/02/solution-no-java-runtime-present-mac-yosemite/ - sp = subprocess.Popen([where_java_bin, "-version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = sp.communicate() - sp.wait() - if("No Java runtime" in str(stderr)): - self.skip_test("Could not find Java " + java_bin_name + ", skipping test(s).\n") - - def java_where_jar(self, version=None): - ENV = self.java_ENV(version) - if self.detect_tool('jar', ENV=ENV): - where_jar = self.detect('JAR', 'jar', ENV=ENV) - else: - where_jar = self.where_is('jar', ENV['PATH']) - if not where_jar: - self.skip_test("Could not find Java jar, skipping test(s).\n") - elif sys.platform == "darwin": - self.java_mac_check(where_jar, 'jar') - - return where_jar - - def java_where_java(self, version=None): - """ - Return a path to the java executable. - """ - ENV = self.java_ENV(version) - where_java = self.where_is('java', ENV['PATH']) - - if not where_java: - self.skip_test("Could not find Java java, skipping test(s).\n") - elif sys.platform == "darwin": - self.java_mac_check(where_java, 'java') - - return where_java - - def java_where_javac(self, version=None): - """ - Return a path to the javac compiler. - """ - ENV = self.java_ENV(version) - if self.detect_tool('javac'): - where_javac = self.detect('JAVAC', 'javac', ENV=ENV) - else: - where_javac = self.where_is('javac', ENV['PATH']) - if not where_javac: - self.skip_test("Could not find Java javac, skipping test(s).\n") - elif sys.platform == "darwin": - self.java_mac_check(where_javac, 'javac') - - self.run(program = where_javac, - arguments = '-version', - stderr=None, - status=None) - if version: - if self.stderr().find('javac %s' % version) == -1: - fmt = "Could not find javac for Java version %s, skipping test(s).\n" - self.skip_test(fmt % version) - else: - m = re.search(r'javac (\d\.\d)', self.stderr()) - if m: - version = m.group(1) - self.javac_is_gcj = False - elif self.stderr().find('gcj'): - version='1.2' - self.javac_is_gcj = True - else: - version = None - self.javac_is_gcj = False - return where_javac, version - - def java_where_javah(self, version=None): - ENV = self.java_ENV(version) - if self.detect_tool('javah'): - where_javah = self.detect('JAVAH', 'javah', ENV=ENV) - else: - where_javah = self.where_is('javah', ENV['PATH']) - if not where_javah: - self.skip_test("Could not find Java javah, skipping test(s).\n") - return where_javah - - def java_where_rmic(self, version=None): - ENV = self.java_ENV(version) - if self.detect_tool('rmic'): - where_rmic = self.detect('RMIC', 'rmic', ENV=ENV) - else: - where_rmic = self.where_is('rmic', ENV['PATH']) - if not where_rmic: - self.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n") - return where_rmic - - - def java_get_class_files(self, dir): - result = [] - for dirpath, dirnames, filenames in os.walk(dir): - for fname in filenames: - if fname.endswith('.class'): - result.append(os.path.join(dirpath, fname)) - return sorted(result) - - - def Qt_dummy_installation(self, dir='qt'): - # create a dummy qt installation - - self.subdir( dir, [dir, 'bin'], [dir, 'include'], [dir, 'lib'] ) - - self.write([dir, 'bin', 'mymoc.py'], """\ -import getopt -import sys -import re -# -w and -z are fake options used in test/QT/QTFLAGS.py -cmd_opts, args = getopt.getopt(sys.argv[1:], 'io:wz', []) -output = None -impl = 0 -opt_string = '' -for opt, arg in cmd_opts: - if opt == '-o': output = open(arg, 'w') - elif opt == '-i': impl = 1 - else: opt_string = opt_string + ' ' + opt -output.write("/* mymoc.py%s */\\n" % opt_string) -for a in args: - contents = open(a, 'r').read() - a = a.replace('\\\\', '\\\\\\\\') - subst = r'{ my_qt_symbol( "' + a + '\\\\n" ); }' - if impl: - contents = re.sub( r'#include.*', '', contents ) - output.write(contents.replace('Q_OBJECT', subst)) -output.close() -sys.exit(0) -""") - - self.write([dir, 'bin', 'myuic.py'], """\ -import os.path -import re -import sys -output_arg = 0 -impl_arg = 0 -impl = None -source = None -opt_string = '' -for arg in sys.argv[1:]: - if output_arg: - output = open(arg, 'w') - output_arg = 0 - elif impl_arg: - impl = arg - impl_arg = 0 - elif arg == "-o": - output_arg = 1 - elif arg == "-impl": - impl_arg = 1 - elif arg[0:1] == "-": - opt_string = opt_string + ' ' + arg - else: - if source: - sys.exit(1) - source = open(arg, 'r') - sourceFile = arg -output.write("/* myuic.py%s */\\n" % opt_string) -if impl: - output.write( '#include "' + impl + '"\\n' ) - includes = re.findall('(.*?)', source.read()) - for incFile in includes: - # this is valid for ui.h files, at least - if os.path.exists(incFile): - output.write('#include "' + incFile + '"\\n') -else: - output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" ) -output.close() -sys.exit(0) -""" ) - - self.write([dir, 'include', 'my_qobject.h'], r""" -#define Q_OBJECT ; -void my_qt_symbol(const char *arg); -""") - - self.write([dir, 'lib', 'my_qobject.cpp'], r""" -#include "../include/my_qobject.h" -#include -void my_qt_symbol(const char *arg) { - fputs( arg, stdout ); -} -""") - - self.write([dir, 'lib', 'SConstruct'], r""" -env = Environment() -import sys -if sys.platform == 'win32': - env.StaticLibrary( 'myqt', 'my_qobject.cpp' ) -else: - env.SharedLibrary( 'myqt', 'my_qobject.cpp' ) -""") - - self.run(chdir = self.workpath(dir, 'lib'), - arguments = '.', - stderr = noisy_ar, - match = self.match_re_dotall) - - self.QT = self.workpath(dir) - self.QT_LIB = 'myqt' - self.QT_MOC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'mymoc.py')) - self.QT_UIC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'myuic.py')) - self.QT_LIB_DIR = self.workpath(dir, 'lib') - - def Qt_create_SConstruct(self, place): - if isinstance(place, list): - place = test.workpath(*place) - self.write(place, """\ -if ARGUMENTS.get('noqtdir', 0): QTDIR=None -else: QTDIR=r'%s' -env = Environment(QTDIR = QTDIR, - QT_LIB = r'%s', - QT_MOC = r'%s', - QT_UIC = r'%s', - tools=['default','qt']) -dup = 1 -if ARGUMENTS.get('variant_dir', 0): - if ARGUMENTS.get('chdir', 0): - SConscriptChdir(1) - else: - SConscriptChdir(0) - dup=int(ARGUMENTS.get('dup', 1)) - if dup == 0: - builddir = 'build_dup0' - env['QT_DEBUG'] = 1 - else: - builddir = 'build' - VariantDir(builddir, '.', duplicate=dup) - print(builddir, dup) - sconscript = Dir(builddir).File('SConscript') -else: - sconscript = File('SConscript') -Export("env dup") -SConscript( sconscript ) -""" % (self.QT, self.QT_LIB, self.QT_MOC, self.QT_UIC)) - - - NCR = 0 # non-cached rebuild - CR = 1 # cached rebuild (up to date) - NCF = 2 # non-cached build failure - CF = 3 # cached build failure - - if sys.platform == 'win32': - Configure_lib = 'msvcrt' - else: - Configure_lib = 'm' - - # to use cygwin compilers on cmd.exe -> uncomment following line - #Configure_lib = 'm' - - def coverage_run(self): - """ Check if the the tests are being run under coverage. - """ - return 'COVERAGE_PROCESS_START' in os.environ or 'COVERAGE_FILE' in os.environ - - def skip_if_not_msvc(self, check_platform=True): - """ Check whether we are on a Windows platform and skip the - test if not. This check can be omitted by setting - check_platform to False. - Then, for a win32 platform, additionally check - whether we have a MSVC toolchain installed - in the system, and skip the test if none can be - found (=MinGW is the only compiler available). - """ - if check_platform: - if sys.platform != 'win32': - msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform - self.skip_test(msg) - return - - try: - import SCons.Tool.MSCommon as msc - if not msc.msvc_exists(): - msg = "No MSVC toolchain found...skipping test\n" - self.skip_test(msg) - except: - pass - - def checkLogAndStdout(self, checks, results, cached, - logfile, sconf_dir, sconstruct, - doCheckLog=True, doCheckStdout=True): - """ - Used to verify the expected output from using Configure() - via the contents of one or both of stdout or config.log file. - The checks, results, cached parameters all are zipped together - for use in comparing results. - - TODO: Perhaps a better API makes sense? - - Parameters - ---------- - checks : The Configure checks being run - - results : The expected results for each check - - cached : If the corresponding check is expected to be cached - - logfile : Name of the config log - - sconf_dir : Name of the sconf dir - - sconstruct : SConstruct file name - - doCheckLog : check specified log file, defaults to true - - doCheckStdout : Check stdout, defaults to true - - Returns - ------- - - """ - - class NoMatch(Exception): - def __init__(self, p): - self.pos = p - - def matchPart(log, logfile, lastEnd, NoMatch=NoMatch): - """ - Match part of the logfile - """ - m = re.match(log, logfile[lastEnd:]) - if not m: - raise NoMatch(lastEnd) - return m.end() + lastEnd - - try: - - # Build regexp for a character which is not - # a linesep, and in the case of CR/LF - # build it with both CR and CR/LF - # TODO: Not sure why this is a good idea. A static string - # could do the same since we only have two variations - # to do with? - # ls = os.linesep - # nols = "(" - # for i in range(len(ls)): - # nols = nols + "(" - # for j in range(i): - # nols = nols + ls[j] - # nols = nols + "[^" + ls[i] + "])" - # if i < len(ls)-1: - # nols = nols + "|" - # nols = nols + ")" - # - # Replaced above logic with \n as we're reading the file - # using non-binary read. Python will translate \r\n -> \n - # For us. - ls = '\n' - nols = '([^\n])' - lastEnd = 0 - - # Read the whole logfile - logfile = self.read(self.workpath(logfile), mode='r') - - # Some debug code to keep around.. - # sys.stderr.write("LOGFILE[%s]:%s"%(type(logfile),logfile)) - - if (doCheckLog and - logfile.find("scons: warning: The stored build information has an unexpected class.") >= 0): - self.fail_test() - - sconf_dir = sconf_dir - sconstruct = sconstruct - - log = r'file\ \S*%s\,line \d+:' % re.escape(sconstruct) + ls - if doCheckLog: - lastEnd = matchPart(log, logfile, lastEnd) - - log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls - if doCheckLog: - lastEnd = matchPart(log, logfile, lastEnd) - - rdstr = "" - cnt = 0 - for check,result,cache_desc in zip(checks, results, cached): - log = re.escape("scons: Configure: " + check) + ls - - if doCheckLog: - lastEnd = matchPart(log, logfile, lastEnd) - - log = "" - result_cached = 1 - for bld_desc in cache_desc: # each TryXXX - for ext, flag in bld_desc: # each file in TryBuild - file = os.path.join(sconf_dir,"conftest_%d%s" % (cnt, ext)) - if flag == self.NCR: - # NCR = Non Cached Rebuild - # rebuild will pass - if ext in ['.c', '.cpp']: - log=log + re.escape(file + " <-") + ls - log=log + r"( \|" + nols + "*" + ls + ")+?" - else: - log=log + "(" + nols + "*" + ls +")*?" - result_cached = 0 - if flag == self.CR: - # CR = cached rebuild (up to date)s - # up to date - log=log + \ - re.escape("scons: Configure: \"%s\" is up to date." - % file) + ls - log=log+re.escape("scons: Configure: The original builder " - "output was:") + ls - log=log+r"( \|.*"+ls+")+" - if flag == self.NCF: - # non-cached rebuild failure - log=log + "(" + nols + "*" + ls + ")*?" - result_cached = 0 - if flag == self.CF: - # cached rebuild failure - log=log + \ - re.escape("scons: Configure: Building \"%s\" failed " - "in a previous run and all its sources are" - " up to date." % file) + ls - log=log+re.escape("scons: Configure: The original builder " - "output was:") + ls - log=log+r"( \|.*"+ls+")+" - cnt = cnt + 1 - if result_cached: - result = "(cached) " + result - rdstr = rdstr + re.escape(check) + re.escape(result) + "\n" - log=log + re.escape("scons: Configure: " + result) + ls + ls - - if doCheckLog: - lastEnd = matchPart(log, logfile, lastEnd) - - log = "" - if doCheckLog: lastEnd = matchPart(ls, logfile, lastEnd) - if doCheckLog and lastEnd != len(logfile): - raise NoMatch(lastEnd) - - except NoMatch as m: - print("Cannot match log file against log regexp.") - print("log file: ") - print("------------------------------------------------------") - print(logfile[m.pos:]) - print("------------------------------------------------------") - print("log regexp: ") - print("------------------------------------------------------") - print(log) - print("------------------------------------------------------") - self.fail_test() - - if doCheckStdout: - exp_stdout = self.wrap_stdout(".*", rdstr) - if not self.match_re_dotall(self.stdout(), exp_stdout): - print("Unexpected stdout: ") - print("-----------------------------------------------------") - print(repr(self.stdout())) - print("-----------------------------------------------------") - print(repr(exp_stdout)) - print("-----------------------------------------------------") - self.fail_test() - - def get_python_version(self): - """ - Returns the Python version (just so everyone doesn't have to - hand-code slicing the right number of characters). - """ - # see also sys.prefix documentation - return python_minor_version_string() - - def get_platform_python_info(self): - """ - Returns a path to a Python executable suitable for testing on - this platform and its associated include path, library path, - and library name. - """ - python = self.where_is('python') - if not python: - self.skip_test('Can not find installed "python", skipping test.\n') - - self.run(program = python, stdin = """\ -import os, sys -try: - if sys.platform == 'win32': - py_ver = 'python%d%d' % sys.version_info[:2] - else: - py_ver = 'python%d.%d' % sys.version_info[:2] -except AttributeError: - py_ver = 'python' + sys.version[:3] -# print include and lib path -try: - import distutils.sysconfig - exec_prefix = distutils.sysconfig.EXEC_PREFIX - print(distutils.sysconfig.get_python_inc()) - lib_path = os.path.join(exec_prefix, 'libs') - if not os.path.exists(lib_path): - lib_path = os.path.join(exec_prefix, 'lib') - print(lib_path) -except: - print(os.path.join(sys.prefix, 'include', py_ver)) - print(os.path.join(sys.prefix, 'lib', py_ver, 'config')) -print(py_ver) -""") - - return [python] + self.stdout().strip().split('\n') - - def start(self, *args, **kw): - """ - Starts SCons in the test environment. - - This method exists to tell Test{Cmd,Common} that we're going to - use standard input without forcing every .start() call in the - individual tests to do so explicitly. - """ - if 'stdin' not in kw: - kw['stdin'] = True - sconsflags = initialize_sconsflags(self.ignore_python_version) - try: - p = TestCommon.start(self, *args, **kw) - finally: - restore_sconsflags(sconsflags) - return p - - def wait_for(self, fname, timeout=20.0, popen=None): - """ - Waits for the specified file name to exist. - """ - waited = 0.0 - while not os.path.exists(fname): - if timeout and waited >= timeout: - sys.stderr.write('timed out waiting for %s to exist\n' % fname) - if popen: - popen.stdin.close() - popen.stdin = None - self.status = 1 - self.finish(popen) - stdout = self.stdout() - if stdout: - sys.stdout.write(self.banner('STDOUT ') + '\n') - sys.stdout.write(stdout) - stderr = self.stderr() - if stderr: - sys.stderr.write(self.banner('STDERR ') + '\n') - sys.stderr.write(stderr) - self.fail_test() - time.sleep(1.0) - waited = waited + 1.0 - - def get_alt_cpp_suffix(self): - """ - Many CXX tests have this same logic. - They all needed to determine if the current os supports - files with .C and .c as different files or not - in which case they are instructed to use .cpp instead of .C - """ - if not case_sensitive_suffixes('.c','.C'): - alt_cpp_suffix = '.cpp' - else: - alt_cpp_suffix = '.C' - return alt_cpp_suffix - - def platform_has_symlink(self): - if not hasattr(os, 'symlink') or sys.platform == 'win32': - return False - else: - return True - - -class Stat: - def __init__(self, name, units, expression, convert=None): - if convert is None: - convert = lambda x: x - self.name = name - self.units = units - self.expression = re.compile(expression) - self.convert = convert - -StatList = [ - Stat('memory-initial', 'kbytes', - r'Memory before reading SConscript files:\s+(\d+)', - convert=lambda s: int(s) // 1024), - Stat('memory-prebuild', 'kbytes', - r'Memory before building targets:\s+(\d+)', - convert=lambda s: int(s) // 1024), - Stat('memory-final', 'kbytes', - r'Memory after building targets:\s+(\d+)', - convert=lambda s: int(s) // 1024), - - Stat('time-sconscript', 'seconds', - r'Total SConscript file execution time:\s+([\d.]+) seconds'), - Stat('time-scons', 'seconds', - r'Total SCons execution time:\s+([\d.]+) seconds'), - Stat('time-commands', 'seconds', - r'Total command execution time:\s+([\d.]+) seconds'), - Stat('time-total', 'seconds', - r'Total build time:\s+([\d.]+) seconds'), -] - - -class TimeSCons(TestSCons): - """Class for timing SCons.""" - def __init__(self, *args, **kw): - """ - In addition to normal TestSCons.TestSCons intialization, - this enables verbose mode (which causes the command lines to - be displayed in the output) and copies the contents of the - directory containing the executing script to the temporary - working directory. - """ - self.variables = kw.get('variables') - default_calibrate_variables = [] - if self.variables is not None: - for variable, value in self.variables.items(): - value = os.environ.get(variable, value) - try: - value = int(value) - except ValueError: - try: - value = float(value) - except ValueError: - pass - else: - default_calibrate_variables.append(variable) - else: - default_calibrate_variables.append(variable) - self.variables[variable] = value - del kw['variables'] - calibrate_keyword_arg = kw.get('calibrate') - if calibrate_keyword_arg is None: - self.calibrate_variables = default_calibrate_variables - else: - self.calibrate_variables = calibrate_keyword_arg - del kw['calibrate'] - - self.calibrate = os.environ.get('TIMESCONS_CALIBRATE', '0') != '0' - - if 'verbose' not in kw and not self.calibrate: - kw['verbose'] = True - - TestSCons.__init__(self, *args, **kw) - - # TODO(sgk): better way to get the script dir than sys.argv[0] - self.test_dir = os.path.dirname(sys.argv[0]) - test_name = os.path.basename(self.test_dir) - - if not os.path.isabs(self.test_dir): - self.test_dir = os.path.join(self.orig_cwd, self.test_dir) - self.copy_timing_configuration(self.test_dir, self.workpath()) - - def main(self, *args, **kw): - """ - The main entry point for standard execution of timings. - - This method run SCons three times: - - Once with the --help option, to have it exit after just reading - the configuration. - - Once as a full build of all targets. - - Once again as a (presumably) null or up-to-date build of - all targets. - - The elapsed time to execute each build is printed after - it has finished. - """ - if 'options' not in kw and self.variables: - options = [] - for variable, value in self.variables.items(): - options.append('%s=%s' % (variable, value)) - kw['options'] = ' '.join(options) - if self.calibrate: - self.calibration(*args, **kw) - else: - self.uptime() - self.startup(*args, **kw) - self.full(*args, **kw) - self.null(*args, **kw) - - def trace(self, graph, name, value, units, sort=None): - fmt = "TRACE: graph=%s name=%s value=%s units=%s" - line = fmt % (graph, name, value, units) - if sort is not None: - line = line + (' sort=%s' % sort) - line = line + '\n' - sys.stdout.write(line) - sys.stdout.flush() - - def report_traces(self, trace, stats): - self.trace('TimeSCons-elapsed', - trace, - self.elapsed_time(), - "seconds", - sort=0) - for name, args in stats.items(): - self.trace(name, trace, **args) - - def uptime(self): - try: - fp = open('/proc/loadavg') - except EnvironmentError: - pass - else: - avg1, avg5, avg15 = fp.readline().split(" ")[:3] - fp.close() - self.trace('load-average', 'average1', avg1, 'processes') - self.trace('load-average', 'average5', avg5, 'processes') - self.trace('load-average', 'average15', avg15, 'processes') - - def collect_stats(self, input): - result = {} - for stat in StatList: - m = stat.expression.search(input) - if m: - value = stat.convert(m.group(1)) - # The dict keys match the keyword= arguments - # of the trace() method above so they can be - # applied directly to that call. - result[stat.name] = {'value':value, 'units':stat.units} - return result - - def add_timing_options(self, kw, additional=None): - """ - Add the necessary timings options to the kw['options'] value. - """ - options = kw.get('options', '') - if additional is not None: - options += additional - kw['options'] = options + ' --debug=memory,time' - - def startup(self, *args, **kw): - """ - Runs scons with the --help option. - - This serves as a way to isolate just the amount of startup time - spent reading up the configuration, since --help exits before any - "real work" is done. - """ - self.add_timing_options(kw, ' --help') - # Ignore the exit status. If the --help run dies, we just - # won't report any statistics for it, but we can still execute - # the full and null builds. - kw['status'] = None - self.run(*args, **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) - # Delete the time-commands, since no commands are ever - # executed on the help run and it is (or should be) always 0.0. - del stats['time-commands'] - self.report_traces('startup', stats) - - def full(self, *args, **kw): - """ - Runs a full build of SCons. - """ - self.add_timing_options(kw) - self.run(*args, **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) - self.report_traces('full', stats) - self.trace('full-memory', 'initial', **stats['memory-initial']) - self.trace('full-memory', 'prebuild', **stats['memory-prebuild']) - self.trace('full-memory', 'final', **stats['memory-final']) - - def calibration(self, *args, **kw): - """ - Runs a full build of SCons, but only reports calibration - information (the variable(s) that were set for this configuration, - and the elapsed time to run. - """ - self.add_timing_options(kw) - self.run(*args, **kw) - for variable in self.calibrate_variables: - value = self.variables[variable] - sys.stdout.write('VARIABLE: %s=%s\n' % (variable, value)) - sys.stdout.write('ELAPSED: %s\n' % self.elapsed_time()) - - def null(self, *args, **kw): - """ - Runs an up-to-date null build of SCons. - """ - # TODO(sgk): allow the caller to specify the target (argument) - # that must be up-to-date. - self.add_timing_options(kw) - self.up_to_date(arguments='.', **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) - # time-commands should always be 0.0 on a null build, because - # no commands should be executed. Remove it from the stats - # so we don't trace it, but only if it *is* 0 so that we'll - # get some indication if a supposedly-null build actually does - # build something. - if float(stats['time-commands']['value']) == 0.0: - del stats['time-commands'] - self.report_traces('null', stats) - self.trace('null-memory', 'initial', **stats['memory-initial']) - self.trace('null-memory', 'prebuild', **stats['memory-prebuild']) - self.trace('null-memory', 'final', **stats['memory-final']) - - def elapsed_time(self): - """ - Returns the elapsed time of the most recent command execution. - """ - return self.endTime - self.startTime - - def run(self, *args, **kw): - """ - Runs a single build command, capturing output in the specified file. - - Because this class is about timing SCons, we record the start - and end times of the elapsed execution, and also add the - --debug=memory and --debug=time options to have SCons report - its own memory and timing statistics. - """ - self.startTime = time.time() - try: - result = TestSCons.run(self, *args, **kw) - finally: - self.endTime = time.time() - return result - - def copy_timing_configuration(self, source_dir, dest_dir): - """ - Copies the timing configuration from the specified source_dir (the - directory in which the controlling script lives) to the specified - dest_dir (a temporary working directory). - - This ignores all files and directories that begin with the string - 'TimeSCons-', and all '.svn' subdirectories. - """ - for root, dirs, files in os.walk(source_dir): - if '.svn' in dirs: - dirs.remove('.svn') - dirs = [ d for d in dirs if not d.startswith('TimeSCons-') ] - files = [ f for f in files if not f.startswith('TimeSCons-') ] - for dirname in dirs: - source = os.path.join(root, dirname) - destination = source.replace(source_dir, dest_dir) - os.mkdir(destination) - if sys.platform != 'win32': - shutil.copystat(source, destination) - for filename in files: - source = os.path.join(root, filename) - destination = source.replace(source_dir, dest_dir) - shutil.copy2(source, destination) - - -# In some environments, $AR will generate a warning message to stderr -# if the library doesn't previously exist and is being created. One -# way to fix this is to tell AR to be quiet (sometimes the 'c' flag), -# but this is difficult to do in a platform-/implementation-specific -# method. Instead, we will use the following as a stderr match for -# tests that use AR so that we will view zero or more "ar: creating -# " messages to be successful executions of the test (see -# test/AR.py for sample usage). - -noisy_ar=r'(ar: creating( archive)? \S+\n?)*' - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestSConsMSVS.py b/QMTest/TestSConsMSVS.py deleted file mode 100644 index 1e879d9..0000000 --- a/QMTest/TestSConsMSVS.py +++ /dev/null @@ -1,1177 +0,0 @@ -""" -TestSConsMSVS.py: a testing framework for the SCons software construction -tool. - -A TestSConsMSVS environment object is created via the usual invocation: - - test = TestSConsMSVS() - -TestSConsMSVS is a subsclass of TestSCons, which is in turn a subclass -of TestCommon, which is in turn is a subclass of TestCmd), and hence -has available all of the methods and attributes from those classes, -as well as any overridden or additional methods or attributes defined -in this subclass. -""" - -# __COPYRIGHT__ - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os -import sys -import platform -import traceback -from xml.etree import ElementTree - -from TestSCons import * -from TestSCons import __all__ - - - -expected_dspfile_6_0 = '''\ -# Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=Test - Win32 Release -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "Test.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "Test.mak" CFG="Test - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "Test - Win32 Release" (based on "Win32 (x86) External Target") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" - -!IF "$(CFG)" == "Test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "" -# PROP BASE Intermediate_Dir "" -# PROP BASE Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP BASE Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP BASE Target_File "Test.exe" -# PROP BASE Bsc_Name "" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "" -# PROP Intermediate_Dir "" -# PROP Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP Target_File "Test.exe" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "Test - Win32 Release" - -!IF "$(CFG)" == "Test - Win32 Release" - -!ENDIF - -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="sdk.h" -# End Source File -# End Group -# Begin Group "Local Headers" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="test.h" -# End Source File -# End Group -# Begin Group "Other Files" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE="readme.txt" -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" -# Begin Source File - -SOURCE="test.rc" -# End Source File -# End Group -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;l;y;def;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="test.c" -# End Source File -# End Group -# Begin Source File - -SOURCE="" -# End Source File -# End Target -# End Project -''' - -expected_dswfile_6_0 = '''\ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "Test"="Test.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### -''' - -SConscript_contents_6_0 = """\ -env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='6.0',HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test.c'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.dsp', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - - - -expected_slnfile_7_0 = """\ -Microsoft Visual Studio Solution File, Format Version 7.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -EndProject -Global - -\tGlobalSection(SolutionConfiguration) = preSolution -\t\tConfigName.0 = Release -\tEndGlobalSection -\tGlobalSection(ProjectDependencies) = postSolution -\tEndGlobalSection -\tGlobalSection(ProjectConfiguration) = postSolution -\t\t.Release.ActiveCfg = Release|Win32 -\t\t.Release.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ExtensibilityGlobals) = postSolution -\tEndGlobalSection -\tGlobalSection(ExtensibilityAddIns) = postSolution -\tEndGlobalSection -EndGlobal -""" - -expected_vcprojfile_7_0 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -SConscript_contents_7_0 = """\ -env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='7.0',HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - - - -expected_slnfile_7_1 = """\ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -\tProjectSection(ProjectDependencies) = postProject -\tEndProjectSection -EndProject -Global - -\tGlobalSection(SolutionConfiguration) = preSolution -\t\tConfigName.0 = Release -\tEndGlobalSection -\tGlobalSection(ProjectDependencies) = postSolution -\tEndGlobalSection -\tGlobalSection(ProjectConfiguration) = postSolution -\t\t.Release.ActiveCfg = Release|Win32 -\t\t.Release.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ExtensibilityGlobals) = postSolution -\tEndGlobalSection -\tGlobalSection(ExtensibilityAddIns) = postSolution -\tEndGlobalSection -EndGlobal -""" - -expected_vcprojfile_7_1 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -SConscript_contents_7_1 = """\ -env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='7.1',HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - - - -expected_slnfile_8_0 = """\ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t.Release|Win32.ActiveCfg = Release|Win32 -\t\t.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_9_0 = """\ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t.Release|Win32.ActiveCfg = Release|Win32 -\t\t.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_10_0 = """\ -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_11_0 = """\ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 11 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_14_0 = """\ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_vcprojfile_8_0 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -expected_vcprojfile_9_0 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -expected_vcprojfile_10_0 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv100 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -expected_vcprojfile_11_0 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv110 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -expected_vcprojfile_14_0 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv140 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -SConscript_contents_8_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_9_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='9.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_10_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='10.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_11_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='11.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_14_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='14.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -class TestSConsMSVS(TestSCons): - """Subclass for testing MSVS-specific portions of SCons.""" - - def msvs_versions(self): - if not hasattr(self, '_msvs_versions'): - - # Determine the SCons version and the versions of the MSVS - # environments installed on the test machine. - # - # We do this by executing SCons with an SConstruct file - # (piped on stdin) that spits out Python assignments that - # we can just exec(). We construct the SCons.__"version"__ - # string in the input here so that the SCons build itself - # doesn't fill it in when packaging SCons. - input = """\ -import SCons -import SCons.Tool.MSCommon -print("self.scons_version =%%s"%%repr(SCons.__%s__)) -print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions())) -""" % 'version' - - self.run(arguments = '-n -q -Q -f -', stdin = input) - exec(self.stdout()) - - return self._msvs_versions - - def vcproj_sys_path(self, fname): - """ - """ - orig = 'sys.path = [ join(sys' - - enginepath = repr(os.path.join(self._cwd, '..', 'engine')) - replace = 'sys.path = [ %s, join(sys' % enginepath - - contents = self.read(fname, mode='r') - contents = contents.replace(orig, replace) - self.write(fname, contents) - - def msvs_substitute(self, input, msvs_ver, - subdir=None, sconscript=None, - python=None, - project_guid=None, - vcproj_sccinfo='', sln_sccinfo=''): - if not hasattr(self, '_msvs_versions'): - self.msvs_versions() - - if subdir: - workpath = self.workpath(subdir) - else: - workpath = self.workpath() - - if sconscript is None: - sconscript = self.workpath('SConstruct') - - if python is None: - python = sys.executable - - if project_guid is None: - project_guid = "{E5466E26-0003-F18B-8F8A-BCD76C86388D}" - - if 'SCONS_LIB_DIR' in os.environ: - exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % os.environ['SCONS_LIB_DIR'] - else: - exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%s'), join(sys.prefix, 'scons-%s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % (self.scons_version, self.scons_version) - exec_script_main_xml = exec_script_main.replace("'", "'") - - result = input.replace(r'', workpath) - result = result.replace(r'', python) - result = result.replace(r'', sconscript) - result = result.replace(r'', exec_script_main) - result = result.replace(r'', exec_script_main_xml) - result = result.replace(r'', project_guid) - result = result.replace('\n', vcproj_sccinfo) - result = result.replace('\n', sln_sccinfo) - return result - - def get_msvs_executable(self, version): - """Returns a full path to the executable (MSDEV or devenv) - for the specified version of Visual Studio. - """ - from SCons.Tool.MSCommon import get_vs_by_version - - msvs = get_vs_by_version(version) - if not msvs: - return None - return msvs.get_executable() - - def run(self, *args, **kw): - """ - Suppress MSVS deprecation warnings. - """ - save_sconsflags = os.environ.get('SCONSFLAGS') - if save_sconsflags: - sconsflags = [save_sconsflags] - else: - sconsflags = [] - sconsflags = sconsflags + ['--warn=no-deprecated'] - os.environ['SCONSFLAGS'] = ' '.join(sconsflags) - try: - result = TestSCons.run(self, *args, **kw) - finally: - os.environ['SCONSFLAGS'] = save_sconsflags or '' - return result - - def get_vs_host_arch(self): - """ Get an MSVS, SDK , and/or MSVS acceptable platform arch - """ - - # Dict to 'canonalize' the arch - _ARCH_TO_CANONICAL = { - "x86": "x86", - "amd64": "amd64", - "i386": "x86", - "emt64": "amd64", - "x86_64": "amd64", - "itanium": "ia64", - "ia64": "ia64", - } - - host_platform = platform.machine() - # TODO(2.5): the native Python platform.machine() function returns - # '' on all Python versions before 2.6, after which it also uses - # PROCESSOR_ARCHITECTURE. - if not host_platform: - host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') - - - try: - host = _ARCH_TO_CANONICAL[host_platform] - except KeyError as e: - # Default to x86 for all other platforms - host = 'x86' - - - return host - - def validate_msvs_file(self, file): - try: - x = ElementTree.parse(file) - except: - print("--------------------------------------------------------------") - print("--------------------------------------------------------------") - print(traceback.format_exc()) - print("Failed to validate xml in MSVS file: ") - print(file) - print("--------------------------------------------------------------") - print("--------------------------------------------------------------") - self.fail_test() -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestSCons_time.py b/QMTest/TestSCons_time.py deleted file mode 100644 index bc116ec..0000000 --- a/QMTest/TestSCons_time.py +++ /dev/null @@ -1,331 +0,0 @@ -""" -TestSCons_time.py: a testing framework for the scons-test.py script - -A TestSCons_time environment object is created via the usual invocation: - - test = TestSCons_time() - -TestSCons_time is a subclass of TestCommon, which is in turn is a subclass -of TestCmd), and hence has available all of the methods and attributes -from those classes, as well as any overridden or additional methods or -attributes defined in this subclass. -""" - -# __COPYRIGHT__ - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os -import os.path -import sys - -from TestCommon import * -from TestCommon import __all__ -# some of the scons_time tests may need regex-based matching: -from TestSCons import search_re, search_re_in_list - -__all__.extend([ 'TestSCons_time', - ]) - -SConstruct = """\ -from __future__ import print_function -import os -print("SConstruct file directory:", os.getcwd()) -""" - -scons_py = """\ -#!/usr/bin/env python -import os -import sys -def write_args(fp, args): - fp.write(args[0] + '\\n') - for arg in args[1:]: - fp.write(' ' + arg + '\\n') -write_args(sys.stdout, sys.argv) -for arg in sys.argv[1:]: - if arg[:10] == '--profile=': - profile = open(arg[10:], 'w') - profile.write('--profile\\n') - write_args(profile, sys.argv) - break -sys.stdout.write('SCONS_LIB_DIR = ' + os.environ['SCONS_LIB_DIR'] + '\\n') -exec(open('SConstruct', 'r').read()) -""" - -aegis_py = """\ -#!/usr/bin/env python -import os -import sys -script_dir = 'src/script' -if not os.path.exists(script_dir): - os.makedirs(script_dir) -open(script_dir + '/scons.py', 'w').write( -r'''%s''') -""" % scons_py - - -svn_py = """\ -#!/usr/bin/env python -import os -import sys -dir = sys.argv[-1] -script_dir = dir + '/src/script' -os.makedirs(script_dir) -open(script_dir + '/scons.py', 'w').write( -r'''%s''') -""" % scons_py - - -logfile_contents = """\ -Memory before reading SConscript files: 100%(index)s -Memory after reading SConscript files: 200%(index)s -Memory before building targets: 300%(index)s -Memory after building targets: 400%(index)s -Object counts: - pre- post- pre- post- - read read build build Class - 101%(index)s 102%(index)s 103%(index)s 104%(index)s Action.CommandAction - 201%(index)s 202%(index)s 203%(index)s 204%(index)s Action.CommandGeneratorAction - 301%(index)s 302%(index)s 303%(index)s 304%(index)s Action.FunctionAction - 401%(index)s 402%(index)s 403%(index)s 404%(index)s Action.LazyAction - 501%(index)s 502%(index)s 503%(index)s 504%(index)s Action.ListAction - 601%(index)s 602%(index)s 603%(index)s 604%(index)s Builder.BuilderBase - 701%(index)s 702%(index)s 703%(index)s 704%(index)s Builder.CompositeBuilder - 801%(index)s 802%(index)s 803%(index)s 804%(index)s Builder.ListBuilder - 901%(index)s 902%(index)s 903%(index)s 904%(index)s Builder.MultiStepBuilder - 1001%(index)s 1002%(index)s 1003%(index)s 1004%(index)s Builder.OverrideWarner - 1101%(index)s 1102%(index)s 1103%(index)s 1104%(index)s Environment.Base - 1201%(index)s 1202%(index)s 1203%(index)s 1204%(index)s Environment.EnvironmentClone - 1301%(index)s 1302%(index)s 1303%(index)s 1304%(index)s Environment.OverrideEnvironment - 1401%(index)s 1402%(index)s 1403%(index)s 1404%(index)s Executor.Executor - 1501%(index)s 1502%(index)s 1503%(index)s 1504%(index)s Node.FS - 1601%(index)s 1602%(index)s 1603%(index)s 1604%(index)s Node.FS.Base - 1701%(index)s 1702%(index)s 1703%(index)s 1704%(index)s Node.FS.Dir - 1801%(index)s 1802%(index)s 1803%(index)s 1804%(index)s Node.FS.File - 1901%(index)s 1902%(index)s 1904%(index)s 1904%(index)s Node.FS.RootDir - 2001%(index)s 2002%(index)s 2003%(index)s 2004%(index)s Node.Node -Total build time: 11.123456 seconds -Total SConscript file execution time: 22.234567 seconds -Total SCons execution time: 33.345678 seconds -Total command execution time: 44.456789 seconds -""" - - -profile_py = """\ -%(body)s - -import profile - -try: dispatch = profile.Profile.dispatch -except AttributeError: pass -else: dispatch['c_exception'] = profile.Profile.trace_dispatch_return - -prof = profile.Profile() -prof.runcall(%(call)s) -prof.dump_stats(r'%(profile_name)s') -""" - - -class TestSCons_time(TestCommon): - """Class for testing the scons-time script. - - This provides a common place for initializing scons-time tests, - eliminating the need to begin every test with the same repeated - initializations. - """ - - def __init__(self, **kw): - """Initialize an SCons_time testing object. - - If they're not overridden by keyword arguments, this - initializes the object with the following default values: - - program = 'scons-time' - interpreter = ['python', '-tt'] - match = match_exact - workdir = '' - - The workdir value means that, by default, a temporary workspace - directory is created for a TestSCons_time environment. - In addition, this method changes directory (chdir) to the - workspace directory, so an explicit "chdir = '.'" on all of the - run() method calls is not necessary. - """ - - self.orig_cwd = os.getcwd() - try: - script_dir = os.environ['SCONS_SCRIPT_DIR'] - except KeyError: - pass - else: - os.chdir(script_dir) - if 'program' not in kw: - p = os.environ.get('SCONS_TIME') - if not p: - p = 'scons-time' - if not os.path.exists(p): - p = 'scons-time.py' - kw['program'] = p - - if 'interpreter' not in kw: - kw['interpreter'] = [python, '-tt'] - - if 'match' not in kw: - kw['match'] = match_exact - - if 'workdir' not in kw: - kw['workdir'] = '' - - TestCommon.__init__(self, **kw) - - def archive_split(self, path): - if path[-7:] == '.tar.gz': - return path[:-7], path[-7:] - else: - return os.path.splitext(path) - - def fake_logfile(self, logfile_name, index=0): - self.write(self.workpath(logfile_name), logfile_contents % locals()) - - def profile_data(self, profile_name, python_name, call, body): - profile_name = self.workpath(profile_name) - python_name = self.workpath(python_name) - d = { - 'profile_name' : profile_name, - 'python_name' : python_name, - 'call' : call, - 'body' : body, - } - self.write(python_name, profile_py % d) - self.run(program = python_name, interpreter = sys.executable) - - def tempdir_re(self, *args): - """ - Returns a regular expression to match a scons-time - temporary directory. - """ - import re - import tempfile - - sep = re.escape(os.sep) - tempdir = tempfile.gettempdir() - - try: - realpath = os.path.realpath - except AttributeError: - pass - else: - tempdir = realpath(tempdir) - - args = (tempdir, 'scons-time-',) + args - x = os.path.join(*args) - x = re.escape(x) - x = x.replace('time\\-', 'time\\-[^%s]*' % sep) - return x - - def write_fake_aegis_py(self, name): - name = self.workpath(name) - self.write(name, aegis_py) - os.chmod(name, 0o755) - return name - - def write_fake_scons_py(self): - self.subdir('src', ['src', 'script']) - self.write('src/script/scons.py', scons_py) - - def write_fake_svn_py(self, name): - name = self.workpath(name) - self.write(name, svn_py) - os.chmod(name, 0o755) - return name - - def write_sample_directory(self, archive, dir, files): - dir = self.workpath(dir) - for name, content in files: - path = os.path.join(dir, name) - d, f = os.path.split(path) - if not os.path.isdir(d): - os.makedirs(d) - open(path, 'w').write(content) - return dir - - def write_sample_tarfile(self, archive, dir, files): - import shutil - try: - import tarfile - - except ImportError: - - self.skip_test('no tarfile module\n') - - else: - - base, suffix = self.archive_split(archive) - - mode = { - '.tar' : 'w', - '.tar.gz' : 'w:gz', - '.tgz' : 'w:gz', - } - - tar = tarfile.open(archive, mode[suffix]) - for name, content in files: - path = os.path.join(dir, name) - open(path, 'wb').write(bytearray(content,'utf-8')) - tarinfo = tar.gettarinfo(path, path) - tarinfo.uid = 111 - tarinfo.gid = 111 - tarinfo.uname = 'fake_user' - tarinfo.gname = 'fake_group' - tar.addfile(tarinfo, open(path, 'rb')) - tar.close() - shutil.rmtree(dir) - return self.workpath(archive) - - def write_sample_zipfile(self, archive, dir, files): - import shutil - try: - import zipfile - except ImportError: - - sys.stderr.write('no zipfile module\n') - self.no_result() - - else: - - zip = zipfile.ZipFile(archive, 'w') - for name, content in files: - path = os.path.join(dir, name) - open(path, 'w').write(content) - zip.write(path) - zip.close() - shutil.rmtree(dir) - return self.workpath(archive) - - sample_project_files = [ - ('SConstruct', SConstruct), - ] - - def write_sample_project(self, archive, dir=None): - base, suffix = self.archive_split(archive) - - write_sample = { - '.tar' : self.write_sample_tarfile, - '.tar.gz' : self.write_sample_tarfile, - '.tgz' : self.write_sample_tarfile, - '.zip' : self.write_sample_zipfile, - }.get(suffix, self.write_sample_directory) - - if not dir: - dir = base - - os.mkdir(dir) - path = write_sample(archive, dir, self.sample_project_files) - - return path - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestSConsign.py b/QMTest/TestSConsign.py deleted file mode 100644 index a48b648..0000000 --- a/QMTest/TestSConsign.py +++ /dev/null @@ -1,90 +0,0 @@ -# __COPYRIGHT__ -from __future__ import print_function - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -__doc__ = """ -TestSConsign.py: a testing framework for the "sconsign" script -tool. - -A TestSConsign environment object is created via the usual invocation: - - test = TestSConsign() - -TestSconsign is a subclass of TestSCons, which is a subclass of -TestCommon, which is in turn is a subclass of TestCmd), and hence -has available all of the methods and attributes from those classes, -as well as any overridden or additional methods or attributes defined -in this subclass. -""" - -import os -import os.path -import sys - -from TestSCons import * -from TestSCons import __all__ - -__all__.extend([ 'TestSConsign', ]) - -class TestSConsign(TestSCons): - """Class for testing the sconsign.py script. - - This provides a common place for initializing sconsign tests, - eliminating the need to begin every test with the same repeated - initializations. - - This adds additional methods for running the sconsign script - without changing the basic ability of the run() method to run - "scons" itself, since we need to run scons to generate the - .sconsign files that we want the sconsign script to read. - """ - def __init__(self, *args, **kw): - try: - script_dir = os.environ['SCONS_SCRIPT_DIR'] - except KeyError: - pass - else: - os.chdir(script_dir) - self.script_dir = os.getcwd() - - TestSCons.__init__(self, *args, **kw) - - self.my_kw = { - 'interpreter' : python, # imported from TestSCons - } - - if 'program' not in kw: - kw['program'] = os.environ.get('SCONS') - if not kw['program']: - if os.path.exists('scons'): - kw['program'] = 'scons' - else: - kw['program'] = 'scons.py' - - sconsign = os.environ.get('SCONSIGN') - if not sconsign: - if os.path.exists(self.script_path('sconsign.py')): - sconsign = 'sconsign.py' - elif os.path.exists(self.script_path('sconsign')): - sconsign = 'sconsign' - else: - print("Can find neither 'sconsign.py' nor 'sconsign' scripts.") - self.no_result() - self.set_sconsign(sconsign) - - def script_path(self, script): - return os.path.join(self.script_dir, script) - - def set_sconsign(self, sconsign): - self.my_kw['program'] = sconsign - - def run_sconsign(self, *args, **kw): - kw.update(self.my_kw) - return self.run(*args, **kw) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/test-framework.rst b/QMTest/test-framework.rst deleted file mode 100644 index d51e5f0..0000000 --- a/QMTest/test-framework.rst +++ /dev/null @@ -1,430 +0,0 @@ -======================= -SCons Testing Framework -======================= - -SCons uses extensive automated tests to try to ensure quality. The primary goal -is that users should be able to upgrade from version to version without any surprise -changes in behavior. - -In general, no change goes into SCons unless it has one or more new or modified -tests that demonstrably exercise the bug being fixed or the feature being added. -There are exceptions to this guideline, but they should be just that, ''exceptions''. -When in doubt, make sure it's tested. - -Test Organization -================= - -There are three types of SCons tests: - -*End-to-End Tests* - End-to-end tests of SCons are all Python scripts (``*.py``) underneath - the ``test/`` subdirectory. They use the test infrastructure modules in the - ``QMTest`` subdirectory. - -*Unit Tests* - Unit tests for individual SCons modules live underneath the - ``src/engine/`` subdirectory and are the same base name as the module - with ``Tests.py`` appended--for example, the unit tests for the - ``Builder.py`` module are in the ``BuilderTests.py`` script. - -*External Tests* - For the support of external Tools (in the form of packages, preferably), the - testing framework got extended, such that it can run in standalone mode. - You can start it from the top-level folder of your Tool's source tree, - where it then finds all Python scripts (``*.py``) underneath the - local ``test/`` directory. - This implies that Tool tests have to be kept in a folder named ``test``, - like for the SCons core. - - -Contrasting End-to-End and Unit Tests -##################################### - -In general, anything that we've put into an end-to-end test script should -be considered a hardened part of the interface (that is, it's something -that a user might do) and should not be broken. Unit tests are now -considered more malleable, more for testing internal interfaces that -can change so long as we don't break users' ``SConscript`` files. (This -wasn't always the case, and there's a lot of meaty code in many of the -unit test scripts that does, in fact, capture external interface -behavior. In general, we should try to move those things to end-to-end -scripts as we find them.) - -It's more difficult to debug end-to-end tests. You can actually go -straight into the Python debugger on the unit test scripts by using the -``runtest.py --pdb`` option, but the end-to-end tests treat an SCons -invocation as a "black box" and just look for external effects. -Simple ``print`` statements within the SCons code itself often don't help -debug end-to-end because they end up in SCons output that gets compared -against expected output and cause a test failure. Probably the most -effective technique is to use the internal ``SCons.Debug.Trace()`` function, -which prints output to ``/dev/tty`` on Linux/UNIX systems and ``con`` on -Windows systems, so you can see what's going on. - -Naming conventions -################## - -The end-to-end tests, more or less, stick to the following naming conventions: - -1. All tests end with a .py suffix. - -2. In the *General* form we use - - ``Feature.py`` - for the test of a specified feature; try to - keep this description reasonably short - - ``Feature-x.py`` - for the test of a specified feature using - option ``x`` - -3. The *command line option* tests take the form - - ``option-x.py`` - for a lower-case single-letter option - - ``option--X.py`` - upper-case single-letter option - (with an extra hyphen, so the file names will - be unique on case-insensitive systems) - - ``option--lo.py`` - long option; abbreviate the long - option name to a few characters - - -Running Tests -============= - -The standard set of SCons tests are run from the top-level source directory -by the ``runtest.py`` script. -There is a ``--qmtest`` option that checks whether the ``QMTest`` package -is installed on your system. If it can be found, then the ``runtest.py`` script -will use it to carry out the tests. - -Help is available through the ``-h`` option: - -:: - - $ python runtest.py -h - -To simply run all the tests, use the ``-a`` option: - -:: - - $ python runtest.py -a - -By default, ``runtest.py`` prints a count and percentage message for each test -case, along with the name of the test file. -If you need the output to be more silent, have a look at the ``-q``, ``-s`` and -``-k`` options. - -You may specifically list one or more tests to be run: - -:: - - $ python runtest.py src/engine/SCons/BuilderTests.py - $ python runtest.py test/option-j.py test/Program.py - -Folder names are allowed arguments as well, so you can do a - -:: - - $ python runtest.py test/SWIG - -to run all SWIG tests only. - -You can also use the ``-f`` option to execute just the tests listed in a specified -text file: - -:: - - $ cat testlist.txt - test/option-j.py - test/Program.py - $ python runtest.py -f testlist.txt - - -One test must be listed per line, and any lines that begin with '#' -will be ignored (the intent being to allow you, for example, -to comment out tests that -are currently passing and then uncomment all of the tests in the file -for a final validation run). - -If more than one test is run, the ``runtest.py`` script prints a summary -of how many tests passed, failed, or yielded no result, and lists any -unsuccessful tests. - -The above invocations all test directly the files underneath the ``src/`` -subdirectory, and do not require that a packaging build be performed first. -The ``runtest.py`` script supports additional options to run tests against -unpacked packages in the ``build/test-*/`` subdirectories. - -If you are testing a separate Tool outside of the SCons source tree, you have -to call the ``runtest.py`` script in *external* (stand-alone) mode:: - - $ python ~/scons/runtest.py -e -a - -. This ensures that the testing framework doesn't try to access SCons classes -needed for some of the *internal* test cases. - -Note, that the actual tests are carried out in a temporary folder each, which gets -deleted afterwards. This ensures that your source directories don't get clobbered -with temporary files from the test runs. It also means that you can't simply change -into a folder to "debug things" after a test has gone wrong. For a way around this, -check out the ``PRESERVE`` environment variable. It can be seen in action in -`How to convert old tests`_ below. - -Not Running Tests -================= - -If you simply want to check which tests would get executed, you can call the -``runtest.py`` script with the ``-l`` option:: - - $ python runtest.py -l - -Then there is also the ``-n`` option, which prints the command line for each -single test, but doesn't actually execute them:: - - $ python runtest.py -n - -Finding Tests -============= - -When started in *standard* mode - -:: - - $ python runtest.py -a - - -, ``runtest.py`` assumes that it is run from the SCons top-level source directory. -It then dives into the ``src`` and ``test`` folders, where it tries to find filenames - - ``*Test.py`` - for the ``src`` directory, and - - ``*.py`` - for the ``test`` folder. - -When using fixtures, you may quickly end up in a position where you have supporting -Python script files in a subfolder, but they shouldn't get picked up as test scripts. -In this case you have two options: - -1. Add a file with the name ``sconstest.skip`` to your subfolder. This lets - ``runtest.py`` skip the contents of the directory completely. -2. Create a file ``.exclude_tests`` in each folder in question, and in it list - line-by-line the files to get excluded from testing. - -The same rules apply when testing external Tools by using the ``-e`` option. - - -"Hello, world!" SCons Test Script -================================= - -To illustrate how the end-to-end test scripts work, -let's walk through a simple "Hello, world!" example: - -:: - - #!python - import TestSCons - - test = TestSCons.TestSCons() - - test.write('SConstruct', """\ - Program('hello.c') - """) - - test.write('hello.c', """\ - int - main(int argc, char *argv[]) - { - printf("Hello, world!\\n"); - exit (0); - } - """) - - test.run() - - test.run(program='./hello', stdout="Hello, world!\n") - - test.pass_test() - - -``import TestSCons`` - Imports the main infrastructure for writing SCons tests. This is normally the only part of the infrastructure that needs importing. Sometimes other Python modules are necessary or helpful, and get imported before this line. - -``test = TestSCons.TestSCons()`` - This initializes an object for testing. A fair amount happens under the covers when the object is created, including: - - * A temporary directory is created for all the in-line files that will get created. - * The temporary directory's removal is arranged for when the test is finished. - * We ``os.chdir()`` to the temporary directory. - -``test.write('SConstruct', ...`` - This line creates an ``SConstruct`` file in the temporary directory, to be used as input to the ``scons`` run(s) that we're testing. Note the use of the Python triple-quote syntax for the contents of the ``SConstruct`` file. Because input files for tests are all created from in-line data like this, the tests can sometimes get a little confusing to read, because some of the Python code is found - -``test.write('hello.c', ...`` - This lines creates an ``hello.c`` file in the temporary directory. Note that we have to escape the ``\\n`` in the ``"Hello, world!\\n"`` string so that it ends up as a single backslash in the ``hello.c`` file on disk. - -``test.run()`` - This actually runs SCons. Like the object initialization, things happen under the covers: - - * The exit status is verified; the test exits with a failure if the exit status is not zero. - * The error output is examined, and the test exits with a failure if there is any - -``test.run(program='./hello', stdout="Hello, world!\n")`` - This shows use of the ``TestSCons.run()`` method to execute a program other than ``scons``, in this case the ``hello`` program we just presumably built. The ``stdout=`` keyword argument also tells the ``TestSCons.run()`` method to fail if the program output does not match the expected string ``"Hello, world!\n"``. Like the previous ``test.run()`` line, it will also fail the test if the exit status is non-zero, or there is any error output. - -``test.pass_test()`` - This is always the last line in a test script. It prints ``PASSED`` on the screen and makes sure we exit with a ``0`` status to indicate the test passed. As a side effect of destroying the ``test`` object, the created temporary directory will be removed. - -Working with fixtures -===================== - -In the simple example above, we have seen how to create files in the temporary test directory. -We give a filename to the ``TestSCons.write()`` method, together with its contents, and it gets -written to the test folder right before its start. - -This technique can still be seen throughout most of the end-to-end tests, but there is a better -way. It's much easier to edit, create and maintain real files, instead of copy/pasting -content to/from a Python script. If the test files get longer, the test script -gets longer and is harder to read. - -Against this, we now have the possibility to copy single files or the contents of a -local folder to the test directory. Since we can reuse these files/folders to setup -several tests, we call them *fixtures* in the following. - -Directory fixtures -################## - -The function ``dir_fixture(self, srcdir, dstdir=None)`` in the ``TestCmd`` class -copies the contents of the specified folder ``srcdir`` from -the directory of the called test script, to the current -temporary test directory. -The ``srcdir`` name may be a list, in which case the elements are -concatenated with the ``os.path.join()`` method. The ``dstdir`` is -assumed to be under the temporary working directory, it gets -created automatically, if it does not already exist. - -A short syntax example:: - - test = TestSCons.TestSCons() - test.dir_fixture('image') - test.run() - -would copy all files and subfolders from the local ``image`` folder, to -the temporary directory for the current test. - -If you'd like to see a real example for this in action, refer to the test -named ``test/packaging/convenience-functions/convenience-functions.py``. - -File fixtures -############# - -Like for directory fixtures, ``file_fixture(self, srcfile, dstfile=None)`` -copies the file ``srcfile`` from the directory of -the called script, to the temporary test directory. -The ``dstfile`` is assumed to be under the temporary working -directory, unless it is an absolute path name. -If ``dstfile`` is specified, its target directory gets created -automatically if it doesn't already exist. - -With a:: - - test = TestSCons.TestSCons() - test.file_fixture('SConstruct') - test.file_fixture(['src','main.cpp'],['src','main.cpp']) - test.run() - -you would copy the files ``SConstruct`` and ``src/main.cpp`` to the temporary -test folder, prior to running the test itself. - -Again, a reference example can be found in the current *default* revision of -SCons, it is ``test/packaging/sandbox-test/sandbox-test.py``. - -For even more examples you should check out one of the external Tools, e.g. the -*Qt4* Tool at https://bitbucket.org/dirkbaechle/scons_qt4. Also visit the SCons -Tools Index at https://github.com/SCons/scons/wiki/ToolsIndex for a complete -list of available Tools, though not all may have tests yet. - -How to convert old tests -######################## - -We now show how to convert a test, still using the ``TestSCons.write()`` method, to -the fixture based approach. For this, we need to get at the files as they -are written to each temporary test folder. - -Luckily, ``runtest.py`` checks for the existence of an environment variable named -``PRESERVE``. If it is set to a non-zero value, the testing framework doesn't delete -the test folder as ususal, but prints its name to the screen. - -So, you should be able to give the commands - -:: - - $ export PRESERVE=1 - $ python runtest.py test/packaging/sandbox-test.py - -, assuming Linux and a bash-like shell. - -The output should then look something like this:: - - 1/1 (100.00%) /usr/bin/python -tt test/packaging/sandbox-test.py - PASSED - Preserved directory /tmp/testcmd.4060.twlYNI - -and you see that the test files have been kept in the folder ``/tmp/testcmd.4060.twlYNI``, -where you can now copy them from to your new *fixture* folder. Then, in the test -script you simply remove all the tedious ``TestSCons.write()`` statements and -replace them by a single ``TestSCons.dir_fixture()``. - -Finally, you shouldn't forget to clean up and remove the temporary test directory. ``;)`` - -Test Infrastructure -=================== - -The test API is in ``QMTest/TestSCons.py``. ``TestSCons`` is a subclass of -``TestCommon``, which is a subclass of ``TestCmd``; all those python files are -in ``QMTest``. Start in ``QMTest/TestCmd.py`` for the base API definitions, -like how to create files (``test.write()``) and run commands (``test.run()``). - -You want to use ``TestSCons`` for the end-to-end tests in ``test``, but ``TestCmd`` -for the unit tests in the ``src`` folder. - -The match functions work like this: - -TestSCons.match_re:: match each line with a RE - * Splits the lines into a list (unless they already are) - * splits the REs at newlines (unless already a list) and puts ^..$ around each - * then each RE must match each line. This means there must be as many REs as lines. - -TestSCons.match_re_dotall:: match all the lines against a single RE - * Joins the lines with newline (unless already a string) - * joins the REs with newline (unless it's a string) and puts ^..$ around the whole thing - * then whole thing must match with python re.DOTALL. - -Use them in a test like this:: - - test.run(..., match=TestSCons.match_re, ...) - -or:: - - test.must_match(..., match=TestSCons.match_re, ...) - -Avoiding Tests based on Tool existence -====================================== - -Here's an easy sample:: - - #!python - intelc = test.detect_tool('intelc', prog='icpc') - if not intelc: - test.skip_test("Could not load 'intelc' Tool; skipping test(s).\n") - -See ``QMTest/TestSCons.py`` for the ``detect_tool`` method. It calls the tool's -``generate()`` method, and then looks for the given prog (tool name by default) in -``env['ENV']['PATH']``. - - diff --git a/SConstruct b/SConstruct index 1a0deb3..fc94abd 100644 --- a/SConstruct +++ b/SConstruct @@ -132,7 +132,7 @@ python_ver = sys.version[0:3] # for the doc toolchain. # addpaths = [os.path.abspath(os.path.join(os.getcwd(), 'bin')), - os.path.abspath(os.path.join(os.getcwd(), 'QMTest'))] + os.path.abspath(os.path.join(os.getcwd(), 'testing/framework'))] for a in addpaths: if a not in sys.path: sys.path.append(a) @@ -634,7 +634,7 @@ for p in [ scons ]: # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, - # and under QMTest (for the testing modules TestCmd.py, TestSCons.py, + # and under testing/framework (for the testing modules TestCmd.py, TestSCons.py, # etc.). This makes sure that our tests pass with what # we really packaged, not because of something hanging around in # the development directory. @@ -704,7 +704,7 @@ for p in [ scons ]: # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, - # and under QMTest (for the testing modules TestCmd.py, TestSCons.py, + # and under testing/framework (for the testing modules TestCmd.py, TestSCons.py, # etc.). This makes sure that our tests pass with what # we really packaged, not because of something hanging around in # the development directory. @@ -825,7 +825,7 @@ for p in [ scons ]: # Export('build_dir', 'env') -SConscript('QMTest/SConscript') +SConscript('testing/SConscript') # # @@ -926,7 +926,7 @@ if sfiles: # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, - # and under QMTest (for the testing modules TestCmd.py, + # and under testing/framework (for the testing modules TestCmd.py, # TestSCons.py, etc.). This makes sure that our tests pass with # what we really packaged, not because of something hanging around # in the development directory. @@ -980,7 +980,7 @@ if sfiles: # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, - # and under QMTest (for the testing modules TestCmd.py, + # and under testing/framework (for the testing modules TestCmd.py, # TestSCons.py, etc.). This makes sure that our tests pass with # what we really packaged, not because of something hanging # around in the development directory. @@ -1018,7 +1018,7 @@ if sfiles: for pf, help_text in packaging_flavors: Alias(pf, [ os.path.join(build_dir, 'test-'+pf), - os.path.join(build_dir, 'QMTest'), + os.path.join(build_dir, 'testing/framework'), os.path.join(build_dir, 'runtest.py'), ]) diff --git a/bin/SConsExamples.py b/bin/SConsExamples.py index a2ed570..7491c58 100644 --- a/bin/SConsExamples.py +++ b/bin/SConsExamples.py @@ -416,8 +416,8 @@ def exampleNamesAreUnique(dpath): # # ############################################################### -sys.path.append(os.path.join(os.getcwd(), 'QMTest')) -sys.path.append(os.path.join(os.getcwd(), 'build', 'QMTest')) +sys.path.append(os.path.join(os.getcwd(), 'testing/framework')) +sys.path.append(os.path.join(os.getcwd(), 'build', 'testing/framework')) scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py') if not os.path.exists(scons_py): diff --git a/bin/time-scons.py b/bin/time-scons.py index b7d8ef1..c5cd0cc 100644 --- a/bin/time-scons.py +++ b/bin/time-scons.py @@ -43,7 +43,7 @@ TimeSCons_revision = 4569 # The pieces of the TimeSCons infrastructure that are necessary to # produce consistent timings, even when the rest of the tree is from # an earlier revision that doesn't have these pieces. -TimeSCons_pieces = ['QMTest', 'timings', 'runtest.py'] +TimeSCons_pieces = ['testing/framework', 'timings', 'runtest.py'] class CommandRunner(object): diff --git a/bin/update-release-info.py b/bin/update-release-info.py index 81e0df5..5b871cb 100644 --- a/bin/update-release-info.py +++ b/bin/update-release-info.py @@ -20,10 +20,10 @@ in various files: - The RELEASE header line in src/CHANGES.txt and src/Announce.txt. - The version string at the top of src/RELEASE.txt. - The version string in the 'default_version' variable in SConstruct - and QMTest/TestSCons.py. - - The copyright years in SConstruct and QMTest/TestSCons.py. + and testing/framework/TestSCons.py. + - The copyright years in SConstruct and testing/framework/TestSCons.py. - The month and year (used for documentation) in SConstruct. - - The unsupported and deprecated Python floors in QMTest/TestSCons.py + - The unsupported and deprecated Python floors in testing/framework/TestSCons.py and src/engine/SCons/Script/Main.py - The version string in the filenames in README. @@ -315,9 +315,9 @@ for suf in ['tar', 'win32', 'zip', 'rpm', 'exe', 'deb']: '-%s.%s' % (version_string, suf), count = 0) -# Update QMTest/TestSCons.py +# Update testing/framework/TestSCons.py -t = UpdateFile(os.path.join('QMTest', 'TestSCons.py')) +t = UpdateFile(os.path.join('testing','framework', 'TestSCons.py')) if DEBUG: t.file = '/tmp/TestSCons.py' t.replace_assign('copyright_years', repr(copyright_years)) t.replace_assign('default_version', repr(version_string)) diff --git a/runtest.py b/runtest.py index cf8925c..e5c5f2f 100755 --- a/runtest.py +++ b/runtest.py @@ -8,9 +8,9 @@ # # - unit tests - included in *Tests.py files from src/ dir # - end-to-end tests - these are *.py files in test/ directory that -# require custom SCons framework from QMTest/ +# require custom SCons framework from testing/ # -# This script adds src/ and QMTest/ directories to PYTHONPATH, +# This script adds src/ and testing/ directories to PYTHONPATH, # performs test discovery and processes them according to options. # # With -p (--package) option, script tests specified package from @@ -625,10 +625,8 @@ old_pythonpath = os.environ.get('PYTHONPATH') # should be able to go away. pythonpaths = [ pythonpath_dir ] -# Add path of the QMTest folder to PYTHONPATH -# [ ] move used parts from QMTest to testing/framework/ scriptpath = os.path.dirname(os.path.realpath(__file__)) -pythonpaths.append(os.path.join(scriptpath, 'QMTest')) + # Add path for testing framework to PYTHONPATH pythonpaths.append(os.path.join(scriptpath, 'testing', 'framework')) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index ee4d7bb..a4590d0 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -35,6 +35,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fix issue # 3106 MSVC if using MSVC_BATCH and target dir had a space would fail due to quirk in MSVC's handling of escaped targetdirs when batch compiling. - Re-Enable parallel SCons (-j) when running via Pypy + - Move SCons test framework files to testing/framework and remove all references to QMtest. + QMTest has not been used by SCons for some time now. From Andrew Featherstone - Removed unused --warn options from the man page and source code. diff --git a/src/test_strings.py b/src/test_strings.py index 297d59d..e9f0abf 100644 --- a/src/test_strings.py +++ b/src/test_strings.py @@ -208,12 +208,10 @@ check_list = [ 'doc/user/SCons-win32-install-4.jpg', 'examples', 'gentoo', - 'QMTest/classes.qmc', - 'QMTest/configuration', - 'QMTest/TestCmd.py', - 'QMTest/TestCmdTests.py', - 'QMTest/TestCommon.py', - 'QMTest/TestCommonTests.py', + 'testing/framework/TestCmd.py', + 'testing/framework/TestCmdTests.py', + 'testing/framework/TestCommon.py', + 'testing/framework/TestCommonTests.py', 'src/MANIFEST.in', 'src/setup.cfg', 'src/engine/MANIFEST.in', diff --git a/test/D/Support/executablesSearch.py b/test/D/Support/executablesSearch.py index 17d9990..c572f56 100755 --- a/test/D/Support/executablesSearch.py +++ b/test/D/Support/executablesSearch.py @@ -51,7 +51,7 @@ def isExecutableOfToolAvailable(test, tool): if __name__ == '__main__': import unittest - sys.path.append(os.path.abspath('../../../QMTest')) + sys.path.append(os.path.abspath('../../../testing/framework')) import TestSCons diff --git a/test/README b/test/README index 2c8423f..be75398 100644 --- a/test/README +++ b/test/README @@ -4,7 +4,7 @@ This directory contains our end-to-end SCons tests. They are all meant to be run essentially standalone, with the exception of the TestSCons.py module and the other modules imported by the various -tests. These modules are in the QMTest/ subdirectory, and PYTHONPATH +tests. These modules are in the testing/framework/ subdirectory, and PYTHONPATH needs to be set up correctly so that the test scripts can find them, and so that the SCons script itself can find the build engine modules. @@ -56,4 +56,4 @@ semblance of uniformity, here are the naming conventions for tests: For some more information about running the tests and writing them, please refer to the documentation for the testing framework. It can be found in -the 'QMTest' folder, as file 'test-framework.rst'. +the 'testing/framework' folder, as file 'test-framework.rst'. diff --git a/test/update-release-info/update-release-info.py b/test/update-release-info/update-release-info.py index 970bcce..d0242a8 100644 --- a/test/update-release-info/update-release-info.py +++ b/test/update-release-info/update-release-info.py @@ -34,7 +34,7 @@ import TestRuntest # Needed to ensure we're using the correct year this_year=time.localtime()[0] -TestSCons = 'QMTest/TestSCons.py' .split('/') +TestSCons = 'testing/framework/TestSCons.py' .split('/') README = 'README.rst' .split('/') ReleaseConfig = 'ReleaseConfig' .split('/') SConstruct = 'SConstruct' .split('/') diff --git a/testing/framework/README.txt b/testing/framework/README.txt new file mode 100644 index 0000000..7750bc7 --- /dev/null +++ b/testing/framework/README.txt @@ -0,0 +1,50 @@ +This directory contains testing infrastructure. Note that not all of +the pieces here are local to SCons. + + README.txt + + What you're looking at right now. + + SConscript + + Configuration for our packaging build, to copy the necessary + parts of the infrastructure into a build directory. + + TestCmd.py + TestCmdTests.py + TestCommon.py + TestCommonTests.py + + The TestCmd infrastructure for testing external commands. + These are for generic command testing, are used by some + other projects, and are developed separately from SCons. + (They're developed by SK, but still...) + + We've captured the unit tests (Test*Tests.py) for these files + along with the actual modules themselves to make it a little + easier to hack on them for our purposes. Note, however, + that any SCons-specific functionality should be implemented + in one of the + + TestRuntest.py + + Test infrastructure for our runtest.py script. + + TestSCons.py + + Test infrastructure for SCons itself. + + TestSConsMSVS.py + + Test infrastructure for SCons' Visual Studio support. + + TestSCons_time.py + + Test infrastructure for the scons-time.py script. + + TestSConsign.py + + Test infrastructure for the sconsign.py script. + +__COPYRIGHT__ +__FILE__ __REVISION__ __DATE__ __DEVELOPER__ diff --git a/testing/framework/SConscript b/testing/framework/SConscript new file mode 100644 index 0000000..0d3832e --- /dev/null +++ b/testing/framework/SConscript @@ -0,0 +1,61 @@ +# +# SConscript file for external packages we need. +# + +# +# __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. +# + +import os.path + +Import('build_dir', 'env') + +files = [ + 'TestCmd.py', + 'TestCommon.py', + 'TestRuntest.py', + 'TestSCons.py', + 'TestSConsign.py', + 'TestSCons_time.py', +] + +def copy(target, source, env): + t = str(target[0]) + s = str(source[0]) + c = open(s, 'r').read() + # Note: We construct the __ VERSION __ substitution string at + # run-time so it doesn't get replaced when this file gets copied + # into the tree for packaging. + c = c.replace('__' + 'VERSION' + '__', env['VERSION']) + open(t, 'w').write(c) + +for file in files: + # Guarantee that real copies of these files always exist in + # build/testing/framework. If there's a symlink there, then this is an Aegis + # build and we blow them away now so that they'll get "built" later. + p = os.path.join(build_dir, 'testing','framework', file) + if os.path.islink(p): + os.unlink(p) + if not os.path.isabs(p): + p = '#' + p + env.Command(p, file, copy) + Local(p) diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py new file mode 100644 index 0000000..0aab9a8 --- /dev/null +++ b/testing/framework/TestCmd.py @@ -0,0 +1,1907 @@ +""" +TestCmd.py: a testing framework for commands and scripts. + +The TestCmd module provides a framework for portable automated testing +of executable commands and scripts (in any language, not just Python), +especially commands and scripts that require file system interaction. + +In addition to running tests and evaluating conditions, the TestCmd +module manages and cleans up one or more temporary workspace +directories, and provides methods for creating files and directories in +those workspace directories from in-line data, here-documents), allowing +tests to be completely self-contained. + +A TestCmd environment object is created via the usual invocation: + + import TestCmd + test = TestCmd.TestCmd() + +There are a bunch of keyword arguments available at instantiation: + + test = TestCmd.TestCmd(description = 'string', + program = 'program_or_script_to_test', + interpreter = 'script_interpreter', + workdir = 'prefix', + subdir = 'subdir', + verbose = Boolean, + match = default_match_function, + match_stdout = default_match_stdout_function, + match_stderr = default_match_stderr_function, + diff = default_diff_stderr_function, + diff_stdout = default_diff_stdout_function, + diff_stderr = default_diff_stderr_function, + combine = Boolean) + +There are a bunch of methods that let you do different things: + + test.verbose_set(1) + + test.description_set('string') + + test.program_set('program_or_script_to_test') + + test.interpreter_set('script_interpreter') + test.interpreter_set(['script_interpreter', 'arg']) + + test.workdir_set('prefix') + test.workdir_set('') + + test.workpath('file') + test.workpath('subdir', 'file') + + test.subdir('subdir', ...) + + test.rmdir('subdir', ...) + + test.write('file', "contents\n") + test.write(['subdir', 'file'], "contents\n") + + test.read('file') + test.read(['subdir', 'file']) + test.read('file', mode) + test.read(['subdir', 'file'], mode) + + test.writable('dir', 1) + test.writable('dir', None) + + test.preserve(condition, ...) + + test.cleanup(condition) + + test.command_args(program = 'program_or_script_to_run', + interpreter = 'script_interpreter', + arguments = 'arguments to pass to program') + + test.run(program = 'program_or_script_to_run', + interpreter = 'script_interpreter', + arguments = 'arguments to pass to program', + chdir = 'directory_to_chdir_to', + stdin = 'input to feed to the program\n') + universal_newlines = True) + + p = test.start(program = 'program_or_script_to_run', + interpreter = 'script_interpreter', + arguments = 'arguments to pass to program', + universal_newlines = None) + + test.finish(self, p) + + test.pass_test() + test.pass_test(condition) + test.pass_test(condition, function) + + test.fail_test() + test.fail_test(condition) + test.fail_test(condition, function) + test.fail_test(condition, function, skip) + test.fail_test(condition, function, skip, message) + + test.no_result() + test.no_result(condition) + test.no_result(condition, function) + test.no_result(condition, function, skip) + + test.stdout() + test.stdout(run) + + test.stderr() + test.stderr(run) + + test.symlink(target, link) + + test.banner(string) + test.banner(string, width) + + test.diff(actual, expected) + + test.diff_stderr(actual, expected) + + test.diff_stdout(actual, expected) + + test.match(actual, expected) + + test.match_stderr(actual, expected) + + test.match_stdout(actual, expected) + + test.set_match_function(match, stdout, stderr) + + test.match_exact("actual 1\nactual 2\n", "expected 1\nexpected 2\n") + test.match_exact(["actual 1\n", "actual 2\n"], + ["expected 1\n", "expected 2\n"]) + test.match_caseinsensitive("Actual 1\nACTUAL 2\n", "expected 1\nEXPECTED 2\n") + + test.match_re("actual 1\nactual 2\n", regex_string) + test.match_re(["actual 1\n", "actual 2\n"], list_of_regexes) + + test.match_re_dotall("actual 1\nactual 2\n", regex_string) + test.match_re_dotall(["actual 1\n", "actual 2\n"], list_of_regexes) + + test.tempdir() + test.tempdir('temporary-directory') + + test.sleep() + test.sleep(seconds) + + test.where_is('foo') + test.where_is('foo', 'PATH1:PATH2') + test.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') + + test.unlink('file') + test.unlink('subdir', 'file') + +The TestCmd module provides pass_test(), fail_test(), and no_result() +unbound functions that report test results for use with the Aegis change +management system. These methods terminate the test immediately, +reporting PASSED, FAILED, or NO RESULT respectively, and exiting with +status 0 (success), 1 or 2 respectively. This allows for a distinction +between an actual failed test and a test that could not be properly +evaluated because of an external condition (such as a full file system +or incorrect permissions). + + import TestCmd + + TestCmd.pass_test() + TestCmd.pass_test(condition) + TestCmd.pass_test(condition, function) + + TestCmd.fail_test() + TestCmd.fail_test(condition) + TestCmd.fail_test(condition, function) + TestCmd.fail_test(condition, function, skip) + TestCmd.fail_test(condition, function, skip, message) + + TestCmd.no_result() + TestCmd.no_result(condition) + TestCmd.no_result(condition, function) + TestCmd.no_result(condition, function, skip) + +The TestCmd module also provides unbound global functions that handle +matching in the same way as the match_*() methods described above. + + import TestCmd + + test = TestCmd.TestCmd(match = TestCmd.match_exact) + + test = TestCmd.TestCmd(match = TestCmd.match_caseinsensitive) + + test = TestCmd.TestCmd(match = TestCmd.match_re) + + test = TestCmd.TestCmd(match = TestCmd.match_re_dotall) + +These functions are also available as static methods: + + import TestCmd + + test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_exact) + + test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_caseinsensitive) + + test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re) + + test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re_dotall) + +These static methods can be accessed by a string naming the method: + + import TestCmd + + test = TestCmd.TestCmd(match = 'match_exact') + + test = TestCmd.TestCmd(match = 'match_caseinsensitive') + + test = TestCmd.TestCmd(match = 'match_re') + + test = TestCmd.TestCmd(match = 'match_re_dotall') + +The TestCmd module provides unbound global functions that can be used +for the "diff" argument to TestCmd.TestCmd instantiation: + + import TestCmd + + test = TestCmd.TestCmd(match = TestCmd.match_re, + diff = TestCmd.diff_re) + + test = TestCmd.TestCmd(diff = TestCmd.simple_diff) + + test = TestCmd.TestCmd(diff = TestCmd.context_diff) + + test = TestCmd.TestCmd(diff = TestCmd.unified_diff) + +These functions are also available as static methods: + + import TestCmd + + test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re, + diff = TestCmd.TestCmd.diff_re) + + test = TestCmd.TestCmd(diff = TestCmd.TestCmd.simple_diff) + + test = TestCmd.TestCmd(diff = TestCmd.TestCmd.context_diff) + + test = TestCmd.TestCmd(diff = TestCmd.TestCmd.unified_diff) + +These static methods can be accessed by a string naming the method: + + import TestCmd + + test = TestCmd.TestCmd(match = 'match_re', diff = 'diff_re') + + test = TestCmd.TestCmd(diff = 'simple_diff') + + test = TestCmd.TestCmd(diff = 'context_diff') + + test = TestCmd.TestCmd(diff = 'unified_diff') + +The "diff" argument can also be used with standard difflib functions: + + import difflib + + test = TestCmd.TestCmd(diff = difflib.context_diff) + + test = TestCmd.TestCmd(diff = difflib.unified_diff) + +Lastly, the where_is() method also exists in an unbound function +version. + + import TestCmd + + TestCmd.where_is('foo') + TestCmd.where_is('foo', 'PATH1:PATH2') + TestCmd.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') +""" + +# Copyright 2000-2010 Steven Knight +# This module is free software, and you may redistribute it and/or modify +# it under the same terms as Python itself, so long as this copyright message +# and disclaimer are retained in their original form. +# +# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# +# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +from __future__ import division, print_function + +__author__ = "Steven Knight " +__revision__ = "TestCmd.py 1.3.D001 2010/06/03 12:58:27 knight" +__version__ = "1.3" + +import atexit +import difflib +import errno +import os +import re +import shutil +import signal +import stat +import sys +import tempfile +import threading +import time +import traceback +import types + + +IS_PY3 = sys.version_info[0] == 3 +IS_WINDOWS = sys.platform == 'win32' + + +class null(object): + pass + + +_Null = null() + +try: + from collections import UserList, UserString +except ImportError: + # no 'collections' module or no UserFoo in collections + exec('from UserList import UserList') + exec('from UserString import UserString') + +__all__ = [ + 'diff_re', + 'fail_test', + 'no_result', + 'pass_test', + 'match_exact', + 'match_caseinsensitive', + 'match_re', + 'match_re_dotall', + 'python', + '_python_', + 'TestCmd', + 'to_bytes', + 'to_str', +] + + +def is_List(e): + return isinstance(e, (list, UserList)) + + +def to_bytes(s): + if isinstance(s, bytes) or bytes is str: + return s + return bytes(s, 'utf-8') + + +def to_str(s): + if bytes is str or is_String(s): + return s + return str(s, 'utf-8') + + +try: + eval('unicode') +except NameError: + def is_String(e): + return isinstance(e, (str, UserString)) +else: + def is_String(e): + return isinstance(e, (str, unicode, UserString)) + +tempfile.template = 'testcmd.' +if os.name in ('posix', 'nt'): + tempfile.template = 'testcmd.' + str(os.getpid()) + '.' +else: + tempfile.template = 'testcmd.' + +re_space = re.compile('\s') + + +def _caller(tblist, skip): + string = "" + arr = [] + for file, line, name, text in tblist: + if file[-10:] == "TestCmd.py": + break + arr = [(file, line, name, text)] + arr + atfrom = "at" + for file, line, name, text in arr[skip:]: + if name in ("?", ""): + name = "" + else: + name = " (" + name + ")" + string = string + ("%s line %d of %s%s\n" % (atfrom, line, file, name)) + atfrom = "\tfrom" + return string + + +def fail_test(self=None, condition=1, function=None, skip=0, message=None): + """Cause the test to fail. + + By default, the fail_test() method reports that the test FAILED + and exits with a status of 1. If a condition argument is supplied, + the test fails only if the condition is true. + """ + if not condition: + return + if not function is None: + function() + of = "" + desc = "" + sep = " " + if not self is None: + if self.program: + of = " of " + self.program + sep = "\n\t" + if self.description: + desc = " [" + self.description + "]" + sep = "\n\t" + + at = _caller(traceback.extract_stack(), skip) + if message: + msg = "\t%s\n" % message + else: + msg = "" + sys.stderr.write("FAILED test" + of + desc + sep + at + msg) + + sys.exit(1) + + +def no_result(self=None, condition=1, function=None, skip=0): + """Causes a test to exit with no valid result. + + By default, the no_result() method reports NO RESULT for the test + and exits with a status of 2. If a condition argument is supplied, + the test fails only if the condition is true. + """ + if not condition: + return + if not function is None: + function() + of = "" + desc = "" + sep = " " + if not self is None: + if self.program: + of = " of " + self.program + sep = "\n\t" + if self.description: + desc = " [" + self.description + "]" + sep = "\n\t" + + at = _caller(traceback.extract_stack(), skip) + sys.stderr.write("NO RESULT for test" + of + desc + sep + at) + + sys.exit(2) + + +def pass_test(self=None, condition=1, function=None): + """Causes a test to pass. + + By default, the pass_test() method reports PASSED for the test + and exits with a status of 0. If a condition argument is supplied, + the test passes only if the condition is true. + """ + if not condition: + return + if not function is None: + function() + sys.stderr.write("PASSED\n") + sys.exit(0) + + +def match_exact(lines=None, matches=None, newline=os.sep): + """ + """ + + if isinstance(lines, bytes) or bytes is str: + newline = to_bytes(newline) + + if not is_List(lines): + lines = lines.split(newline) + if not is_List(matches): + matches = matches.split(newline) + if len(lines) != len(matches): + return + for i in range(len(lines)): + if lines[i] != matches[i]: + return + return 1 + + +def match_caseinsensitive(lines=None, matches=None): + """ + """ + if not is_List(lines): + lines = lines.split("\n") + if not is_List(matches): + matches = matches.split("\n") + if len(lines) != len(matches): + return + for i in range(len(lines)): + if lines[i].lower() != matches[i].lower(): + return + return 1 + + +def match_re(lines=None, res=None): + """ + """ + if not is_List(lines): + # CRs mess up matching (Windows) so split carefully + lines = re.split('\r?\n', lines) + if not is_List(res): + res = res.split("\n") + if len(lines) != len(res): + print("match_re: expected %d lines, found %d" % (len(res), len(lines))) + return + for i in range(len(lines)): + s = "^" + res[i] + "$" + try: + expr = re.compile(s) + except re.error as e: + msg = "Regular expression error in %s: %s" + raise re.error(msg % (repr(s), e.args[0])) + if not expr.search(lines[i]): + print("match_re: mismatch at line %d:\n search re='%s'\n line='%s'" % ( + i, s, lines[i])) + return + return 1 + + +def match_re_dotall(lines=None, res=None): + """ + """ + if not isinstance(lines, str): + lines = "\n".join(lines) + if not isinstance(res, str): + res = "\n".join(res) + s = "^" + res + "$" + try: + expr = re.compile(s, re.DOTALL) + except re.error as e: + msg = "Regular expression error in %s: %s" + raise re.error(msg % (repr(s), e.args[0])) + return expr.match(lines) + + +def simple_diff(a, b, fromfile='', tofile='', + fromfiledate='', tofiledate='', n=3, lineterm='\n'): + """ + A function with the same calling signature as difflib.context_diff + (diff -c) and difflib.unified_diff (diff -u) but which prints + output like the simple, unadorned 'diff" command. + """ + a = [to_str(q) for q in a] + b = [to_str(q) for q in b] + sm = difflib.SequenceMatcher(None, a, b) + + def comma(x1, x2): + return x1 + 1 == x2 and str(x2) or '%s,%s' % (x1 + 1, x2) + result = [] + for op, a1, a2, b1, b2 in sm.get_opcodes(): + if op == 'delete': + result.append("%sd%d" % (comma(a1, a2), b1)) + result.extend(['< ' + l for l in a[a1:a2]]) + elif op == 'insert': + result.append("%da%s" % (a1, comma(b1, b2))) + result.extend(['> ' + l for l in b[b1:b2]]) + elif op == 'replace': + result.append("%sc%s" % (comma(a1, a2), comma(b1, b2))) + result.extend(['< ' + l for l in a[a1:a2]]) + result.append('---') + result.extend(['> ' + l for l in b[b1:b2]]) + return result + + +def diff_re(a, b, fromfile='', tofile='', + fromfiledate='', tofiledate='', n=3, lineterm='\n'): + """ + A simple "diff" of two sets of lines when the expected lines + are regular expressions. This is a really dumb thing that + just compares each line in turn, so it doesn't look for + chunks of matching lines and the like--but at least it lets + you know exactly which line first didn't compare correctl... + """ + result = [] + diff = len(a) - len(b) + if diff < 0: + a = a + [''] * (-diff) + elif diff > 0: + b = b + [''] * diff + i = 0 + for aline, bline in zip(a, b): + s = "^" + aline + "$" + try: + expr = re.compile(s) + except re.error as e: + msg = "Regular expression error in %s: %s" + raise re.error(msg % (repr(s), e.args[0])) + if not expr.search(bline): + result.append("%sc%s" % (i + 1, i + 1)) + result.append('< ' + repr(a[i])) + result.append('---') + result.append('> ' + repr(b[i])) + i = i + 1 + return result + + +if os.name == 'posix': + def escape(arg): + "escape shell special characters" + slash = '\\' + special = '"$' + arg = arg.replace(slash, slash + slash) + for c in special: + arg = arg.replace(c, slash + c) + if re_space.search(arg): + arg = '"' + arg + '"' + return arg +else: + # Windows does not allow special characters in file names + # anyway, so no need for an escape function, we will just quote + # the arg. + def escape(arg): + if re_space.search(arg): + arg = '"' + arg + '"' + return arg + +if os.name == 'java': + python = os.path.join(sys.prefix, 'jython') +else: + python = os.environ.get('python_executable', sys.executable) +_python_ = escape(python) + +if sys.platform == 'win32': + + default_sleep_seconds = 2 + + def where_is(file, path=None, pathext=None): + if path is None: + path = os.environ['PATH'] + if is_String(path): + path = path.split(os.pathsep) + if pathext is None: + pathext = os.environ['PATHEXT'] + if is_String(pathext): + pathext = pathext.split(os.pathsep) + for ext in pathext: + if ext.lower() == file[-len(ext):].lower(): + pathext = [''] + break + for dir in path: + f = os.path.join(dir, file) + for ext in pathext: + fext = f + ext + if os.path.isfile(fext): + return fext + return None + +else: + + def where_is(file, path=None, pathext=None): + if path is None: + path = os.environ['PATH'] + if is_String(path): + path = path.split(os.pathsep) + for dir in path: + f = os.path.join(dir, file) + if os.path.isfile(f): + try: + st = os.stat(f) + except OSError: + continue + if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: + return f + return None + + default_sleep_seconds = 1 + + +import subprocess + +try: + subprocess.Popen.terminate +except AttributeError: + if sys.platform == 'win32': + import win32process + + def terminate(self): + win32process.TerminateProcess(self._handle, 1) + else: + def terminate(self): + os.kill(self.pid, signal.SIGTERM) + method = types.MethodType(terminate, None, subprocess.Popen) + setattr(subprocess.Popen, 'terminate', method) + + +# From Josiah Carlson, +# ASPN : Python Cookbook : Module to allow Asynchronous subprocess use on Windows and Posix platforms +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 + +PIPE = subprocess.PIPE + +if sys.platform == 'win32': # and subprocess.mswindows: + try: + from win32file import ReadFile, WriteFile + from win32pipe import PeekNamedPipe + except ImportError: + # If PyWin32 is not available, try ctypes instead + # XXX These replicate _just_enough_ PyWin32 behaviour for our purposes + import ctypes + from ctypes.wintypes import DWORD + + def ReadFile(hFile, bufSize, ol=None): + assert ol is None + lpBuffer = ctypes.create_string_buffer(bufSize) + bytesRead = DWORD() + bErr = ctypes.windll.kernel32.ReadFile( + hFile, lpBuffer, bufSize, ctypes.byref(bytesRead), ol) + if not bErr: + raise ctypes.WinError() + return (0, ctypes.string_at(lpBuffer, bytesRead.value)) + + def WriteFile(hFile, data, ol=None): + assert ol is None + bytesWritten = DWORD() + bErr = ctypes.windll.kernel32.WriteFile( + hFile, data, len(data), ctypes.byref(bytesWritten), ol) + if not bErr: + raise ctypes.WinError() + return (0, bytesWritten.value) + + def PeekNamedPipe(hPipe, size): + assert size == 0 + bytesAvail = DWORD() + bErr = ctypes.windll.kernel32.PeekNamedPipe( + hPipe, None, size, None, ctypes.byref(bytesAvail), None) + if not bErr: + raise ctypes.WinError() + return ("", bytesAvail.value, None) + import msvcrt +else: + import select + import fcntl + + try: + fcntl.F_GETFL + except AttributeError: + fcntl.F_GETFL = 3 + + try: + fcntl.F_SETFL + except AttributeError: + fcntl.F_SETFL = 4 + + +class Popen(subprocess.Popen): + def recv(self, maxsize=None): + return self._recv('stdout', maxsize) + + def recv_err(self, maxsize=None): + return self._recv('stderr', maxsize) + + def send_recv(self, input='', maxsize=None): + return self.send(input), self.recv(maxsize), self.recv_err(maxsize) + + def get_conn_maxsize(self, which, maxsize): + if maxsize is None: + maxsize = 1024 + elif maxsize < 1: + maxsize = 1 + return getattr(self, which), maxsize + + def _close(self, which): + getattr(self, which).close() + setattr(self, which, None) + + if sys.platform == 'win32': # and subprocess.mswindows: + def send(self, input): + input = to_bytes(input) + if not self.stdin: + return None + + try: + x = msvcrt.get_osfhandle(self.stdin.fileno()) + (errCode, written) = WriteFile(x, input) + except ValueError: + return self._close('stdin') + except (subprocess.pywintypes.error, Exception) as why: + if why.args[0] in (109, errno.ESHUTDOWN): + return self._close('stdin') + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + try: + x = msvcrt.get_osfhandle(conn.fileno()) + (read, nAvail, nMessage) = PeekNamedPipe(x, 0) + if maxsize < nAvail: + nAvail = maxsize + if nAvail > 0: + (errCode, read) = ReadFile(x, nAvail, None) + except ValueError: + return self._close(which) + except (subprocess.pywintypes.error, Exception) as why: + if why.args[0] in (109, errno.ESHUTDOWN): + return self._close(which) + raise + + # if self.universal_newlines: + # read = self._translate_newlines(read) + return read + + else: + def send(self, input): + if not self.stdin: + return None + + if not select.select([], [self.stdin], [], 0)[1]: + return 0 + + try: + written = os.write(self.stdin.fileno(), + bytearray(input, 'utf-8')) + except OSError as why: + if why.args[0] == errno.EPIPE: # broken pipe + return self._close('stdin') + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + try: + flags = fcntl.fcntl(conn, fcntl.F_GETFL) + except TypeError: + flags = None + else: + if not conn.closed: + fcntl.fcntl(conn, fcntl.F_SETFL, flags | os.O_NONBLOCK) + + try: + if not select.select([conn], [], [], 0)[0]: + return '' + + r = conn.read(maxsize) + if not r: + return self._close(which) + + # if self.universal_newlines: + # r = self._translate_newlines(r) + return r + finally: + if not conn.closed and not flags is None: + fcntl.fcntl(conn, fcntl.F_SETFL, flags) + + +disconnect_message = "Other end disconnected!" + + +def recv_some(p, t=.1, e=1, tr=5, stderr=0): + if tr < 1: + tr = 1 + x = time.time() + t + y = [] + r = '' + pr = p.recv + if stderr: + pr = p.recv_err + while time.time() < x or r: + r = pr() + if r is None: + if e: + raise Exception(disconnect_message) + else: + break + elif r: + y.append(r) + else: + time.sleep(max((x - time.time()) / tr, 0)) + return ''.join(y) + + +def send_all(p, data): + while len(data): + sent = p.send(data) + if sent is None: + raise Exception(disconnect_message) + data = memoryview(data)[sent:] + + +_Cleanup = [] + + +def _clean(): + global _Cleanup + cleanlist = [c for c in _Cleanup if c] + del _Cleanup[:] + cleanlist.reverse() + for test in cleanlist: + test.cleanup() + + +atexit.register(_clean) + + +class TestCmd(object): + """Class TestCmd + """ + + def __init__(self, description=None, + program=None, + interpreter=None, + workdir=None, + subdir=None, + verbose=None, + match=None, + match_stdout=None, + match_stderr=None, + diff=None, + diff_stdout=None, + diff_stderr=None, + combine=0, + universal_newlines=True, + timeout=None): + self.external = os.environ.get('SCONS_EXTERNAL_TEST', 0) + self._cwd = os.getcwd() + self.description_set(description) + self.program_set(program) + self.interpreter_set(interpreter) + if verbose is None: + try: + verbose = max(0, int(os.environ.get('TESTCMD_VERBOSE', 0))) + except ValueError: + verbose = 0 + self.verbose_set(verbose) + self.combine = combine + self.universal_newlines = universal_newlines + self.process = None + self.set_timeout(timeout) + self.set_match_function(match, match_stdout, match_stderr) + self.set_diff_function(diff, diff_stdout, diff_stderr) + self._dirlist = [] + self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0} + preserve_value = os.environ.get('PRESERVE', False) + if preserve_value not in [0, '0', 'False']: + self._preserve['pass_test'] = os.environ['PRESERVE'] + self._preserve['fail_test'] = os.environ['PRESERVE'] + self._preserve['no_result'] = os.environ['PRESERVE'] + else: + try: + self._preserve['pass_test'] = os.environ['PRESERVE_PASS'] + except KeyError: + pass + try: + self._preserve['fail_test'] = os.environ['PRESERVE_FAIL'] + except KeyError: + pass + try: + self._preserve['no_result'] = os.environ['PRESERVE_NO_RESULT'] + except KeyError: + pass + self._stdout = [] + self._stderr = [] + self.status = None + self.condition = 'no_result' + self.workdir_set(workdir) + self.subdir(subdir) + self.fixture_dirs = [] + + def __del__(self): + self.cleanup() + + def __repr__(self): + return "%x" % id(self) + + banner_char = '=' + banner_width = 80 + + def banner(self, s, width=None): + if width is None: + width = self.banner_width + return s + self.banner_char * (width - len(s)) + + escape = staticmethod(escape) + + def canonicalize(self, path): + if is_List(path): + path = os.path.join(*tuple(path)) + if not os.path.isabs(path): + path = os.path.join(self.workdir, path) + return path + + def chmod(self, path, mode): + """Changes permissions on the specified file or directory + path name.""" + path = self.canonicalize(path) + os.chmod(path, mode) + + def cleanup(self, condition=None): + """Removes any temporary working directories for the specified + TestCmd environment. If the environment variable PRESERVE was + set when the TestCmd environment was created, temporary working + directories are not removed. If any of the environment variables + PRESERVE_PASS, PRESERVE_FAIL, or PRESERVE_NO_RESULT were set + when the TestCmd environment was created, then temporary working + directories are not removed if the test passed, failed, or had + no result, respectively. Temporary working directories are also + preserved for conditions specified via the preserve method. + + Typically, this method is not called directly, but is used when + the script exits to clean up temporary working directories as + appropriate for the exit status. + """ + if not self._dirlist: + return + os.chdir(self._cwd) + self.workdir = None + if condition is None: + condition = self.condition + if self._preserve[condition]: + for dir in self._dirlist: + print(u"Preserved directory " + dir + "\n") + else: + list = self._dirlist[:] + list.reverse() + for dir in list: + self.writable(dir, 1) + shutil.rmtree(dir, ignore_errors=1) + self._dirlist = [] + + global _Cleanup + if self in _Cleanup: + _Cleanup.remove(self) + + def command_args(self, program=None, + interpreter=None, + arguments=None): + if not self.external: + if program: + if isinstance(program, str) and not os.path.isabs(program): + program = os.path.join(self._cwd, program) + else: + program = self.program + if not interpreter: + interpreter = self.interpreter + else: + if not program: + program = self.program + if not interpreter: + interpreter = self.interpreter + if not isinstance(program, (list, tuple)): + program = [program] + cmd = list(program) + if interpreter: + if not isinstance(interpreter, (list, tuple)): + interpreter = [interpreter] + cmd = list(interpreter) + cmd + if arguments: + if isinstance(arguments, str): + arguments = arguments.split() + cmd.extend(arguments) + return cmd + + def description_set(self, description): + """Set the description of the functionality being tested. + """ + self.description = description + + def set_diff_function(self, diff=_Null, stdout=_Null, stderr=_Null): + """Sets the specified diff functions. + """ + if diff is not _Null: + self._diff_function = diff + if stdout is not _Null: + self._diff_stdout_function = stdout + if stderr is not _Null: + self._diff_stderr_function = stderr + + def diff(self, a, b, name=None, diff_function=None, *args, **kw): + if diff_function is None: + try: + diff_function = getattr(self, self._diff_function) + except TypeError: + diff_function = self._diff_function + if diff_function is None: + diff_function = self.simple_diff + if name is not None: + print(self.banner(name)) + args = (a.splitlines(), b.splitlines()) + args + for line in diff_function(*args, **kw): + print(line) + + def diff_stderr(self, a, b, *args, **kw): + """Compare actual and expected file contents. + """ + try: + diff_stderr_function = getattr(self, self._diff_stderr_function) + except TypeError: + diff_stderr_function = self._diff_stderr_function + return self.diff(a, b, diff_function=diff_stderr_function, *args, **kw) + + def diff_stdout(self, a, b, *args, **kw): + """Compare actual and expected file contents. + """ + try: + diff_stdout_function = getattr(self, self._diff_stdout_function) + except TypeError: + diff_stdout_function = self._diff_stdout_function + return self.diff(a, b, diff_function=diff_stdout_function, *args, **kw) + + simple_diff = staticmethod(simple_diff) + + diff_re = staticmethod(diff_re) + + context_diff = staticmethod(difflib.context_diff) + + unified_diff = staticmethod(difflib.unified_diff) + + def fail_test(self, condition=1, function=None, skip=0, message=None): + """Cause the test to fail. + """ + if not condition: + return + self.condition = 'fail_test' + fail_test(self=self, + condition=condition, + function=function, + skip=skip, + message=message) + + def interpreter_set(self, interpreter): + """Set the program to be used to interpret the program + under test as a script. + """ + self.interpreter = interpreter + + def set_match_function(self, match=_Null, stdout=_Null, stderr=_Null): + """Sets the specified match functions. + """ + if match is not _Null: + self._match_function = match + if stdout is not _Null: + self._match_stdout_function = stdout + if stderr is not _Null: + self._match_stderr_function = stderr + + def match(self, lines, matches): + """Compare actual and expected file contents. + """ + try: + match_function = getattr(self, self._match_function) + except TypeError: + match_function = self._match_function + if match_function is None: + # Default is regular expression matches. + match_function = self.match_re + return match_function(lines, matches) + + def match_stderr(self, lines, matches): + """Compare actual and expected file contents. + """ + try: + match_stderr_function = getattr(self, self._match_stderr_function) + except TypeError: + match_stderr_function = self._match_stderr_function + if match_stderr_function is None: + # Default is to use whatever match= is set to. + match_stderr_function = self.match + return match_stderr_function(lines, matches) + + def match_stdout(self, lines, matches): + """Compare actual and expected file contents. + """ + try: + match_stdout_function = getattr(self, self._match_stdout_function) + except TypeError: + match_stdout_function = self._match_stdout_function + if match_stdout_function is None: + # Default is to use whatever match= is set to. + match_stdout_function = self.match + return match_stdout_function(lines, matches) + + match_exact = staticmethod(match_exact) + + match_caseinsensitive = staticmethod(match_caseinsensitive) + + match_re = staticmethod(match_re) + + match_re_dotall = staticmethod(match_re_dotall) + + def no_result(self, condition=1, function=None, skip=0): + """Report that the test could not be run. + """ + if not condition: + return + self.condition = 'no_result' + no_result(self=self, + condition=condition, + function=function, + skip=skip) + + def pass_test(self, condition=1, function=None): + """Cause the test to pass. + """ + if not condition: + return + self.condition = 'pass_test' + pass_test(self=self, condition=condition, function=function) + + def preserve(self, *conditions): + """Arrange for the temporary working directories for the + specified TestCmd environment to be preserved for one or more + conditions. If no conditions are specified, arranges for + the temporary working directories to be preserved for all + conditions. + """ + if conditions is (): + conditions = ('pass_test', 'fail_test', 'no_result') + for cond in conditions: + self._preserve[cond] = 1 + + def program_set(self, program): + """Set the executable program or script to be tested. + """ + if not self.external: + if program and not os.path.isabs(program): + program = os.path.join(self._cwd, program) + self.program = program + + def read(self, file, mode='rb', newline=None): + """Reads and returns the contents of the specified file name. + The file name may be a list, in which case the elements are + concatenated with the os.path.join() method. The file is + assumed to be under the temporary working directory unless it + is an absolute path name. The I/O mode for the file may + be specified; it must begin with an 'r'. The default is + 'rb' (binary read). + """ + file = self.canonicalize(file) + if mode[0] != 'r': + raise ValueError("mode must begin with 'r'") + if IS_PY3 and 'b' not in mode: + return open(file, mode, newline=newline).read() + else: + return open(file, mode).read() + + def rmdir(self, dir): + """Removes the specified dir name. + The dir name may be a list, in which case the elements are + concatenated with the os.path.join() method. The dir is + assumed to be under the temporary working directory unless it + is an absolute path name. + The dir must be empty. + """ + dir = self.canonicalize(dir) + os.rmdir(dir) + + def _timeout(self): + self.process.terminate() + self.timer.cancel() + self.timer = None + + def set_timeout(self, timeout): + self.timeout = timeout + self.timer = None + + def parse_path(self, path, suppress_current=False): + """Return a list with the single path components of path. + """ + head, tail = os.path.split(path) + result = [] + if not tail: + if head == path: + return [head] + else: + result.append(tail) + head, tail = os.path.split(head) + while head and tail: + result.append(tail) + head, tail = os.path.split(head) + result.append(head or tail) + result.reverse() + + return result + + def dir_fixture(self, srcdir, dstdir=None): + """Copies the contents of the specified folder srcdir from + the directory of the called script, to the current + working directory. + The srcdir name may be a list, in which case the elements are + concatenated with the os.path.join() method. The dstdir is + assumed to be under the temporary working directory, it gets + created automatically, if it does not already exist. + """ + + if srcdir and self.fixture_dirs and not os.path.isabs(srcdir): + for dir in self.fixture_dirs: + spath = os.path.join(dir, srcdir) + if os.path.isdir(spath): + break + else: + spath = srcdir + + if dstdir: + dstdir = self.canonicalize(dstdir) + else: + dstdir = '.' + + if dstdir != '.' and not os.path.exists(dstdir): + dstlist = self.parse_path(dstdir) + if len(dstlist) > 0 and dstlist[0] == ".": + dstlist = dstlist[1:] + for idx in range(len(dstlist)): + self.subdir(dstlist[:idx + 1]) + + if dstdir and self.workdir: + dstdir = os.path.join(self.workdir, dstdir) + + for entry in os.listdir(spath): + epath = os.path.join(spath, entry) + dpath = os.path.join(dstdir, entry) + if os.path.isdir(epath): + # Copy the subfolder + shutil.copytree(epath, dpath) + else: + shutil.copy(epath, dpath) + + def file_fixture(self, srcfile, dstfile=None): + """Copies the file srcfile from the directory of + the called script, to the current working directory. + The dstfile is assumed to be under the temporary working + directory unless it is an absolute path name. + If dstfile is specified its target directory gets created + automatically, if it does not already exist. + """ + srcpath, srctail = os.path.split(srcfile) + + if srcpath and (not self.fixture_dirs or os.path.isabs(srcpath)): + spath = srcfile + else: + for dir in self.fixture_dirs: + spath = os.path.join(dir, srcfile) + if os.path.isfile(spath): + break + + if not dstfile: + if srctail: + dpath = os.path.join(self.workdir, srctail) + else: + return + else: + dstpath, dsttail = os.path.split(dstfile) + if dstpath: + if not os.path.exists(os.path.join(self.workdir, dstpath)): + dstlist = self.parse_path(dstpath) + if len(dstlist) > 0 and dstlist[0] == ".": + dstlist = dstlist[1:] + for idx in range(len(dstlist)): + self.subdir(dstlist[:idx + 1]) + + dpath = os.path.join(self.workdir, dstfile) + shutil.copy(spath, dpath) + + def start(self, program=None, + interpreter=None, + arguments=None, + universal_newlines=None, + timeout=_Null, + **kw): + """ + Starts a program or script for the test environment. + + The specified program will have the original directory + prepended unless it is enclosed in a [list]. + """ + cmd = self.command_args(program, interpreter, arguments) + if self.verbose: + cmd_string = ' '.join([self.escape(c) for c in cmd]) + sys.stderr.write(cmd_string + "\n") + if universal_newlines is None: + universal_newlines = self.universal_newlines + + # On Windows, if we make stdin a pipe when we plan to send + # no input, and the test program exits before + # Popen calls msvcrt.open_osfhandle, that call will fail. + # So don't use a pipe for stdin if we don't need one. + stdin = kw.get('stdin', None) + if stdin is not None: + stdin = subprocess.PIPE + + combine = kw.get('combine', self.combine) + if combine: + stderr_value = subprocess.STDOUT + else: + stderr_value = subprocess.PIPE + + if timeout is _Null: + timeout = self.timeout + if timeout: + self.timer = threading.Timer(float(timeout), self._timeout) + self.timer.start() + + if IS_PY3 and sys.platform == 'win32': + # Set this otherwist stdout/stderr pipes default to + # windows default locale cp1252 which will throw exception + # if using non-ascii characters. + # For example test/Install/non-ascii-name.py + os.environ['PYTHONIOENCODING'] = 'utf-8' + + # It seems that all pythons up to py3.6 still set text mode if you set encoding. + # TODO: File enhancement request on python to propagate universal_newlines even + # if encoding is set.hg c + p = Popen(cmd, + stdin=stdin, + stdout=subprocess.PIPE, + stderr=stderr_value, + env=os.environ, + universal_newlines=False) + + self.process = p + return p + + @staticmethod + def fix_binary_stream(stream): + """ + Handle stdout/stderr from popen when we specify universal_newlines = False. + This will read from the pipes in binary mode, not decode the output, + and not convert line endings to \n. + We do this because in py3 (3.5) with universal_newlines=True, it will + choose the default system locale to decode the output, and this breaks unicode + output. Specifically breaking test/option--tree.py which outputs a unicode char. + + py 3.6 allows us to pass an encoding param to popen thus not requiring the decode + nor end of line handling, because we propagate universal_newlines as specified. + + TODO: Do we need to pass universal newlines into this function? + """ + + if not stream: + return stream + # TODO: Run full tests on both platforms and see if this fixes failures + # It seems that py3.6 still sets text mode if you set encoding. + elif sys.version_info[0] == 3:# TODO and sys.version_info[1] < 6: + stream = stream.decode('utf-8') + stream = stream.replace('\r\n', '\n') + elif sys.version_info[0] == 2: + stream = stream.replace('\r\n', '\n') + + return stream + + + def finish(self, popen=None, **kw): + """ + Finishes and waits for the process being run under control of + the specified popen argument, recording the exit status, + standard output and error output. + """ + if popen is None: + popen = self.process + stdout, stderr = popen.communicate() + + stdout = self.fix_binary_stream(stdout) + stderr = self.fix_binary_stream(stderr) + + if self.timer: + self.timer.cancel() + self.timer = None + self.status = popen.returncode + self.process = None + self._stdout.append(stdout or '') + self._stderr.append(stderr or '') + + def run(self, program=None, + interpreter=None, + arguments=None, + chdir=None, + stdin=None, + universal_newlines=None, + timeout=_Null): + """Runs a test of the program or script for the test + environment. Standard output and error output are saved for + future retrieval via the stdout() and stderr() methods. + + The specified program will have the original directory + prepended unless it is enclosed in a [list]. + """ + if self.external: + if not program: + program = self.program + if not interpreter: + interpreter = self.interpreter + + if universal_newlines is None: + universal_newlines = self.universal_newlines + + if chdir: + oldcwd = os.getcwd() + if not os.path.isabs(chdir): + chdir = os.path.join(self.workpath(chdir)) + if self.verbose: + sys.stderr.write("chdir(" + chdir + ")\n") + os.chdir(chdir) + p = self.start(program=program, + interpreter=interpreter, + arguments=arguments, + universal_newlines=universal_newlines, + timeout=timeout, + stdin=stdin) + if is_List(stdin): + stdin = ''.join(stdin) + + if stdin and IS_PY3:# and sys.version_info[1] < 6: + stdin = to_bytes(stdin) + + # TODO(sgk): figure out how to re-use the logic in the .finish() + # method above. Just calling it from here causes problems with + # subclasses that redefine .finish(). We could abstract this + # into Yet Another common method called both here and by .finish(), + # but that seems ill-thought-out. + stdout, stderr = p.communicate(input=stdin) + if self.timer: + self.timer.cancel() + self.timer = None + self.status = p.returncode + self.process = None + + stdout = self.fix_binary_stream(stdout) + stderr = self.fix_binary_stream(stderr) + + + self._stdout.append(stdout or '') + self._stderr.append(stderr or '') + + if chdir: + os.chdir(oldcwd) + if self.verbose >= 2: + write = sys.stdout.write + write('============ STATUS: %d\n' % self.status) + out = self.stdout() + if out or self.verbose >= 3: + write('============ BEGIN STDOUT (len=%d):\n' % len(out)) + write(out) + write('============ END STDOUT\n') + err = self.stderr() + if err or self.verbose >= 3: + write('============ BEGIN STDERR (len=%d)\n' % len(err)) + write(err) + write('============ END STDERR\n') + + def sleep(self, seconds=default_sleep_seconds): + """Sleeps at least the specified number of seconds. If no + number is specified, sleeps at least the minimum number of + seconds necessary to advance file time stamps on the current + system. Sleeping more seconds is all right. + """ + time.sleep(seconds) + + def stderr(self, run=None): + """Returns the error output from the specified run number. + If there is no specified run number, then returns the error + output of the last run. If the run number is less than zero, + then returns the error output from that many runs back from the + current run. + """ + if not run: + run = len(self._stderr) + elif run < 0: + run = len(self._stderr) + run + run = run - 1 + return self._stderr[run] + + def stdout(self, run=None): + """Returns the standard output from the specified run number. + If there is no specified run number, then returns the standard + output of the last run. If the run number is less than zero, + then returns the standard output from that many runs back from + the current run. + """ + if not run: + run = len(self._stdout) + elif run < 0: + run = len(self._stdout) + run + run = run - 1 + return self._stdout[run] + + def subdir(self, *subdirs): + """Create new subdirectories under the temporary working + directory, one for each argument. An argument may be a list, + in which case the list elements are concatenated using the + os.path.join() method. Subdirectories multiple levels deep + must be created using a separate argument for each level: + + test.subdir('sub', ['sub', 'dir'], ['sub', 'dir', 'ectory']) + + Returns the number of subdirectories actually created. + """ + count = 0 + for sub in subdirs: + if sub is None: + continue + if is_List(sub): + sub = os.path.join(*tuple(sub)) + new = os.path.join(self.workdir, sub) + try: + os.mkdir(new) + except OSError: + pass + else: + count = count + 1 + return count + + def symlink(self, target, link): + """Creates a symlink to the specified target. + The link name may be a list, in which case the elements are + concatenated with the os.path.join() method. The link is + assumed to be under the temporary working directory unless it + is an absolute path name. The target is *not* assumed to be + under the temporary working directory. + """ + if sys.platform == 'win32': + # Skip this on windows as we're not enabling it due to + # it requiring user permissions which aren't always present + # and we don't have a good way to detect those permissions yet. + return + link = self.canonicalize(link) + try: + os.symlink(target, link) + except AttributeError: + pass # Windows has no symlink + + def tempdir(self, path=None): + """Creates a temporary directory. + A unique directory name is generated if no path name is specified. + The directory is created, and will be removed when the TestCmd + object is destroyed. + """ + if path is None: + try: + path = tempfile.mktemp(prefix=tempfile.template) + except TypeError: + path = tempfile.mktemp() + os.mkdir(path) + + # Symlinks in the path will report things + # differently from os.getcwd(), so chdir there + # and back to fetch the canonical path. + cwd = os.getcwd() + try: + os.chdir(path) + path = os.getcwd() + finally: + os.chdir(cwd) + + # Uppercase the drive letter since the case of drive + # letters is pretty much random on win32: + drive, rest = os.path.splitdrive(path) + if drive: + path = drive.upper() + rest + + # + self._dirlist.append(path) + + global _Cleanup + if self not in _Cleanup: + _Cleanup.append(self) + + return path + + def touch(self, path, mtime=None): + """Updates the modification time on the specified file or + directory path name. The default is to update to the + current time if no explicit modification time is specified. + """ + path = self.canonicalize(path) + atime = os.path.getatime(path) + if mtime is None: + mtime = time.time() + os.utime(path, (atime, mtime)) + + def unlink(self, file): + """Unlinks the specified file name. + The file name may be a list, in which case the elements are + concatenated with the os.path.join() method. The file is + assumed to be under the temporary working directory unless it + is an absolute path name. + """ + file = self.canonicalize(file) + os.unlink(file) + + def verbose_set(self, verbose): + """Set the verbose level. + """ + self.verbose = verbose + + def where_is(self, file, path=None, pathext=None): + """Find an executable file. + """ + if is_List(file): + file = os.path.join(*tuple(file)) + if not os.path.isabs(file): + file = where_is(file, path, pathext) + return file + + def workdir_set(self, path): + """Creates a temporary working directory with the specified + path name. If the path is a null string (''), a unique + directory name is created. + """ + if (path != None): + if path == '': + path = None + path = self.tempdir(path) + self.workdir = path + + def workpath(self, *args): + """Returns the absolute path name to a subdirectory or file + within the current temporary working directory. Concatenates + the temporary working directory name with the specified + arguments using the os.path.join() method. + """ + return os.path.join(self.workdir, *tuple(args)) + + def readable(self, top, read=1): + """Make the specified directory tree readable (read == 1) + or not (read == None). + + This method has no effect on Windows systems, which use a + completely different mechanism to control file readability. + """ + + if sys.platform == 'win32': + return + + 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] | stat.S_IREAD)) + else: + def do_chmod(fname): + try: + st = os.stat(fname) + except OSError: + pass + else: + os.chmod(fname, stat.S_IMODE( + st[stat.ST_MODE] & ~stat.S_IREAD)) + + 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. + do_chmod(top) + for dirpath, dirnames, filenames in os.walk(top): + for name in dirnames + filenames: + do_chmod(os.path.join(dirpath, name)) + else: + # It's a directory and we're trying to turn off read + # permission, which means we have to chmod the directories + # 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. + for dirpath, dirnames, filenames in os.walk(top, topdown=0): + for name in dirnames + filenames: + do_chmod(os.path.join(dirpath, name)) + do_chmod(top) + + def writable(self, top, write=1): + """Make the specified directory tree writable (write == 1) + or not (write == None). + """ + + if sys.platform == 'win32': + + if write: + def do_chmod(fname): + try: + os.chmod(fname, stat.S_IWRITE) + except OSError: + pass + else: + def do_chmod(fname): + try: + os.chmod(fname, stat.S_IREAD) + except OSError: + pass + + else: + + 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] | 0o200)) + else: + def do_chmod(fname): + try: + st = os.stat(fname) + except OSError: + pass + else: + os.chmod(fname, stat.S_IMODE( + st[stat.ST_MODE] & ~0o200)) + + if os.path.isfile(top): + do_chmod(top) + else: + do_chmod(top) + for dirpath, dirnames, filenames in os.walk(top, topdown=0): + for name in dirnames + filenames: + do_chmod(os.path.join(dirpath, name)) + + def executable(self, top, execute=1): + """Make the specified directory tree executable (execute == 1) + or not (execute == None). + + This method has no effect on Windows systems, which use a + completely different mechanism to control file executability. + """ + + if sys.platform == 'win32': + return + + 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] | stat.S_IEXEC)) + else: + def do_chmod(fname): + try: + st = os.stat(fname) + except OSError: + pass + else: + os.chmod(fname, stat.S_IMODE( + st[stat.ST_MODE] & ~stat.S_IEXEC)) + + if os.path.isfile(top): + # 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. + do_chmod(top) + for dirpath, dirnames, filenames in os.walk(top): + for name in dirnames + filenames: + do_chmod(os.path.join(dirpath, name)) + else: + # 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. + for dirpath, dirnames, filenames in os.walk(top, topdown=0): + for name in dirnames + filenames: + do_chmod(os.path.join(dirpath, name)) + do_chmod(top) + + def write(self, file, content, mode='wb'): + """Writes the specified content text (second argument) to the + specified file name (first argument). The file name may be + a list, in which case the elements are concatenated with the + os.path.join() method. The file is created under the temporary + working directory. Any subdirectories in the path must already + exist. The I/O mode for the file may be specified; it must + begin with a 'w'. The default is 'wb' (binary write). + """ + file = self.canonicalize(file) + if mode[0] != 'w': + raise ValueError("mode must begin with 'w'") + with open(file, mode) as f: + try: + f.write(content) + except TypeError as e: + # python 3 default strings are not bytes, but unicode + f.write(bytes(content, 'utf-8')) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py new file mode 100644 index 0000000..b9226fd --- /dev/null +++ b/testing/framework/TestCmdTests.py @@ -0,0 +1,3419 @@ +#!/usr/bin/env python +""" +TestCmdTests.py: Unit tests for the TestCmd.py module. + +Copyright 2000-2010 Steven Knight +This module is free software, and you may redistribute it and/or modify +it under the same terms as Python itself, so long as this copyright message +and disclaimer are retained in their original form. + +IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +""" + +__author__ = "Steven Knight " +__revision__ = "TestCmdTests.py 1.3.D001 2010/06/03 12:58:27 knight" + +import os +import shutil +import signal +import stat +from StringIO import StringIO +import sys +import tempfile +import time +import types +import unittest +from UserList import UserList + + +# Strip the current directory so we get the right TestCmd.py module. +sys.path = sys.path[1:] + +import TestCmd + +def _is_readable(path): + # XXX this doesn't take into account UID, it assumes it's our file + return os.stat(path)[stat.ST_MODE] & stat.S_IREAD + +def _is_writable(path): + # XXX this doesn't take into account UID, it assumes it's our file + return os.stat(path)[stat.ST_MODE] & stat.S_IWRITE + +def _is_executable(path): + # XXX this doesn't take into account UID, it assumes it's our file + return os.stat(path)[stat.ST_MODE] & stat.S_IEXEC + +def _clear_dict(dict, *keys): + for key in keys: + try: + dict[key] = '' # del dict[key] + except KeyError: + pass + +import subprocess + +try: + subprocess.Popen.terminate +except AttributeError: + if sys.platform == 'win32': + import win32process + def terminate(self): + win32process.TerminateProcess(self._handle, 1) + else: + def terminate(self): + os.kill(self.pid, signal.SIGTERM) + method = types.MethodType(terminate, None, subprocess.Popen) + setattr(subprocess.Popen, 'terminate', method) + +class ExitError(Exception): + pass + +class TestCmdTestCase(unittest.TestCase): + """Base class for TestCmd test cases, with fixture and utility methods.""" + + def setUp(self): + self.orig_cwd = os.getcwd() + + def tearDown(self): + os.chdir(self.orig_cwd) + + def setup_run_scripts(self): + class T: + pass + + t = T() + + t.script = 'script' + t.scriptx = 'scriptx.bat' + t.script1 = 'script_1.txt' + t.scriptout = 'scriptout' + t.scripterr = 'scripterr' + fmt = "import os, sys; cwd = os.getcwd(); " + \ + "sys.stdout.write('%s: STDOUT: %%s: %%s\\n' %% (cwd, sys.argv[1:])); " + \ + "sys.stderr.write('%s: STDERR: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" + fmtout = "import os, sys; cwd = os.getcwd(); " + \ + "sys.stdout.write('%s: STDOUT: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" + fmterr = "import os, sys; cwd = os.getcwd(); " + \ + "sys.stderr.write('%s: STDERR: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" + text = fmt % (t.script, t.script) + textx = fmt % (t.scriptx, t.scriptx) + if sys.platform == 'win32': + textx = textx.replace('%', '%%') + textx = '@python -c "%s"' % textx + ' %1 %2 %3 %4 %5 %6 %7 %8 %9\n' + else: + textx = '#! /usr/bin/env python\n' + textx + '\n' + text1 = 'A first line to be ignored!\n' + fmt % (t.script1, t.script1) + textout = fmtout % (t.scriptout) + texterr = fmterr % (t.scripterr) + + run_env = TestCmd.TestCmd(workdir = '') + run_env.subdir('sub dir') + t.run_env = run_env + + t.sub_dir = run_env.workpath('sub dir') + t.script_path = run_env.workpath('sub dir', t.script) + t.scriptx_path = run_env.workpath('sub dir', t.scriptx) + t.script1_path = run_env.workpath('sub dir', t.script1) + t.scriptout_path = run_env.workpath('sub dir', t.scriptout) + t.scripterr_path = run_env.workpath('sub dir', t.scripterr) + + run_env.write(t.script_path, text) + run_env.write(t.scriptx_path, textx) + run_env.write(t.script1_path, text1) + run_env.write(t.scriptout_path, textout) + run_env.write(t.scripterr_path, texterr) + + os.chmod(t.script_path, 0o644) # XXX UNIX-specific + os.chmod(t.scriptx_path, 0o755) # XXX UNIX-specific + os.chmod(t.script1_path, 0o644) # XXX UNIX-specific + os.chmod(t.scriptout_path, 0o644) # XXX UNIX-specific + os.chmod(t.scripterr_path, 0o644) # XXX UNIX-specific + + t.orig_cwd = os.getcwd() + + t.workdir = run_env.workpath('sub dir') + os.chdir(t.workdir) + + return t + + def translate_newlines(self, data): + data = data.replace("\r\n", "\n") + return data + + def call_python(self, input, python=None): + if python is None: + python = sys.executable + p = subprocess.Popen(python, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE) + stdout, stderr = p.communicate(input) + stdout = self.translate_newlines(stdout) + stderr = self.translate_newlines(stderr) + return stdout, stderr, p.returncode + + def popen_python(self, input, status=0, stdout="", stderr="", python=None): + if python is None: + python = sys.executable + _stdout, _stderr, _status = self.call_python(input, python) + _stdout = self.translate_newlines(_stdout) + _stderr = self.translate_newlines(_stderr) + assert _status == status, \ + "status = %s, expected %s\n" % (str(_status), str(status)) + \ + "STDOUT ===================\n" + _stdout + \ + "STDERR ===================\n" + _stderr + assert _stdout == stdout, \ + "Expected STDOUT ==========\n" + stdout + \ + "Actual STDOUT ============\n" + _stdout + \ + "STDERR ===================\n" + _stderr + assert _stderr == stderr, \ + "Expected STDERR ==========\n" + stderr + \ + "Actual STDERR ============\n" + _stderr + + def run_match(self, content, *args): + expect = "%s: %s: %s: %s\n" % args + content = self.translate_newlines(content) + assert content == expect, \ + "Expected %s ==========\n" % args[1] + expect + \ + "Actual %s ============\n" % args[1] + content + + + +class __init__TestCase(TestCmdTestCase): + def test_init(self): + """Test init()""" + test = TestCmd.TestCmd() + test = TestCmd.TestCmd(description = 'test') + test = TestCmd.TestCmd(description = 'test', program = 'foo') + test = TestCmd.TestCmd(description = 'test', + program = 'foo', + universal_newlines=None) + + + +class basename_TestCase(TestCmdTestCase): + def test_basename(self): + """Test basename() [XXX TO BE WRITTEN]""" + assert 1 == 1 + + + +class cleanup_TestCase(TestCmdTestCase): + def test_cleanup(self): + """Test cleanup()""" + test = TestCmd.TestCmd(workdir = '') + wdir = test.workdir + test.write('file1', "Test file #1\n") + test.cleanup() + assert not os.path.exists(wdir) + + def test_writable(self): + """Test cleanup() when the directory isn't writable""" + test = TestCmd.TestCmd(workdir = '') + wdir = test.workdir + test.write('file2', "Test file #2\n") + os.chmod(test.workpath('file2'), 0o400) + os.chmod(wdir, 0o500) + test.cleanup() + assert not os.path.exists(wdir) + + def test_shutil(self): + """Test cleanup() when used with shutil""" + test = TestCmd.TestCmd(workdir = '') + wdir = test.workdir + os.chdir(wdir) + + import shutil + save_rmtree = shutil.rmtree + def my_rmtree(dir, ignore_errors=0, wdir=wdir, _rmtree=save_rmtree): + assert os.getcwd() != wdir + return _rmtree(dir, ignore_errors=ignore_errors) + try: + shutil.rmtree = my_rmtree + test.cleanup() + finally: + shutil.rmtree = save_rmtree + + def test_atexit(self): + """Test cleanup() when atexit is used""" + self.popen_python("""from __future__ import print_function +import sys +sys.path = ['%s'] + sys.path +import atexit +def my_exitfunc(): + print("my_exitfunc()") +atexit.register(my_exitfunc) +import TestCmd +result = TestCmd.TestCmd(workdir = '') +sys.exit(0) +""" % self.orig_cwd, stdout='my_exitfunc()\n') + + def test_exitfunc(self): + """Test cleanup() when sys.exitfunc is set""" + self.popen_python("""from __future__ import print_function +import sys +sys.path = ['%s'] + sys.path +def my_exitfunc(): + print("my_exitfunc()") +sys.exitfunc = my_exitfunc +import TestCmd +result = TestCmd.TestCmd(workdir = '') +sys.exit(0) +""" % self.orig_cwd, stdout='my_exitfunc()\n') + + + +class chmod_TestCase(TestCmdTestCase): + def test_chmod(self): + """Test chmod()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'sub') + + wdir_file1 = os.path.join(test.workdir, 'file1') + wdir_sub_file2 = os.path.join(test.workdir, 'sub', 'file2') + + open(wdir_file1, 'w').write("") + open(wdir_sub_file2, 'w').write("") + + if sys.platform == 'win32': + + test.chmod(wdir_file1, stat.S_IREAD) + test.chmod(['sub', 'file2'], stat.S_IWRITE) + + file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) + assert file1_mode == 0o444, '0%o' % file1_mode + file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) + assert file2_mode == 0o666, '0%o' % file2_mode + + test.chmod('file1', stat.S_IWRITE) + test.chmod(wdir_sub_file2, stat.S_IREAD) + + file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) + assert file1_mode == 0o666, '0%o' % file1_mode + file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) + assert file2_mode == 0o444, '0%o' % file2_mode + + else: + + test.chmod(wdir_file1, 0o700) + test.chmod(['sub', 'file2'], 0o760) + + file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) + assert file1_mode == 0o700, '0%o' % file1_mode + file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) + assert file2_mode == 0o760, '0%o' % file2_mode + + test.chmod('file1', 0o765) + test.chmod(wdir_sub_file2, 0o567) + + file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) + assert file1_mode == 0o765, '0%o' % file1_mode + file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) + assert file2_mode == 0o567, '0%o' % file2_mode + + + +class combine_TestCase(TestCmdTestCase): + def test_combine(self): + """Test combining stdout and stderr""" + run_env = TestCmd.TestCmd(workdir = '') + run_env.write('run1', """import sys +sys.stdout.write("run1 STDOUT %s\\n" % sys.argv[1:]) +sys.stdout.write("run1 STDOUT second line\\n") +sys.stderr.write("run1 STDERR %s\\n" % sys.argv[1:]) +sys.stderr.write("run1 STDERR second line\\n") +sys.stdout.write("run1 STDOUT third line\\n") +sys.stderr.write("run1 STDERR third line\\n") +""") + run_env.write('run2', """import sys +sys.stdout.write("run2 STDOUT %s\\n" % sys.argv[1:]) +sys.stdout.write("run2 STDOUT second line\\n") +sys.stderr.write("run2 STDERR %s\\n" % sys.argv[1:]) +sys.stderr.write("run2 STDERR second line\\n") +sys.stdout.write("run2 STDOUT third line\\n") +sys.stderr.write("run2 STDERR third line\\n") +""") + cwd = os.getcwd() + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + try: + test = TestCmd.TestCmd(interpreter = 'python', + workdir = '', + combine = 1) + try: + output = test.stdout() + except IndexError: + pass + else: + raise IndexError("got unexpected output:\n\t`%s'\n" % output) + + # The underlying system subprocess implementations can combine + # stdout and stderr in different orders, so we accomodate both. + + test.program_set('run1') + test.run(arguments = 'foo bar') + stdout_lines = """\ +run1 STDOUT ['foo', 'bar'] +run1 STDOUT second line +run1 STDOUT third line +""" + stderr_lines = """\ +run1 STDERR ['foo', 'bar'] +run1 STDERR second line +run1 STDERR third line +""" + foo_bar_expect = (stdout_lines + stderr_lines, + stderr_lines + stdout_lines) + + test.program_set('run2') + test.run(arguments = 'snafu') + stdout_lines = """\ +run2 STDOUT ['snafu'] +run2 STDOUT second line +run2 STDOUT third line +""" + stderr_lines = """\ +run2 STDERR ['snafu'] +run2 STDERR second line +run2 STDERR third line +""" + snafu_expect = (stdout_lines + stderr_lines, + stderr_lines + stdout_lines) + + # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL + output = test.stdout() + output = self.translate_newlines(output) + assert output in snafu_expect, output + error = test.stderr() + assert error == '', error + + output = test.stdout(run = -1) + output = self.translate_newlines(output) + assert output in foo_bar_expect, output + error = test.stderr(-1) + assert error == '', error + finally: + os.chdir(cwd) + + + +class description_TestCase(TestCmdTestCase): + def test_description(self): + """Test description()""" + test = TestCmd.TestCmd() + assert test.description is None, 'initialized description?' + test = TestCmd.TestCmd(description = 'test') + assert test.description == 'test', 'uninitialized description' + test.description_set('foo') + assert test.description == 'foo', 'did not set description' + + + +class diff_TestCase(TestCmdTestCase): + def test_diff_re(self): + """Test diff_re()""" + result = TestCmd.diff_re(["abcde"], ["abcde"]) + assert result == [], result + result = TestCmd.diff_re(["a.*e"], ["abcde"]) + assert result == [], result + result = TestCmd.diff_re(["a.*e"], ["xxx"]) + assert result == ['1c1', "< 'a.*e'", '---', "> 'xxx'"], result + + def test_diff_custom_function(self): + """Test diff() using a custom function""" + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +def my_diff(a, b): + return [ + '*****', + a, + '*****', + b, + '*****', + ] +test = TestCmd.TestCmd(diff = my_diff) +test.diff("a\\nb1\\nc\\n", "a\\nb2\\nc\\n", "STDOUT") +sys.exit(0) +""" % self.orig_cwd, + stdout = """\ +STDOUT========================================================================== +***** +['a', 'b1', 'c'] +***** +['a', 'b2', 'c'] +***** +""") + + def test_diff_string(self): + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff = 'diff_re') +test.diff("a\\nb1\\nc\\n", "a\\nb2\\nc\\n", 'STDOUT') +sys.exit(0) +""" % self.orig_cwd, + stdout = """\ +STDOUT========================================================================== +2c2 +< 'b1' +--- +> 'b2' +""") + + def test_error(self): + """Test handling a compilation error in TestCmd.diff_re()""" + script_input = """import sys +sys.path = ['%s'] + sys.path +import TestCmd +assert TestCmd.diff_re(["a.*(e"], ["abcde"]) +sys.exit(0) +""" % self.orig_cwd + stdout, stderr, status = self.call_python(script_input) + assert status == 1, status + expect1 = "Regular expression error in '^a.*(e$': missing )\n" + expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" + assert (stderr.find(expect1) != -1 or + stderr.find(expect2) != -1), repr(stderr) + + def test_simple_diff_static_method(self): + """Test calling the TestCmd.TestCmd.simple_diff() static method""" + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +result = TestCmd.TestCmd.simple_diff(['a', 'b', 'c', 'e', 'f1'], + ['a', 'c', 'd', 'e', 'f2']) +expect = ['2d1', '< b', '3a3', '> d', '5c5', '< f1', '---', '> f2'] +assert result == expect, result +sys.exit(0) +""" % self.orig_cwd) + + def test_context_diff_static_method(self): + """Test calling the TestCmd.TestCmd.context_diff() static method""" + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +result = TestCmd.TestCmd.context_diff(['a\\n', 'b\\n', 'c\\n', 'e\\n', 'f1\\n'], + ['a\\n', 'c\\n', 'd\\n', 'e\\n', 'f2\\n']) +result = list(result) +expect = [ + '*** \\n', + '--- \\n', + '***************\\n', + '*** 1,5 ****\\n', + ' a\\n', + '- b\\n', + ' c\\n', + ' e\\n', + '! f1\\n', + '--- 1,5 ----\\n', + ' a\\n', + ' c\\n', + '+ d\\n', + ' e\\n', + '! f2\\n', +] +assert result == expect, result +sys.exit(0) +""" % self.orig_cwd) + + def test_unified_diff_static_method(self): + """Test calling the TestCmd.TestCmd.unified_diff() static method""" + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +result = TestCmd.TestCmd.unified_diff(['a\\n', 'b\\n', 'c\\n', 'e\\n', 'f1\\n'], + ['a\\n', 'c\\n', 'd\\n', 'e\\n', 'f2\\n']) +result = list(result) +expect = [ + '--- \\n', + '+++ \\n', + '@@ -1,5 +1,5 @@\\n', + ' a\\n', + '-b\\n', + ' c\\n', + '+d\\n', + ' e\\n', + '-f1\\n', + '+f2\\n' +] +assert result == expect, result +sys.exit(0) +""" % self.orig_cwd) + + def test_diff_re_static_method(self): + """Test calling the TestCmd.TestCmd.diff_re() static method""" + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +result = TestCmd.TestCmd.diff_re(['a', 'b', 'c', '.', 'f1'], + ['a', 'c', 'd', 'e', 'f2']) +expect = [ + '2c2', + "< 'b'", + '---', + "> 'c'", + '3c3', + "< 'c'", + '---', + "> 'd'", + '5c5', + "< 'f1'", + '---', + "> 'f2'" +] +assert result == expect, result +sys.exit(0) +""" % self.orig_cwd) + + + +class diff_stderr_TestCase(TestCmdTestCase): + def test_diff_stderr_default(self): + """Test diff_stderr() default behavior""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd() +test.diff_stderr('a\nb1\nc\n', 'a\nb2\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +2c2 +< b1 +--- +> b2 +""") + + def test_diff_stderr_not_affecting_diff_stdout(self): + """Test diff_stderr() not affecting diff_stdout() behavior""" + self.popen_python(r"""from __future__ import print_function +import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff_stderr='diff_re') +print("diff_stderr:") +test.diff_stderr('a\nb.\nc\n', 'a\nbb\nc\n') +print("diff_stdout:") +test.diff_stdout('a\nb.\nc\n', 'a\nbb\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +diff_stderr: +diff_stdout: +2c2 +< b. +--- +> bb +""") + + def test_diff_stderr_custom_function(self): + """Test diff_stderr() using a custom function""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +def my_diff(a, b): + return ["a:"] + a + ["b:"] + b +test = TestCmd.TestCmd(diff_stderr=my_diff) +test.diff_stderr('abc', 'def') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +a: +abc +b: +def +""") + + def test_diff_stderr_TestCmd_function(self): + """Test diff_stderr() using a TestCmd function""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff_stderr = TestCmd.diff_re) +test.diff_stderr('a\n.\n', 'b\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +1c1 +< 'a' +--- +> 'b' +""") + + def test_diff_stderr_static_method(self): + """Test diff_stderr() using a static method""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff_stderr=TestCmd.TestCmd.diff_re) +test.diff_stderr('a\n.\n', 'b\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +1c1 +< 'a' +--- +> 'b' +""") + + def test_diff_stderr_string(self): + """Test diff_stderr() using a string to fetch the diff method""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff_stderr='diff_re') +test.diff_stderr('a\n.\n', 'b\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +1c1 +< 'a' +--- +> 'b' +""") + + + +class diff_stdout_TestCase(TestCmdTestCase): + def test_diff_stdout_default(self): + """Test diff_stdout() default behavior""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd() +test.diff_stdout('a\nb1\nc\n', 'a\nb2\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +2c2 +< b1 +--- +> b2 +""") + + def test_diff_stdout_not_affecting_diff_stderr(self): + """Test diff_stdout() not affecting diff_stderr() behavior""" + self.popen_python(r"""from __future__ import print_function +import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff_stdout='diff_re') +print("diff_stdout:") +test.diff_stdout('a\nb.\nc\n', 'a\nbb\nc\n') +print("diff_stderr:") +test.diff_stderr('a\nb.\nc\n', 'a\nbb\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +diff_stdout: +diff_stderr: +2c2 +< b. +--- +> bb +""") + + def test_diff_stdout_custom_function(self): + """Test diff_stdout() using a custom function""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +def my_diff(a, b): + return ["a:"] + a + ["b:"] + b +test = TestCmd.TestCmd(diff_stdout=my_diff) +test.diff_stdout('abc', 'def') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +a: +abc +b: +def +""") + + def test_diff_stdout_TestCmd_function(self): + """Test diff_stdout() using a TestCmd function""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff_stdout = TestCmd.diff_re) +test.diff_stdout('a\n.\n', 'b\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +1c1 +< 'a' +--- +> 'b' +""") + + def test_diff_stdout_static_method(self): + """Test diff_stdout() using a static method""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff_stdout=TestCmd.TestCmd.diff_re) +test.diff_stdout('a\n.\n', 'b\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +1c1 +< 'a' +--- +> 'b' +""") + + def test_diff_stdout_string(self): + """Test diff_stdout() using a string to fetch the diff method""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(diff_stdout='diff_re') +test.diff_stdout('a\n.\n', 'b\nc\n') +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +1c1 +< 'a' +--- +> 'b' +""") + + + +class exit_TestCase(TestCmdTestCase): + def test_exit(self): + """Test exit()""" + def _test_it(cwd, tempdir, condition, preserved): + close_true = {'pass_test': 1, 'fail_test': 0, 'no_result': 0} + exit_status = {'pass_test': 0, 'fail_test': 1, 'no_result': 2} + result_string = {'pass_test': "PASSED\n", + 'fail_test': "FAILED test at line 5 of \n", + 'no_result': "NO RESULT for test at line 5 of \n"} + global ExitError + input = """import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(workdir = '%s') +test.%s() +""" % (cwd, tempdir, condition) + stdout, stderr, status = self.call_python(input, python="python") + if close_true[condition]: + unexpected = (status != 0) + else: + unexpected = (status == 0) + if unexpected: + msg = "Unexpected exit status from python: %s\n" + raise ExitError(msg % status + stdout + stderr) + if status != exit_status[condition]: + msg = "Expected exit status %d, got %d\n" + raise ExitError(msg % (exit_status[condition], status)) + if stderr != result_string[condition]: + msg = "Expected error output:\n%sGot error output:\n%s" + raise ExitError(msg % (result_string[condition], stderr)) + if preserved: + if not os.path.exists(tempdir): + msg = "Working directory %s was mistakenly removed\n" + raise ExitError(msg % tempdir + stdout) + else: + if os.path.exists(tempdir): + msg = "Working directory %s was mistakenly preserved\n" + raise ExitError(msg % tempdir + stdout) + + run_env = TestCmd.TestCmd(workdir = '') + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + try: + cwd = self.orig_cwd + _clear_dict(os.environ, 'PRESERVE', 'PRESERVE_PASS', 'PRESERVE_FAIL', 'PRESERVE_NO_RESULT') + _test_it(cwd, 'dir01', 'pass_test', 0) + _test_it(cwd, 'dir02', 'fail_test', 0) + _test_it(cwd, 'dir03', 'no_result', 0) + os.environ['PRESERVE'] = '1' + _test_it(cwd, 'dir04', 'pass_test', 1) + _test_it(cwd, 'dir05', 'fail_test', 1) + _test_it(cwd, 'dir06', 'no_result', 1) + os.environ['PRESERVE'] = '' # del os.environ['PRESERVE'] + os.environ['PRESERVE_PASS'] = '1' + _test_it(cwd, 'dir07', 'pass_test', 1) + _test_it(cwd, 'dir08', 'fail_test', 0) + _test_it(cwd, 'dir09', 'no_result', 0) + os.environ['PRESERVE_PASS'] = '' # del os.environ['PRESERVE_PASS'] + os.environ['PRESERVE_FAIL'] = '1' + _test_it(cwd, 'dir10', 'pass_test', 0) + _test_it(cwd, 'dir11', 'fail_test', 1) + _test_it(cwd, 'dir12', 'no_result', 0) + os.environ['PRESERVE_FAIL'] = '' # del os.environ['PRESERVE_FAIL'] + os.environ['PRESERVE_NO_RESULT'] = '1' + _test_it(cwd, 'dir13', 'pass_test', 0) + _test_it(cwd, 'dir14', 'fail_test', 0) + _test_it(cwd, 'dir15', 'no_result', 1) + os.environ['PRESERVE_NO_RESULT'] = '' # del os.environ['PRESERVE_NO_RESULT'] + finally: + _clear_dict(os.environ, 'PRESERVE', 'PRESERVE_PASS', 'PRESERVE_FAIL', 'PRESERVE_NO_RESULT') + + + +class fail_test_TestCase(TestCmdTestCase): + def test_fail_test(self): + """Test fail_test()""" + run_env = TestCmd.TestCmd(workdir = '') + run_env.write('run', """import sys +sys.stdout.write("run: STDOUT\\n") +sys.stderr.write("run: STDERR\\n") +""") + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +TestCmd.fail_test(condition = 1) +""" % self.orig_cwd, status = 1, stderr = "FAILED test at line 4 of \n") + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') +test.run() +test.fail_test(condition = (test.status == 0)) +""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 6 of \n" % run_env.workpath('run')) + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(program = 'run', interpreter = 'python', description = 'xyzzy', workdir = '') +test.run() +test.fail_test(condition = (test.status == 0)) +""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s [xyzzy]\n\tat line 6 of \n" % run_env.workpath('run')) + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') +test.run() +def xxx(): + sys.stderr.write("printed on failure\\n") +test.fail_test(condition = (test.status == 0), function = xxx) +""" % self.orig_cwd, status = 1, stderr = "printed on failure\nFAILED test of %s\n\tat line 8 of \n" % run_env.workpath('run')) + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +def test1(self): + self.run() + self.fail_test(condition = (self.status == 0)) +def test2(self): + test1(self) +test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')) +""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 6 of (test1)\n\tfrom line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +def test1(self): + self.run() + self.fail_test(condition = (self.status == 0), skip = 1) +def test2(self): + test1(self) +test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')) +""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) + + + +class interpreter_TestCase(TestCmdTestCase): + def test_interpreter(self): + """Test interpreter()""" + run_env = TestCmd.TestCmd(workdir = '') + run_env.write('run', """import sys +sys.stdout.write("run: STDOUT\\n") +sys.stderr.write("run: STDERR\\n") +""") + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + test = TestCmd.TestCmd(program = 'run', workdir = '') + test.interpreter_set('foo') + assert test.interpreter == 'foo', 'did not set interpreter' + test.interpreter_set('python') + assert test.interpreter == 'python', 'did not set interpreter' + test.run() + + + +class match_TestCase(TestCmdTestCase): + def test_match_default(self): + """Test match() default behavior""" + test = TestCmd.TestCmd() + assert test.match("abcde\n", "a.*e\n") + assert test.match("12345\nabcde\n", "1\\d+5\na.*e\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert test.match(lines, regexes) + + def test_match_custom_function(self): + """Test match() using a custom function""" + def match_length(lines, matches): + return len(lines) == len(matches) + test = TestCmd.TestCmd(match=match_length) + assert not test.match("123\n", "1\n") + assert test.match("123\n", "111\n") + assert not test.match("123\n123\n", "1\n1\n") + assert test.match("123\n123\n", "111\n111\n") + lines = ["123\n", "123\n"] + regexes = ["1\n", "1\n"] + assert test.match(lines, regexes) # due to equal numbers of lines + + def test_match_TestCmd_function(self): + """Test match() using a TestCmd function""" + test = TestCmd.TestCmd(match = TestCmd.match_exact) + assert not test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match(lines, regexes) + assert test.match(lines, lines) + + def test_match_static_method(self): + """Test match() using a static method""" + test = TestCmd.TestCmd(match=TestCmd.TestCmd.match_exact) + assert not test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match(lines, regexes) + assert test.match(lines, lines) + + def test_match_string(self): + """Test match() using a string to fetch the match method""" + test = TestCmd.TestCmd(match='match_exact') + assert not test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match(lines, regexes) + assert test.match(lines, lines) + + + +class match_exact_TestCase(TestCmdTestCase): + def test_match_exact_function(self): + """Test calling the TestCmd.match_exact() function""" + assert not TestCmd.match_exact("abcde\\n", "a.*e\\n") + assert TestCmd.match_exact("abcde\\n", "abcde\\n") + + def test_match_exact_instance_method(self): + """Test calling the TestCmd.TestCmd().match_exact() instance method""" + test = TestCmd.TestCmd() + assert not test.match_exact("abcde\\n", "a.*e\\n") + assert test.match_exact("abcde\\n", "abcde\\n") + + def test_match_exact_static_method(self): + """Test calling the TestCmd.TestCmd.match_exact() static method""" + assert not TestCmd.TestCmd.match_exact("abcde\\n", "a.*e\\n") + assert TestCmd.TestCmd.match_exact("abcde\\n", "abcde\\n") + + def test_evaluation(self): + """Test match_exact() evaluation""" + test = TestCmd.TestCmd() + assert not test.match_exact("abcde\n", "a.*e\n") + assert test.match_exact("abcde\n", "abcde\n") + assert not test.match_exact(["12345\n", "abcde\n"], ["1[0-9]*5\n", "a.*e\n"]) + assert test.match_exact(["12345\n", "abcde\n"], ["12345\n", "abcde\n"]) + assert not test.match_exact(UserList(["12345\n", "abcde\n"]), + ["1[0-9]*5\n", "a.*e\n"]) + assert test.match_exact(UserList(["12345\n", "abcde\n"]), + ["12345\n", "abcde\n"]) + assert not test.match_exact(["12345\n", "abcde\n"], + UserList(["1[0-9]*5\n", "a.*e\n"])) + assert test.match_exact(["12345\n", "abcde\n"], + UserList(["12345\n", "abcde\n"])) + assert not test.match_exact("12345\nabcde\n", "1[0-9]*5\na.*e\n") + assert test.match_exact("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_exact(lines, regexes) + assert test.match_exact(lines, lines) + + + +class match_re_dotall_TestCase(TestCmdTestCase): + def test_match_re_dotall_function(self): + """Test calling the TestCmd.match_re_dotall() function""" + assert TestCmd.match_re_dotall("abcde\nfghij\n", "a.*j\n") + + def test_match_re_dotall_instance_method(self): + """Test calling the TestCmd.TestCmd().match_re_dotall() instance method""" + test = TestCmd.TestCmd() + test.match_re_dotall("abcde\\nfghij\\n", "a.*j\\n") + + def test_match_re_dotall_static_method(self): + """Test calling the TestCmd.TestCmd.match_re_dotall() static method""" + assert TestCmd.TestCmd.match_re_dotall("abcde\nfghij\n", "a.*j\n") + + def test_error(self): + """Test handling a compilation error in TestCmd.match_re_dotall()""" + run_env = TestCmd.TestCmd(workdir = '') + cwd = os.getcwd() + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + try: + script_input = """import sys +sys.path = ['%s'] + sys.path +import TestCmd +assert TestCmd.match_re_dotall("abcde", "a.*(e") +sys.exit(0) +""" % cwd + stdout, stderr, status = self.call_python(script_input) + assert status == 1, status + expect1 = "Regular expression error in '^a.*(e$': missing )\n" + expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" + assert (stderr.find(expect1) != -1 or + stderr.find(expect2) != -1), repr(stderr) + finally: + os.chdir(cwd) + + def test_evaluation(self): + """Test match_re_dotall() evaluation""" + test = TestCmd.TestCmd() + assert test.match_re_dotall("abcde\nfghij\n", "a.*e\nf.*j\n") + assert test.match_re_dotall("abcde\nfghij\n", "a[^j]*j\n") + assert test.match_re_dotall("abcde\nfghij\n", "abcde\nfghij\n") + assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], + ["1[0-9]*5\n", "a.*e\n", "f.*j\n"]) + assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], + ["1.*j\n"]) + assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], + ["12345\n", "abcde\n", "fghij\n"]) + assert test.match_re_dotall(UserList(["12345\n", + "abcde\n", + "fghij\n"]), + ["1[0-9]*5\n", "a.*e\n", "f.*j\n"]) + assert test.match_re_dotall(UserList(["12345\n", + "abcde\n", + "fghij\n"]), + ["1.*j\n"]) + assert test.match_re_dotall(UserList(["12345\n", + "abcde\n", + "fghij\n"]), + ["12345\n", "abcde\n", "fghij\n"]) + assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], + UserList(["1[0-9]*5\n", + "a.*e\n", + "f.*j\n"])) + assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], + UserList(["1.*j\n"])) + assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], + UserList(["12345\n", + "abcde\n", + "fghij\n"])) + assert test.match_re_dotall("12345\nabcde\nfghij\n", + "1[0-9]*5\na.*e\nf.*j\n") + assert test.match_re_dotall("12345\nabcde\nfghij\n", "1.*j\n") + assert test.match_re_dotall("12345\nabcde\nfghij\n", + "12345\nabcde\nfghij\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert test.match_re_dotall(lines, regexes) + assert test.match_re_dotall(lines, lines) + + + +class match_re_TestCase(TestCmdTestCase): + def test_match_re_function(self): + """Test calling the TestCmd.match_re() function""" + assert TestCmd.match_re("abcde\n", "a.*e\n") + + def test_match_re_instance_method(self): + """Test calling the TestCmd.TestCmd().match_re() instance method""" + test = TestCmd.TestCmd() + assert test.match_re("abcde\n", "a.*e\n") + + def test_match_re_static_method(self): + """Test calling the TestCmd.TestCmd.match_re() static method""" + assert TestCmd.TestCmd.match_re("abcde\n", "a.*e\n") + + def test_error(self): + """Test handling a compilation error in TestCmd.match_re()""" + run_env = TestCmd.TestCmd(workdir = '') + cwd = os.getcwd() + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + try: + script_input = """import sys +sys.path = ['%s'] + sys.path +import TestCmd +assert TestCmd.match_re("abcde\\n", "a.*(e\\n") +sys.exit(0) +""" % cwd + stdout, stderr, status = self.call_python(script_input) + assert status == 1, status + expect1 = "Regular expression error in '^a.*(e$': missing )\n" + expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" + assert (stderr.find(expect1) != -1 or + stderr.find(expect2) != -1), repr(stderr) + finally: + os.chdir(cwd) + + def test_evaluation(self): + """Test match_re() evaluation""" + test = TestCmd.TestCmd() + assert test.match_re("abcde\n", "a.*e\n") + assert test.match_re("abcde\n", "abcde\n") + assert test.match_re(["12345\n", "abcde\n"], ["1[0-9]*5\n", "a.*e\n"]) + assert test.match_re(["12345\n", "abcde\n"], ["12345\n", "abcde\n"]) + assert test.match_re(UserList(["12345\n", "abcde\n"]), + ["1[0-9]*5\n", "a.*e\n"]) + assert test.match_re(UserList(["12345\n", "abcde\n"]), + ["12345\n", "abcde\n"]) + assert test.match_re(["12345\n", "abcde\n"], + UserList(["1[0-9]*5\n", "a.*e\n"])) + assert test.match_re(["12345\n", "abcde\n"], + UserList(["12345\n", "abcde\n"])) + assert test.match_re("12345\nabcde\n", "1[0-9]*5\na.*e\n") + assert test.match_re("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert test.match_re(lines, regexes) + assert test.match_re(lines, lines) + + + +class match_stderr_TestCase(TestCmdTestCase): + def test_match_stderr_default(self): + """Test match_stderr() default behavior""" + test = TestCmd.TestCmd() + assert test.match_stderr("abcde\n", "a.*e\n") + assert test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert test.match_stderr(lines, regexes) + + def test_match_stderr_not_affecting_match_stdout(self): + """Test match_stderr() not affecting match_stdout() behavior""" + test = TestCmd.TestCmd(match_stderr=TestCmd.TestCmd.match_exact) + + assert not test.match_stderr("abcde\n", "a.*e\n") + assert test.match_stderr("abcde\n", "abcde\n") + assert not test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") + assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_stderr(lines, regexes) + assert test.match_stderr(lines, lines) + + assert test.match_stdout("abcde\n", "a.*e\n") + assert test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert test.match_stdout(lines, regexes) + + def test_match_stderr_custom_function(self): + """Test match_stderr() using a custom function""" + def match_length(lines, matches): + return len(lines) == len(matches) + test = TestCmd.TestCmd(match_stderr=match_length) + assert not test.match_stderr("123\n", "1\n") + assert test.match_stderr("123\n", "111\n") + assert not test.match_stderr("123\n123\n", "1\n1\n") + assert test.match_stderr("123\n123\n", "111\n111\n") + lines = ["123\n", "123\n"] + regexes = ["1\n", "1\n"] + assert test.match_stderr(lines, regexes) # equal numbers of lines + + def test_match_stderr_TestCmd_function(self): + """Test match_stderr() using a TestCmd function""" + test = TestCmd.TestCmd(match_stderr = TestCmd.match_exact) + assert not test.match_stderr("abcde\n", "a.*e\n") + assert test.match_stderr("abcde\n", "abcde\n") + assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_stderr(lines, regexes) + assert test.match_stderr(lines, lines) + + def test_match_stderr_static_method(self): + """Test match_stderr() using a static method""" + test = TestCmd.TestCmd(match_stderr=TestCmd.TestCmd.match_exact) + assert not test.match_stderr("abcde\n", "a.*e\n") + assert test.match_stderr("abcde\n", "abcde\n") + assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_stderr(lines, regexes) + assert test.match_stderr(lines, lines) + + def test_match_stderr_string(self): + """Test match_stderr() using a string to fetch the match method""" + test = TestCmd.TestCmd(match_stderr='match_exact') + assert not test.match_stderr("abcde\n", "a.*e\n") + assert test.match_stderr("abcde\n", "abcde\n") + assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_stderr(lines, regexes) + assert test.match_stderr(lines, lines) + + + +class match_stdout_TestCase(TestCmdTestCase): + def test_match_stdout_default(self): + """Test match_stdout() default behavior""" + test = TestCmd.TestCmd() + assert test.match_stdout("abcde\n", "a.*e\n") + assert test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert test.match_stdout(lines, regexes) + + def test_match_stdout_not_affecting_match_stderr(self): + """Test match_stdout() not affecting match_stderr() behavior""" + test = TestCmd.TestCmd(match_stdout=TestCmd.TestCmd.match_exact) + + assert not test.match_stdout("abcde\n", "a.*e\n") + assert test.match_stdout("abcde\n", "abcde\n") + assert not test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") + assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_stdout(lines, regexes) + assert test.match_stdout(lines, lines) + + assert test.match_stderr("abcde\n", "a.*e\n") + assert test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert test.match_stderr(lines, regexes) + + def test_match_stdout_custom_function(self): + """Test match_stdout() using a custom function""" + def match_length(lines, matches): + return len(lines) == len(matches) + test = TestCmd.TestCmd(match_stdout=match_length) + assert not test.match_stdout("123\n", "1\n") + assert test.match_stdout("123\n", "111\n") + assert not test.match_stdout("123\n123\n", "1\n1\n") + assert test.match_stdout("123\n123\n", "111\n111\n") + lines = ["123\n", "123\n"] + regexes = ["1\n", "1\n"] + assert test.match_stdout(lines, regexes) # equal numbers of lines + + def test_match_stdout_TestCmd_function(self): + """Test match_stdout() using a TestCmd function""" + test = TestCmd.TestCmd(match_stdout = TestCmd.match_exact) + assert not test.match_stdout("abcde\n", "a.*e\n") + assert test.match_stdout("abcde\n", "abcde\n") + assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_stdout(lines, regexes) + assert test.match_stdout(lines, lines) + + def test_match_stdout_static_method(self): + """Test match_stdout() using a static method""" + test = TestCmd.TestCmd(match_stdout=TestCmd.TestCmd.match_exact) + assert not test.match_stdout("abcde\n", "a.*e\n") + assert test.match_stdout("abcde\n", "abcde\n") + assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_stdout(lines, regexes) + assert test.match_stdout(lines, lines) + + def test_match_stdout_string(self): + """Test match_stdout() using a string to fetch the match method""" + test = TestCmd.TestCmd(match_stdout='match_exact') + assert not test.match_stdout("abcde\n", "a.*e\n") + assert test.match_stdout("abcde\n", "abcde\n") + assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") + assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") + lines = ["vwxyz\n", "67890\n"] + regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + assert not test.match_stdout(lines, regexes) + assert test.match_stdout(lines, lines) + + + +class no_result_TestCase(TestCmdTestCase): + def test_no_result(self): + """Test no_result()""" + run_env = TestCmd.TestCmd(workdir = '') + run_env.write('run', """import sys +sys.stdout.write("run: STDOUT\\n") +sys.stderr.write("run: STDERR\\n") +""") + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +TestCmd.no_result(condition = 1) +""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test at line 4 of \n") + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') +test.run() +test.no_result(condition = (test.status == 0)) +""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 6 of \n" % run_env.workpath('run')) + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(program = 'run', interpreter = 'python', description = 'xyzzy', workdir = '') +test.run() +test.no_result(condition = (test.status == 0)) +""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s [xyzzy]\n\tat line 6 of \n" % run_env.workpath('run')) + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') +test.run() +def xxx(): + sys.stderr.write("printed on no result\\n") +test.no_result(condition = (test.status == 0), function = xxx) +""" % self.orig_cwd, status = 2, stderr = "printed on no result\nNO RESULT for test of %s\n\tat line 8 of \n" % run_env.workpath('run')) + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +def test1(self): + self.run() + self.no_result(condition = (self.status == 0)) +def test2(self): + test1(self) +test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')) +""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 6 of (test1)\n\tfrom line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +def test1(self): + self.run() + self.no_result(condition = (self.status == 0), skip = 1) +def test2(self): + test1(self) +test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')) +""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) + + + +class pass_test_TestCase(TestCmdTestCase): + def test_pass_test(self): + """Test pass_test()""" + run_env = TestCmd.TestCmd(workdir = '') + run_env.write('run', """import sys +sys.stdout.write("run: STDOUT\\n") +sys.stderr.write("run: STDERR\\n") +""") + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +TestCmd.pass_test(condition = 1) +""" % self.orig_cwd, stderr = "PASSED\n") + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') +test.run() +test.pass_test(condition = (test.status == 0)) +""" % self.orig_cwd, stderr = "PASSED\n") + + self.popen_python("""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') +test.run() +def brag(): + sys.stderr.write("printed on success\\n") +test.pass_test(condition = (test.status == 0), function = brag) +""" % self.orig_cwd, stderr = "printed on success\nPASSED\n") + + # TODO(sgk): SHOULD ALSO TEST FAILURE CONDITIONS + + + +class preserve_TestCase(TestCmdTestCase): + def test_preserve(self): + """Test preserve()""" + def cleanup_test(test, cond=None, stdout=""): + io = StringIO() + save = sys.stdout + sys.stdout = io + try: + if cond: + test.cleanup(cond) + else: + test.cleanup() + o = io.getvalue() + assert o == stdout, "o = `%s', stdout = `%s'" % (o, stdout) + finally: + sys.stdout = save + + test = TestCmd.TestCmd(workdir = '') + wdir = test.workdir + try: + test.write('file1', "Test file #1\n") + #test.cleanup() + cleanup_test(test, ) + assert not os.path.exists(wdir) + finally: + if os.path.exists(wdir): + shutil.rmtree(wdir, ignore_errors = 1) + test._dirlist.remove(wdir) + + test = TestCmd.TestCmd(workdir = '') + wdir = test.workdir + try: + test.write('file2', "Test file #2\n") + test.preserve('pass_test') + cleanup_test(test, 'pass_test', "Preserved directory %s\n" % wdir) + assert os.path.isdir(wdir) + cleanup_test(test, 'fail_test') + assert not os.path.exists(wdir) + finally: + if os.path.exists(wdir): + shutil.rmtree(wdir, ignore_errors = 1) + test._dirlist.remove(wdir) + + test = TestCmd.TestCmd(workdir = '') + wdir = test.workdir + try: + test.write('file3', "Test file #3\n") + test.preserve('fail_test') + cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) + assert os.path.isdir(wdir) + cleanup_test(test, 'pass_test') + assert not os.path.exists(wdir) + finally: + if os.path.exists(wdir): + shutil.rmtree(wdir, ignore_errors = 1) + test._dirlist.remove(wdir) + + test = TestCmd.TestCmd(workdir = '') + wdir = test.workdir + try: + test.write('file4', "Test file #4\n") + test.preserve('fail_test', 'no_result') + cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) + assert os.path.isdir(wdir) + cleanup_test(test, 'no_result', "Preserved directory %s\n" % wdir) + assert os.path.isdir(wdir) + cleanup_test(test, 'pass_test') + assert not os.path.exists(wdir) + finally: + if os.path.exists(wdir): + shutil.rmtree(wdir, ignore_errors = 1) + test._dirlist.remove(wdir) + + test = TestCmd.TestCmd(workdir = '') + wdir = test.workdir + try: + test.preserve() + cleanup_test(test, 'pass_test', "Preserved directory %s\n" % wdir) + assert os.path.isdir(wdir) + cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) + assert os.path.isdir(wdir) + cleanup_test(test, 'no_result', "Preserved directory %s\n" % wdir) + assert os.path.isdir(wdir) + finally: + if os.path.exists(wdir): + shutil.rmtree(wdir, ignore_errors = 1) + test._dirlist.remove(wdir) + + + +class program_TestCase(TestCmdTestCase): + def test_program(self): + """Test program()""" + test = TestCmd.TestCmd() + assert test.program is None, 'initialized program?' + test = TestCmd.TestCmd(program = 'test') + assert test.program == os.path.join(os.getcwd(), 'test'), 'uninitialized program' + test.program_set('foo') + assert test.program == os.path.join(os.getcwd(), 'foo'), 'did not set program' + + + +class read_TestCase(TestCmdTestCase): + def test_read(self): + """Test read()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + wdir_file1 = os.path.join(test.workdir, 'file1') + wdir_file2 = os.path.join(test.workdir, 'file2') + wdir_foo_file3 = os.path.join(test.workdir, 'foo', 'file3') + wdir_file4 = os.path.join(test.workdir, 'file4') + wdir_file5 = os.path.join(test.workdir, 'file5') + + open(wdir_file1, 'wb').write("") + open(wdir_file2, 'wb').write("Test\nfile\n#2.\n") + open(wdir_foo_file3, 'wb').write("Test\nfile\n#3.\n") + open(wdir_file4, 'wb').write("Test\nfile\n#4.\n") + open(wdir_file5, 'wb').write("Test\r\nfile\r\n#5.\r\n") + + try: + contents = test.read('no_file') + except IOError: # expect "No such file or directory" + pass + except: + raise + + try: + test.read(test.workpath('file_x'), mode = 'w') + except ValueError: # expect "mode must begin with 'r' + pass + except: + raise + + def _file_matches(file, contents, expected): + assert contents == expected, \ + "Expected contents of " + str(file) + "==========\n" + \ + expected + \ + "Actual contents of " + str(file) + "============\n" + \ + contents + + _file_matches(wdir_file1, test.read('file1'), "") + _file_matches(wdir_file2, test.read('file2'), "Test\nfile\n#2.\n") + _file_matches(wdir_foo_file3, test.read(['foo', 'file3']), + "Test\nfile\n#3.\n") + _file_matches(wdir_foo_file3, + test.read(UserList(['foo', 'file3'])), + "Test\nfile\n#3.\n") + _file_matches(wdir_file4, test.read('file4', mode = 'r'), + "Test\nfile\n#4.\n") + _file_matches(wdir_file5, test.read('file5', mode = 'rb'), + "Test\r\nfile\r\n#5.\r\n") + + + +class rmdir_TestCase(TestCmdTestCase): + def test_rmdir(self): + """Test rmdir()""" + test = TestCmd.TestCmd(workdir = '') + + try: + test.rmdir(['no', 'such', 'dir']) + except EnvironmentError: + pass + else: + raise Exception("did not catch expected EnvironmentError") + + test.subdir(['sub'], + ['sub', 'dir'], + ['sub', 'dir', 'one']) + + s = test.workpath('sub') + s_d = test.workpath('sub', 'dir') + s_d_o = test.workpath('sub', 'dir', 'one') + + try: + test.rmdir(['sub']) + except EnvironmentError: + pass + else: + raise Exception("did not catch expected EnvironmentError") + + assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o + + try: + test.rmdir(['sub']) + except EnvironmentError: + pass + else: + raise Exception("did not catch expected EnvironmentError") + + assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o + + test.rmdir(['sub', 'dir', 'one']) + + assert not os.path.exists(s_d_o), "%s exists?" % s_d_o + assert os.path.isdir(s_d), "%s is gone?" % s_d + + test.rmdir(['sub', 'dir']) + + assert not os.path.exists(s_d), "%s exists?" % s_d + assert os.path.isdir(s), "%s is gone?" % s + + test.rmdir('sub') + + assert not os.path.exists(s), "%s exists?" % s + + + +class run_TestCase(TestCmdTestCase): + def test_run(self): + """Test run()""" + + t = self.setup_run_scripts() + + # Everything before this prepared our "source directory." + # Now do the real test. + try: + test = TestCmd.TestCmd(program = t.script, + interpreter = 'python', + workdir = '', + subdir = 'script_subdir') + + test.run() + self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, + repr([])) + self.run_match(test.stderr(), t.script, "STDERR", t.workdir, + repr([])) + + test.run(arguments = 'arg1 arg2 arg3') + self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, + repr(['arg1', 'arg2', 'arg3'])) + self.run_match(test.stderr(), t.script, "STDERR", t.workdir, + repr(['arg1', 'arg2', 'arg3'])) + + test.run(program = t.scriptx, arguments = 'foo') + self.run_match(test.stdout(), t.scriptx, "STDOUT", t.workdir, + repr(['foo'])) + self.run_match(test.stderr(), t.scriptx, "STDERR", t.workdir, + repr(['foo'])) + + test.run(chdir = os.curdir, arguments = 'x y z') + self.run_match(test.stdout(), t.script, "STDOUT", test.workdir, + repr(['x', 'y', 'z'])) + self.run_match(test.stderr(), t.script, "STDERR", test.workdir, + repr(['x', 'y', 'z'])) + + test.run(chdir = 'script_subdir') + script_subdir = test.workpath('script_subdir') + self.run_match(test.stdout(), t.script, "STDOUT", script_subdir, + repr([])) + self.run_match(test.stderr(), t.script, "STDERR", script_subdir, + repr([])) + + test.run(program = t.script1, interpreter = ['python', '-x']) + self.run_match(test.stdout(), t.script1, "STDOUT", t.workdir, + repr([])) + self.run_match(test.stderr(), t.script1, "STDERR", t.workdir, + repr([])) + + try: + test.run(chdir = 'no_subdir') + except OSError: + pass + + test.run(program = 'no_script', interpreter = 'python') + assert test.status != None, test.status + + try: + test.run(program = 'no_script', interpreter = 'no_interpreter') + except OSError: + # Python versions that use subprocess throw an OSError + # exception when they try to execute something that + # isn't there. + pass + else: + # Python versions that use os.popen3() or the Popen3 + # class run things through the shell, which just returns + # a non-zero exit status. + assert test.status != None, test.status + + testx = TestCmd.TestCmd(program = t.scriptx, + workdir = '', + subdir = 't.scriptx_subdir') + + testx.run() + self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, + repr([])) + self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, + repr([])) + + testx.run(arguments = 'foo bar') + self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, + repr(['foo', 'bar'])) + self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, + repr(['foo', 'bar'])) + + testx.run(program = t.script, interpreter = 'python', arguments = 'bar') + self.run_match(testx.stdout(), t.script, "STDOUT", t.workdir, + repr(['bar'])) + self.run_match(testx.stderr(), t.script, "STDERR", t.workdir, + repr(['bar'])) + + testx.run(chdir = os.curdir, arguments = 'baz') + self.run_match(testx.stdout(), t.scriptx, "STDOUT", testx.workdir, + repr(['baz'])) + self.run_match(testx.stderr(), t.scriptx, "STDERR", testx.workdir, + repr(['baz'])) + + testx.run(chdir = 't.scriptx_subdir') + t.scriptx_subdir = testx.workpath('t.scriptx_subdir') + self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.scriptx_subdir, + repr([])) + self.run_match(testx.stderr(), t.scriptx, "STDERR", t.scriptx_subdir, + repr([])) + + testx.run(program = t.script1, interpreter = ('python', '-x')) + self.run_match(testx.stdout(), t.script1, "STDOUT", t.workdir, + repr([])) + self.run_match(testx.stderr(), t.script1, "STDERR", t.workdir, + repr([])) + + s = os.path.join('.', t.scriptx) + testx.run(program = [s]) + self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, + repr([])) + self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, + repr([])) + + try: + testx.run(chdir = 'no_subdir') + except OSError: + pass + + try: + testx.run(program = 'no_program') + except OSError: + # Python versions that use subprocess throw an OSError + # exception when they try to execute something that + # isn't there. + pass + else: + # Python versions that use os.popen3() or the Popen3 + # class run things through the shell, which just returns + # a non-zero exit status. + assert test.status != None + + test1 = TestCmd.TestCmd(program = t.script1, + interpreter = ['python', '-x'], + workdir = '') + + test1.run() + self.run_match(test1.stdout(), t.script1, "STDOUT", t.workdir, + repr([])) + self.run_match(test1.stderr(), t.script1, "STDERR", t.workdir, + repr([])) + + finally: + os.chdir(t.orig_cwd) + + def test_run_subclass(self): + """Test run() through a subclass with different signatures""" + + t = self.setup_run_scripts() + + # Everything before this prepared our "source directory." + # Now do the real test. + + class MyTestCmdSubclass(TestCmd.TestCmd): + def start(self, additional_argument=None, **kw): + return TestCmd.TestCmd.start(self, **kw) + + try: + test = MyTestCmdSubclass(program = t.script, + interpreter = 'python', + workdir = '', + subdir = 'script_subdir') + + test.run() + self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, + repr([])) + self.run_match(test.stderr(), t.script, "STDERR", t.workdir, + repr([])) + finally: + os.chdir(t.orig_cwd) + + +class run_verbose_TestCase(TestCmdTestCase): + def test_run_verbose(self): + """Test the run() method's verbose attribute""" + + # Prepare our "source directory." + t = self.setup_run_scripts() + + save_stdout = sys.stderr + save_stderr = sys.stderr + + try: + # Test calling TestCmd() with an explicit verbose = 1. + + test = TestCmd.TestCmd(program = t.script, + interpreter = 'python', + workdir = '', + verbose = 1) + + sys.stdout = StringIO() + sys.stderr = StringIO() + + test.run(arguments = ['arg1 arg2']) + o = sys.stdout.getvalue() + assert o == '', o + e = sys.stderr.getvalue() + expect = 'python "%s" "arg1 arg2"\n' % t.script_path + assert expect == e, (expect, e) + + testx = TestCmd.TestCmd(program = t.scriptx, + workdir = '', + verbose = 1) + + sys.stdout = StringIO() + sys.stderr = StringIO() + + testx.run(arguments = ['arg1 arg2']) + expect = '"%s" "arg1 arg2"\n' % t.scriptx_path + o = sys.stdout.getvalue() + assert o == '', o + e = sys.stderr.getvalue() + assert expect == e, (expect, e) + + # Test calling TestCmd() with an explicit verbose = 2. + + outerr_fmt = """\ +============ STATUS: 0 +============ BEGIN STDOUT (len=%s): +%s============ END STDOUT +============ BEGIN STDERR (len=%s) +%s============ END STDERR +""" + + out_fmt = """\ +============ STATUS: 0 +============ BEGIN STDOUT (len=%s): +%s============ END STDOUT +""" + + err_fmt = """\ +============ STATUS: 0 +============ BEGIN STDERR (len=%s) +%s============ END STDERR +""" + + test = TestCmd.TestCmd(program = t.script, + interpreter = 'python', + workdir = '', + verbose = 2) + + sys.stdout = StringIO() + sys.stderr = StringIO() + + test.run(arguments = ['arg1 arg2']) + + line_fmt = "script: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + stderr_line = line_fmt % ('STDERR', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + len(stderr_line), stderr_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) + + expect = 'python "%s" "arg1 arg2"\n' % t.script_path + e = sys.stderr.getvalue() + assert e == expect, (e, expect) + + testx = TestCmd.TestCmd(program = t.scriptx, + workdir = '', + verbose = 2) + + sys.stdout = StringIO() + sys.stderr = StringIO() + + testx.run(arguments = ['arg1 arg2']) + + line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + stderr_line = line_fmt % ('STDERR', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + len(stderr_line), stderr_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) + + expect = '"%s" "arg1 arg2"\n' % t.scriptx_path + e = sys.stderr.getvalue() + assert e == expect, (e, expect) + + # Test calling TestCmd() with an explicit verbose = 3. + + test = TestCmd.TestCmd(program = t.scriptout, + interpreter = 'python', + workdir = '', + verbose = 2) + + sys.stdout = StringIO() + sys.stderr = StringIO() + + test.run(arguments = ['arg1 arg2']) + + line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + expect = out_fmt % (len(stdout_line), stdout_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) + + e = sys.stderr.getvalue() + expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) + assert e == expect, (e, expect) + + test = TestCmd.TestCmd(program = t.scriptout, + interpreter = 'python', + workdir = '', + verbose = 3) + + sys.stdout = StringIO() + sys.stderr = StringIO() + + test.run(arguments = ['arg1 arg2']) + + line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + '0', '') + o = sys.stdout.getvalue() + assert expect == o, (expect, o) + + e = sys.stderr.getvalue() + expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) + assert e == expect, (e, expect) + + # Test letting TestCmd() pick up verbose = 2 from the environment. + + os.environ['TESTCMD_VERBOSE'] = '2' + + test = TestCmd.TestCmd(program = t.script, + interpreter = 'python', + workdir = '') + + sys.stdout = StringIO() + sys.stderr = StringIO() + + test.run(arguments = ['arg1 arg2']) + + line_fmt = "script: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + stderr_line = line_fmt % ('STDERR', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + len(stderr_line), stderr_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) + + expect = 'python "%s" "arg1 arg2"\n' % t.script_path + e = sys.stderr.getvalue() + assert e == expect, (e, expect) + + testx = TestCmd.TestCmd(program = t.scriptx, + workdir = '') + + sys.stdout = StringIO() + sys.stderr = StringIO() + + testx.run(arguments = ['arg1 arg2']) + + line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + stderr_line = line_fmt % ('STDERR', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + len(stderr_line), stderr_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) + + expect = '"%s" "arg1 arg2"\n' % t.scriptx_path + e = sys.stderr.getvalue() + assert e == expect, (e, expect) + + # Test letting TestCmd() pick up verbose = 1 from the environment. + + os.environ['TESTCMD_VERBOSE'] = '1' + + test = TestCmd.TestCmd(program = t.script, + interpreter = 'python', + workdir = '', + verbose = 1) + + sys.stdout = StringIO() + sys.stderr = StringIO() + + test.run(arguments = ['arg1 arg2']) + o = sys.stdout.getvalue() + assert o == '', o + e = sys.stderr.getvalue() + expect = 'python "%s" "arg1 arg2"\n' % t.script_path + assert expect == e, (expect, e) + + testx = TestCmd.TestCmd(program = t.scriptx, + workdir = '', + verbose = 1) + + sys.stdout = StringIO() + sys.stderr = StringIO() + + testx.run(arguments = ['arg1 arg2']) + expect = '"%s" "arg1 arg2"\n' % t.scriptx_path + o = sys.stdout.getvalue() + assert o == '', o + e = sys.stderr.getvalue() + assert expect == e, (expect, e) + + finally: + sys.stdout = save_stdout + sys.stderr = save_stderr + os.chdir(t.orig_cwd) + os.environ['TESTCMD_VERBOSE'] = '' + + + +class set_diff_function_TestCase(TestCmdTestCase): + def test_set_diff_function(self): + """Test set_diff_function()""" + self.popen_python(r"""import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd() +test.diff("a\n", "a\n") +test.set_diff_function('diff_re') +test.diff(".\n", "a\n") +sys.exit(0) +""" % self.orig_cwd) + + def test_set_diff_function_stdout(self): + """Test set_diff_function(): stdout""" + self.popen_python("""from __future__ import print_function +import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd() +print("diff:") +test.diff("a\\n", "a\\n") +print("diff_stdout:") +test.diff_stdout("a\\n", "a\\n") +test.set_diff_function(stdout='diff_re') +print("diff:") +test.diff(".\\n", "a\\n") +print("diff_stdout:") +test.diff_stdout(".\\n", "a\\n") +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +diff: +diff_stdout: +diff: +1c1 +< . +--- +> a +diff_stdout: +""") + + def test_set_diff_function_stderr(self): + """Test set_diff_function(): stderr """ + self.popen_python("""from __future__ import print_function +import sys +sys.path = ['%s'] + sys.path +import TestCmd +test = TestCmd.TestCmd() +print("diff:") +test.diff("a\\n", "a\\n") +print("diff_stderr:") +test.diff_stderr("a\\n", "a\\n") +test.set_diff_function(stderr='diff_re') +print("diff:") +test.diff(".\\n", "a\\n") +print("diff_stderr:") +test.diff_stderr(".\\n", "a\\n") +sys.exit(0) +""" % self.orig_cwd, + stdout="""\ +diff: +diff_stderr: +diff: +1c1 +< . +--- +> a +diff_stderr: +""") + + + +class set_match_function_TestCase(TestCmdTestCase): + def test_set_match_function(self): + """Test set_match_function()""" + test = TestCmd.TestCmd() + assert test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + + test.set_match_function('match_exact') + + assert not test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + + def test_set_match_function_stdout(self): + """Test set_match_function(): stdout """ + test = TestCmd.TestCmd() + assert test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + assert test.match_stdout("abcde\n", "a.*e\n") + assert test.match_stdout("abcde\n", "abcde\n") + + test.set_match_function(stdout='match_exact') + + assert test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + assert not test.match_stdout("abcde\n", "a.*e\n") + assert test.match_stdout("abcde\n", "abcde\n") + + def test_set_match_function_stderr(self): + """Test set_match_function(): stderr """ + test = TestCmd.TestCmd() + assert test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + assert test.match_stderr("abcde\n", "a.*e\n") + assert test.match_stderr("abcde\n", "abcde\n") + + test.set_match_function(stderr='match_exact') + + assert test.match("abcde\n", "a.*e\n") + assert test.match("abcde\n", "abcde\n") + assert not test.match_stderr("abcde\n", "a.*e\n") + assert test.match_stderr("abcde\n", "abcde\n") + + + +class sleep_TestCase(TestCmdTestCase): + def test_sleep(self): + """Test sleep()""" + test = TestCmd.TestCmd() + + start = time.time() + test.sleep() + end = time.time() + diff = end - start + assert diff > 0.9, "only slept %f seconds (start %f, end %f), not default" % (diff, start, end) + + start = time.time() + test.sleep(3) + end = time.time() + diff = end - start + assert diff > 2.9, "only slept %f seconds (start %f, end %f), not 3" % (diff, start, end) + + + +class stderr_TestCase(TestCmdTestCase): + def test_stderr(self): + """Test stderr()""" + run_env = TestCmd.TestCmd(workdir = '') + run_env.write('run1', """import sys +sys.stdout.write("run1 STDOUT %s\\n" % sys.argv[1:]) +sys.stdout.write("run1 STDOUT second line\\n") +sys.stderr.write("run1 STDERR %s\\n" % sys.argv[1:]) +sys.stderr.write("run1 STDERR second line\\n") +""") + run_env.write('run2', """import sys +sys.stdout.write("run2 STDOUT %s\\n" % sys.argv[1:]) +sys.stdout.write("run2 STDOUT second line\\n") +sys.stderr.write("run2 STDERR %s\\n" % sys.argv[1:]) +sys.stderr.write("run2 STDERR second line\\n") +""") + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + test = TestCmd.TestCmd(interpreter = 'python', workdir = '') + try: + output = test.stderr() + except IndexError: + pass + else: + raise IndexError("got unexpected output:\n" + output) + test.program_set('run1') + test.run(arguments = 'foo bar') + test.program_set('run2') + test.run(arguments = 'snafu') + # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL + output = test.stderr() + assert output == "run2 STDERR ['snafu']\nrun2 STDERR second line\n", output + output = test.stderr(run = -1) + assert output == "run1 STDERR ['foo', 'bar']\nrun1 STDERR second line\n", output + + + +class command_args_TestCase(TestCmdTestCase): + def test_command_args(self): + """Test command_args()""" + run_env = TestCmd.TestCmd(workdir = '') + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + test = TestCmd.TestCmd(workdir = '') + + r = test.command_args('prog') + expect = [run_env.workpath('prog')] + assert r == expect, (expect, r) + + r = test.command_args(test.workpath('new_prog')) + expect = [test.workpath('new_prog')] + assert r == expect, (expect, r) + + r = test.command_args('prog', 'python') + expect = ['python', run_env.workpath('prog')] + assert r == expect, (expect, r) + + r = test.command_args('prog', 'python', 'arg1 arg2') + expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2'] + assert r == expect, (expect, r) + + test.program_set('default_prog') + default_prog = run_env.workpath('default_prog') + + r = test.command_args() + expect = [default_prog] + assert r == expect, (expect, r) + + r = test.command_args(interpreter='PYTHON') + expect = ['PYTHON', default_prog] + assert r == expect, (expect, r) + + r = test.command_args(interpreter='PYTHON', arguments='arg3 arg4') + expect = ['PYTHON', default_prog, 'arg3', 'arg4'] + assert r == expect, (expect, r) + + test.interpreter_set('default_python') + + r = test.command_args() + expect = ['default_python', default_prog] + assert r == expect, (expect, r) + + r = test.command_args(arguments='arg5 arg6') + expect = ['default_python', default_prog, 'arg5', 'arg6'] + assert r == expect, (expect, r) + + r = test.command_args('new_prog_1') + expect = [run_env.workpath('new_prog_1')] + assert r == expect, (expect, r) + + r = test.command_args(program='new_prog_2') + expect = [run_env.workpath('new_prog_2')] + assert r == expect, (expect, r) + + + +class start_TestCase(TestCmdTestCase): + def setup_run_scripts(self): + t = TestCmdTestCase.setup_run_scripts(self) + t.recv_script = 'script_recv' + t.recv_script_path = t.run_env.workpath(t.sub_dir, t.recv_script) + t.recv_out_path = t.run_env.workpath('script_recv.out') + text = """\ +import os +import sys + +class Unbuffered: + def __init__(self, file): + self.file = file + def write(self, arg): + self.file.write(arg) + self.file.flush() + def __getattr__(self, attr): + return getattr(self.file, attr) + +sys.stdout = Unbuffered(sys.stdout) +sys.stderr = Unbuffered(sys.stderr) + +sys.stdout.write('script_recv: STDOUT\\n') +sys.stderr.write('script_recv: STDERR\\n') +logfp = open(r'%s', 'wb') +while 1: + line = sys.stdin.readline() + if not line: + break + logfp.write('script_recv: ' + line) + sys.stdout.write('script_recv: STDOUT: ' + line) + sys.stderr.write('script_recv: STDERR: ' + line) +logfp.close() + """ % t.recv_out_path + t.run_env.write(t.recv_script_path, text) + os.chmod(t.recv_script_path, 0o644) # XXX UNIX-specific + return t + + def test_start(self): + """Test start()""" + + t = self.setup_run_scripts() + + # Everything before this prepared our "source directory." + # Now do the real test. + try: + test = TestCmd.TestCmd(program = t.script, + interpreter = 'python', + workdir = '', + subdir = 'script_subdir') + + p = test.start() + self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, + repr([])) + self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, + repr([])) + p.wait() + + p = test.start(arguments='arg1 arg2 arg3') + self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, + repr(['arg1', 'arg2', 'arg3'])) + self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, + repr(['arg1', 'arg2', 'arg3'])) + p.wait() + + p = test.start(program=t.scriptx, arguments='foo') + self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, + repr(['foo'])) + self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, + repr(['foo'])) + p.wait() + + p = test.start(program=t.script1, interpreter=['python', '-x']) + self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, + repr([])) + self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, + repr([])) + p.wait() + + p = test.start(program='no_script', interpreter='python') + status = p.wait() + assert status != None, status + + try: + p = test.start(program='no_script', interpreter='no_interpreter') + except OSError: + # Python versions that use subprocess throw an OSError + # exception when they try to execute something that + # isn't there. + pass + else: + status = p.wait() + # Python versions that use os.popen3() or the Popen3 + # class run things through the shell, which just returns + # a non-zero exit status. + assert status != None, status + + testx = TestCmd.TestCmd(program = t.scriptx, + workdir = '', + subdir = 't.scriptx_subdir') + + p = testx.start() + self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, + repr([])) + self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, + repr([])) + p.wait() + + p = testx.start(arguments='foo bar') + self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, + repr(['foo', 'bar'])) + self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, + repr(['foo', 'bar'])) + p.wait() + + p = testx.start(program=t.script, interpreter='python', arguments='bar') + self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, + repr(['bar'])) + self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, + repr(['bar'])) + p.wait() + + p = testx.start(program=t.script1, interpreter=('python', '-x')) + self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, + repr([])) + self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, + repr([])) + p.wait() + + s = os.path.join('.', t.scriptx) + p = testx.start(program=[s]) + self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, + repr([])) + self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, + repr([])) + p.wait() + + try: + testx.start(program='no_program') + except OSError: + # Python versions that use subprocess throw an OSError + # exception when they try to execute something that + # isn't there. + pass + else: + # Python versions that use os.popen3() or the Popen3 + # class run things through the shell, which just dies + # trying to execute the non-existent program before + # we can wait() for it. + try: + p = p.wait() + except OSError: + pass + + test1 = TestCmd.TestCmd(program = t.script1, + interpreter = ['python', '-x'], + workdir = '') + + p = test1.start() + self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, + repr([])) + self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, + repr([])) + p.wait() + + finally: + os.chdir(t.orig_cwd) + + def test_finish(self): + """Test finish()""" + + t = self.setup_run_scripts() + + # Everything before this prepared our "source directory." + # Now do the real test. + try: + + test = TestCmd.TestCmd(program = t.recv_script, + interpreter = 'python', + workdir = '', + subdir = 'script_subdir') + + test.start(stdin=1) + test.finish() + expect_stdout = """\ +script_recv: STDOUT +""" + expect_stderr = """\ +script_recv: STDERR +""" + stdout = test.stdout() + assert stdout == expect_stdout, stdout + stderr = test.stderr() + assert stderr == expect_stderr, stderr + + p = test.start(stdin=1) + p.send('input\n') + test.finish(p) + expect_stdout = """\ +script_recv: STDOUT +script_recv: STDOUT: input +""" + expect_stderr = """\ +script_recv: STDERR +script_recv: STDERR: input +""" + stdout = test.stdout() + assert stdout == expect_stdout, stdout + stderr = test.stderr() + assert stderr == expect_stderr, stderr + + p = test.start(combine=1, stdin=1) + p.send('input\n') + test.finish(p) + expect_stdout = """\ +script_recv: STDOUT +script_recv: STDERR +script_recv: STDOUT: input +script_recv: STDERR: input +""" + expect_stderr = "" + stdout = test.stdout() + assert stdout == expect_stdout, stdout + stderr = test.stderr() + assert stderr == expect_stderr, stderr + + finally: + os.chdir(t.orig_cwd) + + def test_recv(self): + """Test the recv() method of objects returned by start()""" + + t = self.setup_run_scripts() + + # Everything before this prepared our "source directory." + # Now do the real test. + try: + test = TestCmd.TestCmd(program = t.script, + interpreter = 'python', + workdir = '', + subdir = 'script_subdir') + + p = test.start() + stdout = p.recv() + while stdout == '': + import time + time.sleep(1) + stdout = p.recv() + self.run_match(stdout, t.script, "STDOUT", t.workdir, + repr([])) + p.wait() + + finally: + os.chdir(t.orig_cwd) + + def test_recv_err(self): + """Test the recv_err() method of objects returned by start()""" + + t = self.setup_run_scripts() + + # Everything before this prepared our "source directory." + # Now do the real test. + try: + + test = TestCmd.TestCmd(program = t.script, + interpreter = 'python', + workdir = '', + subdir = 'script_subdir') + + p = test.start() + stderr = p.recv_err() + while stderr == '': + import time + time.sleep(1) + stderr = p.recv_err() + self.run_match(stderr, t.script, "STDERR", t.workdir, + repr([])) + p.wait() + + + finally: + os.chdir(t.orig_cwd) + + def test_send(self): + """Test the send() method of objects returned by start()""" + + t = self.setup_run_scripts() + + # Everything before this prepared our "source directory." + # Now do the real test. + try: + + test = TestCmd.TestCmd(program = t.recv_script, + interpreter = 'python', + workdir = '', + subdir = 'script_subdir') + + p = test.start(stdin=1) + input = 'stdin.write() input to the receive script\n' + p.stdin.write(input) + p.stdin.close() + p.wait() + result = open(t.recv_out_path, 'rb').read() + expect = 'script_recv: ' + input + assert result == expect, repr(result) + + p = test.start(stdin=1) + input = 'send() input to the receive script\n' + p.send(input) + p.stdin.close() + p.wait() + result = open(t.recv_out_path, 'rb').read() + expect = 'script_recv: ' + input + assert result == expect, repr(result) + + finally: + os.chdir(t.orig_cwd) + + # TODO(sgk): figure out how to eliminate the race conditions here. + def __FLAKY__test_send_recv(self): + """Test the send_recv() method of objects returned by start()""" + + t = self.setup_run_scripts() + + # Everything before this prepared our "source directory." + # Now do the real test. + try: + + test = TestCmd.TestCmd(program = t.recv_script, + interpreter = 'python', + workdir = '', + subdir = 'script_subdir') + + def do_send_recv(p, input): + send, stdout, stderr = p.send_recv(input) + stdout = self.translate_newlines(stdout) + stderr = self.translate_newlines(stderr) + return send, stdout, stderr + + p = test.start(stdin=1) + input = 'input to the receive script\n' + send, stdout, stderr = do_send_recv(p, input) + # Buffering issues and a race condition prevent this from + # being completely deterministic, so check for both null + # output and the first write() on each stream. + assert stdout in ("", "script_recv: STDOUT\n"), stdout + assert stderr in ("", "script_recv: STDERR\n"), stderr + send, stdout, stderr = do_send_recv(p, input) + assert stdout in ("", "script_recv: STDOUT\n"), stdout + assert stderr in ("", "script_recv: STDERR\n"), stderr + p.stdin.close() + stdout = self.translate_newlines(p.recv()) + stderr = self.translate_newlines(p.recv_err()) + assert stdout in ("", "script_recv: STDOUT\n"), stdout + assert stderr in ("", "script_recv: STDERR\n"), stderr + p.wait() + stdout = self.translate_newlines(p.recv()) + stderr = self.translate_newlines(p.recv_err()) + expect_stdout = """\ +script_recv: STDOUT +script_recv: STDOUT: input to the receive script +script_recv: STDOUT: input to the receive script +""" + expect_stderr = """\ +script_recv: STDERR +script_recv: STDERR: input to the receive script +script_recv: STDERR: input to the receive script +""" + assert stdout == expect_stdout, stdout + assert stderr == expect_stderr, stderr + result = open(t.recv_out_path, 'rb').read() + expect = ('script_recv: ' + input) * 2 + assert result == expect, (result, stdout, stderr) + + finally: + os.chdir(t.orig_cwd) + + + +class stdin_TestCase(TestCmdTestCase): + def test_stdin(self): + """Test stdin()""" + run_env = TestCmd.TestCmd(workdir = '') + run_env.write('run', """from __future__ import print_function +import fileinput +for line in fileinput.input(): + print('Y'.join(line[:-1].split('X'))) +""") + run_env.write('input', "X on X this X line X\n") + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') + test.run(arguments = 'input') + assert test.stdout() == "Y on Y this Y line Y\n" + test.run(stdin = "X is X here X tooX\n") + assert test.stdout() == "Y is Y here Y tooY\n" + test.run(stdin = """X here X +X there X +""") + assert test.stdout() == "Y here Y\nY there Y\n" + test.run(stdin = ["X line X\n", "X another X\n"]) + assert test.stdout() == "Y line Y\nY another Y\n" + + + +class stdout_TestCase(TestCmdTestCase): + def test_stdout(self): + """Test stdout()""" + run_env = TestCmd.TestCmd(workdir = '') + run_env.write('run1', """import sys +sys.stdout.write("run1 STDOUT %s\\n" % sys.argv[1:]) +sys.stdout.write("run1 STDOUT second line\\n") +sys.stderr.write("run1 STDERR %s\\n" % sys.argv[1:]) +sys.stderr.write("run1 STDERR second line\\n") +""") + run_env.write('run2', """import sys +sys.stdout.write("run2 STDOUT %s\\n" % sys.argv[1:]) +sys.stdout.write("run2 STDOUT second line\\n") +sys.stderr.write("run2 STDERR %s\\n" % sys.argv[1:]) +sys.stderr.write("run2 STDERR second line\\n") +""") + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + test = TestCmd.TestCmd(interpreter = 'python', workdir = '') + try: + output = test.stdout() + except IndexError: + pass + else: + raise IndexError("got unexpected output:\n\t`%s'\n" % output) + test.program_set('run1') + test.run(arguments = 'foo bar') + test.program_set('run2') + test.run(arguments = 'snafu') + # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL + output = test.stdout() + assert output == "run2 STDOUT ['snafu']\nrun2 STDOUT second line\n", output + output = test.stdout(run = -1) + assert output == "run1 STDOUT ['foo', 'bar']\nrun1 STDOUT second line\n", output + + + +class subdir_TestCase(TestCmdTestCase): + def test_subdir(self): + """Test subdir()""" + test = TestCmd.TestCmd(workdir = '', subdir = ['no', 'such', 'subdir']) + assert not os.path.exists(test.workpath('no')) + + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + assert test.subdir('bar') == 1 + assert test.subdir(['foo', 'succeed']) == 1 + if os.name != "nt": + os.chmod(test.workpath('foo'), 0o500) + assert test.subdir(['foo', 'fail']) == 0 + assert test.subdir(['sub', 'dir', 'ectory'], 'sub') == 1 + assert test.subdir('one', + UserList(['one', 'two']), + ['one', 'two', 'three']) == 3 + assert os.path.isdir(test.workpath('foo')) + assert os.path.isdir(test.workpath('bar')) + assert os.path.isdir(test.workpath('foo', 'succeed')) + if os.name != "nt": + assert not os.path.exists(test.workpath('foo', 'fail')) + assert os.path.isdir(test.workpath('sub')) + assert not os.path.exists(test.workpath('sub', 'dir')) + assert not os.path.exists(test.workpath('sub', 'dir', 'ectory')) + assert os.path.isdir(test.workpath('one', 'two', 'three')) + + + +class symlink_TestCase(TestCmdTestCase): + def test_symlink(self): + """Test symlink()""" + try: os.symlink + except AttributeError: return + + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + wdir_file1 = os.path.join(test.workdir, 'file1') + wdir_target1 = os.path.join(test.workdir, 'target1') + wdir_foo_file2 = os.path.join(test.workdir, 'foo', 'file2') + wdir_target2 = os.path.join(test.workdir, 'target2') + wdir_foo_target2 = os.path.join(test.workdir, 'foo', 'target2') + + test.symlink('target1', 'file1') + assert os.path.islink(wdir_file1) + assert not os.path.exists(wdir_file1) + open(wdir_target1, 'w').write("") + assert os.path.exists(wdir_file1) + + test.symlink('target2', ['foo', 'file2']) + assert os.path.islink(wdir_foo_file2) + assert not os.path.exists(wdir_foo_file2) + open(wdir_target2, 'w').write("") + assert not os.path.exists(wdir_foo_file2) + open(wdir_foo_target2, 'w').write("") + assert os.path.exists(wdir_foo_file2) + + + +class tempdir_TestCase(TestCmdTestCase): + def setUp(self): + TestCmdTestCase.setUp(self) + self._tempdir = tempfile.mktemp() + os.mkdir(self._tempdir) + os.chdir(self._tempdir) + + def tearDown(self): + TestCmdTestCase.tearDown(self) + os.rmdir(self._tempdir) + + def test_tempdir(self): + """Test tempdir()""" + test = TestCmd.TestCmd() + tdir1 = test.tempdir() + assert os.path.isdir(tdir1) + test.workdir_set(None) + test.cleanup() + assert not os.path.exists(tdir1) + + test = TestCmd.TestCmd() + tdir2 = test.tempdir('temp') + assert os.path.isdir(tdir2) + tdir3 = test.tempdir() + assert os.path.isdir(tdir3) + test.workdir_set(None) + test.cleanup() + assert not os.path.exists(tdir2) + assert not os.path.exists(tdir3) + + +timeout_script = """\ +import sys +import time +seconds = int(sys.argv[1]) +sys.stdout.write('sleeping %s\\n' % seconds) +sys.stdout.flush() +time.sleep(seconds) +sys.stdout.write('slept %s\\n' % seconds) +sys.stdout.flush() +sys.exit(0) +""" + +class timeout_TestCase(TestCmdTestCase): + def test_initialization(self): + """Test initialization timeout""" + test = TestCmd.TestCmd(workdir='', timeout=2) + test.write('sleep.py', timeout_script) + + test.run([sys.executable, test.workpath('sleep.py'), '4']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 4\n', test.stdout() + + test.run([sys.executable, test.workpath('sleep.py'), '4']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 4\n', test.stdout() + + def test_cancellation(self): + """Test timer cancellation after firing""" + test = TestCmd.TestCmd(workdir='', timeout=4) + test.write('sleep.py', timeout_script) + + test.run([sys.executable, test.workpath('sleep.py'), '6']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 6\n', test.stdout() + + test.run([sys.executable, test.workpath('sleep.py'), '2']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 2\nslept 2\n', test.stdout() + + test.run([sys.executable, test.workpath('sleep.py'), '6']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 6\n', test.stdout() + + def test_run(self): + """Test run() timeout""" + test = TestCmd.TestCmd(workdir='', timeout=8) + test.write('sleep.py', timeout_script) + + test.run([sys.executable, test.workpath('sleep.py'), '2'], + timeout=4) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 2\nslept 2\n', test.stdout() + + test.run([sys.executable, test.workpath('sleep.py'), '6'], + timeout=4) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 6\n', test.stdout() + + def test_set_timeout(self): + """Test set_timeout()""" + test = TestCmd.TestCmd(workdir='', timeout=2) + test.write('sleep.py', timeout_script) + + test.run([sys.executable, test.workpath('sleep.py'), '4']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 4\n', test.stdout() + + test.set_timeout(None) + + test.run([sys.executable, test.workpath('sleep.py'), '4']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 4\nslept 4\n', test.stdout() + + test.set_timeout(6) + + test.run([sys.executable, test.workpath('sleep.py'), '4']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 4\nslept 4\n', test.stdout() + + test.run([sys.executable, test.workpath('sleep.py'), '8']) + assert test.stderr() == '', test.stderr() + assert test.stdout() == 'sleeping 8\n', test.stdout() + + + +class unlink_TestCase(TestCmdTestCase): + def test_unlink(self): + """Test unlink()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + wdir_file1 = os.path.join(test.workdir, 'file1') + wdir_file2 = os.path.join(test.workdir, 'file2') + wdir_foo_file3a = os.path.join(test.workdir, 'foo', 'file3a') + wdir_foo_file3b = os.path.join(test.workdir, 'foo', 'file3b') + wdir_foo_file4 = os.path.join(test.workdir, 'foo', 'file4') + wdir_file5 = os.path.join(test.workdir, 'file5') + + open(wdir_file1, 'w').write("") + open(wdir_file2, 'w').write("") + open(wdir_foo_file3a, 'w').write("") + open(wdir_foo_file3b, 'w').write("") + open(wdir_foo_file4, 'w').write("") + open(wdir_file5, 'w').write("") + + try: + contents = test.unlink('no_file') + except OSError: # expect "No such file or directory" + pass + except: + raise + + test.unlink("file1") + assert not os.path.exists(wdir_file1) + + test.unlink(wdir_file2) + assert not os.path.exists(wdir_file2) + + test.unlink(['foo', 'file3a']) + assert not os.path.exists(wdir_foo_file3a) + + test.unlink(UserList(['foo', 'file3b'])) + assert not os.path.exists(wdir_foo_file3b) + + test.unlink([test.workdir, 'foo', 'file4']) + assert not os.path.exists(wdir_foo_file4) + + # Make it so we can't unlink file5. + # For UNIX, remove write permission from the dir and the file. + # For Windows, open the file. + os.chmod(test.workdir, 0o500) + os.chmod(wdir_file5, 0o400) + f = open(wdir_file5, 'r') + + try: + try: + test.unlink('file5') + except OSError: # expect "Permission denied" + pass + except: + raise + finally: + os.chmod(test.workdir, 0o700) + os.chmod(wdir_file5, 0o600) + f.close() + + + +class touch_TestCase(TestCmdTestCase): + def test_touch(self): + """Test touch()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'sub') + + wdir_file1 = os.path.join(test.workdir, 'file1') + wdir_sub_file2 = os.path.join(test.workdir, 'sub', 'file2') + + open(wdir_file1, 'w').write("") + open(wdir_sub_file2, 'w').write("") + + file1_old_time = os.path.getmtime(wdir_file1) + file2_old_time = os.path.getmtime(wdir_sub_file2) + + test.sleep() + + test.touch(wdir_file1) + + file1_new_time = os.path.getmtime(wdir_file1) + assert file1_new_time > file1_old_time + + test.touch('file1', file1_old_time) + + result = os.path.getmtime(wdir_file1) + # Sub-second granularity of file systems may still vary. + # On Windows, the two times may be off by a microsecond. + assert int(result) == int(file1_old_time), (result, file1_old_time) + + test.touch(['sub', 'file2']) + + file2_new_time = os.path.getmtime(wdir_sub_file2) + assert file2_new_time > file2_old_time + + + +class verbose_TestCase(TestCmdTestCase): + def test_verbose(self): + """Test verbose()""" + test = TestCmd.TestCmd() + assert test.verbose == 0, 'verbose already initialized?' + test = TestCmd.TestCmd(verbose = 1) + assert test.verbose == 1, 'did not initialize verbose' + test.verbose = 2 + assert test.verbose == 2, 'did not set verbose' + + + +class workdir_TestCase(TestCmdTestCase): + def test_workdir(self): + """Test workdir()""" + run_env = TestCmd.TestCmd(workdir = '') + os.chdir(run_env.workdir) + # Everything before this prepared our "source directory." + # Now do the real test. + test = TestCmd.TestCmd() + assert test.workdir is None + + test = TestCmd.TestCmd(workdir = None) + assert test.workdir is None + + test = TestCmd.TestCmd(workdir = '') + assert test.workdir != None + assert os.path.isdir(test.workdir) + + test = TestCmd.TestCmd(workdir = 'dir') + assert test.workdir != None + assert os.path.isdir(test.workdir) + + no_such_subdir = os.path.join('no', 'such', 'subdir') + try: + test = TestCmd.TestCmd(workdir = no_such_subdir) + except OSError: # expect "No such file or directory" + pass + except: + raise + + test = TestCmd.TestCmd(workdir = 'foo') + workdir_foo = test.workdir + assert workdir_foo != None + + test.workdir_set('bar') + workdir_bar = test.workdir + assert workdir_bar != None + + try: + test.workdir_set(no_such_subdir) + except OSError: + pass # expect "No such file or directory" + except: + raise + assert workdir_bar == test.workdir + + assert os.path.isdir(workdir_foo) + assert os.path.isdir(workdir_bar) + + + +class workdirs_TestCase(TestCmdTestCase): + def test_workdirs(self): + """Test workdirs()""" + test = TestCmd.TestCmd() + assert test.workdir is None + test.workdir_set('') + wdir1 = test.workdir + test.workdir_set('') + wdir2 = test.workdir + assert os.path.isdir(wdir1) + assert os.path.isdir(wdir2) + test.cleanup() + assert not os.path.exists(wdir1) + assert not os.path.exists(wdir2) + + + +class workpath_TestCase(TestCmdTestCase): + def test_workpath(self): + """Test workpath()""" + test = TestCmd.TestCmd() + assert test.workdir is None + + test = TestCmd.TestCmd(workdir = '') + wpath = test.workpath('foo', 'bar') + assert wpath == os.path.join(test.workdir, 'foo', 'bar') + + + +class readable_TestCase(TestCmdTestCase): + def test_readable(self): + """Test readable()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + test.write('file1', "Test file #1\n") + test.write(['foo', 'file2'], "Test file #2\n") + + try: symlink = os.symlink + except AttributeError: pass + else: symlink('no_such_file', test.workpath('dangling_symlink')) + + test.readable(test.workdir, 0) + # XXX skip these tests if euid == 0? + assert not _is_readable(test.workdir) + assert not _is_readable(test.workpath('file1')) + assert not _is_readable(test.workpath('foo')) + assert not _is_readable(test.workpath('foo', 'file2')) + + test.readable(test.workdir, 1) + assert _is_readable(test.workdir) + assert _is_readable(test.workpath('file1')) + assert _is_readable(test.workpath('foo')) + assert _is_readable(test.workpath('foo', 'file2')) + + test.readable(test.workdir, 0) + # XXX skip these tests if euid == 0? + assert not _is_readable(test.workdir) + assert not _is_readable(test.workpath('file1')) + assert not _is_readable(test.workpath('foo')) + assert not _is_readable(test.workpath('foo', 'file2')) + + test.readable(test.workpath('file1'), 1) + assert _is_readable(test.workpath('file1')) + + test.readable(test.workpath('file1'), 0) + assert not _is_readable(test.workpath('file1')) + + test.readable(test.workdir, 1) + + + +class writable_TestCase(TestCmdTestCase): + def test_writable(self): + """Test writable()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + test.write('file1', "Test file #1\n") + test.write(['foo', 'file2'], "Test file #2\n") + + try: symlink = os.symlink + except AttributeError: pass + else: symlink('no_such_file', test.workpath('dangling_symlink')) + + test.writable(test.workdir, 0) + # XXX skip these tests if euid == 0? + assert not _is_writable(test.workdir) + assert not _is_writable(test.workpath('file1')) + assert not _is_writable(test.workpath('foo')) + assert not _is_writable(test.workpath('foo', 'file2')) + + test.writable(test.workdir, 1) + assert _is_writable(test.workdir) + assert _is_writable(test.workpath('file1')) + assert _is_writable(test.workpath('foo')) + assert _is_writable(test.workpath('foo', 'file2')) + + test.writable(test.workdir, 0) + # XXX skip these tests if euid == 0? + assert not _is_writable(test.workdir) + assert not _is_writable(test.workpath('file1')) + assert not _is_writable(test.workpath('foo')) + assert not _is_writable(test.workpath('foo', 'file2')) + + test.writable(test.workpath('file1'), 1) + assert _is_writable(test.workpath('file1')) + + test.writable(test.workpath('file1'), 0) + assert not _is_writable(test.workpath('file1')) + + + +class executable_TestCase(TestCmdTestCase): + def test_executable(self): + """Test executable()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + test.write('file1', "Test file #1\n") + test.write(['foo', 'file2'], "Test file #2\n") + + try: symlink = os.symlink + except AttributeError: pass + else: symlink('no_such_file', test.workpath('dangling_symlink')) + + def make_executable(fname): + st = os.stat(fname) + os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0o100)) + + def make_non_executable(fname): + st = os.stat(fname) + os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0o100)) + + test.executable(test.workdir, 0) + # XXX skip these tests if euid == 0? + assert not _is_executable(test.workdir) + make_executable(test.workdir) + assert not _is_executable(test.workpath('file1')) + assert not _is_executable(test.workpath('foo')) + make_executable(test.workpath('foo')) + assert not _is_executable(test.workpath('foo', 'file2')) + make_non_executable(test.workpath('foo')) + make_non_executable(test.workdir) + + test.executable(test.workdir, 1) + assert _is_executable(test.workdir) + assert _is_executable(test.workpath('file1')) + assert _is_executable(test.workpath('foo')) + assert _is_executable(test.workpath('foo', 'file2')) + + test.executable(test.workdir, 0) + # XXX skip these tests if euid == 0? + assert not _is_executable(test.workdir) + make_executable(test.workdir) + assert not _is_executable(test.workpath('file1')) + assert not _is_executable(test.workpath('foo')) + make_executable(test.workpath('foo')) + assert not _is_executable(test.workpath('foo', 'file2')) + + test.executable(test.workpath('file1'), 1) + assert _is_executable(test.workpath('file1')) + + test.executable(test.workpath('file1'), 0) + assert not _is_executable(test.workpath('file1')) + + test.executable(test.workdir, 1) + + + +class write_TestCase(TestCmdTestCase): + def test_write(self): + """Test write()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + test.write('file1', "Test file #1\n") + test.write(['foo', 'file2'], "Test file #2\n") + try: + test.write(['bar', 'file3'], "Test file #3 (should not get created)\n") + except IOError: # expect "No such file or directory" + pass + except: + raise + test.write(test.workpath('file4'), "Test file #4.\n") + test.write(test.workpath('foo', 'file5'), "Test file #5.\n") + try: + test.write(test.workpath('bar', 'file6'), "Test file #6 (should not get created)\n") + except IOError: # expect "No such file or directory" + pass + except: + raise + + try: + test.write('file7', "Test file #8.\n", mode = 'r') + except ValueError: # expect "mode must begin with 'w' + pass + except: + raise + + test.write('file8', "Test file #8.\n", mode = 'w') + test.write('file9', "Test file #9.\r\n", mode = 'wb') + + if os.name != "nt": + os.chmod(test.workdir, 0o500) + try: + test.write('file10', "Test file #10 (should not get created).\n") + except IOError: # expect "Permission denied" + pass + except: + raise + + assert os.path.isdir(test.workpath('foo')) + assert not os.path.exists(test.workpath('bar')) + assert os.path.isfile(test.workpath('file1')) + assert os.path.isfile(test.workpath('foo', 'file2')) + assert not os.path.exists(test.workpath('bar', 'file3')) + assert os.path.isfile(test.workpath('file4')) + assert os.path.isfile(test.workpath('foo', 'file5')) + assert not os.path.exists(test.workpath('bar', 'file6')) + assert not os.path.exists(test.workpath('file7')) + assert os.path.isfile(test.workpath('file8')) + assert os.path.isfile(test.workpath('file9')) + if os.name != "nt": + assert not os.path.exists(test.workpath('file10')) + + assert open(test.workpath('file8'), 'r').read() == "Test file #8.\n" + assert open(test.workpath('file9'), 'rb').read() == "Test file #9.\r\n" + + + +class variables_TestCase(TestCmdTestCase): + def test_variables(self): + """Test global variables""" + run_env = TestCmd.TestCmd(workdir = '') + + variables = [ + 'fail_test', + 'no_result', + 'pass_test', + 'match_exact', + 'match_re', + 'match_re_dotall', + 'python', + '_python_', + 'TestCmd', + ] + + script = "from __future__ import print_function\n" + \ + "import TestCmd\n" + \ + '\n'.join([ "print(TestCmd.%s\n)" % v for v in variables ]) + run_env.run(program=sys.executable, stdin=script) + stderr = run_env.stderr() + assert stderr == "", stderr + + script = "from __future__ import print_function\n" + \ + "from TestCmd import *\n" + \ + '\n'.join([ "print(%s)" % v for v in variables ]) + run_env.run(program=sys.executable, stdin=script) + stderr = run_env.stderr() + assert stderr == "", stderr + + + +if __name__ == "__main__": + tclasses = [ + __init__TestCase, + basename_TestCase, + cleanup_TestCase, + chmod_TestCase, + combine_TestCase, + command_args_TestCase, + description_TestCase, + diff_TestCase, + diff_stderr_TestCase, + diff_stdout_TestCase, + exit_TestCase, + fail_test_TestCase, + interpreter_TestCase, + match_TestCase, + match_exact_TestCase, + match_re_dotall_TestCase, + match_re_TestCase, + match_stderr_TestCase, + match_stdout_TestCase, + no_result_TestCase, + pass_test_TestCase, + preserve_TestCase, + program_TestCase, + read_TestCase, + rmdir_TestCase, + run_TestCase, + run_verbose_TestCase, + set_diff_function_TestCase, + set_match_function_TestCase, + sleep_TestCase, + start_TestCase, + stderr_TestCase, + stdin_TestCase, + stdout_TestCase, + subdir_TestCase, + symlink_TestCase, + tempdir_TestCase, + timeout_TestCase, + unlink_TestCase, + touch_TestCase, + verbose_TestCase, + workdir_TestCase, + workdirs_TestCase, + workpath_TestCase, + writable_TestCase, + write_TestCase, + variables_TestCase, + ] + if sys.platform != 'win32': + tclasses.extend([ + executable_TestCase, + readable_TestCase, + ]) + suite = unittest.TestSuite() + for tclass in tclasses: + names = unittest.getTestCaseNames(tclass, 'test_') + suite.addTests([ tclass(n) for n in names ]) + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py new file mode 100644 index 0000000..47a149b --- /dev/null +++ b/testing/framework/TestCommon.py @@ -0,0 +1,748 @@ +""" +TestCommon.py: a testing framework for commands and scripts + with commonly useful error handling + +The TestCommon module provides a simple, high-level interface for writing +tests of executable commands and scripts, especially commands and scripts +that interact with the file system. All methods throw exceptions and +exit on failure, with useful error messages. This makes a number of +explicit checks unnecessary, making the test scripts themselves simpler +to write and easier to read. + +The TestCommon class is a subclass of the TestCmd class. In essence, +TestCommon is a wrapper that handles common TestCmd error conditions in +useful ways. You can use TestCommon directly, or subclass it for your +program and add additional (or override) methods to tailor it to your +program's specific needs. Alternatively, the TestCommon class serves +as a useful example of how to define your own TestCmd subclass. + +As a subclass of TestCmd, TestCommon provides access to all of the +variables and methods from the TestCmd module. Consequently, you can +use any variable or method documented in the TestCmd module without +having to explicitly import TestCmd. + +A TestCommon environment object is created via the usual invocation: + + import TestCommon + test = TestCommon.TestCommon() + +You can use all of the TestCmd keyword arguments when instantiating a +TestCommon object; see the TestCmd documentation for details. + +Here is an overview of the methods and keyword arguments that are +provided by the TestCommon class: + + test.must_be_writable('file1', ['file2', ...]) + + test.must_contain('file', 'required text\n') + + test.must_contain_all(output, input, ['title', find]) + + test.must_contain_all_lines(output, lines, ['title', find]) + + test.must_contain_any_line(output, lines, ['title', find]) + + test.must_contain_exactly_lines(output, lines, ['title', find]) + + test.must_exist('file1', ['file2', ...]) + + test.must_match('file', "expected contents\n") + + test.must_not_be_writable('file1', ['file2', ...]) + + test.must_not_contain('file', 'banned text\n') + + test.must_not_contain_any_line(output, lines, ['title', find]) + + test.must_not_exist('file1', ['file2', ...]) + + test.run(options = "options to be prepended to arguments", + stdout = "expected standard output from the program", + stderr = "expected error output from the program", + status = expected_status, + match = match_function) + +The TestCommon module also provides the following variables + + TestCommon.python + TestCommon._python_ + TestCommon.exe_suffix + TestCommon.obj_suffix + TestCommon.shobj_prefix + TestCommon.shobj_suffix + TestCommon.lib_prefix + TestCommon.lib_suffix + TestCommon.dll_prefix + TestCommon.dll_suffix + +""" + +# Copyright 2000-2010 Steven Knight +# This module is free software, and you may redistribute it and/or modify +# it under the same terms as Python itself, so long as this copyright message +# and disclaimer are retained in their original form. +# +# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# +# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + +from __future__ import print_function + +__author__ = "Steven Knight " +__revision__ = "TestCommon.py 1.3.D001 2010/06/03 12:58:27 knight" +__version__ = "1.3" + +import copy +import os +import stat +import sys +import glob + +try: + from collections import UserList +except ImportError: + # no 'collections' module or no UserList in collections + exec('from UserList import UserList') + +from TestCmd import * +from TestCmd import __all__ + +__all__.extend([ 'TestCommon', + 'exe_suffix', + 'obj_suffix', + 'shobj_prefix', + 'shobj_suffix', + 'lib_prefix', + 'lib_suffix', + 'dll_prefix', + 'dll_suffix', + ]) + +# Variables that describe the prefixes and suffixes on this system. +if sys.platform == 'win32': + exe_suffix = '.exe' + obj_suffix = '.obj' + shobj_suffix = '.obj' + shobj_prefix = '' + lib_prefix = '' + lib_suffix = '.lib' + dll_prefix = '' + dll_suffix = '.dll' +elif sys.platform == 'cygwin': + exe_suffix = '.exe' + obj_suffix = '.o' + shobj_suffix = '.os' + shobj_prefix = '' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'cyg' + dll_suffix = '.dll' +elif sys.platform.find('irix') != -1: + exe_suffix = '' + obj_suffix = '.o' + shobj_suffix = '.o' + shobj_prefix = '' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'lib' + dll_suffix = '.so' +elif sys.platform.find('darwin') != -1: + exe_suffix = '' + obj_suffix = '.o' + shobj_suffix = '.os' + shobj_prefix = '' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'lib' + dll_suffix = '.dylib' +elif sys.platform.find('sunos') != -1: + exe_suffix = '' + obj_suffix = '.o' + shobj_suffix = '.o' + shobj_prefix = 'so_' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'lib' + dll_suffix = '.so' +else: + exe_suffix = '' + obj_suffix = '.o' + shobj_suffix = '.os' + shobj_prefix = '' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'lib' + dll_suffix = '.so' + +def is_List(e): + return isinstance(e, (list, UserList)) + +def is_Tuple(e): + return isinstance(e, tuple) + +def is_Sequence(e): + return (not hasattr(e, "strip") and + hasattr(e, "__getitem__") or + hasattr(e, "__iter__")) + +def is_writable(f): + mode = os.stat(f)[stat.ST_MODE] + return mode & stat.S_IWUSR + +def separate_files(flist): + existing = [] + missing = [] + for f in flist: + if os.path.exists(f): + existing.append(f) + else: + missing.append(f) + return existing, missing + +if os.name == 'posix': + def _failed(self, status = 0): + if self.status is None or status is None: + return None + return _status(self) != status + def _status(self): + return self.status +elif os.name == 'nt': + def _failed(self, status = 0): + return not (self.status is None or status is None) and \ + self.status != status + def _status(self): + return self.status + +class TestCommon(TestCmd): + + # Additional methods from the Perl Test::Cmd::Common module + # that we may wish to add in the future: + # + # $test->subdir('subdir', ...); + # + # $test->copy('src_file', 'dst_file'); + + def __init__(self, **kw): + """Initialize a new TestCommon instance. This involves just + calling the base class initialization, and then changing directory + to the workdir. + """ + TestCmd.__init__(self, **kw) + os.chdir(self.workdir) + + def options_arguments(self, options, arguments): + """Merges the "options" keyword argument with the arguments.""" + if options: + if arguments is None: + return options + if isinstance(options, str): + options = [options] + if isinstance(arguments, str): + arguments = [arguments] + arguments = ' '.join(options + arguments) + return arguments + + def must_be_writable(self, *files): + """Ensures that the specified file(s) exist and are writable. + An individual file can be specified as a list of directory names, + in which case the pathname will be constructed by concatenating + them. Exits FAILED if any of the files does not exist or is + not writable. + """ + files = [is_List(x) and os.path.join(*x) or x for x in files] + existing, missing = separate_files(files) + unwritable = [x for x in existing if not is_writable(x)] + if missing: + print("Missing files: `%s'" % "', `".join(missing)) + if unwritable: + print("Unwritable files: `%s'" % "', `".join(unwritable)) + self.fail_test(missing + unwritable) + + def must_contain(self, file, required, mode = 'rb', find = None): + """Ensures that the specified file contains the required text. + """ + file_contents = self.read(file, mode) + if find is None: + def find(o, l): + try: + return o.index(l) + except ValueError: + return None + contains = find(file_contents, required) + if not contains: + print("File `%s' does not contain required string." % file) + print(self.banner('Required string ')) + print(required) + print(self.banner('%s contents ' % file)) + print(file_contents) + self.fail_test(not contains) + + def must_contain_all(self, output, input, title=None, find=None): + """Ensures that the specified output string (first argument) + contains all of the specified input as a block (second argument). + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. + """ + if find is None: + def find(o, i): + try: + return o.index(i) + except ValueError: + return None + + if is_List(output): + output = os.newline.join(output) + + if find(output, input) is None: + if title is None: + title = 'output' + print('Missing expected input from {}:'.format(title)) + print(input) + print(self.banner(title + ' ')) + print(output) + self.fail_test() + + def must_contain_all_lines(self, output, lines, title=None, find=None): + """Ensures that the specified output string (first argument) + contains all of the specified lines (second argument). + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. + """ + if find is None: + def find(o, l): + try: + return o.index(l) + except ValueError: + return None + missing = [] + if is_List(output): + output = '\n'.join(output) + + for line in lines: + if find(output, line) is None: + missing.append(line) + + if missing: + if title is None: + title = 'output' + sys.stdout.write("Missing expected lines from %s:\n" % title) + for line in missing: + sys.stdout.write(' ' + repr(line) + '\n') + sys.stdout.write(self.banner(title + ' ') + '\n') + sys.stdout.write(output) + self.fail_test() + + def must_contain_any_line(self, output, lines, title=None, find=None): + """Ensures that the specified output string (first argument) + contains at least one of the specified lines (second argument). + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. + """ + if find is None: + def find(o, l): + try: + return o.index(l) + except ValueError: + return None + for line in lines: + if find(output, line) is not None: + return + + if title is None: + title = 'output' + sys.stdout.write("Missing any expected line from %s:\n" % title) + for line in lines: + sys.stdout.write(' ' + repr(line) + '\n') + sys.stdout.write(self.banner(title + ' ') + '\n') + sys.stdout.write(output) + self.fail_test() + + def must_contain_exactly_lines(self, output, expect, title=None, find=None): + """Ensures that the specified output string (first argument) + contains all of the lines in the expected string (second argument) + with none left over. + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. The function must return the index + of the found line in the output, or None if the line is not found. + """ + out = output.splitlines() + if is_List(expect): + exp = [ e.rstrip('\n') for e in expect ] + else: + exp = expect.splitlines() + if sorted(out) == sorted(exp): + # early out for exact match + return + if find is None: + def find(o, l): + try: + return o.index(l) + except ValueError: + return None + missing = [] + for line in exp: + found = find(out, line) + if found is None: + missing.append(line) + else: + out.pop(found) + + if not missing and not out: + # all lines were matched + return + + if title is None: + title = 'output' + if missing: + sys.stdout.write("Missing expected lines from %s:\n" % title) + for line in missing: + sys.stdout.write(' ' + repr(line) + '\n') + sys.stdout.write(self.banner('Missing %s ' % title) + '\n') + if out: + sys.stdout.write("Extra unexpected lines from %s:\n" % title) + for line in out: + sys.stdout.write(' ' + repr(line) + '\n') + sys.stdout.write(self.banner('Extra %s ' % title) + '\n') + sys.stdout.flush() + self.fail_test() + + def must_contain_lines(self, lines, output, title=None, find = None): + # Deprecated; retain for backwards compatibility. + return self.must_contain_all_lines(output, lines, title, find) + + def must_exist(self, *files): + """Ensures that the specified file(s) must exist. An individual + file be specified as a list of directory names, in which case the + pathname will be constructed by concatenating them. Exits FAILED + if any of the files does not exist. + """ + files = [is_List(x) and os.path.join(*x) or x for x in files] + missing = [x for x in files if not os.path.exists(x) and not os.path.islink(x) ] + if missing: + print("Missing files: `%s'" % "', `".join(missing)) + self.fail_test(missing) + + def must_exist_one_of(self, files): + """Ensures that at least one of the specified file(s) exists. + The filenames can be given as a list, where each entry may be + a single path string, or a tuple of folder names and the final + filename that get concatenated. + Supports wildcard names like 'foo-1.2.3-*.rpm'. + Exits FAILED if none of the files exists. + """ + missing = [] + for x in files: + if is_List(x) or is_Tuple(x): + xpath = os.path.join(*x) + else: + xpath = is_Sequence(x) and os.path.join(x) or x + if glob.glob(xpath): + return + missing.append(xpath) + print("Missing one of: `%s'" % "', `".join(missing)) + self.fail_test(missing) + + def must_match(self, file, expect, mode = 'rb', match=None, message=None, newline=None): + """Matches the contents of the specified file (first argument) + against the expected contents (second argument). The expected + contents are a list of lines or a string which will be split + on newlines. + """ + file_contents = self.read(file, mode, newline) + if not match: + match = self.match + try: + self.fail_test(not match(to_str(file_contents), to_str(expect)), message=message) + except KeyboardInterrupt: + raise + except: + print("Unexpected contents of `%s'" % file) + self.diff(expect, file_contents, 'contents ') + raise + + def must_not_contain(self, file, banned, mode = 'rb', find = None): + """Ensures that the specified file doesn't contain the banned text. + """ + file_contents = self.read(file, mode) + if find is None: + def find(o, l): + try: + return o.index(l) + except ValueError: + return None + contains = find(file_contents, banned) + if contains: + print("File `%s' contains banned string." % file) + print(self.banner('Banned string ')) + print(banned) + print(self.banner('%s contents ' % file)) + print(file_contents) + self.fail_test(contains) + + def must_not_contain_any_line(self, output, lines, title=None, find=None): + """Ensures that the specified output string (first argument) + does not contain any of the specified lines (second argument). + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. + """ + if find is None: + def find(o, l): + try: + return o.index(l) + except ValueError: + return None + unexpected = [] + for line in lines: + if find(output, line) is not None: + unexpected.append(line) + + if unexpected: + if title is None: + title = 'output' + sys.stdout.write("Unexpected lines in %s:\n" % title) + for line in unexpected: + sys.stdout.write(' ' + repr(line) + '\n') + sys.stdout.write(self.banner(title + ' ') + '\n') + sys.stdout.write(output) + self.fail_test() + + def must_not_contain_lines(self, lines, output, title=None, find=None): + return self.must_not_contain_any_line(output, lines, title, find) + + def must_not_exist(self, *files): + """Ensures that the specified file(s) must not exist. + An individual file be specified as a list of directory names, in + which case the pathname will be constructed by concatenating them. + Exits FAILED if any of the files exists. + """ + files = [is_List(x) and os.path.join(*x) or x for x in files] + existing = [x for x in files if os.path.exists(x) or os.path.islink(x)] + if existing: + print("Unexpected files exist: `%s'" % "', `".join(existing)) + self.fail_test(existing) + + def must_not_exist_any_of(self, files): + """Ensures that none of the specified file(s) exists. + The filenames can be given as a list, where each entry may be + a single path string, or a tuple of folder names and the final + filename that get concatenated. + Supports wildcard names like 'foo-1.2.3-*.rpm'. + Exits FAILED if any of the files exists. + """ + existing = [] + for x in files: + if is_List(x) or is_Tuple(x): + xpath = os.path.join(*x) + else: + xpath = is_Sequence(x) and os.path.join(x) or x + if glob.glob(xpath): + existing.append(xpath) + if existing: + print("Unexpected files exist: `%s'" % "', `".join(existing)) + self.fail_test(existing) + + def must_not_be_writable(self, *files): + """Ensures that the specified file(s) exist and are not writable. + An individual file can be specified as a list of directory names, + in which case the pathname will be constructed by concatenating + them. Exits FAILED if any of the files does not exist or is + writable. + """ + files = [is_List(x) and os.path.join(*x) or x for x in files] + existing, missing = separate_files(files) + writable = [file for file in existing if is_writable(file)] + if missing: + print("Missing files: `%s'" % "', `".join(missing)) + if writable: + print("Writable files: `%s'" % "', `".join(writable)) + self.fail_test(missing + writable) + + def _complete(self, actual_stdout, expected_stdout, + actual_stderr, expected_stderr, status, match): + """ + Post-processes running a subcommand, checking for failure + status and displaying output appropriately. + """ + if _failed(self, status): + expect = '' + if status != 0: + expect = " (expected %s)" % str(status) + print("%s returned %s%s" % (self.program, _status(self), expect)) + print(self.banner('STDOUT ')) + print(actual_stdout) + print(self.banner('STDERR ')) + print(actual_stderr) + self.fail_test() + if (expected_stdout is not None + and not match(actual_stdout, expected_stdout)): + self.diff(expected_stdout, actual_stdout, 'STDOUT ') + if actual_stderr: + print(self.banner('STDERR ')) + print(actual_stderr) + self.fail_test() + if (expected_stderr is not None + and not match(actual_stderr, expected_stderr)): + print(self.banner('STDOUT ')) + print(actual_stdout) + self.diff(expected_stderr, actual_stderr, 'STDERR ') + self.fail_test() + + def start(self, program = None, + interpreter = None, + options = None, + arguments = None, + universal_newlines = None, + **kw): + """ + Starts a program or script for the test environment, handling + any exceptions. + """ + arguments = self.options_arguments(options, arguments) + try: + return TestCmd.start(self, program, interpreter, arguments, + universal_newlines, **kw) + except KeyboardInterrupt: + raise + except Exception as e: + print(self.banner('STDOUT ')) + try: + print(self.stdout()) + except IndexError: + pass + print(self.banner('STDERR ')) + try: + print(self.stderr()) + except IndexError: + pass + cmd_args = self.command_args(program, interpreter, arguments) + sys.stderr.write('Exception trying to execute: %s\n' % cmd_args) + raise e + + def finish(self, popen, stdout = None, stderr = '', status = 0, **kw): + """ + Finishes and waits for the process being run under control of + the specified popen argument. Additional arguments are similar + to those of the run() method: + + stdout The expected standard output from + the command. A value of None means + don't test standard output. + + stderr The expected error output from + the command. A value of None means + don't test error output. + + status The expected exit status from the + command. A value of None means don't + test exit status. + """ + TestCmd.finish(self, popen, **kw) + match = kw.get('match', self.match) + self._complete(self.stdout(), stdout, + self.stderr(), stderr, status, match) + + def run(self, options = None, arguments = None, + stdout = None, stderr = '', status = 0, **kw): + """Runs the program under test, checking that the test succeeded. + + The parameters are the same as the base TestCmd.run() method, + with the addition of: + + options Extra options that get prepended to the beginning + of the arguments. + + stdout The expected standard output from + the command. A value of None means + don't test standard output. + + stderr The expected error output from + the command. A value of None means + don't test error output. + + status The expected exit status from the + command. A value of None means don't + test exit status. + + By default, this expects a successful exit (status = 0), does + not test standard output (stdout = None), and expects that error + output is empty (stderr = ""). + """ + kw['arguments'] = self.options_arguments(options, arguments) + try: + match = kw['match'] + del kw['match'] + except KeyError: + match = self.match + TestCmd.run(self, **kw) + self._complete(self.stdout(), stdout, + self.stderr(), stderr, status, match) + + def skip_test(self, message="Skipping test.\n"): + """Skips a test. + + Proper test-skipping behavior is dependent on the external + TESTCOMMON_PASS_SKIPS environment variable. If set, we treat + the skip as a PASS (exit 0), and otherwise treat it as NO RESULT. + In either case, we print the specified message as an indication + that the substance of the test was skipped. + + (This was originally added to support development under Aegis. + Technically, skipping a test is a NO RESULT, but Aegis would + treat that as a test failure and prevent the change from going to + the next step. Since we ddn't want to force anyone using Aegis + to have to install absolutely every tool used by the tests, we + would actually report to Aegis that a skipped test has PASSED + so that the workflow isn't held up.) + """ + if message: + sys.stdout.write(message) + sys.stdout.flush() + pass_skips = os.environ.get('TESTCOMMON_PASS_SKIPS') + if pass_skips in [None, 0, '0']: + # skip=1 means skip this function when showing where this + # result came from. They only care about the line where the + # script called test.skip_test(), not the line number where + # we call test.no_result(). + self.no_result(skip=1) + else: + # We're under the development directory for this change, + # so this is an Aegis invocation; pass the test (exit 0). + self.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/TestCommonTests.py b/testing/framework/TestCommonTests.py new file mode 100644 index 0000000..7949cb8 --- /dev/null +++ b/testing/framework/TestCommonTests.py @@ -0,0 +1,2340 @@ +#!/usr/bin/env python +""" +TestCommonTests.py: Unit tests for the TestCommon.py module. + +Copyright 2000-2010 Steven Knight +This module is free software, and you may redistribute it and/or modify +it under the same terms as Python itself, so long as this copyright message +and disclaimer are retained in their original form. + +IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +""" + +__author__ = "Steven Knight " +__revision__ = "TestCommonTests.py 1.3.D001 2010/06/03 12:58:27 knight" + +import difflib +import os +import re +import signal +import stat +import sys +import unittest + +# Strip the current directory so we get the right TestCommon.py module. +sys.path = sys.path[1:] + +import TestCmd +import TestCommon + +def lstrip(s): + lines = [ _.expandtabs() for _ in s.split('\n') ] + if lines[0] == '': + lines = lines[1:] + spaces = len(re.match('^( *).*', lines[0]).group(1)) + if spaces: + lines = [ l[spaces:] for l in lines ] + return '\n'.join(lines) + +if sys.version[:3] == '1.5': + expected_newline = '\\012' +else: + expected_newline = '\\n' + +def assert_display(expect, result, error=None): + try: + expect = expect.pattern + except AttributeError: + pass + result = [ + '\n', + ('*'*80) + '\n', + expect, + ('*'*80) + '\n', + result, + ('*'*80) + '\n', + ] + if error: + result.append(error) + return ''.join(result) + + +class TestCommonTestCase(unittest.TestCase): + """Base class for TestCommon test cases, fixture and utility methods.""" + create_run_env = True + + def setUp(self): + self.orig_cwd = os.getcwd() + if self.create_run_env: + self.run_env = TestCmd.TestCmd(workdir = '') + + def tearDown(self): + os.chdir(self.orig_cwd) + + def set_up_execution_scripts(self): + run_env = self.run_env + + run_env.subdir('sub dir') + + self.python = sys.executable + + self.pass_script = run_env.workpath('sub dir', 'pass') + self.fail_script = run_env.workpath('sub dir', 'fail') + self.stdout_script = run_env.workpath('sub dir', 'stdout') + self.stderr_script = run_env.workpath('sub dir', 'stderr') + self.signal_script = run_env.workpath('sub dir', 'signal') + self.stdin_script = run_env.workpath('sub dir', 'stdin') + + preamble = "import sys" + stdout = "; sys.stdout.write(r'%s: STDOUT: ' + repr(sys.argv[1:]) + '\\n')" + stderr = "; sys.stderr.write(r'%s: STDERR: ' + repr(sys.argv[1:]) + '\\n')" + exit0 = "; sys.exit(0)" + exit1 = "; sys.exit(1)" + if sys.platform == 'win32': + wrapper = '@python -c "%s" %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9\n' + else: + wrapper = '#! /usr/bin/env python\n%s\n' + wrapper = '#! /usr/bin/env python\n%s\n' + + pass_body = preamble + stdout % self.pass_script + exit0 + fail_body = preamble + stdout % self.fail_script + exit1 + stderr_body = preamble + stderr % self.stderr_script + exit0 + + run_env.write(self.pass_script, wrapper % pass_body) + run_env.write(self.fail_script, wrapper % fail_body) + run_env.write(self.stderr_script, wrapper % stderr_body) + + signal_body = lstrip("""\ + import os + import signal + os.kill(os.getpid(), signal.SIGTERM) + """) + + run_env.write(self.signal_script, wrapper % signal_body) + + stdin_body = lstrip("""\ + import sys + input = sys.stdin.read()[:-1] + sys.stdout.write(r'%s: STDOUT: ' + repr(input) + '\\n') + sys.stderr.write(r'%s: STDERR: ' + repr(input) + '\\n') + """ % (self.stdin_script, self.stdin_script)) + + run_env.write(self.stdin_script, wrapper % stdin_body) + + def run_execution_test(self, script, expect_stdout, expect_stderr): + self.set_up_execution_scripts() + + run_env = self.run_env + + os.chdir(run_env.workpath('sub dir')) + + # Everything before this prepared our "source directory." + # Now do the real test. + script = script % self.__dict__ + run_env.run(program=sys.executable, stdin=script) + + stdout = run_env.stdout() + stderr = run_env.stderr() + + expect_stdout = expect_stdout % self.__dict__ + assert stdout == expect_stdout, assert_display(expect_stdout, + stdout, + stderr) + + try: + match = expect_stderr.match + except AttributeError: + expect_stderr = expect_stderr % self.__dict__ + assert stderr == expect_stderr, assert_display(expect_stderr, + stderr) + else: + assert expect_stderr.match(stderr), assert_display(expect_stderr, + stderr) + + +class __init__TestCase(TestCommonTestCase): + def test___init__(self): + """Test initialization""" + run_env = self.run_env + + os.chdir(run_env.workdir) + script = lstrip("""\ + from __future__ import print_function + from TestCommon import TestCommon + tc = TestCommon(workdir='') + import os + print(os.getcwd()) + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout()[:-1] + assert stdout != run_env.workdir, stdout + stderr = run_env.stderr() + assert stderr == "", stderr + + +class banner_TestCase(TestCommonTestCase): + create_run_env = False + def test_banner(self): + """Test banner()""" + tc = TestCommon.TestCommon(workdir='') + + b = tc.banner('xyzzy ') + assert b == "xyzzy ==========================================================================", b + + tc.banner_width = 10 + + b = tc.banner('xyzzy ') + assert b == "xyzzy ====", b + + b = tc.banner('xyzzy ', 20) + assert b == "xyzzy ==============", b + + tc.banner_char = '-' + + b = tc.banner('xyzzy ') + assert b == "xyzzy ----", b + +class must_be_writable_TestCase(TestCommonTestCase): + def test_file_does_not_exists(self): + """Test must_be_writable(): file does not exist""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_be_writable('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Missing files: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_writable_file_exists(self): + """Test must_be_writable(): writable file exists""" + run_env = self.run_env + + script = lstrip("""\ + import os + import stat + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + f1 = tc.workpath('file1') + mode = os.stat(f1)[stat.ST_MODE] + os.chmod(f1, mode | stat.S_IWUSR) + tc.must_be_writable('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_non_writable_file_exists(self): + """Test must_be_writable(): non-writable file exists""" + run_env = self.run_env + + script = lstrip("""\ + import os + import stat + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + f1 = tc.workpath('file1') + mode = os.stat(f1)[stat.ST_MODE] + os.chmod(f1, mode & ~stat.S_IWUSR) + tc.must_be_writable('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Unwritable files: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_file_specified_as_list(self): + """Test must_be_writable(): file specified as list""" + run_env = self.run_env + + script = lstrip("""\ + import os + import stat + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.subdir('sub') + tc.write(['sub', 'file1'], "sub/file1\\n") + f1 = tc.workpath('sub', 'file1') + mode = os.stat(f1)[stat.ST_MODE] + os.chmod(f1, mode | stat.S_IWUSR) + tc.must_be_writable(['sub', 'file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + +class must_contain_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_contain(): success""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 contents\\n") + tc.must_contain('file1', "1 c") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_missing(self): + """Test must_contain(): file missing""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_contain('file1', "1 c\\n") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr.find("No such file or directory:") != -1, stderr + + def test_failure(self): + """Test must_contain(): failure""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 does not match\\n") + tc.must_contain('file1', "1 c") + tc.run() + """) + expect = lstrip("""\ + File `file1' does not contain required string. + Required string ================================================================ + 1 c + file1 contents ================================================================= + file1 does not match + + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == expect, repr(stdout) + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_mode(self): + """Test must_contain(): mode""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 contents\\n", mode='w') + tc.must_contain('file1', "1 c", mode='r') + tc.write('file2', "file2 contents\\n", mode='wb') + tc.must_contain('file2', "2 c", mode='rb') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + + +class must_contain_all_lines_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_contain_all_lines(): success""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + test.must_contain_all_lines(output, lines) + + test.must_contain_all_lines(output, ['www\\n']) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_failure(self): + """Test must_contain_all_lines(): failure""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + zzz + ''' + + test.must_contain_all_lines(output, lines) + + test.pass_test() + """) + + expect = lstrip("""\ + Missing expected lines from output: + 'xxx%(expected_newline)s' + 'yyy%(expected_newline)s' + output ========================================================================= + www + zzz + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + def test_find(self): + """Test must_contain_all_lines(): find""" + run_env = self.run_env + + script = lstrip(""" + import re + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'x.*', + '.*y', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + def re_search(output, line): + return re.compile(line, re.S).search(output) + test.must_contain_all_lines(output, lines, find=re_search) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_title(self): + """Test must_contain_all_lines(): title""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + zzz + ''' + + test.must_contain_all_lines(output, lines, title='STDERR') + + test.pass_test() + """) + + expect = lstrip("""\ + Missing expected lines from STDERR: + 'xxx%(expected_newline)s' + 'yyy%(expected_newline)s' + STDERR ========================================================================= + www + zzz + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + + +class must_contain_any_line_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_contain_any_line(): success""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'aaa\\n', + 'yyy\\n', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + test.must_contain_any_line(output, lines) + + test.must_contain_any_line(output, ['www\\n']) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_failure(self): + """Test must_contain_any_line(): failure""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + zzz + ''' + + test.must_contain_any_line(output, lines) + + test.pass_test() + """) + + expect = lstrip("""\ + Missing any expected line from output: + 'xxx%(expected_newline)s' + 'yyy%(expected_newline)s' + output ========================================================================= + www + zzz + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + def test_find(self): + """Test must_contain_any_line(): find""" + run_env = self.run_env + + script = lstrip(""" + import re + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'aaa', + '.*y', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + def re_search(output, line): + return re.compile(line, re.S).search(output) + test.must_contain_any_line(output, lines, find=re_search) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_title(self): + """Test must_contain_any_line(): title""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + zzz + ''' + + test.must_contain_any_line(output, lines, title='STDOUT') + + test.pass_test() + """) + + expect = lstrip("""\ + Missing any expected line from STDOUT: + 'xxx%(expected_newline)s' + 'yyy%(expected_newline)s' + STDOUT ========================================================================= + www + zzz + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + + +class must_contain_exactly_lines_TestCase(TestCommonTestCase): + def test_success_list(self): + """Test must_contain_exactly_lines(): success (input list)""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'yyy\\n', + 'xxx\\n', + 'zzz', + 'www\\n', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + test.must_contain_exactly_lines(output, lines) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_success_string(self): + """Test must_contain_exactly_lines(): success (input string)""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = '''\\ + yyy + xxx + zzz + www + ''' + + output = '''\\ + www + xxx + yyy + zzz + ''' + + test.must_contain_exactly_lines(output, lines) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_failure(self): + """Test must_contain_exactly_lines(): failure""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + zzz + ''' + + test.must_contain_exactly_lines(output, lines) + + test.pass_test() + """) + + expect = lstrip("""\ + Missing expected lines from output: + 'xxx' + 'yyy' + Missing output ================================================================= + Extra unexpected lines from output: + 'www' + 'zzz' + Extra output =================================================================== + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + def test_find(self): + """Test must_contain_exactly_lines(): find""" + run_env = self.run_env + + script = lstrip(""" + import re + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'zzz', + '.*y', + 'xxx', + 'www', + ] + + output = '''\\\ + www + xxx + yyy + zzz + ''' + + def re_search(output, line): + pattern = re.compile(line, re.S) + index = 0 + for o in output: + if pattern.search(o): + return index + index +=1 + return None + test.must_contain_exactly_lines(output, lines, find=re_search) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_title(self): + """Test must_contain_exactly_lines(): title""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + zzz + ''' + + test.must_contain_exactly_lines(output, lines, title='STDOUT') + + test.pass_test() + """) + + expect = lstrip("""\ + Missing expected lines from STDOUT: + 'xxx' + 'yyy' + Missing STDOUT ================================================================= + Extra unexpected lines from STDOUT: + 'www' + 'zzz' + Extra STDOUT =================================================================== + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + + +class must_contain_lines_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_contain_lines(): success""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + test.must_contain_lines(lines, output) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_failure(self): + """Test must_contain_lines(): failure""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + zzz + ''' + + test.must_contain_lines(lines, output) + + test.pass_test() + """) + + expect = lstrip("""\ + Missing expected lines from output: + 'xxx%(expected_newline)s' + 'yyy%(expected_newline)s' + output ========================================================================= + www + zzz + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + + +class must_exist_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_exist(): success""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + tc.must_exist('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_failure(self): + """Test must_exist(): failure""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_exist('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Missing files: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_file_specified_as_list(self): + """Test must_exist(): file specified as list""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.subdir('sub') + tc.write(['sub', 'file1'], "sub/file1\\n") + tc.must_exist(['sub', 'file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_broken_link(self) : + """Test must_exist(): exists but it is a broken link""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.symlink('badtarget', "brokenlink") + tc.must_exist('brokenlink') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + +class must_exist_one_of_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_exist_one_of(): success""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + tc.must_exist_one_of(['file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_failure(self): + """Test must_exist_one_of(): failure""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_exist_one_of(['file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Missing one of: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_files_specified_as_list(self): + """Test must_exist_one_of(): files specified as list""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + tc.must_exist_one_of(['file2', 'file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_files_specified_with_wildcards(self): + """Test must_exist_one_of(): files specified with wildcards""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file7', "file7\\n") + tc.must_exist_one_of(['file?']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_given_as_list(self): + """Test must_exist_one_of(): file given as list""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.subdir('sub') + tc.write(['sub', 'file1'], "sub/file1\\n") + tc.must_exist_one_of(['file2', + ['sub', 'file1']]) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_given_as_sequence(self): + """Test must_exist_one_of(): file given as sequence""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.subdir('sub') + tc.write(['sub', 'file1'], "sub/file1\\n") + tc.must_exist_one_of(['file2', + ('sub', 'file1')]) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + +class must_match_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_match(): success""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + tc.must_match('file1', "file1\\n") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_does_not_exists(self): + """Test must_match(): file does not exist""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_match('file1', "file1\\n") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr.find("No such file or directory:") != -1, stderr + + def test_failure(self): + """Test must_match(): failure""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 does not match\\n") + tc.must_match('file1', "file1\\n") + tc.run() + """) + + expect = lstrip("""\ + Unexpected contents of `file1' + contents ======================================================================= + 1c1 + < file1 + --- + > file1 does not match + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == expect, stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_mode(self): + """Test must_match(): mode""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n", mode='w') + tc.must_match('file1', "file1\\n", mode='r') + tc.write('file2', "file2\\n", mode='wb') + tc.must_match('file2', "file2\\n", mode='rb') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + + +class must_not_be_writable_TestCase(TestCommonTestCase): + def test_file_does_not_exists(self): + """Test must_not_be_writable(): file does not exist""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_not_be_writable('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Missing files: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_writable_file_exists(self): + """Test must_not_be_writable(): writable file exists""" + run_env = self.run_env + + script = lstrip("""\ + import os + import stat + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + f1 = tc.workpath('file1') + mode = os.stat(f1)[stat.ST_MODE] + os.chmod(f1, mode | stat.S_IWUSR) + tc.must_not_be_writable('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Writable files: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_non_writable_file_exists(self): + """Test must_not_be_writable(): non-writable file exists""" + run_env = self.run_env + + script = lstrip("""\ + import os + import stat + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + f1 = tc.workpath('file1') + mode = os.stat(f1)[stat.ST_MODE] + os.chmod(f1, mode & ~stat.S_IWUSR) + tc.must_not_be_writable('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_specified_as_list(self): + """Test must_not_be_writable(): file specified as list""" + run_env = self.run_env + + script = lstrip("""\ + import os + import stat + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.subdir('sub') + tc.write(['sub', 'file1'], "sub/file1\\n") + f1 = tc.workpath('sub', 'file1') + mode = os.stat(f1)[stat.ST_MODE] + os.chmod(f1, mode & ~stat.S_IWUSR) + tc.must_not_be_writable(['sub', 'file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + + +class must_not_contain_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_not_contain(): success""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 contents\\n") + tc.must_not_contain('file1', "1 does not contain c") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_does_not_exist(self): + """Test must_not_contain(): file does not exist""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_not_contain('file1', "1 c\\n") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr.find("No such file or directory:") != -1, stderr + + def test_failure(self): + """Test must_not_contain(): failure""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 does contain contents\\n") + tc.must_not_contain('file1', "1 does contain c") + tc.run() + """) + expect = lstrip("""\ + File `file1' contains banned string. + Banned string ================================================================== + 1 does contain c + file1 contents ================================================================= + file1 does contain contents + + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == expect, repr(stdout) + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_mode(self): + """Test must_not_contain(): mode""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 contents\\n", mode='w') + tc.must_not_contain('file1', "1 does not contain c", mode='r') + tc.write('file2', "file2 contents\\n", mode='wb') + tc.must_not_contain('file2', "2 does not contain c", mode='rb') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + + +class must_not_contain_any_line_TestCase(TestCommonTestCase): + def test_failure(self): + """Test must_not_contain_any_line(): failure""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + 'www\\n', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + test.must_not_contain_any_line(output, lines) + + test.pass_test() + """) + + expect = lstrip("""\ + Unexpected lines in output: + 'xxx%(expected_newline)s' + 'yyy%(expected_newline)s' + 'www%(expected_newline)s' + output ========================================================================= + www + xxx + yyy + zzz + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + def test_find(self): + """Test must_not_contain_any_line(): find""" + run_env = self.run_env + + script = lstrip(""" + import re + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'x.*' + '.*y' + ] + + output = '''\\ + www + zzz + ''' + + def re_search(output, line): + return re.compile(line, re.S).search(output) + test.must_not_contain_any_line(output, lines, find=re_search) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_success(self): + """Test must_not_contain_any_line(): success""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n' + 'yyy\\n' + ] + + output = '''\\ + www + zzz + ''' + + test.must_not_contain_any_line(output, lines) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_title(self): + """Test must_not_contain_any_line(): title""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + test.must_not_contain_any_line(output, lines, title='XYZZY') + + test.pass_test() + """) + + expect = lstrip("""\ + Unexpected lines in XYZZY: + 'xxx%(expected_newline)s' + 'yyy%(expected_newline)s' + XYZZY ========================================================================== + www + xxx + yyy + zzz + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + + +class must_not_contain_lines_TestCase(TestCommonTestCase): + def test_failure(self): + """Test must_not_contain_lines(): failure""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n', + 'yyy\\n', + ] + + output = '''\\ + www + xxx + yyy + zzz + ''' + + test.must_not_contain_lines(lines, output) + + test.pass_test() + """) + + expect = lstrip("""\ + Unexpected lines in output: + 'xxx%(expected_newline)s' + 'yyy%(expected_newline)s' + output ========================================================================= + www + xxx + yyy + zzz + """ % globals()) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + stderr = run_env.stderr() + assert stdout == expect, assert_display(expect, stdout, stderr) + assert stderr.find("FAILED") != -1, stderr + + def test_success(self): + """Test must_not_contain_lines(): success""" + run_env = self.run_env + + script = lstrip(""" + import TestCommon + test = TestCommon.TestCommon(workdir='') + + lines = [ + 'xxx\\n' + 'yyy\\n' + ] + + output = '''\\ + www + zzz + ''' + + test.must_not_contain_lines(lines, output) + + test.pass_test() + """) + + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + + +class must_not_exist_TestCase(TestCommonTestCase): + def test_failure(self): + """Test must_not_exist(): failure""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + tc.must_not_exist('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Unexpected files exist: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_success(self): + """Test must_not_exist(): success""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_not_exist('file1') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_specified_as_list(self): + """Test must_not_exist(): file specified as list""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.subdir('sub') + tc.must_not_exist(['sub', 'file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_existing_broken_link(self): + """Test must_not_exist(): exists but it is a broken link""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.symlink('badtarget', 'brokenlink') + tc.must_not_exist('brokenlink') + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Unexpected files exist: `brokenlink'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + +class must_not_exist_any_of_TestCase(TestCommonTestCase): + def test_success(self): + """Test must_not_exist_any_of(): success""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_not_exist_any_of(['file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_failure(self): + """Test must_not_exist_any_of(): failure""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1\\n") + tc.must_not_exist_any_of(['file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Unexpected files exist: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + + def test_files_specified_as_list(self): + """Test must_not_exist_any_of(): files specified as list""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_not_exist_any_of(['file2', 'file1']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_files_specified_with_wildcards(self): + """Test must_not_exist_any_of(): files specified with wildcards""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file7', "file7\\n") + tc.must_not_exist_any_of(['files?']) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_given_as_list(self): + """Test must_not_exist_any_of(): file given as list""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.subdir('sub') + tc.write(['sub', 'file1'], "sub/file1\\n") + tc.must_not_exist_any_of(['file2', + ['sub', 'files*']]) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + def test_file_given_as_sequence(self): + """Test must_not_exist_any_of(): file given as sequence""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.subdir('sub') + tc.write(['sub', 'file1'], "sub/file1\\n") + tc.must_not_exist_any_of(['file2', + ('sub', 'files?')]) + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + +class run_TestCase(TestCommonTestCase): + def test_argument_handling(self): + """Test run(): argument handling""" + + script = lstrip("""\ + from TestCommon import TestCommon, match_exact + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir="", + match=match_exact) + tc.run(arguments = "arg1 arg2 arg3", + stdout = r"%(pass_script)s: STDOUT: ['arg1', 'arg2', 'arg3']" + "\\n") + """) + + self.run_execution_test(script, "", "") + + def test_default_pass(self): + """Test run(): default arguments, script passes""" + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(pass_script)s', + interpreter=r'%(python)s', + workdir='') + tc.run() + """) + + self.run_execution_test(script, "", "") + + def test_default_fail(self): + """Test run(): default arguments, script fails""" + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(fail_script)s', + interpreter='%(python)s', + workdir='') + tc.run() + """) + + expect_stdout = lstrip("""\ + %(fail_script)s returned 1 + STDOUT ========================================================================= + %(fail_script)s: STDOUT: [] + + STDERR ========================================================================= + + """) + + expect_stderr = lstrip("""\ + FAILED test of .*fail + \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) + \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) + \\tfrom line \\d+ of ( \(\))? + """) + expect_stderr = re.compile(expect_stderr, re.M) + + self.run_execution_test(script, expect_stdout, expect_stderr) + + def test_default_stderr(self): + """Test run(): default arguments, error output""" + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(stderr_script)s', + interpreter='%(python)s', + workdir='') + tc.run() + """) + + expect_stdout = lstrip("""\ + STDOUT ========================================================================= + + STDERR ========================================================================= + 0a1 + > %(stderr_script)s: STDERR: [] + """) + + expect_stderr = lstrip("""\ + FAILED test of .*stderr + \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) + \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) + \\tfrom line \\d+ of + """) + expect_stderr = re.compile(expect_stderr, re.M) + + self.run_execution_test(script, expect_stdout, expect_stderr) + + def test_exception_handling(self): + """Test run(): exception handling""" + script = lstrip("""\ + import TestCmd + from TestCommon import TestCommon + def raise_exception(*args, **kw): + raise TypeError("forced TypeError") + TestCmd.TestCmd.start = raise_exception + tc = TestCommon(program='%(pass_script)s', + interpreter='%(python)s', + workdir='') + tc.run() + """) + + expect_stdout = lstrip("""\ + STDOUT ========================================================================= + STDERR ========================================================================= + """) + + expect_stderr = lstrip("""\ + Exception trying to execute: \\[%s, '[^']*pass'\\] + Traceback \\((innermost|most recent call) last\\): + File "", line \\d+, in (\\?|) + File "[^"]+TestCommon.py", line \\d+, in run + TestCmd.run\\(self, \\*\\*kw\\) + File "[^"]+TestCmd.py", line \\d+, in run + .* + File "[^"]+TestCommon.py", line \\d+, in start + raise e + TypeError: forced TypeError + """ % re.escape(repr(sys.executable))) + expect_stderr = re.compile(expect_stderr, re.M) + + self.run_execution_test(script, expect_stdout, expect_stderr) + + def test_ignore_stderr(self): + """Test run(): ignore stderr""" + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(stderr_script)s', + interpreter='%(python)s', + workdir='') + tc.run(stderr = None) + """) + + self.run_execution_test(script, "", "") + + def test_match_function_stdout(self): + """Test run(): explicit match function, stdout""" + + script = lstrip("""\ + def my_match_exact(actual, expect): return actual == expect + from TestCommon import TestCommon, match_re_dotall + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir="", + match=match_re_dotall) + tc.run(arguments = "arg1 arg2 arg3", + stdout = r"%(pass_script)s: STDOUT: ['arg1', 'arg2', 'arg3']" + "\\n", + match = my_match_exact) + """) + + self.run_execution_test(script, "", "") + + def test_match_function_stderr(self): + """Test run(): explicit match function, stderr""" + + script = lstrip("""\ + def my_match_exact(actual, expect): return actual == expect + from TestCommon import TestCommon, match_re_dotall + tc = TestCommon(program=r'%(stderr_script)s', + interpreter='%(python)s', + workdir="", + match=match_re_dotall) + tc.run(arguments = "arg1 arg2 arg3", + stderr = r"%(stderr_script)s: STDERR: ['arg1', 'arg2', 'arg3']" + "\\n", + match = my_match_exact) + """) + + self.run_execution_test(script, "", "") + + def test_matched_status_fails(self): + """Test run(): matched status, script fails""" + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(fail_script)s', + interpreter='%(python)s', + workdir='') + tc.run(status = 1) + """) + + self.run_execution_test(script, "", "") + + def test_matched_stdout(self): + """Test run(): matched stdout""" + + script = lstrip("""\ + from TestCommon import TestCommon, match_exact + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir="", + match=match_exact) + tc.run(stdout = r"%(pass_script)s: STDOUT: []" + "\\n") + """) + + self.run_execution_test(script, "", "") + + def test_matched_stderr(self): + """Test run(): matched stderr""" + + script = lstrip("""\ + from TestCommon import TestCommon, match_exact + tc = TestCommon(program=r'%(stderr_script)s', + interpreter='%(python)s', + workdir="", + match=match_exact) + tc.run(stderr = r"%(stderr_script)s: STDERR: []" + "\\n") + """) + + self.run_execution_test(script, "", "") + + def test_mismatched_status_pass(self): + """Test run(): mismatched status, script passes""" + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir='') + tc.run(status = 1) + """) + + expect_stdout = lstrip("""\ + %(pass_script)s returned 0 (expected 1) + STDOUT ========================================================================= + %(pass_script)s: STDOUT: [] + + STDERR ========================================================================= + + """) + + expect_stderr = lstrip("""\ + FAILED test of .*pass + \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) + \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) + \\tfrom line \\d+ of ( \(\))? + """) + expect_stderr = re.compile(expect_stderr, re.M) + + self.run_execution_test(script, expect_stdout, expect_stderr) + + def test_mismatched_status_fail(self): + """Test run(): mismatched status, script fails""" + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(fail_script)s', + interpreter='%(python)s', + workdir='') + tc.run(status = 2) + """) + + expect_stdout = lstrip("""\ + %(fail_script)s returned 1 (expected 2) + STDOUT ========================================================================= + %(fail_script)s: STDOUT: [] + + STDERR ========================================================================= + + """) + + expect_stderr = lstrip("""\ + FAILED test of .*fail + \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) + \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) + \\tfrom line \\d+ of ( \(\))? + """) + expect_stderr = re.compile(expect_stderr, re.M) + + self.run_execution_test(script, expect_stdout, expect_stderr) + + def test_mismatched_stdout(self): + """Test run(): mismatched stdout""" + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir='') + tc.run(stdout = "Not found\\n") + """) + + expect_stdout = lstrip("""\ + STDOUT ========================================================================= + 1c1 + < Not found + --- + > %(pass_script)s: STDOUT: [] + """) + + expect_stderr = lstrip("""\ + FAILED test of .*pass + \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) + \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) + \\tfrom line \\d+ of ( \(\))? + """) + expect_stderr = re.compile(expect_stderr, re.M) + + self.run_execution_test(script, expect_stdout, expect_stderr) + + def test_mismatched_stderr(self): + """Test run(): mismatched stderr""" + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(stderr_script)s', + interpreter='%(python)s', + workdir='') + tc.run(stderr = "Not found\\n") + """) + + expect_stdout = lstrip("""\ + STDOUT ========================================================================= + + STDERR ========================================================================= + 1c1 + < Not found + --- + > %(stderr_script)s: STDERR: [] + """) + + expect_stderr = lstrip("""\ + FAILED test of .*stderr + \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) + \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) + \\tfrom line \\d+ of ( \(\))? + """) + expect_stderr = re.compile(expect_stderr, re.M) + + self.run_execution_test(script, expect_stdout, expect_stderr) + + def test_option_handling(self): + """Test run(): option handling""" + + script = lstrip("""\ + from TestCommon import TestCommon, match_exact + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir="", + match=match_exact) + tc.run(options = "opt1 opt2 opt3", + stdout = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3']" + "\\n") + """) + + self.run_execution_test(script, "", "") + + def test_options_plus_arguments(self): + """Test run(): option handling with arguments""" + + script = lstrip("""\ + from TestCommon import TestCommon, match_exact + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir="", + match=match_exact) + tc.run(options = "opt1 opt2 opt3", + arguments = "arg1 arg2 arg3", + stdout = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3', 'arg1', 'arg2', 'arg3']" + "\\n") + """) + + self.run_execution_test(script, "", "") + + def test_signal_handling(self): + """Test run(): signal handling""" + + try: + os.kill + except AttributeError: + sys.stderr.write('can not test, no os.kill ... ') + return + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(program=r'%(signal_script)s', + interpreter='%(python)s', + workdir='') + tc.run() + """) + + self.SIGTERM = signal.SIGTERM + + # Script returns the signal value as a negative number. + expect_stdout = lstrip("""\ + %(signal_script)s returned -%(SIGTERM)s + STDOUT ========================================================================= + + STDERR ========================================================================= + + """) + + expect_stderr = lstrip("""\ + FAILED test of .*signal + \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) + \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) + \\tfrom line \\d+ of + """) + expect_stderr = re.compile(expect_stderr, re.M) + + self.run_execution_test(script, expect_stdout, expect_stderr) + + def test_stdin(self): + """Test run(): stdin handling""" + + script = lstrip("""\ + from TestCommon import TestCommon, match_exact + tc = TestCommon(program=r'%(stdin_script)s', + interpreter='%(python)s', + workdir='', + match=match_exact) + expect_stdout = r"%(stdin_script)s: STDOUT: 'input'" + "\\n" + expect_stderr = r"%(stdin_script)s: STDERR: 'input'" + "\\n" + tc.run(stdin="input\\n", stdout = expect_stdout, stderr = expect_stderr) + """) + + expect_stdout = lstrip("""\ + %(pass_script)s returned 0 (expected 1) + STDOUT ========================================================================= + %(pass_script)s: STDOUT: [] + + STDERR ========================================================================= + + """) + + self.run_execution_test(script, "", "") + + + +class start_TestCase(TestCommonTestCase): + def test_option_handling(self): + """Test start(): option handling""" + + script = lstrip("""\ + from TestCommon import TestCommon, match_exact + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir="", + match=match_exact) + p = tc.start(options = "opt1 opt2 opt3") + expect = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3']" + "\\n" + tc.finish(p, stdout = expect) + """) + + self.run_execution_test(script, "", "") + + def test_options_plus_arguments(self): + """Test start(): option handling with arguments""" + + script = lstrip("""\ + from TestCommon import TestCommon, match_exact + tc = TestCommon(program=r'%(pass_script)s', + interpreter='%(python)s', + workdir="", + match=match_exact) + p = tc.start(options = "opt1 opt2 opt3", + arguments = "arg1 arg2 arg3") + expect = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3', 'arg1', 'arg2', 'arg3']" + "\\n" + tc.finish(p, stdout = expect) + """) + + self.run_execution_test(script, "", "") + + + +class skip_test_TestCase(TestCommonTestCase): + def test_skip_test(self): + """Test skip_test()""" + run_env = self.run_env + + script = lstrip("""\ + import TestCommon + test = TestCommon.TestCommon(workdir='') + test.skip_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Skipping test.\n", stdout + stderr = run_env.stderr() + expect = [ + "NO RESULT for test at line 3 of \n", + "NO RESULT for test at line 3 of ()\n", + ] + assert stderr in expect, repr(stderr) + + script = lstrip("""\ + import TestCommon + test = TestCommon.TestCommon(workdir='') + test.skip_test("skipping test because I said so\\n") + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "skipping test because I said so\n", stdout + stderr = run_env.stderr() + expect = [ + "NO RESULT for test at line 3 of \n", + "NO RESULT for test at line 3 of ()\n", + ] + assert stderr in expect, repr(stderr) + + import os + os.environ['TESTCOMMON_PASS_SKIPS'] = '1' + + try: + script = lstrip("""\ + import TestCommon + test = TestCommon.TestCommon(workdir='') + test.skip_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Skipping test.\n", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + + finally: + del os.environ['TESTCOMMON_PASS_SKIPS'] + + + +class variables_TestCase(TestCommonTestCase): + def test_variables(self): + """Test global variables""" + run_env = self.run_env + + variables = [ + 'fail_test', + 'no_result', + 'pass_test', + 'match_exact', + 'match_re', + 'match_re_dotall', + 'python', + '_python_', + 'TestCmd', + + 'TestCommon', + 'exe_suffix', + 'obj_suffix', + 'shobj_prefix', + 'shobj_suffix', + 'lib_prefix', + 'lib_suffix', + 'dll_prefix', + 'dll_suffix', + ] + + script = "from __future__ import print_function" + \ + "import TestCommon\n" + \ + '\n'.join([ "print(TestCommon.%s)\n" % v for v in variables ]) + run_env.run(program=sys.executable, stdin=script) + stderr = run_env.stderr() + assert stderr == "", stderr + + script = "from __future__ import print_function" + \ + "from TestCommon import *\n" + \ + '\n'.join([ "print(%s)" % v for v in variables ]) + run_env.run(program=sys.executable, stdin=script) + stderr = run_env.stderr() + assert stderr == "", stderr + + + +if __name__ == "__main__": + tclasses = [ + __init__TestCase, + banner_TestCase, + must_be_writable_TestCase, + must_contain_TestCase, + must_contain_all_lines_TestCase, + must_contain_any_line_TestCase, + must_contain_exactly_lines_TestCase, + must_contain_lines_TestCase, + must_exist_TestCase, + must_exist_one_of_TestCase, + must_match_TestCase, + must_not_be_writable_TestCase, + must_not_contain_TestCase, + must_not_contain_any_line_TestCase, + must_not_contain_lines_TestCase, + must_not_exist_TestCase, + must_not_exist_any_of_TestCase, + run_TestCase, + start_TestCase, + skip_test_TestCase, + variables_TestCase, + ] + suite = unittest.TestSuite() + for tclass in tclasses: + names = unittest.getTestCaseNames(tclass, 'test_') + suite.addTests([ tclass(n) for n in names ]) + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/TestRuntest.py b/testing/framework/TestRuntest.py new file mode 100644 index 0000000..7b3bb52 --- /dev/null +++ b/testing/framework/TestRuntest.py @@ -0,0 +1,167 @@ +""" +TestRuntest.py: a testing framework for the runtest.py command used to +invoke SCons tests. + +A TestRuntest environment object is created via the usual invocation: + + test = TestRuntest() + +TestRuntest is a subclass of TestCommon, which is in turn is a subclass +of TestCmd), and hence has available all of the methods and attributes +from those classes, as well as any overridden or additional methods or +attributes defined in this subclass. +""" + +# __COPYRIGHT__ + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import os.path +import re +import shutil +import sys + +from TestCommon import * +from TestCommon import __all__ + +__all__.extend([ 'TestRuntest', + 'pythonstring', + ]) + +if re.search('\s', python): + pythonstring = _python_ +else: + pythonstring = python +pythonstring = pythonstring.replace('\\', '\\\\') + + +failing_test_template = """\ +import sys +sys.stdout.write('FAILING TEST STDOUT\\n') +sys.stderr.write('FAILING TEST STDERR\\n') +sys.exit(1) +""" + +no_result_test_template = """\ +import sys +sys.stdout.write('NO RESULT TEST STDOUT\\n') +sys.stderr.write('NO RESULT TEST STDERR\\n') +sys.exit(2) +""" + +passing_test_template = """\ +import sys +sys.stdout.write('PASSING TEST STDOUT\\n') +sys.stderr.write('PASSING TEST STDERR\\n') +sys.exit(0) +""" + +fake_scons_py = """ +__version__ = '1.2.3' +__build__ = 'D123' +__buildsys__ = 'fake_system' +__date__ = 'Jan 1 1970' +__developer__ = 'Anonymous' +""" + +fake___init___py = """ +__version__ = '4.5.6' +__build__ = 'D456' +__buildsys__ = 'another_fake_system' +__date__ = 'Dec 31 1999' +__developer__ = 'John Doe' +""" + +class TestRuntest(TestCommon): + """Class for testing the runtest.py script. + + This provides a common place for initializing Runtest tests, + eliminating the need to begin every test with the same repeated + initializations. + """ + + def __init__(self, **kw): + """Initialize a Runtest testing object. + + If they're not overridden by keyword arguments, this + initializes the object with the following default values: + + program = 'runtest.py' + interpreter = ['python', '-tt'] + match = match_exact + workdir = '' + + The workdir value means that, by default, a temporary + workspace directory is created for a TestRuntest environment. + The superclass TestCommon.__init__() will change directory (chdir) + to the workspace directory, so an explicit "chdir = '.'" on all + of the run() method calls is not necessary. This initialization + also copies the runtest.py and testing/framework/ subdirectory tree to the + temporary directory, duplicating how this test infrastructure + appears in a normal workspace. + """ + if 'program' not in kw: + kw['program'] = 'runtest.py' + if 'interpreter' not in kw: + kw['interpreter'] = [python, '-tt'] + if 'match' not in kw: + kw['match'] = match_exact + if 'workdir' not in kw: + kw['workdir'] = '' + + try: + things_to_copy = kw['things_to_copy'] + except KeyError: + things_to_copy = [ + 'runtest.py', + 'testing/framework', + ] + else: + del kw['things_to_copy'] + + orig_cwd = os.getcwd() + TestCommon.__init__(self, **kw) + + dirs = [os.environ.get('SCONS_RUNTEST_DIR', orig_cwd)] + + for thing in things_to_copy: + for dir in dirs: + t = os.path.join(dir, thing) + if os.path.exists(t): + if os.path.isdir(t): + copy_func = shutil.copytree + else: + copy_func = shutil.copyfile + copy_func(t, self.workpath(thing)) + break + + self.program_set(self.workpath(kw['program'])) + + os.environ['PYTHONPATH'] = '' + + def write_fake_scons_source_tree(self): + os.mkdir('src') + os.mkdir('src/script') + self.write('src/script/scons.py', fake_scons_py) + + os.mkdir('src/engine') + os.mkdir('src/engine/SCons') + self.write('src/engine/SCons/__init__.py', fake___init___py) + os.mkdir('src/engine/SCons/Script') + self.write('src/engine/SCons/Script/__init__.py', fake___init___py) + + def write_failing_test(self, name): + self.write(name, failing_test_template) + + def write_no_result_test(self, name): + self.write(name, no_result_test_template) + + def write_passing_test(self, name): + self.write(name, passing_test_template) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py new file mode 100644 index 0000000..ba4e06b --- /dev/null +++ b/testing/framework/TestSCons.py @@ -0,0 +1,1642 @@ +""" +TestSCons.py: a testing framework for the SCons software construction +tool. + +A TestSCons environment object is created via the usual invocation: + + test = TestSCons() + +TestScons is a subclass of TestCommon, which in turn is a subclass +of TestCmd), and hence has available all of the methods and attributes +from those classes, as well as any overridden or additional methods or +attributes defined in this subclass. +""" + +# __COPYRIGHT__ +from __future__ import division, print_function + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import re +import shutil +import sys +import time +import subprocess + +from TestCommon import * +from TestCommon import __all__ + +from TestCmd import Popen +from TestCmd import PIPE + +# Some tests which verify that SCons has been packaged properly need to +# look for specific version file names. Replicating the version number +# here provides some independent verification that what we packaged +# conforms to what we expect. + +default_version = '3.1.0.alpha.yyyymmdd' + +python_version_unsupported = (2, 6, 0) +python_version_deprecated = (2, 7, 0) + +# In the checked-in source, the value of SConsVersion in the following +# line must remain "__ VERSION __" (without the spaces) so the built +# version in build/testing/framework/TestSCons.py contains the actual version +# string of the packages that have been built. +SConsVersion = '__VERSION__' +if SConsVersion == '__' + 'VERSION' + '__': + SConsVersion = default_version + +__all__.extend([ + 'TestSCons', + 'machine', + 'python', + '_exe', + '_obj', + '_shobj', + 'shobj_', + 'lib_', + '_lib', + 'dll_', + '_dll' + ]) + +machine_map = { + 'i686' : 'i386', + 'i586' : 'i386', + 'i486' : 'i386', +} + +try: + uname = os.uname +except AttributeError: + # Windows doesn't have a uname() function. We could use something like + # sys.platform as a fallback, but that's not really a "machine," so + # just leave it as None. + machine = None +else: + machine = uname()[4] + machine = machine_map.get(machine, machine) + +_exe = exe_suffix +_obj = obj_suffix +_shobj = shobj_suffix +shobj_ = shobj_prefix +_lib = lib_suffix +lib_ = lib_prefix +_dll = dll_suffix +dll_ = dll_prefix + + +if sys.platform == 'cygwin': + # On Cygwin, os.path.normcase() lies, so just report back the + # fact that the underlying Win32 OS is case-insensitive. + def case_sensitive_suffixes(s1, s2): + return 0 +else: + def case_sensitive_suffixes(s1, s2): + return (os.path.normcase(s1) != os.path.normcase(s2)) + + +file_expr = r"""File "[^"]*", line \d+, in [^\n]+ +""" + +# re.escape escapes too much. +def re_escape(str): + for c in '\\.[]()*+?': # Not an exhaustive list. + str = str.replace(c, '\\' + c) + return str + +# +# Helper functions that we use as a replacement to the default re.match +# when searching for special strings in stdout/stderr. +# +def search_re(out, l): + """ Search the regular expression 'l' in the output 'out' + and return the start index when successful. + """ + m = re.search(l, out) + if m: + return m.start() + + return None + +def search_re_in_list(out, l): + """ Search the regular expression 'l' in each line of + the given string list 'out' and return the line's index + when successful. + """ + for idx, o in enumerate(out): + m = re.search(l, o) + if m: + return idx + + return None + +# +# Helpers for handling Python version numbers +# +def python_version_string(): + return sys.version.split()[0] + +def python_minor_version_string(): + return sys.version[:3] + +def unsupported_python_version(version=sys.version_info): + return version < python_version_unsupported + +def deprecated_python_version(version=sys.version_info): + return version < python_version_deprecated + +if deprecated_python_version(): + msg = r""" +scons: warning: Support for pre-2.7.0 Python version (%s) is deprecated. + If this will cause hardship, contact scons-dev@scons.org +""" + + deprecated_python_expr = re_escape(msg % python_version_string()) + file_expr + del msg +else: + deprecated_python_expr = "" + + +def initialize_sconsflags(ignore_python_version): + """ + Add the --warn=no-python-version option to SCONSFLAGS for every + command so test scripts don't have to filter out Python version + deprecation warnings. + Same for --warn=no-visual-c-missing. + """ + save_sconsflags = os.environ.get('SCONSFLAGS') + if save_sconsflags: + sconsflags = [save_sconsflags] + else: + sconsflags = [] + if ignore_python_version and deprecated_python_version(): + sconsflags.append('--warn=no-python-version') + # Provide a way to suppress or provide alternate flags for + # TestSCons purposes by setting TESTSCONS_SCONSFLAGS. + # (The intended use case is to set it to null when running + # timing tests of earlier versions of SCons which don't + # support the --warn=no-visual-c-missing warning.) + visual_c = os.environ.get('TESTSCONS_SCONSFLAGS', + '--warn=no-visual-c-missing') + if visual_c: + sconsflags.append(visual_c) + os.environ['SCONSFLAGS'] = ' '.join(sconsflags) + return save_sconsflags + +def restore_sconsflags(sconsflags): + if sconsflags is None: + del os.environ['SCONSFLAGS'] + else: + os.environ['SCONSFLAGS'] = sconsflags + + +class TestSCons(TestCommon): + """Class for testing SCons. + + This provides a common place for initializing SCons tests, + eliminating the need to begin every test with the same repeated + initializations. + """ + + scons_version = SConsVersion + javac_is_gcj = False + + def __init__(self, **kw): + """Initialize an SCons testing object. + + If they're not overridden by keyword arguments, this + initializes the object with the following default values: + + program = 'scons' if it exists, + else 'scons.py' + interpreter = 'python' + match = match_exact + workdir = '' + + The workdir value means that, by default, a temporary workspace + directory is created for a TestSCons environment. In addition, + this method changes directory (chdir) to the workspace directory, + so an explicit "chdir = '.'" on all of the run() method calls + is not necessary. + """ + self.orig_cwd = os.getcwd() + self.external = os.environ.get('SCONS_EXTERNAL_TEST', 0) + + if not self.external: + try: + script_dir = os.environ['SCONS_SCRIPT_DIR'] + except KeyError: + pass + else: + os.chdir(script_dir) + if 'program' not in kw: + kw['program'] = os.environ.get('SCONS') + if not kw['program']: + if not self.external: + if os.path.exists('scons'): + kw['program'] = 'scons' + else: + kw['program'] = 'scons.py' + else: + kw['program'] = 'scons' + kw['interpreter'] = '' + elif not self.external and not os.path.isabs(kw['program']): + kw['program'] = os.path.join(self.orig_cwd, kw['program']) + if 'interpreter' not in kw and not os.environ.get('SCONS_EXEC'): + kw['interpreter'] = [python, '-tt'] + if 'match' not in kw: + kw['match'] = match_exact + if 'workdir' not in kw: + kw['workdir'] = '' + + # Term causing test failures due to bogus readline init + # control character output on FC8 + # TERM can cause test failures due to control chars in prompts etc. + os.environ['TERM'] = 'dumb' + + self.ignore_python_version = kw.get('ignore_python_version', 1) + if kw.get('ignore_python_version', -1) != -1: + del kw['ignore_python_version'] + + TestCommon.__init__(self, **kw) + + if not self.external: + import SCons.Node.FS + if SCons.Node.FS.default_fs is None: + SCons.Node.FS.default_fs = SCons.Node.FS.FS() + + try: + self.fixture_dirs = (os.environ['FIXTURE_DIRS']).split(os.pathsep) + except KeyError: + pass + + def Environment(self, ENV=None, *args, **kw): + """ + Return a construction Environment that optionally overrides + the default external environment with the specified ENV. + """ + if not self.external: + import SCons.Environment + import SCons.Errors + if not ENV is None: + kw['ENV'] = ENV + try: + return SCons.Environment.Environment(*args, **kw) + except (SCons.Errors.UserError, SCons.Errors.InternalError): + return None + + return None + + def detect(self, var, prog=None, ENV=None, norm=None): + """ + Detect a program named 'prog' by first checking the construction + variable named 'var' and finally searching the path used by + SCons. If either method fails to detect the program, then false + is returned, otherwise the full path to prog is returned. If + prog is None, then the value of the environment variable will be + used as prog. + """ + env = self.Environment(ENV) + if env: + v = env.subst('$'+var) + if not v: + return None + if prog is None: + prog = v + if v != prog: + return None + result = env.WhereIs(prog) + if norm and os.sep != '/': + result = result.replace(os.sep, '/') + return result + + return self.where_is(prog) + + def detect_tool(self, tool, prog=None, ENV=None): + """ + Given a tool (i.e., tool specification that would be passed + to the "tools=" parameter of Environment()) and a program that + corresponds to that tool, return true if and only if we can find + that tool using Environment.Detect(). + + By default, prog is set to the value passed into the tools parameter. + """ + + if not prog: + prog = tool + env = self.Environment(ENV, tools=[tool]) + if env is None: + return None + return env.Detect([prog]) + + def where_is(self, prog, path=None): + """ + Given a program, search for it in the specified external PATH, + or in the actual external PATH if none is specified. + """ + if path is None: + path = os.environ['PATH'] + if self.external: + if isinstance(prog, str): + prog = [prog] + import stat + paths = path.split(os.pathsep) + for p in prog: + for d in paths: + f = os.path.join(d, p) + if os.path.isfile(f): + try: + st = os.stat(f) + except OSError: + # os.stat() raises OSError, not IOError if the file + # doesn't exist, so in this case we let IOError get + # raised so as to not mask possibly serious disk or + # network issues. + continue + if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: + return os.path.normpath(f) + else: + import SCons.Environment + env = SCons.Environment.Environment() + return env.WhereIs(prog, path) + + return None + + def wrap_stdout(self, build_str = "", read_str = "", error = 0, cleaning = 0): + """Wraps standard output string(s) in the normal + "Reading ... done" and "Building ... done" strings + """ + cap,lc = [ ('Build','build'), + ('Clean','clean') ][cleaning] + if error: + term = "scons: %sing terminated because of errors.\n" % lc + else: + term = "scons: done %sing targets.\n" % lc + return "scons: Reading SConscript files ...\n" + \ + read_str + \ + "scons: done reading SConscript files.\n" + \ + "scons: %sing targets ...\n" % cap + \ + build_str + \ + term + + def run(self, *args, **kw): + """ + Set up SCONSFLAGS for every command so test scripts don't need + to worry about unexpected warnings in their output. + """ + sconsflags = initialize_sconsflags(self.ignore_python_version) + try: + TestCommon.run(self, *args, **kw) + finally: + restore_sconsflags(sconsflags) + +# Modifying the options should work and ought to be simpler, but this +# class is used for more than just running 'scons' itself. If there's +# an automated way of determining whether it's running 'scons' or +# something else, this code should be resurected. +# options = kw.get('options') +# if options: +# options = [options] +# else: +# options = [] +# if self.ignore_python_version and deprecated_python_version(): +# options.append('--warn=no-python-version') +# # Provide a way to suppress or provide alternate flags for +# # TestSCons purposes by setting TESTSCONS_SCONSFLAGS. +# # (The intended use case is to set it to null when running +# # timing tests of earlier versions of SCons which don't +# # support the --warn=no-visual-c-missing warning.) +# visual_c = os.environ.get('TESTSCONS_SCONSFLAGS', +# '--warn=no-visual-c-missing') +# if visual_c: +# options.append(visual_c) +# kw['options'] = ' '.join(options) +# TestCommon.run(self, *args, **kw) + + def up_to_date(self, arguments = '.', read_str = "", **kw): + s = "" + for arg in arguments.split(): + s = s + "scons: `%s' is up to date.\n" % arg + kw['arguments'] = arguments + stdout = self.wrap_stdout(read_str = read_str, build_str = s) + # Append '.*' so that timing output that comes after the + # up-to-date output is okay. + kw['stdout'] = re.escape(stdout) + '.*' + kw['match'] = self.match_re_dotall + self.run(**kw) + + def not_up_to_date(self, arguments = '.', **kw): + """Asserts that none of the targets listed in arguments is + up to date, but does not make any assumptions on other targets. + This function is most useful in conjunction with the -n option. + """ + s = "" + for arg in arguments.split(): + s = s + "(?!scons: `%s' is up to date.)" % re.escape(arg) + s = '('+s+'[^\n]*\n)*' + kw['arguments'] = arguments + stdout = re.escape(self.wrap_stdout(build_str='ARGUMENTSGOHERE')) + kw['stdout'] = stdout.replace('ARGUMENTSGOHERE', s) + kw['match'] = self.match_re_dotall + self.run(**kw) + + def option_not_yet_implemented(self, option, arguments=None, **kw): + """ + Verifies expected behavior for options that are not yet implemented: + a warning message, and exit status 1. + """ + msg = "Warning: the %s option is not yet implemented\n" % option + kw['stderr'] = msg + if arguments: + # If it's a long option and the argument string begins with '=', + # it's of the form --foo=bar and needs no separating space. + if option[:2] == '--' and arguments[0] == '=': + kw['arguments'] = option + arguments + else: + kw['arguments'] = option + ' ' + arguments + return self.run(**kw) + + def deprecated_wrap(self, msg): + """ + Calculate the pattern that matches a deprecation warning. + """ + return '\nscons: warning: ' + re_escape(msg) + '\n' + file_expr + + def deprecated_fatal(self, warn, msg): + """ + Determines if the warning has turned into a fatal error. If so, + passes the test, as any remaining runs are now moot. + + This method expects a SConscript to be present that will causes + the warning. The method writes a SConstruct that calls the + SConsscript and looks to see what type of result occurs. + + The pattern that matches the warning is returned. + + TODO: Actually detect that it's now an error. We don't have any + cases yet, so there's no way to test it. + """ + self.write('SConstruct', """if True: + WARN = ARGUMENTS.get('WARN') + if WARN: SetOption('warn', WARN) + SConscript('SConscript') + """) + + def err_out(): + # TODO calculate stderr for fatal error + return re_escape('put something here') + + # no option, should get one of nothing, warning, or error + warning = self.deprecated_wrap(msg) + self.run(arguments = '.', stderr = None) + stderr = self.stderr() + if stderr: + # most common case done first + if match_re_dotall(stderr, warning): + # expected output + pass + elif match_re_dotall(stderr, err_out()): + # now a fatal error; skip the rest of the tests + self.pass_test() + else: + # test failed; have to do this by hand... + print(self.banner('STDOUT ')) + print(self.stdout()) + print(self.diff(warning, stderr, 'STDERR ')) + self.fail_test() + + return warning + + def deprecated_warning(self, warn, msg): + """ + Verifies the expected behavior occurs for deprecation warnings. + This method expects a SConscript to be present that will causes + the warning. The method writes a SConstruct and exercises various + combinations of command-line options and SetOption parameters to + validate that it performs correctly. + + The pattern that matches the warning is returned. + """ + warning = self.deprecated_fatal(warn, msg) + + def RunPair(option, expected): + # run the same test with the option on the command line and + # then with the option passed via SetOption(). + self.run(options = '--warn=' + option, + arguments = '.', + stderr = expected, + match = match_re_dotall) + self.run(options = 'WARN=' + option, + arguments = '.', + stderr = expected, + match = match_re_dotall) + + # all warnings off, should get no output + RunPair('no-deprecated', '') + + # warning enabled, should get expected output + RunPair(warn, warning) + + # warning disabled, should get either nothing or mandatory message + expect = """()|(Can not disable mandataory warning: 'no-%s'\n\n%s)""" % (warn, warning) + RunPair('no-' + warn, expect) + + return warning + + def diff_substr(self, expect, actual, prelen=20, postlen=40): + i = 0 + for x, y in zip(expect, actual): + if x != y: + return "Actual did not match expect at char %d:\n" \ + " Expect: %s\n" \ + " Actual: %s\n" \ + % (i, repr(expect[i-prelen:i+postlen]), + repr(actual[i-prelen:i+postlen])) + i = i + 1 + return "Actual matched the expected output???" + + def python_file_line(self, file, line): + """ + Returns a Python error line for output comparisons. + + The exec of the traceback line gives us the correct format for + this version of Python. + + File "", line 1, + + We stick the requested file name and line number in the right + places, abstracting out the version difference. + """ + # This routine used to use traceback to get the proper format + # that doesn't work well with py3. And the format of the + # traceback seems to be stable, so let's just format + # an appropriate string + # + #exec('import traceback; x = traceback.format_stack()[-1]') + # import traceback + # x = traceback.format_stack() + # x = # XXX: .lstrip() + # x = x.replace('', file) + # x = x.replace('line 1,', 'line %s,' % line) + # x="\n".join(x) + x='File "%s", line %s, in \n'%(file,line) + return x + + def normalize_ps(self, s): + s = re.sub(r'(Creation|Mod)Date: .*', + r'\1Date XXXX', s) + s = re.sub(r'%DVIPSSource:\s+TeX output\s.*', + r'%DVIPSSource: TeX output XXXX', s) + s = re.sub(r'/(BaseFont|FontName) /[A-Z0-9]{6}', + r'/\1 /XXXXXX', s) + s = re.sub(r'BeginFont: [A-Z0-9]{6}', + r'BeginFont: XXXXXX', s) + + return s + + @staticmethod + def to_bytes_re_sub(pattern, repl, str, count=0, flags=0): + """ + Wrapper around re.sub to change pattern and repl to bytes to work with + both python 2 & 3 + """ + pattern = to_bytes(pattern) + repl = to_bytes(repl) + return re.sub(pattern, repl, str, count, flags) + + def normalize_pdf(self, s): + s = self.to_bytes_re_sub(r'/(Creation|Mod)Date \(D:[^)]*\)', + r'/\1Date (D:XXXX)', s) + s = self.to_bytes_re_sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]', + r'/ID [ ]', s) + s = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', + r'/\1 /XXXXXX', s) + s = self.to_bytes_re_sub(r'/Length \d+ *\n/Filter /FlateDecode\n', + r'/Length XXXX\n/Filter /FlateDecode\n', s) + + try: + import zlib + except ImportError: + pass + else: + begin_marker = to_bytes('/FlateDecode\n>>\nstream\n') + end_marker = to_bytes('endstream\nendobj') + + encoded = [] + b = s.find(begin_marker, 0) + while b != -1: + b = b + len(begin_marker) + e = s.find(end_marker, b) + encoded.append((b, e)) + b = s.find(begin_marker, e + len(end_marker)) + + x = 0 + r = [] + for b, e in encoded: + r.append(s[x:b]) + d = zlib.decompress(s[b:e]) + d = self.to_bytes_re_sub(r'%%CreationDate: [^\n]*\n', + r'%%CreationDate: 1970 Jan 01 00:00:00\n', d) + d = self.to_bytes_re_sub(r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d', + r'%DVIPSSource: TeX output 1970.01.01:0000', d) + d = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', + r'/\1 /XXXXXX', d) + r.append(d) + x = e + r.append(s[x:]) + s = to_bytes('').join(r) + + return s + + def paths(self,patterns): + import glob + result = [] + for p in patterns: + result.extend(sorted(glob.glob(p))) + return result + + def unlink_sconsignfile(self,name='.sconsign.dblite'): + """ + Delete sconsign file. + Note on python it seems to append .p3 to the file name so we take care of that + Parameters + ---------- + name - expected name of sconsign file + + Returns + ------- + None + """ + if sys.version_info[0] == 3: + name += '.p3' + self.unlink(name) + + def java_ENV(self, version=None): + """ + Initialize with a default external environment that uses a local + Java SDK in preference to whatever's found in the default PATH. + """ + if not self.external: + try: + return self._java_env[version]['ENV'] + except AttributeError: + self._java_env = {} + except KeyError: + pass + + import SCons.Environment + env = SCons.Environment.Environment() + self._java_env[version] = env + + + if version: + patterns = [ + '/usr/java/jdk%s*/bin' % version, + '/usr/lib/jvm/*-%s*/bin' % version, + '/usr/local/j2sdk%s*/bin' % version, + ] + java_path = self.paths(patterns) + [env['ENV']['PATH']] + else: + patterns = [ + '/usr/java/latest/bin', + '/usr/lib/jvm/*/bin', + '/usr/local/j2sdk*/bin', + ] + java_path = self.paths(patterns) + [env['ENV']['PATH']] + + env['ENV']['PATH'] = os.pathsep.join(java_path) + return env['ENV'] + + return None + + def java_where_includes(self,version=None): + """ + Return java include paths compiling java jni code + """ + import sys + + result = [] + if sys.platform[:6] == 'darwin': + java_home = self.java_where_java_home(version) + jni_path = os.path.join(java_home,'include','jni.h') + if os.path.exists(jni_path): + result.append(os.path.dirname(jni_path)) + + if not version: + version='' + jni_dirs = ['/System/Library/Frameworks/JavaVM.framework/Headers/jni.h', + '/usr/lib/jvm/default-java/include/jni.h', + '/usr/lib/jvm/java-*-oracle/include/jni.h'] + else: + jni_dirs = ['/System/Library/Frameworks/JavaVM.framework/Versions/%s*/Headers/jni.h'%version] + jni_dirs.extend(['/usr/lib/jvm/java-*-sun-%s*/include/jni.h'%version, + '/usr/lib/jvm/java-%s*-openjdk*/include/jni.h'%version, + '/usr/java/jdk%s*/include/jni.h'%version]) + dirs = self.paths(jni_dirs) + if not dirs: + return None + d=os.path.dirname(self.paths(jni_dirs)[0]) + result.append(d) + + if sys.platform == 'win32': + result.append(os.path.join(d,'win32')) + elif sys.platform.startswith('linux'): + result.append(os.path.join(d,'linux')) + return result + + def java_where_java_home(self, version=None): + if sys.platform[:6] == 'darwin': + # osx 10.11, 10.12 + home_tool = '/usr/libexec/java_home' + java_home = False + if os.path.exists(home_tool): + java_home = subprocess.check_output(home_tool).strip() + java_home = java_home.decode() + + if version is None: + if java_home: + return java_home + else: + homes = ['/System/Library/Frameworks/JavaVM.framework/Home', + # osx 10.10 + '/System/Library/Frameworks/JavaVM.framework/Versions/Current/Home'] + for home in homes: + if os.path.exists(home): + return home + + else: + if java_home.find('jdk%s'%version) != -1: + return java_home + else: + home = '/System/Library/Frameworks/JavaVM.framework/Versions/%s/Home' % version + if not os.path.exists(home): + # This works on OSX 10.10 + home = '/System/Library/Frameworks/JavaVM.framework/Versions/Current/' + else: + jar = self.java_where_jar(version) + home = os.path.normpath('%s/..'%jar) + if os.path.isdir(home): + return home + print("Could not determine JAVA_HOME: %s is not a directory" % home) + self.fail_test() + + def java_mac_check(self, where_java_bin, java_bin_name): + # on Mac there is a place holder java installed to start the java install process + # so we need to check the output in this case, more info here: + # http://anas.pk/2015/09/02/solution-no-java-runtime-present-mac-yosemite/ + sp = subprocess.Popen([where_java_bin, "-version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = sp.communicate() + sp.wait() + if("No Java runtime" in str(stderr)): + self.skip_test("Could not find Java " + java_bin_name + ", skipping test(s).\n") + + def java_where_jar(self, version=None): + ENV = self.java_ENV(version) + if self.detect_tool('jar', ENV=ENV): + where_jar = self.detect('JAR', 'jar', ENV=ENV) + else: + where_jar = self.where_is('jar', ENV['PATH']) + if not where_jar: + self.skip_test("Could not find Java jar, skipping test(s).\n") + elif sys.platform == "darwin": + self.java_mac_check(where_jar, 'jar') + + return where_jar + + def java_where_java(self, version=None): + """ + Return a path to the java executable. + """ + ENV = self.java_ENV(version) + where_java = self.where_is('java', ENV['PATH']) + + if not where_java: + self.skip_test("Could not find Java java, skipping test(s).\n") + elif sys.platform == "darwin": + self.java_mac_check(where_java, 'java') + + return where_java + + def java_where_javac(self, version=None): + """ + Return a path to the javac compiler. + """ + ENV = self.java_ENV(version) + if self.detect_tool('javac'): + where_javac = self.detect('JAVAC', 'javac', ENV=ENV) + else: + where_javac = self.where_is('javac', ENV['PATH']) + if not where_javac: + self.skip_test("Could not find Java javac, skipping test(s).\n") + elif sys.platform == "darwin": + self.java_mac_check(where_javac, 'javac') + + self.run(program = where_javac, + arguments = '-version', + stderr=None, + status=None) + if version: + if self.stderr().find('javac %s' % version) == -1: + fmt = "Could not find javac for Java version %s, skipping test(s).\n" + self.skip_test(fmt % version) + else: + m = re.search(r'javac (\d\.\d)', self.stderr()) + if m: + version = m.group(1) + self.javac_is_gcj = False + elif self.stderr().find('gcj'): + version='1.2' + self.javac_is_gcj = True + else: + version = None + self.javac_is_gcj = False + return where_javac, version + + def java_where_javah(self, version=None): + ENV = self.java_ENV(version) + if self.detect_tool('javah'): + where_javah = self.detect('JAVAH', 'javah', ENV=ENV) + else: + where_javah = self.where_is('javah', ENV['PATH']) + if not where_javah: + self.skip_test("Could not find Java javah, skipping test(s).\n") + return where_javah + + def java_where_rmic(self, version=None): + ENV = self.java_ENV(version) + if self.detect_tool('rmic'): + where_rmic = self.detect('RMIC', 'rmic', ENV=ENV) + else: + where_rmic = self.where_is('rmic', ENV['PATH']) + if not where_rmic: + self.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n") + return where_rmic + + + def java_get_class_files(self, dir): + result = [] + for dirpath, dirnames, filenames in os.walk(dir): + for fname in filenames: + if fname.endswith('.class'): + result.append(os.path.join(dirpath, fname)) + return sorted(result) + + + def Qt_dummy_installation(self, dir='qt'): + # create a dummy qt installation + + self.subdir( dir, [dir, 'bin'], [dir, 'include'], [dir, 'lib'] ) + + self.write([dir, 'bin', 'mymoc.py'], """\ +import getopt +import sys +import re +# -w and -z are fake options used in test/QT/QTFLAGS.py +cmd_opts, args = getopt.getopt(sys.argv[1:], 'io:wz', []) +output = None +impl = 0 +opt_string = '' +for opt, arg in cmd_opts: + if opt == '-o': output = open(arg, 'w') + elif opt == '-i': impl = 1 + else: opt_string = opt_string + ' ' + opt +output.write("/* mymoc.py%s */\\n" % opt_string) +for a in args: + contents = open(a, 'r').read() + a = a.replace('\\\\', '\\\\\\\\') + subst = r'{ my_qt_symbol( "' + a + '\\\\n" ); }' + if impl: + contents = re.sub( r'#include.*', '', contents ) + output.write(contents.replace('Q_OBJECT', subst)) +output.close() +sys.exit(0) +""") + + self.write([dir, 'bin', 'myuic.py'], """\ +import os.path +import re +import sys +output_arg = 0 +impl_arg = 0 +impl = None +source = None +opt_string = '' +for arg in sys.argv[1:]: + if output_arg: + output = open(arg, 'w') + output_arg = 0 + elif impl_arg: + impl = arg + impl_arg = 0 + elif arg == "-o": + output_arg = 1 + elif arg == "-impl": + impl_arg = 1 + elif arg[0:1] == "-": + opt_string = opt_string + ' ' + arg + else: + if source: + sys.exit(1) + source = open(arg, 'r') + sourceFile = arg +output.write("/* myuic.py%s */\\n" % opt_string) +if impl: + output.write( '#include "' + impl + '"\\n' ) + includes = re.findall('(.*?)', source.read()) + for incFile in includes: + # this is valid for ui.h files, at least + if os.path.exists(incFile): + output.write('#include "' + incFile + '"\\n') +else: + output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" ) +output.close() +sys.exit(0) +""" ) + + self.write([dir, 'include', 'my_qobject.h'], r""" +#define Q_OBJECT ; +void my_qt_symbol(const char *arg); +""") + + self.write([dir, 'lib', 'my_qobject.cpp'], r""" +#include "../include/my_qobject.h" +#include +void my_qt_symbol(const char *arg) { + fputs( arg, stdout ); +} +""") + + self.write([dir, 'lib', 'SConstruct'], r""" +env = Environment() +import sys +if sys.platform == 'win32': + env.StaticLibrary( 'myqt', 'my_qobject.cpp' ) +else: + env.SharedLibrary( 'myqt', 'my_qobject.cpp' ) +""") + + self.run(chdir = self.workpath(dir, 'lib'), + arguments = '.', + stderr = noisy_ar, + match = self.match_re_dotall) + + self.QT = self.workpath(dir) + self.QT_LIB = 'myqt' + self.QT_MOC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'mymoc.py')) + self.QT_UIC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'myuic.py')) + self.QT_LIB_DIR = self.workpath(dir, 'lib') + + def Qt_create_SConstruct(self, place): + if isinstance(place, list): + place = test.workpath(*place) + self.write(place, """\ +if ARGUMENTS.get('noqtdir', 0): QTDIR=None +else: QTDIR=r'%s' +env = Environment(QTDIR = QTDIR, + QT_LIB = r'%s', + QT_MOC = r'%s', + QT_UIC = r'%s', + tools=['default','qt']) +dup = 1 +if ARGUMENTS.get('variant_dir', 0): + if ARGUMENTS.get('chdir', 0): + SConscriptChdir(1) + else: + SConscriptChdir(0) + dup=int(ARGUMENTS.get('dup', 1)) + if dup == 0: + builddir = 'build_dup0' + env['QT_DEBUG'] = 1 + else: + builddir = 'build' + VariantDir(builddir, '.', duplicate=dup) + print(builddir, dup) + sconscript = Dir(builddir).File('SConscript') +else: + sconscript = File('SConscript') +Export("env dup") +SConscript( sconscript ) +""" % (self.QT, self.QT_LIB, self.QT_MOC, self.QT_UIC)) + + + NCR = 0 # non-cached rebuild + CR = 1 # cached rebuild (up to date) + NCF = 2 # non-cached build failure + CF = 3 # cached build failure + + if sys.platform == 'win32': + Configure_lib = 'msvcrt' + else: + Configure_lib = 'm' + + # to use cygwin compilers on cmd.exe -> uncomment following line + #Configure_lib = 'm' + + def coverage_run(self): + """ Check if the the tests are being run under coverage. + """ + return 'COVERAGE_PROCESS_START' in os.environ or 'COVERAGE_FILE' in os.environ + + def skip_if_not_msvc(self, check_platform=True): + """ Check whether we are on a Windows platform and skip the + test if not. This check can be omitted by setting + check_platform to False. + Then, for a win32 platform, additionally check + whether we have a MSVC toolchain installed + in the system, and skip the test if none can be + found (=MinGW is the only compiler available). + """ + if check_platform: + if sys.platform != 'win32': + msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform + self.skip_test(msg) + return + + try: + import SCons.Tool.MSCommon as msc + if not msc.msvc_exists(): + msg = "No MSVC toolchain found...skipping test\n" + self.skip_test(msg) + except: + pass + + def checkLogAndStdout(self, checks, results, cached, + logfile, sconf_dir, sconstruct, + doCheckLog=True, doCheckStdout=True): + """ + Used to verify the expected output from using Configure() + via the contents of one or both of stdout or config.log file. + The checks, results, cached parameters all are zipped together + for use in comparing results. + + TODO: Perhaps a better API makes sense? + + Parameters + ---------- + checks : The Configure checks being run + + results : The expected results for each check + + cached : If the corresponding check is expected to be cached + + logfile : Name of the config log + + sconf_dir : Name of the sconf dir + + sconstruct : SConstruct file name + + doCheckLog : check specified log file, defaults to true + + doCheckStdout : Check stdout, defaults to true + + Returns + ------- + + """ + + class NoMatch(Exception): + def __init__(self, p): + self.pos = p + + def matchPart(log, logfile, lastEnd, NoMatch=NoMatch): + """ + Match part of the logfile + """ + m = re.match(log, logfile[lastEnd:]) + if not m: + raise NoMatch(lastEnd) + return m.end() + lastEnd + + try: + + # Build regexp for a character which is not + # a linesep, and in the case of CR/LF + # build it with both CR and CR/LF + # TODO: Not sure why this is a good idea. A static string + # could do the same since we only have two variations + # to do with? + # ls = os.linesep + # nols = "(" + # for i in range(len(ls)): + # nols = nols + "(" + # for j in range(i): + # nols = nols + ls[j] + # nols = nols + "[^" + ls[i] + "])" + # if i < len(ls)-1: + # nols = nols + "|" + # nols = nols + ")" + # + # Replaced above logic with \n as we're reading the file + # using non-binary read. Python will translate \r\n -> \n + # For us. + ls = '\n' + nols = '([^\n])' + lastEnd = 0 + + # Read the whole logfile + logfile = self.read(self.workpath(logfile), mode='r') + + # Some debug code to keep around.. + # sys.stderr.write("LOGFILE[%s]:%s"%(type(logfile),logfile)) + + if (doCheckLog and + logfile.find("scons: warning: The stored build information has an unexpected class.") >= 0): + self.fail_test() + + sconf_dir = sconf_dir + sconstruct = sconstruct + + log = r'file\ \S*%s\,line \d+:' % re.escape(sconstruct) + ls + if doCheckLog: + lastEnd = matchPart(log, logfile, lastEnd) + + log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls + if doCheckLog: + lastEnd = matchPart(log, logfile, lastEnd) + + rdstr = "" + cnt = 0 + for check,result,cache_desc in zip(checks, results, cached): + log = re.escape("scons: Configure: " + check) + ls + + if doCheckLog: + lastEnd = matchPart(log, logfile, lastEnd) + + log = "" + result_cached = 1 + for bld_desc in cache_desc: # each TryXXX + for ext, flag in bld_desc: # each file in TryBuild + file = os.path.join(sconf_dir,"conftest_%d%s" % (cnt, ext)) + if flag == self.NCR: + # NCR = Non Cached Rebuild + # rebuild will pass + if ext in ['.c', '.cpp']: + log=log + re.escape(file + " <-") + ls + log=log + r"( \|" + nols + "*" + ls + ")+?" + else: + log=log + "(" + nols + "*" + ls +")*?" + result_cached = 0 + if flag == self.CR: + # CR = cached rebuild (up to date)s + # up to date + log=log + \ + re.escape("scons: Configure: \"%s\" is up to date." + % file) + ls + log=log+re.escape("scons: Configure: The original builder " + "output was:") + ls + log=log+r"( \|.*"+ls+")+" + if flag == self.NCF: + # non-cached rebuild failure + log=log + "(" + nols + "*" + ls + ")*?" + result_cached = 0 + if flag == self.CF: + # cached rebuild failure + log=log + \ + re.escape("scons: Configure: Building \"%s\" failed " + "in a previous run and all its sources are" + " up to date." % file) + ls + log=log+re.escape("scons: Configure: The original builder " + "output was:") + ls + log=log+r"( \|.*"+ls+")+" + cnt = cnt + 1 + if result_cached: + result = "(cached) " + result + rdstr = rdstr + re.escape(check) + re.escape(result) + "\n" + log=log + re.escape("scons: Configure: " + result) + ls + ls + + if doCheckLog: + lastEnd = matchPart(log, logfile, lastEnd) + + log = "" + if doCheckLog: lastEnd = matchPart(ls, logfile, lastEnd) + if doCheckLog and lastEnd != len(logfile): + raise NoMatch(lastEnd) + + except NoMatch as m: + print("Cannot match log file against log regexp.") + print("log file: ") + print("------------------------------------------------------") + print(logfile[m.pos:]) + print("------------------------------------------------------") + print("log regexp: ") + print("------------------------------------------------------") + print(log) + print("------------------------------------------------------") + self.fail_test() + + if doCheckStdout: + exp_stdout = self.wrap_stdout(".*", rdstr) + if not self.match_re_dotall(self.stdout(), exp_stdout): + print("Unexpected stdout: ") + print("-----------------------------------------------------") + print(repr(self.stdout())) + print("-----------------------------------------------------") + print(repr(exp_stdout)) + print("-----------------------------------------------------") + self.fail_test() + + def get_python_version(self): + """ + Returns the Python version (just so everyone doesn't have to + hand-code slicing the right number of characters). + """ + # see also sys.prefix documentation + return python_minor_version_string() + + def get_platform_python_info(self): + """ + Returns a path to a Python executable suitable for testing on + this platform and its associated include path, library path, + and library name. + """ + python = self.where_is('python') + if not python: + self.skip_test('Can not find installed "python", skipping test.\n') + + self.run(program = python, stdin = """\ +import os, sys +try: + if sys.platform == 'win32': + py_ver = 'python%d%d' % sys.version_info[:2] + else: + py_ver = 'python%d.%d' % sys.version_info[:2] +except AttributeError: + py_ver = 'python' + sys.version[:3] +# print include and lib path +try: + import distutils.sysconfig + exec_prefix = distutils.sysconfig.EXEC_PREFIX + print(distutils.sysconfig.get_python_inc()) + lib_path = os.path.join(exec_prefix, 'libs') + if not os.path.exists(lib_path): + lib_path = os.path.join(exec_prefix, 'lib') + print(lib_path) +except: + print(os.path.join(sys.prefix, 'include', py_ver)) + print(os.path.join(sys.prefix, 'lib', py_ver, 'config')) +print(py_ver) +""") + + return [python] + self.stdout().strip().split('\n') + + def start(self, *args, **kw): + """ + Starts SCons in the test environment. + + This method exists to tell Test{Cmd,Common} that we're going to + use standard input without forcing every .start() call in the + individual tests to do so explicitly. + """ + if 'stdin' not in kw: + kw['stdin'] = True + sconsflags = initialize_sconsflags(self.ignore_python_version) + try: + p = TestCommon.start(self, *args, **kw) + finally: + restore_sconsflags(sconsflags) + return p + + def wait_for(self, fname, timeout=20.0, popen=None): + """ + Waits for the specified file name to exist. + """ + waited = 0.0 + while not os.path.exists(fname): + if timeout and waited >= timeout: + sys.stderr.write('timed out waiting for %s to exist\n' % fname) + if popen: + popen.stdin.close() + popen.stdin = None + self.status = 1 + self.finish(popen) + stdout = self.stdout() + if stdout: + sys.stdout.write(self.banner('STDOUT ') + '\n') + sys.stdout.write(stdout) + stderr = self.stderr() + if stderr: + sys.stderr.write(self.banner('STDERR ') + '\n') + sys.stderr.write(stderr) + self.fail_test() + time.sleep(1.0) + waited = waited + 1.0 + + def get_alt_cpp_suffix(self): + """ + Many CXX tests have this same logic. + They all needed to determine if the current os supports + files with .C and .c as different files or not + in which case they are instructed to use .cpp instead of .C + """ + if not case_sensitive_suffixes('.c','.C'): + alt_cpp_suffix = '.cpp' + else: + alt_cpp_suffix = '.C' + return alt_cpp_suffix + + def platform_has_symlink(self): + if not hasattr(os, 'symlink') or sys.platform == 'win32': + return False + else: + return True + + +class Stat: + def __init__(self, name, units, expression, convert=None): + if convert is None: + convert = lambda x: x + self.name = name + self.units = units + self.expression = re.compile(expression) + self.convert = convert + +StatList = [ + Stat('memory-initial', 'kbytes', + r'Memory before reading SConscript files:\s+(\d+)', + convert=lambda s: int(s) // 1024), + Stat('memory-prebuild', 'kbytes', + r'Memory before building targets:\s+(\d+)', + convert=lambda s: int(s) // 1024), + Stat('memory-final', 'kbytes', + r'Memory after building targets:\s+(\d+)', + convert=lambda s: int(s) // 1024), + + Stat('time-sconscript', 'seconds', + r'Total SConscript file execution time:\s+([\d.]+) seconds'), + Stat('time-scons', 'seconds', + r'Total SCons execution time:\s+([\d.]+) seconds'), + Stat('time-commands', 'seconds', + r'Total command execution time:\s+([\d.]+) seconds'), + Stat('time-total', 'seconds', + r'Total build time:\s+([\d.]+) seconds'), +] + + +class TimeSCons(TestSCons): + """Class for timing SCons.""" + def __init__(self, *args, **kw): + """ + In addition to normal TestSCons.TestSCons intialization, + this enables verbose mode (which causes the command lines to + be displayed in the output) and copies the contents of the + directory containing the executing script to the temporary + working directory. + """ + self.variables = kw.get('variables') + default_calibrate_variables = [] + if self.variables is not None: + for variable, value in self.variables.items(): + value = os.environ.get(variable, value) + try: + value = int(value) + except ValueError: + try: + value = float(value) + except ValueError: + pass + else: + default_calibrate_variables.append(variable) + else: + default_calibrate_variables.append(variable) + self.variables[variable] = value + del kw['variables'] + calibrate_keyword_arg = kw.get('calibrate') + if calibrate_keyword_arg is None: + self.calibrate_variables = default_calibrate_variables + else: + self.calibrate_variables = calibrate_keyword_arg + del kw['calibrate'] + + self.calibrate = os.environ.get('TIMESCONS_CALIBRATE', '0') != '0' + + if 'verbose' not in kw and not self.calibrate: + kw['verbose'] = True + + TestSCons.__init__(self, *args, **kw) + + # TODO(sgk): better way to get the script dir than sys.argv[0] + self.test_dir = os.path.dirname(sys.argv[0]) + test_name = os.path.basename(self.test_dir) + + if not os.path.isabs(self.test_dir): + self.test_dir = os.path.join(self.orig_cwd, self.test_dir) + self.copy_timing_configuration(self.test_dir, self.workpath()) + + def main(self, *args, **kw): + """ + The main entry point for standard execution of timings. + + This method run SCons three times: + + Once with the --help option, to have it exit after just reading + the configuration. + + Once as a full build of all targets. + + Once again as a (presumably) null or up-to-date build of + all targets. + + The elapsed time to execute each build is printed after + it has finished. + """ + if 'options' not in kw and self.variables: + options = [] + for variable, value in self.variables.items(): + options.append('%s=%s' % (variable, value)) + kw['options'] = ' '.join(options) + if self.calibrate: + self.calibration(*args, **kw) + else: + self.uptime() + self.startup(*args, **kw) + self.full(*args, **kw) + self.null(*args, **kw) + + def trace(self, graph, name, value, units, sort=None): + fmt = "TRACE: graph=%s name=%s value=%s units=%s" + line = fmt % (graph, name, value, units) + if sort is not None: + line = line + (' sort=%s' % sort) + line = line + '\n' + sys.stdout.write(line) + sys.stdout.flush() + + def report_traces(self, trace, stats): + self.trace('TimeSCons-elapsed', + trace, + self.elapsed_time(), + "seconds", + sort=0) + for name, args in stats.items(): + self.trace(name, trace, **args) + + def uptime(self): + try: + fp = open('/proc/loadavg') + except EnvironmentError: + pass + else: + avg1, avg5, avg15 = fp.readline().split(" ")[:3] + fp.close() + self.trace('load-average', 'average1', avg1, 'processes') + self.trace('load-average', 'average5', avg5, 'processes') + self.trace('load-average', 'average15', avg15, 'processes') + + def collect_stats(self, input): + result = {} + for stat in StatList: + m = stat.expression.search(input) + if m: + value = stat.convert(m.group(1)) + # The dict keys match the keyword= arguments + # of the trace() method above so they can be + # applied directly to that call. + result[stat.name] = {'value':value, 'units':stat.units} + return result + + def add_timing_options(self, kw, additional=None): + """ + Add the necessary timings options to the kw['options'] value. + """ + options = kw.get('options', '') + if additional is not None: + options += additional + kw['options'] = options + ' --debug=memory,time' + + def startup(self, *args, **kw): + """ + Runs scons with the --help option. + + This serves as a way to isolate just the amount of startup time + spent reading up the configuration, since --help exits before any + "real work" is done. + """ + self.add_timing_options(kw, ' --help') + # Ignore the exit status. If the --help run dies, we just + # won't report any statistics for it, but we can still execute + # the full and null builds. + kw['status'] = None + self.run(*args, **kw) + sys.stdout.write(self.stdout()) + stats = self.collect_stats(self.stdout()) + # Delete the time-commands, since no commands are ever + # executed on the help run and it is (or should be) always 0.0. + del stats['time-commands'] + self.report_traces('startup', stats) + + def full(self, *args, **kw): + """ + Runs a full build of SCons. + """ + self.add_timing_options(kw) + self.run(*args, **kw) + sys.stdout.write(self.stdout()) + stats = self.collect_stats(self.stdout()) + self.report_traces('full', stats) + self.trace('full-memory', 'initial', **stats['memory-initial']) + self.trace('full-memory', 'prebuild', **stats['memory-prebuild']) + self.trace('full-memory', 'final', **stats['memory-final']) + + def calibration(self, *args, **kw): + """ + Runs a full build of SCons, but only reports calibration + information (the variable(s) that were set for this configuration, + and the elapsed time to run. + """ + self.add_timing_options(kw) + self.run(*args, **kw) + for variable in self.calibrate_variables: + value = self.variables[variable] + sys.stdout.write('VARIABLE: %s=%s\n' % (variable, value)) + sys.stdout.write('ELAPSED: %s\n' % self.elapsed_time()) + + def null(self, *args, **kw): + """ + Runs an up-to-date null build of SCons. + """ + # TODO(sgk): allow the caller to specify the target (argument) + # that must be up-to-date. + self.add_timing_options(kw) + self.up_to_date(arguments='.', **kw) + sys.stdout.write(self.stdout()) + stats = self.collect_stats(self.stdout()) + # time-commands should always be 0.0 on a null build, because + # no commands should be executed. Remove it from the stats + # so we don't trace it, but only if it *is* 0 so that we'll + # get some indication if a supposedly-null build actually does + # build something. + if float(stats['time-commands']['value']) == 0.0: + del stats['time-commands'] + self.report_traces('null', stats) + self.trace('null-memory', 'initial', **stats['memory-initial']) + self.trace('null-memory', 'prebuild', **stats['memory-prebuild']) + self.trace('null-memory', 'final', **stats['memory-final']) + + def elapsed_time(self): + """ + Returns the elapsed time of the most recent command execution. + """ + return self.endTime - self.startTime + + def run(self, *args, **kw): + """ + Runs a single build command, capturing output in the specified file. + + Because this class is about timing SCons, we record the start + and end times of the elapsed execution, and also add the + --debug=memory and --debug=time options to have SCons report + its own memory and timing statistics. + """ + self.startTime = time.time() + try: + result = TestSCons.run(self, *args, **kw) + finally: + self.endTime = time.time() + return result + + def copy_timing_configuration(self, source_dir, dest_dir): + """ + Copies the timing configuration from the specified source_dir (the + directory in which the controlling script lives) to the specified + dest_dir (a temporary working directory). + + This ignores all files and directories that begin with the string + 'TimeSCons-', and all '.svn' subdirectories. + """ + for root, dirs, files in os.walk(source_dir): + if '.svn' in dirs: + dirs.remove('.svn') + dirs = [ d for d in dirs if not d.startswith('TimeSCons-') ] + files = [ f for f in files if not f.startswith('TimeSCons-') ] + for dirname in dirs: + source = os.path.join(root, dirname) + destination = source.replace(source_dir, dest_dir) + os.mkdir(destination) + if sys.platform != 'win32': + shutil.copystat(source, destination) + for filename in files: + source = os.path.join(root, filename) + destination = source.replace(source_dir, dest_dir) + shutil.copy2(source, destination) + + +# In some environments, $AR will generate a warning message to stderr +# if the library doesn't previously exist and is being created. One +# way to fix this is to tell AR to be quiet (sometimes the 'c' flag), +# but this is difficult to do in a platform-/implementation-specific +# method. Instead, we will use the following as a stderr match for +# tests that use AR so that we will view zero or more "ar: creating +# " messages to be successful executions of the test (see +# test/AR.py for sample usage). + +noisy_ar=r'(ar: creating( archive)? \S+\n?)*' + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py new file mode 100644 index 0000000..1e879d9 --- /dev/null +++ b/testing/framework/TestSConsMSVS.py @@ -0,0 +1,1177 @@ +""" +TestSConsMSVS.py: a testing framework for the SCons software construction +tool. + +A TestSConsMSVS environment object is created via the usual invocation: + + test = TestSConsMSVS() + +TestSConsMSVS is a subsclass of TestSCons, which is in turn a subclass +of TestCommon, which is in turn is a subclass of TestCmd), and hence +has available all of the methods and attributes from those classes, +as well as any overridden or additional methods or attributes defined +in this subclass. +""" + +# __COPYRIGHT__ + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import sys +import platform +import traceback +from xml.etree import ElementTree + +from TestSCons import * +from TestSCons import __all__ + + + +expected_dspfile_6_0 = '''\ +# Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=Test - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Test.mak" CFG="Test - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Test - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "Test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "" +# PROP BASE Intermediate_Dir "" +# PROP BASE Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" +# PROP BASE Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" +# PROP BASE Target_File "Test.exe" +# PROP BASE Bsc_Name "" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "" +# PROP Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" +# PROP Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" +# PROP Target_File "Test.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "Test - Win32 Release" + +!IF "$(CFG)" == "Test - Win32 Release" + +!ENDIF + +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE="sdk.h" +# End Source File +# End Group +# Begin Group "Local Headers" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE="test.h" +# End Source File +# End Group +# Begin Group "Other Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE="readme.txt" +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE="test.rc" +# End Source File +# End Group +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;l;y;def;odl;idl;hpj;bat" +# Begin Source File + +SOURCE="test.c" +# End Source File +# End Group +# Begin Source File + +SOURCE="" +# End Source File +# End Target +# End Project +''' + +expected_dswfile_6_0 = '''\ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Test"="Test.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +''' + +SConscript_contents_6_0 = """\ +env=Environment(platform='win32', tools=['msvs'], + MSVS_VERSION='6.0',HOST_ARCH='%(HOST_ARCH)s') + +testsrc = ['test.c'] +testincs = ['sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.dsp', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') +""" + + + +expected_slnfile_7_0 = """\ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" +EndProject +Global + +\tGlobalSection(SolutionConfiguration) = preSolution +\t\tConfigName.0 = Release +\tEndGlobalSection +\tGlobalSection(ProjectDependencies) = postSolution +\tEndGlobalSection +\tGlobalSection(ProjectConfiguration) = postSolution +\t\t.Release.ActiveCfg = Release|Win32 +\t\t.Release.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ExtensibilityGlobals) = postSolution +\tEndGlobalSection +\tGlobalSection(ExtensibilityAddIns) = postSolution +\tEndGlobalSection +EndGlobal +""" + +expected_vcprojfile_7_0 = """\ + + +\tKeyword="MakeFileProj"> +\t +\t\t +\t +\t +\t\t +\t\t\t +\t\t +\t +\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t +\t +\t +\t + +""" + +SConscript_contents_7_0 = """\ +env=Environment(platform='win32', tools=['msvs'], + MSVS_VERSION='7.0',HOST_ARCH='%(HOST_ARCH)s') + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = ['sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.vcproj', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') +""" + + + +expected_slnfile_7_1 = """\ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" +\tProjectSection(ProjectDependencies) = postProject +\tEndProjectSection +EndProject +Global + +\tGlobalSection(SolutionConfiguration) = preSolution +\t\tConfigName.0 = Release +\tEndGlobalSection +\tGlobalSection(ProjectDependencies) = postSolution +\tEndGlobalSection +\tGlobalSection(ProjectConfiguration) = postSolution +\t\t.Release.ActiveCfg = Release|Win32 +\t\t.Release.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ExtensibilityGlobals) = postSolution +\tEndGlobalSection +\tGlobalSection(ExtensibilityAddIns) = postSolution +\tEndGlobalSection +EndGlobal +""" + +expected_vcprojfile_7_1 = """\ + + +\tKeyword="MakeFileProj"> +\t +\t\t +\t +\t +\t\t +\t\t\t +\t\t +\t +\t +\t +\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t +\t +\t +\t + +""" + +SConscript_contents_7_1 = """\ +env=Environment(platform='win32', tools=['msvs'], + MSVS_VERSION='7.1',HOST_ARCH='%(HOST_ARCH)s') + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = ['sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.vcproj', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') +""" + + + +expected_slnfile_8_0 = """\ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" +EndProject +Global + +\tGlobalSection(SolutionConfigurationPlatforms) = preSolution +\t\tRelease|Win32 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ProjectConfigurationPlatforms) = postSolution +\t\t.Release|Win32.ActiveCfg = Release|Win32 +\t\t.Release|Win32.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(SolutionProperties) = preSolution +\t\tHideSolutionNode = FALSE +\tEndGlobalSection +EndGlobal +""" + +expected_slnfile_9_0 = """\ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" +EndProject +Global + +\tGlobalSection(SolutionConfigurationPlatforms) = preSolution +\t\tRelease|Win32 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ProjectConfigurationPlatforms) = postSolution +\t\t.Release|Win32.ActiveCfg = Release|Win32 +\t\t.Release|Win32.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(SolutionProperties) = preSolution +\t\tHideSolutionNode = FALSE +\tEndGlobalSection +EndGlobal +""" + +expected_slnfile_10_0 = """\ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" +EndProject +Global + +\tGlobalSection(SolutionConfigurationPlatforms) = preSolution +\t\tRelease|Win32 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ProjectConfigurationPlatforms) = postSolution +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(SolutionProperties) = preSolution +\t\tHideSolutionNode = FALSE +\tEndGlobalSection +EndGlobal +""" + +expected_slnfile_11_0 = """\ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 11 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" +EndProject +Global + +\tGlobalSection(SolutionConfigurationPlatforms) = preSolution +\t\tRelease|Win32 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ProjectConfigurationPlatforms) = postSolution +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(SolutionProperties) = preSolution +\t\tHideSolutionNode = FALSE +\tEndGlobalSection +EndGlobal +""" + +expected_slnfile_14_0 = """\ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" +EndProject +Global + +\tGlobalSection(SolutionConfigurationPlatforms) = preSolution +\t\tRelease|Win32 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ProjectConfigurationPlatforms) = postSolution +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(SolutionProperties) = preSolution +\t\tHideSolutionNode = FALSE +\tEndGlobalSection +EndGlobal +""" + +expected_vcprojfile_8_0 = """\ + + +\tKeyword="MakeFileProj"> +\t +\t\t +\t +\t +\t +\t +\t\t +\t\t\t +\t\t +\t +\t +\t +\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t +\t +\t +\t + +""" + +expected_vcprojfile_9_0 = """\ + + +\tKeyword="MakeFileProj"> +\t +\t\t +\t +\t +\t +\t +\t\t +\t\t\t +\t\t +\t +\t +\t +\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t +\t\t +\t\t +\t +\t +\t + +""" + +expected_vcprojfile_10_0 = """\ + + +\t +\t\t +\t\t\tRelease +\t\t\tWin32 +\t\t +\t +\t +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} + +\t\tTest +\t\tMakeFileProj +\t +\t +\t +\t\tMakefile +\t\tfalse +\t\tv100 +\t +\t +\t +\t +\t +\t\t +\t +\t +\t +\t<_ProjectFileVersion>10.0.30319.1 +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" +\t\tTest.exe +\t\tDEF1;DEF2;DEF3=1234 +\t\tinc1;inc2 +\t\t$(NMakeForcedIncludes) +\t\t$(NMakeAssemblySearchPath) +\t\t$(NMakeForcedUsingAssemblies) +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t\t +\t +\t +\t\t +\t +\t +\t +\t + +""" + +expected_vcprojfile_11_0 = """\ + + +\t +\t\t +\t\t\tRelease +\t\t\tWin32 +\t\t +\t +\t +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} + +\t\tTest +\t\tMakeFileProj +\t +\t +\t +\t\tMakefile +\t\tfalse +\t\tv110 +\t +\t +\t +\t +\t +\t\t +\t +\t +\t +\t<_ProjectFileVersion>10.0.30319.1 +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" +\t\tTest.exe +\t\tDEF1;DEF2;DEF3=1234 +\t\tinc1;inc2 +\t\t$(NMakeForcedIncludes) +\t\t$(NMakeAssemblySearchPath) +\t\t$(NMakeForcedUsingAssemblies) +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t\t +\t +\t +\t\t +\t +\t +\t +\t + +""" + +expected_vcprojfile_14_0 = """\ + + +\t +\t\t +\t\t\tRelease +\t\t\tWin32 +\t\t +\t +\t +\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} + +\t\tTest +\t\tMakeFileProj +\t +\t +\t +\t\tMakefile +\t\tfalse +\t\tv140 +\t +\t +\t +\t +\t +\t\t +\t +\t +\t +\t<_ProjectFileVersion>10.0.30319.1 +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" +\t\tTest.exe +\t\tDEF1;DEF2;DEF3=1234 +\t\tinc1;inc2 +\t\t$(NMakeForcedIncludes) +\t\t$(NMakeAssemblySearchPath) +\t\t$(NMakeForcedUsingAssemblies) +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t +\t +\t\t +\t\t +\t +\t +\t\t +\t +\t +\t +\t + +""" + +SConscript_contents_8_0 = """\ +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0', + CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], + CPPPATH=['inc1', 'inc2'], + HOST_ARCH='%(HOST_ARCH)s') + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = ['sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.vcproj', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') +""" + +SConscript_contents_9_0 = """\ +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='9.0', + CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], + CPPPATH=['inc1', 'inc2'], + HOST_ARCH='%(HOST_ARCH)s') + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = ['sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.vcproj', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') +""" + +SConscript_contents_10_0 = """\ +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='10.0', + CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], + CPPPATH=['inc1', 'inc2'], + HOST_ARCH='%(HOST_ARCH)s') + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = ['sdk_dir\sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.vcxproj', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') +""" + +SConscript_contents_11_0 = """\ +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='11.0', + CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], + CPPPATH=['inc1', 'inc2'], + HOST_ARCH='%(HOST_ARCH)s') + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = ['sdk_dir\sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.vcxproj', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') +""" + +SConscript_contents_14_0 = """\ +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='14.0', + CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], + CPPPATH=['inc1', 'inc2'], + HOST_ARCH='%(HOST_ARCH)s') + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = ['sdk_dir\sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.vcxproj', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') +""" + +class TestSConsMSVS(TestSCons): + """Subclass for testing MSVS-specific portions of SCons.""" + + def msvs_versions(self): + if not hasattr(self, '_msvs_versions'): + + # Determine the SCons version and the versions of the MSVS + # environments installed on the test machine. + # + # We do this by executing SCons with an SConstruct file + # (piped on stdin) that spits out Python assignments that + # we can just exec(). We construct the SCons.__"version"__ + # string in the input here so that the SCons build itself + # doesn't fill it in when packaging SCons. + input = """\ +import SCons +import SCons.Tool.MSCommon +print("self.scons_version =%%s"%%repr(SCons.__%s__)) +print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions())) +""" % 'version' + + self.run(arguments = '-n -q -Q -f -', stdin = input) + exec(self.stdout()) + + return self._msvs_versions + + def vcproj_sys_path(self, fname): + """ + """ + orig = 'sys.path = [ join(sys' + + enginepath = repr(os.path.join(self._cwd, '..', 'engine')) + replace = 'sys.path = [ %s, join(sys' % enginepath + + contents = self.read(fname, mode='r') + contents = contents.replace(orig, replace) + self.write(fname, contents) + + def msvs_substitute(self, input, msvs_ver, + subdir=None, sconscript=None, + python=None, + project_guid=None, + vcproj_sccinfo='', sln_sccinfo=''): + if not hasattr(self, '_msvs_versions'): + self.msvs_versions() + + if subdir: + workpath = self.workpath(subdir) + else: + workpath = self.workpath() + + if sconscript is None: + sconscript = self.workpath('SConstruct') + + if python is None: + python = sys.executable + + if project_guid is None: + project_guid = "{E5466E26-0003-F18B-8F8A-BCD76C86388D}" + + if 'SCONS_LIB_DIR' in os.environ: + exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % os.environ['SCONS_LIB_DIR'] + else: + exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%s'), join(sys.prefix, 'scons-%s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % (self.scons_version, self.scons_version) + exec_script_main_xml = exec_script_main.replace("'", "'") + + result = input.replace(r'', workpath) + result = result.replace(r'', python) + result = result.replace(r'', sconscript) + result = result.replace(r'', exec_script_main) + result = result.replace(r'', exec_script_main_xml) + result = result.replace(r'', project_guid) + result = result.replace('\n', vcproj_sccinfo) + result = result.replace('\n', sln_sccinfo) + return result + + def get_msvs_executable(self, version): + """Returns a full path to the executable (MSDEV or devenv) + for the specified version of Visual Studio. + """ + from SCons.Tool.MSCommon import get_vs_by_version + + msvs = get_vs_by_version(version) + if not msvs: + return None + return msvs.get_executable() + + def run(self, *args, **kw): + """ + Suppress MSVS deprecation warnings. + """ + save_sconsflags = os.environ.get('SCONSFLAGS') + if save_sconsflags: + sconsflags = [save_sconsflags] + else: + sconsflags = [] + sconsflags = sconsflags + ['--warn=no-deprecated'] + os.environ['SCONSFLAGS'] = ' '.join(sconsflags) + try: + result = TestSCons.run(self, *args, **kw) + finally: + os.environ['SCONSFLAGS'] = save_sconsflags or '' + return result + + def get_vs_host_arch(self): + """ Get an MSVS, SDK , and/or MSVS acceptable platform arch + """ + + # Dict to 'canonalize' the arch + _ARCH_TO_CANONICAL = { + "x86": "x86", + "amd64": "amd64", + "i386": "x86", + "emt64": "amd64", + "x86_64": "amd64", + "itanium": "ia64", + "ia64": "ia64", + } + + host_platform = platform.machine() + # TODO(2.5): the native Python platform.machine() function returns + # '' on all Python versions before 2.6, after which it also uses + # PROCESSOR_ARCHITECTURE. + if not host_platform: + host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') + + + try: + host = _ARCH_TO_CANONICAL[host_platform] + except KeyError as e: + # Default to x86 for all other platforms + host = 'x86' + + + return host + + def validate_msvs_file(self, file): + try: + x = ElementTree.parse(file) + except: + print("--------------------------------------------------------------") + print("--------------------------------------------------------------") + print(traceback.format_exc()) + print("Failed to validate xml in MSVS file: ") + print(file) + print("--------------------------------------------------------------") + print("--------------------------------------------------------------") + self.fail_test() +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/TestSCons_time.py b/testing/framework/TestSCons_time.py new file mode 100644 index 0000000..bc116ec --- /dev/null +++ b/testing/framework/TestSCons_time.py @@ -0,0 +1,331 @@ +""" +TestSCons_time.py: a testing framework for the scons-test.py script + +A TestSCons_time environment object is created via the usual invocation: + + test = TestSCons_time() + +TestSCons_time is a subclass of TestCommon, which is in turn is a subclass +of TestCmd), and hence has available all of the methods and attributes +from those classes, as well as any overridden or additional methods or +attributes defined in this subclass. +""" + +# __COPYRIGHT__ + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import os.path +import sys + +from TestCommon import * +from TestCommon import __all__ +# some of the scons_time tests may need regex-based matching: +from TestSCons import search_re, search_re_in_list + +__all__.extend([ 'TestSCons_time', + ]) + +SConstruct = """\ +from __future__ import print_function +import os +print("SConstruct file directory:", os.getcwd()) +""" + +scons_py = """\ +#!/usr/bin/env python +import os +import sys +def write_args(fp, args): + fp.write(args[0] + '\\n') + for arg in args[1:]: + fp.write(' ' + arg + '\\n') +write_args(sys.stdout, sys.argv) +for arg in sys.argv[1:]: + if arg[:10] == '--profile=': + profile = open(arg[10:], 'w') + profile.write('--profile\\n') + write_args(profile, sys.argv) + break +sys.stdout.write('SCONS_LIB_DIR = ' + os.environ['SCONS_LIB_DIR'] + '\\n') +exec(open('SConstruct', 'r').read()) +""" + +aegis_py = """\ +#!/usr/bin/env python +import os +import sys +script_dir = 'src/script' +if not os.path.exists(script_dir): + os.makedirs(script_dir) +open(script_dir + '/scons.py', 'w').write( +r'''%s''') +""" % scons_py + + +svn_py = """\ +#!/usr/bin/env python +import os +import sys +dir = sys.argv[-1] +script_dir = dir + '/src/script' +os.makedirs(script_dir) +open(script_dir + '/scons.py', 'w').write( +r'''%s''') +""" % scons_py + + +logfile_contents = """\ +Memory before reading SConscript files: 100%(index)s +Memory after reading SConscript files: 200%(index)s +Memory before building targets: 300%(index)s +Memory after building targets: 400%(index)s +Object counts: + pre- post- pre- post- + read read build build Class + 101%(index)s 102%(index)s 103%(index)s 104%(index)s Action.CommandAction + 201%(index)s 202%(index)s 203%(index)s 204%(index)s Action.CommandGeneratorAction + 301%(index)s 302%(index)s 303%(index)s 304%(index)s Action.FunctionAction + 401%(index)s 402%(index)s 403%(index)s 404%(index)s Action.LazyAction + 501%(index)s 502%(index)s 503%(index)s 504%(index)s Action.ListAction + 601%(index)s 602%(index)s 603%(index)s 604%(index)s Builder.BuilderBase + 701%(index)s 702%(index)s 703%(index)s 704%(index)s Builder.CompositeBuilder + 801%(index)s 802%(index)s 803%(index)s 804%(index)s Builder.ListBuilder + 901%(index)s 902%(index)s 903%(index)s 904%(index)s Builder.MultiStepBuilder + 1001%(index)s 1002%(index)s 1003%(index)s 1004%(index)s Builder.OverrideWarner + 1101%(index)s 1102%(index)s 1103%(index)s 1104%(index)s Environment.Base + 1201%(index)s 1202%(index)s 1203%(index)s 1204%(index)s Environment.EnvironmentClone + 1301%(index)s 1302%(index)s 1303%(index)s 1304%(index)s Environment.OverrideEnvironment + 1401%(index)s 1402%(index)s 1403%(index)s 1404%(index)s Executor.Executor + 1501%(index)s 1502%(index)s 1503%(index)s 1504%(index)s Node.FS + 1601%(index)s 1602%(index)s 1603%(index)s 1604%(index)s Node.FS.Base + 1701%(index)s 1702%(index)s 1703%(index)s 1704%(index)s Node.FS.Dir + 1801%(index)s 1802%(index)s 1803%(index)s 1804%(index)s Node.FS.File + 1901%(index)s 1902%(index)s 1904%(index)s 1904%(index)s Node.FS.RootDir + 2001%(index)s 2002%(index)s 2003%(index)s 2004%(index)s Node.Node +Total build time: 11.123456 seconds +Total SConscript file execution time: 22.234567 seconds +Total SCons execution time: 33.345678 seconds +Total command execution time: 44.456789 seconds +""" + + +profile_py = """\ +%(body)s + +import profile + +try: dispatch = profile.Profile.dispatch +except AttributeError: pass +else: dispatch['c_exception'] = profile.Profile.trace_dispatch_return + +prof = profile.Profile() +prof.runcall(%(call)s) +prof.dump_stats(r'%(profile_name)s') +""" + + +class TestSCons_time(TestCommon): + """Class for testing the scons-time script. + + This provides a common place for initializing scons-time tests, + eliminating the need to begin every test with the same repeated + initializations. + """ + + def __init__(self, **kw): + """Initialize an SCons_time testing object. + + If they're not overridden by keyword arguments, this + initializes the object with the following default values: + + program = 'scons-time' + interpreter = ['python', '-tt'] + match = match_exact + workdir = '' + + The workdir value means that, by default, a temporary workspace + directory is created for a TestSCons_time environment. + In addition, this method changes directory (chdir) to the + workspace directory, so an explicit "chdir = '.'" on all of the + run() method calls is not necessary. + """ + + self.orig_cwd = os.getcwd() + try: + script_dir = os.environ['SCONS_SCRIPT_DIR'] + except KeyError: + pass + else: + os.chdir(script_dir) + if 'program' not in kw: + p = os.environ.get('SCONS_TIME') + if not p: + p = 'scons-time' + if not os.path.exists(p): + p = 'scons-time.py' + kw['program'] = p + + if 'interpreter' not in kw: + kw['interpreter'] = [python, '-tt'] + + if 'match' not in kw: + kw['match'] = match_exact + + if 'workdir' not in kw: + kw['workdir'] = '' + + TestCommon.__init__(self, **kw) + + def archive_split(self, path): + if path[-7:] == '.tar.gz': + return path[:-7], path[-7:] + else: + return os.path.splitext(path) + + def fake_logfile(self, logfile_name, index=0): + self.write(self.workpath(logfile_name), logfile_contents % locals()) + + def profile_data(self, profile_name, python_name, call, body): + profile_name = self.workpath(profile_name) + python_name = self.workpath(python_name) + d = { + 'profile_name' : profile_name, + 'python_name' : python_name, + 'call' : call, + 'body' : body, + } + self.write(python_name, profile_py % d) + self.run(program = python_name, interpreter = sys.executable) + + def tempdir_re(self, *args): + """ + Returns a regular expression to match a scons-time + temporary directory. + """ + import re + import tempfile + + sep = re.escape(os.sep) + tempdir = tempfile.gettempdir() + + try: + realpath = os.path.realpath + except AttributeError: + pass + else: + tempdir = realpath(tempdir) + + args = (tempdir, 'scons-time-',) + args + x = os.path.join(*args) + x = re.escape(x) + x = x.replace('time\\-', 'time\\-[^%s]*' % sep) + return x + + def write_fake_aegis_py(self, name): + name = self.workpath(name) + self.write(name, aegis_py) + os.chmod(name, 0o755) + return name + + def write_fake_scons_py(self): + self.subdir('src', ['src', 'script']) + self.write('src/script/scons.py', scons_py) + + def write_fake_svn_py(self, name): + name = self.workpath(name) + self.write(name, svn_py) + os.chmod(name, 0o755) + return name + + def write_sample_directory(self, archive, dir, files): + dir = self.workpath(dir) + for name, content in files: + path = os.path.join(dir, name) + d, f = os.path.split(path) + if not os.path.isdir(d): + os.makedirs(d) + open(path, 'w').write(content) + return dir + + def write_sample_tarfile(self, archive, dir, files): + import shutil + try: + import tarfile + + except ImportError: + + self.skip_test('no tarfile module\n') + + else: + + base, suffix = self.archive_split(archive) + + mode = { + '.tar' : 'w', + '.tar.gz' : 'w:gz', + '.tgz' : 'w:gz', + } + + tar = tarfile.open(archive, mode[suffix]) + for name, content in files: + path = os.path.join(dir, name) + open(path, 'wb').write(bytearray(content,'utf-8')) + tarinfo = tar.gettarinfo(path, path) + tarinfo.uid = 111 + tarinfo.gid = 111 + tarinfo.uname = 'fake_user' + tarinfo.gname = 'fake_group' + tar.addfile(tarinfo, open(path, 'rb')) + tar.close() + shutil.rmtree(dir) + return self.workpath(archive) + + def write_sample_zipfile(self, archive, dir, files): + import shutil + try: + import zipfile + except ImportError: + + sys.stderr.write('no zipfile module\n') + self.no_result() + + else: + + zip = zipfile.ZipFile(archive, 'w') + for name, content in files: + path = os.path.join(dir, name) + open(path, 'w').write(content) + zip.write(path) + zip.close() + shutil.rmtree(dir) + return self.workpath(archive) + + sample_project_files = [ + ('SConstruct', SConstruct), + ] + + def write_sample_project(self, archive, dir=None): + base, suffix = self.archive_split(archive) + + write_sample = { + '.tar' : self.write_sample_tarfile, + '.tar.gz' : self.write_sample_tarfile, + '.tgz' : self.write_sample_tarfile, + '.zip' : self.write_sample_zipfile, + }.get(suffix, self.write_sample_directory) + + if not dir: + dir = base + + os.mkdir(dir) + path = write_sample(archive, dir, self.sample_project_files) + + return path + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/TestSConsign.py b/testing/framework/TestSConsign.py new file mode 100644 index 0000000..a48b648 --- /dev/null +++ b/testing/framework/TestSConsign.py @@ -0,0 +1,90 @@ +# __COPYRIGHT__ +from __future__ import print_function + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +__doc__ = """ +TestSConsign.py: a testing framework for the "sconsign" script +tool. + +A TestSConsign environment object is created via the usual invocation: + + test = TestSConsign() + +TestSconsign is a subclass of TestSCons, which is a subclass of +TestCommon, which is in turn is a subclass of TestCmd), and hence +has available all of the methods and attributes from those classes, +as well as any overridden or additional methods or attributes defined +in this subclass. +""" + +import os +import os.path +import sys + +from TestSCons import * +from TestSCons import __all__ + +__all__.extend([ 'TestSConsign', ]) + +class TestSConsign(TestSCons): + """Class for testing the sconsign.py script. + + This provides a common place for initializing sconsign tests, + eliminating the need to begin every test with the same repeated + initializations. + + This adds additional methods for running the sconsign script + without changing the basic ability of the run() method to run + "scons" itself, since we need to run scons to generate the + .sconsign files that we want the sconsign script to read. + """ + def __init__(self, *args, **kw): + try: + script_dir = os.environ['SCONS_SCRIPT_DIR'] + except KeyError: + pass + else: + os.chdir(script_dir) + self.script_dir = os.getcwd() + + TestSCons.__init__(self, *args, **kw) + + self.my_kw = { + 'interpreter' : python, # imported from TestSCons + } + + if 'program' not in kw: + kw['program'] = os.environ.get('SCONS') + if not kw['program']: + if os.path.exists('scons'): + kw['program'] = 'scons' + else: + kw['program'] = 'scons.py' + + sconsign = os.environ.get('SCONSIGN') + if not sconsign: + if os.path.exists(self.script_path('sconsign.py')): + sconsign = 'sconsign.py' + elif os.path.exists(self.script_path('sconsign')): + sconsign = 'sconsign' + else: + print("Can find neither 'sconsign.py' nor 'sconsign' scripts.") + self.no_result() + self.set_sconsign(sconsign) + + def script_path(self, script): + return os.path.join(self.script_dir, script) + + def set_sconsign(self, sconsign): + self.my_kw['program'] = sconsign + + def run_sconsign(self, *args, **kw): + kw.update(self.my_kw) + return self.run(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/testing/framework/test-framework.rst b/testing/framework/test-framework.rst new file mode 100644 index 0000000..7082eb9 --- /dev/null +++ b/testing/framework/test-framework.rst @@ -0,0 +1,427 @@ +======================= +SCons Testing Framework +======================= + +SCons uses extensive automated tests to try to ensure quality. The primary goal +is that users should be able to upgrade from version to version without any surprise +changes in behavior. + +In general, no change goes into SCons unless it has one or more new or modified +tests that demonstrably exercise the bug being fixed or the feature being added. +There are exceptions to this guideline, but they should be just that, ''exceptions''. +When in doubt, make sure it's tested. + +Test Organization +================= + +There are three types of SCons tests: + +*End-to-End Tests* + End-to-end tests of SCons are all Python scripts (``*.py``) underneath + the ``test/`` subdirectory. They use the test infrastructure modules in the + ``testing/framework`` subdirectory. + +*Unit Tests* + Unit tests for individual SCons modules live underneath the + ``src/engine/`` subdirectory and are the same base name as the module + with ``Tests.py`` appended--for example, the unit tests for the + ``Builder.py`` module are in the ``BuilderTests.py`` script. + +*External Tests* + For the support of external Tools (in the form of packages, preferably), the + testing framework got extended, such that it can run in standalone mode. + You can start it from the top-level folder of your Tool's source tree, + where it then finds all Python scripts (``*.py``) underneath the + local ``test/`` directory. + This implies that Tool tests have to be kept in a folder named ``test``, + like for the SCons core. + + +Contrasting End-to-End and Unit Tests +##################################### + +In general, anything that we've put into an end-to-end test script should +be considered a hardened part of the interface (that is, it's something +that a user might do) and should not be broken. Unit tests are now +considered more malleable, more for testing internal interfaces that +can change so long as we don't break users' ``SConscript`` files. (This +wasn't always the case, and there's a lot of meaty code in many of the +unit test scripts that does, in fact, capture external interface +behavior. In general, we should try to move those things to end-to-end +scripts as we find them.) + +It's more difficult to debug end-to-end tests. You can actually go +straight into the Python debugger on the unit test scripts by using the +``runtest.py --pdb`` option, but the end-to-end tests treat an SCons +invocation as a "black box" and just look for external effects. +Simple ``print`` statements within the SCons code itself often don't help +debug end-to-end because they end up in SCons output that gets compared +against expected output and cause a test failure. Probably the most +effective technique is to use the internal ``SCons.Debug.Trace()`` function, +which prints output to ``/dev/tty`` on Linux/UNIX systems and ``con`` on +Windows systems, so you can see what's going on. + +Naming conventions +################## + +The end-to-end tests, more or less, stick to the following naming conventions: + +1. All tests end with a .py suffix. + +2. In the *General* form we use + + ``Feature.py`` + for the test of a specified feature; try to + keep this description reasonably short + + ``Feature-x.py`` + for the test of a specified feature using + option ``x`` + +3. The *command line option* tests take the form + + ``option-x.py`` + for a lower-case single-letter option + + ``option--X.py`` + upper-case single-letter option + (with an extra hyphen, so the file names will + be unique on case-insensitive systems) + + ``option--lo.py`` + long option; abbreviate the long + option name to a few characters + + +Running Tests +============= + +The standard set of SCons tests are run from the top-level source directory +by the ``runtest.py`` script. + +Help is available through the ``-h`` option: + +:: + + $ python runtest.py -h + +To simply run all the tests, use the ``-a`` option: + +:: + + $ python runtest.py -a + +By default, ``runtest.py`` prints a count and percentage message for each test +case, along with the name of the test file. +If you need the output to be more silent, have a look at the ``-q``, ``-s`` and +``-k`` options. + +You may specifically list one or more tests to be run: + +:: + + $ python runtest.py src/engine/SCons/BuilderTests.py + $ python runtest.py test/option-j.py test/Program.py + +Folder names are allowed arguments as well, so you can do a + +:: + + $ python runtest.py test/SWIG + +to run all SWIG tests only. + +You can also use the ``-f`` option to execute just the tests listed in a specified +text file: + +:: + + $ cat testlist.txt + test/option-j.py + test/Program.py + $ python runtest.py -f testlist.txt + + +One test must be listed per line, and any lines that begin with '#' +will be ignored (the intent being to allow you, for example, +to comment out tests that +are currently passing and then uncomment all of the tests in the file +for a final validation run). + +If more than one test is run, the ``runtest.py`` script prints a summary +of how many tests passed, failed, or yielded no result, and lists any +unsuccessful tests. + +The above invocations all test directly the files underneath the ``src/`` +subdirectory, and do not require that a packaging build be performed first. +The ``runtest.py`` script supports additional options to run tests against +unpacked packages in the ``build/test-*/`` subdirectories. + +If you are testing a separate Tool outside of the SCons source tree, you have +to call the ``runtest.py`` script in *external* (stand-alone) mode:: + + $ python ~/scons/runtest.py -e -a + +. This ensures that the testing framework doesn't try to access SCons classes +needed for some of the *internal* test cases. + +Note, that the actual tests are carried out in a temporary folder each, which gets +deleted afterwards. This ensures that your source directories don't get clobbered +with temporary files from the test runs. It also means that you can't simply change +into a folder to "debug things" after a test has gone wrong. For a way around this, +check out the ``PRESERVE`` environment variable. It can be seen in action in +`How to convert old tests`_ below. + +Not Running Tests +================= + +If you simply want to check which tests would get executed, you can call the +``runtest.py`` script with the ``-l`` option:: + + $ python runtest.py -l + +Then there is also the ``-n`` option, which prints the command line for each +single test, but doesn't actually execute them:: + + $ python runtest.py -n + +Finding Tests +============= + +When started in *standard* mode + +:: + + $ python runtest.py -a + + +, ``runtest.py`` assumes that it is run from the SCons top-level source directory. +It then dives into the ``src`` and ``test`` folders, where it tries to find filenames + + ``*Test.py`` + for the ``src`` directory, and + + ``*.py`` + for the ``test`` folder. + +When using fixtures, you may quickly end up in a position where you have supporting +Python script files in a subfolder, but they shouldn't get picked up as test scripts. +In this case you have two options: + +1. Add a file with the name ``sconstest.skip`` to your subfolder. This lets + ``runtest.py`` skip the contents of the directory completely. +2. Create a file ``.exclude_tests`` in each folder in question, and in it list + line-by-line the files to get excluded from testing. + +The same rules apply when testing external Tools by using the ``-e`` option. + + +"Hello, world!" SCons Test Script +================================= + +To illustrate how the end-to-end test scripts work, +let's walk through a simple "Hello, world!" example: + +:: + + #!python + import TestSCons + + test = TestSCons.TestSCons() + + test.write('SConstruct', """\ + Program('hello.c') + """) + + test.write('hello.c', """\ + int + main(int argc, char *argv[]) + { + printf("Hello, world!\\n"); + exit (0); + } + """) + + test.run() + + test.run(program='./hello', stdout="Hello, world!\n") + + test.pass_test() + + +``import TestSCons`` + Imports the main infrastructure for writing SCons tests. This is normally the only part of the infrastructure that needs importing. Sometimes other Python modules are necessary or helpful, and get imported before this line. + +``test = TestSCons.TestSCons()`` + This initializes an object for testing. A fair amount happens under the covers when the object is created, including: + + * A temporary directory is created for all the in-line files that will get created. + * The temporary directory's removal is arranged for when the test is finished. + * We ``os.chdir()`` to the temporary directory. + +``test.write('SConstruct', ...`` + This line creates an ``SConstruct`` file in the temporary directory, to be used as input to the ``scons`` run(s) that we're testing. Note the use of the Python triple-quote syntax for the contents of the ``SConstruct`` file. Because input files for tests are all created from in-line data like this, the tests can sometimes get a little confusing to read, because some of the Python code is found + +``test.write('hello.c', ...`` + This lines creates an ``hello.c`` file in the temporary directory. Note that we have to escape the ``\\n`` in the ``"Hello, world!\\n"`` string so that it ends up as a single backslash in the ``hello.c`` file on disk. + +``test.run()`` + This actually runs SCons. Like the object initialization, things happen under the covers: + + * The exit status is verified; the test exits with a failure if the exit status is not zero. + * The error output is examined, and the test exits with a failure if there is any + +``test.run(program='./hello', stdout="Hello, world!\n")`` + This shows use of the ``TestSCons.run()`` method to execute a program other than ``scons``, in this case the ``hello`` program we just presumably built. The ``stdout=`` keyword argument also tells the ``TestSCons.run()`` method to fail if the program output does not match the expected string ``"Hello, world!\n"``. Like the previous ``test.run()`` line, it will also fail the test if the exit status is non-zero, or there is any error output. + +``test.pass_test()`` + This is always the last line in a test script. It prints ``PASSED`` on the screen and makes sure we exit with a ``0`` status to indicate the test passed. As a side effect of destroying the ``test`` object, the created temporary directory will be removed. + +Working with fixtures +===================== + +In the simple example above, we have seen how to create files in the temporary test directory. +We give a filename to the ``TestSCons.write()`` method, together with its contents, and it gets +written to the test folder right before its start. + +This technique can still be seen throughout most of the end-to-end tests, but there is a better +way. It's much easier to edit, create and maintain real files, instead of copy/pasting +content to/from a Python script. If the test files get longer, the test script +gets longer and is harder to read. + +Against this, we now have the possibility to copy single files or the contents of a +local folder to the test directory. Since we can reuse these files/folders to setup +several tests, we call them *fixtures* in the following. + +Directory fixtures +################## + +The function ``dir_fixture(self, srcdir, dstdir=None)`` in the ``TestCmd`` class +copies the contents of the specified folder ``srcdir`` from +the directory of the called test script, to the current +temporary test directory. +The ``srcdir`` name may be a list, in which case the elements are +concatenated with the ``os.path.join()`` method. The ``dstdir`` is +assumed to be under the temporary working directory, it gets +created automatically, if it does not already exist. + +A short syntax example:: + + test = TestSCons.TestSCons() + test.dir_fixture('image') + test.run() + +would copy all files and subfolders from the local ``image`` folder, to +the temporary directory for the current test. + +If you'd like to see a real example for this in action, refer to the test +named ``test/packaging/convenience-functions/convenience-functions.py``. + +File fixtures +############# + +Like for directory fixtures, ``file_fixture(self, srcfile, dstfile=None)`` +copies the file ``srcfile`` from the directory of +the called script, to the temporary test directory. +The ``dstfile`` is assumed to be under the temporary working +directory, unless it is an absolute path name. +If ``dstfile`` is specified, its target directory gets created +automatically if it doesn't already exist. + +With a:: + + test = TestSCons.TestSCons() + test.file_fixture('SConstruct') + test.file_fixture(['src','main.cpp'],['src','main.cpp']) + test.run() + +you would copy the files ``SConstruct`` and ``src/main.cpp`` to the temporary +test folder, prior to running the test itself. + +Again, a reference example can be found in the current *default* revision of +SCons, it is ``test/packaging/sandbox-test/sandbox-test.py``. + +For even more examples you should check out one of the external Tools, e.g. the +*Qt4* Tool at https://bitbucket.org/dirkbaechle/scons_qt4. Also visit the SCons +Tools Index at https://github.com/SCons/scons/wiki/ToolsIndex for a complete +list of available Tools, though not all may have tests yet. + +How to convert old tests +######################## + +We now show how to convert a test, still using the ``TestSCons.write()`` method, to +the fixture based approach. For this, we need to get at the files as they +are written to each temporary test folder. + +Luckily, ``runtest.py`` checks for the existence of an environment variable named +``PRESERVE``. If it is set to a non-zero value, the testing framework doesn't delete +the test folder as ususal, but prints its name to the screen. + +So, you should be able to give the commands + +:: + + $ export PRESERVE=1 + $ python runtest.py test/packaging/sandbox-test.py + +, assuming Linux and a bash-like shell. + +The output should then look something like this:: + + 1/1 (100.00%) /usr/bin/python -tt test/packaging/sandbox-test.py + PASSED + Preserved directory /tmp/testcmd.4060.twlYNI + +and you see that the test files have been kept in the folder ``/tmp/testcmd.4060.twlYNI``, +where you can now copy them from to your new *fixture* folder. Then, in the test +script you simply remove all the tedious ``TestSCons.write()`` statements and +replace them by a single ``TestSCons.dir_fixture()``. + +Finally, you shouldn't forget to clean up and remove the temporary test directory. ``;)`` + +Test Infrastructure +=================== + +The test API is in ``testing/framework/TestSCons.py``. ``TestSCons`` is a subclass of +``TestCommon``, which is a subclass of ``TestCmd``; all those python files are +in ``testing/framework``. Start in ``testing/framework/TestCmd.py`` for the base API definitions, +like how to create files (``test.write()``) and run commands (``test.run()``). + +You want to use ``TestSCons`` for the end-to-end tests in ``test``, but ``TestCmd`` +for the unit tests in the ``src`` folder. + +The match functions work like this: + +TestSCons.match_re:: match each line with a RE + * Splits the lines into a list (unless they already are) + * splits the REs at newlines (unless already a list) and puts ^..$ around each + * then each RE must match each line. This means there must be as many REs as lines. + +TestSCons.match_re_dotall:: match all the lines against a single RE + * Joins the lines with newline (unless already a string) + * joins the REs with newline (unless it's a string) and puts ^..$ around the whole thing + * then whole thing must match with python re.DOTALL. + +Use them in a test like this:: + + test.run(..., match=TestSCons.match_re, ...) + +or:: + + test.must_match(..., match=TestSCons.match_re, ...) + +Avoiding Tests based on Tool existence +====================================== + +Here's an easy sample:: + + #!python + intelc = test.detect_tool('intelc', prog='icpc') + if not intelc: + test.skip_test("Could not load 'intelc' Tool; skipping test(s).\n") + +See ``testing/framework/TestSCons.py`` for the ``detect_tool`` method. It calls the tool's +``generate()`` method, and then looks for the given prog (tool name by default) in +``env['ENV']['PATH']``. + + diff --git a/timings/README.txt b/timings/README.txt index 3c0baff..9921208 100644 --- a/timings/README.txt +++ b/timings/README.txt @@ -5,8 +5,8 @@ This directory contains timing configurations for SCons. Each configuration exists in a subdirectory. The controlling script is named TimeSCons-run.py for the configuration. The TimeSCons-run.py scripts use TestSCons.TimeSCons, a subclass of TestSCons.TestSCons (both -defined in ../QMTest/TestSCons.py), to manage execution of the timing -runs. +defined in ../testing/framework/TestSCons.py), to manage execution of the +timing runs. Unlike the TestSCons.TestSCons base class, the TestSCons.TimeSCons subclass copies the contents of its containing directory to the temporary -- cgit v0.12 From aed736716d539a5f2df2b1478f40c4105fb1a915 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 29 Apr 2018 22:01:42 -0400 Subject: remove obsolete HOWTO dir.. ancient out of date information from when aegis was the source control tool used by SCons --- HOWTO/README | 24 -- HOWTO/change.txt | 61 ----- HOWTO/new-platform.txt | 74 ------ HOWTO/new-script.txt | 75 ------ HOWTO/new-tool.txt | 81 ------ HOWTO/release.txt | 656 ------------------------------------------------- 6 files changed, 971 deletions(-) delete mode 100644 HOWTO/README delete mode 100644 HOWTO/change.txt delete mode 100644 HOWTO/new-platform.txt delete mode 100644 HOWTO/new-script.txt delete mode 100644 HOWTO/new-tool.txt delete mode 100644 HOWTO/release.txt diff --git a/HOWTO/README b/HOWTO/README deleted file mode 100644 index e20df73..0000000 --- a/HOWTO/README +++ /dev/null @@ -1,24 +0,0 @@ -__COPYRIGHT__ - -Here you'll find plain text documentation of how to handle various SCons -project procedures. Files contained herein: - -change.txt - How changes are integrated, including generating and - distributing aedist change sets, and updating the CVS repository - on SourceForge. - -new-platform.txt - Steps to add a new Platform/*.py file. This is probably out - of date. - -new-script.txt - Steps to add a new script or utility (like scons and sconsign) - to what we ship. - -new-tool.txt - Steps to add a new Tool/*.py file. This is probably out of date. - -release.txt - Steps to go through when releasing a new version of SCons. - diff --git a/HOWTO/change.txt b/HOWTO/change.txt deleted file mode 100644 index f295003..0000000 --- a/HOWTO/change.txt +++ /dev/null @@ -1,61 +0,0 @@ -__COPYRIGHT__ - -Handling a change set: - - -- Start the change: - - aedb {cnum} [if it's initiated locally] - aedist -r [if it's a remote submission] - - -- Normal development cycle: - - aeb - aet - aet -bl - aet -reg [optional] - aed - aede - - -- As the reviewer: - - aerpass {cnum} - - -- As the integrator: - - aeib {cnum} - aeb - aet - aet -bl - aet -reg - aed - aeipass - - -- Distribute the change to scons-aedist: - - aedist -s -p scons.0.96 {cnum} > scons.0.96.C{cnum}.ae - pine -attach scons.0.96.C{cnum}.ae scons-aedist@lists.sourceforge.net - Subject: scons.0.96 - {SUMMARY} - Body: aegis -l -p scons.0.96 -c {cnum} cd - - rm scons.0.96.C{cnum}.ae - - [This will eventually be automated.] - - -- Update the aedist baseline on the web site: - - aedist -s -bl -p scons.0.96 > scons.0.96.ae - scp scons.0.96.ae stevenknight@scons.sourceforge.net:/home/groups/s/sc/scons/htdocs/scons.0.96.ae - rm scons.0.96.ae - - [This will eventually be automated.] - - -- Distribute the change to CVS: - - export CVS_RSH=ssh - ae2cvs -n -aegis -p scons.0.96 -c {cnum} -u ~/SCons/scons - ae2cvs -X -aegis -p scons.0.96 -c {cnum} -u ~/SCons/scons - - If you need the "ae2cvs" Perl script, you can find a copy - checked in to the bin/subdirectory. - - [This may eventually be automated.] diff --git a/HOWTO/new-platform.txt b/HOWTO/new-platform.txt deleted file mode 100644 index 8aa11ec..0000000 --- a/HOWTO/new-platform.txt +++ /dev/null @@ -1,74 +0,0 @@ -__COPYRIGHT__ - -Adding a new Platform to the SCons distribution: - - -- Add the following files (aenf): - - src/engine/SCons/Platform/xxx.py - - Use one of the other Platform files as a template. - - The tool_list() function should return the list of tools - that will be available through the default construction - Environment. - - The generate() function should set construction variables: - - ENV - OBJPREFIX - OBJSUFFIX - PROGPREFIX - PROGSUFFIX - LIBPREFIX - LIBSUFFIX - SHLIBPREFIX - SHLIBSUFFIX - LIBPREFIXES - LIBSUFFIXES - - -- Modify the following files (aecp): - - doc/man/scons.1 - - Add the new platform to the example of the platform - keyword when instantiating an Environment. - - Add the list of default tools returned by tool_list() - for this platform. - - rpm/scons.spec - - Add to the list at the bottom of the file: - - /usr/lib/scons/SCons/Platform/xxx.py - /usr/lib/scons/SCons/Platform/xxx.pyc - - [THIS LIST SHOULD BE GENERATED FROM MANIFEST.in, - AND WILL BE SOME DAY.] - - src/CHANGES.txt - - Add mention of the new Platform specification. - - src/engine/MANIFEST.in - - Add to the list: - - SCons/Platform/xxx.py - - src/engine/SCons/Platform/Platform/PlatformTests.py - - Copy and paste one of the other platform tests to verify - the ability to initialize an environment through a call - to the object returned by Platform('xxx') - - src/engine/SCons/Platform/__init__.py - - Add logic to platform_default() (if necessary) to return - the appropriate platform string. - - test/Platform.py - - Add the new platform to the SConstruct and SConscript - files. Add the expected output to the "expect" - variable. diff --git a/HOWTO/new-script.txt b/HOWTO/new-script.txt deleted file mode 100644 index f859c48..0000000 --- a/HOWTO/new-script.txt +++ /dev/null @@ -1,75 +0,0 @@ -__COPYRIGHT__ - -Steps for adding a new script/utility to the SCons packages. Assume -that you're creating a new man page, too. In the template steps -below, "newscript" is the name of the new script/utility being added. - - New files: - - doc/man/newscript.1 - - New man page; use an existing one as a template. - - src/script/newscript.py - - The new script itself. - - test/*.py - - Appropriate tests for the new script. - - Modified files: - - SConstruct - - In the "scons_script" package description: - - Add "newscript" : "newscript.py" to the "filemap" - dictionary. - - In the "scons" package description: - - Add "newscript.1" to the "files" list. - - Add "newscript.1" : "../doc/man/newscript.1" to the - "filemap" dictionary. - - Add "newscript" to the scripts[] list - - debian/rules - - Add: - - rm -f debian/scons/usr/bin/newscript - cp build/scripts/newscript debian/scons/user/bin/newscript - sed '1s|.*|#!/usr/bin/python2.1|' build/scripts/newscript > debian/scons/user/bin/newscript - chmod +x debian/scons/user/bin/newscript - - rm -f debian/scons/usr/share/man/man1/newscript.1 - cp newscript.1 debian/scons/user/share/man/man1/ - - doc/SConscript - - Add "newscript" to man_page_list[] - - gentoo/scons.ebuild.in - - Add "doman newscript.1" - - rpm/scons.spec.in - - Add "gzip -c newscript.1" > $RPM_BUILD_ROOT/usr/man/man1/newscript.1.gz - - Add "%doc /usr/man/man1/newscript.1.gz" - - src/CHANGES.txt - - Document the addition of the new script. - - src/script/MANIFEST.in - - Add a line: "newscript" - - src/setup.py - - Add "script/newscript" to the 'scripts' list. diff --git a/HOWTO/new-tool.txt b/HOWTO/new-tool.txt deleted file mode 100644 index 4297fe6..0000000 --- a/HOWTO/new-tool.txt +++ /dev/null @@ -1,81 +0,0 @@ -__COPYRIGHT__ - -Adding a new Tool to the SCons distribution: - - -- Add the following files (aenf): - - src/engine/SCons/Tool/xxx.py - - Use one of the other Tool files as a template. - - The tool should set and use construction variables: - - XXX (specific tool) - XXXCOM (the command line) - XXXFLAGS (flags for the command line) - - -- Modify the following files (aecp): - - doc/man/scons.1 - - Add the new tool to the default Platform lists - (if appropriate). - - If this creates object files from a new source - file type, add the new file suffixes. - - Document the new construction variables. - - rpm/scons.spec - - Add to the list at the bottom of the file: - - /usr/lib/scons/SCons/Tool/xxx.py - /usr/lib/scons/SCons/Tool/xxx.pyc - - [THIS LIST SHOULD BE GENERATED FROM MANIFEST.in, - AND WILL BE SOME DAY.] - - src/CHANGES.txt - - Add mention of the new Tool specification. - - src/engine/MANIFEST.in - - Add to the list: - - SCons/Tool/xxx.py - - src/engine/SCons/Defaults.py - - If there are any new Actions for the Tool, add them - here. - - src/engine/SCons/Platform/*.py - - Add the Tool to the list(s) returned by tool_list(). - - -- Add the following tests (aent): - - test/XXX.py - - Use one of the other tests as a template. - - The first part should be a platform-independent test - using dummy 'myxxx.py' utilities to test if $XXX is set. - - Remaining parts of the test can check to see if the - specific tool is installed on the system, and test - actually executing the tool. - - test/XXXFLAGS.py - - Use one of the other tests as a template. - - The first part should be a platform-independent test - using dummy 'myxxx.py' utilities to test if $XXXFLAGS is - set. - - Optionally, remaining parts of the test can check to see - if the specific tool is installed on the system, and - test actually executing the tool. diff --git a/HOWTO/release.txt b/HOWTO/release.txt deleted file mode 100644 index 5fbbfc7..0000000 --- a/HOWTO/release.txt +++ /dev/null @@ -1,656 +0,0 @@ -__COPYRIGHT__ - -This document covers how to prepare major releases of SCons--that is, -releases numbered with X.Y format, such as 0.93, 1.0, etc. - -If you need to prepare a specific subrelease (X.Y.Z, such as 0.94.1), -then see the document HOWTO/subrelease.txt. - -Things to do to release a new X.Y version of SCons: - - Prepare the describe-the-release section for the announcements - - summarize changes from src/CHANGES.txt - - template is below, search for "describe-the-release" - - send this out for review while you get the rest of the - release ready! - - Build and test candidate packages - - test on Linux - - test on Windows NT - - 1) tar zxf scons-src-{version}.tar.gz - cd scons-src-{version} - python runtest.py -a - - 2) tar zxf scons-{version}.tar.gz - cd scons-{version} - python setup.py install - cd scons-src-{version} - python runtest.py -a -X -x C:\Python20\scons.bat - - 3) scons-{verson}.win32.exe - cd scons-src-{version} - python runtest.py -a -X -x C:\Python20\scons.bat - - 4) mkdir temporary_directory - cd temporary_directory - tar zxf scons-local-{version}.tar.gz - cd scons-src-{version} - python runtest.py -a -x C:\temporary_directory\scons.py - - Check in any changes necessary to make everything work - - Update the user's guide - - sh bin/docdiff - - sh bin/docupdate - - END THE CURRENT DEVELOPMENT BRANCH - - ae_p scons.0 - - aede {96} - - aerpass {96} - - aeib {96} - - aed - - aeb - - aet - - aet -reg - - aeipass - - START THE NEW BRANCH FOR RELEASE - - aenbr -p scons.0 {97} - - aenc -p scons.0.{97} - - Call it something like, - "Initialize the new branch for release." - Cause = internal_enhancement. - Exempt it from all tests (*_exempt = true). - - ae_p scons.0.{97} - - aedb 100 - - aecd - - # Change the hard-coded package version numbers - # in the following files. - aecp README - vi README - - aecp SConstruct - vi SConstruct - - aecp QMTest/TestSCons.py - vi QMTest/TestSCons.py - - # Read through and update the README files if necessary - [optional] aecp README - [optional] vi README - - [optional] aecp src/README.txt - [optional] vi src/README.txt - - # Prepare src/CHANGES.txt - aecp src/CHANGES.txt - vi src/CHANGES.txt - - date -R the latest release - - should be current if this has been updated - as each change went in. - - # Prepare src/RELEASE.txt - aecp src/RELEASE.txt - vi src/RELEASE.txt - - date -R the latest release - - Read through and edit appropriately. - - Can probably keep most of the existing text - - Add any new known problems - - # Prepare debian/changelog - aecp debian/changelog - vi debian/changelog - - date -R the latest release - - # Now build and prepare the release itself. - aeb - - aed - - aet -reg - - aede - - etc. - - - - Read through the FAQ for any updates - - - - Upload the packages to the SourceForge incoming FTP: - - ftp upload.sourceforge.net - anonymous - - cd incoming - bin - put scons-0.{97}-1.noarch.rpm - put scons-0.{97}-1.src.rpm - put scons-0.{97}.tar.gz - put scons-0.{97}.win32.exe - put scons-0.{97}.zip - put scons-local-0.{97}.tar.gz - put scons-local-0.{97}.zip - put scons-src-0.{97}.tar.gz - put scons-src-0.{97}.zip - put scons_0.{97}-1_all.deb - - Create the new release at the SourceForge project page: - - Pull down the "Admin" menu and select "File Releases" - - Package Name: scons - - => Add Release - - New release name: 0.{97} - - Upload the RELEASE.txt file. - - Upload the CHANGES.txt file. - - Check the "Preserve my pre-formatted text." box (IMPORTANT!) - - Click "Submit/Refresh" (IMPORTANT!) - - Check the SCons files you uploaded - - Click "Add Files and/or Refresh View" - - Edit the file info: - - scons-0.{97}-1.noarch.rpm Any .rpm - scons-0.{97}-1.src.rpm Any Source .rpm - scons-0.{97}.tar.gz Any .gz - scons-0.{97}.win32.exe i386 .exe (32-bit Windows) - scons-0.{97}.zip Any .zip - scons_0.{97}-1_all.deb Any .deb - - Click "Update/Refresh" for each file; this must be done - one at a time. - - Check "I'm sure." and click "Send Notice" in the Email - Release Notice section. - - - Pull down the "Admin" menu and select "File Releases" - - Package Name: scons-local - - => Add Release - - New release name: 0.{97} - - Upload the RELEASE.txt file. - - Upload the CHANGES.txt file. - - Check the "Preserve my pre-formatted text." box (IMPORTANT!) - - Click "Submit/Refresh" (IMPORTANT!) - - Check the SCons files you uploaded - - Click "Add Files and/or Refresh View" - - Edit the file info: - - scons-local-0.{97}.tar.gz Any .gz - scons-local-0.{97}.zip Any .zip - - Click "Update/Refresh" for each file; this must be done - one at a time. - - Check "I'm sure." and click "Send Notice" in the Email - Release Notice section. - - - Pull down the "Admin" menu and select "File Releases" - - Package Name: scons-src - - => Add Release - - New release name: 0.{97} - - Upload the RELEASE.txt file. - - Upload the CHANGES.txt file. - - Check the "Preserve my pre-formatted text." box (IMPORTANT!) - - Click "Submit/Refresh" (IMPORTANT!) - - Check the SCons files you uploaded - - Click "Add Files and/or Refresh View" - - Edit the file info: - - scons-src-0.{97}.tar.gz Any .gz - scons-src-0.{97}.zip Any .zip - - Click "Update/Refresh" for each file; this must be done - one at a time. - - Check "I'm sure." and click "Send Notice" in the Email - Release Notice section. - - - Hide release 0.{95} at the SourceForge download page: - - Pull down the "Admin" menu and select "File Releases" - - Package Name: scons - - => Edit Releases - - Release Name: 0.{95} - - => Edit This Release - - Status: => Hidden - - Click Submit/Refresh - - - Pull down the "Admin" menu and select "File Releases" - - Package Name: scons-local - - => Edit Releases - - Release Name: 0.{95} - - => Edit This Release - - Status: => Hidden - - Click Submit/Refresh - - - Pull down the "Admin" menu and select "File Releases" - - Package Name: scons-src - - => Edit Releases - - Release Name: 0.{95} - - => Edit This Release - - Status: => Hidden - - Click Submit/Refresh - - - - Add a new release for 0.{97} in the Issue Tracker at tigris.org: - - Click "Issue Tracker" on the left-hand nav bar - - Click "Configuration options" - - Click "Add/edit components" - - Under "scons" - To the right of "Add ..." - Click "Version" - - At the bottom of the list click "Add" - - Fill in the "Version:" box with 0.{97} - - Check "Add this version to *all* components." - - Click the "Add" button - - - - Update the scons.org web site: - - svn co http://scons.tigris.org/svn/scons/scons.org - - cd scons.org - - CHANGES.txt: copy new version from built source tree - - download.php: new version number - - includes/templates.php: - update $latestrelease - - includes/versions.php: - update $stablerelease and/or $latestrelease - add new version number to $docversions[], - IMPORTANT: SHIFT $docversions[] INDEX NUMBERS :-( - - index.php: announcement on the home page - remove out-of-date announcements - - news-raw.xhtml: add announcement to list (dup from home page) - - RELEASE.txt: copy new version from built source tree - - mkdir doc/0.{97} - - (cd doc/0.{97} && tar zxf scons-doc-0.{97}.tar.gz) - - svn add doc/0.{97} - - svn commit - - ssh -l scons manam.pair.com - - cd public_html - - mkdir new - - svn co http://scons.tigris.org/svn/scons/scons.org new - - mv production previous && mv new production - - [point your browser to http://www.scons.org/] - - - Update the project pages at tigris.org: - - svn co http://scons.tigris.org/svn/scons/trunk - - cd trunk - - www/project_highlights.html - - www/roadmap.html - - svn commit - - - - - Test downloading from the SourceForge project page - - You may need to wait a good bit; they seem to update - this on half-hour cycles. - - - Test downloading from the web site download page - - - - Add news item to the SourceForge project page - - Pull down "Project => News" - - Click "Submit" - - Fill in the "Subject:" box - - Cut-and-paste the announcement text into the "Details:" box - - Click "submit" - - - - Add news item to the tigris.org project page - - Click "Announcements" - - Click "Add new announcement" - - Double-check the date (probably already set) - - Fill in the "Headline" box - - Fill in the "Body" box (probably short) - - Click "Add new announcement" - - - - Announce to the following mailing lists (template below): - - scons-announce@lists.sourceforge.net - scons-users@lists.sourceforge.net - scons-devel@lists.sourceforge.net - - [right away] - - python-announce@python.org - - [right away, it's moderated and will take - some time to get through] - - linux-announce@news.ornl.gov - - [right away, it's moderated and will take - some time to get through] - - [optional] cons-discuss@gnu.org - - [only if it's a really big announcement, - I said we wouldn't bug this list] - - python-list@python.org - - [wait until business hours so the announcement - hits mailboxes while U.S. workers are active] - - Notify Gentoo Linux of the update - - For now, we will do this by entering a bug report, and - attaching the files in build/gentoo to the report. Go - to: - - http://bugs.gentoo.org/ - - This requires an account (based on your email address) - and a certain amount of Bugzilla-based navigation, - but nothing that's too difficult. - - This is just my best stab at a process that will work - for Gentoo. This process may change if the Gentoo - developers come back and want something submitted in - some other form. - - Notify www.cmtoday.com/contribute.html - - [This guy wants an announcement no more frequently than - once a month, so save it for a future release if it's - been too soon since the previous one.] - - Notify freshmeat.net - - [Wait until the morning so the announcement hits the - main freshmeat.net page while people in the U.S. are - awake and working] - - - - Checkin another change to prepare for development on this branch. - - # Prep the following files to track the changes - # made during the next development cycle - aecp src/CHANGES.txt src/RELEASE.txt - vi src/CHANGES.txt src/RELEASE.txt - - # Optionally, update release numbers in the following: - [optional] aecp HOWTO/change.txt - [optional] vi HOWTO/change.txt - - [optional] aecp HOWTO/release.txt - [optional] vi HOWTO/release.txt - - - - -======================= -Template describe-the-release section: - -IMPORTANT: Release 0.95 contains the following interface changes: - - - XXX - - See the release notes for more information about these changes. - -This release adds the following features: - - - XXX - -This release enhances the following existing features: - - - XXX - -The following fixes have been added: - - - XXX - -Performance has been improved as follows: - - - XXX - -The following changes have been made to the SCons packaging: - - - XXX - -The documentation has been improved: - - - XXX -======================= -Template scons-devel announcement: - -SConspirators-- - -SCons beta release 0.95 is now available for download. - -XXX Template describe-the-release section goes here XXX - -Special thanks to XXX, XXX, and XXX for their contributions to this -release. - - --SK -======================= -Template scons-users + scons-announce announcement: - -Version 0.95 of SCons has been released and is available for download -from the SCons web site: - - http://www.scons.org/ - -Or through the download link at the SCons project page at SourceForge: - - http://sourceforge.net/projects/scons/ - -RPM and Debian packages and a Win32 installer are all available, in -addition to the traditional .tar.gz and .zip files. - - -WHAT'S NEW IN THIS RELEASE? - -XXX Template describe-the-release section goes here XXX - - -ACKNOWLEDGEMENTS - -Special thanks to XXX, XXX, and XXX for their contributions to this -release. - -On behalf of the SCons team, - - --SK -======================= -Template python-announce, linux-announce and python-list announcement: - -SCons is a software construction tool (build tool, or make tool) written -in Python. It is based on the design which won the Software Carpentry -build tool competition in August 2000. - -Version 0.95 of SCons has been released and is available for download -from the SCons web site: - - http://www.scons.org/ - -Or through the download link at the SCons project page at SourceForge: - - http://sourceforge.net/projects/scons/ - -RPM and Debian packages and a Win32 installer are all available, in -addition to the traditional .tar.gz and .zip files. - - -WHAT'S NEW IN THIS RELEASE? - -XXX Template describe-the-release section goes here XXX - - -ABOUT SCONS - -Distinctive features of SCons include: - - - a global view of all dependencies; no multiple passes to get - everything built properly - - configuration files are Python scripts, allowing the full use of a - real scripting language to solve difficult build problems - - a modular architecture allows the SCons Build Engine to be - embedded in other Python software - - the ability to scan files for implicit dependencies (#include files); - - improved parallel build (-j) support that provides consistent - build speedup regardless of source tree layout - - use of MD5 signatures to decide if a file has really changed; no - need to "touch" files to fool make that something is up-to-date - - easily extensible through user-defined Builder and Scanner objects - - build actions can be Python code, as well as external commands - -An scons-users mailing list is available for those interested in getting -started using SCons. You can subscribe at: - - http://lists.sourceforge.net/lists/listinfo/scons-users - -Alternatively, we invite you to subscribe to the low-volume -scons-announce mailing list to receive notification when new versions of -SCons become available: - - http://lists.sourceforge.net/lists/listinfo/scons-announce - - -ACKNOWLEDGEMENTS - -Special thanks to XXX, XXX, and XXX for their contributions to this -release. - -On behalf of the SCons team, - - --SK -- cgit v0.12 From df92a34720a8511f63a12abcd7e0ca2deeeca4d1 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 30 Apr 2018 10:11:01 -0400 Subject: remove obsolete directory www. Was leftover from when SVN was hosted on tigris.org. This directory affected some project pages on tigris.org --- www/bug-submission.html | 234 -------------- .../Administrivia/snippets/HtmlSnippet1.html | 1 - .../Administrivia/snippets/description.txt | 1 - .../Administrivia/snippets/page.xml | 10 - www/cn-project-pages/snippets/page.xml | 16 - www/cn-project-pages/structure.xml | 10 - www/favicon.ico | Bin 1406 -> 0 bytes www/feature-request.html | 99 ------ www/gen_sched_table.py | 51 --- www/index.html | 288 ----------------- www/patch-submission.html | 355 --------------------- www/project_highlights.html | 24 -- www/project_issues.html | 123 ------- www/project_tools.html | 41 --- www/roadmap.html | 173 ---------- www/schedule | 19 -- 16 files changed, 1445 deletions(-) delete mode 100644 www/bug-submission.html delete mode 100644 www/cn-project-pages/Administrivia/snippets/HtmlSnippet1.html delete mode 100644 www/cn-project-pages/Administrivia/snippets/description.txt delete mode 100644 www/cn-project-pages/Administrivia/snippets/page.xml delete mode 100644 www/cn-project-pages/snippets/page.xml delete mode 100644 www/cn-project-pages/structure.xml delete mode 100644 www/favicon.ico delete mode 100644 www/feature-request.html delete mode 100755 www/gen_sched_table.py delete mode 100644 www/index.html delete mode 100644 www/patch-submission.html delete mode 100644 www/project_highlights.html delete mode 100644 www/project_issues.html delete mode 100644 www/project_tools.html delete mode 100644 www/roadmap.html delete mode 100644 www/schedule diff --git a/www/bug-submission.html b/www/bug-submission.html deleted file mode 100644 index abc855b..0000000 --- a/www/bug-submission.html +++ /dev/null @@ -1,234 +0,0 @@ - - -Bug Submission - - - -
    -

    scons
    Bug Submission

    - -

    -You must now -log in -to a tigris.org account -before submitting a new bug report! -

    - -

    -Bugs should be reported at the -"Enter Issue" page. -Please follow the submission guidelines below -to make sure your bug report contains the necessary information. -A more detailed set of submission steps -can be found below. -

    - -

    -The above URL is set up for reporting a bug in SCons itself. -If you are reporting a problem in some other aspect of the SCons Project -(such as the documentation, or any of the web pages), -you must change the Subcomponent field of the submission form -to some other appropriate value. -

    - -
    - -
    - -

    Guidelines for a Useful Bug Report

    - -

    -Your bug will be much more likely to get diagnosed and fixed -if you supply all the necessary information to make it easy to do so. -

    - -
      -
    • - -Log in -to your tigris.org account before submitting a bug report - -

      -If you do not already have a tigris.org account, -register for one at -http://www.tigris.org/servlets/Join. -

      -

      -We no longer accept anonymous bug reports, -due to spambot abuse of the open-door policy. -

      -
    • -
    • -Specify the version of SCons in which you observed the problem -

      -This helps avoid wasted time trying to pinpoint the version, -and also allows us to confirm if a later released version -has already fixed your problem. -

      -
    • -
    • -Provide SConscript files or other configuration that reproduce the problem -

      -If you can, simplify the configuration to just the -minimal subset that demonstrates the problem. -It's much harder to diagnose a problem -if the incorrect behavor is due to -one particular item out of a thousand -in a large configuration. -

      -

      -That said, it's most convenient if you can provide -an actual configuration (set of SConscript files -and/or other input files) -that can be downloaded and run to demonstrate the bug. -The easiest way is to attach a .tar.gz or .zip file -to the bug report. -Note that the tigris.org Issue Tracker -doesn't let you attach a file like this -when you initially submit the report. -You must first create the bug report, -and then attach a file to it as a separate step. -See below for the detailed steps. -

      -

      -If your problem is evident from a few specific SConscript lines, -it's perfectly acceptable just to -paste the lines into the Description field of the bug report. -

      -
    • -
    • -Describe specifically the incorrect behavor you observed -

      -It's best if you can cut and paste the output from SCons, -especially any error messages. -Otherwise, -Vague descriptions like, -"SCons errors out," or "Product XYZ doesn't compile" -are extremely difficult to diagnose, -because the different installed tools on someone else's system -may cause SCons to behave differently -and not demonstrate your bug. -

      -
    • -
    • -Describe what you expected to happen -

      -This isn't always obvious, especially if the -bug does not involve an SCons failure or error message. -Describing the behavior you expected -helps speed up the diagnosis. -

      -
    • -
    - -

    Steps for Submitting a Bug Report

    - -

    -The following guides you step-by-step through the -process of submitting a new SCons bug report. -

    - -

    -NOTE: Creating a bug report with an attached file or files -(such as a .tar.gz or .zip file containing a sample configuration) -is a two-step process in the tigris.org Issue Tracker. -You must first create the bug report, -and then attach the file(s) in a separate step, -as described below. -

    - -
      -
    • -Log in at tigris.org -

      -If you do not already have a tigris.org account, -register for one at -http://www.tigris.org/servlets/Join. -

      -

      -We no longer accept anonymous bug reports, -due to spambot abuse of the open-door policy. -

      -
    • -
    • -Go to the -"Enter issue" page - -

      -By default, the "scons" subcomponent is selected; -if this bug is for a different subcomponent, select that instead. -

      -
    • -
    • -Specify the version of SCons in which you found the bug -

      -

      -
    • -
    • -Specify the Subcomponent (if the bug is not in SCons itself) -

      -The URL two steps above assumes that you're reporting -a bug in the behavior of SCons itself. -If you're reporting a problem in some other aspect of the SCons Project -(such as the documentation, or the packaging), -please change the Subcomponent field to reflect that. -

      -
    • -
    • -Specify the Platform and OS -

      -The Platform field is less important here -(SCons doesn't have many behavioral difference -due to different hardware platforms) -but the OS field is important. -

      -
    • -
    • -Fill in a good Summary line describing the bug -

      -This line is what shows up in summary reports, -so it should be descriptive but not too long. -Avoid overly-general things like "SCons error," etc. -

      -
    • -
    • -Fill in the Description field -

      -This is where you should go into detail -about the configuration, -the exact error you see, -what you expected to happen, etc. -When in doubt, include more information rather than less. -

      -
    • -
    • -Press the "Submit issue" to submit your report -

      -You will now receive a Posting issue page -that gives you the number of the issue you submitted. -

      -
    • -
    • -If you have a .tar.gz, .zip or other file to attach: -
        -
      • -Click the "Attach a file to this issue" link -
      • -
      • -Fill in the "File" field with the path to the file you want to upload -(You can also do this through the Browse... button.) -
      • -
      • -Fill in the Description field -
      • -
      • -Click the "Submit" button -
      • -
      -
    • -
    - -
    - - - diff --git a/www/cn-project-pages/Administrivia/snippets/HtmlSnippet1.html b/www/cn-project-pages/Administrivia/snippets/HtmlSnippet1.html deleted file mode 100644 index a311062..0000000 --- a/www/cn-project-pages/Administrivia/snippets/HtmlSnippet1.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/www/cn-project-pages/Administrivia/snippets/description.txt b/www/cn-project-pages/Administrivia/snippets/description.txt deleted file mode 100644 index 08c477b..0000000 --- a/www/cn-project-pages/Administrivia/snippets/description.txt +++ /dev/null @@ -1 +0,0 @@ -Administrative information and links \ No newline at end of file diff --git a/www/cn-project-pages/Administrivia/snippets/page.xml b/www/cn-project-pages/Administrivia/snippets/page.xml deleted file mode 100644 index 7086b8d..0000000 --- a/www/cn-project-pages/Administrivia/snippets/page.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Administrative links - HtmlSnippet1.html - - - - diff --git a/www/cn-project-pages/snippets/page.xml b/www/cn-project-pages/snippets/page.xml deleted file mode 100644 index 005b144..0000000 --- a/www/cn-project-pages/snippets/page.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - </component> - <component visibility="1" type="Html" order="2"> - <title localize="false" /> - <filename>index.html</filename> - </component> - <component visibility="1" type="Subproject" order="3"> - <title localize="true">Subprojects - - - - diff --git a/www/cn-project-pages/structure.xml b/www/cn-project-pages/structure.xml deleted file mode 100644 index 2c1f3bb..0000000 --- a/www/cn-project-pages/structure.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - g2uyOi - Administrivia - Administrivia - 1 - - - diff --git a/www/favicon.ico b/www/favicon.ico deleted file mode 100644 index 1541e5b..0000000 Binary files a/www/favicon.ico and /dev/null differ diff --git a/www/feature-request.html b/www/feature-request.html deleted file mode 100644 index e644a16..0000000 --- a/www/feature-request.html +++ /dev/null @@ -1,99 +0,0 @@ - - -Feature Requests - - - -
    -

    scons
    Feature Requests

    -
    - -

    -You must now -log in -to a tigris.org account -before submitting a feature request! -

    - -

    -Feature requests should be submitted to the -"Enter Issue" page. -A more detailed set of submission steps -can be found below. -

    - -

    -The "Enter Issue" links on this page, -by default, create a FEATURE request, -a request for completely new SCons functionality. -If your request is for modified behavior -of an already-existing SCons feature, -you may wish to change the Issue type -to an ENHANCEMENT request. -If you're not sure, leave the issue as a FEATURE request; -it can be reclassified in the future, if necessary. - - -

    - -

    Steps for Submitting a Feature Request

    - -

    -The following guides you step-by-step through the -process of submitting a feature request. -

    - -
      -
    • -Log in at tigris.org -

      -If you do not already have a tigris.org account, -register for one at -http://www.tigris.org/servlets/Join. -

      -

      -We no longer accept anonymous feature requests, -due to spambot abuse of the open-door policy. -

      -
    • -
    • -Go to the -"Enter issue" page - -

      -By default, the "scons" subcomponent is selected; -if this bug is for a different subcomponent, select that instead. -

      -
    • -
    • -Fill in a good Summary line describing the feature you're requesting -

      -This line is what shows up in summary reports, -so it should be descriptive but not too long. -

      -
    • -
    • -Fill in the Description field -

      -This is where you should go into detail -about what you'd like to see SCons do differently. -When in doubt, include more information rather than less. -

      -
    • -
    • -Press the "Submit issue" to submit your request -

      -You will now receive a Posting issue page -that gives you the number of the issue you submitted. -If you want to attach a file to your feature request, -you can do so by clicking the -Attach a file to this issue -link and using that page to upload a file. -

      -
    • -
    - -
    - - - diff --git a/www/gen_sched_table.py b/www/gen_sched_table.py deleted file mode 100755 index 85b1b81..0000000 --- a/www/gen_sched_table.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -import sys -import datetime - -months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - -print('') -def row(*cells, **kw): - td = kw.get('tr','td') - print(' ') - for cell in cells: - print(' <%s>%s' % (td,cell,td)) - print(' ') -row('Estimated date', 'Type', 'Comments', tr = 'th') - -if len(sys.argv) > 1: - f = open(sys.argv[1]) -else: f = open('schedule') -now = None -current = 'UNKNOWN' -for line in f: - if line[0] == '#': continue # comment - if line[0] == '=': - date,current = line[1:].strip().split(None, 1) - now = datetime.date(*tuple([int(i) for i in date.split('-')])) - continue - if line[0] == '+': - incr,type,desc = line[1:].strip().split(None,2) - now = now + datetime.timedelta(int(incr)) - else: - print('dunna understand code', line[0]) - sys.exit(1) - #name = current + '.d' + str(now).replace('-','') - date = '%s-%s-%s' % (now.day,months[now.month-1],now.year) - if type == 'ck': - category = 'Ckpt' - elif type == 'rc': - category = 'RC' - else: - category = current = type - row(date, category, desc) -print('
    ') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/www/index.html b/www/index.html deleted file mode 100644 index 203435d..0000000 --- a/www/index.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - -
    - -

    What is SCons?

    - -

    SCons is a next-generation, -cross-platform, build tool. -Think of SCons as an improved -substitute for the classic -Make utility -with integrated functionality -similar to autoconf/automake -and compiler caches such as ccache. -

    - -

    -Unlike build tools that invent their own mini-language -or wedge a scripting language onto some other -configuration file syntax, -SCons configuration files -are actually Python scripts. -The ability to script your build -gives you a tremendous amount of flexibility -to solve complicated build problems -in surprisingly small amounts of maintainable code. -

    - -

    -In short, SCons is an easier, more reliable -and more flexible way to build software. -

    - - - -

    SCons Features

    - -
      - -
    • -Configuration files are Python scripts -

      -This provides much more flexibility for solving -difficult build problems -than traditional build tools. -

      -
    • - -
    • -Reliable, automatic dependency analysis -

      -C, C++ and Fortran are scanned for dependencies, -eliminating the need for a separate make depend step -or a make clean to get all of the dependencies. -Avoids the time waste from debugging phantom problems -that mysteriously disappear after you -make clean; make. -Easily extended to scan for other languages or file types. -

      -
    • - -
    • -Built-in support for multiple languages -

      -C, C++, D, Java, Fortran, Yacc, Lex, Qt and SWIG. -Can also build TeX and LaTeX documents. -Easily extended for other languages or file types. -

      -
    • - -
    • -Cross-platform -

      -Known to work on Linux, -other POSIX systems (AIX, *BSD, HP/UX, IRIX, Solaris), -Windows (NT, 2000, XP), -Mac OS X, -and OS/2. -

      -
    • - -
    • -Fetch files from SCM systems or central directory trees -

      -Built-in support for SCCS, RCS, CVS, BitKeeper and Perforce. -On-disk directory trees can be searched for source files -or pre-built target files. -

      -
    • - -
    • -Support for Microsoft Visual Studio .NET and 2005 -

      -Generates .dsp and .dsw files, -or .sln and .vcproj files, -from the same build configuration used to build on all platforms. -Allows Windows developers to do all the productive -point-and-click debugging they're used to -without having to maintain a separate build configuration -just for Windows. -

      -
    • - -
    • -Reliable detection of file changes using MD5 signatures -

      -Use of traditional file timestamps instead of MD5 can be configured. -

      -
    • - -
    • -Parallel builds -

      -Keeps up to N jobs running simultaneously regardless -of directory hierarchy. -

      -
    • - -
    • -Global view of dependencies -

      -Simplifies builds by eliminating multiple passes -or reording targets to build everything correctly. -

      -
    • - -
    • -Multi-platform configuration (like Autoconf) -

      -Support for finding #include files, -libraries, functions and typedef declarations. -

      -
    • - -
    • -Shared built-file cache -

      -Speeds up multiple builds by allowing developers -to share pre-built targets -(like ccache, but for any type of target file, -not just C/C++ compilation). -

      -
    • - -
    - - - -

    Future

    - -See the Roadmap page. - -
    - - - diff --git a/www/patch-submission.html b/www/patch-submission.html deleted file mode 100644 index a2d9b95..0000000 --- a/www/patch-submission.html +++ /dev/null @@ -1,355 +0,0 @@ - - -Patch Submission - - - -
    -

    scons
    Patch Submission

    -
    - -

    -You must now -log in -to a tigris.org account -before submitting a patch! -

    - -

    -Patches should be submitted to the -"Enter Issue" page. -Please follow the submission guidelines below -to make sure your patch contains the necessary information. -A more detailed set of submission steps -can be found below. -

    - - - -
    - -

    Guidelines for Patch Submission

    - -

    -To try to maintain and improve the quality of SCons releases, -we have some pretty high standards for the quality of patches -that make it into the SCons code base. -This list of guidelines describes how to make it as -easy as possible for your patch to be accepted for integration. -We're still interested in your code -even if you don't follow all of these guidelines, -but then your patch will more than likely sit in the queue -until someone else has time to supply all of the -necessary missing items. -

    - -
      - -
    • - -Please -log in -to your tigris.org account before submitting any patch - -

      -If you do not already have a tigris.org account, -register for one at -http://www.tigris.org/servlets/Join. -

      -

      -We no longer accept anonymous patches, -due to spambot abuse of the open-door policy. -

      -
    • - -
    • -If your patch is extensive, discuss it first on the -scons-dev@scons.org -mailing list - -

      -In fact, for extensive changes, it's a good idea to have this discusssion -before you invest too much time in coding. -It's possible that your idea overlaps with something else -already in the works, -or that your idea is unlikely to be accepted -because it would conflict with planned directions for SCons. -It's much better to find that out, -or get advice on acceptable design choices. -before you've spent a lot of time polishing code -that will be rejected because it doesn't fit -plans for the architecture. -

      -
    • - -
    • -It's better to submit multiple patches with separate bits of functionality than a big patch containing lots of changes -

      -Big, intertwined sets of changes -increase the chances of unintended side effects -that could cause the entire patch to be rejected. -If you submit separate functional changes in separate patches, -those change that meet all the criteria can -still be integrated even -though other pieces might be held up for one reason or another. -

      -
    • - -
    • -Submit your patch in diff -u or diff -c format -

      -In particular, do not submit whole source files, -or diff output without any kind of context information. -It's much more difficult to integrate whole source files -or plain diff output with other changes to -the SCons code base, -especially other changes that might be integrated -after you've submitted your patch. -

      -
    • - -
    • -Your patch must include test case(s) before it can be integrated! -

      -THIS IS THE SINGLE MOST COMMON REASON FOR DELAYS IN INTEGRATING PATCHES -AND THE SINGLE MOST IMPORTANT THING YOU CAN DO TO INCREASE THE -CHANCES OF YOUR PATCH BEING INTEGRATED QUICKLY. -

      -

      -The SCons development methodology requires -that each change be accompanied by one or more -new or modified test cases -that get added to our extensive regression test suite. -This is to make sure that the behavior added by your patch -doesn't get inadvertently broken by other changes in the future. -Patches that fix bugs should contain at least one test case -that demonstrates the behavior being fixed by the patch. -For example, if you're fixing a configuration that causes -SCons to exit with an error and a stack trace, -the test case should trigger that stack trace -when run against the current code. -Patches that add new features or enhancements -should contain test cases that use -the new behavior being added to SCons. -

      -

      -You can do any of the following to supply -test cases with your patch: -

      -
        -
      • -Include actual new or modified SCons test scripts in your patch -

        -This is the best option because it's the easiest to integrate, -and therefore maximizes the chances of your patch being accepted quickly. -(Note that, yes, there's a curve to learning how to -write test scripts in the SCons testing harness. -We're working on documentation to deal with that.) -

        -
      • -
      • -Include a .tar.gz or .zip file containing test configurations -

        -If you can't quite figure out how to deal with the SCons test scripts, -the next best option is to include with your patch an archive file -containing one or more actual test configurations -(SConscript files, input files, etc.). -It will be relatively straightforward for someone integrating your patch, -and who's presumably familiar with the SCons testing harness, -to turn this into an appropriate test script. -Be sure to include a description of how to run your recommended test scenario, -or a script for doing so. -

        -
      • -
      • -Describe how to go about testing the patch -

        -If you really can't cook up a test configuration to include with the patch, -the lowest-common-denominator approach is to just describe -how to go about testing the patch. -Be as specific as possible, -even if you think it should be obvious -how to test the patch. -It might be clear to you while you're writing the code, -but it will still take someone else time -to make sure they understand your intent -and work out the details of how to set up an appropriate case. -The point is you're trying to use your existing knowledge -of the bug being fixed or new feature being added -to make the process of integrating your patch as -simple and quick as possible, -thereby increasing the chance of your patch making it -into the SCons code base. -

        -
      • -
      -

      -If you don't supply any sort of testing -information with your patch, -well, you're still welcome to submit the code. -Just be aware that the patch will likely stay -in the queue until someone has time to reverse-engineer -a test case. -

      -
    • - -
    • -Your patch should not break any existing tests -

      -This almost sounds like it should go without saying, -but the reason we put so much emphasis on test cases -is so that we can make sure functionality doesn't break. -Your patch will almost certainly be run through the -the complete set of checked-in test scripts, -and if any of them break, -your patch will either be rejected outright -or delayed while someone else figures out how to fix it -(or the tests) so that everything works correctly. -You should, of course, avoid this by running your patch -against the regression tests and fixing any problems -before submitting your patch. -If you run your patch against against the regression tests -but can't figure out how to fix all the cases, -the best bet would be to ask the -scons-dev@scons.org -mailing list. -

      -
    • - -
    • -Your patch should include documentation changes -

      -We also insist that changes to the SCons code base -be accompanied by appropriate changes to the documentation. -In practice, right now we make sure the man page is up to date, -and updates to the User's Guide often lag. -

      -

      -Similar to the guidelines above for testing, -if you don't submit changes to the actual man page with your patch, -it's helpful if you at least provide -some suggested text describing your change. -Even if the actual words get rewritten -(usually to make the style consistent with the rest of the man page), -taking the time to provide this -makes the integration easier because -the person integrating the patch doesn't have -to reverse-engineer the intent -of your change to figure out how to describe it. -

      -
    • - -
    - -

    Steps for Submitting a Patch

    - -

    -The following guides you step-by-step through the -process of submitting a patch to SCons. -

    - -

    -NOTE: Attaching a file or files -(such as a .tar.gz or .zip file containing your patch) -is a two-step process in the tigris.org Issue Tracker. -You must first create the patch issue in the Tracker, -and then attach the file(s) in a separate step, -as described below. -

    - -
      - -
    • -Log in at tigris.org -

      -If you do not already have a tigris.org account, -register for one at -http://www.tigris.org/servlets/Join. -

      -

      -We no longer accept anonymous bug reports, -due to spambot abuse of the open-door policy. -

      -
    • - -
    • -Go to the -"Enter issue" page - -

      -By default, the "scons" subcomponent is selected; -if this bug is for a different subcomponent, select that instead. -

      -
    • - -
    • -Specify the version of SCons that you used as a baseline -

      -You can leave this -unspecified-, -in which case the assumption will be that you started with -the code checked in to our Subversion repository -at the time you opened the issue. -

      -
    • - -
    • -Fill in a good Summary line describing the patch -

      -This line is what shows up in summary reports, -so it should be descriptive but not too long. -Avoid overly-general things like "SCons error," etc. -

      -
    • - -
    • -Fill in the Description field -

      -This is where you should describe -the nature of your patch: -the exact error it fixes, -the feature that it adds, -how to go about testing it, -etc. -When in doubt, include more information rather than less. -

      -
    • - -
    • -Press the "Submit issue" to submit your report -

      -You will now receive a Posting issue page -that gives you the number of the issue you submitted. -

      -
    • - -
    • -Click the "Attach a file to this issue" link -

      -

      -
    • - -
    • -Fill in the "File" field with the path to the patch file you want to upload -

      -(You can also do this through the Browse... button.) -

      -
    • - -
    • -Fill in the Description field -

      -

      -
    • - -
    • -Click the "Submit" button to attach your patch file -

      -

      -
    • - -
    - -
    - - - diff --git a/www/project_highlights.html b/www/project_highlights.html deleted file mode 100644 index 98f00bc..0000000 --- a/www/project_highlights.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - -

    -09 Sep 2011: -Release 2.1.0 is now available at the -download page. -

    - - -

    -15 Aug 2010: -Release 2.0.1 is now available at the -download page. -

    - - - - - - diff --git a/www/project_issues.html b/www/project_issues.html deleted file mode 100644 index dc28ca8..0000000 --- a/www/project_issues.html +++ /dev/null @@ -1,123 +0,0 @@ -
    -

    scons
    Issue Tracker -

    -
    - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Query database - Search for issues and defects here or find a specific issue. Always search first before reporting an issue to avoid duplication. -
    -

    - Issue # - -

    -
    -

    - Common queries: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    All Open Issues
    • Bug Reports:
    All Open Non-Open
    • Enhancement:
    All Open Non-Open
    • Feature Requests:
    All Open Non-Open
    • Patches:
    All Open Non-Open
    • Tasks:
    All Open Non-Open
    -
    Enter an issue - - To enter an issue, you must first be a project member and know the component you want to report on. - - - -

    - -Enter: -

    - -
    My issues - View both active issues assigned to you and those that you have entered. -
    My preferences - View and edit your Issue Tracker user settings. -
    Reports - Generate and view issue tracking reports. -
    Configuration options - - Add, view and edit Issue Tracker configuration parameters, including project member permissions, issue tracking groups, project components and subcomponents, etc. -
    -
    diff --git a/www/project_tools.html b/www/project_tools.html deleted file mode 100644 index cf80234..0000000 --- a/www/project_tools.html +++ /dev/null @@ -1,41 +0,0 @@ - -
  • SCons Home Page
  • -
  • SCons Wiki
  • -
    - -
    -
  • Project Membership
  • -
  • Announcements
  • -
  • Discussion lists/fora
  • - -
  • Documents & files
  • -
  • Browse source code
  • -
  • Project metrics
  • -
    - -
    -
  • - -Issue Tracker - -
  • -
    - -
    -
  • Roadmap
  • diff --git a/www/roadmap.html b/www/roadmap.html deleted file mode 100644 index 333d0fd..0000000 --- a/www/roadmap.html +++ /dev/null @@ -1,173 +0,0 @@ - - -scons: Release Roadmap - - - -
    -

    Release Roadmap

    -
    - -
    - -

    Current Releases

    - -

    -The current stable release is 2.1.0, released 09 Sep 2011. -The latest 2.1.x release is 2.1.0, released 09 Sep 2011. -

    - -

    -The latest 1.3.x release is 1.3.1, released 25 July 2010. -

    - -

    Upcoming Releases

    - -

    -Our goal is to meet the dates -for release candidates and the releases themselves; -the beta checkpoint dates are our best guess as this was published, -but they may be adjusted without notice. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Estimated dateTypeComments
    May 2010CkptBeta for 2.0; breaks backward compatibility
    June 2010RCRelease candidate for 2.0.
    June 20102.0Public release that breaks backward compatibility and drops deprecated features
    July 2010CkptBeta for testing new features.
    Aug 2010CkptBeta for testing new features.
    Sept 2010RCRelease candidate for 2.1.
    Oct 20102.1First minor release of v2
    - - - -

    Release Planning

    - -

    Release numbering

    - -

    -Our release numbers are of the form major.minor.revision. -

    - -
      -
    • -Major release (1.0, 2.0, 3.0, etc.) -

      -The major number increments when one of two things happens: -

      -
        -
      • The release knowingly breaks backwards compatibility in some way. -
      • The release knowingly causes a rebuild when you upgrade. -
      -

      -Our goal is that as a user of SCons, -you should always be able to upgrade to a later -release of the same major version -with complete confidence that your build will not break. -We expect that our major releases will be long-lived platforms -with many minor releases to add functionality and fix bugs. -

      -
    • -
    • -Minor release (1.1, 1.2, 1.3, etc.) -

      -Minor numbers increment for releases -that add new functionality and/or bug fixes -to an existing major release. -Any new functionality will never knowingly break backwards compatibility -with any previous minor releases from the same major release. -

      -
    • -
    • -Bug-fix revisions (1.0.1, 1.1.1, 1.2.1, etc.) -

      -Revision numbers are appended and/or incremented -whenever a critical bug fix is necessary -for a major or minor release. -Because most new functionality and bug fixes -will be delivered in minor releases, -we expect that there will be few of these—at most -one per minor release. -

      -
    • -
    • -Release candidates (x.y.z.dyyyymmdd) -

      -A release candidates is a special form of checkpoint -(see below) -that is expected to be the next major or minor release. -If blocking issues show up in the candidate, -another candidate will normally be issued -(potentially delaying the release date), -otherwise the candidate will be repackaged as the major or minor release. -

      -
    • -
    • -Checkpoints (x.y.z.dyyyymmdd) -

      -A checkpoint has a 'dyyymmdd' suffix -and is made every couple of weeks between major or minor releases. -It is intended for beta testing new features -and for ensuring that bug fixes work as intended. -Although existing features from the previous release will not change, -compatibility of features under test is not guaranteed between checkpoints -(i.e., the implementation of the feature may change). -Checkpoints are intended not only to allow for wider testing, -but also to make new features available to users -(who may urgently need one of them) -in advance of them being published in the next major or minor release. -

      -
    • -
    - -
    - - - diff --git a/www/schedule b/www/schedule deleted file mode 100644 index 4b1e9eb..0000000 --- a/www/schedule +++ /dev/null @@ -1,19 +0,0 @@ -#=2008-09-13 1.0.1 -#=2008-10-01 Release candidate for 1.1. -#=2008-10-10 1.1.0 -=2008-12-01 1.1.0 -+4 rc Release candidate for 1.2. (released 7-Dec) -+7 1.2 Second minor release of 1.0. (released 21-Dec) -+17 ck Beta for testing new features. -+14 ck Beta for testing new features. -+7 rc Release candidate for 1.3. -+7 1.3 Third minor release of 1.0. -+7 ck Beta for 2.0; breaks backward compatibility -#+7 ck Beta for 2.0. -+7 rc Release candidate for 2.0. -+7 rc Release candidate for 2.0, if needed. -+7 2.0 Public release that breaks backward compatibility and drops deprecated features -+21 ck Beta for testing new features. -+21 ck Beta for testing new features. -+21 rc Release candidate for 2.1. -+7 2.1 First minor release of 2.0 -- cgit v0.12 From f7340cd04f81e686907dcbb301cc4e385684be35 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 30 Apr 2018 10:27:28 -0400 Subject: [ci skip] Removing obsolete administrative script scons-cdist. Leftover from when SCons used aegis for revision control --- bin/scons-cdist | 272 -------------------------------------------------------- 1 file changed, 272 deletions(-) delete mode 100644 bin/scons-cdist diff --git a/bin/scons-cdist b/bin/scons-cdist deleted file mode 100644 index 58b1bae..0000000 --- a/bin/scons-cdist +++ /dev/null @@ -1,272 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 The SCons Foundation -# -# 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. - -PROG=`basename $0` -NOARGFLAGS="afhlnqrstz" -ARGFLAGS="p:" -ALLFLAGS="${NOARGFLAGS}${ARGFLAGS}" -USAGE="Usage: ${PROG} [-${NOARGFLAGS}] [-p project] change" - -HELP="$USAGE - - -a Update the latest Aegis baseline (aedist) file. - -f Force update, skipping up-front sanity check. - -h Print this help message and exit. - -l Update the local CVS repository. - -n Don't execute, just echo commands. - -p project Set the Aegis project. - -q Quiet, don't print commands before executing them. - -r Rsync the Aegis repository to SourceForge. - -s Update the sourceforge.net CVS repository. - -t Update the tigris.org CVS repository. - -z Update the latest .tar.gz and .zip files. -" - -DO="" -PRINT="echo" -EXECUTE="eval" -SANITY_CHECK="yes" - -while getopts $ALLFLAGS FLAG; do - case $FLAG in - a | l | r | s | t | z ) - DO="${DO}${FLAG}" - ;; - f ) - SANITY_CHECK="no" - ;; - h ) - echo "${HELP}" - exit 0 - ;; - n ) - EXECUTE=":" - ;; - p ) - AEGIS_PROJECT="${OPTARG}" - ;; - q ) - PRINT=":" - ;; - * ) - echo "FLAG = ${FLAG}" >&2 - echo "${USAGE}" >&2 - exit 1 - ;; - esac -done - -shift `expr ${OPTIND} - 1` - -if test "X$1" = "X"; then - echo "${USAGE}" >&2 - exit 1 -fi - -if test "X${AEGIS_PROJECT}" = "X"; then - echo "$PROG: No AEGIS_PROJECT set." >&2 - echo "${USAGE}" >&2 - exit 1 -fi - -if test "X$DO" = "X"; then - DO="alrstz" -fi - -cmd() -{ - $PRINT "$*" - $EXECUTE "$*" -} - -CHANGE=$1 - -if test "X${SANITY_CHECK}" = "Xyes"; then - SCM="cvs" - SCMROOT="/home/scons/CVSROOT/scons" - DELTA=`aegis -l -ter cd ${CHANGE} | sed -n 's/.*, Delta \([0-9]*\)\./\1/p'` - if test "x${DELTA}" = "x"; then - echo "${PROG}: Could not find delta for change ${CHANGE}." >&2 - echo "Has this finished integrating? Change ${CHANGE} not distributed." >&2 - exit 1 - fi - PREV_DELTA=`expr ${DELTA} - 1` - COMMAND="scons-scmcheck -D ${PREV_DELTA} -d q -p ${AEGIS_PROJECT} -s ${SCM} ${SCMROOT}" - $PRINT "${COMMAND}" - OUTPUT=`${COMMAND}` - if test "X${OUTPUT}" != "X"; then - echo "${PROG}: ${SCMROOT} is not up to date:" >&2 - echo "${OUTPUT}" >& 2 - echo "Did you skip any changes? Change ${CHANGE} not distributed." >&2 - exit 1 - fi -fi - -if test X$EXECUTE != "X:" -a "X$SSH_AGENT_PID" = "X"; then - eval `ssh-agent` - ssh-add - trap 'eval `ssh-agent -k`; exit' 0 1 2 3 15 -fi - -cd - -BASELINE=`aesub -p ${AEGIS_PROJECT} -c ${CHANGE} '${Project trunk_name}'` - -TMPBLAE="/tmp/${BASELINE}.ae" -TMPCAE="/tmp/${AEGIS_PROJECT}.C${CHANGE}.ae" - -# Original values for SourceForge. -#SFLOGIN="stevenknight" -#SFHOST="scons.sourceforge.net" -#SFDEST="/home/groups/s/sc/scons/htdocs" - -SCONSLOGIN="scons" -SCONSHOST="manam.pair.com" -#SCONSDEST="public_html/production" -SCONSDEST="public_ftp" - -# -# Copy the baseline .ae to the constant location on SourceForge. -# -case "${DO}" in -*a* ) - cmd "aedist -s -bl -p ${AEGIS_PROJECT} > ${TMPBLAE}" - cmd "scp ${TMPBLAE} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/${BASELINE}.ae" - cmd "rm ${TMPBLAE}" - ;; -esac - -# -# Copy the latest .tar.gz and .zip files to the constant location on -# SourceForge. -# -case "${DO}" in -*z* ) - BUILD_DIST=`aegis -p ${AEGIS_PROJECT} -cd -bl`/build/dist - SCONS_SRC_TAR_GZ=`echo ${AEGIS_PROJECT} | sed 's/scons./scons-src-/'`*.tar.gz - SCONS_SRC_ZIP=`echo ${AEGIS_PROJECT} | sed 's/scons./scons-src-/'`*.zip - cmd "scp ${BUILD_DIST}/${SCONS_SRC_TAR_GZ} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/scons-src-latest.tar.gz" - cmd "scp ${BUILD_DIST}/${SCONS_SRC_ZIP} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/scons-src-latest.zip" -esac - -# -# Sync Aegis tree with SourceForge. -# -# Cribbed and modified from Peter Miller's same-named script in -# /home/groups/a/ae/aegis/aegis at SourceForge. -# -# Guide to what this does with rsync: -# -# --rsh=ssh use ssh for the transfer -# -l copy symlinks as symlinks -# -p preserve permissions -# -r recursive -# -t preserve times -# -z compress data -# --stats file transfer statistics -# --exclude exclude files matching the pattern -# --delete delete files that don't exist locally -# --delete-excluded delete files that match the --exclude patterns -# --progress show progress during the transfer -# -v verbose -# -# We no longer use the --stats option. -# -case "${DO}" in -*r* ) - LOCAL=/home/scons/scons - REMOTE=/home/groups/s/sc/scons/scons - cmd "/usr/bin/rsync --rsh='ssh -l stevenknight' \ - -l -p -r -t -z \ - --exclude build \ - --exclude '*,D' \ - --exclude '*.pyc' \ - --exclude aegis.log \ - --exclude '.sconsign*' \ - --delete --delete-excluded \ - --progress -v \ - ${LOCAL}/. scons.sourceforge.net:${REMOTE}/." - ;; -esac - -# -# Sync the CVS tree with the local repository. -# -case "${DO}" in -*l* ) - ( - export CVSROOT=/home/scons/CVSROOT/scons - #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/baldmt.com/scons" - cmd "ae-cvs-ci ${AEGIS_PROJECT} ${CHANGE}" - ) - ;; -esac - -# -# Sync the Subversion tree with Tigris.org. -# -case "${DO}" in -*t* ) - ( - SVN=http://scons.tigris.org/svn/scons - case ${AEGIS_PROJECT} in - scons.0.96 ) - SVN_URL=${SVN}/branches/core - ;; - scons.0.96.513 ) - SVN_URL=${SVN}/branches/sigrefactor - ;; - * ) - echo "$PROG: Don't know SVN branch for '${AEGIS_PROJECT}'" >&2 - exit 1 - ;; - esac - SVN_CO_FLAGS="--username stevenknight" - #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/tigris.org/scons" - cmd "ae-svn-ci ${AEGIS_PROJECT} ${CHANGE} ${SVN_URL} ${SVN_CO_FLAGS}" - ) - ;; -esac - -# -# Sync the CVS tree with SourceForge. -# -case "${DO}" in -*s* ) - ( - export CVS_RSH=ssh - export CVSROOT=:ext:stevenknight@scons.cvs.sourceforge.net:/cvsroot/scons - #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/sourceforge.net/scons" - cmd "ae-cvs-ci ${AEGIS_PROJECT} ${CHANGE}" - ) - ;; -esac - -# -# Send the change .ae to the scons-aedist mailing list -# -# The subject requires editing by hand... -# -#aedist -s -p ${AEGIS_PROJECT} ${CHANGE} > ${TMPCAE} -#aegis -l -p ${AEGIS_PROJECT} -c ${CHANGE} cd | -# pine -attach_and_delete ${TMPCAE} scons-aedist@lists.sourceforge.net -- cgit v0.12 From 3ce0e0e670245e16bf477cb31d48b770844cf1bb Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 2 May 2018 08:24:30 -0600 Subject: Module used an undefined class name ArchDefinition is defined, then ArchitectureDefinition is used. It is not clear this is actually used (the problem was flagged by Sphinx, which was walking through every file), but let's not leave it broken. Signed-off-by: Mats Wichmann --- src/engine/SCons/Tool/MSCommon/arch.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/SCons/Tool/MSCommon/arch.py b/src/engine/SCons/Tool/MSCommon/arch.py index 9bcc221..ad17959 100644 --- a/src/engine/SCons/Tool/MSCommon/arch.py +++ b/src/engine/SCons/Tool/MSCommon/arch.py @@ -37,22 +37,22 @@ class ArchDefinition(object): self.synonyms = synonyms SupportedArchitectureList = [ - ArchitectureDefinition( + ArchDefinition( 'x86', ['i386', 'i486', 'i586', 'i686'], ), - ArchitectureDefinition( + ArchDefinition( 'x86_64', ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], ), - ArchitectureDefinition( + ArchDefinition( 'ia64', ['IA64'], ), - ArchitectureDefinition( + ArchDefinition( 'arm', ['ARM'], ), -- cgit v0.12 From a7ba3605768e5cd2f797d11913a23726f55f2eba Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 8 May 2018 21:50:14 -0400 Subject: Fix issue where at least test/Java/JAR.py test fail only on windows because source file test1.class is first converted to a File object, and then has toupper() called on it (via _my_normcase only on windows), and the result of which is the source list is empty which causes the fake jar part of the test to fail --- src/engine/SCons/Tool/jar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index 5e3711a..6e319c1 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -140,7 +140,7 @@ def Jar(env, target = None, source = [], *args, **kw): # source for jar, otherwise turn it into a class file then # return the source def file_to_class(s): - if(str(_my_normcase(s)).endswith(java_suffix)): + if _my_normcase(str(s)).endswith(java_suffix): return env.JavaClassFile(source = s, *args, **kw) else: return [env.fs.File(s)] -- cgit v0.12 From 96d638297a71925053b164578506f44deac12acf Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 11 May 2018 17:26:39 -0400 Subject: change test to skip initializing any tools in DefaulEnvironment. Should speed up test on windows a bit --- test/Java/JAR.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/Java/JAR.py b/test/Java/JAR.py index d62696c..faf01a3 100644 --- a/test/Java/JAR.py +++ b/test/Java/JAR.py @@ -52,6 +52,7 @@ sys.exit(0) """) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(tools = ['jar'], JAR = r'%(_python_)s myjar.py') env.Jar(target = 'test1.jar', source = 'test1.class') @@ -70,6 +71,7 @@ test.must_match('test1.jar', "test1.class\nline 3\n", mode='r') if os.path.normcase('.class') == os.path.normcase('.CLASS'): test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(tools = ['jar'], JAR = r'%(_python_)s myjar.py') env.Jar(target = 'test2.jar', source = 'test2.CLASS') @@ -95,6 +97,7 @@ sys.exit(0) """) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(tools = ['jar'], JAR = r'%(_python_)s myjar2.py', JARFLAGS='cvf') @@ -126,6 +129,7 @@ where_jar = test.java_where_jar() test.file_fixture('wrapper_with_args.py') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) foo = Environment(tools = ['javac', 'jar'], JAVAC = r'%(where_javac)s', JAR = r'%(where_jar)s') @@ -249,6 +253,8 @@ test.subdir('testdir2', # simple SConstruct which passes the 3 .java as source # and extracts the jars back to classes test.write(['testdir2', 'SConstruct'], """ +DefaultEnvironment(tools=[]) + foo = Environment() foo.Jar(target = 'foobar', source = [ 'com/javasource/JavaFile1.java', @@ -345,6 +351,8 @@ test.subdir('listOfLists', # test varient dir and lists of lists test.write(['listOfLists', 'SConstruct'], """ +DefaultEnvironment(tools=[]) + foo = Environment() foo.VariantDir('build', 'src', duplicate=0) foo.VariantDir('test', '../manifest_dir', duplicate=0) @@ -435,6 +443,8 @@ test.subdir('testdir3', # Create the jars then extract them back to check contents test.write(['testdir3', 'SConstruct'], """ +DefaultEnvironment(tools=[]) + foo = Environment() bar = foo.Clone() foo.Java(target = 'classes', source = 'com/sub/foo') -- cgit v0.12 From f48da72a3156d90bd6d58a840df1be2b013c5e9b Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 11 May 2018 17:28:57 -0400 Subject: Add some reasonable default windows paths to find java. --- testing/framework/TestSCons.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index ba4e06b..d847845 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -692,20 +692,29 @@ class TestSCons(TestCommon): env = SCons.Environment.Environment() self._java_env[version] = env - if version: - patterns = [ - '/usr/java/jdk%s*/bin' % version, - '/usr/lib/jvm/*-%s*/bin' % version, - '/usr/local/j2sdk%s*/bin' % version, - ] + if sys.platform == 'win32': + patterns = [ + 'C:/Program Files/Java/jdk%s*/bin'%version, + ] + else: + patterns = [ + '/usr/java/jdk%s*/bin' % version, + '/usr/lib/jvm/*-%s*/bin' % version, + '/usr/local/j2sdk%s*/bin' % version, + ] java_path = self.paths(patterns) + [env['ENV']['PATH']] else: - patterns = [ - '/usr/java/latest/bin', - '/usr/lib/jvm/*/bin', - '/usr/local/j2sdk*/bin', - ] + if sys.platform == 'win32': + patterns = [ + 'C:/Program Files/Java/jdk*/bin', + ] + else: + patterns = [ + '/usr/java/latest/bin', + '/usr/lib/jvm/*/bin', + '/usr/local/j2sdk*/bin', + ] java_path = self.paths(patterns) + [env['ENV']['PATH']] env['ENV']['PATH'] = os.pathsep.join(java_path) -- cgit v0.12 From 5efd04aaed4c9700a606e11733656225c5d933e8 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 11 May 2018 17:44:15 -0400 Subject: Add wildcards so we'll catch both 32 and 64 bit versions of java on windows. --- testing/framework/TestSCons.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index d847845..c32e06a 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -695,7 +695,7 @@ class TestSCons(TestCommon): if version: if sys.platform == 'win32': patterns = [ - 'C:/Program Files/Java/jdk%s*/bin'%version, + 'C:/Program Files*/Java/jdk%s*/bin'%version, ] else: patterns = [ @@ -707,7 +707,7 @@ class TestSCons(TestCommon): else: if sys.platform == 'win32': patterns = [ - 'C:/Program Files/Java/jdk*/bin', + 'C:/Program Files*/Java/jdk*/bin', ] else: patterns = [ -- cgit v0.12 From 14c4bbd1d9e5f87a7964fd69a60e7d21d69319ea Mon Sep 17 00:00:00 2001 From: maiphi <39464783+maiphi@users.noreply.github.com> Date: Sun, 20 May 2018 19:42:52 +0200 Subject: Make class SCons.Subst.Literal hashable When using Python 3, substitution of Literal objects requires the objects to be hashable, otherwise an error will be thrown. The hash value is that of the lstr member. --- src/engine/SCons/Subst.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py index 68d247f..0b4190b 100644 --- a/src/engine/SCons/Subst.py +++ b/src/engine/SCons/Subst.py @@ -86,6 +86,9 @@ class Literal(object): def __neq__(self, other): return not self.__eq__(other) + def __hash__(self): + return hash(self.lstr) + class SpecialAttrWrapper(object): """This is a wrapper for what we call a 'Node special attribute.' This is any of the attributes of a Node that we can reference from -- cgit v0.12 From 8d504fd00127bdcffcf436993aa8e8c89011c63e Mon Sep 17 00:00:00 2001 From: maiphi <39464783+maiphi@users.noreply.github.com> Date: Sun, 20 May 2018 20:13:45 +0200 Subject: Update Changes.txt with hashable Literal --- src/CHANGES.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index a4590d0..f9cb5c4 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,6 +7,10 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE + From Philipp Maierhöfer + - Added a __hash__ method to the class Scons.Subst.Literal. Required when substituting Literal + objects when SCons runs with Python 3. + From Richard West: - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. Allows easier debugging within Visual Studio -- cgit v0.12 From 21335a0ef0463d51ecc9ea9cc86c3388415f12aa Mon Sep 17 00:00:00 2001 From: Philipp Maierhoefer Date: Mon, 21 May 2018 11:43:56 +0200 Subject: Added a test case for expansion of Literal objects in ${_concat()} --- test/Subst/Literal.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/Subst/Literal.py diff --git a/test/Subst/Literal.py b/test/Subst/Literal.py new file mode 100644 index 0000000..38ed3c4 --- /dev/null +++ b/test/Subst/Literal.py @@ -0,0 +1,52 @@ +#!/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 Literal objects expand correctly in ${_concat()}. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +env = Environment(PRE='pre=', MID=Literal('\$$ORIGIN'), SUF='') +print(env.subst('${_concat(PRE, MID, SUF, __env__)}')) +""") + +test.run() + +expect = """\ +pre=\$ORIGIN +""" + +test.run(arguments='-Q -q', stdout=expect) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From 75059479aff3cd50b9e547858a56c39eabeb111f Mon Sep 17 00:00:00 2001 From: maiphi <39464783+maiphi@users.noreply.github.com> Date: Tue, 22 May 2018 13:53:23 +0200 Subject: Added missing pass_test() to Literal.py test --- test/Subst/Literal.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Subst/Literal.py b/test/Subst/Literal.py index 38ed3c4..dec243d 100644 --- a/test/Subst/Literal.py +++ b/test/Subst/Literal.py @@ -45,6 +45,8 @@ pre=\$ORIGIN test.run(arguments='-Q -q', stdout=expect) +test.pass_test() + # Local Variables: # tab-width:4 # indent-tabs-mode:nil -- cgit v0.12 From 880fce9c7982397fe16f49160591199d1e117c80 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Thu, 24 May 2018 21:55:52 -0500 Subject: issue-3130: udpated travis file to update apt-get. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9d8b96d..a1f58b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ dist: trusty language: python +addons: + apt: + update: true + install: - ./.travis/install.sh -- cgit v0.12 From c299f365221f80e2325d391e9c15f4c1e2632486 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Wed, 27 Dec 2017 17:46:38 -0500 Subject: fixed swig tests to work on windows --- test/SWIG/SWIGPATH.py | 5 +++-- test/SWIG/implicit-dependencies.py | 5 +++-- test/SWIG/live.py | 11 ++++++++--- test/SWIG/recursive-includes-cpp.py | 6 +++++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/test/SWIG/SWIGPATH.py b/test/SWIG/SWIGPATH.py index 55e8d7e..d516c0c 100644 --- a/test/SWIG/SWIGPATH.py +++ b/test/SWIG/SWIGPATH.py @@ -40,7 +40,8 @@ python = test.where_is('python') if not python: test,skip_test('Can not find installed "python", skipping test.\n') - +swig = swig.replace('\\','/') +python = python.replace('\\','/') test.subdir('inc1', 'inc2') test.write(['inc2', 'dependency.i'], """\ @@ -95,4 +96,4 @@ test.pass_test() # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: +# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file diff --git a/test/SWIG/implicit-dependencies.py b/test/SWIG/implicit-dependencies.py index 465a0d6..8664bf6 100644 --- a/test/SWIG/implicit-dependencies.py +++ b/test/SWIG/implicit-dependencies.py @@ -40,7 +40,8 @@ python = test.where_is('python') if not python: test.skip_test('Can not find installed "python", skipping test.\n') - +swig = swig.replace('\\','/') +python = python.replace('\\','/') test.write("dependency.i", """\ %module dependency """) @@ -75,4 +76,4 @@ test.pass_test() # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: +# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file diff --git a/test/SWIG/live.py b/test/SWIG/live.py index 05971aa..684cff1 100644 --- a/test/SWIG/live.py +++ b/test/SWIG/live.py @@ -48,9 +48,14 @@ if not swig: python, python_include, python_libpath, python_lib = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') +Python_h = python_include + '/Python.h' if not os.path.exists(Python_h): test.skip_test('Can not find %s, skipping test.\n' % Python_h) +swig = swig.replace('\\','/') +python = python.replace('\\','/') +python_include = python_include.replace('\\','/') +python_libpath = python_libpath.replace('\\','/') +python_lib = python_lib.replace('\\','/') # handle testing on other platforms: ldmodule_prefix = '_' @@ -142,7 +147,7 @@ test.up_to_date(arguments = ldmodule_prefix+'foo' + _dll) test.run(arguments = ldmodule_prefix+'bar' + _dll) -test.must_match('wrapper.out', "wrapper.py\n") +test.must_match('wrapper.out', "wrapper.py" + os.linesep) test.run(program = python, stdin = """\ from __future__ import print_function @@ -164,4 +169,4 @@ test.pass_test() # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: +# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index dbcac6d..0baa195 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -30,6 +30,7 @@ in cases of recursive inclusion. """ import os +import sys import TestSCons from SCons.Defaults import DefaultEnvironment @@ -37,6 +38,9 @@ DefaultEnvironment( tools = [ 'swig' ] ) test = TestSCons.TestSCons() +if sys.platform == 'win32': + test.skip_test('test not setup for windows, skipping test') + # Check for prerequisites of this test. for pre_req in ['swig', 'python']: if not test.where_is(pre_req): @@ -124,4 +128,4 @@ test.pass_test() # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: +# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file -- cgit v0.12 From 14083d5ab0b909eb8626fe8e94b47cfd598f45fc Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 09:50:56 -0500 Subject: pr-3052: updated swig test to work for windows. --- test/SWIG/recursive-includes-cpp.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 0baa195..e69b5b9 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -30,7 +30,6 @@ in cases of recursive inclusion. """ import os -import sys import TestSCons from SCons.Defaults import DefaultEnvironment @@ -38,9 +37,6 @@ DefaultEnvironment( tools = [ 'swig' ] ) test = TestSCons.TestSCons() -if sys.platform == 'win32': - test.skip_test('test not setup for windows, skipping test') - # Check for prerequisites of this test. for pre_req in ['swig', 'python']: if not test.where_is(pre_req): @@ -86,7 +82,7 @@ if sys.platform == 'darwin': env['LIBS']=['python%d.%d'%(sys.version_info[0],sys.version_info[1])] env.SharedLibrary( - 'mod.so', + 'mod', [ "mod.i", "main.c", -- cgit v0.12 From 100f49aa06ff3e8ed09fb77edc787f35d2de10fc Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 25 May 2018 11:26:02 -0700 Subject: Handful of changes to get clang tests passing on windows. Use glob to find other possible default mingw install paths. mingw is needed in many cases on windows to compile/archive. --- src/engine/SCons/Tool/clang.py | 3 ++- src/engine/SCons/Tool/mingw.py | 21 ++++++++++++++++++++- test/Clang/clang_default_environment.py | 5 ++--- test/Clang/clang_shared_library.py | 12 +++++++----- test/Clang/clang_specific_environment.py | 1 + test/Clang/clang_static_library.py | 3 ++- test/Clang/clangxx_default_environment.py | 5 ++--- test/Clang/clangxx_shared_library.py | 11 +++++++---- test/Clang/clangxx_specific_environment.py | 3 ++- test/Clang/clangxx_static_library.py | 3 ++- 10 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index c51e753..cbb48cb 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -56,7 +56,8 @@ def generate(env): if env['PLATFORM'] == 'win32': # Ensure that we have a proper path for clang - clang = SCons.Tool.find_program_path(env, compilers[0], default_paths=get_clang_install_dirs(env['PLATFORM'])) + clang = SCons.Tool.find_program_path(env, compilers[0], + default_paths=get_clang_install_dirs(env['PLATFORM'])) if clang: clang_bin_dir = os.path.dirname(clang) env.AppendENVPath('PATH', clang_bin_dir) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index f2e9442..738460d 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -35,6 +35,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path +import glob import SCons.Action import SCons.Builder @@ -115,10 +116,28 @@ res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) # This is what we search for to find mingw: -key_program = 'mingw32-gcc' +# key_program = 'mingw32-gcc' +key_program = 'mingw32-make' + + + +def find_version_specific_mingw_paths(): + """ + One example of default mingw install paths is: + C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev2\mingw64\bin + + Use glob'ing to find such and add to mingw_paths + """ + new_paths = glob.glob(r"C:\mingw-w64\*\mingw64\bin") + + return new_paths def generate(env): + global mingw_paths + # Check for reasoanble mingw default paths + mingw_paths +=find_version_specific_mingw_paths() + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=mingw_paths) if mingw: mingw_bin_dir = os.path.dirname(mingw) diff --git a/test/Clang/clang_default_environment.py b/test/Clang/clang_default_environment.py index 4ac1c68..5ebd839 100644 --- a/test/Clang/clang_default_environment.py +++ b/test/Clang/clang_default_environment.py @@ -35,9 +35,8 @@ if not test.where_is('clang'): ## This will likely NOT use clang test.write('SConstruct', """ -env = Environment() -if env['CC'] != 'clang': - env['CC'] = 'clang' +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang','link']) env.Program('foo.c') """) diff --git a/test/Clang/clang_shared_library.py b/test/Clang/clang_shared_library.py index 5e4d36f..9af3770 100644 --- a/test/Clang/clang_shared_library.py +++ b/test/Clang/clang_shared_library.py @@ -33,20 +33,22 @@ test = TestSCons.TestSCons() if not test.where_is('clang'): test.skip_test("Could not find 'clang', skipping test.\n") -platform = Base()['PLATFORM'] +base = Base() +platform = base['PLATFORM'] if platform == 'posix': - filename = 'foo.os' + filename_options = ['foo.os'] libraryname = 'libfoo.so' elif platform == 'darwin': - filename = 'foo.os' + filename_options = ['foo.os'] libraryname = 'libfoo.dylib' elif platform == 'win32': - filename = 'foo.obj' + filename_options = ['foo.obj','foo.os'] libraryname = 'foo.dll' else: test.fail_test() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment(tools=['clang', 'link']) env.SharedLibrary('foo', 'foo.c') """) @@ -59,7 +61,7 @@ int bar() { test.run() -test.must_exist(test.workpath(filename)) +test.must_exist_one_of([test.workpath(f) for f in filename_options]) test.must_exist(test.workpath(libraryname)) test.pass_test() diff --git a/test/Clang/clang_specific_environment.py b/test/Clang/clang_specific_environment.py index 7266a9f..81991f0 100644 --- a/test/Clang/clang_specific_environment.py +++ b/test/Clang/clang_specific_environment.py @@ -33,6 +33,7 @@ if not test.where_is('clang'): test.skip_test("Could not find 'clang', skipping test.\n") test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment(tools=['clang', 'link']) env.Program('foo.c') """) diff --git a/test/Clang/clang_static_library.py b/test/Clang/clang_static_library.py index 39e9931..7dedc6f 100644 --- a/test/Clang/clang_static_library.py +++ b/test/Clang/clang_static_library.py @@ -33,7 +33,8 @@ if not test.where_is('clang'): test.skip_test("Could not find 'clang', skipping test.\n") test.write('SConstruct', """\ -env = Environment(tools=['clang', 'ar']) +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang', 'ar']) env.StaticLibrary('foo', 'foo.c') """) diff --git a/test/Clang/clangxx_default_environment.py b/test/Clang/clangxx_default_environment.py index beef1e5..5e46404 100644 --- a/test/Clang/clangxx_default_environment.py +++ b/test/Clang/clangxx_default_environment.py @@ -35,9 +35,8 @@ if not test.where_is('clang'): ## This will likely NOT use clang++. test.write('SConstruct', """\ -env = Environment() -if env['CXX'] != 'clang++': - env['CXX'] = 'clang++' +DefaultEnvironment(tools=[]) +env = Environment(tools=['clangxx','link']) env.Program('foo.cpp') """) diff --git a/test/Clang/clangxx_shared_library.py b/test/Clang/clangxx_shared_library.py index d6337ba..6240299 100644 --- a/test/Clang/clangxx_shared_library.py +++ b/test/Clang/clangxx_shared_library.py @@ -36,18 +36,21 @@ if not test.where_is('clang'): platform = Base()['PLATFORM'] if platform == 'posix': - filename = 'foo.os' + filename_options = ['foo.os'] libraryname = 'libfoo.so' elif platform == 'darwin': - filename = 'foo.os' + filename_options = ['foo.os'] libraryname = 'libfoo.dylib' elif platform == 'win32': - filename = 'foo.obj' + filename_options = ['foo.obj','foo.os'] libraryname = 'foo.dll' else: test.fail_test() + + test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment(tools=['clang++', 'link']) env.SharedLibrary('foo', 'foo.cpp') """) @@ -60,7 +63,7 @@ int bar() { test.run() -test.must_exist(test.workpath(filename)) +test.must_exist_one_of([test.workpath(f) for f in filename_options]) test.must_exist(test.workpath(libraryname)) test.pass_test() diff --git a/test/Clang/clangxx_specific_environment.py b/test/Clang/clangxx_specific_environment.py index 773fa94..35540bb 100644 --- a/test/Clang/clangxx_specific_environment.py +++ b/test/Clang/clangxx_specific_environment.py @@ -33,7 +33,8 @@ if not test.where_is('clang'): test.skip_test("Could not find 'clang++', skipping test.\n") test.write('SConstruct', """\ -env = Environment(tools=['clang++', 'link']) +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang++', 'link']) env.Program('foo.cpp') """) diff --git a/test/Clang/clangxx_static_library.py b/test/Clang/clangxx_static_library.py index 77ea58e..c768deb 100644 --- a/test/Clang/clangxx_static_library.py +++ b/test/Clang/clangxx_static_library.py @@ -33,7 +33,8 @@ if not test.where_is('clang'): test.skip_test("Could not find 'clang++', skipping test.\n") test.write('SConstruct', """\ -env = Environment(tools=['clang++', 'ar']) +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang++', 'ar']) env.StaticLibrary('foo', 'foo.cpp') """) -- cgit v0.12 From 49a553beb72d3192f77e3ae39b449daed086c22f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 17 Jan 2018 23:35:26 -0500 Subject: add another packaging build dir to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4050468..1807b29 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ __pycache__/ build/** bootstrap/** .idea/ +src/build/** # Translations -- cgit v0.12 From acb358617aecd3d7ba974585c386a1e31ae40f71 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 17 Jan 2018 23:37:06 -0500 Subject: Added find_program_path() to SCons.Tool. Logic was in mingw. Use this to allow tools to search for normal SCOns paths, and then specify alternative default install paths for such tools. This should resolve clang failing on appveyor on windows --- src/engine/SCons/Tool/__init__.py | 29 +++++++++++++++++++++++++++++ src/engine/SCons/Tool/clang.py | 7 +++++++ src/engine/SCons/Tool/clangxx.py | 11 ++++++++++- src/engine/SCons/Tool/mingw.py | 31 +++++++------------------------ 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 42f84e1..0b340c0 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -1247,6 +1247,35 @@ def tool_list(platform, env): return [x for x in tools if x] + +def find_program_path(env, key_program, default_paths=[]): + """ + Find the location of key_program and then return the path it was located at. + Checking the default install locations. + Mainly for windows where tools aren't all installed in /usr/bin,etc + :param env: Current Environment() + :param key_program: Program we're using to locate the directory to add to PATH. + """ + # First search in the SCons path + path=env.WhereIs(key_program) + if (path): + return path + # then the OS path: + path=SCons.Util.WhereIs(key_program) + if (path): + return path + + # If that doesn't work try default location for mingw + save_path = env['ENV']['PATH'] + for p in default_paths: + env.AppendENVPath('PATH',p) + path = env.WhereIs(key_program) + if not path: + env['ENV']['PATH']=save_path + return path + + + # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index 177e6b2..a6bae43 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -52,6 +52,13 @@ def generate(env): """Add Builders and construction variables for clang to an Environment.""" SCons.Tool.cc.generate(env) + if env['PLATFORM'] == 'win32': + # Ensure that we have a proper path for clang + clang = SCons.Tool.find_program_path(env, compilers[0], default_paths=[r'C:\Program Files\LLVM\bin',]) + if clang: + clang_bin_dir = os.path.dirname(clang) + env.AppendENVPath('PATH', clang_bin_dir) + env['CC'] = env.Detect(compilers) or 'clang' if env['PLATFORM'] in ['cygwin', 'win32']: env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') diff --git a/src/engine/SCons/Tool/clangxx.py b/src/engine/SCons/Tool/clangxx.py index dd501af..63f1b0d 100644 --- a/src/engine/SCons/Tool/clangxx.py +++ b/src/engine/SCons/Tool/clangxx.py @@ -66,13 +66,22 @@ def generate(env): env['SHOBJSUFFIX'] = '.pic.o' elif env['PLATFORM'] == 'sunos': env['SHOBJSUFFIX'] = '.pic.o' + elif env['PLATFORM'] == 'win32': + # Ensure that we have a proper path for clang++ + clangxx = SCons.Tool.find_program_path(env, compilers[0], default_paths=[r'C:\Program Files\LLVM\bin',]) + if clangxx: + clangxx_bin_dir = os.path.dirname(clangxx) + env.AppendENVPath('PATH', clangxx_bin_dir) + # determine compiler version if env['CXX']: pipe = SCons.Action._subproc(env, [env['CXX'], '--version'], stdin='devnull', stderr='devnull', stdout=subprocess.PIPE) - if pipe.wait() != 0: return + if pipe.wait() != 0: + return + # clang -dumpversion is of no use line = pipe.stdout.readline() if sys.version_info[0] > 2: diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index 778db3c..4121902 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -42,26 +42,6 @@ import SCons.Defaults import SCons.Tool import SCons.Util -# This is what we search for to find mingw: -key_program = 'mingw32-gcc' - -def find(env): - # First search in the SCons path - path=env.WhereIs(key_program) - if (path): - return path - # then the OS path: - path=SCons.Util.WhereIs(key_program) - if (path): - return path - - # If that doesn't work try default location for mingw - save_path=env['ENV']['PATH'] - env.AppendENVPath('PATH',r'c:\MinGW\bin') - path =env.WhereIs(key_program) - if not path: - env['ENV']['PATH']=save_path - return path def shlib_generator(target, source, env, for_signature): cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) @@ -126,12 +106,15 @@ res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', source_scanner=SCons.Tool.SourceFileScanner) SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) +# This is what we search for to find mingw: +key_program = 'mingw32-gcc' + + def generate(env): - mingw = find(env) + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=[r'c:\MinGW\bin',]) if mingw: - dir = os.path.dirname(mingw) - env.PrependENVPath('PATH', dir ) - + mingw_bin_dir = os.path.dirname(mingw) + env.AppendENVPath('PATH', mingw_bin_dir) # Most of mingw is the same as gcc and friends... gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas', 'gfortran', 'm4'] -- cgit v0.12 From 00ade7bc85441688369efef881d915804191e0eb Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 14 Feb 2018 11:44:59 -0500 Subject: Update default paths for clang compilers. This should hopefully fix some appveyor failures. --- src/engine/SCons/Tool/clang.py | 5 ++++- src/engine/SCons/Tool/clang/__init__.py | 16 ++++++++++++++++ src/engine/SCons/Tool/clangxx.py | 4 +++- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/engine/SCons/Tool/clang/__init__.py diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index a6bae43..aba5598 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -45,6 +45,8 @@ import sys import SCons.Util import SCons.Tool.cc +from SCons.Tool.clang import get_clang_install_dirs + compilers = ['clang'] @@ -54,7 +56,7 @@ def generate(env): if env['PLATFORM'] == 'win32': # Ensure that we have a proper path for clang - clang = SCons.Tool.find_program_path(env, compilers[0], default_paths=[r'C:\Program Files\LLVM\bin',]) + clang = SCons.Tool.find_program_path(env, compilers[0], default_paths=get_clang_install_dirs(env['PLATFORM'])) if clang: clang_bin_dir = os.path.dirname(clang) env.AppendENVPath('PATH', clang_bin_dir) @@ -64,6 +66,7 @@ def generate(env): env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') else: env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') + # determine compiler version if env['CC']: #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], diff --git a/src/engine/SCons/Tool/clang/__init__.py b/src/engine/SCons/Tool/clang/__init__.py new file mode 100644 index 0000000..775ba80 --- /dev/null +++ b/src/engine/SCons/Tool/clang/__init__.py @@ -0,0 +1,16 @@ +""" +Common routines and data for clang tools +""" + +clang_win32_dirs = [ + r'C:\Program Files\LLVM\bin', + r'C:\cygwin64\bin', + r'C:\msys64', + r'C:\cygwin\bin', + r'C:\msys', +] + +def get_clang_install_dirs(platform): + if platform == 'win32': + return clang_win32_dirs + else return [] \ No newline at end of file diff --git a/src/engine/SCons/Tool/clangxx.py b/src/engine/SCons/Tool/clangxx.py index 63f1b0d..2bf4cf4 100644 --- a/src/engine/SCons/Tool/clangxx.py +++ b/src/engine/SCons/Tool/clangxx.py @@ -46,6 +46,8 @@ import sys import SCons.Tool import SCons.Util import SCons.Tool.cxx +from SCons.Tool.clang import get_clang_install_dirs + compilers = ['clang++'] @@ -68,7 +70,7 @@ def generate(env): env['SHOBJSUFFIX'] = '.pic.o' elif env['PLATFORM'] == 'win32': # Ensure that we have a proper path for clang++ - clangxx = SCons.Tool.find_program_path(env, compilers[0], default_paths=[r'C:\Program Files\LLVM\bin',]) + clangxx = SCons.Tool.find_program_path(env, compilers[0], default_paths=get_clang_install_dirs(env['PLATFORM'])) if clangxx: clangxx_bin_dir = os.path.dirname(clangxx) env.AppendENVPath('PATH', clangxx_bin_dir) -- cgit v0.12 From d4b41bbd10023f8c391b4bbd2928cc435e19eda3 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 25 Mar 2018 12:53:56 -0400 Subject: rename clang command package to clangCommon as it was breaking imports test. --- src/engine/SCons/Tool/clang.py | 2 +- src/engine/SCons/Tool/clang/__init__.py | 16 ---------------- src/engine/SCons/Tool/clangCommon/__init__.py | 17 +++++++++++++++++ src/engine/SCons/Tool/clangxx.py | 2 +- 4 files changed, 19 insertions(+), 18 deletions(-) delete mode 100644 src/engine/SCons/Tool/clang/__init__.py create mode 100644 src/engine/SCons/Tool/clangCommon/__init__.py diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index aba5598..c51e753 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -45,7 +45,7 @@ import sys import SCons.Util import SCons.Tool.cc -from SCons.Tool.clang import get_clang_install_dirs +from SCons.Tool.clangCommon import get_clang_install_dirs compilers = ['clang'] diff --git a/src/engine/SCons/Tool/clang/__init__.py b/src/engine/SCons/Tool/clang/__init__.py deleted file mode 100644 index 775ba80..0000000 --- a/src/engine/SCons/Tool/clang/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Common routines and data for clang tools -""" - -clang_win32_dirs = [ - r'C:\Program Files\LLVM\bin', - r'C:\cygwin64\bin', - r'C:\msys64', - r'C:\cygwin\bin', - r'C:\msys', -] - -def get_clang_install_dirs(platform): - if platform == 'win32': - return clang_win32_dirs - else return [] \ No newline at end of file diff --git a/src/engine/SCons/Tool/clangCommon/__init__.py b/src/engine/SCons/Tool/clangCommon/__init__.py new file mode 100644 index 0000000..37efbf6 --- /dev/null +++ b/src/engine/SCons/Tool/clangCommon/__init__.py @@ -0,0 +1,17 @@ +""" +Common routines and data for clang tools +""" + +clang_win32_dirs = [ + r'C:\Program Files\LLVM\bin', + r'C:\cygwin64\bin', + r'C:\msys64', + r'C:\cygwin\bin', + r'C:\msys', +] + +def get_clang_install_dirs(platform): + if platform == 'win32': + return clang_win32_dirs + else: + return [] \ No newline at end of file diff --git a/src/engine/SCons/Tool/clangxx.py b/src/engine/SCons/Tool/clangxx.py index 2bf4cf4..7194d9e 100644 --- a/src/engine/SCons/Tool/clangxx.py +++ b/src/engine/SCons/Tool/clangxx.py @@ -46,7 +46,7 @@ import sys import SCons.Tool import SCons.Util import SCons.Tool.cxx -from SCons.Tool.clang import get_clang_install_dirs +from SCons.Tool.clangCommon import get_clang_install_dirs compilers = ['clang++'] -- cgit v0.12 From 69a0d4b42ca97f034dd6a00fd72c41a6719e225c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 25 Mar 2018 13:41:06 -0400 Subject: Fix clang and mingw tests --- src/engine/SCons/Tool/mingw.py | 7 ++++++- test/import.py | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index 4121902..de493f0 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -156,7 +156,12 @@ def generate(env): env['PROGSUFFIX'] = '.exe' def exists(env): - return find(env) + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=[r'c:\MinGW\bin',]) + if mingw: + mingw_bin_dir = os.path.dirname(mingw) + env.AppendENVPath('PATH', mingw_bin_dir) + + return mingw # Local Variables: # tab-width:4 diff --git a/test/import.py b/test/import.py index b6e5a8d..11fee9c 100644 --- a/test/import.py +++ b/test/import.py @@ -75,6 +75,8 @@ ignore = ('__init__.py', '386asm.py', 'linkloc.py', # Directory of common stuff for MSVC and MSVS 'MSCommon', + # clang common + "clangCommon", # Sun pkgchk and pkginfo common stuff 'sun_pkg.py', # RPM utilities -- cgit v0.12 From 7ad7a9bc0a802096e2a5beadcca8f6a10822ef95 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 25 Mar 2018 13:51:42 -0400 Subject: added more default mingw paths per comments on pull request --- src/engine/SCons/Tool/mingw.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index de493f0..f2e9442 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -43,6 +43,14 @@ import SCons.Tool import SCons.Util +mingw_paths = [ + r'c:\MinGW\bin', + r'C:\cygwin64\bin', + r'C:\msys64', + r'C:\cygwin\bin', + r'C:\msys', +] + def shlib_generator(target, source, env, for_signature): cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) @@ -111,7 +119,7 @@ key_program = 'mingw32-gcc' def generate(env): - mingw = SCons.Tool.find_program_path(env, key_program, default_paths=[r'c:\MinGW\bin',]) + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=mingw_paths) if mingw: mingw_bin_dir = os.path.dirname(mingw) env.AppendENVPath('PATH', mingw_bin_dir) @@ -156,7 +164,7 @@ def generate(env): env['PROGSUFFIX'] = '.exe' def exists(env): - mingw = SCons.Tool.find_program_path(env, key_program, default_paths=[r'c:\MinGW\bin',]) + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=mingw_paths) if mingw: mingw_bin_dir = os.path.dirname(mingw) env.AppendENVPath('PATH', mingw_bin_dir) -- cgit v0.12 From 35ac5267d91db9379129fc33d49d6699350347e5 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 9 Apr 2018 15:34:24 -0500 Subject: updated yacc tool to add default paths, updated to test to use the tool specifically and know when to skip on windows. --- src/engine/SCons/Tool/yacc.py | 9 +++++++++ test/YACC/YACC.py | 8 ++++---- test/YACC/live-check-output-cleaned.py | 2 +- test/YACC/live.py | 12 +++++++++--- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index 648433b..be65104 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -34,6 +34,7 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path +import sys import SCons.Defaults import SCons.Tool @@ -113,6 +114,14 @@ def generate(env): cxx_file.add_action('.yy', YaccAction) cxx_file.add_emitter('.yy', yyEmitter) + if(sys.platform == 'win32'): + bison = SCons.Tool.find_program_path(env, 'bison', default_paths=['C:\\cygwin64\\bin', 'C:\\cygwin\\bin', 'C:\\msys', 'C:\\msys64' ]) + if bison: + bison_bin_dir = os.path.dirname(bison) + env.AppendENVPath('PATH', bison_bin_dir) + else: + SCons.Warnings.Warning('yacc tool requested, but bison binary not found in ENV PATH') + env['YACC'] = env.Detect('bison') or 'yacc' env['YACCFLAGS'] = SCons.Util.CLVar('') env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' diff --git a/test/YACC/YACC.py b/test/YACC/YACC.py index 3fc1f7c..b27c2a7 100644 --- a/test/YACC/YACC.py +++ b/test/YACC/YACC.py @@ -53,10 +53,10 @@ env.CFile(target = 'ddd', source = 'ddd.ym') test.run(arguments = '.', stderr = None) -test.must_match('aaa.c', "aaa.y\nmyyacc.py\n") -test.must_match('bbb.c', "bbb.yacc\nmyyacc.py\n") -test.must_match('ccc.cc', "ccc.yacc\nmyyacc.py\n") -test.must_match('ddd.m', "ddd.yacc\nmyyacc.py\n") +test.must_match('aaa.c', "aaa.y" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('bbb.c', "bbb.yacc" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('ccc.cc', "ccc.yacc" + os.linesep + "myyacc.py" + os.linesep) +test.must_match('ddd.m', "ddd.yacc" + os.linesep + "myyacc.py" + os.linesep) diff --git a/test/YACC/live-check-output-cleaned.py b/test/YACC/live-check-output-cleaned.py index a6240c9..8329b94 100644 --- a/test/YACC/live-check-output-cleaned.py +++ b/test/YACC/live-check-output-cleaned.py @@ -40,7 +40,7 @@ if not yacc: test.skip_test('No yacc or bison found; skipping test.\n') test.write('SConstruct', """ -foo = Environment(YACCFLAGS='-v -d') +foo = Environment(YACCFLAGS='-v -d', tools = ['default', 'yacc']) foo.CFile(source = 'foo.y') """ % locals()) diff --git a/test/YACC/live.py b/test/YACC/live.py index 6dd08f7..35f6c37 100644 --- a/test/YACC/live.py +++ b/test/YACC/live.py @@ -29,6 +29,8 @@ Test YACC and YACCFLAGS with a live yacc compiler. """ import TestSCons +import sys +import os _exe = TestSCons._exe _python_ = TestSCons._python_ @@ -40,12 +42,16 @@ yacc = test.where_is('yacc') or test.where_is('bison') if not yacc: test.skip_test('No yacc or bison found; skipping test.\n') +if sys.platform == 'win32': + if not test.where_is('gcc'): + test.skip_test('No gcc found on windows; skipping test.\n') + test.file_fixture('wrapper.py') test.write('SConstruct', """ -foo = Environment(YACCFLAGS='-d') +foo = Environment(YACCFLAGS='-d', tools = ['default', 'yacc']) yacc = foo.Dictionary('YACC') -bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc) +bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc, tools = ['default', 'yacc']) foo.Program(target = 'foo', source = 'foo.y') bar.Program(target = 'bar', source = 'bar.y') foo.Program(target = 'hello', source = ['hello.cpp']) @@ -152,7 +158,7 @@ test.run(arguments = 'bar' + _exe) test.up_to_date(arguments = 'bar' + _exe) -test.must_match(test.workpath('wrapper.out'), "wrapper.py\n") +test.must_match(test.workpath('wrapper.out'), "wrapper.py" + os.linesep) test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n") -- cgit v0.12 From de0d6f38404c6880e4b401f7ea6fd629bcb765ea Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 10 Apr 2018 08:03:43 -0500 Subject: removed binary read and write so newlines are crossplatform and updated path for appveyor script --- .appveyor.yml | 2 +- test/YACC/YACC-fixture/myyacc.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index a9d2dfc..a735070 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,7 +2,7 @@ image: Visual Studio 2017 shallow_clone: true install: - - "set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" + - "set PATH=%PYTHON%;%PYTHON%\\Scripts;C:\\cygwin64\\bin;C:\\msys64;%PATH%" - python --version - pip install lxml - pip install pypiwin32 diff --git a/test/YACC/YACC-fixture/myyacc.py b/test/YACC/YACC-fixture/myyacc.py index c2e1abf..756c98f 100644 --- a/test/YACC/YACC-fixture/myyacc.py +++ b/test/YACC/YACC-fixture/myyacc.py @@ -4,10 +4,10 @@ cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:', []) output = None opt_string = '' for opt, arg in cmd_opts: - if opt == '-o': output = open(arg, 'wb') + if opt == '-o': output = open(arg, 'w') else: opt_string = opt_string + ' ' + opt for a in args: - contents = open(a, 'rb').read() - output.write(contents.replace(b'YACC', b'myyacc.py')) + contents = open(a, 'r').read() + output.write(contents.replace('YACC', 'myyacc.py')) output.close() sys.exit(0) -- cgit v0.12 From 1912634e6d65133f8101043ae1e702a7407d60e3 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 10 Apr 2018 11:17:40 -0500 Subject: removed unnecessary parans --- src/engine/SCons/Tool/yacc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index be65104..a43b3b3 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -114,7 +114,7 @@ def generate(env): cxx_file.add_action('.yy', YaccAction) cxx_file.add_emitter('.yy', yyEmitter) - if(sys.platform == 'win32'): + if sys.platform == 'win32': bison = SCons.Tool.find_program_path(env, 'bison', default_paths=['C:\\cygwin64\\bin', 'C:\\cygwin\\bin', 'C:\\msys', 'C:\\msys64' ]) if bison: bison_bin_dir = os.path.dirname(bison) -- cgit v0.12 From 3dc6c4e24ec1ed72a386aeb9a791cb67db46a7c2 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 10 Apr 2018 12:40:36 -0500 Subject: moved default paths for cywing and mingw to their own platform modules. --- src/engine/SCons/Platform/cygwin.py | 9 +++++++++ src/engine/SCons/Platform/mingw.py | 39 +++++++++++++++++++++++++++++++++++++ src/engine/SCons/Tool/yacc.py | 4 +++- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/engine/SCons/Platform/mingw.py diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py index 8b4669c..f6c5086 100644 --- a/src/engine/SCons/Platform/cygwin.py +++ b/src/engine/SCons/Platform/cygwin.py @@ -32,9 +32,18 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import sys + from . import posix from SCons.Platform import TempFileMunge +CYGWIN_DEFAULT_PATHS = [] +if sys.platform == 'win32': + CYGWIN_DEFAULT_PATHS = [ + r'C:\cygwin64\bin', + r'C:\cygwin\bin' + ] + def generate(env): posix.generate(env) diff --git a/src/engine/SCons/Platform/mingw.py b/src/engine/SCons/Platform/mingw.py new file mode 100644 index 0000000..73633d7 --- /dev/null +++ b/src/engine/SCons/Platform/mingw.py @@ -0,0 +1,39 @@ +"""SCons.Platform.mingw + +Platform-specific initialization for the MinGW system. + +""" + +# +# __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 sys + +MINGW_DEFAULT_PATHS = [] +if sys.platform == 'win32': + MINGW_DEFAULT_PATHS = [ + r'C:\msys64', + r'C:\msys' + ] \ No newline at end of file diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index a43b3b3..cd9b9a8 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -39,6 +39,8 @@ import sys import SCons.Defaults import SCons.Tool import SCons.Util +from SCons.Platform.mingw import MINGW_DEFAULT_PATHS +from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") @@ -115,7 +117,7 @@ def generate(env): cxx_file.add_emitter('.yy', yyEmitter) if sys.platform == 'win32': - bison = SCons.Tool.find_program_path(env, 'bison', default_paths=['C:\\cygwin64\\bin', 'C:\\cygwin\\bin', 'C:\\msys', 'C:\\msys64' ]) + bison = SCons.Tool.find_program_path(env, 'bison', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) if bison: bison_bin_dir = os.path.dirname(bison) env.AppendENVPath('PATH', bison_bin_dir) -- cgit v0.12 From 56471eea9278a5e1bfb92a5214e8c99d2c939a35 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 10 Apr 2018 16:08:32 -0500 Subject: updated CHANGES.txt --- src/CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index f9cb5c4..58dc6df 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -19,6 +19,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fixed handling of side-effects in task master (fixes #3013). From Daniel Moody: + - Add common location for default paths for cygwin and mingw in Platform modules + - Updated YACC tool to work on windows with Cygwin/MinGW setups - Set the pickling protocal back to highest which was causing issues with variant dir tests. This will cause issues if reading sconsigns pickled with the previous lower protocal. -- cgit v0.12 From 85d080e18906fb52d425a84e5828189d8a532ce0 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Thu, 12 Apr 2018 15:06:44 -0500 Subject: updated gettext tools to work on windows cygwin and mingw environments. --- src/engine/SCons/Tool/GettextCommon.py | 2 +- src/engine/SCons/Tool/gettext_tool.py | 12 ++++++++++++ src/engine/SCons/Tool/msgfmt.py | 14 ++++++++++++++ src/engine/SCons/Tool/msginit.py | 14 ++++++++++++++ src/engine/SCons/Tool/msgmerge.py | 15 ++++++++++++++- src/engine/SCons/Tool/xgettext.py | 14 +++++++++++++- 6 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/engine/SCons/Tool/GettextCommon.py b/src/engine/SCons/Tool/GettextCommon.py index 3b840a6..2d37def 100644 --- a/src/engine/SCons/Tool/GettextCommon.py +++ b/src/engine/SCons/Tool/GettextCommon.py @@ -447,7 +447,7 @@ def _detect_msgfmt(env): """ Detects *msgmfmt(1)* program. """ if 'MSGFMT' in env: return env['MSGFMT'] - msgfmt = env.Detect('msgfmt'); + msgfmt = env.Detect('msgfmt') if msgfmt: return msgfmt raise SCons.Errors.StopError(MsgfmtNotFound, "Could not detect msgfmt") diff --git a/src/engine/SCons/Tool/gettext_tool.py b/src/engine/SCons/Tool/gettext_tool.py index 6031e49..41bd70b 100644 --- a/src/engine/SCons/Tool/gettext_tool.py +++ b/src/engine/SCons/Tool/gettext_tool.py @@ -27,10 +27,22 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" ############################################################################# def generate(env,**kw): + import sys + import os import SCons.Tool + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + from SCons.Tool.GettextCommon \ import _translate, tool_list for t in tool_list(env['PLATFORM'], env): + if sys.platform == 'win32': + tool = SCons.Tool.find_program_path(env, t, default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if tool: + tool_bin_dir = os.path.dirname(tool) + env.AppendENVPath('PATH', tool_bin_dir) + else: + SCons.Warnings.Warning(t + ' tool requested, but binary not found in ENV PATH') env.Tool(t) env.AddMethod(_translate, 'Translate') ############################################################################# diff --git a/src/engine/SCons/Tool/msgfmt.py b/src/engine/SCons/Tool/msgfmt.py index 4fe6afd..3c9dde7 100644 --- a/src/engine/SCons/Tool/msgfmt.py +++ b/src/engine/SCons/Tool/msgfmt.py @@ -75,8 +75,22 @@ def _create_mo_file_builder(env, **kw): ############################################################################# def generate(env,**kw): """ Generate `msgfmt` tool """ + import sys + import os import SCons.Util + import SCons.Tool from SCons.Tool.GettextCommon import _detect_msgfmt + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + msgfmt = SCons.Tool.find_program_path(env, 'msgfmt', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if msgfmt: + msgfmt_bin_dir = os.path.dirname(msgfmt) + env.AppendENVPath('PATH', msgfmt_bin_dir) + else: + SCons.Warnings.Warning('msgfmt tool requested, but binary not found in ENV PATH') + try: env['MSGFMT'] = _detect_msgfmt(env) except: diff --git a/src/engine/SCons/Tool/msginit.py b/src/engine/SCons/Tool/msginit.py index 39f460d..9e056e7 100644 --- a/src/engine/SCons/Tool/msginit.py +++ b/src/engine/SCons/Tool/msginit.py @@ -77,8 +77,22 @@ def _POInitBuilderWrapper(env, target=None, source=_null, **kw): ############################################################################# def generate(env,**kw): """ Generate the `msginit` tool """ + import sys + import os import SCons.Util + import SCons.Tool from SCons.Tool.GettextCommon import _detect_msginit + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + msginit = SCons.Tool.find_program_path(env, 'msginit', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if msginit: + msginit_bin_dir = os.path.dirname(msginit) + env.AppendENVPath('PATH', msginit_bin_dir) + else: + SCons.Warnings.Warning('msginit tool requested, but binary not found in ENV PATH') + try: env['MSGINIT'] = _detect_msginit(env) except: diff --git a/src/engine/SCons/Tool/msgmerge.py b/src/engine/SCons/Tool/msgmerge.py index 11d7b48..76661d2 100644 --- a/src/engine/SCons/Tool/msgmerge.py +++ b/src/engine/SCons/Tool/msgmerge.py @@ -68,8 +68,21 @@ def _POUpdateBuilderWrapper(env, target=None, source=_null, **kw): ############################################################################# def generate(env,**kw): - """ Generate the `xgettext` tool """ + """ Generate the `msgmerge` tool """ + import sys + import os + import SCons.Tool from SCons.Tool.GettextCommon import _detect_msgmerge + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + msgmerge = SCons.Tool.find_program_path(env, 'msgmerge', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if msgmerge: + msgmerge_bin_dir = os.path.dirname(msgmerge) + env.AppendENVPath('PATH', msgmerge_bin_dir) + else: + SCons.Warnings.Warning('msgmerge tool requested, but binary not found in ENV PATH') try: env['MSGMERGE'] = _detect_msgmerge(env) except: diff --git a/src/engine/SCons/Tool/xgettext.py b/src/engine/SCons/Tool/xgettext.py index 2c0ce40..e4a17ca 100644 --- a/src/engine/SCons/Tool/xgettext.py +++ b/src/engine/SCons/Tool/xgettext.py @@ -288,9 +288,21 @@ def _POTUpdateBuilder(env, **kw): ############################################################################# def generate(env, **kw): """ Generate `xgettext` tool """ + import sys + import os import SCons.Util + import SCons.Tool from SCons.Tool.GettextCommon import RPaths, _detect_xgettext - + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + xgettext = SCons.Tool.find_program_path(env, 'xgettext', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) + if xgettext: + xgettext_bin_dir = os.path.dirname(xgettext) + env.AppendENVPath('PATH', xgettext_bin_dir) + else: + SCons.Warnings.Warning('xgettext tool requested, but binary not found in ENV PATH') try: env['XGETTEXT'] = _detect_xgettext(env) except: -- cgit v0.12 From f8422a59151aa744c025675a7c4bf3d5e3b8d7c0 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 13 Apr 2018 11:08:56 -0500 Subject: updated CHANGES.txt --- src/CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 58dc6df..43c1979 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -19,6 +19,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fixed handling of side-effects in task master (fixes #3013). From Daniel Moody: + - Updated gettext tools to setup default paths for windows with Cygwin/MinGW setups - Add common location for default paths for cygwin and mingw in Platform modules - Updated YACC tool to work on windows with Cygwin/MinGW setups - Set the pickling protocal back to highest which was causing issues -- cgit v0.12 From 8d41ab0567e38e5d03138056560861343bd9a2a7 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 25 May 2018 11:26:02 -0700 Subject: Handful of changes to get clang tests passing on windows. Use glob to find other possible default mingw install paths. mingw is needed in many cases on windows to compile/archive. --- src/engine/SCons/Tool/clang.py | 3 ++- src/engine/SCons/Tool/mingw.py | 21 ++++++++++++++++++++- test/Clang/clang_default_environment.py | 5 ++--- test/Clang/clang_shared_library.py | 12 +++++++----- test/Clang/clang_specific_environment.py | 1 + test/Clang/clang_static_library.py | 3 ++- test/Clang/clangxx_default_environment.py | 5 ++--- test/Clang/clangxx_shared_library.py | 11 +++++++---- test/Clang/clangxx_specific_environment.py | 3 ++- test/Clang/clangxx_static_library.py | 3 ++- 10 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index c51e753..cbb48cb 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -56,7 +56,8 @@ def generate(env): if env['PLATFORM'] == 'win32': # Ensure that we have a proper path for clang - clang = SCons.Tool.find_program_path(env, compilers[0], default_paths=get_clang_install_dirs(env['PLATFORM'])) + clang = SCons.Tool.find_program_path(env, compilers[0], + default_paths=get_clang_install_dirs(env['PLATFORM'])) if clang: clang_bin_dir = os.path.dirname(clang) env.AppendENVPath('PATH', clang_bin_dir) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index f2e9442..738460d 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -35,6 +35,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path +import glob import SCons.Action import SCons.Builder @@ -115,10 +116,28 @@ res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) # This is what we search for to find mingw: -key_program = 'mingw32-gcc' +# key_program = 'mingw32-gcc' +key_program = 'mingw32-make' + + + +def find_version_specific_mingw_paths(): + """ + One example of default mingw install paths is: + C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev2\mingw64\bin + + Use glob'ing to find such and add to mingw_paths + """ + new_paths = glob.glob(r"C:\mingw-w64\*\mingw64\bin") + + return new_paths def generate(env): + global mingw_paths + # Check for reasoanble mingw default paths + mingw_paths +=find_version_specific_mingw_paths() + mingw = SCons.Tool.find_program_path(env, key_program, default_paths=mingw_paths) if mingw: mingw_bin_dir = os.path.dirname(mingw) diff --git a/test/Clang/clang_default_environment.py b/test/Clang/clang_default_environment.py index 4ac1c68..5ebd839 100644 --- a/test/Clang/clang_default_environment.py +++ b/test/Clang/clang_default_environment.py @@ -35,9 +35,8 @@ if not test.where_is('clang'): ## This will likely NOT use clang test.write('SConstruct', """ -env = Environment() -if env['CC'] != 'clang': - env['CC'] = 'clang' +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang','link']) env.Program('foo.c') """) diff --git a/test/Clang/clang_shared_library.py b/test/Clang/clang_shared_library.py index 5e4d36f..9af3770 100644 --- a/test/Clang/clang_shared_library.py +++ b/test/Clang/clang_shared_library.py @@ -33,20 +33,22 @@ test = TestSCons.TestSCons() if not test.where_is('clang'): test.skip_test("Could not find 'clang', skipping test.\n") -platform = Base()['PLATFORM'] +base = Base() +platform = base['PLATFORM'] if platform == 'posix': - filename = 'foo.os' + filename_options = ['foo.os'] libraryname = 'libfoo.so' elif platform == 'darwin': - filename = 'foo.os' + filename_options = ['foo.os'] libraryname = 'libfoo.dylib' elif platform == 'win32': - filename = 'foo.obj' + filename_options = ['foo.obj','foo.os'] libraryname = 'foo.dll' else: test.fail_test() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment(tools=['clang', 'link']) env.SharedLibrary('foo', 'foo.c') """) @@ -59,7 +61,7 @@ int bar() { test.run() -test.must_exist(test.workpath(filename)) +test.must_exist_one_of([test.workpath(f) for f in filename_options]) test.must_exist(test.workpath(libraryname)) test.pass_test() diff --git a/test/Clang/clang_specific_environment.py b/test/Clang/clang_specific_environment.py index 7266a9f..81991f0 100644 --- a/test/Clang/clang_specific_environment.py +++ b/test/Clang/clang_specific_environment.py @@ -33,6 +33,7 @@ if not test.where_is('clang'): test.skip_test("Could not find 'clang', skipping test.\n") test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment(tools=['clang', 'link']) env.Program('foo.c') """) diff --git a/test/Clang/clang_static_library.py b/test/Clang/clang_static_library.py index 39e9931..7dedc6f 100644 --- a/test/Clang/clang_static_library.py +++ b/test/Clang/clang_static_library.py @@ -33,7 +33,8 @@ if not test.where_is('clang'): test.skip_test("Could not find 'clang', skipping test.\n") test.write('SConstruct', """\ -env = Environment(tools=['clang', 'ar']) +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang', 'ar']) env.StaticLibrary('foo', 'foo.c') """) diff --git a/test/Clang/clangxx_default_environment.py b/test/Clang/clangxx_default_environment.py index beef1e5..5e46404 100644 --- a/test/Clang/clangxx_default_environment.py +++ b/test/Clang/clangxx_default_environment.py @@ -35,9 +35,8 @@ if not test.where_is('clang'): ## This will likely NOT use clang++. test.write('SConstruct', """\ -env = Environment() -if env['CXX'] != 'clang++': - env['CXX'] = 'clang++' +DefaultEnvironment(tools=[]) +env = Environment(tools=['clangxx','link']) env.Program('foo.cpp') """) diff --git a/test/Clang/clangxx_shared_library.py b/test/Clang/clangxx_shared_library.py index d6337ba..6240299 100644 --- a/test/Clang/clangxx_shared_library.py +++ b/test/Clang/clangxx_shared_library.py @@ -36,18 +36,21 @@ if not test.where_is('clang'): platform = Base()['PLATFORM'] if platform == 'posix': - filename = 'foo.os' + filename_options = ['foo.os'] libraryname = 'libfoo.so' elif platform == 'darwin': - filename = 'foo.os' + filename_options = ['foo.os'] libraryname = 'libfoo.dylib' elif platform == 'win32': - filename = 'foo.obj' + filename_options = ['foo.obj','foo.os'] libraryname = 'foo.dll' else: test.fail_test() + + test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment(tools=['clang++', 'link']) env.SharedLibrary('foo', 'foo.cpp') """) @@ -60,7 +63,7 @@ int bar() { test.run() -test.must_exist(test.workpath(filename)) +test.must_exist_one_of([test.workpath(f) for f in filename_options]) test.must_exist(test.workpath(libraryname)) test.pass_test() diff --git a/test/Clang/clangxx_specific_environment.py b/test/Clang/clangxx_specific_environment.py index 773fa94..35540bb 100644 --- a/test/Clang/clangxx_specific_environment.py +++ b/test/Clang/clangxx_specific_environment.py @@ -33,7 +33,8 @@ if not test.where_is('clang'): test.skip_test("Could not find 'clang++', skipping test.\n") test.write('SConstruct', """\ -env = Environment(tools=['clang++', 'link']) +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang++', 'link']) env.Program('foo.cpp') """) diff --git a/test/Clang/clangxx_static_library.py b/test/Clang/clangxx_static_library.py index 77ea58e..c768deb 100644 --- a/test/Clang/clangxx_static_library.py +++ b/test/Clang/clangxx_static_library.py @@ -33,7 +33,8 @@ if not test.where_is('clang'): test.skip_test("Could not find 'clang++', skipping test.\n") test.write('SConstruct', """\ -env = Environment(tools=['clang++', 'ar']) +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang++', 'ar']) env.StaticLibrary('foo', 'foo.cpp') """) -- cgit v0.12 From b86c5845efccdec97dd267efe6f61c23db92c5a9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 25 May 2018 12:45:47 -0700 Subject: rebase to upstream/master --- src/CHANGES.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 43c1979..75133e8 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -44,6 +44,13 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Re-Enable parallel SCons (-j) when running via Pypy - Move SCons test framework files to testing/framework and remove all references to QMtest. QMTest has not been used by SCons for some time now. + - Updated logic for mingw and clang on win32 to search default tool install paths if not + found in normal SCons PATH. If the user specifies PATH or tool specific paths they + will be used and the default paths below will be ignored. + - Default path for clang/clangxx : C:\Program Files\LLVM\bin + - Default path for mingw : C:\MinGW\bin and/or C:\mingw-w64\*\mingw64\bin + - Key program to locate mingw : mingw32-make (as the gcc with mingw prefix has no fixed name) + From Andrew Featherstone - Removed unused --warn options from the man page and source code. -- cgit v0.12 From cf74ad1fc7b745f8dfcf5886aa5e0326d104caa0 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 25 May 2018 12:48:45 -0700 Subject: remove mingw tool as it's breaking non win tests --- test/Clang/clangxx_specific_environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Clang/clangxx_specific_environment.py b/test/Clang/clangxx_specific_environment.py index 35540bb..39eaab1 100644 --- a/test/Clang/clangxx_specific_environment.py +++ b/test/Clang/clangxx_specific_environment.py @@ -34,7 +34,7 @@ if not test.where_is('clang'): test.write('SConstruct', """\ DefaultEnvironment(tools=[]) -env = Environment(tools=['mingw','clang++', 'link']) +env = Environment(tools=['clang++', 'link']) env.Program('foo.cpp') """) -- cgit v0.12 From 0e5af237a0eff7c49fb354697f348e7f2b0e629e Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 15:07:11 -0500 Subject: pr-3052: Updated swig tool with some default paths and fixed test to build on windows. --- .appveyor.yml | 8 +++---- src/engine/SCons/Tool/swig.py | 12 ++++++++++ test/SWIG/recursive-includes-cpp.py | 44 +++++++++++++++++++++++++++---------- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index a735070..fe231d2 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,10 +6,10 @@ install: - python --version - pip install lxml - pip install pypiwin32 - - choco install dmd - - choco install ldc - - choco install swig - - choco install vswhere + - choco install --allow-empty-checksums dmd + - choco install --allow-empty-checksums ldc + - choco install --allow-empty-checksums swig + - choco install --allow-empty-checksums vswhere environment: matrix: diff --git a/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py index da4472f..08881a0 100644 --- a/src/engine/SCons/Tool/swig.py +++ b/src/engine/SCons/Tool/swig.py @@ -35,6 +35,7 @@ from __future__ import print_function __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path +import sys import re import subprocess @@ -169,6 +170,17 @@ def generate(env): java_file.add_action('.i', SwigAction) java_file.add_emitter('.i', _swigEmitter) + from SCons.Platform.mingw import MINGW_DEFAULT_PATHS + from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + + if sys.platform == 'win32': + swig = SCons.Tool.find_program_path(env, 'swig', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS + [r'C:\ProgramData\chocolatey\bin'] ) + if swig: + swig_bin_dir = os.path.dirname(swig) + env.AppendENVPath('PATH', swig_bin_dir) + else: + SCons.Warnings.Warning('swig tool requested, but binary not found in ENV PATH') + if 'SWIG' not in env: env['SWIG'] = env.Detect(swigs) or swigs[0] env['SWIGVERSION'] = _get_swig_version(env, env['SWIG']) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index e69b5b9..2767c4b 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -30,6 +30,7 @@ in cases of recursive inclusion. """ import os +import sys import TestSCons from SCons.Defaults import DefaultEnvironment @@ -42,6 +43,11 @@ for pre_req in ['swig', 'python']: if not test.where_is(pre_req): test.skip_test('Can not find installed "' + pre_req + '", skipping test.%s' % os.linesep) +if sys.platform == 'win32': + python_lib = os.path.dirname(sys.executable) + "/libs/" + ('python%d%d'%(sys.version_info[0],sys.version_info[1])) + '.lib' + if( not os.path.isfile(python_lib)): + test.skip_test('Can not find python lib at "' + python_lib + '", skipping test.%s' % os.linesep) + test.write("recursive.h", """\ /* An empty header file. */ """) @@ -62,24 +68,34 @@ test.write("mod.i", """\ #include "main.h" """) +if sys.platform == 'win32': + if(sys.maxsize > 2**32): + TARGET_ARCH = 'x86_64' + else: + TARGET_ARCH = 'x86' + test.write('SConstruct', """\ import distutils.sysconfig import sys - -DefaultEnvironment( tools = [ 'swig' ] ) +import os env = Environment( + TARGET_ARCH = '""" + TARGET_ARCH +"""' SWIGFLAGS = [ '-python' ], CPPPATH = [ distutils.sysconfig.get_python_inc() ], - SHLIBPREFIX = "" + SHLIBPREFIX = "", + tools = [ 'default', 'swig' ] ) if sys.platform == 'darwin': env['LIBS']=['python%d.%d'%(sys.version_info[0],sys.version_info[1])] +elif sys.platform == 'win32': + env.Append(LIBS=['python%d%d'%(sys.version_info[0],sys.version_info[1])]) + env.Append(LIBPATH=[os.path.dirname(sys.executable) + "/libs"]) env.SharedLibrary( 'mod', @@ -90,28 +106,34 @@ env.SharedLibrary( ) """) +if sys.platform == 'win32': + object_suffix = ".obj" +else: + object_suffix = ".os" + expectMain = """\ -+-main.os ++-main%s +-main.c +-main.h - +-recursive.h""" + +-recursive.h""" % object_suffix expectMod = """\ -+-mod_wrap.os ++-mod_wrap%s +-mod_wrap.c | +-mod.i | +-main.h - | +-recursive.h""" + | +-recursive.h""" % object_suffix # Validate that the recursive dependencies are found with SWIG scanning first. -test.run( arguments = '--tree=all mod_wrap.os main.os' ) - +test.run( arguments = '--tree=all mod_wrap'+object_suffix +' main'+object_suffix) +print(test.stdout()) test.must_contain_all( test.stdout(), expectMain ) test.must_contain_all( test.stdout(), expectMod ) -# Validate that the recursive dependencies are found consistently. -test.run( arguments = '--tree=all main.os mod_wrap.os' ) +# Validate that the recursive dependencies are found consistently. +test.run( arguments = '--tree=all main'+object_suffix +' mod_wrap'+object_suffix) +print(test.stdout()) test.must_contain_all( test.stdout(), expectMain ) test.must_contain_all( test.stdout(), expectMod ) -- cgit v0.12 From b20299cf0bbc8b7073838c225d68909d6f021fab Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 15:16:10 -0500 Subject: pr-3052: added a missing comma to the environment args --- test/SWIG/recursive-includes-cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 2767c4b..2f2cff2 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -80,7 +80,7 @@ import sys import os env = Environment( - TARGET_ARCH = '""" + TARGET_ARCH +"""' + TARGET_ARCH = '""" + TARGET_ARCH +"""', SWIGFLAGS = [ '-python' ], -- cgit v0.12 From 0f83c3918a0acf84588902c6c3d3f28e764edb56 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 15:20:02 -0500 Subject: pr-3052: removed debug print statements --- test/SWIG/recursive-includes-cpp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 2f2cff2..2494c45 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -126,14 +126,14 @@ expectMod = """\ # Validate that the recursive dependencies are found with SWIG scanning first. test.run( arguments = '--tree=all mod_wrap'+object_suffix +' main'+object_suffix) -print(test.stdout()) + test.must_contain_all( test.stdout(), expectMain ) test.must_contain_all( test.stdout(), expectMod ) # Validate that the recursive dependencies are found consistently. test.run( arguments = '--tree=all main'+object_suffix +' mod_wrap'+object_suffix) -print(test.stdout()) + test.must_contain_all( test.stdout(), expectMain ) test.must_contain_all( test.stdout(), expectMod ) -- cgit v0.12 From cd53a8f8f59ac6d0400d21044c09c50e92792927 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 15:25:08 -0500 Subject: pr-3052: made target arch transparent to non windows platforms --- test/SWIG/recursive-includes-cpp.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 2494c45..738b6c1 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -70,9 +70,9 @@ test.write("mod.i", """\ if sys.platform == 'win32': if(sys.maxsize > 2**32): - TARGET_ARCH = 'x86_64' + TARGET_ARCH = "TARGET_ARCH = 'x86_64'," else: - TARGET_ARCH = 'x86' + TARGET_ARCH = "TARGET_ARCH = 'x86'," test.write('SConstruct', """\ import distutils.sysconfig @@ -80,7 +80,7 @@ import sys import os env = Environment( - TARGET_ARCH = '""" + TARGET_ARCH +"""', + """ + TARGET_ARCH + """ SWIGFLAGS = [ '-python' ], -- cgit v0.12 From d1ad2461b0c9f8a9a19f7509b8334d46bf9f3391 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 15:26:39 -0500 Subject: pr-3052: Needed to define target arch in the non-windows case --- test/SWIG/recursive-includes-cpp.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 738b6c1..8fbe7c0 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -68,12 +68,14 @@ test.write("mod.i", """\ #include "main.h" """) + if sys.platform == 'win32': if(sys.maxsize > 2**32): TARGET_ARCH = "TARGET_ARCH = 'x86_64'," else: TARGET_ARCH = "TARGET_ARCH = 'x86'," - +else: + TARGET_ARCH = "" test.write('SConstruct', """\ import distutils.sysconfig import sys -- cgit v0.12 From 2cfbdb0e6ce2d1856c3700f4317a6b2242b36851 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 15:28:23 -0500 Subject: pr-3052: added changes to CHANGES.txt --- src/CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index c108378..17ca170 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -19,6 +19,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fixed handling of side-effects in task master (fixes #3013). From Daniel Moody: + - Updated swig to setup default paths for windows - Updated gettext tools to setup default paths for windows with Cygwin/MinGW setups - Add common location for default paths for cygwin and mingw in Platform modules - Updated YACC tool to work on windows with Cygwin/MinGW setups -- cgit v0.12 From ea6fc1fcb2de8a99a7f8a68d51f5969cdc729d2c Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 16:30:38 -0500 Subject: pr-3052: fixed some missing quotes for some tests that use the new default java tests for windows --- test/Repository/Java.py | 2 +- test/Repository/JavaH.py | 8 ++++---- test/Repository/RMIC.py | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/Repository/Java.py b/test/Repository/Java.py index fce85cd..290d742 100644 --- a/test/Repository/Java.py +++ b/test/Repository/Java.py @@ -59,7 +59,7 @@ opts = '-Y ' + test.workpath('rep1') # test.write(['rep1', 'SConstruct'], """ env = Environment(tools = ['javac'], - JAVAC = r'%s') + JAVAC = r'"%s"') env.Java(target = 'classes', source = 'src') """ % javac) diff --git a/test/Repository/JavaH.py b/test/Repository/JavaH.py index ee196cf..1cb5078 100644 --- a/test/Repository/JavaH.py +++ b/test/Repository/JavaH.py @@ -63,8 +63,8 @@ opts = '-Y ' + test.workpath('rep1') # test.write(['rep1', 'SConstruct'], """ env = Environment(tools = ['javac', 'javah'], - JAVAC = r'%s', - JAVAH = r'%s') + JAVAC = r'"%s"', + JAVAH = r'"%s"') classes = env.Java(target = 'classes', source = 'src') env.JavaH(target = 'outdir', source = classes) """ % (javac, javah)) @@ -207,8 +207,8 @@ test.up_to_date(chdir = 'work2', options = opts, arguments = ".") # test.write(['work3', 'SConstruct'], """ env = Environment(tools = ['javac', 'javah'], - JAVAC = r'%s', - JAVAH = r'%s') + JAVAC = r'"%s"', + JAVAH = r'"%s"') classes = env.Java(target = 'classes', source = 'src') hfiles = env.JavaH(target = 'outdir', source = classes) Local(hfiles) diff --git a/test/Repository/RMIC.py b/test/Repository/RMIC.py index 886ccdb..392e6ef 100644 --- a/test/Repository/RMIC.py +++ b/test/Repository/RMIC.py @@ -82,8 +82,8 @@ opts = '-Y ' + test.workpath('rep1') # test.write(['rep1', 'SConstruct'], """ env = Environment(tools = ['javac', 'rmic'], - JAVAC = r'%s', - RMIC = r'%s') + JAVAC = r'"%s"', + RMIC = r'"%s"') classes = env.Java(target = 'classes', source = 'src') # Brute-force removal of the "Hello" class. classes = [c for c in classes if str(c).find('Hello') == -1] @@ -350,8 +350,8 @@ test.up_to_date(chdir = 'work2', options = opts, arguments = ".") # test.write(['work3', 'SConstruct'], """ env = Environment(tools = ['javac', 'rmic'], - JAVAC = r'%s', - RMIC = r'%s') + JAVAC = r'"%s"', + RMIC = r'"%s"') classes = env.Java(target = 'classes', source = 'src') # Brute-force removal of the "Hello" class. classes = [c for c in classes if str(c).find('Hello') == -1] -- cgit v0.12 From 510f8bdfea8338856216e99329692d42fbdfc469 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 16:43:06 -0500 Subject: pr-3052: updated the expected result to work for windows --- test/Repository/RMIC.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Repository/RMIC.py b/test/Repository/RMIC.py index 392e6ef..e08c716 100644 --- a/test/Repository/RMIC.py +++ b/test/Repository/RMIC.py @@ -29,6 +29,7 @@ Test building Java applications when using Repositories. """ import TestSCons +import os python = TestSCons.python @@ -302,7 +303,7 @@ public class Foo2 extends UnicastRemoteObject implements Hello { test.run(chdir = 'work1', options = opts, arguments = ".") expect = [ - ' src/Foo1.java src/Foo2.java', + ' src' + os.sep + 'Foo1.java src' + os.sep + 'Foo2.java', ' com.sub.foo.Foo1 com.sub.foo.Foo2', ] -- cgit v0.12 From df543ac63394c2dffd50264d188fa3a237f69bc8 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 25 May 2018 17:06:08 -0500 Subject: pr-3052: moved some 64 bit logic to TestCmd and used TestCmd system checks --- test/SWIG/recursive-includes-cpp.py | 5 +++-- testing/framework/TestCmd.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 8fbe7c0..2c74cc8 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -32,6 +32,7 @@ in cases of recursive inclusion. import os import sys import TestSCons +import TestCmd from SCons.Defaults import DefaultEnvironment DefaultEnvironment( tools = [ 'swig' ] ) @@ -69,8 +70,8 @@ test.write("mod.i", """\ """) -if sys.platform == 'win32': - if(sys.maxsize > 2**32): +if TestCmd.IS_WINDOWS: + if TestCmd.IS_64_BIT: TARGET_ARCH = "TARGET_ARCH = 'x86_64'," else: TARGET_ARCH = "TARGET_ARCH = 'x86'," diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 0aab9a8..9cd6b39 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -309,7 +309,7 @@ import types IS_PY3 = sys.version_info[0] == 3 IS_WINDOWS = sys.platform == 'win32' - +IS_64_BIT = sys.maxsize > 2**32 class null(object): pass -- cgit v0.12 From 18e0da6a3110f2a291b38dc174053acfbbd688ff Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 25 May 2018 22:58:17 -0400 Subject: pr-3052: updated pkg-config test to handle backslashes and spaces on windows --- test/CPPDEFINES/pkg-config.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/CPPDEFINES/pkg-config.py b/test/CPPDEFINES/pkg-config.py index 4096934..8ec98ea 100644 --- a/test/CPPDEFINES/pkg-config.py +++ b/test/CPPDEFINES/pkg-config.py @@ -35,6 +35,8 @@ test = TestSCons.TestSCons() pkg_config_path = test.where_is('pkg-config') if not pkg_config_path: test.skip_test("Could not find 'pkg-config' in system PATH, skipping test.\n") + +pkg_config_path.replace("\\", "/") test.write('bug.pc', """\ prefix=/usr @@ -73,7 +75,7 @@ class OrderedPrintingDict(OrderedDict): # https://github.com/SCons/scons/issues/2671 # Passing test cases env_1 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST']) -env_1.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags bug') +env_1.ParseConfig('PKG_CONFIG_PATH=. "%(pkg_config_path)s" --cflags bug') print(env_1.subst('$_CPPDEFFLAGS')) env_2 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST']) @@ -82,7 +84,7 @@ print(env_2.subst('$_CPPDEFFLAGS')) # Failing test cases env_3 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)])) -env_3.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags bug') +env_3.ParseConfig('PKG_CONFIG_PATH=. "%(pkg_config_path)s" --cflags bug') print(env_3.subst('$_CPPDEFFLAGS')) env_4 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)])) @@ -91,7 +93,7 @@ print(env_4.subst('$_CPPDEFFLAGS')) # https://github.com/SCons/scons/issues/1738 env_1738_1 = Environment(tools=['default']) -env_1738_1.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags --libs bug') +env_1738_1.ParseConfig('PKG_CONFIG_PATH=. "%(pkg_config_path)s" --cflags --libs bug') env_1738_1.Append(CPPDEFINES={'value' : '1'}) print(env_1738_1.subst('$_CPPDEFFLAGS')) """%locals() ) -- cgit v0.12 From 9de87b96f01a3d9c6b2f731afec856b40c8c6a9e Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 26 May 2018 01:22:19 -0400 Subject: pr-3052: updated pkg-config test to work on windows --- test/CPPDEFINES/pkg-config.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/test/CPPDEFINES/pkg-config.py b/test/CPPDEFINES/pkg-config.py index 8ec98ea..65210ce 100644 --- a/test/CPPDEFINES/pkg-config.py +++ b/test/CPPDEFINES/pkg-config.py @@ -29,14 +29,14 @@ Verify merging with MergeFlags to CPPPDEFINES with various data types. """ import TestSCons +import TestCmd test = TestSCons.TestSCons() pkg_config_path = test.where_is('pkg-config') if not pkg_config_path: test.skip_test("Could not find 'pkg-config' in system PATH, skipping test.\n") - -pkg_config_path.replace("\\", "/") +pkg_config_path = pkg_config_path.replace("\\", "/") test.write('bug.pc', """\ prefix=/usr @@ -57,7 +57,18 @@ int main(int argc, char *argv[]) } """) +if TestCmd.IS_WINDOWS: + pkg_config_file = 'bug.pc' + pkg_config_tools = 'mingw' + pkg_config_cl_path = "" +else: + pkg_config_file = 'bug' + pkg_config_tools = 'default' + pkg_config_cl_path = "PKG_CONFIG_PATH=." + test.write('SConstruct', """\ +import os +import sys # Python3 dicts dont preserve order. Hence we supply subclass of OrderedDict # whose __str__ and __repr__ act like a normal dict. from collections import OrderedDict @@ -74,26 +85,28 @@ class OrderedPrintingDict(OrderedDict): """ + """ # https://github.com/SCons/scons/issues/2671 # Passing test cases -env_1 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST']) -env_1.ParseConfig('PKG_CONFIG_PATH=. "%(pkg_config_path)s" --cflags bug') +env_1 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST'], tools = ['%(pkg_config_tools)s']) +if sys.platform == 'win32': + os.environ['PKG_CONFIG_PATH'] = env_1.Dir('.').abspath.replace("\\\\" , "/") +env_1.ParseConfig('%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags %(pkg_config_file)s') print(env_1.subst('$_CPPDEFFLAGS')) -env_2 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST']) +env_2 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST'], tools = ['%(pkg_config_tools)s']) env_2.MergeFlags('-DSOMETHING -DVARIABLE=2') print(env_2.subst('$_CPPDEFFLAGS')) # Failing test cases -env_3 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)])) -env_3.ParseConfig('PKG_CONFIG_PATH=. "%(pkg_config_path)s" --cflags bug') +env_3 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)]), tools = ['%(pkg_config_tools)s']) +env_3.ParseConfig('%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags %(pkg_config_file)s') print(env_3.subst('$_CPPDEFFLAGS')) -env_4 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)])) +env_4 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)]), tools = ['%(pkg_config_tools)s']) env_4.MergeFlags('-DSOMETHING -DVARIABLE=2') print(env_4.subst('$_CPPDEFFLAGS')) # https://github.com/SCons/scons/issues/1738 -env_1738_1 = Environment(tools=['default']) -env_1738_1.ParseConfig('PKG_CONFIG_PATH=. "%(pkg_config_path)s" --cflags --libs bug') +env_1738_1 = Environment(tools = ['%(pkg_config_tools)s']) +env_1738_1.ParseConfig('%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags --libs %(pkg_config_file)s') env_1738_1.Append(CPPDEFINES={'value' : '1'}) print(env_1738_1.subst('$_CPPDEFFLAGS')) """%locals() ) -- cgit v0.12 From 4850f5bbc3761b3c84f2788e00968d990acd7e4c Mon Sep 17 00:00:00 2001 From: maiphi Date: Sat, 26 May 2018 16:13:15 +0200 Subject: Added missing FORTRANMODDIRPREFIX=-J to the gfortran tool. Also added a test which compiles a .f90 source file to an executable with gfortran and checks that module files are place in FORTRANMODDID correctly. --- src/CHANGES.txt | 1 + src/engine/SCons/Tool/gfortran.py | 2 + test/Fortran/gfortran.py | 77 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 test/Fortran/gfortran.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index f9cb5c4..0544f60 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -10,6 +10,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Philipp Maierhöfer - Added a __hash__ method to the class Scons.Subst.Literal. Required when substituting Literal objects when SCons runs with Python 3. + - Added missing FORTRANMODDIRPREFIX to the gfortran tool. From Richard West: - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. diff --git a/src/engine/SCons/Tool/gfortran.py b/src/engine/SCons/Tool/gfortran.py index d33bf52..45750d6 100644 --- a/src/engine/SCons/Tool/gfortran.py +++ b/src/engine/SCons/Tool/gfortran.py @@ -54,6 +54,8 @@ def generate(env): env['INC%sPREFIX' % dialect] = "-I" env['INC%sSUFFIX' % dialect] = "" + env['FORTRANMODDIRPREFIX'] = "-J" + def exists(env): return env.Detect('gfortran') diff --git a/test/Fortran/gfortran.py b/test/Fortran/gfortran.py new file mode 100644 index 0000000..deab52c --- /dev/null +++ b/test/Fortran/gfortran.py @@ -0,0 +1,77 @@ +#!/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 the gfortran tool compiles a .f90 file to an executable, +placing module files in the directory specified by FORTRANMODDIR. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_exe = TestSCons._exe + +gfortran = test.detect_tool('gfortran') + +if not gfortran: + test.skip_test("Could not find gfortran tool, skipping test.\n") + +test.write('SConstruct', """ +env = Environment(tools=['gfortran','link'], F90PATH='modules', FORTRANMODDIR='modules') +env.Program('test1', 'test1.f90') +""") + +test.write('test1.f90', """\ +module test1mod + implicit none + contains + subroutine hello + implicit none + print *, "hello" + end subroutine hello +end module test1mod +program main + use test1mod + implicit none + call hello() +end program main +""") + +test.run(arguments = '.') + +test.must_exist('test1' + _exe) +test.must_exist(['modules', 'test1mod.mod']) + +test.up_to_date(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From 0b25163bccfe740a7752bc0ed2d8f8fbe020580f Mon Sep 17 00:00:00 2001 From: maiphi Date: Sun, 27 May 2018 00:10:47 +0200 Subject: Added a gfortran test without FORTRANMODDIR set. --- test/Fortran/gfortran.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/test/Fortran/gfortran.py b/test/Fortran/gfortran.py index deab52c..c3dec97 100644 --- a/test/Fortran/gfortran.py +++ b/test/Fortran/gfortran.py @@ -26,7 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Verify that the gfortran tool compiles a .f90 file to an executable, -placing module files in the directory specified by FORTRANMODDIR. +one time with and one time without placing module files in a subdirectory +specified by FORTRANMODDIR. """ import TestSCons @@ -41,7 +42,7 @@ if not gfortran: test.skip_test("Could not find gfortran tool, skipping test.\n") test.write('SConstruct', """ -env = Environment(tools=['gfortran','link'], F90PATH='modules', FORTRANMODDIR='modules') +env = Environment(tools=['gfortran','link']) env.Program('test1', 'test1.f90') """) @@ -64,7 +65,36 @@ end program main test.run(arguments = '.') test.must_exist('test1' + _exe) -test.must_exist(['modules', 'test1mod.mod']) +test.must_exist('test1mod.mod') + +test.up_to_date(arguments = '.') + + +test.write('SConstruct', """ +env = Environment(tools=['gfortran','link'], F90PATH='modules', FORTRANMODDIR='modules') +env.Program('test2', 'test2.f90') +""") + +test.write('test2.f90', """\ +module test2mod + implicit none + contains + subroutine hello + implicit none + print *, "hello" + end subroutine hello +end module test2mod +program main + use test2mod + implicit none + call hello() +end program main +""") + +test.run(arguments = '.') + +test.must_exist('test2' + _exe) +test.must_exist(['modules', 'test2mod.mod']) test.up_to_date(arguments = '.') -- cgit v0.12 From e18cc8783f671eb696686887db5674a22f050814 Mon Sep 17 00:00:00 2001 From: maiphi Date: Sun, 27 May 2018 12:04:45 +0200 Subject: Make test FORTRANMODDIR.py work with non-empty FORTRANMODDIRPREFIX. --- test/Fortran/FORTRANMODDIR.py | 68 +++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/test/Fortran/FORTRANMODDIR.py b/test/Fortran/FORTRANMODDIR.py index 723efaf..e6f2413 100644 --- a/test/Fortran/FORTRANMODDIR.py +++ b/test/Fortran/FORTRANMODDIR.py @@ -24,6 +24,16 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +""" +Verify the following things: +* _FORTRANMODFLAG is correctly constructed from FORTRANMODDIRPREFIX and + FORTRANMODDIR. +* The dependency scanner does not expect a module file to be created + from a "module procedure" statement. +* The dependency scanner expects the module files to be created in the correct + module directory (is is verified by the test.up_to_date()). +""" + import TestSCons _python_ = TestSCons._python_ @@ -31,61 +41,59 @@ _exe = TestSCons._exe test = TestSCons.TestSCons() - - test.write('myfortran.py', r""" import os.path import re import sys +# case insensitive matching, because Fortran is case insensitive mod_regex = "(?im)^\\s*MODULE\\s+(?!PROCEDURE)(\\w+)" contents = open(sys.argv[2]).read() modules = re.findall(mod_regex, contents) -modules = [os.path.join(sys.argv[1], m.lower()+'.mod') for m in modules] +(prefix, moddir) = sys.argv[1].split('=') +if prefix != 'moduledir': + sys.exit(1) +modules = [os.path.join(moddir, m.lower()+'.mod') for m in modules] for t in sys.argv[3:] + modules: open(t, 'wb').write(('myfortran.py wrote %s\n' % os.path.split(t)[1]).encode()) -sys.exit(0) """) test.write('SConstruct', """ -env = Environment(FORTRANCOM = r'%(_python_)s myfortran.py $FORTRANMODDIR $SOURCE $TARGET', - FORTRANMODDIR = 'modules') +env = Environment(FORTRANCOM = r'%(_python_)s myfortran.py $_FORTRANMODFLAG $SOURCE $TARGET', + FORTRANMODDIRPREFIX='moduledir=', FORTRANMODDIR='modules') env.Object(target = 'test1.obj', source = 'test1.f') env.Object(target = 'sub2/test2.obj', source = 'test1.f', FORTRANMODDIR='${TARGET.dir}') env.Object(target = 'sub3/test3.obj', source = 'test1.f', - FORTRANCOM = r'%(_python_)s myfortran.py $_FORTRANMODFLAG $SOURCE $TARGET', + FORTRANCOM = r'%(_python_)s myfortran.py moduledir=$FORTRANMODDIR $SOURCE $TARGET', FORTRANMODDIR='${TARGET.dir}') """ % locals()) test.write('test1.f', """\ - PROGRAM TEST - USE MOD_FOO - USE MOD_BAR - PRINT *,'TEST.f' - CALL P - STOP - END - MODULE MOD_FOO - IMPLICIT NONE - CONTAINS - SUBROUTINE P - PRINT *,'mod_foo' - END SUBROUTINE P - END MODULE MOD_FOO - MODULE PROCEDURE MOD_BAR - IMPLICIT NONE - CONTAINS - SUBROUTINE P - PRINT *,'mod_bar' - END SUBROUTINE P - END MODULE MOD_BAR +module mod_foo + implicit none + interface q + module procedure p + end interface q + contains + subroutine p + implicit none + print *, 'mod_foo::p' + end subroutine p +end module mod_foo +program test + use mod_foo + implicit none + print *, 'test.f' + call p + call q +end """) test.run(arguments = '.', stderr = None) test.must_match('test1.obj', "myfortran.py wrote test1.obj\n") test.must_match(['modules', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n") -test.must_not_exist(['modules', 'mod_bar.mod']) +test.must_not_exist(['modules', 'p.mod']) test.must_match(['sub2', 'test2.obj'], "myfortran.py wrote test2.obj\n") test.must_match(['sub2', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n") @@ -95,8 +103,6 @@ test.must_match(['sub3', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n") test.up_to_date(arguments = '.') - - test.pass_test() # Local Variables: -- cgit v0.12 From 1d7a78c786aa3bd9ae45d34773b10be949dad25a Mon Sep 17 00:00:00 2001 From: maiphi Date: Sun, 27 May 2018 18:19:03 +0200 Subject: Test FORTRANMODDIR.py: use F90 everywhere. --- test/Fortran/FORTRANMODDIR.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/Fortran/FORTRANMODDIR.py b/test/Fortran/FORTRANMODDIR.py index e6f2413..61dcc45 100644 --- a/test/Fortran/FORTRANMODDIR.py +++ b/test/Fortran/FORTRANMODDIR.py @@ -31,7 +31,7 @@ Verify the following things: * The dependency scanner does not expect a module file to be created from a "module procedure" statement. * The dependency scanner expects the module files to be created in the correct - module directory (is is verified by the test.up_to_date()). + module directory (this is verified by the test.up_to_date()). """ import TestSCons @@ -58,17 +58,17 @@ for t in sys.argv[3:] + modules: """) test.write('SConstruct', """ -env = Environment(FORTRANCOM = r'%(_python_)s myfortran.py $_FORTRANMODFLAG $SOURCE $TARGET', +env = Environment(F90COM = r'%(_python_)s myfortran.py $_FORTRANMODFLAG $SOURCE $TARGET', FORTRANMODDIRPREFIX='moduledir=', FORTRANMODDIR='modules') -env.Object(target = 'test1.obj', source = 'test1.f') -env.Object(target = 'sub2/test2.obj', source = 'test1.f', +env.Object(target = 'test1.obj', source = 'test1.f90') +env.Object(target = 'sub2/test2.obj', source = 'test1.f90', FORTRANMODDIR='${TARGET.dir}') -env.Object(target = 'sub3/test3.obj', source = 'test1.f', - FORTRANCOM = r'%(_python_)s myfortran.py moduledir=$FORTRANMODDIR $SOURCE $TARGET', +env.Object(target = 'sub3/test3.obj', source = 'test1.f90', + F90COM = r'%(_python_)s myfortran.py moduledir=$FORTRANMODDIR $SOURCE $TARGET', FORTRANMODDIR='${TARGET.dir}') """ % locals()) -test.write('test1.f', """\ +test.write('test1.f90', """\ module mod_foo implicit none interface q @@ -83,7 +83,7 @@ end module mod_foo program test use mod_foo implicit none - print *, 'test.f' + print *, 'test1.f90' call p call q end -- cgit v0.12 From b649dec7073d1fb1e9fd08b57f2a95288eba6965 Mon Sep 17 00:00:00 2001 From: grbd Date: Mon, 28 May 2018 02:39:14 +0100 Subject: set the pip installation directory to be non version specific --- src/setup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/setup.py b/src/setup.py index 442315c..273da18 100644 --- a/src/setup.py +++ b/src/setup.py @@ -247,10 +247,12 @@ class install_lib(_install_lib): if Options.standalone_lib: # ...but they asked for a standalone directory. self.install_dir = os.path.join(prefix, "scons") - elif Options.version_lib or not Options.standard_lib: + elif Options.version_lib: # ...they asked for a version-specific directory, - # or they get it by default. self.install_dir = os.path.join(prefix, "scons-%s" % Version) + elif not Options.standard_lib: + # default. + self.install_dir = os.path.join(prefix, "scons") msg = "Installed SCons library modules into %s" % self.install_dir Installed.append(msg) -- cgit v0.12 From 1e4effee750f8a1bec2f982b1a46b9f4ab14cf04 Mon Sep 17 00:00:00 2001 From: grbd Date: Mon, 28 May 2018 02:45:35 +0100 Subject: updates Changes.txt --- src/CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 0544f60..f74565a 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -15,6 +15,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Richard West: - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. Allows easier debugging within Visual Studio + - Set the default pip installation directory to be non version specific + to aid with development of external tools (such as using pytest) From Bernard Blackham: - Fixed handling of side-effects in task master (fixes #3013). -- cgit v0.12 From 370e4f79cc9da517926270b8bda352b14e43cbf0 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 29 May 2018 16:53:56 -0700 Subject: Reworded description of scons setup.py changes. --- src/CHANGES.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index f74565a..b0baecb 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -15,8 +15,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Richard West: - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. Allows easier debugging within Visual Studio - - Set the default pip installation directory to be non version specific - to aid with development of external tools (such as using pytest) + - Change setup.py to change the install directory (via pip, or setup.py install) from scons-#.#.# + to scons (Yielding /lib/scons/SCons/ instead of /lib/scons/SCons-#.#.#/). + This changes SCons to better comply with normal Python installation practices. From Bernard Blackham: - Fixed handling of side-effects in task master (fixes #3013). -- cgit v0.12 From e5d006c8ee3b6e3b364153828db15fb0ed3f776b Mon Sep 17 00:00:00 2001 From: Isaac Pascual Monells Date: Tue, 19 Jun 2018 11:16:35 +0200 Subject: Fix documentation apostrophe --- src/engine/SCons/Environment.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Environment.xml b/src/engine/SCons/Environment.xml index ccee68d..324096f 100644 --- a/src/engine/SCons/Environment.xml +++ b/src/engine/SCons/Environment.xml @@ -60,7 +60,7 @@ or this: env = Environment() -env['BUILDERS]['NewBuilder'] = foo +env['BUILDERS']['NewBuilder'] = foo -- cgit v0.12 From 783a977936a6573ecc71dbbbe23116a2c94fcc51 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 22 Jun 2018 17:04:03 -0400 Subject: Fix bug where constant string in python action yielded trying to join an array of items one of which was unicode. --- src/engine/SCons/Util.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index ec29796..b566088 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -1610,9 +1610,13 @@ del __revision__ def to_bytes (s): if s is None: return b'None' + if not PY3 and isinstance(s, UnicodeType): + # PY2, must encode unicode + return bytes(s, 'utf-8') if isinstance (s, (bytes, bytearray)) or bytes is str: + # Above case not covered here as py2 bytes and strings are the same return s - return bytes (s, 'utf-8') + return bytes(s, 'utf-8') def to_str (s): if s is None: -- cgit v0.12 From 6856b04629a18e9147ee6838f630b0367c56e8d8 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 22 Jun 2018 17:25:38 -0400 Subject: Add test for py2 to_Bytes properly handling unicode string and converting it to a bytearray utf-8 encoded. This was breaking python action functions with unicode constant strings --- src/engine/SCons/UtilTests.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index 6b12f8a..38b4da9 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -307,6 +307,14 @@ class UtilTestCase(unittest.TestCase): if HasUnicode: exec("assert not is_Tuple(u'')") + def test_to_Bytes(self): + """ Test the to_Bytes method""" + if not PY3: + self.assertEqual(to_bytes(UnicodeType('Hello')), + bytearray(u'Hello', 'utf-8'), + "Check that to_bytes creates byte array when presented with unicode string. PY2 only") + + def test_to_String(self): """Test the to_String() method.""" assert to_String(1) == "1", to_String(1) @@ -843,17 +851,8 @@ class flattenTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ dictifyTestCase, - flattenTestCase, - MD5TestCase, - NodeListTestCase, - UtilTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() + # Local Variables: # tab-width:4 -- cgit v0.12 From 97f9431309596152d9cb093172e9752d2b744303 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 22 Jun 2018 17:26:07 -0400 Subject: Create bytearray and not bytes as no bytes type for py2 --- src/engine/SCons/Util.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index b566088..52f42d2 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -1607,18 +1607,20 @@ class NullSeq(Null): del __revision__ -def to_bytes (s): + +def to_bytes(s): if s is None: return b'None' if not PY3 and isinstance(s, UnicodeType): # PY2, must encode unicode - return bytes(s, 'utf-8') + return bytearray(s, 'utf-8') if isinstance (s, (bytes, bytearray)) or bytes is str: # Above case not covered here as py2 bytes and strings are the same return s return bytes(s, 'utf-8') -def to_str (s): + +def to_str(s): if s is None: return 'None' if bytes is str or is_String(s): @@ -1626,8 +1628,6 @@ def to_str (s): return str (s, 'utf-8') - -# No cmp in py3, so we'll define it. def cmp(a, b): """ Define cmp because it's no longer available in python3 -- cgit v0.12 From 256000d738fcbf4de12e9d76d4ca6b6317e70a33 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 22 Jun 2018 17:29:10 -0400 Subject: PEP8 plus remove testsuite logic and switch to simpler unittest.main(). This allows easily running a single test --- src/engine/SCons/ActionTests.py | 523 +++++++++++++++++++++++++--------------- 1 file changed, 327 insertions(+), 196 deletions(-) diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 2398c10..2959ede 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -23,6 +23,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + # Define a null function and a null class for use as builder actions. # Where these are defined in the file seems to affect their byte-code # contents, so try to minimize changes by defining them here, before we @@ -30,10 +31,12 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" def GlobalFunc(): pass + class GlobalActFunc(object): def __call__(self): pass + import collections import io import os @@ -56,7 +59,7 @@ import TestUnit # We don't do this as a setUp() method because it's # unnecessary to create a separate directory and script # for each test, they can just use the one. -test = TestCmd.TestCmd(workdir = '') +test = TestCmd.TestCmd(workdir='') test.write('act.py', """\ import os, string, sys @@ -100,6 +103,7 @@ scons_env = SCons.Environment.Environment() # so it doesn't clutter the output. sys.stdout = io.StringIO() + class CmdStringHolder(object): def __init__(self, cmd, literal=None): self.data = str(cmd) @@ -125,6 +129,7 @@ class CmdStringHolder(object): else: return self.data + class Environment(object): def __init__(self, **kw): self.d = {} @@ -134,53 +139,70 @@ class Environment(object): self.d['ESCAPE'] = scons_env['ESCAPE'] for k, v in kw.items(): self.d[k] = v + # Just use the underlying scons_subst*() utility methods. def subst(self, strSubst, raw=0, target=[], source=[], conv=None): return SCons.Subst.scons_subst(strSubst, self, raw, target, source, self.d, conv=conv) + subst_target_source = subst + def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None): return SCons.Subst.scons_subst_list(strSubst, self, raw, - target, source, self.d, conv=conv) + target, source, self.d, conv=conv) + def __getitem__(self, item): return self.d[item] + def __setitem__(self, item, value): self.d[item] = value + def has_key(self, item): return item in self.d + def get(self, key, value=None): return self.d.get(key, value) + def items(self): return list(self.d.items()) + def Dictionary(self): return self.d + def Clone(self, **kw): res = Environment() res.d = SCons.Util.semi_deepcopy(self.d) for k, v in kw.items(): res.d[k] = v return res + def sig_dict(self): d = {} - for k,v in self.items(): d[k] = v + for k, v in self.items(): d[k] = v d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__'] d['TARGET'] = d['TARGETS'][0] d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__'] d['SOURCE'] = d['SOURCES'][0] return d + class DummyNode(object): def __init__(self, name): self.name = name + def str_for_display(self): return '"' + self.name + '"' + def __str__(self): return self.name + def rfile(self): return self + def get_subst_proxy(self): return self + if os.name == 'java': python = os.path.join(sys.prefix, 'jython') else: @@ -189,6 +211,7 @@ _python_ = test.escape(python) _null = SCons.Action._null + def test_varlist(pos_call, str_call, cmd, cmdstrfunc, **kw): def call_action(a, pos_call=pos_call, str_call=str_call, kw=kw): a = SCons.Action.Action(*a, **kw) @@ -226,6 +249,7 @@ def test_varlist(pos_call, str_call, cmd, cmdstrfunc, **kw): a = call_action((cmd, cmdstrfunc, 'a', 'b', 'c')) assert a.varlist == ('a', 'b', 'c', 'x', 'y', 'z'), a.varlist + def test_positional_args(pos_callback, cmd, **kw): """Test that Action() returns the expected type and that positional args work. """ @@ -235,7 +259,9 @@ def test_positional_args(pos_callback, cmd, **kw): if not isinstance(act, SCons.Action._ActionAction): # only valid cmdstrfunc is None - def none(a): pass + def none(a): + pass + test_varlist(pos_callback, none, cmd, None, **kw) else: # _ActionAction should have set these @@ -244,22 +270,27 @@ def test_positional_args(pos_callback, cmd, **kw): assert act.presub is _null, act.presub assert act.chdir is None, act.chdir assert act.exitstatfunc is SCons.Action.default_exitstatfunc, \ - act.exitstatfunc + act.exitstatfunc def cmdstr(a): assert hasattr(a, 'strfunction') assert a.cmdstr == 'cmdstr', a.cmdstr + test_varlist(pos_callback, cmdstr, cmd, 'cmdstr', **kw) - def fun(): pass + def fun(): + pass + def strfun(a, fun=fun): assert a.strfunction is fun, a.strfunction assert a.cmdstr == _null, a.cmdstr + test_varlist(pos_callback, strfun, cmd, fun, **kw) def none(a): assert hasattr(a, 'strfunction') assert a.cmdstr is None, a.cmdstr + test_varlist(pos_callback, none, cmd, None, **kw) """Test handling of bad cmdstrfunc arguments """ @@ -274,18 +305,21 @@ def test_positional_args(pos_callback, cmd, **kw): return act + class ActionTestCase(unittest.TestCase): """Test the Action() factory function""" def test_FunctionAction(self): """Test the Action() factory's creation of FunctionAction objects """ + def foo(): pass def func_action(a, foo=foo): assert isinstance(a, SCons.Action.FunctionAction), a assert a.execfunction == foo, a.execfunction + test_positional_args(func_action, foo) # a singleton list returns the contained action test_positional_args(func_action, [foo]) @@ -293,28 +327,33 @@ class ActionTestCase(unittest.TestCase): def test_CommandAction(self): """Test the Action() factory's creation of CommandAction objects """ + def cmd_action(a): assert isinstance(a, SCons.Action.CommandAction), a assert a.cmd_list == "string", a.cmd_list + test_positional_args(cmd_action, "string") # a singleton list returns the contained action test_positional_args(cmd_action, ["string"]) - try: unicode - except NameError: pass + try: + unicode + except NameError: + pass else: a2 = eval("SCons.Action.Action(u'string')") assert isinstance(a2, SCons.Action.CommandAction), a2 def line_action(a): assert isinstance(a, SCons.Action.CommandAction), a - assert a.cmd_list == [ "explicit", "command", "line" ], a.cmd_list - test_positional_args(line_action, [[ "explicit", "command", "line" ]]) + assert a.cmd_list == ["explicit", "command", "line"], a.cmd_list + + test_positional_args(line_action, [["explicit", "command", "line"]]) def test_ListAction(self): """Test the Action() factory's creation of ListAction objects """ - a1 = SCons.Action.Action(["x", "y", "z", [ "a", "b", "c"]]) + a1 = SCons.Action.Action(["x", "y", "z", ["a", "b", "c"]]) assert isinstance(a1, SCons.Action.ListAction), a1 assert a1.varlist == (), a1.varlist assert isinstance(a1.list[0], SCons.Action.CommandAction), a1.list[0] @@ -324,7 +363,7 @@ class ActionTestCase(unittest.TestCase): assert isinstance(a1.list[2], SCons.Action.CommandAction), a1.list[2] assert a1.list[2].cmd_list == "z", a1.list[2].cmd_list assert isinstance(a1.list[3], SCons.Action.CommandAction), a1.list[3] - assert a1.list[3].cmd_list == [ "a", "b", "c" ], a1.list[3].cmd_list + assert a1.list[3].cmd_list == ["a", "b", "c"], a1.list[3].cmd_list a2 = SCons.Action.Action("x\ny\nz") assert isinstance(a2, SCons.Action.ListAction), a2 @@ -372,20 +411,24 @@ class ActionTestCase(unittest.TestCase): def test_CommandGeneratorAction(self): """Test the Action() factory's creation of CommandGeneratorAction objects """ + def foo(): pass def gen_action(a, foo=foo): assert isinstance(a, SCons.Action.CommandGeneratorAction), a assert a.generator is foo, a.generator + test_positional_args(gen_action, foo, generator=1) def test_LazyCmdGeneratorAction(self): """Test the Action() factory's creation of lazy CommandGeneratorAction objects """ + def lazy_action(a): assert isinstance(a, SCons.Action.LazyAction), a assert a.var == "FOO", a.var assert a.cmd_list == "${FOO}", a.cmd_list + test_positional_args(lazy_action, "$FOO") test_positional_args(lazy_action, "${FOO}") @@ -406,6 +449,7 @@ class ActionTestCase(unittest.TestCase): a2 = SCons.Action.Action(a1) assert a2 is a1, a2 + class _ActionActionTestCase(unittest.TestCase): def test__init__(self): @@ -429,7 +473,7 @@ class _ActionActionTestCase(unittest.TestCase): assert a.chdir is None, a.chdir assert a.exitstatfunc is SCons.Action.default_exitstatfunc, a.exitstatfunc - assert SCons.Action._ActionAction(kwarg = 1) + assert SCons.Action._ActionAction(kwarg=1) assert not hasattr(a, 'kwarg') assert not hasattr(a, 'strfunction') assert a.cmdstr is _null, a.cmdstr @@ -453,7 +497,7 @@ class _ActionActionTestCase(unittest.TestCase): assert not hasattr(a, 'strfunction') assert a.cmdstr is None, a.cmdstr - t = ('a','b','c') + t = ('a', 'b', 'c') a = SCons.Action._ActionAction(varlist=t) assert a.varlist == t, a.varlist @@ -467,14 +511,14 @@ class _ActionActionTestCase(unittest.TestCase): assert a.exitstatfunc is func1, a.exitstatfunc a = SCons.Action._ActionAction( - # alphabetical order ... - chdir='x', - cmdstr='cmdstr', - exitstatfunc=func3, - presub=func2, - strfunction=func1, - varlist=t, - ) + # alphabetical order ... + chdir='x', + cmdstr='cmdstr', + exitstatfunc=func3, + presub=func2, + strfunction=func1, + varlist=t, + ) assert a.chdir is 'x', a.chdir assert a.cmdstr is 'cmdstr', a.cmdstr assert a.exitstatfunc is func3, a.exitstatfunc @@ -485,7 +529,10 @@ class _ActionActionTestCase(unittest.TestCase): def test_dup_keywords(self): """Test handling of both cmdstr and strfunction arguments """ - def func(): pass + + def func(): + pass + try: a = SCons.Action.Action('foo', cmdstr='string', strfunction=func) except SCons.Errors.UserError as e: @@ -513,6 +560,7 @@ class _ActionActionTestCase(unittest.TestCase): try: def execfunc(target, source, env): pass + a = SCons.Action.Action(execfunc) sio = io.StringIO() @@ -532,9 +580,9 @@ class _ActionActionTestCase(unittest.TestCase): save_print_actions = SCons.Action.print_actions save_print_actions_presub = SCons.Action.print_actions_presub save_execute_actions = SCons.Action.execute_actions - #SCons.Action.print_actions = 0 + # SCons.Action.print_actions = 0 - test = TestCmd.TestCmd(workdir = '') + test = TestCmd.TestCmd(workdir='') test.subdir('sub', 'xyz') os.chdir(test.workpath()) @@ -545,16 +593,19 @@ class _ActionActionTestCase(unittest.TestCase): assert isinstance(target, list), type(target) assert isinstance(source, list), type(source) return 7 + a = SCons.Action.Action(execfunc) def firstfunc(target, source, env): assert isinstance(target, list), type(target) assert isinstance(source, list), type(source) return 0 + def lastfunc(target, source, env): assert isinstance(target, list), type(target) assert isinstance(source, list), type(source) return 9 + b = SCons.Action.Action([firstfunc, execfunc, lastfunc]) sio = io.StringIO() @@ -694,8 +745,10 @@ class _ActionActionTestCase(unittest.TestCase): SCons.Action.execute_actions = 1 result = [] + def my_print_cmd_line(s, target, source, env, result=result): result.append(s) + env['PRINT_CMD_LINE_FUNC'] = my_print_cmd_line a("output", "input", env) assert result == ["execfunc(['output'], ['input'])"], result @@ -721,39 +774,43 @@ class _ActionActionTestCase(unittest.TestCase): def func(): pass + a = SCons.Action.Action(func) s = a.presub_lines(env) assert s == ["func(target, source, env)"], s def gen(target, source, env, for_signature): return 'generat' + env.get('GEN', 'or') + a = SCons.Action.Action(gen, generator=1) s = a.presub_lines(env) assert s == ["generator"], s - s = a.presub_lines(Environment(GEN = 'ed')) + s = a.presub_lines(Environment(GEN='ed')) assert s == ["generated"], s a = SCons.Action.Action("$ACT") s = a.presub_lines(env) assert s == [''], s - s = a.presub_lines(Environment(ACT = 'expanded action')) + s = a.presub_lines(Environment(ACT='expanded action')) assert s == ['expanded action'], s def test_add(self): """Test adding Actions to stuff.""" + # Adding actions to other Actions or to stuff that can # be converted into an Action should produce a ListAction # containing all the Actions. def bar(): return None + baz = SCons.Action.Action(bar, generator=1) act1 = SCons.Action.Action('foo bar') - act2 = SCons.Action.Action([ 'foo', bar ]) + act2 = SCons.Action.Action(['foo', bar]) sum = act1 + act2 assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 3, len(sum.list) - assert [isinstance(x, SCons.Action.ActionBase) for x in sum.list] == [ 1, 1, 1 ] + assert [isinstance(x, SCons.Action.ActionBase) for x in sum.list] == [1, 1, 1] sum = act1 + act1 assert isinstance(sum, SCons.Action.ListAction), str(sum) @@ -789,13 +846,13 @@ class _ActionActionTestCase(unittest.TestCase): assert len(sum.list) == 3, len(sum.list) assert isinstance(sum.list[0], SCons.Action.CommandAction) - sum = [ 'foo', 'bar' ] + act1 + sum = ['foo', 'bar'] + act1 assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 3, sum.list assert isinstance(sum.list[0], SCons.Action.CommandAction) assert isinstance(sum.list[1], SCons.Action.CommandAction) - sum = act2 + [ baz, bar ] + sum = act2 + [baz, bar] assert isinstance(sum, SCons.Action.ListAction), str(sum) assert len(sum.list) == 4, len(sum.list) assert isinstance(sum.list[2], SCons.Action.CommandGeneratorAction) @@ -822,17 +879,18 @@ class _ActionActionTestCase(unittest.TestCase): else: assert 0, "Should have thrown a TypeError adding to an int." + class CommandActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a command Action """ a = SCons.Action.CommandAction(["xyzzy"]) - assert a.cmd_list == [ "xyzzy" ], a.cmd_list + assert a.cmd_list == ["xyzzy"], a.cmd_list assert a.cmdstr is _null, a.cmdstr a = SCons.Action.CommandAction(["abra"], cmdstr="cadabra") - assert a.cmd_list == [ "abra" ], a.cmd_list + assert a.cmd_list == ["abra"], a.cmd_list assert a.cmdstr == "cadabra", a.cmdstr def test___str__(self): @@ -952,6 +1010,7 @@ class CommandActionTestCase(unittest.TestCase): def sf(target, source, env): return "sf was called" + act = SCons.Action.CommandAction('foo', strfunction=sf) s = act.strfunction([], [], env) assert s == "sf was called", s @@ -959,26 +1018,35 @@ class CommandActionTestCase(unittest.TestCase): class actclass1(object): def __init__(self, targets, sources, env): pass + def __call__(self): return 1 + class actclass2(object): def __init__(self, targets, sources, env): self.strfunction = 5 + def __call__(self): return 2 + class actclass3(object): def __init__(self, targets, sources, env): pass + def __call__(self): return 3 + def strfunction(self, targets, sources, env): - return 'actclass3 on %s to get %s'%(str(sources[0]), - str(targets[0])) + return 'actclass3 on %s to get %s' % (str(sources[0]), + str(targets[0])) + class actclass4(object): def __init__(self, targets, sources, env): pass + def __call__(self): return 4 + strfunction = None act1 = SCons.Action.Action(actclass1([t1], [s1], env)) @@ -1075,7 +1143,7 @@ class CommandActionTestCase(unittest.TestCase): act = SCons.Action.CommandAction(cmd4) sources = [DummyNode('three'), DummyNode('four'), DummyNode('five')] env2 = env.Clone() - r = act([], source = sources, env = env2) + r = act([], source=sources, env=env2) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'three' 'four'\n", c @@ -1092,13 +1160,13 @@ class CommandActionTestCase(unittest.TestCase): PATH = '' env5['ENV']['XYZZY'] = 'xyzzy' - r = act(target = DummyNode('out5'), source = [], env = env5) + r = act(target=DummyNode('out5'), source=[], env=env5) act = SCons.Action.CommandAction(cmd5) - r = act(target = DummyNode('out5'), - source = [], - env = env.Clone(ENV = {'XYZZY' : 'xyzzy5', - 'PATH' : PATH})) + r = act(target=DummyNode('out5'), + source=[], + env=env.Clone(ENV={'XYZZY': 'xyzzy5', + 'PATH': PATH})) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy5'\n", c @@ -1106,19 +1174,22 @@ class CommandActionTestCase(unittest.TestCase): class Obj(object): def __init__(self, str): self._str = str + def __str__(self): return self._str + def rfile(self): return self + def get_subst_proxy(self): return self cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (_python_, act_py, outfile) act = SCons.Action.CommandAction(cmd6) - r = act(target = [Obj('111'), Obj('222')], - source = [Obj('333'), Obj('444'), Obj('555')], - env = env.Clone()) + r = act(target=[Obj('111'), Obj('222')], + source=[Obj('333'), Obj('444'), Obj('555')], + env=env.Clone()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: '222' '111' '333' '444'\n", c @@ -1133,7 +1204,7 @@ class CommandActionTestCase(unittest.TestCase): expect_nonexistent = 127 # Newer cygwin seems to return 126 for following expect_nonexecutable_file = 126 - expect_nonexecutable_dir = 127 + expect_nonexecutable_dir = 127 elif sys.platform.find('sunos') != -1: expect_nonexistent = 1 expect_nonexecutable_file = 1 @@ -1141,22 +1212,22 @@ class CommandActionTestCase(unittest.TestCase): else: expect_nonexistent = 127 expect_nonexecutable_file = 126 - expect_nonexecutable_dir = 126 + expect_nonexecutable_dir = 126 # Test that a nonexistent command returns 127 act = SCons.Action.CommandAction(python + "_no_such_command_") - r = act([], [], env.Clone(out = outfile)) + r = act([], [], env.Clone(out=outfile)) assert r.status == expect_nonexistent, r.status # Test that trying to execute a directory returns 126 dir, tail = os.path.split(python) act = SCons.Action.CommandAction(dir) - r = act([], [], env.Clone(out = outfile)) + r = act([], [], env.Clone(out=outfile)) assert r.status == expect_nonexecutable_file, r.status # Test that trying to execute a non-executable file returns 126 act = SCons.Action.CommandAction(outfile) - r = act([], [], env.Clone(out = outfile)) + r = act([], [], env.Clone(out=outfile)) assert r.status == expect_nonexecutable_dir, r.status act = SCons.Action.CommandAction('%s %s 1' % (_python_, exit_py)) @@ -1190,51 +1261,59 @@ class CommandActionTestCase(unittest.TestCase): def test_set_handler(self): """Test setting the command handler... """ + class Test(object): def __init__(self): self.executed = 0 - t=Test() + + t = Test() + def func(sh, escape, cmd, args, env, test=t): test.executed = args test.shell = sh return 0 + def escape_func(cmd): return '**' + cmd + '**' class LiteralStr(object): def __init__(self, x): self.data = x + def __str__(self): return self.data + def escape(self, escape_func): return escape_func(self.data) + def is_literal(self): return 1 a = SCons.Action.CommandAction(["xyzzy"]) - e = Environment(SPAWN = func) + e = Environment(SPAWN=func) a([], [], e) - assert t.executed == [ 'xyzzy' ], t.executed + assert t.executed == ['xyzzy'], t.executed a = SCons.Action.CommandAction(["xyzzy"]) - e = Environment(SPAWN = '$FUNC', FUNC = func) + e = Environment(SPAWN='$FUNC', FUNC=func) a([], [], e) - assert t.executed == [ 'xyzzy' ], t.executed + assert t.executed == ['xyzzy'], t.executed a = SCons.Action.CommandAction(["xyzzy"]) - e = Environment(SPAWN = func, SHELL = 'fake shell') + e = Environment(SPAWN=func, SHELL='fake shell') a([], [], e) - assert t.executed == [ 'xyzzy' ], t.executed + assert t.executed == ['xyzzy'], t.executed assert t.shell == 'fake shell', t.shell - a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ]) - e = Environment(SPAWN = func, ESCAPE = escape_func) + a = SCons.Action.CommandAction([LiteralStr("xyzzy")]) + e = Environment(SPAWN=func, ESCAPE=escape_func) a([], [], e) - assert t.executed == [ '**xyzzy**' ], t.executed + assert t.executed == ['**xyzzy**'], t.executed def test_get_contents(self): """Test fetching the contents of a command Action """ + def CmdGen(target, source, env, for_signature): assert for_signature return "%s %s" % \ @@ -1242,10 +1321,10 @@ class CommandActionTestCase(unittest.TestCase): # The number 1 is there to make sure all args get converted to strings. a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$(", "$bar", - "$)", "stuff", "$)", "|", "$baz", 1]) + "$)", "stuff", "$)", "|", "$baz", 1]) c = a.get_contents(target=[], source=[], - env=Environment(foo = 'FFF', bar = 'BBB', - baz = CmdGen)) + env=Environment(foo='FFF', bar='BBB', + baz=CmdGen)) assert c == b"| | FFF BBB 1", c # Make sure that CommandActions use an Environment's @@ -1254,9 +1333,9 @@ class CommandActionTestCase(unittest.TestCase): def subst_target_source(self, strSubst, raw=0, target=[], source=[]): return 'subst_target_source: ' + strSubst - c = a.get_contents(target=DummyNode('ttt'), source = DummyNode('sss'), - env=SpecialEnvironment(foo = 'GGG', bar = 'CCC', - baz = 'ZZZ')) + c = a.get_contents(target=DummyNode('ttt'), source=DummyNode('sss'), + env=SpecialEnvironment(foo='GGG', bar='CCC', + baz='ZZZ')) assert c == b'subst_target_source: | $( $foo | $( $bar $) stuff $) | $baz 1', c # We've discussed using the real target and source names in a @@ -1304,6 +1383,7 @@ class CommandActionTestCase(unittest.TestCase): c = a.get_contents(target=t, source=s, env=env) assert c == b"s4 s5", c + class CommandGeneratorActionTestCase(unittest.TestCase): def factory(self, act, **kw): @@ -1313,16 +1393,18 @@ class CommandGeneratorActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a command generator Action """ + def f(target, source, env): pass + a = self.factory(f) assert a.generator == f def test___str__(self): """Test the pre-substitution strings for command generator Actions """ - def f(target, source, env, for_signature, self=self): + def f(target, source, env, for_signature, self=self): # See if "env" is really a construction environment (or # looks like one) by accessing the FindIxes attribute. # (The Tool/mingw.py module has a generator that uses this, @@ -1331,6 +1413,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase): env.FindIxes return "FOO" + a = self.factory(f) s = str(a) assert s == 'FOO', s @@ -1338,10 +1421,12 @@ class CommandGeneratorActionTestCase(unittest.TestCase): def test_genstring(self): """Test the command generator Action genstring() method """ + def f(target, source, env, for_signature, self=self): dummy = env['dummy'] self.dummy = dummy return "$FOO $TARGET $SOURCE $TARGETS $SOURCES" + a = self.factory(f) self.dummy = 0 s = a.genstring([], [], env=Environment(FOO='xyzzy', dummy=1)) @@ -1358,13 +1443,16 @@ class CommandGeneratorActionTestCase(unittest.TestCase): s = env.subst("$FOO") assert s == 'foo baz\nbar ack', s return "$FOO" + def func_action(target, source, env, self=self): - dummy=env['dummy'] + dummy = env['dummy'] s = env.subst('$foo') assert s == 'bar', s - self.dummy=dummy + self.dummy = dummy + def f2(target, source, env, for_signature, f=func_action): return f + def ch(sh, escape, cmd, args, env, self=self): self.cmd.append(cmd) self.args.append(args) @@ -1373,30 +1461,34 @@ class CommandGeneratorActionTestCase(unittest.TestCase): self.dummy = 0 self.cmd = [] self.args = [] - a([], [], env=Environment(FOO = 'foo baz\nbar ack', - dummy = 1, - SPAWN = ch)) + a([], [], env=Environment(FOO='foo baz\nbar ack', + dummy=1, + SPAWN=ch)) assert self.dummy == 1, self.dummy assert self.cmd == ['foo', 'bar'], self.cmd - assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args + assert self.args == [['foo', 'baz'], ['bar', 'ack']], self.args b = self.factory(f2) self.dummy = 0 - b(target=[], source=[], env=Environment(foo = 'bar', - dummy = 2 )) - assert self.dummy==2, self.dummy + b(target=[], source=[], env=Environment(foo='bar', + dummy=2)) + assert self.dummy == 2, self.dummy del self.dummy class DummyFile(object): def __init__(self, t): self.t = t + def rfile(self): self.t.rfile_called = 1 return self + def get_subst_proxy(self): return self + def f3(target, source, env, for_signature): return '' + c = self.factory(f3) c(target=[], source=DummyFile(self), env=Environment()) assert self.rfile_called @@ -1404,19 +1496,20 @@ class CommandGeneratorActionTestCase(unittest.TestCase): def test_get_contents(self): """Test fetching the contents of a command generator Action """ + def f(target, source, env, for_signature): foo = env['foo'] bar = env['bar'] assert for_signature, for_signature return [["guux", foo, "$(", "$ignore", "$)", bar, - '${test("$( foo $bar $)")}' ]] + '${test("$( foo $bar $)")}']] def test(mystr): assert mystr == "$( foo $bar $)", mystr return "test" - env = Environment(foo = 'FFF', bar = 'BBB', - ignore = 'foo', test=test) + env = Environment(foo='FFF', bar='BBB', + ignore='foo', test=test) a = self.factory(f) c = a.get_contents(target=[], source=[], env=env) assert c == b"guux FFF BBB test", c @@ -1430,9 +1523,9 @@ class CommandGeneratorActionTestCase(unittest.TestCase): # Since the python bytecode has per version differences, we need different expected results per version func_matches = { - (2,7) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3,5) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3,6) : bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (2, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), + (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), + (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), } meth_matches = [ @@ -1446,15 +1539,17 @@ class CommandGeneratorActionTestCase(unittest.TestCase): def f_local(target, source, env, for_signature): return SCons.Action.Action(LocalFunc) - env = Environment(XYZ = 'foo') + env = Environment(XYZ='foo') a = self.factory(f_global) c = a.get_contents(target=[], source=[], env=env) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+repr(func_matches[sys.version_info[:2]]) + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + repr( + func_matches[sys.version_info[:2]]) a = self.factory(f_local) c = a.get_contents(target=[], source=[], env=env) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+repr(func_matches[sys.version_info[:2]]) + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + repr( + func_matches[sys.version_info[:2]]) def f_global(target, source, env, for_signature): return SCons.Action.Action(GlobalFunc, varlist=['XYZ']) @@ -1478,12 +1573,16 @@ class FunctionActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a function Action """ + def func1(): pass + def func2(): pass + def func3(): pass + def func4(): pass @@ -1491,15 +1590,17 @@ class FunctionActionTestCase(unittest.TestCase): assert a.execfunction == func1, a.execfunction assert isinstance(a.strfunction, types.MethodType), type(a.strfunction) - a = SCons.Action.FunctionAction(func2, { 'strfunction' : func3 }) + a = SCons.Action.FunctionAction(func2, {'strfunction': func3}) assert a.execfunction == func2, a.execfunction assert a.strfunction == func3, a.strfunction def test___str__(self): """Test the __str__() method for function Actions """ + def func1(): pass + a = SCons.Action.FunctionAction(func1, {}) s = str(a) assert s == "func1(target, source, env)", s @@ -1507,6 +1608,7 @@ class FunctionActionTestCase(unittest.TestCase): class class1(object): def __call__(self): pass + a = SCons.Action.FunctionAction(class1(), {}) s = str(a) assert s == "class1(target, source, env)", s @@ -1515,22 +1617,25 @@ class FunctionActionTestCase(unittest.TestCase): """Test executing a function Action """ self.inc = 0 + def f(target, source, env): s = env['s'] s.inc = s.inc + 1 s.target = target - s.source=source + s.source = source assert env.subst("$BAR") == 'foo bar', env.subst("$BAR") return 0 + a = SCons.Action.FunctionAction(f, {}) - a(target=1, source=2, env=Environment(BAR = 'foo bar', - s = self)) + a(target=1, source=2, env=Environment(BAR='foo bar', + s=self)) assert self.inc == 1, self.inc assert self.source == [2], self.source assert self.target == [1], self.target global count count = 0 + def function1(target, source, env): global count count = count + 1 @@ -1540,7 +1645,7 @@ class FunctionActionTestCase(unittest.TestCase): return 1 act = SCons.Action.FunctionAction(function1, {}) - r = act(target = [outfile, outfile2], source=[], env=Environment()) + r = act(target=[outfile, outfile2], source=[], env=Environment()) assert r.status == 1, r.status assert count == 1, count @@ -1555,7 +1660,7 @@ class FunctionActionTestCase(unittest.TestCase): f.write("class1a\n") act = SCons.Action.FunctionAction(class1a, {}) - r = act([], [], Environment(out = outfile)) + r = act([], [], Environment(out=outfile)) assert isinstance(r.status, class1a), r.status c = test.read(outfile, 'r') assert c == "class1a\n", c @@ -1567,7 +1672,7 @@ class FunctionActionTestCase(unittest.TestCase): return 2 act = SCons.Action.FunctionAction(class1b(), {}) - r = act([], [], Environment(out = outfile)) + r = act([], [], Environment(out=outfile)) assert r.status == 2, r.status c = test.read(outfile, 'r') assert c == "class1b\n", c @@ -1575,11 +1680,13 @@ class FunctionActionTestCase(unittest.TestCase): def build_it(target, source, env, executor=None, self=self): self.build_it = 1 return 0 + def string_it(target, source, env, executor=None, self=self): self.string_it = 1 return None + act = SCons.Action.FunctionAction(build_it, - { 'strfunction' : string_it }) + {'strfunction': string_it}) r = act([], [], Environment()) assert r == 0, r assert self.build_it @@ -1593,15 +1700,15 @@ class FunctionActionTestCase(unittest.TestCase): pass func_matches = { - (2,7) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3,5) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3,6) : bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (2, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), + (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), + (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), } meth_matches = { - (2,7) : bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'), - (3,5) : bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'), - (3,6) : bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), + (2, 7): bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'), + (3, 5): bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'), + (3, 6): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), } def factory(act, **kw): @@ -1609,18 +1716,20 @@ class FunctionActionTestCase(unittest.TestCase): a = factory(GlobalFunc) c = a.get_contents(target=[], source=[], env=Environment()) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]]) - + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + func_matches[sys.version_info[:2]]) a = factory(LocalFunc) c = a.get_contents(target=[], source=[], env=Environment()) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]]) + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + func_matches[sys.version_info[:2]]) matches_foo = func_matches[sys.version_info[:2]] + b'foo' a = factory(GlobalFunc, varlist=['XYZ']) c = a.get_contents(target=[], source=[], env=Environment()) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]]) + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + func_matches[sys.version_info[:2]]) # assert c in func_matches, repr(c) c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo')) @@ -1631,8 +1740,8 @@ class FunctionActionTestCase(unittest.TestCase): a = factory(GlobalFunc, varlist='XYZ') c = a.get_contents(target=[], source=[], env=Environment()) # assert c in func_matches, repr(c) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]]) - + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + func_matches[sys.version_info[:2]]) c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo')) assert c in matches_foo, repr(c) @@ -1640,6 +1749,7 @@ class FunctionActionTestCase(unittest.TestCase): class Foo(object): def get_contents(self, target, source, env): return b'xyzzy' + a = factory(Foo()) c = a.get_contents(target=[], source=[], env=Environment()) assert c == b'xyzzy', repr(c) @@ -1647,14 +1757,17 @@ class FunctionActionTestCase(unittest.TestCase): class LocalClass(object): def LocalMethod(self): pass + lc = LocalClass() a = factory(lc.LocalMethod) c = a.get_contents(target=[], source=[], env=Environment()) - assert c == meth_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(meth_matches[sys.version_info[:2]]) + assert c == meth_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + meth_matches[sys.version_info[:2]]) def test_strfunction(self): """Test the FunctionAction.strfunction() method """ + def func(): pass @@ -1673,13 +1786,16 @@ class FunctionActionTestCase(unittest.TestCase): s = a.strfunction(target=[], source=[], env=Environment()) assert s == 'function', s + class ListActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a list of subsidiary Actions """ + def func(): pass + a = SCons.Action.ListAction(["x", func, ["y", "z"]]) assert isinstance(a.list[0], SCons.Action.CommandAction) assert isinstance(a.list[1], SCons.Action.FunctionAction) @@ -1689,10 +1805,13 @@ class ListActionTestCase(unittest.TestCase): def test___str__(self): """Test the __str__() method for a list of subsidiary Actions """ - def f(target,source,env): + + def f(target, source, env): pass - def g(target,source,env): + + def g(target, source, env): pass + a = SCons.Action.ListAction([f, g, "XXX", f]) s = str(a) assert s == "f(target, source, env)\ng(target, source, env)\nXXX\nf(target, source, env)", s @@ -1700,10 +1819,13 @@ class ListActionTestCase(unittest.TestCase): def test_genstring(self): """Test the genstring() method for a list of subsidiary Actions """ - def f(target,source,env): + + def f(target, source, env): pass - def g(target,source,env,for_signature): + + def g(target, source, env, for_signature): return 'generated %s %s' % (target[0], source[0]) + g = SCons.Action.Action(g, generator=1) a = SCons.Action.ListAction([f, g, "XXX", f]) s = a.genstring(['foo.x'], ['bar.y'], Environment()) @@ -1713,11 +1835,13 @@ class ListActionTestCase(unittest.TestCase): """Test executing a list of subsidiary Actions """ self.inc = 0 - def f(target,source,env): + + def f(target, source, env): s = env['s'] s.inc = s.inc + 1 + a = SCons.Action.ListAction([f, f, f]) - a([], [], Environment(s = self)) + a([], [], Environment(s=self)) assert self.inc == 3, self.inc cmd2 = r'%s %s %s syzygy' % (_python_, act_py, outfile) @@ -1737,8 +1861,9 @@ class ListActionTestCase(unittest.TestCase): def __init__(self, target, source, env): with open(env['out'], 'a') as f: f.write("class2b\n") + act = SCons.Action.ListAction([cmd2, function2, class2a(), class2b]) - r = act([], [], Environment(out = outfile)) + r = act([], [], Environment(out=outfile)) assert isinstance(r.status, class2b), r.status c = test.read(outfile, 'r') assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c @@ -1746,18 +1871,21 @@ class ListActionTestCase(unittest.TestCase): def test_get_contents(self): """Test fetching the contents of a list of subsidiary Actions """ - self.foo=0 + self.foo = 0 + def gen(target, source, env, for_signature): s = env['s'] - s.foo=1 + s.foo = 1 return "y" + a = SCons.Action.ListAction(["x", SCons.Action.Action(gen, generator=1), "z"]) - c = a.get_contents(target=[], source=[], env=Environment(s = self)) - assert self.foo==1, self.foo + c = a.get_contents(target=[], source=[], env=Environment(s=self)) + assert self.foo == 1, self.foo assert c == b"xyz", c + class LazyActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a lazy-evaluation Action @@ -1776,8 +1904,10 @@ class LazyActionTestCase(unittest.TestCase): def test_genstring(self): """Test the lazy-evaluation Action genstring() method """ + def f(target, source, env): pass + a = SCons.Action.Action('$BAR') env1 = Environment(BAR=f, s=self) env2 = Environment(BAR='xxx', s=self) @@ -1789,15 +1919,17 @@ class LazyActionTestCase(unittest.TestCase): def test_execute(self): """Test executing a lazy-evaluation Action """ + def f(target, source, env): s = env['s'] - s.test=1 + s.test = 1 return 0 + a = SCons.Action.Action('$BAR') - a([], [], env=Environment(BAR = f, s = self)) + a([], [], env=Environment(BAR=f, s=self)) assert self.test == 1, self.test cmd = r'%s %s %s lazy' % (_python_, act_py, outfile) - a([], [], env=Environment(BAR = cmd, s = self)) + a([], [], env=Environment(BAR=cmd, s=self)) c = test.read(outfile, 'r') assert c == "act.py: 'lazy'\n", c @@ -1805,7 +1937,7 @@ class LazyActionTestCase(unittest.TestCase): """Test fetching the contents of a lazy-evaluation Action """ a = SCons.Action.Action("${FOO}") - env = Environment(FOO = [["This", "is", "a", "test"]]) + env = Environment(FOO=[["This", "is", "a", "test"]]) c = a.get_contents(target=[], source=[], env=env) assert c == b"This is a test", c @@ -1816,11 +1948,10 @@ class LazyActionTestCase(unittest.TestCase): def LocalFunc(): pass - func_matches = { - (2,7) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3,5) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3,6) : bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (2, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), + (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), + (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), } meth_matches = [ @@ -1831,27 +1962,26 @@ class LazyActionTestCase(unittest.TestCase): def factory(act, **kw): return SCons.Action.FunctionAction(act, kw) - a = SCons.Action.Action("${FOO}") - env = Environment(FOO = factory(GlobalFunc)) + env = Environment(FOO=factory(GlobalFunc)) c = a.get_contents(target=[], source=[], env=env) # assert c in func_matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in func_matches]) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]]) - + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + func_matches[sys.version_info[:2]]) - - env = Environment(FOO = factory(LocalFunc)) + env = Environment(FOO=factory(LocalFunc)) c = a.get_contents(target=[], source=[], env=env) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]]) + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + func_matches[sys.version_info[:2]]) # matches_foo = [x + b"foo" for x in func_matches] matches_foo = func_matches[sys.version_info[:2]] + b'foo' - - env = Environment(FOO = factory(GlobalFunc, varlist=['XYZ'])) + env = Environment(FOO=factory(GlobalFunc, varlist=['XYZ'])) c = a.get_contents(target=[], source=[], env=env) - assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]]) + assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + func_matches[sys.version_info[:2]]) env['XYZ'] = 'foo' c = a.get_contents(target=[], source=[], env=env) @@ -1861,36 +1991,37 @@ class LazyActionTestCase(unittest.TestCase): class ActionCallerTestCase(unittest.TestCase): def test___init__(self): """Test creation of an ActionCaller""" - ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO' : 4, 'BAR' : 5}) + ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO': 4, 'BAR': 5}) assert ac.parent == 1, ac.parent assert ac.args == [2, 3], ac.args - assert ac.kw == {'FOO' : 4, 'BAR' : 5}, ac.kw + assert ac.kw == {'FOO': 4, 'BAR': 5}, ac.kw def test_get_contents(self): """Test fetching the contents of an ActionCaller""" + def strfunc(): pass def LocalFunc(): pass - matches = { - (2,7) : b'd\x00\x00S', - (3,5) : b'd\x00\x00S', - (3,6) : b'd\x00S\x00', + (2, 7): b'd\x00\x00S', + (3, 5): b'd\x00\x00S', + (3, 6): b'd\x00S\x00', } - af = SCons.Action.ActionFactory(GlobalFunc, strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) - assert c == matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(matches[sys.version_info[:2]]) + assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + matches[sys.version_info[:2]]) af = SCons.Action.ActionFactory(LocalFunc, strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) - assert c == matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(matches[sys.version_info[:2]]) + assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + matches[sys.version_info[:2]]) class LocalActFunc(object): def __call__(self): @@ -1899,12 +2030,14 @@ class ActionCallerTestCase(unittest.TestCase): af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) - assert c == matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(matches[sys.version_info[:2]]) + assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + matches[sys.version_info[:2]]) af = SCons.Action.ActionFactory(LocalActFunc(), strfunc) ac = SCons.Action.ActionCaller(af, [], {}) c = ac.get_contents([], [], Environment()) - assert c == matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(matches[sys.version_info[:2]]) + assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( + matches[sys.version_info[:2]]) matches = [ b"", @@ -1922,12 +2055,14 @@ class ActionCallerTestCase(unittest.TestCase): def test___call__(self): """Test calling an ActionCaller""" actfunc_args = [] + def actfunc(a1, a2, a3, args=actfunc_args): args.extend([a1, a2, a3]) + def strfunc(a1, a2, a3): pass - e = Environment(FOO = 2, BAR = 5) + e = Environment(FOO=2, BAR=5) af = SCons.Action.ActionFactory(actfunc, strfunc) ac = SCons.Action.ActionCaller(af, ['$__env__', '$FOO', 3], {}) @@ -1937,7 +2072,7 @@ class ActionCallerTestCase(unittest.TestCase): assert actfunc_args[2] == 3, actfunc_args del actfunc_args[:] - ac = SCons.Action.ActionCaller(af, [], {'a3' : '$__env__', 'a2' : '$BAR', 'a1' : 4}) + ac = SCons.Action.ActionCaller(af, [], {'a3': '$__env__', 'a2': '$BAR', 'a1': 4}) ac([], [], e) assert actfunc_args[0] == 4, actfunc_args assert actfunc_args[1] == '5', actfunc_args @@ -1947,29 +2082,35 @@ class ActionCallerTestCase(unittest.TestCase): def test_strfunction(self): """Test calling the ActionCaller strfunction() method""" strfunc_args = [] + def actfunc(a1, a2, a3, a4): pass + def strfunc(a1, a2, a3, a4, args=strfunc_args): args.extend([a1, a2, a3, a4]) af = SCons.Action.ActionFactory(actfunc, strfunc) ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3, '$WS'], {}) - ac.strfunction([], [], Environment(FOO = 2, WS='white space')) + ac.strfunction([], [], Environment(FOO=2, WS='white space')) assert strfunc_args == [1, '2', 3, 'white space'], strfunc_args del strfunc_args[:] - d = {'a3' : 6, 'a2' : '$BAR', 'a1' : 4, 'a4' : '$WS'} + d = {'a3': 6, 'a2': '$BAR', 'a1': 4, 'a4': '$WS'} ac = SCons.Action.ActionCaller(af, [], d) - ac.strfunction([], [], Environment(BAR = 5, WS='w s')) + ac.strfunction([], [], Environment(BAR=5, WS='w s')) assert strfunc_args == [4, '5', 6, 'w s'], strfunc_args + class ActionFactoryTestCase(unittest.TestCase): def test___init__(self): """Test creation of an ActionFactory""" + def actfunc(): pass + def strfunc(): pass + ac = SCons.Action.ActionFactory(actfunc, strfunc) assert ac.actfunc is actfunc, ac.actfunc assert ac.strfunc is strfunc, ac.strfunc @@ -1978,10 +2119,13 @@ class ActionFactoryTestCase(unittest.TestCase): """Test calling whatever's returned from an ActionFactory""" actfunc_args = [] strfunc_args = [] + def actfunc(a1, a2, a3, args=actfunc_args): args.extend([a1, a2, a3]) + def strfunc(a1, a2, a3, args=strfunc_args): args.extend([a1, a2, a3]) + af = SCons.Action.ActionFactory(actfunc, strfunc) af(3, 6, 9)([], [], Environment()) assert actfunc_args == [3, 6, 9], actfunc_args @@ -1995,8 +2139,8 @@ class ActionCompareTestCase(unittest.TestCase): Basically ensures we can locate the builder, comparing it to itself along the way.""" - bar = SCons.Builder.Builder(action = {}) - env = Environment( BUILDERS = {'BAR' : bar} ) + bar = SCons.Builder.Builder(action={}) + env = Environment(BUILDERS={'BAR': bar}) name = bar.get_name(env) assert name == 'BAR', name @@ -2005,12 +2149,12 @@ class ActionCompareTestCase(unittest.TestCase): Ensure that we can compare builders (and thereby actions) to each other safely.""" - foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo') - bar = SCons.Builder.Builder(action = {}) + foo = SCons.Builder.Builder(action='$FOO', suffix='.foo') + bar = SCons.Builder.Builder(action={}) assert foo != bar assert foo.action != bar.action - env = Environment( BUILDERS = {'FOO' : foo, - 'BAR' : bar} ) + env = Environment(BUILDERS={'FOO': foo, + 'BAR': bar}) name = foo.get_name(env) assert name == 'FOO', name name = bar.get_name(env) @@ -2024,14 +2168,14 @@ class ActionCompareTestCase(unittest.TestCase): where one of the builders has a suffix dictionary with a None key.""" - foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo') - bar = SCons.Builder.Builder(action = {}, suffix={None:'.bar'}) + foo = SCons.Builder.Builder(action='$FOO', suffix='.foo') + bar = SCons.Builder.Builder(action={}, suffix={None: '.bar'}) bar.add_action('.cow', "$MOO") - dog = SCons.Builder.Builder(suffix = '.bar') + dog = SCons.Builder.Builder(suffix='.bar') - env = Environment( BUILDERS = {'FOO' : foo, - 'BAR' : bar, - 'DOG' : dog} ) + env = Environment(BUILDERS={'FOO': foo, + 'BAR': bar, + 'DOG': dog}) assert foo.get_name(env) == 'FOO', foo.get_name(env) assert bar.get_name(env) == 'BAR', bar.get_name(env) @@ -2040,9 +2184,11 @@ class ActionCompareTestCase(unittest.TestCase): class TestClass(object): """A test class used by ObjectContentsTestCase.test_object_contents""" + def __init__(self): self.a = "a" self.b = "b" + def method(self, arg): pass @@ -2058,14 +2204,14 @@ class ObjectContentsTestCase(unittest.TestCase): # Since the python bytecode has per version differences, we need different expected results per version expected = { - (2,7) : bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'), - (3,5) : bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'), - (3,6) : bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'), + (2, 7): bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'), + (3, 5): bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'), + (3, 6): bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'), } c = SCons.Action._function_contents(func1) - assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected[sys.version_info[:2]]) - + assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr( + expected[sys.version_info[:2]]) def test_object_contents(self): """Test that Action._object_contents works""" @@ -2078,12 +2224,16 @@ class ObjectContentsTestCase(unittest.TestCase): # Since the python bytecode has per version differences, we need different expected results per version expected = { - (2,7): bytearray(b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"), - (3,5): bytearray(b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"), - (3,6): bytearray(b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"), + (2, 7): bytearray( + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"), + (3, 5): bytearray( + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"), + (3, 6): bytearray( + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"), } - assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected[sys.version_info[:2]]) + assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr( + expected[sys.version_info[:2]]) def test_code_contents(self): """Test that Action._code_contents works""" @@ -2093,36 +2243,17 @@ class ObjectContentsTestCase(unittest.TestCase): # Since the python bytecode has per version differences, we need different expected results per version expected = { - (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00GHd\x01\x00S)'), - (3,5) : bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'), - (3,6) : bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'), + (2, 7): bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00GHd\x01\x00S)'), + (3, 5): bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'), + (3, 6): bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'), } - assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+expected[sys.version_info[:2]] - + assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + expected[ + sys.version_info[:2]] if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ _ActionActionTestCase, - ActionTestCase, - CommandActionTestCase, - CommandGeneratorActionTestCase, - FunctionActionTestCase, - ListActionTestCase, - LazyActionTestCase, - ActionCallerTestCase, - ActionFactoryTestCase, - ActionCompareTestCase, - ObjectContentsTestCase ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - - TestUnit.run(suite) - - # Swap this for above to debug otherwise you can't run individual tests as TestUnit is swallowing arguments - # unittest.main() + unittest.main() # Local Variables: # tab-width:4 -- cgit v0.12 From babf25c3aeec87f4bb99c22ed3793a1b27c5ff45 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 22 Jun 2018 20:31:41 -0700 Subject: Update CHANGES.txt to sort contributor list by last name alphabetically. Also added info on bug fix in this branch. --- src/CHANGES.txt | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index b0baecb..a603755 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,26 +7,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - From Philipp Maierhöfer - - Added a __hash__ method to the class Scons.Subst.Literal. Required when substituting Literal - objects when SCons runs with Python 3. - - Added missing FORTRANMODDIRPREFIX to the gfortran tool. - - From Richard West: - - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. - Allows easier debugging within Visual Studio - - Change setup.py to change the install directory (via pip, or setup.py install) from scons-#.#.# - to scons (Yielding /lib/scons/SCons/ instead of /lib/scons/SCons-#.#.#/). - This changes SCons to better comply with normal Python installation practices. - From Bernard Blackham: - Fixed handling of side-effects in task master (fixes #3013). - From Daniel Moody: - - Set the pickling protocal back to highest which was causing issues - with variant dir tests. This will cause issues if reading sconsigns - pickled with the previous lower protocal. - From Ray Donnelly: - Fix the PATH created by scons.bat (and other .bat files) to provide a normalized PATH. Some pythons in the 3.6 series are no longer able to handle paths which @@ -45,19 +28,47 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Re-Enable parallel SCons (-j) when running via Pypy - Move SCons test framework files to testing/framework and remove all references to QMtest. QMTest has not been used by SCons for some time now. + - Fixed issue causing stack trace when python Action function contains a unicode string when being + run with Python 2.7 From Andrew Featherstone - Removed unused --warn options from the man page and source code. + From Philipp Maierhöfer + - Added a __hash__ method to the class Scons.Subst.Literal. Required when substituting Literal + objects when SCons runs with Python 3. + - Added missing FORTRANMODDIRPREFIX to the gfortran tool. + + From Fredrik Medley: + - Fix exception when printing of EnviromentError messages. + Specifically, this fixes error reporting of the race condition when + initializing the cache which error previously was hidden. + From Daniel Moody: - Updated Jar builder to handle nodes and directories better - Updated Jar builder to flatten source list which could contain embedded lists - Removed some magic numbers from jar.py on behalf of Mats Wichmann (mats@linux.com) + - Set the pickling protocal back to highest which was causing issues + with variant dir tests. This will cause issues if reading sconsigns + pickled with the previous lower protocal. + From Gary Oberbrunner: - Fix bug when Installing multiple subdirs outside the source tree - fix to_str to handle None without raising exception + From Jonathon Reinhart: + - Replace all instances of `int main()` in C code with `int main(void)`. + Specifically, this fixes the test cases use by Configure.CheckCC() which + would fail when using -Wstrict-prototypes. + + From Richard West: + - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. + Allows easier debugging within Visual Studio + - Change setup.py to change the install directory (via pip, or setup.py install) from scons-#.#.# + to scons (Yielding /lib/scons/SCons/ instead of /lib/scons/SCons-#.#.#/). + This changes SCons to better comply with normal Python installation practices. + From Mats Wichmann: - Updated manpage scons.xml to fix a nested list problem - Updated doc terminiology: use prepend instead of append as appropriate @@ -65,15 +76,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - update wiki links to new github location - update bug links to new github location - From Jonathon Reinhart: - - Replace all instances of `int main()` in C code with `int main(void)`. - Specifically, this fixes the test cases use by Configure.CheckCC() which - would fail when using -Wstrict-prototypes. - From Fredrik Medley: - - Fix exception when printing of EnviromentError messages. - Specifically, this fixes error reporting of the race condition when - initializing the cache which error previously was hidden. RELEASE 3.0.1 - Mon, 12 Nov 2017 15:31:33 -0700 -- cgit v0.12 From c0b5e62acfa7d4b611a20f26b415af664c22cc88 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 26 Jun 2018 10:08:57 -0700 Subject: Add test for unicode strings it Configure Contexts TryAction calls --- src/engine/SCons/SConfTests.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index 65c4b5f..e19abaf 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -300,10 +300,15 @@ int main(void) { """Test SConf.TryAction """ def actionOK(target, source, env): - open(str(target[0]), "w").write( "RUN OK\n" ) + open(str(target[0]), "w").write("RUN OK\n") return None def actionFAIL(target, source, env): return 1 + def actionUnicode(target, source, env): + open(str(target[0]), "wb").write('2\302\242\n') + return None + + self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), @@ -313,6 +318,11 @@ int main(void) { assert ret and output.encode('utf-8') == bytearray("RUN OK"+os.linesep,'utf-8'), (ret, output) (ret, output) = sconf.TryAction(action=actionFAIL) assert not ret and output == "", (ret, output) + + # GH Issue #3141 - unicode text and py2.7 crashes. + (ret, output) = sconf.TryAction(action=actionUnicode) + assert ret and output == u'2\xa2\n', (ret, output) + finally: sconf.Finish() @@ -779,8 +789,7 @@ int main(void) { if __name__ == "__main__": - suite = unittest.makeSuite(SConfTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 -- cgit v0.12 From 1a80d7b28f8d13798fceff57463c2223c8908c1e Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 26 Jun 2018 10:10:57 -0700 Subject: Fix Issue #3141. Unicode in TryAction crashing. Use get_text_contents instead of get_contents().decode() (get_contents should always be bytes, but not always decode()'able (without specifying encoding) --- src/CHANGES.txt | 1 + src/engine/SCons/SConf.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index b0baecb..4be9068 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -45,6 +45,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Re-Enable parallel SCons (-j) when running via Pypy - Move SCons test framework files to testing/framework and remove all references to QMtest. QMTest has not been used by SCons for some time now. + - Fix GH Issue #3141 unicode string in a TryAction() with python 2.7 crashes. From Andrew Featherstone - Removed unused --warn options from the man page and source code. diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index 48eed8e..a14127e 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -609,7 +609,7 @@ class SConfBase(object): ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) del self.env['BUILDERS']['SConfActionBuilder'] if ok: - outputStr = self.lastTarget.get_contents().decode() + outputStr = self.lastTarget.get_text_contents() return (1, outputStr) return (0, "") -- cgit v0.12 From 8245f89ad9f5bc564586ff58d680623161810177 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 26 Jun 2018 10:16:26 -0700 Subject: Fix new test so it only runs with py2.7 as that's the reported crashing environment --- src/engine/SCons/SConfTests.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index e19abaf..dd5b8b9 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -319,9 +319,10 @@ int main(void) { (ret, output) = sconf.TryAction(action=actionFAIL) assert not ret and output == "", (ret, output) - # GH Issue #3141 - unicode text and py2.7 crashes. - (ret, output) = sconf.TryAction(action=actionUnicode) - assert ret and output == u'2\xa2\n', (ret, output) + if not TestCmd.IS_PY3: + # GH Issue #3141 - unicode text and py2.7 crashes. + (ret, output) = sconf.TryAction(action=actionUnicode) + assert ret and output == u'2\xa2\n', (ret, output) finally: sconf.Finish() -- cgit v0.12 From 5d6bfb7b1a498f7ef0ebc93bc93fc26fb89b9a34 Mon Sep 17 00:00:00 2001 From: MatthewMarinets Date: Thu, 28 Jun 2018 11:23:32 -0700 Subject: -Adjusted JavaCommon to deal with implementations of classes as constructor arguments. -Added a test to JavaCommonTests to test the new functionality. --- src/engine/SCons/Tool/JavaCommon.py | 97 ++++++++++++++++++++++++++++---- src/engine/SCons/Tool/JavaCommonTests.py | 43 ++++++++++++++ 2 files changed, 128 insertions(+), 12 deletions(-) diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index dfb9e33..7a065ca 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -37,6 +37,10 @@ java_parsing = 1 default_java_version = '1.4' +# a switch for which jdk versions to use the Scope state for smarter +# anonymous inner class parsing. +scopeStateVersions = ('1.8') + if java_parsing: # Parse Java files for class names. # @@ -64,7 +68,7 @@ if java_parsing: interfaces, and anonymous inner classes.""" def __init__(self, version=default_java_version): - if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', '1.7', + if not version in ('1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', '5', '6'): msg = "Java version %s not supported" % version raise NotImplementedError(msg) @@ -115,8 +119,8 @@ if java_parsing: ret = SkipState(1, self) self.skipState = ret return ret - - def __getAnonStack(self): + + def _getAnonStack(self): return self.anonStacksStack[-1] def openBracket(self): @@ -125,15 +129,16 @@ if java_parsing: def closeBracket(self): self.brackets = self.brackets - 1 if len(self.stackBrackets) and \ - self.brackets == self.stackBrackets[-1]: + self.brackets == self.stackBrackets[-1]: self.listOutputs.append('$'.join(self.listClasses)) self.localClasses.pop() self.listClasses.pop() self.anonStacksStack.pop() self.stackBrackets.pop() if len(self.stackAnonClassBrackets) and \ - self.brackets == self.stackAnonClassBrackets[-1]: - self.__getAnonStack().pop() + self.brackets == self.stackAnonClassBrackets[-1] and \ + self.version not in scopeStateVersions: + self._getAnonStack().pop() self.stackAnonClassBrackets.pop() def parseToken(self, token): @@ -175,17 +180,83 @@ if java_parsing: self.stackAnonClassBrackets.append(self.brackets) className = [] className.extend(self.listClasses) - self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 - for anon in self.__getAnonStack(): + self._getAnonStack()[-1] = self._getAnonStack()[-1] + 1 + for anon in self._getAnonStack(): className.append(str(anon)) self.listOutputs.append('$'.join(className)) self.nextAnon = self.nextAnon + 1 - self.__getAnonStack().append(0) + self._getAnonStack().append(0) def setPackage(self, package): self.package = package + class ScopeState(object): + """ + A state that parses code within a scope normally, + within the confines of a scope. + """ + def __init__(self, old_state): + self.outer_state = old_state.outer_state + self.old_state = old_state + self.brackets = 0 + + def __getClassState(self): + try: + return self.classState + except AttributeError: + ret = ClassState(self) + self.classState = ret + return ret + + def __getAnonClassState(self): + try: + return self.anonState + except AttributeError: + ret = SkipState(1, AnonClassState(self)) + self.anonState = ret + return ret + + def __getSkipState(self): + try: + return self.skipState + except AttributeError: + ret = SkipState(1, self) + self.skipState = ret + return ret + + def openBracket(self): + self.brackets = self.brackets + 1 + + def closeBracket(self): + self.brackets = self.brackets - 1 + + def parseToken(self, token): + # if self.brackets == 0: + # return self.old_state.parseToken(token) + if token[:2] == '//': + return IgnoreState('\n', self) + elif token == '/*': + return IgnoreState('*/', self) + elif token == '{': + self.openBracket() + elif token == '}': + self.closeBracket() + if self.brackets == 0: + self.outer_state._getAnonStack().pop() + return self.old_state + elif token in ['"', "'"]: + return IgnoreState(token, self) + elif token == "new": + # anonymous inner class + return self.__getAnonClassState() + elif token == '.': + # Skip the attribute, it might be named "class", in which + # case we don't want to treat the following token as + # an inner class name... + return self.__getSkipState() + return self + class AnonClassState(object): """A state that looks for anonymous inner classes.""" def __init__(self, old_state): @@ -212,13 +283,15 @@ if java_parsing: if token == 'new': # look further for anonymous inner class return SkipState(1, AnonClassState(self)) - elif token in [ '"', "'" ]: + elif token in ['"', "'"]: return IgnoreState(token, self) elif token == ')': self.brace_level = self.brace_level - 1 return self if token == '{': self.outer_state.addAnonClass() + if self.outer_state.version in scopeStateVersions: + return ScopeState(old_state = self.old_state).parseToken(token) return self.old_state.parseToken(token) class SkipState(object): @@ -247,8 +320,8 @@ if java_parsing: # 'Foo$1Inner' # https://github.com/SCons/scons/issues/2087 if self.outer_state.localClasses and \ - self.outer_state.stackBrackets[-1] > \ - self.outer_state.stackBrackets[-2]+1: + self.outer_state.stackBrackets[-1] > \ + self.outer_state.stackBrackets[-2]+1: locals = self.outer_state.localClasses[-1] try: idx = locals[token] diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py index 902030d..7e6a482 100644 --- a/src/engine/SCons/Tool/JavaCommonTests.py +++ b/src/engine/SCons/Tool/JavaCommonTests.py @@ -566,6 +566,49 @@ public class Foo assert expect == classes, (expect, classes) + def test_in_function_class_declaration(self): + """ + Test that implementing a class in a function call doesn't confuse SCons. + """ + + input = """ +package com.Matthew; + +public class AnonDemo { + + public static void main(String[] args) { + new AnonDemo().execute(); + } + + public void execute() { + Foo bar = new Foo(new Foo() { + @Override + public int getX() { return this.x; } + }) { + @Override + public int getX() { return this.x; } + }; + } + + public abstract class Foo { + public int x; + public abstract int getX(); + + public Foo(Foo f) { + this.x = f.x; + } + + public Foo() {} + } +} +""" + expect = ['AnonDemo$1', + 'AnonDemo$2', + 'AnonDemo$Foo', + 'AnonDemo'] + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.8') + assert expect == classes, (expect, classes) + if __name__ == "__main__": suite = unittest.TestSuite() -- cgit v0.12 From f1f2d14e1c9d155691675fc17e7d7a47f63b02f4 Mon Sep 17 00:00:00 2001 From: MatthewMarinets <31861583+MatthewMarinets@users.noreply.github.com> Date: Tue, 3 Jul 2018 09:35:31 -0700 Subject: Update CHANGES.txt --- src/CHANGES.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index b0baecb..5ecf245 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -6,7 +6,11 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - + + From Matthew Marinets + - Fixed an issue that caused the Java emitter to incorrectly parse arguments to constructors that + implemented a class. + From Philipp Maierhöfer - Added a __hash__ method to the class Scons.Subst.Literal. Required when substituting Literal objects when SCons runs with Python 3. -- cgit v0.12 From b86c6d4c6ba1ff86897cbbed37ec5c228dfec40e Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 6 Jul 2018 16:39:52 -0700 Subject: Change TestSCons's get_platform_python_info() to use the python which runtest.py was run with instead of blindly searching for python vis test.where_is(). This fixes an issue where the swig tests work fine with py3.6 but not with py2.7 on a win64 box with both installed, runtest launched via py -2 runtest.py ... --- testing/framework/TestSCons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index c32e06a..e625865 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -1262,7 +1262,7 @@ SConscript( sconscript ) this platform and its associated include path, library path, and library name. """ - python = self.where_is('python') + python = os.environ.get('python_executable',self.where_is('python')) if not python: self.skip_test('Can not find installed "python", skipping test.\n') -- cgit v0.12 From 77298ddfa1c2cce17fe722bdccad93fb93f9618a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 8 Jul 2018 11:36:54 -0700 Subject: stop using obsolete distutils.sysconfig to find python include, link paths,and library names. Use sysconfig.get_config_var() calls instead --- test/SWIG/recursive-includes-cpp.py | 5 +++-- testing/framework/TestSCons.py | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 2c74cc8..b943406 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -78,7 +78,7 @@ if TestCmd.IS_WINDOWS: else: TARGET_ARCH = "" test.write('SConstruct', """\ -import distutils.sysconfig +import sysconfig import sys import os @@ -88,7 +88,7 @@ env = Environment( '-python' ], CPPPATH = [ - distutils.sysconfig.get_python_inc() + sysconfig.get_config_var("INCLUDEPY") ], SHLIBPREFIX = "", tools = [ 'default', 'swig' ] @@ -96,6 +96,7 @@ env = Environment( if sys.platform == 'darwin': env['LIBS']=['python%d.%d'%(sys.version_info[0],sys.version_info[1])] + env.Append(LIBPATH=[sysconfig.get_config_var("LIBDIR")]) elif sys.platform == 'win32': env.Append(LIBS=['python%d%d'%(sys.version_info[0],sys.version_info[1])]) env.Append(LIBPATH=[os.path.dirname(sys.executable) + "/libs"]) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index e625865..767570d 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -1261,13 +1261,16 @@ SConscript( sconscript ) Returns a path to a Python executable suitable for testing on this platform and its associated include path, library path, and library name. + Returns: + (path to python, include path, library path, library name) """ python = os.environ.get('python_executable',self.where_is('python')) if not python: self.skip_test('Can not find installed "python", skipping test.\n') - self.run(program = python, stdin = """\ -import os, sys + if sys.platform == 'win32': + self.run(program = python, stdin = """\ +import sysconfig try: if sys.platform == 'win32': py_ver = 'python%d%d' % sys.version_info[:2] @@ -1288,6 +1291,13 @@ except: print(os.path.join(sys.prefix, 'include', py_ver)) print(os.path.join(sys.prefix, 'lib', py_ver, 'config')) print(py_ver) + """) + else: + self.run(program = python, stdin = """\ +import sysconfig +print(sysconfig.get_config_var("INCLUDEPY")) +print(sysconfig.get_config_var("LIBDIR")) +print("python"+sysconfig.get_config_var("LDVERSION")) """) return [python] + self.stdout().strip().split('\n') -- cgit v0.12 From f90b61a1eb5da911786db6b1606a6652457a4240 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 8 Jul 2018 12:04:06 -0700 Subject: Fix for py2.7 + 3.5+ --- test/SWIG/live.py | 1 + testing/framework/TestSCons.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/SWIG/live.py b/test/SWIG/live.py index 684cff1..7c4bdce 100644 --- a/test/SWIG/live.py +++ b/test/SWIG/live.py @@ -46,6 +46,7 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') + python, python_include, python_libpath, python_lib = \ test.get_platform_python_info() Python_h = python_include + '/Python.h' diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 767570d..c9de47a 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -1264,12 +1264,12 @@ SConscript( sconscript ) Returns: (path to python, include path, library path, library name) """ - python = os.environ.get('python_executable',self.where_is('python')) + python = os.environ.get('python_executable', self.where_is('python')) if not python: self.skip_test('Can not find installed "python", skipping test.\n') if sys.platform == 'win32': - self.run(program = python, stdin = """\ + self.run(program=python, stdin="""\ import sysconfig try: if sys.platform == 'win32': @@ -1293,11 +1293,14 @@ except: print(py_ver) """) else: - self.run(program = python, stdin = """\ -import sysconfig + self.run(program=python, stdin="""\ +import sys, sysconfig print(sysconfig.get_config_var("INCLUDEPY")) print(sysconfig.get_config_var("LIBDIR")) -print("python"+sysconfig.get_config_var("LDVERSION")) +py_library_ver = sysconfig.get_config_var("LDVERSION") +if not py_library_ver: + py_library_ver = '%d.%d' % sys.version_info[:2] +print("python"+py_library_ver) """) return [python] + self.stdout().strip().split('\n') -- cgit v0.12 From 8c9aa75f2069bd3c0d2369c404899d457e391d0c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 10 Jul 2018 14:54:27 -0400 Subject: fix TestSCons.get_platform_python_info() to work on win32, had omitted some needed imports --- testing/framework/TestSCons.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index c9de47a..50942ab 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -1270,12 +1270,9 @@ SConscript( sconscript ) if sys.platform == 'win32': self.run(program=python, stdin="""\ -import sysconfig +import sysconfig, sys, os.path try: - if sys.platform == 'win32': py_ver = 'python%d%d' % sys.version_info[:2] - else: - py_ver = 'python%d.%d' % sys.version_info[:2] except AttributeError: py_ver = 'python' + sys.version[:3] # print include and lib path -- cgit v0.12 From 881dbcf2ef36b31be29ad6aff17ee54f04865329 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Wed, 11 Jul 2018 09:04:01 +0800 Subject: typo credit: https://stackoverflow.com/questions/39856184/does-scons-customized-decider-function-require-to-be-class-member --- doc/user/depends.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/depends.xml b/doc/user/depends.xml index f5f2270..8e548d2 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -518,7 +518,7 @@ cc -o hello hello.o Program('hello.c') def decide_if_changed(dependency, target, prev_ni): - if self.get_timestamp() != prev_ni.timestamp: + if dependency.get_timestamp() != prev_ni.timestamp: dep = str(dependency) tgt = str(target) if specific_part_of_file_has_changed(dep, tgt): -- cgit v0.12 From b8180bf3cbfdc23523a201f3c889291bdabc2c67 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Thu, 12 Jul 2018 08:42:03 +0800 Subject: Update CHANGES.txt --- src/CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index b3ef826..01418bc 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -97,6 +97,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - xml validity fixes from SConstruct.py change - update wiki links to new github location - update bug links to new github location + + From Hao Wu + - typo in customized decider example in user guide -- cgit v0.12 From 16c0be7f275d8270b1898b42c8e254f44cfea728 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Thu, 12 Jul 2018 14:08:08 +0800 Subject: Replace usage of unittest.TestSuite with unittest.main() --- src/engine/SCons/ActionTests.py | 1 - src/engine/SCons/CacheDirTests.py | 12 +----------- src/engine/SCons/DefaultsTests.py | 9 +-------- src/engine/SCons/ErrorsTests.py | 5 +---- src/engine/SCons/ExecutorTests.py | 9 +-------- src/engine/SCons/MemoizeTests.py | 12 +----------- src/engine/SCons/Node/AliasTests.py | 13 +------------ src/engine/SCons/Node/NodeTests.py | 12 +----------- src/engine/SCons/Node/PythonTests.py | 13 +------------ src/engine/SCons/Platform/PlatformTests.py | 13 +------------ src/engine/SCons/SConfTests.py | 2 -- src/engine/SCons/Scanner/DTests.py | 10 +--------- src/engine/SCons/Scanner/DirTests.py | 9 +-------- src/engine/SCons/Scanner/LaTeXTests.py | 11 +---------- src/engine/SCons/Scanner/ProgTests.py | 3 +-- src/engine/SCons/Scanner/RCTests.py | 3 +-- src/engine/SCons/Script/MainTests.py | 9 +-------- src/engine/SCons/SubstTests.py | 18 +----------------- src/engine/SCons/TaskmasterTests.py | 4 +--- src/engine/SCons/Tool/JavaCommonTests.py | 9 +-------- src/engine/SCons/Tool/javacTests.py | 9 +++------ src/engine/SCons/Tool/wixTests.py | 4 +--- src/engine/SCons/UtilTests.py | 1 - src/engine/SCons/Variables/BoolVariableTests.py | 5 +---- src/engine/SCons/Variables/EnumVariableTests.py | 5 +---- src/engine/SCons/Variables/ListVariableTests.py | 5 +---- src/engine/SCons/Variables/PackageVariableTests.py | 5 +---- src/engine/SCons/Variables/PathVariableTests.py | 5 +---- src/engine/SCons/Variables/VariablesTests.py | 13 ++++--------- src/engine/SCons/WarningsTests.py | 5 +---- 30 files changed, 32 insertions(+), 202 deletions(-) diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 2959ede..ed88f72 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -50,7 +50,6 @@ import SCons.Environment import SCons.Errors import TestCmd -import TestUnit # Initial setup of the common environment for all tests, # a temporary working directory containing a diff --git a/src/engine/SCons/CacheDirTests.py b/src/engine/SCons/CacheDirTests.py index 494db98..ef87746 100644 --- a/src/engine/SCons/CacheDirTests.py +++ b/src/engine/SCons/CacheDirTests.py @@ -29,7 +29,6 @@ import sys import unittest from TestCmd import TestCmd -import TestUnit import SCons.CacheDir @@ -287,16 +286,7 @@ class FileTestCase(BaseTestCase): SCons.CacheDir.CacheRetrieveSilent = save_CacheRetrieveSilent if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ - CacheDirTestCase, - FileTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) - + unittest.main() # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/DefaultsTests.py b/src/engine/SCons/DefaultsTests.py index 141243b..e04d1eb 100644 --- a/src/engine/SCons/DefaultsTests.py +++ b/src/engine/SCons/DefaultsTests.py @@ -32,7 +32,6 @@ import unittest from collections import UserDict import TestCmd -import TestUnit import SCons.Errors @@ -77,13 +76,7 @@ class DefaultsTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ DefaultsTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/ErrorsTests.py b/src/engine/SCons/ErrorsTests.py index 7819580..5c16160 100644 --- a/src/engine/SCons/ErrorsTests.py +++ b/src/engine/SCons/ErrorsTests.py @@ -28,8 +28,6 @@ import os import sys import unittest -import TestUnit - import SCons.Errors @@ -126,8 +124,7 @@ class ErrorsTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.makeSuite(ErrorsTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py index eeab3ad..59ca5c7 100644 --- a/src/engine/SCons/ExecutorTests.py +++ b/src/engine/SCons/ExecutorTests.py @@ -26,8 +26,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys import unittest -import TestUnit - import SCons.Executor @@ -483,12 +481,7 @@ class ExecutorTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ ExecutorTestCase ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/MemoizeTests.py b/src/engine/SCons/MemoizeTests.py index 3ce9a11..726f656 100644 --- a/src/engine/SCons/MemoizeTests.py +++ b/src/engine/SCons/MemoizeTests.py @@ -26,8 +26,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys import unittest -import TestUnit - import SCons.Memoize # Enable memoization counting @@ -165,15 +163,7 @@ class CountValueTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ - CountDictTestCase, - CountValueTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py index 8e31875..5d9c799 100644 --- a/src/engine/SCons/Node/AliasTests.py +++ b/src/engine/SCons/Node/AliasTests.py @@ -26,8 +26,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys import unittest -import TestUnit - import SCons.Errors import SCons.Node.Alias @@ -113,16 +111,7 @@ class AliasBuildInfoTestCase(unittest.TestCase): bi = SCons.Node.Alias.AliasBuildInfo() if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ - AliasTestCase, - AliasBuildInfoTestCase, - AliasNodeInfoTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 3802f8c..ca6c883 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -30,8 +30,6 @@ import re import sys import unittest -import TestUnit - import SCons.Errors import SCons.Node import SCons.Util @@ -1349,15 +1347,7 @@ class NodeListTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ BuildInfoBaseTestCase, - NodeInfoBaseTestCase, - NodeTestCase, - NodeListTestCase ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py index 346542b..7ef9e52 100644 --- a/src/engine/SCons/Node/PythonTests.py +++ b/src/engine/SCons/Node/PythonTests.py @@ -26,8 +26,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys import unittest -import TestUnit - import SCons.Errors import SCons.Node.Python @@ -113,16 +111,7 @@ class ValueBuildInfoTestCase(unittest.TestCase): bi = SCons.Node.Python.ValueBuildInfo() if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ - ValueTestCase, - ValueBuildInfoTestCase, - ValueNodeInfoTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Platform/PlatformTests.py b/src/engine/SCons/Platform/PlatformTests.py index 3432e94..6f720ec 100644 --- a/src/engine/SCons/Platform/PlatformTests.py +++ b/src/engine/SCons/Platform/PlatformTests.py @@ -28,8 +28,6 @@ import SCons.compat import collections import unittest -import TestUnit - import SCons.Errors import SCons.Platform import SCons.Environment @@ -204,17 +202,8 @@ class PlatformEscapeTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.TestSuite() - - tclasses = [ PlatformTestCase, - TempFileMungeTestCase, - PlatformEscapeTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) + unittest.main() - TestUnit.run(suite) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index dd5b8b9..cf8a7fb 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -33,8 +33,6 @@ from types import * import unittest import TestCmd -import TestUnit - sys.stdout = io.StringIO() diff --git a/src/engine/SCons/Scanner/DTests.py b/src/engine/SCons/Scanner/DTests.py index 51e527a..ad6bd5a 100644 --- a/src/engine/SCons/Scanner/DTests.py +++ b/src/engine/SCons/Scanner/DTests.py @@ -26,7 +26,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import unittest import TestCmd -import TestUnit import SCons.Scanner.D @@ -266,14 +265,7 @@ class DScannerTestCase(unittest.TestCase): self.helper('multiline.d', ['A.d']) if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ - DScannerTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Scanner/DirTests.py b/src/engine/SCons/Scanner/DirTests.py index 9701921..c433663 100644 --- a/src/engine/SCons/Scanner/DirTests.py +++ b/src/engine/SCons/Scanner/DirTests.py @@ -28,7 +28,6 @@ import sys import unittest import TestCmd -import TestUnit import SCons.Node.FS import SCons.Scanner.Dir @@ -121,14 +120,8 @@ class DirEntryScannerTestCase(DirScannerTestBase): sss = list(map(str, deps)) assert sss == [], sss -def suite(): - suite = unittest.TestSuite() - suite.addTest(DirScannerTestCase()) - suite.addTest(DirEntryScannerTestCase()) - return suite - if __name__ == "__main__": - TestUnit.run(suite()) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Scanner/LaTeXTests.py b/src/engine/SCons/Scanner/LaTeXTests.py index 213e89e..5913124 100644 --- a/src/engine/SCons/Scanner/LaTeXTests.py +++ b/src/engine/SCons/Scanner/LaTeXTests.py @@ -31,7 +31,6 @@ import sys import unittest import TestCmd -import TestUnit import SCons.Node.FS import SCons.Scanner.LaTeX @@ -156,16 +155,8 @@ class LaTeXScannerTestCase3(unittest.TestCase): files = ['inc5.xyz', 'subdir/inc4.eps'] deps_match(self, deps, files) - -def suite(): - suite = unittest.TestSuite() - suite.addTest(LaTeXScannerTestCase1()) - suite.addTest(LaTeXScannerTestCase2()) - suite.addTest(LaTeXScannerTestCase3()) - return suite - if __name__ == "__main__": - TestUnit.run(suite()) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Scanner/ProgTests.py b/src/engine/SCons/Scanner/ProgTests.py index e7c791c..45fdcb2 100644 --- a/src/engine/SCons/Scanner/ProgTests.py +++ b/src/engine/SCons/Scanner/ProgTests.py @@ -28,7 +28,6 @@ import sys import unittest import TestCmd -import TestUnit import SCons.Node.FS import SCons.Scanner.Prog @@ -274,7 +273,7 @@ def suite(): return suite if __name__ == "__main__": - TestUnit.run(suite()) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Scanner/RCTests.py b/src/engine/SCons/Scanner/RCTests.py index 2864026..d4f53ca 100644 --- a/src/engine/SCons/Scanner/RCTests.py +++ b/src/engine/SCons/Scanner/RCTests.py @@ -29,7 +29,6 @@ import collections import os import TestCmd -import TestUnit import SCons.Scanner.RC import SCons.Node.FS @@ -167,7 +166,7 @@ def suite(): return suite if __name__ == "__main__": - TestUnit.run(suite()) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Script/MainTests.py b/src/engine/SCons/Script/MainTests.py index fd6aaf4..aa6bfac 100644 --- a/src/engine/SCons/Script/MainTests.py +++ b/src/engine/SCons/Script/MainTests.py @@ -25,8 +25,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import unittest -import TestUnit - import SCons.Errors import SCons.Script.Main @@ -41,12 +39,7 @@ import SCons.Script.Main # of private functionality. if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/SubstTests.py b/src/engine/SCons/SubstTests.py index fcd77df..a111e4b 100644 --- a/src/engine/SCons/SubstTests.py +++ b/src/engine/SCons/SubstTests.py @@ -32,8 +32,6 @@ import unittest from collections import UserDict -import TestUnit - import SCons.Errors from SCons.Subst import * @@ -1243,21 +1241,7 @@ class subst_dict_TestCase(unittest.TestCase): assert SOURCES == ['s3', 'v-rstr-s4', 'v-s5'], SOURCES if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ - CLVar_TestCase, - LiteralTestCase, - SpecialAttrWrapperTestCase, - quote_spaces_TestCase, - scons_subst_TestCase, - scons_subst_list_TestCase, - scons_subst_once_TestCase, - subst_dict_TestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index d237d60..42ed00e 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -30,7 +30,6 @@ import copy import sys import unittest -import TestUnit import SCons.Taskmaster import SCons.Errors @@ -1239,8 +1238,7 @@ Taskmaster: No candidate anymore. if __name__ == "__main__": - suite = unittest.makeSuite(TaskmasterTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py index 902030d..cda785f 100644 --- a/src/engine/SCons/Tool/JavaCommonTests.py +++ b/src/engine/SCons/Tool/JavaCommonTests.py @@ -27,8 +27,6 @@ import os.path import sys import unittest -import TestUnit - import SCons.Scanner.IDL import SCons.Tool.JavaCommon @@ -568,12 +566,7 @@ public class Foo if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ parse_javaTestCase ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/javacTests.py b/src/engine/SCons/Tool/javacTests.py index bf75d8a..96d41b2 100644 --- a/src/engine/SCons/Tool/javacTests.py +++ b/src/engine/SCons/Tool/javacTests.py @@ -24,8 +24,6 @@ import os import unittest -import TestUnit - import SCons.Tool.javac class DummyNode(object): @@ -40,14 +38,14 @@ class pathoptTestCase(unittest.TestCase): popt = SCons.Tool.javac.pathopt('-foopath', 'FOOPATH') env = {'FOOPATH': path} actual = popt(None, None, env, None) - self.assertEquals(expect, actual) + self.assertEqual(expect, actual) def assert_pathopt_default(self, expect, path, default): popt = SCons.Tool.javac.pathopt('-foopath', 'FOOPATH', default='DPATH') env = {'FOOPATH': path, 'DPATH': default} actual = popt(None, None, env, None) - self.assertEquals(expect, actual) + self.assertEqual(expect, actual) def test_unset(self): self.assert_pathopt([], None) @@ -101,5 +99,4 @@ class pathoptTestCase(unittest.TestCase): '') if __name__ == "__main__": - suite = unittest.makeSuite(pathoptTestCase, 'test_') - TestUnit.run(suite) + unittest.main() diff --git a/src/engine/SCons/Tool/wixTests.py b/src/engine/SCons/Tool/wixTests.py index c683e98..6039115 100644 --- a/src/engine/SCons/Tool/wixTests.py +++ b/src/engine/SCons/Tool/wixTests.py @@ -33,7 +33,6 @@ from SCons.Tool.wix import * from SCons.Environment import Environment import TestCmd -import TestUnit # create fake candle and light, so the tool's exists() method will succeed @@ -53,8 +52,7 @@ class WixTestCase(unittest.TestCase): assert env.subst('$WIXSRCSUF') == '.wxs' if __name__ == "__main__": - suite = unittest.makeSuite(WixTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index 38b4da9..842d4d8 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -32,7 +32,6 @@ import unittest from collections import UserDict, UserList, UserString import TestCmd -import TestUnit import SCons.Errors diff --git a/src/engine/SCons/Variables/BoolVariableTests.py b/src/engine/SCons/Variables/BoolVariableTests.py index 7110f6f..3184407 100644 --- a/src/engine/SCons/Variables/BoolVariableTests.py +++ b/src/engine/SCons/Variables/BoolVariableTests.py @@ -26,8 +26,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys import unittest -import TestUnit - import SCons.Errors import SCons.Variables @@ -118,8 +116,7 @@ class BoolVariableTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.makeSuite(BoolVariableTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Variables/EnumVariableTests.py b/src/engine/SCons/Variables/EnumVariableTests.py index 931dfe2..4ecb8bd 100644 --- a/src/engine/SCons/Variables/EnumVariableTests.py +++ b/src/engine/SCons/Variables/EnumVariableTests.py @@ -26,8 +26,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys import unittest -import TestUnit - import SCons.Errors import SCons.Variables @@ -195,8 +193,7 @@ class EnumVariableTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.makeSuite(EnumVariableTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Variables/ListVariableTests.py b/src/engine/SCons/Variables/ListVariableTests.py index adfd353..ef4832c 100644 --- a/src/engine/SCons/Variables/ListVariableTests.py +++ b/src/engine/SCons/Variables/ListVariableTests.py @@ -27,8 +27,6 @@ import copy import sys import unittest -import TestUnit - import SCons.Errors import SCons.Variables @@ -125,8 +123,7 @@ class ListVariableTestCase(unittest.TestCase): n = l.__class__(copy.copy(l)) if __name__ == "__main__": - suite = unittest.makeSuite(ListVariableTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Variables/PackageVariableTests.py b/src/engine/SCons/Variables/PackageVariableTests.py index 3aa411d..cda3a4a 100644 --- a/src/engine/SCons/Variables/PackageVariableTests.py +++ b/src/engine/SCons/Variables/PackageVariableTests.py @@ -30,8 +30,6 @@ import SCons.Errors import SCons.Variables import TestCmd -import TestUnit - class PackageVariableTestCase(unittest.TestCase): def test_PackageVariable(self): @@ -115,8 +113,7 @@ class PackageVariableTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.makeSuite(PackageVariableTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Variables/PathVariableTests.py b/src/engine/SCons/Variables/PathVariableTests.py index 3d9d25c..48ad0e0 100644 --- a/src/engine/SCons/Variables/PathVariableTests.py +++ b/src/engine/SCons/Variables/PathVariableTests.py @@ -31,8 +31,6 @@ import SCons.Errors import SCons.Variables import TestCmd -import TestUnit - class PathVariableTestCase(unittest.TestCase): def test_PathVariable(self): @@ -228,8 +226,7 @@ class PathVariableTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.makeSuite(PathVariableTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Variables/VariablesTests.py b/src/engine/SCons/Variables/VariablesTests.py index 175a14b..43c785f 100644 --- a/src/engine/SCons/Variables/VariablesTests.py +++ b/src/engine/SCons/Variables/VariablesTests.py @@ -27,7 +27,6 @@ import sys import unittest import TestSCons -import TestUnit import SCons.Variables import SCons.Subst @@ -60,7 +59,9 @@ def check(key, value, env): def checkSave(file, expected): gdict = {} ldict = {} - exec(open(file, 'r').read(), gdict, ldict) + with open(file, 'r') as f: + exec(f.read(), gdict, ldict) + assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict) class VariablesTestCase(unittest.TestCase): @@ -690,13 +691,7 @@ class UnknownVariablesTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.TestSuite() - tclasses = [ VariablesTestCase, - UnknownVariablesTestCase ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/WarningsTests.py b/src/engine/SCons/WarningsTests.py index 7b8d00d..b5c5aef 100644 --- a/src/engine/SCons/WarningsTests.py +++ b/src/engine/SCons/WarningsTests.py @@ -26,8 +26,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys import unittest -import TestUnit - import SCons.Warnings class TestOutput(object): @@ -127,8 +125,7 @@ class WarningsTestCase(unittest.TestCase): assert to.out == "Foo", to.out if __name__ == "__main__": - suite = unittest.makeSuite(WarningsTestCase, 'test_') - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 -- cgit v0.12 From f808361cbad2b50e06d03add153931a4670b08a2 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Thu, 12 Jul 2018 14:29:01 +0800 Subject: add change blurb --- src/CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index b3ef826..2f77072 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -98,6 +98,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - update wiki links to new github location - update bug links to new github location + From Hao Wu + - Replace usage of unittest.TestSuite with unittest.main() (fix #3113) + -- cgit v0.12 From 73ea23d8c253aff31c933877fa69bc41da543aef Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 12 Jul 2018 12:12:04 -0400 Subject: Fix missing sys import --- testing/framework/TestSCons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index c9de47a..68ca56a 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -1270,7 +1270,7 @@ SConscript( sconscript ) if sys.platform == 'win32': self.run(program=python, stdin="""\ -import sysconfig +import sysconfig, sys try: if sys.platform == 'win32': py_ver = 'python%d%d' % sys.version_info[:2] -- cgit v0.12 From e00b7ef1f2f3f4bbc372d8b41c9d58c19a159d28 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 12 Jul 2018 07:37:38 -0400 Subject: Add mingw.py to MANIFEST This was causing bootstrap.py to fail on Linux. --- src/engine/MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 3f883be..6159c76 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -26,6 +26,7 @@ SCons/Platform/darwin.py SCons/Platform/hpux.py SCons/Platform/irix.py SCons/Platform/os2.py +SCons/Platform/mingw.py SCons/Platform/posix.py SCons/Platform/sunos.py SCons/Platform/win32.py -- cgit v0.12 From a29fca804cc209bc70c9aaddb0f51d5e81a5cac0 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 12 Jul 2018 08:03:48 -0400 Subject: Fix -jN for Python3.7, which always has thread support --- src/CHANGES.txt | 1 + src/engine/SCons/Script/Main.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 01418bc..4a050b0 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -78,6 +78,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Gary Oberbrunner: - Fix bug when Installing multiple subdirs outside the source tree - fix to_str to handle None without raising exception + - Fix -jN for python 3.7 From Jonathon Reinhart: - Replace all instances of `int main()` in C code with `int main(void)`. diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 923ef1e..1d3fb54 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -1255,7 +1255,11 @@ def _build_targets(fs, options, targets, target_top): is_pypy = platform.python_implementation() == 'PyPy' - python_has_threads = sysconfig.get_config_var('WITH_THREAD') or is_pypy + # As of 3.7, python removed support for threadless platforms. + # See https://www.python.org/dev/peps/pep-0011/ + is_37_or_later = sys.version_info.major > 3 or \ + sys.version_info.major == 3 and sys.version_info.minor >= 7 + python_has_threads = sysconfig.get_config_var('WITH_THREAD') or is_pypy or is_37_or_later # to check if python configured with threads. global num_jobs num_jobs = options.num_jobs -- cgit v0.12 From 453f80b4c656daac96bb7c303621512716036842 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 12 Jul 2018 08:05:48 -0400 Subject: runtest.py: async is a reserved keyword in python 3.7 I don't think the async arg is used anywhere, but I kept it and just changed the name so the tests run. --- runtest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtest.py b/runtest.py index e5c5f2f..293e4ca 100755 --- a/runtest.py +++ b/runtest.py @@ -787,7 +787,7 @@ tests_passing = 0 tests_failing = 0 -def run_test(t, io_lock, async=True): +def run_test(t, io_lock, run_async=True): global tests_completed, tests_passing, tests_failing header = "" command_args = ['-tt'] @@ -930,4 +930,4 @@ else: # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From 4f43aaeea0f59ef995e80adb22d2fc5fa1e5a80c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 15 Jul 2018 14:17:24 -0400 Subject: Swap to use cleaner check for py 3.7 or above from @serhiy-storchaka --- src/engine/SCons/Script/Main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 1d3fb54..6516a15 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -1257,8 +1257,7 @@ def _build_targets(fs, options, targets, target_top): is_pypy = platform.python_implementation() == 'PyPy' # As of 3.7, python removed support for threadless platforms. # See https://www.python.org/dev/peps/pep-0011/ - is_37_or_later = sys.version_info.major > 3 or \ - sys.version_info.major == 3 and sys.version_info.minor >= 7 + is_37_or_later = sys.version_info >= (3, 7) python_has_threads = sysconfig.get_config_var('WITH_THREAD') or is_pypy or is_37_or_later # to check if python configured with threads. global num_jobs -- cgit v0.12 From 0766c2974ac942240d0092c9afb0b168df031935 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 15 Jul 2018 14:58:34 -0400 Subject: For now disable 64 bit tests on appveyor. We burn a lot of time running both 32 and 64 bits. Enable python 3.7. --- .appveyor.yml | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index fe231d2..31ec590 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -40,32 +40,41 @@ environment: - PYTHON: "C:\\Python36" BUILD_JOB_NUM: 4 - - PYTHON: "C:\\Python27-x64" + - PYTHON: "C:\\Python37" BUILD_JOB_NUM: 1 - - PYTHON: "C:\\Python27-x64" + - PYTHON: "C:\\Python37" BUILD_JOB_NUM: 2 - - PYTHON: "C:\\Python27-x64" + - PYTHON: "C:\\Python37" BUILD_JOB_NUM: 3 - - PYTHON: "C:\\Python27-x64" + - PYTHON: "C:\\Python37" BUILD_JOB_NUM: 4 - - PYTHON: "C:\\Python35-x64" - BUILD_JOB_NUM: 1 - - PYTHON: "C:\\Python35-x64" - BUILD_JOB_NUM: 2 - - PYTHON: "C:\\Python35-x64" - BUILD_JOB_NUM: 3 - - PYTHON: "C:\\Python35-x64" - BUILD_JOB_NUM: 4 + # - PYTHON: "C:\\Python27-x64" + # BUILD_JOB_NUM: 1 + # - PYTHON: "C:\\Python27-x64" + # BUILD_JOB_NUM: 2 + # - PYTHON: "C:\\Python27-x64" + # BUILD_JOB_NUM: 3 + # - PYTHON: "C:\\Python27-x64" + # BUILD_JOB_NUM: 4 + + # - PYTHON: "C:\\Python35-x64" + # BUILD_JOB_NUM: 1 + # - PYTHON: "C:\\Python35-x64" + # BUILD_JOB_NUM: 2 + # - PYTHON: "C:\\Python35-x64" + # BUILD_JOB_NUM: 3 + # - PYTHON: "C:\\Python35-x64" + # BUILD_JOB_NUM: 4 - - PYTHON: "C:\\Python36-x64" - BUILD_JOB_NUM: 1 - - PYTHON: "C:\\Python36-x64" - BUILD_JOB_NUM: 2 - - PYTHON: "C:\\Python36-x64" - BUILD_JOB_NUM: 3 - - PYTHON: "C:\\Python36-x64" - BUILD_JOB_NUM: 4 + # - PYTHON: "C:\\Python36-x64" + # BUILD_JOB_NUM: 1 + # - PYTHON: "C:\\Python36-x64" + # BUILD_JOB_NUM: 2 + # - PYTHON: "C:\\Python36-x64" + # BUILD_JOB_NUM: 3 + # - PYTHON: "C:\\Python36-x64" + # BUILD_JOB_NUM: 4 build: off build_script: -- cgit v0.12 From c822776c8ec66a42fbc6764a583e56d2cf1984ed Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 15 Jul 2018 19:45:39 -0400 Subject: Force lxml to build static. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 31ec590..f17bedf 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,7 +4,7 @@ shallow_clone: true install: - "set PATH=%PYTHON%;%PYTHON%\\Scripts;C:\\cygwin64\\bin;C:\\msys64;%PATH%" - python --version - - pip install lxml + - STATIC_DEPS=true & pip install lxml - pip install pypiwin32 - choco install --allow-empty-checksums dmd - choco install --allow-empty-checksums ldc -- cgit v0.12 From 96cc018f721912d905e7b71b72de5747e0730210 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 15 Jul 2018 19:47:09 -0400 Subject: Fix set command --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index f17bedf..edc8e55 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,8 +4,8 @@ shallow_clone: true install: - "set PATH=%PYTHON%;%PYTHON%\\Scripts;C:\\cygwin64\\bin;C:\\msys64;%PATH%" - python --version - - STATIC_DEPS=true & pip install lxml - pip install pypiwin32 + - set STATIC_DEPS=true & pip install lxml - choco install --allow-empty-checksums dmd - choco install --allow-empty-checksums ldc - choco install --allow-empty-checksums swig -- cgit v0.12 From e0c8940559310988fb150b0ee8648da7200a1572 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 16 Jul 2018 10:27:22 -0400 Subject: Fix src/engine/SCons/ActionTests.py. Needed per python version valid strings to compare against for 3.7 --- src/engine/SCons/ActionTests.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index ed88f72..b9cc00b 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -1525,6 +1525,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase): (2, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), } meth_matches = [ @@ -1702,12 +1703,15 @@ class FunctionActionTestCase(unittest.TestCase): (2, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + } meth_matches = { (2, 7): bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 5): bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 6): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), + (3, 7): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), } def factory(act, **kw): @@ -1951,6 +1955,7 @@ class LazyActionTestCase(unittest.TestCase): (2, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), } meth_matches = [ @@ -2008,6 +2013,8 @@ class ActionCallerTestCase(unittest.TestCase): (2, 7): b'd\x00\x00S', (3, 5): b'd\x00\x00S', (3, 6): b'd\x00S\x00', + (3, 7): b'd\x00S\x00', + } af = SCons.Action.ActionFactory(GlobalFunc, strfunc) @@ -2206,6 +2213,7 @@ class ObjectContentsTestCase(unittest.TestCase): (2, 7): bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'), (3, 5): bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'), (3, 6): bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'), + (3, 7): bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'), } c = SCons.Action._function_contents(func1) @@ -2229,6 +2237,8 @@ class ObjectContentsTestCase(unittest.TestCase): b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"), (3, 6): bytearray( b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"), + (3, 7): bytearray( + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"), } assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr( @@ -2245,6 +2255,7 @@ class ObjectContentsTestCase(unittest.TestCase): (2, 7): bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00GHd\x01\x00S)'), (3, 5): bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'), (3, 6): bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'), + (3, 7): bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'), } assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + expected[ -- cgit v0.12 From 25c910740370a3620ff13be26c1ed8cff3b72c0c Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 6 Jul 2018 12:20:13 -0600 Subject: Start supporting newer java versions Signed-off-by: Mats Wichmann --- src/engine/SCons/Tool/JavaCommon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index dfb9e33..a253db0 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -65,7 +65,7 @@ if java_parsing: def __init__(self, version=default_java_version): if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', '1.7', - '1.8', '5', '6'): + '1.8', '5', '6', '9', '10'): msg = "Java version %s not supported" % version raise NotImplementedError(msg) @@ -171,7 +171,7 @@ if java_parsing: if self.version in ('1.1', '1.2', '1.3', '1.4'): clazz = self.listClasses[0] self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) - elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6'): + elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9', '10'): self.stackAnonClassBrackets.append(self.brackets) className = [] className.extend(self.listClasses) -- cgit v0.12 From 1e16a302b72ce5b5ecfc9b1c13588aec4a59daf3 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 17 Jul 2018 08:49:52 -0600 Subject: Fix typos from issue 3194 https://github.com/SCons/scons/issues/3149 Signed-off-by: Mats Wichmann --- doc/user/depends.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 8e548d2..297eaa2 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -571,7 +571,7 @@ int main() { printf("Hello, world!\n"); } The content signature, or MD5 checksum, of the contents of the dependency - file the list time the ⌖ was built. + file the last time the ⌖ was built. @@ -583,7 +583,7 @@ int main() { printf("Hello, world!\n"); } The size in bytes of the dependency - file the list time the target was built. + file the last time the target was built. @@ -595,7 +595,7 @@ int main() { printf("Hello, world!\n"); } The modification time of the dependency - file the list time the ⌖ was built. + file the last time the ⌖ was built. -- cgit v0.12 From 59c79865ef866cb76b7f772a18b58fa6e95d4e5d Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 18 Jul 2018 10:51:41 -0600 Subject: Update java version support Although the newer versions are marketed as simple versions, scons will see them as dotted versions: 9.0, 10.0. Add some comments to this in test code - the RMI test skip ought to work as long as they stay dotted versions (since 10.0 >= 1.8); that code does run the test for a simple version like '6'. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + src/engine/SCons/Tool/JavaCommon.py | 4 ++-- test/Java/RMIC.py | 4 ++++ test/Repository/RMIC.py | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 703bebb..1f0036b 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -93,6 +93,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE This changes SCons to better comply with normal Python installation practices. From Mats Wichmann: + - Recognize new java 9, 10 (as 9.0 and 10.0) - Updated manpage scons.xml to fix a nested list problem - Updated doc terminiology: use prepend instead of append as appropriate - xml validity fixes from SConstruct.py change diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index a253db0..5f54605 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -65,7 +65,7 @@ if java_parsing: def __init__(self, version=default_java_version): if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', '1.7', - '1.8', '5', '6', '9', '10'): + '1.8', '5', '6', '9.0', '10.0'): msg = "Java version %s not supported" % version raise NotImplementedError(msg) @@ -171,7 +171,7 @@ if java_parsing: if self.version in ('1.1', '1.2', '1.3', '1.4'): clazz = self.listClasses[0] self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) - elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9', '10'): + elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0'): self.stackAnonClassBrackets.append(self.brackets) className = [] className.extend(self.listClasses) diff --git a/test/Java/RMIC.py b/test/Java/RMIC.py index b29a466..19e799e 100644 --- a/test/Java/RMIC.py +++ b/test/Java/RMIC.py @@ -108,6 +108,10 @@ if java_version.count('.') == 1: # If it's 1.8 or higher, we skip the further RMIC test # because we'll get warnings about the deprecated API... # it's just not state-of-the-art anymore. +# Recent java versions (9 and greater) are back to being +# marketed as a simple version, but java_where_javac() will +# still return a dotted version, like 10.0. If this changes, +# will need to rework this rule. # Note, how we allow simple version strings like "5" and # "6" to successfully pass this test. if curver < (1, 8): diff --git a/test/Repository/RMIC.py b/test/Repository/RMIC.py index e08c716..433890f 100644 --- a/test/Repository/RMIC.py +++ b/test/Repository/RMIC.py @@ -51,6 +51,10 @@ if java_version.count('.') == 1: # If it's 1.8 or higher, we skip the further RMIC test # because we'll get warnings about the deprecated API... # it's just not state-of-the-art anymore. +# Recent java versions (9 and greater) are back to being +# marketed as a simple version, but java_where_javac() will +# still return a dotted version, like 10.0. If this changes, +# will need to rework this rule. # Note, how we allow simple version strings like "5" and # "6" to successfully pass this test. if curver >= (1, 8): -- cgit v0.12 From 58f4b2e91a62379b71246e4c882df82325c3f7fa Mon Sep 17 00:00:00 2001 From: Piotr Kasprzyk Date: Fri, 20 Jul 2018 16:04:30 +0200 Subject: Fix some misspellings, remove repeated: and Signed-off-by: Piotr Kasprzyk --- doc/user/environments.xml | 6 +++--- doc/user/parseflags.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/user/environments.xml b/doc/user/environments.xml index 2089dfe..65eed72 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -385,9 +385,9 @@ environment, of directory names, suffixes, etc. A &consenv; - is a distinct object creating within + is a distinct object created within a &SConscript; file and - and which contains values that + which contains values that affect how &SCons; decides what action to use to build a target, and even to define which targets @@ -1630,7 +1630,7 @@ env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' But doing so makes your &SConscript; file less portable, (although in this case that may not be a huge concern - since the directories you list are likley system-specific, anyway). + since the directories you list are likely system-specific, anyway). diff --git a/doc/user/parseflags.xml b/doc/user/parseflags.xml index fa35d49..46d6866 100644 --- a/doc/user/parseflags.xml +++ b/doc/user/parseflags.xml @@ -57,7 +57,7 @@ &SCons; construction environments have a &ParseFlags; method that takes a set of typical command-line options - and distrbutes them into the appropriate construction variables. + and distributes them into the appropriate construction variables. Historically, it was created to support the &ParseConfig; method, so it focuses on options used by the GNU Compiler Collection (GCC) for the C and C++ toolchains. -- cgit v0.12 From 9ec0b9861951cc72de39c742c1162a84696ce91f Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 21 Jul 2018 11:55:31 -0600 Subject: Add ability for SConscript to fail on missing script SConscript call now takes an optional must_exist flag, which defaults to False for compatiility with current behavior. If True, an exception is raised if the file is missing. To improve readability, the decision is moved off to a new function rather than being inline in _SConscript. A global setting to control the overall behavior is also added. A deprecation warning is added for the current behavior, which is printed only once. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + src/engine/SCons/Script/SConscript.py | 24 ++++++++++++++++++++++-- src/engine/SCons/Script/SConscript.xml | 18 ++++++++++++++---- src/engine/SCons/Script/__init__.py | 10 ++++++++++ src/engine/SCons/Warnings.py | 7 +++++-- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 79d25db..3b0e603 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -97,6 +97,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE This changes SCons to better comply with normal Python installation practices. From Mats Wichmann: + - Begin adding support for SConscript() failing on missing script - Updated manpage scons.xml to fix a nested list problem - Updated doc terminiology: use prepend instead of append as appropriate - xml validity fixes from SConstruct.py change diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index db6552c..5968346 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -153,6 +153,27 @@ def Return(*vars, **kw): stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) +def handle_missing_SConscript(f, must_exist): + """Take appropriate action on missing file in SConscript() call. + + The action may be to raise an exception, or print a warning. + On first warning, also print a deprecation warning. + """ + + if SCons.Script._no_missing_sconscript or must_exist: + msg = "Fatal: missing SConscript '%s'" % f.get_internal_path() + raise SCons.Errors.UserError(msg) + + if SCons.Script._warn_missing_sconscript_deprecated: + msg = "Calling missing SConscripts without error is deprecated.\n" + \ + "Transition by adding must_exist=0 to SConscript calls.\n" + \ + "Missing SConscript '%s'" % f.get_internal_path() + SCons.Warnings.warn(SCons.Warnings.DeprecatedMissingSConscriptWarning, msg) + SCons.Script._warn_missing_sconscript_deprecated = False + else: + msg = "Ignoring missing SConscript '%s'" % f.get_internal_path() + SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, msg) + def _SConscript(fs, *files, **kw): top = fs.Top sd = fs.SConstruct_dir.rdir() @@ -264,8 +285,7 @@ def _SConscript(fs, *files, **kw): if old_file is not None: call_stack[-1].globals.update({__file__:old_file}) else: - SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, - "Ignoring missing SConscript '%s'" % f.get_internal_path()) + handle_missing_SConscript(f, kw.get('must_exist', False)) finally: SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1 diff --git a/src/engine/SCons/Script/SConscript.xml b/src/engine/SCons/Script/SConscript.xml index 8553fbe..29a89c2 100644 --- a/src/engine/SCons/Script/SConscript.xml +++ b/src/engine/SCons/Script/SConscript.xml @@ -357,12 +357,12 @@ Return('val1 val2') -(scripts, [exports, variant_dir, duplicate]) - +(scripts, [exports, variant_dir, duplicate, must_exist=flag]) + -(dirs=subdirs, [name=script, exports, variant_dir, duplicate]) - +(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist=flag]) + @@ -562,6 +562,16 @@ and what about this alternative? TODO??? SConscript('build/SConscript', src_dir='src') --> + +The optional +must_exist +argument, if true, causes an exception to be raised if a requested +&SConscript; file is not found. The default is false, +which only prints a warning, but this behavior is deprecated. +For scripts which truly intend to be optional, transition to +explicty supplying +must_exist=False to the call. + Here are some composite examples: diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 89fc061..90bc311 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -276,6 +276,16 @@ def HelpFunction(text, append=False): # Will be non-zero if we are reading an SConscript file. sconscript_reading = 0 +_no_missing_sconscript = False +_warn_missing_sconscript_deprecated = True + +def set_missing_sconscript_error(flag=1): + """Set behavior on missing file in SConscript() call. Returns previous value""" + global _no_missing_sconscript + old = _no_missing_sconscript + _no_missing_sconscript = flag + return old + # def Variables(files=[], args=ARGUMENTS): return SCons.Variables.Variables(files, args) diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py index e8158a4..63acd22 100644 --- a/src/engine/SCons/Warnings.py +++ b/src/engine/SCons/Warnings.py @@ -147,6 +147,9 @@ class DeprecatedSigModuleWarning(MandatoryDeprecatedWarning): class DeprecatedBuilderKeywordsWarning(MandatoryDeprecatedWarning): pass +class DeprecatedMissingSConscriptWarning(DeprecatedWarning): + pass + # The below is a list of 2-tuples. The first element is a class object. # The second element is true if that class is enabled, false if it is disabled. @@ -179,8 +182,8 @@ def warn(clazz, *args): global _enabled, _warningAsException, _warningOut warning = clazz(args) - for clazz, flag in _enabled: - if isinstance(warning, clazz): + for cls, flag in _enabled: + if isinstance(warning, cls): if flag: if _warningAsException: raise warning -- cgit v0.12 From bde85d7501235a67bee32c65f83a9e4da52e3968 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 24 Jul 2018 15:44:48 -0600 Subject: Fix py3 ResourceWarning in TestCmd class TestCmd method read() uses a shortcut to return data from a file, "return open(...).read()". Python 3 warns this is a resource leak because the file has no chance to be closed with the open being in the return statement. Split into two lines and use a context manager (with statement). Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + testing/framework/TestCmd.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 79d25db..e3a646c 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -102,6 +102,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - xml validity fixes from SConstruct.py change - update wiki links to new github location - update bug links to new github location + - convert TestCmd.read to use with statement on open (quiets 17 py3 warnings) From Hao Wu - typo in customized decider example in user guide diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 9cd6b39..9499ff4 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -1247,9 +1247,11 @@ class TestCmd(object): if mode[0] != 'r': raise ValueError("mode must begin with 'r'") if IS_PY3 and 'b' not in mode: - return open(file, mode, newline=newline).read() + with open(file, mode, newline=newline) as f: + return f.read() else: - return open(file, mode).read() + with open(file, mode) as f: + return f.read() def rmdir(self, dir): """Removes the specified dir name. -- cgit v0.12 From 8308dd2ad4286ac3c9e882913451e9e517a0a238 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 24 Jul 2018 17:43:59 -0600 Subject: Quiet UtilTest ResourceWarning Use context manager around file opens to quiet a Python 3 resource leak warning. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + src/engine/SCons/UtilTests.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index e3a646c..26ed803 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -103,6 +103,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - update wiki links to new github location - update bug links to new github location - convert TestCmd.read to use with statement on open (quiets 17 py3 warnings) + - quiet warning in UtilTests.py From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index 842d4d8..81ec09d 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -488,8 +488,10 @@ class UtilTestCase(unittest.TestCase): filename = tempfile.mktemp() str = '1234567890 ' + filename try: - open(filename, 'w').write(str) - assert open(get_native_path(filename)).read() == str + with open(filename, 'w') as f: + f.write(str) + with open(get_native_path(filename)) as f: + assert f.read() == str finally: try: os.unlink(filename) -- cgit v0.12 From a27c072faaa7dd5eb598d1df031c34e2f4492571 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 25 Jul 2018 06:28:59 -0600 Subject: Change octal constants in QT tests Resolving two test failures in Python 3 caused by: http://www.python.org/dev/peps/pep-3127/: octal literals must now be specified with a leading "0o" or "0O" instead of "0"; Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + test/QT/generated-ui.py | 2 +- test/QT/up-to-date.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 26ed803..c875f1f 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -104,6 +104,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - update bug links to new github location - convert TestCmd.read to use with statement on open (quiets 17 py3 warnings) - quiet warning in UtilTests.py + - fix tests specifying octal constants for Py3 From Hao Wu - typo in customized decider example in user guide diff --git a/test/QT/generated-ui.py b/test/QT/generated-ui.py index e4632b1..4692a82 100644 --- a/test/QT/generated-ui.py +++ b/test/QT/generated-ui.py @@ -61,7 +61,7 @@ import SCons.Defaults def ExpHeaderScanner(node, env, path): return [] def generate(env): - HeaderAction=SCons.Action.Action([SCons.Defaults.Copy('$TARGET','$SOURCE'),SCons.Defaults.Chmod('$TARGET',0755)]) + HeaderAction=SCons.Action.Action([SCons.Defaults.Copy('$TARGET','$SOURCE'),SCons.Defaults.Chmod('$TARGET',0o755)]) HeaderBuilder= SCons.Builder.Builder(action=HeaderAction) env['BUILDERS']['ExportHeaders'] = HeaderBuilder def exists(env): diff --git a/test/QT/up-to-date.py b/test/QT/up-to-date.py index 21c758e..7a7e565 100644 --- a/test/QT/up-to-date.py +++ b/test/QT/up-to-date.py @@ -66,7 +66,7 @@ import SCons.Defaults def ExpHeaderScanner(node, env, path): return [] def generate(env): - HeaderAction=SCons.Action.Action([SCons.Defaults.Copy('$TARGET','$SOURCE'),SCons.Defaults.Chmod('$TARGET',0755)]) + HeaderAction=SCons.Action.Action([SCons.Defaults.Copy('$TARGET','$SOURCE'),SCons.Defaults.Chmod('$TARGET',0o755)]) HeaderBuilder= SCons.Builder.Builder(action=HeaderAction) env['BUILDERS']['ExportHeaders'] = HeaderBuilder def exists(env): -- cgit v0.12 From d15065cdcfccf8943fa3ede7b225b698f3aec707 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 25 Jul 2018 16:46:25 -0600 Subject: Testing: python 3 fix for must_contain TestCommon defines a method must_contain which checks for a file including a given string. With Python 3, the test runs into some typing problems. This could be fixed either by changing all the tests which call the routine either omitting the mode argument (which then defaults to 'rb'), or specifying a mode which includes 'b'; or by modifying must_contain to align the types of the file data and the data to check for. This patch uses the latter approach. This is a test-only change, no run-time scons code is modified. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 5 +++-- testing/framework/TestCommon.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index c875f1f..cc8e5c6 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -103,8 +103,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - update wiki links to new github location - update bug links to new github location - convert TestCmd.read to use with statement on open (quiets 17 py3 warnings) - - quiet warning in UtilTests.py - - fix tests specifying octal constants for Py3 + - quiet py3 warning in UtilTests.py + - fix tests specifying octal constants for py3 + - fix must_contain tests for py3 From Hao Wu - typo in customized decider example in user guide diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index 47a149b..e551cce 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -268,6 +268,15 @@ class TestCommon(TestCmd): def must_contain(self, file, required, mode = 'rb', find = None): """Ensures that the specified file contains the required text. """ + if 'b' in mode: + # Python 3: reading a file in binary mode returns a + # bytes object. We cannot find the index of a different + # (str) type in that, so encode "required". For Py2 + # it is all just strings, so it still works. + try: + required = required.encode() + except AttributeError: + pass # in case it's encoded already file_contents = self.read(file, mode) if find is None: def find(o, l): -- cgit v0.12 From 14daa07c0d95e1cd806e92911a74bde3ccf95b9f Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 28 Jul 2018 10:04:35 -0600 Subject: Add tests for SConscript(must_warn) option Testcases added to confirm the behavior of: first attempt to call a non-existent script gives a deprecation warning, additional ones give plain warning; True/False values for must_warn behave as expected; if scons default is changed to exception the call fails but if must_warn=False it still works. Tweaked the logic to actually get that last bit to work. Also minor doc update. Signed-off-by: Mats Wichmann --- src/engine/SCons/Script/SConscript.py | 7 +- src/engine/SCons/Script/SConscript.xml | 13 ++-- test/SConscript/must_exist.py | 119 +++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 test/SConscript/must_exist.py diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 5968346..fb6ec0d 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -153,14 +153,14 @@ def Return(*vars, **kw): stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) -def handle_missing_SConscript(f, must_exist): +def handle_missing_SConscript(f, must_exist=None): """Take appropriate action on missing file in SConscript() call. The action may be to raise an exception, or print a warning. On first warning, also print a deprecation warning. """ - if SCons.Script._no_missing_sconscript or must_exist: + if must_exist or (SCons.Script._no_missing_sconscript and must_exist is not False): msg = "Fatal: missing SConscript '%s'" % f.get_internal_path() raise SCons.Errors.UserError(msg) @@ -285,7 +285,7 @@ def _SConscript(fs, *files, **kw): if old_file is not None: call_stack[-1].globals.update({__file__:old_file}) else: - handle_missing_SConscript(f, kw.get('must_exist', False)) + handle_missing_SConscript(f, kw.get('must_exist', None)) finally: SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1 @@ -568,6 +568,7 @@ class SConsEnvironment(SCons.Environment.Base): files, exports = self._get_SConscript_filenames(ls, subst_kw) subst_kw['exports'] = exports + return _SConscript(self.fs, *files, **subst_kw) def SConscriptChdir(self, flag): diff --git a/src/engine/SCons/Script/SConscript.xml b/src/engine/SCons/Script/SConscript.xml index 29a89c2..a6258c4 100644 --- a/src/engine/SCons/Script/SConscript.xml +++ b/src/engine/SCons/Script/SConscript.xml @@ -357,12 +357,12 @@ Return('val1 val2') -(scripts, [exports, variant_dir, duplicate, must_exist=flag]) - +(scripts, [exports, variant_dir, duplicate, must_exist]) + -(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist=flag]) - +(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist]) + @@ -562,12 +562,13 @@ and what about this alternative? TODO??? SConscript('build/SConscript', src_dir='src') --> + The optional must_exist argument, if true, causes an exception to be raised if a requested -&SConscript; file is not found. The default is false, -which only prints a warning, but this behavior is deprecated. +&SConscript; file is not found. The current default is false, +causing only a warning to be omitted, but this behavior is deprecated. For scripts which truly intend to be optional, transition to explicty supplying must_exist=False to the call. diff --git a/test/SConscript/must_exist.py b/test/SConscript/must_exist.py new file mode 100644 index 0000000..a4341fa --- /dev/null +++ b/test/SConscript/must_exist.py @@ -0,0 +1,119 @@ +#!/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__" + +''' +Test handling of must_exist flag and global setting requiring the +file to exist in an SConscript call +''' + +import TestSCons + +test = TestSCons.TestSCons() + +# catch the exception if is raised, send it on as a warning +# this gives us traceability of the line responsible +SConstruct_path = test.workpath('SConstruct') +test.write(SConstruct_path, """\ +import SCons +from SCons.Warnings import _warningOut +import sys + +# 1. call should succeed with deprecation warning +try: + SConscript('missing/SConscript') +except SCons.Errors.UserError as e: + if _warningOut: + _warningOut(e) +# 2. call should succeed with warning +try: + SConscript('missing/SConscript') +except SCons.Errors.UserError as e: + if _warningOut: + _warningOut(e) +# 3. call should raise exception +try: + SConscript('missing/SConscript', must_exist=True) +except SCons.Errors.UserError as e: + if _warningOut: + _warningOut(e) +# 4. call should succeed with warning +try: + SConscript('missing/SConscript', must_exist=False) +except SCons.Errors.UserError as e: + if _warningOut: + _warningOut(e) +SCons.Script.set_missing_sconscript_error() +# 5. with system setting changed, should raise exception +try: + SConscript('missing/SConscript') +except SCons.Errors.UserError as e: + if _warningOut: + _warningOut(e) +# 6. must_exist=False should override system setting +try: + SConscript('missing/SConscript', must_exist=False) +except SCons.Errors.UserError as e: + if _warningOut: + _warningOut(e) +""") + +# we should see two exceptions as "Fatal" and +# and see four warnings, the first having the depr message +warn1 = """ +scons: warning: Calling missing SConscripts without error is deprecated. +Transition by adding must_exist=0 to SConscript calls. +Missing SConscript 'missing/SConscript' +""" + test.python_file_line(SConstruct_path, 7) + +warn2 = """ +scons: warning: Ignoring missing SConscript 'missing/SConscript' +""" + test.python_file_line(SConstruct_path, 13) + +err1 = """ +scons: warning: Fatal: missing SConscript 'missing/SConscript' +""" + test.python_file_line(SConstruct_path, 22) + +warn3 = """ +scons: warning: Ignoring missing SConscript 'missing/SConscript' +""" + test.python_file_line(SConstruct_path, 25) + +err2 = """ +scons: warning: Fatal: missing SConscript 'missing/SConscript' +""" + test.python_file_line(SConstruct_path, 35) + +warn4 = """ +scons: warning: Ignoring missing SConscript 'missing/SConscript' +""" + test.python_file_line(SConstruct_path, 38) + +expect_stderr = warn1 + warn2 + err1 + warn3 + err2 + warn4 +test.run(arguments = ".", stderr = expect_stderr) +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From 6166782fe90fc96a866424958fd6a632c76318f6 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 29 Jul 2018 08:12:42 -0600 Subject: Add a docstring for SConscript() Signed-off-by: Mats Wichmann --- src/engine/SCons/Script/SConscript.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index fb6ec0d..3b73010 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -543,6 +543,31 @@ class SConsEnvironment(SCons.Environment.Base): raise SCons.Errors.UserError("Import of non-existent variable '%s'"%x) def SConscript(self, *ls, **kw): + """Execute SCons configuration files. + + Parameters: + *ls (str or list): configuration file(s) to execute. + + Keyword arguments: + dirs (list): execute SConscript in each listed directory. + name (str): execute script 'name' (used with 'dirs'). + exports (list or dict): locally export variables the script(s) + can import. + variant_dir (str): mirror sources needed for build to variant_dir + to allow building there. + duplicate (bool): pysically duplicate sources instead of just + adjusting paths of derived files (used only with 'variant_dir') + (default is True). + must_exist (bool): fail if a requested script is missing + (default is False, default is deprecated). + + Returns: + variables returned by the called script + + Raises: + UserError if a script is not found and such exceptions are enabled. + """ + if 'build_dir' in kw: msg = """The build_dir keyword has been deprecated; use the variant_dir keyword instead.""" SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) @@ -568,7 +593,6 @@ class SConsEnvironment(SCons.Environment.Base): files, exports = self._get_SConscript_filenames(ls, subst_kw) subst_kw['exports'] = exports - return _SConscript(self.fs, *files, **subst_kw) def SConscriptChdir(self, flag): -- cgit v0.12 From 44ed2ad161836d9d3f119fc6454c9e92a356c1e1 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 29 Jul 2018 20:24:18 -0600 Subject: Some further adjustments to missing-sconscript tests Signed-off-by: Mats Wichmann --- src/engine/SCons/Script/SConscript.py | 4 ++-- test/SConscript/must_exist.py | 4 ++-- test/option-f.py | 9 ++++++--- test/option/warn-missing-sconscript.py | 11 +++++++++-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 3b73010..1d83bb6 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -165,10 +165,10 @@ def handle_missing_SConscript(f, must_exist=None): raise SCons.Errors.UserError(msg) if SCons.Script._warn_missing_sconscript_deprecated: - msg = "Calling missing SConscripts without error is deprecated.\n" + \ + msg = "Calling missing SConscript without error is deprecated.\n" + \ "Transition by adding must_exist=0 to SConscript calls.\n" + \ "Missing SConscript '%s'" % f.get_internal_path() - SCons.Warnings.warn(SCons.Warnings.DeprecatedMissingSConscriptWarning, msg) + SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, msg) SCons.Script._warn_missing_sconscript_deprecated = False else: msg = "Ignoring missing SConscript '%s'" % f.get_internal_path() diff --git a/test/SConscript/must_exist.py b/test/SConscript/must_exist.py index a4341fa..ac90cd1 100644 --- a/test/SConscript/must_exist.py +++ b/test/SConscript/must_exist.py @@ -72,7 +72,7 @@ try: except SCons.Errors.UserError as e: if _warningOut: _warningOut(e) -# 6. must_exist=False should override system setting +# 6. must_exist=False overrides system setting, should emit warning try: SConscript('missing/SConscript', must_exist=False) except SCons.Errors.UserError as e: @@ -83,7 +83,7 @@ except SCons.Errors.UserError as e: # we should see two exceptions as "Fatal" and # and see four warnings, the first having the depr message warn1 = """ -scons: warning: Calling missing SConscripts without error is deprecated. +scons: warning: Calling missing SConscript without error is deprecated. Transition by adding must_exist=0 to SConscript calls. Missing SConscript 'missing/SConscript' """ + test.python_file_line(SConstruct_path, 7) diff --git a/test/option-f.py b/test/option-f.py index 21afacb..46e2686 100644 --- a/test/option-f.py +++ b/test/option-f.py @@ -97,9 +97,12 @@ test.run(arguments = '-f Build2 -f SConscript .', stdout=expect) test.run(arguments = '-f no_such_file .', stdout = test.wrap_stdout("scons: `.' is up to date.\n"), stderr = None) -test.fail_test(not test.match_re(test.stderr(), """ -scons: warning: Ignoring missing SConscript 'no_such_file' -""" + TestSCons.file_expr)) +expect = """ +scons: warning: Calling missing SConscript without error is deprecated. +Transition by adding must_exist=0 to SConscript calls. +Missing SConscript 'no_such_file'""" +stderr = test.stderr() +test.must_contain_all(test.stderr(), expect) test.pass_test() diff --git a/test/option/warn-missing-sconscript.py b/test/option/warn-missing-sconscript.py index 4f1f8bd..492131b 100644 --- a/test/option/warn-missing-sconscript.py +++ b/test/option/warn-missing-sconscript.py @@ -51,16 +51,23 @@ test.write("foo.c",""" """) expect = r""" -scons: warning: Ignoring missing SConscript 'no_such_file' +scons: warning: Calling missing SConscript without error is deprecated. +Transition by adding must_exist=0 to SConscript calls. +Missing SConscript 'no_such_file' """ + TestSCons.file_expr +# this is the old message: +#expect = r""" +#scons: warning: Ignoring missing SConscript 'no_such_file' +"" + TestSCons.file_expr + test.run(arguments = '--warn=missing-sconscript .', stderr = expect) test.run(arguments = '--warn=no-missing-sconscript .', stderr = "") test.run(arguments = 'WARN=missing-sconscript .', stderr = expect) -test.run(arguments = 'WARN=no-missing-sconscript .') +test.run(arguments = 'WARN=no-missing-sconscript .', stderr = "") test.pass_test() -- cgit v0.12 From b4d4d281f771c2bec37829188b96ec2b148a8198 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 30 Jul 2018 07:33:30 -0600 Subject: Fix for #3162: tweak SConscript() docstrings a little more Also handle_missing_SConscript(), internal interface added by this patch series. Signed-off-by: Mats Wichmann --- src/engine/SCons/Script/SConscript.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 1d83bb6..560402c 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -156,8 +156,16 @@ stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) def handle_missing_SConscript(f, must_exist=None): """Take appropriate action on missing file in SConscript() call. - The action may be to raise an exception, or print a warning. - On first warning, also print a deprecation warning. + Print a warning or raise an exception on missing file. + On first warning, print a deprecation message. + + Args: + f (str): path of missing configuration file + must_exist (bool): raise exception if file does not exist + + Raises: + UserError if 'must_exist' is True or if global + SCons.Script._no_missing_sconscript is True. """ if must_exist or (SCons.Script._no_missing_sconscript and must_exist is not False): @@ -550,22 +558,22 @@ class SConsEnvironment(SCons.Environment.Base): Keyword arguments: dirs (list): execute SConscript in each listed directory. - name (str): execute script 'name' (used with 'dirs'). - exports (list or dict): locally export variables the script(s) - can import. - variant_dir (str): mirror sources needed for build to variant_dir - to allow building there. - duplicate (bool): pysically duplicate sources instead of just + name (str): execute script 'name' (used only with 'dirs'). + exports (list or dict): locally export variables the + called script(s) can import. + variant_dir (str): mirror sources needed for the build in + a variant directory to allow building in it. + duplicate (bool): physically duplicate sources instead of just adjusting paths of derived files (used only with 'variant_dir') (default is True). must_exist (bool): fail if a requested script is missing (default is False, default is deprecated). Returns: - variables returned by the called script + list of variables returned by the called script Raises: - UserError if a script is not found and such exceptions are enabled. + UserError: a script is not found and such exceptions are enabled. """ if 'build_dir' in kw: -- cgit v0.12 From 0cf044543fb089bcd997b3ad2d877b71026ce5bf Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 31 Jul 2018 16:57:26 -0600 Subject: Try a more scons-y file conversion for Py3 file reads Instead of custom conversion as in the previous iteration, use the to_bytes function. The two known tests which incorrectly let the text-mode xml file be opened in binary mode are adjusted to supply mode='r' Signed-off-by: Mats Wichmann --- test/Docbook/basic/xinclude/xinclude.py | 2 +- test/Docbook/dependencies/xinclude/xinclude.py | 2 +- testing/framework/TestCommon.py | 24 ++++++++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/test/Docbook/basic/xinclude/xinclude.py b/test/Docbook/basic/xinclude/xinclude.py index 302c777..9b22c13 100644 --- a/test/Docbook/basic/xinclude/xinclude.py +++ b/test/Docbook/basic/xinclude/xinclude.py @@ -44,7 +44,7 @@ test.dir_fixture('image') # Normal invocation test.run() test.must_exist(test.workpath('manual_xi.xml')) -test.must_contain(test.workpath('manual_xi.xml'),'This is an included text.') +test.must_contain(test.workpath('manual_xi.xml'),'This is an included text.', mode='r') # Cleanup diff --git a/test/Docbook/dependencies/xinclude/xinclude.py b/test/Docbook/dependencies/xinclude/xinclude.py index 115163c..c3d9e25 100644 --- a/test/Docbook/dependencies/xinclude/xinclude.py +++ b/test/Docbook/dependencies/xinclude/xinclude.py @@ -44,7 +44,7 @@ test.dir_fixture('image') # Normal invocation test.run() test.must_exist(test.workpath('manual_xi.xml')) -test.must_contain(test.workpath('manual_xi.xml'),'This is an included text.') +test.must_contain(test.workpath('manual_xi.xml'),'This is an included text.', mode='r') # Change included file test.write('include.txt', 'This is another text.') diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index e551cce..e55b491 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -265,18 +265,26 @@ class TestCommon(TestCmd): print("Unwritable files: `%s'" % "', `".join(unwritable)) self.fail_test(missing + unwritable) - def must_contain(self, file, required, mode = 'rb', find = None): - """Ensures that the specified file contains the required text. + def must_contain(self, file, required, mode='rb', find=None): + """Ensures specified file contains the required text. + + Args: + file (string): name of file to search in. + required (string): text to search for. For the default + find function, type must match the return type from + reading the file; current implementation will convert. + mode (string): file open mode. + find (func): optional custom search routine. Must take the + form "find(output, line)" returning non-zero on success + and None on failure. + + Calling test exits FAILED if search result is false """ if 'b' in mode: # Python 3: reading a file in binary mode returns a # bytes object. We cannot find the index of a different - # (str) type in that, so encode "required". For Py2 - # it is all just strings, so it still works. - try: - required = required.encode() - except AttributeError: - pass # in case it's encoded already + # (str) type in that, so convert. + required = to_bytes(required) file_contents = self.read(file, mode) if find is None: def find(o, l): -- cgit v0.12 From 6892a428fa4a8fc8b3994e05458104a48d3c9ed1 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 1 Aug 2018 13:41:30 -0600 Subject: Fix conflict on java-version patch due to dropped character --- src/engine/SCons/Tool/JavaCommon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 5f54605..47555a7 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -64,7 +64,7 @@ if java_parsing: interfaces, and anonymous inner classes.""" def __init__(self, version=default_java_version): - if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', '1.7', + if not version in ('1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0'): msg = "Java version %s not supported" % version raise NotImplementedError(msg) -- cgit v0.12 From e5390b34767b3911108841caedd6c1c73f982fbd Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 6 Aug 2018 18:01:23 -0600 Subject: Fix for #3162: be more descriptive in changelog --- src/CHANGES.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 3b0e603..cf2a8b1 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -97,12 +97,23 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE This changes SCons to better comply with normal Python installation practices. From Mats Wichmann: - - Begin adding support for SConscript() failing on missing script - Updated manpage scons.xml to fix a nested list problem - Updated doc terminiology: use prepend instead of append as appropriate - xml validity fixes from SConstruct.py change - update wiki links to new github location - update bug links to new github location + - Make it easier for SConscript() call to fail on missing script. + It was possible to call SCons.Warnings.warningAsException + (not documented as a user API) to make all warnings fail. Now + SConscript can take an optional must_exist flag which if true fails + if the script does not exist. Not failing on missing script is + now considered deprecated, and the first instance will print a + deprecation message. It is now also possible to flip the scons + behavior (which still defaults to warn, not fail) by calling + SCons.Script.set_missing_sconscript_error, which is also not a + documented interface at the moment. + +Begin adding support for SConscript() failing on missing script From Hao Wu - typo in customized decider example in user guide -- cgit v0.12 From ab762bc68d5f4bf06e696897291c8c76ea006ae2 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 7 Aug 2018 22:00:48 -0700 Subject: Add python 3.7 to travis builds --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index a1f58b0..31fd324 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,11 @@ jobs: sudo: required - <<: *test_job + python: 3.7 + env: PYVER=37 + sudo: required + + - <<: *test_job python: pypy env: PYVER=pypy sudo: required -- cgit v0.12 From 15ec7da378436e4d9ddb197a6bad43c7059b336c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 7 Aug 2018 22:04:03 -0700 Subject: Add python 3.7 dev to travis builds --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 31fd324..58ca595 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ jobs: sudo: required - <<: *test_job - python: 3.7 + python: 3.7-dev env: PYVER=37 sudo: required -- cgit v0.12 From 522d235045a3e485c12f34ecb631c1ecfdd4c216 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 9 Aug 2018 09:38:41 -0600 Subject: Fix the new missing-sconscript test for Windows Missed that string matching on the invalid sconscript path needs to be OS-agnostic - the CI builders don't include a windows image, so this was not caught. Call normpath on the path before pushing it into the expected strings. Signed-off-by: Mats Wichmann --- test/SConscript/must_exist.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/test/SConscript/must_exist.py b/test/SConscript/must_exist.py index ac90cd1..3faf0ce 100644 --- a/test/SConscript/must_exist.py +++ b/test/SConscript/must_exist.py @@ -29,6 +29,7 @@ Test handling of must_exist flag and global setting requiring the file to exist in an SConscript call ''' +import os import TestSCons test = TestSCons.TestSCons() @@ -82,31 +83,33 @@ except SCons.Errors.UserError as e: # we should see two exceptions as "Fatal" and # and see four warnings, the first having the depr message +# need to build the path in the expected msg in an OS-agnostic way +missing = os.path.normpath('missing/SConscript') warn1 = """ scons: warning: Calling missing SConscript without error is deprecated. Transition by adding must_exist=0 to SConscript calls. -Missing SConscript 'missing/SConscript' -""" + test.python_file_line(SConstruct_path, 7) +Missing SConscript '{}' +""".format(missing) + test.python_file_line(SConstruct_path, 7) warn2 = """ -scons: warning: Ignoring missing SConscript 'missing/SConscript' -""" + test.python_file_line(SConstruct_path, 13) +scons: warning: Ignoring missing SConscript '{}' +""".format(missing) + test.python_file_line(SConstruct_path, 13) err1 = """ -scons: warning: Fatal: missing SConscript 'missing/SConscript' -""" + test.python_file_line(SConstruct_path, 22) +scons: warning: Fatal: missing SConscript '{}' +""".format(missing) + test.python_file_line(SConstruct_path, 22) warn3 = """ -scons: warning: Ignoring missing SConscript 'missing/SConscript' -""" + test.python_file_line(SConstruct_path, 25) +scons: warning: Ignoring missing SConscript '{}' +""".format(missing) + test.python_file_line(SConstruct_path, 25) err2 = """ -scons: warning: Fatal: missing SConscript 'missing/SConscript' -""" + test.python_file_line(SConstruct_path, 35) +scons: warning: Fatal: missing SConscript '{}' +""".format(missing) + test.python_file_line(SConstruct_path, 35) warn4 = """ -scons: warning: Ignoring missing SConscript 'missing/SConscript' -""" + test.python_file_line(SConstruct_path, 38) +scons: warning: Ignoring missing SConscript '{}' +""".format(missing) + test.python_file_line(SConstruct_path, 38) expect_stderr = warn1 + warn2 + err1 + warn3 + err2 + warn4 test.run(arguments = ".", stderr = expect_stderr) -- cgit v0.12 From 680805f3ff9fc0c2823f084ff2f5b31e38f9471f Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 10 Aug 2018 15:45:54 -0500 Subject: Updated FS to handle removal of splitunc function from python 3.7 --- src/engine/SCons/Node/FS.py | 5 ++++- src/engine/SCons/Node/FSTests.py | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 9a48432..54cd423 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -132,7 +132,10 @@ def initialize_do_splitdrive(): global do_splitdrive global has_unc drive, path = os.path.splitdrive('X:/foo') - has_unc = hasattr(os.path, 'splitunc') + # splitunc is removed from python 3.7 and newer + # so we can also just test if splitdrive works with UNC + has_unc = (hasattr(os.path, 'splitunc') + or os.path.splitdrive(r'\\split\drive\test')[0] == r'\\split\drive') do_splitdrive = not not drive or has_unc diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 273f809..2c4dcfa 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -1657,7 +1657,12 @@ class FSTestCase(_tempdirTestCase): import ntpath x = test.workpath(*dirs) drive, path = ntpath.splitdrive(x) - unc, path = ntpath.splitunc(path) + try: + unc, path = ntpath.splitunc(path) + except AttributeError: + # could be python 3.7 or newer, make sure splitdrive can do UNC + assert ntpath.splitdrive(r'\\split\drive\test')[0] == r'\\split\drive' + pass path = strip_slash(path) return '//' + path[1:] -- cgit v0.12 From a582b0671ee3dbf152dfec15bef968410af5ece8 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 10 Aug 2018 16:00:14 -0500 Subject: Switched test to use assertFalse because of deprication warning in 3.7 --- src/engine/SCons/Node/FSTests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 2c4dcfa..698f574 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -605,7 +605,7 @@ class VariantDirTestCase(unittest.TestCase): print("File `%s' alter_targets() `%s' != expected `%s'" % (f, tp, expect)) errors = errors + 1 - self.failIf(errors) + self.assertFalse(errors) class BaseTestCase(_tempdirTestCase): def test_stat(self): -- cgit v0.12 From 47db1a99a1017144a10e0f1537b1d6b585bae786 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 10 Aug 2018 16:01:53 -0500 Subject: updated CHANGES.txt --- src/CHANGES.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 51cbc27..58b8a7e 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,7 +7,10 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - From Matthew Marinets + From Daniel Moody: + - Updated FS.py to handle removal of splitunc function from python 3.7 + + From Matthew Marinets: - Fixed an issue that caused the Java emitter to incorrectly parse arguments to constructors that implemented a class. -- cgit v0.12 From 17fe6134fee6c574f27405d480572549f433c5be Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 10 Aug 2018 16:25:26 -0500 Subject: trying just some of the base packages, no docs or extra langs --- .travis/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/install.sh b/.travis/install.sh index 16f7263..c15e991 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -8,7 +8,7 @@ sudo apt-get -y install gdc # dependencies for docbook tests sudo apt-get -y install docbook-xml xsltproc libxml2-dev libxslt-dev fop docbook-xsl-doc-pdf # dependencies for latex tests -sudo apt-get -y install texlive-full biber texmaker +sudo apt-get -y install texlive texlive-bibtex-extra texlive-latex3 biber texmaker # need some things for building dependencies for other tests sudo apt-get -y install python-pip python-dev build-essential libpcre3-dev autoconf automake libtool bison subversion git # dependencies for docbook tests continued -- cgit v0.12 From eea7786935c30c9048bd186849f269b39fd174ab Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 10 Aug 2018 17:12:22 -0500 Subject: removing the biber-extras for reduced travis install footprint --- .travis/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/install.sh b/.travis/install.sh index c15e991..24735a8 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -8,7 +8,7 @@ sudo apt-get -y install gdc # dependencies for docbook tests sudo apt-get -y install docbook-xml xsltproc libxml2-dev libxslt-dev fop docbook-xsl-doc-pdf # dependencies for latex tests -sudo apt-get -y install texlive texlive-bibtex-extra texlive-latex3 biber texmaker +sudo apt-get -y install texlive texlive-latex3 biber texmaker # need some things for building dependencies for other tests sudo apt-get -y install python-pip python-dev build-essential libpcre3-dev autoconf automake libtool bison subversion git # dependencies for docbook tests continued -- cgit v0.12 From 5ecccc638450e06302e69a8875359ce6a304125e Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 12 Aug 2018 01:08:12 -0400 Subject: updated test to reflect the removal of find function from mingw tool --- test/MinGW/MinGWSharedLibrary.py | 7 +++++-- test/MinGW/WINDOWS_INSERT_DEF.py | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/test/MinGW/MinGWSharedLibrary.py b/test/MinGW/MinGWSharedLibrary.py index dcebd45..86ddd43 100644 --- a/test/MinGW/MinGWSharedLibrary.py +++ b/test/MinGW/MinGWSharedLibrary.py @@ -32,8 +32,11 @@ when using MinGW. import sys import TestSCons +import SCons.Tool import SCons.Tool.mingw import SCons.Defaults +from SCons.Platform.mingw import MINGW_DEFAULT_PATHS +from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS _python_ = TestSCons._python_ @@ -42,10 +45,10 @@ test = TestSCons.TestSCons() if sys.platform not in ('cygwin','win32',): test.skip_test("Skipping mingw test on non-Windows %s platform."%sys.platform) -if not SCons.Tool.mingw.find(SCons.Defaults.DefaultEnvironment()): +gcc = SCons.Tool.find_program_path(test.Environment(), 'gcc', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) +if not gcc: test.skip_test("Skipping mingw test, no MinGW found.\n") - test.write('foobar.cc', """ int abc(int a) { return (a+1); diff --git a/test/MinGW/WINDOWS_INSERT_DEF.py b/test/MinGW/WINDOWS_INSERT_DEF.py index 67d3e9b..f82ebd8 100644 --- a/test/MinGW/WINDOWS_INSERT_DEF.py +++ b/test/MinGW/WINDOWS_INSERT_DEF.py @@ -31,8 +31,11 @@ Make sure that WINDOWS_INSERT_DEF isn't ignored when using MinGW. import sys import TestSCons +import SCons.Tool import SCons.Tool.mingw import SCons.Defaults +from SCons.Platform.mingw import MINGW_DEFAULT_PATHS +from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS test = TestSCons.TestSCons() @@ -40,7 +43,8 @@ if sys.platform not in ('cygwin', 'win32'): test.skip_test( "Skipping mingw test on non-Windows platform: %s" % sys.platform) -if not SCons.Tool.mingw.find(SCons.Defaults.DefaultEnvironment()): +gcc = SCons.Tool.find_program_path(test.Environment(), 'gcc', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) +if not gcc: test.skip_test("Skipping mingw test, no MinGW found.\n") test.write('hello.c', r""" -- cgit v0.12 From 57d09cd12f950ba938ab0164f3b68ed937353318 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 13 Aug 2018 01:28:05 -0400 Subject: updated vc and tests to check the msvc cl binary --- src/engine/SCons/Tool/MSCommon/vc.py | 14 ++++++++++++-- test/MSVS/vs-10.0Exp-exec.py | 8 ++++++-- test/MSVS/vs-11.0-exec.py | 8 ++++++-- test/MSVS/vs-11.0Exp-exec.py | 8 ++++++-- test/MSVS/vs-14.0-exec.py | 8 ++++++-- test/MSVS/vs-6.0-clean.py | 2 +- test/MSVS/vs-6.0-exec.py | 8 ++++++-- test/MSVS/vs-7.0-exec.py | 8 ++++++-- test/MSVS/vs-7.1-exec.py | 8 ++++++-- test/MSVS/vs-8.0-exec.py | 7 ++++++- test/MSVS/vs-8.0Exp-exec.py | 8 ++++++-- test/MSVS/vs-9.0-exec.py | 8 ++++++-- test/MSVS/vs-9.0Exp-exec.py | 8 ++++++-- 13 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py index 53bd397..e5c9aaf 100644 --- a/src/engine/SCons/Tool/MSCommon/vc.py +++ b/src/engine/SCons/Tool/MSCommon/vc.py @@ -43,6 +43,7 @@ import platform from string import digits as string_digits import SCons.Warnings +from SCons.Tool import find_program_path from . import common @@ -357,9 +358,13 @@ def get_installed_vcs(): for ver in _VCVER: debug('trying to find VC %s' % ver) try: - if find_vc_pdir(ver): + VC_DIR = find_vc_pdir(ver) + if VC_DIR: debug('found VC %s' % ver) - installed_versions.append(ver) + if os.path.exists(os.path.join(VC_DIR, r'bin\cl.exe')): + installed_versions.append(ver) + else: + debug('find_vc_pdir no cl.exe found %s' % ver) else: debug('find_vc_pdir return None for ver %s' % ver) except VisualCException as e: @@ -565,6 +570,11 @@ def msvc_setup_env(env): for k, v in d.items(): debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) env.PrependENVPath(k, v, delete_existing=True) + + msvc_cl = find_program_path(env, 'cl') + if not msvc_cl: + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, + "Could not find MSVC compiler 'cl.exe', it may need to be installed separately with Visual Studio") def msvc_exists(version=None): vcs = cached_get_installed_vcs() diff --git a/test/MSVS/vs-10.0Exp-exec.py b/test/MSVS/vs-10.0Exp-exec.py index a63f6c4..d6114bd 100644 --- a/test/MSVS/vs-10.0Exp-exec.py +++ b/test/MSVS/vs-10.0Exp-exec.py @@ -55,10 +55,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-11.0-exec.py b/test/MSVS/vs-11.0-exec.py index 21645f5..48acd1c 100644 --- a/test/MSVS/vs-11.0-exec.py +++ b/test/MSVS/vs-11.0-exec.py @@ -55,10 +55,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-11.0Exp-exec.py b/test/MSVS/vs-11.0Exp-exec.py index be48971..6a288a5 100644 --- a/test/MSVS/vs-11.0Exp-exec.py +++ b/test/MSVS/vs-11.0Exp-exec.py @@ -55,10 +55,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-14.0-exec.py b/test/MSVS/vs-14.0-exec.py index f2a826c..d2b7112 100644 --- a/test/MSVS/vs-14.0-exec.py +++ b/test/MSVS/vs-14.0-exec.py @@ -55,10 +55,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-6.0-clean.py b/test/MSVS/vs-6.0-clean.py index 6b9bd98..0cbadba 100644 --- a/test/MSVS/vs-6.0-clean.py +++ b/test/MSVS/vs-6.0-clean.py @@ -72,7 +72,7 @@ env.MSVSSolution(target = 'Test.dsw', variant = 'Release') """%{'HOST_ARCH':host_arch}) -test.run(arguments=".") +test.run() test.must_exist(test.workpath('Test.dsp')) dsp = test.read('Test.dsp', 'r') diff --git a/test/MSVS/vs-6.0-exec.py b/test/MSVS/vs-6.0-exec.py index d017790..0864f76 100644 --- a/test/MSVS/vs-6.0-exec.py +++ b/test/MSVS/vs-6.0-exec.py @@ -54,10 +54,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-7.0-exec.py b/test/MSVS/vs-7.0-exec.py index e62ee77..e95ca83 100644 --- a/test/MSVS/vs-7.0-exec.py +++ b/test/MSVS/vs-7.0-exec.py @@ -54,10 +54,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-7.1-exec.py b/test/MSVS/vs-7.1-exec.py index 42f6ae8..11ea617 100644 --- a/test/MSVS/vs-7.1-exec.py +++ b/test/MSVS/vs-7.1-exec.py @@ -54,10 +54,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-8.0-exec.py b/test/MSVS/vs-8.0-exec.py index 96c4c29..4b0a6dd 100644 --- a/test/MSVS/vs-8.0-exec.py +++ b/test/MSVS/vs-8.0-exec.py @@ -55,9 +55,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -print("os.environ.update(%%s)" %% repr(env['ENV'])) +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-8.0Exp-exec.py b/test/MSVS/vs-8.0Exp-exec.py index 66196f1..0e4396d 100644 --- a/test/MSVS/vs-8.0Exp-exec.py +++ b/test/MSVS/vs-8.0Exp-exec.py @@ -55,10 +55,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-9.0-exec.py b/test/MSVS/vs-9.0-exec.py index 7b544aa..3f823fa 100644 --- a/test/MSVS/vs-9.0-exec.py +++ b/test/MSVS/vs-9.0-exec.py @@ -55,10 +55,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) diff --git a/test/MSVS/vs-9.0Exp-exec.py b/test/MSVS/vs-9.0Exp-exec.py index caa763e..5a65faf 100644 --- a/test/MSVS/vs-9.0Exp-exec.py +++ b/test/MSVS/vs-9.0Exp-exec.py @@ -55,10 +55,14 @@ if not msvs_version in test.msvs_versions(): test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if(test.stdout() == ""): + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) -- cgit v0.12 From 2fc51a43533f7700cf8084da3d688e1746bbfa8c Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 13 Aug 2018 01:43:02 -0400 Subject: also check for 64 bit compiler and added some comments --- src/engine/SCons/Tool/MSCommon/vc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py index e5c9aaf..32ee96f 100644 --- a/src/engine/SCons/Tool/MSCommon/vc.py +++ b/src/engine/SCons/Tool/MSCommon/vc.py @@ -361,7 +361,9 @@ def get_installed_vcs(): VC_DIR = find_vc_pdir(ver) if VC_DIR: debug('found VC %s' % ver) - if os.path.exists(os.path.join(VC_DIR, r'bin\cl.exe')): + # check to see if the x86 or 64 bit compiler is in the bin dir + if (os.path.exists(os.path.join(VC_DIR, r'bin\cl.exe')) + or os.path.exists(os.path.join(VC_DIR, r'bin\amd64\cl.exe'))): installed_versions.append(ver) else: debug('find_vc_pdir no cl.exe found %s' % ver) @@ -571,6 +573,7 @@ def msvc_setup_env(env): debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) env.PrependENVPath(k, v, delete_existing=True) + # final check to issue a warning if the compiler is not present msvc_cl = find_program_path(env, 'cl') if not msvc_cl: SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, -- cgit v0.12 From c91224a97b07b4793817f8bf28be86ad082dd7f1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 13 Aug 2018 01:44:15 -0400 Subject: updated CHANGES.txt --- src/CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6534f3a..662873d 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -9,6 +9,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Daniel Moody: - Updated FS.py to handle removal of splitunc function from python 3.7 + - Updated the vc.py to ignore MSVS versions where not compiler could be found From Matthew Marinets: - Fixed an issue that caused the Java emitter to incorrectly parse arguments to constructors that -- cgit v0.12 From 00673a71ed8aeb99831272e0daba01ad36cdd073 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 7 Aug 2018 17:05:50 -0600 Subject: Fix rpm tests for newer rpmbuild (fixes #3164) Two issues: (1) ARCHITECTURE, if supplied, actually needs to be set in the specfile BuildArch: field (or passed on the command line). (2) rpmbuild on Fedora builds all kind of debug stuff behind the scenes (it makes packages not requested by the specfile!). This causes scons rpm tests to fail, so force in a flag to turn off this generation. That is done unconditionally at the moment by writing %global debug_package %{nil}\n to the specfile. If anyone eventually ends up needing to produce these debug packages through scons, we can add a flag to allow it by omitting the above setting. Some doc and comment cleanup done, and one of the tests now builds a noarch package to test actually supplying an ARCHITECTURE value that is not the machine default. Also, one of the tools called by rpm packaging did not supply the right suffix for the Tar builder, presumably a copy-paste error in a place that is not stressed by any test. Fedora packaging now also generates a file containing the build id and puts it into the package (another behind the scenes bit of magic - this does not appear in our %files section but is still added to the package). It is possible to turn this behavior off as well, but for the tests affected, the choice was made instead to have them now check the files in the generated package /contain/ the expected filenames rather than /exactly-match/ the expected filenames. Testing: on recent Fedora all the rpm-related tests now pass after six failed previously; on a Centos 7.4 system which did not fail any tests, the modified code still passes - did not introduce regressions. At the moment do not believe any of the CI setup will cause rpm tests to be run. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 4 +++ src/engine/SCons/Tool/packaging/__init__.xml | 15 ++++++---- src/engine/SCons/Tool/packaging/rpm.py | 41 ++++++++++++++++------------ src/engine/SCons/Tool/packaging/tarbz2.py | 2 +- test/packaging/option--package-type.py | 8 ++++-- test/packaging/rpm/multipackage.py | 6 ++-- test/packaging/rpm/package.py | 6 ++-- test/packaging/rpm/tagging.py | 7 +++-- 8 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6534f3a..8be3bbe 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -120,6 +120,10 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - quiet py3 warning in UtilTests.py - fix tests specifying octal constants for py3 - fix must_contain tests for py3 + - rpm package generation: fix supplying a build architecture; disable + auto debug package generation on certain rpmbuild versions; adjust some tests + to be resistant to automatic supplying of build-id file on certain rpmbuild + versions; a little doc and comment cleanup. From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/Tool/packaging/__init__.xml b/src/engine/SCons/Tool/packaging/__init__.xml index e4b1865..68e4fed 100644 --- a/src/engine/SCons/Tool/packaging/__init__.xml +++ b/src/engine/SCons/Tool/packaging/__init__.xml @@ -106,7 +106,9 @@ This is used to fill in the Architecture: field in an Ipkg control file, -and as part of the name of a generated RPM file. +and the BuildArch: field +in the RPM .spec file, +as well as forming part of the name of a generated RPM package file. @@ -120,7 +122,6 @@ the control for Ipkg, the .wxs for MSI). If set, the function will be called after the SCons template for the file has been written. -XXX @@ -164,10 +165,10 @@ section of an RPM -The abbreviated name of the license under which -this project is released (gpl, lpgl, bsd etc.). +The abbreviated name, preferably the SPDX code, of the license under which +this project is released (GPL-3.0, LGPL-2.1, BSD-2-Clause etc.). See http://www.opensource.org/licenses/alphabetical -for a list of license names. +for a list of license names and SPDX codes. @@ -383,6 +384,7 @@ This is used to fill in the BuildRequires: field in the RPM .spec file. +Note this should only be used on a host managed by rpm as the dependencies will not be resolvable at build time otherwise. @@ -441,7 +443,8 @@ field in the RPM This is used to fill in the Epoch: -field in the controlling information for RPM packages. +field in the RPM +.spec file. diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py index a3481a4..e93c4f0 100644 --- a/src/engine/SCons/Tool/packaging/rpm.py +++ b/src/engine/SCons/Tool/packaging/rpm.py @@ -51,10 +51,9 @@ def package(env, target, source, PACKAGEROOT, NAME, VERSION, if str(target[0])!="%s-%s"%(NAME, VERSION): raise UserError( "Setting target is not supported for rpm." ) else: - # This should be overridable from the construction environment, - # which it is by using ARCHITECTURE=. + # Deduce the build architecture, but allow it to be overridden + # by setting ARCHITECTURE in the construction env. buildarchitecture = SCons.Tool.rpmutils.defaultMachine() - if 'ARCHITECTURE' in kw: buildarchitecture = kw['ARCHITECTURE'] @@ -126,20 +125,18 @@ def build_specfile(target, source, env): """ Builds a RPM specfile from a dictionary with string metadata and by analyzing a tree of nodes. """ - file = open(target[0].get_abspath(), 'w') - - try: - file.write( build_specfile_header(env) ) - file.write( build_specfile_sections(env) ) - file.write( build_specfile_filesection(env, source) ) - file.close() + with open(target[0].get_abspath(), 'w') as file: + try: + file.write(build_specfile_header(env)) + file.write(build_specfile_sections(env)) + file.write(build_specfile_filesection(env, source)) - # call a user specified function - if 'CHANGE_SPECFILE' in env: - env['CHANGE_SPECFILE'](target, source) + # call a user specified function + if 'CHANGE_SPECFILE' in env: + env['CHANGE_SPECFILE'](target, source) - except KeyError as e: - raise SCons.Errors.UserError( '"%s" package field for RPM is missing.' % e.args[0] ) + except KeyError as e: + raise SCons.Errors.UserError('"%s" package field for RPM is missing.' % e.args[0]) # @@ -211,6 +208,7 @@ def build_specfile_header(spec): 'X_RPM_URL' : 'Url: %s\n', 'SOURCE_URL' : 'Source: %s\n', 'SUMMARY_' : 'Summary(%s): %s\n', + 'ARCHITECTURE' : 'BuildArch: %s\n', 'X_RPM_DISTRIBUTION' : 'Distribution: %s\n', 'X_RPM_ICON' : 'Icon: %s\n', 'X_RPM_PACKAGER' : 'Packager: %s\n', @@ -232,16 +230,23 @@ def build_specfile_header(spec): 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', } # fill in default values: - # Adding a BuildRequires renders the .rpm unbuildable under System, which + # Adding a BuildRequires renders the .rpm unbuildable under systems which # are not managed by rpm, since the database to resolve this dependency is # missing (take Gentoo as an example) -# if not s.has_key('x_rpm_BuildRequires'): -# s['x_rpm_BuildRequires'] = 'scons' + #if 'X_RPM_BUILDREQUIRES' not in spec: + # spec['X_RPM_BUILDREQUIRES'] = 'scons' if 'X_RPM_BUILDROOT' not in spec: spec['X_RPM_BUILDROOT'] = '%{_tmppath}/%{name}-%{version}-%{release}' str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec ) + + # github #3164: if we don't turn off debug package generation + # the tests which build packages all fail. This just slams + # a flag into the specfile header, could make it an X_RPM_NODEBUGPKGS + # which is controllable by an environment setting. + str += '%global debug_package %{nil}\n' + return str # diff --git a/src/engine/SCons/Tool/packaging/tarbz2.py b/src/engine/SCons/Tool/packaging/tarbz2.py index 2e38da2..3806e76 100644 --- a/src/engine/SCons/Tool/packaging/tarbz2.py +++ b/src/engine/SCons/Tool/packaging/tarbz2.py @@ -32,7 +32,7 @@ from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot def package(env, target, source, PACKAGEROOT, **kw): bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.gz') + bld.set_suffix('.tar.bz2') target, source = putintopackageroot(target, source, env, PACKAGEROOT) target, source = stripinstallbuilder(target, source, env) return bld(env, target, source, TARFLAGS='-jc') diff --git a/test/packaging/option--package-type.py b/test/packaging/option--package-type.py index c8f22ca..7ff8535 100644 --- a/test/packaging/option--package-type.py +++ b/test/packaging/option--package-type.py @@ -26,6 +26,9 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Test the --package-type option. + +Side effect: also tests that we can produce a noarch package +by supplying the ARCHITECTURE tag. """ import TestSCons @@ -63,12 +66,13 @@ env.Package( NAME = 'foo', X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', DESCRIPTION = 'this should be really long', source = [ prog ], - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz', + ARCHITECTURE = 'noarch' ) """ % locals()) src_rpm = 'foo-1.2.3-0.src.rpm' -machine_rpm = 'foo-1.2.3-0.%s.rpm' % SCons.Tool.rpmutils.defaultMachine() +machine_rpm = 'foo-1.2.3-0.noarch.rpm' test.run(arguments='package PACKAGETYPE=rpm', stderr = None) diff --git a/test/packaging/rpm/multipackage.py b/test/packaging/rpm/multipackage.py index ab8734d..f756cf4 100644 --- a/test/packaging/rpm/multipackage.py +++ b/test/packaging/rpm/multipackage.py @@ -109,8 +109,10 @@ test.must_exist( machine_rpm2 ) test.must_exist( src_rpm2 ) test.must_not_exist( 'bin/main' ) -test.fail_test( not os.popen('rpm -qpl %s' % machine_rpm).read()=='/bin/main\n') -test.fail_test( not os.popen('rpm -qpl %s' % src_rpm).read()=='foo-1.2.3.spec\nfoo-1.2.3.tar.gz\n') +out = os.popen( 'rpm -qpl %s' % machine_rpm).read() +test.must_contain_all_lines( out, '/bin/main') +out = os.popen( 'rpm -qpl %s' % src_rpm).read() +test.fail_test( not out == 'foo-1.2.3.spec\nfoo-1.2.3.tar.gz\n') test.pass_test() diff --git a/test/packaging/rpm/package.py b/test/packaging/rpm/package.py index d8c2785..6723a9f 100644 --- a/test/packaging/rpm/package.py +++ b/test/packaging/rpm/package.py @@ -88,8 +88,10 @@ machine_rpm = 'foo-1.2.3-0.%s.rpm' % SCons.Tool.rpmutils.defaultMachine() test.must_exist( machine_rpm ) test.must_exist( src_rpm ) test.must_not_exist( 'bin/main' ) -test.fail_test( not os.popen('rpm -qpl %s' % machine_rpm).read()=='/bin/main\n') -test.fail_test( not os.popen('rpm -qpl %s' % src_rpm).read()=='foo-1.2.3.spec\nfoo-1.2.3.tar.gz\n') +out = os.popen( 'rpm -qpl %s' % machine_rpm).read() +test.must_contain_all_lines( out, '/bin/main') +out = os.popen( 'rpm -qpl %s' % src_rpm).read() +test.fail_test( not out == 'foo-1.2.3.spec\nfoo-1.2.3.tar.gz\n') test.pass_test() diff --git a/test/packaging/rpm/tagging.py b/test/packaging/rpm/tagging.py index a558242..15f82a7 100644 --- a/test/packaging/rpm/tagging.py +++ b/test/packaging/rpm/tagging.py @@ -91,9 +91,12 @@ src_rpm = 'foo-1.2.3-0.src.rpm' machine_rpm = 'foo-1.2.3-0.%s.rpm' % SCons.Tool.rpmutils.defaultMachine() test.must_exist( machine_rpm ) +out = os.popen('rpm -qpl %s' % machine_rpm).read() +test.must_contain_all_lines( out, '/bin/main') + test.must_exist( src_rpm ) -test.fail_test( not os.popen('rpm -qpl %s' % machine_rpm).read()=='/bin/main\n') -test.fail_test( not os.popen('rpm -qpl %s' % src_rpm).read()=='foo-1.2.3.spec\nfoo-1.2.3.tar.gz\n') +out = os.popen('rpm -qpl %s' % src_rpm).read() +test.fail_test( not out == 'foo-1.2.3.spec\nfoo-1.2.3.tar.gz\n') expect = '(0755, root, users) /bin/main' test.must_contain_all_lines(test.read('foo-1.2.3.spec',mode='r'), [expect]) -- cgit v0.12 From 2a39eb278ecca56f66011f2d602e12b924ad0e2c Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 13 Aug 2018 10:40:42 -0600 Subject: SWIG recursive test checkes for Python.h The generated code will #include , so skip the test if the appropriate development support for Python is not installed, the test will FAIL otherwise. The other SWIG tests do this already. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + test/SWIG/recursive-includes-cpp.py | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6534f3a..be5a90a 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -120,6 +120,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - quiet py3 warning in UtilTests.py - fix tests specifying octal constants for py3 - fix must_contain tests for py3 + - one swig test now checks for Python.h instead of failing From Hao Wu - typo in customized decider example in user guide diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index b943406..34c7d6e 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -44,6 +44,12 @@ for pre_req in ['swig', 'python']: if not test.where_is(pre_req): test.skip_test('Can not find installed "' + pre_req + '", skipping test.%s' % os.linesep) +# file generated by swig will #include , make sure we have one +python, py_include, py_libpath, py_lib = test.get_platform_python_info() +Python_h = os.path.join(py_include, 'Python.h') +if not os.path.exists(Python_h): + test.skip_test('Can not find %s, skipping test.\n' % Python_h) + if sys.platform == 'win32': python_lib = os.path.dirname(sys.executable) + "/libs/" + ('python%d%d'%(sys.version_info[0],sys.version_info[1])) + '.lib' if( not os.path.isfile(python_lib)): @@ -150,4 +156,4 @@ test.pass_test() # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From a5c17eae4d5197d423a8fcef40b1d31e74439772 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 13 Aug 2018 14:55:01 -0600 Subject: Quiet Py3.6+ ResourceWarnings on tests When a test calls _subproc to set up a subprocess.Popen call, if the arg vector contains the string 'devnull' os.devnull is opened and the resulting file object is stored in the kw vector prior to passing it to Popen. Reigster a close() call with atexit in this case. Eliminates a lot of Python 3.6+ ResourceWarning messages - 336 on this machine. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + src/engine/SCons/Action.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6534f3a..7c5abba 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -120,6 +120,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - quiet py3 warning in UtilTests.py - fix tests specifying octal constants for py3 - fix must_contain tests for py3 + - if test opens os.devnull, register with atexit so file opens do not leak. From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index b44088b..249363f 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -107,6 +107,7 @@ import sys import subprocess import itertools import inspect +import atexit import SCons.Debug from SCons.Debug import logInstanceCreation @@ -772,12 +773,15 @@ def _subproc(scons_env, cmd, error = 'ignore', **kw): io = kw.get('stdin') if is_String(io) and io == 'devnull': kw['stdin'] = open(os.devnull) + atexit.register(kw['stdin'].close) io = kw.get('stdout') if is_String(io) and io == 'devnull': kw['stdout'] = open(os.devnull, 'w') + atexit.register(kw['stdout'].close) io = kw.get('stderr') if is_String(io) and io == 'devnull': kw['stderr'] = open(os.devnull, 'w') + atexit.register(kw['stderr'].close) # Figure out what shell environment to use ENV = kw.get('env', None) -- cgit v0.12 From 2252e4b3ca7cb9ee75e739cabceee6fd8d324476 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 13 Aug 2018 16:08:21 -0500 Subject: use clang from local/bin instead of installing --- .travis.yml | 5 ++++- .travis/install.sh | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 58ca595..01c5eed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,9 @@ jobs: include: - &test_job stage: Test - script: python runtest.py -a || if [[ $? == 2 ]]; then true; else false; fi + script: + - export PATH=/usr/local/bin:$PATH + - python runtest.py -a || if [[ $? == 2 ]]; then true; else false; fi before_script: skip after_success: skip python: 2.7 @@ -67,6 +69,7 @@ jobs: - echo "coverage.process_startup()" | sudo tee --append ${PYSITEDIR}/usercustomize.py script: + - export PATH=/usr/local/bin:$PATH - export TOTAL_BUILD_JOBS=8 # write the coverage config file - export COVERAGE_PROCESS_START=$PWD/.coveragerc diff --git a/.travis/install.sh b/.travis/install.sh index 24735a8..d61872d 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -x -# dependencies for clang tests -sudo apt-get -y install clang # dependencies for gdc tests sudo apt-get -y install gdc # dependencies for docbook tests -- cgit v0.12 From ceb52d82b137d5fc6c9c2bab773f0d6e3a8b515b Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 13 Aug 2018 14:13:42 -0700 Subject: Fix wrong path to testing frameworks SConscript (was breaking bootstrap builds) --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index fc94abd..f644d6d 100644 --- a/SConstruct +++ b/SConstruct @@ -825,7 +825,7 @@ for p in [ scons ]: # Export('build_dir', 'env') -SConscript('testing/SConscript') +SConscript('testing/framework/SConscript') # # -- cgit v0.12 From 51704ca99e0787de2a6a6b7ba9502d2043b95546 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 13 Aug 2018 16:26:28 -0500 Subject: testing symbolic links instead of messing with the environment --- .travis.yml | 86 +----------------------------------------------------- .travis/install.sh | 5 ++++ 2 files changed, 6 insertions(+), 85 deletions(-) diff --git a/.travis.yml b/.travis.yml index 01c5eed..2c2896d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,93 +19,9 @@ jobs: - &test_job stage: Test script: - - export PATH=/usr/local/bin:$PATH - - python runtest.py -a || if [[ $? == 2 ]]; then true; else false; fi + - python runtest.py test/Clang before_script: skip after_success: skip python: 2.7 env: PYVER=27 sudo: required - - - <<: *test_job - python: 3.5 - env: PYVER=35 - sudo: required - - - <<: *test_job - python: 3.6 - env: PYVER=36 - sudo: required - - - <<: *test_job - python: 3.7-dev - env: PYVER=37 - sudo: required - - - <<: *test_job - python: pypy - env: PYVER=pypy - sudo: required - - - - &coverage_jobs - stage: Coverage - - before_script: - - sudo pip install coverage - - sudo pip install coveralls - # set this ensure user sites are available - - export PYTHONNOUSERSITE= - # attempt to get a location where we can store the usercustomize.py file - - python -m site - - export PYSITEDIR=$(python -m site --user-site) - - sudo mkdir -p $PYSITEDIR - - sudo touch ${PYSITEDIR}/usercustomize.py - - export COVERAGE_FILE=$PWD/.coverage_file - # write the usercustomize.py file so all python processes use coverage and know where the config file is - - echo "import os" | sudo tee --append ${PYSITEDIR}/usercustomize.py - - echo "os.environ['COVERAGE_PROCESS_START'] = '$PWD/.coveragerc'" | sudo tee --append ${PYSITEDIR}/usercustomize.py - - echo "import coverage" | sudo tee --append ${PYSITEDIR}/usercustomize.py - - echo "coverage.process_startup()" | sudo tee --append ${PYSITEDIR}/usercustomize.py - - script: - - export PATH=/usr/local/bin:$PATH - - export TOTAL_BUILD_JOBS=8 - # write the coverage config file - - export COVERAGE_PROCESS_START=$PWD/.coveragerc - - echo "[run]" >> .coveragerc - - echo "source = $PWD/src" >> .coveragerc - - echo "parallel = True" >> .coveragerc - - printf "omit =\n\t*Tests.py\n\tsrc/test_*\n\tsrc/setup.py\n\n" >> .coveragerc - - echo "[path] = $PWD" >> .coveragerc - # get a list of all the tests to split them up - - python runtest.py -l -a > all_tests - - let "start = ($(wc -l < all_tests) / ${TOTAL_BUILD_JOBS}) * (${BUILD_JOB_NUM} - 1)"; true; - - let "end = ($(wc -l < all_tests) / ${TOTAL_BUILD_JOBS}) * ${BUILD_JOB_NUM}" - - if (( ${BUILD_JOB_NUM} == ${TOTAL_BUILD_JOBS} )); then end=$(wc -l < all_tests); fi - - if (( ${start} == 0 )); then start=1; fi - - sed -n ${start},${end}p all_tests > build_tests - - coverage run -p --rcfile=$PWD/.coveragerc runtest.py -f build_tests || if [[ $? == 2 ]]; then true; else false; fi - - after_success: - - coverage combine - - coverage report - - coveralls --rcfile=$PWD/.coveragerc - - env: BUILD_JOB_NUM=1 - - - <<: *coverage_jobs - env: BUILD_JOB_NUM=2 - - <<: *coverage_jobs - env: BUILD_JOB_NUM=3 - - <<: *coverage_jobs - env: BUILD_JOB_NUM=4 - - <<: *coverage_jobs - env: BUILD_JOB_NUM=5 - - <<: *coverage_jobs - env: BUILD_JOB_NUM=6 - - <<: *coverage_jobs - env: BUILD_JOB_NUM=7 - - <<: *coverage_jobs - env: BUILD_JOB_NUM=8 - diff --git a/.travis/install.sh b/.travis/install.sh index d61872d..e404499 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -1,6 +1,11 @@ #!/usr/bin/env bash set -x +sudo ln -s /usr/local/bin/clang /usr/bin/clang +sudo ln -s /usr/local/bin/clang++ /usr/bin/clang++ + +return + # dependencies for gdc tests sudo apt-get -y install gdc # dependencies for docbook tests -- cgit v0.12 From 0690e461007d2ffdf77740c3c4d88f34115d33b1 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 13 Aug 2018 16:41:06 -0500 Subject: using the correct path --- .travis/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis/install.sh b/.travis/install.sh index e404499..8b51bec 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash set -x -sudo ln -s /usr/local/bin/clang /usr/bin/clang -sudo ln -s /usr/local/bin/clang++ /usr/bin/clang++ +sudo ln -s /usr/local/clang-5.0.0/bin/clang /usr/bin/clang +sudo ln -s /usr/local/clang-5.0.0/bin/clang++ /usr/bin/clang++ return -- cgit v0.12 From 2677cc2b860128928efee8897b42caad6e7ba8cd Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 13 Aug 2018 16:49:46 -0500 Subject: reverting travis back to master --- .travis.yml | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c2896d..58ca595 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,10 +18,91 @@ jobs: include: - &test_job stage: Test - script: - - python runtest.py test/Clang + script: python runtest.py -a || if [[ $? == 2 ]]; then true; else false; fi before_script: skip after_success: skip python: 2.7 env: PYVER=27 sudo: required + + - <<: *test_job + python: 3.5 + env: PYVER=35 + sudo: required + + - <<: *test_job + python: 3.6 + env: PYVER=36 + sudo: required + + - <<: *test_job + python: 3.7-dev + env: PYVER=37 + sudo: required + + - <<: *test_job + python: pypy + env: PYVER=pypy + sudo: required + + + - &coverage_jobs + stage: Coverage + + before_script: + - sudo pip install coverage + - sudo pip install coveralls + # set this ensure user sites are available + - export PYTHONNOUSERSITE= + # attempt to get a location where we can store the usercustomize.py file + - python -m site + - export PYSITEDIR=$(python -m site --user-site) + - sudo mkdir -p $PYSITEDIR + - sudo touch ${PYSITEDIR}/usercustomize.py + - export COVERAGE_FILE=$PWD/.coverage_file + # write the usercustomize.py file so all python processes use coverage and know where the config file is + - echo "import os" | sudo tee --append ${PYSITEDIR}/usercustomize.py + - echo "os.environ['COVERAGE_PROCESS_START'] = '$PWD/.coveragerc'" | sudo tee --append ${PYSITEDIR}/usercustomize.py + - echo "import coverage" | sudo tee --append ${PYSITEDIR}/usercustomize.py + - echo "coverage.process_startup()" | sudo tee --append ${PYSITEDIR}/usercustomize.py + + script: + - export TOTAL_BUILD_JOBS=8 + # write the coverage config file + - export COVERAGE_PROCESS_START=$PWD/.coveragerc + - echo "[run]" >> .coveragerc + - echo "source = $PWD/src" >> .coveragerc + - echo "parallel = True" >> .coveragerc + - printf "omit =\n\t*Tests.py\n\tsrc/test_*\n\tsrc/setup.py\n\n" >> .coveragerc + - echo "[path] = $PWD" >> .coveragerc + # get a list of all the tests to split them up + - python runtest.py -l -a > all_tests + - let "start = ($(wc -l < all_tests) / ${TOTAL_BUILD_JOBS}) * (${BUILD_JOB_NUM} - 1)"; true; + - let "end = ($(wc -l < all_tests) / ${TOTAL_BUILD_JOBS}) * ${BUILD_JOB_NUM}" + - if (( ${BUILD_JOB_NUM} == ${TOTAL_BUILD_JOBS} )); then end=$(wc -l < all_tests); fi + - if (( ${start} == 0 )); then start=1; fi + - sed -n ${start},${end}p all_tests > build_tests + - coverage run -p --rcfile=$PWD/.coveragerc runtest.py -f build_tests || if [[ $? == 2 ]]; then true; else false; fi + + after_success: + - coverage combine + - coverage report + - coveralls --rcfile=$PWD/.coveragerc + + env: BUILD_JOB_NUM=1 + + - <<: *coverage_jobs + env: BUILD_JOB_NUM=2 + - <<: *coverage_jobs + env: BUILD_JOB_NUM=3 + - <<: *coverage_jobs + env: BUILD_JOB_NUM=4 + - <<: *coverage_jobs + env: BUILD_JOB_NUM=5 + - <<: *coverage_jobs + env: BUILD_JOB_NUM=6 + - <<: *coverage_jobs + env: BUILD_JOB_NUM=7 + - <<: *coverage_jobs + env: BUILD_JOB_NUM=8 + -- cgit v0.12 From cb85766498fa08cdf1f306824ab00b77624a730e Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 13 Aug 2018 16:50:28 -0500 Subject: added comments to the travis install script --- .travis/install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis/install.sh b/.travis/install.sh index 8b51bec..ddef1e6 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -1,11 +1,10 @@ #!/usr/bin/env bash set -x +# setup clang for clang tests using local clang installation sudo ln -s /usr/local/clang-5.0.0/bin/clang /usr/bin/clang sudo ln -s /usr/local/clang-5.0.0/bin/clang++ /usr/bin/clang++ -return - # dependencies for gdc tests sudo apt-get -y install gdc # dependencies for docbook tests -- cgit v0.12 From b4b5abd17a3357561a3b7b9f7f7916778a719d93 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 14 Aug 2018 15:00:29 -0600 Subject: Redo fix for file closing in SCons.Action._subproc When the _subproc wrapper is called, a keyword dictionary is used to hold open file objects if any were indicated as being redirected to os.devull. These objects just go missing when SCons.Action._subproc is done. A previous iteration registered the close method of the object with atexit, which made Python 3 happy the open file resources were not leaking, but has some unexpected side effects. Apparently Action cannot import either atexit or the SCons equivalent, SCons.exitfuncs without triggering these effects. As an alternative, close the objects in the parent's kw dictionary that have close methods after the subprocess.Popen object has been created. Signed-off-by: Mats Wichmann --- src/engine/SCons/Action.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 249363f..32ceaa9 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -107,7 +107,6 @@ import sys import subprocess import itertools import inspect -import atexit import SCons.Debug from SCons.Debug import logInstanceCreation @@ -116,8 +115,7 @@ import SCons.Util import SCons.Subst # we use these a lot, so try to optimize them -is_String = SCons.Util.is_String -is_List = SCons.Util.is_List +from SCons.Util import is_String, is_List class _null(object): pass @@ -773,15 +771,12 @@ def _subproc(scons_env, cmd, error = 'ignore', **kw): io = kw.get('stdin') if is_String(io) and io == 'devnull': kw['stdin'] = open(os.devnull) - atexit.register(kw['stdin'].close) io = kw.get('stdout') if is_String(io) and io == 'devnull': kw['stdout'] = open(os.devnull, 'w') - atexit.register(kw['stdout'].close) io = kw.get('stderr') if is_String(io) and io == 'devnull': kw['stderr'] = open(os.devnull, 'w') - atexit.register(kw['stderr'].close) # Figure out what shell environment to use ENV = kw.get('env', None) @@ -807,7 +802,7 @@ def _subproc(scons_env, cmd, error = 'ignore', **kw): kw['env'] = new_env try: - return subprocess.Popen(cmd, **kw) + pobj = subprocess.Popen(cmd, **kw) except EnvironmentError as e: if error == 'raise': raise # return a dummy Popen instance that only returns error @@ -821,7 +816,14 @@ def _subproc(scons_env, cmd, error = 'ignore', **kw): def readline(self): return '' def __iter__(self): return iter(()) stdout = stderr = f() - return dummyPopen(e) + pobj = dummyPopen(e) + finally: + # clean up open file handles stored in parent's kw + import io + for k, v in kw.items(): + if hasattr(v, 'close'): + v.close() + return pobj class CommandAction(_ActionAction): -- cgit v0.12 From 52ecb92eb30698b67de2662a892e20be3f1fda20 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 15 Aug 2018 08:16:11 -0600 Subject: Move checking for python header to common location A previous change added a check for Python.h in one SWIG test which did not have it which turns that test into a skip instead of a fail if the header is not installed. It was pointed out that having 12 tests check for the same thing might be optimised by putting the check in the routine which returns info about the python development environment, so this change makes that modification. Signed-off-by: Mats Wichmann --- test/SWIG/SWIGOUTDIR-python.py | 6 +--- test/SWIG/build-dir.py | 6 +--- test/SWIG/generated_swigfile.py | 6 +--- test/SWIG/live.py | 11 +++----- test/SWIG/module-deduced-name.py | 6 +--- test/SWIG/module-parens.py | 6 +--- test/SWIG/module-quoted.py | 6 +--- test/SWIG/module-spaces.py | 6 +--- test/SWIG/noproxy.py | 6 +--- test/SWIG/recursive-includes-cpp.py | 7 ++--- test/SWIG/remove-modules.py | 6 +--- test/SWIG/subdir.py | 6 +--- testing/framework/TestSCons.py | 55 +++++++++++++++++++++++++------------ 13 files changed, 53 insertions(+), 80 deletions(-) diff --git a/test/SWIG/SWIGOUTDIR-python.py b/test/SWIG/SWIGOUTDIR-python.py index db0cc95..53b5bdf 100644 --- a/test/SWIG/SWIGOUTDIR-python.py +++ b/test/SWIG/SWIGOUTDIR-python.py @@ -30,7 +30,6 @@ that Python files are created in the specified output directory. """ import TestSCons -import os import sys test = TestSCons.TestSCons() @@ -39,11 +38,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) # On Windows, build a 32-bit exe if on 32-bit python. if sys.platform == 'win32' and sys.maxsize <= 2**32: diff --git a/test/SWIG/build-dir.py b/test/SWIG/build-dir.py index eba3fea..b203abe 100644 --- a/test/SWIG/build-dir.py +++ b/test/SWIG/build-dir.py @@ -29,7 +29,6 @@ Make sure SWIG works when a VariantDir (or variant_dir) is used. Test case courtesy Joe Maruszewski. """ -import os.path import sys import TestSCons @@ -49,11 +48,8 @@ else: test.subdir(['source']) -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) if sys.platform == 'win32' and sys.maxsize <= 2**32: swig_arch_var="TARGET_ARCH='x86'," diff --git a/test/SWIG/generated_swigfile.py b/test/SWIG/generated_swigfile.py index d09b473..4686dae 100644 --- a/test/SWIG/generated_swigfile.py +++ b/test/SWIG/generated_swigfile.py @@ -29,7 +29,6 @@ Verify that SCons realizes the -noproxy option means no .py file will be created. """ -import os import sys import TestSCons @@ -54,11 +53,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) # handle testing on other platforms: ldmodule_prefix = '_' diff --git a/test/SWIG/live.py b/test/SWIG/live.py index 7c4bdce..f0cf1d5 100644 --- a/test/SWIG/live.py +++ b/test/SWIG/live.py @@ -28,7 +28,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Test SWIG behavior with a live, installed SWIG. """ -import os.path +import os import sys import TestSCons @@ -46,12 +46,9 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') - -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = python_include + '/Python.h' -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) + swig = swig.replace('\\','/') python = python.replace('\\','/') python_include = python_include.replace('\\','/') @@ -170,4 +167,4 @@ test.pass_test() # tab-width:4 # indent-tabs-mode:nil # End: -# vim: set expandtab tabstop=4 shiftwidth=4: \ No newline at end of file +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/SWIG/module-deduced-name.py b/test/SWIG/module-deduced-name.py index bdaef4f..6b4d6d9 100644 --- a/test/SWIG/module-deduced-name.py +++ b/test/SWIG/module-deduced-name.py @@ -31,7 +31,6 @@ emitter should return the basename of the module only. """ import TestSCons -import os import sys test = TestSCons.TestSCons() @@ -40,11 +39,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Cannot find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Cannot find %s, skipping test.\n' % Python_h) # On Windows, build a 32-bit exe if on 32-bit python. if sys.platform == 'win32' and sys.maxsize <= 2**32: diff --git a/test/SWIG/module-parens.py b/test/SWIG/module-parens.py index d8c1744..040ef28 100644 --- a/test/SWIG/module-parens.py +++ b/test/SWIG/module-parens.py @@ -29,7 +29,6 @@ Verify that we handle %module(directors="1") statements, both with and without white space before the opening parenthesis. """ -import os.path import sys import TestSCons @@ -39,11 +38,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) # swig-python expects specific filenames. # the platform specific suffix won't necessarily work. diff --git a/test/SWIG/module-quoted.py b/test/SWIG/module-quoted.py index 6f4b891..8bf9e86 100644 --- a/test/SWIG/module-quoted.py +++ b/test/SWIG/module-quoted.py @@ -29,7 +29,6 @@ Verify that we correctly parse quoted module names; e.g. %module "test" (SWIG permits double-quoted names but not single-quoted ones.) """ -import os.path import sys import TestSCons @@ -39,11 +38,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) # swig-python expects specific filenames. # the platform specific suffix won't necessarily work. diff --git a/test/SWIG/module-spaces.py b/test/SWIG/module-spaces.py index 2833dff..ce16497 100644 --- a/test/SWIG/module-spaces.py +++ b/test/SWIG/module-spaces.py @@ -29,7 +29,6 @@ Verify that we correctly parse module names with spaces on the line after the module name ; e.g. "%module test " """ -import os.path import sys import TestSCons @@ -39,11 +38,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) # swig-python expects specific filenames. # the platform specific suffix won't necessarily work. diff --git a/test/SWIG/noproxy.py b/test/SWIG/noproxy.py index 1aaeb08..b4abbc3 100644 --- a/test/SWIG/noproxy.py +++ b/test/SWIG/noproxy.py @@ -29,7 +29,6 @@ Verify that SCons realizes the -noproxy option means no .py file will be created. """ -import os import sys import TestSCons @@ -47,11 +46,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) # handle testing on other platforms: ldmodule_prefix = '_' diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 34c7d6e..4cb8fe5 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -44,11 +44,8 @@ for pre_req in ['swig', 'python']: if not test.where_is(pre_req): test.skip_test('Can not find installed "' + pre_req + '", skipping test.%s' % os.linesep) -# file generated by swig will #include , make sure we have one -python, py_include, py_libpath, py_lib = test.get_platform_python_info() -Python_h = os.path.join(py_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) +python, python_include, python_libpath, python_lib, python_h = \ + test.get_platform_python_info() if sys.platform == 'win32': python_lib = os.path.dirname(sys.executable) + "/libs/" + ('python%d%d'%(sys.version_info[0],sys.version_info[1])) + '.lib' diff --git a/test/SWIG/remove-modules.py b/test/SWIG/remove-modules.py index f5ce60d..ecbc19c 100644 --- a/test/SWIG/remove-modules.py +++ b/test/SWIG/remove-modules.py @@ -29,7 +29,6 @@ Verify that swig-generated modules are removed. The %module directive specifies the module name. """ -import os.path import sys import TestSCons @@ -47,11 +46,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) # handle testing on other platforms: ldmodule_prefix = '_' diff --git a/test/SWIG/subdir.py b/test/SWIG/subdir.py index e23b858..062f8dc 100644 --- a/test/SWIG/subdir.py +++ b/test/SWIG/subdir.py @@ -29,7 +29,6 @@ Verify that we expect the .py file created by the -python flag to be in the same subdirectory as the taget. """ -import os import sys import TestSCons @@ -49,11 +48,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib = \ +python, python_include, python_libpath, python_lib, python_h = \ test.get_platform_python_info() -Python_h = os.path.join(python_include, 'Python.h') -if not os.path.exists(Python_h): - test.skip_test('Can not find %s, skipping test.\n' % Python_h) # handle testing on other platforms: ldmodule_prefix = '_' diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 50942ab..14a8fcc 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -564,7 +564,7 @@ class TestSCons(TestCommon): Returns a Python error line for output comparisons. The exec of the traceback line gives us the correct format for - this version of Python. + this version of Python. File "", line 1, @@ -607,7 +607,7 @@ class TestSCons(TestCommon): pattern = to_bytes(pattern) repl = to_bytes(repl) return re.sub(pattern, repl, str, count, flags) - + def normalize_pdf(self, s): s = self.to_bytes_re_sub(r'/(Creation|Mod)Date \(D:[^)]*\)', r'/\1Date (D:XXXX)', s) @@ -811,7 +811,7 @@ class TestSCons(TestCommon): where_jar = self.where_is('jar', ENV['PATH']) if not where_jar: self.skip_test("Could not find Java jar, skipping test(s).\n") - elif sys.platform == "darwin": + elif sys.platform == "darwin": self.java_mac_check(where_jar, 'jar') return where_jar @@ -885,7 +885,7 @@ class TestSCons(TestCommon): self.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n") return where_rmic - + def java_get_class_files(self, dir): result = [] for dirpath, dirnames, filenames in os.walk(dir): @@ -1078,7 +1078,7 @@ SConscript( sconscript ) doCheckLog=True, doCheckStdout=True): """ Used to verify the expected output from using Configure() - via the contents of one or both of stdout or config.log file. + via the contents of one or both of stdout or config.log file. The checks, results, cached parameters all are zipped together for use in comparing results. @@ -1160,19 +1160,19 @@ SConscript( sconscript ) sconstruct = sconstruct log = r'file\ \S*%s\,line \d+:' % re.escape(sconstruct) + ls - if doCheckLog: + if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls - if doCheckLog: + if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) - + rdstr = "" cnt = 0 for check,result,cache_desc in zip(checks, results, cached): log = re.escape("scons: Configure: " + check) + ls - if doCheckLog: + if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) log = "" @@ -1217,7 +1217,7 @@ SConscript( sconscript ) rdstr = rdstr + re.escape(check) + re.escape(result) + "\n" log=log + re.escape("scons: Configure: " + result) + ls + ls - if doCheckLog: + if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd) log = "" @@ -1260,9 +1260,12 @@ SConscript( sconscript ) """ Returns a path to a Python executable suitable for testing on this platform and its associated include path, library path, - and library name. + library name, and the path to the Python.h header if found. + If the Python executable or Python header is not found, + the test is skipped. + Returns: - (path to python, include path, library path, library name) + (path to python, include path, library path, library name, header path) """ python = os.environ.get('python_executable', self.where_is('python')) if not python: @@ -1279,28 +1282,44 @@ except AttributeError: try: import distutils.sysconfig exec_prefix = distutils.sysconfig.EXEC_PREFIX - print(distutils.sysconfig.get_python_inc()) + include = distutils.sysconfig.get_python_inc() + print(include) lib_path = os.path.join(exec_prefix, 'libs') if not os.path.exists(lib_path): lib_path = os.path.join(exec_prefix, 'lib') print(lib_path) except: - print(os.path.join(sys.prefix, 'include', py_ver)) + include = os.path.join(sys.prefix, 'include', py_ver) + print(include) print(os.path.join(sys.prefix, 'lib', py_ver, 'config')) print(py_ver) - """) +Python_h = os.path.join(include, "Python.h") +if os.path.exists(Python_h): + print(Python_h) +else: + print("False") +""") else: self.run(program=python, stdin="""\ -import sys, sysconfig -print(sysconfig.get_config_var("INCLUDEPY")) +import sys, sysconfig, os.path +include = sysconfig.get_config_var("INCLUDEPY") +print(include) print(sysconfig.get_config_var("LIBDIR")) py_library_ver = sysconfig.get_config_var("LDVERSION") if not py_library_ver: py_library_ver = '%d.%d' % sys.version_info[:2] print("python"+py_library_ver) +Python_h = os.path.join(include, "Python.h") +if os.path.exists(Python_h): + print(Python_h) +else: + print("False") """) + detected = self.stdout().strip().split('\n') + if detected[3] == "False": + self.skip_test('Can not find required "Python.h", skipping test.\n') - return [python] + self.stdout().strip().split('\n') + return [python] + detected def start(self, *args, **kw): """ -- cgit v0.12 From e83bf211d3419e157af0e445240b13f07b3f6496 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 15 Aug 2018 13:28:06 -0600 Subject: Remove unintended "import io" During develoment of PR#3177, an interation checked objects for being a subclass of io.IOBase, which necessitated importing the io module. Later the check was changed to see if the object simply had a close method, in which case that is called. The import was not removed; cleaning that up. Signed-off-by: Mats Wichmann --- src/engine/SCons/Action.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 32ceaa9..44ddacd 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -819,7 +819,6 @@ def _subproc(scons_env, cmd, error = 'ignore', **kw): pobj = dummyPopen(e) finally: # clean up open file handles stored in parent's kw - import io for k, v in kw.items(): if hasattr(v, 'close'): v.close() -- cgit v0.12 From 8b47503f49fa423404cad0d95f070c1674dd8c5a Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 17 Aug 2018 09:04:31 -0600 Subject: get_platform_python_info fail on missing Python.h evolved the get_platform_python_info test method to fail only if a new flag python_h_required is True. Rolled back the previous change to have the method return the path to the Python.h, this was not really needed since the include path is already returned. Signed-off-by: Mats Wichmann --- test/SWIG/SWIGOUTDIR-python.py | 4 ++-- test/SWIG/build-dir.py | 4 ++-- test/SWIG/generated_swigfile.py | 4 ++-- test/SWIG/live.py | 4 ++-- test/SWIG/module-deduced-name.py | 4 ++-- test/SWIG/module-parens.py | 4 ++-- test/SWIG/module-quoted.py | 4 ++-- test/SWIG/module-spaces.py | 4 ++-- test/SWIG/noproxy.py | 4 ++-- test/SWIG/recursive-includes-cpp.py | 4 ++-- test/SWIG/remove-modules.py | 4 ++-- test/SWIG/subdir.py | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/SWIG/SWIGOUTDIR-python.py b/test/SWIG/SWIGOUTDIR-python.py index 53b5bdf..8d6703f 100644 --- a/test/SWIG/SWIGOUTDIR-python.py +++ b/test/SWIG/SWIGOUTDIR-python.py @@ -38,8 +38,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # On Windows, build a 32-bit exe if on 32-bit python. if sys.platform == 'win32' and sys.maxsize <= 2**32: diff --git a/test/SWIG/build-dir.py b/test/SWIG/build-dir.py index b203abe..d268c87 100644 --- a/test/SWIG/build-dir.py +++ b/test/SWIG/build-dir.py @@ -48,8 +48,8 @@ else: test.subdir(['source']) -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) if sys.platform == 'win32' and sys.maxsize <= 2**32: swig_arch_var="TARGET_ARCH='x86'," diff --git a/test/SWIG/generated_swigfile.py b/test/SWIG/generated_swigfile.py index 4686dae..145349b 100644 --- a/test/SWIG/generated_swigfile.py +++ b/test/SWIG/generated_swigfile.py @@ -53,8 +53,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # handle testing on other platforms: ldmodule_prefix = '_' diff --git a/test/SWIG/live.py b/test/SWIG/live.py index f0cf1d5..a64defe 100644 --- a/test/SWIG/live.py +++ b/test/SWIG/live.py @@ -46,8 +46,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) swig = swig.replace('\\','/') python = python.replace('\\','/') diff --git a/test/SWIG/module-deduced-name.py b/test/SWIG/module-deduced-name.py index 6b4d6d9..14d23a2 100644 --- a/test/SWIG/module-deduced-name.py +++ b/test/SWIG/module-deduced-name.py @@ -39,8 +39,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Cannot find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # On Windows, build a 32-bit exe if on 32-bit python. if sys.platform == 'win32' and sys.maxsize <= 2**32: diff --git a/test/SWIG/module-parens.py b/test/SWIG/module-parens.py index 040ef28..4ce7511 100644 --- a/test/SWIG/module-parens.py +++ b/test/SWIG/module-parens.py @@ -38,8 +38,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # swig-python expects specific filenames. # the platform specific suffix won't necessarily work. diff --git a/test/SWIG/module-quoted.py b/test/SWIG/module-quoted.py index 8bf9e86..60bc0b0 100644 --- a/test/SWIG/module-quoted.py +++ b/test/SWIG/module-quoted.py @@ -38,8 +38,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # swig-python expects specific filenames. # the platform specific suffix won't necessarily work. diff --git a/test/SWIG/module-spaces.py b/test/SWIG/module-spaces.py index ce16497..6a3f270 100644 --- a/test/SWIG/module-spaces.py +++ b/test/SWIG/module-spaces.py @@ -38,8 +38,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # swig-python expects specific filenames. # the platform specific suffix won't necessarily work. diff --git a/test/SWIG/noproxy.py b/test/SWIG/noproxy.py index b4abbc3..f94f553 100644 --- a/test/SWIG/noproxy.py +++ b/test/SWIG/noproxy.py @@ -46,8 +46,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # handle testing on other platforms: ldmodule_prefix = '_' diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 4cb8fe5..3999cc3 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -44,8 +44,8 @@ for pre_req in ['swig', 'python']: if not test.where_is(pre_req): test.skip_test('Can not find installed "' + pre_req + '", skipping test.%s' % os.linesep) -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) if sys.platform == 'win32': python_lib = os.path.dirname(sys.executable) + "/libs/" + ('python%d%d'%(sys.version_info[0],sys.version_info[1])) + '.lib' diff --git a/test/SWIG/remove-modules.py b/test/SWIG/remove-modules.py index ecbc19c..a4d7b16 100644 --- a/test/SWIG/remove-modules.py +++ b/test/SWIG/remove-modules.py @@ -46,8 +46,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # handle testing on other platforms: ldmodule_prefix = '_' diff --git a/test/SWIG/subdir.py b/test/SWIG/subdir.py index 062f8dc..6951753 100644 --- a/test/SWIG/subdir.py +++ b/test/SWIG/subdir.py @@ -48,8 +48,8 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') -python, python_include, python_libpath, python_lib, python_h = \ - test.get_platform_python_info() +python, python_include, python_libpath, python_lib = \ + test.get_platform_python_info(python_h_required=True) # handle testing on other platforms: ldmodule_prefix = '_' -- cgit v0.12 From bd27fe421c384dffebdcd3a1732a925480e2db1f Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 17 Aug 2018 09:14:03 -0600 Subject: add missed TestCmd.py changes Signed-off-by: Mats Wichmann --- testing/framework/TestSCons.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 14a8fcc..bf6aabb 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -1256,21 +1256,25 @@ SConscript( sconscript ) # see also sys.prefix documentation return python_minor_version_string() - def get_platform_python_info(self): + def get_platform_python_info(self, python_h_required=False): """ Returns a path to a Python executable suitable for testing on - this platform and its associated include path, library path, - library name, and the path to the Python.h header if found. - If the Python executable or Python header is not found, - the test is skipped. + this platform and its associated include path, library path and + library name. - Returns: - (path to python, include path, library path, library name, header path) + If the Python executable or Python header (if required) + is not found, the test is skipped. + + Returns a tuple: + (path to python, include path, library path, library name) """ python = os.environ.get('python_executable', self.where_is('python')) if not python: self.skip_test('Can not find installed "python", skipping test.\n') + # construct a program to run in the intended environment + # in order to fetch the characteristics of that Python. + # Windows Python doesn't store all the info in config vars. if sys.platform == 'win32': self.run(program=python, stdin="""\ import sysconfig, sys, os.path @@ -1315,11 +1319,11 @@ if os.path.exists(Python_h): else: print("False") """) - detected = self.stdout().strip().split('\n') - if detected[3] == "False": + incpath, libpath, libname, python_h = self.stdout().strip().split('\n') + if python_h == "False" and python_h_required: self.skip_test('Can not find required "Python.h", skipping test.\n') - return [python] + detected + return (python, incpath, libpath, libname) def start(self, *args, **kw): """ -- cgit v0.12 From 9968415b4bbb46ab95023077844590b962e1ffcf Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 19 Aug 2018 18:01:34 -0700 Subject: Fix test/AS/nasm.py as it previously would only run with nasm version 0.98 --- test/AS/nasm.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/AS/nasm.py b/test/AS/nasm.py index 1733789..4d57951 100644 --- a/test/AS/nasm.py +++ b/test/AS/nasm.py @@ -47,17 +47,17 @@ if sys.platform.find('linux') == -1: test.skip_test("skipping test on non-Linux platform '%s'\n" % sys.platform) try: - import popen2 - stdout = popen2.popen2('nasm -v')[0] + import subprocess + stdout = subprocess.check_output(['nasm','-v']) except OSError: test.skip_test('could not determine nasm version; skipping test\n') else: - version = stdout.read().split()[2] - if version[:4] != '0.98': + version = stdout.split()[2].split('.') + if int(version[0]) ==0 and int(version[1]) < 98: test.skip_test("skipping test of nasm version %s\n" % version) machine = os.uname()[4] - if not machine in ('i386', 'i486', 'i586', 'i686'): + if not machine in ('i386', 'i486', 'i586', 'i686', 'x86_64'): fmt = "skipping test of nasm %s on non-x86 machine '%s'\n" test.skip_test(fmt % (version, machine)) @@ -78,6 +78,8 @@ test.file_fixture('wrapper.py') test.write('SConstruct', """ eee = Environment(tools = ['gcc', 'gnulink', 'nasm'], + CFLAGS = ['-m32'], + LINKFLAGS = '-m32', ASFLAGS = '-f %(nasm_format)s') fff = eee.Clone(AS = r'%(_python_)s wrapper.py ' + WhereIs('nasm')) eee.Program(target = 'eee', source = ['eee.asm', 'eee_main.c']) @@ -99,6 +101,7 @@ name: """) test.write('eee_main.c', r""" +#include extern char name[]; int -- cgit v0.12 From 6719b7292b7bd42d9e8e62391134a2e3d10001bf Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 20 Aug 2018 07:43:39 -0700 Subject: Fix bytes->string decoding issue as subprocess returned stdout is bytes in py3 --- test/AS/nasm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/AS/nasm.py b/test/AS/nasm.py index 4d57951..4d93c7f 100644 --- a/test/AS/nasm.py +++ b/test/AS/nasm.py @@ -52,6 +52,7 @@ try: except OSError: test.skip_test('could not determine nasm version; skipping test\n') else: + stdout = stdout.decode() version = stdout.split()[2].split('.') if int(version[0]) ==0 and int(version[1]) < 98: test.skip_test("skipping test of nasm version %s\n" % version) -- cgit v0.12 From c25ff60873b0e2ec170afc988090605f9f1e6620 Mon Sep 17 00:00:00 2001 From: cclauss Date: Wed, 22 Aug 2018 12:04:39 +0200 Subject: Travis CI: Upgrade to Python 3.7 production release Upgrade Python 3.7 from pre-release to production release in alignment with travis-ci/travis-ci#9069 @bdbaddog --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 58ca595..8894823 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,9 +36,10 @@ jobs: sudo: required - <<: *test_job - python: 3.7-dev + python: 3.7 env: PYVER=37 sudo: required + dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) - <<: *test_job python: pypy -- cgit v0.12 From 0357e3670d475fbd0a1b56b6fb05f78e92ad8485 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 29 Aug 2018 10:52:54 -0700 Subject: Add ghostscript to travis package install to get dvipdf --- .travis/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/install.sh b/.travis/install.sh index ddef1e6..50331e5 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -10,7 +10,7 @@ sudo apt-get -y install gdc # dependencies for docbook tests sudo apt-get -y install docbook-xml xsltproc libxml2-dev libxslt-dev fop docbook-xsl-doc-pdf # dependencies for latex tests -sudo apt-get -y install texlive texlive-latex3 biber texmaker +sudo apt-get -y install texlive texlive-latex3 biber texmaker ghostscript # need some things for building dependencies for other tests sudo apt-get -y install python-pip python-dev build-essential libpcre3-dev autoconf automake libtool bison subversion git # dependencies for docbook tests continued -- cgit v0.12 From 68c0272e4b60b1eb9cbee62ae1fddbcec4867e69 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 29 Aug 2018 08:22:09 -0600 Subject: win32 handle exception object correctly Code inspection reveals this problem (4 instances): Class 'OSError' does not define '__getitem__' so the [] operator cannot be used on its instances. Use e.errno for e[0] and e.strerror for e[1]. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + src/engine/SCons/Platform/win32.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index ab15214..7feec12 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -123,6 +123,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - fix must_contain tests for py3 - one swig test now checks for Python.h instead of failing - if test opens os.devnull, register with atexit so file opens do not leak. + - win32.py handle exception object appropriately for class From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index 5c877b5..351684a 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -202,9 +202,9 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: ret = exitvalmap[e[0]] except KeyError: - sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1])) + sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e.errno, cmd, e.strerror)) if stderr is not None: - stderr.write("scons: %s: %s\n" % (cmd, e[1])) + stderr.write("scons: %s: %s\n" % (cmd, e.strerror)) # copy child output from tempfiles to our streams # and do clean up stuff if stdout is not None and stdoutRedirected == 0: -- cgit v0.12 From c487700ccd4cae873170b812c2f201f1400d8dea Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 30 Aug 2018 09:31:21 -0600 Subject: Do not use time.clock for Py3 in runtest time.clock is deprecated since Python 3.3 and will be removed in Python 3.8. 3.7 started issuing DeprecationWarning, which fails about 16 tests in AppVeyor CI (and running manually), since the warning appears in stderr stream and so real vs expected does not match any longer. Arguably the tests could be fixed to do a different check (contains rather than exactly-equal), but a change needs to be made anyway. use time.perf_counter as the time function for Python 3. This works for Windows and non-Windows; however since this function did not exist in 2.7 (added for 3.3), the Py2 case is left, which selects between time.clock and time.time depending on OS. Note this addresses runtest.py, but not code in bench/, which need attention also. Suggest that be a separate change since it's not failing tests. Signed-off-by: Mats Wichmann --- runtest.py | 12 ++++++++---- src/CHANGES.txt | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/runtest.py b/runtest.py index 293e4ca..1cac45b 100755 --- a/runtest.py +++ b/runtest.py @@ -95,11 +95,12 @@ import time import threading try: # python3 from queue import Queue + PY3=True except ImportError as e: # python2 from Queue import Queue + PY3=False import subprocess - cwd = os.getcwd() baseline = 0 @@ -770,10 +771,13 @@ os.environ["python_executable"] = python # but time.time() does a better job on Linux systems, so let that be # the non-Windows default. -if sys.platform == 'win32': - time_func = time.clock +if PY3: + time_func = time.perf_counter else: - time_func = time.time + if sys.platform == 'win32': + time_func = time.clock + else: + time_func = time.time if print_times: print_time_func = lambda fmt, time: sys.stdout.write(fmt % time) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index ab15214..27fe66a 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -123,6 +123,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - fix must_contain tests for py3 - one swig test now checks for Python.h instead of failing - if test opens os.devnull, register with atexit so file opens do not leak. + - for py3, use time.perf_counter instead of depr time.clock, which is + used in win32 case for py2. py37 depr warnings were failing a bunch + of tests on windows since warn messes up expected stderr. From Hao Wu - typo in customized decider example in user guide -- cgit v0.12 From b215cbc5022148bff6fe6986780794fa80e75b2b Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 30 Aug 2018 10:48:27 -0600 Subject: Rework the time.clock -> time.perf_counter change Signed-off-by: Mats Wichmann --- runtest.py | 5 ++--- src/CHANGES.txt | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/runtest.py b/runtest.py index 1cac45b..697b872 100755 --- a/runtest.py +++ b/runtest.py @@ -95,10 +95,8 @@ import time import threading try: # python3 from queue import Queue - PY3=True except ImportError as e: # python2 from Queue import Queue - PY3=False import subprocess cwd = os.getcwd() @@ -771,7 +769,8 @@ os.environ["python_executable"] = python # but time.time() does a better job on Linux systems, so let that be # the non-Windows default. -if PY3: +#TODO: clean up when py2 support is dropped +try: time_func = time.perf_counter else: if sys.platform == 'win32': diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 27fe66a..d81a9f9 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -123,9 +123,10 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - fix must_contain tests for py3 - one swig test now checks for Python.h instead of failing - if test opens os.devnull, register with atexit so file opens do not leak. - - for py3, use time.perf_counter instead of depr time.clock, which is - used in win32 case for py2. py37 depr warnings were failing a bunch - of tests on windows since warn messes up expected stderr. + - use time.perf_counter instead of time.clock if it exists. + time.clock deprecated since py3.3, due to remove in 3.8. deprecation + warnings from py3.7 were failing a bunch of tests on Windows since they + mess up expected stderr. From Hao Wu - typo in customized decider example in user guide -- cgit v0.12 From 4913b28bfc6738f49241113202ce1be02bbe9368 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 30 Aug 2018 11:34:19 -0600 Subject: Part of previous change restored somehow the change from if test to try block got partly lost during commit. changing else->except --- runtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtest.py b/runtest.py index 697b872..45ffffc 100755 --- a/runtest.py +++ b/runtest.py @@ -772,7 +772,7 @@ os.environ["python_executable"] = python #TODO: clean up when py2 support is dropped try: time_func = time.perf_counter -else: +except AttributeError: if sys.platform == 'win32': time_func = time.clock else: -- cgit v0.12 From 40982e03a34d869900d6ae2201c19cbdcf9a6d6f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 30 Aug 2018 18:38:25 -0700 Subject: see if defining _JAVA_OPTIONS= to nothing will quiet STDERR from java which is breaking tests for py3.7 --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8894823..3ee530e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,7 +37,9 @@ jobs: - <<: *test_job python: 3.7 - env: PYVER=37 + env: + - PYVER=37 + - _JAVA_OPTIONS= sudo: required dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) -- cgit v0.12 From 3de25a92e801acec7afcdb9b563a132ca327327c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 30 Aug 2018 20:09:25 -0700 Subject: Attempt to fix py37 java falures. Which are likely caused by newer oracle java echoing values in _JAVA_OPTIONS env variable to stderr. --- .travis/install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis/install.sh b/.travis/install.sh index 50331e5..7cdfaa7 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -33,3 +33,7 @@ if [[ "$PYVER" == 27 ]]; then tar xzf rel-3.0.12.tar.gz cd swig-rel-3.0.12 && ./autogen.sh && ./configure --prefix=/usr && make && sudo make install && cd .. fi + +# Clear this flag as newer Oracle Java's will echo to stderr any values set in it which breaks several java +# tests +unset _JAVA_OPTIONS \ No newline at end of file -- cgit v0.12 From 26be7837c6bef1afe1e5b574eaa7f5166629005f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 30 Aug 2018 20:33:42 -0700 Subject: More deletion of _JAVA_OPTIONS to runtest.py from travis startup files. --- .travis.yml | 1 - .travis/install.sh | 6 +----- runtest.py | 4 ++++ 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3ee530e..4c4a1f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,6 @@ jobs: python: 3.7 env: - PYVER=37 - - _JAVA_OPTIONS= sudo: required dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) diff --git a/.travis/install.sh b/.travis/install.sh index 7cdfaa7..6f2cfe2 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -32,8 +32,4 @@ if [[ "$PYVER" == 27 ]]; then wget https://github.com/swig/swig/archive/rel-3.0.12.tar.gz tar xzf rel-3.0.12.tar.gz cd swig-rel-3.0.12 && ./autogen.sh && ./configure --prefix=/usr && make && sudo make install && cd .. -fi - -# Clear this flag as newer Oracle Java's will echo to stderr any values set in it which breaks several java -# tests -unset _JAVA_OPTIONS \ No newline at end of file +fi \ No newline at end of file diff --git a/runtest.py b/runtest.py index 45ffffc..9ffe374 100755 --- a/runtest.py +++ b/runtest.py @@ -618,6 +618,10 @@ os.environ['SCONS_VERSION'] = version old_pythonpath = os.environ.get('PYTHONPATH') +# Clear _JAVA_OPTIONS which java tools output to stderr when run breaking tests +if '_JAVA_OPTIONS' in os.environ: + del os.environ['_JAVA_OPTIONS'] + # FIXME: the following is necessary to pull in half of the testing # harness from $srcdir/etc. Those modules should be transfered # to testing/, in which case this manipulation of PYTHONPATH -- cgit v0.12 From f9aa719a202271b6786510a8025e8065cb8835bb Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 31 Aug 2018 08:15:08 -0600 Subject: Convert one more e[0] to e.errno for OSError usage Signed-off-by: Mats Wichmann --- src/engine/SCons/Platform/win32.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index 351684a..ea2fd3f 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -200,7 +200,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): except OSError as e: # catch any error try: - ret = exitvalmap[e[0]] + ret = exitvalmap[e.errno] except KeyError: sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e.errno, cmd, e.strerror)) if stderr is not None: -- cgit v0.12 From 701d5330aa9f5bca4601353a05ac86ca88e745a6 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 31 Aug 2018 09:04:30 -0700 Subject: Have travis-ci post #scons when builds complete. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4c4a1f9..ea63e57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ dist: trusty language: python +notifications: + irc: "chat.freenode.net#scons" + addons: apt: update: true -- cgit v0.12 From 2180ff6d0388162586fff59e066bc1e3e4bb9600 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 26 Aug 2018 22:54:00 -0600 Subject: Stop using custom OrderedDict OrdredDict is in the standard library for all supported Python versions (2.7 and 3.5+) and has improvements over the ActiveState recipe version of OrderedDict we have been using. Switch to importing from collections instead of getting it from SCons.Util (tests already did this). At the same time, reorganize the Util.py imports - import Iterable from collections.abc if possible (it is deprecated to import it from collections, will stop working in 3.8); try getting the User{Dict,List,String} from collections if possible - that is, try the 3.x way first. Signed-off-by: Mats Wichmann --- src/engine/SCons/Action.py | 3 +- src/engine/SCons/Tool/javac.py | 3 +- src/engine/SCons/Util.py | 74 +++++------------------------------------- 3 files changed, 13 insertions(+), 67 deletions(-) diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 44ddacd..9a3888b 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -107,6 +107,7 @@ import sys import subprocess import itertools import inspect +from collections import OrderedDict import SCons.Debug from SCons.Debug import logInstanceCreation @@ -1294,7 +1295,7 @@ class ListAction(ActionBase): return result def get_varlist(self, target, source, env, executor=None): - result = SCons.Util.OrderedDict() + result = OrderedDict() for act in self.list: for var in act.get_varlist(target, source, env, executor): result[var] = True diff --git a/src/engine/SCons/Tool/javac.py b/src/engine/SCons/Tool/javac.py index b26c0b3..72c48f7 100644 --- a/src/engine/SCons/Tool/javac.py +++ b/src/engine/SCons/Tool/javac.py @@ -34,6 +34,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path +from collections import OrderedDict import SCons.Action import SCons.Builder @@ -70,7 +71,7 @@ def emit_java_classes(target, source, env): if isinstance(entry, SCons.Node.FS.File): slist.append(entry) elif isinstance(entry, SCons.Node.FS.Dir): - result = SCons.Util.OrderedDict() + result = OrderedDict() dirnode = entry.rdir() def find_java_files(arg, dirpath, filenames): java_files = sorted([n for n in filenames diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 52f42d2..f691cd9 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -37,21 +37,18 @@ import pprint PY3 = sys.version_info[0] == 3 try: + from collections import UserDict, UserList, UserString +except ImportError: from UserDict import UserDict -except ImportError as e: - from collections import UserDict - -try: from UserList import UserList -except ImportError as e: - from collections import UserList - -from collections import Iterable + from UserString import UserString try: - from UserString import UserString -except ImportError as e: - from collections import UserString + from collections.abc import Iterable +except ImportError: + from collections import Iterable + +from collections import OrderedDict # Don't "from types import ..." these because we need to get at the # types module later to look for UnicodeType. @@ -63,7 +60,7 @@ MethodType = types.MethodType FunctionType = types.FunctionType try: - unicode + _ = type(unicode) except NameError: UnicodeType = str else: @@ -1032,59 +1029,6 @@ class CLVar(UserList): def __str__(self): return ' '.join(self.data) -# A dictionary that preserves the order in which items are added. -# Submitted by David Benjamin to ActiveState's Python Cookbook web site: -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 -# Including fixes/enhancements from the follow-on discussions. -class OrderedDict(UserDict): - def __init__(self, dict = None): - self._keys = [] - UserDict.__init__(self, dict) - - def __delitem__(self, key): - UserDict.__delitem__(self, key) - self._keys.remove(key) - - def __setitem__(self, key, item): - UserDict.__setitem__(self, key, item) - if key not in self._keys: self._keys.append(key) - - def clear(self): - UserDict.clear(self) - self._keys = [] - - def copy(self): - dict = OrderedDict() - dict.update(self) - return dict - - def items(self): - return list(zip(self._keys, list(self.values()))) - - def keys(self): - return self._keys[:] - - def popitem(self): - try: - key = self._keys[-1] - except IndexError: - raise KeyError('dictionary is empty') - - val = self[key] - del self[key] - - return (key, val) - - def setdefault(self, key, failobj = None): - UserDict.setdefault(self, key, failobj) - if key not in self._keys: self._keys.append(key) - - def update(self, dict): - for (key, val) in dict.items(): - self.__setitem__(key, val) - - def values(self): - return list(map(self.get, self._keys)) class Selector(OrderedDict): """A callable ordered dictionary that maps file suffixes to -- cgit v0.12 From 2915abd83891c89e0302cf3f35e367de55c0abab Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 4 Sep 2018 07:50:08 -0600 Subject: generalize the fix for #3164 Add a new flag, X_RPM_EXTRADEFS, which lets the user supply any additional flag lines needed in the specfile. If the tag is not present in the specfile, supply a value which turns off debug package generation. To retain debug package generation, supply the flag with a value of None, or with a list that does not include the disable value. So like the previous change the default is now not to generate debug, but that default can be overridden, and it is documented. --- src/CHANGES.txt | 9 +++++--- src/engine/SCons/Tool/packaging/__init__.xml | 32 ++++++++++++++++++++++++++++ src/engine/SCons/Tool/packaging/rpm.py | 20 +++++++++++------ test/packaging/rpm/cleanup.py | 10 +++------ test/packaging/rpm/explicit-target.py | 10 +++------ test/packaging/rpm/internationalization.py | 17 +++------------ test/packaging/rpm/multipackage.py | 9 ++------ test/packaging/rpm/package.py | 9 ++------ test/packaging/rpm/src/main.c | 5 +++++ test/packaging/rpm/tagging.py | 10 ++------- 10 files changed, 72 insertions(+), 59 deletions(-) create mode 100644 test/packaging/rpm/src/main.c diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 8be3bbe..8c5b90a 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -121,9 +121,12 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - fix tests specifying octal constants for py3 - fix must_contain tests for py3 - rpm package generation: fix supplying a build architecture; disable - auto debug package generation on certain rpmbuild versions; adjust some tests - to be resistant to automatic supplying of build-id file on certain rpmbuild - versions; a little doc and comment cleanup. + auto debug package generation on certain rpmbuild versions; adjust some + tests to be resistant to automatic supplying of build-id file on + certain rpmbuild versions; tests now use a file fixture for the + repeated (trivial) main.c program. A little doc and comment cleanup. + A new tag is added, X_RPM_EXTRADEFS, to allow supplying custom settings + to the specfile without adding specific logic for each one to scons. From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/Tool/packaging/__init__.xml b/src/engine/SCons/Tool/packaging/__init__.xml index 68e4fed..348b2be 100644 --- a/src/engine/SCons/Tool/packaging/__init__.xml +++ b/src/engine/SCons/Tool/packaging/__init__.xml @@ -471,6 +471,38 @@ field in the RPM + + + +A list used to supply extra defintions or flags +to be added to the RPM .spec file. +Each item is added as-is with a carriage return appended. +This is useful if some specific RPM feature not otherwise +anticipated by SCons needs to be turned on or off. +Note if this variable is omitted, SCons will by +default supply the value +'%global debug_package %{nil}' +to disable debug package generation. +To enable debug package generation, include this +variable set either to None, or to a custom +list that does not include the default line. +Added in version 3.1. + + + +env.Package( + NAME = 'foo', +... + X_RPM_EXTRADEFS = [ + '%define _unpackaged_files_terminate_build 0' + '%define _missing_doc_files_terminate_build 0' + ], +... ) + + + + + diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py index e93c4f0..0a4db6c 100644 --- a/src/engine/SCons/Tool/packaging/rpm.py +++ b/src/engine/SCons/Tool/packaging/rpm.py @@ -198,7 +198,8 @@ def build_specfile_header(spec): 'PACKAGEVERSION' : '%%define release %s\nRelease: %%{release}\n', 'X_RPM_GROUP' : 'Group: %s\n', 'SUMMARY' : 'Summary: %s\n', - 'LICENSE' : 'License: %s\n', } + 'LICENSE' : 'License: %s\n', + } str = str + SimpleTagCompiler(mandatory_header_fields).compile( spec ) @@ -227,7 +228,8 @@ def build_specfile_header(spec): 'X_RPM_PREFIX' : 'Prefix: %s\n', # internal use - 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', } + 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', + } # fill in default values: # Adding a BuildRequires renders the .rpm unbuildable under systems which @@ -241,11 +243,17 @@ def build_specfile_header(spec): str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec ) + # Add any extra specfile definitions the user may have supplied. + # These flags get no processing, they are just added. # github #3164: if we don't turn off debug package generation - # the tests which build packages all fail. This just slams - # a flag into the specfile header, could make it an X_RPM_NODEBUGPKGS - # which is controllable by an environment setting. - str += '%global debug_package %{nil}\n' + # the tests which build packages all fail. If there are no + # extra flags, default to adding this one. If the user wants + # to turn this back on, supply the flag set to None. + + if 'X_RPM_EXTRADEFS' not in spec: + spec['X_RPM_EXTRADEFS'] = ['%global debug_package %{nil}'] + for extra in spec['X_RPM_EXTRADEFS']: + str += extra + '\n' return str diff --git a/test/packaging/rpm/cleanup.py b/test/packaging/rpm/cleanup.py index b77dfd1..7483750 100644 --- a/test/packaging/rpm/cleanup.py +++ b/test/packaging/rpm/cleanup.py @@ -28,6 +28,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Assert that files created by the RPM packager will be removed by 'scons -c'. """ +import os import TestSCons import SCons.Tool.rpmutils @@ -47,13 +48,8 @@ if not rpm: rpm_build_root = test.workpath('rpm_build_root') test.subdir('src') - -test.write( [ 'src', 'main.c' ], r""" -int main( int argc, char* argv[] ) -{ - return 0; -} -""") +mainpath = os.path.join('src', 'main.c') +test.file_fixture(mainpath, mainpath) test.write('SConstruct', """ env=Environment(tools=['default', 'packaging']) diff --git a/test/packaging/rpm/explicit-target.py b/test/packaging/rpm/explicit-target.py index c383b57..e8fbd39 100644 --- a/test/packaging/rpm/explicit-target.py +++ b/test/packaging/rpm/explicit-target.py @@ -28,6 +28,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Test the ability to create a rpm package from a explicit target name. """ +import os import TestSCons _python_ = TestSCons._python_ @@ -44,13 +45,8 @@ if not rpm: rpm_build_root = test.workpath('rpm_build_root') test.subdir('src') - -test.write( [ 'src', 'main.c' ], r""" -int main( int argc, char* argv[] ) -{ - return 0; -} -""") +mainpath = os.path.join('src', 'main.c') +test.file_fixture(mainpath, mainpath) test.write('SConstruct', """ import os diff --git a/test/packaging/rpm/internationalization.py b/test/packaging/rpm/internationalization.py index 2ef0dfd..ad77f40 100644 --- a/test/packaging/rpm/internationalization.py +++ b/test/packaging/rpm/internationalization.py @@ -33,7 +33,6 @@ These are x-rpm-Group, description, summary and the lang_xx file tag. import os import SCons.Tool.rpmutils - import TestSCons _python_ = TestSCons._python_ @@ -52,12 +51,7 @@ rpm_build_root = test.workpath('rpm_build_root') # # test INTERNATIONAL PACKAGE META-DATA # -test.write( [ 'main.c' ], r""" -int main( int argc, char* argv[] ) -{ - return 0; -} -""") +test.file_fixture('src/main.c', 'main.c') test.write('SConstruct', """ # -*- coding: utf-8 -*- @@ -123,13 +117,8 @@ test.fail_test( out != 'Application/office-hello-this should be really long' ) # # test INTERNATIONAL PACKAGE TAGS # - -test.write( [ 'main.c' ], r""" -int main( int argc, char* argv[] ) -{ - return 0; -} -""") +mainpath = os.path.join('src', 'main.c') +test.file_fixture(mainpath) test.write( ['man.de'], '' ) test.write( ['man.en'], '' ) diff --git a/test/packaging/rpm/multipackage.py b/test/packaging/rpm/multipackage.py index f756cf4..fd67a09 100644 --- a/test/packaging/rpm/multipackage.py +++ b/test/packaging/rpm/multipackage.py @@ -47,13 +47,8 @@ if not rpm: rpm_build_root = test.workpath('rpm_build_root') test.subdir('src') - -test.write( [ 'src', 'main.c' ], r""" -int main( int argc, char* argv[] ) -{ - return 0; -} -""") +mainpath = os.path.join('src', 'main.c') +test.file_fixture(mainpath, mainpath) test.write('SConstruct', """ import os diff --git a/test/packaging/rpm/package.py b/test/packaging/rpm/package.py index 6723a9f..2ba66b9 100644 --- a/test/packaging/rpm/package.py +++ b/test/packaging/rpm/package.py @@ -46,13 +46,8 @@ if not rpm: rpm_build_root = test.workpath('rpm_build_root') test.subdir('src') - -test.write( [ 'src', 'main.c' ], r""" -int main( int argc, char* argv[] ) -{ - return 0; -} -""") +mainpath = os.path.join('src', 'main.c') +test.file_fixture(mainpath, mainpath) test.write('SConstruct', """ import os diff --git a/test/packaging/rpm/src/main.c b/test/packaging/rpm/src/main.c new file mode 100644 index 0000000..49e8969 --- /dev/null +++ b/test/packaging/rpm/src/main.c @@ -0,0 +1,5 @@ + +int main( int argc, char* argv[] ) +{ + return 0; +} diff --git a/test/packaging/rpm/tagging.py b/test/packaging/rpm/tagging.py index 15f82a7..b685c91 100644 --- a/test/packaging/rpm/tagging.py +++ b/test/packaging/rpm/tagging.py @@ -30,7 +30,6 @@ Test the ability to add file tags import os import SCons.Tool.rpmutils - import TestSCons _python_ = TestSCons._python_ @@ -50,13 +49,8 @@ rpm_build_root = test.workpath('rpm_build_root') # Test adding an attr tag to the built program. # test.subdir('src') - -test.write( [ 'src', 'main.c' ], r""" -int main( int argc, char* argv[] ) -{ -return 0; -} -""") +mainpath = os.path.join('src', 'main.c') +test.file_fixture(mainpath, mainpath) test.write('SConstruct', """ import os -- cgit v0.12 From c53e548e750f31917e36ed35af8519ebfc85344e Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 6 Sep 2018 11:34:38 -0400 Subject: Improve issue template --- .github/issue_template.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index 9f632b0..1d35486 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -2,10 +2,11 @@ # See: http://scons.org/bugs.html # If the issue is confirmed to be a bug please include the following information +* Link to SCons Users thread discussing your issue. * Version of SCons * Version of Python * Which python distribution if applicable (python.org, cygwin, anaconda, macports, brew,etc) * How you installed SCons * What Platform are you on? (Linux/Windows and which version) * How to reproduce your issue? Please include a small self contained reproducer. Likely a SConstruct should do for most issues. -* Link to SCons Users thread discussing your issue. +* How you invoke scons (The command line you're using "scons --flags some_arguments") \ No newline at end of file -- cgit v0.12 From 4edd05726a8fad7f192e486a9539c44ea012cd94 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 6 Sep 2018 12:17:56 -0600 Subject: Update CHANGES.txt for OrderedDict change Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 95680dd..6f873b0 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -128,6 +128,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE time.clock deprecated since py3.3, due to remove in 3.8. deprecation warnings from py3.7 were failing a bunch of tests on Windows since they mess up expected stderr. + - Remove obsoleted internal implementaiton of OrderedDict. From Hao Wu - typo in customized decider example in user guide -- cgit v0.12 From 8772bd06c5443632436ebe9588703ede09d859bd Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 4 Sep 2018 10:49:04 -0600 Subject: Modifications for deprected getargspec Py3 deprecated inspect.getargspec, prefer getfullargspec which is a drop-in replacement. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 5 +++++ src/engine/SCons/Tool/packaging/__init__.py | 16 +++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6f7e851..ba575dc 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -136,6 +136,11 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE time.clock deprecated since py3.3, due to remove in 3.8. deprecation warnings from py3.7 were failing a bunch of tests on Windows since they mess up expected stderr. + - Prefer Py3's inspect.getfullargspec over deprecated inspect.getargspec. + Switched to "new" (standard in Py2.7) usage of receiving a namedtuple - + we were unpacking to a four-tuple, two of the items of which were unused; + getfullargspec returns a named tuple with seven elements so it is a + cleaner drop-in replacement using the namedtuple. From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py index dc4b80d..26302a6 100644 --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -163,15 +163,21 @@ def Package(env, target=None, source=None, **kw): # this exception means that a needed argument for the packager is # missing. As our packagers get their "tags" as named function # arguments we need to find out which one is missing. - from inspect import getargspec - args,varargs,varkw,defaults=getargspec(packager.package) - if defaults!=None: - args=args[:-len(defaults)] # throw away arguments with default values + #TODO: getargspec deprecated in Py3. cleanup when Py2.7 dropped. + try: + from inspect import getfullargspec + except ImportError: + from inspect import getargspec + argspec = getargspec(packager.package) + args = argspec.args + if argspec.defaults: + # throw away arguments with default values + args = args[:-len(argspec.defaults)] args.remove('env') args.remove('target') args.remove('source') # now remove any args for which we have a value in kw. - args=[x for x in args if x not in kw] + args = [x for x in args if x not in kw] if len(args)==0: raise # must be a different error, so re-raise -- cgit v0.12 From bbde3d2bbf2a7e34d369c5be069fde2d5e235670 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 7 Sep 2018 07:37:58 -0600 Subject: Improvements to tarball packager Both gz and bzip2 now skip the test if tar is not found (previously they would not test, but mark the test OK). Since tar is now found on recent Windows 10, but the helper program bzip2 is not, check for bzip2 as well. This is actually a correct test for other systems as well, they have just been very unlikely to not have had bzip2 provisioned so it has not bitten us. Note: on Windows, with cygwin installed, the test may well find bzip2 but c:\Windows\System32\tar.exe does not find it and the test fails anyway. Work needed on this "finds too much" issue (not unique to this test). Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 5 ++++- test/packaging/tar/bz2_packaging.py | 17 +++++++++++------ test/packaging/tar/gz.py | 13 +++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6f7e851..6fa9649 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -129,13 +129,16 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Document and comment cleanup. - Added new Environment Value X_RPM_EXTRADEFS to supply custom settings to the specfile without adding specific logic for each one to scons. - - One swig test now checks for Python.h instead of failing + - The test for Python.h needed by swig tests is moved to get_python_platform + so it does not have to be repeated in every test; picks up one failure + which did not make the (previously needed) check. - If test opens os.devnull, register with atexit so file opens do not leak. - Fix bugs in Win32 process spawn logic to handle OSError exception correctly. - Use time.perf_counter instead of time.clock if it exists. time.clock deprecated since py3.3, due to remove in 3.8. deprecation warnings from py3.7 were failing a bunch of tests on Windows since they mess up expected stderr. + - tar packaging test fixups From Hao Wu - typo in customized decider example in user guide diff --git a/test/packaging/tar/bz2_packaging.py b/test/packaging/tar/bz2_packaging.py index 1552fd1..2a8b506 100644 --- a/test/packaging/tar/bz2_packaging.py +++ b/test/packaging/tar/bz2_packaging.py @@ -36,18 +36,23 @@ python = TestSCons.python test = TestSCons.TestSCons() tar = test.detect('TAR', 'tar') +if not tar: + test.skip_test('tar not found, skipping test\n') -if tar: - test.subdir('src') +bz2 = test.where_is('bzip2') +if not bz2: + test.skip_test('tar found, but helper bzip2 not found, skipping test\n') - test.write( [ 'src', 'main.c' ], r""" +test.subdir('src') + +test.write([ 'src', 'main.c'], r""" int main( int argc, char* argv[] ) { return 0; } """) - test.write('SConstruct', """ +test.write('SConstruct', """ Program( 'src/main.c' ) env=Environment(tools=['default', 'packaging']) env.Package( PACKAGETYPE = 'src_tarbz2', @@ -56,9 +61,9 @@ env.Package( PACKAGETYPE = 'src_tarbz2', source = [ 'src/main.c', 'SConstruct' ] ) """) - test.run(arguments='', stderr = None) +test.run(arguments='', stderr=None) - test.must_exist( 'src.tar.bz2' ) +test.must_exist('src.tar.bz2') test.pass_test() diff --git a/test/packaging/tar/gz.py b/test/packaging/tar/gz.py index f841c59..05661b7 100644 --- a/test/packaging/tar/gz.py +++ b/test/packaging/tar/gz.py @@ -36,18 +36,19 @@ python = TestSCons.python test = TestSCons.TestSCons() tar = test.detect('TAR', 'tar') +if not tar: + test.skip_test('tar not found, skipping test\n') -if tar: - test.subdir('src') +test.subdir('src') - test.write( [ 'src', 'main.c' ], r""" +test.write(['src', 'main.c'], r""" int main( int argc, char* argv[] ) { return 0; } """) - test.write('SConstruct', """ +test.write('SConstruct', """ Program( 'src/main.c' ) env=Environment(tools=['default', 'packaging']) env.Package( PACKAGETYPE = 'src_targz', @@ -56,9 +57,9 @@ env.Package( PACKAGETYPE = 'src_targz', source = [ 'src/main.c', 'SConstruct' ] ) """) - test.run(arguments='', stderr = None) +test.run(arguments='', stderr=None) - test.must_exist( 'src.tar.gz' ) +test.must_exist('src.tar.gz') test.pass_test() -- cgit v0.12 From 6c16e221611037b41353f18955ef54fdd09f463c Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 3 Sep 2018 08:21:39 -0600 Subject: Update testing docs A little formatting cleanup and some wording changes. Debugging end to end tests got its own section, could use more content probably. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + testing/framework/test-framework.rst | 524 +++++++++++++++++++++-------------- 2 files changed, 311 insertions(+), 214 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6f7e851..7d2eb0f 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -136,6 +136,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE time.clock deprecated since py3.3, due to remove in 3.8. deprecation warnings from py3.7 were failing a bunch of tests on Windows since they mess up expected stderr. + - updated the test-framework.rst documentation. From Hao Wu - typo in customized decider example in user guide diff --git a/testing/framework/test-framework.rst b/testing/framework/test-framework.rst index 7082eb9..cb6b8e1 100644 --- a/testing/framework/test-framework.rst +++ b/testing/framework/test-framework.rst @@ -2,14 +2,15 @@ SCons Testing Framework ======================= -SCons uses extensive automated tests to try to ensure quality. The primary goal -is that users should be able to upgrade from version to version without any surprise -changes in behavior. +SCons uses extensive automated tests to ensure quality. The primary goal +is that users be able to upgrade from version to version without +any surprise changes in behavior. -In general, no change goes into SCons unless it has one or more new or modified -tests that demonstrably exercise the bug being fixed or the feature being added. -There are exceptions to this guideline, but they should be just that, ''exceptions''. -When in doubt, make sure it's tested. +In general, no change goes into SCons unless it has one or more new +or modified tests that demonstrably exercise the bug being fixed or +the feature being added. There are exceptions to this guideline, but +they should be just that, ''exceptions''. When in doubt, make sure +it's tested. Test Organization ================= @@ -17,212 +18,193 @@ Test Organization There are three types of SCons tests: *End-to-End Tests* - End-to-end tests of SCons are all Python scripts (``*.py``) underneath - the ``test/`` subdirectory. They use the test infrastructure modules in the - ``testing/framework`` subdirectory. + End-to-end tests of SCons are Python scripts (``*.py``) underneath the + ``test/`` subdirectory. They use the test infrastructure modules in + the ``testing/framework`` subdirectory. They build set up complete + projects and call scons to execute them, checking that the behavior is + as expected. *Unit Tests* Unit tests for individual SCons modules live underneath the ``src/engine/`` subdirectory and are the same base name as the module - with ``Tests.py`` appended--for example, the unit tests for the - ``Builder.py`` module are in the ``BuilderTests.py`` script. + to be tests, with ``Tests`` appended before the ``.py``. For example, + the unit tests for the ``Builder.py`` module are in the + ``BuilderTests.py`` script. Unit tests tend to be based on assertions. *External Tests* - For the support of external Tools (in the form of packages, preferably), the - testing framework got extended, such that it can run in standalone mode. + For the support of external Tools (in the form of packages, preferably), + the testing framework is extended so it can run in standalone mode. You can start it from the top-level folder of your Tool's source tree, - where it then finds all Python scripts (``*.py``) underneath the - local ``test/`` directory. - This implies that Tool tests have to be kept in a folder named ``test``, - like for the SCons core. - + where it then finds all Python scripts (``*.py``) underneath the local + ``test/`` directory. This implies that Tool tests have to be kept in + a folder named ``test``, like for the SCons core. + Contrasting End-to-End and Unit Tests ##################################### -In general, anything that we've put into an end-to-end test script should -be considered a hardened part of the interface (that is, it's something -that a user might do) and should not be broken. Unit tests are now -considered more malleable, more for testing internal interfaces that -can change so long as we don't break users' ``SConscript`` files. (This -wasn't always the case, and there's a lot of meaty code in many of the -unit test scripts that does, in fact, capture external interface +In general, functionality with end-to-end tests +should be considered a hardened part of the public interface (that is, +something that a user might do) and should not be broken. Unit tests +are now considered more malleable, more for testing internal interfaces +that can change so long as we don't break users' ``SConscript`` files. +(This wasn't always the case, and there's a lot of meaty code in many +of the unit test scripts that does, in fact, capture external interface behavior. In general, we should try to move those things to end-to-end scripts as we find them.) -It's more difficult to debug end-to-end tests. You can actually go -straight into the Python debugger on the unit test scripts by using the -``runtest.py --pdb`` option, but the end-to-end tests treat an SCons -invocation as a "black box" and just look for external effects. -Simple ``print`` statements within the SCons code itself often don't help -debug end-to-end because they end up in SCons output that gets compared -against expected output and cause a test failure. Probably the most -effective technique is to use the internal ``SCons.Debug.Trace()`` function, -which prints output to ``/dev/tty`` on Linux/UNIX systems and ``con`` on -Windows systems, so you can see what's going on. - -Naming conventions +End-to-end tests are by their nature harder to debug. +You can drop straight into the Python debugger on the unit test +scripts by using the ``runtest.py --pdb`` option, but the end-to-end +tests treat an SCons invocation as a "black box" and just look for +external effects; simple methods like inserting ``print`` statements +in the SCons code itself can disrupt those external effects. +See `Debugging End-to-End Tests`_ for some more thoughts. + +Naming Conventions ################## -The end-to-end tests, more or less, stick to the following naming conventions: +The end-to-end tests, more or less, stick to the following naming +conventions: -1. All tests end with a .py suffix. +#. All tests end with a .py suffix. -2. In the *General* form we use +#. In the *General* form we use ``Feature.py`` - for the test of a specified feature; try to - keep this description reasonably short + for the test of a specified feature; try to keep this description + reasonably short ``Feature-x.py`` - for the test of a specified feature using - option ``x`` - -3. The *command line option* tests take the form + for the test of a specified feature using option ``x`` +#. The *command line option* tests take the form ``option-x.py`` for a lower-case single-letter option ``option--X.py`` - upper-case single-letter option - (with an extra hyphen, so the file names will - be unique on case-insensitive systems) + upper-case single-letter option (with an extra hyphen, so the + file names will be unique on case-insensitive systems) - ``option--lo.py`` - long option; abbreviate the long - option name to a few characters + ``option--lo.py`` + long option; abbreviate the long option name to a few characters Running Tests ============= -The standard set of SCons tests are run from the top-level source directory -by the ``runtest.py`` script. - -Help is available through the ``-h`` option: +The standard set of SCons tests are run from the top-level source +directory by the ``runtest.py`` script. -:: +Help is available through the ``-h`` option:: $ python runtest.py -h -To simply run all the tests, use the ``-a`` option: - -:: +To simply run all the tests, use the ``-a`` option:: $ python runtest.py -a -By default, ``runtest.py`` prints a count and percentage message for each test -case, along with the name of the test file. -If you need the output to be more silent, have a look at the ``-q``, ``-s`` and -``-k`` options. - -You may specifically list one or more tests to be run: +By default, ``runtest.py`` prints a count and percentage message for each +test case, along with the name of the test file. If you need the output +to be more silent, have a look at the ``-q``, ``-s`` and ``-k`` options. -:: +You may specifically list one or more tests to be run:: $ python runtest.py src/engine/SCons/BuilderTests.py $ python runtest.py test/option-j.py test/Program.py -Folder names are allowed arguments as well, so you can do a - -:: +Folder names are allowed arguments as well, so you can do:: $ python runtest.py test/SWIG to run all SWIG tests only. -You can also use the ``-f`` option to execute just the tests listed in a specified -text file: - -:: +You can also use the ``-f`` option to execute just the tests listed in +a specified text file:: $ cat testlist.txt test/option-j.py test/Program.py $ python runtest.py -f testlist.txt - One test must be listed per line, and any lines that begin with '#' -will be ignored (the intent being to allow you, for example, -to comment out tests that -are currently passing and then uncomment all of the tests in the file -for a final validation run). +will be ignored (the intent being to allow you, for example, to comment +out tests that are currently passing and then uncomment all of the tests +in the file for a final validation run). If more than one test is run, the ``runtest.py`` script prints a summary of how many tests passed, failed, or yielded no result, and lists any unsuccessful tests. The above invocations all test directly the files underneath the ``src/`` -subdirectory, and do not require that a packaging build be performed first. -The ``runtest.py`` script supports additional options to run tests against -unpacked packages in the ``build/test-*/`` subdirectories. +subdirectory, and do not require that a packaging build be performed +first. The ``runtest.py`` script supports additional options to run +tests against unpacked packages in the ``build/test-*/`` subdirectories. -If you are testing a separate Tool outside of the SCons source tree, you have -to call the ``runtest.py`` script in *external* (stand-alone) mode:: +If you are testing a separate Tool outside of the SCons source tree, you +have to call the ``runtest.py`` script in *external* (stand-alone) mode:: $ python ~/scons/runtest.py -e -a -. This ensures that the testing framework doesn't try to access SCons classes -needed for some of the *internal* test cases. +This ensures that the testing framework doesn't try to access SCons +classes needed for some of the *internal* test cases. -Note, that the actual tests are carried out in a temporary folder each, which gets -deleted afterwards. This ensures that your source directories don't get clobbered -with temporary files from the test runs. It also means that you can't simply change -into a folder to "debug things" after a test has gone wrong. For a way around this, -check out the ``PRESERVE`` environment variable. It can be seen in action in +Note, that the actual tests are carried out in a temporary folder each, +which gets deleted afterwards. This ensures that your source directories +don't get clobbered with temporary files from the test runs. It also +means that you can't simply change into a folder to "debug things" after +a test has gone wrong. For a way around this, check out the ``PRESERVE`` +environment variable. It can be seen in action in `How to convert old tests`_ below. Not Running Tests ================= -If you simply want to check which tests would get executed, you can call the -``runtest.py`` script with the ``-l`` option:: +If you simply want to check which tests would get executed, you can call +the ``runtest.py`` script with the ``-l`` option:: $ python runtest.py -l -Then there is also the ``-n`` option, which prints the command line for each -single test, but doesn't actually execute them:: +Then there is also the ``-n`` option, which prints the command line for +each single test, but doesn't actually execute them:: $ python runtest.py -n Finding Tests ============= -When started in *standard* mode - -:: +When started in *standard* mode:: $ python runtest.py -a +``runtest.py`` assumes that it is run from the SCons top-level source +directory. It then dives into the ``src`` and ``test`` folders, where +it tries to find filenames -, ``runtest.py`` assumes that it is run from the SCons top-level source directory. -It then dives into the ``src`` and ``test`` folders, where it tries to find filenames - - ``*Test.py`` - for the ``src`` directory, and - - ``*.py`` - for the ``test`` folder. +``*Test.py`` + for the ``src`` directory -When using fixtures, you may quickly end up in a position where you have supporting -Python script files in a subfolder, but they shouldn't get picked up as test scripts. -In this case you have two options: +``*.py`` + for the ``test`` folder -1. Add a file with the name ``sconstest.skip`` to your subfolder. This lets - ``runtest.py`` skip the contents of the directory completely. -2. Create a file ``.exclude_tests`` in each folder in question, and in it list - line-by-line the files to get excluded from testing. +When using fixtures, you may quickly end up in a position where you have +supporting Python script files in a subfolder, but they shouldn't get +picked up as test scripts. In this case you have two options: -The same rules apply when testing external Tools by using the ``-e`` option. +#. Add a file with the name ``sconstest.skip`` to your subfolder. This + lets ``runtest.py`` skip the contents of the directory completely. +#. Create a file ``.exclude_tests`` in each folder in question, and in + it list line-by-line the files to get excluded from testing. +The same rules apply when testing external Tools by using the ``-e`` +option. -"Hello, world!" SCons Test Script -================================= -To illustrate how the end-to-end test scripts work, -let's walk through a simple "Hello, world!" example: +Example End-to-End Test Script +============================== -:: +To illustrate how the end-to-end test scripts work, let's walk through +a simple "Hello, world!" example:: #!python import TestSCons @@ -249,157 +231,268 @@ let's walk through a simple "Hello, world!" example: test.pass_test() -``import TestSCons`` - Imports the main infrastructure for writing SCons tests. This is normally the only part of the infrastructure that needs importing. Sometimes other Python modules are necessary or helpful, and get imported before this line. +``import TestSCons`` + Imports the main infrastructure for writing SCons tests. This is + normally the only part of the infrastructure that needs importing. + Sometimes other Python modules are necessary or helpful, and get + imported before this line. ``test = TestSCons.TestSCons()`` - This initializes an object for testing. A fair amount happens under the covers when the object is created, including: + This initializes an object for testing. A fair amount happens under + the covers when the object is created, including: - * A temporary directory is created for all the in-line files that will get created. - * The temporary directory's removal is arranged for when the test is finished. - * We ``os.chdir()`` to the temporary directory. + * A temporary directory is created for all the in-line files that will + get created. -``test.write('SConstruct', ...`` - This line creates an ``SConstruct`` file in the temporary directory, to be used as input to the ``scons`` run(s) that we're testing. Note the use of the Python triple-quote syntax for the contents of the ``SConstruct`` file. Because input files for tests are all created from in-line data like this, the tests can sometimes get a little confusing to read, because some of the Python code is found + * The temporary directory's removal is arranged for when + the test is finished. -``test.write('hello.c', ...`` - This lines creates an ``hello.c`` file in the temporary directory. Note that we have to escape the ``\\n`` in the ``"Hello, world!\\n"`` string so that it ends up as a single backslash in the ``hello.c`` file on disk. + * The test does ``os.chdir()`` to the temporary directory. + +``test.write('SConstruct', ...)`` + This line creates an ``SConstruct`` file in the temporary directory, + to be used as input to the ``scons`` run(s) that we're testing. + Note the use of the Python triple-quote syntax for the contents + of the ``SConstruct`` file. Because input files for tests are all + created from in-line data like this, the tests can sometimes get + a little confusing to read, because some of the Python code is found + +``test.write('hello.c', ...)`` + This lines creates an ``hello.c`` file in the temporary directory. + Note that we have to escape the ``\\n`` in the + ``"Hello, world!\\n"`` string so that it ends up as a single + backslash in the ``hello.c`` file on disk. ``test.run()`` - This actually runs SCons. Like the object initialization, things happen under the covers: + This actually runs SCons. Like the object initialization, things + happen under the covers: - * The exit status is verified; the test exits with a failure if the exit status is not zero. - * The error output is examined, and the test exits with a failure if there is any + * The exit status is verified; the test exits with a failure if + the exit status is not zero. + * The error output is examined, and the test exits with a failure + if there is any. ``test.run(program='./hello', stdout="Hello, world!\n")`` - This shows use of the ``TestSCons.run()`` method to execute a program other than ``scons``, in this case the ``hello`` program we just presumably built. The ``stdout=`` keyword argument also tells the ``TestSCons.run()`` method to fail if the program output does not match the expected string ``"Hello, world!\n"``. Like the previous ``test.run()`` line, it will also fail the test if the exit status is non-zero, or there is any error output. + This shows use of the ``TestSCons.run()`` method to execute a program + other than ``scons``, in this case the ``hello`` program we just + presumably built. The ``stdout=`` keyword argument also tells the + ``TestSCons.run()`` method to fail if the program output does not + match the expected string ``"Hello, world!\n"``. Like the previous + ``test.run()`` line, it will also fail the test if the exit status is + non-zero, or there is any error output. ``test.pass_test()`` - This is always the last line in a test script. It prints ``PASSED`` on the screen and makes sure we exit with a ``0`` status to indicate the test passed. As a side effect of destroying the ``test`` object, the created temporary directory will be removed. + This is always the last line in a test script. It prints ``PASSED`` + on the screen and makes sure we exit with a ``0`` status to indicate + the test passed. As a side effect of destroying the ``test`` object, + the created temporary directory will be removed. -Working with fixtures +Working with Fixtures ===================== -In the simple example above, we have seen how to create files in the temporary test directory. -We give a filename to the ``TestSCons.write()`` method, together with its contents, and it gets -written to the test folder right before its start. - -This technique can still be seen throughout most of the end-to-end tests, but there is a better -way. It's much easier to edit, create and maintain real files, instead of copy/pasting -content to/from a Python script. If the test files get longer, the test script -gets longer and is harder to read. - -Against this, we now have the possibility to copy single files or the contents of a -local folder to the test directory. Since we can reuse these files/folders to setup -several tests, we call them *fixtures* in the following. - -Directory fixtures +In the simple example above, the files to set up the test are created +on the fly by the test program. We give a filename to the ``TestSCons.write()`` +method, and a string holding its contents, and it gets written to the test +folder right before starting.. + +This technique can still be seen throughout most of the end-to-end tests, +but there is a better way. To create a test, you need to create the +files that will be used, then when they work reasonably, they need to +be pasted into the script. The process repeats for maintenance. Once +a test gets more complex and/or grows many steps, the test script gets +harder to read. Why not keep the files as is? + +In testing parlance, a fixture is a repeatable test setup. The scons +test harness allows the use of saved files or directories to be used +in that sense: "the fixture for this test is foo", instead of writing +a whole bunch of strings to create files. Since these setups can be +reusable across multiple tests, the *fixture* terminology applies well. + +Directory Fixtures ################## -The function ``dir_fixture(self, srcdir, dstdir=None)`` in the ``TestCmd`` class -copies the contents of the specified folder ``srcdir`` from -the directory of the called test script, to the current -temporary test directory. -The ``srcdir`` name may be a list, in which case the elements are -concatenated with the ``os.path.join()`` method. The ``dstdir`` is -assumed to be under the temporary working directory, it gets -created automatically, if it does not already exist. +The function ``dir_fixture(self, srcdir, dstdir=None)`` in the ``TestCmd`` +class copies the contents of the specified folder ``srcdir`` from +the directory of the called test script to the current temporary test +directory. The ``srcdir`` name may be a list, in which case the elements +are concatenated with the ``os.path.join()`` method. The ``dstdir`` +is assumed to be under the temporary working directory, it gets created +automatically, if it does not already exist. A short syntax example:: test = TestSCons.TestSCons() test.dir_fixture('image') - test.run() + test.run() + +would copy all files and subfolders from the local ``image`` folder, +to the temporary directory for the current test. -would copy all files and subfolders from the local ``image`` folder, to -the temporary directory for the current test. +To see a real example for this in action, refer to the test named +``test/packaging/convenience-functions/convenience-functions.py``. -If you'd like to see a real example for this in action, refer to the test -named ``test/packaging/convenience-functions/convenience-functions.py``. - -File fixtures +File Fixtures ############# Like for directory fixtures, ``file_fixture(self, srcfile, dstfile=None)`` -copies the file ``srcfile`` from the directory of -the called script, to the temporary test directory. -The ``dstfile`` is assumed to be under the temporary working -directory, unless it is an absolute path name. -If ``dstfile`` is specified, its target directory gets created +copies the file ``srcfile`` from the directory of the called script, +to the temporary test directory. The ``dstfile`` is assumed to be +under the temporary working directory, unless it is an absolute path +name. If ``dstfile`` is specified, its target directory gets created automatically if it doesn't already exist. -With a:: +With the following code:: test = TestSCons.TestSCons() test.file_fixture('SConstruct') test.file_fixture(['src','main.cpp'],['src','main.cpp']) - test.run() + test.run() -you would copy the files ``SConstruct`` and ``src/main.cpp`` to the temporary -test folder, prior to running the test itself. +The files ``SConstruct`` and ``src/main.cpp`` are copied to the +temporary test directory. Notice the second ``file_fixture`` line +preserves the path of the original, otherwise ``main.cpp`` +would have landed in the top level of the test directory. -Again, a reference example can be found in the current *default* revision of -SCons, it is ``test/packaging/sandbox-test/sandbox-test.py``. +Again, a reference example can be found in the current revision +of SCons, it is ``test/packaging/sandbox-test/sandbox-test.py``. -For even more examples you should check out one of the external Tools, e.g. the -*Qt4* Tool at https://bitbucket.org/dirkbaechle/scons_qt4. Also visit the SCons -Tools Index at https://github.com/SCons/scons/wiki/ToolsIndex for a complete +For even more examples you should check out +one of the external Tools, e.g. the *Qt4* Tool at +https://bitbucket.org/dirkbaechle/scons_qt4. Also visit the SCons Tools +Index at https://github.com/SCons/scons/wiki/ToolsIndex for a complete list of available Tools, though not all may have tests yet. -How to convert old tests -######################## +How to Convert Old Tests to Use Fixures +####################################### -We now show how to convert a test, still using the ``TestSCons.write()`` method, to -the fixture based approach. For this, we need to get at the files as they -are written to each temporary test folder. +Tests using the inline ``TestSCons.write()`` method can easily be +converted to the fixture based approach. For this, we need to get at the +files as they are written to each temporary test folder. -Luckily, ``runtest.py`` checks for the existence of an environment variable named -``PRESERVE``. If it is set to a non-zero value, the testing framework doesn't delete -the test folder as ususal, but prints its name to the screen. +``runtest.py`` checks for the existence of an environment +variable named ``PRESERVE``. If it is set to a non-zero value, the testing +framework preserves the test folder instead of deleting it, and prints +its name to the screen. -So, you should be able to give the commands +So, you should be able to give the commands:: -:: + $ PRESERVE=1 python runtest.py test/packaging/sandbox-test.py - $ export PRESERVE=1 - $ python runtest.py test/packaging/sandbox-test.py +assuming Linux and a bash-like shell. For a Windows ``cmd`` shell, use +``set PRESERVE=1`` (that will leave it set for the duration of the +``cmd`` session, unless manually deleted). -, assuming Linux and a bash-like shell. - The output should then look something like this:: 1/1 (100.00%) /usr/bin/python -tt test/packaging/sandbox-test.py PASSED Preserved directory /tmp/testcmd.4060.twlYNI -and you see that the test files have been kept in the folder ``/tmp/testcmd.4060.twlYNI``, -where you can now copy them from to your new *fixture* folder. Then, in the test -script you simply remove all the tedious ``TestSCons.write()`` statements and -replace them by a single ``TestSCons.dir_fixture()``. +You can now copy the files from that folder to your new +*fixture* folder. Then, in the test script you simply remove all the +tedious ``TestSCons.write()`` statements and replace them by a single +``TestSCons.dir_fixture()``. + +Finally, don't forget to clean up and remove the temporary test +directory. ``;)`` + +When Not to Use a Fixture +######################### + +Note that some files are not appropriate for use in a fixture as-is: +fixture files should be static. If the creation of the file involves +interpolating data discovered during the run of the test script, +that process should stay in the script. Here is an example of this +kind of usage that does not lend itself to a fixture:: + + import TestSCons + _python_ = TestSCons._python_ + + test.write('SConstruct', """ + cc = Environment().Dictionary('CC') + env = Environment(LINK = r'%(_python_)s mylink.py', + LINKFLAGS = [], + CC = r'%(_python_)s mycc.py', + CXX = cc, + CXXFLAGS = []) + env.Program(target = 'test1', source = 'test1.c') + """ % locals()) + +Here the value of ``_python_`` is picked out of the script's +``locals`` dictionary and interpolated into the string that +will be written to ``SConstruct``. + +The other files created in this test may still be candidates for +use in a fixture, however. + +Debugging End-to-End Tests +========================== + +Most of the end to end tests have expectations for standard output +and error from the test runs. The expectation could be either +that there is nothing on that stream, or that it will contain +very specific text which the test matches against. So adding +``print()`` calls, or ``sys,stderr.write()`` or similar will +emit data that the tests do not expect, and cause further failures. +Say you have three different tests in a script, and the third +one is unexpectedly failing. You add some debug prints to the +part of scons that is involved, and now the first test of the +three starts failing, aborting the test run before it gets +to the third test you were trying to debug. + +Still, there are some techniques to help debugging. + +Probably the most effective technique is to use the internal +``SCons.Debug.Trace()`` function, which prints output to +``/dev/tty`` on Linux/UNIX systems and ``con`` on Windows systems, +so you can see what's going on. + +If you do need to add informational messages in scons code +to debug a problem, you can use logging and send the messages +to a file instead, so they don't interrupt the test expectations. + +Part of the technique discussed in the section +`How to Convert Old Tests to Use Fixures`_ can also be helpful +for debugging purposes. If you have a failing test, try:: + + $ PRESERVE=1 python runtest.py test/failing-test.py + +You can now go to the save directory reported from this run +and invoke the test manually to see what it is doing, without +the presence of the test infrastructure which would otherwise +"swallow" output you may be interested in. In this case, +adding debug prints may be more useful. -Finally, you shouldn't forget to clean up and remove the temporary test directory. ``;)`` Test Infrastructure =================== -The test API is in ``testing/framework/TestSCons.py``. ``TestSCons`` is a subclass of -``TestCommon``, which is a subclass of ``TestCmd``; all those python files are -in ``testing/framework``. Start in ``testing/framework/TestCmd.py`` for the base API definitions, -like how to create files (``test.write()``) and run commands (``test.run()``). +The main test API in the ``TestSCons.py`` class. ``TestSCons`` +is a subclass of ``TestCommon``, which is a subclass of ``TestCmd``. +All those classes are defined in python files of the same name +in ``testing/framework``. Start in +``testing/framework/TestCmd.py`` for the base API definitions, like how +to create files (``test.write()``) and run commands (``test.run()``). -You want to use ``TestSCons`` for the end-to-end tests in ``test``, but ``TestCmd`` -for the unit tests in the ``src`` folder. +Use ``TestSCons`` for the end-to-end tests in ``test``, but use +``TestCmd`` for the unit tests in the ``src`` folder. The match functions work like this: -TestSCons.match_re:: match each line with a RE +``TestSCons.match_re`` + match each line with a RE + * Splits the lines into a list (unless they already are) * splits the REs at newlines (unless already a list) and puts ^..$ around each - * then each RE must match each line. This means there must be as many REs as lines. + * then each RE must match each line. This means there must be as many + REs as lines. + +``TestSCons.match_re_dotall`` + match all the lines against a single RE -TestSCons.match_re_dotall:: match all the lines against a single RE * Joins the lines with newline (unless already a string) - * joins the REs with newline (unless it's a string) and puts ^..$ around the whole thing + * joins the REs with newline (unless it's a string) and puts ``^..$`` + around the whole thing * then whole thing must match with python re.DOTALL. Use them in a test like this:: @@ -410,18 +503,21 @@ or:: test.must_match(..., match=TestSCons.match_re, ...) -Avoiding Tests based on Tool existence +Avoiding Tests Based on Tool Existence ====================================== -Here's an easy sample:: +Here's a simple example:: #!python intelc = test.detect_tool('intelc', prog='icpc') if not intelc: test.skip_test("Could not load 'intelc' Tool; skipping test(s).\n") -See ``testing/framework/TestSCons.py`` for the ``detect_tool`` method. It calls the tool's -``generate()`` method, and then looks for the given prog (tool name by default) in -``env['ENV']['PATH']``. - +See ``testing/framework/TestSCons.py`` for the ``detect_tool`` method. +It calls the tool's ``generate()`` method, and then looks for the given +program (tool name by default) in ``env['ENV']['PATH']``. +The ``where_is`` method can be used to look for programs that +are do not have tool specifications. The existing test code +will have many samples of using either or both of these to detect +if it is worth even proceeding with a test. -- cgit v0.12 From fe27baf677366633c9ec64f2f7b22cb4063adc28 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 11 Sep 2018 07:24:41 -0600 Subject: Add java 11 jdk 11.0 is almost at GA, and it will be the first edition designated as Long Term Support (LTS), with commercial support until Sept 2023, so we might as well add support to scons. Going forward, is it worth continuing to check for individual versions as being "supported"? Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 2 +- src/engine/SCons/Tool/JavaCommon.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6f7e851..353d5ab 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -101,7 +101,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE This changes SCons to better comply with normal Python installation practices. From Mats Wichmann: - - Recognize new java 9, 10 (as 9.0 and 10.0) + - Recognize new java 9, 10, 11 (as 9.0 and 10.0, 11.0) - Updated manpage scons.xml to fix a nested list problem - Updated doc terminiology: use prepend instead of append as appropriate - XML validity fixes from SConstruct.py change diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 8349164..e90e768 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -69,7 +69,7 @@ if java_parsing: def __init__(self, version=default_java_version): if not version in ('1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', - '1.8', '5', '6', '9.0', '10.0'): + '1.8', '5', '6', '9.0', '10.0', '11.0'): msg = "Java version %s not supported" % version raise NotImplementedError(msg) @@ -177,7 +177,7 @@ if java_parsing: if self.version in ('1.1', '1.2', '1.3', '1.4'): clazz = self.listClasses[0] self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) - elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0'): + elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0', '11.0'): self.stackAnonClassBrackets.append(self.brackets) className = [] className.extend(self.listClasses) -- cgit v0.12 From b71404ff2d256460f23c4494932af2ea61d1a271 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 20 Sep 2018 22:35:51 -0400 Subject: Fix test/Execute.py failing on windows with python 2.7.15. It worked fine with python 2.7.14 and below. The output message changed from / to \ and broke the test. --- test/Execute.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/Execute.py b/test/Execute.py index edd746e..c40d0d0 100644 --- a/test/Execute.py +++ b/test/Execute.py @@ -27,7 +27,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Test the Execute() function for executing actions directly. """ - +import sys import TestSCons _python_ = TestSCons._python_ @@ -82,11 +82,12 @@ test.write('k.in', "k.in\n") test.write('l.in', "l.in\n") test.write('m.in', "m.in\n") -import sys if sys.platform == 'win32' and sys.version_info[0] == 2: + # note that nonexistent.in will have a \ on windows with python < 2.7.15 + # and a / on >= 2.7.15 (The third line below) expect = r"""scons: \*\*\* Error 1 scons: \*\*\* Error 2 -scons: \*\*\* nonexistent.in/\*\.\*: (The system cannot find the path specified|Das System kann den angegebenen Pfad nicht finden)""" +scons: \*\*\* nonexistent.in(/|\\)\*\.\*: (The system cannot find the path specified|Das System kann den angegebenen Pfad nicht finden)""" elif sys.platform == 'win32' and sys.version_info[0] == 3: expect = r"""scons: \*\*\* Error 1 scons: \*\*\* Error 2 -- cgit v0.12 From 92830df278cc22505fb1e7984e422dda35ce3503 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 13:03:29 -0700 Subject: Add centos specific likely qt path to moc as it's not in the default PATH defined by SCons --- src/engine/SCons/Tool/qt.py | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 77269a8..535f3d7 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -37,6 +37,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path import re +import glob import SCons.Action import SCons.Builder @@ -44,6 +45,8 @@ import SCons.Defaults import SCons.Scanner import SCons.Tool import SCons.Util +import SCons.Tool.cxx +cplusplus = SCons.Tool.cxx class ToolQtWarning(SCons.Warnings.Warning): pass @@ -60,12 +63,33 @@ header_extensions = [".h", ".hxx", ".hpp", ".hh"] if SCons.Util.case_sensitive_suffixes('.h', '.H'): header_extensions.append('.H') -import SCons.Tool.cxx -cplusplus = SCons.Tool.cxx -#cplusplus = __import__('cxx', globals(), locals(), []) - cxx_suffixes = cplusplus.CXXSuffixes + +# +def find_platform_specific_qt_paths(): + """ + If the platform has non-standard paths which it installs QT in,return the likely default path + :return: + """ + + # qt_bin_dirs = [] + qt_bin_dir = None + if os.path.isfile('/etc/redhat-release'): + with open('/etc/redhat-release','r') as rr: + lines = rr.readlines() + distro = lines[0].split()[0] + if distro == 'CentOS': + # Centos installs QT under /usr/{lib,lib64}/qt{4,5,-3.3}/bin + # so we need to handle this differently + # qt_bin_dirs = glob.glob('/usr/lib64/qt*/bin') + qt_bin_dir = '/usr/lib64/qt-3.3/bin' + + return qt_bin_dir + + +QT_BIN_DIR = find_platform_specific_qt_paths() + def checkMocIncluded(target, source, env): moc = target[0] cpp = source[0] @@ -188,13 +212,13 @@ AutomocStatic = _Automoc('StaticObject') def _detect(env): """Not really safe, but fast method to detect the QT library""" - QTDIR = None - if not QTDIR: - QTDIR = env.get('QTDIR',None) + + QTDIR = env.get('QTDIR',None) if not QTDIR: QTDIR = os.environ.get('QTDIR',None) if not QTDIR: - moc = env.WhereIs('moc') + try_paths = os.pathsep.join([env['ENV']['PATH'],QT_BIN_DIR]) + moc = env.WhereIs('moc',try_paths) if moc: QTDIR = os.path.dirname(os.path.dirname(moc)) SCons.Warnings.warn( -- cgit v0.12 From 9189b30403f5de0566902a46278f592498ac1eea Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 14:10:05 -0700 Subject: Handle when we're not on CentOS --- src/engine/SCons/Tool/qt.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 535f3d7..b8cf77a 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -217,8 +217,7 @@ def _detect(env): if not QTDIR: QTDIR = os.environ.get('QTDIR',None) if not QTDIR: - try_paths = os.pathsep.join([env['ENV']['PATH'],QT_BIN_DIR]) - moc = env.WhereIs('moc',try_paths) + moc = env.WhereIs('moc') or env.WhereIs('moc',QT_BIN_DIR) if moc: QTDIR = os.path.dirname(os.path.dirname(moc)) SCons.Warnings.warn( -- cgit v0.12 From 5f09733499c7d1c8cb559a02ea1e6e4fef3825ea Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 15:41:42 -0700 Subject: Add info to CHANGES.txt --- src/CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index a155da7..2e7c998 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -46,6 +46,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fix GH Issue #3141 unicode string in a TryAction() with python 2.7 crashes. - Fixed issue causing stack trace when python Action function contains a unicode string when being run with Python 2.7 + - Add alternate path to QT install for Centos in qt tool: /usr/lib64/qt-3.3/bin From Andrew Featherstone - Removed unused --warn options from the man page and source code. -- cgit v0.12 From 9d8ca555d5e95fde61fc3f43fa0fde632e487279 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 19:28:49 -0400 Subject: Fix Github issue #2580 - hash mark not properly handled in FRAMEWORKPATH --- src/CHANGES.txt | 2 ++ src/engine/SCons/Tool/applelink.py | 3 +- test/LINK/applelink.py | 70 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 test/LINK/applelink.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 12df163..1461d62 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -47,6 +47,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fixed issue causing stack trace when python Action function contains a unicode string when being run with Python 2.7 - Add alternate path to QT install for Centos in qt tool: /usr/lib64/qt-3.3/bin + - Fix GH Issue #2580 - # in FRAMEWORKPATH doesn't get properly expanded. The # is left in the + command line. From Andrew Featherstone - Removed unused --warn options from the man page and source code. diff --git a/src/engine/SCons/Tool/applelink.py b/src/engine/SCons/Tool/applelink.py index c5f4376..65b569a 100644 --- a/src/engine/SCons/Tool/applelink.py +++ b/src/engine/SCons/Tool/applelink.py @@ -45,7 +45,8 @@ def generate(env): link.generate(env) env['FRAMEWORKPATHPREFIX'] = '-F' - env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' + env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__, RDirs)}' + env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') diff --git a/test/LINK/applelink.py b/test/LINK/applelink.py new file mode 100644 index 0000000..0d05d65 --- /dev/null +++ b/test/LINK/applelink.py @@ -0,0 +1,70 @@ +#!/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 os + +import TestSCons + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + + +test.write('foo.c', r""" +#include +#include +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("foo.c\n"); + exit (0); +} +""") + +# Test issue # 2580 +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment() + +env.Object( + target = '#foo.o', + source = ['foo.c'], + FRAMEWORKS = ['Ogre'], + FRAMEWORKPATH = ['#frameworks'] +) +""" % locals()) + +test.run(arguments='-Q', stdout='gcc -o foo.o -c -Fframeworks foo.c\n') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From 6da2e3926185a44c20bbd3be3564b332e98afd65 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Sep 2018 21:00:54 -0400 Subject: fix test so it should run on any platform --- src/engine/SCons/Defaults.py | 3 +++ src/engine/SCons/Tool/applelink.py | 3 +-- test/LINK/applelink.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 69d5c94..87e831d 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -342,6 +342,7 @@ Touch = ActionFactory(touch_func, # Internal utility functions + def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): """ Creates a new list from 'list' by first interpolating each element @@ -358,6 +359,7 @@ def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): return _concat_ixes(prefix, list, suffix, env) + def _concat_ixes(prefix, list, suffix, env): """ Creates a new list from 'list' by concatenating the 'prefix' and @@ -395,6 +397,7 @@ def _concat_ixes(prefix, list, suffix, env): return result + def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): """ This is a wrapper around _concat()/_concat_ixes() that checks for diff --git a/src/engine/SCons/Tool/applelink.py b/src/engine/SCons/Tool/applelink.py index 65b569a..5a06f9c 100644 --- a/src/engine/SCons/Tool/applelink.py +++ b/src/engine/SCons/Tool/applelink.py @@ -39,6 +39,7 @@ import SCons.Util # the -rpath option, so we use the "link" tool instead of "gnulink". from . import link + def generate(env): """Add Builders and construction variables for applelink to an Environment.""" @@ -68,8 +69,6 @@ def generate(env): env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - - def exists(env): return env['PLATFORM'] == 'darwin' diff --git a/test/LINK/applelink.py b/test/LINK/applelink.py index 0d05d65..b13b36f 100644 --- a/test/LINK/applelink.py +++ b/test/LINK/applelink.py @@ -49,7 +49,7 @@ main(int argc, char *argv[]) # Test issue # 2580 test.write('SConstruct', """ DefaultEnvironment(tools=[]) -env = Environment() +env = Environment(PLATFORM='darwin') env.Object( target = '#foo.o', -- cgit v0.12 From 338551fa63ccf0e12121da2429ab1eae16636881 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 24 Sep 2018 08:23:10 -0600 Subject: Update getargspec patch: actually use getfullargspec If able to import getfullargspec, use it to read args. Previous version still used getargspec, which might not be imported. Signed-off-by: Mats Wichmann --- src/engine/SCons/Tool/packaging/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py index 26302a6..4c92022 100644 --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -27,14 +27,15 @@ SCons Packaging Tool. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import SCons.Defaults import SCons.Environment from SCons.Variables import * from SCons.Errors import * from SCons.Util import is_List, make_path_relative from SCons.Warnings import warn, Warning -import os, imp -import SCons.Defaults +import os +import imp __all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] @@ -166,9 +167,10 @@ def Package(env, target=None, source=None, **kw): #TODO: getargspec deprecated in Py3. cleanup when Py2.7 dropped. try: from inspect import getfullargspec + argspec = getfullargspec(packager.package) except ImportError: from inspect import getargspec - argspec = getargspec(packager.package) + argspec = getargspec(packager.package) args = argspec.args if argspec.defaults: # throw away arguments with default values -- cgit v0.12 From 9864e8bf0a69f6aa35bb6af794da9988b1bbc50b Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 25 Sep 2018 17:48:03 -0600 Subject: Stop using deprecated unittest asserts failUnless and failIf have been deprecated since 2.7 and 3.1, the docs call them aliases that exist for historical reasons. In Python 3.7, the default DeprecationWarnings make things very noisy, so flip these to assertTrue and assertFalse. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + src/engine/SCons/JobTests.py | 70 ++++++++++++++++---------------- src/engine/SCons/Scanner/CTests.py | 2 +- src/engine/SCons/Scanner/DTests.py | 2 +- src/engine/SCons/Scanner/FortranTests.py | 2 +- src/engine/SCons/Scanner/IDLTests.py | 2 +- src/engine/SCons/Scanner/LaTeXTests.py | 2 +- src/engine/SCons/Scanner/RCTests.py | 2 +- src/engine/SCons/Scanner/ScannerTests.py | 52 ++++++++++++------------ 9 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6dbca60..8a65750 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -149,6 +149,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Updated the test-framework.rst documentation. - Remove obsoleted internal implementaiton of OrderedDict. - Test for tar packaging fixups + - Stop using deprecated unittest asserts From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/JobTests.py b/src/engine/SCons/JobTests.py index 39918db..325c0e1 100644 --- a/src/engine/SCons/JobTests.py +++ b/src/engine/SCons/JobTests.py @@ -98,7 +98,7 @@ class Task(object): return True def execute(self): - self.taskmaster.test_case.failUnless(self.was_prepared, + self.taskmaster.test_case.assertTrue(self.was_prepared, "the task wasn't prepared") self.taskmaster.guard.acquire() @@ -126,17 +126,17 @@ class Task(object): def executed(self): self.taskmaster.num_executed = self.taskmaster.num_executed + 1 - self.taskmaster.test_case.failUnless(self.was_prepared, + self.taskmaster.test_case.assertTrue(self.was_prepared, "the task wasn't prepared") - self.taskmaster.test_case.failUnless(self.was_executed, + self.taskmaster.test_case.assertTrue(self.was_executed, "the task wasn't really executed") - self.taskmaster.test_case.failUnless(isinstance(self, Task), + self.taskmaster.test_case.assertTrue(isinstance(self, Task), "the task wasn't really a Task instance") def failed(self): self.taskmaster.num_failed = self.taskmaster.num_failed + 1 self.taskmaster.stop = 1 - self.taskmaster.test_case.failUnless(self.was_prepared, + self.taskmaster.test_case.assertTrue(self.was_prepared, "the task wasn't prepared") def postprocess(self): @@ -171,17 +171,17 @@ class ExceptionTask(object): def executed(self): self.taskmaster.num_executed = self.taskmaster.num_executed + 1 - self.taskmaster.test_case.failUnless(self.was_prepared, + self.taskmaster.test_case.assertTrue(self.was_prepared, "the task wasn't prepared") - self.taskmaster.test_case.failUnless(self.was_executed, + self.taskmaster.test_case.assertTrue(self.was_executed, "the task wasn't really executed") - self.taskmaster.test_case.failUnless(self.__class__ is Task, + self.taskmaster.test_case.assertTrue(self.__class__ is Task, "the task wasn't really a Task instance") def failed(self): self.taskmaster.num_failed = self.taskmaster.num_failed + 1 self.taskmaster.stop = 1 - self.taskmaster.test_case.failUnless(self.was_prepared, + self.taskmaster.test_case.assertTrue(self.was_prepared, "the task wasn't prepared") def postprocess(self): @@ -262,15 +262,15 @@ class ParallelTestCase(unittest.TestCase): jobs = SCons.Job.Jobs(num_jobs, taskmaster) jobs.run() - self.failUnless(not taskmaster.tasks_were_serial(), + self.assertTrue(not taskmaster.tasks_were_serial(), "the tasks were not executed in parallel") - self.failUnless(taskmaster.all_tasks_are_executed(), + self.assertTrue(taskmaster.all_tasks_are_executed(), "all the tests were not executed") - self.failUnless(taskmaster.all_tasks_are_iterated(), + self.assertTrue(taskmaster.all_tasks_are_iterated(), "all the tests were not iterated over") - self.failUnless(taskmaster.all_tasks_are_postprocessed(), + self.assertTrue(taskmaster.all_tasks_are_postprocessed(), "all the tests were not postprocessed") - self.failIf(taskmaster.num_failed, + self.assertFalse(taskmaster.num_failed, "some task(s) failed to execute") # Verify that parallel jobs will pull all of the completed tasks @@ -325,15 +325,15 @@ class SerialTestCase(unittest.TestCase): jobs = SCons.Job.Jobs(1, taskmaster) jobs.run() - self.failUnless(taskmaster.tasks_were_serial(), + self.assertTrue(taskmaster.tasks_were_serial(), "the tasks were not executed in series") - self.failUnless(taskmaster.all_tasks_are_executed(), + self.assertTrue(taskmaster.all_tasks_are_executed(), "all the tests were not executed") - self.failUnless(taskmaster.all_tasks_are_iterated(), + self.assertTrue(taskmaster.all_tasks_are_iterated(), "all the tests were not iterated over") - self.failUnless(taskmaster.all_tasks_are_postprocessed(), + self.assertTrue(taskmaster.all_tasks_are_postprocessed(), "all the tests were not postprocessed") - self.failIf(taskmaster.num_failed, + self.assertFalse(taskmaster.num_failed, "some task(s) failed to execute") class NoParallelTestCase(unittest.TestCase): @@ -346,18 +346,18 @@ class NoParallelTestCase(unittest.TestCase): try: taskmaster = Taskmaster(num_tasks, self, RandomTask) jobs = SCons.Job.Jobs(2, taskmaster) - self.failUnless(jobs.num_jobs == 1, + self.assertTrue(jobs.num_jobs == 1, "unexpected number of jobs %d" % jobs.num_jobs) jobs.run() - self.failUnless(taskmaster.tasks_were_serial(), + self.assertTrue(taskmaster.tasks_were_serial(), "the tasks were not executed in series") - self.failUnless(taskmaster.all_tasks_are_executed(), + self.assertTrue(taskmaster.all_tasks_are_executed(), "all the tests were not executed") - self.failUnless(taskmaster.all_tasks_are_iterated(), + self.assertTrue(taskmaster.all_tasks_are_iterated(), "all the tests were not iterated over") - self.failUnless(taskmaster.all_tasks_are_postprocessed(), + self.assertTrue(taskmaster.all_tasks_are_postprocessed(), "all the tests were not postprocessed") - self.failIf(taskmaster.num_failed, + self.assertFalse(taskmaster.num_failed, "some task(s) failed to execute") finally: SCons.Job.Parallel = save_Parallel @@ -371,13 +371,13 @@ class SerialExceptionTestCase(unittest.TestCase): jobs = SCons.Job.Jobs(1, taskmaster) jobs.run() - self.failIf(taskmaster.num_executed, + self.assertFalse(taskmaster.num_executed, "a task was executed") - self.failUnless(taskmaster.num_iterated == 1, + self.assertTrue(taskmaster.num_iterated == 1, "exactly one task should have been iterated") - self.failUnless(taskmaster.num_failed == 1, + self.assertTrue(taskmaster.num_failed == 1, "exactly one task should have failed") - self.failUnless(taskmaster.num_postprocessed == 1, + self.assertTrue(taskmaster.num_postprocessed == 1, "exactly one task should have been postprocessed") class ParallelExceptionTestCase(unittest.TestCase): @@ -388,13 +388,13 @@ class ParallelExceptionTestCase(unittest.TestCase): jobs = SCons.Job.Jobs(num_jobs, taskmaster) jobs.run() - self.failIf(taskmaster.num_executed, + self.assertFalse(taskmaster.num_executed, "a task was executed") - self.failUnless(taskmaster.num_iterated >= 1, + self.assertTrue(taskmaster.num_iterated >= 1, "one or more task should have been iterated") - self.failUnless(taskmaster.num_failed >= 1, + self.assertTrue(taskmaster.num_failed >= 1, "one or more tasks should have failed") - self.failUnless(taskmaster.num_postprocessed >= 1, + self.assertTrue(taskmaster.num_postprocessed >= 1, "one or more tasks should have been postprocessed") #--------------------------------------------------------------------- @@ -525,10 +525,10 @@ class _SConsTaskTest(unittest.TestCase): for N in testnodes: state = N.get_state() - self.failUnless(state in [SCons.Node.no_state, N.expect_to_be], + self.assertTrue(state in [SCons.Node.no_state, N.expect_to_be], "Node %s got unexpected result: %s" % (N, state)) - self.failUnless([N for N in testnodes if N.get_state()], + self.assertTrue([N for N in testnodes if N.get_state()], "no nodes ran at all.") diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py index c31d373..4b7233c 100644 --- a/src/engine/SCons/Scanner/CTests.py +++ b/src/engine/SCons/Scanner/CTests.py @@ -218,7 +218,7 @@ def deps_match(self, deps, headers): global my_normpath scanned = list(map(my_normpath, list(map(str, deps)))) expect = list(map(my_normpath, headers)) - self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) # define some tests: diff --git a/src/engine/SCons/Scanner/DTests.py b/src/engine/SCons/Scanner/DTests.py index ad6bd5a..d0157bf 100644 --- a/src/engine/SCons/Scanner/DTests.py +++ b/src/engine/SCons/Scanner/DTests.py @@ -79,7 +79,7 @@ def deps_match(self, deps, headers): global my_normpath scanned = list(map(my_normpath, list(map(str, deps)))) expect = list(map(my_normpath, headers)) - self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) """ Examples from https://dlang.org/spec/module.html diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py index aaefa79..5a09e3b 100644 --- a/src/engine/SCons/Scanner/FortranTests.py +++ b/src/engine/SCons/Scanner/FortranTests.py @@ -258,7 +258,7 @@ class DummyEnvironment(object): def deps_match(self, deps, headers): scanned = list(map(os.path.normpath, list(map(str, deps)))) expect = list(map(os.path.normpath, headers)) - self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) # define some tests: diff --git a/src/engine/SCons/Scanner/IDLTests.py b/src/engine/SCons/Scanner/IDLTests.py index 7b53801..2cd67d5 100644 --- a/src/engine/SCons/Scanner/IDLTests.py +++ b/src/engine/SCons/Scanner/IDLTests.py @@ -243,7 +243,7 @@ if os.path.normcase('foo') == os.path.normcase('FOO'): def deps_match(self, deps, headers): scanned = list(map(my_normpath, list(map(str, deps)))) expect = list(map(my_normpath, headers)) - self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) # define some tests: diff --git a/src/engine/SCons/Scanner/LaTeXTests.py b/src/engine/SCons/Scanner/LaTeXTests.py index 5913124..0114d45 100644 --- a/src/engine/SCons/Scanner/LaTeXTests.py +++ b/src/engine/SCons/Scanner/LaTeXTests.py @@ -122,7 +122,7 @@ def deps_match(self, deps, headers): global my_normpath scanned = list(map(my_normpath, list(map(str, deps)))) expect = list(map(my_normpath, headers)) - self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) class LaTeXScannerTestCase1(unittest.TestCase): diff --git a/src/engine/SCons/Scanner/RCTests.py b/src/engine/SCons/Scanner/RCTests.py index d4f53ca..551e613 100644 --- a/src/engine/SCons/Scanner/RCTests.py +++ b/src/engine/SCons/Scanner/RCTests.py @@ -116,7 +116,7 @@ if os.path.normcase('foo') == os.path.normcase('FOO'): def deps_match(self, deps, headers): scanned = sorted(map(my_normpath, list(map(str, deps)))) expect = sorted(map(my_normpath, headers)) - self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) # define some tests: diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py index 8050216..5cdd5b1 100644 --- a/src/engine/SCons/Scanner/ScannerTests.py +++ b/src/engine/SCons/Scanner/ScannerTests.py @@ -132,16 +132,16 @@ class BaseTestCase(unittest.TestCase): scanned = scanner(filename, env, path) scanned_strs = [str(x) for x in scanned] - self.failUnless(self.filename == filename, "the filename was passed incorrectly") - self.failUnless(self.env == env, "the environment was passed incorrectly") - self.failUnless(scanned_strs == deps, "the dependencies were returned incorrectly") + self.assertTrue(self.filename == filename, "the filename was passed incorrectly") + self.assertTrue(self.env == env, "the environment was passed incorrectly") + self.assertTrue(scanned_strs == deps, "the dependencies were returned incorrectly") for d in scanned: - self.failUnless(not isinstance(d, str), "got a string in the dependencies") + self.assertTrue(not isinstance(d, str), "got a string in the dependencies") if len(args) > 0: - self.failUnless(self.arg == args[0], "the argument was passed incorrectly") + self.assertTrue(self.arg == args[0], "the argument was passed incorrectly") else: - self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been") + self.assertFalse(hasattr(self, "arg"), "an argument was given when it shouldn't have been") def test___call__dict(self): """Test calling Scanner.Base objects with a dictionary""" @@ -245,7 +245,7 @@ class BaseTestCase(unittest.TestCase): dict[s] = 777 i = hash(id(s)) h = hash(list(dict.keys())[0]) - self.failUnless(h == i, + self.assertTrue(h == i, "hash Scanner base class expected %s, got %s" % (i, h)) def test_scan_check(self): @@ -260,7 +260,7 @@ class BaseTestCase(unittest.TestCase): self.checked = {} path = s.path(env) scanned = s(DummyNode('x'), env, path) - self.failUnless(self.checked['x'] == 1, + self.assertTrue(self.checked['x'] == 1, "did not call check function") def test_recursive(self): @@ -269,42 +269,42 @@ class BaseTestCase(unittest.TestCase): s = SCons.Scanner.Base(function = self.func) n = s.recurse_nodes(nodes) - self.failUnless(n == [], + self.assertTrue(n == [], "default behavior returned nodes: %s" % n) s = SCons.Scanner.Base(function = self.func, recursive = None) n = s.recurse_nodes(nodes) - self.failUnless(n == [], + self.assertTrue(n == [], "recursive = None returned nodes: %s" % n) s = SCons.Scanner.Base(function = self.func, recursive = 1) n = s.recurse_nodes(nodes) - self.failUnless(n == n, + self.assertTrue(n == n, "recursive = 1 didn't return all nodes: %s" % n) def odd_only(nodes): return [n for n in nodes if n % 2] s = SCons.Scanner.Base(function = self.func, recursive = odd_only) n = s.recurse_nodes(nodes) - self.failUnless(n == [1, 3], + self.assertTrue(n == [1, 3], "recursive = 1 didn't return all nodes: %s" % n) def test_get_skeys(self): """Test the Scanner.Base get_skeys() method""" s = SCons.Scanner.Base(function = self.func) sk = s.get_skeys() - self.failUnless(sk == [], + self.assertTrue(sk == [], "did not initialize to expected []") s = SCons.Scanner.Base(function = self.func, skeys = ['.1', '.2']) sk = s.get_skeys() - self.failUnless(sk == ['.1', '.2'], + self.assertTrue(sk == ['.1', '.2'], "sk was %s, not ['.1', '.2']") s = SCons.Scanner.Base(function = self.func, skeys = '$LIST') env = DummyEnvironment(LIST = ['.3', '.4']) sk = s.get_skeys(env) - self.failUnless(sk == ['.3', '.4'], + self.assertTrue(sk == ['.3', '.4'], "sk was %s, not ['.3', '.4']") def test_select(self): @@ -432,19 +432,19 @@ class CurrentTestCase(unittest.TestCase): path = s.path(env) hnb = HasNoBuilder() s(hnb, env, path) - self.failUnless(hnb.called_has_builder, "did not call has_builder()") - self.failUnless(not hnb.called_is_up_to_date, "did call is_up_to_date()") - self.failUnless(hnb.func_called, "did not call func()") + self.assertTrue(hnb.called_has_builder, "did not call has_builder()") + self.assertTrue(not hnb.called_is_up_to_date, "did call is_up_to_date()") + self.assertTrue(hnb.func_called, "did not call func()") inc = IsNotCurrent() s(inc, env, path) - self.failUnless(inc.called_has_builder, "did not call has_builder()") - self.failUnless(inc.called_is_up_to_date, "did not call is_up_to_date()") - self.failUnless(not inc.func_called, "did call func()") + self.assertTrue(inc.called_has_builder, "did not call has_builder()") + self.assertTrue(inc.called_is_up_to_date, "did not call is_up_to_date()") + self.assertTrue(not inc.func_called, "did call func()") ic = IsCurrent() s(ic, env, path) - self.failUnless(ic.called_has_builder, "did not call has_builder()") - self.failUnless(ic.called_is_up_to_date, "did not call is_up_to_date()") - self.failUnless(ic.func_called, "did not call func()") + self.assertTrue(ic.called_has_builder, "did not call has_builder()") + self.assertTrue(ic.called_is_up_to_date, "did not call is_up_to_date()") + self.assertTrue(ic.func_called, "did not call func()") class ClassicTestCase(unittest.TestCase): @@ -566,7 +566,7 @@ class ClassicTestCase(unittest.TestCase): s = SCons.Scanner.Classic("Test", [], None, "", function=self.func, recursive=1) n = s.recurse_nodes(nodes) - self.failUnless(n == n, + self.assertTrue(n == n, "recursive = 1 didn't return all nodes: %s" % n) def odd_only(nodes): @@ -574,7 +574,7 @@ class ClassicTestCase(unittest.TestCase): s = SCons.Scanner.Classic("Test", [], None, "", function=self.func, recursive=odd_only) n = s.recurse_nodes(nodes) - self.failUnless(n == [1, 3], + self.assertTrue(n == [1, 3], "recursive = 1 didn't return all nodes: %s" % n) -- cgit v0.12 From 98c03d901cb4b9aa6ee80ab13888477a4cc3637e Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Sep 2018 17:16:34 -0700 Subject: Change so DefaultEnvironment and Enviroment don't initialize any tools to speed up test on windows --- test/Depends/Depends.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Depends/Depends.py b/test/Depends/Depends.py index 6804fd0..054b9a1 100644 --- a/test/Depends/Depends.py +++ b/test/Depends/Depends.py @@ -51,9 +51,10 @@ SUBDIR_foo_dep = os.path.join('$SUBDIR', 'foo.dep') SUBDIR_f3_out = os.path.join('$SUBDIR', 'f3.out') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) Foo = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES subdir/foo.dep') Bar = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES subdir/bar.dep') -env = Environment(BUILDERS = { 'Foo' : Foo, 'Bar' : Bar }, SUBDIR='subdir') +env = Environment(tools=[], BUILDERS = { 'Foo' : Foo, 'Bar' : Bar }, SUBDIR='subdir') env.Depends(target = ['f1.out', 'f2.out'], dependency = r'%(SUBDIR_foo_dep)s') env.Depends(target = r'%(SUBDIR_f3_out)s', dependency = 'subdir/bar.dep') env.Foo(target = 'f1.out', source = 'f1.in') -- cgit v0.12 From e3a56e698a663c309d42d56e1064077d4c5de602 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Sep 2018 19:57:44 -0700 Subject: More changes to speed up testing on windows. --- test/Builder/TargetSubst.py | 3 ++- test/Builder/add_src_builder.py | 3 ++- test/Builder/different-actions.py | 5 +++-- test/Builder/ensure_suffix.py | 3 ++- test/Builder/multi/different-actions.py | 3 ++- test/Builder/multi/different-environments.py | 3 ++- test/Builder/multi/different-multi.py | 4 +++- test/Builder/multi/different-order.py | 3 ++- test/Builder/multi/different-overrides.py | 3 ++- test/Builder/multi/different-target-lists.py | 3 ++- test/Builder/multi/error.py | 4 +++- test/Builder/multi/lone-target-list.py | 4 +++- test/Builder/multi/multi.py | 4 +++- test/Builder/multi/same-actions.py | 4 +++- test/Builder/multi/same-overrides.py | 4 +++- test/Builder/multi/same-targets.py | 4 +++- test/Builder/non-multi.py | 4 +++- test/Builder/same-actions-diff-envs.py | 6 ++++-- test/Builder/same-actions-diff-overrides.py | 4 +++- test/Builder/srcdir.py | 2 ++ test/Builder/wrapper.py | 5 ++++- test/CacheDir/CacheDir.py | 3 ++- test/CacheDir/NoCache.py | 1 + test/CacheDir/SideEffect.py | 3 ++- test/CacheDir/VariantDir.py | 5 +++-- test/CacheDir/debug.py | 3 ++- test/CacheDir/environment.py | 3 ++- test/CacheDir/multi-targets.py | 3 ++- test/CacheDir/multiple-targets.py | 3 ++- test/CacheDir/option--cd.py | 3 ++- test/CacheDir/option--cf.py | 2 +- test/CacheDir/option--cr.py | 3 ++- test/CacheDir/option--cs.py | 4 +++- test/CacheDir/readonly-cache.py | 1 + test/CacheDir/scanner-target.py | 1 + test/CacheDir/source-scanner.py | 1 + test/CacheDir/timestamp-match.py | 1 + test/CacheDir/timestamp-newer.py | 1 + test/Decider/Environment.py | 3 ++- test/Decider/MD5-timestamp.py | 3 ++- test/Decider/Node.py | 1 + test/Decider/default.py | 1 + test/Decider/mixed.py | 5 +++-- test/Decider/switch-rebuild.py | 3 ++- test/Decider/timestamp.py | 5 +++-- test/Decider/unknown.py | 1 + test/Depends/no-Builder.py | 3 ++- test/Dir/Dir.py | 6 ++++-- test/Dir/PyPackageDir/image/SConstruct | 8 +++++--- test/Dir/mixed-targets.py | 3 ++- test/Dir/source.py | 3 ++- 51 files changed, 115 insertions(+), 49 deletions(-) diff --git a/test/Builder/TargetSubst.py b/test/Builder/TargetSubst.py index 7d0438c..76ca76c 100644 --- a/test/Builder/TargetSubst.py +++ b/test/Builder/TargetSubst.py @@ -35,7 +35,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) builder = Builder(action=Copy('$TARGET', '$SOURCE')) tgt = builder(env, target="${SOURCE}.out", source="infile") """) diff --git a/test/Builder/add_src_builder.py b/test/Builder/add_src_builder.py index d5b13c1..e499933 100644 --- a/test/Builder/add_src_builder.py +++ b/test/Builder/add_src_builder.py @@ -38,6 +38,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) copy_out = Builder(action = Copy('$TARGET', '$SOURCE'), suffix = '.out', src_suffix = '.mid') @@ -46,7 +47,7 @@ copy_mid = Builder(action = Copy('$TARGET', '$SOURCE'), suffix = '.mid', \ src_suffix = '.in') -env = Environment() +env = Environment(tools=[]) env['BUILDERS']['CopyOut'] = copy_out env['BUILDERS']['CopyMid'] = copy_mid diff --git a/test/Builder/different-actions.py b/test/Builder/different-actions.py index f98db93..f355586 100644 --- a/test/Builder/different-actions.py +++ b/test/Builder/different-actions.py @@ -34,8 +34,9 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ -e1 = Environment() -e2 = Environment() +DefaultEnvironment(tools=[]) +e1 = Environment(tools=[]) +e2 = Environment(tools=[]) e1.Command('out.txt', [], 'echo 1 > $TARGET') e2.Command('out.txt', [], 'echo 2 > $TARGET') diff --git a/test/Builder/ensure_suffix.py b/test/Builder/ensure_suffix.py index 4515ff6..52fb1d4 100644 --- a/test/Builder/ensure_suffix.py +++ b/test/Builder/ensure_suffix.py @@ -35,7 +35,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) tbuilder = Builder(action=Copy('$TARGET', '$SOURCE'), suffix='.dll', diff --git a/test/Builder/multi/different-actions.py b/test/Builder/multi/different-actions.py index 66b1e8e..30e98f8 100644 --- a/test/Builder/multi/different-actions.py +++ b/test/Builder/multi/different-actions.py @@ -34,13 +34,14 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: file.write(open(str(s), 'rb').read()) B = Builder(action=Action(build, varlist=['XXX']), multi=1) -env = Environment(BUILDERS = { 'B' : B }, XXX = 'foo') +env = Environment(tools=[], BUILDERS = { 'B' : B }, XXX = 'foo') env2 = env.Clone(XXX = 'var') env.B(target = 'file6.out', source = 'file6a.in') env2.B(target = 'file6.out', source = 'file6b.in') diff --git a/test/Builder/multi/different-environments.py b/test/Builder/multi/different-environments.py index c3e96c2..686e15c 100644 --- a/test/Builder/multi/different-environments.py +++ b/test/Builder/multi/different-environments.py @@ -47,8 +47,9 @@ build(sys.argv[1],sys.argv[2],sys.argv[3:]) """) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) B = Builder(action=r'%(_python_)s build.py $foo $TARGET $SOURCES', multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = 'file03.out', source = 'file03a.in', foo=1) env.B(target = 'file03.out', source = 'file03b.in', foo=2) """ % locals()) diff --git a/test/Builder/multi/different-multi.py b/test/Builder/multi/different-multi.py index dce235e..28002bd 100644 --- a/test/Builder/multi/different-multi.py +++ b/test/Builder/multi/different-multi.py @@ -34,6 +34,8 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: @@ -47,7 +49,7 @@ def build2(env, target, source): # or how we mess with the Builder internals. B = Builder(action=build, multi=1, name='B') C = Builder(action=build2, multi=1, name='C') -env = Environment(BUILDERS = { 'B' : B, 'C' : C }) +env = Environment(tools=[], BUILDERS = { 'B' : B, 'C' : C }) env.B(target = 'file8.out', source = 'file8.in') env.C(target = 'file8.out', source = 'file8.in') """) diff --git a/test/Builder/multi/different-order.py b/test/Builder/multi/different-order.py index 99a19c2..c423969 100644 --- a/test/Builder/multi/different-order.py +++ b/test/Builder/multi/different-order.py @@ -36,6 +36,7 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def build(env, target, source): for t in target: file = open(str(target[0]), 'wb') @@ -43,7 +44,7 @@ def build(env, target, source): file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = ['file10a.out', 'file10b.out'], source = 'file10.in') env.B(target = ['file10b.out', 'file10a.out'], source = 'file10.in') """) diff --git a/test/Builder/multi/different-overrides.py b/test/Builder/multi/different-overrides.py index 8eb3e13..6a38a93 100644 --- a/test/Builder/multi/different-overrides.py +++ b/test/Builder/multi/different-overrides.py @@ -34,13 +34,14 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = 'file3.out', source = 'file3a.in', foo=1) env.B(target = 'file3.out', source = 'file3b.in', foo=2) """) diff --git a/test/Builder/multi/different-target-lists.py b/test/Builder/multi/different-target-lists.py index 39e388c..437311f 100644 --- a/test/Builder/multi/different-target-lists.py +++ b/test/Builder/multi/different-target-lists.py @@ -40,6 +40,7 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def build(env, target, source): for t in target: file = open(str(target[0]), 'wb') @@ -47,7 +48,7 @@ def build(env, target, source): file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = ['file11a.out', 'file11b.out'], source = 'file11a.in') env.B(target = ['file11b.out', 'file11c.out'], source = 'file11b.in') """) diff --git a/test/Builder/multi/error.py b/test/Builder/multi/error.py index 37a012b..2de23d3 100644 --- a/test/Builder/multi/error.py +++ b/test/Builder/multi/error.py @@ -34,13 +34,15 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=0) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = 'file2.out', source = 'file2a.in') env.B(target = 'file2.out', source = 'file2b.in') """) diff --git a/test/Builder/multi/lone-target-list.py b/test/Builder/multi/lone-target-list.py index 40e7dc9..7c02c4a 100644 --- a/test/Builder/multi/lone-target-list.py +++ b/test/Builder/multi/lone-target-list.py @@ -33,6 +33,8 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + def build(env, target, source): for t in target: file = open(str(target[0]), 'wb') @@ -40,7 +42,7 @@ def build(env, target, source): file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = ['file12a.out', 'file12b.out'], source = 'file12a.in') env.B(target = 'file12a.out', source = 'file12b.in') """) diff --git a/test/Builder/multi/multi.py b/test/Builder/multi/multi.py index b2ceae9..0f83d71 100644 --- a/test/Builder/multi/multi.py +++ b/test/Builder/multi/multi.py @@ -34,13 +34,15 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = 'file1.out', source = 'file1a.in') env.B(target = 'file1.out', source = 'file1b.in') """) diff --git a/test/Builder/multi/same-actions.py b/test/Builder/multi/same-actions.py index 5695fa3..0b75566 100644 --- a/test/Builder/multi/same-actions.py +++ b/test/Builder/multi/same-actions.py @@ -34,13 +34,15 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env2 = env.Clone(DIFFERENT_VARIABLE = 'true') env.B(target = 'file5.out', source = 'file5a.in') env2.B(target = 'file5.out', source = 'file5b.in') diff --git a/test/Builder/multi/same-overrides.py b/test/Builder/multi/same-overrides.py index 95c0759..e51b2ef 100644 --- a/test/Builder/multi/same-overrides.py +++ b/test/Builder/multi/same-overrides.py @@ -45,8 +45,10 @@ build(sys.argv[1],sys.argv[2],sys.argv[3:]) """) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + B = Builder(action=r'%(_python_)s build.py $foo $TARGET $SOURCES', multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = 'file4.out', source = 'file4a.in', foo=3) env.B(target = 'file4.out', source = 'file4b.in', foo=3) """ % locals()) diff --git a/test/Builder/multi/same-targets.py b/test/Builder/multi/same-targets.py index 714b9da..c800a1c 100644 --- a/test/Builder/multi/same-targets.py +++ b/test/Builder/multi/same-targets.py @@ -34,6 +34,8 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + def build(env, target, source): for t in target: file = open(str(t), 'wb') @@ -41,7 +43,7 @@ def build(env, target, source): file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = ['file9a.out', 'file9b.out'], source = 'file9a.in') env.B(target = ['file9a.out', 'file9b.out'], source = 'file9b.in') """) diff --git a/test/Builder/non-multi.py b/test/Builder/non-multi.py index baf0ed0..0ddb038 100644 --- a/test/Builder/non-multi.py +++ b/test/Builder/non-multi.py @@ -34,13 +34,15 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: file.write(open(str(s), 'rb').read()) B = Builder(action=build, multi=0) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = 'file7.out', source = 'file7.in') env.B(target = 'file7.out', source = 'file7.in') env.B(target = 'file8.out', source = 'file8.in', arg=1) diff --git a/test/Builder/same-actions-diff-envs.py b/test/Builder/same-actions-diff-envs.py index b111737..289ee09 100644 --- a/test/Builder/same-actions-diff-envs.py +++ b/test/Builder/same-actions-diff-envs.py @@ -34,13 +34,15 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + def build(env, target, source): file = open(str(target[0]), 'w') file.write('1') B = Builder(action=build) -env = Environment(BUILDERS = { 'B' : B }) -env2 = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env2 = Environment(tools=[], BUILDERS = { 'B' : B }) env.B('out.txt', []) env2.B('out.txt', []) """) diff --git a/test/Builder/same-actions-diff-overrides.py b/test/Builder/same-actions-diff-overrides.py index dde7dd9..2b7cefe 100644 --- a/test/Builder/same-actions-diff-overrides.py +++ b/test/Builder/same-actions-diff-overrides.py @@ -34,12 +34,14 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + def build(env, target, source): file = open(str(target[0]), 'w') file.write('1') B = Builder(action=build) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B('out.txt', [], arg=1) env.B('out.txt', [], arg=2) """) diff --git a/test/Builder/srcdir.py b/test/Builder/srcdir.py index 669c5e1..d7a9e18 100644 --- a/test/Builder/srcdir.py +++ b/test/Builder/srcdir.py @@ -48,6 +48,8 @@ o.close() """) test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) + Command('output', ['file1', File('file2'), r'%(file3)s', 'file4'], r'%(_python_)s cat.py $TARGET $SOURCES', diff --git a/test/Builder/wrapper.py b/test/Builder/wrapper.py index ae62846..3ee9f79 100644 --- a/test/Builder/wrapper.py +++ b/test/Builder/wrapper.py @@ -34,6 +34,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + import os.path import string def cat(target, source, env): @@ -49,7 +51,8 @@ def Wrapper(env, target, source): env.Cat(t1, source) t2 = 't2-'+str(target[0]) env.Cat(t2, source) -env = Environment(BUILDERS = {'Cat' : Cat, +env = Environment(tools=[], + BUILDERS = {'Cat' : Cat, 'Wrapper' : Wrapper}) env.Wrapper('f1.out', 'f1.in') env.Wrapper('f2.in') diff --git a/test/CacheDir/CacheDir.py b/test/CacheDir/CacheDir.py index 45c5db5..3d2c3b5 100644 --- a/test/CacheDir/CacheDir.py +++ b/test/CacheDir/CacheDir.py @@ -45,6 +45,7 @@ src_all = test.workpath('src', 'all') test.subdir('cache', 'src') test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) CacheDir(r'%(cache)s') SConscript('SConscript') """ % locals()) @@ -57,7 +58,7 @@ def cat(env, source, target): for src in source: f.write(open(str(src), "r").read()) f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') env.Cat('bbb.out', 'bbb.in') env.Cat('ccc.out', 'ccc.in') diff --git a/test/CacheDir/NoCache.py b/test/CacheDir/NoCache.py index b035b44..f0929aa 100644 --- a/test/CacheDir/NoCache.py +++ b/test/CacheDir/NoCache.py @@ -35,6 +35,7 @@ test = TestSCons.TestSCons() test.subdir('cache', 'alpha', 'beta') sconstruct = """ +DefaultEnvironment(tools=[]) import os CacheDir(r'%s') diff --git a/test/CacheDir/SideEffect.py b/test/CacheDir/SideEffect.py index 2ddba31..4eae4c3 100644 --- a/test/CacheDir/SideEffect.py +++ b/test/CacheDir/SideEffect.py @@ -37,6 +37,7 @@ test.subdir('cache', 'work') cache = test.workpath('cache') test.write(['work', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) def copy(source, target): open(target, "w").write(open(source, "r").read()) @@ -51,7 +52,7 @@ def build(env, source, target): CacheDir(r'%(cache)s') Build = Builder(action=build) -env = Environment(BUILDERS={'Build':Build}, SUBDIR='subdir') +env = Environment(tools=[], BUILDERS={'Build':Build}, SUBDIR='subdir') env.Build('f1.out', 'f1.in') env.Build('f2.out', 'f2.in') env.Build('f3.out', 'f3.in') diff --git a/test/CacheDir/VariantDir.py b/test/CacheDir/VariantDir.py index d31b9ed..58918be 100644 --- a/test/CacheDir/VariantDir.py +++ b/test/CacheDir/VariantDir.py @@ -47,7 +47,7 @@ def cat(env, source, target): for src in source: f.write(open(str(src), "r").read()) f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') env.Cat('bbb.out', 'bbb.in') env.Cat('ccc.out', 'ccc.in') @@ -65,7 +65,8 @@ test.write(['src', 'ccc.in'], "ccc.in\n") # test.write('SConstruct', """\ -env = Environment(TWO = '2') +DefaultEnvironment(tools=[]) +env = Environment(tools=[], TWO = '2') CacheDir(r'%s') VariantDir('build', 'src', duplicate=0) SConscript('build/SConscript') diff --git a/test/CacheDir/debug.py b/test/CacheDir/debug.py index e3186b0..7e08e0b 100644 --- a/test/CacheDir/debug.py +++ b/test/CacheDir/debug.py @@ -45,6 +45,7 @@ debug_out = test.workpath('cache-debug.out') test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) CacheDir(r'%(cache)s') SConscript('SConscript') """ % locals()) @@ -57,7 +58,7 @@ def cat(env, source, target): for src in source: f.write(open(str(src), "r").read()) f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') env.Cat('bbb.out', 'bbb.in') env.Cat('ccc.out', 'ccc.in') diff --git a/test/CacheDir/environment.py b/test/CacheDir/environment.py index e37b999..5d8eb6c 100644 --- a/test/CacheDir/environment.py +++ b/test/CacheDir/environment.py @@ -46,6 +46,7 @@ src_all = test.workpath('src', 'all') test.subdir('cache', 'src') test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) CacheDir(r'%(cache)s') SConscript('SConscript') """ % locals()) @@ -58,7 +59,7 @@ def cat(env, source, target): for src in source: f.write(open(str(src), "r").read()) f.close() -env_cache = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env_cache = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env_nocache = env_cache.Clone() env_nocache.CacheDir(None) env_cache.Cat('aaa.out', 'aaa.in') diff --git a/test/CacheDir/multi-targets.py b/test/CacheDir/multi-targets.py index 9ca194e..7977ba0 100644 --- a/test/CacheDir/multi-targets.py +++ b/test/CacheDir/multi-targets.py @@ -40,11 +40,12 @@ multiple_bar = test.workpath('multiple', 'bar') multiple_foo = test.workpath('multiple', 'foo') test.write(['multiple', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) def touch(env, source, target): open('foo', 'w').write("") open('bar', 'w').write("") CacheDir(r'%(cache)s') -env = Environment() +env = Environment(tools=[]) env.Command(['foo', 'bar'], ['input'], touch) """ % locals()) diff --git a/test/CacheDir/multiple-targets.py b/test/CacheDir/multiple-targets.py index d7e6ac7..9f94e4c 100644 --- a/test/CacheDir/multiple-targets.py +++ b/test/CacheDir/multiple-targets.py @@ -38,11 +38,12 @@ test = TestSCons.TestSCons() test.subdir('cache') test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def touch(env, source, target): open('foo', 'w').write("") open('bar', 'w').write("") CacheDir(r'%s') -env = Environment() +env = Environment(tools=[], ) env.Command(['foo', 'bar'], ['input'], touch) """ % (test.workpath('cache'))) diff --git a/test/CacheDir/option--cd.py b/test/CacheDir/option--cd.py index fad5add..20d1184 100644 --- a/test/CacheDir/option--cd.py +++ b/test/CacheDir/option--cd.py @@ -39,6 +39,7 @@ test = TestSCons.TestSCons() test.subdir('cache', 'src') test.write(['src', 'SConstruct'], """ +DefaultEnvironment(tools=[]) def cat(env, source, target): target = str(target[0]) open('cat.out', 'a').write(target + "\\n") @@ -46,7 +47,7 @@ def cat(env, source, target): for src in source: f.write(open(str(src), "r").read()) f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') env.Cat('bbb.out', 'bbb.in') env.Cat('ccc.out', 'ccc.in') diff --git a/test/CacheDir/option--cf.py b/test/CacheDir/option--cf.py index bb9d1cc..5e823ae 100644 --- a/test/CacheDir/option--cf.py +++ b/test/CacheDir/option--cf.py @@ -46,7 +46,7 @@ def cat(env, source, target): for src in source: f.write(open(str(src), "r").read()) f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') env.Cat('bbb.out', 'bbb.in') env.Cat('ccc.out', 'ccc.in') diff --git a/test/CacheDir/option--cr.py b/test/CacheDir/option--cr.py index 792dede..4ed587c 100644 --- a/test/CacheDir/option--cr.py +++ b/test/CacheDir/option--cr.py @@ -39,6 +39,7 @@ test = TestSCons.TestSCons() test.subdir('cache', 'src') test.write(['src', 'SConstruct'], """ +DefaultEnvironment(tools=[]) def cat(env, source, target): target = str(target[0]) open('cat.out', 'a').write(target + "\\n") @@ -46,7 +47,7 @@ def cat(env, source, target): for src in source: f.write(open(str(src), "r").read()) f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') env.Cat('bbb.out', 'bbb.in') env.Cat('ccc.out', 'ccc.in') diff --git a/test/CacheDir/option--cs.py b/test/CacheDir/option--cs.py index 2e37e5a..b73fb70 100644 --- a/test/CacheDir/option--cs.py +++ b/test/CacheDir/option--cs.py @@ -55,6 +55,7 @@ file.close() cache = test.workpath('cache') test.write(['src1', 'SConstruct'], """ +DefaultEnvironment(tools=[]) def cat(env, source, target): target = str(target[0]) open('cat.out', 'a').write(target + "\\n") @@ -62,7 +63,8 @@ def cat(env, source, target): for src in source: f.write(open(str(src), "r").read()) f.close() -env = Environment(BUILDERS={'Internal':Builder(action=cat), +env = Environment(tools=[], + BUILDERS={'Internal':Builder(action=cat), 'External':Builder(action=r'%(_python_)s build.py $TARGET $SOURCES')}) env.External('aaa.out', 'aaa.in') env.External('bbb.out', 'bbb.in') diff --git a/test/CacheDir/readonly-cache.py b/test/CacheDir/readonly-cache.py index db3b3ec..6520106 100755 --- a/test/CacheDir/readonly-cache.py +++ b/test/CacheDir/readonly-cache.py @@ -37,6 +37,7 @@ from stat import * test = TestSCons.TestSCons() test.write(['SConstruct'], """\ +DefaultEnvironment(tools=[]) CacheDir('cache') Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) """) diff --git a/test/CacheDir/scanner-target.py b/test/CacheDir/scanner-target.py index c39042e..7df9792 100644 --- a/test/CacheDir/scanner-target.py +++ b/test/CacheDir/scanner-target.py @@ -41,6 +41,7 @@ test = TestSCons.TestSCons() test.subdir('cache') test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) import SCons CacheDir(r'%s') diff --git a/test/CacheDir/source-scanner.py b/test/CacheDir/source-scanner.py index 2359872..f00360d 100644 --- a/test/CacheDir/source-scanner.py +++ b/test/CacheDir/source-scanner.py @@ -43,6 +43,7 @@ cache = test.workpath('cache') test.subdir('cache', 'subdir') test.write(['subdir', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) import SCons CacheDir(r'%(cache)s') diff --git a/test/CacheDir/timestamp-match.py b/test/CacheDir/timestamp-match.py index afc3f63..4b64137 100644 --- a/test/CacheDir/timestamp-match.py +++ b/test/CacheDir/timestamp-match.py @@ -33,6 +33,7 @@ import TestSCons test = TestSCons.TestSCons() test.write(['SConstruct'], """\ +DefaultEnvironment(tools=[]) Decider('timestamp-match') CacheDir('cache') Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) diff --git a/test/CacheDir/timestamp-newer.py b/test/CacheDir/timestamp-newer.py index 8a47fec..618f467 100644 --- a/test/CacheDir/timestamp-newer.py +++ b/test/CacheDir/timestamp-newer.py @@ -33,6 +33,7 @@ import TestSCons test = TestSCons.TestSCons() test.write(['SConstruct'], """\ +DefaultEnvironment(tools=[]) Decider('timestamp-newer') CacheDir('cache') Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) diff --git a/test/Decider/Environment.py b/test/Decider/Environment.py index 7c609ee..58cd57b 100644 --- a/test/Decider/Environment.py +++ b/test/Decider/Environment.py @@ -34,8 +34,9 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) import os.path -env = Environment() +env = Environment(tools=[]) env.Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) def my_decider(dependency, target, prev_ni): return os.path.exists('has-changed') diff --git a/test/Decider/MD5-timestamp.py b/test/Decider/MD5-timestamp.py index f8776c3..6fcdb42 100644 --- a/test/Decider/MD5-timestamp.py +++ b/test/Decider/MD5-timestamp.py @@ -36,7 +36,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -m = Environment() +DefaultEnvironment(tools=[]) +m = Environment(tools=[]) m.Decider('MD5-timestamp') m.Command('content1.out', 'content1.in', Copy('$TARGET', '$SOURCE')) m.Command('content2.out', 'content2.in', Copy('$TARGET', '$SOURCE')) diff --git a/test/Decider/Node.py b/test/Decider/Node.py index cdd3e35..c1910de 100644 --- a/test/Decider/Node.py +++ b/test/Decider/Node.py @@ -33,6 +33,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) import os.path file_in = File('file.in') file_out = File('file.out') diff --git a/test/Decider/default.py b/test/Decider/default.py index f05e869..5d0a452 100644 --- a/test/Decider/default.py +++ b/test/Decider/default.py @@ -33,6 +33,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) import os.path Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) def my_decider(dependency, target, prev_ni): diff --git a/test/Decider/mixed.py b/test/Decider/mixed.py index 5598468..08daa7d 100644 --- a/test/Decider/mixed.py +++ b/test/Decider/mixed.py @@ -34,9 +34,10 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) import os.path -denv = Environment() -env = Environment() +denv = Environment(tools=[]) +env = Environment(tools=[]) n1_in = File('n1.in') n2_in = File('n2.in') n3_in = File('n3.in') diff --git a/test/Decider/switch-rebuild.py b/test/Decider/switch-rebuild.py index 45becbe..d2b288d 100644 --- a/test/Decider/switch-rebuild.py +++ b/test/Decider/switch-rebuild.py @@ -34,12 +34,13 @@ import TestSCons test = TestSCons.TestSCons(match=TestSCons.match_re_dotall) base_sconstruct_contents = """\ +DefaultEnvironment(tools=[]) Decider('%s') def build(env, target, source): open(str(target[0]), 'wt').write(open(str(source[0]), 'rt').read()) B = Builder(action=build) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target='switch.out', source='switch.in') """ diff --git a/test/Decider/timestamp.py b/test/Decider/timestamp.py index 8389745..e528d77 100644 --- a/test/Decider/timestamp.py +++ b/test/Decider/timestamp.py @@ -37,11 +37,12 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -m = Environment() +DefaultEnvironment(tools=[]) +m = Environment(tools=[]) m.Decider('timestamp-match') m.Command('match1.out', 'match1.in', Copy('$TARGET', '$SOURCE')) m.Command('match2.out', 'match2.in', Copy('$TARGET', '$SOURCE')) -n = Environment() +n = Environment(tools=[]) n.Decider('timestamp-newer') n.Command('newer1.out', 'newer1.in', Copy('$TARGET', '$SOURCE')) n.Command('newer2.out', 'newer2.in', Copy('$TARGET', '$SOURCE')) diff --git a/test/Decider/unknown.py b/test/Decider/unknown.py index ec24eff..9f79da3 100644 --- a/test/Decider/unknown.py +++ b/test/Decider/unknown.py @@ -34,6 +34,7 @@ import TestSCons test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) Decider('fiddle-dee-dee') """) diff --git a/test/Depends/no-Builder.py b/test/Depends/no-Builder.py index f55fcf9..7e04bc9 100644 --- a/test/Depends/no-Builder.py +++ b/test/Depends/no-Builder.py @@ -34,7 +34,8 @@ test = TestSCons.TestSCons() # test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) file1 = File('file1') file2 = File('file2') env.Depends(file1, [[file2, 'file3']]) diff --git a/test/Dir/Dir.py b/test/Dir/Dir.py index 2e8204c..e726b94 100644 --- a/test/Dir/Dir.py +++ b/test/Dir/Dir.py @@ -35,7 +35,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ -env = Environment(FOO = 'fff', BAR = 'bbb') +DefaultEnvironment(tools=[]) +env = Environment(tools=[], FOO = 'fff', BAR = 'bbb') print(Dir('ddd')) print(Dir('$FOO')) print(Dir('${BAR}_$BAR')) @@ -58,12 +59,13 @@ scons: `.' is up to date. test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) import os def my_mkdir(target=None, source=None, env=None): os.mkdir(str(target[0])) MDBuilder = Builder(action=my_mkdir, target_factory=Dir) -env = Environment() +env = Environment(tools=[]) env.Append(BUILDERS = {'MD':MDBuilder}) env.MD(target='sub1', source=['SConstruct']) env.MD(target='sub2', source=['SConstruct'], OVERRIDE='foo') diff --git a/test/Dir/PyPackageDir/image/SConstruct b/test/Dir/PyPackageDir/image/SConstruct index 90d2a80..7e841ac 100644 --- a/test/Dir/PyPackageDir/image/SConstruct +++ b/test/Dir/PyPackageDir/image/SConstruct @@ -13,17 +13,19 @@ def TestPyPackageDir(env, modname): relpath = relpath.replace(os.sep, '/') print(relpath) +DefaultEnvironment(tools=[]) + print("Test identification of directory for a given python package") -env = Environment() +env = Environment(tools=[]) TestPyPackageDir(env, 'testmod1') TestPyPackageDir(env, 'testmod2') TestPyPackageDir(env, 'submod1.testmod3') TestPyPackageDir(env, 'submod1.submod2.testmod4') print("Test parameter substitution") -env = Environment(FOO = 'submod1.submod2.testmod4') +env = Environment(tools=[], FOO = 'submod1.submod2.testmod4') TestPyPackageDir(env, '${FOO}') -env = Environment(FOO = 'submod1.submod2', BAR = 'testmod4') +env = Environment(tools=[], FOO = 'submod1.submod2', BAR = 'testmod4') TestPyPackageDir(env, '${FOO}.${BAR}') sys.path = oldsyspath diff --git a/test/Dir/mixed-targets.py b/test/Dir/mixed-targets.py index 9702e40..3389746 100644 --- a/test/Dir/mixed-targets.py +++ b/test/Dir/mixed-targets.py @@ -46,12 +46,13 @@ def copier(target, source, env): shutil.copytree(str(source[0]), 'build') return 0 +DefaultEnvironment(tools=[]) Copier = Builder(action = copier, target_scanner = SCons.Defaults.DirEntryScanner, target_factory = Entry, source_factory = Entry) -env = Environment(BUILDERS = {'Copier': Copier}) +env = Environment(tools=[], BUILDERS = {'Copier': Copier}) env.Copier(['build/dir', 'build/file1'], ['src']) """) diff --git a/test/Dir/source.py b/test/Dir/source.py index c272c8f..5fe917d 100644 --- a/test/Dir/source.py +++ b/test/Dir/source.py @@ -42,6 +42,7 @@ test.subdir('tstamp', [ 'tstamp', 'subdir' ], 'cmd-content', [ 'cmd-content', 'subdir' ]) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def writeTarget(target, source, env): f=open(str(target[0]), 'w') f.write("stuff\\n") @@ -52,7 +53,7 @@ test_bld_dir = Builder(action=writeTarget, source_factory=Dir, source_scanner=DirScanner) test_bld_file = Builder(action=writeTarget) -env = Environment() +env = Environment(tools=[]) env['BUILDERS']['TestDir'] = test_bld_dir env['BUILDERS']['TestFile'] = test_bld_file -- cgit v0.12 From 0196c818e85949922479a437717b15f710173cef Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Sep 2018 20:10:22 -0700 Subject: Speedup Clean tests for windows --- test/Clean/Option.py | 3 ++- test/Clean/basic.py | 3 ++- test/Clean/function.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/Clean/Option.py b/test/Clean/Option.py index 6264428..f49c226 100644 --- a/test/Clean/Option.py +++ b/test/Clean/Option.py @@ -46,8 +46,9 @@ file.close() """) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = 'foo.out', source = 'foo.in') mode = ARGUMENTS.get('MODE') diff --git a/test/Clean/basic.py b/test/Clean/basic.py index 7c4730e..e9f0540 100644 --- a/test/Clean/basic.py +++ b/test/Clean/basic.py @@ -45,8 +45,9 @@ file.close() """) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(target = 'foo1.out', source = 'foo1.in') env.B(target = 'foo2.out', source = 'foo2.xxx') env.B(target = 'foo2.xxx', source = 'foo2.in') diff --git a/test/Clean/function.py b/test/Clean/function.py index aa53a35..7ddf727 100644 --- a/test/Clean/function.py +++ b/test/Clean/function.py @@ -51,8 +51,9 @@ subd_foon_in = os.path.join('subd', 'foon.in') subd_foox_in = os.path.join('subd', 'foox.in') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') -env = Environment(BUILDERS = { 'B' : B }, FOO = 'foo2') +env = Environment(tools=[], BUILDERS = { 'B' : B }, FOO = 'foo2') env.B(target = 'foo1.out', source = 'foo1.in') env.B(target = 'foo2.out', source = 'foo2.xxx') foo2_xxx = env.B(target = 'foo2.xxx', source = 'foo2.in') -- cgit v0.12 From e3155a940ade6b04427c9dd35a6aca213a65c5c9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Sep 2018 20:31:48 -0700 Subject: Speed up Climb tests on windows --- test/Climb/explicit-parent--D.py | 3 ++- test/Climb/filename--D.py | 1 + test/Climb/filename--U.py | 1 + test/Climb/filename-u.py | 1 + test/Climb/option--D.py | 3 ++- test/Climb/option--U.py | 5 +++-- test/Climb/option-u.py | 3 ++- 7 files changed, 12 insertions(+), 5 deletions(-) diff --git a/test/Climb/explicit-parent--D.py b/test/Climb/explicit-parent--D.py index f894e33..6f669c3 100644 --- a/test/Climb/explicit-parent--D.py +++ b/test/Climb/explicit-parent--D.py @@ -36,13 +36,14 @@ test = TestSCons.TestSCons() test.subdir(['subdir']) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def cat(env, source, target): target = str(target[0]) f = open(target, "wb") for src in source: f.write(open(str(src), "rb").read()) f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('f1.out', 'f1.in') f2 = env.Cat('f2.out', 'f2.in') Default(f2) diff --git a/test/Climb/filename--D.py b/test/Climb/filename--D.py index 6fea9bc..fee72f6 100644 --- a/test/Climb/filename--D.py +++ b/test/Climb/filename--D.py @@ -36,6 +36,7 @@ test = TestSCons.TestSCons() test.subdir('subdir', 'other') test.write('main.scons', """\ +DefaultEnvironment(tools=[]) print("main.scons") SConscript('subdir/sub.scons') """) diff --git a/test/Climb/filename--U.py b/test/Climb/filename--U.py index 49ccc0a..91a83f4 100644 --- a/test/Climb/filename--U.py +++ b/test/Climb/filename--U.py @@ -36,6 +36,7 @@ test = TestSCons.TestSCons() test.subdir('subdir', 'other') test.write('main.scons', """\ +DefaultEnvironment(tools=[]) print("main.scons") SConscript('subdir/sub.scons') """) diff --git a/test/Climb/filename-u.py b/test/Climb/filename-u.py index 4485c4b..006e53e 100644 --- a/test/Climb/filename-u.py +++ b/test/Climb/filename-u.py @@ -36,6 +36,7 @@ test = TestSCons.TestSCons() test.subdir('subdir', 'other') test.write('main.scons', """\ +DefaultEnvironment(tools=[]) print("main.scons") SConscript('subdir/sub.scons') """) diff --git a/test/Climb/option--D.py b/test/Climb/option--D.py index 57c0853..42b92d8 100644 --- a/test/Climb/option--D.py +++ b/test/Climb/option--D.py @@ -41,9 +41,10 @@ file.close() """) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) import SCons.Defaults B = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES') -env = Environment() +env = Environment(tools=[]) env['BUILDERS']['B'] = B env.B(target = 'sub1/foo.out', source = 'sub1/foo.in') Export('env') diff --git a/test/Climb/option--U.py b/test/Climb/option--U.py index 04ab26a..c0e6e1e 100644 --- a/test/Climb/option--U.py +++ b/test/Climb/option--U.py @@ -43,8 +43,9 @@ file.close() """) test.write('SConstruct', r""" +DefaultEnvironment(tools=[]) import SCons.Defaults -env = Environment() +env = Environment(tools=[]) env['BUILDERS']['B'] = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES', multi=1) Default(env.B(target = 'sub1/foo.out', source = 'sub1/foo.in')) Export('env') @@ -83,7 +84,7 @@ test.must_not_exist(test.workpath('sub2/xxx.out')) test.unlink(['sub1', 'foo.out']) test.write('SConscript', """\ -env = Environment() +env = Environment(tools=[], ) assert env.GetLaunchDir() == r'%s' """%test.workpath('sub1')) test.run(arguments = '-U', diff --git a/test/Climb/option-u.py b/test/Climb/option-u.py index eeb2ccc..21e83cf 100644 --- a/test/Climb/option-u.py +++ b/test/Climb/option-u.py @@ -41,13 +41,14 @@ test.subdir('sub1', 'sub4', ['sub4', 'dir']) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def cat(env, source, target): target = str(target[0]) f = open(target, "wb") for src in source: f.write(open(str(src), "rb").read()) f.close() -env = Environment() +env = Environment(tools=[]) env.Append(BUILDERS = {'Cat' : Builder(action=cat)}) env.Cat(target = 'sub1/f1a.out', source = 'sub1/f1a.in') env.Cat(target = 'sub1/f1b.out', source = 'sub1/f1b.in') -- cgit v0.12 From 8811fa184a7781acaaa906d1100c9824272552ee Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Sep 2018 20:36:26 -0700 Subject: Speedup explain tests on windows --- test/explain/alias-order.py | 3 ++- test/explain/basic.py | 3 ++- test/explain/function-actions.py | 3 ++- test/explain/get_csig.py | 3 ++- test/explain/save-info.py | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/explain/alias-order.py b/test/explain/alias-order.py index 611c512..bdb8154 100644 --- a/test/explain/alias-order.py +++ b/test/explain/alias-order.py @@ -38,7 +38,8 @@ args = '--debug=explain target2.dat' test.subdir('src') test.write(['src', 'SConstruct'],""" -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) def action( source, target, env ): f = open( str(target[0]), 'wb' ) diff --git a/test/explain/basic.py b/test/explain/basic.py index ec7238e..19fc328 100644 --- a/test/explain/basic.py +++ b/test/explain/basic.py @@ -81,6 +81,7 @@ sys.exit(0) SConstruct_contents = """\ +DefaultEnvironment(tools=[]) import re include_re = re.compile(r'^include\s+(\S+)$', re.M) @@ -98,7 +99,7 @@ kscan = Scanner(name = 'kfile', cat = Builder(action = [[r'%(python)s', r'%(cat_py)s', '$TARGET', '$SOURCES']]) one_cat = Builder( action = [[r'%(python)s', r'%(cat_py)s', '$TARGET', '${SOURCES[0]}']]) -env = Environment() +env = Environment(tools=[]) env.Append(BUILDERS = {'Cat':cat, 'OneCat':one_cat}, SCANNERS = kscan) env.PrependENVPath('PATHEXT', '.PY') diff --git a/test/explain/function-actions.py b/test/explain/function-actions.py index bd3ad01..076b24e 100644 --- a/test/explain/function-actions.py +++ b/test/explain/function-actions.py @@ -38,9 +38,10 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) import shutil -env = Environment() +env = Environment(tools=[]) mode = int(ARGUMENTS.get('mode')) if mode: def DifferentCopy(target, source, env): diff --git a/test/explain/get_csig.py b/test/explain/get_csig.py index 8a0266a..b193797 100644 --- a/test/explain/get_csig.py +++ b/test/explain/get_csig.py @@ -39,7 +39,8 @@ test = TestSCons.TestSCons() args = "--debug=explain" test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) def action( source, target, env ): target[0].get_csig() diff --git a/test/explain/save-info.py b/test/explain/save-info.py index 048ee8d..08255e0 100644 --- a/test/explain/save-info.py +++ b/test/explain/save-info.py @@ -69,6 +69,7 @@ sys.exit(0) """) test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) import re include_re = re.compile(r'^include\s+(\S+)$', re.M) @@ -85,7 +86,7 @@ kscan = Scanner(name = 'kfile', cat = Builder(action = r'%(_python_)s %(cat_py)s $TARGET $SOURCES') -env = Environment() +env = Environment(tools=[]) env.Append(BUILDERS = {'Cat':cat}, SCANNERS = kscan) -- cgit v0.12 From 8a2ee96a418eb01a53fa4e84d095c276cf058ed9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 09:43:13 -0700 Subject: Speed up GetBuildFailures tests for windows --- test/GetBuildFailures/option-k.py | 1 + test/GetBuildFailures/parallel.py | 1 + test/GetBuildFailures/serial.py | 1 + 3 files changed, 3 insertions(+) diff --git a/test/GetBuildFailures/option-k.py b/test/GetBuildFailures/option-k.py index 12ae07b..28e80c8 100644 --- a/test/GetBuildFailures/option-k.py +++ b/test/GetBuildFailures/option-k.py @@ -57,6 +57,7 @@ test.write('mypass.py', contents) test.write('myfail.py', contents) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) Command('f3', 'f3.in', r'@%(_python_)s mypass.py - f3 $TARGET $SOURCE') Command('f4', 'f4.in', r'@%(_python_)s myfail.py f3 f4 $TARGET $SOURCE') Command('f5', 'f5.in', r'@%(_python_)s myfail.py f4 f5 $TARGET $SOURCE') diff --git a/test/GetBuildFailures/parallel.py b/test/GetBuildFailures/parallel.py index f9503e0..ee8847f 100644 --- a/test/GetBuildFailures/parallel.py +++ b/test/GetBuildFailures/parallel.py @@ -73,6 +73,7 @@ test.write('mypass.py', contents) test.write('myfail.py', contents) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) Command('f3', 'f3.in', r'@%(_python_)s mypass.py - f3 $TARGET $SOURCE') Command('f4', 'f4.in', r'@%(_python_)s myfail.py f3 f4 $TARGET $SOURCE') Command('f5', 'f5.in', r'@%(_python_)s myfail.py f4 f5 $TARGET $SOURCE') diff --git a/test/GetBuildFailures/serial.py b/test/GetBuildFailures/serial.py index 55a990f..144d8bc 100644 --- a/test/GetBuildFailures/serial.py +++ b/test/GetBuildFailures/serial.py @@ -60,6 +60,7 @@ test.write('mypass.py', contents) test.write('myfail.py', contents) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) Command('f03', 'f03.in', r'@%(_python_)s mypass.py - f03 $TARGET $SOURCE') Command('f04', 'f04.in', r'@%(_python_)s myfail.py f03 f04 $TARGET $SOURCE') Command('f05', 'f05.in', r'@%(_python_)s myfail.py f04 f05 $TARGET $SOURCE') -- cgit v0.12 From c0f0caae5890c032a6a82e8b24124b21b3152f99 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 09:44:21 -0700 Subject: Speed up GetOption tests for windows --- test/GetOption/help.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/GetOption/help.py b/test/GetOption/help.py index 4f15fe6..f83dc54 100644 --- a/test/GetOption/help.py +++ b/test/GetOption/help.py @@ -33,6 +33,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) if GetOption('help'): print("GetOption('help') set") else: -- cgit v0.12 From 9ac18d0456d1f7a43596221b83537083f532a823 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 09:51:36 -0700 Subject: Speed up Glob tests for windows --- test/Glob/Repository.py | 3 ++- test/Glob/VariantDir.py | 5 +++-- test/Glob/basic.py | 3 ++- test/Glob/exclude.py | 3 ++- test/Glob/source.py | 3 ++- test/Glob/strings.py | 3 ++- test/Glob/subdir.py | 3 ++- test/Glob/subst.py | 3 ++- 8 files changed, 17 insertions(+), 9 deletions(-) diff --git a/test/Glob/Repository.py b/test/Glob/Repository.py index 22a7f88..3308e62 100644 --- a/test/Glob/Repository.py +++ b/test/Glob/Repository.py @@ -47,6 +47,7 @@ work_src_yyy = test.workpath('work', 'src', 'yyy') opts = "-Y " + test.workpath('repository') test.write(['repository', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) def cat(env, source, target): target = str(target[0]) f = open(target, "wb") @@ -60,7 +61,7 @@ File('../repository/mmm.in') m = Glob('m*.in') assert str(m[0]) == 'mmm.in' -env = Environment(BUILDERS={'Build':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS={'Build':Builder(action=cat)}) env.Build('aaa.out', Glob('a*.in')) env.Build('bbb.out', Glob('b*.in')) env.Build('ccc.out', Glob('c*.in')) diff --git a/test/Glob/VariantDir.py b/test/Glob/VariantDir.py index c9c1d07..3beb9ab 100644 --- a/test/Glob/VariantDir.py +++ b/test/Glob/VariantDir.py @@ -37,6 +37,7 @@ test.subdir('src') test.subdir('src/sub1') test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) VariantDir('var1', 'src') VariantDir('var2', 'src') @@ -48,7 +49,7 @@ SConscript('src/sub1/SConscript', src_dir = 'src', variant_dir = 'var3', duplica """) test.write(['src', 'SConscript'], """\ -env = Environment() +env = Environment(tools=[]) def concatenate(target, source, env): fp = open(str(target[0]), 'wb') @@ -63,7 +64,7 @@ env.Concatenate('fex.out', sorted(Glob('f*.in', exclude = 'f1.in'), key=lambda t """) test.write(['src', 'sub1', 'SConscript'], """\ -env = Environment() +env = Environment(tools=[]) def concatenate(target, source, env): fp = open(str(target[0]), 'wb') diff --git a/test/Glob/basic.py b/test/Glob/basic.py index 9afbbc6..ad998c3 100644 --- a/test/Glob/basic.py +++ b/test/Glob/basic.py @@ -33,7 +33,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): fp = open(str(target[0]), 'wb') diff --git a/test/Glob/exclude.py b/test/Glob/exclude.py index fe93b82..bc3e774 100644 --- a/test/Glob/exclude.py +++ b/test/Glob/exclude.py @@ -36,7 +36,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): fp = open(str(target[0]), 'wb') diff --git a/test/Glob/source.py b/test/Glob/source.py index f1ea566..3d40d05 100644 --- a/test/Glob/source.py +++ b/test/Glob/source.py @@ -37,7 +37,8 @@ test = TestSCons.TestSCons() test.subdir('src', 'var1', 'var2') test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): fp = open(str(target[0]), 'wb') diff --git a/test/Glob/strings.py b/test/Glob/strings.py index 3e47d10..0780ace 100644 --- a/test/Glob/strings.py +++ b/test/Glob/strings.py @@ -37,6 +37,7 @@ test = TestSCons.TestSCons() test.subdir('src') test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) VariantDir('var1', 'src') VariantDir('var2', 'src') @@ -45,7 +46,7 @@ SConscript('var2/SConscript') """) test.write(['src', 'SConscript'], """\ -env = Environment() +env = Environment(tools=[]) def concatenate(target, source, env): fp = open(str(target[0]), 'wb') diff --git a/test/Glob/subdir.py b/test/Glob/subdir.py index 6fc00f6..1227788 100644 --- a/test/Glob/subdir.py +++ b/test/Glob/subdir.py @@ -36,7 +36,8 @@ test = TestSCons.TestSCons() test.subdir('subdir') test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): fp = open(str(target[0]), 'wb') diff --git a/test/Glob/subst.py b/test/Glob/subst.py index e21da81..6a145f1 100644 --- a/test/Glob/subst.py +++ b/test/Glob/subst.py @@ -34,7 +34,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -env = Environment(PATTERN = 'f*.in') +DefaultEnvironment(tools=[]) +env = Environment(tools=[], PATTERN = 'f*.in') def copy(target, source, env): fp = open(str(target[0]), 'wb') -- cgit v0.12 From c9a9dd8ad57e6257728630866a6d42b67e3a3bb8 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 09:53:48 -0700 Subject: Speed up implicit tests for windows --- test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py | 4 +++- test/implicit/asynchronous-modification.py | 3 ++- test/implicit/changed-node.py | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py b/test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py index 2c9fd08..3d05fe3 100644 --- a/test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py +++ b/test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py @@ -64,9 +64,11 @@ extra = '' test.write('generate_build_py.py', generate_build_py_py_contents % locals()) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) generate = Builder(action = r'%(_python_)s $GENERATE $TARGET') build = Builder(action = r'$BUILD_PY $TARGET $SOURCES') -env = Environment(BUILDERS = { +env = Environment(tools=[], + BUILDERS = { 'GenerateBuild' : generate, 'BuildFile' : build, }, diff --git a/test/implicit/asynchronous-modification.py b/test/implicit/asynchronous-modification.py index fac8ef5..90a6392 100644 --- a/test/implicit/asynchronous-modification.py +++ b/test/implicit/asynchronous-modification.py @@ -39,7 +39,8 @@ test = TestSCons.TestSCons() test.write(['SConstruct'], """\ import SCons.Defaults -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) env['BUILDERS']['C'] = Builder(action = Copy('$TARGET', '$SOURCE'), source_scanner = SCons.Defaults.CScan) env['BUILDERS']['Mod'] = Builder(action = r'%(_python_)s mod.py') diff --git a/test/implicit/changed-node.py b/test/implicit/changed-node.py index 4f879c0..8b818ba 100644 --- a/test/implicit/changed-node.py +++ b/test/implicit/changed-node.py @@ -40,6 +40,7 @@ test.subdir('d', ['d', '3']) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) SetOption('implicit_cache', 1) SetOption('max_drift', 1) -- cgit v0.12 From a5a1c40c8aaa71574cf4a439e1f5d742aef9103a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 10:01:47 -0700 Subject: Speed up implicit-cache tests for windows --- test/implicit-cache/DualTargets.py | 3 ++- test/implicit-cache/GetOption.py | 2 ++ test/implicit-cache/RemoveImplicitDep.py | 3 ++- test/implicit-cache/SetOption.py | 1 + test/implicit-cache/basic.py | 1 + 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/implicit-cache/DualTargets.py b/test/implicit-cache/DualTargets.py index 45174ea..8612d1a 100644 --- a/test/implicit-cache/DualTargets.py +++ b/test/implicit-cache/DualTargets.py @@ -34,6 +34,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) import os.path def emitter(target, source, env): @@ -49,7 +50,7 @@ def source_scan(node, env, path): return [base + '.lib'] -env = Environment() +env = Environment(tools=[]) env['BUILDERS']['DualTarget'] = Builder( action = Action( [ diff --git a/test/implicit-cache/GetOption.py b/test/implicit-cache/GetOption.py index 818784a..561c21b 100644 --- a/test/implicit-cache/GetOption.py +++ b/test/implicit-cache/GetOption.py @@ -34,6 +34,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) assert not GetOption('implicit_cache') SetOption('implicit_cache', 1) assert GetOption('implicit_cache') @@ -42,6 +43,7 @@ assert GetOption('implicit_cache') test.run() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) assert GetOption('implicit_cache') SetOption('implicit_cache', 0) assert GetOption('implicit_cache') diff --git a/test/implicit-cache/RemoveImplicitDep.py b/test/implicit-cache/RemoveImplicitDep.py index 6a9b873..c65dfec 100644 --- a/test/implicit-cache/RemoveImplicitDep.py +++ b/test/implicit-cache/RemoveImplicitDep.py @@ -37,11 +37,12 @@ test = TestSCons.TestSCons() test.subdir(['src']) SConstruct_contents = """\ +DefaultEnvironment(tools=[]) import SCons.Script SetOption( 'implicit_cache', 1 ) -env = Environment() +env = Environment(tools=[]) act = Action([Touch('${TARGETS[0]}'),Touch('${TARGETS[1]}')]) env.Append(BUILDERS = {'BuildMe':Builder(action=act,source_scanner=SCons.Script.SourceFileScanner)} ) diff --git a/test/implicit-cache/SetOption.py b/test/implicit-cache/SetOption.py index 41a7fb6..f86ad47 100644 --- a/test/implicit-cache/SetOption.py +++ b/test/implicit-cache/SetOption.py @@ -36,6 +36,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) SetOption('implicit_cache', 1) env=Environment(CPPPATH=['i1', 'i2']) env.Object('foo.c') diff --git a/test/implicit-cache/basic.py b/test/implicit-cache/basic.py index a3a6546..b7cd984 100644 --- a/test/implicit-cache/basic.py +++ b/test/implicit-cache/basic.py @@ -53,6 +53,7 @@ test = TestSCons.TestSCons() test.subdir('include', 'subdir', ['subdir', 'include'], 'inc2') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(CPPPATH = Split('inc2 include')) obj = env.Object(target='prog', source='subdir/prog.c') env.Program(target='prog', source=obj) -- cgit v0.12 From ce9f09c5b8fa72b63e0f22cccb454430ef83f45f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 10:09:28 -0700 Subject: Speed up Install tests for windows --- test/Install/Clone.py | 1 + test/Install/INSTALLSTR.py | 3 ++- test/Install/Install.py | 3 ++- test/Install/InstallAs.py | 3 ++- test/Install/dir-exists.py | 1 + test/Install/directories.py | 4 +++- test/Install/multi-dir/src/SConstruct | 3 ++- test/Install/multi.py | 3 ++- test/Install/no-top-relative.py | 3 ++- test/Install/non-ascii-name.py | 1 + test/Install/option--install-sandbox.py | 3 ++- test/Install/tool.py | 1 + test/Install/wrap-by-attribute.py | 3 ++- 13 files changed, 23 insertions(+), 9 deletions(-) diff --git a/test/Install/Clone.py b/test/Install/Clone.py index da66157..14e0688 100644 --- a/test/Install/Clone.py +++ b/test/Install/Clone.py @@ -34,6 +34,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env1 = Environment(DESTDIR='sub1', tools=[]) # Call env1.Install() but not env1.InstallAs() *before* we clone it. diff --git a/test/Install/INSTALLSTR.py b/test/Install/INSTALLSTR.py index 0cc5fce..145b81d 100644 --- a/test/Install/INSTALLSTR.py +++ b/test/Install/INSTALLSTR.py @@ -37,7 +37,8 @@ test = TestSCons.TestSCons() test.subdir('install') test.write('SConstruct', """\ -env = Environment(INSTALLSTR = 'INSTALL $SOURCE => $TARGET!') +DefaultEnvironment(tools=[]) +env = Environment(tools=[], INSTALLSTR = 'INSTALL $SOURCE => $TARGET!') env.Install('install', 'file') """) diff --git a/test/Install/Install.py b/test/Install/Install.py index 72a7545..da97d2a 100644 --- a/test/Install/Install.py +++ b/test/Install/Install.py @@ -48,6 +48,7 @@ f6_sep = f6_txt.replace(os.sep, '/') _SUBDIR_f4_out = os.path.join('$SUBDIR', 'f4.out') test.write(['work', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) def cat(env, source, target): target = str(target[0]) f = open(target, "w") @@ -60,7 +61,7 @@ def my_install(dest, source, env): shutil.copy2(source, dest) open('my_install.out', 'a').write(dest) -env1 = Environment() +env1 = Environment(tools=[]) env1.Append(BUILDERS={'Cat':Builder(action=cat)}) env3 = env1.Clone(INSTALL = my_install) diff --git a/test/Install/InstallAs.py b/test/Install/InstallAs.py index 537ea3a..47ee9f6 100644 --- a/test/Install/InstallAs.py +++ b/test/Install/InstallAs.py @@ -47,7 +47,8 @@ _SUBDIR_file3_in = os.path.join('$SUBDIR', 'file3.in') # test.write('SConstruct', r""" -env = Environment(INSTALLDIR=r'%(install)s', SUBDIR='subdir') +DefaultEnvironment(tools=[]) +env = Environment(tools=[], INSTALLDIR=r'%(install)s', SUBDIR='subdir') InstallAs(r'%(install_file1_out)s', 'file1.in') env.InstallAs([r'%(_INSTALLDIR_file2_out)s', r'%(install_file3_out)s'], ['file2.in', r'%(_SUBDIR_file3_in)s']) diff --git a/test/Install/dir-exists.py b/test/Install/dir-exists.py index f981a35..9882d22 100644 --- a/test/Install/dir-exists.py +++ b/test/Install/dir-exists.py @@ -35,6 +35,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) Execute(Mkdir('a')) Execute(Mkdir('b')) f=Command('a/f', None, 'echo hi > $TARGET') diff --git a/test/Install/directories.py b/test/Install/directories.py index 74a7ac5..3ebc713 100644 --- a/test/Install/directories.py +++ b/test/Install/directories.py @@ -46,10 +46,12 @@ test.subdir('outside', ['work', 'dir4', 'sub']) test.write(['work', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) + Install('../outside', 'dir1') InstallAs('../outside/d2', 'dir2') -env = Environment() env.Install('../outside', 'dir3') env.InstallAs('../outside/d4', 'dir4') """) diff --git a/test/Install/multi-dir/src/SConstruct b/test/Install/multi-dir/src/SConstruct index e10ec8d..05b46a9 100644 --- a/test/Install/multi-dir/src/SConstruct +++ b/test/Install/multi-dir/src/SConstruct @@ -2,7 +2,8 @@ # outside the source tree can cause SCons to fail to create the dest # dir. import os, os.path, shutil -env=Environment() +DefaultEnvironment(tools=[]) +env=Environment(tools=[]) dst='../build' env.Install(os.path.join(dst,'__foo/bar/baz'), 'a') env.Install(os.path.join(dst,'__foo/bar/baz/a/b'), 'x/y') diff --git a/test/Install/multi.py b/test/Install/multi.py index a7a6eb8..1716d17 100644 --- a/test/Install/multi.py +++ b/test/Install/multi.py @@ -34,7 +34,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) env.Install('install', 'file1') env.Install('install', 'file1') """) diff --git a/test/Install/no-top-relative.py b/test/Install/no-top-relative.py index 3eb24f7..31c7130 100644 --- a/test/Install/no-top-relative.py +++ b/test/Install/no-top-relative.py @@ -39,7 +39,8 @@ test = TestSCons.TestSCons() test.subdir(['test']) test.write(['SConstruct'], """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) i = env.Install("#/install", "#/test/#testfile.txt#"); env.Default(i); """) diff --git a/test/Install/non-ascii-name.py b/test/Install/non-ascii-name.py index 186af09..462040d 100644 --- a/test/Install/non-ascii-name.py +++ b/test/Install/non-ascii-name.py @@ -39,6 +39,7 @@ test.write('中文.txt', "test stuff here in file 中文.txt.\n") test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) InstallAs("b", Glob("*.txt")) """) test.run(arguments='.') diff --git a/test/Install/option--install-sandbox.py b/test/Install/option--install-sandbox.py index 020cd3a..45366c1 100644 --- a/test/Install/option--install-sandbox.py +++ b/test/Install/option--install-sandbox.py @@ -49,7 +49,8 @@ file1_out = target+os.path.join( target, # test.write('SConstruct', r""" -env = Environment(SUBDIR='subdir') +DefaultEnvironment(tools=[]) +env = Environment(tools=[], SUBDIR='subdir') f1 = env.Install(r'%(destdir)s', 'file1.out') f2 = env.InstallAs(['file2.out', r'%(_SUBDIR_file3_out)s'], ['file2.in', r'%(_SUBDIR_file3_in)s']) diff --git a/test/Install/tool.py b/test/Install/tool.py index 1e49584..ba92d0a 100644 --- a/test/Install/tool.py +++ b/test/Install/tool.py @@ -36,6 +36,7 @@ test = TestSCons.TestSCons() test.subdir('iii') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(tools = []) env.Install('iii', 'foo.in') env.InstallAs('foo.out', 'foo.in') diff --git a/test/Install/wrap-by-attribute.py b/test/Install/wrap-by-attribute.py index 02513af..6989fa8 100644 --- a/test/Install/wrap-by-attribute.py +++ b/test/Install/wrap-by-attribute.py @@ -42,6 +42,7 @@ test = TestSCons.TestSCons() test.subdir('outside', 'sub') test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) import os.path def cat(env, source, target): @@ -51,7 +52,7 @@ def cat(env, source, target): f.write(open(str(src), "rb").read()) f.close() -env = Environment(DESTDIR='dest') +env = Environment(tools=[], DESTDIR='dest') env.Append(BUILDERS={'Cat':Builder(action=cat)}) env.SconsInternalInstallFunc = env.Install -- cgit v0.12 From dc4ff77f81747174ad99956fef601dbd540eed3f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 10:34:43 -0700 Subject: Speed up import.py by excluding default tool installs when not needed for windows --- test/import.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/import.py b/test/import.py index 11fee9c..9799850 100644 --- a/test/import.py +++ b/test/import.py @@ -63,6 +63,7 @@ platforms = [ for platform in platforms: test.write('SConstruct', """ +DefaultEnvironment(tools=[]) print("Platform %(platform)s") env = Environment(platform = '%(platform)s', tools=[]) import SCons.Platform.%(platform)s @@ -152,22 +153,24 @@ error_output = { # An SConstruct for importing Tool names that have illegal characters # for Python variable names. indirect_import = """\ +DefaultEnvironment(tools=[]) print("Tool %(tool)s (indirect)") env = Environment(tools = ['%(tool)s']) SCons = __import__('SCons.Tool.%(tool)s', globals(), locals(), []) m = getattr(SCons.Tool, '%(tool)s') -env = Environment() +env = Environment(tools=[]) m.generate(env) """ # An SConstruct for importing Tool names "normally." direct_import = """\ +DefaultEnvironment(tools=[]) print("Tool %(tool)s (direct)") env = Environment(tools = ['%(tool)s']) import SCons.Tool.%(tool)s -env = Environment() +env = Environment(tools=[]) SCons.Tool.%(tool)s.exists(env) SCons.Tool.%(tool)s.generate(env) """ -- cgit v0.12 From 981388a605495675d79b18c1eb63d885fae2470d Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 10:38:32 -0700 Subject: Speed up long-lines tests for windows --- test/long-lines/signature.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/long-lines/signature.py b/test/long-lines/signature.py index ce38bec..af234a3 100644 --- a/test/long-lines/signature.py +++ b/test/long-lines/signature.py @@ -54,11 +54,13 @@ fp.write('TIMESTAMP=%s\\n' % args[3]) os.chmod(build_py, 0o755) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) arg = 'a_long_ignored_argument' extra_arguments = arg while len(extra_arguments) <= 1024: extra_arguments = extra_arguments + ' ' + arg -env = Environment(FILECOM=[r'%(build_py)s', +env = Environment(tools=[], + FILECOM=[r'%(build_py)s', '$TARGET', '$SOURCE', '$FILEFLAG', '$(', '$TIMESTAMP', '$)', -- cgit v0.12 From 8fca42cbacd48d6f832721363813d9b3d1e3163d Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 10:42:18 -0700 Subject: Speed up M4 tests for windows --- test/M4/M4.py | 8 +++++--- test/M4/M4COM.py | 3 ++- test/M4/M4COMSTR.py | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/M4/M4.py b/test/M4/M4.py index 76010e6..82c084c 100644 --- a/test/M4/M4.py +++ b/test/M4/M4.py @@ -46,7 +46,8 @@ sys.exit(0) """) test.write('SConstruct', """ -env = Environment(tools=['default', 'm4'], +DefaultEnvironment(tools=[]) +env = Environment(tools=['m4'], M4 = r'%(_python_)s mym4.py') env.M4(target = 'aaa.x', source = 'aaa.x.m4') """ % locals()) @@ -76,10 +77,11 @@ if m4: test.file_fixture('wrapper.py') test.write('SConstruct', """ -foo = Environment(tools=['default', 'm4'], +DefaultEnvironment(tools=[]) +foo = Environment(tools=['m4'], M4=r'%(m4)s', M4FLAGS='-DFFF=fff') m4 = foo.Dictionary('M4') -bar = Environment(tools=['default', 'm4'], +bar = Environment(tools=['m4'], M4 = r'%(_python_)s wrapper.py ' + m4, M4FLAGS='-DBBB=bbb') foo.M4(target = 'foo.x', source = 'foo.x.m4') bar.M4(target = 'bar', source = 'bar.m4') diff --git a/test/M4/M4COM.py b/test/M4/M4COM.py index 5a2f076..4e95419 100644 --- a/test/M4/M4COM.py +++ b/test/M4/M4COM.py @@ -47,7 +47,8 @@ sys.exit(0) """) test.write('SConstruct', """ -env = Environment(tools=['default', 'm4'], +DefaultEnvironment(tools=[]) +env = Environment(tools=['m4'], M4COM = r'%(_python_)s mym4.py $TARGET $SOURCES') env.M4(target = 'aaa.out', source = 'aaa.in') """ % locals()) diff --git a/test/M4/M4COMSTR.py b/test/M4/M4COMSTR.py index da01e6c..0e6725b 100644 --- a/test/M4/M4COMSTR.py +++ b/test/M4/M4COMSTR.py @@ -48,7 +48,8 @@ sys.exit(0) """) test.write('SConstruct', """ -env = Environment(tools=['default', 'm4'], +DefaultEnvironment(tools=[]) +env = Environment(tools=['m4'], M4COM = r'%(_python_)s mym4.py $TARGET $SOURCES', M4COMSTR = 'M4ing $TARGET from $SOURCE') env.M4(target = 'aaa.out', source = 'aaa.in') -- cgit v0.12 From db67204cfdb270dbc08f36813e20c0a41c225b25 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 11:03:34 -0700 Subject: Speed up option tests for windows --- test/option/d.py | 3 ++- test/option/debug-count.py | 1 + test/option/debug-duplicate.py | 3 ++- test/option/debug-findlibs.py | 1 + test/option/debug-includes.py | 1 + test/option/debug-memoizer.py | 3 ++- test/option/debug-memory.py | 3 ++- test/option/debug-multiple.py | 1 + test/option/debug-objects.py | 3 ++- test/option/debug-pdb.py | 3 ++- test/option/debug-prepare.py | 3 ++- test/option/debug-presub.py | 4 +++- test/option/debug-stacktrace.py | 3 ++- test/option/debug-time.py | 4 +++- test/option/environment-overrides.py | 2 +- test/option/help-options.py | 2 +- test/option/md5-chunksize.py | 6 ++++-- test/option/no-print-directory.py | 2 +- test/option/option_profile.py | 1 + test/option/print-directory.py | 2 +- test/option/repository.py | 3 ++- test/option/srcdir.py | 3 ++- test/option/stack-size.py | 3 ++- test/option/taskmastertrace.py | 3 ++- test/option/tree-all.py | 1 + test/option/tree-derived.py | 1 + test/option/tree-lib.py | 1 + test/option/warn-dependency.py | 3 ++- test/option/warn-duplicate-environment.py | 3 ++- test/option/warn-misleading-keywords.py | 3 ++- test/option/warn-missing-sconscript.py | 3 ++- 31 files changed, 54 insertions(+), 24 deletions(-) diff --git a/test/option/d.py b/test/option/d.py index 055769a..36be02b 100644 --- a/test/option/d.py +++ b/test/option/d.py @@ -32,7 +32,7 @@ import TestSCons test = TestSCons.TestSCons() -test.write('SConstruct', "") +test.write('SConstruct', "DefaultEnvironment(tools=[])\n") test.run(arguments = '-d .', stderr = "Warning: ignoring -d option\n") @@ -44,6 +44,7 @@ test.pass_test() test.subdir('subdir') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment() env.Program(target = 'aaa', source = 'aaa.c') env.Program(target = 'bbb', source = 'bbb.c') diff --git a/test/option/debug-count.py b/test/option/debug-count.py index b82a434..2b5b745 100644 --- a/test/option/debug-count.py +++ b/test/option/debug-count.py @@ -44,6 +44,7 @@ except ImportError: test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def cat(target, source, env): open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read()) env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))}) diff --git a/test/option/debug-duplicate.py b/test/option/debug-duplicate.py index df2723e..8b9810d 100644 --- a/test/option/debug-duplicate.py +++ b/test/option/debug-duplicate.py @@ -33,7 +33,8 @@ test = TestSCons.TestSCons() test.subdir('sub1') test.write('SConstruct', """\ -env=Environment() +DefaultEnvironment(tools=[]) +env=Environment(tools=[]) Export('env') env.SConscript('SConscript', variant_dir='Build') """) diff --git a/test/option/debug-findlibs.py b/test/option/debug-findlibs.py index 9d5c82a..78ecee9 100644 --- a/test/option/debug-findlibs.py +++ b/test/option/debug-findlibs.py @@ -42,6 +42,7 @@ ofp.close() """) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx', LIBPATH = ['sub1', 'sub2', '.'], LIBS = ['iii', 'jjj', 'kkk', 'lll', 'mmm'], diff --git a/test/option/debug-includes.py b/test/option/debug-includes.py index 0982a20..e5c8e0a 100644 --- a/test/option/debug-includes.py +++ b/test/option/debug-includes.py @@ -34,6 +34,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(OBJSUFFIX = '.obj', SHOBJSUFFIX = '.shobj', LIBPREFIX = '', diff --git a/test/option/debug-memoizer.py b/test/option/debug-memoizer.py index 222ba67..f65bcb8 100644 --- a/test/option/debug-memoizer.py +++ b/test/option/debug-memoizer.py @@ -36,9 +36,10 @@ test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def cat(target, source, env): open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read()) -env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=Action(cat))}) env.Cat('file.out', 'file.in') """) diff --git a/test/option/debug-memory.py b/test/option/debug-memory.py index c9165ed..6d395d6 100644 --- a/test/option/debug-memory.py +++ b/test/option/debug-memory.py @@ -47,9 +47,10 @@ except ImportError: test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def cat(target, source, env): open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read()) -env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=Action(cat))}) env.Cat('file.out', 'file.in') """) diff --git a/test/option/debug-multiple.py b/test/option/debug-multiple.py index f121a2c..23af13b 100644 --- a/test/option/debug-multiple.py +++ b/test/option/debug-multiple.py @@ -36,6 +36,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def cat(target, source, env): open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read()) env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))}) diff --git a/test/option/debug-objects.py b/test/option/debug-objects.py index 6a919a4..e86684a 100644 --- a/test/option/debug-objects.py +++ b/test/option/debug-objects.py @@ -41,9 +41,10 @@ except ImportError: test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def cat(target, source, env): open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read()) -env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))}) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=Action(cat))}) env.Cat('file.out', 'file.in') """) diff --git a/test/option/debug-pdb.py b/test/option/debug-pdb.py index d4d285c..ec303a5 100644 --- a/test/option/debug-pdb.py +++ b/test/option/debug-pdb.py @@ -29,7 +29,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) """) test.run(arguments = "--debug=pdb", stdin = "n\ns\nq\n") diff --git a/test/option/debug-prepare.py b/test/option/debug-prepare.py index 350c352..0ed95f9 100644 --- a/test/option/debug-prepare.py +++ b/test/option/debug-prepare.py @@ -31,7 +31,8 @@ _python_ = TestSCons._python_ test = TestSCons.TestSCons() test.write('SConstruct', """\ -env=Environment() +DefaultEnvironment(tools=[]) +env=Environment(tools=[]) dest=env.Command('foo.out', 'SConstruct', [Copy('$TARGET', '$SOURCE'), Copy('${TARGET}.extra', '$SOURCE')]) diff --git a/test/option/debug-presub.py b/test/option/debug-presub.py index 28eeb83..0b0555a 100644 --- a/test/option/debug-presub.py +++ b/test/option/debug-presub.py @@ -37,6 +37,7 @@ sys.exit(0) """) test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def cat(env, source, target): target = str(target[0]) f = open(target, "wb") @@ -47,7 +48,8 @@ FILE = Builder(action="$FILECOM") TEMP = Builder(action="$TEMPCOM") LIST = Builder(action="$LISTCOM") FUNC = Builder(action=cat) -env = Environment(PYTHON=r'%(_python_)s', +env = Environment(tools=[], + PYTHON=r'%(_python_)s', BUILDERS = {'FILE':FILE, 'TEMP':TEMP, 'LIST':LIST, 'FUNC':FUNC}, FILECOM="$PYTHON cat.py $SOURCES $TARGET", TEMPCOM="$PYTHON cat.py $SOURCES temp\\n$PYTHON cat.py temp $TARGET", diff --git a/test/option/debug-stacktrace.py b/test/option/debug-stacktrace.py index fcc4c1b..490fecf 100644 --- a/test/option/debug-stacktrace.py +++ b/test/option/debug-stacktrace.py @@ -33,6 +33,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) def kfile_scan(node, env, target): raise Exception("kfile_scan error") @@ -40,7 +41,7 @@ kscan = Scanner(name = 'kfile', function = kfile_scan, skeys = ['.k']) -env = Environment() +env = Environment(tools=[]) env.Append(SCANNERS = [kscan]) env.Command('foo', 'foo.k', Copy('$TARGET', '$SOURCE')) diff --git a/test/option/debug-time.py b/test/option/debug-time.py index 7dd17fe..987e49f 100644 --- a/test/option/debug-time.py +++ b/test/option/debug-time.py @@ -44,7 +44,9 @@ sys.exit(0) """) test.write('SConstruct', """ -env = Environment(PYTHON = r'%(_python_)s', +DefaultEnvironment(tools=[]) +env = Environment(tools=[], + PYTHON = r'%(_python_)s', SLEEP_CAT = r'sleep_cat.py', CATCOM = '$PYTHON $SLEEP_CAT $SECONDS $TARGET $SOURCES', SECONDS = ARGUMENTS.get('SLEEP', '0')) diff --git a/test/option/environment-overrides.py b/test/option/environment-overrides.py index 8680e2c..fe2ac24 100644 --- a/test/option/environment-overrides.py +++ b/test/option/environment-overrides.py @@ -32,7 +32,7 @@ import TestSCons test = TestSCons.TestSCons() -test.write('SConstruct', "") +test.write('SConstruct', "DefaultEnvironment(tools=[])\n") test.run(arguments = '-e .', stderr = "Warning: ignoring -e option\n") diff --git a/test/option/help-options.py b/test/option/help-options.py index e14eff2..1835a62 100644 --- a/test/option/help-options.py +++ b/test/option/help-options.py @@ -34,7 +34,7 @@ import TestSCons test = TestSCons.TestSCons() -test.write('SConstruct', "") +test.write('SConstruct', "DefaultEnvironment(tools=[])\n") test.run(arguments = '-H') diff --git a/test/option/md5-chunksize.py b/test/option/md5-chunksize.py index dbb2615..708143f 100644 --- a/test/option/md5-chunksize.py +++ b/test/option/md5-chunksize.py @@ -39,9 +39,10 @@ file.close() """) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) SetOption('md5_chunksize', 128) B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) f1 = env.B(target = 'f1.out', source = 'f1.in') f2 = env.B(target = 'f2.out', source = 'f2.in') Requires(f2, f1) @@ -102,13 +103,14 @@ get_stat(["test.stat"], ["test.big"]) """) test2.write('SConstruct', """ +DefaultEnvironment(tools=[]) import os def get_stat(target, source, env): stat = os.stat(source[0].get_abspath()) dest = open(target[0].get_abspath(),'w') dest.write(str(stat)) dest.close() -env = Environment() +env = Environment(tools=[]) env.Command('test.big', 'SConstruct', 'dd if=/dev/zero of=test.big seek=100 bs=1M count=0 2>/dev/null') env.AlwaysBuild('test.big') env.Command('test.stat', 'test.big', Action(get_stat)) diff --git a/test/option/no-print-directory.py b/test/option/no-print-directory.py index 850a484..2a5857e 100644 --- a/test/option/no-print-directory.py +++ b/test/option/no-print-directory.py @@ -32,7 +32,7 @@ import TestSCons test = TestSCons.TestSCons() -test.write('SConstruct', "") +test.write('SConstruct', "DefaultEnvironment(tools=[])\n") test.run(arguments = '--no-print-directory .', stderr = "Warning: ignoring --no-print-directory option\n") diff --git a/test/option/option_profile.py b/test/option/option_profile.py index cb9d22c..4538e0e 100644 --- a/test/option/option_profile.py +++ b/test/option/option_profile.py @@ -48,6 +48,7 @@ except ImportError: test.skip_test('No pstats module, skipping test.\n') test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) Command('file.out', 'file.in', Copy("$TARGET", "$SOURCE")) """) diff --git a/test/option/print-directory.py b/test/option/print-directory.py index 96d9d7b..79d81d9 100644 --- a/test/option/print-directory.py +++ b/test/option/print-directory.py @@ -32,7 +32,7 @@ import TestSCons test = TestSCons.TestSCons() -test.write('SConstruct', "") +test.write('SConstruct', "DefaultEnvironment(tools=[])\n") test.run(arguments = '-w .', stderr = "Warning: ignoring -w option\n") diff --git a/test/option/repository.py b/test/option/repository.py index a71e71e..392c421 100644 --- a/test/option/repository.py +++ b/test/option/repository.py @@ -37,7 +37,8 @@ test.subdir('repository', 'work1') repository = test.workpath('repository') test.write(['repository', 'SConstruct'], """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) env.Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) """) diff --git a/test/option/srcdir.py b/test/option/srcdir.py index 0a92f13..15c8f95 100644 --- a/test/option/srcdir.py +++ b/test/option/srcdir.py @@ -37,7 +37,8 @@ test.subdir('repository', 'work1') repository = test.workpath('repository') test.write(['repository', 'SConstruct'], r""" -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) env.Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) """) diff --git a/test/option/stack-size.py b/test/option/stack-size.py index 3d7a715..febec5a 100644 --- a/test/option/stack-size.py +++ b/test/option/stack-size.py @@ -49,8 +49,9 @@ file.close() test.write(['work1', 'SConstruct'], """ +DefaultEnvironment(tools=[]) B = Builder(action = r'%(_python_)s ../build.py $TARGETS $SOURCES') -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) f1 = env.B(target = 'f1.out', source = 'f1.in') f2 = env.B(target = 'f2.out', source = 'f2.in') Requires(f2, f1) diff --git a/test/option/taskmastertrace.py b/test/option/taskmastertrace.py index c426692..b38645f 100644 --- a/test/option/taskmastertrace.py +++ b/test/option/taskmastertrace.py @@ -33,7 +33,8 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) # We name the files 'Tfile' so that they will sort after the SConstruct # file regardless of whether the test is being run on a case-sensitive diff --git a/test/option/tree-all.py b/test/option/tree-all.py index fc0f689..6222ba1 100644 --- a/test/option/tree-all.py +++ b/test/option/tree-all.py @@ -39,6 +39,7 @@ LINK = test.detect('LINK') if LINK is None: LINK = CC test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx') env.Program('Foo', Split('Foo.c Bar.c')) """) diff --git a/test/option/tree-derived.py b/test/option/tree-derived.py index 8490612..84d30fc 100644 --- a/test/option/tree-derived.py +++ b/test/option/tree-derived.py @@ -34,6 +34,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx') env.Program('foo', Split('foo.c bar.c')) """) diff --git a/test/option/tree-lib.py b/test/option/tree-lib.py index 4725d42..1e6df02 100644 --- a/test/option/tree-lib.py +++ b/test/option/tree-lib.py @@ -38,6 +38,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(LIBPREFIX='', LIBSUFFIX='.lib', OBJSUFFIX='.obj', diff --git a/test/option/warn-dependency.py b/test/option/warn-dependency.py index ca0c2aa..dca3815 100644 --- a/test/option/warn-dependency.py +++ b/test/option/warn-dependency.py @@ -34,12 +34,13 @@ test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) test.write("SConstruct", """\ +DefaultEnvironment(tools=[]) import SCons.Defaults def build(target, source, env): pass -env=Environment() +env=Environment(tools=[]) env['BUILDERS']['test'] = Builder(action=build, source_scanner=SCons.Defaults.ObjSourceScan) env.test(target='foo', source='foo.c') diff --git a/test/option/warn-duplicate-environment.py b/test/option/warn-duplicate-environment.py index 09ced2d..1509e41 100644 --- a/test/option/warn-duplicate-environment.py +++ b/test/option/warn-duplicate-environment.py @@ -34,6 +34,7 @@ test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: @@ -44,7 +45,7 @@ if WARN: SetOption('warn', WARN) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env2 = env.Clone(DIFFERENT_VARIABLE = 'true') env.B(target = 'file1.out', source = 'file1a.in') env2.B(target = 'file1.out', source = 'file1b.in') diff --git a/test/option/warn-misleading-keywords.py b/test/option/warn-misleading-keywords.py index 67bc965..ca934e5 100644 --- a/test/option/warn-misleading-keywords.py +++ b/test/option/warn-misleading-keywords.py @@ -34,6 +34,7 @@ test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def build(env, target, source): file = open(str(target[0]), 'wb') for s in source: @@ -44,7 +45,7 @@ if WARN: SetOption('warn', WARN) B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools=[], BUILDERS = { 'B' : B }) env.B(targets = 'file3a.out', source = 'file3a.in') env.B(target = 'file3b.out', sources = 'file3b.in') """) diff --git a/test/option/warn-missing-sconscript.py b/test/option/warn-missing-sconscript.py index 492131b..f5e697b 100644 --- a/test/option/warn-missing-sconscript.py +++ b/test/option/warn-missing-sconscript.py @@ -34,10 +34,11 @@ test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) test.write("SConstruct", """\ +DefaultEnvironment(tools=[]) def build(target, source, env): pass -env=Environment() +env=Environment(tools=[]) env['BUILDERS']['test'] = Builder(action=build) env.test(target='foo', source='foo.c') WARN = ARGUMENTS.get('WARN') -- cgit v0.12 From a7db6eb20c568892efc75fb0613aa0e0f3828624 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Sep 2018 17:22:49 -0700 Subject: Fix applelink test to work on win32 --- test/LINK/applelink.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/LINK/applelink.py b/test/LINK/applelink.py index b13b36f..3419fc1 100644 --- a/test/LINK/applelink.py +++ b/test/LINK/applelink.py @@ -50,7 +50,6 @@ main(int argc, char *argv[]) test.write('SConstruct', """ DefaultEnvironment(tools=[]) env = Environment(PLATFORM='darwin') - env.Object( target = '#foo.o', source = ['foo.c'], @@ -59,7 +58,7 @@ env.Object( ) """ % locals()) -test.run(arguments='-Q', stdout='gcc -o foo.o -c -Fframeworks foo.c\n') +test.run(arguments='-Q -n', stdout='gcc -o foo.o -c -Fframeworks foo.c\n') test.pass_test() -- cgit v0.12 From 0e8af5bca4f4eb579021b5cca705e16caa8b0fc9 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 27 Sep 2018 09:44:22 -0600 Subject: Fix packaging strip-install-dir test The expected message was not OS-neutral, failing on Windows due to backslashes. Now that Windows has tar this turned up. Interpolate os.sep to fix. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 1 + test/packaging/strip-install-dir.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 8a65750..52ab116 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -150,6 +150,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Remove obsoleted internal implementaiton of OrderedDict. - Test for tar packaging fixups - Stop using deprecated unittest asserts + - messages in strip-install-dir test now os-neutral From Hao Wu - typo in customized decider example in user guide diff --git a/test/packaging/strip-install-dir.py b/test/packaging/strip-install-dir.py index 92f0361..b81a6b4 100644 --- a/test/packaging/strip-install-dir.py +++ b/test/packaging/strip-install-dir.py @@ -27,6 +27,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Test stripping the InstallBuilder of the Package source file. """ +import os import TestSCons @@ -52,10 +53,10 @@ env.Package( NAME = 'foo', expected = """scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... -Copy file(s): "main.c" to "foo-1.2.3/bin/main.c" -tar -zc -f foo-1.2.3.tar.gz foo-1.2.3/bin/main.c +Copy file(s): "main.c" to "foo-1.2.3{os_sep}bin{os_sep}main.c" +tar -zc -f foo-1.2.3.tar.gz foo-1.2.3{os_sep}bin{os_sep}main.c scons: done building targets. -""" +""".format(os_sep=os.sep) test.run(arguments='', stderr = None, stdout=expected) -- cgit v0.12 From 4d5876fc98ef438b8d92d211dfdd91b4136f483e Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 8 Sep 2018 08:34:12 -0600 Subject: Add xz compression to tar packaging choices. A few tweaks to scons_dev_master.py which needed to have xz added anyway: changed mercurial to git in initial setup, install different java, more pythons. Note about adding ipkg-build. Docs updated slightly for wording in addition to adding the new tar packager. Signed-off-by: Mats Wichmann --- bin/scons_dev_master.py | 19 ++++--- src/CHANGES.txt | 1 + src/engine/SCons/Tool/packaging/__init__.py | 6 ++- src/engine/SCons/Tool/packaging/__init__.xml | 12 +++-- src/engine/SCons/Tool/packaging/src_tarbz2.py | 2 +- src/engine/SCons/Tool/packaging/src_targz.py | 2 +- src/engine/SCons/Tool/packaging/src_tarxz.py | 43 ++++++++++++++++ src/engine/SCons/Tool/packaging/tarbz2.py | 2 +- src/engine/SCons/Tool/packaging/targz.py | 2 +- src/engine/SCons/Tool/packaging/tarxz.py | 44 ++++++++++++++++ test/packaging/guess-package-name.py | 25 +++++++-- test/packaging/tar/xz_packaging.py | 74 +++++++++++++++++++++++++++ 12 files changed, 210 insertions(+), 22 deletions(-) create mode 100644 src/engine/SCons/Tool/packaging/src_tarxz.py create mode 100644 src/engine/SCons/Tool/packaging/tarxz.py create mode 100644 test/packaging/tar/xz_packaging.py diff --git a/bin/scons_dev_master.py b/bin/scons_dev_master.py index 3d67cb5..4fa5899 100644 --- a/bin/scons_dev_master.py +++ b/bin/scons_dev_master.py @@ -11,11 +11,12 @@ import sys from Command import CommandRunner, Usage INITIAL_PACKAGES = [ - 'mercurial', + 'git', ] INSTALL_PACKAGES = [ 'wget', + 'xz-utils', ] PYTHON_PACKAGES = [ @@ -60,8 +61,7 @@ DOCUMENTATION_PACKAGES = [ 'gcc-doc', 'pkg-config', 'python-doc', - 'sun-java5-doc', - 'sun-java6-doc', + 'openjdk-8-doc', 'swig-doc', 'texlive-doc', ] @@ -70,21 +70,22 @@ TESTING_PACKAGES = [ 'bison', 'cssc', 'cvs', + 'hg', 'flex', 'g++', 'gcc', 'gcj', 'ghostscript', -# 'libgcj7-dev', 'm4', 'openssh-client', 'openssh-server', 'python-profiler', 'python-all-dev', + 'python3-all-dev', + 'pypy-dev', 'rcs', 'rpm', -# 'sun-java5-jdk', - 'sun-java6-jdk', + 'openjdk-8-jdk', 'swig', 'texlive-base-bin', 'texlive-extra-utils', @@ -131,7 +132,7 @@ Usage: scons_dev_master.py [-hnqy] [--password PASSWORD] [--username USER] buildbot Install packages for running BuildBot """ - scons_url = 'https://bdbaddog@bitbucket.org/scons/scons' + scons_url = 'https://github.com/SCons/scons.git' sudo = 'sudo' password = '""' username = 'guest' @@ -180,13 +181,15 @@ Usage: scons_dev_master.py [-hnqy] [--password PASSWORD] [--username USER] cmd.run('%(sudo)s apt-get %(yesflag)s upgrade') elif arg == 'checkout': cmd.run('%(sudo)s apt-get %(yesflag)s install %(initial_packages)s') - cmd.run('hg clone" %(scons_url)s') + cmd.run('git clone" %(scons_url)s') elif arg == 'building': cmd.run('%(sudo)s apt-get %(yesflag)s install %(building_packages)s') elif arg == 'docs': cmd.run('%(sudo)s apt-get %(yesflag)s install %(doc_packages)s') elif arg == 'testing': cmd.run('%(sudo)s apt-get %(yesflag)s install %(testing_packages)s') + #TODO: maybe copy ipkg-build from openwrt git + #cmd.run('%(sudo)s wget https://raw.githubusercontent.com/openwrt/openwrt/master/scripts/ipkg-build SOMEWHERE') elif arg == 'buildbot': cmd.run('%(sudo)s apt-get %(yesflag)s install %(buildbot_packages)s') elif arg == 'python-versions': diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 52ab116..6bbff3e 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -151,6 +151,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Test for tar packaging fixups - Stop using deprecated unittest asserts - messages in strip-install-dir test now os-neutral + - Add xz compression format to packaging choices. From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py index 4c92022..2e98b07 100644 --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -37,7 +37,11 @@ from SCons.Warnings import warn, Warning import os import imp -__all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] +__all__ = [ + 'src_targz', 'src_tarbz2', 'src_xz', 'src_zip', + 'targz', 'tarbz2', 'xz', 'zip', + 'rpm', 'msi', 'ipk', +] # # Utility and Builder function diff --git a/src/engine/SCons/Tool/packaging/__init__.xml b/src/engine/SCons/Tool/packaging/__init__.xml index 348b2be..9b8eecf 100644 --- a/src/engine/SCons/Tool/packaging/__init__.xml +++ b/src/engine/SCons/Tool/packaging/__init__.xml @@ -64,13 +64,15 @@ the following packagers available: * msi - Microsoft Installer - * rpm - Redhat Package Manger + * rpm - RPM Package Manger * ipkg - Itsy Package Management System - * tarbz2 - compressed tar - * targz - compressed tar + * tarbz2 - bzip2 compressed tar + * targz - gzip compressed tar + * tarxz - xz compressed tar * zip - zip file - * src_tarbz2 - compressed tar source - * src_targz - compressed tar source + * src_tarbz2 - bzip2 compressed tar source + * src_targz - gzip compressed tar source + * src_tarxz - xz compressed tar source * src_zip - zip file source diff --git a/src/engine/SCons/Tool/packaging/src_tarbz2.py b/src/engine/SCons/Tool/packaging/src_tarbz2.py index 04ea71b..d132291 100644 --- a/src/engine/SCons/Tool/packaging/src_tarbz2.py +++ b/src/engine/SCons/Tool/packaging/src_tarbz2.py @@ -1,4 +1,4 @@ -"""SCons.Tool.Packaging.tarbz2 +"""SCons.Tool.Packaging.src_tarbz2 The tarbz2 SRC packager. """ diff --git a/src/engine/SCons/Tool/packaging/src_targz.py b/src/engine/SCons/Tool/packaging/src_targz.py index 62d6f5d..7b759dc 100644 --- a/src/engine/SCons/Tool/packaging/src_targz.py +++ b/src/engine/SCons/Tool/packaging/src_targz.py @@ -1,4 +1,4 @@ -"""SCons.Tool.Packaging.targz +"""SCons.Tool.Packaging.src_targz The targz SRC packager. """ diff --git a/src/engine/SCons/Tool/packaging/src_tarxz.py b/src/engine/SCons/Tool/packaging/src_tarxz.py new file mode 100644 index 0000000..5889b8a --- /dev/null +++ b/src/engine/SCons/Tool/packaging/src_tarxz.py @@ -0,0 +1,43 @@ +"""SCons.Tool.Packaging.src_tarxz + +The tarxz SRC packager. +""" + +# +# __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__" + +from SCons.Tool.packaging import putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.xz') + target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) + return bld(env, target, source, TARFLAGS='-Jc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/Tool/packaging/tarbz2.py b/src/engine/SCons/Tool/packaging/tarbz2.py index 3806e76..392bf95 100644 --- a/src/engine/SCons/Tool/packaging/tarbz2.py +++ b/src/engine/SCons/Tool/packaging/tarbz2.py @@ -1,6 +1,6 @@ """SCons.Tool.Packaging.tarbz2 -The tarbz2 SRC packager. +The tarbz2 packager. """ # diff --git a/src/engine/SCons/Tool/packaging/targz.py b/src/engine/SCons/Tool/packaging/targz.py index acb6344..4130f32 100644 --- a/src/engine/SCons/Tool/packaging/targz.py +++ b/src/engine/SCons/Tool/packaging/targz.py @@ -1,6 +1,6 @@ """SCons.Tool.Packaging.targz -The targz SRC packager. +The targz packager. """ # diff --git a/src/engine/SCons/Tool/packaging/tarxz.py b/src/engine/SCons/Tool/packaging/tarxz.py new file mode 100644 index 0000000..747296b --- /dev/null +++ b/src/engine/SCons/Tool/packaging/tarxz.py @@ -0,0 +1,44 @@ +"""SCons.Tool.Packaging.tarxz + +The tarxz packager. +""" + +# +# __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__" + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.xz') + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + target, source = stripinstallbuilder(target, source, env) + return bld(env, target, source, TARFLAGS='-Jc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/packaging/guess-package-name.py b/test/packaging/guess-package-name.py index 33c3329..8ba4c9e 100644 --- a/test/packaging/guess-package-name.py +++ b/test/packaging/guess-package-name.py @@ -81,9 +81,9 @@ env.Package( NAME = 'libfoo', source = [ 'src/main.c', 'SConstruct' ] ) """) -test.run(stderr = None) +test.run(stderr=None) -test.must_exist( 'src.tar.gz' ) +test.must_exist('src.tar.gz') # # TEST: default package name creation with overridden packager. @@ -98,9 +98,26 @@ env.Package( NAME = 'libfoo', source = [ 'src/main.c', 'SConstruct' ] ) """) -test.run(stderr = None) +test.run(stderr=None) -test.must_exist( 'libfoo-1.2.3.tar.bz2' ) +test.must_exist('libfoo-1.2.3.tar.bz2') + +# +# TEST: default package name creation with another packager. +# + +test.write('SConstruct', """ +env=Environment(tools=['default', 'packaging']) +env.Program( 'src/main.c' ) +env.Package( NAME = 'libfoo', + VERSION = '1.2.3', + PACKAGETYPE = 'src_tarxz', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(stderr=None) + +test.must_exist('libfoo-1.2.3.tar.xz') test.pass_test() diff --git a/test/packaging/tar/xz_packaging.py b/test/packaging/tar/xz_packaging.py new file mode 100644 index 0000000..194b110 --- /dev/null +++ b/test/packaging/tar/xz_packaging.py @@ -0,0 +1,74 @@ +#!/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__" + +""" +This tests the SRC xz packager, which does the following: + - create a tar package from the specified files +""" + +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +tar = test.detect('TAR', 'tar') +if not tar: + test.skip_test('tar not found, skipping test\n') + +xz = test.where_is('xz') +if not xz: + test.skip_test('tar found, but helper xz not found, skipping test\n') + +test.subdir('src') + +test.write([ 'src', 'main.c'], r""" +int main( int argc, char* argv[] ) +{ + return 0; +} +""") + +test.write('SConstruct', """ +Program( 'src/main.c' ) +env=Environment(tools=['default', 'packaging']) +env.Package( PACKAGETYPE = 'src_tarxz', + target = 'src.tar.xz', + PACKAGEROOT = 'test', + source = [ 'src/main.c', 'SConstruct' ] ) +""") + +test.run(arguments='', stderr=None) + +test.must_exist('src.tar.xz') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From 00a71297271f35b1f313b132d7a5de9edcee1271 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 3 Oct 2018 11:58:21 -0600 Subject: Resync generated docs There have been a few doc changes since 3.0.1, this commit resyncs the generated docs to pick up fixes and wording. No code or test effect. Signed-off-by: Mats Wichmann --- doc/generated/builders.gen | 12 +-- doc/generated/examples/caching_ex-random_1.xml | 2 +- doc/generated/examples/environments_ex3_1.xml | 22 +----- doc/generated/examples/java_jar1_1.xml | 2 +- .../examples/troubleshoot_stacktrace_2.xml | 2 +- doc/generated/functions.gen | 19 ++++- doc/generated/variables.gen | 89 +++++++++++++++------- doc/generated/variables.mod | 2 + src/engine/SCons/Tool/packaging/__init__.xml | 2 +- 9 files changed, 93 insertions(+), 59 deletions(-) diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen index d851c93..ada4e43 100644 --- a/doc/generated/builders.gen +++ b/doc/generated/builders.gen @@ -1206,13 +1206,15 @@ the following packagers available: * msi - Microsoft Installer - * rpm - Redhat Package Manger + * rpm - RPM Package Manger * ipkg - Itsy Package Management System - * tarbz2 - compressed tar - * targz - compressed tar + * tarbz2 - bzip2 compressed tar + * targz - gzip compressed tar + * tarxz - xz compressed tar * zip - zip file - * src_tarbz2 - compressed tar source - * src_targz - compressed tar source + * src_tarbz2 - bzip2 compressed tar source + * src_targz - gzip compressed tar source + * src_tarxz - xz compressed tar source * src_zip - zip file source diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index 19b02f5..18b04eb 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -2,8 +2,8 @@ % scons -Q cc -o f3.o -c f3.c cc -o f5.o -c f5.c -cc -o f2.o -c f2.c cc -o f4.o -c f4.c +cc -o f2.o -c f2.c cc -o f1.o -c f1.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/environments_ex3_1.xml b/doc/generated/examples/environments_ex3_1.xml index 12b6ae0..3262302 100644 --- a/doc/generated/examples/environments_ex3_1.xml +++ b/doc/generated/examples/environments_ex3_1.xml @@ -1,22 +1,6 @@ % scons -Q -UnicodeDecodeError: 'utf8' codec can't decode byte 0xc2 in position 547: invalid continuation byte: - File "/home/my/project/SConstruct", line 6: - dbg.Program('foo', 'foo.c') - File "bootstrap/src/engine/SCons/Environment.py", line 260: - return MethodWrapper.__call__(self, target, source, *args, **kw) - File "bootstrap/src/engine/SCons/Environment.py", line 224: - return self.method(*nargs, **kwargs) - File "bootstrap/src/engine/SCons/Builder.py", line 635: - return self._execute(env, target, source, OverrideWarner(kw), ekw) - File "bootstrap/src/engine/SCons/Builder.py", line 541: - source = self.src_builder_sources(env, source, overwarn) - File "bootstrap/src/engine/SCons/Builder.py", line 748: - tlist = bld._execute(env, None, [s], overwarn) - File "bootstrap/src/engine/SCons/Builder.py", line 557: - _node_errors(self, env, tlist, slist) - File "bootstrap/src/engine/SCons/Builder.py", line 303: - msg = "Two environments with different actions were specified for the same target: %s\n(action 1: %s)\n(action 2: %s)" % (t,t_contents.decode('utf-8'),contents.decode('utf-8')) - File "/home/bdbaddog/tools/python-2.7.13/lib/python2.7/encodings/utf_8.py", line 16: - return codecs.utf_8_decode(input, errors, True) + +scons: *** Two environments with different actions were specified for the same target: foo.o +File "/home/my/project/SConstruct", line 6, in <module> diff --git a/doc/generated/examples/java_jar1_1.xml b/doc/generated/examples/java_jar1_1.xml index daa3d1a..de93227 100644 --- a/doc/generated/examples/java_jar1_1.xml +++ b/doc/generated/examples/java_jar1_1.xml @@ -1,5 +1,5 @@ % scons -Q javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java -scons: *** [test.jar] Source `classes.class' not found, needed by target `test.jar'. +jar cf test.jar classes diff --git a/doc/generated/examples/troubleshoot_stacktrace_2.xml b/doc/generated/examples/troubleshoot_stacktrace_2.xml index 4b55f0d..52bfa43 100644 --- a/doc/generated/examples/troubleshoot_stacktrace_2.xml +++ b/doc/generated/examples/troubleshoot_stacktrace_2.xml @@ -4,7 +4,7 @@ scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'. scons: internal stack trace: File "bootstrap/src/engine/SCons/Job.py", line 199, in start task.prepare() - File "bootstrap/src/engine/SCons/Script/Main.py", line 175, in prepare + File "bootstrap/src/engine/SCons/Script/Main.py", line 176, in prepare return SCons.Taskmaster.OutOfDateTask.prepare(self) File "bootstrap/src/engine/SCons/Taskmaster.py", line 198, in prepare executor.prepare() diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index 79a83c6..617a0a4 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -3523,16 +3523,16 @@ below, for a complete explanation of the arguments and behavior. - SConscript(scripts, [exports, variant_dir, duplicate]) + SConscript(scripts, [exports, variant_dir, duplicate, must_exist]) - env.SConscript(scripts, [exports, variant_dir, duplicate]) + env.SConscript(scripts, [exports, variant_dir, duplicate, must_exist]) - SConscript(dirs=subdirs, [name=script, exports, variant_dir, duplicate]) + SConscript(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist]) - env.SConscript(dirs=subdirs, [name=script, exports, variant_dir, duplicate]) + env.SConscript(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist]) @@ -3734,6 +3734,17 @@ TODO??? SConscript('build/SConscript', src_dir='src') +The optional +must_exist +argument, if true, causes an exception to be raised if a requested +SConscript file is not found. The current default is false, +causing only a warning to be omitted, but this behavior is deprecated. +For scripts which truly intend to be optional, transition to +explicty supplying +must_exist=False to the call. + + + Here are some composite examples: diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index a756d35..9246249 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -51,7 +51,9 @@ This is used to fill in the Architecture: field in an Ipkg control file, -and as part of the name of a generated RPM file. +and the BuildArch: field +in the RPM .spec file, +as well as forming part of the name of a generated RPM package file. @@ -240,7 +242,7 @@ or this: env = Environment() -env['BUILDERS]['NewBuilder'] = foo +env['BUILDERS']['NewBuilder'] = foo @@ -389,7 +391,6 @@ the control for Ipkg, the .wxs for MSI). If set, the function will be called after the SCons template for the file has been written. -XXX @@ -2943,15 +2944,6 @@ is -dNOPAUSE -dBATCH -sDEVICE=pdfwrite HOST_ARCH - The name of the host hardware architecture used to create the Environment. - If a platform is specified when creating the Environment, then - that Platform's logic will handle setting this value. - This value is immutable, and should not be changed by the user after - the Environment is initialized. - Currently only set for Win32. - - - Sets the host architecture for Visual Studio compiler. If not set, default to the detected host architecture: note that this may depend on the python you are using. @@ -2967,7 +2959,16 @@ Valid values are the same as for This is currently only used on Windows, but in the future it will be used on other OSes as well. - + + + The name of the host hardware architecture used to create the Environment. + If a platform is specified when creating the Environment, then + that Platform's logic will handle setting this value. + This value is immutable, and should not be changed by the user after + the Environment is initialized. + Currently only set for Win32. + + HOST_OS @@ -3209,7 +3210,7 @@ The command line used to call the Java archive tool. The string displayed when the Java archive tool is called -If this is not set, then $JARCOM (the command line) is displayed. +If this is not set, then $JARCOM (the command line) is displayed. @@ -3219,7 +3220,7 @@ env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") The string displayed when the Java archive tool is called -If this is not set, then $JARCOM (the command line) is displayed. +If this is not set, then $JARCOM (the command line) is displayed. @@ -3933,10 +3934,10 @@ and these suffixes. LICENSE -The abbreviated name of the license under which -this project is released (gpl, lpgl, bsd etc.). +The abbreviated name, preferably the SPDX code, of the license under which +this project is released (GPL-3.0, LGPL-2.1, BSD-2-Clause etc.). See http://www.opensource.org/licenses/alphabetical -for a list of license names. +for a list of license names and SPDX codes. @@ -7100,13 +7101,6 @@ that may not be set or used in a construction environment. TARGET_ARCH - The name of the target hardware architecture for the compiled objects - created by this Environment. - This defaults to the value of HOST_ARCH, and the user can override it. - Currently only set for Win32. - - - Sets the target architecture for Visual Studio compiler (i.e. the arch of the binaries generated by the compiler). If not set, default to $HOST_ARCH, or, if that is unset, to the architecture of the @@ -7131,7 +7125,14 @@ and ia64 (Itanium). For example, if you want to compile 64-bit binaries, you would set TARGET_ARCH='x86_64' in your SCons environment. - + + + The name of the target hardware architecture for the compiled objects + created by this Environment. + This defaults to the value of HOST_ARCH, and the user can override it. + Currently only set for Win32. + + TARGET_OS @@ -7547,6 +7548,7 @@ This is used to fill in the BuildRequires: field in the RPM .spec file. +Note this should only be used on a host managed by rpm as the dependencies will not be resolvable at build time otherwise. @@ -7605,7 +7607,8 @@ field in the RPM This is used to fill in the Epoch: -field in the controlling information for RPM packages. +field in the RPM +.spec file. @@ -7631,6 +7634,38 @@ field in the RPM + + X_RPM_EXTRADEFS + + +A list used to supply extra defintions or flags +to be added to the RPM .spec file. +Each item is added as-is with a carriage return appended. +This is useful if some specific RPM feature not otherwise +anticipated by SCons needs to be turned on or off. +Note if this variable is omitted, SCons will by +default supply the value +'%global debug_package %{nil}' +to disable debug package generation. +To enable debug package generation, include this +variable set either to None, or to a custom +list that does not include the default line. +Added in version 3.1. + + + +env.Package( + NAME = 'foo', +... + X_RPM_EXTRADEFS = [ + '%define _unpackaged_files_terminate_build 0' + '%define _missing_doc_files_terminate_build 0' + ], +... ) + + + + X_RPM_GROUP diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod index 6ecf6c9..52ee4e1 100644 --- a/doc/generated/variables.mod +++ b/doc/generated/variables.mod @@ -584,6 +584,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $X_RPM_EPOCH"> $X_RPM_EXCLUDEARCH"> $X_RPM_EXLUSIVEARCH"> +$X_RPM_EXTRADEFS"> $X_RPM_GROUP"> $X_RPM_GROUP_lang"> $X_RPM_ICON"> @@ -1213,6 +1214,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $X_RPM_EPOCH"> $X_RPM_EXCLUDEARCH"> $X_RPM_EXLUSIVEARCH"> +$X_RPM_EXTRADEFS"> $X_RPM_GROUP"> $X_RPM_GROUP_lang"> $X_RPM_ICON"> diff --git a/src/engine/SCons/Tool/packaging/__init__.xml b/src/engine/SCons/Tool/packaging/__init__.xml index 9b8eecf..41a3f11 100644 --- a/src/engine/SCons/Tool/packaging/__init__.xml +++ b/src/engine/SCons/Tool/packaging/__init__.xml @@ -477,7 +477,7 @@ field in the RPM A list used to supply extra defintions or flags -to be added to the RPM .spec file. +to be added to the RPM .spec file. Each item is added as-is with a carriage return appended. This is useful if some specific RPM feature not otherwise anticipated by SCons needs to be turned on or off. -- cgit v0.12 From 6bb0f67833cd198253dcf94342da7e792aaf9a55 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 26 Aug 2018 09:10:58 -0600 Subject: A few syntax cleanups Suggested by PyCharm. Includes three "real" changes: 1. src/engine/SCons/Node/__init__.py has a print statement in a function which references 'self', but there is no 'self' defined (it is not a method in a class). Guessing it should have been 'node'. 2. src/engine/SCons/Environment.py makes a call using 'kwbd' which is not defined, looks like a copy-paste error and should be 'bd'. 3. src/engine/SCons/Tool/JavaCommon.py splits 'file', which is not defined, was evidently supposed to be 'fn'. These should be double-checked. The rest are purely syntax: whitespace, dropping trailing semicolons, using "is" to test for None, simplifying comparisons, normalizing docstring commenting ("always triple double quotes"), unneeded backslashes. Signed-off-by: Mats Wichmann --- src/CHANGES.txt | 2 ++ src/engine/SCons/Action.py | 4 ++-- src/engine/SCons/Environment.py | 4 ++-- src/engine/SCons/Node/FS.py | 10 +++++----- src/engine/SCons/Node/__init__.py | 4 ++-- src/engine/SCons/Platform/posix.py | 2 +- src/engine/SCons/SConf.py | 2 +- src/engine/SCons/Scanner/Fortran.py | 2 +- src/engine/SCons/Scanner/LaTeX.py | 16 ++++++++-------- src/engine/SCons/Tool/GettextCommon.py | 6 +++--- src/engine/SCons/Tool/JavaCommon.py | 2 +- src/engine/SCons/Tool/__init__.py | 12 ++++++------ src/engine/SCons/Tool/intelc.py | 2 +- src/engine/SCons/Tool/jar.py | 2 +- src/engine/SCons/Tool/msvs.py | 10 +++++----- src/engine/SCons/Tool/packaging/__init__.py | 8 +++----- src/engine/SCons/Tool/packaging/msi.py | 5 ++--- src/engine/SCons/Tool/rpcgen.py | 2 +- src/engine/SCons/Tool/tex.py | 2 +- src/engine/SCons/Util.py | 10 +++++----- src/script/scons-time.py | 6 +++--- src/script/sconsign.py | 2 +- 22 files changed, 57 insertions(+), 58 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6bbff3e..6359560 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -152,6 +152,8 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Stop using deprecated unittest asserts - messages in strip-install-dir test now os-neutral - Add xz compression format to packaging choices. + - Syntax cleanups - trailing blanks, use "is" to compare with None, etc. + Three uses of variables not defined are changed. From Hao Wu - typo in customized decider example in user guide diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 9a3888b..78fb7d0 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -843,8 +843,8 @@ class CommandAction(_ActionAction): _ActionAction.__init__(self, **kw) if is_List(cmd): if [c for c in cmd if is_List(c)]: - raise TypeError("CommandAction should be given only " \ - "a single command") + raise TypeError("CommandAction should be given only " + "a single command") self.cmd_list = cmd def __str__(self): diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 42b28e6..81d0e5a 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -150,7 +150,7 @@ def _set_BUILDERS(env, key, value): for k in list(bd.keys()): del bd[k] except KeyError: - bd = BuilderDict(kwbd, env) + bd = BuilderDict(bd, env) env._dict[key] = bd for k, v in value.items(): if not SCons.Builder.is_a_Builder(v): @@ -2258,7 +2258,7 @@ class Base(SubstitutionEnvironment): while (node != node.srcnode()): node = node.srcnode() return node - sources = list(map( final_source, sources )); + sources = list(map(final_source, sources)) # remove duplicates return list(set(sources)) diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 54cd423..bc15064 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -132,9 +132,9 @@ def initialize_do_splitdrive(): global do_splitdrive global has_unc drive, path = os.path.splitdrive('X:/foo') - # splitunc is removed from python 3.7 and newer + # splitunc is removed from python 3.7 and newer # so we can also just test if splitdrive works with UNC - has_unc = (hasattr(os.path, 'splitunc') + has_unc = (hasattr(os.path, 'splitunc') or os.path.splitdrive(r'\\split\drive\test')[0] == r'\\split\drive') do_splitdrive = not not drive or has_unc @@ -1397,10 +1397,10 @@ class FS(LocalFS): if not isinstance(d, SCons.Node.Node): d = self.Dir(d) self.Top.addRepository(d) - + def PyPackageDir(self, modulename): """Locate the directory of a given python module name - + For example scons might resolve to Windows: C:\Python27\Lib\site-packages\scons-2.5.1 Linux: /usr/lib/scons @@ -3305,7 +3305,7 @@ class File(Base): try: node = dir.entries[norm_name] except KeyError: node = dir.file_on_disk(self.name) if node and node.exists() and \ - (isinstance(node, File) or isinstance(node, Entry) \ + (isinstance(node, File) or isinstance(node, Entry) or not node.is_derived()): result = node # Copy over our local attributes to the repository diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index e186752..c18a954 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -155,7 +155,7 @@ def exists_file(node): # The source file does not exist. Make sure no old # copy remains in the variant directory. if print_duplicate: - print("dup: no src for %s, unlinking old variant copy"%self) + print("dup: no src for %s, unlinking old variant copy" % node) if exists_base(node) or node.islink(): node.fs.unlink(node.get_internal_path()) # Return None explicitly because the Base.exists() call @@ -665,7 +665,7 @@ class Node(object, with_metaclass(NoSlotsPyPy)): executor.cleanup() def reset_executor(self): - "Remove cached executor; forces recompute when needed." + """Remove cached executor; forces recompute when needed.""" try: delattr(self, 'executor') except AttributeError: diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py index 8db08db..ae2ad1a 100644 --- a/src/engine/SCons/Platform/posix.py +++ b/src/engine/SCons/Platform/posix.py @@ -48,7 +48,7 @@ exitvalmap = { } def escape(arg): - "escape shell special characters" + """escape shell special characters""" slash = '\\' special = '"$' diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index a14127e..0dcbab8 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -1000,7 +1000,7 @@ def CheckLib(context, library = None, symbol = "main", compiles without flags. """ - if library == []: + if not library: library = [None] if not SCons.Util.is_List(library): diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py index 1b55130..6065bbd 100644 --- a/src/engine/SCons/Scanner/Fortran.py +++ b/src/engine/SCons/Scanner/Fortran.py @@ -78,7 +78,7 @@ class F90Scanner(SCons.Scanner.Classic): def scan(self, node, env, path=()): # cache the includes list in node so we only scan it once: - if node.includes != None: + if node.includes is not None: mods_and_includes = node.includes else: # retrieve all included filenames diff --git a/src/engine/SCons/Scanner/LaTeX.py b/src/engine/SCons/Scanner/LaTeX.py index 5800443..1cf2567 100644 --- a/src/engine/SCons/Scanner/LaTeX.py +++ b/src/engine/SCons/Scanner/LaTeX.py @@ -128,8 +128,8 @@ class LaTeX(SCons.Scanner.Base): Unlike most scanners, which use regular expressions that just return the included file name, this returns a tuple consisting of the keyword for the inclusion ("include", "includegraphics", - "input", or "bibliography"), and then the file name itself. - Based on a quick look at LaTeX documentation, it seems that we + "input", or "bibliography"), and then the file name itself. + Based on a quick look at LaTeX documentation, it seems that we should append .tex suffix for the "include" keywords, append .tex if there is no extension for the "input" keyword, and need to add .bib for the "bibliography" keyword that does not accept extensions by itself. @@ -137,7 +137,7 @@ class LaTeX(SCons.Scanner.Base): Finally, if there is no extension for an "includegraphics" keyword latex will append .ps or .eps to find the file, while pdftex may use .pdf, .jpg, .tif, .mps, or .png. - + The actual subset and search order may be altered by DeclareGraphicsExtensions command. This complication is ignored. The default order corresponds to experimentation with teTeX:: @@ -333,7 +333,7 @@ class LaTeX(SCons.Scanner.Base): line_continues_a_comment = False for line in text.splitlines(): line,comment = self.comment_re.findall(line)[0] - if line_continues_a_comment == True: + if line_continues_a_comment: out[-1] = out[-1] + line.lstrip() else: out.append(line) @@ -349,7 +349,7 @@ class LaTeX(SCons.Scanner.Base): # path_dict = dict(list(path)) # add option for whitespace (\s) before the '[' noopt_cre = re.compile('\s*\[.*$') - if node.includes != None: + if node.includes is not None: includes = node.includes else: text = self.canonical_text(node.get_text_contents()) @@ -386,8 +386,8 @@ class LaTeX(SCons.Scanner.Base): directory of the main file just as latex does""" path_dict = dict(list(path)) - - queue = [] + + queue = [] queue.extend( self.scan(node) ) seen = {} @@ -402,7 +402,7 @@ class LaTeX(SCons.Scanner.Base): source_dir = node.get_dir() #for include in includes: while queue: - + include = queue.pop() inc_type, inc_subdir, inc_filename = include diff --git a/src/engine/SCons/Tool/GettextCommon.py b/src/engine/SCons/Tool/GettextCommon.py index 2d37def..b7d02a7 100644 --- a/src/engine/SCons/Tool/GettextCommon.py +++ b/src/engine/SCons/Tool/GettextCommon.py @@ -390,7 +390,7 @@ def _detect_xgettext(env): """ Detects *xgettext(1)* binary """ if 'XGETTEXT' in env: return env['XGETTEXT'] - xgettext = env.Detect('xgettext'); + xgettext = env.Detect('xgettext') if xgettext: return xgettext raise SCons.Errors.StopError(XgettextNotFound, "Could not detect xgettext") @@ -409,7 +409,7 @@ def _detect_msginit(env): """ Detects *msginit(1)* program. """ if 'MSGINIT' in env: return env['MSGINIT'] - msginit = env.Detect('msginit'); + msginit = env.Detect('msginit') if msginit: return msginit raise SCons.Errors.StopError(MsginitNotFound, "Could not detect msginit") @@ -428,7 +428,7 @@ def _detect_msgmerge(env): """ Detects *msgmerge(1)* program. """ if 'MSGMERGE' in env: return env['MSGMERGE'] - msgmerge = env.Detect('msgmerge'); + msgmerge = env.Detect('msgmerge') if msgmerge: return msgmerge raise SCons.Errors.StopError(MsgmergeNotFound, "Could not detect msgmerge") diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index e90e768..23cc43b 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -389,7 +389,7 @@ else: is that the file name matches the public class name, and that the path to the file is the same as the package name. """ - return os.path.split(file) + return os.path.split(fn) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 0b340c0..8a94a71 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -238,7 +238,7 @@ class Tool(object): setattr(SCons.Tool, self.name, module) found_module = module - + if found_module is not None: sys.path = oldpythonpath return found_module @@ -1081,11 +1081,11 @@ class ToolInitializer(object): env.Tool(tool) return - # If we fall through here, there was no tool module found. - # This is where we can put an informative error message - # about the inability to find the tool. We'll start doing - # this as we cut over more pre-defined Builder+Tools to use - # the ToolInitializer class. + # If we fall through here, there was no tool module found. + # This is where we can put an informative error message + # about the inability to find the tool. We'll start doing + # this as we cut over more pre-defined Builder+Tools to use + # the ToolInitializer class. def Initializers(env): ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs', '_InternalInstallVersionedLib']) diff --git a/src/engine/SCons/Tool/intelc.py b/src/engine/SCons/Tool/intelc.py index 02da912..9fc0bf7 100644 --- a/src/engine/SCons/Tool/intelc.py +++ b/src/engine/SCons/Tool/intelc.py @@ -73,7 +73,7 @@ def linux_ver_normalize(vstr): m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', vstr) if m: vmaj,vmin,build = m.groups() - return float(vmaj) * 10. + float(vmin) + float(build) / 1000.; + return float(vmaj) * 10. + float(vmin) + float(build) / 1000. else: f = float(vstr) if is_windows: diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index 6e319c1..481f8f5 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -115,7 +115,7 @@ def Jar(env, target = None, source = [], *args, **kw): return jars # they passed no target so make a target implicitly - if target == None: + if target is None: try: # make target from the first source file target = os.path.splitext(str(source[0]))[0] + env.subst('$JARSUFFIX') diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index e1175e0..62f27f2 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -149,9 +149,9 @@ def splitFully(path): return [base] def makeHierarchy(sources): - '''Break a list of files into a hierarchy; for each value, if it is a string, + """Break a list of files into a hierarchy; for each value, if it is a string, then it is a file. If it is a dictionary, it is a folder. The string is - the original path of the file.''' + the original path of the file.""" hierarchy = {} for file in sources: @@ -189,7 +189,7 @@ class _UserGenerator(object): elif SCons.Util.is_List(env['variant']): variants = env['variant'] - if 'DebugSettings' not in env or env['DebugSettings'] == None: + if 'DebugSettings' not in env or env['DebugSettings'] is None: dbg_settings = [] elif SCons.Util.is_Dict(env['DebugSettings']): dbg_settings = [env['DebugSettings']] @@ -1287,7 +1287,7 @@ class _GenerateV10DSP(_DSPGenerator, _GenerateV10User): 'Other Files': ''} cats = sorted([k for k in list(categories.keys()) if self.sources[k]], - key = lambda a: a.lower()) + key = lambda a: a.lower()) # print vcxproj.filters file first self.filters_file.write('\t\n') @@ -1519,7 +1519,7 @@ class _GenerateV7DSW(_DSWGenerator): name = base self.file.write('Project("%s") = "%s", "%s", "%s"\n' % (external_makefile_guid, name, dspinfo['SLN_RELATIVE_FILE_PATH'], dspinfo['GUID'])) - if self.version_num >= 7.1 and self.version_num < 8.0: + if 7.1 <= self.version_num < 8.0: self.file.write('\tProjectSection(ProjectDependencies) = postProject\n' '\tEndProjectSection\n') self.file.write('EndProject\n') diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py index 2e98b07..d4e5f3d 100644 --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -107,7 +107,7 @@ def Package(env, target=None, source=None, **kw): from SCons.Script import GetOption kw['PACKAGETYPE'] = GetOption('package_type') - if kw['PACKAGETYPE'] == None: + if kw['PACKAGETYPE'] is None: if 'Tar' in env['BUILDERS']: kw['PACKAGETYPE']='targz' elif 'Zip' in env['BUILDERS']: @@ -295,10 +295,8 @@ def stripinstallbuilder(target, source, env): It also warns about files which have no install builder attached. """ def has_no_install_location(file): - return not (file.has_builder() and\ - hasattr(file.builder, 'name') and\ - (file.builder.name=="InstallBuilder" or\ - file.builder.name=="InstallAsBuilder")) + return not file.has_builder() and hasattr(file.builder, 'name') \ + and file.builder.name in ["InstallBuilder", "InstallAsBuilder"] if len([src for src in source if has_no_install_location(src)]): warn(Warning, "there are files to package which have no\ diff --git a/src/engine/SCons/Tool/packaging/msi.py b/src/engine/SCons/Tool/packaging/msi.py index c25f856..7e28df3 100644 --- a/src/engine/SCons/Tool/packaging/msi.py +++ b/src/engine/SCons/Tool/packaging/msi.py @@ -114,8 +114,7 @@ def gen_dos_short_file_name(file, filename_set): # thisis1.txt, thisis2.txt etc. duplicate, num = not None, 1 while duplicate: - shortname = "%s%s" % (fname[:8-len(str(num))].upper(),\ - str(num)) + shortname = "%s%s" % (fname[:8-len(str(num))].upper(), str(num)) if len(ext) >= 2: shortname = "%s%s" % (shortname, ext[:4].upper()) @@ -301,7 +300,7 @@ def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, if c.nodeName == 'Directory' and c.attributes['LongName'].value == escape(d)] - if already_created != []: + if already_created: Directory = already_created[0] dir_parts.remove(d) upper_dir += d diff --git a/src/engine/SCons/Tool/rpcgen.py b/src/engine/SCons/Tool/rpcgen.py index 4af5965..5ed5658 100644 --- a/src/engine/SCons/Tool/rpcgen.py +++ b/src/engine/SCons/Tool/rpcgen.py @@ -43,7 +43,7 @@ rpcgen_service = cmd % ('m', '$RPCGENSERVICEFLAGS') rpcgen_xdr = cmd % ('c', '$RPCGENXDRFLAGS') def generate(env): - "Add RPCGEN Builders and construction variables for an Environment." + """Add RPCGEN Builders and construction variables for an Environment.""" client = Builder(action=rpcgen_client, suffix='_clnt.c', src_suffix='.x') header = Builder(action=rpcgen_header, suffix='.h', src_suffix='.x') diff --git a/src/engine/SCons/Tool/tex.py b/src/engine/SCons/Tool/tex.py index 8e09d56..2dfa229 100644 --- a/src/engine/SCons/Tool/tex.py +++ b/src/engine/SCons/Tool/tex.py @@ -790,7 +790,7 @@ def tex_emitter_core(target, source, env, graphics_extensions): for multibibmatch in multibib_re.finditer(content): if Verbose: print("multibib match ",multibibmatch.group(1)) - if multibibmatch != None: + if multibibmatch is not None: baselist = multibibmatch.group(1).split(',') if Verbose: print("multibib list ", baselist) diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index f691cd9..424c694 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -103,7 +103,7 @@ def containsOnly(str, set): return 1 def splitext(path): - "Same as os.path.splitext() but faster." + """Same as os.path.splitext() but faster.""" sep = rightmost_separator(path, os.sep) dot = path.rfind('.') # An ext is only real if it has at least one non-digit char @@ -1370,8 +1370,8 @@ def make_path_relative(path): # The original idea for AddMethod() and RenameFunction() come from the # following post to the ActiveState Python Cookbook: # -# ASPN: Python Cookbook : Install bound methods in an instance -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 +# ASPN: Python Cookbook : Install bound methods in an instance +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 # # That code was a little fragile, though, so the following changes # have been wrung on it: @@ -1388,8 +1388,8 @@ def make_path_relative(path): # the "new" module, as alluded to in Alex Martelli's response to the # following Cookbook post: # -# ASPN: Python Cookbook : Dynamically added methods to a class -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 +# ASPN: Python Cookbook : Dynamically added methods to a class +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 def AddMethod(obj, function, name=None): """ diff --git a/src/script/scons-time.py b/src/script/scons-time.py index aa875f1..d114f9a 100644 --- a/src/script/scons-time.py +++ b/src/script/scons-time.py @@ -58,12 +58,12 @@ def make_temp_file(**kw): return result def HACK_for_exec(cmd, *args): - ''' + """ For some reason, Python won't allow an exec() within a function that also declares an internal function (including lambda functions). This function is a hack that calls exec() in a function with no internal functions. - ''' + """ if not args: exec(cmd) elif len(args) == 1: exec(cmd, args[0]) else: exec(cmd, args[0], args[1]) @@ -147,7 +147,7 @@ class Gnuplotter(Plotter): return line.plot_string() def vertical_bar(self, x, type, label, comment): - if self.get_min_x() <= x and x <= self.get_max_x(): + if self.get_min_x() <= x <= self.get_max_x(): points = [(x, 0), (x, self.max_graph_value(self.get_max_y()))] self.line(points, type, label, comment) diff --git a/src/script/sconsign.py b/src/script/sconsign.py index e7bc271..559dffe 100644 --- a/src/script/sconsign.py +++ b/src/script/sconsign.py @@ -275,7 +275,7 @@ def map_bkids(entry, name): result = [] for i in range(len(bkids)): result.append(nodeinfo_string(bkids[i], bkidsigs[i], " ")) - if result == []: + if not result: return None return "\n ".join(result) -- cgit v0.12 From 2470a8e579846495d57d72712e6642e20578fd36 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 4 Oct 2018 10:37:44 -0600 Subject: Bugfix on packaging init syntax change Signed-off-by: Mats Wichmann --- src/engine/SCons/Tool/packaging/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py index d4e5f3d..6261f98 100644 --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -295,8 +295,9 @@ def stripinstallbuilder(target, source, env): It also warns about files which have no install builder attached. """ def has_no_install_location(file): - return not file.has_builder() and hasattr(file.builder, 'name') \ - and file.builder.name in ["InstallBuilder", "InstallAsBuilder"] + return not (file.has_builder() and hasattr(file.builder, 'name') + and file.builder.name in ["InstallBuilder", "InstallAsBuilder"]) + if len([src for src in source if has_no_install_location(src)]): warn(Warning, "there are files to package which have no\ -- cgit v0.12 From f03ff1b92efbffd410d744869a3b5327f5d211be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 5 Oct 2018 14:35:17 +0200 Subject: add test cases to exhibit bugs in must_[not_]contain() --- testing/framework/TestCommonTests.py | 56 ++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/testing/framework/TestCommonTests.py b/testing/framework/TestCommonTests.py index 7949cb8..c54f33f 100644 --- a/testing/framework/TestCommonTests.py +++ b/testing/framework/TestCommonTests.py @@ -306,6 +306,23 @@ class must_contain_TestCase(TestCommonTestCase): stderr = run_env.stderr() assert stderr == "PASSED\n", stderr + def test_success_index_0(self): + """Test must_contain(): success at index 0""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 contents\\n") + tc.must_contain('file1', "file1 c") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "", stdout + stderr = run_env.stderr() + assert stderr == "PASSED\n", stderr + def test_file_missing(self): """Test must_contain(): file missing""" run_env = self.run_env @@ -1145,6 +1162,9 @@ class must_match_TestCase(TestCommonTestCase): """) expect = lstrip("""\ + match_re: mismatch at line 0: + search re='^file1$' + line='file1 does not match' Unexpected contents of `file1' contents ======================================================================= 1c1 @@ -1324,6 +1344,31 @@ class must_not_contain_TestCase(TestCommonTestCase): stderr = run_env.stderr() assert stderr.find("FAILED") != -1, stderr + def test_failure_index_0(self): + """Test must_not_contain(): failure at index 0""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.write('file1', "file1 does contain contents\\n") + tc.must_not_contain('file1', "file1 does") + tc.run() + """) + expect = lstrip("""\ + File `file1' contains banned string. + Banned string ================================================================== + file1 does + file1 contents ================================================================= + file1 does contain contents + + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == expect, repr(stdout) + stderr = run_env.stderr() + assert stderr.find("FAILED") != -1, stderr + def test_mode(self): """Test must_not_contain(): mode""" run_env = self.run_env @@ -1812,6 +1857,7 @@ class run_TestCase(TestCommonTestCase): """) expect_stdout = lstrip("""\ + match_re: expected 1 lines, found 2 STDOUT ========================================================================= STDERR ========================================================================= @@ -2024,6 +2070,9 @@ class run_TestCase(TestCommonTestCase): """) expect_stdout = lstrip("""\ + match_re: mismatch at line 0: + search re='^Not found$' + line='%(pass_script)s: STDOUT: []' STDOUT ========================================================================= 1c1 < Not found @@ -2053,6 +2102,9 @@ class run_TestCase(TestCommonTestCase): """) expect_stdout = lstrip("""\ + match_re: mismatch at line 0: + search re='^Not found$' + line='%(stderr_script)s: STDERR: []' STDOUT ========================================================================= STDERR ========================================================================= @@ -2286,14 +2338,14 @@ class variables_TestCase(TestCommonTestCase): 'dll_suffix', ] - script = "from __future__ import print_function" + \ + script = "from __future__ import print_function\n" + \ "import TestCommon\n" + \ '\n'.join([ "print(TestCommon.%s)\n" % v for v in variables ]) run_env.run(program=sys.executable, stdin=script) stderr = run_env.stderr() assert stderr == "", stderr - script = "from __future__ import print_function" + \ + script = "from __future__ import print_function\n" + \ "from TestCommon import *\n" + \ '\n'.join([ "print(%s)" % v for v in variables ]) run_env.run(program=sys.executable, stdin=script) -- cgit v0.12 From 0846483714b5f94bd8f346333479a32b39c90e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 5 Oct 2018 14:35:41 +0200 Subject: fixed bugs in must_[not_]_contain() --- src/CHANGES.txt | 3 +++ testing/framework/TestCommon.py | 11 +++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6359560..72ba123 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -6,6 +6,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE + From Paweł Tomulik: + - Fixed must_contain() and must_not_contain() to work with substrings + located at zero offset in a file. From Daniel Moody: - Updated FS.py to handle removal of splitunc function from python 3.7 diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index e55b491..53c0388 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -292,14 +292,13 @@ class TestCommon(TestCmd): return o.index(l) except ValueError: return None - contains = find(file_contents, required) - if not contains: + if find(file_contents, required) is None: print("File `%s' does not contain required string." % file) print(self.banner('Required string ')) print(required) print(self.banner('%s contents ' % file)) print(file_contents) - self.fail_test(not contains) + self.fail_test() def must_contain_all(self, output, input, title=None, find=None): """Ensures that the specified output string (first argument) @@ -514,14 +513,14 @@ class TestCommon(TestCmd): return o.index(l) except ValueError: return None - contains = find(file_contents, banned) - if contains: + + if find(file_contents, banned) is not None: print("File `%s' contains banned string." % file) print(self.banner('Banned string ')) print(banned) print(self.banner('%s contents ' % file)) print(file_contents) - self.fail_test(contains) + self.fail_test() def must_not_contain_any_line(self, output, lines, title=None, find=None): """Ensures that the specified output string (first argument) -- cgit v0.12 From ec2f065ab7ab954c08ab6cae2d1e8b0a210be401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 5 Oct 2018 16:16:06 +0200 Subject: make the must[_not]_contain_...() functions compatible with str.find --- testing/framework/TestCommon.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index 53c0388..6352214 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -275,8 +275,8 @@ class TestCommon(TestCmd): reading the file; current implementation will convert. mode (string): file open mode. find (func): optional custom search routine. Must take the - form "find(output, line)" returning non-zero on success - and None on failure. + form "find(output, line)" non-negative integer on success + and None, or -1, on failure. Calling test exits FAILED if search result is false """ @@ -292,7 +292,7 @@ class TestCommon(TestCmd): return o.index(l) except ValueError: return None - if find(file_contents, required) is None: + if find(file_contents, required) in (None,-1): print("File `%s' does not contain required string." % file) print(self.banner('Required string ')) print(required) @@ -321,7 +321,7 @@ class TestCommon(TestCmd): if is_List(output): output = os.newline.join(output) - if find(output, input) is None: + if find(output, input) in (None, -1): if title is None: title = 'output' print('Missing expected input from {}:'.format(title)) @@ -352,7 +352,7 @@ class TestCommon(TestCmd): output = '\n'.join(output) for line in lines: - if find(output, line) is None: + if find(output, line) in (None, -1): missing.append(line) if missing: @@ -383,7 +383,7 @@ class TestCommon(TestCmd): except ValueError: return None for line in lines: - if find(output, line) is not None: + if find(output, line) not in (None, -1): return if title is None: @@ -425,7 +425,7 @@ class TestCommon(TestCmd): missing = [] for line in exp: found = find(out, line) - if found is None: + if found in (None, -1): missing.append(line) else: out.pop(found) @@ -514,7 +514,7 @@ class TestCommon(TestCmd): except ValueError: return None - if find(file_contents, banned) is not None: + if find(file_contents, banned) not in (None, -1): print("File `%s' contains banned string." % file) print(self.banner('Banned string ')) print(banned) @@ -541,7 +541,7 @@ class TestCommon(TestCmd): return None unexpected = [] for line in lines: - if find(output, line) is not None: + if find(output, line) not in (None, -1): unexpected.append(line) if unexpected: -- cgit v0.12 From 0afde1f83a5682dc46493aa341ff0a813492839e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 5 Oct 2018 18:16:36 +0200 Subject: fixed some docs in TestCommon.py and refactored a little --- testing/framework/TestCommon.py | 98 ++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 59 deletions(-) diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index 6352214..60e0abd 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -206,6 +206,28 @@ def separate_files(flist): missing.append(f) return existing, missing +def contains(seq, subseq, find): + # Returns True or False. + if find is None: + return subseq in seq + else: + f = find(seq, subseq) + return f not in (None, -1) and f is not False + +def find_index(seq, subseq, find): + # Returns either an index of the subseq within the seq, or None. + # Accepts a function find(seq, subseq), which returns an integer on success + # and either: None, False, or -1, on failure. + if find is None: + try: + return seq.index(subseq) + except ValueError: + return None + else: + i = find(seq, subseq) + return None if (i in (None, -1) or i is False) else i + + if os.name == 'posix': def _failed(self, status = 0): if self.status is None or status is None: @@ -276,7 +298,7 @@ class TestCommon(TestCmd): mode (string): file open mode. find (func): optional custom search routine. Must take the form "find(output, line)" non-negative integer on success - and None, or -1, on failure. + and None, False, or -1, on failure. Calling test exits FAILED if search result is false """ @@ -286,13 +308,8 @@ class TestCommon(TestCmd): # (str) type in that, so convert. required = to_bytes(required) file_contents = self.read(file, mode) - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - if find(file_contents, required) in (None,-1): + + if not contains(file_contents, required, find): print("File `%s' does not contain required string." % file) print(self.banner('Required string ')) print(required) @@ -308,20 +325,13 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching + function, of the form "find(output, line), to use when searching for lines in the output. """ - if find is None: - def find(o, i): - try: - return o.index(i) - except ValueError: - return None - if is_List(output): output = os.newline.join(output) - if find(output, input) in (None, -1): + if not contains(output, input, find): if title is None: title = 'output' print('Missing expected input from {}:'.format(title)) @@ -338,21 +348,15 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching + function, of the form "find(output, line), to use when searching for lines in the output. """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None missing = [] if is_List(output): output = '\n'.join(output) for line in lines: - if find(output, line) in (None, -1): + if not contains(output, line, find): missing.append(line) if missing: @@ -373,17 +377,11 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching + function, of the form "find(output, line), to use when searching for lines in the output. """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None for line in lines: - if find(output, line) not in (None, -1): + if contains(output, line, find): return if title is None: @@ -404,7 +402,7 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching + function, of the form "find(output, line), to use when searching for lines in the output. The function must return the index of the found line in the output, or None if the line is not found. """ @@ -416,19 +414,13 @@ class TestCommon(TestCmd): if sorted(out) == sorted(exp): # early out for exact match return - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None missing = [] for line in exp: - found = find(out, line) - if found in (None, -1): + i = find_index(out, line, find) + if i is None: missing.append(line) else: - out.pop(found) + out.pop(i) if not missing and not out: # all lines were matched @@ -507,14 +499,8 @@ class TestCommon(TestCmd): """Ensures that the specified file doesn't contain the banned text. """ file_contents = self.read(file, mode) - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - - if find(file_contents, banned) not in (None, -1): + + if contains(file_contents, banned, find): print("File `%s' contains banned string." % file) print(self.banner('Banned string ')) print(banned) @@ -530,18 +516,12 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching + function, of the form "find(output, line), to use when searching for lines in the output. """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None unexpected = [] for line in lines: - if find(output, line) not in (None, -1): + if contains(output, line, find): unexpected.append(line) if unexpected: -- cgit v0.12 From 488e42055a96491962e052f255acca7835fe2735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 5 Oct 2018 21:48:37 +0200 Subject: update src/CHANGES.txt --- src/CHANGES.txt | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 72ba123..4fefe98 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -6,22 +6,18 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - From Paweł Tomulik: - - Fixed must_contain() and must_not_contain() to work with substrings - located at zero offset in a file. - From Daniel Moody: - Updated FS.py to handle removal of splitunc function from python 3.7 - Updated the vc.py to ignore MSVS versions where not compiler could be found From Matthew Marinets: - - Fixed an issue that caused the Java emitter to incorrectly parse arguments to constructors that + - Fixed an issue that caused the Java emitter to incorrectly parse arguments to constructors that implemented a class. From Bernard Blackham: - Fixed handling of side-effects in task master (fixes #3013). - + From Ray Donnelly: - Fix the PATH created by scons.bat (and other .bat files) to provide a normalized PATH. Some pythons in the 3.6 series are no longer able to handle paths which @@ -38,7 +34,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Fix issue # 3106 MSVC if using MSVC_BATCH and target dir had a space would fail due to quirk in MSVC's handling of escaped targetdirs when batch compiling. - Re-Enable parallel SCons (-j) when running via Pypy - - Move SCons test framework files to testing/framework and remove all references to QMtest. + - Move SCons test framework files to testing/framework and remove all references to QMtest. QMTest has not been used by SCons for some time now. - Updated logic for mingw and clang on win32 to search default tool install paths if not found in normal SCons PATH. If the user specifies PATH or tool specific paths they @@ -77,7 +73,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Updated gettext tools to setup default paths for windows with Cygwin/MinGW setups - Add common location for default paths for cygwin and mingw in Platform modules - Updated YACC tool to work on windows with Cygwin/MinGW setups - - Set the pickling protocal back to highest which was causing issues + - Set the pickling protocal back to highest which was causing issues with variant dir tests. This will cause issues if reading sconsigns pickled with the previous lower protocal. @@ -99,6 +95,10 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE Specifically, this fixes the test cases use by Configure.CheckCC() which would fail when using -Wstrict-prototypes. + From Paweł Tomulik: + - Fixed must_contain(), must_not_contain(), etc. to work with substrings + located at zero offset. + From Richard West: - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. Allows easier debugging within Visual Studio @@ -127,11 +127,11 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Quiet py3 warning in UtilTests.py - Fix tests specifying octal constants for py3 - Fix must_contain tests for py3 - - RPM package generation: + - RPM package generation: - Fix supplying a build architecture - Disable auto debug package generation on certain rpmbuild versions - Adjust some tests to only supply build-id file on certain rpmbuild versions - - Tests now use a file fixture for the repeated (trivial) main.c program. + - Tests now use a file fixture for the repeated (trivial) main.c program. - Document and comment cleanup. - Added new Environment Value X_RPM_EXTRADEFS to supply custom settings to the specfile without adding specific logic for each one to scons. @@ -157,9 +157,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE - Add xz compression format to packaging choices. - Syntax cleanups - trailing blanks, use "is" to compare with None, etc. Three uses of variables not defined are changed. - + From Hao Wu - - typo in customized decider example in user guide + - typo in customized decider example in user guide From Hao Wu - Replace usage of unittest.TestSuite with unittest.main() (fix #3113) @@ -173,24 +173,24 @@ RELEASE 3.0.1 - Mon, 12 Nov 2017 15:31:33 -0700 - Jar can take multiple targets, and will make a duplicate jar from the sources for each target - Added some warnings in case the Jar builder makes an implicit target - Added Jar method and changed jar build to be more specific. Jar method will take in - directories or classes as source. Added more tests to JAR to ensure the jar was + directories or classes as source. Added more tests to JAR to ensure the jar was packaged with the correct compiled class files. - - Added a No result test case to handle bug which seems unrelated to java in the + - Added a No result test case to handle bug which seems unrelated to java in the swig-dependencies.py test, more info here: http://scons.tigris.org/issues/show_bug.cgi?id=2907 - Added a travis script to test on ubuntu trusty now that the project is on github - so that Continuus Integration tests can be run automatically. It tests most case and considers - no result a pass as well. Improving this script can install more dependincies allowing for more + so that Continuus Integration tests can be run automatically. It tests most case and considers + no result a pass as well. Improving this script can install more dependincies allowing for more tests to be run. - + From Daniel Moody: - Updated the Jar Builder tool in Tool/__init__.py so that is doesn't force class files as sources, allowing directories to be passed, which was causing test/Java/JAR.py to fail. From William Deegan: - Fix issue where code in utility routine to_String_for_subst() had code whose result was never - properly returned. + properly returned. (Found by: James Rinkevich https://pairlist4.pair.net/pipermail/scons-users/2017-October/006358.html ) - - Fixed Variables.GenerateHelpText() to now use the sort parameter. Due to incorrect 2to3 fixer changes + - Fixed Variables.GenerateHelpText() to now use the sort parameter. Due to incorrect 2to3 fixer changes 8 years ago it was being used as a boolean parameter. Now you can specify sort to be a callable, or boolean value. (True = normal sort). Manpage also updated. - Fixed Tool loading logic from exploding sys.path with many site_scons/site_tools prepended on py3. @@ -209,7 +209,7 @@ RELEASE 3.0.1 - Mon, 12 Nov 2017 15:31:33 -0700 From Zachary Tessler: - Fix incorrect warning for repeated identical builder calls that use overrides - + RELEASE 3.0.0 - Mon, 18 Sep 2017 08:32:04 -0700 NOTE: This is a major release. You should expect that some targets may rebuild when upgrading. @@ -282,9 +282,9 @@ will cause rebuilds. - Add support for Visual Studio 2017. This support requires vswhere.exe a helper tool installed with newer installs of 2017. SCons expects it to be located at "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" - It can be downloaded separately at + It can be downloaded separately at https://github.com/Microsoft/vswhere - + From Tom Tanner: - Allow nested $( ... $) sections -- cgit v0.12 From 8c8deec7bee7fd90cc4006d583e5f4e5ad9f3ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 5 Oct 2018 22:07:56 +0200 Subject: fixed minor typos --- testing/framework/.TestCommonTests.py.swp | Bin 0 -> 16384 bytes testing/framework/TestCommon.py | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 testing/framework/.TestCommonTests.py.swp diff --git a/testing/framework/.TestCommonTests.py.swp b/testing/framework/.TestCommonTests.py.swp new file mode 100644 index 0000000..fcee08e Binary files /dev/null and b/testing/framework/.TestCommonTests.py.swp differ diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index 60e0abd..6b4b0bd 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -325,7 +325,7 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(output, line), to use when searching + function, of the form "find(output, line)", to use when searching for lines in the output. """ if is_List(output): @@ -348,7 +348,7 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(output, line), to use when searching + function, of the form "find(output, line)", to use when searching for lines in the output. """ missing = [] @@ -377,7 +377,7 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(output, line), to use when searching + function, of the form "find(output, line)", to use when searching for lines in the output. """ for line in lines: @@ -402,7 +402,7 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(output, line), to use when searching + function, of the form "find(output, line)", to use when searching for lines in the output. The function must return the index of the found line in the output, or None if the line is not found. """ @@ -516,7 +516,7 @@ class TestCommon(TestCmd): of output being searched, and only shows up in failure output. An optional fourth argument can be used to supply a different - function, of the form "find(output, line), to use when searching + function, of the form "find(output, line)", to use when searching for lines in the output. """ unexpected = [] -- cgit v0.12 From 01203bc7ac4f71fe0b76da0248f7139539b2d3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Mon, 8 Oct 2018 12:19:34 +0200 Subject: minor adjustment to src/CHANGES.txt --- src/CHANGES.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 4fefe98..c8ec110 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -6,6 +6,7 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE + From Daniel Moody: - Updated FS.py to handle removal of splitunc function from python 3.7 - Updated the vc.py to ignore MSVS versions where not compiler could be found @@ -17,7 +18,6 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE From Bernard Blackham: - Fixed handling of side-effects in task master (fixes #3013). - From Ray Donnelly: - Fix the PATH created by scons.bat (and other .bat files) to provide a normalized PATH. Some pythons in the 3.6 series are no longer able to handle paths which @@ -96,8 +96,9 @@ RELEASE 3.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE would fail when using -Wstrict-prototypes. From Paweł Tomulik: - - Fixed must_contain(), must_not_contain(), etc. to work with substrings - located at zero offset. + - In the testing framework, module TestCommon, fixed must_contain(), + must_not_contain(), and related methods of TestCommon class to work with + substrings located at zero offset. From Richard West: - Add SConstruct.py, Sconstruct.py, sconstruct.py to the search path for the root SConstruct file. -- cgit v0.12