goc/tests/e2e/samples_mgr.go
2021-09-02 17:55:16 +08:00

139 lines
3.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Copyright 2021 Qiniu Cloud (qiniu.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package e2e
import (
"fmt"
"log"
"net"
"os"
"path/filepath"
"regexp"
"strconv"
. "github.com/onsi/ginkgo"
"github.com/gofrs/flock"
"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
//
// ginkgo 的并发执行时的运行模型是多进程模型,会有多个独立的 go test 进程。
// 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.
//
// 所以这里设计成每个 test case 在各自的临时目录中生成 sample以便将来测试用例可以并发执行。
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
}
// 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
}