tests: add lowmem-system tests 76/221976/2
authorAdrian Szyndela <adrian.s@samsung.com>
Wed, 8 Jan 2020 15:38:21 +0000 (16:38 +0100)
committerAdrian Szyndela <adrian.s@samsung.com>
Fri, 10 Jan 2020 15:15:55 +0000 (16:15 +0100)
Change-Id: I2111d33c92d5dd5588685ee95373a8d53e015155

tests/CMakeLists.txt
tests/lowmem-system-env.cpp [new file with mode: 0644]
tests/lowmem-system-env.hpp [new file with mode: 0644]
tests/lowmem-system-mock.cpp [new file with mode: 0644]
tests/lowmem-system-test.cpp [new file with mode: 0644]

index f511742..fc4294e 100644 (file)
@@ -72,6 +72,10 @@ ADD_MEMORY_TESTS(lowmem-limit-test "glib-2.0"
                lowmem-limit-test.cpp lowmem-limit-mock.cpp lowmem-limit-env.cpp lowmem-env.cpp lowmem-env-mock.cpp
                ../src/memory/lowmem-limit.c)
 # lowmem-system unit test
+ADD_MEMORY_TESTS(lowmem-system-test ""
+                      "-Wl,--wrap=opendir,--wrap=readdir,--wrap=closedir"
+               lowmem-system-test.cpp lowmem-system-mock.cpp lowmem-system-env.cpp lowmem-env.cpp lowmem-env-mock.cpp
+                          ../src/memory/lowmem-system.c)
 
 INSTALL(TARGETS watchdog-test
   DESTINATION ${RD_TESTS_PATH})
