Unit tests in libcuspatial are written using Google Test.
In general we should test to make sure all code paths are covered. This is not always easy or possible. But generally this means we test all supported combinations of algorithms and data types, and the main iterator and container types supported by algorithms. Here are some other guidelines.
throw
.libcuspatial currently has two C++ APIs: the column-based API uses libcudf data structures as input and output. These tests can use libcudf features for constructing columns and tables. The header-only API does not depend on libcudf at all and so tests of these APIs should not include any libcudf headers. Header-only and column-based API tests are located together in cuspatial/tests
, however header-only API tests are .cu
files and column-based API files are .cpp
files.
Generally, we test algorithms and business logic in the header-only API's unit tests. Column-based API tests should only cover specifics of the column-based API, such as type handling, input validation, and exceptions that are only thrown by that API. Column-based API tests typically also tests empty imputs, to ensure that empty column inputs result in empty column output rather than throwing exceptions.
The naming of unit test directories and source files should be consistent with the feature being tested. For example, the tests for APIs in distance.hpp
should live in files in cuspatial/cpp/tests/distance/
.
In the interest of improving compile time, whenever possible, test source files should be .cpp
files because nvcc
is slower than gcc
in compiling host code. Note that thrust::device_vector
includes device code, and so must only be used in .cu
files. rmm::device_uvector
, rmm::device_buffer
and the various column_wrapper
types described later can be used in .cpp
files, and are therefore preferred in test code over thrust::device_vector
.
Testing header-only APIs requires CUDA compilation so should be done in .cu
files.
All libcuspatial unit tests should make use of a GTest "Test Fixture". Even if the fixture is empty, it should inherit from the base fixture cuspatial::test::BaseFixture
found in cpp/tests/base_fixture.hpp
. This ensures that RMM is properly initialized and finalized. cuspatial::test::BaseFixture
already inherits from testing::Test
and therefore it is not necessary for your test fixtures to inherit from it.
Example:
class MyTestFixture : public cuspatial::test::BaseFixture {...};
In general, libcuspatial features must work across all supported types (for cuspatial this typically just means float
and double
). In order to automate the process of running the same tests across multiple types, we use GTest's Typed Tests. Typed tests allow you to write a test once and run it across a list of types.
For example:
In this example, all tests using the TypedTestFixture
fixture will run once for each type in the list defined in TestTypes
(float, double
).
libcuspatial test utilities include cuspatial::test::expect_vector_equivalent()
in cpp/tests/utility/vector_equality()
. This function compares two containers using Google Test's approximate matching for floating-point values. It can handle vectors of cuspatial::vec_2d<T>
, where T
is float
or double
. It automatically copies data in device containers to host containers before comparing, so you can pass it one host and one device vector, for example.
Example:
Before creating your own test utilities, look to see if one already exists that does what you need. If not, consider adding a new utility to do what you need. However, make sure that the utility is generic enough to be useful for other tests and is not overly tailored to your specific testing need.