Add more tests

This commit is contained in:
Max Claus Nunes 2018-06-20 22:40:09 -03:00
parent 8e2f1573d6
commit 2847f41c96
14 changed files with 175 additions and 45 deletions

View File

@ -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.

View File

@ -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

View File

@ -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
View File

@ -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) {

View File

@ -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

View File

@ -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")
}

5
testdata/build-failure/main.go vendored Normal file
View File

@ -0,0 +1,5 @@
package main
// nolint
func main() error {
}

0
testdata/hidden-test/.hiden-file vendored Normal file
View File

View File

View File

@ -1,2 +1,3 @@
#!/usr/bin/env bash
sleep 2
echo "Gaper"

View File

@ -1 +1,2 @@
timeout 2 > nul
@echo Gaper

3
testdata/server/main_test.go vendored Normal file
View File

@ -0,0 +1,3 @@
package main
// an empty test file just to check during tests we can ignore _test.go files

View File

@ -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

75
watcher_test.go Normal file
View File

@ -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")
}
}