首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >重新执行过程的Golang代码覆盖率?

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

Stack Overflow用户
提问于 2020-02-28 19:22:57
回答 1查看 921关注 0票数 2

为了在一定条件下发现Linux名称空间,我的开源Golang包艾克恩斯需要重新执行它作为一个新的子进程使用的应用程序,以便能够在Golang运行时启动之前切换挂载名称空间。Linux的工作方式使得在运行时生成挂载命名空间线程之后无法将它们从Golang应用程序中切换出来。

这意味着最初的进程"P“-作为子进程"C”(雷克斯包)重新运行自己的副本,通过子进程的环境传递一个特殊的指示,该指示向子进程发出信号,只运行属于包含的"lxkns“包的特定"action”函数(请参阅下面的详细信息),而不是正常运行整个应用程序(避免无穷无尽的递归生成子程序)。

代码语言:javascript
复制
forkchild := exec.Command("/proc/self/exe")
forkchild.Start()
...
forkchild.Wait()

目前,我从VisualStudio代码调用覆盖率测试,该代码运行:

代码语言:javascript
复制
go test -timeout 30s -coverprofile=/tmp/vscode-goXXXXX/go-code-cover github.com/thediveo/lxkns

因此,"P“-重新执行副本"C”本身,并告诉它运行一些动作"A",打印一些结果到stdout,然后立即终止。"P“等待”C“的输出,解析它,然后继续它的程序流。

模块测试使用Ginkgo/Gomega和一个专用的TestMain,以便在测试作为子测试被重新执行时捕获,以便只运行所请求的"action“函数。

代码语言:javascript
复制
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运行时是只在退出时写入覆盖率数据,还是覆盖指定的文件,还是追加?(我已经很高兴获得指向相应运行时源的指针。)

注意:在我的测试用例中,切换挂载名称空间与在新的挂载命名空间中创建覆盖率文件没有冲突。原因是这些测试挂载名称空间是初始挂载名称空间的副本,所以创建一个新文件通常也会出现在文件系统中。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-03-07 23:34:45

在@Volker对我的问题发表评论之后,我知道我必须接受挑战,并直接搜索Go的testing包的源代码。虽然@marco.m的建议在许多情况下是有帮助的,但它不能处理我的一些小建议。与我最初的问题相关的testing的机制如下,大大简化了:

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

现在,带着这些知识,一个疯狂的解决方案开始出现,有点“走下坡路”;

  • testing.M包装在一个特殊的启用重执行的reexec.testing.M版本中:它检测它是否在启用覆盖率的情况下运行:
    • 如果它是“父”进程P,那么它将正常运行测试,然后从重新执行的子进程C收集覆盖率配置文件数据文件,并将它们合并到P的覆盖率概要数据文件中。
    • 在P中,当要重新执行新的子C时,将为子C分配一个新的专用覆盖配置文件数据文件名。然后,C通过它的“-test.coverprofile=”CLI获得文件名。
    • 在C中,我们运行所需的操作函数。接下来,我们需要运行一个空测试集来触发编写C的覆盖率配置文件数据。为此,P中的重新执行函数添加了一个具有非常特殊的“test.run=测试模式”的比勒费尔德,这很可能导致一个空的结果。记住,P会--在运行完所有测试之后--获取单个C覆盖率概要数据文件,并将它们合并到P中。

  • 当未启用覆盖率分析时,则不需要采取任何特殊操作。

这种解决方案的缺点是,它依赖于Go的testing在编写代码覆盖率报告的方式和时间方面的一些未得到保证的行为。但是,由于Linux内核命名空间发现包已经比Docker的libnetwork更难实现,所以这只是一个更远的量程。

对于测试开发人员来说,整个enchilada隐藏在一个“增强的”rxtst.M包装器中。

代码语言:javascript
复制
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 (go精确代码覆盖率计算),然后在下面的屏幕截图中显示discoverNsfsBindMounts()函数只运行一次(1)。这个函数不是直接从P.中的任何地方调用的,而是注册这个函数,然后在一个重新执行的子C中运行。以前,没有报告discoverNsfsBindMounts()的代码覆盖率,但是现在在包github.com/thediveo/gons/reexec/testing代码覆盖的帮助下,C的代码覆盖率透明地合并到了P的代码覆盖率中。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60458049

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档