]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tests: add test for recurse_dir()
authorLennart Poettering <lennart@poettering.net>
Fri, 1 Oct 2021 14:36:21 +0000 (16:36 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 7 Oct 2021 09:58:26 +0000 (11:58 +0200)
src/test/meson.build
src/test/test-recurse-dir.c [new file with mode: 0644]

index cef2532407af1c4da8d3c41a4cbcc751fe4c6ff2..534e3d1924f29ec96a43c101e3b2f8f5979240cd 100644 (file)
@@ -163,6 +163,8 @@ tests += [
 
         [['src/test/test-copy.c']],
 
+        [['src/test/test-recurse-dir.c']],
+
         [['src/test/test-data-fd-util.c']],
 
         [['src/test/test-static-destruct.c']],
diff --git a/src/test/test-recurse-dir.c b/src/test/test-recurse-dir.c
new file mode 100644 (file)
index 0000000..5872936
--- /dev/null
@@ -0,0 +1,161 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <ftw.h>
+
+#include "log.h"
+#include "recurse-dir.h"
+#include "strv.h"
+#include "tests.h"
+
+static char **list_nftw = NULL;
+
+static int nftw_cb(
+                const char *fpath,
+                const struct stat *sb,
+                int typeflag,
+                struct FTW *ftwbuf) {
+
+        if (ftwbuf->level == 0) /* skip top-level */
+                return FTW_CONTINUE;
+
+        switch (typeflag) {
+
+        case FTW_F:
+                log_debug("ftw found %s", fpath);
+                assert_se(strv_extend(&list_nftw, fpath) >= 0);
+                break;
+
+        case FTW_SL:
+                log_debug("ftw found symlink %s", fpath);
+                assert_se(strv_extendf(&list_nftw, "%s→", fpath) >= 0);
+                break;
+
+        case FTW_D:
+                log_debug("ftw entering %s", fpath);
+                assert_se(strv_extendf(&list_nftw, "%s/", fpath) >= 0);
+                break;
+
+        case FTW_DNR:
+                log_debug("ftw open directory failed %s", fpath);
+                break;
+
+        case FTW_NS:
+                log_debug("ftw stat inode failed %s", fpath);
+                break;
+
+        case FTW_DP:
+        case FTW_SLN:
+        default:
+                assert_not_reached();
+        }
+
+        return FTW_CONTINUE;
+}
+
+static int recurse_dir_callback(
+                RecurseDirEvent event,
+                const char *path,
+                int dir_fd,
+                int inode_fd,
+                const struct dirent *de,
+                const struct statx *sx,
+                void *userdata) {
+
+        char ***l = userdata;
+
+        assert(path);
+        assert(de);
+
+        switch (event) {
+
+        case RECURSE_DIR_ENTRY:
+                assert_se(!IN_SET(de->d_type, DT_UNKNOWN, DT_DIR));
+
+                log_debug("found %s", path);
+
+                if (de->d_type == DT_LNK)
+                        assert_se(strv_extendf(l, "%s→", path) >= 0);
+                else
+                        assert_se(strv_extend(l, path) >= 0);
+                break;
+
+        case RECURSE_DIR_ENTER:
+                assert_se(de->d_type == DT_DIR);
+
+                log_debug("entering %s", path);
+                assert_se(strv_extendf(l, "%s/", path) >= 0);
+                break;
+
+        case RECURSE_DIR_LEAVE:
+                log_debug("leaving %s", path);
+                break;
+
+        case RECURSE_DIR_SKIP_MOUNT:
+                log_debug("skipping mount %s", path);
+                break;
+
+        case RECURSE_DIR_SKIP_DEPTH:
+                log_debug("skipping depth %s", path);
+                break;
+
+        case RECURSE_DIR_SKIP_OPEN_DIR_ERROR_BASE...RECURSE_DIR_SKIP_OPEN_DIR_ERROR_MAX:
+                log_debug_errno(event - RECURSE_DIR_SKIP_OPEN_DIR_ERROR_BASE, "failed to open dir %s: %m", path);
+                break;
+
+        case RECURSE_DIR_SKIP_OPEN_INODE_ERROR_BASE...RECURSE_DIR_SKIP_OPEN_INODE_ERROR_MAX:
+                log_debug_errno(event - RECURSE_DIR_SKIP_OPEN_INODE_ERROR_BASE, "failed to open inode %s: %m", path);
+                break;
+
+        case RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE...RECURSE_DIR_SKIP_STAT_INODE_ERROR_MAX:
+                log_debug_errno(event - RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE, "failed to stat inode %s: %m", path);
+                break;
+
+        default:
+                assert_not_reached();
+        }
+
+        return RECURSE_DIR_CONTINUE;
+}
+
+int main(int argc, char *argv[]) {
+        _cleanup_strv_free_ char **list_recurse_dir = NULL;
+        const char *p;
+        int r;
+
+        log_show_color(true);
+        test_setup_logging(LOG_INFO);
+
+        if (argc > 1)
+                p = argv[1];
+        else
+                p = "/usr/share/man"; /* something hopefully reasonably stable while we run (and limited in size) */
+
+        /* Enumerate the specified dirs in full, once via nftw(), and once via recurse_dir(), and ensure the results are identical */
+        r = recurse_dir_at(AT_FDCWD, p, 0, UINT_MAX, RECURSE_DIR_SORT|RECURSE_DIR_ENSURE_TYPE|RECURSE_DIR_SAME_MOUNT, recurse_dir_callback, &list_recurse_dir);
+        if (r == -ENOENT) {
+                log_warning_errno(r, "Couldn't open directory %s, ignoring: %m", p);
+                return EXIT_TEST_SKIP;
+        }
+        assert_se(r >= 0);
+
+        assert_se(nftw(p, nftw_cb, 64, FTW_PHYS|FTW_MOUNT) >= 0);
+
+        strv_sort(list_recurse_dir);
+        strv_sort(list_nftw);
+
+        for (size_t i = 0;; i++) {
+                const char *a = list_nftw ? list_nftw[i] : NULL,
+                        *b = list_recurse_dir ? list_recurse_dir[i] : NULL;
+
+                if (!streq_ptr(a, b)) {
+                        log_error("entry %zu different: %s vs %s", i, strna(a), strna(b));
+                        assert_not_reached();
+                }
+
+                if (!a)
+                        break;
+        }
+
+        list_nftw = strv_free(list_nftw);
+        return 0;
+}