Browse Source

tests: complain about memory leaks

Currently this detects leaking VM (and all subclasses), Qubes and
libvirt-related objects. Usable only with --failfast.
Wojtek Porczyk 6 years ago
parent
commit
5aa9fa2db4
1 changed files with 37 additions and 0 deletions
  1. 37 0
      qubes/tests/__init__.py

+ 37 - 0
qubes/tests/__init__.py

@@ -374,9 +374,29 @@ class QubesTestCase(unittest.TestCase):
 
     def setUp(self):
         super().setUp()
+        self.addCleanup(self.cleanup_gc)
+
         self.loop = asyncio.get_event_loop()
         self.addCleanup(self.cleanup_loop)
 
+    def cleanup_gc(self):
+        gc.collect()
+        leaked = [obj for obj in gc.get_objects() + gc.garbage
+            if isinstance(obj,
+                (qubes.Qubes, qubes.vm.BaseVM,
+                libvirt.virConnect, libvirt.virDomain))]
+
+        if leaked:
+            try:
+                import objgraph
+                objgraph.show_backrefs(leaked,
+                    max_depth=15, extra_info=extra_info,
+                    filename='/tmp/objgraph-{}.png'.format(self.id()))
+            except ImportError:
+                pass
+
+        assert not leaked
+
     def cleanup_loop(self):
         '''Check if the loop is empty'''
         # XXX BEWARE this is touching undocumented, implementation-specific
@@ -1013,6 +1033,23 @@ def list_templates():
             _templates = ()
     return _templates
 
+def extra_info(obj):
+    '''Return short info identifying object.
+
+    For example, if obj is a qube, return its name. This is for use with
+    :py:mod:`objgraph` package.
+    '''
+    # Feel free to extend to other cases.
+
+    if isinstance(obj, qubes.vm.qubesvm.QubesVM):
+        try:
+            return obj.name
+        except AttributeError:
+            pass
+    if isinstance(obj, unittest.TestCase):
+        return obj.id()
+
+    return ''
 
 def load_tests(loader, tests, pattern): # pylint: disable=unused-argument
     # discard any tests from this module, because it hosts base classes