From 9c40619f41e1a8e54befd66a3c76a36e79589a1e Mon Sep 17 00:00:00 2001 From: Ambrose Chua Date: Mon, 26 Mar 2018 20:23:28 +0800 Subject: [PATCH] Vendor and drone --- .drone.yml | 24 + vendor/github.com/judwhite/go-svc/svc/doc.go | 17 + .../judwhite/go-svc/svc/internal/test/test.go | 62 +++ vendor/github.com/judwhite/go-svc/svc/svc.go | 34 ++ .../judwhite/go-svc/svc/svc_common_test.go | 36 ++ .../judwhite/go-svc/svc/svc_other.go | 39 ++ .../judwhite/go-svc/svc/svc_other_test.go | 68 +++ .../judwhite/go-svc/svc/svc_windows.go | 145 ++++++ .../judwhite/go-svc/svc/svc_windows_test.go | 438 ++++++++++++++++++ vendor/github.com/judwhite/go-svc/svc/test.sh | 148 ++++++ .../judwhite/go-svc/svc/test_cover.sh | 9 + vendor/vgo.list | 4 + 12 files changed, 1024 insertions(+) create mode 100644 .drone.yml create mode 100644 vendor/github.com/judwhite/go-svc/svc/doc.go create mode 100644 vendor/github.com/judwhite/go-svc/svc/internal/test/test.go create mode 100644 vendor/github.com/judwhite/go-svc/svc/svc.go create mode 100644 vendor/github.com/judwhite/go-svc/svc/svc_common_test.go create mode 100644 vendor/github.com/judwhite/go-svc/svc/svc_other.go create mode 100644 vendor/github.com/judwhite/go-svc/svc/svc_other_test.go create mode 100644 vendor/github.com/judwhite/go-svc/svc/svc_windows.go create mode 100644 vendor/github.com/judwhite/go-svc/svc/svc_windows_test.go create mode 100644 vendor/github.com/judwhite/go-svc/svc/test.sh create mode 100644 vendor/github.com/judwhite/go-svc/svc/test_cover.sh create mode 100644 vendor/vgo.list diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..0a25568 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,24 @@ +matrix: + GO_VERSION: + - "1.10" + GOOS: + - "windows" + - "linux" + - "darwin" + GOARCH: + - "amd64" +pipeline: + build: + pipeline: + build: + image: golang:${GO_VERSION} + environment: + - FILENAME=${DRONE_REPO_NAME}-${GOOS}-${GOARCH} + commands: + - go build -ldflags "-s -w" -o ${FILENAME/windows-amd64/windows-amd64.exe} + release: + image: plugins/github-release + secrets: [ github_token ] + files: app/build/outputs/apk/app-debug.apk + when: + event: tag diff --git a/vendor/github.com/judwhite/go-svc/svc/doc.go b/vendor/github.com/judwhite/go-svc/svc/doc.go new file mode 100644 index 0000000..daaf986 --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/doc.go @@ -0,0 +1,17 @@ +/* +Package svc helps you write Windows Service executables without getting in the way of other target platforms. + +To get started, implement the Init, Start, and Stop methods to do +any work needed during these steps. + +Init and Start cannot block. Launch long-running your code in a new Goroutine. + +Stop may block for a short amount of time to attempt clean shutdown. + +Call svc.Run() with a reference to your svc.Service implementation to start your program. + +When running in console mode Ctrl+C is treated like a Stop Service signal. + +For a full guide visit https://github.com/judwhite/go-svc +*/ +package svc diff --git a/vendor/github.com/judwhite/go-svc/svc/internal/test/test.go b/vendor/github.com/judwhite/go-svc/svc/internal/test/test.go new file mode 100644 index 0000000..85752e7 --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/internal/test/test.go @@ -0,0 +1,62 @@ +package test + +import ( + "path/filepath" + "reflect" + "runtime" + "testing" +) + +// Equal asserts two parameters are equal by using reflect.DeepEqual. +func Equal(t *testing.T, expected, actual interface{}) { + if !reflect.DeepEqual(expected, actual) { + _, file, line, _ := runtime.Caller(1) + t.Logf("\033[31m%s:%d:\n\n\t %#v (expected)\n\n\t!= %#v (actual)\033[39m\n\n", + filepath.Base(file), line, expected, actual) + t.FailNow() + } +} + +// NotEqual asserts two parameters are not equal by using reflect.DeepEqual. +func NotEqual(t *testing.T, expected, actual interface{}) { + if !reflect.DeepEqual(expected, actual) { + _, file, line, _ := runtime.Caller(1) + t.Logf("\033[31m%s:%d:\n\n\tvalue should not equal %#v\033[39m\n\n", + filepath.Base(file), line, actual) + t.FailNow() + } +} + +// Nil asserts the parameter is nil. +func Nil(t *testing.T, object interface{}) { + if !isNil(object) { + _, file, line, _ := runtime.Caller(1) + t.Logf("\033[31m%s:%d:\n\n\t (expected)\n\n\t!= %#v (actual)\033[39m\n\n", + filepath.Base(file), line, object) + t.FailNow() + } +} + +// NotNil asserts the parameter is not nil. +func NotNil(t *testing.T, object interface{}) { + if isNil(object) { + _, file, line, _ := runtime.Caller(1) + t.Logf("\033[31m%s:%d:\n\n\tExpected value not to be \033[39m\n\n", + filepath.Base(file), line) + t.FailNow() + } +} + +func isNil(object interface{}) bool { + if object == nil { + return true + } + + value := reflect.ValueOf(object) + kind := value.Kind() + if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { + return true + } + + return false +} diff --git a/vendor/github.com/judwhite/go-svc/svc/svc.go b/vendor/github.com/judwhite/go-svc/svc/svc.go new file mode 100644 index 0000000..3703d1e --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/svc.go @@ -0,0 +1,34 @@ +package svc + +import "os/signal" + +// Create variable signal.Notify function so we can mock it in tests +var signalNotify = signal.Notify + +// Service interface contains Start and Stop methods which are called +// when the service is started and stopped. The Init method is called +// before the service is started, and after it's determined if the program +// is running as a Windows Service. +// +// The Start method must be non-blocking. +// +// Implement this interface and pass it to the Run function to start your program. +type Service interface { + // Init is called before the program/service is started and after it's + // determined if the program is running as a Windows Service. + Init(Environment) error + + // Start is called after Init. This method must be non-blocking. + Start() error + + // Stop is called in response to os.Interrupt, os.Kill, or when a + // Windows Service is stopped. + Stop() error +} + +// Environment contains information about the environment +// your application is running in. +type Environment interface { + // IsWindowsService returns true if the program is running as a Windows Service. + IsWindowsService() bool +} diff --git a/vendor/github.com/judwhite/go-svc/svc/svc_common_test.go b/vendor/github.com/judwhite/go-svc/svc/svc_common_test.go new file mode 100644 index 0000000..c370138 --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/svc_common_test.go @@ -0,0 +1,36 @@ +package svc + +type mockProgram struct { + start func() error + stop func() error + init func(Environment) error +} + +func (p *mockProgram) Start() error { + return p.start() +} + +func (p *mockProgram) Stop() error { + return p.stop() +} + +func (p *mockProgram) Init(wse Environment) error { + return p.init(wse) +} + +func makeProgram(startCalled, stopCalled, initCalled *int) *mockProgram { + return &mockProgram{ + start: func() error { + *startCalled++ + return nil + }, + stop: func() error { + *stopCalled++ + return nil + }, + init: func(wse Environment) error { + *initCalled++ + return nil + }, + } +} diff --git a/vendor/github.com/judwhite/go-svc/svc/svc_other.go b/vendor/github.com/judwhite/go-svc/svc/svc_other.go new file mode 100644 index 0000000..6acfd64 --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/svc_other.go @@ -0,0 +1,39 @@ +// +build !windows + +package svc + +import ( + "os" + "syscall" +) + +// Run runs your Service. +// +// Run will block until one of the signals specified in sig is received. +// If sig is empty syscall.SIGINT and syscall.SIGTERM are used by default. +func Run(service Service, sig ...os.Signal) error { + env := environment{} + if err := service.Init(env); err != nil { + return err + } + + if err := service.Start(); err != nil { + return err + } + + if len(sig) == 0 { + sig = []os.Signal{syscall.SIGINT, syscall.SIGTERM} + } + + signalChan := make(chan os.Signal, 1) + signalNotify(signalChan, sig...) + <-signalChan + + return service.Stop() +} + +type environment struct{} + +func (environment) IsWindowsService() bool { + return false +} diff --git a/vendor/github.com/judwhite/go-svc/svc/svc_other_test.go b/vendor/github.com/judwhite/go-svc/svc/svc_other_test.go new file mode 100644 index 0000000..c0948ad --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/svc_other_test.go @@ -0,0 +1,68 @@ +// +build !windows + +package svc + +import ( + "os" + "syscall" + "testing" + + "github.com/judwhite/go-svc/svc/internal/test" +) + +func TestDefaultSignalHandling(t *testing.T) { + signals := []os.Signal{syscall.SIGINT, syscall.SIGTERM} // default signals handled + for _, signal := range signals { + testSignalNotify(t, signal) + } +} + +func TestUserDefinedSignalHandling(t *testing.T) { + signals := []os.Signal{syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP} + for _, signal := range signals { + testSignalNotify(t, signal, signals...) + } +} + +func testSignalNotify(t *testing.T, signal os.Signal, sig ...os.Signal) { + // arrange + + // sigChan is the chan we'll send to here. if a signal matches a registered signal + // type in the Run function (in svc_other.go) the signal will be delegated to the + // channel passed to signalNotify, which is created in the Run function in svc_other.go. + // shortly: we send here and the Run function gets it if it matches the filter. + sigChan := make(chan os.Signal) + + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + + signalNotify = func(c chan<- os.Signal, sig ...os.Signal) { + if c == nil { + panic("os/signal: Notify using nil channel") + } + + go func() { + for val := range sigChan { + for _, registeredSig := range sig { + if val == registeredSig { + c <- val + } + } + } + }() + } + + go func() { + sigChan <- signal + }() + + // act + if err := Run(prg, sig...); err != nil { + t.Fatal(err) + } + + // assert + test.Equal(t, 1, startCalled) + test.Equal(t, 1, stopCalled) + test.Equal(t, 1, initCalled) +} diff --git a/vendor/github.com/judwhite/go-svc/svc/svc_windows.go b/vendor/github.com/judwhite/go-svc/svc/svc_windows.go new file mode 100644 index 0000000..75a4da3 --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/svc_windows.go @@ -0,0 +1,145 @@ +// +build windows + +package svc + +import ( + "os" + "sync" + "syscall" + + wsvc "golang.org/x/sys/windows/svc" +) + +// Create variables for svc and signal functions so we can mock them in tests +var svcIsAnInteractiveSession = wsvc.IsAnInteractiveSession +var svcRun = wsvc.Run + +type windowsService struct { + i Service + errSync sync.Mutex + stopStartErr error + isInteractive bool + signals []os.Signal + Name string +} + +// Run runs an implementation of the Service interface. +// +// Run will block until the Windows Service is stopped or Ctrl+C is pressed if +// running from the console. +// +// Stopping the Windows Service and Ctrl+C will call the Service's Stop method to +// initiate a graceful shutdown. +// +// Note that WM_CLOSE is not handled (end task) and the Service's Stop method will +// not be called. +// +// The sig parameter is to keep parity with the non-Windows API. Only syscall.SIGINT +// (Ctrl+C) can be handled on Windows. Nevertheless, you can override the default +// signals which are handled by specifying sig. +func Run(service Service, sig ...os.Signal) error { + var err error + + interactive, err := svcIsAnInteractiveSession() + if err != nil { + return err + } + + if len(sig) == 0 { + sig = []os.Signal{syscall.SIGINT} + } + + ws := &windowsService{ + i: service, + isInteractive: interactive, + signals: sig, + } + + if err = service.Init(ws); err != nil { + return err + } + + return ws.run() +} + +func (ws *windowsService) setError(err error) { + ws.errSync.Lock() + ws.stopStartErr = err + ws.errSync.Unlock() +} + +func (ws *windowsService) getError() error { + ws.errSync.Lock() + err := ws.stopStartErr + ws.errSync.Unlock() + return err +} + +func (ws *windowsService) IsWindowsService() bool { + return !ws.isInteractive +} + +func (ws *windowsService) run() error { + ws.setError(nil) + if ws.IsWindowsService() { + // Return error messages from start and stop routines + // that get executed in the Execute method. + // Guarded with a mutex as it may run a different thread + // (callback from Windows). + runErr := svcRun(ws.Name, ws) + startStopErr := ws.getError() + if startStopErr != nil { + return startStopErr + } + if runErr != nil { + return runErr + } + return nil + } + + err := ws.i.Start() + if err != nil { + return err + } + + signalChan := make(chan os.Signal, 1) + signalNotify(signalChan, ws.signals...) + <-signalChan + + err = ws.i.Stop() + + return err +} + +// Execute is invoked by Windows +func (ws *windowsService) Execute(args []string, r <-chan wsvc.ChangeRequest, changes chan<- wsvc.Status) (bool, uint32) { + const cmdsAccepted = wsvc.AcceptStop | wsvc.AcceptShutdown + changes <- wsvc.Status{State: wsvc.StartPending} + + if err := ws.i.Start(); err != nil { + ws.setError(err) + return true, 1 + } + + changes <- wsvc.Status{State: wsvc.Running, Accepts: cmdsAccepted} +loop: + for { + c := <-r + switch c.Cmd { + case wsvc.Interrogate: + changes <- c.CurrentStatus + case wsvc.Stop, wsvc.Shutdown: + changes <- wsvc.Status{State: wsvc.StopPending} + err := ws.i.Stop() + if err != nil { + ws.setError(err) + return true, 2 + } + break loop + default: + continue loop + } + } + + return false, 0 +} diff --git a/vendor/github.com/judwhite/go-svc/svc/svc_windows_test.go b/vendor/github.com/judwhite/go-svc/svc/svc_windows_test.go new file mode 100644 index 0000000..62bc11e --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/svc_windows_test.go @@ -0,0 +1,438 @@ +// +build windows + +package svc + +import ( + "errors" + "os" + "syscall" + "testing" + "time" + + "github.com/judwhite/go-svc/svc/internal/test" + wsvc "golang.org/x/sys/windows/svc" +) + +func setupWinServiceTest(wsf *mockWinServiceFuncs) { + // wsfWrapper allows signalNotify, svcIsInteractive, and svcRun to be set once. + // Inidivual test functions set "wsf" to add behavior. + wsfWrapper := &mockWinServiceFuncs{ + signalNotify: func(c chan<- os.Signal, sig ...os.Signal) { + if c == nil { + panic("os/signal: Notify using nil channel") + } + + if wsf.signalNotify != nil { + wsf.signalNotify(c, sig...) + } else { + wsf1 := *wsf + go func() { + for val := range wsf1.sigChan { + for _, registeredSig := range sig { + if val == registeredSig { + c <- val + } + } + } + }() + } + }, + svcIsInteractive: func() (bool, error) { + return wsf.svcIsInteractive() + }, + svcRun: func(name string, handler wsvc.Handler) error { + return wsf.svcRun(name, handler) + }, + } + + signalNotify = wsfWrapper.signalNotify + svcIsAnInteractiveSession = wsfWrapper.svcIsInteractive + svcRun = wsfWrapper.svcRun +} + +type mockWinServiceFuncs struct { + signalNotify func(chan<- os.Signal, ...os.Signal) + svcIsInteractive func() (bool, error) + sigChan chan os.Signal + svcRun func(string, wsvc.Handler) error + ws *windowsService + executeReturnedBool bool + executeReturnedUInt32 uint32 + changes []wsvc.Status +} + +func setWindowsServiceFuncs(isInteractive bool, onRunningSendCmd *wsvc.Cmd) (*mockWinServiceFuncs, chan<- wsvc.ChangeRequest) { + changeRequestChan := make(chan wsvc.ChangeRequest, 4) + changesChan := make(chan wsvc.Status) + done := make(chan struct{}) + + var wsf *mockWinServiceFuncs + wsf = &mockWinServiceFuncs{ + sigChan: make(chan os.Signal), + svcIsInteractive: func() (bool, error) { + return isInteractive, nil + }, + svcRun: func(name string, handler wsvc.Handler) error { + wsf.ws = handler.(*windowsService) + wsf.executeReturnedBool, wsf.executeReturnedUInt32 = handler.Execute(nil, changeRequestChan, changesChan) + done <- struct{}{} + return nil + }, + } + + var currentState wsvc.State + + go func() { + loop: + for { + select { + case change := <-changesChan: + wsf.changes = append(wsf.changes, change) + currentState = change.State + + if change.State == wsvc.Running && onRunningSendCmd != nil { + changeRequestChan <- wsvc.ChangeRequest{ + Cmd: *onRunningSendCmd, + CurrentStatus: wsvc.Status{State: currentState}, + } + } + case <-done: + break loop + } + } + }() + + setupWinServiceTest(wsf) + + return wsf, changeRequestChan +} + +func TestWinService_RunWindowsService_NonInteractive(t *testing.T) { + for _, svcCmd := range []wsvc.Cmd{wsvc.Stop, wsvc.Shutdown} { + testRunWindowsServiceNonInteractive(t, svcCmd) + } +} + +func testRunWindowsServiceNonInteractive(t *testing.T, svcCmd wsvc.Cmd) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + + wsf, _ := setWindowsServiceFuncs(false, &svcCmd) + + // act + if err := Run(prg); err != nil { + t.Fatal(err) + } + + // assert + changes := wsf.changes + + test.Equal(t, 1, startCalled) + test.Equal(t, 1, stopCalled) + test.Equal(t, 1, initCalled) + + test.Equal(t, 3, len(changes)) + test.Equal(t, wsvc.StartPending, changes[0].State) + test.Equal(t, wsvc.Running, changes[1].State) + test.Equal(t, wsvc.StopPending, changes[2].State) + + test.Equal(t, false, wsf.executeReturnedBool) + test.Equal(t, uint32(0), wsf.executeReturnedUInt32) + + test.Nil(t, wsf.ws.getError()) +} + +func TestRunWindowsServiceNonInteractive_StartError(t *testing.T) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + prg.start = func() error { + startCalled++ + return errors.New("start error") + } + + svcStop := wsvc.Stop + wsf, _ := setWindowsServiceFuncs(false, &svcStop) + + // act + err := Run(prg) + + // assert + test.Equal(t, "start error", err.Error()) + + changes := wsf.changes + + test.Equal(t, 1, startCalled) + test.Equal(t, 0, stopCalled) + test.Equal(t, 1, initCalled) + + test.Equal(t, 1, len(changes)) + test.Equal(t, wsvc.StartPending, changes[0].State) + + test.Equal(t, true, wsf.executeReturnedBool) + test.Equal(t, uint32(1), wsf.executeReturnedUInt32) + + test.Equal(t, "start error", wsf.ws.getError().Error()) +} + +func TestRunWindowsServiceInteractive_StartError(t *testing.T) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + prg.start = func() error { + startCalled++ + return errors.New("start error") + } + + wsf, _ := setWindowsServiceFuncs(true, nil) + + // act + err := Run(prg) + + // assert + test.Equal(t, "start error", err.Error()) + + changes := wsf.changes + + test.Equal(t, 1, startCalled) + test.Equal(t, 0, stopCalled) + test.Equal(t, 1, initCalled) + + test.Equal(t, 0, len(changes)) +} + +func TestRunWindowsService_BeforeStartError(t *testing.T) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + prg.init = func(Environment) error { + initCalled++ + return errors.New("before start error") + } + + wsf, _ := setWindowsServiceFuncs(false, nil) + + // act + err := Run(prg) + + // assert + test.Equal(t, "before start error", err.Error()) + + changes := wsf.changes + + test.Equal(t, 0, startCalled) + test.Equal(t, 0, stopCalled) + test.Equal(t, 1, initCalled) + + test.Equal(t, 0, len(changes)) +} + +func TestRunWindowsService_IsAnInteractiveSessionError(t *testing.T) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + + wsf, _ := setWindowsServiceFuncs(false, nil) + wsf.svcIsInteractive = func() (bool, error) { + return false, errors.New("IsAnInteractiveSession error") + } + + // act + err := Run(prg) + + // assert + test.Equal(t, "IsAnInteractiveSession error", err.Error()) + + changes := wsf.changes + + test.Equal(t, 0, startCalled) + test.Equal(t, 0, stopCalled) + test.Equal(t, 0, initCalled) + + test.Equal(t, 0, len(changes)) +} + +func TestRunWindowsServiceNonInteractive_RunError(t *testing.T) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + + svcStop := wsvc.Stop + wsf, _ := setWindowsServiceFuncs(false, &svcStop) + wsf.svcRun = func(name string, handler wsvc.Handler) error { + wsf.ws = handler.(*windowsService) + return errors.New("wsvc.Run error") + } + + // act + err := Run(prg) + + // assert + test.Equal(t, "wsvc.Run error", err.Error()) + + changes := wsf.changes + + test.Equal(t, 0, startCalled) + test.Equal(t, 0, stopCalled) + test.Equal(t, 1, initCalled) + + test.Equal(t, 0, len(changes)) + + test.Nil(t, wsf.ws.getError()) +} + +func TestRunWindowsServiceNonInteractive_Interrogate(t *testing.T) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + + wsf, changeRequest := setWindowsServiceFuncs(false, nil) + + time.AfterFunc(50*time.Millisecond, func() { + // ignored, PausePending won't be in changes slice + // make sure we don't panic/err on unexpected values + changeRequest <- wsvc.ChangeRequest{ + Cmd: wsvc.Pause, + CurrentStatus: wsvc.Status{State: wsvc.PausePending}, + } + }) + + time.AfterFunc(100*time.Millisecond, func() { + // handled, Paused will be in changes slice + changeRequest <- wsvc.ChangeRequest{ + Cmd: wsvc.Interrogate, + CurrentStatus: wsvc.Status{State: wsvc.Paused}, + } + }) + + time.AfterFunc(200*time.Millisecond, func() { + // handled, but CurrentStatus overridden with StopPending; + // ContinuePending won't be in changes slice + changeRequest <- wsvc.ChangeRequest{ + Cmd: wsvc.Stop, + CurrentStatus: wsvc.Status{State: wsvc.ContinuePending}, + } + }) + + // act + if err := Run(prg); err != nil { + t.Fatal(err) + } + + // assert + changes := wsf.changes + + test.Equal(t, 1, startCalled) + test.Equal(t, 1, stopCalled) + test.Equal(t, 1, initCalled) + + test.Equal(t, 4, len(changes)) + test.Equal(t, wsvc.StartPending, changes[0].State) + test.Equal(t, wsvc.Running, changes[1].State) + test.Equal(t, wsvc.Paused, changes[2].State) + test.Equal(t, wsvc.StopPending, changes[3].State) + + test.Equal(t, false, wsf.executeReturnedBool) + test.Equal(t, uint32(0), wsf.executeReturnedUInt32) + + test.Nil(t, wsf.ws.getError()) +} + +func TestRunWindowsServiceInteractive_StopError(t *testing.T) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + prg.stop = func() error { + stopCalled++ + return errors.New("stop error") + } + + wsf, _ := setWindowsServiceFuncs(true, nil) + + go func() { + wsf.sigChan <- os.Interrupt + }() + + // act + err := Run(prg) + + // assert + test.Equal(t, "stop error", err.Error()) + test.Equal(t, 1, startCalled) + test.Equal(t, 1, stopCalled) + test.Equal(t, 1, initCalled) + test.Equal(t, 0, len(wsf.changes)) +} + +func TestRunWindowsServiceNonInteractive_StopError(t *testing.T) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + prg.stop = func() error { + stopCalled++ + return errors.New("stop error") + } + + shutdownCmd := wsvc.Shutdown + wsf, _ := setWindowsServiceFuncs(false, &shutdownCmd) + + // act + err := Run(prg) + + // assert + changes := wsf.changes + + test.Equal(t, "stop error", err.Error()) + + test.Equal(t, 1, startCalled) + test.Equal(t, 1, stopCalled) + test.Equal(t, 1, initCalled) + + test.Equal(t, 3, len(changes)) + test.Equal(t, wsvc.StartPending, changes[0].State) + test.Equal(t, wsvc.Running, changes[1].State) + test.Equal(t, wsvc.StopPending, changes[2].State) + + test.Equal(t, true, wsf.executeReturnedBool) + test.Equal(t, uint32(2), wsf.executeReturnedUInt32) + + test.Equal(t, "stop error", wsf.ws.getError().Error()) +} + +func TestDefaultSignalHandling(t *testing.T) { + signals := []os.Signal{syscall.SIGINT} // default signal handled + for _, signal := range signals { + testSignalNotify(t, signal) + } +} + +func TestUserDefinedSignalHandling(t *testing.T) { + signals := []os.Signal{syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP} + for _, signal := range signals { + testSignalNotify(t, signal, signals...) + } +} + +func testSignalNotify(t *testing.T, signal os.Signal, sig ...os.Signal) { + // arrange + var startCalled, stopCalled, initCalled int + prg := makeProgram(&startCalled, &stopCalled, &initCalled) + + wsf, _ := setWindowsServiceFuncs(true, nil) + + go func() { + wsf.sigChan <- signal + }() + + // act + if err := Run(prg, sig...); err != nil { + t.Fatal(err) + } + + // assert + test.Equal(t, 1, startCalled) + test.Equal(t, 1, stopCalled) + test.Equal(t, 1, initCalled) + test.Equal(t, 0, len(wsf.changes)) +} diff --git a/vendor/github.com/judwhite/go-svc/svc/test.sh b/vendor/github.com/judwhite/go-svc/svc/test.sh new file mode 100644 index 0000000..2ef114a --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/test.sh @@ -0,0 +1,148 @@ +#!/bin/bash + +# go get -u github.com/kisielk/errcheck +# go get -u github.com/golang/lint/golint +# go get -u honnef.co/go/simple/cmd/gosimple +# go get -u honnef.co/go/unused/cmd/unused +# go get -u github.com/mdempsky/unconvert +# go get -u github.com/client9/misspell/cmd/misspell +# go get -u github.com/gordonklaus/ineffassign +# go get -u github.com/fzipp/gocyclo + +FILES=$(ls *.go) + +echo "Checking gofmt..." +fmtRes=$(gofmt -l -s -d $FILES) +if [ -n "${fmtRes}" ]; then + echo "gofmt checking failed: ${fmtRes}" + exit 255 +fi + +echo "Checking errcheck..." +# buffer.WriteString always returns nil error; panics if buffer too large +errRes=$(errcheck -blank -ignore 'Write[String|Rune|Byte],os:Close') +# TODO: add -asserts flag (maybe) +if [ $? -ne 0 ]; then + echo "errcheck checking failed: ${errRes}!" + exit 255 +fi +if [ -n "${errRes}" ]; then + echo "errcheck checking failed: ${errRes}" + exit 255 +fi + +echo "Checking govet..." +go vet $FILES +if [ $? -ne 0 ]; then + exit 255 +fi + +echo "Checking govet -shadow..." +for path in $FILES; do + go tool vet -shadow ${path} + if [ $? -ne 0 ]; then + exit 255 + fi +done + +echo "Checking golint..." +lintError=0 +for path in $FILES; do + lintRes=$(golint ${path}) + if [ -n "${lintRes}" ]; then + echo "golint checking ${path} failed: ${lintRes}" + lintError=1 + fi +done + +if [ ${lintError} -ne 0 ]; then + exit 255 +fi + +echo "Checking gosimple..." +gosimpleRes=$(gosimple .) +if [ $? -ne 0 ]; then + echo "gosimple checking failed: ${gosimpleRes}!" + exit 255 +fi +if [ -n "${gosimpleRes}" ]; then + echo "gosimple checking failed: ${gosimpleRes}" + exit 255 +fi + +echo "Checking unused..." +unusedRes=$(unused .) +if [ $? -ne 0 ]; then + echo "unused checking failed: ${unusedRes}!" + exit 255 +fi +if [ -n "${unusedRes}" ]; then + echo "unused checking failed: ${unusedRes}" + exit 255 +fi + +echo "Checking unconvert..." +unconvertRes=$(unconvert .) +if [ $? -ne 0 ]; then + echo "unconvert checking failed: ${unconvertRes}!" + exit 255 +fi +if [ -n "${unconvertRes}" ]; then + echo "unconvert checking failed: ${unconvertRes}" + exit 255 +fi + +echo "Checking misspell..." +misspellRes=$(misspell $FILES) +if [ $? -ne 0 ]; then + echo "misspell checking failed: ${misspellRes}!" + exit 255 +fi +if [ -n "${misspellRes}" ]; then + echo "misspell checking failed: ${misspellRes}" + exit 255 +fi + +echo "Checking ineffassign..." +ineffassignRes=$(ineffassign -n .) +if [ $? -ne 0 ]; then + echo "ineffassign checking failed: ${ineffassignRes}!" + exit 255 +fi +if [ -n "${ineffassignRes}" ]; then + echo "ineffassign checking failed: ${ineffassignRes}" + exit 255 +fi + +echo "Checking gocyclo..." +gocycloRes=$(gocyclo -over 20 $FILES) +if [ -n "${gocycloRes}" ]; then + echo "gocyclo warning: ${gocycloRes}" +fi + +echo "Running tests..." +if [ -f cover.out ]; then + rm cover.out +fi + +go test -timeout 3m --race -cpu 1 +if [ $? -ne 0 ]; then + exit 255 +fi + +go test -timeout 3m --race -cpu 2 +if [ $? -ne 0 ]; then + exit 255 +fi + +go test -timeout 3m --race -cpu 4 +if [ $? -ne 0 ]; then + exit 255 +fi + +go test -timeout 3m -coverprofile cover.out +if [ $? -ne 0 ]; then + exit 255 +fi + +echo "Success" diff --git a/vendor/github.com/judwhite/go-svc/svc/test_cover.sh b/vendor/github.com/judwhite/go-svc/svc/test_cover.sh new file mode 100644 index 0000000..9059c37 --- /dev/null +++ b/vendor/github.com/judwhite/go-svc/svc/test_cover.sh @@ -0,0 +1,9 @@ +if [ ! -f cover.out ]; then + echo "Running tests..." + go test -timeout 3m -coverprofile cover.out + if [ $? -ne 0 ]; then + exit 255 + fi +fi + +go tool cover -html=cover.out diff --git a/vendor/vgo.list b/vendor/vgo.list new file mode 100644 index 0000000..4967629 --- /dev/null +++ b/vendor/vgo.list @@ -0,0 +1,4 @@ +MODULE VERSION +github.com/productionwentdown/forward - +github.com/judwhite/go-svc v1.0.0 +golang.org/x/sys v0.0.0-20180322165403-91ee8cde4354