The framework will now generate mtree data when the pmpkg.makemtree is set to True, but only for "local" packages. Since the mtree data is only used by -Qkk, and this change is sufficient to test that function, there seems to be no reason to generate mtree data for other packages. If that becomes a requirement in the future there are comments in the code that sketch out an implemention for that. The two new unit tests are similar to the previous -Qkk tests (005 is like 002, and 006 is like 004), but with mtree data. So the filesystem is actually validated against the mtree (instead of merely checking for the warning about a lacking mtree). Signed-off-by: Jeremy Heiner <ScalaProtractor at gmail.com> --- test/pacman/pmdb.py | 3 ++ test/pacman/pmpkg.py | 18 ++++++++++++ test/pacman/tests/TESTS | 2 ++ test/pacman/tests/querycheck005.py | 16 +++++++++++ test/pacman/tests/querycheck006.py | 20 +++++++++++++ test/pacman/util.py | 57 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 test/pacman/tests/querycheck005.py create mode 100644 test/pacman/tests/querycheck006.py diff --git a/test/pacman/pmdb.py b/test/pacman/pmdb.py index 53de91e..088e284 100644 --- a/test/pacman/pmdb.py +++ b/test/pacman/pmdb.py @@ -224,6 +224,9 @@ def db_write(self, pkg): make_section(data, "BACKUP", pkg.local_backup_entries()) entry["files"] = "\n".join(data) + if pkg.mtreedata: + entry["mtree"] = pkg.mtreedata + if any(pkg.install.values()): entry["install"] = pkg.installfile() diff --git a/test/pacman/pmpkg.py b/test/pacman/pmpkg.py index db97449..25e841a 100644 --- a/test/pacman/pmpkg.py +++ b/test/pacman/pmpkg.py @@ -60,6 +60,8 @@ def __init__(self, name, version = "1.0-1"): # files self.files = [] self.backup = [] + self.makemtree = False # True => finalize generates mtreedata + self.mtreedata = "" # you can set this, but makemtree is easier # install self.install = { "pre_install": "", @@ -145,6 +147,19 @@ def makepkg(self, path): if any(self.install.values()): archive_files.append((".INSTALL", self.installfile())) + # .MTREE + if self.mtreedata: + raise NotImplementedError("attempt to put mtree into tar") + # TODO: they work great in local dbs, but not in tar'd pkgs. + # it seems unlikely that any test will ever need to, but: + # easiest solution seems to be to hack the 'Generate package + # file system' loop just below to copy the attributes from + # the mtreedata over to the TarInfo fields. pulling the data + # out with re.search shouldn't be too hard. put that code + # over in util so it gets updated with the other mtree code. + # then remove the raise and uncomment the following line: + # archive_files.append((".MTREE", self.mtreedata)) + self.path = os.path.join(path, self.filename()) util.mkdir(os.path.dirname(self.path)) @@ -226,6 +241,9 @@ def finalize(self): name = os.path.dirname(name) self.files.sort() + if self.makemtree: + self.mtreedata = util.mtree(self.files) + self.finalized = True def local_backup_entries(self): diff --git a/test/pacman/tests/TESTS b/test/pacman/tests/TESTS index f4e6caf..c460fe0 100644 --- a/test/pacman/tests/TESTS +++ b/test/pacman/tests/TESTS @@ -92,6 +92,8 @@ TESTS += test/pacman/tests/querycheck001.py TESTS += test/pacman/tests/querycheck002.py TESTS += test/pacman/tests/querycheck003.py TESTS += test/pacman/tests/querycheck004.py +TESTS += test/pacman/tests/querycheck005.py +TESTS += test/pacman/tests/querycheck006.py TESTS += test/pacman/tests/reason001.py TESTS += test/pacman/tests/remove001.py TESTS += test/pacman/tests/remove002.py diff --git a/test/pacman/tests/querycheck005.py b/test/pacman/tests/querycheck005.py new file mode 100644 index 0000000..95c776d --- /dev/null +++ b/test/pacman/tests/querycheck005.py @@ -0,0 +1,16 @@ +self.description = "Query--check mtree, all there" + +pkg = pmpkg("dummy") +pkg.files = [ + "usr/lib/libdummy.so.0", + "usr/lib/libdummy.so -> libdummy.so.0", + "usr/share/dummy/C/", + "usr/share/dummy/C/msgs", + "usr/share/dummy/en -> C"] +pkg.makemtree = True +self.addpkg2db("local",pkg) + +self.args = "-Qkk" + +self.addrule("PACMAN_RETCODE=0") +self.addrule("!PACMAN_OUTPUT=dummy: no mtree file") diff --git a/test/pacman/tests/querycheck006.py b/test/pacman/tests/querycheck006.py new file mode 100644 index 0000000..879fd00 --- /dev/null +++ b/test/pacman/tests/querycheck006.py @@ -0,0 +1,20 @@ +self.description = "Query--check mtree, missing" + +pkg = pmpkg("dummy") +pkg.files = [ + "usr/lib/libdummy.so.0", + "usr/lib/libdummy.so -> libdummy.so.0", + "usr/share/dummy/C/", + "usr/share/dummy/C/msgs", + "usr/share/dummy/en -> C"] +pkg.makemtree = True +pkg.install['post_install'] = "rm -r ../usr/share/dummy/*" +self.addpkg2db("local",pkg) + +self.args = "-Qkk" + +self.addrule("PACMAN_RETCODE=1") +self.addrule("PACMAN_OUTPUT=/usr/share/dummy/C .No such file") +self.addrule("PACMAN_OUTPUT=/usr/share/dummy/C/msgs .No such file") +self.addrule("PACMAN_OUTPUT=/usr/share/dummy/en .No such file") +self.addrule("PACMAN_OUTPUT=, 3 altered files") diff --git a/test/pacman/util.py b/test/pacman/util.py index beb7e3d..2158b80 100644 --- a/test/pacman/util.py +++ b/test/pacman/util.py @@ -20,6 +20,8 @@ import os import re import hashlib +import subprocess +import sys import tap @@ -93,6 +95,11 @@ def mkfile(base, name, data="", utime=False): if info["islink"]: os.symlink(info["link"], path) + if utime: + if sys.hexversion < 0x03030000: + subprocess.call(["touch","-ht","196912311905.55",path]) + else: + os.utime(path, (355,355), follow_symlinks=False) else: writedata(path, data) if utime: @@ -158,6 +165,56 @@ def mkmd5sum(data): # +# mtree +# obviously not a full implementation of the spec (e.g. no +# quoting of paths), but just enough for a few tests. +# freebsd mtree(5) has a bit more detail than arch or openbsd. +# scripts/makepkg calls bsdtar to build mtrees at line 1890. +# + +def mtree(files): + + lines = ["#mtree"] # no version (ala bsdtar) + for name in files: + info = getfileinfo(name) + filename = info["filename"] + + if info["isdir"]: + filename = filename.rstrip("/") + mode = info["perms"] if info["hasperms"] else 0o755 + attrs = ("type=dir mode=%o" + % (mode)) + + elif info["islink"]: + mode = info["perms"] if info["hasperms"] else 0o777 + attrs = ("type=link time=355.000000000" + " mode=%o link=%s" + % (mode, info["link"])) + + else: + mode = info["perms"] if info["hasperms"] else 0o644 + contents = name + "\n" + md5 = hashlib.md5(contents).hexdigest() + sha256 = hashlib.sha256(contents).hexdigest() + attrs = ("type=file time=355.000000000" + " mode=%o size=%d md5digest=%s sha256digest=%s" + % (mode, len(contents), md5, sha256)) + + lines.append("./%s uid=0 gid=0 %s" % (filename, attrs)) + + mtree = "\n".join(lines)+"\n" + + # scripts/makepkg compresses the mtree (via bsdtar using gzip). + # python's gzip always opens a file (can't make it use StringIO). + # gzip uses zlib, but if we zlib.compress(mtree) it causes libalpm + # to reject the mtree with "Unrecognized archive format". + # fortunately libalpm reads the uncompressed mtree files just fine, + # so we don't have to jump through any hoops to get it compressed. + + return mtree + + +# # Miscellaneous # -- 1.8.4.2