[pacman-dev] [PATCH 3/3] Added tests for -Q --check (both fast(files) and full(mtree)).

Jeremy Heiner scalaprotractor at gmail.com
Wed Oct 2 11:48:58 EDT 2013


Two new "hooks" were made available in pmtest: genhook is called after
everything is generated (just before the snapshot is taken), and
snaphook is called just after the snapshot. A hook is a list of
strings. Calling a hook is "exec"ing each string in the list.

Some helper functions were added to pmtest, notably pacmanrun and
addrule__pacman_warned. pacmanrun and rootjoin are used in a hook to
install a package and then intentionally mess it up, which then allows
the "-Qk" test output to be verified (with addrule__pacman_warned).

Signed-off-by: Jeremy Heiner <ScalaProtractor at gmail.com>
---
 test/pacman/pmpkg.py               | 47 ++++++++++++++++++++++++++++
 test/pacman/pmtest.py              | 63 +++++++++++++++++++++++++++-----------
 test/pacman/tests/querycheck001.py | 18 +++++++++++
 test/pacman/tests/querycheck002.py | 24 +++++++++++++++
 test/pacman/tests/querycheck003.py | 24 +++++++++++++++
 test/pacman/tests/querycheck004.py | 27 ++++++++++++++++
 test/pacman/tests/querycheck005.py | 33 ++++++++++++++++++++
 test/pacman/tests/querycheck006.py | 20 ++++++++++++
 test/pacman/tests/querycheck007.py | 20 ++++++++++++
 test/pacman/tests/querycheck008.py | 33 ++++++++++++++++++++
 test/pacman/tests/querycheck009.py | 30 ++++++++++++++++++
 test/pacman/tests/querycheck010.py | 27 ++++++++++++++++
 test/pacman/util.py                |  1 +
 13 files changed, 349 insertions(+), 18 deletions(-)
 create mode 100644 test/pacman/tests/querycheck001.py
 create mode 100644 test/pacman/tests/querycheck002.py
 create mode 100644 test/pacman/tests/querycheck003.py
 create mode 100644 test/pacman/tests/querycheck004.py
 create mode 100644 test/pacman/tests/querycheck005.py
 create mode 100644 test/pacman/tests/querycheck006.py
 create mode 100644 test/pacman/tests/querycheck007.py
 create mode 100644 test/pacman/tests/querycheck008.py
 create mode 100644 test/pacman/tests/querycheck009.py
 create mode 100644 test/pacman/tests/querycheck010.py

diff --git a/test/pacman/pmpkg.py b/test/pacman/pmpkg.py
index 9c9e447..bb0facf 100644
--- a/test/pacman/pmpkg.py
+++ b/test/pacman/pmpkg.py
@@ -23,6 +23,8 @@
 import shutil
 from StringIO import StringIO
 import tarfile
+import hashlib
+import zlib
 
 import util
 
@@ -59,6 +61,7 @@ def __init__(self, name, version = "1.0-1"):
         # files
         self.files = []
         self.backup = []
