diff options
Diffstat (limited to 'src/test/testing_common.c')
-rw-r--r-- | src/test/testing_common.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/test/testing_common.c b/src/test/testing_common.c new file mode 100644 index 0000000000..403c83bdd2 --- /dev/null +++ b/src/test/testing_common.c @@ -0,0 +1,301 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Ordinarily defined in tor_main.c; this bit is just here to provide one + * since we're not linking to tor_main.c */ +const char tor_git_revision[] = ""; + +/** + * \file test_common.c + * \brief Common pieces to implement unit tests. + **/ + +#include "orconfig.h" +#include "or.h" +#include "config.h" +#include "rephist.h" +#include "backtrace.h" +#include "test.h" + +#include <stdio.h> +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef _WIN32 +/* For mkdir() */ +#include <direct.h> +#else +#include <dirent.h> +#endif + +#include "or.h" + +#ifdef USE_DMALLOC +#include <dmalloc.h> +#include <openssl/crypto.h> +#include "main.h" +#endif + +/** Temporary directory (set up by setup_directory) under which we store all + * our files during testing. */ +static char temp_dir[256]; +#ifdef _WIN32 +#define pid_t int +#endif +static pid_t temp_dir_setup_in_pid = 0; + +/** Select and create the temporary directory we'll use to run our unit tests. + * Store it in <b>temp_dir</b>. Exit immediately if we can't create it. + * idempotent. */ +static void +setup_directory(void) +{ + static int is_setup = 0; + int r; + char rnd[256], rnd32[256]; + if (is_setup) return; + +/* Due to base32 limitation needs to be a multiple of 5. */ +#define RAND_PATH_BYTES 5 + crypto_rand(rnd, RAND_PATH_BYTES); + base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES); + +#ifdef _WIN32 + { + char buf[MAX_PATH]; + const char *tmp = buf; + const char *extra_backslash = ""; + /* If this fails, we're probably screwed anyway */ + if (!GetTempPathA(sizeof(buf),buf)) + tmp = "c:\\windows\\temp\\"; + if (strcmpend(tmp, "\\")) { + /* According to MSDN, it should be impossible for GetTempPath to give us + * an answer that doesn't end with \. But let's make sure. */ + extra_backslash = "\\"; + } + tor_snprintf(temp_dir, sizeof(temp_dir), + "%s%stor_test_%d_%s", tmp, extra_backslash, + (int)getpid(), rnd32); + r = mkdir(temp_dir); + } +#else + tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s", + (int) getpid(), rnd32); + r = mkdir(temp_dir, 0700); + if (!r) { + /* undo sticky bit so tests don't get confused. */ + r = chown(temp_dir, getuid(), getgid()); + } +#endif + if (r) { + fprintf(stderr, "Can't create directory %s:", temp_dir); + perror(""); + exit(1); + } + is_setup = 1; + temp_dir_setup_in_pid = getpid(); +} + +/** Return a filename relative to our testing temporary directory */ +const char * +get_fname(const char *name) +{ + static char buf[1024]; + setup_directory(); + if (!name) + return temp_dir; + tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name); + return buf; +} + +/* Remove a directory and all of its subdirectories */ +static void +rm_rf(const char *dir) +{ + struct stat st; + smartlist_t *elements; + + elements = tor_listdir(dir); + if (elements) { + SMARTLIST_FOREACH_BEGIN(elements, const char *, cp) { + char *tmp = NULL; + tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp); + if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) { + rm_rf(tmp); + } else { + if (unlink(tmp)) { + fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno)); + } + } + tor_free(tmp); + } SMARTLIST_FOREACH_END(cp); + SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); + smartlist_free(elements); + } + if (rmdir(dir)) + fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno)); +} + +/** Remove all files stored under the temporary directory, and the directory + * itself. Called by atexit(). */ +static void +remove_directory(void) +{ + if (getpid() != temp_dir_setup_in_pid) { + /* Only clean out the tempdir when the main process is exiting. */ + return; + } + + rm_rf(temp_dir); +} + +/** Define this if unit tests spend too much time generating public keys*/ +#undef CACHE_GENERATED_KEYS + +static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL}; +#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys) + +/** Generate and return a new keypair for use in unit tests. If we're using + * the key cache optimization, we might reuse keys: we only guarantee that + * keys made with distinct values for <b>idx</b> are different. The value of + * <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */ +crypto_pk_t * +pk_generate(int idx) +{ + int res; +#ifdef CACHE_GENERATED_KEYS + tor_assert(idx < N_PREGEN_KEYS); + if (! pregen_keys[idx]) { + pregen_keys[idx] = crypto_pk_new(); + res = crypto_pk_generate_key(pregen_keys[idx]); + tor_assert(!res); + } + return crypto_pk_dup_key(pregen_keys[idx]); +#else + crypto_pk_t *result; + (void) idx; + result = crypto_pk_new(); + res = crypto_pk_generate_key(result); + tor_assert(!res); + return result; +#endif +} + +/** Free all storage used for the cached key optimization. */ +static void +free_pregenerated_keys(void) +{ + unsigned idx; + for (idx = 0; idx < N_PREGEN_KEYS; ++idx) { + if (pregen_keys[idx]) { + crypto_pk_free(pregen_keys[idx]); + pregen_keys[idx] = NULL; + } + } +} + +static void * +passthrough_test_setup(const struct testcase_t *testcase) +{ + return testcase->setup_data; +} +static int +passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + (void)testcase; + (void)ptr; + return 1; +} + +const struct testcase_setup_t passthrough_setup = { + passthrough_test_setup, passthrough_test_cleanup +}; + +extern struct testgroup_t testgroups[]; + +/** Main entry point for unit test code: parse the command line, and run + * some unit tests. */ +int +main(int c, const char **v) +{ + or_options_t *options; + char *errmsg = NULL; + int i, i_out; + int loglevel = LOG_ERR; + int accel_crypto = 0; + +#ifdef USE_DMALLOC + { + int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_); + tor_assert(r); + } +#endif + + update_approx_time(time(NULL)); + options = options_new(); + tor_threads_init(); + init_logging(1); + configure_backtrace_handler(get_version()); + + for (i_out = i = 1; i < c; ++i) { + if (!strcmp(v[i], "--warn")) { + loglevel = LOG_WARN; + } else if (!strcmp(v[i], "--notice")) { + loglevel = LOG_NOTICE; + } else if (!strcmp(v[i], "--info")) { + loglevel = LOG_INFO; + } else if (!strcmp(v[i], "--debug")) { + loglevel = LOG_DEBUG; + } else if (!strcmp(v[i], "--accel")) { + accel_crypto = 1; + } else { + v[i_out++] = v[i]; + } + } + c = i_out; + + { + log_severity_list_t s; + memset(&s, 0, sizeof(s)); + set_log_severity_config(loglevel, LOG_ERR, &s); + add_stream_log(&s, "", fileno(stdout)); + } + + options->command = CMD_RUN_UNITTESTS; + if (crypto_global_init(accel_crypto, NULL, NULL)) { + printf("Can't initialize crypto subsystem; exiting.\n"); + return 1; + } + crypto_set_tls_dh_prime(NULL); + crypto_seed_rng(1); + rep_hist_init(); + network_init(); + setup_directory(); + options_init(options); + options->DataDirectory = tor_strdup(temp_dir); + options->EntryStatistics = 1; + if (set_options(options, &errmsg) < 0) { + printf("Failed to set initial options: %s\n", errmsg); + tor_free(errmsg); + return 1; + } + + atexit(remove_directory); + + int have_failed = (tinytest_main(c, v, testgroups) != 0); + + free_pregenerated_keys(); +#ifdef USE_DMALLOC + tor_free_all(0); + dmalloc_log_unfreed(); +#endif + + if (have_failed) + return 1; + else + return 0; +} + |