github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/service_windows_test.go (about) 1 // Copyright 2012-2019 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 //go:build windows 15 16 package server 17 18 import ( 19 "errors" 20 "fmt" 21 "os" 22 "testing" 23 "time" 24 25 "golang.org/x/sys/windows/svc" 26 ) 27 28 // TestWinServiceWrapper reproduces the tests for service, 29 // with extra complication for windows API 30 func TestWinServiceWrapper(t *testing.T) { 31 /* 32 Since the windows API can't be tested through just the Run func, 33 a basic mock checks the intractions of the server, as happens in svc.Run. 34 This test checks : 35 - that the service fails to start within an unreasonable timeframe (serverC) 36 - that the service signals its state correctly to windows with svc.StartPending (mockC) 37 - that no other signal is sent to the windows service API(mockC) 38 */ 39 var ( 40 wsw = &winServiceWrapper{New(DefaultOptions())} 41 args = make([]string, 0) 42 serverC = make(chan error, 1) 43 mockC = make(chan error, 1) 44 changes = make(chan svc.ChangeRequest) 45 status = make(chan svc.Status) 46 ) 47 time.Sleep(time.Millisecond) 48 os.Setenv("NATS_STARTUP_DELAY", "1ms") // purposefly small 49 // prepare mock expectations 50 wsm := &winSvcMock{status: status} 51 wsm.Expect(svc.StartPending) 52 go func() { 53 mockC <- wsm.Listen(50*time.Millisecond, t) 54 close(mockC) 55 }() 56 57 go func() { // ensure failure with these conditions 58 _, exitCode := wsw.Execute(args, changes, status) 59 if exitCode == 0 { // expect error 60 serverC <- errors.New("Should have exitCode != 0") 61 } 62 wsw.server.Shutdown() 63 close(serverC) 64 }() 65 66 for expectedErrs := 2; expectedErrs >= 0; { 67 select { 68 case err := <-mockC: 69 if err != nil { 70 t.Fatalf("windows.svc mock: %v", err) 71 } else { 72 expectedErrs-- 73 } 74 case err := <-serverC: 75 if err != nil { 76 t.Fatalf("Server behavior: %v", err) 77 } else { 78 expectedErrs-- 79 } 80 case <-time.After(2 * time.Second): 81 t.Fatal("Test timed out") 82 } 83 } 84 } 85 86 // winSvcMock mocks part of the golang.org/x/sys/windows/svc 87 // execution stack, listening to svc.Status on its chan. 88 type winSvcMock struct { 89 status chan svc.Status 90 expectedSt []svc.State 91 } 92 93 // Expect allows to prepare a winSvcMock to receive a specific type of StatusMessage 94 func (w *winSvcMock) Expect(st svc.State) { 95 w.expectedSt = append(w.expectedSt, st) 96 } 97 98 // Listen is the mock's mainloop, expects messages to comply with previous Expect(). 99 func (w *winSvcMock) Listen(dur time.Duration, t *testing.T) error { 100 t.Helper() 101 timeout := time.NewTimer(dur) 102 defer timeout.Stop() 103 for idx, state := range w.expectedSt { 104 select { 105 case status := <-w.status: 106 if status.State == state { 107 t.Logf("message %d on status, OK\n", idx) 108 continue 109 } else { 110 return fmt.Errorf("message to winsock: expected %v, got %v", state, status.State) 111 } 112 case <-timeout.C: 113 return errors.New("Mock timed out") 114 } 115 } 116 select { 117 case <-timeout.C: 118 return nil 119 case st := <-w.status: 120 return fmt.Errorf("extra message to winsock: got %v", st) 121 122 } 123 }