Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/dunitest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Dunitest x86_64

on:
workflow_dispatch:
push:
branches: ["master", "feat-*", "fix-*"]
pull_request:
branches: ["master", "feat-*", "fix-*"]

env:
ARCH: x86_64
HOME: /root
RUSTUP_DIST_SERVER: "https://rsproxy.cn"
RUSTUP_UPDATE_ROOT: "https://rsproxy.cn/rustup"

jobs:
dunitest:
name: Dunitest
runs-on: ubuntu-latest
timeout-minutes: 30
container:
image: dragonos/dragonos-dev:v1.22
options: --privileged -v /dev:/dev
steps:
- name: Checkout DragonOS code
uses: actions/checkout@v4

- name: Change source
run: |
find . -type f \( -name "*.toml" -o -name "Makefile" \) -exec sed -i 's/git\.mirrors\.dragonos\.org\.cn/github\.com/g' {} +

- name: Build DragonOS
shell: bash -ileo pipefail {0}
run: |
make clean
make -j$(nproc) all

- name: Run dunitest
shell: bash -ileo pipefail {0}
env:
DISK_SAVE_MODE: "1"
run: |
make test-dunit
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,22 @@ test-syscall: prepare_rootfs_manifest
exit $$status; \
}

test-dunit: prepare_rootfs_manifest
@echo "构建运行并执行dunitest测试"
$(MAKE) all -j $(NPROCS)
@if [ "$(DISK_SAVE_MODE)" = "1" ]; then \
echo "磁盘节省模式启用,正在清理用户程序构建缓存..."; \
$(DADK) -f $(ROOT_PATH)/dadk-manifest.generated.toml user clean --level in-src -w $(ROOT_PATH); \
fi
SKIP_GRUB=1 $(MAKE) write_diskimage || exit 1
$(MAKE) qemu-nographic AUTO_TEST=dunit DUNITEST_DIR=/opt/tests/dunitest &
sleep 5
@bash user/apps/tests/dunitest/monitor_test_results.sh

test-dunit-local:
@echo "构建并执行 dunitest 本地测试"
$(MAKE) -C user/apps/tests/dunitest test-local -j $(NPROCS)

fmt: check_arch
@echo "格式化代码"
FMT_CHECK=$(FMT_CHECK) $(MAKE) fmt -C kernel
Expand Down Expand Up @@ -342,6 +358,9 @@ help:
@echo " make clean-docs - 清理文档"
@echo " make test-syscall - 构建运行并执行syscall测试"
@echo " - 可通过DISK_SAVE_MODE=1启用磁盘节省模式"
@echo " make test-dunit - 构建运行并执行dunitest测试"
@echo " - 可通过DISK_SAVE_MODE=1启用磁盘节省模式"
@echo " make test-dunit-local - 本地运行 dunitest 测例"
@echo ""
@echo "环境变量:"
@echo " DISK_SAVE_MODE=1 - 启用磁盘节省模式,在写入磁盘镜像前清理构建缓存"
Expand Down
137 changes: 137 additions & 0 deletions docs/kernel/ktest/dunitest.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
==============================
dunitest 用户态测试框架
==============================

dunitest 是 DragonOS 的用户态单元测试框架,用于运行基于 Google Test 的 C++ 测例并输出结构化报告。


现状
====

- runner 自动发现 ``bin/`` 下的可执行文件并执行
- 默认超时 60 秒,可通过 ``--timeout-sec`` 覆盖
- 测例过滤顺序:white list -> block list -> --pattern
- 汇总统计默认按 gtest 的测试用例数聚合(不是按测试程序个数)
- 测试失败或超时时,runner 返回非 0

目录与职责
==========

.. code-block:: text

user/apps/tests/dunitest/
├── runner/ # Rust 测试运行器
├── suites/ # 测试源码(按 suite 分目录)
├── bin/ # 编译产物(runner 自动发现)
├── whitelist.txt # 默认白名单
├── scripts/run_tests.sh # 系统内执行入口
└── Makefile

关键规则
========

