Add unit testing framework readme (#766)

This commit is contained in:
Changho Hwang
2026-03-31 22:30:35 -07:00
committed by GitHub
parent 4f3638b60d
commit d2f7056cf4

130
test/README.md Normal file
View File

@@ -0,0 +1,130 @@
# MSCCL++ C++ Test Framework
A lightweight, GTest-like test framework with MPI support for testing MSCCL++ C++ APIs. Defined in `framework.hpp` / `framework.cc`.
## Adding a New Test (Step-by-Step)
### Single-process test (unit/)
1. **Create the test file** `test/unit/my_feature_tests.cc` (or `.cu` for CUDA):
```cpp
#include "../framework.hpp"
#include <mscclpp/my_feature.hpp>
TEST(MyFeatureTest, BasicUsage) {
EXPECT_EQ(myFunction(), 42);
}
```
2. **Register it in CMake** — add the filename to `test/unit/CMakeLists.txt`:
```cmake
target_sources(unit_tests PRIVATE
...
my_feature_tests.cc # <-- add here
)
```
3. **Build and run**:
```bash
cmake --build build -j
./build/test/unit_tests --filter=MyFeatureTest
```
### Multi-process test (mp_unit/)
1. **Create the test file** `test/mp_unit/my_feature_tests.cc` (or `.cu`):
```cpp
#include "mp_unit_tests.hpp"
TEST(MyFeatureTest, MultiRank) {
int rank = gEnv->rank;
EXPECT_GE(rank, 0);
}
```
Use fixtures from `mp_unit_tests.hpp` (e.g., `CommunicatorTest`) if you need pre-established connections.
2. **Register it in CMake** — add the filename to `test/mp_unit/CMakeLists.txt`:
```cmake
target_sources(mp_unit_tests PRIVATE
...
my_feature_tests.cc # <-- add here
)
```
3. **Build and run**:
```bash
cmake --build build -j
mpirun -np 2 ./build/test/mp_unit_tests --filter=MyFeatureTest
```
### Notes
- No separate test registration step is needed — `TEST()` auto-registers via static initialization.
- The `test_framework` static library is built from `framework.cc` in the top-level `test/CMakeLists.txt` and linked into both `unit_tests` and `mp_unit_tests`. You do not need to modify it.
- Use `.cu` extension for files that contain CUDA kernel code; use `.cc` for host-only tests.
- Each test binary needs a `main()` that calls `RUN_ALL_TESTS()`. See `unit/unit_tests_main.cc` (single-process) and `mp_unit/mp_unit_tests.cc` (multi-process with `Environment` setup).
- Additional run options: `--filter=-Pattern` (exclude), `--exclude-perf-tests` (skip `PERF_TEST`s).
## Macros
| Macro | Behavior |
|---|---|
| `TEST(Suite, Name)` | Register a test. If `Suite` is a defined class, it's used as a fixture. |
| `PERF_TEST(Suite, Name)` | Same as `TEST` but marked as perf (skippable via `--exclude-perf-tests`). |
| `EXPECT_*` | Non-fatal assertions: `EXPECT_TRUE`, `EXPECT_FALSE`, `EXPECT_EQ`, `EXPECT_NE`, `EXPECT_LT`, `EXPECT_LE`, `EXPECT_GT`, `EXPECT_GE` |
| `ASSERT_*` | Fatal assertions (abort test on failure): same variants as `EXPECT_*`, plus `ASSERT_NO_THROW` |
| `FAIL()` | Fail immediately. Supports streaming: `FAIL() << "reason";` |
| `SKIP_TEST()` | Skip the current test. Supports streaming: `SKIP_TEST() << "reason";` |
| `CUDA_CHECK(call)` | Check a CUDA API return code, throw on error. |
## Fixtures
Define a class inheriting from `mscclpp::test::TestCase` with `SetUp()` / `TearDown()`, then use the class name as the suite name:
```cpp
class MyFixture : public mscclpp::test::TestCase {
public:
void SetUp() override { /* per-test setup */ }
void TearDown() override { /* per-test cleanup */ }
protected:
int sharedState_ = 0;
};
TEST(MyFixture, SomeTest) {
sharedState_ = 42;
EXPECT_EQ(sharedState_, 42);
}
```
See `mp_unit/mp_unit_tests.hpp` (`BootstrapTest`, `CommunicatorTest`, etc.) for real fixture examples.
## Global Environments
Register an `Environment` subclass for one-time global setup/teardown (e.g., MPI bootstrap):
```cpp
class MyEnv : public mscclpp::test::Environment {
public:
void SetUp() override { /* global init */ }
void TearDown() override { /* global cleanup */ }
};
// In main(), before RUN_ALL_TESTS():
mscclpp::test::TestRegistry::instance().addEnvironment(new MyEnv());
```
See `mp_unit/mp_unit_tests.cc` for the `MultiProcessTestEnv` example.
## Utilities
- `mscclpp::test::utils::isMainRank()` — true on MPI rank 0
- `mscclpp::test::utils::getMPIRank()` / `getMPISize()`
- `mscclpp::test::utils::Timer` — high-resolution timer with `start()`, `stop()`, `elapsedMilliseconds()`
- `mscclpp::test::currentTestName()` — returns `"Suite.Name"` for the running test