diff --git a/tests/lowmem-system-env.cpp b/tests/lowmem-system-env.cpp
new file mode 100644 (file)
index 0000000..84591cd
--- /dev/null
@@ -0,0 +1,113 @@
+#include "lowmem-system-env.hpp"
+
+#include "cmocka_wrap.hpp"
+
+#include "resourced.h"
+
+#include <iostream>
+#include <cstring>
+
+LowmemSystemEnv *global_test_lowmem_system_env{nullptr};
+
+LowmemSystemEnv::LowmemSystemEnv()
+{
+       assert_null(global_test_lowmem_system_env);
+       global_test_lowmem_system_env = this;
+       local_dirent.d_type = DT_DIR;
+}
+
+LowmemSystemEnv::~LowmemSystemEnv()
+{
+       assert_non_null(global_test_lowmem_system_env);
+       global_test_lowmem_system_env = nullptr;
+}
+
+void LowmemSystemEnv::lowmem_reassign_limit(const char *dir, unsigned int limit)
+{
+       check_expected(dir);
+       check_expected(limit);
+}
+
+int LowmemSystemEnv::cgroup_read_node_int32(const char *cgroup_name, const char *file_name, int32_t *value)
+{
+       std::cerr << "  cgroup_name=" << cgroup_name << ", file_name=" << file_name << std::endl;
+       check_expected(cgroup_name);
+       check_expected(file_name);
+       *value = static_cast<int32_t>(mock());
+       std::cerr << "  -> " << *value << std::endl;
+       return RESOURCED_ERROR_NONE;
+}
+
+int LowmemSystemEnv::cgroup_write_node_uint32(const char *cgroup_name, const char *file_name, uint32_t value)
+{
+       std::cerr << "  cgroup_name=" << cgroup_name << ", file_name=" << file_name;
+       std::cerr << ", value=" << value << std::endl;
+       check_expected(cgroup_name);
+       check_expected(file_name);
+       check_expected(value);
+       return RESOURCED_ERROR_NONE;
+}
+
+int LowmemSystemEnv::cgroup_read_node_uint32(const char *cgroup_name, const char *file_name, uint32_t *value)
+{
+       std::cerr << "  cgroup_name=" << cgroup_name << ", file_name=" << file_name << std::endl;
+       check_expected(cgroup_name);
+       check_expected(file_name);
+       *value = static_cast<uint32_t>(mock());
+       std::cerr << "  -> " << *value << std::endl;
+       return RESOURCED_ERROR_NONE;
+}
+
+void LowmemSystemEnv::g_source_destroy(GSource *source)
+{
+       delete source;
+}
+
+GSource *LowmemSystemEnv::g_timeout_source_new_seconds(guint interval)
+{
+       return new GSource;
+}
+
+void LowmemSystemEnv::g_source_set_callback(GSource *source, GSourceFunc fun, gpointer data, GDestroyNotify notify)
+{
+       timeout_callback = fun;
+       assert_null(notify);
+}
+
+guint LowmemSystemEnv::g_source_attach(GSource *source, GMainContext *context)
+{
+       return 1;
+}
+
+void LowmemSystemEnv::trigger_booting_done()
+{
+       lowmem_env.trigger_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, nullptr);
+}
+
+void LowmemSystemEnv::trigger_timeout()
+{
+       assert_non_null(timeout_callback);
+       timeout_callback(NULL);
+}
+
+std::pair<bool,DIR *> LowmemSystemEnv::opendir(const char *name)
+{
+       std::cerr << "  name=" << name << std::endl;
+       return std::make_pair(true, reinterpret_cast<DIR*>(1));
+}
+
+std::pair<bool,int> LowmemSystemEnv::closedir(DIR *dirp)
+{
+       return std::make_pair(true, 0);
+}
+
+std::pair<bool,struct dirent *> LowmemSystemEnv::readdir(DIR *dirp)
+{
+       const char *dirname = mock_ptr_type(const char *);
+       if (dirname) {
+               strncpy(local_dirent.d_name, dirname, sizeof(local_dirent.d_name));
+       } else {
+               local_dirent.d_name[0] = 0;
+       }
+       return std::make_pair(true, dirname ? &local_dirent : nullptr);
+}
diff --git a/tests/lowmem-system-env.hpp b/tests/lowmem-system-env.hpp
new file mode 100644 (file)
index 0000000..05bab72
--- /dev/null
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "lowmem-env.hpp"
+
+#include <glib.h>
+#include <dirent.h>
+
+#include <utility>
+
+class LowmemSystemEnv {
+public:
+       LowmemSystemEnv();
+       ~LowmemSystemEnv();
+
+       int cgroup_read_node_int32(const char *cgroup_name, const char *file_name, int32_t *value);
+       int cgroup_write_node_uint32(const char *cgroup_name, const char *file_name, uint32_t value);
+       int cgroup_read_node_uint32(const char *cgroup_name, const char *file_name, uint32_t *value);
+
+       void g_source_destroy(GSource *source);
+       GSource *g_timeout_source_new_seconds(guint interval);
+       void g_source_set_callback(GSource *source, GSourceFunc fun, gpointer data, GDestroyNotify notify);
+       guint g_source_attach(GSource *source, GMainContext *context);
+
+       void lowmem_reassign_limit(const char *dir, unsigned int limit);
+
+       void trigger_booting_done();
+       void trigger_timeout();
+
+       std::pair<bool,DIR *> opendir(const char *name);
+       std::pair<bool,int> closedir(DIR *dirp);
+       std::pair<bool,struct dirent *> readdir(DIR *dirp);
+private:
+       LowmemEnv lowmem_env;
+       GSourceFunc timeout_callback{nullptr};
+       dirent local_dirent;
+};
+
+extern LowmemSystemEnv *global_test_lowmem_system_env;
diff --git a/tests/lowmem-system-mock.cpp b/tests/lowmem-system-mock.cpp
new file mode 100644 (file)
index 0000000..182ded0
--- /dev/null
@@ -0,0 +1,31 @@
+#include "lowmem-mock.hpp"
+#include "lowmem-system-env.hpp"
+
+#define MOCK_SYSTEM(rettype, name, def_args, call_args) \
+       MOCK(global_test_lowmem_system_env, rettype, name, def_args, call_args)
+
+MOCK_SYSTEM(void, lowmem_reassign_limit, (const char *dir, unsigned int limit), (dir, limit))
+MOCK_SYSTEM(int, cgroup_read_node_int32,
+               (const char *cgroup_name, const char *file_name, int32_t *value),
+               (cgroup_name, file_name, value))
+MOCK_SYSTEM(int, cgroup_write_node_uint32,
+               (const char *cgroup_name, const char *file_name, uint32_t value),
+               (cgroup_name, file_name, value))
+MOCK_SYSTEM(int, cgroup_read_node_uint32,
+               (const char *cgroup_name, const char *file_name, uint32_t *value),
+               (cgroup_name, file_name, value))
+
+MOCK_SYSTEM(void, g_source_destroy, (GSource *source), (source))
+MOCK_SYSTEM(GSource *, g_timeout_source_new_seconds, (guint interval), (interval))
+MOCK_SYSTEM(void, g_source_set_callback,
+               (GSource *source, GSourceFunc fun, gpointer data, GDestroyNotify notify),
+               (source, fun, data, notify))
+MOCK_SYSTEM(guint, g_source_attach, (GSource *source, GMainContext *context), (source, context))
+
+#define WRAP_SYSTEM(rettype, name, def_args, call_args) \
+       WRAP(global_test_lowmem_system_env, rettype, name, def_args, call_args)
+
+WRAP_SYSTEM(DIR *, opendir, (const char *name), (name))
+WRAP_SYSTEM(int, closedir, (DIR *dirp), (dirp))
+WRAP_SYSTEM(struct dirent *, readdir, (DIR *dirp), (dirp))
+
diff --git a/tests/lowmem-system-test.cpp b/tests/lowmem-system-test.cpp
new file mode 100644 (file)
index 0000000..95a7a67
--- /dev/null
@@ -0,0 +1,139 @@
+#include "lowmem-system-env.hpp"
+#include "cmocka_wrap.hpp"
+
+extern "C" {
+#include "lowmem-handler.h"
+#include "memory-common.h"
+}
+
+#include <vector>
+
+#define MEMORY_SYSTEMD_SYSTEM  "/sys/fs/cgroup/memory/system.slice"
+#define MEMORY_SYSTEMD_USER    "/sys/fs/cgroup/memory/user.slice"
+
+class Subdir {
+public:
+       Subdir(const char *subdir, const char *dir, uint32_t l)
+               : subdir_name{subdir}, full_name{std::string{dir}+"/"+subdir}, limit{l} {}
+
+       const char *get_subdir() const { return subdir_name.c_str(); }
+       const char *get_subdir_full() const { return full_name.c_str(); }
+       uint32_t get_limit() const { return limit; }
+
+private:
+       std::string subdir_name;
+       std::string full_name;
+       uint32_t limit;
+};
+
+typedef std::vector<Subdir> Subdirs;
+
+void mock_cgroup_read_node_int32(const char *cgroup_name, const char *file_name, int32_t value)
+{
+       std::cerr << "mocking cgroup read: " << cgroup_name << " " << file_name << " " << value << std::endl;
+       expect_string(cgroup_read_node_int32, cgroup_name, cgroup_name);
+       expect_string(cgroup_read_node_int32, file_name, file_name);
+       will_return(cgroup_read_node_int32, value);
+}
+
+void mock_cgroup_read_node_uint32(const char *cgroup_name, const char *file_name, uint32_t value)
+{
+       std::cerr << "mocking cgroup read: " << cgroup_name << " " << file_name << " " << value << std::endl;
+       expect_string(cgroup_read_node_uint32, cgroup_name, cgroup_name);
+       expect_string(cgroup_read_node_uint32, file_name, file_name);
+       will_return(cgroup_read_node_uint32, value);
+}
+
+void expect_cgroup_write_node_uint32(const char *cgroup_name, const char *file_name, uint32_t value)
+{
+       std::cerr << "expecting cgroup write: " << cgroup_name << " " << file_name << " " << value << std::endl;
+       expect_string(cgroup_write_node_uint32, cgroup_name, cgroup_name);
+       expect_string(cgroup_write_node_uint32, file_name, file_name);
+       expect_value(cgroup_write_node_uint32, value, value);
+}
+
+void mock_search_systemd_cgroup(const char *dir, int32_t root_swappiness, int32_t dir_swappiness,
+               const Subdirs &subdirs = Subdirs())
+{
+       mock_cgroup_read_node_int32(LOWMEM_ROOT_CGROUP, MEMCG_SWAPNESS_PATH, root_swappiness);
+       mock_cgroup_read_node_int32(dir, MEMCG_SWAPNESS_PATH, dir_swappiness);
+
+       if (root_swappiness >= 0 && root_swappiness != dir_swappiness) {
+               expect_cgroup_write_node_uint32(dir, MEMCG_SWAPNESS_PATH, root_swappiness);
+       }
+
+       // subdirs
+       // LINE: FOREACH_DIRENT(de, d, return -errno)
+       for (const auto &subdir: subdirs) {
+               // mock subdir
+               will_return(readdir, subdir.get_subdir());
+
+               mock_cgroup_read_node_uint32(subdir.get_subdir_full(), MEMCG_LIMIT_PATH, subdir.get_limit());
+               if (subdir.get_limit() > 0) {
+                       expect_cgroup_write_node_uint32(subdir.get_subdir_full(), MEMCG_SWAPNESS_PATH, root_swappiness);
+
+                       expect_string(lowmem_reassign_limit, dir, subdir.get_subdir_full());
+                       expect_value(lowmem_reassign_limit, limit, subdir.get_limit());
+               }
+       }
+
+       // finish subdirs
+       will_return(readdir, nullptr);
+}
+
+void simple_system_test(void **)
+{
+       LowmemSystemEnv env;
+
+       lowmem_system_init();
+       env.trigger_booting_done();
+
+       const int32_t SWAPPINESS_NO_CHANGE = 1000;
+       mock_search_systemd_cgroup(MEMORY_SYSTEMD_SYSTEM, SWAPPINESS_NO_CHANGE, SWAPPINESS_NO_CHANGE);
+
+       const int32_t SWAPPINESS_CHANGE_TO = 0;
+       mock_search_systemd_cgroup(MEMORY_SYSTEMD_USER, SWAPPINESS_CHANGE_TO, 100000);
+
+       env.trigger_timeout();
+       lowmem_system_exit();
+}
+
+void simple_system_with_subdirs_test(void **)
+{
+       LowmemSystemEnv env;
+
+       lowmem_system_init();
+       env.trigger_booting_done();
+
+       const int32_t SWAPPINESS_CHANGE_TO = 1000;
+       const Subdirs subdirs_system{
+               {"my.sweet.dir", MEMORY_SYSTEMD_SYSTEM, 0},
+               {"my.sweeter.dir", MEMORY_SYSTEMD_SYSTEM, SWAPPINESS_CHANGE_TO},
+               {"my sweetest.dir", MEMORY_SYSTEMD_SYSTEM, SWAPPINESS_CHANGE_TO*SWAPPINESS_CHANGE_TO}
+       };
+
+       mock_search_systemd_cgroup(MEMORY_SYSTEMD_SYSTEM,
+                       SWAPPINESS_CHANGE_TO, SWAPPINESS_CHANGE_TO*SWAPPINESS_CHANGE_TO,
+                       subdirs_system);
+
+       const Subdirs subdirs_user{
+               {"my.user.dir", MEMORY_SYSTEMD_USER, 0},
+               {"my.userer.dir", MEMORY_SYSTEMD_USER, SWAPPINESS_CHANGE_TO},
+               {"my userest.dir", MEMORY_SYSTEMD_USER, SWAPPINESS_CHANGE_TO*SWAPPINESS_CHANGE_TO}
+       };
+       mock_search_systemd_cgroup(MEMORY_SYSTEMD_USER,
+                       SWAPPINESS_CHANGE_TO, SWAPPINESS_CHANGE_TO-SWAPPINESS_CHANGE_TO,
+                       subdirs_user);
+
+       env.trigger_timeout();
+       lowmem_system_exit();
+}
+
+int main(void) {
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(simple_system_test),
+               cmocka_unit_test(simple_system_with_subdirs_test),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}