+        self.mtree = False
         # install
         self.install = {
             "pre_install": "",
@@ -104,6 +107,33 @@ def parse_filename(name):
             filename, extra = filename.split("|")
         return filename
 
+    def mtreefile(self, info, data):
+        if self.mtree:
+            self.mtree.append(
+                "./%s uid=%d gid=%d mode=%o time=%d.%d"
+                " type=file size=%d md5digest=%s sha256digest=%s"
+                % (info.name, info.uid, info.gid, info.mode,
+                   info.mtime >> 32, info.mtime & 0xFFFFFFFF,
+                   info.size, hashlib.md5(data).hexdigest(),
+                   hashlib.sha256(data).hexdigest()))
+
+    def mtreedir(self, info):
+        if self.mtree:
+            self.mtree.append(
+                "./%s uid=%d gid=%d mode=%o time=%d.%d"
+                " type=dir"
+                % (info.name, info.uid, info.gid, info.mode,
+                   info.mtime >> 32, info.mtime & 0xFFFFFFFF))
+
+    def mtreelink(self, info):
+        if self.mtree:
+            self.mtree.append(
+                "./%s uid=%d gid=%d mode=%o time=%d.%d"
+                " type=link link=%s"
+                % (info.name, info.uid, info.gid, info.mode,
+                   info.mtime >> 32, info.mtime & 0xFFFFFFFF,
+                   info.linkname))
+
     def makepkg(self, path):
         """Creates an Arch Linux package archive.
 
@@ -148,11 +178,14 @@ def makepkg(self, path):
         util.mkdir(os.path.dirname(self.path))
 
         # Generate package metadata
+        if self.mtree:
+            self.mtree = ["#mtree"]
         tar = tarfile.open(self.path, "w:gz")
         for name, data in archive_files:
             info = tarfile.TarInfo(name)
             info.size = len(data)
             tar.addfile(info, StringIO(data))
+            self.mtreefile(info, data)
 
         # Generate package file system
         for name in self.files:
@@ -162,18 +195,32 @@ def makepkg(self, path):
                 info.mode = fileinfo["perms"]
             elif fileinfo["isdir"]:
                 info.mode = 0755
+            elif fileinfo["islink"]:
+                info.mode = 0777
             if fileinfo["isdir"]:
                 info.type = tarfile.DIRTYPE
                 tar.addfile(info)
+                self.mtreedir(info)
             elif fileinfo["islink"]:
                 info.type = tarfile.SYMTYPE
                 info.linkname = fileinfo["link"]
                 tar.addfile(info)
+                self.mtreelink(info)
             else:
                 # TODO wow what a hack, adding a newline to match mkfile?
                 filedata = name + "\n"
                 info.size = len(filedata)
                 tar.addfile(info, StringIO(filedata))
+                self.mtreefile(info, filedata)
+
+        # .MTREE
+        if self.mtree:
+            filedata = "\n".join(self.mtree)+"\n" # zlib.compress(filedata)?
+            # but that causes "Unrecognized archive format" error, and this
+            # seems to work anyway (libalpm is happy with uncompressed file)
+            info = tarfile.TarInfo(".MTREE")
+            info.size = len(filedata)
+            tar.addfile(info, StringIO(filedata))
 
         tar.close()
 
diff --git a/test/pacman/pmtest.py b/test/pacman/pmtest.py
index a0a1455..0afb222 100644
--- a/test/pacman/pmtest.py
+++ b/test/pacman/pmtest.py
@@ -18,6 +18,7 @@
 
 
 import os
+import re
 import shlex
 import shutil
 import stat
@@ -99,6 +100,9 @@ def load(self):
         self.localpkgs = []
         self.createlocalpkgs = False
         self.filesystem = []
+        self.rundir = self.rootjoin(util.TMPDIR)
+        self.genhook = []
+        self.snaphook = []
 
         self.description = ""
         self.option = {}
@@ -198,12 +202,50 @@ def generate(self):
 
         # Done.
         vprint("    Taking a snapshot of the file system")
+        for cmd in self.genhook:
+            vprint("\texec "+cmd)
+            exec cmd
         for roots, dirs, files in os.walk(root):
             for i in files:
                 filename = os.path.join(roots, i)
                 f = pmfile.PacmanFile(root, self.rootremove(filename))
                 self.files.append(f)
                 vprint("\t%s" % f.name)
+        for cmd in self.snaphook:
+            vprint("\texec "+cmd)
+            exec cmd
+
+    def pacmanbin(self):
+        return([self.env.pacman["bin"],
+            "--config", self.rootjoin(util.PACCONF),
+            "--root", self.env.root,
+            "--dbpath", self.rootjoin(util.PM_DBPATH),
+            "--cachedir", self.rootjoin(util.PM_CACHEDIR)])
+
+    def pacmansub(self, cmd, output):
+        vprint("\trunning: %s" % " ".join(cmd))
+        time_start = time.time()
+        retcode = subprocess.call(cmd, stdout=output, stderr=output,
+                cwd=self.rundir, env={'LC_ALL': 'C'})
+        time_end = time.time()
+        vprint("\ttime elapsed: %.2fs" % (time_end - time_start))
+        vprint("\tretcode = %s" % retcode)
+        return retcode
+
+    def pacmanrun(self, args):
+        cmd = self.env.cmdroot()
+        cmd.extend(self.pacmanbin())
+        cmd.append("--noconfirm")
+        cmd.extend(shlex.split(args))
+        output = open(self.rootjoin(util.GENFILE), 'a')
+        self.pacmansub(cmd, output)
+        output.close()
+
+    def addrule__pacman_warned(self, pkg, path, msg):
+        if not path.startswith(self.env.root):
+            path = self.rootjoin(path)
+        match = re.escape("warning: %s: %s (%s)" % (pkg.name, path, msg))
+        self.addrule("PACMAN_OUTPUT=^"+match+"$")
 
     def run(self):
         if os.path.isfile(util.PM_LOCK):
@@ -226,11 +268,7 @@ def run(self):
                 "--tool=memcheck", "--leak-check=full",
                 "--show-reachable=yes",
                 "--suppressions=%s" % suppfile])
-        cmd.extend([pacman["bin"],
-            "--config", self.rootjoin(util.PACCONF),
-            "--root", root,
-            "--dbpath", self.rootjoin(util.PM_DBPATH),
-            "--cachedir", self.rootjoin(util.PM_CACHEDIR)])
+        cmd.extend(self.pacmanbin())
         if not pacman["manual-confirm"]:
             cmd.append("--noconfirm")
         if pacman["debug"]:
@@ -240,28 +278,17 @@ def run(self):
             output = open(self.rootjoin(util.LOGFILE), 'w')
         else:
             output = None
-        vprint("\trunning: %s" % " ".join(cmd))
-
-        # Change to the tmp dir before running pacman, so that local package
-        # archives are made available more easily.
-        tmpdir = self.rootjoin(util.TMPDIR)
-        time_start = time.time()
-        self.retcode = subprocess.call(cmd, stdout=output, stderr=output,
-                cwd=tmpdir, env={'LC_ALL': 'C'})
-        time_end = time.time()
-        vprint("\ttime elapsed: %.2fs" % (time_end - time_start))
 
+        self.retcode = self.pacmansub(cmd, output)
         if output:
             output.close()
 
-        vprint("\tretcode = %s" % self.retcode)
-
         # Check if the lock is still there
         if os.path.isfile(util.PM_LOCK):
             tap.diag("\tERROR: %s not removed" % util.PM_LOCK)
             os.unlink(util.PM_LOCK)
         # Look for a core file
-        if os.path.isfile(os.path.join(tmpdir, "core")):
+        if os.path.isfile(os.path.join(self.rundir, "core")):
             tap.diag("\tERROR: pacman dumped a core file")
 
     def check(self):
diff --git a/test/pacman/tests/querycheck001.py b/test/pacman/tests/querycheck001.py
new file mode 100644
index 0000000..8587b50
--- /dev/null
+++ b/test/pacman/tests/querycheck001.py
@@ -0,0 +1,18 @@
+self.description = "Query--check files, all there"
+
+pkg = pmpkg("dummy")
+pkg.files = [
+    "etc/dummy.conf",
+    "lib/libdummy.so.0",
+    "lib/libdummy.so -> ./libdummy.so.0",
+    "lib/dummy/",
+    "lib/dummy/stuff",
+    "bin/dummy"]
+
+self.addpkg(pkg)
+self.genhook.extend([
+    "self.pacmanrun('-U %s')" % pkg.filename()])
+
+self.args = "-Qk"
+
+self.addrule("PACMAN_RETCODE=0")
diff --git a/test/pacman/tests/querycheck002.py b/test/pacman/tests/querycheck002.py
new file mode 100644
index 0000000..c385893
--- /dev/null
+++ b/test/pacman/tests/querycheck002.py
@@ -0,0 +1,24 @@
+self.description = "Query--check files, missing a file"
+
+pkg = pmpkg("dummy")
+pkg.files = [
+    "etc/dummy.conf",
+    "lib/libdummy.so.0",
+    "lib/libdummy.so -> ./libdummy.so.0",
+    "lib/dummy/",
+    "lib/dummy/stuff",
+    "bin/dummy"]
+
+self.addpkg(pkg)
+
+zap = pkg.files[1]
+
+self.genhook.extend([
+    "self.pacmanrun('-U %s')" % pkg.filename(),
+    "os.unlink(self.rootjoin('%s'))" % zap])
+
+self.args = "-Qk"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule__pacman_warned(pkg, zap, "No such file or directory")
+self.addrule("PACMAN_OUTPUT= 1 missing file$")
diff --git a/test/pacman/tests/querycheck003.py b/test/pacman/tests/querycheck003.py
new file mode 100644
index 0000000..07e1bc9
--- /dev/null
+++ b/test/pacman/tests/querycheck003.py
@@ -0,0 +1,24 @@
+self.description = "Query--check files, missing a link"
+
+pkg = pmpkg("dummy")
+pkg.files = [
+    "etc/dummy.conf",
+    "lib/libdummy.so.0",
+    "lib/libdummy.so -> ./libdummy.so.0",
+    "lib/dummy/",
+    "lib/dummy/stuff",
+    "bin/dummy"]
+
+self.addpkg(pkg)
+
+zap = pkg.files[2].split(' -> ')[0]
+
+self.genhook.extend([
+    "self.pacmanrun('-U %s')" % pkg.filename(),
+    "os.unlink(self.rootjoin('%s'))" % zap])
+
+self.args = "-Qk"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule__pacman_warned(pkg, zap, "No such file or directory")
+self.addrule("PACMAN_OUTPUT= 1 missing file$")
diff --git a/test/pacman/tests/querycheck004.py b/test/pacman/tests/querycheck004.py
new file mode 100644
index 0000000..dcf8173
--- /dev/null
+++ b/test/pacman/tests/querycheck004.py
@@ -0,0 +1,27 @@
+self.description = "Query--check files, missing a dir"
+
+pkg = pmpkg("dummy")
+pkg.files = [
+    "etc/dummy.conf",
+    "lib/libdummy.so.0",
+    "lib/libdummy.so -> ./libdummy.so.0",
+    "lib/dummy/",
+    "lib/dummy/stuff",
+    "bin/dummy"]
+
+self.addpkg(pkg)
+
+zap = pkg.files[3]
+
+self.genhook.extend([
+    "self.pacmanrun('-U %s')" % pkg.filename(),
+    "shutil.rmtree(self.rootjoin('%s'))" % zap])
+
+self.args = "-Qk"
+
+missing = "No such file or directory"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule__pacman_warned(pkg, zap, missing)
+self.addrule__pacman_warned(pkg, pkg.files[4], missing)
+self.addrule("PACMAN_OUTPUT= 2 missing files$")
diff --git a/test/pacman/tests/querycheck005.py b/test/pacman/tests/querycheck005.py
new file mode 100644
index 0000000..e673cc6
--- /dev/null
+++ b/test/pacman/tests/querycheck005.py
@@ -0,0 +1,33 @@
+self.description = "Query--check files, missing several"
+
+pkg = pmpkg("dummy")
+pkg.files = [
+    "etc/dummy.conf",
+    "lib/libdummy.so.0",
+    "lib/libdummy.so -> ./libdummy.so.0",
+    "lib/dummy/",
+    "lib/dummy/stuff",
+    "bin/dummy"]
+
+self.addpkg(pkg)
+
+zap1 = pkg.files[1]
+zap2 = pkg.files[2].split(' -> ')[0]
+zap3 = pkg.files[3]
+
+self.genhook.extend([
+    "self.pacmanrun('-U %s')" % pkg.filename(),
+    "os.unlink(self.rootjoin('%s'))" % zap1,
+    "os.unlink(self.rootjoin('%s'))" % zap2,
+    "shutil.rmtree(self.rootjoin('%s'))" % zap3])
+
+self.args = "-Qk"
+
+missing = "No such file or directory"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule__pacman_warned(pkg, zap1, missing)
+self.addrule__pacman_warned(pkg, zap2, missing)
+self.addrule__pacman_warned(pkg, zap3, missing)
+self.addrule__pacman_warned(pkg, pkg.files[4], missing)
+self.addrule("PACMAN_OUTPUT= 4 missing files$")
diff --git a/test/pacman/tests/querycheck006.py b/test/pacman/tests/querycheck006.py
new file mode 100644
index 0000000..cf178f4
--- /dev/null
+++ b/test/pacman/tests/querycheck006.py
@@ -0,0 +1,20 @@
+self.description = "Query--check mtree, no mtree"
+
+pkg = pmpkg("dummy")
+pkg.files = [
+    "etc/dummy.conf",
+    "lib/libdummy.so.0",
+    "lib/libdummy.so -> ./libdummy.so.0",
+    "lib/dummy/",
+    "lib/dummy/stuff",
+    "bin/dummy"]
+pkg.mtree = False
+
+self.addpkg(pkg)
+self.genhook.extend([
+    "self.pacmanrun('-U %s')" % pkg.filename()])
+
+self.args = "-Qkk"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PACMAN_OUTPUT=dummy: no mtree file")
diff --git a/test/pacman/tests/querycheck007.py b/test/pacman/tests/querycheck007.py
new file mode 100644
index 0000000..d43d846
--- /dev/null
+++ b/test/pacman/tests/querycheck007.py
@@ -0,0 +1,20 @@
+self.description = "Query--check mtree, all there"
+
+pkg = pmpkg("dummy")
+pkg.files = [
+    "etc/dummy.conf",
+    "lib/libdummy.so.0",
+    "lib/libdummy.so -> ./libdummy.so.0",
+    "lib/dummy/",
+    "lib/dummy/stuff",
+    "bin/dummy"]
+pkg.mtree = True
+
+self.addpkg(pkg)
+self.genhook.extend([
+    "self.pacmanrun('-U %s')" % pkg.filename()])
+
+self.args = "-Qkk"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("!PACMAN_OUTPUT=dummy: no mtree file")
diff --git a/test/pacman/tests/querycheck008.py b/test/pacman/tests/querycheck008.py
new file mode 100644
index 0000000..ddf9260
--- /dev/null
+++ b/test/pacman/tests/querycheck008.py
@@ -0,0 +1,33 @@
+self.description = "Query--check mtree, missing several"
+
+pkg = pmpkg("dummy")
+pkg.files = [
+    "etc/dummy.conf",
+    "lib/libdummy.so.0",
+    "lib/libdummy.so -> ./libdummy.so.0",
+    "lib/dummy/",
+    "lib/dummy/stuff",
+    "bin/dummy"]
+pkg.mtree = True
+
+zap1 = pkg.files[1]
+zap2 = pkg.files[2].split(' -> ')[0]
+zap3 = pkg.files[3]
+
+self.addpkg(pkg)
+self.genhook.extend([
+    "self.pacmanrun('-U %s')" % pkg.filename(),
+    "os.unlink(self.rootjoin('%s'))" % zap1,
+    "os.unlink(self.rootjoin('%s'))" % zap2,
+    "shutil.rmtree(self.rootjoin('%s'))" % zap3])
+
+self.args = "-Qkk"
+
+missing = "No such file or directory"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule__pacman_warned(pkg, zap1, missing)
+self.addrule__pacman_warned(pkg, zap2, missing)
+self.addrule__pacman_warned(pkg, zap3, missing)
+self.addrule__pacman_warned(pkg, pkg.files[4], missing)
+self.addrule("PACMAN_OUTPUT= 4 altered files$")
diff --git a/test/pacman/tests/querycheck009.py b/test/pacman/tests/querycheck009.py
new file mode 100644
index 0000000..6caf733
--- /dev/null
+++ b/test/pacman/tests/querycheck009.py
@@ -0,0 +1,30 @@
+self.description = "Query--check mtree, bad types"
+
+pkg = pmpkg("dummy")
+pkg.mtree = True
+pkg.files = ["dummy/z"]
+for file, mangle in [
+        ("dummy/f1", ["os.unlink('%s')", "os.mkdir('%s')"]),
+        ("dummy/f2", ["os.unlink('%s')", "os.symlink('z','%s')"]),
+        ("dummy/l1 -> z", ["os.unlink('%s')", "os.mkdir('%s')"]),
+        ("dummy/l2 -> z", ["os.unlink('%s')", "util.mkfile('%s')"]),
+        ("dummy/d1/", ["os.rmdir('%s')", "util.mkfile('%s')"]),
+        ("dummy/d2/", ["os.rmdir('%s')", "os.symlink('z','%s')"])]:
+    pkg.files.append(file)
+    parsed = pkg.parse_filename(file)
+    noslash = parsed.rstrip('/')
+    err = "File type mismatch"
+    if noslash != parsed:
+        err = "Not a directory"
+    rooted = self.rootjoin(noslash)
+    for cmd in mangle:
+        self.genhook.append(cmd % rooted)
+    self.addrule__pacman_warned(pkg, parsed, err)
+
+self.addpkg(pkg)
+self.genhook.insert(0, "self.pacmanrun('-U %s')" % pkg.filename())
+
+self.args = "-Qkk"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("PACMAN_OUTPUT= 6 altered files$")
diff --git a/test/pacman/tests/querycheck010.py b/test/pacman/tests/querycheck010.py
new file mode 100644
index 0000000..caba37b
--- /dev/null
+++ b/test/pacman/tests/querycheck010.py
@@ -0,0 +1,27 @@
+self.description = "Query--check mtree, bad perms+time+size+link"
+
+pkg = pmpkg("dummy")
+pkg.mtree = True
+pkg.files = ["dummy/"]
+for file, err, mangle in [
+        #("dummy/uid", "UID mismatch", ["os.chown('%s',?,-1)"]),#needs root
+        #("dummy/gid", "GID mismatch", ["os.chown('%s',-1,?)"]),#needs root
+        ("dummy/mode", "Permissions mismatch", ["os.chmod('%s',0400)"]),
+        ("dummy/mtime", "Modification time mismatch", ["os.utime('%s',None)"]),
+        ("dummy/size", "Size mismatch", ["util.mkfile('%s')"]),
+        ("dummy/link -> size", "Symlink path mismatch", [
+            "os.unlink('%s')", "os.symlink('z','%s')"])]:
+    pkg.files.append(file)
+    parsed = pkg.parse_filename(file)
+    rooted = self.rootjoin(parsed)
+    for cmd in mangle:
+        self.genhook.append(cmd % rooted)
+    self.addrule__pacman_warned(pkg, parsed, err)
+
+self.addpkg(pkg)
+self.genhook.insert(0, "self.pacmanrun('-U %s')" % pkg.filename())
+
+self.args = "-Qkk"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("PACMAN_OUTPUT= 4 altered files$")
diff --git a/test/pacman/util.py b/test/pacman/util.py
index 5c9a0c0..d2df8dc 100644
--- a/test/pacman/util.py
+++ b/test/pacman/util.py
@@ -40,6 +40,7 @@
 TMPDIR      = "tmp"
 SYNCREPO    = "var/pub"
 LOGFILE     = "var/log/pactest.log"
+GENFILE     = "var/log/testgen.log"
 
 verbose = 0
 
-- 
1.8.4



More information about the pacman-dev mailing list