@@ -39,11 +39,19 @@ var (
3939 prodSetWindowPos = moduser32 .NewProc ("SetWindowPos" )
4040 prodGetConsoleWindow = modkernel32 .NewProc ("GetConsoleWindow" )
4141 prodGetParent = moduser32 .NewProc ("GetParent" )
42- )
4342
44- var (
45- // Test helpers
46- fnProcessList = winapi .ProcessList
43+ // for testing
44+ mgrConnect = mgr .Connect
45+ mgrDisconnect = func (m * mgr.Mgr ) error { return m .Disconnect () }
46+ mgrOpenService = func (m * mgr.Mgr , name string ) (* mgr.Service , error ) { return m .OpenService (name ) }
47+ svcClose = func (s * mgr.Service ) error { return s .Close () }
48+ svcConfig = func (s * mgr.Service ) (mgr.Config , error ) { return s .Config () }
49+ svcQuery = func (s * mgr.Service ) (svc.Status , error ) { return s .Query () }
50+ svcUpdateConfig = func (s * mgr.Service , c mgr.Config ) error { return s .UpdateConfig (c ) }
51+ svcStart = func (s * mgr.Service ) error { return s .Start () }
52+ svcControl = func (s * mgr.Service , c svc.Cmd ) (svc.Status , error ) { return s .Control (c ) }
53+ timeSleep = time .Sleep
54+ fnProcessList = winapi .ProcessList
4755)
4856
4957const (
@@ -57,41 +65,41 @@ const (
5765
5866// GetServiceState interrogates local system services and returns their status and configuration.
5967func GetServiceState (name string ) (svc.Status , mgr.Config , error ) {
60- m , err := mgr . Connect ()
68+ m , err := mgrConnect ()
6169 if err != nil {
6270 return svc.Status {}, mgr.Config {}, err
6371 }
64- defer m . Disconnect ( )
65- s , err := m . OpenService ( name )
72+ defer mgrDisconnect ( m )
73+ s , err := mgrOpenService ( m , name )
6674 if err != nil {
6775 return svc.Status {}, mgr.Config {}, fmt .Errorf ("could not access service: %v" , err )
6876 }
69- defer s . Close ( )
77+ defer svcClose ( s )
7078
71- config , err := s . Config ( )
79+ config , err := svcConfig ( s )
7280 if err != nil {
7381 return svc.Status {}, mgr.Config {}, err
7482 }
75- status , err := s . Query ( )
83+ status , err := svcQuery ( s )
7684 return status , config , err
7785}
7886
7987// ChangeService can change a services type or/and startup behaviour
8088// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicestartmode
8189// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicetype
8290func ChangeService (name string , c mgr.Config ) error {
83- m , err := mgr . Connect ()
91+ m , err := mgrConnect ()
8492 if err != nil {
8593 return err
8694 }
87- defer m . Disconnect ( )
88- s , err := m . OpenService ( name )
95+ defer mgrDisconnect ( m )
96+ s , err := mgrOpenService ( m , name )
8997 if err != nil {
9098 return fmt .Errorf ("could not access service: %v" , err )
9199 }
92- defer s . Close ( )
100+ defer svcClose ( s )
93101
94- return s . UpdateConfig ( c )
102+ return svcUpdateConfig ( s , c )
95103}
96104
97105const (
@@ -112,25 +120,25 @@ func GetSysEnv(key string) (string, error) {
112120
113121// RestartService attempts to restart local system services.
114122func RestartService (name string ) error {
115- m , err := mgr . Connect ()
123+ m , err := mgrConnect ()
116124 if err != nil {
117125 return err
118126 }
119- defer m . Disconnect ( )
120- s , err := m . OpenService ( name )
127+ defer mgrDisconnect ( m )
128+ s , err := mgrOpenService ( m , name )
121129 if err != nil {
122130 return err
123131 }
124- defer s . Close ( )
132+ defer svcClose ( s )
125133
126- if err := stopService (s ); err != nil {
134+ if err := stopService (s , name ); err != nil {
127135 return err
128136 }
129137
130- return s . Start ( )
138+ return svcStart ( s )
131139}
132140
133- // RestartServiceWithVerify attempts to restart local system services and verifies the service is running with a 60 second timeout.
141+ // RestartServiceWithVerify attempts to restart local system services and verifies the service is running with a timeout.
134142func RestartServiceWithVerify (name string , retryCount ... int ) error {
135143 retryAttempts := 12
136144 if len (retryCount ) > 0 {
@@ -139,24 +147,29 @@ func RestartServiceWithVerify(name string, retryCount ...int) error {
139147 if err := RestartService (name ); err != nil {
140148 return err
141149 }
142- status := svc.Status {
143- State : svc .StartPending , // Assume the service is starting
150+
151+ // Check the actual state immediately, rather than faking it
152+ status , _ , err := GetServiceState (name )
153+ if err != nil {
154+ return err
144155 }
145- for retry := 0 ; status .State == svc .StartPending ; retry ++ {
156+
157+ // Loop as long as the service is NOT running
158+ for retry := 0 ; status .State != svc .Running ; retry ++ {
159+ if retry == retryAttempts {
160+ return fmt .Errorf ("timed out waiting for service %q to start" , name )
161+ }
162+
146163 deck .Infof ("Waiting for service %q to start, sleeping for 5 seconds" , name )
147- time . Sleep (5 * time .Second )
148- var err error
164+ timeSleep (5 * time .Second )
165+
149166 status , _ , err = GetServiceState (name )
150167 if err != nil {
151168 return err
152169 }
153- if retry == retryAttempts {
154- return fmt .Errorf ("timed out waiting for service %q to start" , name )
155- }
156- }
157- if status .State != svc .Running {
158- return fmt .Errorf ("service %q is not running after restart, current state: %v" , name , status .State )
159170 }
171+
172+ // If the loop exits normally, we know status.State == svc.Running
160173 return nil
161174}
162175
@@ -181,18 +194,18 @@ func SetSysEnv(key, value string) error {
181194
182195// StartService attempts to start local system services.
183196func StartService (name string ) error {
184- m , err := mgr . Connect ()
197+ m , err := mgrConnect ()
185198 if err != nil {
186199 return err
187200 }
188- defer m . Disconnect ( )
189- s , err := m . OpenService ( name )
201+ defer mgrDisconnect ( m )
202+ s , err := mgrOpenService ( m , name )
190203 if err != nil {
191204 return err
192205 }
193- defer s . Close ( )
206+ defer svcClose ( s )
194207
195- return s . Start ( )
208+ return svcStart ( s )
196209}
197210
198211// StartServiceWithVerify attempts to start local system services and verifies
@@ -210,7 +223,7 @@ func StartServiceWithVerify(name string, retryCount ...int) error {
210223 }
211224 for retry := 0 ; status .State == svc .StartPending ; retry ++ {
212225 deck .Infof ("Waiting for service %q to start, sleeping for 5 seconds" , name )
213- time . Sleep (5 * time .Second )
226+ timeSleep (5 * time .Second )
214227 var err error
215228 status , _ , err = GetServiceState (name )
216229 if err != nil {
@@ -226,28 +239,28 @@ func StartServiceWithVerify(name string, retryCount ...int) error {
226239 return nil
227240}
228241
229- func stopService (s * mgr.Service ) error {
242+ func stopService (s * mgr.Service , name string ) error {
230243 // although s.Control returns stat, if the service is already stopped it returns an error
231- stat , err := s . Query ( )
244+ stat , err := svcQuery ( s )
232245 if err != nil {
233246 return err
234247 }
235248 if stat .State == svc .Stopped {
236249 return nil
237250 }
238- stat , err = s . Control ( svc .Stop )
251+ stat , err = svcControl ( s , svc .Stop )
239252 if err != nil {
240253 return err
241254 }
242255 retry := 0
243256 for stat .State != svc .Stopped {
244- deck .Infof ("Waiting for service %q to stop." , s . Name )
245- time . Sleep (5 * time .Second )
257+ deck .Infof ("Waiting for service %q to stop." , name )
258+ timeSleep (5 * time .Second )
246259 retry ++
247260 if retry > 12 {
248- return fmt .Errorf ("timed out waiting for service %q to stop" , s . Name )
261+ return fmt .Errorf ("timed out waiting for service %q to stop" , name )
249262 }
250- stat , err = s . Query ( )
263+ stat , err = svcQuery ( s )
251264 if err != nil {
252265 return err
253266 }
@@ -257,18 +270,18 @@ func stopService(s *mgr.Service) error {
257270
258271// StopService attempts to stop local system services.
259272func StopService (name string ) error {
260- m , err := mgr . Connect ()
273+ m , err := mgrConnect ()
261274 if err != nil {
262275 return err
263276 }
264- defer m . Disconnect ( )
265- s , err := m . OpenService ( name )
277+ defer mgrDisconnect ( m )
278+ s , err := mgrOpenService ( m , name )
266279 if err != nil {
267280 return err
268281 }
269- defer s . Close ( )
282+ defer svcClose ( s )
270283
271- return stopService (s )
284+ return stopService (s , name )
272285}
273286
274287// WaitForProcessExit waits for a process to stop (no longer appear in the process list).
0 commit comments