From 0a477c04e1e7466949f65078ac52cfdc94144735 Mon Sep 17 00:00:00 2001 From: Bingchang Chen Date: Tue, 16 Jul 2024 02:27:08 -0700 Subject: [PATCH] add coverage to devcontainer (#1616) --- .devcontainer/Dockerfile | 14 ++++++++-- core/application/Application.cpp | 4 +-- docs/cn/developer-guide/test/unit-test.md | 32 ++++++++++++++++++++--- tools/coverage-diff/main.py | 10 ++++++- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 1e2e46070d..8d1733d393 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -28,6 +28,18 @@ RUN wget http://mirrors.ustc.edu.cn/gnu/libc/glibc-2.18.tar.gz && \ cd ../ && \ rm -fr glibc-2.18* +# install python3.8 +RUN cd /opt && curl -O https://cdn.npmmirror.com/binaries/python/3.8.12/Python-3.8.12.tgz && \ + tar -zxvf Python-3.8.12.tgz && cd Python-3.8.12 && \ + mkdir /usr/local/python3 && \ + ./configure --prefix=/usr/local/python3 && \ + make clean && make && make install && \ + cp /usr/local/python3/bin/python3.8 /usr/bin/python3 +# install gcovr +RUN python3 -m pip install --upgrade pip +RUN cp /usr/local/python3/bin/pip3 /usr/bin/pip3 && pip3 install gcovr==7.0 +RUN cp /usr/local/python3/bin/gcovr /usr/bin/gcovr + # Create the user COPY .env /tmp/.env RUN source /tmp/.env && rm /tmp/.env; \ @@ -43,6 +55,4 @@ RUN source /tmp/.env && rm /tmp/.env; \ chown -R $USERNAME:$GROUPNAME /opt $(eval echo ~$USERNAME); \ chmod -R 755 $(eval echo ~$USERNAME); -USER $USERNAME - RUN go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct diff --git a/core/application/Application.cpp b/core/application/Application.cpp index c38509b727..5b7c81c505 100644 --- a/core/application/Application.cpp +++ b/core/application/Application.cpp @@ -184,7 +184,7 @@ void Application::Init() { LOG_INFO(sLogger, ("app info", appInfo)); } -void Application::Start() { +void Application::Start() { // GCOVR_EXCL_START LogtailMonitor::GetInstance()->UpdateConstMetric("start_time", GetTimeStamp(time(NULL), "%Y-%m-%d %H:%M:%S")); #if defined(__ENTERPRISE__) && defined(_MSC_VER) @@ -296,7 +296,7 @@ void Application::Start() { this_thread::sleep_for(chrono::seconds(1)); } -} +} // GCOVR_EXCL_STOP void Application::GenerateInstanceId() { mInstanceId = CalculateRandomUUID() + "_" + LogFileProfiler::mIpAddr + "_" + ToString(mStartTime); diff --git a/docs/cn/developer-guide/test/unit-test.md b/docs/cn/developer-guide/test/unit-test.md index 8bcd700c91..4e3ddfa129 100644 --- a/docs/cn/developer-guide/test/unit-test.md +++ b/docs/cn/developer-guide/test/unit-test.md @@ -1,10 +1,36 @@ # 单元测试 -## 测试框架 +## C++单元测试 + +C++部分单测基于 gtest 实现,具体编写方法可以参考已有测试用例。 + +### 单测覆盖率统计 + +在开发镜像中,内置了C++单测覆盖率统计工具 gcovr,可以通过以下命令在本地查看覆盖率。 + +1. 进入开发容器中,参考[开发环境](../development-environment.md)。 +2. CMake 设置参数 BUILD_LOGTAIL_UT=ON,编译单测 +``` +cmake -DBUILD_LOGTAIL_UT=ON <其他编译参数> .. +``` +3. 运行脚本 `./scripts/run_core_ut.sh`,运行单测 +4. 生成覆盖率报告 + +```shell +mkdir -p coverage-report +# 生成详细的报告 +gcovr -r ./core --txt coverage-report/index.txt --html-details --html coverage-report/index.html -e ".*sdk.*" -e ".*observer.*" -e ".*log_pb.*" -e ".*unittest.*" -e ".*config_server.*" -e ".*fuse.*" -e ".*go_pipeline.*" +# 生成本次commit diff的报告 +python3 tools/coverage-diff/main.py coverage-report/index.txt +``` + +## Go插件单元测试 + +### 测试框架 如何需要一些测试指令,比如 `assert`, `require`, `mock` 以及 `suite`,可以使用这个包协助你进行测试: `github.com/stretchr/testify`。更多使用方法可以查看 [使用说明](https://github.com/stretchr/testify)。 -## 测试工具 +### 测试工具 从插件开发以及 [日志打印](How-to-use-logger.md) 篇幅可以看到,ilogtail.Context 接口包含了iLogtail 的元配置信息,因此提供了Mock Context 以及Mock Collector 实现进行单元测试。 @@ -40,6 +66,6 @@ func TestInputSystem_CollectOpenFD(t *testing.T) { } ``` -## 测试插件行为 +### 测试插件行为 如果需要日志的方式进行验证插件具体行为,可以参加[日志功能高级用法](../plugin-development/logger-api.md) diff --git a/tools/coverage-diff/main.py b/tools/coverage-diff/main.py index 81bacd14cf..27c4216a44 100644 --- a/tools/coverage-diff/main.py +++ b/tools/coverage-diff/main.py @@ -24,13 +24,21 @@ def get_changed_files(): parser.add_argument("path", type=str, help="The path of coverage file") args = parser.parse_args() changed_files = get_changed_files() + line_cache = "" not_satified = [] with open(args.path, 'r') as file: for line in file: + if len(line_cache) > 0: + line = line_cache + line + line_cache = "" if '/' in line or ('%' in line and 'TOTAL' not in line): for changed_file in changed_files: if line.startswith(changed_file): units = line.split() + if len(units) < 4: + # some files with long filename will be split into 2 lines + line_cache = line + continue coverage_rate = int(units[3][:-1]) if coverage_rate < 80: not_satified.append(changed_file) @@ -39,4 +47,4 @@ def get_changed_files(): else: print(line) if len(not_satified) > 0: - raise Exception(f"Coverage rate is less than 80% for the following files: {not_satified}") + print(f"Coverage rate is less than 80% for the following files: {not_satified}")