/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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); }