Auto-discovering meson tests
One of the things I missed when I migrated from cmake
to meson
was the ease
at which cmake
discovers tests.
1# Tests
2option(PACKAGE_TESTS "Build the tests" OFF)
3if(PACKAGE_TESTS)
4 find_package(GTest REQUIRED)
5 enable_testing()
6 include(GoogleTest)
7 add_subdirectory(gtests)
8endif()
Thankfully, meson
can kind of emulate this behavior, even in its restricted
syntax. The key concept is arrays and their iterators.
1test_array = [#
2 # ['Pretty name', 'binary_name', 'BlahTest.cpp']
3 ['String parser helpers', 'strparse_run', 'StringHelpersTest.cpp'],
4 ['Improved Dimer', 'impldim_run', 'ImpDimerTest.cpp'],
5]
6foreach test : test_array
7 test(test.get(0),
8 executable(test.get(1),
9 sources : ['gtests/'+test.get(2)],
10 dependencies : [ prog_deps, gtest_dep, gmock_dep ],
11 link_with : proglib,
12 cpp_args : prog_extra_args
13 ),
14 )
15endforeach
Now this is pretty neat already, but we can go a step further and augment this with a working directory concept.
1test_array = [#
2 # ['Pretty name', 'binary_name', 'BlahTest.cpp', 'working_dir']
3 ['Improved Dimer', 'impldim_run', 'ImpDimerTest.cpp', '/gtests/data/'],
4 ['Matter converter', 'matter_run', 'MatterTest.cpp', '/gtests/data/systems/sulfolene'],
5 ['String parser helpers', 'strparse_run', 'StringHelpersTest.cpp', ''],
6]
7foreach test : test_array
8 test(test.get(0),
9 executable(test.get(1),
10 sources : ['gtests/'+test.get(2)],
11 dependencies : [ prog_deps, gtest_dep, gmock_dep ],
12 link_with : proglib,
13 cpp_args : prog_extra_args
14 ),
15 workdir : meson.source_root() + test.get(3)
16 )
17endforeach
This is rather satisfying. Combined with some wraps
, it is also pretty
portable.
Addenum: Tests with arguments
Recently, some openblas
work made me rethink how meson
handles tests and arguments. It turns out something relatively simple in make
:
1xblat2s: sblat2.o $(BLASLIB)
2 $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $^
3run: all
4 ./xblat2s < sblat2.in
Is rather complicated to emulate directly within meson
. A wrapper is required, first in C
:
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5int main(int argc, char *argv[]) {
6 if (argc != 3) {
7 fprintf(stderr, "Usage: %s <executable> <input_file>\n", argv[0]);
8 return EXIT_FAILURE;
9 }
10
11 char command[1024];
12 snprintf(command, sizeof(command), "%s < %s", argv[1], argv[2]);
13
14 int result = system(command);
15 if (result != 0) {
16 fprintf(stderr, "Error: Command '%s' failed with return code %d.\n", command, result);
17 return EXIT_FAILURE;
18 }
19
20 return EXIT_SUCCESS;
21}
Which can then be used within meson
:
1_blas_input_test_array = [#
2 # ['Pretty name', 'binary_name', 'BlahTest.cpp', 'inputfile.in']
3 ['Test REAL Level 2 BLAS', 'xblat2s', 'sblat2.f', 'sblat2.in'],
4]
5test_runner = executable('run_test',
6 sources: ['run_test.c'],
7 install: false)
8# NOTE: For the tests to pass the executables need to be compiled first
9foreach _test : _blas_input_test_array
10 executable(_test.get(1),
11 sources : _test.get(2),
12 link_with : netlib_blas,
13 )
14 test_exe = meson.current_build_dir() / _test.get(1)
15 input_file = meson.source_root() + '/lapack-netlib/BLAS/TESTING/' + _test.get(3)
16 test(_test.get(0), fortran_test_runner,
17 args: [test_exe, input_file],
18 workdir: meson.current_build_dir())
19endforeach
However, the main pain point with this is that the executables are not exactly test dependencies so this fails if the entire project hasn’t been compiled before running the tests. Still a pretty minor caveat.