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);