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