|
| 1 | +# 测试体系设计 |
| 2 | + |
| 3 | +**核心思路:** 测试屏蔽平台差异,底层实例化不同平台,特化需单独处理的测试。 |
| 4 | + |
| 5 | +## 1. 架构 |
| 6 | + |
| 7 | +``` |
| 8 | +tests/ |
| 9 | +├── CMakeLists.txt # 顶层:include 宏 + add_subdirectory |
| 10 | +├── common/ |
| 11 | +│ ├── CMakeLists.txt # header-only interface library |
| 12 | +│ ├── test_macros.cmake # CMake 宏:infini_train_add_test / infini_train_add_test_suite |
| 13 | +│ └── test_utils.h # C++ 基类、skip 宏、填充工具函数 |
| 14 | +├── tensor/ # Tensor 创建 / 拷贝 / 销毁 / 算子 |
| 15 | +├── optimizer/ # Optimizer 创建 / step |
| 16 | +├── autograd/ # 各 autograd op 的 forward / backward |
| 17 | +├── hook/ # Module hook + precision check |
| 18 | +├── lora/ # LoRA 相关 |
| 19 | +├── dtype/ # Scalar / dtype dispatch + 编译期负面测试 |
| 20 | +└── transformer/ # Transformer 架构测试 |
| 21 | +``` |
| 22 | + |
| 23 | +### 核心设计:设备参数化 |
| 24 | + |
| 25 | +测试不区分 CPU / CUDA 平台。一个测试定义通过 GTest 参数化自动在所有可用设备上运行: |
| 26 | + |
| 27 | +- `INFINI_TRAIN_REGISTER_TEST(TestName)` — 注册 CPU + CUDA 两个实例 |
| 28 | + |
| 29 | +无 GPU 时 CUDA 实例在注册阶段直接跳过(不会出现在测试列表里),并打印一条 `LOG(INFO)` 提示。 |
| 30 | + |
| 31 | +### 基类层次 |
| 32 | + |
| 33 | +| 基类 | 用途 | 提供的能力 | |
| 34 | +|------|------|-----------| |
| 35 | +| `InfiniTrainTest` | 通用参数化测试 | `GetDevice()`, `createTensor(shape, dtype, requires_grad)` | |
| 36 | +| `AutogradTestBase` | Autograd 测试 | `createTensor(shape, value)` 自动 `requires_grad=true` + 顺序填充 | |
| 37 | + |
| 38 | +**为什么需要 AutogradTestBase?** |
| 39 | + |
| 40 | +- 所有 autograd 测试都需要 `requires_grad=true` |
| 41 | +- 所有 autograd 测试都需要填充数据 |
| 42 | +- 前向/反向传播测试必须有输入数据才能验证结果。`AutogradTestBase` 把 `FillSequentialTensor` 内置了,避免每个测试都手动调用 |
| 43 | + |
| 44 | +### 跳过特定平台 |
| 45 | + |
| 46 | +这些宏函数涉及到了具体平台,用来针对性检验或跳过某些测试样例。 |
| 47 | + |
| 48 | +在个别测试内部按需跳过: |
| 49 | + |
| 50 | +```cpp |
| 51 | +// 跳过 CPU 实例(用于硬编码加速器设备的测试,未来新平台仍会运行) |
| 52 | +SKIP_CPU(); |
| 53 | + |
| 54 | +// 只在 CPU 实例运行(用于硬编码 CPU 设备的测试) |
| 55 | +ONLY_CPU(); |
| 56 | + |
| 57 | +// 只在 CUDA 实例运行(用于硬编码 CUDA 设备的测试) |
| 58 | +ONLY_CUDA(); |
| 59 | + |
| 60 | +// 需要 ≥n 个加速器设备 |
| 61 | +REQUIRE_MIN_DEVICES(n); |
| 62 | +``` |
| 63 | + |
| 64 | +### CMake 宏 |
| 65 | + |
| 66 | +`test_macros.cmake` 提供两个宏减少 CMakeLists 样板: |
| 67 | + |
| 68 | +- `infini_train_add_test(name SOURCES ... LABELS ...)` — 创建可执行文件、链接 GTest + 框架库、用 `gtest_discover_tests` 自动发现用例 |
| 69 | +- `infini_train_add_test_suite(name SOURCES ... LABELS ...)` — 按 label(cpu/cuda)拆分为多个 CTest target,通过 `TEST_FILTER` 路由到对应的参数化前缀(`CPU/*`, `CUDA/*`) |
| 70 | + |
| 71 | +## 2. 构建与运行 |
| 72 | + |
| 73 | +```bash |
| 74 | +# 构建(从 build 目录) |
| 75 | +cmake -DBUILD_TEST=ON -DUSE_CUDA=ON .. |
| 76 | +make -j$(nproc) |
| 77 | + |
| 78 | +# 运行全部测试 |
| 79 | +ctest --output-on-failure |
| 80 | + |
| 81 | +# 只运行 CPU 测试 |
| 82 | +ctest -L cpu --output-on-failure |
| 83 | + |
| 84 | +# 只运行 CUDA 测试 |
| 85 | +ctest -L cuda --output-on-failure |
| 86 | + |
| 87 | +# 运行单个测试二进制(看完整 GTest 输出) |
| 88 | +./test_tensor_cpu |
| 89 | +./test_autograd_cuda |
| 90 | + |
| 91 | +# GTest filter 过滤特定用例 |
| 92 | +./test_tensor_cpu --gtest_filter="CPU/TensorCreateTest.*" |
| 93 | +``` |
| 94 | + |
| 95 | +无 GPU 机器上 `cmake -DBUILD_TEST=ON -DUSE_CUDA=OFF ..` 即可,CUDA 测试实例不会注册。 |
| 96 | + |
| 97 | +## 3. 新增测试 |
| 98 | + |
| 99 | +### 3.1 新增 GTest 参数化测试(推荐) |
| 100 | + |
| 101 | +以新增 `tests/foo/` 为例,完整流程: |
| 102 | + |
| 103 | +**Step 1: 创建目录和测试文件** |
| 104 | + |
| 105 | +```bash |
| 106 | +mkdir tests/foo |
| 107 | +``` |
| 108 | + |
| 109 | +```cpp |
| 110 | +// tests/foo/test_foo_basic.cc |
| 111 | +#include <gtest/gtest.h> |
| 112 | +#include "infini_train/include/tensor.h" |
| 113 | +#include "test_utils.h" |
| 114 | + |
| 115 | +using namespace infini_train; |
| 116 | + |
| 117 | +class FooBasicTest : public infini_train::test::InfiniTrainTest {}; |
| 118 | + |
| 119 | +TEST_P(FooBasicTest, CreateTensor) { |
| 120 | + auto tensor = createTensor({2, 3}); |
| 121 | + EXPECT_NE(tensor, nullptr); |
| 122 | +} |
| 123 | + |
| 124 | +TEST_P(FooBasicTest, CUDAOnlyFeature) { |
| 125 | + SKIP_CPU(); |
| 126 | + // CUDA-specific logic ... |
| 127 | +} |
| 128 | + |
| 129 | +INFINI_TRAIN_REGISTER_TEST(FooBasicTest); |
| 130 | +``` |
| 131 | +
|
| 132 | +**基类选择(或创建):** |
| 133 | +
|
| 134 | +| 场景 | 基类 | |
| 135 | +|------|------| |
| 136 | +| 通用测试 | `InfiniTrainTest`(提供 `createTensor(shape, dtype, requires_grad)`) | |
| 137 | +| 需要 autograd | `AutogradTestBase`(提供 `createTensor(shape, value)`,自动 `requires_grad=true` + 顺序填充) | |
| 138 | +
|
| 139 | +**Step 2: 写 CMakeLists.txt** |
| 140 | +
|
| 141 | +```cmake |
| 142 | +# tests/foo/CMakeLists.txt |
| 143 | +file(GLOB FOO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_*.cc) |
| 144 | +
|
| 145 | +infini_train_add_test_suite(test_foo |
| 146 | + SOURCES ${FOO_SOURCES} |
| 147 | +) |
| 148 | +``` |
| 149 | + |
| 150 | +`file(GLOB test_*.cc)` 会自动拾取目录下所有测试文件。以后新增 `test_foo_advanced.cc` 只需放入目录,无需改 CMakeLists.txt(重新 cmake 即可)。 |
| 151 | + |
| 152 | +**Step 3: 注册到顶层** |
| 153 | + |
| 154 | +在 `tests/CMakeLists.txt` 中添加一行: |
| 155 | + |
| 156 | +```cmake |
| 157 | +add_subdirectory(foo) |
| 158 | +``` |
| 159 | + |
| 160 | +**生成的 CTest target:** `test_foo_cpu`、`test_foo_cuda`,可通过 `ctest -L cpu` 等按标签筛选。 |
| 161 | + |
| 162 | +### 3.2 在已有目录新增测试文件 |
| 163 | + |
| 164 | +所有使用 `file(GLOB ...)` 的目录(autograd、tensor、optimizer、hook、lora): |
| 165 | + |
| 166 | +1. 把新文件放入对应目录,命名为 `test_*.cc` |
| 167 | +2. 重新 `cmake ..`(glob 在 configure 时求值) |
| 168 | +3. 完成 |
| 169 | + |
| 170 | +无需修改任何 CMakeLists.txt。 |
| 171 | + |
| 172 | +### 3.3 工具函数速查 |
| 173 | + |
| 174 | +`test_utils.h` 提供的常用工具: |
| 175 | + |
| 176 | +| 函数 / 宏 | 用途 | |
| 177 | +|-----------|------| |
| 178 | +| `GetDevice()` | 返回当前参数化的 `Device`(基类方法) | |
| 179 | +| `createTensor(shape, dtype, requires_grad)` | 在当前设备创建 tensor(`InfiniTrainTest` 基类方法) | |
| 180 | +| `FillSequentialTensor(tensor, start)` | 填充递增值,自动处理 Device tensor(先填 CPU 再 copy) | |
| 181 | +| `SKIP_CPU()` | 跳过 CPU 实例 | |
| 182 | +| `ONLY_CPU()` | 只在 CPU 实例运行 | |
| 183 | +| `ONLY_CUDA()` | 只在 CUDA 实例运行 | |
| 184 | +| `REQUIRE_MIN_DEVICES(n)` | 加速器设备不足时 skip | |
| 185 | + |
| 186 | +## 4. 扩展新设备平台(以沐曦 MACA 为例) |
| 187 | + |
| 188 | +当前测试体系围绕 CPU / CUDA 两种设备参数化。如果需要支持新平台(以沐曦 MACA 为例),需要改动以下几处: |
| 189 | + |
| 190 | +### 4.1 框架层:注册新设备类型 |
| 191 | + |
| 192 | +在 `infini_train/include/device.h` 的 `DeviceType` 枚举中新增: |
| 193 | + |
| 194 | +```cpp |
| 195 | +enum class DeviceType : int8_t { |
| 196 | + kCPU = 0, |
| 197 | + kCUDA = 1, |
| 198 | + kMACA = 2, // 新增 |
| 199 | +}; |
| 200 | +``` |
| 201 | +
|
| 202 | +### 4.2 测试工具层:`test_utils.h` |
| 203 | +
|
| 204 | +1. 新增运行时检测函数和 `CudaDeviceTypes` 的对称版本: |
| 205 | +
|
| 206 | +```cpp |
| 207 | +#ifdef USE_MACA |
| 208 | +inline int GetMacaDeviceCount() { /* macaGetDeviceCount ... */ } |
| 209 | +#else |
| 210 | +inline int GetMacaDeviceCount() { return 0; } |
| 211 | +#endif |
| 212 | +inline bool HasMacaRuntime() { return GetMacaDeviceCount() > 0; } |
| 213 | +
|
| 214 | +inline std::vector<Device::DeviceType> MacaDeviceTypes() { |
| 215 | + if (HasMacaRuntime()) { |
| 216 | + return {Device::DeviceType::kMACA}; |
| 217 | + } |
| 218 | + LOG(INFO) << "No MACA runtime found, skipping MACA tests."; |
| 219 | + return {}; |
| 220 | +} |
| 221 | +``` |
| 222 | + |
| 223 | +2. 新增 `ONLY_MACA()` 宏: |
| 224 | + |
| 225 | +```cpp |
| 226 | +#define ONLY_MACA() \ |
| 227 | + do { if (GetParam() != infini_train::Device::DeviceType::kMACA) { GTEST_SKIP() << "MACA-only test"; } } while (0) |
| 228 | +``` |
| 229 | +
|
| 230 | +### 4.3 注册宏:新增 MACA 实例 |
| 231 | +
|
| 232 | +```cpp |
| 233 | +#define INFINI_TRAIN_REGISTER_TEST(TestName) \ |
| 234 | + INSTANTIATE_TEST_SUITE_P(CPU, TestName, \ |
| 235 | + ::testing::Values(infini_train::Device::DeviceType::kCPU)); \ |
| 236 | + INSTANTIATE_TEST_SUITE_P(CUDA, TestName, \ |
| 237 | + ::testing::ValuesIn(infini_train::test::CudaDeviceTypes())); \ |
| 238 | + INSTANTIATE_TEST_SUITE_P(MACA, TestName, \ |
| 239 | + ::testing::ValuesIn(infini_train::test::MacaDeviceTypes())) |
| 240 | +``` |
| 241 | + |
| 242 | +### 4.4 CMake 层:`test_macros.cmake` |
| 243 | + |
| 244 | +将默认 label 列表从 `cpu cuda` 扩展为 `cpu cuda maca` |
| 245 | + |
| 246 | +### 4.5 检查清单 |
| 247 | + |
| 248 | +| 步骤 | 文件 | 改动 | |
| 249 | +|------|------|------| |
| 250 | +| 1 | `device.h` | `DeviceType` 枚举新增 `kMACA` | |
| 251 | +| 2 | `test_utils.h` | 新增 `GetMacaDeviceCount()` / `HasMacaRuntime()` / `MacaDeviceTypes()` / `ONLY_MACA()` | |
| 252 | +| 3 | `test_utils.h` | `INFINI_TRAIN_REGISTER_TEST` 新增 MACA 实例 | |
| 253 | +| 4 | `test_macros.cmake` | 将默认 label 列表扩展为 `cpu cuda maca` | |
| 254 | +| 5 | `CMakeLists.txt`(根) | 新增 `USE_MACA` option + MACA SDK 查找 + kernel 编译 | |
0 commit comments