【免费下载链接】dgl
Python package built to ease deep learning on graph, on top of existing DL frameworks.
导读
本文以 DGL 仓库根目录下的 tests/README.md 为核心骨架,系统梳理 DGL 单元测试的目录组织原则、Python 与 C++ 两套测试体系、测试运行方式以及 CI 自动化脚本,并结合仓库内的 tests/、script/build_dgl.sh、CMakeLists.txt 等源码与配置给出可验证的底层依据。读完本文,你将掌握 DGL 测试代码的放置规范、如何编译并运行 C++ 单元测试、如何在本地与 CI 中跑 Python 单元测试,以及各环境变量与脚本参数的真实作用。
一、测试体系总览
tests/README.md 开宗明义地将 DGL 的单元测试划分为两大部分:Python Unittest 与 C++ Unittest。前者覆盖图构建、消息传递、数据加载等 Python 层功能;后者直接针对 C++ 图结构实现(如 unit_graph、sparse matrix、采样器、序列化等)做白盒验证。
按 README 所述,Python 测试的代码组织遵循如下原则:
| 目录(README 中的逻辑名) | 职责 |
|---|---|
backend | 面向被支持深度学习框架的附加统一张量接口,仅供单元测试使用,不属于 DGL 本体,且其代码本身不是单元测试 |
compute | 所有框架无关的计算相关单元测试 |
${DGLBACKEND}(如 pytorch、mxnet) | 所有框架特定的计算相关单元测试 |
graph_index | C++ 图结构实现的单元测试;若涉及 Python API,应保持最小化(通常是 C++ 函数的简单包装) |
lint | Pylint 相关文件 |
scripts | CI 自动化测试脚本 |
对照当前仓库实际布局,上述逻辑目录在 tests/ 下落实为:backend 对应 tests/backend/,框架无关测试落在 tests/python/common/,框架特定测试落在 tests/python/pytorch/、tests/python/mxnet/、tests/python/tensorflow/,graph_index 对应的 C++ 测试落在 tests/cpp/,lint 与 scripts 则分别对应 tests/lint/ 与 tests/scripts/。README 描述的是设计原则,实际仓库在 tests/python/ 下用 common 与 ${DGLBACKEND} 两级子目录落实了"框架无关 / 框架特定"的隔离思想。
二、Python 单元测试
2.1 统一的测试后端接口(backend)
DGL 支持在 PyTorch、MXNet、TensorFlow 等深度学习框架之上运行,因此单元测试需要一套与框架无关的断言与张量操作接口,这就是 backend 目录存在的意义。tests/backend/backend_unittest.py 定义了该接口的"契约":cuda()、is_cuda_available()、array_equal()、allclose()、randn()、full()、sparse_to_numpy()、softmax()、spmm()、add/sub/mul/div、sum/max/min/prod、matmul()、dot() 等。文件中所有函数体均为 pass,只声明签名与语义,真正的实现由各框架子目录提供。
以 PyTorch 为例,tests/backend/pytorch/init.py 中:
cuda()返回th.device("cuda:0"),is_cuda_available()委托th.cuda.is_available();array_equal(a, b)通过th.equal(a.cpu(), b.cpu())实现精确相等比较;allclose(a, b, rtol=1e-4, atol=1e-4)将张量转 float 并移到 CPU 后调用th.allclose,默认相对容差与绝对容差均为1e-4;softmax(x, dim)直接映射th.softmax,spmm(x, y)映射th.spmm。
这意味着所有框架无关的测试用例只需 import backend as F(见 tests/utils/checks.py 首行),即可在切换 DGLBACKEND 时复用同一套断言逻辑,这是 DGL 多框架支持在测试层的直接体现。
2.2 测试辅助工具(tests/utils)
tests/utils/init.py 导出四个常用断言助手:
check_fail(fn, *args, **kwargs):执行函数并断言其抛异常(用于负向测试);assert_is_identical(g, g2):比较两个同构图的节点/边数、all_edges(order="eid")的 src/dst、以及ndata/edata各特征是否一致;assert_is_identical_hetero(g, g2, ignore_internal_data=False):比较异构图,覆盖ntypes、canonical_etypes、meta 图、各类型节点/边特征,并支持剔除is_internal_column标记的内部数据列后再比较;check_graph_equal(g1, g2, *, check_idtype=True, check_feature=True):逐项校验 device、idtype、类型集合、batch 大小、meta 图、每类型的节点/边数与特征,是当前测试中使用最广泛的"图相等"判据。
此外,tests/python/common/test_convert.py 等用例会使用 utils.parametrize_idtype 对 int32/int64 两种节点 id 类型做参数化,可见测试基础设施与 DGL 的 idtype 特性是配套设计的。
2.3 框架无关与框架特定测试的划分
README 强调 compute(框架无关)与 ${DGLBACKEND}(框架特定)分离。实际仓库中:
- tests/python/common/ 下聚集了大量与框架无关的用例,例如
test_batch-graph.py、test_batch-heterograph.py、test_convert.py、test_frame.py、test_heterograph*.py、test_subgraph.py、test_traversal.py、test_readout.py,以及dataloading/、data/、sampling/、transforms/、ops/、function/等子目录; tests/python/pytorch/、tests/python/mxnet/、tests/python/tensorflow/则存放强依赖具体框架语义的用例(如 PyTorch 的nn/模块、optim/、sparse/等)。
特别的,tests/python/test_dgl_import.py 验证了延迟导入约束:import dgl 之后 dgl.graphbolt 不得出现在 sys.modules 中,即 graphbolt 子模块不允许在导入 dgl 主包时被连带加载。这既是性能约束,也是"模块边界清晰"这一工程原则的测试化表达。
2.4 本地运行 Python 单元测试
仓库提供了两个入口:
方式一:直接使用 pytest
先确保已编译 DGL 库并设置好环境变量(以 PyTorch 后端为例):
export DGLBACKEND=pytorch
export DGL_LIBRARY_PATH=${DGL_HOME}/build
export PYTHONPATH=${DGL_HOME}/python:${DGL_HOME}/tests
python3 -m pytest -v tests/python/common/test_convert.py
方式二:使用 script/run_pytest.sh
该脚本封装了上述环境准备(-c 指定 CPU、-g 指定 GPU):
# 需先设置 DGL_HOME 并在仓库根目录执行
bash script/run_pytest.sh -c tests/python/common/test_subgraph.py
bash script/run_pytest.sh -g tests/python/pytorch/test_nn.py
脚本内部固定 DGLBACKEND=pytorch,设置 DGL_LIBRARY_PATH=${DGL_HOME}/build、PYTHONPATH(含 python、tests 及 graphbolt 测试目录)、DGLTESTDEV 与 DGL_DOWNLOAD_DIR,随后执行 python3 -m pytest -v $@。需要注意的是,-g 只负责设置测试设备环境,实际 CUDA 可用性仍取决于宿主机驱动与已安装的框架版本。
三、C++ 单元测试
3.1 构建与运行
README 给出的 C++ 单元测试编译命令为:
# 假设当前目录为 dgl 仓库根目录,且 googletest 子模块已初始化
bash script/build_dgl.sh -c -r
./runUnitTests
逐项解读这两个命令:
script/build_dgl.sh的参数语义(见脚本头部的 usage):-c表示"重启一次仅 CPU 的构建"(-g为 CUDA 构建),-r表示"移除所有中间产物"(删除build、graphbolt/build、dgl_sparse/build、tensoradapter/pytorch/build后重新 cmake)。脚本要求DGL_HOME环境变量已设置,且必须在仓库根目录执行;-r触发清理后,脚本以cmake -DBUILD_TYPE=dev -DUSE_CUDA=OFF ..配置构建目录,随后make -j编译;- 是否生成
runUnitTests由 CMakeLists.txt 中的BUILD_CPP_TEST选项控制:开启后add_subdirectory(./third_party/googletest),通过file(GLOB_RECURSE TEST_SRC_FILES ${PROJECT_SOURCE_DIR}/tests/cpp/*.cc)收集tests/cpp/下所有*.cc,构建出链接gtest、gtest_main与dgl库的可执行目标runUnitTests,并注册add_test(UnitTests runUnitTests)。
因此 ./runUnitTests 实际运行的是 tests/cpp/ 下全部 C++ 用例的聚合测试程序,其产物位于 build/ 目录。若在 build/ 目录内执行,亦可直接用 ./runUnitTests。
3.2 测试覆盖范围
tests/cpp/ 中的测试文件与 DGL 核心 C++ 模块一一对应,主要包括:
- 图结构:
test_unit_graph.cc、graph_index_test.cc、test_spmat_coo.cc、test_spmat_csr.cc; - 矩阵与算子:
test_csrmm.cc、test_spmm.cc、test_rowwise.cc、test_aten.cc; - 采样与序列化:
test_sampler.cc、test_serialize.cc、test_zerocopy_serialize.cc、test_smart_ptr_serialize.cc; - 分布式与并发基础设施:
test_partition.cc、message_queue_test.cc、socket_communicator_test.cc、test_concurrent_id_hash_map.cc、string_test.cc。
这些用例直接操作 DGL 的 C++ 张量(NDArray)与图数据结构,印证了 README 中"graph_index 目录测试的是 C++ 图结构实现,Python API 层应保持最小"的组织原则——Python 侧只需做薄薄的封装即可复用。
3.3 公共测试头文件
tests/cpp/common.h 提供了 C++ 用例的公共设施:
- 上下文常量:
CTX = DGLContext{kDGLCPU, 0}、CPU,以及在DGL_USE_CUDA宏开启时的GPU; - 指针与长度助手:
Ptr<T>(nd)、PI64(nd)、PI32(nd)、Len(nd),用于直接读取dgl::runtime::NDArray的底层数据; - 断言助手:
ArrayEQ<T>(a1, a2)比较两个张量的 ndim、dtype、ctx、shape 与逐元素值(比较前自动CopyTo(CPU));IsInArray<T>(a, x)判断标量是否存在于张量中。
#ifdef DGL_USE_CUDA 的条件编译说明:若以 CUDA 方式构建,C++ 测试同样可以覆盖 GPU 路径,否则 GPU 上下文相关代码不会参与编译。
四、CI 自动化脚本(tests/scripts)
tests/scripts/ 中的 task_*.sh 是 CI 流水线(仓库根目录的 Jenkinsfile 及各 task 脚本)实际调用的入口,本地开发时也可复用以模拟 CI 环境。
4.1 Python 单元测试脚本 task_unit_test.sh
tests/scripts/task_unit_test.sh 用法为 bash task_unit_test.sh <backend> <device>,两个参数缺一不可。脚本核心行为:
- 设置
DGLBACKEND、DGLTESTDEV、DGL_LIBRARY_PATH=${PWD}/build、PYTHONPATH=tests:${PWD}/python、DGL_DOWNLOAD_DIR=${PWD}/_download等环境变量; - device 为
gpu时导出CUDA_VISIBLE_DEVICES=0,否则设为-1(彻底屏蔽 GPU); - 激活
${DGLBACKEND}-ci的 conda 环境并安装expecttest; - 对非 MXNet 后端,先跑
tests/python/test_dgl_import.py验证延迟导入约束,再以--durations=100跑tests/python/common(输出最慢的 100 个用例);MXNet 后端则跳过 import 检查并忽略test_ffi.py; - 最后执行
tests/python/${DGLBACKEND}的框架特定用例,每阶段失败都会以fail函数终止脚本并返回非零退出码。
4.2 C++ 单元测试脚本 task_cpp_unit_test.sh
tests/scripts/task_cpp_unit_test.sh 十分精简:pushd build 后导出 LD_LIBRARY_PATH=$PWD(确保运行时能找到 libdgl),直接执行 ./runUnitTests,失败则退出。由此可见,CI 中的 C++ 测试依赖前置的构建阶段(对应 tests/scripts/build_dgl.sh,它负责 cmake + make 并分别以 pytorch、mxnet、tensorflow 后端构建 Python 包)已经产出 runUnitTests。
4.3 其他专项脚本
- tests/scripts/task_lint.sh:先通过
tests/lint/lint.py对dgl、dgl_sparse的 C++ 代码做 cpplint 风格检查,再用pylint --rcfile=tests/lint/pylintrc python/dgl检查 Python 代码风格; - tests/scripts/task_dist_test.sh:分布式测试,固定
DGLBACKEND=pytorch,依据DIST_DGL_TEST_IP_CONFIG指定的机器列表逐台 SSH 验证pytest、psutil、torch是否安装,再运行tests/dist/test_*.py; task_distributed_test.sh、task_go_test.sh、task_example_test.sh、task_unit_test.bat、task_cpp_unit_test.bat等分别覆盖分布式、dglgo、示例回归及 Windows 平台场景。
五、测试相关的关键环境变量速查
结合 README、tests/scripts/task_unit_test.sh 与 script/run_pytest.sh,运行 DGL 测试涉及的环境变量可归纳如下:
| 环境变量 | 含义与典型取值 |
|---|---|
DGLBACKEND | 测试框架后端,如 pytorch、mxnet、tensorflow;决定加载 tests/backend/<backend> 接口与 tests/python/<backend> 用例 |
DGLTESTDEV | 测试设备,cpu 或 gpu,配合 CUDA_VISIBLE_DEVICES 使用 |
DGL_LIBRARY_PATH | 指向编译产物目录(通常是 ${PWD}/build),供 Python 层加载 libdgl |
PYTHONPATH | 至少包含 python 与 tests,确保 import dgl 与 import backend as F 可用 |
DGL_DOWNLOAD_DIR | 数据集下载缓存目录(CI 中为 ${PWD}/_download) |
CUDA_VISIBLE_DEVICES | -1 屏蔽 GPU,0 指定首块 GPU,用于设备维度切换 |
DIST_DGL_TEST_IP_CONFIG | 分布式测试的机器 IP 列表文件 |
DGL_HOME | 仓库根目录,build_dgl.sh 与 run_pytest.sh 均强制校验 |
六、小结:从组织原则到可执行实践
DGL 的测试体系可以用三句话概括其设计意图:
- 层次清晰:框架无关的公共逻辑(
backend接口、tests/python/common、tests/utils)与框架特定实现(tests/python/${DGLBACKEND})严格分离,保证多框架下一套用例多处运行; - 语言分层:图结构等底层核心由
tests/cpp/通过 googletest 直接验证,Python 侧只保留最小封装,避免重复测试; - CI 可复现:
tests/scripts/中的task_*.sh将构建、Python 测试、C++ 测试、lint、分布式测试固化为确定性的执行流程,本地开发者既可照搬task_unit_test.sh的运行方式,也可用script/run_pytest.sh快速跑单个目标文件。
对于希望在 DGL 上做二次开发或贡献代码的开发者,遵循 tests/README.md 的目录约定放置用例,并用 bash script/build_dgl.sh -c -r && ./runUnitTests 验证 C++ 层、用 pytest 或 script/run_pytest.sh 验证 Python 层,即可完整接入这套测试体系。
【免费下载链接】dgl
Python package built to ease deep learning on graph, on top of existing DL frameworks.
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



