From 2da9736210f0d3524948acf12c40d74152e5959d Mon Sep 17 00:00:00 2001
From: Andreas Gattringer <andreas.gattringer@univie.ac.at>
Date: Wed, 11 Jan 2023 21:37:52 +0100
Subject: [PATCH] Re-added unit tests

---
 CMakeLists.txt                     |   6 +-
 src/cats/debug/debug_vital_rates.h |   3 +-
 src/tests/CMakeLists.txt           |  13 ++
 src/tests/csv_test.c               | 108 +++++++++++++
 src/tests/test.csv                 |   3 +
 src/tests/vital_rate_test.c        | 248 +++++++++++++++++++++++++++++
 6 files changed, 377 insertions(+), 4 deletions(-)
 create mode 100644 src/tests/CMakeLists.txt
 create mode 100644 src/tests/csv_test.c
 create mode 100644 src/tests/test.csv
 create mode 100644 src/tests/vital_rate_test.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a52693a..2993a79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -73,6 +73,6 @@ include_directories("src")
 include_directories("src/cats")
 add_subdirectory(src/)
 
-#if(BUILD_TESTING)
-#    add_subdirectory(src/tests)
-#endif()
\ No newline at end of file
+if(BUILD_TESTING)
+    add_subdirectory(src/tests)
+endif()
\ No newline at end of file
diff --git a/src/cats/debug/debug_vital_rates.h b/src/cats/debug/debug_vital_rates.h
index 5802c86..70184fc 100644
--- a/src/cats/debug/debug_vital_rates.h
+++ b/src/cats/debug/debug_vital_rates.h
@@ -26,5 +26,6 @@
 #define CATS_DEBUG_VITAL_RATES_H
 #include "configuration/configuration.h"
 void debug_vital_rates(struct cats_configuration *conf);
