-
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 6 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,231 @@ | ||
//+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" | ||
|
||
// Build is the default that fmt, vet, runs test and builds | ||
var Default = Build | ||
|
||
var Aliases = map[string]interface{}{ | ||
"test": TestRace, | ||
} | ||
|
||
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") | ||
} | ||
|
||
// 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) | ||
} | ||
|
||
// Build run linters, tests, download modules and install | ||
func Build() error { | ||
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 Build on %s\n", runtime.Version()) | ||
return nil | ||
} | ||
|
||
mg.Deps(Fmt, Vet, TestRace) | ||
if err := sh.Run("go", "mod", "download"); err != nil { | ||
return err | ||
} | ||
return sh.Run("go", "install", "./...") | ||
} | ||
|
||
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.
wondering what it'll build, install and run in our case ? examples ? what one(s) ?
this is a library, only what use it can be installed. We could eventually want to build "examples" but then we would have to make those examples a bit more "unified"
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.
@jbguerraz I agree, I'm working on issue #36 where I will introduce a Dockerfile and run the examples as a part of integration tests. I can exclude
install
from this PR. This PR is only aimed at resolving #35.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.
let's keep it simple as @jbguerraz suggested and keep the scope of the PR w.r.t what we have now. i.e perhaps just call
test
andvet
, i believe i saw some errors when i ranlint
which we can tackle in a different PR.