goc/tests/e2e/samples_mgr.go

126 lines
3.1 KiB
Go
Raw Normal View History

2021-07-22 11:57:56 +00:00
package e2e
import (
"fmt"
"log"
2021-07-23 07:25:03 +00:00
"net"
2021-07-22 11:57:56 +00:00
"os"
"path/filepath"
"regexp"
2021-07-23 07:25:03 +00:00
"strconv"
2021-07-22 11:57:56 +00:00
. "github.com/onsi/ginkgo"
2021-07-23 07:25:03 +00:00
"github.com/gofrs/flock"
2021-07-22 11:57:56 +00:00
"github.com/tongjingran/copy"
"gopkg.in/yaml.v3"
)
type Sample struct {
Dir string `yaml:"dir"`
Description string `yaml:"description"`
}
// SamplesMgr create and return sample for test case
//
2021-07-23 07:25:03 +00:00
// ginkgo 的并发执行时的运行模型是多进程模型,会有多个独立的 go test 进程。
2021-07-22 11:57:56 +00:00
// Ginkgo has support for running specs in parallel. It does this by spawning separate go test processes and serving specs to each process off of a shared queue.
2021-07-23 07:25:03 +00:00
//
// 所以这里设计成每个 test case 在各自的临时目录中生成 sample以便将来测试用例可以并发执行。
2021-07-22 11:57:56 +00:00
type SamplesMgr struct {
Samples map[string]Sample `yaml:"samples"`
path string `yaml:"-"`
}
func NewSamplesMgr() *SamplesMgr {
path, _ := os.Getwd()
metaData, err := os.ReadFile(filepath.Join(path, "samples", "meta.yaml"))
if err != nil {
log.Fatalf("fail to read sample meta")
}
mgr := SamplesMgr{}
err = yaml.Unmarshal(metaData, &mgr)
if err != nil {
log.Fatalf("fail to parse the meta yaml")
}
mgr.path = path
return &mgr
}
// GetSampleByKey return the sample folder location for test use
func (m *SamplesMgr) GetSampleByKey(key string) (string, error) {
sample, ok := m.Samples[key]
if !ok {
return "", fmt.Errorf("no sample found")
}
desc := CurrentGinkgoTestDescription()
caseTitle := desc.FullTestText + " " + key
m1 := regexp.MustCompile(`[/\\?%*:|"<>]`)
caseTitle = m1.ReplaceAllString(caseTitle, "-")
dst := filepath.Join(m.path, "tmp", caseTitle)
err := os.RemoveAll(dst)
if err != nil {
log.Fatalf("fail to clean the temp sample: %v", err)
}
err = os.MkdirAll(dst, os.ModePerm)
if err != nil {
log.Fatalf("fail to create sample dir: %v", err)
}
src := filepath.Join(m.path, "samples", sample.Dir)
err = copy.Copy(src, dst)
if err != nil {
log.Fatalf("fail to copy the sample project: %v", err)
}
return dst, nil
}
2021-07-23 07:25:03 +00:00
// GetAvailablePort get next available host port among multiprocess ginkgo test cases
//
// 利用文件锁和端口探活,获取下一个可用的 port
func (m *SamplesMgr) GetAvailablePort() (string, error) {
fileLockPath := filepath.Join(m.path, "tmp", "port.lock")
// 文件锁ginkgo parallel 模式是多进程,必须用跨平台跨进程的同步方式
lock := flock.New(fileLockPath)
lock.Lock()
defer lock.Unlock()
// 该文件记录 counter代表下一个可用的 port
portFilePath := filepath.Join(m.path, "tmp", "port.record")
data, err := os.ReadFile(portFilePath)
if err != nil {
os.Create(portFilePath)
}
port, err := strconv.Atoi(string(data))
if err != nil {
port = 7777
} else {
port += 1
}
// 循环检测直到找到可用 port
for {
if port == 65534 {
port = 7777
}
conn, err := net.Dial("tcp", ":"+strconv.Itoa(port))
if err == nil {
port += 1
conn.Close()
} else {
break
}
}
err = os.WriteFile(portFilePath, []byte(strconv.Itoa(port)), os.ModePerm)
return "127.0.0.1:" + strconv.Itoa(port), err
}