Skip to content

Commit 2bc41c2

Browse files
committed
tests: Test special watches with dying domains
Unit tests used to avoid calling Xenstored.main before, preferring to call Process.process_packet to circumvent some hard-to-mock logic. As a result, a few edge cases that can only be triggered from within the main loop in xenstored.ml slipped through - hence the bug that required 45c16ec. Now that xenstored.ml can handle being called in unit tests, we can verify that the main loop handles shutdown and dead domains correctly, sending the right number of correct special watch events when it needs to. Adds a hack to the mock domain_getinfo implementation, where all domids > 2000 are always considered 'dying' and domains > 1000 are always considered shutdown. Create a unit test that steps over the main loop in xenstored.ml, verifying that the correct watch events are sent. Signed-off-by: Andrii Sultanov <andriy.sultanov@vates.tech>
1 parent 89dbf26 commit 2bc41c2

File tree

2 files changed

+95
-7
lines changed

2 files changed

+95
-7
lines changed

tests/plugin_interface_v1.ml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@ module Domain_getinfo_V1_impl = struct
3838
let interface_open () = 1
3939

4040
let domain_getinfo _h domid =
41-
{domid; dying= false; shutdown= false; shutdown_code= 1}
41+
(* Special casing for test_introduce_release_watches_on_domain_death *)
42+
if domid > 2000 then
43+
{domid; dying= true; shutdown= false; shutdown_code= 1}
44+
else if domid > 1000 then
45+
{domid; dying= false; shutdown= true; shutdown_code= 1}
46+
else
47+
{domid; dying= false; shutdown= false; shutdown_code= 1}
4248

4349
let domain_getinfolist _h =
4450
[|{domid= 1; dying= false; shutdown= false; shutdown_code= 1}|]

tests/unit_tests.ml

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ let initialize () =
99
let cons = Connections.create () in
1010
(store, doms, cons)
1111

12+
let initialize_main_loop () =
13+
Domains.xenstored_port := "/tmp/port" ;
14+
let fd =
15+
Unix.openfile !Domains.xenstored_port [Unix.O_RDWR; Unix.O_CREAT] 0o600
16+
in
17+
let _ = Unix.write_substring fd "0" 0 1 in
18+
Unix.close fd ;
19+
Domains.xenstored_kva := "/tmp/kva" ;
20+
let fd =
21+
Unix.openfile !Domains.xenstored_kva [Unix.O_RDWR; Unix.O_CREAT] 0o600
22+
in
23+
Unix.close fd
24+
1225
let create_dom0_conn cons doms =
1326
(* NOTE: We can't use Domains.create0 since that opens several files
1427
unavailable in the test env *)
@@ -434,11 +447,8 @@ let test_transaction_watches () =
434447
let actual = Xenbus.Xb.unsafe_pop_output dom0.xb in
435448
check_result actual (Transaction_end, ["OK"])
436449

437-
(* Check that @introduceDomain and @releaseDomain watches appear on respective calls *)
438-
let test_introduce_release_watches () =
439-
let store, doms, cons = initialize () in
440-
let dom0 = create_dom0_conn cons doms in
441-
450+
let register_spec_watches store cons doms dom0 =
451+
(* Register both special watches *)
442452
run store cons doms
443453
[(dom0, none, (Watch, ["@introduceDomain"; "token"]), (Watch, ["OK"]))] ;
444454
assert_watches dom0 [("@introduceDomain", "token", None)] ;
@@ -450,7 +460,14 @@ let test_introduce_release_watches () =
450460
assert_watches dom0
451461
[("@releaseDomain", "token", None); ("@introduceDomain", "token", None)] ;
452462
(* One Watchevent is fired immediately after adding the watch unconditionally *)
453-
check_for_watchevent dom0 "@releaseDomain" "token" ;
463+
check_for_watchevent dom0 "@releaseDomain" "token"
464+
465+
(* Check that @introduceDomain and @releaseDomain watches appear on respective calls *)
466+
let test_introduce_release_watches () =
467+
let store, doms, cons = initialize () in
468+
let dom0 = create_dom0_conn cons doms in
469+
470+
register_spec_watches store cons doms dom0 ;
454471

