From f1ac141da175ed8939044f9a55146980cf4634a8 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Fri, 24 Jul 2009 16:30:08 +0000 Subject: Update the Test{Cmd,Common}.py 0.36, with better diff reporting (specifically of output matches using regular expressions). Update tests for corresponding inteface changes. Add use of diff_re() to test/sconsign/script/Configure.py so we can get accurate information about its buildbot failure. --- QMTest/TestCmd.py | 111 +++++++++++++++++++++++++++++++--- QMTest/TestCommon.py | 66 +------------------- test/SConstruct.py | 2 +- test/option-n.py | 3 +- test/scons-time/run/option/quiet.py | 4 +- test/scons-time/run/option/verbose.py | 4 +- test/sconsign/script/Configure.py | 3 +- 7 files changed, 114 insertions(+), 79 deletions(-) diff --git a/QMTest/TestCmd.py b/QMTest/TestCmd.py index cff50c7..54a32bc 100644 --- a/QMTest/TestCmd.py +++ b/QMTest/TestCmd.py @@ -25,6 +25,7 @@ There are a bunch of keyword arguments available at instantiation: subdir = 'subdir', verbose = Boolean, match = default_match_function, + diff = default_diff_function, combine = Boolean) There are a bunch of methods that let you do different things: @@ -103,6 +104,11 @@ There are a bunch of methods that let you do different things: test.symlink(target, link) + test.banner(string) + test.banner(string, width) + + test.diff(actual, expected) + test.match(actual, expected) test.match_exact("actual 1\nactual 2\n", "expected 1\nexpected 2\n") @@ -164,6 +170,24 @@ in the same way as the match_*() methods described above. test = TestCmd.TestCmd(match = TestCmd.match_re_dotall) +The TestCmd module provides unbound 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) + +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. @@ -191,8 +215,8 @@ version. # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. __author__ = "Steven Knight " -__revision__ = "TestCmd.py 0.35.D001 2009/02/08 07:10:39 knight" -__version__ = "0.35" +__revision__ = "TestCmd.py 0.36.D001 2009/07/24 08:45:26 knight" +__version__ = "0.36" import errno import os @@ -220,6 +244,11 @@ __all__ = [ 'TestCmd' ] +try: + import difflib +except ImportError: + __all__.append('simple_diff') + def is_List(e): return type(e) is types.ListType \ or isinstance(e, UserList.UserList) @@ -424,6 +453,36 @@ def match_re_dotall(lines = None, res = None): if expr.match(lines): return 1 +try: + import difflib +except ImportError: + pass +else: + 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. + """ + 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(map(lambda l: '< ' + l, a[a1:a2])) + elif op == 'insert': + result.append("%da%s" % (a1, comma(b1, b2))) + result.extend(map(lambda l: '> ' + l, b[b1:b2])) + elif op == 'replace': + result.append("%sc%s" % (comma(a1, a2), comma(b1, b2))) + result.extend(map(lambda l: '< ' + l, a[a1:a2])) + result.append('---') + result.extend(map(lambda l: '> ' + l, b[b1:b2])) + return result + def diff_re(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n'): """ @@ -456,8 +515,11 @@ def diff_re(a, b, fromfile='', tofile='', return result if os.name == 'java': + python_executable = os.path.join(sys.prefix, 'jython') + else: + python_executable = sys.executable if sys.platform == 'win32': @@ -756,6 +818,7 @@ def recv_some(p, t=.1, e=1, tr=5, stderr=0): time.sleep(max((x-time.time())/tr, 0)) return ''.join(y) +# TODO(3.0: rewrite to use memoryview() def send_all(p, data): while len(data): sent = p.send(data) @@ -776,6 +839,7 @@ class TestCmd: subdir = None, verbose = None, match = None, + diff = None, combine = 0, universal_newlines = 1): self._cwd = os.getcwd() @@ -791,9 +855,20 @@ class TestCmd: self.combine = combine self.universal_newlines = universal_newlines if not match is None: - self.match_func = match + self.match_function = match else: - self.match_func = match_re + self.match_function = match_re + if not diff is None: + self.diff_function = diff + else: + try: + difflib + except NameError: + pass + else: + self.diff_function = simple_diff + #self.diff_function = difflib.context_diff + #self.diff_function = difflib.unified_diff self._dirlist = [] self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0} if os.environ.has_key('PRESERVE') and not os.environ['PRESERVE'] is '': @@ -826,6 +901,14 @@ class TestCmd: 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)) + if os.name == 'posix': def escape(self, arg): @@ -930,9 +1013,21 @@ class TestCmd: """ self.description = description -# def diff(self): -# """Diff two arrays. -# """ + try: + difflib + except NameError: + def diff(self, a, b, name, *args, **kw): + print self.banner('Expected %s' % name) + print a + print self.banner('Actual %s' % name) + print b + else: + def diff(self, a, b, name, *args, **kw): + print self.banner(name) + args = (a.splitlines(), b.splitlines()) + args + lines = apply(self.diff_function, args, kw) + for l in lines: + print l def fail_test(self, condition = 1, function = None, skip = 0): """Cause the test to fail. @@ -954,7 +1049,7 @@ class TestCmd: def match(self, lines, matches): """Compare actual and expected file contents. """ - return self.match_func(lines, matches) + return self.match_function(lines, matches) def match_exact(self, lines, matches): """Compare actual and expected file contents. diff --git a/QMTest/TestCommon.py b/QMTest/TestCommon.py index 5431b8c..5356fac 100644 --- a/QMTest/TestCommon.py +++ b/QMTest/TestCommon.py @@ -87,8 +87,8 @@ The TestCommon module also provides the following variables # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. __author__ = "Steven Knight " -__revision__ = "TestCommon.py 0.35.D001 2009/02/08 07:10:39 knight" -__version__ = "0.35" +__revision__ = "TestCommon.py 0.36.D001 2009/07/24 08:45:26 knight" +__version__ = "0.36" import copy import os @@ -169,36 +169,6 @@ else: dll_prefix = 'lib' dll_suffix = '.so' -try: - import difflib -except ImportError: - pass -else: - 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. - """ - 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(map(lambda l: '< ' + l, a[a1:a2])) - elif op == 'insert': - result.append("%da%s" % (a1, comma(b1, b2))) - result.extend(map(lambda l: '> ' + l, b[b1:b2])) - elif op == 'replace': - result.append("%sc%s" % (comma(a1, a2), comma(b1, b2))) - result.extend(map(lambda l: '< ' + l, a[a1:a2])) - result.append('---') - result.extend(map(lambda l: '> ' + l, b[b1:b2])) - return result - def is_List(e): return type(e) is types.ListType \ or isinstance(e, UserList.UserList) @@ -247,38 +217,6 @@ class TestCommon(TestCmd): """ apply(TestCmd.__init__, [self], kw) os.chdir(self.workdir) - try: - difflib - except NameError: - pass - else: - self.diff_function = simple_diff - #self.diff_function = difflib.context_diff - #self.diff_function = difflib.unified_diff - - 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)) - - try: - difflib - except NameError: - def diff(self, a, b, name, *args, **kw): - print self.banner('Expected %s' % name) - print a - print self.banner('Actual %s' % name) - print b - else: - def diff(self, a, b, name, *args, **kw): - print self.banner(name) - args = (a.splitlines(), b.splitlines()) + args - lines = apply(self.diff_function, args, kw) - for l in lines: - print l def must_be_writable(self, *files): """Ensures that the specified file(s) exist and are writable. diff --git a/test/SConstruct.py b/test/SConstruct.py index 4b80f3e..f061728 100644 --- a/test/SConstruct.py +++ b/test/SConstruct.py @@ -36,7 +36,7 @@ test.run(arguments = ".", scons: \*\*\* No SConstruct file found. """ + TestSCons.file_expr) -test.match_func = TestCmd.match_exact +test.match_function = TestCmd.match_exact wpath = test.workpath() diff --git a/test/option-n.py b/test/option-n.py index abb0284..b8dde0f 100644 --- a/test/option-n.py +++ b/test/option-n.py @@ -168,7 +168,8 @@ test.fail_test(os.path.exists(test.workpath('build', 'f4.in'))) # test Configure-calls in conjunction with -n test.subdir('configure') -test.match_func = TestSCons.match_re_dotall +test.match_function = TestSCons.match_re_dotall +test.diff_function = TestSCons.diff_re test.write('configure/SConstruct', """def CustomTest(context): def userAction(target,source,env): diff --git a/test/scons-time/run/option/quiet.py b/test/scons-time/run/option/quiet.py index 58b5e82..2910e8e 100644 --- a/test/scons-time/run/option/quiet.py +++ b/test/scons-time/run/option/quiet.py @@ -34,8 +34,8 @@ import TestSCons_time python = TestSCons_time.python -test = TestSCons_time.TestSCons_time(match = TestSCons_time.match_re) -test.diff_function = TestSCons_time.diff_re +test = TestSCons_time.TestSCons_time(match = TestSCons_time.match_re, + diff = TestSCons_time.diff_re) scons_py = re.escape(test.workpath('src', 'script', 'scons.py')) src_engine = re.escape(test.workpath('src', 'engine')) diff --git a/test/scons-time/run/option/verbose.py b/test/scons-time/run/option/verbose.py index 8b0be4a..7f693d1 100644 --- a/test/scons-time/run/option/verbose.py +++ b/test/scons-time/run/option/verbose.py @@ -35,8 +35,8 @@ import TestSCons_time _python_ = re.escape(TestSCons_time._python_) -test = TestSCons_time.TestSCons_time(match = TestSCons_time.match_re) -test.diff_function = TestSCons_time.diff_re +test = TestSCons_time.TestSCons_time(match = TestSCons_time.match_re, + diff = TestSCons_time.diff_re) scons_py = re.escape(test.workpath('src', 'script', 'scons.py')) src_engine = re.escape(test.workpath('src', 'engine')) diff --git a/test/sconsign/script/Configure.py b/test/sconsign/script/Configure.py index fb54dd2..865b607 100644 --- a/test/sconsign/script/Configure.py +++ b/test/sconsign/script/Configure.py @@ -37,7 +37,8 @@ import TestSConsign _obj = TestSCons._obj -test = TestSConsign.TestSConsign(match = TestSConsign.match_re) +test = TestSConsign.TestSConsign(match = TestSConsign.match_re, + diff = TestSConsign.diff_re) CC = test.detect('CC', norm=1) CC_dir, CC_file = os.path.split(CC) -- cgit v0.12