summaryrefslogtreecommitdiffstats
path: root/lib/dpkg/t
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dpkg/t')
-rw-r--r--lib/dpkg/t/b-fsys-hash.c81
-rw-r--r--lib/dpkg/t/b-pkg-hash.c65
-rw-r--r--lib/dpkg/t/c-tarextract.c151
-rw-r--r--lib/dpkg/t/c-treewalk.c132
-rw-r--r--lib/dpkg/t/c-trigdeferred.c96
-rw-r--r--lib/dpkg/t/data/meminfo-no-data0
-rw-r--r--lib/dpkg/t/data/meminfo-no-info52
-rw-r--r--lib/dpkg/t/data/meminfo-no-unit53
-rw-r--r--lib/dpkg/t/data/meminfo-ok53
-rw-r--r--lib/dpkg/t/t-ar.c61
-rw-r--r--lib/dpkg/t/t-arch.c225
-rw-r--r--lib/dpkg/t/t-buffer.c82
-rw-r--r--lib/dpkg/t/t-c-ctype.c106
-rw-r--r--lib/dpkg/t/t-command.c225
-rw-r--r--lib/dpkg/t/t-deb-version.c90
-rw-r--r--lib/dpkg/t/t-ehandle.c128
-rw-r--r--lib/dpkg/t/t-error.c87
-rw-r--r--lib/dpkg/t/t-file.c106
-rw-r--r--lib/dpkg/t/t-fsys-dir.c91
-rw-r--r--lib/dpkg/t/t-fsys-hash.c108
-rw-r--r--lib/dpkg/t/t-headers-cpp.cc82
-rw-r--r--lib/dpkg/t/t-macros.c45
-rw-r--r--lib/dpkg/t/t-meminfo.c88
-rw-r--r--lib/dpkg/t/t-mod-db.c57
-rw-r--r--lib/dpkg/t/t-namevalue.c48
-rw-r--r--lib/dpkg/t/t-pager.c75
-rw-r--r--lib/dpkg/t/t-path.c181
-rw-r--r--lib/dpkg/t/t-pkg-format.c141
-rw-r--r--lib/dpkg/t/t-pkg-hash.c179
-rw-r--r--lib/dpkg/t/t-pkg-list.c90
-rw-r--r--lib/dpkg/t/t-pkg-queue.c116
-rw-r--r--lib/dpkg/t/t-pkg-show.c70
-rw-r--r--lib/dpkg/t/t-pkginfo.c153
-rw-r--r--lib/dpkg/t/t-progname.c53
-rw-r--r--lib/dpkg/t/t-string.c281
-rw-r--r--lib/dpkg/t/t-subproc.c100
-rw-r--r--lib/dpkg/t/t-tar.c148
-rwxr-xr-xlib/dpkg/t/t-tarextract.t159
-rw-r--r--lib/dpkg/t/t-test-skip.c31
-rw-r--r--lib/dpkg/t/t-test.c66
-rwxr-xr-xlib/dpkg/t/t-treewalk.t154
-rwxr-xr-xlib/dpkg/t/t-trigdeferred.t116
-rw-r--r--lib/dpkg/t/t-trigger.c49
-rw-r--r--lib/dpkg/t/t-varbuf.c475
-rw-r--r--lib/dpkg/t/t-version.c308
45 files changed, 5257 insertions, 0 deletions
diff --git a/lib/dpkg/t/b-fsys-hash.c b/lib/dpkg/t/b-fsys-hash.c
new file mode 100644
index 0000000..7ae8f9c
--- /dev/null
+++ b/lib/dpkg/t/b-fsys-hash.c
@@ -0,0 +1,81 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * b-fsys-hash.c - test fsys database load and hash performance
+ *
+ * Copyright © 2009-2019 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <dpkg/i18n.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/dpkg-db.h>
+
+#include <dpkg/perf.h>
+
+#include <dpkg/db-fsys.h>
+
+static const char *admindir;
+
+int
+main(int argc, const char *const *argv)
+{
+ struct perf_slot ps;
+
+ push_error_context();
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ admindir = dpkg_db_set_dir(admindir);
+
+ perf_ts_mark_print("init");
+
+ perf_ts_slot_start(&ps);
+ fsys_hash_init();
+ perf_ts_slot_stop(&ps);
+
+ perf_ts_slot_print(&ps, "fsys_hash_init");
+
+ perf_ts_slot_start(&ps);
+ modstatdb_open(msdbrw_readonly | msdbrw_available_readonly);
+ perf_ts_slot_stop(&ps);
+
+ perf_ts_slot_print(&ps, "modstatdb_init");
+
+ perf_ts_slot_start(&ps);
+ ensure_allinstfiles_available_quiet();
+ perf_ts_slot_stop(&ps);
+
+ perf_ts_slot_print(&ps, "load .list");
+
+ if (test_is_verbose()) {
+ pkg_hash_report(stdout);
+ fsys_hash_report(stdout);
+ }
+
+ modstatdb_shutdown();
+ pop_error_context(ehflag_normaltidy);
+
+ perf_ts_mark_print("shutdown");
+
+ return 0;
+}
diff --git a/lib/dpkg/t/b-pkg-hash.c b/lib/dpkg/t/b-pkg-hash.c
new file mode 100644
index 0000000..26cdc12
--- /dev/null
+++ b/lib/dpkg/t/b-pkg-hash.c
@@ -0,0 +1,65 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * b-pkg-hash.c - test pkg database load and hash performance
+ *
+ * Copyright © 2009-2019 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <dpkg/i18n.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/dpkg-db.h>
+
+#include <dpkg/perf.h>
+
+static const char *admindir;
+
+int
+main(int argc, const char *const *argv)
+{
+ struct perf_slot ps;
+
+ push_error_context();
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ admindir = dpkg_db_set_dir(admindir);
+
+ perf_ts_mark_print("init");
+
+ perf_ts_slot_start(&ps);
+ modstatdb_open(msdbrw_readonly | msdbrw_available_readonly);
+ perf_ts_slot_stop(&ps);
+
+ perf_ts_slot_print(&ps, "modstatdb_init");
+
+ if (test_is_verbose())
+ pkg_hash_report(stdout);
+
+ modstatdb_shutdown();
+ pop_error_context(ehflag_normaltidy);
+
+ perf_ts_mark_print("shutdown");
+
+ return 0;
+}
diff --git a/lib/dpkg/t/c-tarextract.c b/lib/dpkg/t/c-tarextract.c
new file mode 100644
index 0000000..882253d
--- /dev/null
+++ b/lib/dpkg/t/c-tarextract.c
@@ -0,0 +1,151 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * c-tarextract.c - test tar extractor
+ *
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+#if HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h> /* Needed on AIX for major()/minor(). */
+#endif
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dpkg/ehandle.h>
+#include <dpkg/fdio.h>
+#include <dpkg/buffer.h>
+#include <dpkg/tarfn.h>
+
+struct tar_context {
+ int tar_fd;
+};
+
+static int
+tar_read(struct tar_archive *tar, char *buffer, int size)
+{
+ struct tar_context *tc = tar->ctx;
+
+ return fd_read(tc->tar_fd, buffer, size);
+}
+
+static int
+tar_object_skip(struct tar_archive *tar, struct tar_entry *te)
+{
+ struct tar_context *tc = tar->ctx;
+ off_t size;
+
+ size = (te->size + TARBLKSZ - 1) / TARBLKSZ * TARBLKSZ;
+ if (size == 0)
+ return 0;
+
+ return fd_skip(tc->tar_fd, size, NULL);
+}
+
+static int
+tar_object(struct tar_archive *tar, struct tar_entry *te)
+{
+ printf("%s mode=%o time=%jd.%.9d uid=%d gid=%d", te->name,
+ te->stat.mode, te->mtime, 0, te->stat.uid, te->stat.gid);
+ if (te->stat.uname)
+ printf(" uname=%s", te->stat.uname);
+ if (te->stat.gname)
+ printf(" gname=%s", te->stat.gname);
+
+ switch (te->type) {
+ case TAR_FILETYPE_FILE0:
+ case TAR_FILETYPE_FILE:
+ tar_object_skip(tar, te);
+ printf(" type=file size=%jd", (intmax_t)te->size);
+ break;
+ case TAR_FILETYPE_HARDLINK:
+ printf(" type=hardlink linkto=%s size=%jd",
+ te->linkname, (intmax_t)te->size);
+ break;
+ case TAR_FILETYPE_SYMLINK:
+ printf(" type=symlink linkto=%s size=%jd",
+ te->linkname, (intmax_t)te->size);
+ break;
+ case TAR_FILETYPE_DIR:
+ printf(" type=dir");
+ break;
+ case TAR_FILETYPE_CHARDEV:
+ case TAR_FILETYPE_BLOCKDEV:
+ printf(" type=device id=%d.%d", major(te->dev), minor(te->dev));
+ break;
+ case TAR_FILETYPE_FIFO:
+ printf(" type=fifo");
+ break;
+ default:
+ ohshit("unexpected tar entry type '%c'", te->type);
+ }
+
+ printf("\n");
+
+ return 0;
+}
+
+struct tar_operations tar_ops = {
+ .read = tar_read,
+ .extract_file = tar_object,
+ .link = tar_object,
+ .symlink = tar_object,
+ .mkdir = tar_object,
+ .mknod = tar_object,
+};
+
+int
+main(int argc, char **argv)
+{
+ struct tar_archive tar;
+ struct tar_context tar_ctx;
+ const char *tar_name = argv[1];
+
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ push_error_context();
+
+ if (tar_name) {
+ tar_ctx.tar_fd = open(tar_name, O_RDONLY);
+ if (tar_ctx.tar_fd < 0)
+ ohshite("cannot open file '%s'", tar_name);
+ } else {
+ tar_ctx.tar_fd = STDIN_FILENO;
+ }
+
+ tar.err = DPKG_ERROR_OBJECT;
+ tar.ctx = &tar_ctx;
+ tar.ops = &tar_ops;
+
+ if (tar_extractor(&tar))
+ ohshite("extracting tar");
+
+ dpkg_error_destroy(&tar.err);
+
+ if (tar_name)
+ close(tar_ctx.tar_fd);
+
+ pop_error_context(ehflag_normaltidy);
+
+ return 0;
+}
diff --git a/lib/dpkg/t/c-treewalk.c b/lib/dpkg/t/c-treewalk.c
new file mode 100644
index 0000000..4cd2b8c
--- /dev/null
+++ b/lib/dpkg/t/c-treewalk.c
@@ -0,0 +1,132 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * c-treewalk.c - test tree walk implementation
+ *
+ * Copyright © 2013-2016 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <dpkg/ehandle.h>
+#include <dpkg/treewalk.h>
+
+static int
+treenode_type(struct treenode *node)
+{
+ switch (treenode_get_mode(node) & S_IFMT) {
+ case S_IFREG:
+ return 'f';
+ case S_IFDIR:
+ return 'd';
+ case S_IFLNK:
+ return 'l';
+ case S_IFIFO:
+ return 'p';
+ case S_IFBLK:
+ return 'b';
+ case S_IFCHR:
+ return 'c';
+ case S_IFSOCK:
+ return 's';
+ default:
+ return '?';
+ }
+}
+
+static int
+treenode_visit_meta(struct treenode *node)
+{
+ printf("T=%c N=%s V=%s R=%s\n",
+ treenode_type(node), treenode_get_name(node),
+ treenode_get_virtname(node), treenode_get_pathname(node));
+
+ return 0;
+}
+
+static int
+treenode_visit_virt(struct treenode *node)
+{
+ printf("%s\n", treenode_get_virtname(node));
+
+ return 0;
+}
+
+static int
+treenode_visit_path(struct treenode *node)
+{
+ printf("%s\n", treenode_get_pathname(node));
+
+ return 0;
+}
+
+static const char *skipname;
+
+static bool
+treenode_skip(struct treenode *node)
+{
+ const char *absname = treenode_get_pathname(node);
+
+ if (strcmp(absname, skipname) == 0)
+ return true;
+
+ return false;
+}
+
+static void
+test_treewalk_list(const char *rootdir)
+{
+ const char *visitname = getenv("TREEWALK_VISIT");
+ struct treewalk_funcs funcs = { NULL };
+
+ if (visitname == NULL || strcmp(visitname, "meta") == 0)
+ funcs.visit = treenode_visit_meta;
+ else if (strcmp(visitname, "virt") == 0)
+ funcs.visit = treenode_visit_virt;
+ else if (strcmp(visitname, "path") == 0)
+ funcs.visit = treenode_visit_path;
+ else
+ ohshit("unknown treewalk visit name");
+
+ skipname = getenv("TREEWALK_SKIP");
+ if (skipname)
+ funcs.skip = treenode_skip;
+
+ treewalk(rootdir, 0, &funcs);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *rootdir = argv[1];
+
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ push_error_context();
+
+ if (rootdir == NULL)
+ ohshit("missing treewalk root dir");
+
+ test_treewalk_list(rootdir);
+
+ pop_error_context(ehflag_normaltidy);
+
+ return 0;
+}
diff --git a/lib/dpkg/t/c-trigdeferred.c b/lib/dpkg/t/c-trigdeferred.c
new file mode 100644
index 0000000..5c21ea3
--- /dev/null
+++ b/lib/dpkg/t/c-trigdeferred.c
@@ -0,0 +1,96 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * c-trigdeferred.c - test triggered deferred file parser
+ *
+ * Copyright © 2016 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <dpkg/dpkg-db.h>
+#include <dpkg/ehandle.h>
+#include <dpkg/trigdeferred.h>
+
+static void
+test_tdm_begin(const char *trig)
+{
+ printf("<T='%s'", trig);
+}
+
+static void
+test_tdm_package(const char *awname)
+{
+ printf(" P='%s'", awname);
+}
+
+static void
+test_tdm_end(void)
+{
+ printf(" E>\n");
+}
+
+static const struct trigdefmeths test_tdm = {
+ .trig_begin = test_tdm_begin,
+ .package = test_tdm_package,
+ .trig_end = test_tdm_end,
+};
+
+static int
+test_trigdeferred_parser(const char *admindir)
+{
+ enum trigdef_update_flags tduf;
+ enum trigdef_update_status tdus;
+
+ dpkg_db_set_dir(admindir);
+
+ trigdef_set_methods(&test_tdm);
+
+ tduf = TDUF_NO_LOCK | TDUF_WRITE_IF_EMPTY | TDUF_WRITE_IF_ENOENT;
+ tdus = trigdef_update_start(tduf);
+ if (tdus < TDUS_OK)
+ return -tdus + TDUS_OK;
+
+ trigdef_parse();
+
+ trigdef_process_done();
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *admindir = argv[1];
+ int ret;
+
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ push_error_context();
+
+ if (admindir == NULL)
+ ohshit("missing triggers deferred admindir");
+
+ ret = test_trigdeferred_parser(admindir);
+
+ pop_error_context(ehflag_normaltidy);
+
+ return ret;
+}
diff --git a/lib/dpkg/t/data/meminfo-no-data b/lib/dpkg/t/data/meminfo-no-data
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/dpkg/t/data/meminfo-no-data
diff --git a/lib/dpkg/t/data/meminfo-no-info b/lib/dpkg/t/data/meminfo-no-info
new file mode 100644
index 0000000..e54196b
--- /dev/null
+++ b/lib/dpkg/t/data/meminfo-no-info
@@ -0,0 +1,52 @@
+MemTotal: 3951508 kB
+MemFree: 130976 kB
+MemAvailable: 10584 kB
+Buffers: 2448 kB
+SwapCached: 12936 kB
+Active: 3111920 kB
+Inactive: 610376 kB
+Active(anon): 3102668 kB
+Inactive(anon): 606952 kB
+Active(file): 9252 kB
+Inactive(file): 3424 kB
+Unevictable: 0 kB
+Mlocked: 0 kB
+SwapTotal: 4194300 kB
+SwapFree: 3777400 kB
+Zswap: 0 kB
+Zswapped: 0 kB
+Dirty: 0 kB
+Writeback: 0 kB
+AnonPages: 12960 kB
+Mapped: 6700 kB
+Shmem: 3684416 kB
+KReclaimable: 27616 kB
+Slab: 54652 kB
+SReclaimable: 27616 kB
+SUnreclaim: 27036 kB
+KernelStack: 2496 kB
+PageTables: 1516 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 6170052 kB
+Committed_AS: 4212940 kB
+VmallocTotal: 34359738367 kB
+VmallocUsed: 16116 kB
+VmallocChunk: 0 kB
+Percpu: 2288 kB
+HardwareCorrupted: 0 kB
+AnonHugePages: 0 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+FileHugePages: 0 kB
+FilePmdMapped: 0 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB
+Hugetlb: 0 kB
+DirectMap4k: 110452 kB
+DirectMap2M: 5132288 kB
+DirectMap1G: 5242880 kB
diff --git a/lib/dpkg/t/data/meminfo-no-unit b/lib/dpkg/t/data/meminfo-no-unit
new file mode 100644
index 0000000..4db2d9c
--- /dev/null
+++ b/lib/dpkg/t/data/meminfo-no-unit
@@ -0,0 +1,53 @@
+MemTotal: 3951508
+MemFree: 130976
+MemAvailable: 10584
+Buffers: 2448
+Cached: 3694676
+SwapCached: 12936
+Active: 3111920
+Inactive: 610376
+Active(anon): 3102668
+Inactive(anon): 606952
+Active(file): 9252
+Inactive(file): 3424
+Unevictable: 0
+Mlocked: 0
+SwapTotal: 4194300
+SwapFree: 3777400
+Zswap: 0
+Zswapped: 0
+Dirty: 0
+Writeback: 0
+AnonPages: 12960
+Mapped: 6700
+Shmem: 3684416
+KReclaimable: 27616
+Slab: 54652
+SReclaimable: 27616
+SUnreclaim: 27036
+KernelStack: 2496
+PageTables: 1516
+NFS_Unstable: 0
+Bounce: 0
+WritebackTmp: 0
+CommitLimit: 6170052
+Committed_AS: 4212940
+VmallocTotal: 34359738367
+VmallocUsed: 16116
+VmallocChunk: 0
+Percpu: 2288
+HardwareCorrupted: 0
+AnonHugePages: 0
+ShmemHugePages: 0
+ShmemPmdMapped: 0
+FileHugePages: 0
+FilePmdMapped: 0
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048
+Hugetlb: 0
+DirectMap4k: 110452
+DirectMap2M: 5132288
+DirectMap1G: 5242880
diff --git a/lib/dpkg/t/data/meminfo-ok b/lib/dpkg/t/data/meminfo-ok
new file mode 100644
index 0000000..d4fdf0b
--- /dev/null
+++ b/lib/dpkg/t/data/meminfo-ok
@@ -0,0 +1,53 @@
+MemTotal: 3951508 kB
+MemFree: 130976 kB
+MemAvailable: 10584 kB
+Buffers: 2448 kB
+Cached: 3694676 kB
+SwapCached: 12936 kB
+Active: 3111920 kB
+Inactive: 610376 kB
+Active(anon): 3102668 kB
+Inactive(anon): 606952 kB
+Active(file): 9252 kB
+Inactive(file): 3424 kB
+Unevictable: 0 kB
+Mlocked: 0 kB
+SwapTotal: 4194300 kB
+SwapFree: 3777400 kB
+Zswap: 0 kB
+Zswapped: 0 kB
+Dirty: 0 kB
+Writeback: 0 kB
+AnonPages: 12960 kB
+Mapped: 6700 kB
+Shmem: 3684416 kB
+KReclaimable: 27616 kB
+Slab: 54652 kB
+SReclaimable: 27616 kB
+SUnreclaim: 27036 kB
+KernelStack: 2496 kB
+PageTables: 1516 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 6170052 kB
+Committed_AS: 4212940 kB
+VmallocTotal: 34359738367 kB
+VmallocUsed: 16116 kB
+VmallocChunk: 0 kB
+Percpu: 2288 kB
+HardwareCorrupted: 0 kB
+AnonHugePages: 0 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+FileHugePages: 0 kB
+FilePmdMapped: 0 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB
+Hugetlb: 0 kB
+DirectMap4k: 110452 kB
+DirectMap2M: 5132288 kB
+DirectMap1G: 5242880 kB
diff --git a/lib/dpkg/t/t-ar.c b/lib/dpkg/t/t-ar.c
new file mode 100644
index 0000000..88e9387
--- /dev/null
+++ b/lib/dpkg/t/t-ar.c
@@ -0,0 +1,61 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-ar.c - test ar implementation
+ *
+ * Copyright © 2010 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <string.h>
+
+#include <dpkg/test.h>
+#include <dpkg/ar.h>
+
+static void
+test_ar_normalize_name(void)
+{
+ struct dpkg_ar_hdr arh;
+
+ memccpy(arh.ar_name, "member-name/ ", '\0', sizeof(arh.ar_name));
+ dpkg_ar_normalize_name(&arh);
+ test_str(arh.ar_name, ==, "member-name");
+
+ memccpy(arh.ar_name, "member-name ", '\0', sizeof(arh.ar_name));
+ dpkg_ar_normalize_name(&arh);
+ test_str(arh.ar_name, ==, "member-name");
+}
+
+static void
+test_ar_member_is_illegal(void)
+{
+ struct dpkg_ar_hdr arh;
+
+ memset(&arh, ' ', sizeof(arh));
+ test_pass(dpkg_ar_member_is_illegal(&arh));
+
+ memcpy(arh.ar_fmag, DPKG_AR_FMAG, sizeof(arh.ar_fmag));
+ test_fail(dpkg_ar_member_is_illegal(&arh));
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(4);
+
+ test_ar_normalize_name();
+ test_ar_member_is_illegal();
+}
diff --git a/lib/dpkg/t/t-arch.c b/lib/dpkg/t/t-arch.c
new file mode 100644
index 0000000..430980e
--- /dev/null
+++ b/lib/dpkg/t/t-arch.c
@@ -0,0 +1,225 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-arch.c - test dpkg_arch implementation
+ *
+ * Copyright © 2011 Linaro Limited
+ * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org>
+ * Copyright © 2011-2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/varbuf.h>
+#include <dpkg/arch.h>
+
+static void
+test_dpkg_arch_name_is_illegal(void)
+{
+ /* Test invalid architecture names. */
+ test_fail(dpkg_arch_name_is_illegal("") == NULL);
+ test_fail(dpkg_arch_name_is_illegal("-i386") == NULL);
+ test_fail(dpkg_arch_name_is_illegal(" i386") == NULL);
+ test_fail(dpkg_arch_name_is_illegal(":any") == NULL);
+ test_fail(dpkg_arch_name_is_illegal("amd64_test") == NULL);
+ test_fail(dpkg_arch_name_is_illegal("i386:test") == NULL);
+ test_fail(dpkg_arch_name_is_illegal("i386 amd64") == NULL);
+ test_fail(dpkg_arch_name_is_illegal("i386,amd64") == NULL);
+ test_fail(dpkg_arch_name_is_illegal("i386|amd64") == NULL);
+
+ /* Test valid architecture names. */
+ test_pass(dpkg_arch_name_is_illegal("i386") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("amd64") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("hurd-i386") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("kfreebsd-i386") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("kfreebsd-amd64") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("ia64") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("alpha") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("armel") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("hppa") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("mips") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("mipsel") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("powerpc") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("s390") == NULL);
+ test_pass(dpkg_arch_name_is_illegal("sparc") == NULL);
+}
+
+static void
+test_dpkg_arch_get_list(void)
+{
+ struct dpkg_arch *arch;
+ int count = 1;
+
+ /* Must never return NULL. */
+ arch = dpkg_arch_get_list();
+ test_alloc(arch);
+
+ while ((arch = arch->next))
+ count++;
+
+ /* The default list should contain 3 architectures. */
+ test_pass(count == 3);
+}
+
+static void
+test_dpkg_arch_find(void)
+{
+ struct dpkg_arch *arch;
+
+ /* Test existence and initial values of default architectures. */
+ arch = dpkg_arch_find("all");
+ test_pass(arch->type == DPKG_ARCH_ALL);
+ test_pass(dpkg_arch_get(DPKG_ARCH_ALL) == arch);
+ arch = dpkg_arch_find(ARCHITECTURE);
+ test_pass(arch->type == DPKG_ARCH_NATIVE);
+ test_pass(dpkg_arch_get(DPKG_ARCH_NATIVE) == arch);
+ arch = dpkg_arch_find("any");
+ test_pass(arch->type == DPKG_ARCH_WILDCARD);
+ test_pass(dpkg_arch_get(DPKG_ARCH_WILDCARD) == arch);
+
+ /* Test missing architecture. */
+ arch = dpkg_arch_find(NULL);
+ test_pass(arch->type == DPKG_ARCH_NONE);
+ test_pass(dpkg_arch_get(DPKG_ARCH_NONE) == arch);
+ test_str(arch->name, ==, "");
+
+ /* Test empty architectures. */
+ arch = dpkg_arch_find("");
+ test_pass(arch->type == DPKG_ARCH_EMPTY);
+ test_pass(dpkg_arch_get(DPKG_ARCH_EMPTY) == arch);
+ test_str(arch->name, ==, "");
+
+ /* Test for an unknown type. */
+ test_pass(dpkg_arch_get(1000) == NULL);
+
+ /* New valid architectures are marked unknown. */
+ arch = dpkg_arch_find("foobar");
+ test_pass(arch->type == DPKG_ARCH_UNKNOWN);
+ test_str(arch->name, ==, "foobar");
+
+ /* New illegal architectures are marked illegal. */
+ arch = dpkg_arch_find("a:b");
+ test_pass(arch->type == DPKG_ARCH_ILLEGAL);
+ test_str(arch->name, ==, "a:b");
+}
+
+static void
+test_dpkg_arch_reset_list(void)
+{
+ dpkg_arch_reset_list();
+
+ test_dpkg_arch_get_list();
+}
+
+static void
+test_dpkg_arch_modify(void)
+{
+ struct dpkg_arch *arch;
+
+ dpkg_arch_reset_list();
+
+ /* Insert a new unknown arch. */
+ arch = dpkg_arch_find("foo");
+ test_pass(arch->type == DPKG_ARCH_UNKNOWN);
+ test_str(arch->name, ==, "foo");
+
+ /* Check that existing unknown arch gets tagged. */
+ arch = dpkg_arch_add("foo");
+ test_pass(arch->type == DPKG_ARCH_FOREIGN);
+ test_str(arch->name, ==, "foo");
+
+ /* Check that new unknown arch gets tagged. */
+ arch = dpkg_arch_add("quux");
+ test_pass(arch->type == DPKG_ARCH_FOREIGN);
+ test_str(arch->name, ==, "quux");
+
+ /* Unmark foreign architectures. */
+
+ arch = dpkg_arch_find("foo");
+ dpkg_arch_unmark(arch);
+ test_pass(arch->type == DPKG_ARCH_UNKNOWN);
+
+ arch = dpkg_arch_find("bar");
+ dpkg_arch_unmark(arch);
+ test_pass(arch->type == DPKG_ARCH_UNKNOWN);
+
+ arch = dpkg_arch_find("quux");
+ dpkg_arch_unmark(arch);
+ test_pass(arch->type == DPKG_ARCH_UNKNOWN);
+}
+
+static void
+test_dpkg_arch_varbuf_archqual(void)
+{
+ struct varbuf vb = VARBUF_INIT;
+
+ varbuf_add_archqual(&vb, dpkg_arch_get(DPKG_ARCH_NONE));
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "");
+ varbuf_reset(&vb);
+
+ varbuf_add_archqual(&vb, dpkg_arch_get(DPKG_ARCH_EMPTY));
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "");
+ varbuf_reset(&vb);
+
+ varbuf_add_archqual(&vb, dpkg_arch_get(DPKG_ARCH_ALL));
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, ":all");
+ varbuf_reset(&vb);
+
+ varbuf_add_archqual(&vb, dpkg_arch_get(DPKG_ARCH_WILDCARD));
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, ":any");
+ varbuf_reset(&vb);
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_dpkg_arch_describe(void)
+{
+ struct dpkg_arch *arch;
+
+ arch = dpkg_arch_get(DPKG_ARCH_NONE);
+ test_str(dpkg_arch_describe(arch), ==, "<none>");
+
+ arch = dpkg_arch_get(DPKG_ARCH_EMPTY);
+ test_str(dpkg_arch_describe(arch), ==, "<empty>");
+
+ arch = dpkg_arch_get(DPKG_ARCH_ALL);
+ test_str(dpkg_arch_describe(arch), ==, "all");
+
+ arch = dpkg_arch_get(DPKG_ARCH_WILDCARD);
+ test_str(dpkg_arch_describe(arch), ==, "any");
+
+ arch = dpkg_arch_get(DPKG_ARCH_NATIVE);
+ test_str(dpkg_arch_describe(arch), ==, ARCHITECTURE);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(60);
+
+ test_dpkg_arch_name_is_illegal();
+ test_dpkg_arch_get_list();
+ test_dpkg_arch_find();
+ test_dpkg_arch_reset_list();
+ test_dpkg_arch_modify();
+ test_dpkg_arch_varbuf_archqual();
+ test_dpkg_arch_describe();
+}
diff --git a/lib/dpkg/t/t-buffer.c b/lib/dpkg/t/t-buffer.c
new file mode 100644
index 0000000..fa939e5
--- /dev/null
+++ b/lib/dpkg/t/t-buffer.c
@@ -0,0 +1,82 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-buffer.c - test buffer handling
+ *
+ * Copyright © 2009-2011 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/buffer.h>
+#include <dpkg/dpkg.h>
+
+#include <sys/types.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static const char str_empty[] = "";
+static const char ref_hash_empty[] = "d41d8cd98f00b204e9800998ecf8427e";
+static const char str_test[] = "this is a test string\n";
+static const char ref_hash_test[] = "475aae3b885d70a9130eec23ab33f2b9";
+
+static void
+test_buffer_hash(void)
+{
+ char hash[MD5HASHLEN + 1];
+
+ buffer_md5(str_empty, hash, strlen(str_empty));
+ test_str(hash, ==, ref_hash_empty);
+
+ buffer_md5(str_test, hash, strlen(str_test));
+ test_str(hash, ==, ref_hash_test);
+}
+
+static void
+test_fdio_hash(void)
+{
+ char hash[MD5HASHLEN + 1];
+ char *test_file;
+ int fd;
+
+ test_file = test_alloc(strdup("test.XXXXXX"));
+ fd = mkstemp(test_file);
+ test_pass(fd >= 0);
+
+ test_pass(fd_md5(fd, hash, -1, NULL) >= 0);
+ test_str(hash, ==, ref_hash_empty);
+
+ test_pass(write(fd, str_test, strlen(str_test)) == (ssize_t)strlen(str_test));
+ test_pass(lseek(fd, 0, SEEK_SET) == 0);
+
+ test_pass(fd_md5(fd, hash, -1, NULL) >= 0);
+ test_str(hash, ==, ref_hash_test);
+
+ test_pass(unlink(test_file) == 0);
+
+ free(test_file);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(10);
+
+ test_buffer_hash();
+ test_fdio_hash();
+}
diff --git a/lib/dpkg/t/t-c-ctype.c b/lib/dpkg/t/t-c-ctype.c
new file mode 100644
index 0000000..2da0553
--- /dev/null
+++ b/lib/dpkg/t/t-c-ctype.c
@@ -0,0 +1,106 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-c-ctype.c - test C locale ctype functions
+ *
+ * Copyright © 2009-2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/c-ctype.h>
+
+static void
+test_ctype(void)
+{
+ int c;
+
+ for (c = -1; c < 256; c++) {
+ /* Test blank. */
+ if (c == '\t' || c == ' ')
+ test_pass(c_isblank(c));
+ else
+ test_fail(c_isblank(c));
+
+ /* Test white. */
+ if (c == '\t' || c == ' ' || c == '\n')
+ test_pass(c_iswhite(c));
+ else
+ test_fail(c_iswhite(c));
+
+ /* Test space. */
+ if (c == '\t' || c == '\v' || c == '\f' ||
+ c == '\r' || c == '\n' || c == ' ')
+ test_pass(c_isspace(c));
+ else
+ test_fail(c_isspace(c));
+
+ /* Test digit. */
+ if (c >= '0' && c <= '9')
+ test_pass(c_isdigit(c));
+ else
+ test_fail(c_isdigit(c));
+
+ /* Test lower case. */
+ if (c >= 'a' && c <= 'z')
+ test_pass(c_islower(c));
+ else
+ test_fail(c_islower(c));
+
+ /* Test upper case. */
+ if (c >= 'A' && c <= 'Z')
+ test_pass(c_isupper(c));
+ else
+ test_fail(c_isupper(c));
+
+ /* Test alpha. */
+ if (c_islower(c) || c_isupper(c))
+ test_pass(c_isalpha(c));
+ else
+ test_fail(c_isalpha(c));
+
+ /* Test alphanumeric. */
+ if (c_isdigit(c) || c_isalpha(c))
+ test_pass(c_isalnum(c));
+ else
+ test_fail(c_isalnum(c));
+ }
+}
+
+static void
+test_casing(void)
+{
+ test_pass(c_tolower('A') == 'a');
+ test_pass(c_tolower('Z') == 'z');
+
+ test_pass(c_tolower('a') == 'a');
+ test_pass(c_tolower('z') == 'z');
+
+ test_pass(c_tolower('0') == '0');
+ test_pass(c_tolower('9') == '9');
+
+ /* Test if we can handle the value for EOF. */
+ test_pass(c_tolower(-1) == -1);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(2063);
+
+ test_ctype();
+ test_casing();
+}
diff --git a/lib/dpkg/t/t-command.c b/lib/dpkg/t/t-command.c
new file mode 100644
index 0000000..33e002e
--- /dev/null
+++ b/lib/dpkg/t/t-command.c
@@ -0,0 +1,225 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-command.c - test command implementation
+ *
+ * Copyright © 2010-2012 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dpkg/test.h>
+#include <dpkg/subproc.h>
+#include <dpkg/command.h>
+#include <dpkg/dpkg.h>
+
+static void
+test_command_init(void)
+{
+ struct command cmd;
+
+ command_init(&cmd, "/absolute/path/to/progname", NULL);
+ test_str(cmd.filename, ==, "/absolute/path/to/progname");
+ test_str(cmd.name, ==, "progname");
+ test_pass(cmd.argc == 0);
+ test_pass(cmd.argv[0] == NULL);
+
+ command_destroy(&cmd);
+ test_pass(cmd.filename == NULL);
+ test_pass(cmd.name == NULL);
+ test_pass(cmd.argc == 0);
+ test_pass(cmd.argv == NULL);
+
+ command_init(&cmd, "progname", NULL);
+ test_str(cmd.filename, ==, "progname");
+ test_str(cmd.name, ==, "progname");
+ test_pass(cmd.argc == 0);
+ test_pass(cmd.argv[0] == NULL);
+
+ command_destroy(&cmd);
+
+ command_init(&cmd, "progname", "description");
+ test_str(cmd.filename, ==, "progname");
+ test_str(cmd.name, ==, "description");
+ test_pass(cmd.argc == 0);
+ test_pass(cmd.argv[0] == NULL);
+
+ command_destroy(&cmd);
+}
+
+static void
+test_command_grow_argv(void)
+{
+ struct command cmd;
+ int argv_size, i;
+
+ command_init(&cmd, "test", NULL);
+
+ argv_size = cmd.argv_size + 4;
+ for (i = 0; i < argv_size; i++)
+ command_add_arg(&cmd, "arg");
+
+ test_pass(cmd.argc == argv_size);
+ test_pass(cmd.argv_size >= argv_size);
+ test_str(cmd.argv[0], ==, "arg");
+ test_str(cmd.argv[argv_size - 1], ==, "arg");
+ test_pass(cmd.argv[argv_size] == NULL);
+
+ command_destroy(&cmd);
+}
+
+static void
+test_command_add_arg(void)
+{
+ struct command cmd;
+
+ command_init(&cmd, "test", NULL);
+
+ command_add_arg(&cmd, "arg 0");
+ test_pass(cmd.argc == 1);
+ test_str(cmd.argv[0], ==, "arg 0");
+ test_pass(cmd.argv[1] == NULL);
+
+ command_add_arg(&cmd, "arg 1");
+ test_pass(cmd.argc == 2);
+ test_str(cmd.argv[0], ==, "arg 0");
+ test_str(cmd.argv[1], ==, "arg 1");
+ test_pass(cmd.argv[2] == NULL);
+
+ command_add_arg(&cmd, "arg 2");
+ test_pass(cmd.argc == 3);
+ test_str(cmd.argv[0], ==, "arg 0");
+ test_str(cmd.argv[1], ==, "arg 1");
+ test_str(cmd.argv[2], ==, "arg 2");
+ test_pass(cmd.argv[3] == NULL);
+
+ command_destroy(&cmd);
+}
+
+static void
+test_command_add_argl(void)
+{
+ struct command cmd;
+ const char *args[] = {
+ "arg 1",
+ "arg 2",
+ "arg 3",
+ NULL,
+ };
+
+ command_init(&cmd, "test", NULL);
+
+ command_add_arg(&cmd, "arg 0");
+
+ command_add_argl(&cmd, args);
+ test_pass(cmd.argc == 4);
+ test_str(cmd.argv[0], ==, "arg 0");
+ test_str(cmd.argv[1], ==, "arg 1");
+ test_str(cmd.argv[2], ==, "arg 2");
+ test_str(cmd.argv[3], ==, "arg 3");
+ test_pass(cmd.argv[4] == NULL);
+
+ command_destroy(&cmd);
+}
+
+static void
+test_command_add_args(void)
+{
+ struct command cmd;
+
+ command_init(&cmd, "test", NULL);
+
+ command_add_arg(&cmd, "arg 0");
+
+ command_add_args(&cmd, "arg 1", "arg 2", "arg 3", NULL);
+ test_pass(cmd.argc == 4);
+ test_str(cmd.argv[0], ==, "arg 0");
+ test_str(cmd.argv[1], ==, "arg 1");
+ test_str(cmd.argv[2], ==, "arg 2");
+ test_str(cmd.argv[3], ==, "arg 3");
+ test_pass(cmd.argv[4] == NULL);
+
+ command_destroy(&cmd);
+}
+
+static void
+test_command_exec(void)
+{
+ struct command cmd;
+ pid_t pid;
+ int ret;
+
+ command_init(&cmd, "true", "exec test");
+
+ command_add_arg(&cmd, "true");
+ command_add_arg(&cmd, "arg 0");
+ command_add_arg(&cmd, "arg 1");
+
+ pid = subproc_fork();
+
+ if (pid == 0)
+ command_exec(&cmd);
+
+ ret = subproc_reap(pid, "command exec test", 0);
+ test_pass(ret == 0);
+
+ command_destroy(&cmd);
+}
+
+static void
+test_command_shell(void)
+{
+ pid_t pid;
+ int ret;
+
+ pid = subproc_fork();
+ if (pid == 0)
+ command_shell("true", "command shell pass test");
+ ret = subproc_reap(pid, "command shell pass test", 0);
+ test_pass(ret == 0);
+
+ pid = subproc_fork();
+ if (pid == 0)
+ command_shell("false", "command shell fail test");
+ ret = subproc_reap(pid, "command shell fail test", SUBPROC_RETERROR);
+ test_fail(ret == 0);
+
+ unsetenv("SHELL");
+ pid = subproc_fork();
+ if (pid == 0)
+ command_shell("true", "command default shell test");
+ ret = subproc_reap(pid, "command default shell test", 0);
+ test_pass(ret == 0);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(49);
+
+ test_command_init();
+ test_command_grow_argv();
+ test_command_add_arg();
+ test_command_add_argl();
+ test_command_add_args();
+ test_command_exec();
+ test_command_shell();
+}
diff --git a/lib/dpkg/t/t-deb-version.c b/lib/dpkg/t/t-deb-version.c
new file mode 100644
index 0000000..38b4608
--- /dev/null
+++ b/lib/dpkg/t/t-deb-version.c
@@ -0,0 +1,90 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-deb-version.c - test deb version handling
+ *
+ * Copyright © 2012 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <limits.h>
+#include <stdio.h>
+
+#include <dpkg/test.h>
+#include <dpkg/deb-version.h>
+
+static void
+test_deb_version_parse(void)
+{
+ struct deb_version v;
+ char *vs;
+
+ /* Test valid versions. */
+ test_pass(deb_version_parse(&v, "0.0") == NULL);
+ test_pass(v.major == 0 && v.minor == 0);
+
+ test_pass(deb_version_parse(&v, "1.1") == NULL);
+ test_pass(v.major == 1 && v.minor == 1);
+
+ test_pass(deb_version_parse(&v, "1.001") == NULL);
+ test_pass(v.major == 1 && v.minor == 1);
+
+ test_pass(deb_version_parse(&v, "1.0010") == NULL);
+ test_pass(v.major == 1 && v.minor == 10);
+
+ test_pass(deb_version_parse(&v, "0.939000") == NULL);
+ test_pass(v.major == 0 && v.minor == 939000);
+
+ test_pass(deb_version_parse(&v, "1.1\n") == NULL);
+ test_pass(v.major == 1 && v.minor == 1);
+
+ /* Test invalid versions. */
+ test_fail(deb_version_parse(&v, "0") == NULL);
+ test_fail(deb_version_parse(&v, "a") == NULL);
+ test_fail(deb_version_parse(&v, "a.b") == NULL);
+ test_fail(deb_version_parse(&v, "a~b") == NULL);
+ test_fail(deb_version_parse(&v, " 1.1") == NULL);
+ test_fail(deb_version_parse(&v, "2 .2") == NULL);
+ test_fail(deb_version_parse(&v, "3. 3") == NULL);
+ test_fail(deb_version_parse(&v, "4.4 ") == NULL);
+ test_fail(deb_version_parse(&v, " 5.5 ") == NULL);
+
+ /* Test integer limits. */
+ if (asprintf(&vs, "%d.0", INT_MAX) < 0)
+ test_bail("cannot allocate memory for asprintf()");
+ test_pass(deb_version_parse(&v, vs) == NULL);
+ free(vs);
+
+ if (asprintf(&vs, "%d.0", INT_MAX - 1) < 0)
+ test_bail("cannot allocate memory for asprintf()");
+ test_pass(deb_version_parse(&v, vs) == NULL);
+ free(vs);
+
+ if (asprintf(&vs, "%u.0", 1U + (unsigned int)INT_MAX) < 0)
+ test_bail("cannot allocate memory for asprintf()");
+ test_fail(deb_version_parse(&v, vs) == NULL);
+ free(vs);
+
+ /* TODO: Complete. */
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(24);
+
+ test_deb_version_parse();
+}
diff --git a/lib/dpkg/t/t-ehandle.c b/lib/dpkg/t/t-ehandle.c
new file mode 100644
index 0000000..979d2b7
--- /dev/null
+++ b/lib/dpkg/t/t-ehandle.c
@@ -0,0 +1,128 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-ehandle.c - test error handling implementation
+ *
+ * Copyright © 2016 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <stdbool.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <dpkg/test.h>
+#include <dpkg/ehandle.h>
+
+jmp_buf global_handler_jump;
+
+static void
+printer_empty(const char *msg, const void *data)
+{
+}
+
+static void
+handler_func(void)
+{
+ longjmp(global_handler_jump, 1);
+}
+
+static void
+test_error_handler_func(void)
+{
+ bool pass;
+
+ if (setjmp(global_handler_jump)) {
+ pass = true;
+ pop_error_context(ehflag_normaltidy);
+ } else {
+ pass = false;
+ push_error_context_func(handler_func, printer_empty, "test func");
+ ohshit("test func error");
+ test_bail("ohshit() is not supposed to return");
+ }
+ test_pass(pass);
+}
+
+static void
+test_error_handler_jump(void)
+{
+ jmp_buf handler_jump;
+ bool pass;
+
+ if (setjmp(handler_jump)) {
+ pass = true;
+ pop_error_context(ehflag_normaltidy);
+ } else {
+ pass = false;
+ push_error_context_jump(&handler_jump, printer_empty, "test jump");
+ ohshit("test jump error");
+ test_bail("ohshit() is not supposed to return");
+ }
+ test_pass(pass);
+}
+
+static void
+cleanup_error(int argc, void **argv)
+{
+ ohshit("cleanup error");
+}
+
+static void
+test_cleanup_error(void)
+{
+ jmp_buf handler_jump;
+ bool pass;
+
+ if (setjmp(handler_jump)) {
+ /* The ohshit() is not supposed to get us here, as it should
+ * be caught by the internal recursive error context. */
+ pass = false;
+
+ pop_cleanup(ehflag_normaltidy);
+ pop_error_context(ehflag_normaltidy);
+ } else {
+ push_error_context_jump(&handler_jump, printer_empty, "test cleanup");
+ push_cleanup(cleanup_error, ~ehflag_normaltidy, 0);
+ pop_error_context(ehflag_bombout);
+
+ /* We should have recovered from the cleanup handler failing,
+ * and arrived here correctly. */
+ pass = true;
+ }
+
+ test_pass(pass);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(3);
+
+ if (!test_is_verbose()) {
+ int fd;
+
+ /* Shut up stderr, we do not want the error output. */
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ test_bail("cannot open /dev/null");
+ dup2(fd, 2);
+ }
+
+ test_error_handler_func();
+ test_error_handler_jump();
+ test_cleanup_error();
+}
diff --git a/lib/dpkg/t/t-error.c b/lib/dpkg/t/t-error.c
new file mode 100644
index 0000000..e93459e
--- /dev/null
+++ b/lib/dpkg/t/t-error.c
@@ -0,0 +1,87 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-error.c - test error message reporting
+ *
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <dpkg/test.h>
+#include <dpkg/error.h>
+
+static void
+test_dpkg_error_put(void)
+{
+ struct dpkg_error err = DPKG_ERROR_INIT;
+ char *errstr_ref = NULL;
+
+ test_pass(err.type == DPKG_MSG_NONE);
+ test_pass(err.str == NULL);
+
+ test_pass(dpkg_put_warn(NULL, "void error") < 0);
+ test_pass(dpkg_put_error(NULL, "void error") < 0);
+ test_pass(dpkg_put_errno(NULL, "void error") < 0);
+
+ test_pass(dpkg_put_warn(&err, "test warning %d", 10) < 0);
+ test_pass(err.syserrno == 0);
+ test_str(err.str, ==, "test warning 10");
+ test_warn(err);
+
+ test_pass(dpkg_put_error(&err, "test error %d", 20) < 0);
+ test_pass(err.syserrno == 0);
+ test_str(err.str, ==, "test error 20");
+ test_error(err);
+
+ errno = ENOENT;
+ if (asprintf(&errstr_ref, "test errno 30 (%s)", strerror(errno)) < 0)
+ test_bail("cannot allocate string");
+ test_pass(dpkg_put_errno(&err, "test errno %d", 30) < 0);
+ test_str(err.str, ==, errstr_ref);
+ test_pass(err.syserrno == ENOENT);
+ test_error(err);
+ free(errstr_ref);
+ errno = 0;
+}
+
+static void
+test_dpkg_error_destroy(void)
+{
+ struct dpkg_error err = DPKG_ERROR_INIT;
+
+ errno = ENOENT;
+ test_pass(dpkg_put_errno(&err, "test destroy") < 0);
+ test_pass(err.syserrno == ENOENT);
+ test_pass(err.type == DPKG_MSG_ERROR);
+ test_pass(err.str != NULL);
+ dpkg_error_destroy(&err);
+ test_pass(err.type == DPKG_MSG_NONE);
+ test_pass(err.syserrno == 0);
+ test_pass(err.str == NULL);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(24);
+
+ test_dpkg_error_put();
+ test_dpkg_error_destroy();
+}
diff --git a/lib/dpkg/t/t-file.c b/lib/dpkg/t/t-file.c
new file mode 100644
index 0000000..0004df4
--- /dev/null
+++ b/lib/dpkg/t/t-file.c
@@ -0,0 +1,106 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-file.c - test file functions
+ *
+ * Copyright © 2018 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/file.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static const char ref_data[] =
+ "this is a test string\n"
+ "within a test file\n"
+ "containing multiple lines\n"
+;
+
+static void
+test_file_slurp(void)
+{
+ struct varbuf vb = VARBUF_INIT;
+ struct dpkg_error err = DPKG_ERROR_INIT;
+ char *test_file;
+ char *test_dir;
+ int fd;
+
+ test_pass(file_slurp("/nonexistent", &vb, &err) < 0);
+ test_pass(vb.used == 0);
+ test_pass(vb.buf == NULL);
+ test_pass(err.syserrno == ENOENT);
+ test_error(err);
+ varbuf_destroy(&vb);
+
+ test_dir = test_alloc(strdup("test.XXXXXX"));
+ test_pass(mkdtemp(test_dir) != NULL);
+ test_pass(file_slurp(test_dir, &vb, &err) < 0);
+ test_pass(vb.used == 0);
+ test_pass(vb.buf == NULL);
+ test_pass(err.syserrno == 0);
+ test_error(err);
+ varbuf_destroy(&vb);
+ test_pass(rmdir(test_dir) == 0);
+
+ test_file = test_alloc(strdup("test.XXXXXX"));
+ fd = mkstemp(test_file);
+ test_pass(fd >= 0);
+
+ test_pass(file_slurp(test_file, &vb, &err) == 0);
+ test_pass(vb.used == 0);
+ test_pass(vb.buf == NULL);
+ test_pass(err.syserrno == 0);
+ test_pass(err.type == DPKG_MSG_NONE);
+ varbuf_destroy(&vb);
+
+ test_pass(write(fd, ref_data, strlen(ref_data)) == (ssize_t)strlen(ref_data));
+ test_pass(lseek(fd, 0, SEEK_SET) == 0);
+
+ test_pass(file_slurp(test_file, &vb, &err) == 0);
+ test_pass(vb.used == strlen(ref_data));
+ test_mem(vb.buf, ==, ref_data, min(vb.used, strlen(ref_data)));
+ test_pass(err.syserrno == 0);
+ test_pass(err.type == DPKG_MSG_NONE);
+ varbuf_destroy(&vb);
+
+ test_fail(file_is_exec(test_dir));
+ test_fail(file_is_exec(test_file));
+ test_pass(chmod(test_file, 0755) == 0);
+ test_pass(file_is_exec(test_file));
+ test_pass(chmod(test_file, 0750) == 0);
+ test_pass(file_is_exec(test_file));
+
+ test_pass(unlink(test_file) == 0);
+ free(test_file);
+ free(test_dir);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(32);
+
+ test_file_slurp();
+}
diff --git a/lib/dpkg/t/t-fsys-dir.c b/lib/dpkg/t/t-fsys-dir.c
new file mode 100644
index 0000000..32f4aab
--- /dev/null
+++ b/lib/dpkg/t/t-fsys-dir.c
@@ -0,0 +1,91 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-fsys-dir.c - test filesystem handling
+ *
+ * Copyright © 2018 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <stdlib.h>
+
+#include <dpkg/test.h>
+#include <dpkg/fsys.h>
+
+static void
+test_fsys_dir(void)
+{
+ const char *newdir;
+ char *dir;
+
+ test_str(dpkg_fsys_get_dir(), ==, "");
+
+ newdir = dpkg_fsys_set_dir("/testdir//./");
+ test_str(newdir, ==, "/testdir");
+ test_str(dpkg_fsys_get_dir(), ==, "/testdir");
+
+ newdir = dpkg_fsys_set_dir("/testdir");
+ test_str(newdir, ==, "/testdir");
+ test_str(dpkg_fsys_get_dir(), ==, "/testdir");
+
+ newdir = dpkg_fsys_set_dir(newdir);
+ test_str(newdir, ==, "/testdir");
+ test_str(dpkg_fsys_get_dir(), ==, "/testdir");
+
+ dir = dpkg_fsys_get_path("testfile");
+ test_str(dir, ==, "/testdir/testfile");
+ free(dir);
+
+ dir = dpkg_fsys_get_path("/testfile");
+ test_str(dir, ==, "/testdir/testfile");
+ free(dir);
+
+ setenv("DPKG_ROOT", "/testenvdir//./", 1);
+ dpkg_fsys_set_dir(NULL);
+ test_str(dpkg_fsys_get_dir(), ==, "/testenvdir");
+
+ setenv("DPKG_ROOT", "/testenvdir", 1);
+ dpkg_fsys_set_dir(NULL);
+ test_str(dpkg_fsys_get_dir(), ==, "/testenvdir");
+
+ dir = dpkg_fsys_get_path("testfile");
+ test_str(dir, ==, "/testenvdir/testfile");
+ free(dir);
+
+ dir = dpkg_fsys_get_path("/testfile");
+ test_str(dir, ==, "/testenvdir/testfile");
+ free(dir);
+
+ unsetenv("DPKG_ROOT");
+ dpkg_fsys_set_dir(NULL);
+ test_str(dpkg_fsys_get_dir(), ==, "");
+
+ dir = dpkg_fsys_get_path("testfile");
+ test_str(dir, ==, "/testfile");
+ free(dir);
+
+ dir = dpkg_fsys_get_path("/testfile");
+ test_str(dir, ==, "/testfile");
+ free(dir);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(16);
+
+ test_fsys_dir();
+}
diff --git a/lib/dpkg/t/t-fsys-hash.c b/lib/dpkg/t/t-fsys-hash.c
new file mode 100644
index 0000000..7739328
--- /dev/null
+++ b/lib/dpkg/t/t-fsys-hash.c
@@ -0,0 +1,108 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-fsys-hash.c - test fsys-hash implementation
+ *
+ * Copyright © 2018 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/fsys.h>
+
+static void
+test_fsys_nodes(void)
+{
+ struct fsys_namenode *fnn;
+ struct fsys_hash_iter *iter;
+ const char *name;
+
+ test_pass(fsys_hash_entries() == 0);
+
+ fsys_hash_init();
+
+ fnn = fsys_hash_find_node("/nonexistent", FHFF_NONE);
+ test_pass(fnn == NULL);
+ test_pass(fsys_hash_entries() == 0);
+
+ name = "/test/path/aa";
+ fnn = fsys_hash_find_node(name, FHFF_NOCOPY);
+ test_pass(fnn != NULL);
+ test_pass(fsys_hash_entries() == 1);
+ test_pass(fnn->name == name);
+ test_str(fnn->name, ==, "/test/path/aa");
+ test_pass(fnn->flags == 0);
+ test_pass(fnn->oldhash == NULL);
+ test_pass(fnn->newhash == NULL);
+
+ fnn = fsys_hash_find_node("//./test/path/bb", 0);
+ test_pass(fnn != NULL);
+ test_pass(fsys_hash_entries() == 2);
+ test_str(fnn->name, ==, "/test/path/bb");
+ test_pass(fnn->flags == 0);
+ test_pass(fnn->oldhash == NULL);
+ test_pass(fnn->newhash == NULL);
+
+ fnn = fsys_hash_find_node("/test/path/cc", 0);
+ test_pass(fnn != NULL);
+ test_pass(fsys_hash_entries() == 3);
+ test_str(fnn->name, ==, "/test/path/cc");
+ test_pass(fnn->flags == 0);
+ test_pass(fnn->oldhash == NULL);
+ test_pass(fnn->newhash == NULL);
+
+ iter = fsys_hash_iter_new();
+ while ((fnn = fsys_hash_iter_next(iter))) {
+ if (strcmp(fnn->name, "/test/path/aa") == 0)
+ test_str(fnn->name, ==, "/test/path/aa");
+ else if (strcmp(fnn->name, "/test/path/bb") == 0)
+ test_str(fnn->name, ==, "/test/path/bb");
+ else if (strcmp(fnn->name, "/test/path/cc") == 0)
+ test_str(fnn->name, ==, "/test/path/cc");
+ else
+ test_fail("unknown fsys_namenode");
+ }
+ fsys_hash_iter_free(iter);
+
+ fsys_hash_init();
+ test_pass(fsys_hash_entries() == 3);
+ fnn = fsys_hash_find_node("/test/path/aa", FHFF_NONE);
+ test_pass(fnn != NULL);
+ fnn = fsys_hash_find_node("/test/path/bb", FHFF_NONE);
+ test_pass(fnn != NULL);
+ fnn = fsys_hash_find_node("/test/path/cc", FHFF_NONE);
+ test_pass(fnn != NULL);
+ test_pass(fsys_hash_entries() == 3);
+
+ fsys_hash_reset();
+ test_pass(fsys_hash_entries() == 0);
+ fnn = fsys_hash_find_node("/test/path/aa", FHFF_NONE);
+ test_pass(fnn == NULL);
+ fnn = fsys_hash_find_node("/test/path/bb", FHFF_NONE);
+ test_pass(fnn == NULL);
+ fnn = fsys_hash_find_node("/test/path/cc", FHFF_NONE);
+ test_pass(fnn == NULL);
+ test_pass(fsys_hash_entries() == 0);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(35);
+
+ test_fsys_nodes();
+}
diff --git a/lib/dpkg/t/t-headers-cpp.cc b/lib/dpkg/t/t-headers-cpp.cc
new file mode 100644
index 0000000..b5becf9
--- /dev/null
+++ b/lib/dpkg/t/t-headers-cpp.cc
@@ -0,0 +1,82 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-headers-cpp.cc - test C++ inclusion of headers
+ *
+ * Copyright © 2018 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <cstdbool>
+
+#include <dpkg/ar.h>
+#include <dpkg/arch.h>
+#include <dpkg/atomic-file.h>
+#include <dpkg/buffer.h>
+#include <dpkg/c-ctype.h>
+#include <dpkg/color.h>
+#include <dpkg/command.h>
+#include <dpkg/compress.h>
+#include <dpkg/db-ctrl.h>
+#include <dpkg/db-fsys.h>
+#include <dpkg/deb-version.h>
+#include <dpkg/debug.h>
+#include <dpkg/dir.h>
+#include <dpkg/dlist.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/ehandle.h>
+#include <dpkg/error.h>
+#include <dpkg/fdio.h>
+#include <dpkg/file.h>
+#include <dpkg/fsys.h>
+#include <dpkg/glob.h>
+#include <dpkg/i18n.h>
+#include <dpkg/macros.h>
+#include <dpkg/namevalue.h>
+#include <dpkg/options.h>
+#include <dpkg/pager.h>
+#include <dpkg/parsedump.h>
+#include <dpkg/path.h>
+#include <dpkg/pkg-array.h>
+#include <dpkg/pkg-files.h>
+#include <dpkg/pkg-format.h>
+#include <dpkg/pkg-list.h>
+#include <dpkg/pkg-queue.h>
+#include <dpkg/pkg-show.h>
+#include <dpkg/pkg-spec.h>
+#include <dpkg/pkg.h>
+#include <dpkg/progname.h>
+#include <dpkg/program.h>
+#include <dpkg/progress.h>
+#include <dpkg/report.h>
+#include <dpkg/string.h>
+#include <dpkg/subproc.h>
+#include <dpkg/tarfn.h>
+#include <dpkg/test.h>
+#include <dpkg/treewalk.h>
+#include <dpkg/trigdeferred.h>
+#include <dpkg/triglib.h>
+#include <dpkg/varbuf.h>
+#include <dpkg/version.h>
+
+TEST_ENTRY(test)
+{
+ test_plan(1);
+
+ test_pass(true);
+}
diff --git a/lib/dpkg/t/t-macros.c b/lib/dpkg/t/t-macros.c
new file mode 100644
index 0000000..9f4eaa1
--- /dev/null
+++ b/lib/dpkg/t/t-macros.c
@@ -0,0 +1,45 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-macros.c - test C support macros
+ *
+ * Copyright © 2009,2012 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/macros.h>
+
+TEST_ENTRY(test)
+{
+ test_plan(12);
+
+ test_pass(min(10, 30) == 10);
+ test_pass(min(30, 10) == 10);
+ test_pass(min(0, 10) == 0);
+ test_pass(min(-10, 0) == -10);
+
+ test_pass(max(10, 30) == 30);
+ test_pass(max(30, 10) == 30);
+ test_pass(max(0, 10) == 10);
+ test_pass(max(-10, 0) == 0);
+
+ test_pass(clamp(0, 0, 0) == 0);
+ test_pass(clamp(0, -10, 10) == 0);
+ test_pass(clamp(20, -10, 10) == 10);
+ test_pass(clamp(-20, -10, 10) == -10);
+}
diff --git a/lib/dpkg/t/t-meminfo.c b/lib/dpkg/t/t-meminfo.c
new file mode 100644
index 0000000..b231081
--- /dev/null
+++ b/lib/dpkg/t/t-meminfo.c
@@ -0,0 +1,88 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-meminfo.c - test memory information handling code
+ *
+ * Copyright © 2022 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/meminfo.h>
+
+static char *
+test_data_file(const char *filename)
+{
+ char *pathname;
+ int rc;
+
+ rc = asprintf(&pathname, "%s/t/data/%s", test_get_srcdir(), filename);
+ if (rc < 0)
+ test_bail("cannot allocate data filename");
+
+ return pathname;
+}
+
+static void
+test_meminfo(void)
+{
+ char *pathname;
+ uint64_t mem;
+ int rc;
+
+ mem = 0;
+ pathname = test_data_file("meminfo-no-file");
+ rc = meminfo_get_available_from_file(pathname, &mem);
+ test_pass(rc == MEMINFO_NO_FILE);
+ test_pass(mem == 0);
+ free(pathname);
+
+ mem = 0;
+ pathname = test_data_file("meminfo-no-data");
+ rc = meminfo_get_available_from_file(pathname, &mem);
+ test_pass(rc == MEMINFO_NO_DATA);
+ test_pass(mem == 0);
+ free(pathname);
+
+ mem = 0;
+ pathname = test_data_file("meminfo-no-unit");
+ rc = meminfo_get_available_from_file(pathname, &mem);
+ test_pass(rc == MEMINFO_NO_UNIT);
+ test_pass(mem == 0);
+ free(pathname);
+
+ mem = 0;
+ pathname = test_data_file("meminfo-no-info");
+ rc = meminfo_get_available_from_file(pathname, &mem);
+ test_pass(rc == MEMINFO_NO_INFO);
+ test_pass(mem == 0);
+ free(pathname);
+
+ mem = 0;
+ pathname = test_data_file("meminfo-ok");
+ rc = meminfo_get_available_from_file(pathname, &mem);
+ test_pass(rc == MEMINFO_OK);
+ test_pass(mem == 3919974400UL);
+ free(pathname);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(10);
+
+ test_meminfo();
+}
diff --git a/lib/dpkg/t/t-mod-db.c b/lib/dpkg/t/t-mod-db.c
new file mode 100644
index 0000000..c40d8e8
--- /dev/null
+++ b/lib/dpkg/t/t-mod-db.c
@@ -0,0 +1,57 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-mod-db.c - test database implementation
+ *
+ * Copyright © 2011 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <stdlib.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg-db.h>
+
+static void
+test_db_dir(void)
+{
+ char *dir;
+
+ test_str(dpkg_db_get_dir(), ==, ADMINDIR);
+
+ dpkg_db_set_dir("testdir");
+ test_str(dpkg_db_get_dir(), ==, "testdir");
+
+ setenv("DPKG_ADMINDIR", "testenvdir", 1);
+ dpkg_db_set_dir(NULL);
+ test_str(dpkg_db_get_dir(), ==, "testenvdir");
+
+ unsetenv("DPKG_ADMINDIR");
+ dpkg_db_set_dir(NULL);
+ test_str(dpkg_db_get_dir(), ==, ADMINDIR);
+
+ dir = dpkg_db_get_path("testfile");
+ test_str(dir, ==, ADMINDIR "/testfile");
+ free(dir);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(5);
+
+ test_db_dir();
+}
diff --git a/lib/dpkg/t/t-namevalue.c b/lib/dpkg/t/t-namevalue.c
new file mode 100644
index 0000000..c864766
--- /dev/null
+++ b/lib/dpkg/t/t-namevalue.c
@@ -0,0 +1,48 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-namevalue.c - test name/value implementation
+ *
+ * Copyright © 2018 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/namevalue.h>
+#include <dpkg/dpkg-db.h>
+
+static void
+test_namevalue(void)
+{
+ const struct namevalue *nv;
+
+ nv = namevalue_find_by_name(booleaninfos, "");
+ test_pass(nv == NULL);
+
+ nv = namevalue_find_by_name(booleaninfos, "no");
+ test_pass(nv != NULL);
+ test_pass(nv->value == false);
+ test_pass(nv->length == strlen("no"));
+ test_str(nv->name, ==, "no");
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(5);
+
+ test_namevalue();
+}
diff --git a/lib/dpkg/t/t-pager.c b/lib/dpkg/t/t-pager.c
new file mode 100644
index 0000000..2481e80
--- /dev/null
+++ b/lib/dpkg/t/t-pager.c
@@ -0,0 +1,75 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-pager.c - test pager implementation
+ *
+ * Copyright © 2010-2012 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dpkg/test.h>
+#include <dpkg/pager.h>
+#include <dpkg/dpkg.h>
+
+static void
+test_dup_file(int fd, const char *filename, int flags)
+{
+ int newfd;
+
+ newfd = open(filename, flags);
+ dup2(newfd, fd);
+ close(newfd);
+}
+
+static void
+test_pager_get_exec(void)
+{
+ const char *pager, *default_pager;
+ int origfd = dup(STDOUT_FILENO);
+
+ /* Test stdout being a tty. */
+ test_todo_block("environment might not expose controlling terminal") {
+ test_dup_file(STDOUT_FILENO, "/dev/tty", O_WRONLY);
+ setenv("PAGER", "test-pager", 1);
+ pager = pager_get_exec();
+ unsetenv("PAGER");
+ default_pager = pager_get_exec();
+ dup2(origfd, STDOUT_FILENO);
+ test_str(pager, ==, "test-pager");
+ test_str(default_pager, ==, DEFAULTPAGER);
+ }
+
+ /* Test stdout not being a tty. */
+ test_dup_file(STDOUT_FILENO, "/dev/null", O_WRONLY);
+ pager = pager_get_exec();
+ dup2(origfd, STDOUT_FILENO);
+ test_str(pager, ==, CAT);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(3);
+
+ test_pager_get_exec();
+}
diff --git a/lib/dpkg/t/t-path.c b/lib/dpkg/t/t-path.c
new file mode 100644
index 0000000..deb1b72
--- /dev/null
+++ b/lib/dpkg/t/t-path.c
@@ -0,0 +1,181 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-path.c - test path handling code
+ *
+ * Copyright © 2009-2012 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <dpkg/test.h>
+#include <dpkg/path.h>
+
+/* Use the test_trim_eq_ref macro to avoid leaking the string and to get
+ * meaningful line numbers from assert. */
+#define test_trim_eq_ref(p, ref) \
+do { \
+ char *t = test_alloc(strdup((p))); \
+ path_trim_slash_slashdot(t); \
+ test_str(t, ==, (ref)); \
+ free(t); \
+} while (0)
+
+static void
+test_path_trim(void)
+{
+ test_trim_eq_ref("/a", "/a");
+ test_trim_eq_ref("./././.", ".");
+ test_trim_eq_ref("./././", ".");
+ test_trim_eq_ref("./.", ".");
+ test_trim_eq_ref("./", ".");
+ test_trim_eq_ref("/./././.", "/");
+ test_trim_eq_ref("/./", "/");
+ test_trim_eq_ref("/.", "/");
+ test_trim_eq_ref("/", "/");
+ test_trim_eq_ref("", "");
+ test_trim_eq_ref("/./../.", "/./..");
+ test_trim_eq_ref("/foo/bar/./", "/foo/bar");
+ test_trim_eq_ref("./foo/bar/./", "./foo/bar");
+ test_trim_eq_ref("/./foo/bar/./", "/./foo/bar");
+}
+
+static void
+test_path_skip(void)
+{
+ test_str(path_skip_slash_dotslash("./././."), ==, ".");
+ test_str(path_skip_slash_dotslash("./././"), ==, "");
+ test_str(path_skip_slash_dotslash("./."), ==, ".");
+ test_str(path_skip_slash_dotslash("./"), ==, "");
+ test_str(path_skip_slash_dotslash("/./././."), ==, ".");
+ test_str(path_skip_slash_dotslash("/./"), ==, "");
+ test_str(path_skip_slash_dotslash("/."), ==, ".");
+ test_str(path_skip_slash_dotslash("/"), ==, "");
+ test_str(path_skip_slash_dotslash("/./../."), ==, "../.");
+ test_str(path_skip_slash_dotslash("/foo/bar/./"), ==, "foo/bar/./");
+ test_str(path_skip_slash_dotslash("./foo/bar/./"), ==, "foo/bar/./");
+ test_str(path_skip_slash_dotslash("/./foo/bar/./"), ==, "foo/bar/./");
+}
+
+static void
+test_path_basename(void)
+{
+ test_str(path_basename("./."), ==, ".");
+ test_str(path_basename("./"), ==, "");
+ test_str(path_basename("/."), ==, ".");
+ test_str(path_basename("/"), ==, "");
+ test_str(path_basename("/foo"), ==, "foo");
+ test_str(path_basename("/foo/bar"), ==, "bar");
+ test_str(path_basename("/foo/bar/"), ==, "");
+}
+
+static void
+test_path_temp(void)
+{
+ char *template;
+
+ template = path_make_temp_template("test");
+
+ test_pass(strstr(template, "test") != NULL);
+ test_pass(strstr(template, "XXXXXX") != NULL);
+
+ free(template);
+}
+
+static bool
+string_is_ascii(const char *str)
+{
+ while (*str) {
+ if (!isascii(*str))
+ return false;
+
+ str++;
+ }
+
+ return true;
+}
+
+static void
+test_path_quote(void)
+{
+ const char src_7_bit[] = "string with 7-bit chars only";
+ const char src_7_bit_trim[] = "string with 7-bit chars";
+ const char src_8_bit[] = "text w/ 8-bit chars: \\ \370 \300 \342 end";
+ const char src_8_bit_end[] = "text \370";
+ const char src_bs_end[] = "text \\";
+ char *dst;
+ size_t len;
+
+ /* Test 0 length. */
+ dst = NULL;
+ path_quote_filename(dst, src_7_bit, 0);
+
+ /* Test no quoting. */
+ len = strlen(src_7_bit) + 1;
+ dst = test_alloc(malloc(len));
+
+ path_quote_filename(dst, src_7_bit, len);
+ test_str(dst, ==, src_7_bit);
+ free(dst);
+
+ /* Test no quoting with limit. */
+ len = strlen(src_7_bit_trim) + 1;
+ dst = test_alloc(malloc(len));
+
+ path_quote_filename(dst, src_7_bit, len);
+ test_str(dst, ==, src_7_bit_trim);
+ free(dst);
+
+ /* Test normal quoting. */
+ len = strlen(src_8_bit) * 2 + 1;
+ dst = test_alloc(malloc(len));
+
+ path_quote_filename(dst, src_8_bit, len);
+ test_pass(strstr(dst, "end") != NULL);
+ test_pass(string_is_ascii(dst));
+ free(dst);
+
+ /* Test normal quoting with limit. */
+ len = strlen(src_8_bit_end) + 1 + 2;
+ dst = test_alloc(malloc(len));
+
+ path_quote_filename(dst, src_8_bit_end, len);
+ test_str(dst, ==, "text ");
+ free(dst);
+
+ /* Test backslash quoting with limit. */
+ len = strlen(src_bs_end) + 1;
+ dst = test_alloc(malloc(len));
+
+ path_quote_filename(dst, src_bs_end, len);
+ test_str(dst, ==, "text ");
+ free(dst);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(41);
+
+ test_path_trim();
+ test_path_skip();
+ test_path_basename();
+ test_path_temp();
+ test_path_quote();
+}
diff --git a/lib/dpkg/t/t-pkg-format.c b/lib/dpkg/t/t-pkg-format.c
new file mode 100644
index 0000000..a6d33fe
--- /dev/null
+++ b/lib/dpkg/t/t-pkg-format.c
@@ -0,0 +1,141 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-pkg-format.c - test pkg-format implementation
+ *
+ * Copyright © 2022 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/pkg.h>
+#include <dpkg/pkg-format.h>
+
+static void
+prep_pkg(struct pkgset *set, struct pkginfo *pkg)
+{
+ pkg_blank(pkg);
+
+ pkgset_blank(set);
+ pkgset_link_pkg(set, pkg);
+
+ set->name = "test-bin";
+ pkg->installed.description = "short synopsis -- some package\n"
+ " This is the extended description for this package-\n"
+ " .\n"
+ " Potentially expanding multiple lines.\n";
+ pkg->installed.arch = dpkg_arch_get(DPKG_ARCH_ALL);
+ pkg->installed.version = DPKG_VERSION_OBJECT(0, "4.5", "2");
+}
+
+static void
+prep_virtpkg(struct pkgset *set, struct pkginfo *pkg)
+{
+ pkg_blank(pkg);
+
+ pkgset_blank(set);
+ pkgset_link_pkg(set, pkg);
+
+ set->name = "test-virt";
+}
+
+static void
+test_field(struct pkginfo *pkg, const char *fmt, const char *exp)
+{
+ struct pkg_format_node *head;
+ struct varbuf vb = VARBUF_INIT;
+
+ head = pkg_format_parse(fmt, NULL);
+ test_pass(head);
+ pkg_format_print(&vb, head, pkg, &pkg->installed);
+ test_str(vb.buf, ==, exp);
+ pkg_format_free(head);
+}
+
+static void
+test_pkg_format_real_fields(void)
+{
+ struct pkgset pkgset;
+ struct pkginfo pkg;
+
+ prep_pkg(&pkgset, &pkg);
+
+ test_field(&pkg, "${Package}_${Version}_${Architecture}",
+ "test-bin_4.5-2_all");
+}
+
+static void
+test_pkg_format_virtual_fields(void)
+{
+ struct pkgset pkgset;
+ struct pkginfo pkg;
+
+ prep_pkg(&pkgset, &pkg);
+
+ test_field(&pkg, "${source:Package}_${source:Version}",
+ "test-bin_4.5-2");
+
+ pkg.installed.source = "test-src";
+ test_field(&pkg, "${source:Package}_${source:Version}",
+ "test-src_4.5-2");
+ test_field(&pkg, "${source:Upstream-Version}",
+ "4.5");
+
+ pkg.installed.source = "test-src (1:3.4-6)";
+ test_field(&pkg, "${source:Package}_${source:Version}",
+ "test-src_1:3.4-6");
+ test_field(&pkg, "${source:Upstream-Version}",
+ "3.4");
+
+ test_field(&pkg, "${binary:Synopsis}",
+ "short synopsis -- some package");
+
+ test_field(&pkg, "${binary:Summary}",
+ "short synopsis -- some package");
+
+ prep_virtpkg(&pkgset, &pkg);
+ test_field(&pkg, "${source:Package}_${source:Version}",
+ "test-virt_");
+ test_field(&pkg, "${source:Upstream-Version}",
+ "");
+}
+
+static void
+test_pkg_format_virtual_fields_db_fsys(void)
+{
+ struct pkg_format_node *head;
+
+ head = pkg_format_parse("prefix ${unknown-variable} suffix", NULL);
+ test_pass(head);
+ test_fail(pkg_format_needs_db_fsys(head));
+ pkg_format_free(head);
+
+ head = pkg_format_parse("prefix ${db-fsys:Files} suffix", NULL);
+ test_pass(head);
+ test_pass(pkg_format_needs_db_fsys(head));
+ pkg_format_free(head);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(24);
+
+ test_pkg_format_real_fields();
+ test_pkg_format_virtual_fields();
+ test_pkg_format_virtual_fields_db_fsys();
+}
diff --git a/lib/dpkg/t/t-pkg-hash.c b/lib/dpkg/t/t-pkg-hash.c
new file mode 100644
index 0000000..2c009f8
--- /dev/null
+++ b/lib/dpkg/t/t-pkg-hash.c
@@ -0,0 +1,179 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-pkg-hash.c - test pkg-hash implementation
+ *
+ * Copyright © 2018 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/pkg.h>
+
+static void
+test_pkg_hash(void)
+{
+ struct dpkg_arch *arch;
+ struct pkgset *set;
+ struct pkginfo *pkg;
+ struct pkg_hash_iter *iter;
+ int pkginstance;
+
+ test_pass(pkg_hash_count_set() == 0);
+ test_pass(pkg_hash_count_pkg() == 0);
+
+ set = pkg_hash_find_set("pkg-aa");
+ test_pass(set != NULL);
+ test_str(set->name, ==, "pkg-aa");
+ test_pass(pkg_hash_count_set() == 1);
+ test_pass(pkg_hash_count_pkg() == 1);
+
+ set = pkg_hash_find_set("pkg-aa");
+ test_pass(set != NULL);
+ test_str(set->name, ==, "pkg-aa");
+ test_pass(pkg_hash_count_set() == 1);
+ test_pass(pkg_hash_count_pkg() == 1);
+
+ set = pkg_hash_find_set("Pkg-AA");
+ test_pass(set != NULL);
+ test_str(set->name, ==, "pkg-aa");
+ test_pass(pkg_hash_count_set() == 1);
+ test_pass(pkg_hash_count_pkg() == 1);
+
+ set = pkg_hash_find_set("pkg-bb");
+ pkg_set_status(&set->pkg, PKG_STAT_INSTALLED);
+ test_pass(set != NULL);
+ test_str(set->name, ==, "pkg-bb");
+ test_pass(pkg_hash_count_set() == 2);
+ test_pass(pkg_hash_count_pkg() == 2);
+
+ set = pkg_hash_find_set("pkg-cc");
+ test_pass(set != NULL);
+ test_str(set->name, ==, "pkg-cc");
+ test_pass(pkg_hash_count_set() == 3);
+ test_pass(pkg_hash_count_pkg() == 3);
+
+ arch = dpkg_arch_find("arch-xx");
+ pkg = pkg_hash_find_pkg("pkg-aa", arch);
+ pkg_set_status(pkg, PKG_STAT_INSTALLED);
+ test_pass(pkg != NULL);
+ test_str(pkg->set->name, ==, "pkg-aa");
+ test_str(pkg->installed.arch->name, ==, "arch-xx");
+ test_str(pkg->available.arch->name, ==, "arch-xx");
+ test_pass(pkg_hash_count_set() == 3);
+ test_pass(pkg_hash_count_pkg() == 3);
+
+ arch = dpkg_arch_find("arch-yy");
+ pkg = pkg_hash_find_pkg("pkg-aa", arch);
+ test_pass(pkg != NULL);
+ test_str(pkg->set->name, ==, "pkg-aa");
+ test_str(pkg->installed.arch->name, ==, "arch-yy");
+ test_str(pkg->available.arch->name, ==, "arch-yy");
+ test_pass(pkg_hash_count_set() == 3);
+ test_pass(pkg_hash_count_pkg() == 4);
+
+ arch = dpkg_arch_find("arch-zz");
+ pkg = pkg_hash_find_pkg("pkg-aa", arch);
+ pkg_set_status(pkg, PKG_STAT_UNPACKED);
+ test_pass(pkg != NULL);
+ test_str(pkg->set->name, ==, "pkg-aa");
+ test_str(pkg->installed.arch->name, ==, "arch-zz");
+ test_str(pkg->available.arch->name, ==, "arch-zz");
+ test_pass(pkg_hash_count_set() == 3);
+ test_pass(pkg_hash_count_pkg() == 5);
+
+ arch = dpkg_arch_find("arch-xx");
+ pkg = pkg_hash_find_pkg("pkg-aa", arch);
+ test_pass(pkg != NULL);
+ test_str(pkg->set->name, ==, "pkg-aa");
+ test_str(pkg->installed.arch->name, ==, "arch-xx");
+ test_str(pkg->available.arch->name, ==, "arch-xx");
+ test_pass(pkg_hash_count_set() == 3);
+ test_pass(pkg_hash_count_pkg() == 5);
+
+ set = pkg_hash_find_set("pkg-aa");
+ test_str(set->name, ==, "pkg-aa");
+ pkg = pkg_hash_get_singleton(set);
+ test_pass(pkg == NULL);
+ test_pass(pkg_hash_count_set() == 3);
+ test_pass(pkg_hash_count_pkg() == 5);
+
+ pkg = pkg_hash_find_singleton("pkg-bb");
+ test_pass(pkg != NULL);
+ test_str(pkg->set->name, ==, "pkg-bb");
+ test_pass(pkg_hash_count_set() == 3);
+ test_pass(pkg_hash_count_pkg() == 5);
+
+ pkg = pkg_hash_find_singleton("pkg-cc");
+ test_pass(pkg != NULL);
+ test_str(pkg->set->name, ==, "pkg-cc");
+ test_pass(pkg_hash_count_set() == 3);
+ test_pass(pkg_hash_count_pkg() == 5);
+
+ iter = pkg_hash_iter_new();
+ while ((set = pkg_hash_iter_next_set(iter))) {
+ if (strcmp(set->name, "pkg-aa") == 0)
+ test_str(set->name, ==, "pkg-aa");
+ else if (strcmp(set->name, "pkg-bb") == 0)
+ test_str(set->name, ==, "pkg-bb");
+ else if (strcmp(set->name, "pkg-cc") == 0)
+ test_str(set->name, ==, "pkg-cc");
+ else
+ test_fail("unknown fsys_namenode");
+ }
+ pkg_hash_iter_free(iter);
+
+ pkginstance = 0;
+ iter = pkg_hash_iter_new();
+ while ((pkg = pkg_hash_iter_next_pkg(iter))) {
+ pkginstance++;
+ if (strcmp(pkg->set->name, "pkg-aa") == 0) {
+ struct pkgbin *pkgbin = &pkg->installed;
+
+ test_str(pkg->set->name, ==, "pkg-aa");
+ if (strcmp(pkgbin->arch->name, "arch-xx") == 0)
+ test_str(pkgbin->arch->name, ==, "arch-xx");
+ else if (strcmp(pkgbin->arch->name, "arch-yy") == 0)
+ test_str(pkgbin->arch->name, ==, "arch-yy");
+ else if (strcmp(pkgbin->arch->name, "arch-zz") == 0)
+ test_str(pkgbin->arch->name, ==, "arch-zz");
+ else
+ test_fail("unknown pkginfo instance");
+ } else if (strcmp(pkg->set->name, "pkg-bb") == 0) {
+ test_str(pkg->set->name, ==, "pkg-bb");
+ } else if (strcmp(pkg->set->name, "pkg-cc") == 0) {
+ test_str(pkg->set->name, ==, "pkg-cc");
+ } else {
+ test_fail("unknown fsys_namenode");
+ }
+ }
+ pkg_hash_iter_free(iter);
+ test_pass(pkginstance == 5);
+
+ pkg_hash_reset();
+ test_pass(pkg_hash_count_set() == 0);
+ test_pass(pkg_hash_count_pkg() == 0);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(72);
+
+ test_pkg_hash();
+}
diff --git a/lib/dpkg/t/t-pkg-list.c b/lib/dpkg/t/t-pkg-list.c
new file mode 100644
index 0000000..722cf2e
--- /dev/null
+++ b/lib/dpkg/t/t-pkg-list.c
@@ -0,0 +1,90 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-pkg-list.c - test pkg-list implementation
+ *
+ * Copyright © 2010,2012 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/pkg-list.h>
+
+static void
+test_pkg_list_new(void)
+{
+ struct pkg_list *l1, *l2, *l3;
+ struct pkginfo pkg1, pkg2, pkg3;
+
+ l1 = pkg_list_new(&pkg1, NULL);
+ test_alloc(l1);
+ test_pass(l1->next == NULL);
+ test_pass(l1->pkg == &pkg1);
+
+ l2 = pkg_list_new(&pkg2, l1);
+ test_alloc(l2);
+ test_pass(l2->next == l1);
+ test_pass(l2->pkg == &pkg2);
+
+ l3 = pkg_list_new(&pkg3, l2);
+ test_alloc(l3);
+ test_pass(l3->next == l2);
+ test_pass(l3->pkg == &pkg3);
+
+ pkg_list_free(l3);
+}
+
+static void
+test_pkg_list_prepend(void)
+{
+ struct pkg_list *head = NULL, *l1, *l2, *l3;
+ struct pkginfo pkg1, pkg2, pkg3, pkg4;
+
+ pkg_list_prepend(&head, &pkg1);
+ test_alloc(head);
+ test_pass(head->next == NULL);
+ test_pass(head->pkg == &pkg1);
+ l1 = head;
+
+ pkg_list_prepend(&head, &pkg2);
+ test_alloc(head);
+ test_pass(head->next == l1);
+ test_pass(head->pkg == &pkg2);
+ l2 = head;
+
+ pkg_list_prepend(&head, &pkg3);
+ test_alloc(head);
+ test_pass(head->next == l2);
+ test_pass(head->pkg == &pkg3);
+ l3 = head;
+
+ pkg_list_prepend(&head, &pkg4);
+ test_alloc(head);
+ test_pass(head->next == l3);
+ test_pass(head->pkg == &pkg4);
+
+ pkg_list_free(head);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(14);
+
+ test_pkg_list_new();
+ test_pkg_list_prepend();
+}
diff --git a/lib/dpkg/t/t-pkg-queue.c b/lib/dpkg/t/t-pkg-queue.c
new file mode 100644
index 0000000..cf1e327
--- /dev/null
+++ b/lib/dpkg/t/t-pkg-queue.c
@@ -0,0 +1,116 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-pkg-queue.c - test pkg-queue implementation
+ *
+ * Copyright © 2010,2012 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/pkg-queue.h>
+
+static void
+test_pkg_queue_init(void)
+{
+ struct pkg_queue q = PKG_QUEUE_INIT;
+ struct pkg_list l;
+
+ test_pass(q.length == 0);
+ test_pass(q.head == NULL);
+ test_pass(q.tail == NULL);
+
+ test_pass(pkg_queue_is_empty(&q));
+
+ q = (struct pkg_queue){ .length = 10, .head = &l, .tail = &l };
+
+ pkg_queue_init(&q);
+ test_pass(q.length == 0);
+ test_pass(q.head == NULL);
+ test_pass(q.tail == NULL);
+
+ test_pass(pkg_queue_is_empty(&q));
+}
+
+static void
+test_pkg_queue_push_pop(void)
+{
+ struct pkg_queue q = PKG_QUEUE_INIT;
+ struct pkg_list *l1, *l2, *l3;
+ struct pkginfo *pkgp, pkg1, pkg2, pkg3;
+
+ test_pass(pkg_queue_is_empty(&q));
+
+ /* Test push operations. */
+
+ l1 = pkg_queue_push(&q, &pkg1);
+ test_pass(l1 != NULL);
+ test_pass(q.head == l1);
+ test_pass(q.tail == l1);
+ test_pass(q.length == 1);
+
+ l2 = pkg_queue_push(&q, &pkg2);
+ test_pass(l2 != NULL);
+ test_pass(q.head == l1);
+ test_pass(q.tail == l2);
+ test_pass(q.length == 2);
+
+ l3 = pkg_queue_push(&q, &pkg3);
+ test_pass(l3 != NULL);
+ test_pass(q.head == l1);
+ test_pass(q.tail == l3);
+ test_pass(q.length == 3);
+
+ /* Test pop operations. */
+
+ pkgp = pkg_queue_pop(&q);
+ test_pass(pkgp == &pkg1);
+ test_pass(q.head == l2);
+ test_pass(q.tail == l3);
+ test_pass(q.length == 2);
+
+ pkgp = pkg_queue_pop(&q);
+ test_pass(pkgp == &pkg2);
+ test_pass(q.head == l3);
+ test_pass(q.tail == l3);
+ test_pass(q.length == 1);
+
+ pkgp = pkg_queue_pop(&q);
+ test_pass(pkgp == &pkg3);
+ test_pass(q.head == NULL);
+ test_pass(q.tail == NULL);
+ test_pass(q.length == 0);
+
+ test_pass(pkg_queue_is_empty(&q));
+
+ pkgp = pkg_queue_pop(&q);
+ test_pass(pkgp == NULL);
+ test_pass(q.head == NULL);
+ test_pass(q.tail == NULL);
+ test_pass(q.length == 0);
+
+ pkg_queue_destroy(&q);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(38);
+
+ test_pkg_queue_init();
+ test_pkg_queue_push_pop();
+}
diff --git a/lib/dpkg/t/t-pkg-show.c b/lib/dpkg/t/t-pkg-show.c
new file mode 100644
index 0000000..6bb361d
--- /dev/null
+++ b/lib/dpkg/t/t-pkg-show.c
@@ -0,0 +1,70 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-pkg-show.c - test pkg-show implementation
+ *
+ * Copyright © 2018 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/arch.h>
+
+static void
+test_pkg_show_name(void)
+{
+ struct dpkg_arch *arch;
+ struct pkginfo *pkg;
+ const char *pkgname;
+
+ arch = dpkg_arch_find("arch");
+ test_pass(arch);
+
+ pkg = pkg_hash_find_pkg("test", arch);
+ test_pass(pkg);
+ test_str(pkg->set->name, ==, "test");
+ test_pass(pkg->installed.arch->type == DPKG_ARCH_UNKNOWN);
+
+ pkgname = pkg_name(pkg, pnaw_never);
+ test_pass(pkgname);
+ test_str(pkgname, ==, "test");
+
+ pkgname = pkg_name(pkg, pnaw_nonambig);
+ test_pass(pkgname);
+ test_str(pkgname, ==, "test:arch");
+
+ pkgname = pkg_name(pkg, pnaw_always);
+ test_pass(pkgname);
+ test_str(pkgname, ==, "test:arch");
+
+ pkgname = pkg_name(pkg, pnaw_same);
+ test_pass(pkgname);
+ test_str(pkgname, ==, "test");
+
+ pkg->installed.multiarch = PKG_MULTIARCH_SAME;
+ pkgname = pkg_name(pkg, pnaw_same);
+ test_pass(pkgname);
+ test_str(pkgname, ==, "test:arch");
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(14);
+
+ test_pkg_show_name();
+}
diff --git a/lib/dpkg/t/t-pkginfo.c b/lib/dpkg/t/t-pkginfo.c
new file mode 100644
index 0000000..787cefe
--- /dev/null
+++ b/lib/dpkg/t/t-pkginfo.c
@@ -0,0 +1,153 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-pkginfo.c - test pkginfo handling
+ *
+ * Copyright © 2009-2010,2012-2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/pkg.h>
+
+static void
+test_pkginfo_informative(void)
+{
+ struct pkginfo pkg;
+
+ pkg_blank(&pkg);
+ test_fail(pkg_is_informative(&pkg, &pkg.installed));
+
+ pkg_set_want(&pkg, PKG_WANT_PURGE);
+ test_pass(pkg_is_informative(&pkg, &pkg.installed));
+
+ pkg_blank(&pkg);
+ pkg.installed.description = "test description";
+ test_pass(pkg_is_informative(&pkg, &pkg.installed));
+
+ /* TODO: Complete. */
+}
+
+static void
+test_pkginfo_eflags(void)
+{
+ struct pkginfo pkg;
+
+ pkg_blank(&pkg);
+ test_pass(pkg.eflag == PKG_EFLAG_OK);
+
+ pkg_set_eflags(&pkg, PKG_EFLAG_REINSTREQ);
+ test_pass(pkg.eflag == PKG_EFLAG_REINSTREQ);
+
+ pkg_clear_eflags(&pkg, PKG_EFLAG_REINSTREQ);
+ test_pass(pkg.eflag == PKG_EFLAG_OK);
+
+ pkg_set_eflags(&pkg, 0x11);
+ test_pass(pkg.eflag == 0x11);
+ pkg_reset_eflags(&pkg);
+ test_pass(pkg.eflag == PKG_EFLAG_OK);
+}
+
+static void
+test_pkginfo_instance_tracking(void)
+{
+ struct pkgset set;
+ struct pkginfo pkg2, pkg3, pkg4;
+
+ pkgset_blank(&set);
+ pkg_blank(&pkg2);
+ pkg_blank(&pkg3);
+ pkg_blank(&pkg4);
+
+ test_pass(pkgset_installed_instances(&set) == 0);
+
+ /* Link the other instances into the pkgset. */
+ pkgset_link_pkg(&set, &pkg4);
+ pkgset_link_pkg(&set, &pkg3);
+ pkgset_link_pkg(&set, &pkg2);
+
+ /* Test installation state transitions. */
+ pkg_set_status(&pkg4, PKG_STAT_INSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_INSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_TRIGGERSPENDING);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_TRIGGERSAWAITED);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_HALFCONFIGURED);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_UNPACKED);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_HALFINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_CONFIGFILES);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_NOTINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 0);
+
+ pkg_set_status(&pkg4, PKG_STAT_NOTINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 0);
+
+ /* Toggle installation states on various packages. */
+ pkg_set_status(&pkg4, PKG_STAT_INSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg2, PKG_STAT_HALFINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 2);
+
+ pkg_set_status(&set.pkg, PKG_STAT_CONFIGFILES);
+ test_pass(pkgset_installed_instances(&set) == 3);
+
+ pkg_set_status(&pkg3, PKG_STAT_NOTINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 3);
+
+ pkg_set_status(&pkg3, PKG_STAT_UNPACKED);
+ test_pass(pkgset_installed_instances(&set) == 4);
+
+ pkg_set_status(&set.pkg, PKG_STAT_NOTINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 3);
+
+ pkg_set_status(&pkg2, PKG_STAT_NOTINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 2);
+
+ pkg_set_status(&pkg3, PKG_STAT_NOTINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 1);
+
+ pkg_set_status(&pkg4, PKG_STAT_NOTINSTALLED);
+ test_pass(pkgset_installed_instances(&set) == 0);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(28);
+
+ test_pkginfo_informative();
+ test_pkginfo_eflags();
+ test_pkginfo_instance_tracking();
+
+ /* TODO: Complete. */
+}
diff --git a/lib/dpkg/t/t-progname.c b/lib/dpkg/t/t-progname.c
new file mode 100644
index 0000000..e90e923
--- /dev/null
+++ b/lib/dpkg/t/t-progname.c
@@ -0,0 +1,53 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-progname.c - test program name handling
+ *
+ * Copyright © 2011 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/progname.h>
+
+static void
+test_progname(void)
+{
+ const char *progname;
+
+ /* Test initially empty progname. */
+ progname = dpkg_get_progname();
+ /* Handle libtool executables. */
+ if (strncmp(progname, "lt-", 3) == 0)
+ progname += 3;
+ test_str(progname, ==, "t-progname");
+
+ /* Test setting a new progname. */
+ dpkg_set_progname("newname");
+ test_str(dpkg_get_progname(), ==, "newname");
+
+ /* Test setting a new progname with path. */
+ dpkg_set_progname("path/newprogname");
+ test_str(dpkg_get_progname(), ==, "newprogname");
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(3);
+
+ test_progname();
+}
diff --git a/lib/dpkg/t/t-string.c b/lib/dpkg/t/t-string.c
new file mode 100644
index 0000000..7b4350d
--- /dev/null
+++ b/lib/dpkg/t/t-string.c
@@ -0,0 +1,281 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-string.c - test string handling
+ *
+ * Copyright © 2009-2011, 2014-2015 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/string.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <stdio.h>
+
+static void
+test_str_is_set(void)
+{
+ /* Test if strings are unset. */
+ test_pass(str_is_unset(NULL));
+ test_pass(str_is_unset(""));
+ test_fail(str_is_unset("aaa"));
+
+ /* Test if strings are set. */
+ test_fail(str_is_set(NULL));
+ test_fail(str_is_set(""));
+ test_pass(str_is_set("ccc"));
+}
+
+static void
+test_str_match_end(void)
+{
+ test_pass(str_match_end("foo bar quux", "quux"));
+ test_pass(str_match_end("foo bar quux", "bar quux"));
+ test_pass(str_match_end("foo bar quux", "foo bar quux"));
+ test_fail(str_match_end("foo bar quux", "foo bar quux zorg"));
+ test_fail(str_match_end("foo bar quux", "foo bar"));
+ test_fail(str_match_end("foo bar quux", "foo"));
+}
+
+static void
+test_str_fnv_hash(void)
+{
+ test_pass(str_fnv_hash("") == 0x811c9dc5U);
+ test_pass(str_fnv_hash("a") == 0xe40c292cUL);
+ test_pass(str_fnv_hash("b") == 0xe70c2de5UL);
+ test_pass(str_fnv_hash("c") == 0xe60c2c52UL);
+ test_pass(str_fnv_hash("d") == 0xe10c2473UL);
+ test_pass(str_fnv_hash("e") == 0xe00c22e0UL);
+ test_pass(str_fnv_hash("f") == 0xe30c2799UL);
+ test_pass(str_fnv_hash("fo") == 0x6222e842UL);
+ test_pass(str_fnv_hash("foo") == 0xa9f37ed7UL);
+ test_pass(str_fnv_hash("foob") == 0x3f5076efUL);
+ test_pass(str_fnv_hash("fooba") == 0x39aaa18aUL);
+ test_pass(str_fnv_hash("foobar") == 0xbf9cf968UL);
+
+ test_pass(str_fnv_hash("test-string") == 0xd28f6e61UL);
+ test_pass(str_fnv_hash("Test-string") == 0x00a54b81UL);
+ test_pass(str_fnv_hash("rest-string") == 0x1cdeebffUL);
+ test_pass(str_fnv_hash("Rest-string") == 0x20464b9fUL);
+}
+
+static void
+test_str_concat(void)
+{
+ char buf[1024], *str;
+
+ memset(buf, 0, sizeof(buf));
+ str = str_concat(buf, NULL);
+ test_pass(str == buf);
+ test_str(buf, ==, "");
+
+ memset(buf, 0, sizeof(buf));
+ str = str_concat(buf, "aaa", NULL);
+ test_str(buf, ==, "aaa");
+ test_pass(str == buf + 3);
+
+ memset(buf, 0, sizeof(buf));
+ str = str_concat(buf, "zzzz", "yy", "xxxx", NULL);
+ test_str(buf, ==, "zzzzyyxxxx");
+ test_pass(str == buf + 10);
+
+ memset(buf, 0, sizeof(buf));
+ str = str_concat(buf, "1234", "", "5678", NULL);
+ test_str(buf, ==, "12345678");
+ test_pass(str == buf + 8);
+
+ memset(buf, ' ', sizeof(buf));
+ str = str_concat(buf, "eol", NULL, "bom", NULL);
+ test_str(buf, ==, "eol");
+ test_pass(str == buf + 3);
+}
+
+static void
+test_str_fmt(void)
+{
+ char *str;
+
+ str = str_fmt("%s", "abcde");
+ test_str(str, ==, "abcde");
+ free(str);
+
+ str = str_fmt("%d", 15);
+ test_str(str, ==, "15");
+ free(str);
+}
+
+static void
+test_str_escape_fmt(void)
+{
+ char buf[1024], *q;
+
+ memset(buf, 'a', sizeof(buf));
+ q = str_escape_fmt(buf, "", sizeof(buf));
+ strcpy(q, " end");
+ test_str(buf, ==, " end");
+
+ memset(buf, 'a', sizeof(buf));
+ q = str_escape_fmt(buf, "%", sizeof(buf));
+ strcpy(q, " end");
+ test_str(buf, ==, "%% end");
+
+ memset(buf, 'a', sizeof(buf));
+ q = str_escape_fmt(buf, "%%%", sizeof(buf));
+ strcpy(q, " end");
+ test_str(buf, ==, "%%%%%% end");
+
+ memset(buf, 'a', sizeof(buf));
+ q = str_escape_fmt(buf, "%b%b%c%c%%", sizeof(buf));
+ strcpy(q, " end");
+ test_str(buf, ==, "%%b%%b%%c%%c%%%% end");
+
+ /* Test delimited buffer. */
+ memset(buf, 'a', sizeof(buf));
+ q = str_escape_fmt(buf, NULL, 0);
+ test_mem(buf, ==, "aaaa", 4);
+ test_pass(buf == q);
+ test_pass(strnlen(buf, sizeof(buf)) == sizeof(buf));
+
+ memset(buf, 'a', sizeof(buf));
+ q = str_escape_fmt(buf, "b", 1);
+ test_str(q, ==, "");
+
+ memset(buf, 'a', sizeof(buf));
+ q = str_escape_fmt(buf, "%%%", 5);
+ strcpy(q, " end");
+ test_str(buf, ==, "%%%% end");
+
+ memset(buf, 'a', sizeof(buf));
+ q = str_escape_fmt(buf, "%%%", 4);
+ strcpy(q, " end");
+ test_str(buf, ==, "%% end");
+}
+
+static void
+test_str_rtrim_spaces(void)
+{
+ char buf[1024];
+ char *str_end;
+
+ strcpy(buf, "");
+ str_end = str_rtrim_spaces(buf, buf + strlen(buf));
+ test_pass(str_end == buf);
+ test_str(buf, ==, "");
+
+ strcpy(buf, " \t\t \r\n ");
+ str_end = str_rtrim_spaces(buf, buf + strlen(buf));
+ test_pass(str_end == buf);
+ test_str(buf, ==, "");
+
+ strcpy(buf, "abcd");
+ str_end = str_rtrim_spaces(buf, buf + strlen(buf));
+ test_pass(str_end == buf + 4);
+ test_str(buf, ==, "abcd");
+
+ strcpy(buf, "abcd ");
+ str_end = str_rtrim_spaces(buf, buf + strlen(buf));
+ test_pass(str_end == buf + 4);
+ test_str(buf, ==, "abcd");
+
+ strcpy(buf, "abcd\t \t ");
+ str_end = str_rtrim_spaces(buf, buf + strlen(buf));
+ test_pass(str_end == buf + 4);
+ test_str(buf, ==, "abcd");
+
+ strcpy(buf, " \t \t abcd");
+ str_end = str_rtrim_spaces(buf, buf + strlen(buf));
+ test_pass(str_end == buf + 12);
+ test_str(buf, ==, " \t \t abcd");
+}
+
+static void
+test_str_quote_meta(void)
+{
+ char *str;
+
+ str = str_quote_meta("foo1 2bar");
+ test_str(str, ==, "foo1\\ 2bar");
+ free(str);
+
+ str = str_quote_meta("foo1?2bar");
+ test_str(str, ==, "foo1\\?2bar");
+ free(str);
+
+ str = str_quote_meta("foo1*2bar");
+ test_str(str, ==, "foo1\\*2bar");
+ free(str);
+}
+
+static void
+test_str_strip_quotes(void)
+{
+ char buf[1024], *str;
+
+ strcpy(buf, "unquoted text");
+ str = str_strip_quotes(buf);
+ test_str(str, ==, "unquoted text");
+
+ strcpy(buf, "contained 'quoted text'");
+ str = str_strip_quotes(buf);
+ test_str(str, ==, "contained 'quoted text'");
+
+ strcpy(buf, "contained \"quoted text\"");
+ str = str_strip_quotes(buf);
+ test_str(str, ==, "contained \"quoted text\"");
+
+ strcpy(buf, "'unbalanced quotes");
+ str = str_strip_quotes(buf);
+ test_pass(str == NULL);
+
+ strcpy(buf, "\"unbalanced quotes");
+ str = str_strip_quotes(buf);
+ test_pass(str == NULL);
+
+ strcpy(buf, "'mismatched quotes\"");
+ str = str_strip_quotes(buf);
+ test_pass(str == NULL);
+
+ strcpy(buf, "\"mismatched quotes'");
+ str = str_strip_quotes(buf);
+ test_pass(str == NULL);
+
+ strcpy(buf, "'completely quoted text'");
+ str = str_strip_quotes(buf);
+ test_str(str, ==, "completely quoted text");
+
+ strcpy(buf, "\"completely quoted text\"");
+ str = str_strip_quotes(buf);
+ test_str(str, ==, "completely quoted text");
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(74);
+
+ test_str_is_set();
+ test_str_match_end();
+ test_str_fnv_hash();
+ test_str_concat();
+ test_str_fmt();
+ test_str_escape_fmt();
+ test_str_quote_meta();
+ test_str_strip_quotes();
+ test_str_rtrim_spaces();
+}
diff --git a/lib/dpkg/t/t-subproc.c b/lib/dpkg/t/t-subproc.c
new file mode 100644
index 0000000..7ce610b
--- /dev/null
+++ b/lib/dpkg/t/t-subproc.c
@@ -0,0 +1,100 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-subproc.c - test sub-process module
+ *
+ * Copyright © 2011 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <dpkg/test.h>
+#include <dpkg/subproc.h>
+
+static void
+test_subproc_fork(void)
+{
+ struct sigaction sa;
+ pid_t pid;
+ int ret;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ /* Test exit(). */
+ pid = subproc_fork();
+ if (pid == 0)
+ exit(0);
+ ret = subproc_reap(pid, "subproc exit pass", SUBPROC_RETERROR);
+ test_pass(ret == 0);
+
+ pid = subproc_fork();
+ if (pid == 0)
+ exit(128);
+ ret = subproc_reap(pid, "subproc exit fail", SUBPROC_RETERROR);
+ test_pass(ret == 128);
+
+ /* Test signals. */
+ pid = subproc_fork();
+ if (pid == 0)
+ raise(SIGINT);
+ ret = subproc_reap(pid, "subproc signal", SUBPROC_WARN);
+ test_pass(ret == -1);
+
+ pid = subproc_fork();
+ if (pid == 0)
+ raise(SIGTERM);
+ ret = subproc_reap(pid, "subproc signal", SUBPROC_WARN);
+ test_pass(ret == -1);
+
+ pid = subproc_fork();
+ if (pid == 0)
+ raise(SIGPIPE);
+ ret = subproc_reap(pid, "subproc SIGPIPE",
+ SUBPROC_WARN | SUBPROC_NOPIPE);
+ test_pass(ret == 0);
+
+ pid = subproc_fork();
+ if (pid == 0)
+ raise(SIGPIPE);
+ ret = subproc_reap(pid, "subproc SIGPIPE", SUBPROC_WARN);
+ test_pass(ret == -1);
+}
+
+TEST_ENTRY(test)
+{
+ int fd;
+
+ test_plan(6);
+
+ /* XXX: Shut up stderr, we don't want the error output. */
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ test_bail("cannot open /dev/null");
+ dup2(fd, 2);
+
+ test_subproc_fork();
+}
diff --git a/lib/dpkg/t/t-tar.c b/lib/dpkg/t/t-tar.c
new file mode 100644
index 0000000..6fa217d
--- /dev/null
+++ b/lib/dpkg/t/t-tar.c
@@ -0,0 +1,148 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-tar.c - test tar implementation
+ *
+ * Copyright © 2017 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <errno.h>
+
+#include <dpkg/test.h>
+#include <dpkg/tarfn.h>
+
+static void
+test_tar_atol8(void)
+{
+ uintmax_t u;
+
+ /* Test valid octal numbers. */
+ u = tar_atoul("000000\0\0\0\0\0\0", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ u = tar_atoul("00000000000\0", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ u = tar_atoul("00000000001\0", 12, UINTMAX_MAX);
+ test_pass(u == 1);
+ u = tar_atoul("00000000777\0", 12, UINTMAX_MAX);
+ test_pass(u == 511);
+ u = tar_atoul("77777777777\0", 12, UINTMAX_MAX);
+ test_pass(u == 8589934591);
+
+ /* Test legacy formatted octal numbers. */
+ u = tar_atoul(" 0\0", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ u = tar_atoul(" 1\0", 12, UINTMAX_MAX);
+ test_pass(u == 1);
+ u = tar_atoul(" 777\0", 12, UINTMAX_MAX);
+ test_pass(u == 511);
+
+ /* Test extended octal numbers not terminated by space or NUL,
+ * (as is required by POSIX), but accepted by several implementations
+ * to get one byte larger values. */
+ u = tar_atoul("000000000000", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ u = tar_atoul("000000000001", 12, UINTMAX_MAX);
+ test_pass(u == 1);
+ u = tar_atoul("000000000777", 12, UINTMAX_MAX);
+ test_pass(u == 511);
+ u = tar_atoul("777777777777", 12, UINTMAX_MAX);
+ test_pass(u == 68719476735);
+
+ /* Test invalid octal numbers. */
+ errno = 0;
+ u = tar_atoul(" ", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ test_pass(errno == EINVAL);
+
+ errno = 0;
+ u = tar_atoul(" 11111aaa ", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ test_pass(errno == ERANGE);
+
+ errno = 0;
+ u = tar_atoul(" 8 ", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ test_pass(errno == ERANGE);
+
+ errno = 0;
+ u = tar_atoul(" 18 ", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ test_pass(errno == ERANGE);
+
+ errno = 0;
+ u = tar_atoul(" aa ", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ test_pass(errno == ERANGE);
+}
+
+static void
+test_tar_atol256(void)
+{
+ uintmax_t u;
+ intmax_t i;
+
+ /* Test positive numbers. */
+ u = tar_atoul("\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, UINTMAX_MAX);
+ test_pass(u == 0);
+ u = tar_atoul("\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 12, UINTMAX_MAX);
+ test_pass(u == 1);
+ u = tar_atoul("\x80\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00", 12, UINTMAX_MAX);
+ test_pass(u == 8589934592);
+ u = tar_atoul("\x80\x00\x00\x00\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 12, UINTMAX_MAX);
+ test_pass(u == INTMAX_MAX);
+
+ /* Test overflow. */
+ errno = 0;
+ u = tar_atoul("\x80\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00", 12, UINTMAX_MAX);
+ test_pass(u == UINTMAX_MAX);
+ test_pass(errno == ERANGE);
+
+ errno = 0;
+ u = tar_atoul("\x80\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, UINTMAX_MAX);
+ test_pass(u == UINTMAX_MAX);
+ test_pass(errno == ERANGE);
+
+ /* Test negative numbers. */
+ i = tar_atosl("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 12, INTMAX_MIN, INTMAX_MAX);
+ test_pass(i == -1);
+ i = tar_atosl("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", 12, INTMAX_MIN, INTMAX_MAX);
+ test_pass(i == -2);
+ i = tar_atosl("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00", 12, INTMAX_MIN, INTMAX_MAX);
+ test_pass(i == -8589934592);
+ i = tar_atosl("\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00", 12, INTMAX_MIN, INTMAX_MAX);
+ test_pass(i == INTMAX_MIN);
+
+ /* Test underflow. */
+ errno = 0;
+ i = tar_atosl("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00", 12, INTMAX_MIN, INTMAX_MAX);
+ test_pass(i == INTMAX_MIN);
+ test_pass(errno == ERANGE);
+
+ errno = 0;
+ i = tar_atosl("\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, INTMAX_MIN, INTMAX_MAX);
+ test_pass(i == INTMAX_MIN);
+ test_pass(errno == ERANGE);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(38);
+
+ test_tar_atol8();
+ test_tar_atol256();
+}
diff --git a/lib/dpkg/t/t-tarextract.t b/lib/dpkg/t/t-tarextract.t
new file mode 100755
index 0000000..5499cdc
--- /dev/null
+++ b/lib/dpkg/t/t-tarextract.t
@@ -0,0 +1,159 @@
+#!/usr/bin/perl
+#
+# Copyright © 2014 Guillem Jover <guillem@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+use Test::More;
+use Cwd;
+use File::Path qw(make_path remove_tree);
+use File::Temp qw(tempdir);
+use File::Spec;
+use File::Find;
+use POSIX qw(mkfifo);
+
+use Dpkg ();
+use Dpkg::File;
+use Dpkg::IPC;
+
+use strict;
+use warnings;
+use version;
+
+my $srcdir = $ENV{srcdir} || '.';
+my $builddir = $ENV{builddir} || '.';
+my $tmpdir = 't.tmp/t-tarextract';
+
+# We require GNU tar >= 1.27 for --owner=NAME:ID and --group=NAME:ID.
+my $tar_version = qx($Dpkg::PROGTAR --version 2>/dev/null);
+if ($tar_version and $tar_version =~ m/^tar \(GNU tar\) (\d+\.\d+)/ and
+ qv("v$1") >= qv('v1.27'))
+{
+ plan tests => 12;
+} else {
+ plan skip_all => 'needs GNU tar >= 1.27';
+}
+
+# Set a known umask.
+umask 0022;
+
+sub tar_create_tree {
+ my $type = shift;
+
+ my $long_a = 'a' x 29;
+ my $long_b = 'b' x 29;
+ my $long_c = 'c' x 29;
+ my $long_d = 'd' x 29;
+ my $long_e = 'e' x 29;
+ my $long_f = 'f' x 22;
+
+ # Populate tar hierarchy
+ file_touch('file');
+ link 'file', 'hardlink';
+
+ make_path("$long_a/$long_b/$long_c/$long_d/$long_e/");
+ make_path("$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/");
+ file_touch("$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/long");
+
+ # POSIX specifies that symlinks have undefined permissions in their
+ # mode, so their handling is system dependent. Linux does not honor
+ # the umask for symlinks, other systems like GNU/Hurd or kFreeBSD do,
+ # which means we get different results due to this.
+ my $umask = umask 0;
+
+ symlink "$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/long",
+ 'symlink-long';
+ symlink 'file', 'symlink-a';
+ symlink 'hardlink', 'symlink-b';
+ symlink 'dangling', 'symlink-c';
+
+ umask $umask;
+
+ mkdir 'directory';
+ mkfifo('fifo', 0770);
+
+ # TODO: Need root.
+ # system 'mknod', 'chardev', 'c', '1', '3';
+ # system 'mknod', 'blockdev', 'b', '0', '0';
+}
+
+sub test_tar_extractor {
+ my $stdout;
+ my $stderr;
+
+ my $expected_tar = <<'TAR';
+. mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./fifo mode=10750 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=fifo
+./file mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=file size=0
+./hardlink mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=hardlink linkto=./file size=0
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff/long mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=file size=0
+./directory mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./symlink-a mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=file size=0
+./symlink-b mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=hardlink size=0
+./symlink-c mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=dangling size=0
+./symlink-long mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff/long size=0
+TAR
+
+ make_path($tmpdir);
+
+ my $cwd = getcwd();
+
+ # Check generated tarballs.
+ foreach my $type (qw(v7 ustar oldgnu gnu)) {
+ my $dirtree = "$tmpdir/$type";
+ my @paths;
+
+ mkdir $dirtree;
+ chdir $dirtree;
+ tar_create_tree($type);
+ find({ no_chdir => 1, wanted => sub {
+ return if $type eq 'v7' and length > 99;
+ return if $type eq 'v7' and -l and length readlink > 99;
+ return if $type eq 'v7' and not (-f or -l or -d);
+ return if $type eq 'ustar' and length > 256;
+ return if $type eq 'ustar' and -l and length readlink > 100;
+ push @paths, $_;
+ },
+ preprocess => sub { my (@files) = sort @_; @files } }, '.');
+ chdir $cwd;
+
+ my $paths_list = join "\0", @paths;
+ spawn(exec => [ $Dpkg::PROGTAR, '-cf', "$dirtree.tar",
+ '--format', $type,
+ '-C', $dirtree, '--mtime=@100000000',
+ '--owner=user:100', '--group=group:200',
+ '--null', '--no-unquote', '--no-recursion', '-T-' ],
+ wait_child => 1, from_string => \$paths_list);
+
+ my $expected = $expected_tar;
+ $expected =~ s/[ug]name=[^ ]+ //g if $type eq 'v7';
+ $expected =~ s/\n^.*fifo.*$//mg if $type eq 'v7';
+ $expected =~ s/\n^.*dddd.*$//mg if $type eq 'v7';
+ $expected =~ s/\n^.*symlink-long.*$//mg if $type eq 'ustar';
+
+ spawn(exec => [ "$builddir/t/c-tarextract", "$dirtree.tar" ],
+ nocheck => 1, to_string => \$stdout, to_error => \$stderr);
+ ok($? == 0, "tar extractor $type should succeed");
+ is($stderr, undef, "tar extractor $type stderr is empty");
+ is($stdout, $expected, "tar extractor $type is ok");
+ }
+}
+
+test_tar_extractor();
diff --git a/lib/dpkg/t/t-test-skip.c b/lib/dpkg/t/t-test-skip.c
new file mode 100644
index 0000000..972cdf1
--- /dev/null
+++ b/lib/dpkg/t/t-test-skip.c
@@ -0,0 +1,31 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-test-skip.c - test suite self tests, skip all
+ *
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+
+TEST_ENTRY(test)
+{
+ test_skip_all("ignore all tests");
+
+ test_fail(1);
+}
diff --git a/lib/dpkg/t/t-test.c b/lib/dpkg/t/t-test.c
new file mode 100644
index 0000000..48ce872
--- /dev/null
+++ b/lib/dpkg/t/t-test.c
@@ -0,0 +1,66 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-test.c - test suite self tests
+ *
+ * Copyright © 2009, 2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+
+TEST_ENTRY(test)
+{
+ test_plan(22);
+
+ test_pass(1);
+ test_fail(0);
+
+ test_skip("ignore test");
+
+ test_skip_block(1) {
+ test_pass(0);
+ test_pass(1);
+ }
+
+ test_todo(0, "unimplemented test", "failing test");
+
+ test_todo_block("unimplemented block") {
+ test_pass(0);
+ test_fail(1);
+ }
+
+ test_str("aaa", ==, "aaa");
+ test_str("aaa", <, "bbb");
+ test_str("ccc", >, "bbb");
+ test_str("ccc", !=, "bbb");
+
+ test_mem("aaa", ==, "aaa", 3);
+ test_mem("aaa", <, "bbb", 3);
+ test_mem("ccc", >, "bbb", 3);
+ test_mem("ccc", !=, "bbb", 3);
+
+ test_mem("abcd", ==, "abcd", 4);
+ test_mem("abcd", ==, "abcd", 5);
+ test_mem("ababcd", ==, "ababff", 4);
+ test_mem("ababcd", !=, "ababff", 6);
+
+ setenv("srcdir", "aaa", 1);
+ setenv("builddir", "bbb", 1);
+ test_str(test_get_srcdir(), ==, "aaa");
+ test_str(test_get_builddir(), ==, "bbb");
+}
diff --git a/lib/dpkg/t/t-treewalk.t b/lib/dpkg/t/t-treewalk.t
new file mode 100755
index 0000000..573103d
--- /dev/null
+++ b/lib/dpkg/t/t-treewalk.t
@@ -0,0 +1,154 @@
+#!/usr/bin/perl
+#
+# Copyright © 2016 Guillem Jover <guillem@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+use version;
+
+use Test::More tests => 6;
+use Cwd;
+use File::Path qw(make_path remove_tree);
+use File::Temp qw(tempdir);
+use File::Basename;
+use File::Find;
+
+use Dpkg::File;
+use Dpkg::IPC;
+
+my $srcdir = $ENV{srcdir} || '.';
+my $builddir = $ENV{builddir} || '.';
+my $tmpdir = 't.tmp/t-treewalk';
+
+# Set a known umask.
+umask 0022;
+
+# Populate the tree hierarchy.
+sub make_tree {
+ my ($dirtree) = @_;
+ my $cwd = getcwd();
+
+ make_path($dirtree);
+ chdir $dirtree;
+
+ # Deep tree.
+ make_path('aaaa/aaaa/aaaa/aaaa/');
+ file_touch('aaaa/aaaa/aaaa/aaaa/abcde');
+ file_touch('aaaa/aaaa/aaaa/aaaa/ddddd');
+ file_touch('aaaa/aaaa/aaaa/aaaa/wwwwa');
+ file_touch('aaaa/aaaa/aaaa/aaaa/wwwwz');
+ file_touch('aaaa/aaaa/aaaa/aaaa/zzzzz');
+
+ # Shallow tree.
+ make_path('bbbb/');
+ file_touch('bbbb/abcde');
+ file_touch('bbbb/ddddd');
+ file_touch('bbbb/wwwwa');
+ file_touch('bbbb/wwwwz');
+ file_touch('bbbb/zzzzz');
+
+ # Populated tree.
+ make_path('cccc/aa/aa/aa/');
+ make_path('cccc/aa/aa/bb/aa/');
+ file_touch('cccc/aa/aa/bb/aa/file-a');
+ file_touch('cccc/aa/aa/bb/aa/file-z');
+ make_path('cccc/aa/bb/');
+ make_path('cccc/bb/aa/');
+ make_path('cccc/bb/bb/aa/aa/');
+ file_touch('cccc/bb/bb/aa/aa/file-a');
+ file_touch('cccc/bb/bb/aa/aa/file-z');
+ make_path('cccc/bb/bb/bb/');
+ file_touch('cccc/bb/bb/bb/file-w');
+ make_path('cccc/cc/aa/');
+ make_path('cccc/cc/bb/aa/');
+ file_touch('cccc/cc/bb/aa/file-t');
+ make_path('cccc/cc/bb/bb/');
+ file_touch('cccc/cc/bb/bb/file-x');
+ make_path('cccc/cc/cc/');
+ make_path('cccc/dd/aa/aa/aa/');
+ file_touch('cccc/dd/aa/aa/aa/file-y');
+ make_path('cccc/dd/aa/aa/bb/');
+ file_touch('cccc/dd/aa/aa/bb/file-o');
+ make_path('cccc/dd/aa/bb/aa/');
+ file_touch('cccc/dd/aa/bb/aa/file-k');
+ make_path('cccc/dd/aa/bb/bb/');
+ file_touch('cccc/dd/aa/bb/bb/file-l');
+ make_path('cccc/dd/aa/cc/aa/');
+ file_touch('cccc/dd/aa/cc/aa/file-s');
+ make_path('cccc/dd/aa/cc/bb/');
+ file_touch('cccc/dd/aa/cc/bb/file-u');
+
+ # Tree with symlinks cycles.
+ make_path('llll/self/');
+ file_touch('llll/file');
+ symlink '..', 'llll/self/loop';
+ make_path('llll/real/');
+ file_touch('llll/real/file-r');
+ symlink '../virt', 'llll/real/loop';
+ make_path('llll/virt/');
+ file_touch('llll/virt/file-v');
+ symlink '../real', 'llll/virt/loop';
+
+ chdir $cwd;
+}
+
+sub test_treewalker {
+ my $stdout;
+ my $stderr;
+ my $dirtree = $tmpdir;
+
+ # Check generated tarballs.
+ foreach my $type (qw(full skip)) {
+ my @paths;
+
+ make_tree($dirtree);
+
+ find({ no_chdir => 1, wanted => sub {
+ return if $type eq 'skip' and m{^\Q$dirtree\E/cccc};
+ push @paths, s{\./}{}r;
+ },
+ }, $dirtree);
+
+ my $expected;
+
+ foreach my $path (sort @paths) {
+ lstat $path;
+
+ my $ptype;
+ if (-f _) {
+ $ptype = 'f';
+ } elsif (-l _) {
+ $ptype = 'l';
+ } elsif (-d _) {
+ $ptype = 'd';
+ }
+ my $pname = basename($path);
+ my $pvirt = $path =~ s{\Q$dirtree\E/?}{}r;
+
+ $expected .= "T=$ptype N=$pname V=$pvirt R=$path\n";
+ }
+
+ $ENV{TREEWALK_SKIP} = $type eq 'skip' ? "$dirtree/cccc" : undef;
+
+ spawn(exec => [ "$builddir/t/c-treewalk", $dirtree ],
+ nocheck => 1, to_string => \$stdout, to_error => \$stderr);
+ ok($? == 0, "tree walker $type should succeed");
+ is($stderr, undef, "tree walker $type stderr is empty");
+ is($stdout, $expected, "tree walker $type is ok");
+ }
+}
+
+test_treewalker();
diff --git a/lib/dpkg/t/t-trigdeferred.t b/lib/dpkg/t/t-trigdeferred.t
new file mode 100755
index 0000000..bf73541
--- /dev/null
+++ b/lib/dpkg/t/t-trigdeferred.t
@@ -0,0 +1,116 @@
+#!/usr/bin/perl
+#
+# Copyright © 2016 Guillem Jover <guillem@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+use version;
+
+use Test::More;
+use Cwd;
+use File::Path qw(make_path remove_tree);
+use File::Temp qw(tempdir);
+use File::Basename;
+use File::Find;
+
+use Dpkg::File;
+use Dpkg::IPC;
+
+my $srcdir = $ENV{srcdir} || '.';
+my $builddir = $ENV{builddir} || '.';
+my $tmpdir = 't.tmp/t-trigdeferred';
+
+my @deferred = (
+ {
+ exitcode => 0,
+ original => <<'TEXT',
+
+ # Comment
+ # Another Comment
+ /root/pathname/file-trigger pkg-a pkg-b pkg-c
+named-trigger pkg-1 pkg-2 pkg-3
+parse-trigger pkg:a pkg+b pkg.0 0-pkg
+:other-trigger -
+TEXT
+ expected => <<'TEXT',
+<T='/root/pathname/file-trigger' P='pkg-a' P='pkg-b' P='pkg-c' E>
+<T='named-trigger' P='pkg-1' P='pkg-2' P='pkg-3' E>
+<T='parse-trigger' P='pkg:a' P='pkg+b' P='pkg.0' P='0-pkg' E>
+<T=':other-trigger' P='-' E>
+TEXT
+ }, {
+ exitcode => 2,
+ original => <<"TEXT",
+\b # invalid character
+TEXT
+ }, {
+ exitcode => 2,
+ original => <<'TEXT',
+trigger -pkg
+TEXT
+ }, {
+ exitcode => 2,
+ original => <<'TEXT',
+trigger +pkg
+TEXT
+ }, {
+ exitcode => 2,
+ original => <<'TEXT',
+trigger .pkg
+TEXT
+ }, {
+ exitcode => 2,
+ original => <<'TEXT',
+trigger :pkg
+TEXT
+ }, {
+ exitcode => 2,
+ original => 'missing newline',
+ }
+);
+
+plan tests => scalar(@deferred) * 3;
+
+# Set a known umask.
+umask 0022;
+
+sub test_trigdeferred {
+ my $stdout;
+ my $stderr;
+ my $admindir = "$tmpdir";
+
+ # Check triggers deferred file parsing.
+ make_path("$admindir/triggers");
+
+ foreach my $test (@deferred) {
+ file_dump("$admindir/triggers/Unincorp", $test->{original});
+
+ spawn(exec => [ "$builddir/t/c-trigdeferred", $admindir ],
+ nocheck => 1, to_string => \$stdout, error_to_string => \$stderr);
+ my $exitcode = $? >> 8;
+
+ is($exitcode, $test->{exitcode}, 'trigger deferred expected exitcode');
+ if ($test->{exitcode} == 0) {
+ is($stderr, '', 'trigger deferred expected stderr');
+ is($stdout, $test->{expected}, 'trigger deferred expected stdout');
+ } else {
+ isnt($stderr, '', 'trigger deferred expected stderr');
+ isnt($stdout, undef, 'trigger deferred expected stdout');
+ }
+ }
+}
+
+test_trigdeferred();
diff --git a/lib/dpkg/t/t-trigger.c b/lib/dpkg/t/t-trigger.c
new file mode 100644
index 0000000..af78f23
--- /dev/null
+++ b/lib/dpkg/t/t-trigger.c
@@ -0,0 +1,49 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-trigger.c - test triggers
+ *
+ * Copyright © 2012 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/triglib.h>
+
+static void
+test_trig_name_is_illegal(void)
+{
+ /* Test invalid trigger names. */
+ test_fail(trig_name_is_illegal("") == NULL);
+ test_fail(trig_name_is_illegal("\a") == NULL);
+ test_fail(trig_name_is_illegal("\t") == NULL);
+ test_fail(trig_name_is_illegal("\200") == NULL);
+ test_fail(trig_name_is_illegal("trigger name") == NULL);
+
+ /* Test valid trigger names. */
+ test_pass(trig_name_is_illegal("TRIGGER") == NULL);
+ test_pass(trig_name_is_illegal("trigger") == NULL);
+ test_pass(trig_name_is_illegal("0123456789") == NULL);
+ test_pass(trig_name_is_illegal("/file/trigger") == NULL);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(9);
+
+ test_trig_name_is_illegal();
+}
diff --git a/lib/dpkg/t/t-varbuf.c b/lib/dpkg/t/t-varbuf.c
new file mode 100644
index 0000000..ee1fdbc
--- /dev/null
+++ b/lib/dpkg/t/t-varbuf.c
@@ -0,0 +1,475 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-varbuf.c - test varbuf implementation
+ *
+ * Copyright © 2009-2011, 2013-2015 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <dpkg/test.h>
+#include <dpkg/varbuf.h>
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+static void
+test_varbuf_init(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 0);
+ test_pass(vb.used == 0);
+ test_pass(vb.size == 0);
+ test_pass(vb.buf == NULL);
+
+ varbuf_destroy(&vb);
+ test_pass(vb.used == 0);
+ test_pass(vb.size == 0);
+ test_pass(vb.buf == NULL);
+}
+
+static void
+test_varbuf_prealloc(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 10);
+ test_pass(vb.used == 0);
+ test_pass(vb.size >= 10);
+ test_pass(vb.buf != NULL);
+
+ varbuf_destroy(&vb);
+ test_pass(vb.used == 0);
+ test_pass(vb.size == 0);
+ test_pass(vb.buf == NULL);
+}
+
+static void
+test_varbuf_new(void)
+{
+ struct varbuf *vb;
+
+ vb = varbuf_new(0);
+ test_pass(vb != NULL);
+ test_pass(vb->used == 0);
+ test_pass(vb->size == 0);
+ test_pass(vb->buf == NULL);
+ varbuf_free(vb);
+
+ vb = varbuf_new(10);
+ test_pass(vb != NULL);
+ test_pass(vb->used == 0);
+ test_pass(vb->size >= 10);
+ test_pass(vb->buf != NULL);
+ varbuf_free(vb);
+}
+
+static void
+test_varbuf_grow(void)
+{
+ struct varbuf vb;
+ jmp_buf grow_jump;
+ size_t old_size;
+ bool grow_overflow;
+ int i;
+
+ varbuf_init(&vb, 10);
+
+ /* Test that we grow when needed. */
+ varbuf_grow(&vb, 100);
+ test_pass(vb.used == 0);
+ test_pass(vb.size >= 100);
+
+ old_size = vb.size;
+
+ /* Test that we are not leaking. */
+ for (i = 0; i < 10; i++) {
+ varbuf_grow(&vb, 100);
+ test_pass(vb.used == 0);
+ test_pass(vb.size >= 100);
+ test_pass(vb.size == old_size);
+ }
+
+ /* Test that we grow when needed, with used space. */
+ vb.used = 10;
+ varbuf_grow(&vb, 100);
+ test_pass(vb.used == 10);
+ test_pass(vb.size >= 110);
+
+ /* Test that we do not allow allocation overflows. */
+ grow_overflow = false;
+ old_size = vb.size;
+ test_try(grow_jump) {
+ varbuf_grow(&vb, SIZE_MAX - vb.size + 2);
+ } test_catch {
+ grow_overflow = true;
+ } test_finally;
+ test_pass(vb.size == old_size && grow_overflow);
+
+ grow_overflow = false;
+ old_size = vb.size;
+ test_try(grow_jump) {
+ varbuf_grow(&vb, (SIZE_MAX - vb.size - 2) / 2);
+ } test_catch {
+ grow_overflow = true;
+ } test_finally;
+ test_pass(vb.size == old_size && grow_overflow);
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_trunc(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 50);
+
+ /* Test that we truncate (grow). */
+ varbuf_trunc(&vb, 20);
+ test_pass(vb.used == 20);
+ test_pass(vb.size >= 50);
+
+ /* Test that we truncate (shrink). */
+ varbuf_trunc(&vb, 10);
+ test_pass(vb.used == 10);
+ test_pass(vb.size >= 50);
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_add_buf(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 5);
+
+ varbuf_add_buf(&vb, "1234567890", 10);
+ test_pass(vb.used == 10);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "1234567890", 10);
+
+ varbuf_add_buf(&vb, "abcde", 5);
+ test_pass(vb.used == 15);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "1234567890abcde", 15);
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_add_char(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 1);
+
+ varbuf_add_char(&vb, 'a');
+ test_pass(vb.used == 1);
+ test_pass(vb.size >= vb.used);
+ test_pass(vb.buf[0] == 'a');
+
+ varbuf_add_char(&vb, 'b');
+ test_pass(vb.used == 2);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "ab", 2);
+
+ varbuf_add_char(&vb, 'c');
+ test_pass(vb.used == 3);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "abc", 3);
+
+ varbuf_add_char(&vb, 'd');
+ test_pass(vb.used == 4);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "abcd", 4);
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_dup_char(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 5);
+
+ varbuf_dup_char(&vb, 'z', 10);
+ test_pass(vb.used == 10);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "zzzzzzzzzz", 10);
+
+ varbuf_dup_char(&vb, 'y', 5);
+ test_pass(vb.used == 15);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "zzzzzzzzzzyyyyy", 15);
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_map_char(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 5);
+
+ varbuf_add_buf(&vb, "1234a5678a9012a", 15);
+
+ varbuf_map_char(&vb, 'a', 'z');
+ test_pass(vb.used == 15);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "1234z5678z9012z", 15);
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_add_dir(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 10);
+
+ varbuf_add_dir(&vb, "");
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "/");
+ varbuf_add_dir(&vb, "");
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "/");
+ varbuf_add_dir(&vb, "aa");
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "/aa/");
+ varbuf_add_dir(&vb, "");
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "/aa/");
+
+ varbuf_reset(&vb);
+
+ varbuf_add_dir(&vb, "/foo/bar");
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "/foo/bar/");
+
+ varbuf_reset(&vb);
+
+ varbuf_add_dir(&vb, "/foo/bar/");
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "/foo/bar/");
+ varbuf_add_dir(&vb, "quux");
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "/foo/bar/quux/");
+ varbuf_add_dir(&vb, "zoo");
+ varbuf_end_str(&vb);
+ test_str(vb.buf, ==, "/foo/bar/quux/zoo/");
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_end_str(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 10);
+
+ varbuf_add_buf(&vb, "1234567890X", 11);
+ test_pass(vb.used == 11);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "1234567890X", 11);
+
+ varbuf_trunc(&vb, 10);
+
+ varbuf_end_str(&vb);
+ test_pass(vb.used == 10);
+ test_pass(vb.size >= vb.used + 1);
+ test_pass(vb.buf[10] == '\0');
+ test_str(vb.buf, ==, "1234567890");
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_get_str(void)
+{
+ struct varbuf vb;
+ const char *str;
+
+ varbuf_init(&vb, 10);
+
+ varbuf_add_buf(&vb, "1234567890", 10);
+ str = varbuf_get_str(&vb);
+ test_pass(vb.buf == str);
+ test_pass(vb.used == 10);
+ test_pass(vb.buf[vb.used] == '\0');
+ test_pass(str[vb.used] == '\0');
+ test_str(vb.buf, ==, "1234567890");
+ test_str(str, ==, "1234567890");
+
+ varbuf_add_buf(&vb, "abcde", 5);
+ str = varbuf_get_str(&vb);
+ test_pass(vb.buf == str);
+ test_pass(vb.used == 15);
+ test_pass(vb.buf[vb.used] == '\0');
+ test_pass(str[vb.used] == '\0');
+ test_str(vb.buf, ==, "1234567890abcde");
+ test_str(str, ==, "1234567890abcde");
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_printf(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 5);
+
+ /* Test normal format printing. */
+ varbuf_printf(&vb, "format %s number %d", "string", 10);
+ test_pass(vb.used == strlen("format string number 10"));
+ test_pass(vb.size >= vb.used);
+ test_str(vb.buf, ==, "format string number 10");
+
+ varbuf_reset(&vb);
+
+ /* Test concatenated format printing. */
+ varbuf_printf(&vb, "format %s number %d", "string", 10);
+ varbuf_printf(&vb, " extra %s", "string");
+ test_pass(vb.used == strlen("format string number 10 extra string"));
+ test_pass(vb.size >= vb.used);
+ test_str(vb.buf, ==, "format string number 10 extra string");
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_reset(void)
+{
+ struct varbuf vb;
+
+ varbuf_init(&vb, 10);
+
+ varbuf_add_buf(&vb, "1234567890", 10);
+
+ varbuf_reset(&vb);
+ test_pass(vb.used == 0);
+ test_pass(vb.size >= vb.used);
+
+ varbuf_add_buf(&vb, "abcdefghijklmno", 15);
+ test_pass(vb.used == 15);
+ test_pass(vb.size >= vb.used);
+ test_mem(vb.buf, ==, "abcdefghijklmno", 15);
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_snapshot(void)
+{
+ struct varbuf vb;
+ struct varbuf_state vbs;
+
+ varbuf_init(&vb, 0);
+
+ test_pass(vb.used == 0);
+ varbuf_snapshot(&vb, &vbs);
+ test_pass(vb.used == 0);
+ test_pass(vb.used == vbs.used);
+ test_pass(varbuf_rollback_len(&vbs) == 0);
+ test_str(varbuf_rollback_start(&vbs), ==, "");
+
+ varbuf_add_buf(&vb, "1234567890", 10);
+ varbuf_end_str(&vb);
+ test_pass(vb.used == 10);
+ test_pass(varbuf_rollback_len(&vbs) == 10);
+ test_str(varbuf_rollback_start(&vbs), ==, "1234567890");
+ varbuf_rollback(&vbs);
+ varbuf_end_str(&vb);
+ test_pass(vb.used == 0);
+ test_pass(varbuf_rollback_len(&vbs) == 0);
+ test_str(varbuf_rollback_start(&vbs), ==, "");
+
+ varbuf_add_buf(&vb, "1234567890", 10);
+ varbuf_end_str(&vb);
+ test_pass(vb.used == 10);
+ test_pass(varbuf_rollback_len(&vbs) == 10);
+ test_str(varbuf_rollback_start(&vbs), ==, "1234567890");
+ varbuf_snapshot(&vb, &vbs);
+ test_pass(vb.used == 10);
+ test_pass(varbuf_rollback_len(&vbs) == 0);
+ test_str(varbuf_rollback_start(&vbs), ==, "");
+
+ varbuf_add_buf(&vb, "1234567890", 10);
+ varbuf_end_str(&vb);
+ test_pass(vb.used == 20);
+ test_pass(varbuf_rollback_len(&vbs) == 10);
+ test_str(varbuf_rollback_start(&vbs), ==, "1234567890");
+ varbuf_rollback(&vbs);
+ varbuf_end_str(&vb);
+ test_pass(vb.used == 10);
+ test_pass(varbuf_rollback_len(&vbs) == 0);
+ test_str(varbuf_rollback_start(&vbs), ==, "");
+
+ varbuf_destroy(&vb);
+}
+
+static void
+test_varbuf_detach(void)
+{
+ struct varbuf vb;
+ char *str;
+
+ varbuf_init(&vb, 0);
+
+ varbuf_add_buf(&vb, "1234567890", 10);
+
+ str = varbuf_detach(&vb);
+
+ test_mem(str, ==, "1234567890", 10);
+ test_pass(vb.used == 0);
+ test_pass(vb.size == 0);
+ test_pass(vb.buf == NULL);
+
+ free(str);
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(152);
+
+ test_varbuf_init();
+ test_varbuf_prealloc();
+ test_varbuf_new();
+ test_varbuf_grow();
+ test_varbuf_trunc();
+ test_varbuf_add_buf();
+ test_varbuf_add_char();
+ test_varbuf_dup_char();
+ test_varbuf_map_char();
+ test_varbuf_add_dir();
+ test_varbuf_end_str();
+ test_varbuf_get_str();
+ test_varbuf_printf();
+ test_varbuf_reset();
+ test_varbuf_snapshot();
+ test_varbuf_detach();
+
+ /* TODO: Complete. */
+}
diff --git a/lib/dpkg/t/t-version.c b/lib/dpkg/t/t-version.c
new file mode 100644
index 0000000..6a771ca
--- /dev/null
+++ b/lib/dpkg/t/t-version.c
@@ -0,0 +1,308 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-version.c - test version handling
+ *
+ * Copyright © 2009-2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <stdlib.h>
+
+#include <dpkg/test.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/dpkg-db.h>
+
+static void
+test_version_blank(void)
+{
+ struct dpkg_version a;
+
+ dpkg_version_blank(&a);
+ test_pass(a.epoch == 0);
+ test_pass(a.version == NULL);
+ test_pass(a.revision == NULL);
+}
+
+static void
+test_version_is_informative(void)
+{
+ struct dpkg_version a;
+
+ dpkg_version_blank(&a);
+ test_fail(dpkg_version_is_informative(&a));
+
+ a.epoch = 1;
+ test_pass(dpkg_version_is_informative(&a));
+
+ dpkg_version_blank(&a);
+ a.version = "1";
+ test_pass(dpkg_version_is_informative(&a));
+
+ dpkg_version_blank(&a);
+ a.revision = "1";
+ test_pass(dpkg_version_is_informative(&a));
+}
+
+static void
+test_version_compare(void)
+{
+ struct dpkg_version a, b;
+
+ dpkg_version_blank(&a);
+ dpkg_version_blank(&b);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ a.epoch = 1;
+ b.epoch = 2;
+ test_fail(dpkg_version_compare(&a, &b) == 0);
+
+ a = DPKG_VERSION_OBJECT(0, "1", "1");
+ b = DPKG_VERSION_OBJECT(0, "2", "1");
+ test_fail(dpkg_version_compare(&a, &b) == 0);
+
+ a = DPKG_VERSION_OBJECT(0, "1", "1");
+ b = DPKG_VERSION_OBJECT(0, "1", "2");
+ test_fail(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test for version equality. */
+ a = b = DPKG_VERSION_OBJECT(0, "0", "0");
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ a = DPKG_VERSION_OBJECT(0, "0", "00");
+ b = DPKG_VERSION_OBJECT(0, "00", "0");
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ a = b = DPKG_VERSION_OBJECT(1, "2", "3");
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test for epoch difference. */
+ a = DPKG_VERSION_OBJECT(0, "0", "0");
+ b = DPKG_VERSION_OBJECT(1, "0", "0");
+ test_pass(dpkg_version_compare(&a, &b) < 0);
+ test_pass(dpkg_version_compare(&b, &a) > 0);
+
+ /* Test for version component difference. */
+ a = DPKG_VERSION_OBJECT(0, "a", "0");
+ b = DPKG_VERSION_OBJECT(0, "b", "0");
+ test_pass(dpkg_version_compare(&a, &b) < 0);
+ test_pass(dpkg_version_compare(&b, &a) > 0);
+
+ /* Test for revision component difference. */
+ a = DPKG_VERSION_OBJECT(0, "0", "a");
+ b = DPKG_VERSION_OBJECT(0, "0", "b");
+ test_pass(dpkg_version_compare(&a, &b) < 0);
+ test_pass(dpkg_version_compare(&b, &a) > 0);
+
+ /* TODO: Complete. */
+}
+
+static void
+test_version_relate(void)
+{
+ struct dpkg_version a, b;
+
+ dpkg_version_blank(&a);
+ dpkg_version_blank(&b);
+ test_pass(dpkg_version_relate(&a, DPKG_RELATION_NONE, &b));
+
+ a = DPKG_VERSION_OBJECT(0, "1", "1");
+ b = DPKG_VERSION_OBJECT(0, "1", "1");
+ test_pass(dpkg_version_relate(&a, DPKG_RELATION_EQ, &b));
+ test_fail(dpkg_version_relate(&a, DPKG_RELATION_LT, &b));
+ test_pass(dpkg_version_relate(&a, DPKG_RELATION_LE, &b));
+ test_fail(dpkg_version_relate(&a, DPKG_RELATION_GT, &b));
+ test_pass(dpkg_version_relate(&a, DPKG_RELATION_GE, &b));
+
+ a = DPKG_VERSION_OBJECT(0, "1", "1");
+ b = DPKG_VERSION_OBJECT(0, "2", "1");
+ test_fail(dpkg_version_relate(&a, DPKG_RELATION_EQ, &b));
+ test_pass(dpkg_version_relate(&a, DPKG_RELATION_LT, &b));
+ test_pass(dpkg_version_relate(&a, DPKG_RELATION_LE, &b));
+ test_fail(dpkg_version_relate(&a, DPKG_RELATION_GT, &b));
+ test_fail(dpkg_version_relate(&a, DPKG_RELATION_GE, &b));
+
+ a = DPKG_VERSION_OBJECT(0, "2", "1");
+ b = DPKG_VERSION_OBJECT(0, "1", "1");
+ test_fail(dpkg_version_relate(&a, DPKG_RELATION_EQ, &b));
+ test_fail(dpkg_version_relate(&a, DPKG_RELATION_LT, &b));
+ test_fail(dpkg_version_relate(&a, DPKG_RELATION_LE, &b));
+ test_pass(dpkg_version_relate(&a, DPKG_RELATION_GT, &b));
+ test_pass(dpkg_version_relate(&a, DPKG_RELATION_GE, &b));
+}
+
+static void
+test_version_parse(void)
+{
+ struct dpkg_error err;
+ struct dpkg_version a, b;
+ const char *p;
+ char *verstr;
+
+ /* Test 0 versions. */
+ dpkg_version_blank(&a);
+ b = DPKG_VERSION_OBJECT(0, "0", "");
+
+ test_pass(parseversion(&a, "0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ test_pass(parseversion(&a, "0:0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ b = DPKG_VERSION_OBJECT(0, "0", "0");
+ test_pass(parseversion(&a, "0:0-0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ b = DPKG_VERSION_OBJECT(0, "0.0", "0.0");
+ test_pass(parseversion(&a, "0:0.0-0.0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test epoched versions. */
+ b = DPKG_VERSION_OBJECT(1, "0", "");
+ test_pass(parseversion(&a, "1:0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ b = DPKG_VERSION_OBJECT(5, "1", "");
+ test_pass(parseversion(&a, "5:1", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test multiple hyphens. */
+ b = DPKG_VERSION_OBJECT(0, "0-0", "0");
+ test_pass(parseversion(&a, "0:0-0-0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ b = DPKG_VERSION_OBJECT(0, "0-0-0", "0");
+ test_pass(parseversion(&a, "0:0-0-0-0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test multiple colons. */
+ b = DPKG_VERSION_OBJECT(0, "0:0", "0");
+ test_pass(parseversion(&a, "0:0:0-0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ b = DPKG_VERSION_OBJECT(0, "0:0:0", "0");
+ test_pass(parseversion(&a, "0:0:0:0-0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test multiple hyphens and colons. */
+ b = DPKG_VERSION_OBJECT(0, "0:0-0", "0");
+ test_pass(parseversion(&a, "0:0:0-0-0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ b = DPKG_VERSION_OBJECT(0, "0-0:0", "0");
+ test_pass(parseversion(&a, "0:0-0:0-0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test valid characters in upstream version. */
+ b = DPKG_VERSION_OBJECT(0, "09azAZ.-+~:", "0");
+ test_pass(parseversion(&a, "0:09azAZ.-+~:-0", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test valid characters in revision. */
+ b = DPKG_VERSION_OBJECT(0, "0", "azAZ09.+~");
+ test_pass(parseversion(&a, "0:0-azAZ09.+~", NULL) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test version with leading and trailing spaces. */
+ b = DPKG_VERSION_OBJECT(0, "0", "1");
+ test_pass(parseversion(&a, " 0:0-1", &err) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+ test_pass(parseversion(&a, "0:0-1 ", &err) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+ test_pass(parseversion(&a, " 0:0-1 ", &err) == 0);
+ test_pass(dpkg_version_compare(&a, &b) == 0);
+
+ /* Test empty version. */
+ test_pass(parseversion(&a, "", &err) != 0);
+ test_error(err);
+ test_pass(parseversion(&a, " ", &err) != 0);
+ test_error(err);
+
+ /* Test empty upstream version after epoch. */
+ test_fail(parseversion(&a, "0:", &err) == 0);
+ test_error(err);
+
+ /* Test empty epoch in version. */
+ test_fail(parseversion(&a, ":1.0", &err) == 0);
+ test_error(err);
+
+ /* Test empty revision in version. */
+ test_fail(parseversion(&a, "1.0-", &err) == 0);
+ test_error(err);
+
+ /* Test version with embedded spaces. */
+ test_fail(parseversion(&a, "0:0 0-1", &err) == 0);
+ test_error(err);
+
+ /* Test version with negative epoch. */
+ test_fail(parseversion(&a, "-1:0-1", &err) == 0);
+ test_error(err);
+
+ /* Test version with huge epoch. */
+ test_fail(parseversion(&a, "999999999999999999999999:0-1", &err) == 0);
+ test_error(err);
+
+ /* Test invalid characters in epoch. */
+ test_fail(parseversion(&a, "a:0-0", &err) == 0);
+ test_error(err);
+ test_fail(parseversion(&a, "A:0-0", &err) == 0);
+ test_error(err);
+
+ /* Test invalid empty upstream version. */
+ test_fail(parseversion(&a, "-0", &err) == 0);
+ test_error(err);
+ test_fail(parseversion(&a, "0:-0", &err) == 0);
+ test_error(err);
+
+ /* Test upstream version not starting with a digit */
+ test_fail(parseversion(&a, "0:abc3-0", &err) == 0);
+ test_warn(err);
+
+ /* Test invalid characters in upstream version. */
+ verstr = test_alloc(strdup("0:0a-0"));
+ for (p = "!#@$%&/|\\<>()[]{};,_=*^'"; *p; p++) {
+ verstr[3] = *p;
+ test_fail(parseversion(&a, verstr, &err) == 0);
+ test_warn(err);
+ }
+ free(verstr);
+
+ /* Test invalid characters in revision. */
+ test_fail(parseversion(&a, "0:0-0:0", &err) == 0);
+ test_warn(err);
+
+ verstr = test_alloc(strdup("0:0-0"));
+ for (p = "!#@$%&/|\\<>()[]{}:;,_=*^'"; *p; p++) {
+ verstr[4] = *p;
+ test_fail(parseversion(&a, verstr, &err) == 0);
+ test_warn(err);
+ }
+ free(verstr);
+
+ /* TODO: Complete. */
+}
+
+TEST_ENTRY(test)
+{
+ test_plan(196);
+
+ test_version_blank();
+ test_version_is_informative();
+ test_version_compare();
+ test_version_relate();
+ test_version_parse();
+}