From b839cb56596aef01c2d89f4d5b86adc9e6fa7c60 Mon Sep 17 00:00:00 2001 From: lyyyuna Date: Thu, 22 Jul 2021 19:57:56 +0800 Subject: [PATCH] add e2e test & ci --- .github/workflows/e2e-linux.yml | 9 ++- .github/workflows/e2e-wins.yml | 9 ++- .gitignore | 1 + Makefile | 6 +- go.mod | 3 + go.sum | 6 ++ tests/README.md | 1 + tests/e2e/cmd.go | 83 +++++++++++++++++++++++++ tests/e2e/e2e_suite_test.go | 13 ++++ tests/e2e/e2e_test.go | 44 +++++++++++++ tests/e2e/samples/basic-project/go.mod | 1 + tests/e2e/samples/basic-project/main.go | 7 +++ tests/e2e/samples/go.mod | 1 + tests/e2e/samples/meta.yaml | 4 ++ tests/e2e/samples_mgr.go | 78 +++++++++++++++++++++++ 15 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 tests/README.md create mode 100644 tests/e2e/cmd.go create mode 100644 tests/e2e/e2e_suite_test.go create mode 100644 tests/e2e/e2e_test.go create mode 100644 tests/e2e/samples/basic-project/go.mod create mode 100644 tests/e2e/samples/basic-project/main.go create mode 100644 tests/e2e/samples/go.mod create mode 100644 tests/e2e/samples/meta.yaml create mode 100644 tests/e2e/samples_mgr.go diff --git a/.github/workflows/e2e-linux.yml b/.github/workflows/e2e-linux.yml index f93c7ca..c7e19ab 100644 --- a/.github/workflows/e2e-linux.yml +++ b/.github/workflows/e2e-linux.yml @@ -12,7 +12,7 @@ on: - '**.png' jobs: job_1: - name: Build goc binary + name: e2e test strategy: matrix: os: [ubuntu-latest, macos-latest] @@ -27,6 +27,11 @@ jobs: - name: Go build run: | go build . + go install . - name: Use goc to build self run: | - ./goc build -o ./gocc . \ No newline at end of file + ./goc build -o ./gocc . + - name: run e2e test + run: | + go get github.com/onsi/ginkgo/ginkgo + make e2e \ No newline at end of file diff --git a/.github/workflows/e2e-wins.yml b/.github/workflows/e2e-wins.yml index 83581f1..35002a0 100644 --- a/.github/workflows/e2e-wins.yml +++ b/.github/workflows/e2e-wins.yml @@ -12,7 +12,7 @@ on: - '**.png' jobs: job_1: - name: Build goc binary + name: e2e test strategy: matrix: os: [windows-latest] @@ -27,6 +27,11 @@ jobs: - name: Go build run: | go build . + go install . - name: Use goc to build self run: | - .\goc.exe build -o gocc . \ No newline at end of file + .\goc.exe build -o gocc . + - name: run e2e test + run: | + go get github.com/onsi/ginkgo/ginkgo + ginkgo tests/e2e/... \ No newline at end of file diff --git a/.gitignore b/.gitignore index e69de29..256f76d 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +tests/e2e/tmp/* \ No newline at end of file diff --git a/Makefile b/Makefile index 8091562..2cfac69 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,11 @@ fmt: govet-check: go vet ./... +e2e: + ginkgo tests/e2e/... + clean: find ./ -type f -name 'coverage.txt' -delete find ./ -type f -name 'goc' -delete - find ./ -type f -name 'gocc' -delete \ No newline at end of file + find ./ -type f -name 'gocc' -delete + rm -rf ./tests/e2e/tmp \ No newline at end of file diff --git a/go.mod b/go.mod index 59817bc..0a8c27f 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,8 @@ require ( github.com/mattn/go-isatty v0.0.13 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/olekukonko/tablewriter v0.0.5 + github.com/onsi/ginkgo v1.16.4 + github.com/onsi/gomega v1.14.0 github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 @@ -19,6 +21,7 @@ require ( golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d golang.org/x/tools v0.1.3 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b k8s.io/kubectl v0.21.2 k8s.io/test-infra v0.0.0-20210618100605-34aa2f2aa75b ) diff --git a/go.sum b/go.sum index 3bdd798..2a0c689 100644 --- a/go.sum +++ b/go.sum @@ -365,6 +365,7 @@ github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.8.1/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/fake-gcs-server v0.0.0-20180612165233-e85be23bdaa8/go.mod h1:1/HufuJ+eaDf4KTnYdS6HJMGvMRU8d4cYTuu/1QaBbI= github.com/fsouza/fake-gcs-server v1.19.4/go.mod h1:I0/88nHCASqJJ5M7zVF0zKODkYTcuXFW5J5yajsNJnE= @@ -929,6 +930,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/octago/sflags v0.2.0/go.mod h1:G0bjdxh4qPRycF74a2B8pU36iTp9QHGx0w0dFZXPt80= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -946,6 +948,7 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -958,6 +961,8 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= +github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1826,6 +1831,7 @@ gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..532cd4c --- /dev/null +++ b/tests/README.md @@ -0,0 +1 @@ +# How to run e2e test \ No newline at end of file diff --git a/tests/e2e/cmd.go b/tests/e2e/cmd.go new file mode 100644 index 0000000..2ade2f0 --- /dev/null +++ b/tests/e2e/cmd.go @@ -0,0 +1,83 @@ +package e2e + +import ( + "bytes" + "context" + "fmt" + "os/exec" + "time" +) + +// LongRunCmd defines a cmd which run for a long time +type LongRunCmd struct { + cancel context.CancelFunc + cmd *exec.Cmd + stderrBuf bytes.Buffer + stdoutBuf bytes.Buffer + err error + done bool +} + +func NewLongRunCmd(dir string, args []string) *LongRunCmd { + ctx, cancel := context.WithCancel(context.Background()) + cmd := exec.CommandContext(ctx, args[0], args[1:]...) + cmd.Dir = dir + + var stderrBuf bytes.Buffer + var stdoutBuf bytes.Buffer + cmd.Stdout = &stdoutBuf + cmd.Stderr = &stderrBuf + + return &LongRunCmd{ + cmd: cmd, + cancel: cancel, + stderrBuf: stderrBuf, + stdoutBuf: stdoutBuf, + } +} + +// Run in backend +func (l *LongRunCmd) Run() { + go func() { + err := l.cmd.Start() + if err != nil { + l.err = err + } + + err = l.cmd.Wait() + if err != nil { + l.err = err + } + l.err = nil + l.done = true + }() + time.Sleep(time.Millisecond * 100) +} + +func (l *LongRunCmd) Stop() { + l.cancel() +} + +func (l *LongRunCmd) CheckExitStatus() error { + if l.done == true { + return l.err + } else { + return fmt.Errorf("running") + } +} + +func (l *LongRunCmd) GetStdoutStdErr() (string, string) { + return l.stdoutBuf.String(), l.stderrBuf.String() +} + +// RunShortRunCmd defines a cmd which run and exits immediately +func RunShortRunCmd(dir string, args []string) (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) + defer cancel() + + cmd := exec.CommandContext(ctx, args[0], args[1:]...) + cmd.Dir = dir + + output, err := cmd.CombinedOutput() + return string(output), err +} diff --git a/tests/e2e/e2e_suite_test.go b/tests/e2e/e2e_suite_test.go new file mode 100644 index 0000000..a80ded3 --- /dev/null +++ b/tests/e2e/e2e_suite_test.go @@ -0,0 +1,13 @@ +package e2e + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestGoc(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Goc e2e Test Suite") +} diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go new file mode 100644 index 0000000..db9108f --- /dev/null +++ b/tests/e2e/e2e_test.go @@ -0,0 +1,44 @@ +package e2e + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("1 [基础测试]", func() { + var ( + mgr *SamplesMgr + ) + + BeforeEach(func() { + mgr = NewSamplesMgr() + }) + + Describe("1 测试 build 命令", func() { + It("1.1.1 简单工程", func() { + dir, err := mgr.GetSampleByKey("basic") + Expect(err).To(BeNil(), "找不到 sample") + + By("使用 goc build 命令编译") + _, err = RunShortRunCmd(dir, []string{"goc", "build", "."}) + Expect(err).To(BeNil(), "goc build 运行错误") + }) + }) + + Describe("2 测试 server 命令", func() { + It("1.2.1 测试 API 接口", func() { + dir, err := mgr.GetSampleByKey("basic") + Expect(err).To(BeNil(), "找不到 sample") + + By("启动 goc server") + lc := NewLongRunCmd(dir, []string{"goc", "server", "."}) + lc.Run() + defer lc.Stop() + + By("使用 goc list 获取服务列表") + output, err := RunShortRunCmd(dir, []string{"goc", "list"}) + Expect(err).To(BeNil(), "goc list 运行错误") + Expect(output).To(ContainSubstring("REMOTEIP"), "goc list 输出应该包含 REMOTEIP") + }) + }) +}) diff --git a/tests/e2e/samples/basic-project/go.mod b/tests/e2e/samples/basic-project/go.mod new file mode 100644 index 0000000..5c4a90f --- /dev/null +++ b/tests/e2e/samples/basic-project/go.mod @@ -0,0 +1 @@ +module basic \ No newline at end of file diff --git a/tests/e2e/samples/basic-project/main.go b/tests/e2e/samples/basic-project/main.go new file mode 100644 index 0000000..635db7a --- /dev/null +++ b/tests/e2e/samples/basic-project/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("hello, world") +} diff --git a/tests/e2e/samples/go.mod b/tests/e2e/samples/go.mod new file mode 100644 index 0000000..ea98d38 --- /dev/null +++ b/tests/e2e/samples/go.mod @@ -0,0 +1 @@ +module fake \ No newline at end of file diff --git a/tests/e2e/samples/meta.yaml b/tests/e2e/samples/meta.yaml new file mode 100644 index 0000000..f2e6ecb --- /dev/null +++ b/tests/e2e/samples/meta.yaml @@ -0,0 +1,4 @@ +samples: + basic: + dir: basic-project + description: a basic project only print hello world \ No newline at end of file diff --git a/tests/e2e/samples_mgr.go b/tests/e2e/samples_mgr.go new file mode 100644 index 0000000..4350a4d --- /dev/null +++ b/tests/e2e/samples_mgr.go @@ -0,0 +1,78 @@ +package e2e + +import ( + "fmt" + "log" + "os" + "path/filepath" + "regexp" + + . "github.com/onsi/ginkgo" + + "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 的运行模型是多进程模型,每一个 test case 是一个独立的进程 +// 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. +// 所以这里会单独在一个临时目录中生成 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 +}