Skip to content

Commit

Permalink
feat: add catch2 tests
Browse files Browse the repository at this point in the history
  • Loading branch information
c-dilks committed Apr 24, 2023
1 parent 9fd17d0 commit 54d1f2d
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 4 deletions.
23 changes: 19 additions & 4 deletions src/utilities/eicrecon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ find_package(fmt REQUIRED)
find_package(ROOT REQUIRED COMPONENTS Core Tree Hist RIO EG)

# Compile all sources into executable
file(GLOB SOURCES *.cpp *.cc *.c *.hpp *.hh *.h)
set(SOURCES
eicrecon_cli.cpp
eicrecon.cc
parser.cc
eicrecon_cli.h
parser.h
print_info.h
)

set( INCLUDE_DIRS ${PROJECT_BINARY_DIR} ${EICRECON_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR} ${JANA_INCLUDE_DIR} ${ROOT_INCLUDE_DIRS})
set( LINK_LIBRARIES ${JANA_LIB} ${ROOT_LIBRARIES} ${CMAKE_DL_LIBS} Threads::Threads podio::podio podio::podioRootIO)
Expand All @@ -20,7 +27,15 @@ add_executable( eicrecon ${SOURCES} )
target_include_directories( eicrecon PUBLIC ${INCLUDE_DIRS})
target_link_libraries(eicrecon ${LINK_LIBRARIES} DD4hep::DDParsers fmt::fmt)
target_compile_definitions(eicrecon PRIVATE EICRECON_APP_VERSION=${CMAKE_PROJECT_VERSION})


# Install executable
install(TARGETS eicrecon DESTINATION bin)

# Define tests
find_package(Catch2 3)
if(Catch2_FOUND)
add_executable( param_parser_test param_parser_test.cc parser.cc parser.h)
target_include_directories( param_parser_test PUBLIC ${INCLUDE_DIRS})
target_link_libraries(param_parser_test ${LINK_LIBRARIES} DD4hep::DDParsers fmt::fmt Catch2::Catch2WithMain)
install(TARGETS param_parser_test DESTINATION bin)
else()
message(STATUS "Catch2 is not found. Skipping bin/eicrecon tests...")
endif()
151 changes: 151 additions & 0 deletions src/utilities/eicrecon/param_parser_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (C) 2023 Christopher Dilks
// Subject to the terms in the LICENSE file found in the top-level directory.

#include <catch2/catch_test_macros.hpp>
#include <fmt/format.h>
#include <functional>
#include <sstream>
#include <iomanip>

#include "parser.h"

// common methods
// ---------------------------------------------

// generate a test parameter name, with incrementing counter
std::string KeyName(int &i, std::string key="test:param") {
return key + std::to_string(i++);
}

// run a test and print results
bool RunTest(
std::shared_ptr<jana::parser::Parser> val_parser,
std::string key,
std::string val_in,
std::string val_exp,
std::function<bool(std::string,std::string)> check
)
{
fmt::print("TEST {}:\n", key);
auto val_parsed = val_parser->dd4hep_to_string(val_in, key);
if(!val_parsed.success) return false;
auto val_out = val_parsed.result;
auto passed = check(val_out, val_exp);
fmt::print(" RESULT:\n{:>20}: '{}'\n{:>20}: '{}'\n{:>20}: '{}'\n{:>20}: {}\n\n",
"Input", val_in,
"Output", val_out,
"Expectation", val_exp,
"Passed", passed
);
return passed;
}

// tests
// ---------------------------------------------
TEST_CASE("Parameter Parsing", "[param_parser]" ) {

// instantiate Parser
auto m_parser = std::make_shared<jana::parser::Parser>();

// --------------------------------------------
SECTION("parse numbers, units, and math") {

// implementation: check if difference between parsed number and expected number is less than a threshold
int precision = 15;
double diff_threshold = std::pow(10, -precision);
int i = 1;
auto test_number = [&] (std::string val_in, double val_exp) {
std::stringstream val_exp_stream;
val_exp_stream << std::setprecision(precision) << val_exp;
auto val_exp_str = val_exp_stream.str();
return RunTest(
m_parser,
KeyName(i, "test:param:number"),
val_in,
val_exp_str,
[&] (std::string a, std::string b) { return std::abs( std::stod(a) - std::stod(b) ) < diff_threshold; }
);
};

// tests
REQUIRE(test_number( "7", 7 ));
REQUIRE(test_number( "7*6", 42 ));
REQUIRE(test_number( "1-10/2", -4 ));
REQUIRE(test_number( "1*mm", 1 ));
REQUIRE(test_number( "1*m", 1000 ));
REQUIRE(test_number( "5*GeV", 5 ));
REQUIRE(test_number( "5e+3*MeV", 5 ));
REQUIRE(test_number( "5*keV", 5e-6 ));
REQUIRE(test_number( "8*ns", 8 ));
REQUIRE(test_number( "8*s", 8e+9 ));
REQUIRE(test_number( "1240*eV*nm", 1240/1e9/1e6 ));

}

// --------------------------------------------
SECTION("parse strings") {

// implementation: check if the string remains the same
int i = 1;
auto test_string = [&] (std::string val_in) {
return RunTest(
m_parser,
KeyName(i, "test:param:string"),
val_in,
val_in, // (val_exp, checking if it's the same)
[&] (std::string a, std::string b) { return a==b; }
);
};

// tests
REQUIRE(test_string( "test_string" ));
REQUIRE(test_string( "this,string,is,not,a,list" )); // this string should not be parsed as a list of numbers
REQUIRE(test_string( "" )); // empty string
REQUIRE(test_string( "1240*ev*nm" )); // typo in units (ev); should be parsed as string

}

// --------------------------------------------
SECTION("parse lists") {

// implementation: check if the numerical value difference of each list element is less than threshold
int i = 1;
double diff_threshold = 1e-15;
auto test_list = [&] (std::string val_in, std::string val_exp) {
auto compare_lists = [&] (std::string a, std::string b) {
auto tokenize = [] (std::string str) {
std::istringstream token_stream(str);
std::string token;
std::vector<double> token_list;
while(getline(token_stream, token, ','))
token_list.push_back(std::stod(token));
return token_list;
};
auto a_vals = tokenize(a);
auto b_vals = tokenize(b);
if(a_vals.size() != b_vals.size()) {
fmt::print(stderr," ERROR: lists have differing sizes\n");
return false;
}
for(unsigned k=0; k<a_vals.size(); k++) {
fmt::print(" compare element {}: {} vs. {}\n", k, a_vals.at(k), b_vals.at(k));
if( std::abs(a_vals.at(k) - b_vals.at(k)) > diff_threshold )
return false;
}
return true;
};
return RunTest(
m_parser,
KeyName(i, "test:param:list"),
val_in,
val_exp,
compare_lists
);
};

// tests
REQUIRE(test_list( "1,2,3,4,5", "1,2,3,4,5" ));
REQUIRE(test_list( "5e+3*MeV,8*s,6*7,11", "5,8e+9,42,11" ));

}
}

0 comments on commit 54d1f2d

Please sign in to comment.