From e486448494c9bed623eaa7c1ee44bba553170939 Mon Sep 17 00:00:00 2001
From: Kevin Day <Kevin@kevux.org>
Date: Mon, 10 Mar 2025 23:47:08 -0500
Subject: [PATCH] Progress: Continue working on completing the remove program.

Fix a bug exposed by the directory unit tests where the `recurse.path_top` is not being defined and must be.
Add additional safety checks to fall back to a NULL string if `recurse.path_top` is NULL.

Add basic file and directory tests.
There are still a lot more tests to write.
This should be an acceptable start.

Refresh the stand alone build settings and config.h files.
---
 data/build/remove/settings-mocks.remove            |   5 +
 data/build/remove/settings-tests.remove            |   2 +-
 data/build/stand_alone/configs/remove-config.h     |   4 +-
 data/build/stand_alone/settings/settings.remove    |   2 +-
 .../c/program/kevux/tools/remove/main/operate.c    |  17 +-
 .../c/program/kevux/tools/remove/main/preprocess.c |   3 +-
 tests/unit/remove/c/mock-remove.c                  |  37 +++-
 tests/unit/remove/c/mock-remove.h                  |   7 +
 .../unit/remove/c/test-remove-directory_no_args.c  | 213 +++++++++++++++++++++
 .../unit/remove/c/test-remove-directory_no_args.h  |  28 +++
 tests/unit/remove/c/test-remove-regular_no_args.c  | 159 ++++++++++++++-
 tests/unit/remove/c/test-remove-regular_no_args.h  |  14 +-
 tests/unit/remove/c/test-remove.c                  |   8 +-
 tests/unit/remove/c/test-remove.h                  |   1 +
 14 files changed, 471 insertions(+), 29 deletions(-)
 create mode 100644 tests/unit/remove/c/test-remove-directory_no_args.c
 create mode 100644 tests/unit/remove/c/test-remove-directory_no_args.h

diff --git a/data/build/remove/settings-mocks.remove b/data/build/remove/settings-mocks.remove
index 1b41928..44e30b6 100644
--- a/data/build/remove/settings-mocks.remove
+++ b/data/build/remove/settings-mocks.remove
@@ -85,8 +85,13 @@ flags_library -fPIC
 flags_program-android -fPIE -Wl,-z,relro
 
 # Inject mocks.
+flags -Wl,--wrap=f_directory_empty
+flags -Wl,--wrap=f_directory_is
 flags -Wl,--wrap=f_directory_remove
 flags -Wl,--wrap=f_file_exists
+flags -Wl,--wrap=f_file_is
 flags -Wl,--wrap=f_file_remove
+flags -Wl,--wrap=f_file_stat
+flags -Wl,--wrap=fl_directory_do
 flags -Wl,--wrap=fll_program_print_version
 flags -Wl,--wrap=kt_remove_print_message_help
diff --git a/data/build/remove/settings-tests.remove b/data/build/remove/settings-tests.remove
index 5df8f23..beeb297 100644
--- a/data/build/remove/settings-tests.remove
+++ b/data/build/remove/settings-tests.remove
@@ -31,7 +31,7 @@ build_libraries-individual_thread -lf_thread
 
 build_sources_program test-remove.c
 build_sources_program test-remove-print_help.c test-remove-print_version.c
-build_sources_program test-remove-regular_no_args.c
+build_sources_program test-remove-directory_no_args.c test-remove-regular_no_args.c
 
 build_script no
 build_shared yes
diff --git a/data/build/stand_alone/configs/remove-config.h b/data/build/stand_alone/configs/remove-config.h
index faf74ae..b8425b8 100644
--- a/data/build/stand_alone/configs/remove-config.h
+++ b/data/build/stand_alone/configs/remove-config.h
@@ -240,7 +240,7 @@
 //#define _di_f_directory_exists_
 #define _di_f_directory_exists_at_
 //#define _di_f_directory_flag_d_
-#define _di_f_directory_is_
+//#define _di_f_directory_is_
 #define _di_f_directory_is_at_
 #define _di_f_directory_list_
 #define _di_f_directory_listing_delete_
@@ -1071,7 +1071,7 @@
 #define _di_f_string_mash_nulless_
 #define _di_f_string_mish_
 #define _di_f_string_mish_nulless_
-#define _di_f_string_null_s_
+//#define _di_f_string_null_s_
 #define _di_f_string_placeholder_s_
 #define _di_f_string_prepend_
 #define _di_f_string_prepend_assure_