455472
(* Watchevent is pushed to the queue first, then Introduce *)
456473
run store cons doms
@@ -470,6 +487,67 @@ let test_introduce_release_watches () =
470487
let actual = Xenbus.Xb.unsafe_pop_output dom0.xb in
471488
check_result actual (Release, ["OK"])
472489

490+
(* Check that @introduceDomain and @releaseDomain watches appear on domains dying *)
491+
let test_introduce_release_watches_on_domain_death () =
492+
(* Only the Xenstored.main loop checks for domains dying, so we need to
493+
do special handling here unlike any other unit test *)
494+
initialize_main_loop () ;
495+
let one_loop_iteration, store, cons, doms = Xenstored.main () in
496+
let dom0 = Hashtbl.find cons.domains 0 in
497+
498+
register_spec_watches store cons doms dom0 ;
499+
500+
(* Domains in [1000; 2000] are considered shutdown on the first query *)
501+
let _ = create_domU_conn cons doms 1001 in
502+
(* Check that a @releaseDomain watch event is sent when a domain shuts down *)
503+
one_loop_iteration () ;
504+
let actual = Xenbus.Xb.unsafe_pop_output dom0.xb in
505+
check_result actual (Watchevent, ["@releaseDomain"; "token"]) ;
506+
check_no_watchevents dom0 ;
507+
508+
(* Domains > 2000 are considered dead on the first query *)
509+
let _ = create_domU_conn cons doms 2001 in
510+
let _ = create_domU_conn cons doms 2002 in
511+
512+
(* Check that only a single @releaseDomain watch event is sent
513+
when multiple domains die *)
514+
one_loop_iteration () ;
515+
let actual = Xenbus.Xb.unsafe_pop_output dom0.xb in
516+
check_result actual (Watchevent, ["@releaseDomain"; "token"]) ;
517+
check_no_watchevents dom0 ;
518+
519+
(* Register a releaseDomain with depth=1 *)
520+
run store cons doms
521+
[
522+
( dom0
523+
, none
524+
, (Watch, ["@releaseDomain"; "tokendepth"; "1"])
525+
, (Watch, ["OK"])
526+
)
527+
] ;
528+
assert_watches dom0
529+
[
530+
("@releaseDomain", "tokendepth", Some 1)
531+
; ("@releaseDomain", "token", None)
532+
; ("@introduceDomain", "token", None)
533+
] ;
534+
(* One Watchevent is fired immediately after adding the watch unconditionally *)
535+
check_for_watchevent dom0 "@releaseDomain" "tokendepth" ;
536+
537+
(* Verify that detailed watch events are sent when multiple domains die *)
538+
let _ = create_domU_conn cons doms 2003 in
539+
let _ = create_domU_conn cons doms 2004 in
540+
one_loop_iteration () ;
541+
let actual = Xenbus.Xb.unsafe_pop_output dom0.xb in
542+
check_result actual (Watchevent, ["@releaseDomain/2004"; "tokendepth"]) ;
543+
let actual = Xenbus.Xb.unsafe_pop_output dom0.xb in
544+
check_result actual (Watchevent, ["@releaseDomain"; "token"]) ;
545+
let actual = Xenbus.Xb.unsafe_pop_output dom0.xb in
546+
check_result actual (Watchevent, ["@releaseDomain/2003"; "tokendepth"]) ;
547+
let actual = Xenbus.Xb.unsafe_pop_output dom0.xb in
548+
check_result actual (Watchevent, ["@releaseDomain/1001"; "tokendepth"]) ;
549+
check_no_watchevents dom0
550+
473551
(* Check that rm generates recursive watches *)
474552
let test_recursive_rm_watch () =
475553
let store, doms, cons = initialize () in
@@ -876,6 +954,10 @@ let () =
876954
, `Quick
877955
, test_introduce_release_watches
878956
)
957+
; ( "test_introduce_release_watches_on_domain_death"
958+
, `Quick
959+
, test_introduce_release_watches_on_domain_death
960+
)
879961
; ("test_recursive_rm_watch", `Quick, test_recursive_rm_watch)
880962
; ("test_no_watch_on_error", `Quick, test_no_watch_on_error)
881963
; ("test_watches_depth", `Quick, test_watches_depth)

0 commit comments

Comments
 (0)