diff --git a/tests/Makefile.am b/tests/Makefile.am
index e5cf7403302bf90fad4da2ed383f70f20df24053..b1d6dab4c09c7b6a87387923f1ffc8f4c181dc64 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -220,6 +220,8 @@ test_programs += interfacexml2xmltest
test_programs += cputest
+test_programs += metadatatest
+
test_scripts = \
capabilityschematest \
interfaceschematest \
@@ -574,6 +576,11 @@ cputest_SOURCES = \
testutils.c testutils.h
cputest_LDADD = $(LDADDS) $(LIBXML_LIBS)
+metadatatest_SOURCES = \
+ metadatatest.c \
+ testutils.c testutils.h
+metadatatest_LDADD = $(LDADDS) $(LIBXML_LIBS)
+
virshtest_SOURCES = \
virshtest.c \
testutils.c testutils.h
diff --git a/tests/metadatatest.c b/tests/metadatatest.c
new file mode 100644
index 0000000000000000000000000000000000000000..6bcf3355d2211236a891a1c140f47b78c3fa604c
--- /dev/null
+++ b/tests/metadatatest.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; If not, see
+ * .
+ *
+ * Author: Peter Krempa
+ */
+
+#include
+
+#include "testutils.h"
+
+#include "virerror.h"
+#include "virxml.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+static const char metadata1[] =
+"\n"
+" foobar\n"
+" foofoo\n"
+" zomg\n"
+"";
+
+
+static const char metadata1_ns[] =
+"\n"
+" foobar\n"
+" foofoo\n"
+" zomg\n"
+"";
+
+
+static const char metadata2[] =
+"\n"
+" baz\n"
+"";
+
+
+static const char metadata2_ns[] =
+"\n"
+" baz\n"
+"";
+
+
+static char *
+getMetadataFromXML(virDomainPtr dom)
+{
+ xmlDocPtr doc = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlNodePtr node;
+
+ char *xml = NULL;
+ char *ret = NULL;
+
+ if (!(xml = virDomainGetXMLDesc(dom, 0)))
+ goto cleanup;
+
+ if (!(doc = virXMLParseStringCtxt(xml, "(domain_definition)", &ctxt)))
+ goto cleanup;
+
+ if (!(node = virXPathNode("//metadata/*", ctxt)))
+ goto cleanup;
+
+ ret = virXMLNodeToString(node->doc, node);
+
+cleanup:
+ VIR_FREE(xml);
+ xmlFreeDoc(doc);
+ xmlXPathFreeContext(ctxt);
+
+ return ret;
+}
+
+
+static void
+metadataXMLConvertApostrophe(char *str)
+{
+ do {
+ if (*str == '\"')
+ *str = '\'';
+ } while ((*++str) != '\0');
+}
+
+
+static bool
+verifyMetadata(virDomainPtr dom,
+ const char *expectXML,
+ const char *expectAPI,
+ const char *uri)
+{
+ bool ret = false;
+ char *metadataXML = NULL;
+ char *metadataAPI = NULL;
+
+ if (!expectAPI) {
+ if ((metadataAPI = virDomainGetMetadata(dom,
+ VIR_DOMAIN_METADATA_ELEMENT,
+ uri, 0))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "expected no metadata in API, but got:\n[%s]",
+ metadataAPI);
+ goto cleanup;
+ }
+ } else {
+ if (!(metadataAPI = virDomainGetMetadata(dom,
+ VIR_DOMAIN_METADATA_ELEMENT,
+ uri, 0)))
+ goto cleanup;
+
+ metadataXMLConvertApostrophe(metadataAPI);
+
+ if (STRNEQ(metadataAPI, expectAPI)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "XML metadata in API doesn't match expected metadata: "
+ "expected:\n[%s]\ngot:\n[%s]",
+ expectAPI, metadataAPI);
+ goto cleanup;
+ }
+
+ }
+
+ if (!expectXML) {
+ if ((metadataXML = getMetadataFromXML(dom))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "expected no metadata in XML, but got:\n[%s]",
+ metadataXML);
+ goto cleanup;
+ }
+ } else {
+ if (!(metadataXML = getMetadataFromXML(dom)))
+ goto cleanup;
+
+ metadataXMLConvertApostrophe(metadataXML);
+
+ if (STRNEQ(metadataXML, expectXML)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "XML in dump doesn't match expected metadata: "
+ "expected:\n[%s]\ngot:\n[%s]",
+ expectXML, metadataXML);
+ goto cleanup;
+ }
+ }
+
+ ret = true;
+
+cleanup:
+ VIR_FREE(metadataXML);
+ VIR_FREE(metadataAPI);
+
+ return ret;
+}
+
+
+struct metadataTest {
+ virConnectPtr conn;
+ virDomainPtr dom;
+};
+
+
+static int
+testAssignMetadata(const void *data)
+{
+ const struct metadataTest *test = data;
+
+ if (virDomainSetMetadata(test->dom, VIR_DOMAIN_METADATA_ELEMENT,
+ metadata1, "herp", "http://herp.derp/", 0) < 0)
+ return -1;
+
+ if (!verifyMetadata(test->dom, metadata1_ns, metadata1, "http://herp.derp/"))
+ return -1;
+
+ return 0;
+}
+
+static int
+testRewriteMetadata(const void *data)
+{
+ const struct metadataTest *test = data;
+
+ if (virDomainSetMetadata(test->dom, VIR_DOMAIN_METADATA_ELEMENT,
+ metadata2, "blurb", "http://herp.derp/", 0) < 0)
+ return -1;
+
+ if (!verifyMetadata(test->dom, metadata2_ns, metadata2, "http://herp.derp/"))
+ return -1;
+
+ return 0;
+}
+
+static int
+testEraseMetadata(const void *data)
+{
+ const struct metadataTest *test = data;
+
+ if (virDomainSetMetadata(test->dom, VIR_DOMAIN_METADATA_ELEMENT,
+ NULL, NULL, "http://herp.derp/", 0) < 0)
+ return -1;
+
+ if (!verifyMetadata(test->dom, NULL, NULL, "http://herp.derp/"))
+ return -1;
+
+ return 0;
+}
+
+static int
+mymain(void)
+{
+ struct metadataTest test;
+ int ret = EXIT_SUCCESS;
+
+ if (!(test.conn = virConnectOpen("test:///default")))
+ return EXIT_FAILURE;
+
+ if (!(test.dom = virDomainLookupByName(test.conn, "test"))) {
+ virConnectClose(test.conn);
+ return EXIT_FAILURE;
+ }
+
+ if (virtTestRun("Assign metadata ", 1, testAssignMetadata, &test) < 0)
+ ret = EXIT_FAILURE;
+ if (virtTestRun("Rewrite Metadata ", 1, testRewriteMetadata, &test) < 0)
+ ret = EXIT_FAILURE;
+ if (virtTestRun("Erase metadata ", 1, testEraseMetadata, &test) < 0)
+ ret = EXIT_FAILURE;
+
+ virDomainFree(test.dom);
+ virConnectClose(test.conn);
+
+ return ret;
+}
+
+VIRT_TEST_MAIN(mymain)