Skip to content

Commit

Permalink
feat: test algorithms automatically
Browse files Browse the repository at this point in the history
  • Loading branch information
c-dilks committed Feb 13, 2024
1 parent c709857 commit 33c8952
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 9 deletions.
6 changes: 1 addition & 5 deletions examples/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ endif

# build executables
foreach src : example_sources
exe = executable(
executable(
src.split('.')[0],
src,
include_directories: project_inc,
Expand All @@ -31,10 +31,6 @@ foreach src : example_sources
install: true,
install_rpath: ':'.join(example_rpaths),
)
test(src, exe,
args: ['data.hipo', '1000'],
workdir: get_option('prefix') / '..',
)
endforeach

# install config files
Expand Down
6 changes: 5 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ project(
)
project_description = 'Implementation Guardian of Analysis Algorithms'

# meson modules
pkg = import('pkgconfig')
fs = import('fs')

# resolve dependencies
# NOTE: those that are typically installed by package managers should use `meson/minimum-version.sh`
fmt_dep = dependency(
Expand Down Expand Up @@ -71,14 +75,14 @@ add_project_arguments(
# build and install shared libraries
subdir('src/iguana/services')
subdir('src/iguana/algorithms')
subdir('src/iguana/tests')

# build bindings
if get_option('bind_python')
subdir('bind/python')
endif

# generate pkg-config file
pkg = import('pkgconfig')
pkg.generate(
name: meson.project_name(),
description: project_description,
Expand Down
8 changes: 5 additions & 3 deletions meson.options
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
option('examples', type: 'boolean', value: false, description: 'Build Iguana C++ examples')
option('documentation', type: 'boolean', value: false, description: 'Generate API documentation (requires Doxygen)')
option('bind_python', type: 'boolean', value: false, description: 'Generate Python bindings and their examples')
option('examples', type: 'boolean', value: false, description: 'Build Iguana C++ examples')
option('documentation', type: 'boolean', value: false, description: 'Generate API documentation (requires Doxygen)')
option('test_data_file', type: 'string', value: '', description: 'Sample HIPO file for testing. Must be an absolute path or relative to the build directory. If unspecified, tests are not built')
option('test_num_events', type: 'string', value: '10', description: 'Number of events from `test_data_file` to test')
option('bind_python', type: 'boolean', value: false, description: 'Generate Python bindings and their examples')
11 changes: 11 additions & 0 deletions src/iguana/algorithms/meson.build
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# algorithm source files
algo_sources = [
'Algorithm.cc',
'AlgorithmFactory.cc',
Expand All @@ -8,6 +9,7 @@ algo_sources = [
'clas12/LorentzTransformer.cc',
]

# algorithm headers
algo_public_headers = [
'Algorithm.h',
'AlgorithmBoilerplate.h',
Expand All @@ -19,10 +21,19 @@ algo_public_headers = [
'clas12/LorentzTransformer.h',
]

# algorithm configurations
algo_config_dirs = [
'clas12/config',
]

# algorithm unique names and required banks, for those we want to test automatically
algos_and_banks_for_unit_testing = {
'example::ExampleAlgorithm': ['REC::Particle'],
'clas12::EventBuilderFilter': ['REC::Particle'],
'clas12::ZVertexFilter': ['REC::Particle'],
'clas12::LorentzTransformer': ['REC::Particle'],
}

algo_lib = shared_library(
'IguanaAlgorithms',
algo_sources,
Expand Down
58 changes: 58 additions & 0 deletions src/iguana/tests/iguana-test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <iguana/algorithms/AlgorithmSequence.h>
#include <hipo4/reader.h>

int main(int argc, char **argv) {

// parse arguments
if(argc-1 < 3) {
fmt::print(stderr, "USAGE: {} [data_file] [num_events] [algorithm_name] [banks]...\n", argv[0]);
return 2;
}
const std::string data_file = std::string(argv[1]);
const int num_events = std::stoi(argv[2]);
const std::string algo_name = std::string(argv[3]);
std::vector<std::string> bank_names;
for(int i=4; i<argc; i++)
bank_names.push_back(std::string(argv[i]));
fmt::print("TEST IGUANA:\n");
fmt::print(" {:>20} = {}\n", "data_file", data_file);
fmt::print(" {:>20} = {}\n", "num_events", num_events);
fmt::print(" {:>20} = {}\n", "algo_name", algo_name);
fmt::print(" {:>20} = {}\n", "banks", fmt::join(bank_names,", "));
fmt::print("\n");

// open the HIPO file; we use 2 readers, one for 'before' (i.e., not passed through iguana), and one for 'after'
// (passed through iguana), so we may compare them
hipo::reader reader_before(data_file.c_str()); // NOTE: not copy-constructable, so make two separate readers
hipo::reader reader_after(data_file.c_str());
auto banks_before = reader_before.getBanks(bank_names);
auto banks_after = reader_after.getBanks(bank_names);

// define the algorithm
iguana::AlgorithmSequence seq;
seq.Add(algo_name);
seq.SetOption(algo_name, "log", "debug");

// start the algorithm
seq.Start(banks_after);

// event loop
int it_ev = 0;
while(reader_after.next(banks_after) && (num_events==0 || it_ev++ < num_events)) {
// iterate the 'before' reader too
reader_before.next(banks_before);
// run the algorithm
seq.Run(banks_after);
// print the banks, before and after
for(decltype(bank_names)::size_type it_bank=0; it_bank < bank_names.size(); it_bank++) {
fmt::print("{:=^70}\n", fmt::format(" BEFORE: {} ", bank_names.at(it_bank)));
banks_before.at(it_bank).show();
fmt::print("{:=^70}\n", fmt::format(" AFTER: {} ", bank_names.at(it_bank)));
banks_after.at(it_bank).show();
}
}

// stop the algorithm
seq.Stop();
return 0;
}
25 changes: 25 additions & 0 deletions src/iguana/tests/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
test_exe_name = 'iguana-test'

test_exe = executable(
test_exe_name,
test_exe_name + '.cc',
include_directories: project_inc,
dependencies: project_deps,
link_with: project_libs,
install: true,
# install_rpath: # don't bother with rpath, since `meson test` will run it in the build directory
)

if fs.is_file(get_option('test_data_file'))
message('Test sample file provided; you may run tests with `meson test`')
foreach algo, banks : algos_and_banks_for_unit_testing
test(
test_exe_name + ' ' + algo.replace('::', '__'),
test_exe,
args: [ get_option('test_data_file'), get_option('test_num_events'), algo ] + banks,
)
endforeach
else
stat_file = get_option('test_data_file')=='' ? 'provided' : 'found'
message('Test sample file NOT ' + stat_file + '; you may run tests manually with `' + test_exe_name + '`')
endif

0 comments on commit 33c8952

Please sign in to comment.