diff --git a/data/build/stand_alone/settings/settings.remove b/data/build/stand_alone/settings/settings.remove
index e977fca..d505fdc 100644
--- a/data/build/stand_alone/settings/settings.remove
+++ b/data/build/stand_alone/settings/settings.remove
@@ -71,7 +71,7 @@ build_sources_program fll/level_2/error.c fll/level_2/private-error.c fll/level_
 build_sources_program fll/level_2/print.c
 build_sources_program fll/level_2/program.c fll/level_2/program/common.c fll/level_2/program/print.c fll/level_2/private-program.c
 
-build_sources_program program/kevux/tools/remove/main/common.c program/kevux/tools/remove/main/common/define.c program/kevux/tools/remove/main/common/enumeration.c program/kevux/tools/remove/main/common/print.c program/kevux/tools/remove/main/common/string.c program/kevux/tools/remove/main/common/type.c program/kevux/tools/remove/main/convert.c program/kevux/tools/remove/main/operate.c program/kevux/tools/remove/main/preprocess.c program/kevux/tools/remove/main/print/error.c program/kevux/tools/remove/main/print/message.c program/kevux/tools/remove/main/print/simulate.c program/kevux/tools/remove/main/print/verbose.c program/kevux/tools/remove/main/print/warning.c program/kevux/tools/remove/main/remove.c program/kevux/tools/remove/main/signal.c program/kevux/tools/remove/main/thread.c
+build_sources_program program/kevux/tools/remove/main/common.c program/kevux/tools/remove/main/common/define.c program/kevux/tools/remove/main/common/enumeration.c program/kevux/tools/remove/main/common/print.c program/kevux/tools/remove/main/common/string.c program/kevux/tools/remove/main/common/type.c program/kevux/tools/remove/main/convert.c program/kevux/tools/remove/main/operate.c program/kevux/tools/remove/main/preprocess.c program/kevux/tools/remove/main/print/debug.c program/kevux/tools/remove/main/print/error.c program/kevux/tools/remove/main/print/message.c program/kevux/tools/remove/main/print/simulate.c program/kevux/tools/remove/main/print/verbose.c program/kevux/tools/remove/main/print/warning.c program/kevux/tools/remove/main/remove.c program/kevux/tools/remove/main/signal.c program/kevux/tools/remove/main/thread.c
 
 build_sources_program program/kevux/tools/remove/remove/config.c program/kevux/tools/remove/remove/remove.c program/kevux/tools/remove/remove/main.c program/kevux/tools/remove/remove/string.c
 