-
+struct cats_grid *minimal_grid(struct cats_configuration *conf, struct cats_environment *env);
+struct cats_environment *minimal_suitability_environment(void);
 #endif //CATS_DEBUG_VITAL_RATES_H
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
new file mode 100644
index 0000000..7e0e75d
--- /dev/null
+++ b/src/tests/CMakeLists.txt
@@ -0,0 +1,13 @@
+find_package(CMOCKA REQUIRED)
+include_directories(${LIBCMOCKA_INCLUDE_DIR})
+
+
+add_test(NAME vital_rate_test COMMAND vital_rate_test)
+add_test(NAME csv_test COMMAND csv_test)
+
+add_executable(vital_rate_test vital_rate_test.c)
+add_executable(csv_test csv_test.c )
+configure_file("test.csv" "test.csv")
+
+target_link_libraries(vital_rate_test cmocka cats_logging cats_memory cats_strings cats_ini cats_csv libcats)
+target_link_libraries(csv_test cmocka cats_strings cats_csv cats_strings cats_logging cats_memory)
diff --git a/src/tests/csv_test.c b/src/tests/csv_test.c
new file mode 100644
index 0000000..a29ef4f
--- /dev/null
+++ b/src/tests/csv_test.c
@@ -0,0 +1,108 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * csv_test.c
+ *
+ * Copyright (C) 2011-2022, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stddef.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <bits/types/FILE.h>
+#include <stdio.h>
+#include "memory/cats_memory.h"
+#include "cats_strings/cats_strings.h"
+#include "cats_csv/cats_csv.h"
+#include <unistd.h>
+#include <limits.h>
+
+struct csv_test {
+        FILE *csv_file;
+        struct cats_csv *csv;
+};
+#define CSV_FILE "test.csv"
+
+int setup(void **state)
+{
+        struct csv_test *test = calloc_or_die(1, sizeof(struct csv_test));
+        test->csv_file = fopen(CSV_FILE, "r");
+        test->csv = NULL;
+        ENSURE_FILE_OPENED(test->csv_file, CSV_FILE);
+        *state = test;
+        return 0;
+}
+
+
+int teardown(void **state)
+{
+        return 0;
+}
+
+void test_csv_open(void **state)
+{
+        struct csv_test *test = *state;
+        assert_non_null(test->csv_file);
+        test->csv = csv_read_file(test->csv_file, 3);
+        assert_non_null(test->csv);
+        assert_int_equal(test->csv->data_row_count, 2);
+        assert_int_equal(test->csv->column_count, 3);
+
+
+
+}
+
+void test_read_csv(void **state)
+{
+        struct csv_test *test = *state;
+        struct cats_csv *csv = test->csv;
+        double x0C = csv_get_double_field_name(csv, 0, "C");
+        int32_t col_B = csv_get_field_idx(csv, "B");
+        double x1B = csv_get_double_field_idx(csv, 1, 1);
+        assert_true(col_B == 1);
+        assert_true(3.0 == x0C);
+        assert_true(5.0 == x1B);
+
+
+}
+
+void test_free_csv(void **state)
+{
+        struct csv_test *test = *state;
+        struct cats_csv *csv = test->csv;
+        csv_free(&csv);
+        assert_null(csv);
+}
+
+int main(int argc, char **argv)
+{
+
+        const struct CMUnitTest tests[] =
+                {
+                        cmocka_unit_test(test_csv_open),
+                        cmocka_unit_test(test_read_csv),
+                        cmocka_unit_test(test_free_csv),
+
+                };
+
+
+        int count_fail_tests = cmocka_run_group_tests (tests, setup, teardown);
+
+        return count_fail_tests;
+
+}
\ No newline at end of file
diff --git a/src/tests/test.csv b/src/tests/test.csv
new file mode 100644
index 0000000..cbe2c72
--- /dev/null
+++ b/src/tests/test.csv
@@ -0,0 +1,3 @@
+A,B,C
+1,2,3
+4,5,6
diff --git a/src/tests/vital_rate_test.c b/src/tests/vital_rate_test.c
new file mode 100644
index 0000000..94bad0e
--- /dev/null
+++ b/src/tests/vital_rate_test.c
@@ -0,0 +1,248 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * vital_rate_test.c
+ *
+ * Copyright (C) 2011-2022, University of Vienna and Vienna Institute for Nature Conservation & Analyses, Andreas Gattringer.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "cats_global.h"
+
+#include "inline_vital_rates.h"
+#include "vital_rates/setup_rates.h"
+#include "debug/debug_vital_rates.h"
+#include "memory/cats_memory.h"
+#include <stddef.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+struct cats_global global;
+struct cats_debug_options cats_debug;
+
+struct test_vital_rates {
+        struct cats_vital_rate vr;
+        struct cats_vital_rate_hybrid_function vital_dependency_registry[MAX_VITAL_RATES];
+        struct cats_grid *grid;
+        struct cats_configuration *conf;
+        struct cats_environment *env;
+        struct cats_species_param param;
+        struct cats_vital_rate hybrid_vital_rate;
+        int32_t MAX_SUIT_STEPS;
+        int32_t MAX_POP;
+        int32_t link_count;
+};
+
+
+int setup(void **state)
+{
+        logging_initialize(LOG_ERROR, NULL, NULL, true);
+        struct test_vital_rates *test = calloc_or_die(1, sizeof(struct test_vital_rates));
+        test->link_count = setup_default_links(test->vital_dependency_registry);
+        test->env = minimal_suitability_environment();
+        test->grid = minimal_grid(test->conf, test->env);
+        test->grid->suitability = test->env;
+        test->param.OT = 0.5;
+        test->param.ZT = 0.5 / 2.0;
+        test->MAX_POP = 1000;
+        test->param.scale_factor = 0.5;
+        test->MAX_SUIT_STEPS = 1000;
+        test->param.demographic_slope = 15;
+        test->hybrid_vital_rate.environment_set = test->env;
+        test->hybrid_vital_rate.density = DENSITY_DEP_NEGATIVE;
+        test->hybrid_vital_rate.density_ts = 0.5;
+        test->hybrid_vital_rate.max_rate = 1.0;
+        *state = test;
+        return 0;
+}
+
+
+int teardown(void **state)
+{
+        return 0;
+}
+
+
+cats_dt_rates sfs[] = {0.1, 0.5, 0.9};
+cats_dt_rates ots[] = {0.1, 0.5, 0.9};
+
+
+void vital_rate_monotonic(void **state, cats_rate_function func, cats_dt_rates density)
+{
+        assert(density <= 1);
+        assert(density == -1 || density >= 0);
+        struct test_vital_rates *test = *state;
+        struct cats_vital_rate rate = test->hybrid_vital_rate;
+        struct cats_vital_rate rate_no_density = test->hybrid_vital_rate;
+
+        cats_dt_rates value = 0;
+        cats_dt_rates no_density_value = 0;
+        cats_dt_rates old_value = 0;
+        cats_dt_rates N = 0;
+
+        if (density >= 0) {
+                N = test->MAX_POP * density;
+                rate.density = DENSITY_DEP_NEGATIVE;
+                rate_no_density.density = NO_DENSITY_DEP;
+        } else {
+                N = 0;
+                rate.density = NO_DENSITY_DEP;
+        }
+
+
+        for (int32_t s = 0; s < test->MAX_SUIT_STEPS + 1; s++) {
+                cats_dt_rates suit = (cats_dt_environment) s / (cats_dt_environment) test->MAX_SUIT_STEPS;
+
+                value = func(&rate, &test->param, suit, N, test->MAX_POP);
+                assert_true(value >= old_value);
+                assert_true(value >= 0);
+                assert_true(value <= rate.max_rate);
+
+                if (density != -1) {
+                        no_density_value = func(&rate_no_density, &test->param, suit, 0, test->MAX_POP);
+                        assert_true(no_density_value >= value);
+                }
+
+                old_value = value;
+        }
+
+}
+
+
+void vital_rate_sigmoid_density_monotonic(void **state)
+{
+        struct test_vital_rates *test = *state;
+        for (int32_t p = 0; p < test->MAX_POP + 1; p++) {
+                cats_dt_rates density = (cats_dt_rates) p / (cats_dt_rates) test->MAX_POP;
+                vital_rate_monotonic(state, sigmoid, density);
+        }
+
+}
+
+void vital_rate_linear_density_monotonic(void **state)
+{
+        struct test_vital_rates *test = *state;
+        for (int32_t p = 0; p < test->MAX_POP + 1; p++) {
+                cats_dt_rates density = (cats_dt_rates) p / (cats_dt_rates) test->MAX_POP;
+                vital_rate_monotonic(state, linear_rate, density);
+        }
+
+}
+
+void vital_rate_constant_density_monotonic(void **state)
+{
+        struct test_vital_rates *test = *state;
+        for (int32_t p = 0; p < test->MAX_POP + 1; p++) {
+                cats_dt_rates density = (cats_dt_rates) p / (cats_dt_rates) test->MAX_POP;
+                vital_rate_monotonic(state, constant_value_rate, density);
+        }
+
+}
+
+
+void vital_rate_sigmoid_monotonic(void **state)
+{
+        vital_rate_monotonic(state, sigmoid, -1);
+}
+
+
+void vital_rate_linear_monotonic(void **state)
+{
+        vital_rate_monotonic(state, linear_rate, -1);
+}
+
+
+void vital_rate_constant_monotonic(void **state)
+{
+        vital_rate_monotonic(state, constant_value_rate, -1);
+}
+
+
+void vital_rate_sigmoid(void **state)
+{
+        cats_rate_function func = sigmoid;
+        struct test_vital_rates *test = *state;
+        struct cats_vital_rate *rate = &test->hybrid_vital_rate;
+        cats_dt_rates zero_value = func(rate, &test->param, 0.0, 0, test->MAX_POP);
+        cats_dt_rates one_value = func(rate, &test->param, 1.0, 0, test->MAX_POP);
+        cats_dt_rates OT_value = func(rate, &test->param, test->param.OT, 0, test->MAX_POP);
+        assert_true(zero_value < 1e-3);
+        assert_true(zero_value >= 0);
+        assert_true(one_value >= 1 - 1e-3);
+        assert_true(one_value <= 1);
+        assert_true(OT_value == test->param.scale_factor * rate->max_rate);
+
+}
+
+
+void vital_rate_linear(void **state)
+{
+        cats_rate_function func = linear_rate;
+        struct test_vital_rates *test = *state;
+        struct cats_vital_rate *rate = &test->hybrid_vital_rate;
+        cats_dt_rates zero_value = func(rate, &test->param, 0.0, 0, test->MAX_POP);
+        cats_dt_rates one_value = func(rate, &test->param, 1.0, 0, test->MAX_POP);
+        cats_dt_rates OT_value = func(rate, &test->param, test->param.OT, 0, test->MAX_POP);
+        assert_true(zero_value < 1e-3);
+        assert_true(zero_value >= 0);
+        assert_true(one_value >= 1 - 1e-3);
+        assert_true(one_value <= 1);
+        assert_true(OT_value == test->param.scale_factor * rate->max_rate);
+
+}
+
+
+void vital_rate_constant(void **state)
+{
+        cats_rate_function func = constant_value_rate;
+        struct test_vital_rates *test = *state;
+        struct cats_vital_rate rate = test->hybrid_vital_rate;
+        rate.max_rate = 0.3;
+        cats_dt_rates zero_value = func(&rate, &test->param, 0.0, 0, test->MAX_POP);
+        cats_dt_rates one_value = func(&rate, &test->param, 1.0, 0, test->MAX_POP);
+        cats_dt_rates OT_value = func(&rate, &test->param, test->param.OT, 0, test->MAX_POP);
+        assert_true(zero_value == rate.max_rate);
+        assert_true(one_value == rate.max_rate);
+        assert_true(OT_value == rate.max_rate);
+
+}
+
+
+int main(int argc, char **argv)
+{
+
+        const struct CMUnitTest tests[] =
+                {
+                        //cmocka_unit_test(hybrid_vital_rate_zero_below_suitability_ts),
+                        cmocka_unit_test(vital_rate_sigmoid_monotonic),
+                        cmocka_unit_test(vital_rate_linear_monotonic),
+                        cmocka_unit_test(vital_rate_constant_monotonic),
+                        cmocka_unit_test(vital_rate_sigmoid),
+                        cmocka_unit_test(vital_rate_linear),
+                        cmocka_unit_test(vital_rate_constant),
+                        cmocka_unit_test(vital_rate_sigmoid_density_monotonic),
+                        cmocka_unit_test(vital_rate_linear_density_monotonic),
+                        cmocka_unit_test(vital_rate_constant_density_monotonic),
+                };
+
+
+        int count_fail_tests = cmocka_run_group_tests (tests, setup, teardown);
+
+        return count_fail_tests;
+
+}
\ No newline at end of file
-- 
GitLab