diff --git a/Makefile.in b/Makefile.in
index 48fdbd2..27da831 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -55,7 +55,7 @@ GUTS=$(filter-out "$(GLOB_PATTERN)",$(wildcard $(GLOB_PATTERN)))
 
 SOURCES=$(wildcard *.c)
 OBJS=$(subst .c,.o,$(SOURCES))
-TESTS=$(patsubst %.c,%,$(wildcard test/*.c))
+TESTS=$(patsubst %.c,%,$(wildcard test/test-*.c))
 
 SHOBJS=pseudo_tables.o pseudo_util.o
 DBOBJS=pseudo_db.o
@@ -78,7 +78,7 @@ all: $(LIBPSEUDO) $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) $(PSEUDO_PROFILE)
 test: all $(TESTS) | $(BIN) $(LIB)
 	./run_tests.sh -v
 
-test/%: test/%.c
+test/test-%: test/test-%.c
 	$(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o $@ $<
 
 install-lib: $(LIBPSEUDO)
diff --git a/test/ftw-test-impl.c b/test/ftw-test-impl.c
new file mode 100644
index 0000000..b117bd8
--- /dev/null
+++ b/test/ftw-test-impl.c
@@ -0,0 +1,93 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <ftw.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static int current_recursion_level = 0;
+static int max_recursion = 0;
+
+static int print_filename = 0;
+static int print_ownership = 0;
+
+
+static int callback(const char* fpath, const struct FTW_STAT_STRUCT *sb, int __attribute__ ((unused)) typeflag){
+    if (current_recursion_level < max_recursion) {
+        ++current_recursion_level;
+        if (FTW_NAME("./walking/a1", callback, 10) != 0) {
+            printf("Recursive call failed\n");
+            exit(1);
+        }
+    }
+
+    if (print_filename) {
+        char *absolute_path = realpath(fpath, NULL);
+        printf("%s\n", absolute_path);
+        free(absolute_path);
+    }
+
+    if (print_ownership) {
+        printf("%d - %d\n", sb->st_gid, sb->st_uid);
+    }
+
+    return 0;
+}
+
+static int run_test() {
+    int ret;
+    ret = FTW_NAME("./walking", callback, 10);
+    return ret;
+}
+
+/*
+ * Walk the given directory structure, and print the given details
+ * (ownership or absolute path) of the entries encountered.
+ */
+static int test_walking(){
+    return run_test();
+}
+
+/*
+ * This test is very similar to test_walking(), but the callback at the
+ * start also calls ftw(), "max_recursion" times.
+ * It is trying to test pseudo's implementation of handling multiple
+ * concurrent (n)ftw calls in the same thread.
+ */
+static int test_walking_recursion(){
+    max_recursion = 3;
+    return run_test();
+}
+
+/*
+ * Arguments:
+ * argv[1]: the test name
+ * argv[2]: "filename" or "ownership" - determine which detail to print
+ */
+int main(int argc, char* argv[])
+{
+    if (argc != 3) {
+        printf("Usage: %s TESTNAME DETAILS\n", argv[0]);
+        printf("TESTNAME is one of: no_recursion, recursion\n");
+        printf("DETAILS is one of: filename, ownership\n");
+        return 1;
+    }
+
+    if (strcmp(argv[2], "filename") == 0) {
+        print_filename = 1;
+    } else if (strcmp(argv[2], "ownership") == 0) {
+        print_ownership = 1;
+    } else {
+        printf("Unknown second parameter");
+        return 1;
+    }
+    
+    if (strcmp(argv[1], "no_recursion") == 0) {
+        return test_walking();
+    } else if (strcmp(argv[1], "recursion") == 0) {
+        return test_walking_recursion();
+    } else {
+        printf("Unknown test name: %s\n", argv[1]);
+        return 1;
+    }
+}
diff --git a/test/nftw-test-impl.c b/test/nftw-test-impl.c
new file mode 100644
index 0000000..714da76
--- /dev/null
+++ b/test/nftw-test-impl.c
@@ -0,0 +1,163 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <ftw.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#define TEST_CHDIR 1
+#define TEST_NO_CHDIR 0
+
+static int print_filename = 0;
+static int print_ownership = 0;
+static int print_cwd = 0;
+
+static int skip_subtree = 0;
+static int skip_subtree_counter = 0;
+
+static int skip_siblings = 0;
+char* basedir;
+
+
+// When FTW_CHDIR is enabled, cwd changes each time, so realpath
+// might not be able to resolve the path
+//char* filepath = realpath(fpath, NULL);
+char* get_absolute_path(const char* fpath) {
+    char* filepath = realpath(fpath, NULL);
+
+    if (filepath == NULL) {
+        if (fpath[0] == '/') {
+            filepath = malloc(strlen(fpath) + 1);
+            strcpy(filepath, fpath);
+        } else {
+            filepath = malloc(strlen(fpath) + strlen(basedir));
+            memset(filepath, 0, strlen(fpath) + strlen(basedir));
+            strcat(filepath, basedir);
+            // skip the initial "."
+            strcat(filepath, fpath + 1);
+        }
+    }
+    return filepath;
+}
+
+static int callback(const char* fpath, const struct NFTW_STAT_STRUCT *sb, int typeflag, struct FTW *ftwbuf){
+    if (print_filename) {
+        char* filepath = get_absolute_path(fpath);
+        printf("%s\n", filepath);
+        free(filepath);
+    }
+
+
+    if (print_ownership) {
+        printf("%d - %d\n", sb->st_gid, sb->st_uid);
+    }
+
+    if (print_cwd) {
+        char* cwd = getcwd(NULL, 0);
+        printf("%s\n", cwd);
+        free(cwd);
+    }
+
+
+    // if subtrees are skipped, don't skip immediately at the root,
+    // otherwise it's not much of a test. Skip it after encountering 2
+    // subfolders (which is an arbitrary numbers, without much science,
+    // admittedly, but better than 0)
+    if (skip_subtree && typeflag == FTW_D) {
+        if (skip_subtree_counter >= 2)
+            return FTW_SKIP_SUBTREE;
+
+        skip_subtree_counter++;
+    }
+
+    if (skip_siblings && typeflag == FTW_F) {
+        return FTW_SKIP_SIBLINGS;
+    }
+
+    return 0;
+}
+
+static int run_test(int flags) {
+    return NFTW_NAME("./walking", callback, 10, flags);
+}
+
+static int test_skip_siblings_file_depth_walking(int change_dir){
+    int flags = FTW_ACTIONRETVAL | FTW_DEPTH;
+
+    // store base_dir, because the fpath returned by (n)ftw can be relative to this
+    // folder - that way a full absolute path can be constructed and compared,
+    // if needed.
+    if (change_dir){
+        basedir = getcwd(NULL, 0);
+        flags |= FTW_CHDIR;
+    }
+
+    skip_siblings = 1;
+    return run_test(flags);
+}
+
+/*
+ * Every time a folder entry is sent to the callback, respond with FTW_SKIP_SUBTREE.
+ * This should skip that particular folder completely, and continue processing
+ * with its siblings (or parent, if there are no siblings).
+ * Return value is expected to be 0, default walking order.
+ */
+static int test_skip_subtree_on_folder(){
+    int flags = FTW_ACTIONRETVAL;
+    skip_subtree = 1;
+    return run_test(flags);
+}
+
+/*
+ * Arguments:
+ * argv[1]: always the test name
+ * argv[2], argv[3]: in case the test name refers to a test without using
+ *                   pseudo (no_pseudo), then they should be the gid and uid
+ *                   of the current user. Otherwise these arguments are ignored.
+ *
+ * skip_subtree_pseudo/skip_subtree_no_pseudo: these tests are calling nftw()
+ * with the FTW_ACTIONRETVAL flag, which reacts based on the return value from the
+ * callback. These tests check the call's reaction to FTW_SKIP_SUBTREE call,
+ * upon which nftw() should stop processing the current folder, and continue
+ * with the next sibling of the folder.
+ *
+ * skip_siblings_pseudo/skip_siblings_no_pseudo: very similar to skip_subtree
+ * tests, but it verified FTW_SKIP_SIBLINGS response, which should stop processing
+ * the current folder, and continue in its parent.
+ *
+ * skip_siblings_chdir_pseudo/skip_siblings_chdir_no_pseudoL same as skip_siblings
+ * tests, but also pass the FTW_CHDIR flag and verify that the working directory
+ * is changed as expected between callback calls.
+ */
+int main(int argc, char* argv[])
+{
+    if (argc != 3) {
+        printf("Usage: %s TESTNAME DETAILS\n", argv[0]);
+        printf("TESTNAME is one of: skip_subtree, skip_siblings, skip_siblings_chdir\n");
+        printf("DETAILS is one of: filename, ownership, cwd\n");
+        return 1;
+    }
+
+    if (strcmp(argv[2], "filename") == 0) {
+        print_filename = 1;
+    } else if (strcmp(argv[2], "ownership") == 0) {
+        print_ownership = 1;
+    } else if (strcmp(argv[2], "cwd") == 0) {
+        print_cwd = 1;
+    } else {
+        printf("Unknown second parameter\n");
+        return 1;
+    }
+
+
+    if (strcmp(argv[1], "skip_subtree") == 0) {
+        return test_skip_subtree_on_folder();
+    } else if (strcmp(argv[1], "skip_siblings") == 0) {
+        return test_skip_siblings_file_depth_walking(TEST_NO_CHDIR);
+    } else if (strcmp(argv[1], "skip_siblings_chdir") == 0) {
+        return test_skip_siblings_file_depth_walking(TEST_CHDIR);
+    } else {
+        printf("Unknown test name\n");
+        return 1;
+    }
+}
diff --git a/test/test-ftw.c b/test/test-ftw.c
new file mode 100644
index 0000000..5c47dd9
--- /dev/null
+++ b/test/test-ftw.c
@@ -0,0 +1,4 @@
+#define FTW_NAME ftw
+#define FTW_STAT_STRUCT stat
+
+#include "ftw-test-impl.c"
diff --git a/test/test-ftw64.c b/test/test-ftw64.c
new file mode 100644
index 0000000..0b8f906
--- /dev/null
+++ b/test/test-ftw64.c
@@ -0,0 +1,4 @@
+#define FTW_NAME ftw64
+#define FTW_STAT_STRUCT stat64
+
+#include "ftw-test-impl.c"
diff --git a/test/test-nftw.c b/test/test-nftw.c
new file mode 100644
index 0000000..ecadc1e
--- /dev/null
+++ b/test/test-nftw.c
@@ -0,0 +1,4 @@
+#define NFTW_NAME nftw
+#define NFTW_STAT_STRUCT stat
+
+#include "nftw-test-impl.c"
diff --git a/test/test-nftw.sh b/test/test-nftw.sh
new file mode 100755
index 0000000..fb4d78a
--- /dev/null
+++ b/test/test-nftw.sh
@@ -0,0 +1,232 @@
+#!/bin/bash
+#
+# Test nftw call and its behavior modifying flags
+#
+# The ./test/test-(n)ftw binaries print out different details of the filentries
+# they encounter during walking the test directory. These details are saved, both using
+# glibc (pseudo disabled), and with using the pseudo shim. glibc output is used
+# as the oracle, and it is expected that the two outputs are always the same - except
+# for ownership details. When it comes to file ownership, pseudo shim is expected to
+# return root owner, while glibc should return the current user.
+#
+# The verify_results() function compares the saved details, with- and without- pseudo:
+# filename, ownership and current_working_directory. If they don't match, the test fails.
+#
+# Below there are a number of tests executed, separated visually, they save different details
+# of the same testcases with and without pseudo, before calling the verification function.
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+
+trap "rm -rf ./walking ./with_pseudo* ./without_pseudo*" 0
+
+ret=0
+
+verify_results(){
+  current_user_uid=`env -i id -u`
+  current_user_gid=`env -i id -g`
+
+  # the list of files should match with and without pseudo
+  diff -Naur ./with_pseudo_filename ./without_pseudo_filename > /dev/null
+  file_list_result=$?
+
+  # in case there is a cwd file (recording the current working dir for each entry),
+  # than they should match also
+  cwd_list_result=0
+  if [ -f ./with_pseudo_cwd ]; then
+    diff -Naur ./with_pseudo_cwd ./without_pseudo_cwd > /dev/null
+    cwd_list_result=$?
+  fi
+
+  # ownership file's content is "$GID - $UID", per line
+  # when using pseudo, both GID and UID should be 0
+  incorrect_pseudo_ownership_count=`grep -vc "0 - 0" with_pseudo_ownership || true`
+
+  # when not using pseudo, the GID and UID should match the
+  # current user's IDs
+  incorrect_no_pseudo_ownership_count=`grep -vc "$current_user_gid - $current_user_uid" without_pseudo_ownership || true`
+
+  if [ $file_list_result -ne 0 ]; then
+    echo test-nftw: $1: Failed, invalid file list
+    ret=1
+  elif [ $incorrect_pseudo_ownership_count -ne 0 ]; then
+    echo test-nftw: $1: Failed, incorrect pseudo ownership details
+    ret=1
+  elif [ $incorrect_no_pseudo_ownership_count -ne 0 ]; then
+    echo test-nftw: $1: Failed, incorrect no pseudo ownership details
+    ret=1
+  elif [ $cwd_list_result -ne 0 ]; then
+    echo test-nftw: $1: Failed, incorrect current working directory
+    ret = 1
+  else
+    echo test-nftw: $1: Passed
+  fi  
+}
+
+
+
+mkdir -p walking/a1/b1/c1
+touch walking/a1/b1/c1/file
+mkdir walking/a1/b2
+mkdir walking/a1/b3
+touch walking/a1/b1/c1/file2
+touch walking/a1/b1/c1/file3
+touch walking/a1/b2/file4
+touch walking/a1/b2/file5
+
+
+
+
+# Test for ftw(), walk tree without recursion
+unset PSEUDO_DISABLED
+./test/test-ftw no_recursion filename > with_pseudo_filename
+./test/test-ftw no_recursion ownership > with_pseudo_ownership
+
+export PSEUDO_DISABLED=1
+./test/test-ftw no_recursion filename > without_pseudo_filename
+./test/test-ftw no_recursion ownership > without_pseudo_ownership
+
+verify_results "ftw no recursion"
+
+
+
+
+
+
+
+# Test for ftw64(), walk tree without recursion
+unset PSEUDO_DISABLED
+./test/test-ftw64 no_recursion filename > with_pseudo_filename
+./test/test-ftw64 no_recursion ownership > with_pseudo_ownership
+
+export PSEUDO_DISABLED=1
+./test/test-ftw64 no_recursion filename > without_pseudo_filename
+./test/test-ftw64 no_recursion ownership > without_pseudo_ownership
+
+verify_results "ftw64 no recursion"
+
+
+
+
+
+
+
+# Test for ftw(), walk tree while calling ftw() again recursively, from
+# within an ftw() callback itself
+unset PSEUDO_DISABLED
+./test/test-ftw recursion filename > with_pseudo_filename
+./test/test-ftw recursion ownership > with_pseudo_ownership
+
+export PSEUDO_DISABLED=1
+./test/test-ftw recursion filename > without_pseudo_filename
+./test/test-ftw recursion ownership > without_pseudo_ownership
+
+verify_results "ftw recursion"
+
+
+
+
+
+
+# Test for ftw64(), walk tree while calling ftw64() again recursively, from
+# within an ftw64() callback itself
+unset PSEUDO_DISABLED
+./test/test-ftw64 recursion filename > with_pseudo_filename
+./test/test-ftw64 recursion ownership > with_pseudo_ownership
+
+export PSEUDO_DISABLED=1
+./test/test-ftw64 recursion filename > without_pseudo_filename
+./test/test-ftw64 recursion ownership > without_pseudo_ownership
+
+verify_results "ftw64 recursion"
+
+
+
+
+
+# Test for nftw(), walk tree, but return SKIP_SUBTREE response each
+# time a directory entry is received by the callback.
+unset PSEUDO_DISABLED
+./test/test-nftw skip_subtree filename > with_pseudo_filename
+./test/test-nftw skip_subtree ownership > with_pseudo_ownership
+
+export PSEUDO_DISABLED=1
+./test/test-nftw skip_subtree filename > without_pseudo_filename
+./test/test-nftw skip_subtree ownership > without_pseudo_ownership
+
+verify_results "nftw skip_subtree"
+
+
+
+
+
+# Test for nftw64(), walk tree, but return SKIP_SUBTREE response each
+# time a directory entry is received by the callback.
+unset PSEUDO_DISABLED
+./test/test-nftw64 skip_subtree filename > with_pseudo_filename
+./test/test-nftw64 skip_subtree ownership > with_pseudo_ownership
+
+export PSEUDO_DISABLED=1
+./test/test-nftw64 skip_subtree filename > without_pseudo_filename
+./test/test-nftw64 skip_subtree ownership > without_pseudo_ownership
+
+verify_results "nftw64 skip_subtree"
+
+
+
+# Test for nftw(), walk tree, but return SKIP_SIBLINGS response each
+# time a file entry is received by the callback
+unset PSEUDO_DISABLED
+./test/test-nftw skip_siblings filename > with_pseudo_filename
+./test/test-nftw skip_siblings ownership > with_pseudo_ownership
+
+#export PSEUDO_DISABLED=1
+./test/test-nftw skip_siblings filename > without_pseudo_filename
+./test/test-nftw skip_siblings ownership > without_pseudo_ownership
+
+verify_results "nftw skip_siblings"
+
+
+
+
+
+
+# Test for nftw64(), walk tree, but return SKIP_SIBLINGS response each
+# time a file entry is received by the callback
+unset PSEUDO_DISABLED
+./test/test-nftw64 skip_siblings filename > with_pseudo_filename
+./test/test-nftw64 skip_siblings ownership > with_pseudo_ownership
+
+export PSEUDO_DISABLED=1
+./test/test-nftw64 skip_siblings filename > without_pseudo_filename
+./test/test-nftw64 skip_siblings ownership > without_pseudo_ownership
+
+verify_results "nftw64 skip_siblings"
+
+
+
+
+
+
+
+# Test for nftw(), walk tree, but return SKIP_SIBLINGS response each
+# time a file entry is received by the callback, and also set the
+# FTW_CHDIR flag on the call to switch to the corresponding folder
+
+unset PSEUDO_DISABLED
+./test/test-nftw skip_siblings_chdir filename > with_pseudo_filename
+./test/test-nftw skip_siblings_chdir ownership > with_pseudo_ownership
+./test/test-nftw skip_siblings_chdir cwd > with_pseudo_cwd
+
+
+
+export PSEUDO_DISABLED=1
+./test/test-nftw skip_siblings_chdir filename > without_pseudo_filename
+./test/test-nftw skip_siblings_chdir ownership > without_pseudo_ownership
+./test/test-nftw skip_siblings_chdir cwd > without_pseudo_cwd
+
+verify_results "nftw skip_siblings_chdir"
+
+
+
+exit $ret
diff --git a/test/test-nftw64.c b/test/test-nftw64.c
new file mode 100644
index 0000000..20f25af
--- /dev/null
+++ b/test/test-nftw64.c
@@ -0,0 +1,4 @@
+#define NFTW_NAME nftw64
+#define NFTW_STAT_STRUCT stat64
+
+#include "nftw-test-impl.c"
