@@ -22,7 +22,9 @@ import (
2222 "path"
2323 "path/filepath"
2424 "reflect"
25+ "syscall"
2526 "testing"
27+ "time"
2628
2729 "github.com/godbus/dbus"
2830)
@@ -144,6 +146,252 @@ func TestStartStopUnit(t *testing.T) {
144146 }
145147}
146148
149+ // Ensure that basic unit restarting works.
150+ func TestRestartUnit (t * testing.T ) {
151+ target := "start-stop.service"
152+ conn := setupConn (t )
153+
154+ setupUnit (target , conn , t )
155+ linkUnit (target , conn , t )
156+
157+ // Start the unit
158+ reschan := make (chan string )
159+ _ , err := conn .StartUnit (target , "replace" , reschan )
160+ if err != nil {
161+ t .Fatal (err )
162+ }
163+
164+ job := <- reschan
165+ if job != "done" {
166+ t .Fatal ("Job is not done:" , job )
167+ }
168+
169+ units , err := conn .ListUnits ()
170+ if err != nil {
171+ t .Fatal (err )
172+ }
173+
174+ unit := getUnitStatus (units , target )
175+ if unit == nil {
176+ t .Fatalf ("Test unit not found in list" )
177+ } else if unit .ActiveState != "active" {
178+ t .Fatalf ("Test unit not active" )
179+ }
180+
181+ // Restart the unit
182+ reschan = make (chan string )
183+ _ , err = conn .RestartUnit (target , "replace" , reschan )
184+ if err != nil {
185+ t .Fatal (err )
186+ }
187+
188+ job = <- reschan
189+ if job != "done" {
190+ t .Fatal ("Job is not done:" , job )
191+ }
192+
193+ // Stop the unit
194+ _ , err = conn .StopUnit (target , "replace" , reschan )
195+ if err != nil {
196+ t .Fatal (err )
197+ }
198+
199+ // wait for StopUnit job to complete
200+ <- reschan
201+
202+ units , err = conn .ListUnits ()
203+ if err != nil {
204+ t .Fatal (err )
205+ }
206+
207+ unit = getUnitStatus (units , target )
208+ if unit != nil {
209+ t .Fatalf ("Test unit found in list, should be stopped" )
210+ }
211+
212+ // Try to restart the unit.
213+ // It should still succeed, even if the unit is inactive.
214+ reschan = make (chan string )
215+ _ , err = conn .TryRestartUnit (target , "replace" , reschan )
216+ if err != nil {
217+ t .Fatal (err )
218+ }
219+
220+ // wait for StopUnit job to complete
221+ <- reschan
222+
223+ units , err = conn .ListUnits ()
224+ if err != nil {
225+ t .Fatal (err )
226+ }
227+
228+ unit = getUnitStatus (units , target )
229+ if unit != nil {
230+ t .Fatalf ("Test unit found in list, should be stopped" )
231+ }
232+ }
233+
234+ // Ensure that basic unit reloading works.
235+ func TestReloadUnit (t * testing.T ) {
236+ target := "reload.service"
237+ conn := setupConn (t )
238+
239+ err := conn .Subscribe ()
240+ if err != nil {
241+ t .Fatal (err )
242+ }
243+
244+ subSet := conn .NewSubscriptionSet ()
245+ evChan , errChan := subSet .Subscribe ()
246+
247+ subSet .Add (target )
248+
249+ setupUnit (target , conn , t )
250+ linkUnit (target , conn , t )
251+
252+ // Start the unit
253+ reschan := make (chan string )
254+ _ , err = conn .StartUnit (target , "replace" , reschan )
255+ if err != nil {
256+ t .Fatal (err )
257+ }
258+
259+ job := <- reschan
260+ if job != "done" {
261+ t .Fatal ("Job is not done:" , job )
262+ }
263+
264+ units , err := conn .ListUnits ()
265+ if err != nil {
266+ t .Fatal (err )
267+ }
268+
269+ unit := getUnitStatus (units , target )
270+ if unit == nil {
271+ t .Fatalf ("Test unit not found in list" )
272+ } else if unit .ActiveState != "active" {
273+ t .Fatalf ("Test unit not active" )
274+ }
275+
276+ // Reload the unit
277+ reschan = make (chan string )
278+
279+ _ , err = conn .ReloadUnit (target , "replace" , reschan )
280+ if err != nil {
281+ t .Fatal (err )
282+ }
283+
284+ job = <- reschan
285+ if job != "done" {
286+ t .Fatal ("Job is not done:" , job )
287+ }
288+
289+ timeout := make (chan bool , 1 )
290+ go func () {
291+ time .Sleep (3 * time .Second )
292+ close (timeout )
293+ }()
294+
295+ // Wait for the event, expecting the target UnitStatus meets all of the
296+ // following conditions:
297+ // * target is non-nil
298+ // * target's ActiveState is active.
299+ waitevent:
300+ for {
301+ select {
302+ case changes := <- evChan :
303+ tch , ok := changes [target ]
304+ if ! ok {
305+ continue waitevent
306+ }
307+ if tch != nil && tch .Name == target && tch .ActiveState == "active" {
308+ break waitevent
309+ }
310+ case err = <- errChan :
311+ t .Fatal (err )
312+ case <- timeout :
313+ t .Fatal ("Reached timeout" )
314+ }
315+ }
316+ }
317+
318+ // Ensure that basic unit reload-or-restarting works.
319+ func TestReloadOrRestartUnit (t * testing.T ) {
320+ target := "reload.service"
321+ conn := setupConn (t )
322+
323+ setupUnit (target , conn , t )
324+ linkUnit (target , conn , t )
325+
326+ // Start the unit
327+ reschan := make (chan string )
328+ _ , err := conn .StartUnit (target , "replace" , reschan )
329+ if err != nil {
330+ t .Fatal (err )
331+ }
332+
333+ job := <- reschan
334+ if job != "done" {
335+ t .Fatal ("Job is not done:" , job )
336+ }
337+
338+ units , err := conn .ListUnits ()
339+ if err != nil {
340+ t .Fatal (err )
341+ }
342+
343+ unit := getUnitStatus (units , target )
344+ if unit == nil {
345+ t .Fatalf ("Test unit not found in list" )
346+ } else if unit .ActiveState != "active" {
347+ t .Fatalf ("Test unit not active" )
348+ }
349+
350+ // Reload or restart the unit
351+ reschan = make (chan string )
352+ _ , err = conn .ReloadOrRestartUnit (target , "replace" , reschan )
353+ if err != nil {
354+ t .Fatal (err )
355+ }
356+
357+ job = <- reschan
358+ if job != "done" {
359+ t .Fatal ("Job is not done:" , job )
360+ }
361+
362+ // Stop the unit
363+ _ , err = conn .StopUnit (target , "replace" , reschan )
364+ if err != nil {
365+ t .Fatal (err )
366+ }
367+
368+ // wait for StopUnit job to complete
369+ <- reschan
370+
371+ units , err = conn .ListUnits ()
372+ if err != nil {
373+ t .Fatal (err )
374+ }
375+
376+ unit = getUnitStatus (units , target )
377+ if unit != nil && unit .ActiveState == "active" {
378+ t .Fatalf ("Test unit still active, should be inactive." )
379+ }
380+
381+ // Reload or try to restart the unit
382+ // It should still succeed, even if the unit is inactive.
383+ reschan = make (chan string )
384+ _ , err = conn .ReloadOrTryRestartUnit (target , "replace" , reschan )
385+ if err != nil {
386+ t .Fatal (err )
387+ }
388+
389+ job = <- reschan
390+ if job != "done" {
391+ t .Fatal ("Job is not done:" , job )
392+ }
393+ }
394+
147395// Ensure that ListUnitsByNames works.
148396func TestListUnitsByNames (t * testing.T ) {
149397 target1 := "systemd-journald.service"
@@ -538,6 +786,122 @@ func TestStartStopTransientScope(t *testing.T) {
538786 // int sd_pid_get_unit(pid_t pid, char **session)
539787}
540788
789+ // Ensure that basic unit gets killed by SIGTERM
790+ func TestKillUnit (t * testing.T ) {
791+ target := "start-stop.service"
792+ conn := setupConn (t )
793+
794+ err := conn .Subscribe ()
795+ if err != nil {
796+ t .Fatal (err )
797+ }
798+
799+ subSet := conn .NewSubscriptionSet ()
800+ evChan , errChan := subSet .Subscribe ()
801+
802+ subSet .Add (target )
803+
804+ setupUnit (target , conn , t )
805+ linkUnit (target , conn , t )
806+
807+ // Start the unit
808+ reschan := make (chan string )
809+ _ , err = conn .StartUnit (target , "replace" , reschan )
810+ if err != nil {
811+ t .Fatal (err )
812+ }
813+
814+ job := <- reschan
815+ if job != "done" {
816+ t .Fatal ("Job is not done:" , job )
817+ }
818+
819+ // send SIGTERM
820+ conn .KillUnit (target , int32 (syscall .SIGTERM ))
821+
822+ timeout := make (chan bool , 1 )
823+ go func () {
824+ time .Sleep (3 * time .Second )
825+ close (timeout )
826+ }()
827+
828+ // Wait for the event, expecting the target UnitStatus meets one of the
829+ // following conditions:
830+ // * target is nil, meaning the unit has completely gone.
831+ // * target is non-nil, and its ActiveState is not active.
832+ waitevent:
833+ for {
834+ select {
835+ case changes := <- evChan :
836+ tch , ok := changes [target ]
837+ if ! ok {
838+ continue waitevent
839+ }
840+ if tch == nil || (tch != nil && tch .Name == target && tch .ActiveState != "active" ) {
841+ break waitevent
842+ }
843+ case err = <- errChan :
844+ t .Fatal (err )
845+ case <- timeout :
846+ t .Fatal ("Reached timeout" )
847+ }
848+ }
849+ }
850+
851+ // Ensure that a failed unit gets reset
852+ func TestResetFailedUnit (t * testing.T ) {
853+ target := "start-failed.service"
854+ conn := setupConn (t )
855+
856+ setupUnit (target , conn , t )
857+ linkUnit (target , conn , t )
858+
859+ // Start the unit
860+ reschan := make (chan string )
861+ _ , err := conn .StartUnit (target , "replace" , reschan )
862+ if err != nil {
863+ t .Fatal (err )
864+ }
865+
866+ job := <- reschan
867+ if job != "failed" {
868+ t .Fatal ("Job is not failed:" , job )
869+ }
870+
871+ units , err := conn .ListUnits ()
872+ if err != nil {
873+ t .Fatal (err )
874+ }
875+
876+ unit := getUnitStatus (units , target )
877+ if unit == nil {
878+ t .Fatalf ("Test unit not found in list" )
879+ }
880+
881+ // reset the failed unit
882+ err = conn .ResetFailedUnit (target )
883+ if err != nil {
884+ t .Fatal (err )
885+ }
886+
887+ // Ensure that the target unit is actually gone
888+ units , err = conn .ListUnits ()
889+ if err != nil {
890+ t .Fatal (err )
891+ }
892+
893+ found := false
894+ for _ , u := range units {
895+ if u .Name == target {
896+ found = true
897+ break
898+ }
899+ }
900+ if found {
901+ t .Fatalf ("Test unit still found in list. units = %v" , units )
902+ }
903+ }
904+
541905func TestConnJobListener (t * testing.T ) {
542906 target := "start-stop.service"
543907 conn := setupConn (t )
@@ -620,3 +984,13 @@ func TestMaskUnmask(t *testing.T) {
620984 }
621985
622986}
987+
988+ // Test a global Reload
989+ func TestReload (t * testing.T ) {
990+ conn := setupConn (t )
991+
992+ err := conn .Reload ()
993+ if err != nil {
994+ t .Fatal (err )
995+ }
996+ }
0 commit comments