Migrate qbox/goc to qiniu/goc (#8)
* build/install * all branches * e2e test * goc binary PATH add checkout code add +x test github action test for github action test for github actions test for github actions test for github actions fix ci fix fix temp path error * go1.11 1.10 not support go env command fix * fix build dir error for go 1.11/1.12 mod project * e2e test test test fix yaml syntax error fix fix fix * fix env error fix fix * add unit test * add unit test coverage report update update * add license
This commit is contained in:
parent
08fa9619b8
commit
c2391c5b18
63
.github/workflows/e2e_test_check.yml
vendored
Normal file
63
.github/workflows/e2e_test_check.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
name: e2e test
|
||||
on:
|
||||
# Trigger the workflow on push or pull request,
|
||||
# but only for the master branch
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
jobs:
|
||||
job_1:
|
||||
name: Build goc binary
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Go build
|
||||
run: |
|
||||
cd cmd/goc
|
||||
go build
|
||||
- name: Go build test binary
|
||||
run: |
|
||||
cd tests/e2e
|
||||
go get -u github.com/onsi/ginkgo/ginkgo
|
||||
ginkgo build ./...
|
||||
- name: Upload binary result for job 1
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: goc
|
||||
path: cmd/goc/goc
|
||||
- name: Upload binary result for job 1
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: e2e.test
|
||||
path: tests/e2e/e2e.test
|
||||
|
||||
job_2:
|
||||
name: E2E test
|
||||
needs: job_1
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Download built binary
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: /home/runner/tools
|
||||
- name: Do test
|
||||
run: |
|
||||
cd tests
|
||||
./run-ci-actions.sh
|
2
.github/workflows/style_check.yml
vendored
2
.github/workflows/style_check.yml
vendored
@ -3,8 +3,6 @@ on:
|
||||
# Trigger the workflow on push or pull request,
|
||||
# but only for the master branch
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
|
5
.github/workflows/ut_check.yml
vendored
5
.github/workflows/ut_check.yml
vendored
@ -3,8 +3,6 @@ on:
|
||||
# Trigger the workflow on push or pull request,
|
||||
# but only for the master branch
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
@ -27,4 +25,5 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Go test
|
||||
run: |
|
||||
go test ./...
|
||||
export DEFAULT_EXCEPT_PKGS=e2e
|
||||
go test -p 1 -cover $(go list ./... | grep -v -E $DEFAULT_EXCEPT_PKGS)
|
||||
|
@ -1,5 +1,6 @@
|
||||

|
||||

|
||||

|
||||
|
||||
# goc
|
||||
A Comprehensive Coverage Testing System for The Go Programming Language
|
||||
|
105
cmd/goc/app/build.go
Normal file
105
cmd/goc/app/build.go
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 app
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var buildCmd = &cobra.Command{
|
||||
Use: "build",
|
||||
Short: "Do cover for all go files and execute go build command",
|
||||
Long: `This build command is a little different from the official one, for instance:
|
||||
* 'goc build' is equal to 'goc cover && go build'
|
||||
* 'goc build --center=http://127.0.0.1:7777 -- -static app/kodo' is equal to 'goc cover --center=http://127.0.0.1:7777 && go build -static app/kodo'
|
||||
* 'goc build -- -o output' is equal to 'goc cover && go build -output, both relative/absolute output paths are supported'`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
newgopath, newwd, tmpdir, pkgs := build.MvProjectsToTmp(target, args)
|
||||
doCover(cmd, args, newgopath, tmpdir)
|
||||
newArgs, modified := modifyOutputArg(args)
|
||||
doBuild(newArgs, newgopath, newwd)
|
||||
|
||||
// if not modified
|
||||
// find the binary in temp build dir
|
||||
// and copy them into original dir
|
||||
if false == modified {
|
||||
build.MvBinaryToOri(pkgs, tmpdir)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
buildCmd.Flags().StringVarP(¢er, "center", "", "http://127.0.0.1:7777", "cover profile host center")
|
||||
|
||||
rootCmd.AddCommand(buildCmd)
|
||||
}
|
||||
|
||||
func doBuild(args []string, newgopath string, newworkingdir string) {
|
||||
log.Println("Go building in temp...")
|
||||
newArgs := []string{"build"}
|
||||
newArgs = append(newArgs, args...)
|
||||
cmd := exec.Command("go", newArgs...)
|
||||
cmd.Dir = newworkingdir
|
||||
|
||||
if newgopath != "" {
|
||||
// Change to temp GOPATH for go install command
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", newgopath))
|
||||
}
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to execute: go build %v. The error is: %v, the stdout/stderr is: %v", strings.Join(args, " "), err, string(out))
|
||||
}
|
||||
log.Println("Go build exit successful.")
|
||||
}
|
||||
|
||||
// As we build in the temp build dir, we have to modify the "-o output",
|
||||
// if output is a relative path, transform it to abspath
|
||||
func modifyOutputArg(args []string) (newArgs []string, modified bool) {
|
||||
var output string
|
||||
fs := flag.NewFlagSet("goc-build", flag.PanicOnError)
|
||||
fs.StringVar(&output, "o", "", "output dir")
|
||||
|
||||
// parse the go args after "--"
|
||||
fs.Parse(args)
|
||||
|
||||
// skip if output is not present
|
||||
if output == "" {
|
||||
modified = false
|
||||
newArgs = args
|
||||
return
|
||||
}
|
||||
|
||||
abs, err := filepath.Abs(output)
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to transform the path: %v to absolute path, the error is: %v", output, err)
|
||||
}
|
||||
|
||||
// the second -o arg will overwrite the first one
|
||||
newArgs = append(args, "-o", abs)
|
||||
modified = true
|
||||
return
|
||||
}
|
221
cmd/goc/app/cover.go
Normal file
221
cmd/goc/app/cover.go
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var coverCmd = &cobra.Command{
|
||||
Use: "cover",
|
||||
Short: "do cover for the target source ",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
doCover(cmd, args, "", "")
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
target string
|
||||
center string
|
||||
)
|
||||
|
||||
func init() {
|
||||
coverCmd.Flags().StringVarP(¢er, "center", "", "http://127.0.0.1:7777", "cover profile host center")
|
||||
coverCmd.Flags().StringVarP(&target, "target", "", ".", "target folder to cover")
|
||||
|
||||
rootCmd.AddCommand(coverCmd)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
|
||||
func doCover(cmd *cobra.Command, args []string, newgopath string, newtarget string) {
|
||||
if newtarget != "" {
|
||||
target = newtarget
|
||||
}
|
||||
if !isDirExist(target) {
|
||||
log.Fatalf("target directory %s not exist", target)
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
//
|
||||
listArgs := []string{"list", "-json"}
|
||||
if len(args) != 0 {
|
||||
listArgs = append(listArgs, args...)
|
||||
}
|
||||
listArgs = append(listArgs, "./...")
|
||||
pkgs := cover.ListPackages(target, listArgs, newgopath)
|
||||
|
||||
//
|
||||
//
|
||||
var seen = make(map[string]*cover.PackageCover)
|
||||
var seenCache = make(map[string]*cover.PackageCover)
|
||||
for _, pkg := range pkgs {
|
||||
//
|
||||
if pkg.Name == "main" {
|
||||
log.Printf("handle package: %v", pkg.ImportPath)
|
||||
// inject the main package
|
||||
mainCover, err := cover.AddCounters(pkg, newgopath)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err)
|
||||
}
|
||||
|
||||
// new a testcover for this service
|
||||
tc := cover.TestCover{
|
||||
Mode: "atomic",
|
||||
Center: center,
|
||||
MainPkgCover: mainCover,
|
||||
}
|
||||
|
||||
// handle its dependency
|
||||
var internalPkgCache = make(map[string][]*cover.PackageCover)
|
||||
tc.CacheCover = make(map[string]*cover.PackageCover)
|
||||
for _, dep := range pkg.Deps {
|
||||
|
||||
if packageCover, ok := seen[dep]; ok {
|
||||
tc.DepsCover = append(tc.DepsCover, packageCover)
|
||||
continue
|
||||
}
|
||||
|
||||
//only focus package neither standard Go library nor dependency library
|
||||
if depPkg, ok := pkgs[dep]; ok {
|
||||
|
||||
if findInternal(dep) {
|
||||
|
||||
//scan exist cache cover to tc.CacheCover
|
||||
if cache, ok := seenCache[dep]; ok {
|
||||
log.Printf("cache cover exist: %s", cache.Package.ImportPath)
|
||||
tc.CacheCover[cache.Package.Dir] = cache
|
||||
continue
|
||||
}
|
||||
|
||||
// add counter for internal package
|
||||
inPkgCover, err := cover.AddCounters(depPkg, newgopath)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err)
|
||||
}
|
||||
parentDir := getInternalParent(depPkg.Dir)
|
||||
parentImportPath := getInternalParent(depPkg.ImportPath)
|
||||
|
||||
//if internal parent dir or import is root path, ignore the dep. the dep is Go library nor dependency library
|
||||
if parentDir == "" {
|
||||
continue
|
||||
}
|
||||
if parentImportPath == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
pkg := &cover.Package{
|
||||
ImportPath: parentImportPath,
|
||||
Dir: parentDir,
|
||||
}
|
||||
|
||||
// Some internal package have same parent dir or import path
|
||||
// Cache all vars by internal parent dir for all child internal counter vars
|
||||
cacheCover := cover.AddCacheCover(pkg, inPkgCover)
|
||||
if v, ok := tc.CacheCover[cacheCover.Package.Dir]; ok {
|
||||
for cVar, val := range v.Vars {
|
||||
cacheCover.Vars[cVar] = val
|
||||
}
|
||||
tc.CacheCover[cacheCover.Package.Dir] = cacheCover
|
||||
} else {
|
||||
tc.CacheCover[cacheCover.Package.Dir] = cacheCover
|
||||
}
|
||||
|
||||
// Cache all internal vars to internal parent package
|
||||
inCover := cover.CacheInternalCover(inPkgCover)
|
||||
if v, ok := internalPkgCache[cacheCover.Package.Dir]; ok {
|
||||
v = append(v, inCover)
|
||||
internalPkgCache[cacheCover.Package.Dir] = v
|
||||
} else {
|
||||
var covers []*cover.PackageCover
|
||||
covers = append(covers, inCover)
|
||||
internalPkgCache[cacheCover.Package.Dir] = covers
|
||||
}
|
||||
seenCache[dep] = cacheCover
|
||||
continue
|
||||
}
|
||||
|
||||
packageCover, err := cover.AddCounters(depPkg, newgopath)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err)
|
||||
}
|
||||
tc.DepsCover = append(tc.DepsCover, packageCover)
|
||||
seen[dep] = packageCover
|
||||
}
|
||||
}
|
||||
|
||||
if errs := cover.InjectCacheCounters(internalPkgCache, tc.CacheCover); len(errs) > 0 {
|
||||
log.Fatalf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs)
|
||||
}
|
||||
|
||||
// inject Http Cover APIs
|
||||
var httpCoverApis = fmt.Sprintf("%s/http_cover_apis_auto_generated.go", pkg.Dir)
|
||||
if err := cover.InjectCountersHandlers(tc, httpCoverApis); err != nil {
|
||||
log.Fatalf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isDirExist(path string) bool {
|
||||
s, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return s.IsDir()
|
||||
}
|
||||
|
||||
// Refer: https://github.com/golang/go/blob/master/src/cmd/go/internal/load/pkg.go#L1334:6
|
||||
// findInternal looks for the final "internal" path element in the given import path.
|
||||
// If there isn't one, findInternal returns ok=false.
|
||||
// Otherwise, findInternal returns ok=true and the index of the "internal".
|
||||
func findInternal(path string) bool {
|
||||
// Three cases, depending on internal at start/end of string or not.
|
||||
// The order matters: we must return the index of the final element,
|
||||
// because the final one produces the most restrictive requirement
|
||||
// on the importer.
|
||||
switch {
|
||||
case strings.HasSuffix(path, "/internal"):
|
||||
return true
|
||||
case strings.Contains(path, "/internal/"):
|
||||
return true
|
||||
case path == "internal", strings.HasPrefix(path, "internal/"):
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getInternalParent(path string) string {
|
||||
switch {
|
||||
case strings.HasSuffix(path, "/internal"):
|
||||
return strings.Split(path, "/internal")[0]
|
||||
case strings.Contains(path, "/internal/"):
|
||||
return strings.Split(path, "/internal/")[0]
|
||||
case path == "internal":
|
||||
return ""
|
||||
case strings.HasPrefix(path, "internal/"):
|
||||
return strings.Split(path, "internal/")[0]
|
||||
}
|
||||
return ""
|
||||
}
|
69
cmd/goc/app/install.go
Normal file
69
cmd/goc/app/install.go
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/pkg/build"
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Do cover for all go files and execute go install command",
|
||||
Long: `This install command is a little different from the official one, for instance:
|
||||
* 'goc install -- ./...' is equal to 'goc cover && go install ./...'
|
||||
* 'goc install --center=http://127.0.0.1:7777 -- -static ./...' is equal to 'goc cover --center=http://127.0.0.1:7777 && go install -static ./...'`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
newgopath, newwd, tmpdir, pkgs := build.MvProjectsToTmp(target, args)
|
||||
doCover(cmd, args, newgopath, tmpdir)
|
||||
doInstall(args, newgopath, newwd, pkgs)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
installCmd.Flags().StringVarP(¢er, "center", "", "http://127.0.0.1:7777", "cover profile host center")
|
||||
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
func doInstall(args []string, newgopath string, newworkingdir string, pkgs map[string]*cover.Package) {
|
||||
log.Println("Go building in temp...")
|
||||
newArgs := []string{"install"}
|
||||
newArgs = append(newArgs, args...)
|
||||
cmd := exec.Command("go", newArgs...)
|
||||
cmd.Dir = newworkingdir
|
||||
|
||||
// Change the temp GOBIN, to force binary install to original place
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("GOBIN=%v", build.FindWhereToInstall(pkgs)))
|
||||
if newgopath != "" {
|
||||
// Change to temp GOPATH for go install command
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GOPATH=%v", newgopath))
|
||||
}
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to execute: go install %v. The error is: %v, the stdout/stderr is: %v", strings.Join(args, " "), err, string(out))
|
||||
}
|
||||
log.Printf("Go install successful. Binary installed in: %v", build.FindWhereToInstall(pkgs))
|
||||
}
|
4
go.mod
4
go.mod
@ -4,7 +4,11 @@ go 1.13
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/onsi/ginkgo v1.11.0
|
||||
github.com/onsi/gomega v1.8.1
|
||||
github.com/otiai10/copy v1.0.2
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
golang.org/x/tools v0.0.0-20200303214625-2b0b585e22fe
|
||||
k8s.io/test-infra v0.0.0-20200511080351-8ac9dbfab055
|
||||
)
|
||||
|
9
go.sum
9
go.sum
@ -401,6 +401,7 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
@ -522,11 +523,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
|
||||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
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=
|
||||
@ -539,6 +542,7 @@ github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.m
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.0/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc=
|
||||
github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
@ -795,6 +799,7 @@ golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -849,6 +854,7 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -897,6 +903,7 @@ golang.org/x/tools v0.0.0-20200303214625-2b0b585e22fe h1:Kh3iY7o/2bMfQXZdwLdL9jD
|
||||
golang.org/x/tools v0.0.0-20200303214625-2b0b585e22fe/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
|
||||
gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
|
||||
@ -957,6 +964,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
@ -977,6 +985,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=
|
||||
|
77
pkg/build/binarymove.go
Normal file
77
pkg/build/binarymove.go
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 build
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/otiai10/copy"
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
)
|
||||
|
||||
func MvBinaryToOri(pkgs map[string]*cover.Package, newgopath string) {
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.Name == "main" {
|
||||
_, binaryTarget := filepath.Split(pkg.Target)
|
||||
|
||||
binaryTmpPath := filepath.Join(getTmpwd(newgopath, pkgs, !checkIfLegacyProject(pkgs)), binaryTarget)
|
||||
|
||||
if false == checkIfFileExist(binaryTmpPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
curwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot get current working directoy, the error is: %v", err)
|
||||
}
|
||||
binaryOriPath := filepath.Join(curwd, binaryTarget)
|
||||
|
||||
if checkIfFileExist(binaryOriPath) {
|
||||
// if we have file in the original place with same name,
|
||||
// but this file is not a binary,
|
||||
// then we skip it
|
||||
if false == checkIfExecutable(binaryOriPath) {
|
||||
log.Printf("Skipping binary: %v, as we find a file in the original place with same name but not executable.", binaryOriPath)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Generating binary: %v", binaryOriPath)
|
||||
if err = copy.Copy(binaryTmpPath, binaryOriPath); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkIfExecutable(path string) bool {
|
||||
fileInfo, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fileInfo.Mode()&0100 != 0
|
||||
}
|
||||
|
||||
func checkIfFileExist(path string) bool {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return !fileInfo.IsDir()
|
||||
}
|
40
pkg/build/gomodules.go
Normal file
40
pkg/build/gomodules.go
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 build
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/otiai10/copy"
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
)
|
||||
|
||||
func cpGoModulesProject(tmpBuildDir string, pkgs map[string]*cover.Package) {
|
||||
for _, v := range pkgs {
|
||||
if v.Name == "main" {
|
||||
dst := tmpBuildDir
|
||||
src := v.Module.Dir
|
||||
|
||||
if err := copy.Copy(src, dst); err != nil {
|
||||
log.Printf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
83
pkg/build/legacy.go
Normal file
83
pkg/build/legacy.go
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 build
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/otiai10/copy"
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
)
|
||||
|
||||
func cpLegacyProject(tmpBuildDir string, pkgs map[string]*cover.Package) {
|
||||
|
||||
visited := make(map[string]bool)
|
||||
|
||||
for k, v := range pkgs {
|
||||
dst := filepath.Join(tmpBuildDir, "src", k)
|
||||
src := v.Dir
|
||||
|
||||
if _, ok := visited[src]; ok {
|
||||
// Skip if already copied
|
||||
continue
|
||||
}
|
||||
|
||||
if err := copy.Copy(src, dst); err != nil {
|
||||
log.Printf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
}
|
||||
|
||||
visited[src] = true
|
||||
|
||||
cpDepPackages(tmpBuildDir, v, visited)
|
||||
}
|
||||
}
|
||||
|
||||
// only cp dependency in root(current gopath),
|
||||
// skip deps in other GOPATHs
|
||||
func cpDepPackages(tmpBuildDir string, pkg *cover.Package, visited map[string]bool) {
|
||||
/*
|
||||
oriGOPATH := os.Getenv("GOPATH")
|
||||
if oriGOPATH == "" {
|
||||
oriGOPATH = filepath.Join(os.Getenv("HOME"), "go")
|
||||
}
|
||||
gopaths := strings.Split(oriGOPATH, ":")
|
||||
*/
|
||||
gopath := pkg.Root
|
||||
for _, dep := range pkg.Deps {
|
||||
src := filepath.Join(gopath, "src", dep)
|
||||
// Check if copied
|
||||
if _, ok := visited[src]; ok {
|
||||
// Skip if already copied
|
||||
continue
|
||||
}
|
||||
// Check if we can found in the root gopath
|
||||
_, err := os.Stat(src)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
dst := filepath.Join(tmpBuildDir, "src", dep)
|
||||
|
||||
if err := copy.Copy(src, dst); err != nil {
|
||||
log.Printf("Failed to Copy the folder from %v to %v, the error is: %v ", src, dst, err)
|
||||
}
|
||||
|
||||
visited[src] = true
|
||||
}
|
||||
}
|
149
pkg/build/tmpfolder.go
Normal file
149
pkg/build/tmpfolder.go
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 build
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
)
|
||||
|
||||
func MvProjectsToTmp(target string, args []string) (newgopath string, newWorkingDir string, tmpBuildDir string, pkgs map[string]*cover.Package) {
|
||||
listArgs := []string{"list", "-json"}
|
||||
if len(args) != 0 {
|
||||
listArgs = append(listArgs, args...)
|
||||
}
|
||||
listArgs = append(listArgs, "./...")
|
||||
pkgs = cover.ListPackages(target, listArgs, "")
|
||||
|
||||
tmpBuildDir, newWorkingDir, isMod := mvProjectsToTmp(pkgs)
|
||||
origopath := os.Getenv("GOPATH")
|
||||
if isMod == true {
|
||||
newgopath = ""
|
||||
} else if origopath == "" {
|
||||
newgopath = tmpBuildDir
|
||||
} else {
|
||||
newgopath = fmt.Sprintf("%v:%v", tmpBuildDir, origopath)
|
||||
}
|
||||
log.Printf("New GOPATH: %v", newgopath)
|
||||
return
|
||||
}
|
||||
|
||||
func mvProjectsToTmp(pkgs map[string]*cover.Package) (string, string, bool) {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot get current working directoy, the error is: %v", err)
|
||||
}
|
||||
tmpBuildDir := filepath.Join(os.TempDir(), TmpFolderName(path))
|
||||
|
||||
// Delete previous tmp folder and its content
|
||||
os.RemoveAll(tmpBuildDir)
|
||||
// Create a new tmp folder
|
||||
err = os.MkdirAll(filepath.Join(tmpBuildDir, "src"), os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to create the temporary build directory. The err is: %v", err)
|
||||
}
|
||||
log.Printf("Temp project generated in: %v", tmpBuildDir)
|
||||
|
||||
isMod := false
|
||||
var tmpWorkingDir string
|
||||
if checkIfLegacyProject(pkgs) {
|
||||
cpLegacyProject(tmpBuildDir, pkgs)
|
||||
tmpWorkingDir = getTmpwd(tmpBuildDir, pkgs, false)
|
||||
} else {
|
||||
cpGoModulesProject(tmpBuildDir, pkgs)
|
||||
tmpWorkingDir = getTmpwd(tmpBuildDir, pkgs, true)
|
||||
isMod = true
|
||||
}
|
||||
|
||||
log.Printf("New working/building directory in: %v", tmpWorkingDir)
|
||||
return tmpBuildDir, tmpWorkingDir, isMod
|
||||
}
|
||||
|
||||
func TmpFolderName(path string) string {
|
||||
sum := sha256.Sum256([]byte(path))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
|
||||
return "goc-" + h
|
||||
}
|
||||
|
||||
// Check if it is go module project
|
||||
// true legacy
|
||||
// flase go mod
|
||||
func checkIfLegacyProject(pkgs map[string]*cover.Package) bool {
|
||||
for _, v := range pkgs {
|
||||
|
||||
if v.Module == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
log.Fatalln("Should never be reached....")
|
||||
return false
|
||||
}
|
||||
|
||||
func getTmpwd(tmpBuildDir string, pkgs map[string]*cover.Package, isMod bool) string {
|
||||
for _, pkg := range pkgs {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot get current working directoy, the error is: %v", err)
|
||||
}
|
||||
|
||||
index := -1
|
||||
var parentPath string
|
||||
if isMod == false {
|
||||
index = strings.Index(path, pkg.Root)
|
||||
parentPath = pkg.Root
|
||||
} else {
|
||||
index = strings.Index(path, pkg.Module.Dir)
|
||||
parentPath = pkg.Module.Dir
|
||||
}
|
||||
|
||||
if index == -1 {
|
||||
log.Fatalf("goc install not executed in project directory.")
|
||||
}
|
||||
tmpwd := filepath.Join(tmpBuildDir, path[len(parentPath):])
|
||||
// log.Printf("New building directory in: %v", tmpwd)
|
||||
return tmpwd
|
||||
}
|
||||
|
||||
log.Fatalln("Should never be reached....")
|
||||
return ""
|
||||
}
|
||||
|
||||
func FindWhereToInstall(pkgs map[string]*cover.Package) string {
|
||||
if GOBIN := os.Getenv("GOBIN"); GOBIN != "" {
|
||||
return GOBIN
|
||||
}
|
||||
|
||||
// old GOPATH dir
|
||||
GOPATH := os.Getenv("GOPATH")
|
||||
if true == checkIfLegacyProject(pkgs) {
|
||||
for _, v := range pkgs {
|
||||
return filepath.Join(v.Root, "bin")
|
||||
}
|
||||
}
|
||||
if GOPATH != "" {
|
||||
return filepath.Join(strings.Split(GOPATH, ":")[0], "bin")
|
||||
}
|
||||
return filepath.Join(os.Getenv("HOME"), "go", "bin")
|
||||
}
|
133
pkg/build/tmpfolder_test.go
Normal file
133
pkg/build/tmpfolder_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 build
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/qiniu/goc/pkg/cover"
|
||||
)
|
||||
|
||||
const TEST_GO_LIST_LEGACY = `{
|
||||
"Dir": "/Users/lyyyuna/gitup/linking/src/qiniu.com/linking/api/linking.v1",
|
||||
"ImportPath": "qiniu.com/linking/api/linking.v1",
|
||||
"Name": "linking",
|
||||
"Target": "/Users/lyyyuna/gitup/linking/pkg/darwin_amd64/qiniu.com/linking/api/linking.v1.a",
|
||||
"Root": "/Users/lyyyuna/gitup/linking",
|
||||
"Match": [
|
||||
"./..."
|
||||
],
|
||||
"Stale": true,
|
||||
"StaleReason": "stale dependency: vendor/github.com/modern-go/concurrent",
|
||||
"GoFiles": [
|
||||
"client.go"
|
||||
],
|
||||
"Imports": [
|
||||
"vendor/github.com/json-iterator/go",
|
||||
"github.com/qiniu/rpc.v2",
|
||||
"vendor/github.com/qiniu/xlog.v1",
|
||||
"vendor/qiniu.com/auth/qiniumac.v1"
|
||||
],
|
||||
"ImportMap": {
|
||||
"github.com/json-iterator/go": "vendor/github.com/json-iterator/go",
|
||||
"github.com/qiniu/xlog.v1": "vendor/github.com/qiniu/xlog.v1",
|
||||
"qiniu.com/auth/qiniumac.v1": "vendor/qiniu.com/auth/qiniumac.v1"
|
||||
},
|
||||
"Deps": [
|
||||
"bufio"
|
||||
]
|
||||
}`
|
||||
|
||||
const TEST_GO_LIST_MOD = `{
|
||||
"Dir": "/Users/lyyyuna/gitup/tonghu-chat",
|
||||
"ImportPath": "github.com/lyyyuna/tonghu-chat",
|
||||
"Name": "main",
|
||||
"Target": "/Users/lyyyuna/go/bin/tonghu-chat",
|
||||
"Root": "/Users/lyyyuna/gitup/tonghu-chat",
|
||||
"Module": {
|
||||
"Path": "github.com/lyyyuna/tonghu-chat",
|
||||
"Main": true,
|
||||
"Dir": "/Users/lyyyuna/gitup/tonghu-chat",
|
||||
"GoMod": "/Users/lyyyuna/gitup/tonghu-chat/go.mod",
|
||||
"GoVersion": "1.14"
|
||||
},
|
||||
"Match": [
|
||||
"./..."
|
||||
],
|
||||
"Stale": true,
|
||||
"StaleReason": "not installed but available in build cache",
|
||||
"GoFiles": [
|
||||
"main.go"
|
||||
],
|
||||
"Imports": [
|
||||
"github.com/gin-gonic/gin",
|
||||
"github.com/gorilla/websocket"
|
||||
],
|
||||
"Deps": [
|
||||
"bufio"
|
||||
]
|
||||
}`
|
||||
|
||||
func constructPkg(raw string) *cover.Package {
|
||||
var pkg cover.Package
|
||||
if err := json.Unmarshal([]byte(raw), &pkg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &pkg
|
||||
}
|
||||
|
||||
func TestLegacyProjectJudgement(t *testing.T) {
|
||||
pkgs := make(map[string]*cover.Package)
|
||||
pkg := constructPkg(TEST_GO_LIST_LEGACY)
|
||||
pkgs[pkg.ImportPath] = pkg
|
||||
if expect, got := true, checkIfLegacyProject(pkgs); expect != got {
|
||||
t.Fatalf("Expected %v, but got %v.", expect, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModProjectJudgement(t *testing.T) {
|
||||
pkgs := make(map[string]*cover.Package)
|
||||
pkg := constructPkg(TEST_GO_LIST_MOD)
|
||||
pkgs[pkg.ImportPath] = pkg
|
||||
if expect, got := false, checkIfLegacyProject(pkgs); expect != got {
|
||||
t.Fatalf("Expected %v, but got %v.", expect, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDirParseInLegacyProject(t *testing.T) {
|
||||
workingDir := "../../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project"
|
||||
gopath, _ := filepath.Abs("../../tests/samples/simple_gopath_project")
|
||||
|
||||
os.Chdir(workingDir)
|
||||
fmt.Println(gopath)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
os.Setenv("GO111MODULE", "off")
|
||||
|
||||
newgopath, newwd, tmpdir, _ := MvProjectsToTmp(".", nil)
|
||||
if -1 == strings.Index(newwd, tmpdir) {
|
||||
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", newwd, tmpdir)
|
||||
}
|
||||
|
||||
if -1 == strings.Index(newgopath, ":") || -1 == strings.Index(newgopath, tmpdir) {
|
||||
t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", newgopath, tmpdir)
|
||||
}
|
||||
}
|
384
pkg/cover/cover.go
Normal file
384
pkg/cover/cover.go
Normal file
@ -0,0 +1,384 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 cover
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestCover is a collection of all counters
|
||||
type TestCover struct {
|
||||
Mode string
|
||||
Center string // cover profile host center
|
||||
MainPkgCover *PackageCover
|
||||
DepsCover []*PackageCover
|
||||
CacheCover map[string]*PackageCover
|
||||
}
|
||||
|
||||
// PackageCover holds all the generate coverage variables of a package
|
||||
type PackageCover struct {
|
||||
Package *Package
|
||||
Vars map[string]*FileVar
|
||||
}
|
||||
|
||||
// FileVar holds the name of the generated coverage variables targeting the named file.
|
||||
type FileVar struct {
|
||||
File string
|
||||
Var string
|
||||
}
|
||||
|
||||
// Package map a package output by go list
|
||||
// this is subset of pakcage struct in: https://github.com/golang/go/blob/master/src/cmd/go/internal/load/pkg.go#L58
|
||||
type Package struct {
|
||||
Dir string `json:"Dir"` // directory containing package sources
|
||||
ImportPath string `json:"ImportPath"` // import path of package in dir
|
||||
Name string `json:"Name"` // package name
|
||||
Target string `json:",omitempty"` // installed target for this package (may be executable)
|
||||
Root string `json:",omitempty"` // Go root, Go path dir, or module root dir containing this package
|
||||
|
||||
Module *ModulePublic `json:",omitempty"` // info about package's module, if any
|
||||
Goroot bool `json:"Goroot,omitempty"` // is this package in the Go root?
|
||||
Standard bool `json:"Standard,omitempty"` // is this package part of the standard Go library?
|
||||
DepOnly bool `json:"DepOnly,omitempty"` // package is only a dependency, not explicitly listed
|
||||
|
||||
// Source files
|
||||
GoFiles []string `json:"GoFiles,omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
||||
CgoFiles []string `json:"CgoFiles,omitempty"` // .go source files that import "C"
|
||||
|
||||
// Dependency information
|
||||
Deps []string `json:"Deps,omitempty"` // all (recursively) imported dependencies
|
||||
Imports []string `json:",omitempty"` // import paths used by this package
|
||||
ImportMap map[string]string `json:",omitempty"` // map from source import to ImportPath (identity entries omitted)
|
||||
|
||||
// Error information
|
||||
Incomplete bool `json:"Incomplete,omitempty"` // this package or a dependency has an error
|
||||
Error *PackageError `json:"Error,omitempty"` // error loading package
|
||||
DepsErrors []*PackageError `json:"DepsErrors,omitempty"` // errors loading dependencies
|
||||
}
|
||||
|
||||
type ModulePublic struct {
|
||||
Path string `json:",omitempty"` // module path
|
||||
Version string `json:",omitempty"` // module version
|
||||
Versions []string `json:",omitempty"` // available module versions
|
||||
Replace *ModulePublic `json:",omitempty"` // replaced by this module
|
||||
Time *time.Time `json:",omitempty"` // time version was created
|
||||
Update *ModulePublic `json:",omitempty"` // available update (with -u)
|
||||
Main bool `json:",omitempty"` // is this the main module?
|
||||
Indirect bool `json:",omitempty"` // module is only indirectly needed by main module
|
||||
Dir string `json:",omitempty"` // directory holding local copy of files, if any
|
||||
GoMod string `json:",omitempty"` // path to go.mod file describing module, if any
|
||||
GoVersion string `json:",omitempty"` // go version used in module
|
||||
Error *ModuleError `json:",omitempty"` // error loading module
|
||||
}
|
||||
|
||||
type ModuleError struct {
|
||||
Err string // error text
|
||||
}
|
||||
|
||||
// PackageError is the error info for a package when list failed
|
||||
type PackageError struct {
|
||||
ImportStack []string // shortest path from package named on command line to this one
|
||||
Pos string // position of error (if present, file:line:col)
|
||||
Err string // the error itself
|
||||
}
|
||||
|
||||
// ListPackages list all packages under specific via go list command
|
||||
func ListPackages(dir string, args []string, newgopath string) map[string]*Package {
|
||||
cmd := exec.Command("go", args...)
|
||||
log.Printf("go list cmd is: %v", cmd.Args)
|
||||
cmd.Dir = dir
|
||||
if newgopath != "" {
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", newgopath))
|
||||
}
|
||||
out, _ := cmd.Output()
|
||||
// if err != nil {
|
||||
// log.Fatalf("excute `go list -json ./...` command failed, err: %v, out: %v", err, string(out))
|
||||
// }
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(out))
|
||||
pkgs := make(map[string]*Package, 0)
|
||||
for {
|
||||
var pkg Package
|
||||
if err := dec.Decode(&pkg); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Fatalf("reading go list output: %v", err)
|
||||
}
|
||||
if pkg.Error != nil {
|
||||
log.Fatalf("list package %s failed with output: %v", pkg.ImportPath, pkg.Error)
|
||||
}
|
||||
|
||||
// for _, err := range pkg.DepsErrors {
|
||||
// log.Fatalf("dependency package list failed, err: %v", err)
|
||||
// }
|
||||
|
||||
pkgs[pkg.ImportPath] = &pkg
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// AddCounters add counters for all go files under the package
|
||||
func AddCounters(pkg *Package, newgopath string) (*PackageCover, error) {
|
||||
coverVarMap := declareCoverVars(pkg)
|
||||
|
||||
// to construct: go tool cover -mode=atomic -o dest src (note: dest==src)
|
||||
var args = []string{"tool", "cover", "-mode=atomic"}
|
||||
for file, coverVar := range coverVarMap {
|
||||
var newArgs = args
|
||||
newArgs = append(newArgs, "-var", coverVar.Var)
|
||||
longPath := path.Join(pkg.Dir, file)
|
||||
newArgs = append(newArgs, "-o", longPath, longPath)
|
||||
cmd := exec.Command("go", newArgs...)
|
||||
if newgopath != "" {
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", newgopath))
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execuate go tool cover -mode=atomic -var %s -o %s %s failed, err: %v, out: %s", coverVar.Var, longPath, longPath, err, string(out))
|
||||
}
|
||||
}
|
||||
|
||||
return &PackageCover{
|
||||
Package: pkg,
|
||||
Vars: coverVarMap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// declareCoverVars attaches the required cover variables names
|
||||
// to the files, to be used when annotating the files.
|
||||
func declareCoverVars(p *Package) map[string]*FileVar {
|
||||
coverVars := make(map[string]*FileVar)
|
||||
coverIndex := 0
|
||||
// We create the cover counters as new top-level variables in the package.
|
||||
// We need to avoid collisions with user variables (GoCover_0 is unlikely but still)
|
||||
// and more importantly with dot imports of other covered packages,
|
||||
// so we append 12 hex digits from the SHA-256 of the import path.
|
||||
// The point is only to avoid accidents, not to defeat users determined to
|
||||
// break things.
|
||||
sum := sha256.Sum256([]byte(p.ImportPath))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
for _, file := range p.GoFiles {
|
||||
// These names appear in the cmd/cover HTML interface.
|
||||
var longFile = path.Join(p.ImportPath, file)
|
||||
coverVars[file] = &FileVar{
|
||||
File: longFile,
|
||||
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
|
||||
}
|
||||
coverIndex++
|
||||
}
|
||||
|
||||
for _, file := range p.CgoFiles {
|
||||
// These names appear in the cmd/cover HTML interface.
|
||||
var longFile = path.Join(p.ImportPath, file)
|
||||
coverVars[file] = &FileVar{
|
||||
File: longFile,
|
||||
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
|
||||
}
|
||||
coverIndex++
|
||||
}
|
||||
|
||||
return coverVars
|
||||
}
|
||||
|
||||
func declareCacheVars(in *PackageCover) map[string]*FileVar {
|
||||
sum := sha256.Sum256([]byte(in.Package.ImportPath))
|
||||
h := fmt.Sprintf("%x", sum[:5])
|
||||
|
||||
vars := make(map[string]*FileVar)
|
||||
coverIndex := 0
|
||||
for _, v := range in.Vars {
|
||||
cacheVar := fmt.Sprintf("GoCacheCover_%d_%x", coverIndex, h)
|
||||
vars[cacheVar] = v
|
||||
coverIndex++
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
func CacheInternalCover(in *PackageCover) *PackageCover {
|
||||
c := &PackageCover{}
|
||||
vars := declareCacheVars(in)
|
||||
c.Package = in.Package
|
||||
c.Vars = vars
|
||||
return c
|
||||
}
|
||||
|
||||
func AddCacheCover(pkg *Package, in *PackageCover) *PackageCover {
|
||||
c := &PackageCover{}
|
||||
sum := sha256.Sum256([]byte(pkg.ImportPath))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
goFile := fmt.Sprintf("cache_vars_auto_generated_%x.go", h)
|
||||
p := &Package{
|
||||
Dir: fmt.Sprintf("%s/cache_%x", pkg.Dir, h),
|
||||
ImportPath: fmt.Sprintf("%s/cache_%x", pkg.ImportPath, h),
|
||||
Name: fmt.Sprintf("cache_%x", h),
|
||||
}
|
||||
p.GoFiles = append(p.GoFiles, goFile)
|
||||
c.Package = p
|
||||
c.Vars = declareCacheVars(in)
|
||||
return c
|
||||
}
|
||||
|
||||
// CoverageList is a collection and summary over multiple file Coverage objects
|
||||
type CoverageList struct {
|
||||
*Coverage
|
||||
Groups []Coverage
|
||||
ConcernedFiles map[string]bool
|
||||
CovThresholdInt int
|
||||
}
|
||||
|
||||
// Coverage stores test coverage summary data for one file
|
||||
type Coverage struct {
|
||||
FileName string
|
||||
NCoveredStmts int
|
||||
NAllStmts int
|
||||
LineCovLink string
|
||||
}
|
||||
|
||||
type codeBlock struct {
|
||||
fileName string // the file the code block is in
|
||||
numStatements int // number of statements in the code block
|
||||
coverageCount int // number of times the block is covered
|
||||
}
|
||||
|
||||
func CovList(f io.Reader) (g *CoverageList, err error) {
|
||||
scanner := bufio.NewScanner(f)
|
||||
scanner.Scan() // discard first line
|
||||
g = NewCoverageList("", map[string]bool{}, 0)
|
||||
|
||||
for scanner.Scan() {
|
||||
row := scanner.Text()
|
||||
blk, err := toBlock(row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blk.addToGroupCov(g)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewCoverageList constructs new (file) group Coverage
|
||||
func NewCoverageList(name string, concernedFiles map[string]bool, covThresholdInt int) *CoverageList {
|
||||
return &CoverageList{
|
||||
Coverage: newCoverage(name),
|
||||
Groups: []Coverage{},
|
||||
ConcernedFiles: concernedFiles,
|
||||
CovThresholdInt: covThresholdInt,
|
||||
}
|
||||
}
|
||||
|
||||
func newCoverage(name string) *Coverage {
|
||||
return &Coverage{name, 0, 0, ""}
|
||||
}
|
||||
|
||||
// convert a line in profile file to a codeBlock struct
|
||||
func toBlock(line string) (res *codeBlock, err error) {
|
||||
slice := strings.Split(line, " ")
|
||||
if len(slice) != 3 {
|
||||
return nil, fmt.Errorf("the profile line %s is not expected", line)
|
||||
}
|
||||
blockName := slice[0]
|
||||
nStmts, _ := strconv.Atoi(slice[1])
|
||||
coverageCount, _ := strconv.Atoi(slice[2])
|
||||
return &codeBlock{
|
||||
fileName: blockName[:strings.Index(blockName, ":")],
|
||||
numStatements: nStmts,
|
||||
coverageCount: coverageCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// add blk Coverage to file group Coverage
|
||||
func (blk *codeBlock) addToGroupCov(g *CoverageList) {
|
||||
if g.size() == 0 || g.lastElement().Name() != blk.fileName {
|
||||
// when a new file name is processed
|
||||
coverage := newCoverage(blk.fileName)
|
||||
g.append(coverage)
|
||||
}
|
||||
cov := g.lastElement()
|
||||
cov.NAllStmts += blk.numStatements
|
||||
if blk.coverageCount > 0 {
|
||||
cov.NCoveredStmts += blk.numStatements
|
||||
}
|
||||
}
|
||||
|
||||
func (g *CoverageList) size() int {
|
||||
return len(g.Groups)
|
||||
}
|
||||
|
||||
func (g *CoverageList) lastElement() *Coverage {
|
||||
return &g.Groups[g.size()-1]
|
||||
}
|
||||
|
||||
func (g *CoverageList) append(c *Coverage) {
|
||||
g.Groups = append(g.Groups, *c)
|
||||
}
|
||||
|
||||
// Group returns the collection of file Coverage objects
|
||||
func (g *CoverageList) Group() *[]Coverage {
|
||||
return &g.Groups
|
||||
}
|
||||
|
||||
// Map returns maps the file name to its coverage for faster retrieval
|
||||
// & membership check
|
||||
func (g *CoverageList) Map() map[string]Coverage {
|
||||
m := make(map[string]Coverage)
|
||||
for _, c := range g.Groups {
|
||||
m[c.Name()] = c
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Name returns the file name
|
||||
func (c *Coverage) Name() string {
|
||||
return c.FileName
|
||||
}
|
||||
|
||||
// Percentage returns the percentage of statements covered
|
||||
func (c *Coverage) Percentage() string {
|
||||
ratio, err := c.Ratio()
|
||||
if err == nil {
|
||||
return PercentStr(ratio)
|
||||
}
|
||||
|
||||
return "N/A"
|
||||
}
|
||||
|
||||
func (c *Coverage) Ratio() (ratio float32, err error) {
|
||||
if c.NAllStmts == 0 {
|
||||
err = fmt.Errorf("[%s] has 0 statement", c.Name())
|
||||
} else {
|
||||
ratio = float32(c.NCoveredStmts) / float32(c.NAllStmts)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PercentStr converts a fraction number to percentage string representation
|
||||
func PercentStr(f float32) string {
|
||||
return fmt.Sprintf("%.1f%%", f*100)
|
||||
}
|
97
pkg/cover/cover_test.go
Normal file
97
pkg/cover/cover_test.go
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 cover
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testCoverage() (c *Coverage) {
|
||||
return &Coverage{FileName: "fake-coverage", NCoveredStmts: 200, NAllStmts: 300}
|
||||
}
|
||||
|
||||
func TestCoverageRatio(t *testing.T) {
|
||||
c := testCoverage()
|
||||
actualRatio, _ := c.Ratio()
|
||||
assert.Equal(t, float32(c.NCoveredStmts)/float32(c.NAllStmts), actualRatio)
|
||||
}
|
||||
|
||||
func TestRatioErr(t *testing.T) {
|
||||
c := &Coverage{FileName: "fake-coverage", NCoveredStmts: 200, NAllStmts: 0}
|
||||
_, err := c.Ratio()
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestPercentageNA(t *testing.T) {
|
||||
c := &Coverage{FileName: "fake-coverage", NCoveredStmts: 200, NAllStmts: 0}
|
||||
assert.Equal(t, "N/A", c.Percentage())
|
||||
}
|
||||
|
||||
func TestGenLocalCoverDiffReport(t *testing.T) {
|
||||
//coverage increase
|
||||
newList := &CoverageList{Groups: []Coverage{{FileName: "fake-coverage", NCoveredStmts: 15, NAllStmts: 20}}}
|
||||
baseList := &CoverageList{Groups: []Coverage{{FileName: "fake-coverage", NCoveredStmts: 10, NAllStmts: 20}}}
|
||||
rows := GenLocalCoverDiffReport(newList, baseList)
|
||||
assert.Equal(t, 1, len(rows))
|
||||
assert.Equal(t, []string{"fake-coverage", "50.0%", "75.0%", "25.0%"}, rows[0])
|
||||
|
||||
//coverage decrease
|
||||
baseList = &CoverageList{Groups: []Coverage{{FileName: "fake-coverage", NCoveredStmts: 20, NAllStmts: 20}}}
|
||||
rows = GenLocalCoverDiffReport(newList, baseList)
|
||||
assert.Equal(t, []string{"fake-coverage", "100.0%", "75.0%", "-25.0%"}, rows[0])
|
||||
|
||||
//diff file
|
||||
baseList = &CoverageList{Groups: []Coverage{{FileName: "fake-coverage-v1", NCoveredStmts: 10, NAllStmts: 20}}}
|
||||
rows = GenLocalCoverDiffReport(newList, baseList)
|
||||
assert.Equal(t, []string{"fake-coverage", "None", "75.0%", "75.0%"}, rows[0])
|
||||
}
|
||||
|
||||
func TestCovList(t *testing.T) {
|
||||
fileName := "qiniu.com/kodo/apiserver/server/main.go"
|
||||
|
||||
// percentage is 100%
|
||||
p := strings.NewReader("mode: atomic\n" +
|
||||
fileName + ":32.49,33.13 1 30\n")
|
||||
covL, err := CovList(p)
|
||||
covF := covL.Map()[fileName]
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "100.0%", covF.Percentage())
|
||||
|
||||
// percentage is 50%
|
||||
p = strings.NewReader("mode: atomic\n" +
|
||||
fileName + ":32.49,33.13 1 30\n" +
|
||||
fileName + ":42.49,43.13 1 0\n")
|
||||
covL, err = CovList(p)
|
||||
covF = covL.Map()[fileName]
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "50.0%", covF.Percentage())
|
||||
|
||||
// two files
|
||||
fileName1 := "qiniu.com/kodo/apiserver/server/svr.go"
|
||||
p = strings.NewReader("mode: atomic\n" +
|
||||
fileName + ":32.49,33.13 1 30\n" +
|
||||
fileName1 + ":42.49,43.13 1 0\n")
|
||||
covL, err = CovList(p)
|
||||
covF = covL.Map()[fileName]
|
||||
covF1 := covL.Map()[fileName1]
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "100.0%", covF.Percentage())
|
||||
assert.Equal(t, "0.0%", covF1.Percentage())
|
||||
}
|
56
pkg/cover/delta.go
Normal file
56
pkg/cover/delta.go
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 cover
|
||||
|
||||
type GroupChanges struct {
|
||||
Added []Coverage
|
||||
Deleted []Coverage
|
||||
Unchanged []Coverage
|
||||
Changed []Incremental
|
||||
BaseGroup *CoverageList
|
||||
NewGroup *CoverageList
|
||||
}
|
||||
|
||||
type Incremental struct {
|
||||
base Coverage
|
||||
new Coverage
|
||||
}
|
||||
|
||||
func GenLocalCoverDiffReport(newList *CoverageList, baseList *CoverageList) [][]string {
|
||||
var rows [][]string
|
||||
basePMap := baseList.Map()
|
||||
|
||||
for _, l := range newList.Groups {
|
||||
baseCov, ok := basePMap[l.Name()]
|
||||
if !ok {
|
||||
rows = append(rows, []string{l.FileName, "None", l.Percentage(), PercentStr(Delta(l, baseCov))})
|
||||
continue
|
||||
}
|
||||
if l.Percentage() == baseCov.Percentage() {
|
||||
continue
|
||||
}
|
||||
rows = append(rows, []string{l.FileName, baseCov.Percentage(), l.Percentage(), PercentStr(Delta(l, baseCov))})
|
||||
}
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
func Delta(new Coverage, base Coverage) float32 {
|
||||
baseRatio, _ := base.Ratio()
|
||||
newRatio, _ := new.Ratio()
|
||||
return newRatio - baseRatio
|
||||
}
|
368
pkg/cover/instrument.go
Normal file
368
pkg/cover/instrument.go
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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 cover
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// InjectCountersHandlers generate a file _cover_http_apis.go besides the main.go file
|
||||
func InjectCountersHandlers(tc TestCover, dest string) error {
|
||||
f, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := coverMainTmpl.Execute(f, tc); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var coverMainTmpl = template.Must(template.New("coverMain").Parse(coverMain))
|
||||
|
||||
const coverMain = `
|
||||
// Code generated by goc system. DO NOT EDIT.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
{{range $i, $pkgCover := .DepsCover}}
|
||||
_cover{{$i}} {{$pkgCover.Package.ImportPath | printf "%q"}}
|
||||
{{end}}
|
||||
|
||||
{{range $k, $pkgCover := .CacheCover}}
|
||||
{{$pkgCover.Package.ImportPath | printf "%q"}}
|
||||
{{end}}
|
||||
|
||||
)
|
||||
|
||||
func init() {
|
||||
go registerHandlers()
|
||||
}
|
||||
|
||||
func loadValues() (map[string][]uint32, map[string][]testing.CoverBlock) {
|
||||
var (
|
||||
coverCounters = make(map[string][]uint32)
|
||||
coverBlocks = make(map[string][]testing.CoverBlock)
|
||||
)
|
||||
|
||||
{{range $i, $pkgCover := .DepsCover}}
|
||||
{{range $file, $cover := $pkgCover.Vars}}
|
||||
loadFileCover(coverCounters, coverBlocks, {{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range $file, $cover := .MainPkgCover.Vars}}
|
||||
loadFileCover(coverCounters, coverBlocks, {{printf "%q" $cover.File}}, {{$cover.Var}}.Count[:], {{$cover.Var}}.Pos[:], {{$cover.Var}}.NumStmt[:])
|
||||
{{end}}
|
||||
|
||||
{{range $k, $pkgCover := .CacheCover}}
|
||||
{{range $v, $cover := $pkgCover.Vars}}
|
||||
loadFileCover(coverCounters, coverBlocks, {{printf "%q" $cover.File}}, {{$pkgCover.Package.Name}}.{{$v}}.Count[:], {{$pkgCover.Package.Name}}.{{$v}}.Pos[:], {{$pkgCover.Package.Name}}.{{$v}}.NumStmt[:])
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
return coverCounters, coverBlocks
|
||||
}
|
||||
|
||||
func loadFileCover(coverCounters map[string][]uint32, coverBlocks map[string][]testing.CoverBlock, fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
|
||||
if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
|
||||
panic("coverage: mismatched sizes")
|
||||
}
|
||||
if coverCounters[fileName] != nil {
|
||||
// Already registered.
|
||||
return
|
||||
}
|
||||
coverCounters[fileName] = counter
|
||||
block := make([]testing.CoverBlock, len(counter))
|
||||
for i := range counter {
|
||||
block[i] = testing.CoverBlock{
|
||||
Line0: pos[3*i+0],
|
||||
Col0: uint16(pos[3*i+2]),
|
||||
Line1: pos[3*i+1],
|
||||
Col1: uint16(pos[3*i+2] >> 16),
|
||||
Stmts: numStmts[i],
|
||||
}
|
||||
}
|
||||
coverBlocks[fileName] = block
|
||||
}
|
||||
|
||||
func registerHandlers() {
|
||||
ln, host, err := listen()
|
||||
if err != nil {
|
||||
log.Fatalf("profile listen failed, err:%v", err)
|
||||
}
|
||||
log.Println("profile listen on", host)
|
||||
profileAddr := "http://" + host
|
||||
if resp, err := registerSelf(profileAddr); err != nil {
|
||||
log.Fatalf("register address %v failed, err: %v, response: %v", profileAddr, err, string(resp))
|
||||
}
|
||||
go genProfileAddr(host)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
// Coverage reports the current code coverage as a fraction in the range [0, 1].
|
||||
// If coverage is not enabled, Coverage returns 0.
|
||||
mux.HandleFunc("/v1/cover/coverage", func(w http.ResponseWriter, r *http.Request) {
|
||||
counters, _ := loadValues()
|
||||
|
||||
var n, d int64
|
||||
for _, counter := range counters {
|
||||
for i := range counter {
|
||||
if atomic.LoadUint32(&counter[i]) > 0 {
|
||||
n++
|
||||
}
|
||||
d++
|
||||
}
|
||||
}
|
||||
if d == 0 {
|
||||
fmt.Fprint(w, 0)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "%f", float64(n)/float64(d))
|
||||
})
|
||||
|
||||
// coverprofile reports a coverage profile with the coverage percentage
|
||||
mux.HandleFunc("/v1/cover/profile", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, "mode: atomic\n")
|
||||
counters, blocks := loadValues()
|
||||
|
||||
var active, total int64
|
||||
var count uint32
|
||||
for name, counts := range counters {
|
||||
block := blocks[name]
|
||||
for i := range counts {
|
||||
stmts := int64(block[i].Stmts)
|
||||
total += stmts
|
||||
count = atomic.LoadUint32(&counts[i]) // For -mode=atomic.
|
||||
if count > 0 {
|
||||
active += stmts
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "%s:%d.%d,%d.%d %d %d\n", name,
|
||||
block[i].Line0, block[i].Col0,
|
||||
block[i].Line1, block[i].Col1,
|
||||
stmts,
|
||||
count)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "invalid block format, err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/v1/cover/clear", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, "TO BE IMPLEMENTED!\n")
|
||||
})
|
||||
|
||||
log.Fatal(http.Serve(ln, mux))
|
||||
}
|
||||
|
||||
func registerSelf(address string) ([]byte, error) {
|
||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/cover/register?name=%s&address=%s", {{.Center | printf "%q"}}, os.Args[0], address), nil)
|
||||
if err != nil {
|
||||
log.Fatalf("http.NewRequest failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil && isNetworkError(err) {
|
||||
log.Printf("[WARN]error occured:%v, try again", err)
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("registed faile, err:%v", err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response body, err:%v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("registed failed, response code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return body, err
|
||||
}
|
||||
|
||||
func isNetworkError(err error) bool {
|
||||
if err == io.EOF {
|
||||
return true
|
||||
}
|
||||
_, ok := err.(net.Error)
|
||||
return ok
|
||||
}
|
||||
|
||||
func listen() (ln net.Listener, host string, err error) {
|
||||
// 获取上次使用的监听地址
|
||||
if previousAddr := getPreviousAddr(); previousAddr != "" {
|
||||
ss := strings.Split(previousAddr, ":")
|
||||
// listen on all network interface
|
||||
ln, err = net.Listen("tcp4", ":"+ss[len(ss)-1])
|
||||
if err == nil {
|
||||
host = previousAddr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ln, err = net.Listen("tcp4", ":0")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
adds, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var localIPV4 string
|
||||
var nonLocalIPV4 string
|
||||
for _, addr := range adds {
|
||||
if ipNet, ok := addr.(*net.IPNet); ok && ipNet.IP.To4() != nil {
|
||||
if ipNet.IP.IsLoopback() {
|
||||
localIPV4 = ipNet.IP.String()
|
||||
} else {
|
||||
nonLocalIPV4 = ipNet.IP.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
if nonLocalIPV4 != "" {
|
||||
host = fmt.Sprintf("%s:%d", nonLocalIPV4, ln.Addr().(*net.TCPAddr).Port)
|
||||
} else {
|
||||
host = fmt.Sprintf("%s:%d", localIPV4, ln.Addr().(*net.TCPAddr).Port)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getPreviousAddr() string {
|
||||
file, err := os.Open(os.Args[0] + "_profile_listen_addr")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := bufio.NewReader(file)
|
||||
addr, _, _ := reader.ReadLine()
|
||||
return string(addr)
|
||||
}
|
||||
|
||||
func genProfileAddr(profileAddr string) {
|
||||
fn := os.Args[0] + "_profile_listen_addr"
|
||||
f, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fmt.Fprintf(f, strings.TrimPrefix(profileAddr, "http://"))
|
||||
}
|
||||
`
|
||||
|
||||
var coverParentFileTmpl = template.Must(template.New("coverParentFileTmpl").Parse(coverParentFile))
|
||||
|
||||
const coverParentFile = `
|
||||
// Code generated by goc system. DO NOT EDIT.
|
||||
|
||||
package {{.}}
|
||||
|
||||
`
|
||||
|
||||
var coverParentVarsTmpl = template.Must(template.New("coverParentVarsTmpl").Parse(coverParentVars))
|
||||
|
||||
const coverParentVars = `
|
||||
|
||||
import (
|
||||
|
||||
{{range $i, $pkgCover := .}}
|
||||
_cover{{$i}} {{$pkgCover.Package.ImportPath | printf "%q"}}
|
||||
{{end}}
|
||||
|
||||
)
|
||||
|
||||
{{range $i, $pkgCover := .}}
|
||||
{{range $v, $cover := $pkgCover.Vars}}
|
||||
var {{$v}} = &_cover{{$i}}.{{$cover.Var}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
`
|
||||
|
||||
func InjectCacheCounters(covers map[string][]*PackageCover, cache map[string]*PackageCover) []error {
|
||||
var errs []error
|
||||
for k, v := range covers {
|
||||
if pkg, ok := cache[k]; ok {
|
||||
err := checkCacheDir(pkg.Package.Dir)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, pkgName := path.Split(k)
|
||||
err = injectCache(v, pkgName, fmt.Sprintf("%s/%s", pkg.Package.Dir, pkg.Package.GoFiles[0]))
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// InjectCacheCounters generate a file _cover_http_apis.go besides the main.go file
|
||||
func injectCache(covers []*PackageCover, pkg, dest string) error {
|
||||
f, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := coverParentFileTmpl.Execute(f, pkg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := coverParentVarsTmpl.Execute(f, covers); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkCacheDir(p string) error {
|
||||
_, err := os.Stat(p)
|
||||
if os.IsNotExist(err) {
|
||||
err := os.Mkdir(p, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
29
tests/e2e/e2e_suite_test.go
Normal file
29
tests/e2e/e2e_suite_test.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestE2e(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "E2E goc Suite")
|
||||
}
|
167
tests/e2e/simple_project_test.go
Normal file
167
tests/e2e/simple_project_test.go
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
Copyright 2020 Qiniu Cloud (七牛云)
|
||||
|
||||
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_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/qiniu/goc/pkg/build"
|
||||
)
|
||||
|
||||
var TESTS_ROOT string
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
TESTS_ROOT, _ = os.Getwd()
|
||||
By("Current working directory: " + TESTS_ROOT)
|
||||
TESTS_ROOT = filepath.Join(TESTS_ROOT, "..")
|
||||
})
|
||||
|
||||
var _ = Describe("E2E", func() {
|
||||
var GOPATH string
|
||||
|
||||
BeforeEach(func() {
|
||||
GOPATH = os.Getenv("GOPATH")
|
||||
// in GitHub Action, this value is empty
|
||||
if GOPATH == "" {
|
||||
GOPATH = filepath.Join(os.Getenv("HOME"), "go")
|
||||
}
|
||||
})
|
||||
|
||||
Context("Go module", func() {
|
||||
It("Simple project", func() {
|
||||
startTime := time.Now()
|
||||
|
||||
By("goc build")
|
||||
testProjDir := filepath.Join(TESTS_ROOT, "samples/simple_project")
|
||||
cmd := exec.Command("goc", "build")
|
||||
cmd.Dir = testProjDir
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
Expect(err).To(BeNil(), "goc build on this project should be successful", string(out))
|
||||
|
||||
By("goc install")
|
||||
testProjDir = filepath.Join(TESTS_ROOT, "samples/simple_project")
|
||||
cmd = exec.Command("goc", "install", "./...")
|
||||
cmd.Dir = testProjDir
|
||||
|
||||
out, err = cmd.CombinedOutput()
|
||||
Expect(err).To(BeNil(), "goc install on this project should be successful", string(out))
|
||||
|
||||
By("check files in generated temporary directory")
|
||||
tempDir := filepath.Join(os.TempDir(), build.TmpFolderName(testProjDir))
|
||||
_, err = os.Lstat(tempDir)
|
||||
Expect(err).To(BeNil(), "projects should be copied to temporary directory")
|
||||
|
||||
By("check if cover variables are injected")
|
||||
_, err = os.Lstat(filepath.Join(tempDir, "http_cover_apis_auto_generated.go"))
|
||||
Expect(err).To(BeNil(), "a http server file should be generated")
|
||||
|
||||
By("check generated binary")
|
||||
objects := []string{GOPATH + "/bin", testProjDir}
|
||||
for _, dir := range objects {
|
||||
obj := filepath.Join(dir, "simple-project")
|
||||
fInfo, err := os.Lstat(obj)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(startTime.Before(fInfo.ModTime())).To(Equal(true), "new binary should be generated, not the old one")
|
||||
|
||||
cmd := exec.Command("go", "tool", "objdump", "simple-project")
|
||||
cmd.Dir = dir
|
||||
out, err = cmd.CombinedOutput()
|
||||
Expect(err).To(BeNil(), "the binary cannot be disassembled")
|
||||
|
||||
cnt := strings.Count(string(out), "GoCover")
|
||||
Expect(cnt).To(BeNumerically(">", 0), "GoCover varibale should be in the binary")
|
||||
|
||||
cnt = strings.Count(string(out), "main.registerSelf")
|
||||
Expect(cnt).To(BeNumerically(">", 0), "main.registerSelf function should be in the binary")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Context("GOPATH", func() {
|
||||
var GOPATH string
|
||||
|
||||
BeforeEach(func() {
|
||||
GOPATH = os.Getenv("GOPATH")
|
||||
})
|
||||
|
||||
It("Simple GOPATH project", func() {
|
||||
startTime := time.Now()
|
||||
testProjDir := filepath.Join(TESTS_ROOT, "samples/simple_gopath_project")
|
||||
oriWorkingDir := filepath.Join(testProjDir, "src/qiniu.com/simple_gopath_project")
|
||||
GOPATH = testProjDir
|
||||
|
||||
By("goc build")
|
||||
cmd := exec.Command("goc", "build")
|
||||
cmd.Dir = oriWorkingDir
|
||||
// use GOPATH mode to compile project
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", GOPATH), "GO111MODULE=off")
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
Expect(err).To(BeNil(), "goc build on this project should be successful", string(out), cmd.Dir)
|
||||
|
||||
By("goc install")
|
||||
testProjDir = filepath.Join(TESTS_ROOT, "samples/simple_gopath_project")
|
||||
cmd = exec.Command("goc", "install", "./...")
|
||||
cmd.Dir = filepath.Join(testProjDir, "src/qiniu.com/simple_gopath_project")
|
||||
// use GOPATH mode to compile project
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", testProjDir), "GO111MODULE=off")
|
||||
|
||||
out, err = cmd.CombinedOutput()
|
||||
Expect(err).To(BeNil(), "goc install on this project should be successful", string(out))
|
||||
|
||||
By("check files in generated temporary directory")
|
||||
tempDir := filepath.Join(os.TempDir(), build.TmpFolderName(oriWorkingDir))
|
||||
_, err = os.Lstat(tempDir)
|
||||
Expect(err).To(BeNil(), "projects should be copied to temporary directory")
|
||||
|
||||
By("check if cover variables are injected")
|
||||
newWorkingDir := filepath.Join(tempDir, "src/qiniu.com/simple_gopath_project")
|
||||
_, err = os.Lstat(filepath.Join(newWorkingDir, "http_cover_apis_auto_generated.go"))
|
||||
Expect(err).To(BeNil(), "a http server file should be generated")
|
||||
|
||||
By("check generated binary")
|
||||
objects := []string{GOPATH + "/bin", oriWorkingDir}
|
||||
for _, dir := range objects {
|
||||
obj := filepath.Join(dir, "simple_gopath_project")
|
||||
fInfo, err := os.Lstat(obj)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(startTime.Before(fInfo.ModTime())).To(Equal(true), "new binary should be generated, not the old one")
|
||||
|
||||
cmd := exec.Command("go", "tool", "objdump", "simple_gopath_project")
|
||||
cmd.Dir = dir
|
||||
out, err = cmd.CombinedOutput()
|
||||
Expect(err).To(BeNil(), "the binary cannot be disassembled")
|
||||
|
||||
cnt := strings.Count(string(out), "GoCover")
|
||||
Expect(cnt).To(BeNumerically(">", 0), "GoCover varibale should be in the binary")
|
||||
|
||||
cnt = strings.Count(string(out), "main.registerSelf")
|
||||
Expect(cnt).To(BeNumerically(">", 0), "main.registerSelf function should be in the binary")
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
})
|
25
tests/run-ci-actions.sh
Executable file
25
tests/run-ci-actions.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2020 Qiniu Cloud (七牛云)
|
||||
#
|
||||
# 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.
|
||||
|
||||
set -ex
|
||||
|
||||
chmod +x /home/runner/tools/goc/goc
|
||||
export PATH=/home/runner/tools/goc:$PATH
|
||||
|
||||
chmod +x /home/runner/tools/e2e.test/e2e.test
|
||||
export PATH=/home/runner/tools/e2e.test:$PATH
|
||||
|
||||
cd e2e
|
||||
e2e.test ./...
|
0
tests/samples/simple_gopath_project/go.mod
Normal file
0
tests/samples/simple_gopath_project/go.mod
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello, world.")
|
||||
}
|
3
tests/samples/simple_project/go.mod
Normal file
3
tests/samples/simple_project/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module example.com/simple-project
|
||||
|
||||
go 1.11
|
9
tests/samples/simple_project/main.go
Normal file
9
tests/samples/simple_project/main.go
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello, world.")
|
||||
}
|
Loading…
Reference in New Issue
Block a user