为了在一定条件下发现Linux名称空间,我的开源Golang包艾克恩斯需要重新执行它作为一个新的子进程使用的应用程序,以便能够在Golang运行时启动之前切换挂载名称空间。Linux的工作方式使得在运行时生成挂载命名空间线程之后无法将它们从Golang应用程序中切换出来。
这意味着最初的进程"P“-作为子进程"C”(雷克斯包)重新运行自己的副本,通过子进程的环境传递一个特殊的指示,该指示向子进程发出信号,只运行属于包含的"lxkns“包的特定"action”函数(请参阅下面的详细信息),而不是正常运行整个应用程序(避免无穷无尽的递归生成子程序)。
forkchild := exec.Command("/proc/self/exe")
forkchild.Start()
...
forkchild.Wait()目前,我从VisualStudio代码调用覆盖率测试,该代码运行:
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“函数。
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")
}我还想从重新执行的子进程中创建代码覆盖数据。
注意:在我的测试用例中,切换挂载名称空间与在新的挂载命名空间中创建覆盖率文件没有冲突。原因是这些测试挂载名称空间是初始挂载名称空间的副本,所以创建一个新文件通常也会出现在文件系统中。
发布于 2020-03-07 23:34:45
在@Volker对我的问题发表评论之后,我知道我必须接受挑战,并直接搜索Go的testing包的源代码。虽然@marco.m的建议在许多情况下是有帮助的,但它不能处理我的一些小建议。与我最初的问题相关的testing的机制如下,大大简化了:
coverReport(),它写入覆盖数据文件(以ASCII文本格式);如果该文件已经存在(以前运行的陈旧版本),那么它将首先被截断。请注意,coverReport()有一个恼人的习惯,就是将一些“统计”信息打印到os.Stdout。-test.coverprofile= (通过标志包)获取CLI参数、os.Args和-test.outputdir=。If还实现了toOutputDir(path),如果指定的话,它将覆盖配置文件放置在-test.outputdir中。coverReport()什么时候被打电话呢?简单地说,在testing.M.Run()的末尾。
现在,带着这些知识,一个疯狂的解决方案开始出现,有点“走下坡路”;
testing.M包装在一个特殊的启用重执行的reexec.testing.M版本中:它检测它是否在启用覆盖率的情况下运行:-test.coverprofile=”CLI获得文件名。test.run=测试模式”的比勒费尔德,这很可能导致一个空的结果。记住,P会--在运行完所有测试之后--获取单个C覆盖率概要数据文件,并将它们合并到P中。
这种解决方案的缺点是,它依赖于Go的testing在编写代码覆盖率报告的方式和时间方面的一些未得到保证的行为。但是,由于Linux内核命名空间发现包已经比Docker的libnetwork更难实现,所以这只是一个更远的量程。
对于测试开发人员来说,整个enchilada隐藏在一个“增强的”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 (go精确代码覆盖率计算),然后在下面的屏幕截图中显示discoverNsfsBindMounts()函数只运行一次(1)。这个函数不是直接从P.中的任何地方调用的,而是注册这个函数,然后在一个重新执行的子C中运行。以前,没有报告discoverNsfsBindMounts()的代码覆盖率,但是现在在包github.com/thediveo/gons/reexec/testing代码覆盖的帮助下,C的代码覆盖率透明地合并到了P的代码覆盖率中。

https://stackoverflow.com/questions/60458049
复制相似问题