-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Magefile for easy testing #37
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
*.dylib | ||
*.html | ||
.vscode/* | ||
.idea/ | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
//+build mage | ||
|
||
package main | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/magefile/mage/mg" | ||
"github.com/magefile/mage/sh" | ||
) | ||
|
||
var ldflags = "" | ||
|
||
// allow user to override go executable by running as GOEXE=xxx make ... on unix-like systems | ||
var goexe = "go" | ||
|
||
// Check is the default that fmt, vet, runs test and builds | ||
var Default = Check | ||
|
||
func init() { | ||
if exe := os.Getenv("GOEXE"); exe != "" { | ||
goexe = exe | ||
} | ||
|
||
// We want to use Go 1.11 modules even if the source lives inside GOPATH. | ||
// The default is "auto". | ||
os.Setenv("GO111MODULE", "on") | ||
} | ||
|
||
// Build runs go mod download and then installs the binary. | ||
func Build() error { | ||
if err := sh.Run("go", "mod", "download"); err != nil { | ||
return err | ||
} | ||
return sh.Run("go", "install", "./...") | ||
} | ||
|
||
// Lint run golint linter | ||
// https://github.com/golang/lint | ||
func Lint() error { | ||
pkgs, err := packages() | ||
if err != nil { | ||
return err | ||
} | ||
failed := false | ||
for _, pkg := range pkgs { | ||
// We don't actually want to fail this target if we find golint errors, | ||
// so we don't pass -set_exit_status, but we still print out any failures. | ||
if _, err := sh.Exec(nil, os.Stderr, nil, "golint", pkg); err != nil { | ||
fmt.Printf("ERROR: running go lint on %q: %v\n", pkg, err) | ||
failed = true | ||
} | ||
} | ||
if failed { | ||
return errors.New("errors running golint") | ||
} | ||
return nil | ||
} | ||
|
||
// Fmt run gofmt linter | ||
func Fmt() error { | ||
if !isGoLatest() { | ||
return nil | ||
} | ||
pkgs, err := packages() | ||
if err != nil { | ||
return err | ||
} | ||
failed := false | ||
first := true | ||
for _, pkg := range pkgs { | ||
files, err := filepath.Glob(filepath.Join(pkg, "*.go")) | ||
if err != nil { | ||
return nil | ||
} | ||
for _, f := range files { | ||
// gofmt doesn't exit with non-zero when it finds unformatted code | ||
// so we have to explicitly look for output, and if we find any, we | ||
// should fail this target. | ||
s, err := sh.Output("gofmt", "-l", f) | ||
if err != nil { | ||
fmt.Printf("ERROR: running gofmt on %q: %v\n", f, err) | ||
failed = true | ||
} | ||
if s != "" { | ||
if first { | ||
fmt.Println("The following files are not gofmt'ed:") | ||
first = false | ||
} | ||
failed = true | ||
fmt.Println(s) | ||
} | ||
} | ||
} | ||
if failed { | ||
return errors.New("improperly formatted go files") | ||
} | ||
return nil | ||
} | ||
|
||
// Vet run go vet linter | ||
func Vet() error { | ||
if err := sh.Run(goexe, "vet", "./..."); err != nil { | ||
return fmt.Errorf("error running go vet: %v", err) | ||
} | ||
return nil | ||
} | ||
|
||
// TestRace run tests with race detector | ||
func TestRace() error { | ||
env := map[string]string{"GOFLAGS": testGoFlags()} | ||
return runCmd(env, goexe, "test", "-race", "./...", buildFlags(), "-tags", buildTags()) | ||
} | ||
|
||
// TestCoverHTML generates test coverage report | ||
func TestCoverHTML() error { | ||
const ( | ||
coverAll = "coverage-all.out" | ||
cover = "coverage.out" | ||
) | ||
f, err := os.Create(coverAll) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
if _, err := f.Write([]byte("mode: count")); err != nil { | ||
return err | ||
} | ||
pkgs, err := packages() | ||
if err != nil { | ||
return err | ||
} | ||
for _, pkg := range pkgs { | ||
if err := sh.Run(goexe, "test", "-coverprofile="+cover, pkg); err != nil { | ||
return err | ||
} | ||
b, err := ioutil.ReadFile(cover) | ||
if err != nil { | ||
if os.IsNotExist(err) { | ||
continue | ||
} | ||
return err | ||
} | ||
idx := bytes.Index(b, []byte{'\n'}) | ||
b = b[idx+1:] | ||
if _, err := f.Write(b); err != nil { | ||
return err | ||
} | ||
} | ||
if err := f.Close(); err != nil { | ||
return err | ||
} | ||
return sh.Run(goexe, "tool", "cover", "-html="+coverAll) | ||
} | ||
|
||
// Check run linters and tests | ||
func Check() { | ||
if strings.Contains(runtime.Version(), "1.8") { | ||
// Go 1.8 doesn't play along with go test ./... and /vendor. | ||
// We could fix that, but that would take time. | ||
fmt.Printf("Skip Check on %s\n", runtime.Version()) | ||
return | ||
} | ||
|
||
mg.Deps(Fmt) | ||
// TODO: Enable once errors are fixed | ||
// mg.Deps(Lint, Vet) | ||
mg.Deps(TestRace) | ||
mg.Deps(Build) | ||
} | ||
|
||
var ( | ||
pkgPrefixLen = len("github.com/grafadruid/go-druid") | ||
pkgs []string | ||
pkgsInit sync.Once | ||
) | ||
|
||
// testGoFlags returns test flags that need to be set | ||
func testGoFlags() string { | ||
return "-v" | ||
} | ||
|
||
func packages() ([]string, error) { | ||
var err error | ||
pkgsInit.Do(func() { | ||
var s string | ||
s, err = sh.Output(goexe, "list", "./...") | ||
if err != nil { | ||
return | ||
} | ||
pkgs = strings.Split(s, "\n") | ||
for i := range pkgs { | ||
pkgs[i] = "." + pkgs[i][pkgPrefixLen:] | ||
} | ||
}) | ||
return pkgs, err | ||
} | ||
|
||
func buildFlags() []string { | ||
if runtime.GOOS == "windows" { | ||
return []string{"-buildmode", "exe"} | ||
} | ||
return nil | ||
} | ||
|
||
func buildTags() string { | ||
return "none" | ||
} | ||
|
||
func isGoLatest() bool { | ||
return strings.Contains(runtime.Version(), "1.14") | ||
} | ||
|
||
func runCmd(env map[string]string, cmd string, args ...interface{}) error { | ||
if mg.Verbose() { | ||
return runWith(env, cmd, args...) | ||
} | ||
output, err := sh.OutputWith(env, cmd, argsToStrings(args...)...) | ||
if err != nil { | ||
fmt.Fprint(os.Stderr, output) | ||
} | ||
|
||
return err | ||
} | ||
|
||
func runWith(env map[string]string, cmd string, inArgs ...interface{}) error { | ||
s := argsToStrings(inArgs...) | ||
return sh.RunWith(env, cmd, s...) | ||
} | ||
|
||
func argsToStrings(v ...interface{}) []string { | ||
var args []string | ||
for _, arg := range v { | ||
switch v := arg.(type) { | ||
case string: | ||
if v != "" { | ||
args = append(args, v) | ||
} | ||
case []string: | ||
if v != nil { | ||
args = append(args, v...) | ||
} | ||
default: | ||
panic("invalid type") | ||
} | ||
} | ||
|
||
return args | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,28 @@ | |
# go-druid | ||
A Golang client for Druid. | ||
Now supports Query API and Common API. | ||
|
||
### Development | ||
|
||
#### Testing | ||
`go-druid` uses mage to run tests locally. | ||
Install Mage: | ||
``` | ||
git clone https://github.com/magefile/mage | ||
cd mage | ||
go run bootstrap.go | ||
``` | ||
`mage -l` provides a list of targets that can be run. Default is `Check` | ||
|
||
``` | ||
Targets: | ||
build runs go mod download and then installs the binary. | ||
check* run linters and tests | ||
fmt run gofmt linter | ||
lint run golint linter https://github.com/golang/lint | ||
testCoverHTML generates test coverage report | ||
testRace run tests with race detector | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. original had both: https://github.com/gohugoio/hugo/blob/master/magefile.go#L188 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would we ever need to run without race? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's keep both but default target should call |
||
vet run go vet linter | ||
|
||
* default target | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have Build in Check? it is counter intuitive 😄 . Rename Check to Build?