2 * QEMU XenStore XsNode testing
4 * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
10 #include "qemu/osdep.h"
11 #include "qapi/error.h"
12 #include "qemu/module.h"
14 static int nr_xs_nodes
;
15 static GList
*xs_node_list
;
17 #define XS_NODE_UNIT_TEST
20 * We don't need the core Xen definitions. And we *do* want to be able
21 * to run the unit tests even on architectures that Xen doesn't support
22 * (because life's too short to bother doing otherwise, and test coverage
25 #define __XEN_PUBLIC_XEN_H__
26 typedef unsigned long xen_pfn_t
;
28 #include "hw/i386/kvm/xenstore_impl.c"
33 static void dump_ref(const char *name
, XsNode
*n
, int indent
)
37 if (!indent
&& name
) {
38 printf("%s:\n", name
);
41 for (i
= 0; i
< indent
; i
++) {
45 printf("->%p(%d, '%s'): '%.*s'%s%s\n", n
, n
->ref
, n
->name
,
46 (int)(n
->content
? n
->content
->len
: strlen("<empty>")),
47 n
->content
? (char *)n
->content
->data
: "<empty>",
48 n
->modified_in_tx
? " MODIFIED" : "",
49 n
->deleted_in_tx
? " DELETED" : "");
52 g_hash_table_foreach(n
->children
, (void *)dump_ref
,
53 GINT_TO_POINTER(indent
+ 2));
57 /* This doesn't happen in qemu but we want to make valgrind happy */
58 static void xs_impl_delete(XenstoreImplState
*s
, bool last
)
62 xs_impl_reset_watches(s
, DOMID_GUEST
);
63 g_assert(!s
->nr_domu_watches
);
65 err
= xs_impl_rm(s
, DOMID_QEMU
, XBT_NULL
, "/local");
67 g_assert(s
->nr_nodes
== 1);
69 g_hash_table_unref(s
->watches
);
70 g_hash_table_unref(s
->transactions
);
71 xs_node_unref(s
->root
);
80 for (l
= xs_node_list
; l
; l
= l
->next
) {
82 printf("Remaining node at %p name %s ref %u\n", n
, n
->name
,
86 g_assert(!nr_xs_nodes
);
90 char path
[XENSTORE_ABS_PATH_MAX
+ 1];
96 static bool compare_perms(GList
*p1
, GList
*p2
)
99 if (!p2
|| g_strcmp0(p1
->data
, p2
->data
)) {
108 static bool compare_content(GByteArray
*c1
, GByteArray
*c2
)
110 size_t len1
= 0, len2
= 0;
126 return !memcmp(c1
->data
, c2
->data
, len1
);
129 static void compare_child(gpointer
, gpointer
, gpointer
);
131 static void compare_nodes(struct compare_walk
*cw
, XsNode
*n1
, XsNode
*n2
)
133 int nr_children1
= 0, nr_children2
= 0;
136 nr_children1
= g_hash_table_size(n1
->children
);
139 nr_children2
= g_hash_table_size(n2
->children
);
142 if (n1
->ref
!= n2
->ref
||
143 n1
->deleted_in_tx
!= n2
->deleted_in_tx
||
144 n1
->modified_in_tx
!= n2
->modified_in_tx
||
145 !compare_perms(n1
->perms
, n2
->perms
) ||
146 !compare_content(n1
->content
, n2
->content
) ||
147 nr_children1
!= nr_children2
) {
148 cw
->compare_ok
= false;
149 printf("Compare failure on '%s'\n", cw
->path
);
153 XsNode
*oldparent
= cw
->parent_2
;
155 g_hash_table_foreach(n1
->children
, compare_child
, cw
);
157 cw
->parent_2
= oldparent
;
161 static void compare_child(gpointer key
, gpointer val
, gpointer opaque
)
163 struct compare_walk
*cw
= opaque
;
164 char *childname
= key
;
165 XsNode
*child1
= val
;
166 XsNode
*child2
= g_hash_table_lookup(cw
->parent_2
->children
, childname
);
167 int pathlen
= strlen(cw
->path
);
170 cw
->compare_ok
= false;
171 printf("Child '%s' does not exist under '%s'\n", childname
, cw
->path
);
175 strncat(cw
->path
, "/", sizeof(cw
->path
) - 1);
176 strncat(cw
->path
, childname
, sizeof(cw
->path
) - 1);
178 compare_nodes(cw
, child1
, child2
);
179 cw
->path
[pathlen
] = '\0';
182 static bool compare_trees(XsNode
*n1
, XsNode
*n2
)
184 struct compare_walk cw
;
188 cw
.compare_ok
= true;
194 compare_nodes(&cw
, n1
, n2
);
195 return cw
.compare_ok
;
198 static void compare_tx(gpointer key
, gpointer val
, gpointer opaque
)
200 XenstoreImplState
*s2
= opaque
;
201 XsTransaction
*t1
= val
, *t2
;
202 unsigned int tx_id
= GPOINTER_TO_INT(key
);
204 t2
= g_hash_table_lookup(s2
->transactions
, key
);
207 g_assert(t1
->tx_id
== tx_id
);
208 g_assert(t2
->tx_id
== tx_id
);
209 g_assert(t1
->base_tx
== t2
->base_tx
);
210 g_assert(t1
->dom_id
== t2
->dom_id
);
211 if (!compare_trees(t1
->root
, t2
->root
)) {
212 printf("Comparison failure in TX %u after serdes:\n", tx_id
);
213 dump_ref("Original", t1
->root
, 0);
214 dump_ref("Deserialised", t2
->root
, 0);
217 g_assert(t1
->nr_nodes
== t2
->nr_nodes
);
220 static int write_str(XenstoreImplState
*s
, unsigned int dom_id
,
221 unsigned int tx_id
, const char *path
,
224 GByteArray
*d
= g_byte_array_new();
227 g_byte_array_append(d
, (void *)content
, strlen(content
));
228 err
= xs_impl_write(s
, dom_id
, tx_id
, path
, d
);
229 g_byte_array_unref(d
);
233 static void watch_cb(void *_str
, const char *path
, const char *token
)
237 g_string_append(str
, path
);
238 g_string_append(str
, token
);
241 static void check_serdes(XenstoreImplState
*s
)
243 XenstoreImplState
*s2
= xs_impl_create(DOMID_GUEST
);
244 GByteArray
*bytes
= xs_impl_serialize(s
);
245 int nr_transactions1
, nr_transactions2
;
248 ret
= xs_impl_deserialize(s2
, bytes
, DOMID_GUEST
, watch_cb
, NULL
);
251 g_byte_array_unref(bytes
);
253 g_assert(s
->last_tx
== s2
->last_tx
);
254 g_assert(s
->root_tx
== s2
->root_tx
);
256 if (!compare_trees(s
->root
, s2
->root
)) {
257 printf("Comparison failure in main tree after serdes:\n");
258 dump_ref("Original", s
->root
, 0);
259 dump_ref("Deserialised", s2
->root
, 0);
263 nr_transactions1
= g_hash_table_size(s
->transactions
);
264 nr_transactions2
= g_hash_table_size(s2
->transactions
);
265 g_assert(nr_transactions1
== nr_transactions2
);
267 g_hash_table_foreach(s
->transactions
, compare_tx
, s2
);
269 g_assert(s
->nr_domu_watches
== s2
->nr_domu_watches
);
270 g_assert(s
->nr_domu_transactions
== s2
->nr_domu_transactions
);
271 g_assert(s
->nr_nodes
== s2
->nr_nodes
);
272 xs_impl_delete(s2
, false);
275 static XenstoreImplState
*setup(void)
277 XenstoreImplState
*s
= xs_impl_create(DOMID_GUEST
);
282 abspath
= g_strdup_printf("/local/domain/%u", DOMID_GUEST
);
284 err
= write_str(s
, DOMID_QEMU
, XBT_NULL
, abspath
, "");
286 g_assert(s
->nr_nodes
== 4);
288 perms
= g_list_append(NULL
, g_strdup_printf("n%u", DOMID_QEMU
));
289 perms
= g_list_append(perms
, g_strdup_printf("r%u", DOMID_GUEST
));
291 err
= xs_impl_set_perms(s
, DOMID_QEMU
, XBT_NULL
, abspath
, perms
);
294 g_list_free_full(perms
, g_free
);
297 abspath
= g_strdup_printf("/local/domain/%u/some", DOMID_GUEST
);
299 err
= write_str(s
, DOMID_QEMU
, XBT_NULL
, abspath
, "");
301 g_assert(s
->nr_nodes
== 5);
303 perms
= g_list_append(NULL
, g_strdup_printf("n%u", DOMID_GUEST
));
305 err
= xs_impl_set_perms(s
, DOMID_QEMU
, XBT_NULL
, abspath
, perms
);
308 g_list_free_full(perms
, g_free
);
314 static void test_xs_node_simple(void)
316 GByteArray
*data
= g_byte_array_new();
317 XenstoreImplState
*s
= setup();
318 GString
*guest_watches
= g_string_new(NULL
);
319 GString
*qemu_watches
= g_string_new(NULL
);
327 err
= xs_impl_watch(s
, DOMID_GUEST
, "some", "guestwatch",
328 watch_cb
, guest_watches
);
330 g_assert(guest_watches
->len
== strlen("someguestwatch"));
331 g_assert(!strcmp(guest_watches
->str
, "someguestwatch"));
332 g_string_truncate(guest_watches
, 0);
334 err
= xs_impl_watch(s
, 0, "/local/domain/1/some", "qemuwatch",
335 watch_cb
, qemu_watches
);
337 g_assert(qemu_watches
->len
== strlen("/local/domain/1/someqemuwatch"));
338 g_assert(!strcmp(qemu_watches
->str
, "/local/domain/1/someqemuwatch"));
339 g_string_truncate(qemu_watches
, 0);
341 /* Read gives ENOENT when it should */
342 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "foo", data
);
343 g_assert(err
== ENOENT
);
346 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/relative/path",
348 g_assert(s
->nr_nodes
== 7);
350 g_assert(!strcmp(guest_watches
->str
,
351 "some/relative/pathguestwatch"));
352 g_assert(!strcmp(qemu_watches
->str
,
353 "/local/domain/1/some/relative/pathqemuwatch"));
355 g_string_truncate(qemu_watches
, 0);
356 g_string_truncate(guest_watches
, 0);
357 xs_impl_reset_watches(s
, 0);
359 /* Read gives back what we wrote */
360 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/relative/path", data
);
362 g_assert(data
->len
== strlen("something"));
363 g_assert(!memcmp(data
->data
, "something", data
->len
));
365 /* Even if we use an absolute path */
366 g_byte_array_set_size(data
, 0);
367 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
,
368 "/local/domain/1/some/relative/path", data
);
370 g_assert(data
->len
== strlen("something"));
372 g_assert(!qemu_watches
->len
);
373 g_assert(!guest_watches
->len
);
374 /* Keep a copy, to force COW mode */
375 old_root
= xs_node_ref(s
->root
);
377 /* Write somewhere we aren't allowed, in COW mode */
378 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "/local/domain/badplace",
380 g_assert(err
== EACCES
);
381 g_assert(s
->nr_nodes
== 7);
383 /* Write works again */
384 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
,
385 "/local/domain/1/some/relative/path2",
388 g_assert(s
->nr_nodes
== 8);
389 g_assert(!qemu_watches
->len
);
390 g_assert(!strcmp(guest_watches
->str
, "some/relative/path2guestwatch"));
391 g_string_truncate(guest_watches
, 0);
393 /* Overwrite an existing node */
394 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/relative/path",
397 g_assert(s
->nr_nodes
== 8);
398 g_assert(!qemu_watches
->len
);
399 g_assert(!strcmp(guest_watches
->str
, "some/relative/pathguestwatch"));
400 g_string_truncate(guest_watches
, 0);
402 /* We can list the two files we wrote */
403 err
= xs_impl_directory(s
, DOMID_GUEST
, XBT_NULL
, "some/relative", &gencnt
,
407 g_assert(gencnt
== 2);
408 g_assert(!strcmp(items
->data
, "path"));
409 g_assert(items
->next
);
410 g_assert(!strcmp(items
->next
->data
, "path2"));
411 g_assert(!items
->next
->next
);
412 g_list_free_full(items
, g_free
);
414 err
= xs_impl_unwatch(s
, DOMID_GUEST
, "some", "guestwatch",
415 watch_cb
, guest_watches
);
418 err
= xs_impl_unwatch(s
, DOMID_GUEST
, "some", "guestwatch",
419 watch_cb
, guest_watches
);
420 g_assert(err
== ENOENT
);
422 err
= xs_impl_watch(s
, DOMID_GUEST
, "some/relative/path2", "watchp2",
423 watch_cb
, guest_watches
);
425 g_assert(guest_watches
->len
== strlen("some/relative/path2watchp2"));
426 g_assert(!strcmp(guest_watches
->str
, "some/relative/path2watchp2"));
427 g_string_truncate(guest_watches
, 0);
429 err
= xs_impl_watch(s
, DOMID_GUEST
, "/local/domain/1/some/relative",
430 "watchrel", watch_cb
, guest_watches
);
432 g_assert(guest_watches
->len
==
433 strlen("/local/domain/1/some/relativewatchrel"));
434 g_assert(!strcmp(guest_watches
->str
,
435 "/local/domain/1/some/relativewatchrel"));
436 g_string_truncate(guest_watches
, 0);
438 /* Write somewhere else which already existed */
439 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/relative", "moredata");
441 g_assert(s
->nr_nodes
== 8);
443 /* Write somewhere we aren't allowed */
444 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "/local/domain/badplace",
446 g_assert(err
== EACCES
);
448 g_assert(!strcmp(guest_watches
->str
,
449 "/local/domain/1/some/relativewatchrel"));
450 g_string_truncate(guest_watches
, 0);
452 g_byte_array_set_size(data
, 0);
453 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/relative", data
);
455 g_assert(data
->len
== strlen("moredata"));
456 g_assert(!memcmp(data
->data
, "moredata", data
->len
));
458 /* Overwrite existing data */
459 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/relative", "otherdata");
461 g_string_truncate(guest_watches
, 0);
463 g_byte_array_set_size(data
, 0);
464 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/relative", data
);
466 g_assert(data
->len
== strlen("otherdata"));
467 g_assert(!memcmp(data
->data
, "otherdata", data
->len
));
469 /* Remove the subtree */
470 err
= xs_impl_rm(s
, DOMID_GUEST
, XBT_NULL
, "some/relative");
472 g_assert(s
->nr_nodes
== 5);
474 /* Each watch fires with the least specific relevant path */
475 g_assert(strstr(guest_watches
->str
,
476 "some/relative/path2watchp2"));
477 g_assert(strstr(guest_watches
->str
,
478 "/local/domain/1/some/relativewatchrel"));
479 g_string_truncate(guest_watches
, 0);
481 g_byte_array_set_size(data
, 0);
482 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/relative", data
);
483 g_assert(err
== ENOENT
);
484 g_byte_array_unref(data
);
486 xs_impl_reset_watches(s
, DOMID_GUEST
);
487 g_string_free(qemu_watches
, true);
488 g_string_free(guest_watches
, true);
489 xs_node_unref(old_root
);
490 xs_impl_delete(s
, true);
494 static void do_test_xs_node_tx(bool fail
, bool commit
)
496 XenstoreImplState
*s
= setup();
497 GString
*watches
= g_string_new(NULL
);
498 GByteArray
*data
= g_byte_array_new();
499 unsigned int tx_id
= XBT_NULL
;
505 err
= xs_impl_watch(s
, DOMID_GUEST
, "some", "watch",
508 g_assert(watches
->len
== strlen("somewatch"));
509 g_assert(!strcmp(watches
->str
, "somewatch"));
510 g_string_truncate(watches
, 0);
512 /* Write something */
513 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/relative/path",
515 g_assert(s
->nr_nodes
== 7);
517 g_assert(!strcmp(watches
->str
,
518 "some/relative/pathwatch"));
519 g_string_truncate(watches
, 0);
521 /* Create a transaction */
522 err
= xs_impl_transaction_start(s
, DOMID_GUEST
, &tx_id
);
526 /* Write something else in the root */
527 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/relative/path",
530 g_assert(s
->nr_nodes
== 7);
531 g_assert(!strcmp(watches
->str
,
532 "some/relative/pathwatch"));
533 g_string_truncate(watches
, 0);
536 g_assert(!watches
->len
);
538 /* Perform a write in the transaction */
539 err
= write_str(s
, DOMID_GUEST
, tx_id
, "some/relative/path",
542 g_assert(s
->nr_nodes
== 7);
543 g_assert(!watches
->len
);
545 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/relative/path", data
);
548 g_assert(data
->len
== strlen("another thing"));
549 g_assert(!memcmp(data
->data
, "another thing", data
->len
));
551 g_assert(data
->len
== strlen("something"));
552 g_assert(!memcmp(data
->data
, "something", data
->len
));
554 g_byte_array_set_size(data
, 0);
556 err
= xs_impl_read(s
, DOMID_GUEST
, tx_id
, "some/relative/path", data
);
558 g_assert(data
->len
== strlen("something else"));
559 g_assert(!memcmp(data
->data
, "something else", data
->len
));
560 g_byte_array_set_size(data
, 0);
564 /* Attempt to commit the transaction */
565 err
= xs_impl_transaction_end(s
, DOMID_GUEST
, tx_id
, commit
);
566 if (commit
&& fail
) {
567 g_assert(err
== EAGAIN
);
571 if (commit
&& !fail
) {
572 g_assert(!strcmp(watches
->str
,
573 "some/relative/pathwatch"));
574 g_string_truncate(watches
, 0);
576 g_assert(!watches
->len
);
578 g_assert(s
->nr_nodes
== 7);
582 err
= xs_impl_unwatch(s
, DOMID_GUEST
, "some", "watch",
586 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/relative/path", data
);
589 g_assert(data
->len
== strlen("another thing"));
590 g_assert(!memcmp(data
->data
, "another thing", data
->len
));
592 g_assert(data
->len
== strlen("something else"));
593 g_assert(!memcmp(data
->data
, "something else", data
->len
));
595 g_assert(data
->len
== strlen("something"));
596 g_assert(!memcmp(data
->data
, "something", data
->len
));
598 g_byte_array_unref(data
);
599 g_string_free(watches
, true);
600 xs_impl_delete(s
, true);
603 static void test_xs_node_tx_fail(void)
605 do_test_xs_node_tx(true, true);
608 static void test_xs_node_tx_abort(void)
610 do_test_xs_node_tx(false, false);
611 do_test_xs_node_tx(true, false);
613 static void test_xs_node_tx_succeed(void)
615 do_test_xs_node_tx(false, true);
618 static void test_xs_node_tx_rm(void)
620 XenstoreImplState
*s
= setup();
621 GString
*watches
= g_string_new(NULL
);
622 GByteArray
*data
= g_byte_array_new();
623 unsigned int tx_id
= XBT_NULL
;
629 err
= xs_impl_watch(s
, DOMID_GUEST
, "some", "watch",
632 g_assert(watches
->len
== strlen("somewatch"));
633 g_assert(!strcmp(watches
->str
, "somewatch"));
634 g_string_truncate(watches
, 0);
636 /* Write something */
637 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark/relative/path",
640 g_assert(s
->nr_nodes
== 9);
641 g_assert(!strcmp(watches
->str
,
642 "some/deep/dark/relative/pathwatch"));
643 g_string_truncate(watches
, 0);
645 /* Create a transaction */
646 err
= xs_impl_transaction_start(s
, DOMID_GUEST
, &tx_id
);
649 /* Delete the tree in the transaction */
650 err
= xs_impl_rm(s
, DOMID_GUEST
, tx_id
, "some/deep/dark");
652 g_assert(s
->nr_nodes
== 9);
653 g_assert(!watches
->len
);
655 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark/relative/path",
658 g_assert(data
->len
== strlen("something"));
659 g_assert(!memcmp(data
->data
, "something", data
->len
));
660 g_byte_array_set_size(data
, 0);
664 /* Commit the transaction */
665 err
= xs_impl_transaction_end(s
, DOMID_GUEST
, tx_id
, true);
667 g_assert(s
->nr_nodes
== 6);
669 g_assert(!strcmp(watches
->str
, "some/deep/darkwatch"));
670 g_string_truncate(watches
, 0);
672 /* Now the node is gone */
673 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark/relative/path",
675 g_assert(err
== ENOENT
);
676 g_byte_array_unref(data
);
678 err
= xs_impl_unwatch(s
, DOMID_GUEST
, "some", "watch",
682 g_string_free(watches
, true);
683 xs_impl_delete(s
, true);
686 static void test_xs_node_tx_resurrect(void)
688 XenstoreImplState
*s
= setup();
689 GString
*watches
= g_string_new(NULL
);
690 GByteArray
*data
= g_byte_array_new();
691 unsigned int tx_id
= XBT_NULL
;
696 /* Write something */
697 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark/relative/path",
700 g_assert(s
->nr_nodes
== 9);
702 /* Another node to remain shared */
703 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/place/safe", "keepme");
705 g_assert(s
->nr_nodes
== 11);
707 /* This node will be wiped and resurrected */
708 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark",
711 g_assert(s
->nr_nodes
== 11);
714 err
= xs_impl_watch(s
, DOMID_GUEST
, "some", "watch",
717 g_assert(watches
->len
== strlen("somewatch"));
718 g_assert(!strcmp(watches
->str
, "somewatch"));
719 g_string_truncate(watches
, 0);
721 /* Create a transaction */
722 err
= xs_impl_transaction_start(s
, DOMID_GUEST
, &tx_id
);
725 /* Delete the tree in the transaction */
726 err
= xs_impl_rm(s
, DOMID_GUEST
, tx_id
, "some/deep");
728 g_assert(s
->nr_nodes
== 11);
729 g_assert(!watches
->len
);
731 /* Resurrect part of it */
732 err
= write_str(s
, DOMID_GUEST
, tx_id
, "some/deep/dark/different/path",
735 g_assert(s
->nr_nodes
== 11);
739 /* Commit the transaction */
740 err
= xs_impl_transaction_end(s
, DOMID_GUEST
, tx_id
, true);
742 g_assert(s
->nr_nodes
== 11);
747 g_assert(strstr(watches
->str
, "some/deep/dark/different/pathwatch"));
748 /* topmost deleted */
749 g_assert(strstr(watches
->str
, "some/deep/dark/relativewatch"));
751 g_assert(strstr(watches
->str
, "some/deep/darkwatch"));
753 g_string_truncate(watches
, 0);
755 /* Now the node is gone */
756 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark/relative/path",
758 g_assert(err
== ENOENT
);
759 g_byte_array_unref(data
);
763 err
= xs_impl_unwatch(s
, DOMID_GUEST
, "some", "watch",
767 g_string_free(watches
, true);
768 xs_impl_delete(s
, true);
771 static void test_xs_node_tx_resurrect2(void)
773 XenstoreImplState
*s
= setup();
774 GString
*watches
= g_string_new(NULL
);
775 GByteArray
*data
= g_byte_array_new();
776 unsigned int tx_id
= XBT_NULL
;
781 /* Write something */
782 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark/relative/path",
785 g_assert(s
->nr_nodes
== 9);
787 /* Another node to remain shared */
788 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/place/safe", "keepme");
790 g_assert(s
->nr_nodes
== 11);
792 /* This node will be wiped and resurrected */
793 err
= write_str(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark",
796 g_assert(s
->nr_nodes
== 11);
799 err
= xs_impl_watch(s
, DOMID_GUEST
, "some", "watch",
802 g_assert(watches
->len
== strlen("somewatch"));
803 g_assert(!strcmp(watches
->str
, "somewatch"));
804 g_string_truncate(watches
, 0);
806 /* Create a transaction */
807 err
= xs_impl_transaction_start(s
, DOMID_GUEST
, &tx_id
);
810 /* Delete the tree in the transaction */
811 err
= xs_impl_rm(s
, DOMID_GUEST
, tx_id
, "some/deep");
813 g_assert(s
->nr_nodes
== 11);
814 g_assert(!watches
->len
);
816 /* Resurrect part of it */
817 err
= write_str(s
, DOMID_GUEST
, tx_id
, "some/deep/dark/relative/path",
820 g_assert(s
->nr_nodes
== 11);
824 /* Commit the transaction */
825 err
= xs_impl_transaction_end(s
, DOMID_GUEST
, tx_id
, true);
827 g_assert(s
->nr_nodes
== 11);
832 g_assert(strstr(watches
->str
, "some/deep/dark/relative/pathwatch"));
834 g_assert(strstr(watches
->str
, "some/deep/darkwatch"));
836 g_string_truncate(watches
, 0);
838 /* Now the node is gone */
839 err
= xs_impl_read(s
, DOMID_GUEST
, XBT_NULL
, "some/deep/dark/relative/path",
842 g_assert(data
->len
== strlen("something"));
843 g_assert(!memcmp(data
->data
, "something", data
->len
));
845 g_byte_array_unref(data
);
849 err
= xs_impl_unwatch(s
, DOMID_GUEST
, "some", "watch",
853 g_string_free(watches
, true);
854 xs_impl_delete(s
, true);
857 int main(int argc
, char **argv
)
859 g_test_init(&argc
, &argv
, NULL
);
860 module_call_init(MODULE_INIT_QOM
);
862 g_test_add_func("/xs_node/simple", test_xs_node_simple
);
863 g_test_add_func("/xs_node/tx_abort", test_xs_node_tx_abort
);
864 g_test_add_func("/xs_node/tx_fail", test_xs_node_tx_fail
);
865 g_test_add_func("/xs_node/tx_succeed", test_xs_node_tx_succeed
);
866 g_test_add_func("/xs_node/tx_rm", test_xs_node_tx_rm
);
867 g_test_add_func("/xs_node/tx_resurrect", test_xs_node_tx_resurrect
);
868 g_test_add_func("/xs_node/tx_resurrect2", test_xs_node_tx_resurrect2
);