From a230da6ccf61de56e16ac57fd4d3900ffd4f8c76 Mon Sep 17 00:00:00 2001 From: Adrian Szyndela Date: Wed, 8 Jan 2020 16:38:21 +0100 Subject: [PATCH] tests: add lowmem-system tests Change-Id: I2111d33c92d5dd5588685ee95373a8d53e015155 --- tests/CMakeLists.txt | 4 ++ tests/lowmem-system-env.cpp | 113 +++++++++++++++++++++++++++++++++++ tests/lowmem-system-env.hpp | 38 ++++++++++++ tests/lowmem-system-mock.cpp | 31 ++++++++++ tests/lowmem-system-test.cpp | 139 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 325 insertions(+) create mode 100644 tests/lowmem-system-env.cpp create mode 100644 tests/lowmem-system-env.hpp create mode 100644 tests/lowmem-system-mock.cpp create mode 100644 tests/lowmem-system-test.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f511742..fc4294e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 index 0000000..84591cd --- /dev/null +++ b/tests/lowmem-system-env.cpp @@ -0,0 +1,113 @@ +#include "lowmem-system-env.hpp" + +#include "cmocka_wrap.hpp" + +#include "resourced.h" + +#include +#include + +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(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(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 LowmemSystemEnv::opendir(const char *name) +{ + std::cerr << " name=" << name << std::endl; + return std::make_pair(true, reinterpret_cast(1)); +} + +std::pair LowmemSystemEnv::closedir(DIR *dirp) +{ + return std::make_pair(true, 0); +} + +std::pair 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 index 0000000..05bab72 --- /dev/null +++ b/tests/lowmem-system-env.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "lowmem-env.hpp" + +#include +#include + +#include + +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 opendir(const char *name); + std::pair closedir(DIR *dirp); + std::pair 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 index 0000000..182ded0 --- /dev/null +++ b/tests/lowmem-system-mock.cpp @@ -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 index 0000000..95a7a67 --- /dev/null +++ b/tests/lowmem-system-test.cpp @@ -0,0 +1,139 @@ +#include "lowmem-system-env.hpp" +#include "cmocka_wrap.hpp" + +extern "C" { +#include "lowmem-handler.h" +#include "memory-common.h" +} + +#include + +#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 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); +} -- 2.7.4