summaryrefslogtreecommitdiff
path: root/usr/src/test/os-tests/tests/libtopo/digraph-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/test/os-tests/tests/libtopo/digraph-test.c')
-rw-r--r--usr/src/test/os-tests/tests/libtopo/digraph-test.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/usr/src/test/os-tests/tests/libtopo/digraph-test.c b/usr/src/test/os-tests/tests/libtopo/digraph-test.c
new file mode 100644
index 0000000000..b829f0bb8f
--- /dev/null
+++ b/usr/src/test/os-tests/tests/libtopo/digraph-test.c
@@ -0,0 +1,380 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Joyent, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libnvpair.h>
+#include <string.h>
+#include <stropts.h>
+#include <unistd.h>
+#include <fm/libtopo.h>
+#include <sys/debug.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/varargs.h>
+
+
+#define TEST_HOME "/opt/os-tests/tests/libtopo/"
+#define TEST_XML_IN "digraph-test-in.xml"
+#define TEST_XML_IN_BADSCHEME "digraph-test-in-badscheme.xml"
+#define TEST_XML_IN_BADNUM "digraph-test-in-badnum.xml"
+#define TEST_XML_IN_BADEDGE "digraph-test-in-badedge.xml"
+#define TEST_XML_IN_BADELEMENT "digraph-test-in-badelement.xml"
+#define TEST_GRAPH_SZ 7
+#define TEST_XML_OUT_DIR "/var/tmp"
+#define TEST_XML_OUT_PREFIX "digraph-test-out"
+
+static const char *pname;
+
+extern int topo_hdl_errno(topo_hdl_t *);
+
+/*
+ * Generate an ISO 8601 timestamp
+ */
+static void
+get_timestamp(char *buf, size_t bufsize)
+{
+ time_t utc_time;
+ struct tm *p_tm;
+
+ (void) time(&utc_time);
+ p_tm = localtime(&utc_time);
+
+ (void) strftime(buf, bufsize, "%FT%TZ", p_tm);
+}
+
+/* PRINTFLIKE1 */
+static void
+logmsg(const char *format, ...)
+{
+ char timestamp[128];
+ va_list ap;
+
+ get_timestamp(timestamp, sizeof (timestamp));
+ (void) fprintf(stdout, "%s ", timestamp);
+ va_start(ap, format);
+ (void) vfprintf(stdout, format, ap);
+ va_end(ap);
+ (void) fprintf(stdout, "\n");
+ (void) fflush(stdout);
+}
+
+static topo_digraph_t *
+test_deserialize(topo_hdl_t *thp, const char *path)
+{
+ struct stat statbuf = { 0 };
+ char *buf = NULL;
+ int fd = -1;
+ topo_digraph_t *tdg = NULL;
+
+ logmsg("\tOpening test XML topology");
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ logmsg("\tfailed to open %s (%s)", path, strerror(errno));
+ goto out;
+ }
+ if (fstat(fd, &statbuf) != 0) {
+ logmsg("\tfailed to stat %s (%s)", path, strerror(errno));
+ goto out;
+ }
+ if ((buf = malloc(statbuf.st_size)) == NULL) {
+ logmsg("\tfailed to alloc read buffer: (%s)", strerror(errno));
+ goto out;
+ }
+ if (read(fd, buf, statbuf.st_size) != statbuf.st_size) {
+ logmsg("\tfailed to read file: (%s)", strerror(errno));
+ goto out;
+ }
+
+ logmsg("\tDeserializing XML topology");
+ tdg = topo_digraph_deserialize(thp, buf, statbuf.st_size);
+ if (tdg == NULL) {
+ logmsg("\ttopo_digraph_deserialize() failed!");
+ goto out;
+ }
+ logmsg("\ttopo_digraph_deserialize() succeeded");
+out:
+ free(buf);
+ if (fd > 0) {
+ (void) close(fd);
+ }
+ return (tdg);
+}
+
+struct cb_arg {
+ topo_vertex_t **vertices;
+};
+
+static int
+test_paths_cb(topo_hdl_t *thp, topo_vertex_t *vtx, boolean_t last_vtx,
+ void *arg)
+{
+ struct cb_arg *cbarg = arg;
+ uint_t idx = topo_node_instance(topo_vertex_node(vtx));
+
+ cbarg->vertices[idx] = vtx;
+
+ return (TOPO_WALK_NEXT);
+}
+
+static int
+test_paths(topo_hdl_t *thp, topo_digraph_t *tdg)
+{
+ topo_vertex_t *vertices[TEST_GRAPH_SZ];
+ struct cb_arg cbarg = { 0 };
+ int ret = -1;
+ topo_path_t **paths;
+ uint_t np;
+
+ cbarg.vertices = vertices;
+ if (topo_vertex_iter(thp, tdg, test_paths_cb, &cbarg) != 0) {
+ logmsg("\tfailed to iterate over graph vertices");
+ goto out;
+ }
+
+ logmsg("\tCalculating number of paths between node 0 and node 4");
+ if (topo_digraph_paths(thp, tdg, vertices[0], vertices[4], &paths,
+ &np) < 0) {
+ logmsg("\ttopo_digraph_paths() failed");
+ goto out;
+ }
+ if (np != 2) {
+ logmsg("\t%d paths found (expected 2)", np);
+ goto out;
+ }
+ for (uint_t i = 0; i < np; i++) {
+ topo_path_destroy(thp, paths[i]);
+ }
+ topo_hdl_free(thp, paths, np * sizeof (topo_path_t *));
+
+ logmsg("\tCalculating number of paths between node 6 and node 4");
+ if (topo_digraph_paths(thp, tdg, vertices[6], vertices[4], &paths,
+ &np) < 0) {
+ logmsg("\ttopo_digraph_paths() failed");
+ goto out;
+ }
+ if (np != 1) {
+ logmsg("\t%d paths found (expected 1)", np);
+ goto out;
+ }
+ for (uint_t i = 0; i < np; i++) {
+ topo_path_destroy(thp, paths[i]);
+ }
+ topo_hdl_free(thp, paths, np * sizeof (topo_path_t *));
+
+ logmsg("\tCalculating number of paths between node 5 and node 1");
+ if (topo_digraph_paths(thp, tdg, vertices[5], vertices[1], &paths,
+ &np) < 0) {
+ logmsg("\ttopo_digraph_paths() failed");
+ goto out;
+ }
+ if (np != 0) {
+ logmsg("\t%d paths found (expected 0)", np);
+ goto out;
+ }
+ ret = 0;
+
+out:
+ if (np > 0) {
+ for (uint_t i = 0; i < np; i++) {
+ topo_path_destroy(thp, paths[i]);
+ }
+ topo_hdl_free(thp, paths, np * sizeof (topo_path_t *));
+ }
+ return (ret);
+}
+
+static int
+test_serialize(topo_hdl_t *thp, topo_digraph_t *tdg, const char *path)
+{
+ FILE *xml_out;
+
+ if ((xml_out = fopen(path, "w")) == NULL) {
+ logmsg("\tfailed to open %s for writing (%s)",
+ strerror(errno));
+ return (-1);
+ }
+ logmsg("\tSerializing topology to XML (%s)", path);
+ if (topo_digraph_serialize(thp, tdg, xml_out) != 0) {
+ logmsg("\ttopo_digraph_serialize() failed!");
+ (void) fclose(xml_out);
+ return (-1);
+ }
+ (void) fclose(xml_out);
+ return (0);
+}
+
+int
+main(int argc, char **argv)
+{
+ topo_hdl_t *thp = NULL;
+ topo_digraph_t *tdg;
+ char *root = "/", *out_path = NULL;
+ boolean_t abort_on_exit = B_FALSE;
+ int err, status = EXIT_FAILURE;
+
+ pname = argv[0];
+
+ /*
+ * Setting DIGRAPH_TEST_CORE causes us to abort and dump core before
+ * exiting. This is useful for examining for memory leaks.
+ */
+ if (getenv("DIGRAPH_TEST_CORE") != NULL) {
+ abort_on_exit = B_TRUE;
+ }
+
+ logmsg("Opening libtopo");
+ if ((thp = topo_open(TOPO_VERSION, root, &err)) == NULL) {
+ logmsg("failed to get topo handle: %s", topo_strerror(err));
+ goto out;
+ }
+
+ logmsg("TEST: Deserialize directed graph topology");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN)) == NULL) {
+ logmsg("FAIL");
+ goto out;
+ }
+ logmsg("PASS");
+
+ logmsg("TEST: Serialize directed graph topology");
+ if ((out_path = tempnam(TEST_XML_OUT_DIR, TEST_XML_OUT_PREFIX)) ==
+ NULL) {
+ logmsg("\tFailed to create temporary file name under %s (%s)",
+ TEST_XML_OUT_DIR, strerror(errno));
+ logmsg("FAIL");
+ goto out;
+ }
+ if (test_serialize(thp, tdg, out_path) != 0) {
+ logmsg("FAIL");
+ goto out;
+ }
+ logmsg("PASS");
+
+ logmsg("Closing libtopo");
+ topo_close(thp);
+
+ logmsg("Reopening libtopo");
+ if ((thp = topo_open(TOPO_VERSION, root, &err)) == NULL) {
+ logmsg("failed to get topo handle: %s", topo_strerror(err));
+ goto out;
+ }
+
+ logmsg("TEST: Deserialize directed graph topology (pass 2)");
+ if ((tdg = test_deserialize(thp, out_path)) == NULL) {
+ logmsg("FAIL");
+ goto out;
+ }
+ logmsg("PASS");
+
+ logmsg("TEST: Calculating paths between vertices");
+ if (test_paths(thp, tdg) != 0) {
+ logmsg("FAIL");
+ goto out;
+ }
+ logmsg("PASS");
+
+ logmsg("Closing libtopo");
+ topo_close(thp);
+
+ logmsg("Reopening libtopo");
+ if ((thp = topo_open(TOPO_VERSION, root, &err)) == NULL) {
+ logmsg("failed to get topo handle: %s", topo_strerror(err));
+ goto out;
+ }
+
+ /*
+ * The following tests attempt to deserialize XML files that either
+ * violate the DTD or contain invalid attribute values.
+ *
+ * The expection is that topo_digraph_deserialize() should fail
+ * gracefully (i.e. not segfault) and topo_errno should be set.
+ */
+ logmsg("TEST: Deserialize directed graph topology (bad scheme)");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN_BADSCHEME)) !=
+ NULL) {
+ logmsg("FAIL");
+ goto out;
+ } else if (topo_hdl_errno(thp) == 0) {
+ logmsg("\texpected topo_errno to be non-zero");
+ logmsg("FAIL");
+ goto out;
+ } else {
+ logmsg("PASS");
+ }
+
+ logmsg("TEST: Deserialize directed graph topology (bad number)");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN_BADNUM)) !=
+ NULL) {
+ logmsg("FAIL");
+ goto out;
+ } else if (topo_hdl_errno(thp) == 0) {
+ logmsg("\texpected topo_errno to be non-zero");
+ logmsg("FAIL");
+ goto out;
+ } else {
+ logmsg("PASS");
+ }
+
+ logmsg("TEST: Deserialize directed graph topology (bad edge)");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN_BADEDGE)) !=
+ NULL) {
+ logmsg("FAIL");
+ goto out;
+ } else if (topo_hdl_errno(thp) == 0) {
+ logmsg("\texpected topo_errno to be non-zero");
+ logmsg("FAIL");
+ goto out;
+ } else {
+ logmsg("PASS");
+ }
+
+ logmsg("TEST: Deserialize directed graph topology (bad element)");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN_BADELEMENT)) !=
+ NULL) {
+ logmsg("FAIL");
+ goto out;
+ } else if (topo_hdl_errno(thp) == 0) {
+ logmsg("\texpected topo_errno to be non-zero");
+ logmsg("FAIL");
+ goto out;
+ } else {
+ logmsg("PASS");
+ }
+
+ /*
+ * If any tests failed, we don't unlink the temp file, as its contents
+ * may be useful for root-causing the test failure.
+ */
+ if (unlink(out_path) != 0) {
+ logmsg("Failed to unlink temp file: %s (%s)", out_path,
+ strerror(errno));
+ }
+ status = EXIT_SUCCESS;
+out:
+ if (thp != NULL) {
+ topo_close(thp);
+ }
+ if (out_path != NULL) {
+ free(out_path);
+ }
+ logmsg("digraph tests %s",
+ status == EXIT_SUCCESS ? "passed" : "failed");
+
+ if (abort_on_exit) {
+ abort();
+ }
+ return (status);
+}