]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - tests/util.py
Disable pristine-tar option in gbp.conf, since there is no pristine-tar branch.
[bcachefs-tools-debian] / tests / util.py
index b6e05e3a97d2696f1f06369fc64bd1746bc37f4f..68fe9e950672a4824e3f3aba981578380a4e2dd8 100644 (file)
@@ -1,29 +1,36 @@
 #!/usr/bin/python3
 
+import errno
 import os
-import pytest
 import re
 import subprocess
-import sys
 import tempfile
 import threading
 import time
 
 from pathlib import Path
 
-DIR = Path('..')
-BCH_PATH = DIR / 'bcachefs'
+BASE_PATH= os.path.dirname(__file__)
+BCH_PATH = os.path.abspath(os.path.join(BASE_PATH, '../target/release', 'bcachefs'))
+VALGRIND_PATH= os.path.abspath(os.path.join(BASE_PATH,
+    'valgrind-suppressions.txt'))
 
 VPAT = re.compile(r'ERROR SUMMARY: (\d+) errors from (\d+) contexts')
 
+ENABLE_VALGRIND = os.getenv('BCACHEFS_TEST_USE_VALGRIND', 'no') == 'yes'
+
 class ValgrindFailedError(Exception):
     def __init__(self, log):
         self.log = log
 
-def check_valgrind(logfile):
-    log = logfile.read().decode('utf-8')
+def check_valgrind(log):
     m = VPAT.search(log)
-    assert m is not None, 'Internal error: valgrind log did not match.'
+    if m is None:
+        print('Internal error: valgrind log did not match.')
+        print('-- valgrind log:')
+        print(log)
+        print('-- end log --')
+        assert False
 
     errors = int(m.group(1))
     if errors > 0:
@@ -37,20 +44,24 @@ def run(cmd, *args, valgrind=False, check=False):
     ValgrindFailedError if there's a problem.
     """
     cmds = [cmd] + list(args)
+    valgrind = valgrind and ENABLE_VALGRIND
 
+    print("Running '{}'".format(cmds))
     if valgrind:
         vout = tempfile.NamedTemporaryFile()
         vcmd = ['valgrind',
                '--leak-check=full',
+               '--gen-suppressions=all',
+               '--suppressions={}'.format(VALGRIND_PATH),
                '--log-file={}'.format(vout.name)]
         cmds = vcmd + cmds
 
-    print("Running '{}'".format(cmds))
-    res = subprocess.run(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                         encoding='utf-8', check=check)
-
-    if valgrind:
-        check_valgrind(vout)
+        res = subprocess.run(cmds, stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE, encoding='utf-8', check=check)
+        check_valgrind(vout.read().decode('utf-8'))
+    else:
+        res = subprocess.run(cmds, stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE, encoding='utf-8', check=check)
 
     return res
 
@@ -65,7 +76,7 @@ def sparse_file(lpath, size):
     This is typically used to create device files for bcachefs.
     """
     path = Path(lpath)
-    f = path.touch(mode = 0o600, exist_ok = False)
+    path.touch(mode = 0o600, exist_ok = False)
     os.truncate(path, size)
 
     return path
@@ -124,7 +135,7 @@ class FuseError(Exception):
     def __init__(self, msg):
         self.msg = msg
 
-class BFuse(threading.Thread):
+class BFuse:
     '''bcachefs fuse runner.
 
     This class runs bcachefs in fusemount mode, and waits until the mount has
@@ -134,7 +145,7 @@ class BFuse(threading.Thread):
     '''
 
     def __init__(self, dev, mnt):
-        threading.Thread.__init__(self)
+        self.thread = None
         self.dev = dev
         self.mnt = mnt
         self.ready = threading.Event()
@@ -147,12 +158,19 @@ class BFuse(threading.Thread):
     def run(self):
         """Background thread which runs "bcachefs fusemount" under valgrind"""
 
-        vout = tempfile.NamedTemporaryFile()
-        cmd = [ 'valgrind',
-                '--leak-check=full',
-                '--log-file={}'.format(vout.name),
-                BCH_PATH,
-                'fusemount', '-f', self.dev, self.mnt]
+        vlog = None
+        cmd = []
+
+        if ENABLE_VALGRIND:
+            vlog = tempfile.NamedTemporaryFile()
+            cmd += [ 'valgrind',
+                     '--leak-check=full',
+                     '--gen-suppressions=all',
+                     '--suppressions=valgrind-suppressions.txt',
+                     '--log-file={}'.format(vlog.name) ]
+
+        cmd += [ BCH_PATH,
+                 'fusemount', '-f', self.dev, self.mnt]
 
         print("Running {}".format(cmd))
 
@@ -167,10 +185,19 @@ class BFuse(threading.Thread):
         (out2, _) = self.proc.communicate()
         print("Process exited.")
 
+        self.returncode = self.proc.returncode
+        if self.returncode == 0:
+            errors = [ 'btree iterators leaked!',
+                       'emergency read only!' ]
+            for e in errors:
+                if e in out2:
+                    print('Debug error found in output: "{}"'.format(e))
+                    self.returncode = errno.ENOMSG
+
         self.stdout = out1 + out2
         self.stderr = err.read()
-        self.returncode = self.proc.returncode
-        self.vout = vout
+        if vlog:
+            self.vout = vlog.read().decode('utf-8')
 
     def expect(self, pipe, regex):
         """Wait for the child process to mount."""
@@ -189,23 +216,38 @@ class BFuse(threading.Thread):
 
     def mount(self):
         print("Starting fuse thread.")
-        self.start()
+
+        assert not self.thread
+        self.thread = threading.Thread(target=self.run)
+        self.thread.start()
+
         self.ready.wait()
         print("Fuse is mounted.")
 
     def unmount(self, timeout=None):
         print("Unmounting fuse.")
         run("fusermount3", "-zu", self.mnt)
-        print("Waiting for thread to exit.")
 
-        self.join(timeout)
-        if self.isAlive():
-            self.proc.kill()
-            self.join()
+        if self.thread:
+            print("Waiting for thread to exit.")
+            self.thread.join(timeout)
+            if self.thread.is_alive():
+                if self.proc:
+                    self.proc.kill()
+                self.thread.join()
+        else:
+            print("Thread was already done.")
+
+        self.thread = None
+        self.ready.clear()
 
-        check_valgrind(self.vout)
+        if self.vout:
+            check_valgrind(self.vout)
 
     def verify(self):
+        # avoid throwing exception in assertion
+        assert self.stdout is not None
+        assert self.stderr is not None
         assert self.returncode == 0
         assert len(self.stdout) > 0
         assert len(self.stderr) == 0