Feature unittest (#157)

* Adding support for unit testing using boost unit test and ctest

* Adding libboost-test to Travis config.

* Adding libboost-test to Travis config.

* Modifying per element comparison

* Modifying per element comparison

* Fixing typo

* Fixing typo

* Adding custom comparison for strings

* Updating Travis to run unit tests

* Updating Travis to run unit tests

* Fixing typo

* Preparing unit testing to run on Appveyor

* Preparing unit testing to run on Appveyor

* Preparing unit testing to run on Appveyor

* Preparing unit testing to run on Appveyor and Travis

* Preparing unit testing to run on Appveyor and Travis

* Preparing unit testing to run on Appveyor and Travis

* Preparing unit testing to run on Appveyor

* Preparing unit testing to run on Appveyor

* Fixing unit testing path issue in CMake

* Fixing unit testing path issue in CMake

* Fixing bugs in cmake and appveyor scripts

* Rolling back generate_export_header in cmake
This commit is contained in:
Michael Tryby
2018-03-21 14:10:10 -04:00
committed by Sam Hatchett
parent 63b4438765
commit bbe40c5ba4
11 changed files with 520 additions and 30 deletions

View File

@@ -3,15 +3,15 @@ language: python
env:
global:
- EPANET_HOME=`pwd`
- BUILD_HOME=buildproducts
- BUILD_HOME=buildprod
- TEST_HOME=tests/epanet-nrtestsuite
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y libboost-test-dev
- sudo apt-get install -y swig
install:
- pip install --src build/packages -r tools/requirements.txt
#install:
before_script:
- mkdir -p $BUILD_HOME
@@ -19,7 +19,12 @@ before_script:
- cmake ..
script:
- make
- cmake --build .
# run unit tests
- cd tests
- ctest
# run regression tests
- cd $EPANET_HOME
- pip install -r tools/requirements.txt
- tools/gen-config.sh $EPANET_HOME/$BUILD_HOME/bin > $TEST_HOME/apps/epanet-$TRAVIS_COMMIT.json
- tools/run-nrtest.sh $TEST_HOME $TRAVIS_COMMIT

View File

@@ -41,11 +41,19 @@
cmake_minimum_required (VERSION 2.8.8)
project(EPANET)
add_subdirectory(tools/epanet-output)
add_subdirectory(tests)
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
# Sets for output directory for executables and libraries.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# Sets the position independent code property for all targets
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
IF (APPLE)
SET(CMAKE_INSTALL_NAME_DIR @executable_path)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
@@ -56,20 +64,35 @@ IF (MSVC)
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
ENDIF (MSVC)
# create object library for reuse in other targets
include_directories(include src)
#include_directories(include src)
# configure file groups
file(GLOB EPANET_SOURCES src/*.c)
set(EPANET_API_HEADER include/epanet2.h)
#set(EPANET_API_HEADER include/epanet2.h)
set(EPANET_CLI_SOURCES run/main.c)
file(GLOB EPANET_LIB_ALL src/*)
source_group("Library" FILES ${EPANET_LIB_ALL})
source_group("CLI" REGULAR_EXPRESSION "run/.*")
# the shared library
add_library(epanet SHARED ${EPANET_SOURCES} ${EPANET_API_HEADER})
add_library(epanet SHARED ${EPANET_SOURCES}) #${EPANET_API_HEADER})
target_include_directories(epanet PUBLIC ${PROJECT_SOURCE_DIR}/include)
# create export lib so we can link against dll using Visual Studio
#include(GenerateExportHeader)
#GENERATE_EXPORT_HEADER(epanet
# BASE_NAME epanet
# EXPORT_MACRO_NAME DLLEXPORT
# EXPORT_FILE_NAME epanet_export.h
# STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC)
#
#file(COPY ${CMAKE_CURRENT_BINARY_DIR}/epanet_export.h
# DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/include)
# the standalone executable
add_executable(runepanet ${EPANET_CLI_SOURCES})

View File

@@ -17,32 +17,42 @@ init:
- set EPANET_HOME=%APPVEYOR_BUILD_FOLDER%
- set BUILD_HOME=buildprod
- set TEST_HOME=tests\epanet-nrtestsuite
- set NRTEST_SCRIPT=%EPANET_HOME%\%BUILD_HOME%\packages\nrtest\scripts
- set NRTEST_SCRIPT=C:\Python27\Scripts
- set GENERATOR="Visual Studio 10 2010"
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
- '%BUILD_HOME% -> CMakeLists.txt'
- set BOOST_ROOT="C:\\Libraries\\boost"
- set BOOST_LIB="C:\\Libraries\\boost\\lib32-msvc-12.0"
# called after repo clone
install:
- choco install swig
- python -m pip install --src %BUILD_HOME%\packages -r tools\requirements.txt
# called before build
before_build:
- mkdir %BUILD_HOME%
- cd %BUILD_HOME%
- cmake -G %GENERATOR% -DCMAKE_BUILD_TYPE=Release ..
- cmake -G %GENERATOR%
-DBOOST_ROOT="%BOOST_ROOT%"
-DBOOST_LIBRARYDIR="%BOOST_LIB%"
-DBoost_USE_STATIC_LIBS="ON" ..
# run custom build script
build_script:
- cmake --build . --target runepanet --config Release
- cmake --build . --config Release
before_test:
- cd %EPANET_HOME%
- python -m pip install -r tools\requirements.txt
- tools\gen-config.cmd %EPANET_HOME%\%BUILD_HOME%\bin\Release > %TEST_HOME%\apps\epanet-%APPVEYOR_REPO_COMMIT%.json
# run custom test script
test_script:
# run unit tests
- cd %BUILD_HOME%\tests
- ctest -C Release
# run regression tests
- cd %EPANET_HOME%
- tools\run-nrtest.cmd %NRTEST_SCRIPT% %TEST_HOME% %APPVEYOR_REPO_COMMIT%
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml

View File

@@ -60,6 +60,7 @@
#endif
#endif
//#include "epanet_export.h"
// --- Define the EPANET toolkit constants

46
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,46 @@
#
# 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()
# Sets for output directory for executables and libraries.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
#Prep ourselves for compiling boost
find_package(Boost REQUIRED)
include_directories (${Boost_INCLUDE_DIRS})
#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)
#Finally add it to test execution -
#Notice the WORKING_DIRECTORY and COMMAND
add_test(NAME ${testName}
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${testName}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data)
endforeach(testSrc)

BIN
tests/data/net1.out Normal file

Binary file not shown.

358
tests/test_output.cpp Normal file
View File

@@ -0,0 +1,358 @@
/*
* test_epanet_output.cpp
*
* Created: 8/4/2017
* Author: Michael E. Tryby
* US EPA - ORD/NRMRL
*
* Unit testing for EPANET Output API.
*/
// NOTE: Travis installs libboost test version 1.5.4
// NOTE: Can not dyn link boost using Visual Studio 10 2010
//#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "output"
#include <boost/test/included/unit_test.hpp>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include "epanet_output.h"
#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<float>& test,
std::vector<float>& ref, long cdd_tol)
{
float tmp, min_cdd = 100.0;
// TODO: What is the vectors aren't the same length?
std::vector<float>::iterator test_it;
std::vector<float>::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 floor(min_cdd) <= cdd_tol;
}
boost::test_tools::predicate_result check_string(std::string test, std::string ref)
{
if (ref.compare(test) == 0)
return true;
else
return false;
}
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());
array = NULL;
array_dim = 0;
}
~Fixture() {
ENR_free((void**)&array);
error = ENR_close(&p_handle);
}
std::string path;
int error;
ENR_Handle p_handle;
float* array;
int array_dim;
};
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<int> test;
test.assign(i_array, i_array + array_dim);
const int ref_dim = 5;
int ref_array[ref_dim] = {11,2,13,1,0};
std::vector<int> ref;
ref.assign(ref_array, ref_array + ref_dim);
BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), test.begin(), test.end());
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);
std::string test (name);
std::string ref ("10");
BOOST_CHECK(check_string(test, ref));
delete(name);
}
BOOST_FIXTURE_TEST_CASE(test_getNodeAttribute, Fixture) {
error = ENR_getNodeAttribute(p_handle, 1, ENR_quality, &array, &array_dim);
BOOST_REQUIRE(error == 0);
const int ref_dim = 11;
float ref_array[ref_dim] = { 1.0f,
0.44407997f,
0.43766347f,
0.42827705f,
0.41342604f,
0.42804748f,
0.44152543f,
0.40502965f,
0.38635802f,
1.0f,
0.96745253f};
std::vector<float> ref_vec;
ref_vec.assign(ref_array, ref_array + ref_dim);
std::vector<float> test_vec;
test_vec.assign(array, array + array_dim);
BOOST_CHECK(check_cdd(test_vec, ref_vec, 3));
}
BOOST_FIXTURE_TEST_CASE(test_getLinkAttribute, Fixture) {
error = ENR_getLinkAttribute(p_handle, 1, ENR_flow, &array ,&array_dim);
BOOST_REQUIRE(error == 0);
const int ref_dim = 13;
float ref_array[ref_dim] = { 1848.5812f,
1220.4274f,
130.11162f,
187.6893f,
119.8884f,
40.464489f,
-748.58112f,
478.15378f,
191.73459f,
30.111609f,
140.46449f,
59.535515f,
1848.5812f};
std::vector<float> ref_vec;
ref_vec.assign(ref_array, ref_array + ref_dim);
std::vector<float> test_vec;
test_vec.assign(array, array + array_dim);
BOOST_CHECK(check_cdd(test_vec, ref_vec, 3));
}
BOOST_FIXTURE_TEST_CASE(test_getNodeResult, Fixture) {
error = ENR_getNodeResult(p_handle, 1, 2, &array, &array_dim);
BOOST_REQUIRE(error == 0);
const int ref_dim = 4;
float ref_array[ref_dim] = {0.041142918f,
150.0f,
987.98358f,
120.45029f};
std::vector<float> ref_vec;
ref_vec.assign(ref_array, ref_array + ref_dim);
std::vector<float> test_vec;
test_vec.assign(array, array + array_dim);
BOOST_CHECK(check_cdd(test_vec, ref_vec, 3));
}
BOOST_FIXTURE_TEST_CASE(test_getLinkResult, Fixture) {
error = ENR_getLinkResult(p_handle, 24, 13, &array, &array_dim);
BOOST_REQUIRE(error == 0);
const int ref_dim = 8;
float ref_array[ref_dim] = {0.58586824f,
1892.2433f,
0.0f,
-200.71875f,
1.0f,
3.0f,
1.0f,
0.0f};
std::vector<float> ref_vec;
ref_vec.assign(ref_array, ref_array + ref_dim);
std::vector<float> test_vec;
test_vec.assign(array, array + array_dim);
BOOST_CHECK(check_cdd(test_vec, ref_vec, 3));
}
BOOST_FIXTURE_TEST_CASE(test_getNodeSeries, Fixture){
error = ENR_getNodeSeries(p_handle, 2, ENR_pressure, 0, 10, &array, &array_dim);
BOOST_REQUIRE(error == 0);
const int ref_dim = 10;
float ref_array[ref_dim] = {119.25731f,
120.45029f,
121.19854f,
122.00622f,
122.37414f,
122.8122f,
122.82034f,
122.90379f,
123.40434f,
123.81807f};
std::vector<float> ref_vec;
ref_vec.assign(ref_array, ref_array + ref_dim);
std::vector<float> test_vec;
test_vec.assign(array, array + array_dim);
BOOST_CHECK(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);
const int ref_dim = 10;
float ref_array[ref_dim] = {1234.2072f,
1220.4274f,
1164.4f,
1154.8175f,
1100.0635f,
1094.759f,
1041.7854f,
1040.7617f,
1087.556f,
1082.5011f};
std::vector<float> ref_vec;
ref_vec.assign(ref_array, ref_array + ref_dim);
std::vector<float> test_vec;
test_vec.assign(array, array + array_dim);
BOOST_CHECK(check_cdd(test_vec, ref_vec, 3));
}
BOOST_FIXTURE_TEST_CASE(test_getNetReacts, Fixture) {
error = ENR_getNetReacts(p_handle, &array, &array_dim);
BOOST_REQUIRE(error == 0);
const int ref_dim = 4;
float ref_array[ref_dim] = {18806.59f,
85424.438f,
115174.05f,
238972.66f};
std::vector<float> ref_vec;
ref_vec.assign(ref_array, ref_array + ref_dim);
std::vector<float> test_vec;
test_vec.assign(array, array + array_dim);
BOOST_CHECK(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);
const int ref_dim = 6;
float ref_array[ref_dim] = {57.712959f,
75.0f,
880.41583f,
96.254318f,
96.707115f,
0.0f};
std::vector<float> ref_vec;
ref_vec.assign(ref_array, ref_array + ref_dim);
std::vector<float> test_vec;
test_vec.assign(array, array + array_dim);
BOOST_CHECK(check_cdd(test_vec, ref_vec, 3));
}
BOOST_AUTO_TEST_SUITE_END()

