mirror of https://github.com/maxcnunes/gaper.git
Add more tests
This commit is contained in:
parent
8e2f1573d6
commit
2847f41c96
|
@ -53,6 +53,14 @@ GLOBAL OPTIONS:
|
|||
--version, -v print the version
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
Ignore watch over all test files:
|
||||
|
||||
```
|
||||
--ignore './**/*_test.go'
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
See the [Contributing guide](/CONTRIBUTING.md) for steps on how to contribute to this project.
|
||||
|
|
21
builder.go
21
builder.go
|
@ -12,7 +12,6 @@ import (
|
|||
type Builder interface {
|
||||
Build() error
|
||||
Binary() string
|
||||
Errors() string
|
||||
}
|
||||
|
||||
type builder struct {
|
||||
|
@ -25,8 +24,9 @@ type builder struct {
|
|||
|
||||
// NewBuilder ...
|
||||
func NewBuilder(dir string, bin string, wd string, buildArgs []string) Builder {
|
||||
if len(bin) == 0 {
|
||||
bin = "bin"
|
||||
// resolve bin name by current folder name
|
||||
if bin == "" {
|
||||
bin = filepath.Base(wd)
|
||||
}
|
||||
|
||||
// does not work on Windows without the ".exe" extension
|
||||
|
@ -45,11 +45,6 @@ func (b *builder) Binary() string {
|
|||
return b.binary
|
||||
}
|
||||
|
||||
// Errors ...
|
||||
func (b *builder) Errors() string {
|
||||
return b.errors
|
||||
}
|
||||
|
||||
// Build ...
|
||||
func (b *builder) Build() error {
|
||||
logger.Info("Building program")
|
||||
|
@ -64,14 +59,8 @@ func (b *builder) Build() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if command.ProcessState.Success() {
|
||||
b.errors = ""
|
||||
} else {
|
||||
b.errors = string(output)
|
||||
}
|
||||
|
||||
if len(b.errors) > 0 {
|
||||
return fmt.Errorf(b.errors)
|
||||
if !command.ProcessState.Success() {
|
||||
return fmt.Errorf("error building: %s", output)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -27,3 +27,26 @@ func TestBuilderSuccessBuild(t *testing.T) {
|
|||
}
|
||||
assert.NotNil(t, file, "binary not written properly")
|
||||
}
|
||||
|
||||
func TestBuilderFailureBuild(t *testing.T) {
|
||||
bArgs := []string{}
|
||||
bin := "srv"
|
||||
dir := filepath.Join("testdata", "build-failure")
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't get current working directory: %v", err)
|
||||
}
|
||||
|
||||
b := NewBuilder(dir, bin, wd, bArgs)
|
||||
err = b.Build()
|
||||
assert.NotNil(t, err, "build error")
|
||||
assert.Equal(t, err.Error(), "exit status 2")
|
||||
}
|
||||
|
||||
func TestBuilderDefaultBinName(t *testing.T) {
|
||||
bin := ""
|
||||
dir := filepath.Join("testdata", "server")
|
||||
wd := "/src/projects/project-name"
|
||||
b := NewBuilder(dir, bin, wd, nil)
|
||||
assert.Equal(t, b.Binary(), "project-name")
|
||||
}
|
||||
|
|
18
main.go
18
main.go
|
@ -15,10 +15,6 @@ import (
|
|||
|
||||
var logger = NewLogger("gaper")
|
||||
|
||||
// default values
|
||||
var defaultExtensions = cli.StringSlice{"go"}
|
||||
var defaultPoolInterval = 500
|
||||
|
||||
// exit statuses
|
||||
var exitStatusSuccess = 0
|
||||
var exitStatusError = 1
|
||||
|
@ -94,12 +90,10 @@ func main() {
|
|||
},
|
||||
cli.IntFlag{
|
||||
Name: "poll-interval, p",
|
||||
Value: defaultPoolInterval,
|
||||
Usage: "how often in milliseconds to poll watched files for changes",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "extensions, e",
|
||||
Value: &defaultExtensions,
|
||||
Usage: "a comma-delimited list of file extensions to watch for changes",
|
||||
},
|
||||
cli.StringFlag{
|
||||
|
@ -135,11 +129,6 @@ func runGaper(cfg *Config) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// resolve bin name by current folder name
|
||||
if cfg.BinName == "" {
|
||||
cfg.BinName = filepath.Base(wd)
|
||||
}
|
||||
|
||||
if len(cfg.WatchItems) == 0 {
|
||||
cfg.WatchItems = append(cfg.WatchItems, cfg.BuildPath)
|
||||
}
|
||||
|
@ -182,7 +171,9 @@ func runGaper(cfg *Config) error {
|
|||
case event := <-watcher.Events:
|
||||
logger.Debug("Detected new changed file: ", event)
|
||||
changeRestart = true
|
||||
restart(builder, runner)
|
||||
if err := restart(builder, runner); err != nil {
|
||||
return err
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
return fmt.Errorf("error on watching files: %v", err)
|
||||
case err := <-runner.Errors():
|
||||
|
@ -249,8 +240,7 @@ func handleProgramExit(builder Builder, runner Runner, err error, noRestartOn st
|
|||
return nil
|
||||
}
|
||||
|
||||
restart(builder, runner)
|
||||
return nil
|
||||
return restart(builder, runner)
|
||||
}
|
||||
|
||||
func shutdown(runner Runner) {
|
||||
|
|
22
runner.go
22
runner.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -12,6 +13,9 @@ import (
|
|||
// OSWindows ...
|
||||
const OSWindows = "windows"
|
||||
|
||||
// os errors
|
||||
var errFinished = errors.New("os: process already finished")
|
||||
|
||||
// Runner ...
|
||||
type Runner interface {
|
||||
Run() (*exec.Cmd, error)
|
||||
|
@ -28,6 +32,7 @@ type runner struct {
|
|||
command *exec.Cmd
|
||||
starttime time.Time
|
||||
errors chan error
|
||||
end chan bool // used internally by Kill to wait a process die
|
||||
}
|
||||
|
||||
// NewRunner ...
|
||||
|
@ -39,6 +44,7 @@ func NewRunner(wStdout io.Writer, wStderr io.Writer, bin string, args []string)
|
|||
writerStderr: wStderr,
|
||||
starttime: time.Now(),
|
||||
errors: make(chan error),
|
||||
end: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,15 +52,14 @@ func NewRunner(wStdout io.Writer, wStderr io.Writer, bin string, args []string)
|
|||
func (r *runner) Run() (*exec.Cmd, error) {
|
||||
logger.Info("Starting program")
|
||||
|
||||
if r.command == nil || r.Exited() {
|
||||
if err := r.runBin(); err != nil {
|
||||
return nil, fmt.Errorf("error running: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
if r.command != nil && !r.Exited() {
|
||||
return r.command, nil
|
||||
}
|
||||
|
||||
if err := r.runBin(); err != nil {
|
||||
return nil, fmt.Errorf("error running: %v", err)
|
||||
}
|
||||
|
||||
return r.command, nil
|
||||
}
|
||||
|
||||
|
@ -66,7 +71,7 @@ func (r *runner) Kill() error {
|
|||
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
r.command.Wait() // nolint errcheck
|
||||
<-r.end
|
||||
close(done)
|
||||
}()
|
||||
|
||||
|
@ -82,7 +87,7 @@ func (r *runner) Kill() error {
|
|||
// Wait for our process to die before we return or hard kill after 3 sec
|
||||
select {
|
||||
case <-time.After(3 * time.Second):
|
||||
if err := r.command.Process.Kill(); err != nil {
|
||||
if err := r.command.Process.Kill(); err != nil && err.Error() != errFinished.Error() {
|
||||
return fmt.Errorf("failed to kill: %v", err)
|
||||
}
|
||||
case <-done:
|
||||
|
@ -128,6 +133,7 @@ func (r *runner) runBin() error {
|
|||
// wait for exit errors
|
||||
go func() {
|
||||
r.errors <- r.command.Wait()
|
||||
r.end <- true
|
||||
}()
|
||||
|
||||
return nil
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
@ -33,3 +34,21 @@ func TestRunnerSuccessRun(t *testing.T) {
|
|||
assert.Equal(t, "Gaper\n", stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerSuccessKill(t *testing.T) {
|
||||
bin := filepath.Join("testdata", "print-gaper")
|
||||
if runtime.GOOS == OSWindows {
|
||||
bin += ".bat"
|
||||
}
|
||||
|
||||
runner := NewRunner(os.Stdout, os.Stderr, bin, nil)
|
||||
|
||||
_, err := runner.Run()
|
||||
assert.Nil(t, err, "error running binary")
|
||||
|
||||
err = runner.Kill()
|
||||
assert.Nil(t, err, "error killing program")
|
||||
|
||||
errCmd := <-runner.Errors()
|
||||
assert.NotNil(t, errCmd, "kill program")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package main
|
||||
|
||||
// nolint
|
||||
func main() error {
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
sleep 2
|
||||
echo "Gaper"
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
timeout 2 > nul
|
||||
@echo Gaper
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package main
|
||||
|
||||
// an empty test file just to check during tests we can ignore _test.go files
|
24
watcher.go
24
watcher.go
|
@ -10,6 +10,12 @@ import (
|
|||
zglob "github.com/mattn/go-zglob"
|
||||
)
|
||||
|
||||
// DefaultExtensions used by the watcher
|
||||
var DefaultExtensions = []string{"go"}
|
||||
|
||||
// DefaultPoolInterval used by the watcher
|
||||
var DefaultPoolInterval = 500
|
||||
|
||||
// Watcher ...
|
||||
type Watcher struct {
|
||||
PollInterval int
|
||||
|
@ -22,6 +28,14 @@ type Watcher struct {
|
|||
|
||||
// NewWatcher ...
|
||||
func NewWatcher(pollInterval int, watchItems []string, ignoreItems []string, extensions []string) (*Watcher, error) {
|
||||
if pollInterval == 0 {
|
||||
pollInterval = DefaultPoolInterval
|
||||
}
|
||||
|
||||
if len(extensions) == 0 {
|
||||
extensions = DefaultExtensions
|
||||
}
|
||||
|
||||
allowedExts := make(map[string]bool)
|
||||
for _, ext := range extensions {
|
||||
allowedExts["."+ext] = true
|
||||
|
@ -76,8 +90,9 @@ func (w *Watcher) scanChange(watchPath string) (string, error) {
|
|||
var fileChanged string
|
||||
|
||||
err := filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
|
||||
if path == ".git" && info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
// ignore hidden files and directories
|
||||
if filepath.Base(path)[0] == '.' {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, x := range w.IgnoreItems {
|
||||
|
@ -86,11 +101,6 @@ func (w *Watcher) scanChange(watchPath string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// ignore hidden files
|
||||
if filepath.Base(path)[0] == '.' {
|
||||
return nil
|
||||
}
|
||||
|
||||
ext := filepath.Ext(path)
|
||||
if _, ok := w.AllowedExtensions[ext]; ok && info.ModTime().After(startTime) {
|
||||
fileChanged = path
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWatcherDefaultValues(t *testing.T) {
|
||||
pollInterval := 0
|
||||
watchItems := []string{filepath.Join("testdata", "server")}
|
||||
var ignoreItems []string
|
||||
var extensions []string
|
||||
|
||||
w, err := NewWatcher(pollInterval, watchItems, ignoreItems, extensions)
|
||||
|
||||
assert.Nil(t, err, "wacher error")
|
||||
assert.Equal(t, 500, w.PollInterval)
|
||||
assert.Equal(t, watchItems, w.WatchItems)
|
||||
assert.Len(t, w.IgnoreItems, 0)
|
||||
assert.Equal(t, map[string]bool{".go": true}, w.AllowedExtensions)
|
||||
}
|
||||
|
||||
func TestWatcherGlobPath(t *testing.T) {
|
||||
pollInterval := 0
|
||||
watchItems := []string{filepath.Join("testdata", "server")}
|
||||
ignoreItems := []string{"./testdata/**/*_test.go"}
|
||||
var extensions []string
|
||||
|
||||
w, err := NewWatcher(pollInterval, watchItems, ignoreItems, extensions)
|
||||
file := filepath.Join("testdata", "server", "main_test.go")
|
||||
assert.Nil(t, err, "wacher error")
|
||||
assert.Equal(t, []string{file}, w.IgnoreItems)
|
||||
}
|
||||
|
||||
func TestWatcherWatchChange(t *testing.T) {
|
||||
srvdir := filepath.Join("testdata", "server")
|
||||
hiddendir := filepath.Join("testdata", "hidden-test")
|
||||
|
||||
hiddenfile1 := filepath.Join("testdata", ".hidden-file")
|
||||
hiddenfile2 := filepath.Join("testdata", ".hidden-folder", ".gitkeep")
|
||||
mainfile := filepath.Join("testdata", "server", "main.go")
|
||||
testfile := filepath.Join("testdata", "server", "main_test.go")
|
||||
|
||||
pollInterval := 0
|
||||
watchItems := []string{srvdir, hiddendir}
|
||||
ignoreItems := []string{testfile}
|
||||
extensions := []string{"go"}
|
||||
|
||||
w, err := NewWatcher(pollInterval, watchItems, ignoreItems, extensions)
|
||||
assert.Nil(t, err, "wacher error")
|
||||
|
||||
go w.Watch()
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
|
||||
// update hidden files and dirs to check builtin hidden ignore is working
|
||||
os.Chtimes(hiddenfile1, time.Now(), time.Now())
|
||||
os.Chtimes(hiddenfile2, time.Now(), time.Now())
|
||||
|
||||
// update testfile first to check ignore is working
|
||||
os.Chtimes(testfile, time.Now(), time.Now())
|
||||
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
os.Chtimes(mainfile, time.Now(), time.Now())
|
||||
|
||||
select {
|
||||
case event := <-w.Events:
|
||||
assert.Equal(t, mainfile, event)
|
||||
case err := <-w.Errors:
|
||||
assert.Nil(t, err, "wacher event error")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue