diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c64c03..214f94a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ cmake_minimum_required (VERSION 2.8.8) project(EPANET) +add_subdirectory(tests) SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..52feb09 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# CMakeLists.txt - CMake configuration file for epanet/tests +# +# Created: February 13, 2018 +# Author: Constantin Savtchenko +# Ref: http://neyasystems.com/an-engineers-guide-to-unit-testing-cmake-and-boost-unit-tests/ +# +# Modified by: Michael E. Tryby +# US EPA ORD/NRMRL +# + +#Setup CMake to run tests +enable_testing() + +#Prep ourselves for compiling boost +find_package(Boost COMPONENTS unit_test_framework REQUIRED) +include_directories (${Boost_INCLUDE_DIRS} ../include ../tools/epanet-output/src) + +#I like to keep test files in a separate source directory called test +file(GLOB TEST_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} test_*.cpp) + +#Run through each source +foreach(testSrc ${TEST_SRCS}) + #Extract the filename without an extension (NAME_WE) + get_filename_component(testName ${testSrc} NAME_WE) + + #Add compile target + add_executable(${testName} ${testSrc}) + + #link to Boost libraries AND your targets and dependencies + target_link_libraries(${testName} ${Boost_LIBRARIES} epanet epanet-output) + + #I like to move testing binaries into a testBin directory + set_target_properties(${testName} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + + #Finally add it to test execution - + #Notice the WORKING_DIRECTORY and COMMAND + add_test(NAME ${testName} + COMMAND ${CMAKE_BINARY_DIR}/bin/${testName} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data) +endforeach(testSrc) diff --git a/tests/data/net1.out b/tests/data/net1.out new file mode 100644 index 0000000..6cede26 Binary files /dev/null and b/tests/data/net1.out differ diff --git a/tests/test_output.cpp b/tests/test_output.cpp new file mode 100644 index 0000000..dc48616 --- /dev/null +++ b/tests/test_output.cpp @@ -0,0 +1,308 @@ +/* + * test_epanet_output.cpp + * + * Created: 8/4/2017 + * Author: Michael E. Tryby + * US EPA - ORD/NRMRL + * + * Unit testing for EPANET Output API. +*/ + +#define BOOST_TEST_DYN_LINK + +#define BOOST_TEST_MODULE "output" +#include +#include + +#include +#include +#include +#include + +#include "epanet_output.h" + +//#define PROJECT_HOME "C:/Users/mtryby/Workspace/GitRepo/michaeltryby/epanet/" +#define DATA_PATH "./net1.out" + +using namespace std; + +// Custom test to check the minimum number of correct decimal digits between +// the test and the ref vectors. +boost::test_tools::predicate_result check_cdd(std::vector& test, + std::vector& ref, long cdd_tol) +{ + float tmp, min_cdd = 100.0; + + // TODO: What is the vectors aren't the same length? + + std::vector::iterator test_it; + std::vector::iterator ref_it; + + for (test_it = test.begin(); test_it < test.end(); ++test_it) { + for (ref_it = ref.begin(); ref_it < ref.end(); ++ref_it) { + + if (*test_it != *ref_it) { + tmp = - log10f(abs(*test_it - *ref_it)); + if (tmp < min_cdd) min_cdd = tmp; + } + } + } + + if (min_cdd == 100.0) + return true; + else + return std::lround(min_cdd) <= cdd_tol; +} + + +BOOST_AUTO_TEST_SUITE (test_output_auto) + +BOOST_AUTO_TEST_CASE(InitTest) { + ENR_Handle p_handle = NULL; + + int error = ENR_init(&p_handle); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(p_handle != NULL); +} + +BOOST_AUTO_TEST_CASE(OpenTest) { + std::string path = std::string(DATA_PATH); + ENR_Handle p_handle = NULL; + ENR_init(&p_handle); + + int error = ENR_open(p_handle, path.c_str()); + BOOST_REQUIRE(error == 0); + ENR_close(&p_handle); +} + +BOOST_AUTO_TEST_CASE(CloseTest) { + ENR_Handle p_handle = NULL; + int error = ENR_init(&p_handle); + + error = ENR_close(&p_handle); + BOOST_REQUIRE(error == -1); + BOOST_CHECK(p_handle != NULL); +} + +BOOST_AUTO_TEST_SUITE_END() + + +struct Fixture{ + Fixture() { + path = std::string(DATA_PATH); + + error = ENR_init(&p_handle); + ENR_clearError(p_handle); + error = ENR_open(p_handle, path.c_str()); + } + ~Fixture() { + ENR_free((void**)&array); + error = ENR_close(&p_handle); + } + + std::string path; + int error = 0; + ENR_Handle p_handle = NULL; + + float* array = NULL; + int array_dim = 0; + +}; + +BOOST_AUTO_TEST_SUITE(test_output_fixture) + +BOOST_FIXTURE_TEST_CASE(test_getNetSize, Fixture) +{ + int *i_array = NULL; + + error = ENR_getNetSize(p_handle, &i_array, &array_dim); + BOOST_REQUIRE(error == 0); + + // nodes, tanks, links, pumps, valves + std::vector test_vec; + test_vec.assign(i_array, i_array + array_dim); + std::vector ref_vec({11,2,13,1,0}); + + BOOST_TEST(ref_vec == test_vec, boost::test_tools::per_element()); + + ENR_free((void**)&i_array); +} + +BOOST_FIXTURE_TEST_CASE(test_getElementName, Fixture) { + char* name = new char[MAXID]; + int length, index = 1; + + error = ENR_getElementName(p_handle, ENR_node, index, &name, &length); + BOOST_REQUIRE(error == 0); + + BOOST_TEST("10" == name); + + delete(name); +} + +BOOST_FIXTURE_TEST_CASE(test_getNodeAttribute, Fixture) { + + error = ENR_getNodeAttribute(p_handle, 1, ENR_quality, &array, &array_dim); + BOOST_REQUIRE(error == 0); + + std::vector ref_vec({ 1.0, + 0.44407997, + 0.43766347, + 0.42827705, + 0.41342604, + 0.42804748, + 0.44152543, + 0.40502965, + 0.38635802, + 1.0, + 0.96745253 }); + + std::vector test_vec; + test_vec.assign(array, array + array_dim); + + BOOST_TEST(ref_vec == test_vec, boost::test_tools::per_element()); +} + +BOOST_FIXTURE_TEST_CASE(test_getLinkAttribute, Fixture) { + + error = ENR_getLinkAttribute(p_handle, 1, ENR_flow, &array ,&array_dim); + BOOST_REQUIRE(error == 0); + + std::vector ref_vec({ 1848.5812, + 1220.4274, + 130.11162, + 187.6893, + 119.8884, + 40.464489, + -748.58112, + 478.15378, + 191.73459, + 30.111609, + 140.46449, + 59.535515, + 1848.5812}); + + + std::vector test_vec; + test_vec.assign(array, array + array_dim); + + BOOST_TEST(ref_vec == test_vec, boost::test_tools::per_element()); +} + +BOOST_FIXTURE_TEST_CASE(test_getNodeResult, Fixture) { + + error = ENR_getNodeResult(p_handle, 1, 2, &array, &array_dim); + BOOST_REQUIRE(error == 0); + + std::vector ref_vec({0.041142918, + 150.0, + 987.98358, + 120.45029}); + + std::vector test_vec; + test_vec.assign(array, array + array_dim); + + BOOST_TEST(ref_vec == test_vec, boost::test_tools::per_element()); +} + +BOOST_FIXTURE_TEST_CASE(test_getLinkResult, Fixture) { + + error = ENR_getLinkResult(p_handle, 24, 13, &array, &array_dim); + BOOST_REQUIRE(error == 0); + + std::vector ref_vec({0.58586824, + 1892.2433, + 0.0, + -200.71875, + 1.0, + 3.0, + 1.0, + 0.0}); + + std::vector test_vec; + test_vec.assign(array, array + array_dim); + + BOOST_TEST(ref_vec == test_vec, boost::test_tools::per_element()); +} + +BOOST_FIXTURE_TEST_CASE(test_getNodeSeries, Fixture){ + + error = ENR_getNodeSeries(p_handle, 2, ENR_pressure, 0, 10, &array, &array_dim); + BOOST_REQUIRE(error == 0); + + std::vector ref_vec({119.25731, + 120.45029, + 121.19854, + 122.00622, + 122.37414, + 122.8122, + 122.82034, + 122.90379, + 123.40434, + 123.81807}); + + std::vector test_vec; + test_vec.assign(array, array + array_dim); + + BOOST_TEST(check_cdd(test_vec, ref_vec, 3)); +} + +BOOST_FIXTURE_TEST_CASE(test_getLinkSeries, Fixture) { + + error = ENR_getLinkSeries(p_handle, 2, ENR_flow, 0, 10, &array, &array_dim); + BOOST_REQUIRE(error == 0); + + std::vector ref_vec({1234.2072, + 1220.4274, + 1164.4, + 1154.8175, + 1100.0635, + 1094.759, + 1041.7854, + 1040.7617, + 1087.556, + 1082.5011}); + + std::vector test_vec; + test_vec.assign(array, array + array_dim); + + BOOST_TEST(ref_vec == test_vec, boost::test_tools::per_element()); +} + +BOOST_FIXTURE_TEST_CASE(test_getNetReacts, Fixture) { + + error = ENR_getNetReacts(p_handle, &array, &array_dim); + BOOST_REQUIRE(error == 0); + + std::vector ref_vec({18806.59, + 85424.438, + 115174.05, + 238972.66}); + + std::vector test_vec; + test_vec.assign(array, array + array_dim); + + BOOST_TEST(check_cdd(test_vec, ref_vec, 2)); +} + +BOOST_FIXTURE_TEST_CASE(test_getEnergyUsage, Fixture) { + + int linkIdx; + + error = ENR_getEnergyUsage(p_handle, 1, &linkIdx, &array, &array_dim); + BOOST_REQUIRE(error == 0); + + std::vector ref_vec({57.712959, + 75.0, + 880.41583, + 96.254318, + 96.707115, + 0.0}); + + std::vector test_vec; + test_vec.assign(array, array + array_dim); + + BOOST_TEST(ref_vec == test_vec, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_toolkit.cpp b/tests/test_toolkit.cpp new file mode 100644 index 0000000..e64ffe1 --- /dev/null +++ b/tests/test_toolkit.cpp @@ -0,0 +1,68 @@ +// +// test_epanet_toolkit.cpp +// +// Date Created: January 24, 2018 +// +// Author: Michael E. Tryby +// US EPA - ORD/NRMRL +// + +#define BOOST_TEST_DYN_LINK + +#define BOOST_TEST_MODULE "toolkit" +#include + +#include +#include "epanet2.h" + +// NOTE: Project Home needs to be updated to run unit test +#define PROJECT_HOME "C:/Users/mtryby/Workspace/GitRepo/michaeltryby/epanet" +#define DATA_PATH "/tests/network_tests/net1" + +using namespace std; + +BOOST_AUTO_TEST_SUITE (test_toolkit) + +BOOST_AUTO_TEST_CASE (test_alloc_free) +{ + int error = 0; + EN_ProjectHandle ph = NULL; + + error = EN_alloc(&ph); + + BOOST_REQUIRE(error == 0); + BOOST_CHECK(ph != NULL); + + error = EN_free(&ph); + + BOOST_REQUIRE(error == 0); + BOOST_CHECK(ph == NULL); +} + +BOOST_AUTO_TEST_SUITE_END( ) + +// int main(int argc, char *argv[]) +// { +// int error = 0; +// EN_ProjectHandle project = NULL; + +// std::string data_path = std::string(PROJECT_HOME) + std::string(DATA_PATH); +// std::string inputFile(data_path); +// inputFile.append(std::string("/net1.inp")); +// std::string reportFile(data_path); +// reportFile.append(std::string("/net1.rpt")); +// std::string outputFile(data_path); +// outputFile.append(std::string("/net1.out")); + +// error = EN_alloc(&project); +// error = EN_open(project, inputFile.c_str(), reportFile.c_str(), outputFile.c_str()); + +// error = EN_solveH(project); +// error = EN_solveQ(project); +// error = EN_report(project); + +// error = EN_close(project); +// error = EN_free(&project); + +// return error; +// }