🧪 test: 增加集测用例

This commit is contained in:
lyyyuna 2021-09-09 11:49:49 +08:00 committed by Li Yiyang
parent c7c76efb37
commit fae8077705
11 changed files with 401 additions and 77 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
tests/e2e/tmp/*
.goc.kvstore
.goc.kvstore
coverage.txt

View File

@ -83,6 +83,7 @@ func init() {
if isOffline(tmp) {
log.Printf("[goc][Error] needs re-register")
register(host)
continue
}
} else {
log.Printf("[goc][Error] rpc fail to dial to goc server: %v", err)

View File

@ -15,7 +15,9 @@ package server
import (
"bytes"
"fmt"
"net/http"
"regexp"
"strings"
"sync"
"time"
@ -26,33 +28,6 @@ import (
"k8s.io/test-infra/gopherage/pkg/cov"
)
func idMaps(idQuery string) func(key string) bool {
idMap := make(map[string]bool)
if strings.Contains(idQuery, ",") == false {
} else {
ids := strings.Split(idQuery, ",")
for _, id := range ids {
idMap[id] = true
}
}
inIdMaps := func(key string) bool {
// if no id in query, then all id agent will be return
if len(idMap) == 0 {
return true
}
// other
_, ok := idMap[key]
if !ok {
return false
} else {
return true
}
}
return inIdMaps
}
// listAgents return all service informations
func (gs *gocServer) listAgents(c *gin.Context) {
idQuery := c.Query("id")
@ -79,22 +54,65 @@ func (gs *gocServer) listAgents(c *gin.Context) {
})
}
func (gs *gocServer) removeAgents(c *gin.Context) {
idQuery := c.Query("id")
ifInIdMap := idMaps(idQuery)
gs.agents.Range(func(key, value interface{}) bool {
// check if id is in the query ids
if !ifInIdMap(key.(string)) {
return true
}
agent, ok := value.(*gocCoveredAgent)
if !ok {
return false
}
agent.closeConnection()
gs.agents.Delete(key)
return true
})
gs.removeAllAgentsFromStore()
}
// getProfiles get and merge all agents' informations
//
// it is synchronous
func (gs *gocServer) getProfiles(c *gin.Context) {
idQuery := c.Query("id")
ifInIdMap := idMaps(idQuery)
pattern := c.Query("pattern")
extra := c.Query("extra")
isExtra := filterExtra(extra)
var mu sync.Mutex
var wg sync.WaitGroup
mergedProfiles := make([][]*cover.Profile, 0)
gs.agents.Range(func(key, value interface{}) bool {
// check if id is in the query ids
if !ifInIdMap(key.(string)) {
// not in
return true
}
agent, ok := value.(*gocCoveredAgent)
if !ok {
return false
}
// check if extra matches
if !isExtra(agent.Extra) {
// not match
return true
}
wg.Add(1)
// 并发 rpc且每个 rpc 设超时时间 10 second
go func() {
@ -140,9 +158,17 @@ func (gs *gocServer) getProfiles(c *gin.Context) {
agent.closeRpcConnOnce()
return
}
// check if pattern matches
newProfile, err := filterProfileByPattern(pattern, profile)
if err != nil {
log.Errorf("%v", err)
return
}
mu.Lock()
defer mu.Unlock()
mergedProfiles = append(mergedProfiles, profile)
mergedProfiles = append(mergedProfiles, newProfile)
}()
return true
@ -177,14 +203,31 @@ func (gs *gocServer) getProfiles(c *gin.Context) {
//
// it is async, the function will return immediately
func (gs *gocServer) resetProfiles(c *gin.Context) {
idQuery := c.Query("id")
ifInIdMap := idMaps(idQuery)
extra := c.Query("extra")
isExtra := filterExtra(extra)
gs.agents.Range(func(key, value interface{}) bool {
// check if id is in the query ids
if !ifInIdMap(key.(string)) {
// not in
return true
}
agent, ok := value.(*gocCoveredAgent)
if !ok {
return false
}
// check if extra matches
if !isExtra(agent.Extra) {
// not match
return true
}
var req ProfileReq = "resetprofile"
var res ProfileRes
go func() {
@ -256,54 +299,60 @@ func (gs *gocServer) watchProfileUpdate(c *gin.Context) {
<-gwc.exitCh
}
func (gs *gocServer) removeAgentById(c *gin.Context) {
id := c.Query("id")
func filterProfileByPattern(pattern string, profiles []*cover.Profile) ([]*cover.Profile, error) {
rawagent, ok := gs.agents.Load(id)
if !ok {
c.JSON(http.StatusNotFound, gin.H{
"msg": "agent not found",
})
return
if strings.TrimSpace(pattern) == "" {
return profiles, nil
}
agent, ok := rawagent.(*gocCoveredAgent)
if !ok {
c.JSON(http.StatusNotFound, gin.H{
"msg": "agent not found",
})
return
var out = make([]*cover.Profile, 0)
for _, profile := range profiles {
matched, err := regexp.MatchString(pattern, profile.FileName)
if err != nil {
return nil, fmt.Errorf("filterProfile failed with pattern %s for profile %s, err: %v", pattern, profile.FileName, err)
}
if matched {
out = append(out, profile)
break // no need to check again for the file
}
}
// 关闭相应连接
agent.closeConnection()
// 从维护 agent 池里删除
gs.agents.Delete(id)
// 从持久化中删除
gs.removeAgentFromStore(id)
return out, nil
}
func (gs *gocServer) removeAgents(c *gin.Context) {
idQuery := c.Query("id")
ifInIdMap := idMaps(idQuery)
func idMaps(idQuery string) func(key string) bool {
idMap := make(map[string]bool)
if len(strings.TrimSpace(idQuery)) == 0 {
} else {
ids := strings.Split(idQuery, ",")
for _, id := range ids {
idMap[id] = true
}
}
gs.agents.Range(func(key, value interface{}) bool {
// check if id is in the query ids
if !ifInIdMap(key.(string)) {
inIdMaps := func(key string) bool {
// if no id in query, then all id agent will be return
if len(idMap) == 0 {
return true
}
agent, ok := value.(*gocCoveredAgent)
// other
_, ok := idMap[key]
if !ok {
return false
} else {
return true
}
}
agent.closeConnection()
gs.agents.Delete(key)
return true
})
gs.removeAllAgentsFromStore()
return inIdMaps
}
func filterExtra(extraPattern string) func(string) bool {
re := regexp.MustCompile(extraPattern)
return func(extra string) bool {
return re.Match([]byte(extra))
}
}

View File

@ -21,8 +21,8 @@ import (
)
const (
PongWait = 10 * time.Second
PingWait = 5 * time.Second
PongWait = 5 * time.Second
PingWait = 2 * time.Second
)
type ProfileReq string

View File

@ -232,7 +232,7 @@ func (gs *gocServer) restoreAgents() {
id, err := strconv.Atoi(agent.Id)
if err != nil {
log.Fatalf("fail to transfer id to number: %v", err)
log.Fatalf("fail to transform id to number: %v", err)
}
if maxId < id {
maxId = id

View File

@ -48,6 +48,10 @@ func (s *FileStore) Get(key string) (string, error) {
}
}
if scanner.Err() != nil {
return "", scanner.Err()
}
return "", fmt.Errorf("no key found")
}
@ -55,7 +59,7 @@ func (s *FileStore) Set(key string, value string) error {
s.mu.Lock()
defer s.mu.Unlock()
f, err := os.OpenFile(s.storePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)
f, err := os.OpenFile(s.storePath, os.O_RDWR, os.ModePerm)
if err != nil {
return err
}
@ -64,6 +68,7 @@ func (s *FileStore) Set(key string, value string) error {
var outputLines string
scanner := bufio.NewScanner(f)
isFound := false
for scanner.Scan() {
line := scanner.Text()
items := strings.SplitN(line, " ", 2)
@ -78,6 +83,10 @@ func (s *FileStore) Set(key string, value string) error {
outputLines += line + "\n"
}
if scanner.Err() != nil {
return scanner.Err()
}
if !isFound {
outputLines += key + " " + value + "\n"
}
@ -88,6 +97,7 @@ func (s *FileStore) Set(key string, value string) error {
f.Seek(0, os.SEEK_SET)
f.WriteString(outputLines)
f.Sync()
return nil
}
@ -96,7 +106,7 @@ func (s *FileStore) Remove(key string) error {
s.mu.Lock()
defer s.mu.Unlock()
f, err := os.OpenFile(s.storePath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
f, err := os.OpenFile(s.storePath, os.O_RDWR, os.ModePerm)
if err != nil {
return err
}
@ -117,12 +127,17 @@ func (s *FileStore) Remove(key string) error {
}
}
if scanner.Err() != nil {
return scanner.Err()
}
if err := os.Truncate(s.storePath, 0); err != nil {
return err
}
f.Seek(0, os.SEEK_SET)
f.WriteString(outputLines)
f.Sync()
return nil
}
@ -151,6 +166,10 @@ func (s *FileStore) Range(pattern string) ([]string, error) {
}
}
if scanner.Err() != nil {
return nil, scanner.Err()
}
if len(output) == 0 {
return nil, fmt.Errorf("no key found")
} else {
@ -162,7 +181,7 @@ func (s *FileStore) RangeRemove(pattern string) error {
s.mu.Lock()
defer s.mu.Unlock()
f, err := os.OpenFile(s.storePath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
f, err := os.OpenFile(s.storePath, os.O_RDWR, os.ModePerm)
if err != nil {
return err
}
@ -183,12 +202,17 @@ func (s *FileStore) RangeRemove(pattern string) error {
}
}
if scanner.Err() != nil {
return scanner.Err()
}
if err := os.Truncate(s.storePath, 0); err != nil {
return err
}
f.Seek(0, os.SEEK_SET)
f.WriteString(outputLines)
f.Sync()
return nil
}

View File

@ -21,6 +21,7 @@ import (
"os/exec"
"runtime"
"strings"
"sync"
"time"
)
@ -32,6 +33,8 @@ type LongRunCmd struct {
stdoutBuf bytes.Buffer
err error
done bool
once sync.Once
}
// NewLongRunCmd defines a command which will be run forever
@ -85,7 +88,9 @@ func (l *LongRunCmd) Run() {
}
func (l *LongRunCmd) Stop() {
l.cancel()
l.once.Do(func() {
l.cancel()
})
}
func (l *LongRunCmd) CheckExitStatus() error {

View File

@ -46,20 +46,20 @@ var _ = Describe("1 [基础测试]", func() {
})
Describe("2 测试 server 命令", func() {
It("1.2.1 测试编译/list/profile基础场景", func() {
It("1.2.1 测试编译/service/profile基础场景", func() {
dir, err := mgr.GetSampleByKey("basic2")
Expect(err).To(BeNil(), "找不到 sample")
By("启动 goc server")
lc := NewLongRunCmd([]string{"goc", "server", "."}, dir, nil)
lc := NewLongRunCmd([]string{"goc", "server"}, dir, nil)
lc.Run()
defer lc.Stop()
By("编译一个长时间运行的程序")
By("编译 basic2")
output, err := RunShortRunCmd([]string{"goc", "build", "."}, dir, nil)
Expect(err).To(BeNil(), "编译失败:"+output)
By("长时间运行 basic2")
By("运行 basic2")
basicC := NewLongRunCmd([]string{"./basic2"}, dir, nil)
basicC.Run()
defer basicC.Stop()
@ -80,5 +80,234 @@ basic2/main.go:9.6,12.3 2 2`
Expect(err).To(BeNil(), "goc profile get运行错误")
Expect(output).To(ContainSubstring(profileStr), "goc profile get 获取的覆盖率有误")
})
It("1.2.2 测试 server 重启", func() {
dir, err := mgr.GetSampleByKey("basic2")
Expect(err).To(BeNil(), "找不到 sample")
By("启动 goc server")
lc := NewLongRunCmd([]string{"goc", "server"}, dir, nil)
lc.Run()
defer lc.Stop()
By("编译 basic2")
output, err := RunShortRunCmd([]string{"goc", "build", "."}, dir, nil)
Expect(err).To(BeNil(), "编译失败:"+output)
By("运行 basic2")
basicC := NewLongRunCmd([]string{"./basic2"}, dir, nil)
basicC.Run()
defer basicC.Stop()
By("获取 service 列表")
Eventually(func() {
output, err = RunShortRunCmd([]string{"goc", "service", "get"}, dir, nil)
Expect(err).To(BeNil(), "goc servive get 运行错误")
Expect(output).To(ContainSubstring("1 CONNECT 127.0.0.1 ./basic2"), "goc service get 输出应该包含 basic 服务")
}, 3*time.Second, 1*time.Second).Should(Succeed())
By("重启 goc server")
lc.Stop()
lc2 := NewLongRunCmd([]string{"goc", "server"}, dir, nil)
lc2.Run()
defer lc2.Stop()
By("再次获取 service 列表")
Eventually(func() {
output, err = RunShortRunCmd([]string{"goc", "service", "get"}, dir, nil)
Expect(err).To(BeNil(), "goc servive get 运行错误")
Expect(output).To(ContainSubstring("1 CONNECT 127.0.0.1 ./basic2"), "goc service get 输出应该包含 basic 服务")
}, 15*time.Second, 1*time.Second).Should(Succeed(), "10s 内 basic2 服务重新注册成功,且 id 不变")
})
})
Describe("3 测试 goc service 相关命令", func() {
It("1.3.1 get/delete 组合", func() {
dir, err := mgr.GetSampleByKey("basic2")
Expect(err).To(BeNil(), "找不到 sample")
By("启动 goc server")
lc := NewLongRunCmd([]string{"goc", "server"}, dir, nil)
lc.Run()
defer lc.Stop()
By("编译 basic2")
output, err := RunShortRunCmd([]string{"goc", "build", "."}, dir, nil)
Expect(err).To(BeNil(), "编译失败:"+output)
By("长时间运行 basic2")
basicC := NewLongRunCmd([]string{"./basic2"}, dir, nil)
basicC.Run()
defer basicC.Stop()
By("再长时间运行 basic2 -f hello")
time.Sleep(time.Microsecond * 100)
basicC2 := NewLongRunCmd([]string{"./basic2", "-f", "hello"}, dir,
[]string{
"GOC_REGISTER_EXTRA=fantastic", // 额外的注册信息
})
basicC2.Run()
time.Sleep(time.Second)
By("测试获取 service 信息")
output, err = RunShortRunCmd([]string{"goc", "service", "get"}, dir, nil)
Expect(err).To(BeNil(), "goc servive get 运行错误")
Expect(output).To(ContainSubstring("2 CONNECT 127.0.0.1 ./basic2 -f"), "goc service get 应该显示第二个服务")
By("测试获取指定 id 的 service 信息")
output, err = RunShortRunCmd([]string{"goc", "service", "get", "--id", "1"}, dir, nil)
Expect(err).To(BeNil(), "goc servive get 运行错误")
Expect(output).To(ContainSubstring("1 CONNECT 127.0.0.1 ./basic2"), "id=1 的服务能返回")
Expect(output).NotTo(ContainSubstring("2 CONNECT 127.0.0.1 ./basic2 -f"), "id=2 的服务没有返回")
By("测试能否获取 extra")
output, err = RunShortRunCmd([]string{"goc", "service", "get", "--wide"}, dir, nil)
Expect(err).To(BeNil(), "goc servive get --wide 运行错误")
Expect(output).To(ContainSubstring("fantastic"), "注入的 extra 信息没有获取到")
By("basic2 -f hello 退出")
basicC2.Stop()
By("测试 get 能否获取 DISCONNECT 状态")
Eventually(func() {
output, err = RunShortRunCmd([]string{"goc", "service", "get"}, dir, nil)
Expect(err).To(BeNil(), "goc servive get 运行错误")
Expect(output).To(ContainSubstring("2 DISCONNECT 127.0.0.1 ./basic2 -f"), "应该在 10s 内感知到 agent 断连")
}, 10*time.Second, 1*time.Second).Should(Succeed())
By("测试删除 id=2(已经退出) 的服务列表")
output, err = RunShortRunCmd([]string{"goc", "service", "delete", "--id", "2"}, dir, nil)
Expect(err).To(BeNil(), "删除服务列表失败")
output, err = RunShortRunCmd([]string{"goc", "service", "get"}, dir, nil)
Expect(err).To(BeNil(), "goc servive get 运行错误")
Expect(output).NotTo(ContainSubstring("./basic2 -f"), "DISCONNECT 的服务未被删除")
By("测试删除 id=1(还在运行) 的服务列表")
output, err = RunShortRunCmd([]string{"goc", "service", "delete", "--id", "1"}, dir, nil)
Expect(err).To(BeNil(), "删除服务列表失败")
Eventually(func() {
output, err = RunShortRunCmd([]string{"goc", "service", "get"}, dir, nil)
Expect(err).To(BeNil(), "goc servive get 运行错误")
Expect(output).To(ContainSubstring("3 CONNECT"), "20s 内 basic2 重新注册成功")
Expect(output).NotTo(ContainSubstring("1 "), "id=1 的服务不在")
}, 20*time.Second, 1*time.Second).Should(Succeed())
})
})
Describe("4 测试 goc profile 相关命令", func() {
It("1.4.1 测试 get/clear", func() {
dir, err := mgr.GetSampleByKey("basic3")
Expect(err).To(BeNil(), "找不到 sample")
By("启动 goc server")
lc := NewLongRunCmd([]string{"goc", "server"}, dir, nil)
lc.Run()
defer lc.Stop()
By("编译 basic3")
output, err := RunShortRunCmd([]string{"goc", "build", "."}, dir, nil)
Expect(err).To(BeNil(), "编译失败:"+output)
By("运行 basic3")
basicC := NewLongRunCmd([]string{"./basic3"}, dir, nil)
basicC.Run()
defer basicC.Stop()
By("运行 basic3 -f")
basicC2 := NewLongRunCmd([]string{"./basic3", "-f"}, dir, nil)
basicC2.Run()
defer basicC2.Stop()
time.Sleep(time.Second)
By("获取覆盖率")
output, err = RunShortRunCmd([]string{"goc", "profile", "get"}, dir, nil)
Expect(err).To(BeNil(), "获取覆盖率失败")
Expect(output).To(ContainSubstring("basic3/main.go:8.13,11.2 2 2"))
By("只获取 id=1 的覆盖率")
output, err = RunShortRunCmd([]string{"goc", "profile", "get", "--id", "1"}, dir, nil)
Expect(err).To(BeNil(), "获取覆盖率失败")
Expect(output).To(ContainSubstring("basic3/main.go:8.13,11.2 2 1"), "id=1 只有 1 个覆盖率")
By("运行 basic3 -g")
basicC3 := NewLongRunCmd([]string{"./basic3", "-g"}, dir, []string{
"GOC_REGISTER_EXTRA=fantastic", // 额外的注册信息
})
basicC3.Run()
defer basicC3.Stop()
By("只获取 extra=fantastic 的覆盖率")
output, err = RunShortRunCmd([]string{"goc", "profile", "get", "--extra", "fantastic"}, dir, nil)
Expect(err).To(BeNil(), "获取覆盖率失败")
Expect(output).To(ContainSubstring("basic3/main.go:8.13,11.2 2 1"), "extra=fantastic 的覆盖率只有 1")
By("获取 id=10 的覆盖率")
output, err = RunShortRunCmd([]string{"goc", "profile", "get", "--id", "10"}, dir, nil)
Expect(err).NotTo(BeNil(), "获取覆盖率不应该成功")
Expect(output).To(ContainSubstring("can't merge zero profiles"), "错误信息不对")
By("清空 id=2 的覆盖率")
output, err = RunShortRunCmd([]string{"goc", "profile", "get"}, dir, nil)
Expect(err).To(BeNil(), "获取覆盖率失败")
Expect(output).To(ContainSubstring("basic3/main.go:8.13,11.2 2 3"))
output, err = RunShortRunCmd([]string{"goc", "profile", "clear", "--id", "2"}, dir, nil)
Expect(err).To(BeNil(), "清空覆盖率失败")
output, err = RunShortRunCmd([]string{"goc", "profile", "get"}, dir, nil)
Expect(err).To(BeNil(), "获取覆盖率失败")
Expect(output).To(ContainSubstring("basic3/main.go:8.13,11.2 2 2"))
By("清空 extra=fantastic 的覆盖率")
output, err = RunShortRunCmd([]string{"goc", "profile", "clear", "--extra", "fantastic"}, dir, nil)
Expect(err).To(BeNil(), "清空覆盖率失败")
output, err = RunShortRunCmd([]string{"goc", "profile", "get"}, dir, nil)
Expect(err).To(BeNil(), "获取覆盖率失败")
Expect(output).To(ContainSubstring("basic3/main.go:8.13,11.2 2 1"))
By("运行 basic3 -h")
basicC4 := NewLongRunCmd([]string{"./basic3", "-h"}, dir, []string{
"GOC_REGISTER_EXTRA=fantastic", // 额外的注册信息
})
basicC4.Run()
defer basicC4.Stop()
output, err = RunShortRunCmd([]string{"goc", "profile", "get"}, dir, nil)
Expect(err).To(BeNil(), "获取覆盖率失败")
Expect(output).To(ContainSubstring("basic3/main.go:8.13,11.2 2 2"))
By("清空所有的覆盖率")
output, err = RunShortRunCmd([]string{"goc", "profile", "clear"}, dir, nil)
Expect(err).To(BeNil(), "清空覆盖率失败")
output, err = RunShortRunCmd([]string{"goc", "profile", "get"}, dir, nil)
Expect(err).To(BeNil(), "获取覆盖率失败")
Expect(output).To(ContainSubstring("basic3/main.go:8.13,11.2 2 0"))
})
})
Describe("5 测试 install 命令", func() {
It("1.5.1 简单工程", func() {
dir, err := mgr.GetSampleByKey("basic")
Expect(err).To(BeNil(), "找不到 sample")
By("使用 goc install 命令编译")
gobinEnv := "GOBIN=" + dir + "/bin"
_, err = RunShortRunCmd([]string{"goc", "install", "."}, dir, []string{
gobinEnv,
})
Expect(err).To(BeNil(), "goc install 运行错误")
By("检查代码是否被插入二进制")
contain, err := SearchSymbolInBinary(dir+"/bin", "basic", "basic/goc-cover-agent-apis-auto-generated-11111-22222-package.loadFileCover")
Expect(err).To(BeNil(), "二进制检查失败")
Expect(contain).To(BeTrue(), "二进制中未找到插桩的符号")
})
})
})

View File

@ -0,0 +1 @@
module basic3

View File

@ -0,0 +1,11 @@
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("hello, world")
time.Sleep(10000 * time.Second)
}

View File

@ -4,4 +4,7 @@ samples:
description: a basic project only print hello world
basic2:
dir: basic4ever-project
description: a basic project which will never exist
description: a basic project which will never exist
basic3:
dir: basic-longsleep-project
description: a basic project which sleep a long time