]> Kevux Git Server - fll/commitdiff
Update: Add unit tests for f_environment, make changes to f_environment, and add...
authorKevin Day <thekevinday@gmail.com>
Fri, 22 Apr 2022 04:09:50 +0000 (23:09 -0500)
committerKevin Day <thekevinday@gmail.com>
Fri, 22 Apr 2022 04:14:14 +0000 (23:14 -0500)
Add the unit tests for f_environment.

The new function f_environment_secure_is() brings in libcap dependency requirements to f_environment.
This is added to add support for something similar to secure_getenv() rather than wrapping a non-standard method.
I am still not very experience with capabilities and do not guarantee an accurate replacement for secure_getenv().

Replace F_valid_not error returns with F_parameter error returns.

24 files changed:
level_0/f_environment/c/environment.c
level_0/f_environment/c/environment.h
level_0/f_environment/data/build/defines
level_0/f_environment/data/build/dependencies-tests [new file with mode: 0644]
level_0/f_environment/data/build/settings
level_0/f_environment/data/build/settings-mocks [new file with mode: 0644]
level_0/f_environment/data/build/settings-tests [new file with mode: 0644]
level_0/f_environment/data/build/testfile [new file with mode: 0644]
level_0/f_environment/tests/unit/c/mock-environment.c [new file with mode: 0644]
level_0/f_environment/tests/unit/c/mock-environment.h [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-clear.c [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-clear.h [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-exists.c [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-exists.h [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-get.c [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-get.h [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-secure_is.c [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-secure_is.h [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-set.c [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-set.h [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-unset.c [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment-unset.h [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment.c [new file with mode: 0644]
level_0/f_environment/tests/unit/c/test-environment.h [new file with mode: 0644]

index 114cc5f1fdac141caa8600819b58aedb8ebb41d0..48346d6572639e6b64f80a3d691e161d1e8f3568 100644 (file)
@@ -69,6 +69,47 @@ extern "C" {
   }
 #endif // _di_f_environment_get_
 
+#ifndef _di_f_environment_secure_is_
+  f_status_t f_environment_secure_is(void) {
+
+    if (geteuid() == getuid() && getegid() == getgid()) {
+      return F_true;
+    }
+
+    #ifndef _di_libcap_
+      cap_t capability = cap_get_proc();
+
+      if (!capability) {
+        if (errno == EINVAL) return F_status_set_error(F_parameter);
+        if (errno == EPERM) return F_status_set_error(F_prohibited);
+        if (errno == ENOMEM) return F_status_set_error(F_memory_not);
+
+        return F_status_set_error(F_failure);
+      }
+
+      cap_flag_value_t value;
+
+      memset(&value, 0, sizeof(cap_flag_value_t));
+
+      if (cap_get_flag(capability, CAP_SETUID, CAP_EFFECTIVE, &value) == -1) {
+        cap_free(capability);
+
+        if (errno == EINVAL) return F_status_set_error(F_parameter);
+
+        return F_status_set_error(F_failure);
+      }
+
+      cap_free(capability);
+
+      if (value == CAP_SET) {
+        return F_true;
+      }
+    #endif // _di_libcap_
+
+    return F_false;
+  }
+#endif // _di_f_environment_secure_is_
+
 #ifndef _di_f_environment_set_
   f_status_t f_environment_set(const f_string_static_t name, const f_string_static_t value, const bool replace) {
 
@@ -77,7 +118,7 @@ extern "C" {
     }
 
     if (setenv(name.string, value.string, replace) < 0) {
-      if (errno == EINVAL) return F_status_set_error(F_valid_not);
+      if (errno == EINVAL) return F_status_set_error(F_parameter);
       if (errno == ENOMEM) return F_status_set_error(F_memory_not);
 
       return F_status_set_error(F_failure);
@@ -95,7 +136,7 @@ extern "C" {
     }
 
     if (unsetenv(name.string) < 0) {
-      if (errno == EINVAL) return F_status_set_error(F_valid_not);
+      if (errno == EINVAL) return F_status_set_error(F_parameter);
       if (errno == ENOMEM) return F_status_set_error(F_memory_not);
 
       return F_status_set_error(F_failure);
index 018ab017a65c1d151327bdbcb5b3caedd9f7a054..b536bf585181460466418f4bd59b419305c60193 100644 (file)
 
 // Libc includes.
 #include <stdio.h>
+#include <unistd.h>
 #include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef _di_libcap_
+  #include <sys/capability.h>
+#endif // _di_libcap_
 
 // FLL-0 includes.
 #include <fll/level_0/type.h>
@@ -97,6 +103,39 @@ extern "C" {
 #endif // _di_f_environment_get_
 
 /**
+ * Check to see if the environment is secure for calling getenv() safely for "secure execution".
+ *
+ * This is intended to closely mimic the checks secure_getenv().
+ *
+ * Any of these conditions must be true for secure environment:
+ * - The process' effective UID matches the real UID and the effective GID matches the real GID.
+ * - The process has the effective CAP_SETUID set.
+ *
+ * The documentation for secure_getenv() is unclear on which capabilities are expected to be set.
+ * This takes a conservative approach and only returns true for the above mentioned capabilities.
+ *
+ * @return
+ *   F_true if the environment is secure according to the described rules.
+ *   F_false if the environment is not secure according to the described rules.
+ *
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if name is an invalid string.
+ *   F_prohibited (with error bit) if the file system does not permit this operation (usually due to the cap_get_proc() call).
+ *
+ *   F_failure (with error bit) on any other error.
+ *
+ * @see cap_get_flag()
+ * @see cap_get_proc()
+ * @see getegid()
+ * @see geteuid()
+ * @see getgid()
+ * @see getuid()
+ */
+#ifndef _di_f_environment_secure_is_
+  extern f_status_t f_environment_secure_is(void);
+#endif // _di_f_environment_secure_is_
+
+/**
  * Assign the given value to the named environment variable.
  *
  * If the name does not exist, then it is created.
@@ -117,7 +156,8 @@ extern "C" {
  *   F_data_not if name.used is 0.
  *
  *   F_memory_not (with error bit) on out of memory.
- *   F_valid_not (with error bit) if name is an invalid string.
+ *   F_parameter (with error bit) if name is an invalid string.
+ *
  *   F_failure (with error bit) on any other error.
  *
  * @see setenv()
@@ -139,7 +179,8 @@ extern "C" {
  *   F_data_not if name.used is 0.
  *
  *   F_memory_not (with error bit) on out of memory.
- *   F_valid_not (with error bit) if name is an invalid string.
+ *   F_parameter (with error bit) if name is an invalid string.
+ *
  *   F_failure (with error bit) on any other error.
  *
  * @see unsetenv()
index c6653172ef1e71eb1aff738b1cbad4300feeacd6..881dfb4f62a53346c9dbc7b589a05f6c7856a8a4 100644 (file)
@@ -1,2 +1,3 @@
 # fss-0000
 
+_di_libcap_ Disable libcap support, allow for compiling and linking without libcap (-lcap).
diff --git a/level_0/f_environment/data/build/dependencies-tests b/level_0/f_environment/data/build/dependencies-tests
new file mode 100644 (file)
index 0000000..dea3179
--- /dev/null
@@ -0,0 +1,3 @@
+# fss-0001
+
+cmocka 1.*
index f1b9c66fa65a500ab2f571536d0f7355b191317e..c3275f784b5c3fadd671ae6034ec8e01c1842569 100644 (file)
@@ -17,7 +17,7 @@ build_indexer ar
 build_indexer_arguments rcs
 build_language c
 
-build_libraries -lc
+build_libraries -lc -lcap
 build_libraries-individual -lf_memory -lf_string
 
 build_sources_library environment.c
@@ -46,6 +46,8 @@ search_exclusive yes
 search_shared yes
 search_static yes
 
+#defines -D_di_libcap_
+
 flags -O2 -z now -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses
 flags-clang -Wno-logical-op-parentheses
 flags-test -fstack-protector
diff --git a/level_0/f_environment/data/build/settings-mocks b/level_0/f_environment/data/build/settings-mocks
new file mode 100644 (file)
index 0000000..8e0956d
--- /dev/null
@@ -0,0 +1,75 @@
+# fss-0001
+#
+# Build the project with appropriate mocks linked in via the dynamic linker's "--wrap" functionality.
+#
+# The -Wl,--wrap does not work across shared files.
+# Therefore, this file is a work-around to inject the mocks into the library for testing purposes.
+# This should exactly match the "settings" file, except for the additional "-Wl,--wrap" parts and the additional mock source file.
+#
+# The flags -o0 must be passed to prevent the compiler from optimizing away any functions being mocked (which results in the mock not happening and a real function being called).
+# Alternatively, figure out which optimization that is disabled by -o0 and have that specific optimization disabled.
+#
+
+build_name f_environment
+
+version_major 0
+version_minor 5
+version_micro 9
+version_file micro
+version_target minor
+
+modes individual clang test
+modes_default individual
+
+build_compiler gcc
+build_compiler-clang clang
+build_indexer ar
+build_indexer_arguments rcs
+build_language c
+
+build_libraries -lc -lcap
+build_libraries-individual -lf_memory -lf_string
+
+build_sources_library environment.c ../../tests/unit/c/mock-environment.c
+
+build_sources_headers environment.h environment/common.h environment/type.h
+
+build_script yes
+build_shared yes
+build_static no
+
+path_headers fll/level_0
+path_library_script script
+path_library_shared shared
+path_library_static static
+
+has_path_standard yes
+preserve_path_headers yes
+
+search_exclusive yes
+search_shared yes
+search_static yes
+
+#defines -D_di_libcap_
+
+flags -O0 -z now -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses
+flags-clang -Wno-logical-op-parentheses
+flags-test -fstack-protector
+
+flags_library -fPIC
+
+# Inject mocks.
+flags -Wl,--wrap=clearenv
+flags -Wl,--wrap=getegid
+flags -Wl,--wrap=getenv
+flags -Wl,--wrap=geteuid
+flags -Wl,--wrap=getgid
+flags -Wl,--wrap=getuid
+flags -Wl,--wrap=setenv
+flags -Wl,--wrap=unsetenv
+
+# Disable these mock injections when using "-D_di_libcap_".
+flags -Wl,--wrap=cap_free
+flags -Wl,--wrap=cap_get_flag
+flags -Wl,--wrap=cap_get_proc
+flags -Wl,--wrap=cap_set_proc
diff --git a/level_0/f_environment/data/build/settings-tests b/level_0/f_environment/data/build/settings-tests
new file mode 100644 (file)
index 0000000..3428a36
--- /dev/null
@@ -0,0 +1,54 @@
+# fss-0001
+#
+# Builds a program that is links to the generated library and is executed to perform tests.
+#
+# Memory leaks in the test program can be checked for by running valgrind with this executable.
+#
+
+build_name test-f_environment
+
+version_major 0
+version_minor 5
+version_micro 9
+version_file major
+version_target major
+
+modes individual clang test
+modes_default individual
+
+build_compiler gcc
+build_compiler-clang clang
+build_indexer ar
+build_indexer_arguments rcs
+build_language c
+
+build_libraries -lc -lcmocka
+build_libraries-individual -lf_memory -lf_string -lf_environment
+
+build_sources_program test-environment-clear.c test-environment-exists.c test-environment-get.c test-environment-secure_is.c test-environment-set.c test-environment-unset.c
+build_sources_program test-environment.c
+
+build_script no
+build_shared yes
+build_static no
+
+path_headers tests/unit/c
+path_sources tests/unit/c
+
+has_path_standard no
+preserve_path_headers yes
+
+search_exclusive yes
+search_shared yes
+search_static yes
+
+#defines -D_di_libcap_
+defines -Ibuild/includes
+defines_static -Lbuild/libraries/static
+defines_shared -Lbuild/libraries/shared
+
+flags -O2 -z now -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses
+flags-clang -Wno-logical-op-parentheses
+flags-test -fstack-protector
+
+flags_program -fPIE
diff --git a/level_0/f_environment/data/build/testfile b/level_0/f_environment/data/build/testfile
new file mode 100644 (file)
index 0000000..f1f064b
--- /dev/null
@@ -0,0 +1,45 @@
+# fss-0005 iki-0002
+
+settings:
+  load_build yes
+  fail exit
+
+  environment LD_LIBRARY_PATH
+
+main:
+  build settings-mocks
+  build settings-tests
+
+  operate ld_library_path
+
+  if exists build/programs/shared/test-f_environment
+    shell build/programs/shared/test-f_environment
+
+  if exists build/programs/static/test-f_environment
+    shell build/programs/static/test-f_environment
+
+  if not exists build/programs/shared/test-f_environment
+  and not exists build/programs/static/test-f_environment
+    operate not_created
+
+not_created:
+  print
+  print 'context:"error"Failed to test due to being unable to find either a shared or static test binary to perform tests. context:"reset"'
+
+  exit failure
+
+ld_library_path:
+  if defined environment LD_LIBRARY_PATH
+  and defined parameter work
+    define LD_LIBRARY_PATH 'build/libraries/shared:parameter:"work:value"libraries/shared:define:"LD_LIBRARY_PATH"'
+
+  else
+  if defined environment LD_LIBRARY_PATH
+    define LD_LIBRARY_PATH 'build/libraries/shared:parameter:define:"LD_LIBRARY_PATH"'
+
+  else
+  if defined parameter work
+    define LD_LIBRARY_PATH 'build/libraries/shared:parameter:"work:value"libraries/shared'
+
+  else
+    define LD_LIBRARY_PATH build/libraries/shared
diff --git a/level_0/f_environment/tests/unit/c/mock-environment.c b/level_0/f_environment/tests/unit/c/mock-environment.c
new file mode 100644 (file)
index 0000000..889f668
--- /dev/null
@@ -0,0 +1,113 @@
+#include "mock-environment.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_libcap_
+  int __wrap_cap_free(void *obj_d) {
+
+    return mock_type(int);
+  }
+
+  int __wrap_cap_get_flag(cap_t cap_p, cap_value_t cap, cap_flag_t flag, cap_flag_value_t *value_p) {
+
+    const bool failure = mock_type(bool);
+
+    if (failure) {
+      errno = mock_type(int);
+
+      return -1;
+    }
+
+    *value_p = mock_type(cap_flag_value_t);
+
+    return mock_type(int);
+  }
+
+  cap_t __wrap_cap_get_proc(void) {
+
+    const bool failure = mock_type(bool);
+
+    if (failure) {
+      errno = mock_type(int);
+
+      return 0;
+    }
+
+    return mock_type(cap_t);
+  }
+
+  int __wrap_cap_set_proc(cap_t cap_p) {
+
+    const bool failure = mock_type(bool);
+
+    if (failure) {
+      errno = mock_type(int);
+
+      return -1;
+    }
+
+    return mock_type(int);
+  }
+#endif // _di_libcap_
+
+int __wrap_clearenv(void) {
+
+  return mock_type(int);
+}
+
+int __wrap_getegid(void) {
+
+  return mock_type(int);
+}
+
+char *__wrap_getenv(const char *name) {
+
+  return mock_type(char *);
+}
+
+int __wrap_geteuid(void) {
+
+  return mock_type(int);
+}
+
+int __wrap_getgid(void) {
+
+  return mock_type(int);
+}
+
+int __wrap_getuid(void) {
+
+  return mock_type(int);
+}
+
+int __wrap_setenv(const char *name, const char *value, int overwrite) {
+
+  const bool failure = mock_type(bool);
+
+  if (failure) {
+    errno = mock_type(int);
+
+    return -1;
+  }
+
+  return mock_type(int);
+}
+
+int __wrap_unsetenv(const char *name) {
+
+  const bool failure = mock_type(bool);
+
+  if (failure) {
+    errno = mock_type(int);
+
+    return -1;
+  }
+
+  return mock_type(int);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_environment/tests/unit/c/mock-environment.h b/level_0/f_environment/tests/unit/c/mock-environment.h
new file mode 100644 (file)
index 0000000..0350384
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Environment
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the environment project.
+ */
+#ifndef _MOCK__environment_h
+#define _MOCK__environment_h
+
+// Libc includes.
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdint.h>
+
+#ifndef _di_libcap_
+  #include <sys/capability.h>
+#endif // _di_libcap_
+
+// cmocka includes.
+#include <cmocka.h>
+
+// FLL-0 includes.
+#include <fll/level_0/environment.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const static int mock_errno_generic = 32767;
+
+#ifndef _di_libcap_
+  extern int __wrap_cap_free(void *obj_d);
+  extern int __wrap_cap_get_flag(cap_t cap_p, cap_value_t cap, cap_flag_t flag, cap_flag_value_t *value_p);
+  extern cap_t __wrap_cap_get_proc(void);
+  extern int __wrap_cap_set_proc(cap_t cap_p);
+#endif // _di_libcap_
+
+extern int __wrap_clearenv(void);
+extern int __wrap_getegid(void);
+extern char *__wrap_getenv(const char *name);
+extern int __wrap_geteuid(void);
+extern int __wrap_getgid(void);
+extern int __wrap_getuid(void);
+extern int __wrap_setenv(const char *name, const char *value, int overwrite);
+extern int __wrap_unsetenv(const char *name);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _MOCK__environment_h
diff --git a/level_0/f_environment/tests/unit/c/test-environment-clear.c b/level_0/f_environment/tests/unit/c/test-environment-clear.c
new file mode 100644 (file)
index 0000000..449d07a
--- /dev/null
@@ -0,0 +1,32 @@
+#include "test-environment.h"
+#include "test-environment-clear.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_environment_clear__fails(void **state) {
+
+  {
+    will_return(__wrap_clearenv, -1);
+
+    const f_status_t status = f_environment_clear();
+
+    assert_int_equal(F_status_set_fine(status), F_failure);
+  }
+}
+
+void test__f_environment_clear__works(void **state) {
+
+  {
+    will_return(__wrap_clearenv, 0);
+
+    const f_status_t status = f_environment_clear();
+
+    assert_int_equal(status, F_none);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_environment/tests/unit/c/test-environment-clear.h b/level_0/f_environment/tests/unit/c/test-environment-clear.h
new file mode 100644 (file)
index 0000000..f12ab71
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Environment
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the environment project.
+ */
+#ifndef _TEST__F_environment_clear_h
+#define _TEST__F_environment_clear_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_environment_clear()
+ */
+extern void test__f_environment_clear__fails(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_environment_clear()
+ */
+extern void test__f_environment_clear__works(void **state);
+
+#endif // _TEST__F_environment_clear_h
diff --git a/level_0/f_environment/tests/unit/c/test-environment-exists.c b/level_0/f_environment/tests/unit/c/test-environment-exists.c
new file mode 100644 (file)
index 0000000..1c37f9e
--- /dev/null
@@ -0,0 +1,45 @@
+#include "test-environment.h"
+#include "test-environment-exists.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_environment_exists__fails(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  {
+    will_return(__wrap_getenv, 0);
+
+    const f_status_t status = f_environment_exists(path);
+
+    assert_int_equal(status, F_false);
+  }
+}
+
+void test__f_environment_exists__returns_data_not(void **state) {
+
+  {
+    const f_status_t status = f_environment_exists(f_string_empty_s);
+
+    assert_int_equal(status, F_data_not);
+  }
+}
+
+void test__f_environment_exists__works(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  {
+    will_return(__wrap_getenv, path.string);
+
+    const f_status_t status = f_environment_exists(path);
+
+    assert_int_equal(status, F_true);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_environment/tests/unit/c/test-environment-exists.h b/level_0/f_environment/tests/unit/c/test-environment-exists.h
new file mode 100644 (file)
index 0000000..294558b
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Environment
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the environment project.
+ */
+#ifndef _TEST__F_environment_exists_h
+#define _TEST__F_environment_exists_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_environment_exists()
+ */
+extern void test__f_environment_exists__fails(void **state);
+
+/**
+ * Test that function works but the path is empty.
+ *
+ * @see f_environment_exists()
+ */
+extern void test__f_environment_exists__returns_data_not(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_environment_exists()
+ */
+extern void test__f_environment_exists__works(void **state);
+
+#endif // _TEST__F_environment_exists_h
diff --git a/level_0/f_environment/tests/unit/c/test-environment-get.c b/level_0/f_environment/tests/unit/c/test-environment-get.c
new file mode 100644 (file)
index 0000000..352b28b
--- /dev/null
@@ -0,0 +1,84 @@
+#include "test-environment.h"
+#include "test-environment-get.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_environment_get__fails(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  f_string_dynamic_t buffer = f_string_dynamic_t_initialize;
+
+  {
+    will_return(__wrap_getenv, 0);
+
+    const f_status_t status = f_environment_get(path, &buffer);
+
+    assert_int_equal(status, F_exist_not);
+  }
+
+  f_string_dynamic_resize(0, &buffer);
+}
+
+#ifndef _di_level_0_parameter_checking_
+  void test__f_environment_get__parameter_checking(void **state) {
+
+    const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+    {
+      const f_status_t status = f_environment_get(path, 0);
+
+      assert_int_equal(F_status_set_fine(status), F_parameter);
+    }
+  }
+#endif // _di_level_0_parameter_checking_
+
+void test__f_environment_get__returns_data_not(void **state) {
+
+  f_string_dynamic_t buffer = f_string_dynamic_t_initialize;
+
+  {
+    const f_status_t status = f_environment_get(f_string_empty_s, &buffer);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  f_string_dynamic_resize(0, &buffer);
+}
+
+void test__f_environment_get__works(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  f_string_dynamic_t buffer = f_string_dynamic_t_initialize;
+
+  {
+    will_return(__wrap_getenv, path.string);
+
+    const f_status_t status = f_environment_get(path, &buffer);
+
+    assert_int_equal(status, F_none);
+    assert_int_equal(buffer.used, path.used);
+    assert_string_equal(buffer.string, path.string);
+  }
+
+  buffer.used = 0;
+
+  {
+    will_return(__wrap_getenv, f_string_empty_s.string);
+
+    const f_status_t status = f_environment_get(path, &buffer);
+
+    assert_int_equal(status, F_none);
+    assert_int_equal(buffer.used, f_string_empty_s.used);
+    assert_string_equal(buffer.string, f_string_empty_s.string);
+  }
+
+  f_string_dynamic_resize(0, &buffer);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_environment/tests/unit/c/test-environment-get.h b/level_0/f_environment/tests/unit/c/test-environment-get.h
new file mode 100644 (file)
index 0000000..32edb8e
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Environment
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the environment project.
+ */
+#ifndef _TEST__F_environment_get_h
+#define _TEST__F_environment_get_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_environment_get()
+ */
+extern void test__f_environment_get__fails(void **state);
+
+/**
+ * Test that parameter checking works as expected.
+ *
+ * @see f_environment_get()
+ */
+#ifndef _di_level_0_parameter_checking_
+  extern void test__f_environment_get__parameter_checking(void **state);
+#endif // _di_level_0_parameter_checking_
+
+/**
+ * Test that function works but the path is empty.
+ *
+ * @see f_environment_get()
+ */
+extern void test__f_environment_get__returns_data_not(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_environment_get()
+ */
+extern void test__f_environment_get__works(void **state);
+
+#endif // _TEST__F_environment_get_h
diff --git a/level_0/f_environment/tests/unit/c/test-environment-secure_is.c b/level_0/f_environment/tests/unit/c/test-environment-secure_is.c
new file mode 100644 (file)
index 0000000..80e78b3
--- /dev/null
@@ -0,0 +1,169 @@
+#include "test-environment.h"
+#include "test-environment-secure_is.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_environment_secure_is__fails(void **state) {
+
+  #ifndef _di_libcap_
+    {
+      int errnos[] = {
+        EINVAL,
+        EPERM,
+        ENOMEM,
+        mock_errno_generic,
+      };
+
+      f_status_t statuss[] = {
+        F_parameter,
+        F_prohibited,
+        F_memory_not,
+        F_failure,
+      };
+
+      for (int i = 0; i < 4; ++i) {
+
+        will_return(__wrap_geteuid, 1);
+        will_return(__wrap_getuid, 2);
+
+        will_return(__wrap_cap_get_proc, true);
+        will_return(__wrap_cap_get_proc, errnos[i]);
+
+        const f_status_t status = f_environment_secure_is();
+
+        assert_int_equal(F_status_set_fine(status), statuss[i]);
+      } // for
+    }
+
+    {
+      int errnos[] = {
+        EINVAL,
+        mock_errno_generic,
+      };
+
+      f_status_t statuss[] = {
+        F_parameter,
+        F_failure,
+      };
+
+      for (int i = 0; i < 2; ++i) {
+
+        long stub = 0;
+        cap_t capability = (cap_t) &stub;
+
+        will_return(__wrap_geteuid, 1);
+        will_return(__wrap_getuid, 1);
+
+        will_return(__wrap_getegid, 3);
+        will_return(__wrap_getgid, 4);
+
+        will_return(__wrap_cap_get_proc, false);
+        will_return(__wrap_cap_get_proc, capability);
+
+        will_return(__wrap_cap_get_flag, true);
+        will_return(__wrap_cap_get_flag, errnos[i]);
+
+        will_return(__wrap_cap_free, 0);
+
+        const f_status_t status = f_environment_secure_is();
+
+        assert_int_equal(F_status_set_fine(status), statuss[i]);
+      } // for
+    }
+  #endif // _di_libcap_
+}
+
+void test__f_environment_secure_is__works(void **state) {
+
+  {
+    will_return(__wrap_geteuid, 1);
+    will_return(__wrap_getuid, 1);
+
+    will_return(__wrap_getegid, 1);
+    will_return(__wrap_getgid, 1);
+
+    const f_status_t status = f_environment_secure_is();
+
+    assert_int_equal(status, F_true);
+  }
+
+  {
+    #ifndef _di_libcap_
+      long stub = 0;
+      cap_t capability = (cap_t) &stub;
+    #endif // _di_libcap_
+
+    will_return(__wrap_geteuid, 1);
+    will_return(__wrap_getuid, 0);
+
+    #ifndef _di_libcap_
+      will_return(__wrap_cap_get_proc, false);
+      will_return(__wrap_cap_get_proc, capability);
+
+      will_return(__wrap_cap_get_flag, false);
+      will_return(__wrap_cap_get_flag, CAP_CLEAR);
+      will_return(__wrap_cap_get_flag, 0);
+
+      will_return(__wrap_cap_free, 0);
+    #endif // _di_libcap_
+
+    const f_status_t status = f_environment_secure_is();
+
+    assert_int_equal(status, F_false);
+  }
+
+  #ifndef _di_libcap_
+    {
+      long stub = 0;
+      cap_t capability = (cap_t) &stub;
+
+      will_return(__wrap_geteuid, 1);
+      will_return(__wrap_getuid, 1);
+
+      will_return(__wrap_getegid, 1);
+      will_return(__wrap_getgid, 2);
+
+      will_return(__wrap_cap_get_proc, false);
+      will_return(__wrap_cap_get_proc, capability);
+
+      will_return(__wrap_cap_get_flag, false);
+      will_return(__wrap_cap_get_flag, CAP_CLEAR);
+      will_return(__wrap_cap_get_flag, 0);
+
+      will_return(__wrap_cap_free, 0);
+
+      const f_status_t status = f_environment_secure_is();
+
+      assert_int_equal(status, F_false);
+    }
+  #endif // _di_libcap_
+
+  #ifndef _di_libcap_
+    {
+      long stub = 0;
+      cap_t capability = (cap_t) &stub;;
+
+      will_return(__wrap_geteuid, 1);
+      will_return(__wrap_getuid, 2);
+
+      will_return(__wrap_cap_get_proc, false);
+      will_return(__wrap_cap_get_proc, capability);
+
+      will_return(__wrap_cap_get_flag, false);
+      will_return(__wrap_cap_get_flag, CAP_SET);
+      will_return(__wrap_cap_get_flag, 0);
+
+      will_return(__wrap_cap_free, 0);
+
+      const f_status_t status = f_environment_secure_is();
+
+      assert_int_equal(status, F_true);
+    }
+  #endif // _di_libcap_
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_environment/tests/unit/c/test-environment-secure_is.h b/level_0/f_environment/tests/unit/c/test-environment-secure_is.h
new file mode 100644 (file)
index 0000000..ceb7ab5
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Environment
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the environment project.
+ */
+#ifndef _TEST__F_environment_secure_is_h
+#define _TEST__F_environment_secure_is_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_environment_secure_is()
+ */
+extern void test__f_environment_secure_is__fails(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_environment_secure_is()
+ */
+extern void test__f_environment_secure_is__works(void **state);
+
+#endif // _TEST__F_environment_secure_is_h
diff --git a/level_0/f_environment/tests/unit/c/test-environment-set.c b/level_0/f_environment/tests/unit/c/test-environment-set.c
new file mode 100644 (file)
index 0000000..6d15726
--- /dev/null
@@ -0,0 +1,70 @@
+#include "test-environment.h"
+#include "test-environment-set.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_environment_set__fails(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  {
+    int errnos[] = {
+      EINVAL,
+      ENOMEM,
+      mock_errno_generic,
+    };
+
+    f_status_t statuss[] = {
+      F_parameter,
+      F_memory_not,
+      F_failure,
+    };
+
+    for (int i = 0; i < 3; ++i) {
+
+      will_return(__wrap_setenv, true);
+      will_return(__wrap_setenv, errnos[i]);
+
+      const f_status_t status = f_environment_set(path, path, F_false);
+
+      assert_int_equal(F_status_set_fine(status), statuss[i]);
+    } // for
+  }
+}
+
+void test__f_environment_set__returns_data_not(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  {
+    const f_status_t status = f_environment_set(f_string_empty_s, f_string_empty_s, F_false);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_environment_set(f_string_empty_s, path, F_false);
+
+    assert_int_equal(status, F_data_not);
+  }
+}
+
+void test__f_environment_set__works(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  {
+    will_return(__wrap_setenv, false);
+    will_return(__wrap_setenv, 0);
+
+    const f_status_t status = f_environment_set(path, path, F_false);
+
+    assert_int_equal(status, F_none);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_environment/tests/unit/c/test-environment-set.h b/level_0/f_environment/tests/unit/c/test-environment-set.h
new file mode 100644 (file)
index 0000000..f45a3ff
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Environment
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the environment project.
+ */
+#ifndef _TEST__F_environment_set_h
+#define _TEST__F_environment_set_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_environment_set()
+ */
+extern void test__f_environment_set__fails(void **state);
+
+/**
+ * Test that function works but the path is empty.
+ *
+ * @see f_environment_set()
+ */
+extern void test__f_environment_set__returns_data_not(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_environment_set()
+ */
+extern void test__f_environment_set__works(void **state);
+
+#endif // _TEST__F_environment_set_h
diff --git a/level_0/f_environment/tests/unit/c/test-environment-unset.c b/level_0/f_environment/tests/unit/c/test-environment-unset.c
new file mode 100644 (file)
index 0000000..2c630e2
--- /dev/null
@@ -0,0 +1,62 @@
+#include "test-environment.h"
+#include "test-environment-unset.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_environment_unset__fails(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  {
+    int errnos[] = {
+      EINVAL,
+      ENOMEM,
+      mock_errno_generic,
+    };
+
+    f_status_t statuss[] = {
+      F_parameter,
+      F_memory_not,
+      F_failure,
+    };
+
+    for (int i = 0; i < 3; ++i) {
+
+      will_return(__wrap_unsetenv, true);
+      will_return(__wrap_unsetenv, errnos[i]);
+
+      const f_status_t status = f_environment_unset(path);
+
+      assert_int_equal(F_status_set_fine(status), statuss[i]);
+    } // for
+  }
+}
+
+void test__f_environment_unset__returns_data_not(void **state) {
+
+  {
+    const f_status_t status = f_environment_unset(f_string_empty_s);
+
+    assert_int_equal(status, F_data_not);
+  }
+}
+
+void test__f_environment_unset__works(void **state) {
+
+  const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4);
+
+  {
+    will_return(__wrap_unsetenv, false);
+    will_return(__wrap_unsetenv, 0);
+
+    const f_status_t status = f_environment_unset(path);
+
+    assert_int_equal(status, F_none);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_environment/tests/unit/c/test-environment-unset.h b/level_0/f_environment/tests/unit/c/test-environment-unset.h
new file mode 100644 (file)
index 0000000..923f3e8
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Environment
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the environment project.
+ */
+#ifndef _TEST__F_environment_unset_h
+#define _TEST__F_environment_unset_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_environment_unset()
+ */
+extern void test__f_environment_unset__fails(void **state);
+
+/**
+ * Test that function works but the path is empty.
+ *
+ * @see f_environment_unset()
+ */
+extern void test__f_environment_unset__returns_data_not(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_environment_unset()
+ */
+extern void test__f_environment_unset__works(void **state);
+
+#endif // _TEST__F_environment_unset_h
diff --git a/level_0/f_environment/tests/unit/c/test-environment.c b/level_0/f_environment/tests/unit/c/test-environment.c
new file mode 100644 (file)
index 0000000..8096ba7
--- /dev/null
@@ -0,0 +1,59 @@
+#include "test-environment.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int setup(void **state) {
+
+  return 0;
+}
+
+int setdown(void **state) {
+
+  errno = 0;
+
+  return 0;
+}
+
+int main(void) {
+
+  const struct CMUnitTest tests[] = {
+    cmocka_unit_test(test__f_environment_clear__fails),
+    cmocka_unit_test(test__f_environment_clear__works),
+
+    cmocka_unit_test(test__f_environment_exists__fails),
+    cmocka_unit_test(test__f_environment_exists__returns_data_not),
+    cmocka_unit_test(test__f_environment_exists__works),
+
+    cmocka_unit_test(test__f_environment_get__fails),
+    cmocka_unit_test(test__f_environment_get__returns_data_not),
+    cmocka_unit_test(test__f_environment_get__works),
+
+    cmocka_unit_test(test__f_environment_secure_is__fails),
+    cmocka_unit_test(test__f_environment_secure_is__works),
+
+    cmocka_unit_test(test__f_environment_set__fails),
+    cmocka_unit_test(test__f_environment_set__returns_data_not),
+    cmocka_unit_test(test__f_environment_set__works),
+
+    cmocka_unit_test(test__f_environment_unset__fails),
+    cmocka_unit_test(test__f_environment_unset__returns_data_not),
+    cmocka_unit_test(test__f_environment_unset__works),
+
+    #ifndef _di_level_0_parameter_checking_
+      // f_environment_clear() doesn't use parameter checking.
+      // f_environment_exists() doesn't use parameter checking.
+      cmocka_unit_test(test__f_environment_get__parameter_checking),
+      // f_environment_secure_is() doesn't use parameter checking.
+      // f_environment_set() doesn't use parameter checking.
+      // f_environment_unset() doesn't use parameter checking.
+    #endif // _di_level_0_parameter_checking_
+  };
+
+  return cmocka_run_group_tests(tests, setup, setdown);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_environment/tests/unit/c/test-environment.h b/level_0/f_environment/tests/unit/c/test-environment.h
new file mode 100644 (file)
index 0000000..a10e751
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Environment
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the environment project.
+ */
+#ifndef _TEST__F_environment_h
+#define _TEST__F_environment_h
+
+// Libc includes.
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdint.h>
+
+// cmocka includes.
+#include <cmocka.h>
+
+// FLL-0 includes.
+#include <fll/level_0/environment.h>
+
+// Mock includes.
+#include "mock-environment.h"
+
+// Test includes.
+#include "test-environment-clear.h"
+#include "test-environment-exists.h"
+#include "test-environment-get.h"
+#include "test-environment-secure_is.h"
+#include "test-environment-set.h"
+#include "test-environment-unset.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Perform any setup operations.
+ *
+ * @param state
+ *   The test state.
+ *
+ * @return
+ *   The status of this function, where 0 means success.
+ */
+extern int setup(void **state);
+
+/**
+ * Peform any setdown operations.
+ *
+ * @param state
+ *   The test state.
+ *
+ * @return
+ *   The status of this function, where 0 means success.
+ */
+extern int setdown(void **state);
+
+/**
+ * Run all tests.
+ *
+ * @return
+ *   The final result of the tests.
+ *
+ * @see cmocka_run_group_tests()
+ * @see cmocka_unit_test()
+ */
+extern int main(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _TEST__F_environment_h