summaryrefslogtreecommitdiff
path: root/testing/framework/TestCmd.py
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2019-07-23 16:55:05 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2019-07-23 16:55:05 +0200
commitd240a76169c685e488fe7352adec69d39c60ef9f (patch)
tree144aeb4702d258b0cfaf99cffb9f21e94b026459 /testing/framework/TestCmd.py
parent6ca68f29862bc7cbe5428426eee600cb1f3c05c1 (diff)
parentc2d9eaeffe9d77d8d5087fb21a3256fde32047d4 (diff)
Merge branch 'feature/upstream' into develop
Diffstat (limited to 'testing/framework/TestCmd.py')
-rw-r--r--testing/framework/TestCmd.py165
1 files changed, 119 insertions, 46 deletions
diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py
index a6a8045..81e03f3 100644
--- a/testing/framework/TestCmd.py
+++ b/testing/framework/TestCmd.py
@@ -366,13 +366,11 @@ else:
def is_String(e):
return isinstance(e, (str, unicode, UserString))
-tempfile.template = 'testcmd.'
+testprefix = 'testcmd.'
if os.name in ('posix', 'nt'):
- tempfile.template = 'testcmd.' + str(os.getpid()) + '.'
-else:
- tempfile.template = 'testcmd.'
+ testprefix += "%s." % str(os.getpid())
-re_space = re.compile('\s')
+re_space = re.compile(r'\s')
def _caller(tblist, skip):
@@ -470,6 +468,14 @@ def pass_test(self=None, condition=1, function=None):
def match_exact(lines=None, matches=None, newline=os.sep):
"""
+ Match function using exact match.
+
+ :param lines: data lines
+ :type lines: str or list[str]
+ :param matches: expected lines to match
+ :type matches: str or list[str]
+ :param newline: line separator
+ :returns: an object (1) on match, else None, like re.match
"""
if isinstance(lines, bytes) or bytes is str:
@@ -480,30 +486,51 @@ def match_exact(lines=None, matches=None, newline=os.sep):
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 None
+ for line, match in zip(lines, matches):
+ if line != match:
+ return None
return 1
def match_caseinsensitive(lines=None, matches=None):
"""
+ Match function using case-insensitive matching.
+
+ Only a simplistic comparison is done, based on lowercasing the
+ strings. This has plenty of holes for unicode data using
+ non-English languages.
+
+ TODO: casefold() is better than lower() if we don't need Py2 support.
+
+ :param lines: data lines
+ :type lines: str or list[str]
+ :param matches: expected lines to match
+ :type matches: str or list[str]
+ :returns: True or False
+ :returns: an object (1) on match, else None, like re.match
"""
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 None
+ for line, match in zip(lines, matches):
+ if line.lower() != match.lower():
+ return None
return 1
def match_re(lines=None, res=None):
"""
+ Match function using line-by-line regular expression match.
+
+ :param lines: data lines
+ :type lines: str or list[str]
+ :param res: regular expression(s) for matching
+ :type res: str or list[str]
+ :returns: an object (1) on match, else None, like re.match
"""
if not is_List(lines):
# CRs mess up matching (Windows) so split carefully
@@ -512,29 +539,39 @@ def match_re(lines=None, res=None):
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] + "$"
+ return None
+ for i, (line, regex) in enumerate(zip(lines, res)):
+ s = r"^{}$".format(regex)
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
+ if not expr.search(line):
+ miss_tmpl = "match_re: mismatch at line {}:\n search re='{}'\n line='{}'"
+ print(miss_tmpl.format(i, s, line))
+ return None
return 1
def match_re_dotall(lines=None, res=None):
"""
+ Match function using regular expression match.
+
+ Unlike match_re, the arguments are converted to strings (if necessary)
+ and must match exactly.
+
+ :param lines: data lines
+ :type lines: str or list[str]
+ :param res: regular expression(s) for matching
+ :type res: str or list[str]
+ :returns: a match object, or None as for re.match
"""
if not isinstance(lines, str):
lines = "\n".join(lines)
if not isinstance(res, str):
res = "\n".join(res)
- s = "^" + res + "$"
+ s = r"^{}$".format(res)
try:
expr = re.compile(s, re.DOTALL)
except re.error as e:
@@ -544,11 +581,32 @@ def match_re_dotall(lines=None, res=None):
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.
+ fromfiledate='', tofiledate='', n=0, lineterm=''):
+ r"""
+ Compare two sequences of lines; generate the delta as a simple diff.
+
+ Similar to difflib.context_diff and difflib.unified_diff but
+ output is like from the 'diff" command without arguments. The function
+ keeps the same signature as the difflib ones so they will be
+ interchangeable, but except for lineterm, the arguments beyond the
+ two sequences are ignored in this version. By default, the
+ diff is not created with trailing newlines, set the lineterm
+ argument to '\n' to do so.
+
+ :raises re.error: if a regex fails to compile
+
+ Example:
+
+ >>> print(''.join(simple_diff('one\ntwo\nthree\nfour\n'.splitlines(True),
+ ... 'zero\none\ntree\nfour\n'.splitlines(True), lineterm='\n')))
+ 0a1
+ > zero
+ 2,3c3
+ < two
+ < three
+ ---
+ > tree
+
"""
a = [to_str(q) for q in a]
b = [to_str(q) for q in b]
@@ -556,25 +614,30 @@ def simple_diff(a, b, fromfile='', tofile='',
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]])
+ yield "{}d{}{}".format(comma(a1, a2), b1, lineterm)
+ for l in a[a1:a2]:
+ yield '< ' + l
elif op == 'insert':
- result.append("%da%s" % (a1, comma(b1, b2)))
- result.extend(['> ' + l for l in b[b1:b2]])
+ yield "{}a{}{}".format(a1, comma(b1, b2), lineterm)
+ for l in b[b1:b2]:
+ yield '> ' + l
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
+ yield "{}c{}{}".format(comma(a1, a2), comma(b1, b2), lineterm)
+ for l in a[a1:a2]:
+ yield '< ' + l
+ yield '---{}'.format(lineterm)
+ for l in b[b1:b2]:
+ yield '> ' + l
def diff_re(a, b, fromfile='', tofile='',
fromfiledate='', tofiledate='', n=3, lineterm='\n'):
"""
+ Compare a and b (lists of strings) where a are regexes.
+
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
@@ -587,9 +650,8 @@ def diff_re(a, b, fromfile='', tofile='',
a = a + [''] * (-diff)
elif diff > 0:
b = b + [''] * diff
- i = 0
- for aline, bline in zip(a, b):
- s = "^" + aline + "$"
+ for i, (aline, bline) in enumerate(zip(a, b)):
+ s = r"^{}$".format(aline)
try:
expr = re.compile(s)
except re.error as e:
@@ -600,7 +662,6 @@ def diff_re(a, b, fromfile='', tofile='',
result.append('< ' + repr(a[i]))
result.append('---')
result.append('> ' + repr(b[i]))
- i = i + 1
return result
@@ -974,6 +1035,12 @@ class TestCmd(object):
self.subdir(subdir)
self.fixture_dirs = []
+ try:
+ self.fixture_dirs = (os.environ['FIXTURE_DIRS']).split(os.pathsep)
+ except KeyError:
+ pass
+
+
def __del__(self):
self.cleanup()
@@ -1026,7 +1093,7 @@ class TestCmd(object):
condition = self.condition
if self._preserve[condition]:
for dir in self._dirlist:
- print(u"Preserved directory " + dir + "\n")
+ print(u"Preserved directory " + dir)
else:
list = self._dirlist[:]
list.reverse()
@@ -1242,6 +1309,7 @@ class TestCmd(object):
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
@@ -1261,6 +1329,7 @@ class TestCmd(object):
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
@@ -1302,6 +1371,7 @@ class TestCmd(object):
"""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
@@ -1343,6 +1413,7 @@ class TestCmd(object):
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
@@ -1439,6 +1510,7 @@ class TestCmd(object):
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
@@ -1455,7 +1527,7 @@ class TestCmd(object):
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:
+ 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:
@@ -1629,7 +1701,7 @@ class TestCmd(object):
try:
os.mkdir(new)
except OSError as e:
- print("Got error :%s"%e)
+ print("Got error creating dir: %s :%s" % (sub, e))
pass
else:
count = count + 1
@@ -1662,10 +1734,11 @@ class TestCmd(object):
"""
if path is None:
try:
- path = tempfile.mktemp(prefix=tempfile.template)
+ path = tempfile.mkdtemp(prefix=testprefix)
except TypeError:
- path = tempfile.mktemp()
- os.mkdir(path)
+ path = tempfile.mkdtemp()
+ else:
+ os.mkdir(path)
# Symlinks in the path will report things
# differently from os.getcwd(), so chdir there