RAJA Tests¶
As noted in Continuous Integration (CI) Testing, all RAJA test checks must pass before any PR contribution will be merged. Additionally, we recommend that contributors include new tests in their code contributions when adding new features and bug fixes.
Note
If RAJA team members think adequate testing is not included in a PR branch, they will ask for additional testing to be added during the review process.
Test Organization¶
The goals of the RAJA test organization are to:
Make it easy to see what is tested and where tests live. We want developers and users to be able to find tests easily and know where to put new tests when they add them.
Parameterize tests as much as reasonable to ensure that features work with all supported RAJA back-ends and we are testing them consistently. We want the source files for each test case to allow testing of each RAJA back-end. Specifically, tests for each back-end are generated by instantiating the same source routines with different type information.
Have test source code generated for compilation by CMake when the code is configured. This significantly reduces code redundancy and enables our test parameterization goals.
All RAJA tests reside in the RAJA/test directory. The test directory structure looks like this:
RAJA/test/functional/forall
kernel
scan
...
include/...
integration/...
unit/algorithm
atomic
index
...
RAJA tests are partitioned into three main categories:
Unit tests exercise basic interfaces and features of individual RAJA classes and methods in standalone fashion; i.e., integrated with other parts of RAJA as minimally as is reasonable. RAJA unit tests reside in sub-directories of the RAJA/test/unit directory.
Functional tests integrate multiple RAJA features in common ways to test how RAJA is used in practice. RAJA functional tests reside in sub-directories of the RAJA/test/functional directory.
Integration tests exercise features that integrate RAJA with other libraries, such as Kokkos performance tools as plug-ins. RAJA integration tests reside in sub-directories of the RAJA/test/integration directory.
The RAJA/test/include directory contains header files that define types and other items that are commonly used in various tests.
Important
Please follow the existing sub-directory structure and code implementation patterns for RAJA tests when adding or modifying tests.
Anatomy Of A Test Case¶
This section discusses in some detail the structure of files for a single
RAJA test case and how the work together. In particular, we describe the set
of basic tests that exercise RAJA::forall
execution with various RAJA
segment types.
Note
The implementation pattern described in the following sections is similarly used by all other RAJA tests.
Since these tests integrate multiple RAJA features, it is considered a functional test. The files for this test are located in the RAJA/test/functional/forall/segment directory. The contents of the directory are:
$ ls -c1 -R ./test/functional/forall/segment
./test/functional/forall/segment:
tests
test-forall-segment.cpp.in
CMakeLists.txt
./test/functional/forall/segment/tests:
test-forall-RangeStrideSegment.hpp
test-forall-RangeSegment.hpp
test-forall-ListSegment.hpp
Next, we describe these and their relationships.
Test Source File¶
The test-forall-segment.cpp.in file is the parameterized test source file. It contains header file include statements:
//
// test/include headers
//
#include "RAJA_test-base.hpp"
#include "RAJA_test-camp.hpp"
#include "RAJA_test-index-types.hpp"
#include "RAJA_test-forall-data.hpp"
#include "RAJA_test-forall-execpol.hpp"
//
// Header for tests in ./tests directory
//
// Note: CMake adds ./tests as an include dir for these tests.
//
#include "test-forall-@SEGTYPE@.hpp"
The first set of header files live in the RAJA/test/include
directory
mentioned earlier. The headers are centrally located since their contents
are shared with other test files. The last include statement pulls in the
header file containing the parameterized tests for the corresponding RAJA
segment type.
Next, a camp::cartesian_product
type is defined to assemble sets of types
used in the parameterized tests:
//
// Cartesian product of types used in parameterized tests
//
using @BACKEND@ForallSegmentTypes =
Test< camp::cartesian_product<StrongIdxTypeList,
@BACKEND@ResourceList,
@BACKEND@ForallExecPols>>::Types;
The first template argument defining the camp::cartesian_product object
type refers to a list of segment index types defined in the
RAJA_test-index-types.hpp header file.
The second argument refers to a list
of RAJA/camp resource types appropriate for the RAJA execution back-end defined
in the RAJA_test-camp.hpp header file (see Test Header files for
where this is used). The third argument refers to a list of RAJA
execution policy types defined in the
RAJA_test-forall-execpol.hpp
header file. This results in the generation of a combinatorial collection of
typed tests being run. Each test is defined by a unique tuple of types,
described in Test Header files.
Lastly, the parameterized set of tests is instantiated:
//
// Instantiate parameterized test
//
INSTANTIATE_TYPED_TEST_SUITE_P(@BACKEND@,
Forall@SEGTYPE@Test,
@BACKEND@ForallSegmentTypes);
INSTANTIATE_TYPED_TEST_SUITE_P
is a GoogleTest macro. The first
argument is a label noting the RAJA back-end used for the generated tests.
This can be used to filter the tests when they are manually run.
The second argument is a label identifying the test set, and the
third argument matches the CMake generated name for the
camp::cartesian_product
type described above.
Important
The second argument passed to the
INSTANTIATE_TYPED_TEST_SUITE_P
macro must match the name of
the test suite class discussed in Test Header files.
CMakeLists.txt File¶
The concrete version of each of the items described above is generated by
CMake when a RAJA build is configured. CMake fills in the segment type and
back-end identifiers, @SEGTYPE@
and @BACKEND@
, respectively. These
identifiers and the test file and executable generation process is defined in
the
CMakeLists.txt file in the test directory. If you look in the file,
you will see nested loops over RAJA back-ends and segment types which
process the test source file test-forall-segment.cpp.in
multiple times
to create a uniquely named source file for each back-end/segment type
combination in the RAJA build space. Each source file will be compiled into
a similarly named, unique test executable when the code is compiled.
Test Header files¶
Recall the line in the test source file:
#include "test-forall-@SEGTYPE@.hpp"
This identifies the header file containing the actual test code used to generate the tests. The test header files are located in the RAJA/test/functional/forall/segment/tests directory. The main elements of each test header file are described next. We use the test-forall-RangeSegment.hpp file to illustrate the essential test implementation elements.
The file contains the following important items:
test implementation method
typed test suite class
typed test invocation
type test suite registration
The test implementation is contained in a parameterized template method:
template <typename INDEX_TYPE, typename WORKING_RES, typename EXEC_POLICY>
void ForallRangeSegmentTestImpl(INDEX_TYPE first, INDEX_TYPE last)
{
...
}
Here, the template parameters identify the index type of the RAJA
segment INDEX_TYPE
, the resource type for allocating test memory in the
proper execution environment WORKING_RES
, and the execution policy
EXEC_POLICY
for the RAJA::forall
method used to run the tests.
The test suite class plugs into the GoogleTest framework:
TYPED_TEST_SUITE_P(ForallRangeSegmentTest);
template <typename T>
class ForallRangeSegmentTest : public ::testing::Test
{
};
using the TYPED_TEST_SUITE_P
GoogleTest macro.
Important
The name of the test class must be identical to the label passed
to the GoogleTest TYPED_TEST_SUITE_P
macro.
The specific tests that are run are defined by calls to the test implementation
template method ForallRangeSegmentTestImpl
described above:
TYPED_TEST_P(ForallRangeSegmentTest, RangeSegmentForall)
{
using INDEX_TYPE = typename camp::at<TypeParam, camp::num<0>>::type;
using WORKING_RES = typename camp::at<TypeParam, camp::num<1>>::type;
using EXEC_POLICY = typename camp::at<TypeParam, camp::num<2>>::type;
// test zero-length range segment
ForallRangeSegmentTestImpl<INDEX_TYPE, WORKING_RES, EXEC_POLICY>(INDEX_TYPE(3), INDEX_TYPE(3));
ForallRangeSegmentTestImpl<INDEX_TYPE, WORKING_RES, EXEC_POLICY>(INDEX_TYPE(0), INDEX_TYPE(27));
ForallRangeSegmentTestImpl<INDEX_TYPE, WORKING_RES, EXEC_POLICY>(INDEX_TYPE(1), INDEX_TYPE(2047));
ForallRangeSegmentTestImpl<INDEX_TYPE, WORKING_RES, EXEC_POLICY>(INDEX_TYPE(1), INDEX_TYPE(32000));
runNegativeTests<INDEX_TYPE, WORKING_RES, EXEC_POLICY>();
}
Here, TYPED_TEST_P
is a GoogleTest macro defining the method for
executing the tests. Note that the first three lines
in the method extract the template parameter types from the camp::tuple
produced by the camp::cartesian_product
described earlier in
Test Source File. If you look in the file, you will see an example of
how we use C++ SFINAE to exclude running tests with negative index values
for index types that are unsigned.
Important
The label passed as the first argument to the GoogleTest
TYPED_TEST_P
macro must match the name of the test suite class. The second argument is discussed below.It is critical to use the same type ordering when extracting the types that was used when the
camp::cartesian_product
type was defined in the test source file, described in Test Source File.
Lastly, the test suite is registered with GoogleTest using the
REGISTER_TYPED_TEST_SUITE_P
macro:
REGISTER_TYPED_TEST_SUITE_P(ForallRangeSegmentTest,
RangeSegmentForall);
Important
The label passed as the first argument to the GoogleTest
REGISTER_TYPED_TEST_SUITE_P
macro must match the name of the test suite class.The label passed as the second argument to the GoogleTest
REGISTER_TYPED_TEST_SUITE_P
macro must match the label passed as the second argument to theTYPED_TEST_P
macro.