View File

@@ -0,0 +1,40 @@
#
# CMakeLists.txt - CMake configuration file for epanet-output library
#
# Created: March 9, 2018
# Author: Michael E. Tryby
# US EPA ORD/NRMRL
#
cmake_minimum_required (VERSION 3.0)
# Sets for output directory for executables and libraries.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# configure file groups
set(EPANET_OUT_SOURCES src/epanet_output.c src/errormanager.c)
# the binary output file API
add_library(epanet-output SHARED ${EPANET_OUT_SOURCES})
target_include_directories(epanet-output PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
include(GenerateExportHeader)
generate_export_header(epanet-output
BASE_NAME epanet_output
EXPORT_MACRO_NAME DLLEXPORT
EXPORT_FILE_NAME epanet_output_export.h
STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC)
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/epanet_output_export.h DESTINATION
${CMAKE_CURRENT_SOURCE_DIR}/include)

View File

@@ -61,15 +61,19 @@ typedef enum {
} ENR_LinkAttribute;
#ifdef WINDOWS
#ifdef __cplusplus
#define DLLEXPORT __declspec(dllexport) __cdecl
#else
#define DLLEXPORT __declspec(dllexport) __stdcall
#endif
#else
#define DLLEXPORT
#endif
// #ifdef WINDOWS
// #ifdef __cplusplus
// #define DLLEXPORT __declspec(dllexport) __cdecl
// #else
// #define DLLEXPORT __declspec(dllexport) __stdcall
// #endif
// #else
// #define DLLEXPORT
// #endif
#include "epanet_output_export.h"
#ifdef __cplusplus
extern "C" {

View File

@@ -25,6 +25,8 @@ setup(
version = "1.0",
ext_modules = [
Extension("_epanet_output",
define_macros = [('epanet_output_EXPORTS', None)],
include_dirs = ['include'],
sources = ['src/epanet_output.i', 'src/epanet_output.c', 'src/errormanager.c'],
swig_opts=['-modern'],
language = 'C'

View File

@@ -11,6 +11,7 @@
# $ pip install --src build/packages -r tools/requirements.txt
#
-e git+https://github.com/OpenWaterAnalytics/nrtest.git@master#egg=nrtest
#-e git+https://github.com/OpenWaterAnalytics/nrtest.git@master#egg=nrtest
nrtest>=0.2.3
-e ./tools/epanet-output
-e ./tools/nrtest-epanet