139 lines
3.6 KiB
Go
139 lines
3.6 KiB
Go
/*
|
||
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
|
||
}
|