@@ -239,3 +239,106 @@ func TestIsWindowsServiceWhenParentExits(t *testing.T) {
239
239
}
240
240
}
241
241
}
242
+
243
+ func TestServiceRestart (t * testing.T ) {
244
+ if os .Getenv ("GO_BUILDER_NAME" ) == "" {
245
+ // Don't install services on arbitrary users' machines.
246
+ t .Skip ("Skipping test that modifies system services: GO_BUILDER_NAME not set" )
247
+ }
248
+ if testing .Short () {
249
+ t .Skip ("Skipping test in short mode that modifies system services" )
250
+ }
251
+
252
+ const name = "svctestservice"
253
+
254
+ m , err := mgr .Connect ()
255
+ if err != nil {
256
+ t .Fatalf ("SCM connection failed: %v" , err )
257
+ }
258
+ defer m .Disconnect ()
259
+
260
+ // Build the service executable
261
+ exepath := filepath .Join (t .TempDir (), "a.exe" )
262
+ o , err := exec .Command ("go" , "build" , "-o" , exepath , "golang.org/x/sys/windows/svc/example" ).CombinedOutput ()
263
+ if err != nil {
264
+ t .Fatalf ("Failed to build service program: %v\n %v" , err , string (o ))
265
+ }
266
+
267
+ // Ensure any existing service is stopped and deleted
268
+ stopAndDeleteIfInstalled (t , m , name )
269
+
270
+ // Create the service
271
+ s , err := m .CreateService (name , exepath , mgr.Config {DisplayName : "x-sys svc test service" })
272
+ if err != nil {
273
+ t .Fatalf ("CreateService(%s) failed: %v" , name , err )
274
+ }
275
+ defer s .Close ()
276
+
277
+ // Set the service to restart on failure
278
+ actions := []mgr.RecoveryAction {
279
+ {Type : mgr .ServiceRestart , Delay : 1 * time .Second }, // Restart after 1 second
280
+ }
281
+ err = s .SetRecoveryActions (actions , 0 )
282
+ if err != nil {
283
+ t .Fatalf ("Failed to set service recovery actions: %v" , err )
284
+ }
285
+
286
+ // Set the flag to perform recovery actions on non-crash failures
287
+ err = s .SetRecoveryActionsOnNonCrashFailures (true )
288
+ if err != nil {
289
+ t .Fatalf ("Failed to set RecoveryActionsOnNonCrashFailures: %v" , err )
290
+ }
291
+
292
+ // Start the service
293
+ testState (t , s , svc .Stopped )
294
+ err = s .Start ()
295
+ if err != nil {
296
+ t .Fatalf ("Start(%s) failed: %v" , s .Name , err )
297
+ }
298
+
299
+ // Wait for the service to start
300
+ waitState (t , s , svc .Running )
301
+
302
+ // Get the initial process ID
303
+ status , err := s .Query ()
304
+ if err != nil {
305
+ t .Fatalf ("Query(%s) failed: %v" , s .Name , err )
306
+ }
307
+ initialPID := status .ProcessId
308
+ t .Logf ("Initial PID: %d" , initialPID )
309
+
310
+ // Wait up to 30 seconds for the PID to change, indicating a restart
311
+ var newPID uint32
312
+ success := false
313
+ for i := 0 ; i < 30 ; i ++ {
314
+ time .Sleep (1 * time .Second )
315
+
316
+ status , err = s .Query ()
317
+ if err != nil {
318
+ t .Fatalf ("Query(%s) failed: %v" , s .Name , err )
319
+ }
320
+ newPID = status .ProcessId
321
+
322
+ if newPID != 0 && newPID != initialPID {
323
+ success = true
324
+ t .Logf ("Service restarted successfully, new PID: %d" , newPID )
325
+ break
326
+ }
327
+ }
328
+
329
+ if ! success {
330
+ t .Fatalf ("Service did not restart within the expected time" )
331
+ }
332
+
333
+ // Cleanup: Stop and delete the service
334
+ _ , err = s .Control (svc .Stop )
335
+ if err != nil {
336
+ t .Fatalf ("Control(%s) failed: %v" , s .Name , err )
337
+ }
338
+ waitState (t , s , svc .Stopped )
339
+
340
+ err = s .Delete ()
341
+ if err != nil {
342
+ t .Fatalf ("Delete failed: %v" , err )
343
+ }
344
+ }
0 commit comments