diff options
Diffstat (limited to 'testing/framework/TestCmd.py')
| -rw-r--r-- | testing/framework/TestCmd.py | 165 | 
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  | 