1. 源码位置:``suites/<suite>/*.cc``
2. 编译输出:``bin/<suite>/<case>_test``
3. runner 用例名:``<suite>/<case>``(自动去掉 ``_test`` 后缀)

示例:

- 二进制:``bin/demo/gtest_demo_test``
- 用例名:``demo/gtest_demo``
- white list 条目:``demo/gtest_demo``

如何新增测例
============

推荐:普通功能测试优先放在 ``normal`` suite
---------------------------------------

- 普通/通用功能测例建议统一放在 ``suites/normal/`` 下,便于集中维护
- 示例:``suites/normal/capability.cc``
- 在 ``whitelist.txt`` 中对应条目写作:``normal/capability``

1. 新增 gtest 源码
-----------------

新增文件,例如:

.. code-block:: text

suites/normal/capability.cc

2. 把 suite 加入 Makefile
-------------------------

编辑 ``user/apps/tests/dunitest/Makefile`` 的 ``SUITES``:

.. code-block:: makefile

# 如果新增了目录,需要在这里加入
SUITES = demo normal

3. 构建并运行(支持并行)
----------------------

在仓库根目录:

.. code-block:: bash

make test-dunit-local

或在 dunitest 目录:

.. code-block:: bash

make run -j$(nproc)

构建日志示例:

.. code-block:: text

编译测例: suites/normal/capability.cc -> bin/normal/capability_test

4. 加入 white list
--------------------------

编辑 ``whitelist.txt``,每行一个用例名:

.. code-block:: text

demo/gtest_demo
normal/capability

Runner 参数
===========

.. code-block:: text

dunitest-runner [OPTIONS]

--bin-dir <PATH> 测试二进制目录(默认: bin)
--timeout-sec <SEC> 单测超时秒数(默认: 60)
--whitelist <PATH> white list 路径(默认: whitelist.txt)
--blocklist <PATH> block list 路径(默认: blocklist.txt)
--results-dir <PATH> 报告目录(默认: results)
--list 仅列出测例,不执行
--verbose 详细输出
--pattern <PATTERN> 名称子串过滤(可多次指定)

报告输出
========

执行后在 ``results/`` 下生成:

- ``test_report.txt``:文本报告
- ``summary.json``:JSON 汇总
- ``failed_cases.txt``:失败/超时列表
- ``<case>.log``:单测日志

终端汇总口径说明:

- ``总测试数/通过/失败/跳过`` 按 gtest 用例数统计
- 当某个程序没有产出 gtest 统计信息时,才按测试程序粒度回退统计

安装说明
========

在 ``user/apps/tests/dunitest/`` 目录下执行 ``make install`` 即可
1 change: 1 addition & 0 deletions docs/kernel/ktest/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
.. toctree::
:maxdepth: 1

dunitest
gvisor_syscall_test

5 changes: 4 additions & 1 deletion tools/run-qemu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,12 @@ KERNEL_CMDLINE=" "
# 自动测试选项,支持的选项:
# - none: 不进行自动测试
# - syscall: 进行gvisor系统调用测试
# - dunit: 进行dunitest测试
AUTO_TEST=${AUTO_TEST:=none}
# gvisor测试目录
SYSCALL_TEST_DIR=${SYSCALL_TEST_DIR:=/opt/tests/gvisor}
# dunitest测试目录
DUNITEST_DIR=${DUNITEST_DIR:=/opt/tests/dunitest}

BIOS_TYPE=""
#这个变量为true则使用virtio磁盘
Expand Down Expand Up @@ -260,7 +263,7 @@ while true;do

setup_kernel_init_program() {
if [ ${ARCH} == "x86_64" ]; then
KERNEL_CMDLINE+=" init=/bin/busybox init AUTO_TEST=${AUTO_TEST} SYSCALL_TEST_DIR=${SYSCALL_TEST_DIR} "
KERNEL_CMDLINE+=" init=/bin/busybox init AUTO_TEST=${AUTO_TEST} SYSCALL_TEST_DIR=${SYSCALL_TEST_DIR} DUNITEST_DIR=${DUNITEST_DIR} "
# KERNEL_CMDLINE+=" init=/bin/dragonreach "
elif [ ${ARCH} == "riscv64" ]; then
KERNEL_CMDLINE+=" init=/bin/riscv_rust_init "
Expand Down
5 changes: 5 additions & 0 deletions user/apps/tests/dunitest/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/target
/results
/install
/third_party
/bin
90 changes: 90 additions & 0 deletions user/apps/tests/dunitest/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
ifdef DADK_CURRENT_BUILD_DIR
INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
else
INSTALL_DIR = ./install
endif

TOOLCHAIN = +nightly-2025-08-10-x86_64-unknown-linux-gnu
RUSTFLAGS +=
ifeq ($(ARCH), x86_64)
export RUST_TARGET = x86_64-unknown-linux-musl
else ifeq ($(ARCH), riscv64)
export RUST_TARGET = riscv64gc-unknown-linux-gnu
else
export RUST_TARGET = x86_64-unknown-linux-musl
endif

RUNNER_DIR = runner
RUNNER_BIN = $(RUNNER_DIR)/target/$(RUST_TARGET)/release/dunitest-runner
RESULTS_DIR = results
BIN_DIR = bin

# 要编译的测试套件目录列表(suites/ 下的子目录名)
SUITES = demo normal

GTEST_ROOT = third_party/googletest
GTEST_REPO = https://git.mirrors.dragonos.org.cn/DragonOS-Community/googletest
GTEST_TAG = v1.17.0
GTEST_SRC = $(GTEST_ROOT)/googletest/src/gtest-all.cc
GTEST_TMP_ROOT = $(GTEST_ROOT).tmp
CXX ?= g++
CXXFLAGS ?= -Wall -O2 -std=c++17 -pthread
GTEST_INCLUDES = -I$(GTEST_ROOT)/googletest -I$(GTEST_ROOT)/googletest/include
SUITE_SRCS = $(foreach suite,$(SUITES),$(wildcard suites/$(suite)/*.cc))
TEST_BINS = $(patsubst suites/%.cc,$(BIN_DIR)/%_test,$(SUITE_SRCS))

.PHONY: all build build-suites run list test-local install clean fmt fetch-gtest

all: build install

build: build-suites
@echo "构建 dunitest runner..."
@cd $(RUNNER_DIR) && RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release

fetch-gtest: $(GTEST_SRC)

$(GTEST_SRC):
@echo "拉取 googletest ($(GTEST_TAG))..."
@rm -rf "$(GTEST_TMP_ROOT)"
@mkdir -p "$(dir $(GTEST_ROOT))"
@if [ -d "$(GTEST_ROOT)" ]; then \
echo "检测到残缺的 $(GTEST_ROOT),重新拉取..."; \
rm -rf "$(GTEST_ROOT)"; \
fi
@git clone --depth 1 --branch "$(GTEST_TAG)" "$(GTEST_REPO)" "$(GTEST_TMP_ROOT)"
@mv "$(GTEST_TMP_ROOT)" "$(GTEST_ROOT)"
@test -f "$(GTEST_SRC)"

build-suites: $(TEST_BINS)

$(BIN_DIR)/%_test: suites/%.cc $(GTEST_SRC)
@mkdir -p "$(dir $@)"
@echo "编译测例: $< -> $@"
@$(CXX) $(CXXFLAGS) $(GTEST_INCLUDES) "$<" $(GTEST_SRC) -o "$@"

run: build
@$(RUNNER_BIN) --bin-dir $(BIN_DIR) --results-dir $(RESULTS_DIR)

list: build
@$(RUNNER_BIN) --bin-dir $(BIN_DIR) --list

test-local: build
@$(RUNNER_BIN) --bin-dir $(BIN_DIR) --results-dir $(RESULTS_DIR) --verbose

install: build
@echo "安装 dunitest 到 $(INSTALL_DIR)"
@mkdir -p $(INSTALL_DIR)
@cp -f $(RUNNER_BIN) $(INSTALL_DIR)/dunitest-runner
@chmod +x $(INSTALL_DIR)/dunitest-runner
@cp -rf $(BIN_DIR) $(INSTALL_DIR)/
@cp -f whitelist.txt $(INSTALL_DIR)/whitelist.txt
@cp -f scripts/run_tests.sh $(INSTALL_DIR)/run_tests.sh
@chmod +x $(INSTALL_DIR)/run_tests.sh
@echo "安装完成"

clean:
@rm -rf $(RESULTS_DIR) install $(BIN_DIR)
@cd $(RUNNER_DIR) && cargo clean

fmt:
@cd $(RUNNER_DIR) && cargo fmt
Loading
Loading