summaryrefslogtreecommitdiff
path: root/test/libtest/driver/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/libtest/driver/driver.c')
-rw-r--r--test/libtest/driver/driver.c216
1 files changed, 216 insertions, 0 deletions
diff --git a/test/libtest/driver/driver.c b/test/libtest/driver/driver.c
new file mode 100644
index 000000000000..aa85c7cb4ddf
--- /dev/null
+++ b/test/libtest/driver/driver.c
@@ -0,0 +1,216 @@
+/*-
+ * Copyright (c) 2018, Joseph Koshy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The implementation of the test driver.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "driver.h"
+
+#if defined(ELFTC_VCSID)
+ELFTC_VCSID("$Id$");
+#endif
+
+#define SYSTEM_TMPDIR_ENV_VAR "TMPDIR"
+
+bool
+test_driver_add_search_path(struct test_run *tr, const char *directory_name)
+{
+ char *canonical_path;
+ struct test_search_path_entry *entry;
+
+ if (!test_driver_is_directory(directory_name))
+ return (false);
+
+ if ((canonical_path = realpath(directory_name, NULL)) == NULL)
+ err(1, "Cannot determine the canonical path for \"%s\"",
+ directory_name);
+
+ /* Look for, and ignore duplicates. */
+ STAILQ_FOREACH(entry, &tr->tr_search_path, tsp_next) {
+ if (strcmp(canonical_path, entry->tsp_directory) == 0)
+ return (true);
+ }
+
+ entry = calloc(1, sizeof(*entry));
+ entry->tsp_directory = canonical_path;
+
+ STAILQ_INSERT_TAIL(&tr->tr_search_path, entry, tsp_next);
+
+ return (true);
+}
+
+/*
+ * Return an initialized test run descriptor.
+ *
+ * The caller should use test_driver_free_run() to release the returned
+ * descriptor.
+ */
+struct test_run *
+test_driver_allocate_run(void)
+{
+ struct test_run *tr;
+
+ tr = calloc(sizeof(struct test_run), 1);
+ tr->tr_action = TEST_RUN_EXECUTE;
+ tr->tr_style = TR_STYLE_LIBTEST;
+ STAILQ_INIT(&tr->tr_test_cases);
+ STAILQ_INIT(&tr->tr_search_path);
+
+ return (tr);
+}
+
+/*
+ * Destroy an allocated test run descriptor.
+ *
+ * The passed in pointer should not be used after this function returns.
+ */
+void
+test_driver_free_run(struct test_run *tr)
+{
+ struct test_search_path_entry *path_entry;
+ struct test_case_selector *test_case_entry;
+ struct test_function_selector *function_entry;
+
+ free(tr->tr_runtime_base_directory);
+ free(tr->tr_name);
+ if (tr->tr_artefact_archive)
+ free(tr->tr_artefact_archive);
+
+ /* Free the search path list. */
+ while (!STAILQ_EMPTY(&tr->tr_search_path)) {
+ path_entry = STAILQ_FIRST(&tr->tr_search_path);
+ STAILQ_REMOVE_HEAD(&tr->tr_search_path, tsp_next);
+ free(path_entry);
+ }
+
+ /* Free the test selector list. */
+ while (!STAILQ_EMPTY(&tr->tr_test_cases)) {
+ test_case_entry = STAILQ_FIRST(&tr->tr_test_cases);
+ STAILQ_REMOVE_HEAD(&tr->tr_test_cases, tcs_next);
+
+ /* Free the linked test functions. */
+ while (!STAILQ_EMPTY(&test_case_entry->tcs_functions)) {
+ function_entry =
+ STAILQ_FIRST(&test_case_entry->tcs_functions);
+ STAILQ_REMOVE_HEAD(&test_case_entry->tcs_functions,
+ tfs_next);
+
+ free(function_entry);
+ }
+
+ free(test_case_entry);
+ }
+
+ free(tr);
+}
+
+/*
+ * Populate unset fields of a struct test_run with defaults.
+ */
+bool
+test_driver_finish_run_initialization(struct test_run *tr, const char *argv0)
+{
+ struct timeval tv;
+ const char *basedir;
+ const char *search_path;
+ const char *last_component;
+ char *argv0_copy, *path_copy, *path_element;
+ char test_name[NAME_MAX];
+
+ if (tr->tr_name == NULL) {
+ /* Per POSIX, basename(3) can modify its argument. */
+ argv0_copy = strdup(argv0);
+ last_component = basename(argv0_copy);
+
+ if (gettimeofday(&tv, NULL))
+ return (false);
+
+ (void) snprintf(test_name, sizeof(test_name), "%s+%ld%ld",
+ last_component, (long) tv.tv_sec, (long) tv.tv_usec);
+
+ tr->tr_name = strdup(test_name);
+
+ free(argv0_copy);
+ }
+
+ /*
+ * Select a base directory, if one was not specified.
+ */
+ if (tr->tr_runtime_base_directory == NULL) {
+ basedir = getenv(TEST_TMPDIR_ENV_VAR);
+ if (basedir == NULL)
+ basedir = getenv(SYSTEM_TMPDIR_ENV_VAR);
+ if (basedir == NULL)
+ basedir = "/tmp";
+ tr->tr_runtime_base_directory = realpath(basedir, NULL);
+ if (tr->tr_runtime_base_directory == NULL)
+ err(1, "realpath(%s) failed", basedir);
+ }
+
+ /*
+ * Add the search paths specified by the environment variable
+ * 'TEST_PATH' to the end of the search list.
+ */
+ if ((search_path = getenv(TEST_SEARCH_PATH_ENV_VAR)) != NULL &&
+ *search_path != '\0') {
+ path_copy = strdup(search_path);
+ path_element = strtok(path_copy, ":");
+ do {
+ if (!test_driver_add_search_path(tr, path_element))
+ warnx("in environment variable \"%s\": path "
+ "\"%s\" does not name a directory.",
+ TEST_SEARCH_PATH_ENV_VAR, path_element);
+ } while ((path_element = strtok(NULL, ":")) != NULL);
+ }
+
+ return (true);
+}
+
+/*
+ * Helper: return true if the passed in path names a directory, or false
+ * otherwise.
+ */
+bool
+test_driver_is_directory(const char *path)
+{
+ struct stat sb;
+ if (stat(path, &sb) != 0)
+ return false;
+ return S_ISDIR(sb.st_mode);
+}