gpt4 book ai didi

重新执行过程的 Golang 代码覆盖率?

转载 作者:行者123 更新时间:2023-12-01 20:22:29 28 4
gpt4 key购买 nike

为了在特定条件下发现 Linux 命名空间,我的开源 Golang 包 lxkns需要重新执行它作为新子进程使用的应用程序,以便能够在 Golang 运行时启动之前切换挂载命名空间。 Linux之道mount namespaces work 使得在运行时启动 OS 线程后无法从 Golang 应用程序切换它们。

这意味着原始进程“P”重新运行自己的副本作为子“C”(reexec 包),通过子环境传递一个特殊指示,通知子进程只运行特定的“ Action ”属于包含的“lxkns”包的函数(详见下文),而不是正常运行整个应用程序(避免无休止的递归产生子代)。

forkchild := exec.Command("/proc/self/exe")
forkchild.Start()
...
forkchild.Wait()

目前,我从 VisualStudio Code 调用覆盖测试,它运行:

go test -timeout 30s -coverprofile=/tmp/vscode-goXXXXX/go-code-cover github.com/thediveo/lxkns

因此,“P”重新执行自身的副本“C”,并告诉它运行一些 Action “A”,将一些结果打印到标准输出,然后立即终止。 “P”等待“C”的输出,对其进行解析,然后继续其程序流程。

模块测试使用 Ginkgo/Gomega 和专用的 TestMain为了捕捉测试何时作为子项重新执行,以便仅运行请求的“ Action ”功能。

package lxkns

import (
"os"
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/thediveo/gons/reexec"
)

func TestMain(m *testing.M) {
// Ensure that the registered handler is run in the re-executed child. This
// won't trigger the handler while we're in the parent, because the
// parent's Arg[0] won't match the name of our handler.
reexec.CheckAction()
os.Exit(m.Run())
}

func TestLinuxKernelNamespaces(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "lxkns package")
}

我还想从重新执行的子进程创建代码覆盖率数据。
  • 是否可以从被测程序本身启用代码覆盖,以及如何实现?
  • 是否可以将子进程写入的代码覆盖率数据附加到父进程“P”的覆盖率数据中?
  • Golang 运行时是否仅在退出时写入覆盖率数据并覆盖指定的文件,还是追加? (如果有指向相应运行时源的指针,我已经很高兴了。)

  • 注意:在我的测试用例中,切换挂载命名空间不会与在新挂载命名空间中创建覆盖文件冲突。原因是这些测试挂载命名空间是初始挂载命名空间的副本,因此创建新文件也会正常显示在文件系统中。

    最佳答案

    在@Volker 对我的 Q 发表评论后,我知道我必须接受挑战并直接前往 Go 的源代码 testing包裹。虽然@marco.m 的建议在许多情况下都有帮助,但它无法处理我公认的有点奇怪的用例。 testing与我原来的问题相关的机制如下,大大简化:

  • cover.go : 实现 coverReport()写入覆盖数据文件(ASCII 文本格式);如果文件已经存在(之前运行的旧版本),那么它将首先被截断。请注意 coverReport()有将一些“统计”信息打印到 os.Stdout 的烦人习惯。
  • testing.go :
  • 获取 CLI 参数 -test.coverprofile=-test.outputdir=来自 os.Args (通过标志包)。如果还实现 toOutputDir(path)将封面配置文件放在 -test.outputdir 中如果指定。
  • 但是 coverReport() 什么时候被叫?简单来说,在testing.M.Run()结尾.

  • 现在有了这些知识,一个疯狂的解决方案开始出现,有点“变坏”;)
  • 包裹 testing.M在一个特殊的启用重新执行的版本中 reexec.testing.M :它检测它是否在启用覆盖的情况下运行:
  • 如果它是“父”进程 P,那么它正常运行测试,然后它从重新执行的子进程 C 中收集覆盖率配置文件数据文件并将它们合并到 P 的覆盖率配置文件数据文件中。
  • 而在 P 中,当即将重新执行一个新的子 C 时,为子 C 分配一个新的专用覆盖配置文件数据文件名。然后 C 通过其“个人”-test.coverprofile= 获取文件名CLI 参数。
  • 在 C 中,我们运行所需的 Action 函数。接下来,我们需要运行一个空的测试集以触发写入 C 的覆盖率配置文件数据。为此,P 中的重新执行函数添加了 test.run=。具有非常特殊的“Bielefeld 测试模式”,很可能会导致空结果。请记住,P 将——在它运行完所有测试之后——拾取单独的 C 覆盖率配置文件数据文件并将它们合并到 P 中。
  • 如果未启用覆盖分析,则无需采取任何特殊操作。

  • 这个解决方案的缺点是它依赖于 Go 的 testing 的一些不受保证的行为。关于它如何以及何时编写代码覆盖率报告。但由于 Linux 内核命名空间发现包已经比 Docker 的 libnetwork 更难插入 Go,这只是一个超出边缘的量子。

    对于测试开发人员来说,整个辣酱 Jade 米饼馅都隐藏在“增强版” rxtst.M 中。包装。

    import (
    "testing"
    rxtst "github.com/thediveo/gons/reexec/testing"
    )

    func TestMain(m *testing.M) {
    // Ensure that the registered handler is run in the re-executed child.
    // This won't trigger the handler while we're in the parent. We're using
    // gons' very special coverage profiling support for re-execution.
    mm := &rxtst.M{M: m}
    os.Exit(mm.Run())
    }


    全程跑 lxkns具有覆盖率的测试套件,最好使用 go-acc (进行准确的代码覆盖率计算),然后在下面的屏幕截图中显示函数 discoverNsfsBindMounts()运行一次 (1)。此函数不会从 P 中的任何位置直接调用。相反,此函数已注册,然后在重新执行的子 C 中运行。以前,没有报告 discoverNsfsBindMounts() 的代码覆盖率。 ,但现在借助包 github.com/thediveo/gons/reexec/testing C 的代码覆盖率透明地合并到 P 的代码覆盖率中。

    enter image description here

    关于重新执行过程的 Golang 代码覆盖率?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60458049/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com