diff --git a/sources/c/program/kevux/tools/remove/main/operate.c b/sources/c/program/kevux/tools/remove/main/operate.c
index b02d2b6..1e6056a 100644
--- a/sources/c/program/kevux/tools/remove/main/operate.c
+++ b/sources/c/program/kevux/tools/remove/main/operate.c
@@ -50,8 +50,9 @@ extern "C" {
     recurse.state.code = flag_operate;
     recurse.state.interrupt = &kt_remove_signal_check_recurse;
 
-    recurse.flag = f_directory_recurse_do_flag_list_e | f_directory_recurse_do_flag_top_after_e;
     recurse.depth_max = main->setting.flag & kt_remove_main_flag_recurse_d ? kt_remove_depth_max_d : 0;
+    recurse.flag = f_directory_recurse_do_flag_list_e | f_directory_recurse_do_flag_top_after_e;
+    recurse.path_top = &path;
 
     recurse.action = &kt_remove_operate_file_directory_action;
     recurse.handle = &kt_remove_operate_file_recurse_handle;
@@ -59,10 +60,12 @@ extern "C" {
     fl_directory_do(path, &recurse);
 
     const f_status_t status = f_directory_recurse_do_delete(&recurse);
-    if (F_status_is_error(recurse.state.status)) return recurse.state.status;
-    if (F_status_is_error(status)) return status;
 
-    return F_yes;
+    return F_status_is_error(recurse.state.status)
+      ? recurse.state.status
+      : F_status_is_error(status)
+        ? status
+        : F_yes;
   }
 #endif // _di_kt_remove_operate_file_directory_
 
@@ -72,7 +75,11 @@ extern "C" {
     if (!recurse || !recurse->state.custom || F_status_set_fine(recurse->state.status) == F_interrupt) return;
     if (!kt_remove_operate_shall_remove(recurse->state.code) || !(flag & f_directory_recurse_do_flag_action_e)) return;
 
-    const f_string_static_t path = flag & f_directory_recurse_do_flag_top_after_e ? *recurse->path_top : recurse->path;
+    const f_string_static_t path = flag & f_directory_recurse_do_flag_top_after_e
+      ? recurse->path_top
+        ? *recurse->path_top
+        : f_string_null_s
+      : recurse->path;
 
     recurse->state.status = kt_remove_operate_file_remove(
       (kt_remove_main_t *) recurse->state.custom,
diff --git a/sources/c/program/kevux/tools/remove/main/preprocess.c b/sources/c/program/kevux/tools/remove/main/preprocess.c
index 1e1bc4b..c23eaf1 100644
--- a/sources/c/program/kevux/tools/remove/main/preprocess.c
+++ b/sources/c/program/kevux/tools/remove/main/preprocess.c
@@ -349,8 +349,9 @@ extern "C" {
     recurse.state.code = flag_operate;
     recurse.state.interrupt = &kt_remove_signal_check_recurse;
 
-    recurse.flag = f_directory_recurse_do_flag_list_e | f_directory_recurse_do_flag_top_after_e;
     recurse.depth_max = main->setting.flag & kt_remove_main_flag_recurse_d ? kt_remove_depth_max_d : 0;
+    recurse.flag = f_directory_recurse_do_flag_list_e | f_directory_recurse_do_flag_top_after_e;
+    recurse.path_top = &path;
 
     recurse.action = &kt_remove_preprocess_file_recurse_action;
     recurse.handle = &kt_remove_operate_file_recurse_handle;
diff --git a/tests/unit/remove/c/mock-remove.c b/tests/unit/remove/c/mock-remove.c
index 3153283..0ebdeb7 100644
--- a/tests/unit/remove/c/mock-remove.c
+++ b/tests/unit/remove/c/mock-remove.c
@@ -5,35 +5,52 @@ extern "C" {
 #endif
 
 int mock_unwrap = 0;
+f_status_t mock_directory_call_action = 0;
 f_status_t mock_status = 0;
 
-f_status_t __wrap_f_directory_remove(const f_string_static_t path, const int depth_max, const bool preserve) {
-
-  const bool failure = mock_type(bool);
+f_status_t __wrap_f_directory_empty(const f_string_static_t path) {
+  return mock_type(f_status_t);
+}
 
-  if (failure) return mock_type(f_status_t);
+f_status_t __wrap_f_directory_is(const f_string_static_t path) {
+  return mock_type(f_status_t);
+}
 
+f_status_t __wrap_f_directory_remove(const f_string_static_t path, const int depth_max, const bool preserve) {
   return mock_type(f_status_t);
 }
 
 f_status_t __wrap_f_file_exists(const f_string_static_t path, const bool dereference) {
+  return mock_type(f_status_t);
+}
 
-  const bool failure = mock_type(bool);
-
-  if (failure) return mock_type(f_status_t);
-
+f_status_t __wrap_f_file_is(const f_string_static_t path, const int type, const bool dereference) {
   return mock_type(f_status_t);
 }
 
 f_status_t __wrap_f_file_remove(const f_string_static_t path) {
+  return mock_type(f_status_t);
+}
 
-  const bool failure = mock_type(bool);
+f_status_t __wrap_f_file_stat(const f_string_static_t path, const bool dereference, struct stat * const stat_file) {
 
-  if (failure) return mock_type(f_status_t);
+  struct stat * const mocked_stat = mock_ptr_type(struct stat *);
+
+  memmove(stat_file, mocked_stat, sizeof(struct stat));
 
   return mock_type(f_status_t);
 }
 
+void __wrap_fl_directory_do(const f_string_static_t path, f_directory_recurse_do_t * const recurse) {
+
+  if (mock_directory_call_action) {
+    recurse->action(recurse, path, mock_type(uint32_t));
+  }
+  else {
+    recurse->state.status = mock_type(f_status_t);
+  }
+}
+
 f_status_t __wrap_fll_program_print_version(fl_print_t * const print, const f_string_static_t version) {
 
   mock_status = mock_type(f_status_t);
diff --git a/tests/unit/remove/c/mock-remove.h b/tests/unit/remove/c/mock-remove.h
index 7693110..213ba46 100644
--- a/tests/unit/remove/c/mock-remove.h
+++ b/tests/unit/remove/c/mock-remove.h
@@ -30,12 +30,19 @@ extern "C" {
 const static int mock_errno_generic = 32767;
 
 extern int mock_unwrap;
+extern f_status_t mock_directory_call_action;
 extern f_status_t mock_status;
 
+extern f_status_t __wrap_f_directory_empty(const f_string_static_t path);
+extern f_status_t __wrap_f_directory_is(const f_string_static_t path);
 extern f_status_t __wrap_f_directory_remove(const f_string_static_t path, const int depth_max, const bool preserve);
 
 extern f_status_t __wrap_f_file_exists(const f_string_static_t path, const bool dereference);
+extern f_status_t __wrap_f_file_is(const f_string_static_t path, const int type, const bool dereference);
 extern f_status_t __wrap_f_file_remove(const f_string_static_t path);
+extern f_status_t __wrap_f_file_stat(const f_string_static_t path, const bool dereference, struct stat * const stat_file);
+
+extern void __wrap_fl_directory_do(const f_string_static_t path, f_directory_recurse_do_t * const recurse);
 
 extern f_status_t __wrap_fll_program_print_version(fl_print_t * const print, const f_string_static_t version);
 
diff --git a/tests/unit/remove/c/test-remove-directory_no_args.c b/tests/unit/remove/c/test-remove-directory_no_args.c
new file mode 100644
index 0000000..e4229a2
--- /dev/null
+++ b/tests/unit/remove/c/test-remove-directory_no_args.c
@@ -0,0 +1,213 @@
+#include "test-remove.h"
+#include "test-remove-directory_no_args.h"
+
+#include <program/kevux/tools/remove/remove/main.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__kt_remove__directory_no_args__empty_exists_link(void **state) {
+
+  mock_unwrap = 0;
+  mock_directory_call_action = F_true;
+
+  struct stat statistics;
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_directory_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_true); // A link, kt_remove_flag_file_operate_link_d is set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // Directory processing.
+    will_return(__wrap_f_directory_empty, F_true);
+    will_return(__wrap_fl_directory_do, f_directory_recurse_do_flag_action_e | f_directory_recurse_do_flag_top_after_e);
+    will_return(__wrap_f_directory_is, F_true);
+
+    // This will fail if f_directory_remove() is not called, therefore success here means f_directory_remove has been called.
+    will_return(__wrap_f_directory_remove, F_okay);
+
+    const int result = kt_main_test__remove(2, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+V", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_directory_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_true); // A link, kt_remove_flag_file_operate_link_d is set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // Directory processing.
+    will_return(__wrap_f_directory_empty, F_true);
+    will_return(__wrap_fl_directory_do, f_directory_recurse_do_flag_action_e | f_directory_recurse_do_flag_top_after_e);
+    will_return(__wrap_f_directory_is, F_true);
+
+    // This will fail if f_directory_remove() is not called, therefore success here means f_directory_remove has been called.
+    will_return(__wrap_f_directory_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+D", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_directory_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_true); // A link, kt_remove_flag_file_operate_link_d is set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // Directory processing.
+    will_return(__wrap_f_directory_empty, F_true);
+    will_return(__wrap_fl_directory_do, f_directory_recurse_do_flag_action_e | f_directory_recurse_do_flag_top_after_e);
+    will_return(__wrap_f_directory_is, F_true);
+
+    // This will fail if f_directory_remove() is not called, therefore success here means f_directory_remove has been called.
+    will_return(__wrap_f_directory_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+}
+
+void test__kt_remove__directory_no_args__empty_exists_link_not(void **state) {
+
+  mock_unwrap = 0;
+  mock_directory_call_action = F_true;
+
+  struct stat statistics;
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_directory_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_false); // A link, kt_remove_flag_file_operate_link_d is not set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // Directory processing.
+    will_return(__wrap_f_directory_empty, F_true);
+    will_return(__wrap_fl_directory_do, f_directory_recurse_do_flag_action_e | f_directory_recurse_do_flag_top_after_e);
+    will_return(__wrap_f_directory_is, F_true);
+
+    // This will fail if f_directory_remove() is not called, therefore success here means f_directory_remove has been called.
+    will_return(__wrap_f_directory_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+V", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_directory_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_false); // A link, kt_remove_flag_file_operate_link_d is not set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // Directory processing.
+    will_return(__wrap_f_directory_empty, F_true);
+    will_return(__wrap_fl_directory_do, f_directory_recurse_do_flag_action_e | f_directory_recurse_do_flag_top_after_e);
+    will_return(__wrap_f_directory_is, F_true);
+
+    // This will fail if f_directory_remove() is not called, therefore success here means f_directory_remove has been called.
+    will_return(__wrap_f_directory_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+D", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_directory_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_false); // A link, kt_remove_flag_file_operate_link_d is not set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // Directory processing.
+    will_return(__wrap_f_directory_empty, F_true);
+    will_return(__wrap_fl_directory_do, f_directory_recurse_do_flag_action_e | f_directory_recurse_do_flag_top_after_e);
+    will_return(__wrap_f_directory_is, F_true);
+
+    // This will fail if f_directory_remove() is not called, therefore success here means f_directory_remove has been called.
+    will_return(__wrap_f_directory_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+}
+
+void test__kt_remove__directory_no_args__empty_exists_not(void **state) {
+
+  mock_unwrap = 0;
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", 0 };
+
+    will_return(__wrap_f_file_exists, F_false);
+
+    const int result = kt_main_test__remove(2, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+V", 0 };
+
+    will_return(__wrap_f_file_exists, F_false);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+D", 0 };
+
+    will_return(__wrap_f_file_exists, F_false);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/tests/unit/remove/c/test-remove-directory_no_args.h b/tests/unit/remove/c/test-remove-directory_no_args.h
new file mode 100644
index 0000000..b2c048d
--- /dev/null
+++ b/tests/unit/remove/c/test-remove-directory_no_args.h
@@ -0,0 +1,28 @@
+/**
+ * Kevux Tools - Remove
+ *
+ * Project: Kevux Tools
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the remove.
+ */
+#ifndef _TEST__KT_remove__directory_no_args
+#define _TEST__KT_remove__directory_no_args
+
+/**
+ * Test that the remove works for file that exists and is a link, for directory files (that are empty) without other complex arguments.
+ */
+extern void test__kt_remove__directory_no_args__empty_exists_link(void **state);
+
+/**
+ * Test that the remove works for file that exists and is a not link, for directory files (that are empty) without other complex arguments.
+ */
+extern void test__kt_remove__directory_no_args__empty_exists_link_not(void **state);
+
+/**
+ * Test that the remove works for file that does not exist, for directory files (that are empty) without other complex arguments.
+ */
+extern void test__kt_remove__directory_no_args__empty_exists_not(void **state);
+
+#endif // _TEST__KT_remove__directory_no_args
diff --git a/tests/unit/remove/c/test-remove-regular_no_args.c b/tests/unit/remove/c/test-remove-regular_no_args.c
index bceed4f..2e4edec 100644
--- a/tests/unit/remove/c/test-remove-regular_no_args.c
+++ b/tests/unit/remove/c/test-remove-regular_no_args.c
@@ -7,25 +7,172 @@
 extern "C" {
 #endif
 
-void test__kt_remove__regular_no_args__works(void **state) {
+void test__kt_remove__regular_no_args__exists_link(void **state) {
 
   mock_unwrap = 0;
 
+  struct stat statistics;
+
   {
     const f_string_t argv[] = { "mocked_main", "to_remove", 0 };
 
-    mock_status = F_okay;
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_regular_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_true); // A link, kt_remove_flag_file_operate_link_d is set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // This will fail if f_file_remove() is not called, therefore success here means f_file_remove has been called.
+    will_return(__wrap_f_file_remove, F_okay);
+
+    const int result = kt_main_test__remove(2, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+V", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_regular_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_true); // A link, kt_remove_flag_file_operate_link_d is set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // This will fail if f_file_remove() is not called, therefore success here means f_file_remove has been called.
+    will_return(__wrap_f_file_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+D", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_regular_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_true); // A link, kt_remove_flag_file_operate_link_d is set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // This will fail if f_file_remove() is not called, therefore success here means f_file_remove has been called.
+    will_return(__wrap_f_file_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+}
+
+void test__kt_remove__regular_no_args__exists_link_not(void **state) {
+
+  mock_unwrap = 0;
+
+  struct stat statistics;
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_regular_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_false); // A link, kt_remove_flag_file_operate_link_d is not set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // This will fail if f_file_remove() is not called, therefore success here means f_file_remove has been called.
+    will_return(__wrap_f_file_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+V", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_regular_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_false); // A link, kt_remove_flag_file_operate_link_d is not set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // This will fail if f_file_remove() is not called, therefore success here means f_file_remove has been called.
+    will_return(__wrap_f_file_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+D", 0 };
+
+    memset(&statistics, 0, sizeof(struct stat));
+    statistics.st_mode = F_file_mode_all_d | F_file_type_regular_d; // Should result in kt_remove_flag_file_operate_remove_d.
+
+    // Pre-process file.
+    will_return(__wrap_f_file_exists, F_true);
+    will_return(__wrap_f_file_is, F_false); // A link, kt_remove_flag_file_operate_link_d is not set.
+    will_return(__wrap_f_file_stat, &statistics);
+    will_return(__wrap_f_file_stat, F_false);
+
+    // This will fail if f_file_remove() is not called, therefore success here means f_file_remove has been called.
+    will_return(__wrap_f_file_remove, F_okay);
+
+    const int result = kt_main_test__remove(3, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+}
+
+void test__kt_remove__regular_no_args__exists_not(void **state) {
+
+  mock_unwrap = 0;
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", 0 };
 
     will_return(__wrap_f_file_exists, F_false);
+
+    const int result = kt_main_test__remove(2, argv, 0);
+
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+V", 0 };
+
     will_return(__wrap_f_file_exists, F_false);
 
-    // @todo this is incomplete.
-    //will_return(__wrap_f_file_remove, F_test);
+    const int result = kt_main_test__remove(3, argv, 0);
 
-    const int result = kt_main_test__remove(2, argv, 0);
+    assert_int_equal(result, 0);
+  }
+
+  {
+    const f_string_t argv[] = { "mocked_main", "to_remove", "+D", 0 };
+
+    will_return(__wrap_f_file_exists, F_false);
+
+    const int result = kt_main_test__remove(3, argv, 0);
 
     assert_int_equal(result, 0);
-    assert_int_equal(mock_status, F_okay);
   }
 }
 
diff --git a/tests/unit/remove/c/test-remove-regular_no_args.h b/tests/unit/remove/c/test-remove-regular_no_args.h
index 4f39503..f1d6303 100644
--- a/tests/unit/remove/c/test-remove-regular_no_args.h
+++ b/tests/unit/remove/c/test-remove-regular_no_args.h
@@ -11,8 +11,18 @@
 #define _TEST__KT_remove__regular_no_args
 
 /**
- * Test that the remove works, for regular files without other arguments.
+ * Test that the remove works for file that exists and is a link, for regular files without other complex arguments.
  */
-extern void test__kt_remove__regular_no_args__works(void **state);
+extern void test__kt_remove__regular_no_args__exists_link(void **state);
+
+/**
+ * Test that the remove works for file that exists and is a not link, for regular files without other complex arguments.
+ */
+extern void test__kt_remove__regular_no_args__exists_link_not(void **state);
+
+/**
+ * Test that the remove works for file that does not exist, for regular files without other complex arguments.
+ */
+extern void test__kt_remove__regular_no_args__exists_not(void **state);
 
 #endif // _TEST__KT_remove__regular_no_args
diff --git a/tests/unit/remove/c/test-remove.c b/tests/unit/remove/c/test-remove.c
index f948bdd..1b89e12 100644
--- a/tests/unit/remove/c/test-remove.c
+++ b/tests/unit/remove/c/test-remove.c
@@ -22,7 +22,13 @@ int main(void) {
     cmocka_unit_test(test__kt_remove__print_help__works),
     cmocka_unit_test(test__kt_remove__print_version__works),
 
-    cmocka_unit_test(test__kt_remove__regular_no_args__works),
+    cmocka_unit_test(test__kt_remove__directory_no_args__empty_exists_link),
+    cmocka_unit_test(test__kt_remove__directory_no_args__empty_exists_link_not),
+    cmocka_unit_test(test__kt_remove__directory_no_args__empty_exists_not),
+
+    cmocka_unit_test(test__kt_remove__regular_no_args__exists_link),
+    cmocka_unit_test(test__kt_remove__regular_no_args__exists_link_not),
+    cmocka_unit_test(test__kt_remove__regular_no_args__exists_not),
   };
 
   return cmocka_run_group_tests(tests, setup, setdown);
diff --git a/tests/unit/remove/c/test-remove.h b/tests/unit/remove/c/test-remove.h
index 6bfd56e..e687aaf 100644
--- a/tests/unit/remove/c/test-remove.h
+++ b/tests/unit/remove/c/test-remove.h
@@ -32,6 +32,7 @@
 // Test includes.
 #include "test-remove-print_help.h"
 #include "test-remove-print_version.h"
+#include "test-remove-directory_no_args.h"
 #include "test-remove-regular_no_args.h"
 
 #ifdef __cplusplus
-- 
1.8.3.1