changeset 351:1f22d4b10fef

tests: Improve bake tests output, add support for partial output checks.
author Ludovic Chabant <ludovic@chabant.com>
date Fri, 17 Apr 2015 16:08:23 -0700
parents b8ff1780b491
children 498a917cd2d4
files tests/conftest.py
diffstat 1 files changed, 75 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/tests/conftest.py	Fri Apr 17 16:06:02 2015 -0700
+++ b/tests/conftest.py	Fri Apr 17 16:08:23 2015 -0700
@@ -80,33 +80,37 @@
 
         if expected_output_files:
             actual = fs.getStructure('kitchen/_counter')
-            error = _compare_dicts(actual, expected_output_files)
+            error = _compare_dicts(expected_output_files, actual)
             if error:
                 raise ExpectedBakeOutputError(error)
 
         if expected_partial_files:
-            for key, content in expected_partial_files.items():
+            keys = list(sorted(expected_partial_files.keys()))
+            for key in keys:
                 try:
-                    actual = fs.getStructure('kitchen/_counter/' +
+                    actual = fs.getFileEntry('kitchen/_counter/' +
                                              key.lstrip('/'))
-                except Exception:
+                except Exception as e:
                     raise ExpectedBakeOutputError([
-                        "Missing expected output file: %s" % key])
-                if not isinstance(actual, str):
-                    raise ExpectedBakeOutputError([
-                        "Expected output file is a directory: %s" % key])
-                if actual != content:
-                    raise ExpectedBakeOutputError([
-                        "Unexpected output file contents:",
-                        "%s: %s" % (key, content),
-                        "%s: %s" % (key, actual)])
+                        "Can't access output file %s: %s" % (key, e)])
+
+                expected = expected_partial_files[key]
+                # HACK because for some reason PyYAML adds a new line for those
+                # and I have no idea why.
+                expected = expected.rstrip('\n')
+                cmpres = _compare_str(expected, actual, key)
+                if cmpres:
+                    raise ExpectedBakeOutputError(cmpres)
 
     def reportinfo(self):
         return self.fspath, 0, "bake: %s" % self.name
 
     def repr_failure(self, excinfo):
         if isinstance(excinfo.value, ExpectedBakeOutputError):
-            return ('\n'.join(excinfo.value.args[0]))
+            return ('\n'.join(
+                ['Unexpected bake output. Left is expected output, '
+                    'right is actual output'] +
+                excinfo.value.args[0]))
         return super(BakeTestItem, self).repr_failure(excinfo)
 
 
@@ -123,6 +127,23 @@
             _add_mock_files(fs, path, subspec)
 
 
+def _compare(left, right, path):
+    if type(left) != type(right):
+        return (["Different items: ",
+                 "%s: %s" % (path, pprint.pformat(left)),
+                 "%s: %s" % (path, pprint.pformat(right))])
+    if isinstance(left, str):
+        return _compare_str(left, right, path)
+    elif isinstance(left, dict):
+        return _compare_dicts(left, right, path)
+    elif isinstance(left, list):
+        return _compare_lists(left, right, path)
+    elif left != right:
+        return (["Different items: ",
+                 "%s: %s" % (path, pprint.pformat(left)),
+                 "%s: %s" % (path, pprint.pformat(right))])
+
+
 def _compare_dicts(left, right, basepath=''):
     key_diff = set(left.keys()) ^ set(right.keys())
     if key_diff:
@@ -140,45 +161,49 @@
         lv = left[key]
         rv = right[key]
         childpath = basepath + '/' + key
-        if type(lv) != type(rv):
-            return (["Different items: ",
-                     "%s/%s: %s" % (basepath, key, pprint.pformat(lv)),
-                     "%s/%s: %s" % (basepath, key, pprint.pformat(rv))])
+        cmpres = _compare(lv, rv, childpath)
+        if cmpres:
+            return cmpres
+    return None
+
 
-        if isinstance(lv, dict):
-            r = _compare_dicts(lv, rv, childpath)
-            if r:
-                return r
-        elif isinstance(lv, list):
-            r = _compare_lists(lv, rv, childpath)
-            if r:
-                return r
-        elif lv != rv:
-            return (["Different items: ",
-                     "%s/%s: %s" % (basepath, key, pprint.pformat(lv)),
-                     "%s/%s: %s" % (basepath, key, pprint.pformat(rv))])
+def _compare_lists(left, right, path):
+    for i in range(min(len(left), len(right))):
+        l = left[i]
+        r = right[i]
+        cmpres = _compare(l, r, path)
+        if cmpres:
+            return cmpres
+    if len(left) > len(right):
+        return (["Left '%s' contains more items. First extra item: " % path,
+                 left[len(right)]])
+    if len(right) > len(left):
+        return (["Right '%s' contains more items. First extra item: " % path,
+                 right[len(left)]])
     return None
 
 
-def _compare_lists(left, right):
+def _compare_str(left, right, path):
+    if left == right:
+        return None
     for i in range(min(len(left), len(right))):
-        l = left[i]
-        r = right[i]
-        if type(l) != type(r):
-            return ['Different items at index %d:' % i,
-                    pprint.pformat(l),
-                    pprint.pformat(r)]
-        if isinstance(l, dict):
-            r = _compare_dicts(l, r)
-            if r:
-                return r
-        elif isinstance(l, list):
-            r = _compare_lists(l, r)
-            if r:
-                return r
-        elif l != r:
-            return ['Different items at index %d:' % i,
-                    pprint.pformat(l),
-                    pprint.pformat(r)]
-    return None
+        if left[i] != right[i]:
+            start = max(0, i - 5)
+            lend = min(len(left), i + 5)
+            rend = min(len(right), i + 5)
+            return ["Items '%s' differ at index %d:" % (path, i),
+                    left[start:lend],
+                    (' ' * start + '^'),
+                    right[start:rend],
+                    (' ' * start + '^')]
+    if len(left) > len(right):
+        return ["Left is longer.",
+                "Left '%s': " % path, left,
+                "Right '%s': " % path, right,
+                "Extra items: %r" % left[len(right):]]
+    if len(right) > len(left):
+        return ["Right is longer.",
+                "Left '%s': " % path, left,
+                "Right '%s': " % path, right,
+                "Extra items: %r" % right[len(left):]]