[tor-commits] [tor/master] Split the slow unit tests into their own binary

nickm at torproject.org nickm at torproject.org
Fri Jan 23 16:18:36 UTC 2015


commit 7322de15dc2f0858291f40df1a55a66c80b7fda9
Author: Nick Mathewson <nickm at torproject.org>
Date:   Fri Jan 23 11:15:53 2015 -0500

    Split the slow unit tests into their own binary
    
    This can run in parallel with the faster ones and the other tests.
---
 .gitignore                  |    2 +
 changes/ticket13243         |    3 +
 src/test/include.am         |   22 ++-
 src/test/test.c             |  271 +---------------------------
 src/test/test_crypto.c      |  393 -----------------------------------------
 src/test/test_crypto_slow.c |  410 +++++++++++++++++++++++++++++++++++++++++++
 src/test/test_slow.c        |   29 +++
 src/test/test_util.c        |  369 --------------------------------------
 src/test/test_util_slow.c   |  388 ++++++++++++++++++++++++++++++++++++++++
 src/test/testing_common.c   |  298 +++++++++++++++++++++++++++++++
 10 files changed, 1151 insertions(+), 1034 deletions(-)

diff --git a/.gitignore b/.gitignore
index e63576c..0cdf2c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -160,11 +160,13 @@ cscope.*
 /src/test/bench
 /src/test/bench.exe
 /src/test/test
+/src/test/test-slow
 /src/test/test-bt-cl
 /src/test/test-child
 /src/test/test-ntor-cl
 /src/test/test_workqueue
 /src/test/test.exe
+/src/test/test-slow.exe
 /src/test/test-bt-cl.exe
 /src/test/test-child.exe
 /src/test/test-ntor-cl.exe
diff --git a/changes/ticket13243 b/changes/ticket13243
new file mode 100644
index 0000000..ad6e4de
--- /dev/null
+++ b/changes/ticket13243
@@ -0,0 +1,3 @@
+  o Testing:
+    - Move the slower unit tests into a new "./src/test/test-slow" binary
+      that can be run independently of the other tests. Closes ticket 13243.
diff --git a/src/test/include.am b/src/test/include.am
index d206033..595be0b 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -1,8 +1,12 @@
-TESTS += src/test/test
+TESTS += src/test/test src/test/test-slow
 
 noinst_PROGRAMS+= src/test/bench
 if UNITTESTS_ENABLED
-noinst_PROGRAMS+= src/test/test src/test/test-child src/test/test_workqueue
+noinst_PROGRAMS+= \
+	src/test/test \
+	src/test/test-slow \
+	src/test/test-child \
+	src/test/test_workqueue
 endif
 
 src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
@@ -57,8 +61,17 @@ src_test_test_SOURCES = \
 	src/test/test_status.c \
 	src/test/test_threads.c \
 	src/test/test_util.c \
+	src/test/testing_common.c \
 	src/ext/tinytest.c
 
+src_test_test_slow_SOURCES = \
+	src/test/test_slow.c \
+	src/test/test_crypto_slow.c \
+	src/test/test_util_slow.c \
+	src/test/testing_common.c \
+	src/ext/tinytest.c
+
+
 src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
 
 src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS)
@@ -80,6 +93,11 @@ src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
 	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
 	@TOR_SYSTEMD_LIBS@
 
+src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS)
+src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS)
+src_test_test_slow_LDADD = $(src_test_test_LDADD)
+src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS)
+
 src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
         @TOR_LDFLAGS_libevent@
 src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \
diff --git a/src/test/test.c b/src/test/test.c
index d752336..ff4f517 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -3,10 +3,6 @@
  * 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.c
  * \brief Unit tests for many pieces of the lower level Tor modules.
@@ -67,171 +63,6 @@ double fabs(double x);
 #include "crypto_curve25519.h"
 #include "onion_ntor.h"
 
-#ifdef USE_DMALLOC
-#include <dmalloc.h>
-#include <openssl/crypto.h>
-#include "main.h"
-#endif
-
-/** Set to true if any unit test has failed.  Mostly, this is set by the macros
- * in test.h */
-int have_failed = 0;
-
-/** 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)
-{
-#ifdef CACHE_GENERATED_KEYS
-  tor_assert(idx < N_PREGEN_KEYS);
-  if (! pregen_keys[idx]) {
-    pregen_keys[idx] = crypto_pk_new();
-    tor_assert(!crypto_pk_generate_key(pregen_keys[idx]));
-  }
-  return crypto_pk_dup_key(pregen_keys[idx]);
-#else
-  crypto_pk_t *result;
-  (void) idx;
-  result = crypto_pk_new();
-  tor_assert(!crypto_pk_generate_key(result));
-  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;
-    }
-  }
-}
-
 /** Run unit tests for the onion handshake code. */
 static void
 test_onion_handshake(void *arg)
@@ -1258,23 +1089,6 @@ test_stats(void *arg)
   tor_free(s);
 }
 
-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
-};
-
 #define ENT(name)                                                       \
   { #name, test_ ## name , 0, NULL, NULL }
 #define FORK(name)                                                      \
@@ -1335,7 +1149,7 @@ extern struct testcase_t status_tests[];
 extern struct testcase_t thread_tests[];
 extern struct testcase_t util_tests[];
 
-static struct testgroup_t testgroups[] = {
+struct testgroup_t testgroups[] = {
   { "", test_array },
   { "accounting/", accounting_tests },
   { "addr/", addr_tests },
@@ -1379,86 +1193,3 @@ static struct testgroup_t testgroups[] = {
   END_OF_GROUPS
 };
 
-/** 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);
-
-  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;
-}
-
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 8426c71..e9fb8bf 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -5,7 +5,6 @@
 
 #include "orconfig.h"
 #define CRYPTO_CURVE25519_PRIVATE
-#define CRYPTO_S2K_PRIVATE
 #include "or.h"
 #include "test.h"
 #include "aes.h"
@@ -14,8 +13,6 @@
 #include "crypto_curve25519.h"
 #include "crypto_ed25519.h"
 #include "ed25519_vectors.inc"
-#include "crypto_s2k.h"
-#include "crypto_pwbox.h"
 
 extern const char AUTHORITY_SIGNKEY_3[];
 extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
@@ -712,379 +709,6 @@ test_crypto_formats(void *arg)
   tor_free(data3);
 }
 
-/** Run unit tests for our secret-to-key passphrase hashing functionality. */
-static void
-test_crypto_s2k_rfc2440(void *arg)
-{
-  char buf[29];
-  char buf2[29];
-  char *buf3 = NULL;
-  int i;
-
-  (void)arg;
-  memset(buf, 0, sizeof(buf));
-  memset(buf2, 0, sizeof(buf2));
-  buf3 = tor_malloc(65536);
-  memset(buf3, 0, 65536);
-
-  secret_to_key_rfc2440(buf+9, 20, "", 0, buf);
-  crypto_digest(buf2+9, buf3, 1024);
-  tt_mem_op(buf,OP_EQ, buf2, 29);
-
-  memcpy(buf,"vrbacrda",8);
-  memcpy(buf2,"vrbacrda",8);
-  buf[8] = 96;
-  buf2[8] = 96;
-  secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf);
-  for (i = 0; i < 65536; i += 16) {
-    memcpy(buf3+i, "vrbacrda12345678", 16);
-  }
-  crypto_digest(buf2+9, buf3, 65536);
-  tt_mem_op(buf,OP_EQ, buf2, 29);
-
- done:
-  tor_free(buf3);
-}
-
-static void
-run_s2k_tests(const unsigned flags, const unsigned type,
-              int speclen, const int keylen, int legacy)
-{
-  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN];
-  int r;
-  size_t sz;
-  const char pw1[] = "You can't come in here unless you say swordfish!";
-  const char pw2[] = "Now, I give you one more guess.";
-
-  r = secret_to_key_new(buf, sizeof(buf), &sz,
-                        pw1, strlen(pw1), flags);
-  tt_int_op(r, OP_EQ, S2K_OKAY);
-  tt_int_op(buf[0], OP_EQ, type);
-
-  tt_int_op(sz, OP_EQ, keylen + speclen);
-
-  if (legacy) {
-    memmove(buf, buf+1, sz-1);
-    --sz;
-    --speclen;
-  }
-
-  tt_int_op(S2K_OKAY, OP_EQ,
-            secret_to_key_check(buf, sz, pw1, strlen(pw1)));
-
-  tt_int_op(S2K_BAD_SECRET, OP_EQ,
-            secret_to_key_check(buf, sz, pw2, strlen(pw2)));
-
-  /* Move key to buf2, and clear it. */
-  memset(buf3, 0, sizeof(buf3));
-  memcpy(buf2, buf+speclen, keylen);
-  memset(buf+speclen, 0, sz - speclen);
-
-  /* Derivekey should produce the same results. */
-  tt_int_op(S2K_OKAY, OP_EQ,
-      secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));
-
-  tt_mem_op(buf2, OP_EQ, buf3, keylen);
-
-  /* Derivekey with a longer output should fill the output. */
-  memset(buf2, 0, sizeof(buf2));
-  tt_int_op(S2K_OKAY, OP_EQ,
-   secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
-                           pw1, strlen(pw1)));
-
-  tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2));
-
-  memset(buf3, 0, sizeof(buf3));
-  tt_int_op(S2K_OKAY, OP_EQ,
-            secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
-                                    pw1, strlen(pw1)));
-  tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
-  tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
-
- done:
-  ;
-}
-
-static void
-test_crypto_s2k_general(void *arg)
-{
-  const char *which = arg;
-
-  if (!strcmp(which, "scrypt")) {
-    run_s2k_tests(0, 2, 19, 32, 0);
-  } else if (!strcmp(which, "scrypt-low")) {
-    run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0);
-  } else if (!strcmp(which, "pbkdf2")) {
-    run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0);
-  } else if (!strcmp(which, "rfc2440")) {
-    run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0);
-  } else if (!strcmp(which, "rfc2440-legacy")) {
-    run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1);
-  } else {
-    tt_fail();
-  }
-}
-
-static void
-test_crypto_s2k_errors(void *arg)
-{
-  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN];
-  size_t sz;
-
-  (void)arg;
-
-  /* Bogus specifiers: simple */
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_derivekey(buf, sizeof(buf),
-                                    (const uint8_t*)"", 0, "ABC", 3));
-  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
-            secret_to_key_derivekey(buf, sizeof(buf),
-                                    (const uint8_t*)"\x10", 1, "ABC", 3));
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_derivekey(buf, sizeof(buf),
-                                    (const uint8_t*)"\x01\x02", 2, "ABC", 3));
-
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
-  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
-            secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3));
-
-  /* too long gets "BAD_LEN" too */
-  memset(buf, 0, sizeof(buf));
-  buf[0] = 2;
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_derivekey(buf2, sizeof(buf2),
-                                    buf, sizeof(buf), "ABC", 3));
-
-  /* Truncated output */
-#ifdef HAVE_LIBSCRYPT_H
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
-                                                 "ABC", 3, 0));
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
-                                                 "ABC", 3, S2K_FLAG_LOW_MEM));
-#endif
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz,
-                                              "ABC", 3, S2K_FLAG_USE_PBKDF2));
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz,
-                                              "ABC", 3, S2K_FLAG_NO_SCRYPT));
-
-#ifdef HAVE_LIBSCRYPT_H
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0));
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18,
-                                                 S2K_FLAG_LOW_MEM));
-#endif
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17,
-                                                 S2K_FLAG_USE_PBKDF2));
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9,
-                                                 S2K_FLAG_NO_SCRYPT));
-
-  /* Now try using type-specific bogus specifiers. */
-
-  /* It's a bad pbkdf2 buffer if it has an iteration count that would overflow
-   * int32_t. */
-  memset(buf, 0, sizeof(buf));
-  buf[0] = 1; /* pbkdf2 */
-  buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */
-  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
-            secret_to_key_derivekey(buf2, sizeof(buf2),
-                                    buf, 18, "ABC", 3));
-
-#ifdef HAVE_LIBSCRYPT_H
-  /* It's a bad scrypt buffer if N would overflow uint64 */
-  memset(buf, 0, sizeof(buf));
-  buf[0] = 2; /* scrypt */
-  buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */
-  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
-            secret_to_key_derivekey(buf2, sizeof(buf2),
-                                    buf, 19, "ABC", 3));
-#endif
-
- done:
-  ;
-}
-
-static void
-test_crypto_scrypt_vectors(void *arg)
-{
-  char *mem_op_hex_tmp = NULL;
-  uint8_t spec[64], out[64];
-
-  (void)arg;
-#ifndef HAVE_LIBSCRYPT_H
-  if (1)
-    tt_skip();
-#endif
-
-  /* Test vectors from
-     http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11.
-
-     Note that the names of 'r' and 'N' are switched in that section. Or
-     possibly in libscrypt.
-  */
-
-  base16_decode((char*)spec, sizeof(spec),
-                "0400", 4);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(64, OP_EQ,
-            secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2));
-  test_memeq_hex(out,
-                 "77d6576238657b203b19ca42c18a0497"
-                 "f16b4844e3074ae8dfdffa3fede21442"
-                 "fcd0069ded0948f8326a753a0fc81f17"
-                 "e8d3e0fb2e0d3628cf35e20c38d18906");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "4e61436c" "0A34", 12);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(64, OP_EQ,
-            secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2));
-  test_memeq_hex(out,
-                 "fdbabe1c9d3472007856e7190d01e9fe"
-                 "7c6ad7cbc8237830e77376634b373162"
-                 "2eaf30d92e22a3886ff109279d9830da"
-                 "c727afb94a83ee6d8360cbdfa2cc0640");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "536f6469756d43686c6f72696465" "0e30", 32);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(64, OP_EQ,
-            secret_to_key_compute_key(out, 64, spec, 16,
-                                      "pleaseletmein", 13, 2));
-  test_memeq_hex(out,
-                 "7023bdcb3afd7348461c06cd81fd38eb"
-                 "fda8fbba904f8e3ea9b543f6545da1f2"
-                 "d5432955613f0fcf62d49705242a9af9"
-                 "e61e85dc0d651e40dfcf017b45575887");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "536f6469756d43686c6f72696465" "1430", 32);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(64, OP_EQ,
-            secret_to_key_compute_key(out, 64, spec, 16,
-                                      "pleaseletmein", 13, 2));
-  test_memeq_hex(out,
-                 "2101cb9b6a511aaeaddbbe09cf70f881"
-                 "ec568d574a2ffd4dabe5ee9820adaa47"
-                 "8e56fd8f4ba5d09ffa1c6d927c40f4c3"
-                 "37304049e8a952fbcbf45c6fa77a41a4");
-
- done:
-  tor_free(mem_op_hex_tmp);
-}
-
-static void
-test_crypto_pbkdf2_vectors(void *arg)
-{
-  char *mem_op_hex_tmp = NULL;
-  uint8_t spec[64], out[64];
-  (void)arg;
-
-  /* Test vectors from RFC6070, section 2 */
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c74" "00" , 10);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(20, OP_EQ,
-            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
-  test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c74" "01" , 10);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(20, OP_EQ,
-            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
-  test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c74" "0C" , 10);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(20, OP_EQ,
-            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
-  test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c74" "18" , 10);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(20, OP_EQ,
-            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
-  test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c7453414c5473616c7453414c5473616c745"
-                "3414c5473616c7453414c5473616c74" "0C" , 74);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(25, OP_EQ,
-            secret_to_key_compute_key(out, 25, spec, 37,
-                                      "passwordPASSWORDpassword", 24, 1));
-  test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "7361006c74" "0c" , 12);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(16, OP_EQ,
-            secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1));
-  test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3");
-
- done:
-  tor_free(mem_op_hex_tmp);
-}
-
-static void
-test_crypto_pwbox(void *arg)
-{
-  uint8_t *boxed=NULL, *decoded=NULL;
-  size_t len, dlen;
-  unsigned i;
-  const char msg[] = "This bunny reminds you that you still have a "
-    "salamander in your sylladex. She is holding the bunny Dave got you. "
-    "It’s sort of uncanny how similar they are, aside from the knitted "
-    "enhancements. Seriously, what are the odds?? So weird.";
-  const char pw[] = "I'm a night owl and a wise bird too";
-
-  const unsigned flags[] = { 0,
-                             S2K_FLAG_NO_SCRYPT,
-                             S2K_FLAG_LOW_MEM,
-                             S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM,
-                             S2K_FLAG_USE_PBKDF2 };
-  (void)arg;
-
-  for (i = 0; i < ARRAY_LENGTH(flags); ++i) {
-    tt_int_op(0, OP_EQ, crypto_pwbox(&boxed, &len,
-                                  (const uint8_t*)msg, strlen(msg),
-                                  pw, strlen(pw), flags[i]));
-    tt_assert(boxed);
-    tt_assert(len > 128+32);
-
-    tt_int_op(0, OP_EQ, crypto_unpwbox(&decoded, &dlen, boxed, len,
-                                    pw, strlen(pw)));
-
-    tt_assert(decoded);
-    tt_uint_op(dlen, OP_EQ, strlen(msg));
-    tt_mem_op(decoded, OP_EQ, msg, dlen);
-
-    tor_free(decoded);
-
-    tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
-                                                     boxed, len,
-                                                     pw, strlen(pw)-1));
-    boxed[len-1] ^= 1;
-    tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
-                                                     boxed, len,
-                                                     pw, strlen(pw)));
-    boxed[0] = 255;
-    tt_int_op(UNPWBOX_CORRUPTED, OP_EQ, crypto_unpwbox(&decoded, &dlen,
-                                                    boxed, len,
-                                                    pw, strlen(pw)));
-
-    tor_free(boxed);
-  }
-
- done:
-  tor_free(boxed);
-  tor_free(decoded);
-}
-
 /** Test AES-CTR encryption and decryption with IV. */
 static void
 test_crypto_aes_iv(void *arg)
@@ -1988,23 +1612,6 @@ struct testcase_t crypto_tests[] = {
   { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
   CRYPTO_LEGACY(digests),
   CRYPTO_LEGACY(dh),
-  CRYPTO_LEGACY(s2k_rfc2440),
-#ifdef HAVE_LIBSCRYPT_H
-  { "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"scrypt" },
-  { "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"scrypt-low" },
-#endif
-  { "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"pbkdf2" },
-  { "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"rfc2440" },
-  { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"rfc2440-legacy" },
-  { "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL },
-  { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL },
-  { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
-  { "pwbox", test_crypto_pwbox, 0, NULL, NULL },
   { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
     (void*)"aes" },
   { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c
new file mode 100644
index 0000000..94fd69d
--- /dev/null
+++ b/src/test/test_crypto_slow.c
@@ -0,0 +1,410 @@
+/* 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 */
+
+#include "orconfig.h"
+#define CRYPTO_S2K_PRIVATE
+#include "or.h"
+#include "test.h"
+#include "crypto_s2k.h"
+#include "crypto_pwbox.h"
+
+
+/** Run unit tests for our secret-to-key passphrase hashing functionality. */
+static void
+test_crypto_s2k_rfc2440(void *arg)
+{
+  char buf[29];
+  char buf2[29];
+  char *buf3 = NULL;
+  int i;
+
+  (void)arg;
+  memset(buf, 0, sizeof(buf));
+  memset(buf2, 0, sizeof(buf2));
+  buf3 = tor_malloc(65536);
+  memset(buf3, 0, 65536);
+
+  secret_to_key_rfc2440(buf+9, 20, "", 0, buf);
+  crypto_digest(buf2+9, buf3, 1024);
+  tt_mem_op(buf,OP_EQ, buf2, 29);
+
+  memcpy(buf,"vrbacrda",8);
+  memcpy(buf2,"vrbacrda",8);
+  buf[8] = 96;
+  buf2[8] = 96;
+  secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf);
+  for (i = 0; i < 65536; i += 16) {
+    memcpy(buf3+i, "vrbacrda12345678", 16);
+  }
+  crypto_digest(buf2+9, buf3, 65536);
+  tt_mem_op(buf,OP_EQ, buf2, 29);
+
+ done:
+  tor_free(buf3);
+}
+
+static void
+run_s2k_tests(const unsigned flags, const unsigned type,
+              int speclen, const int keylen, int legacy)
+{
+  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN];
+  int r;
+  size_t sz;
+  const char pw1[] = "You can't come in here unless you say swordfish!";
+  const char pw2[] = "Now, I give you one more guess.";
+
+  r = secret_to_key_new(buf, sizeof(buf), &sz,
+                        pw1, strlen(pw1), flags);
+  tt_int_op(r, OP_EQ, S2K_OKAY);
+  tt_int_op(buf[0], OP_EQ, type);
+
+  tt_int_op(sz, OP_EQ, keylen + speclen);
+
+  if (legacy) {
+    memmove(buf, buf+1, sz-1);
+    --sz;
+    --speclen;
+  }
+
+  tt_int_op(S2K_OKAY, OP_EQ,
+            secret_to_key_check(buf, sz, pw1, strlen(pw1)));
+
+  tt_int_op(S2K_BAD_SECRET, OP_EQ,
+            secret_to_key_check(buf, sz, pw2, strlen(pw2)));
+
+  /* Move key to buf2, and clear it. */
+  memset(buf3, 0, sizeof(buf3));
+  memcpy(buf2, buf+speclen, keylen);
+  memset(buf+speclen, 0, sz - speclen);
+
+  /* Derivekey should produce the same results. */
+  tt_int_op(S2K_OKAY, OP_EQ,
+      secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));
+
+  tt_mem_op(buf2, OP_EQ, buf3, keylen);
+
+  /* Derivekey with a longer output should fill the output. */
+  memset(buf2, 0, sizeof(buf2));
+  tt_int_op(S2K_OKAY, OP_EQ,
+   secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
+                           pw1, strlen(pw1)));
+
+  tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2));
+
+  memset(buf3, 0, sizeof(buf3));
+  tt_int_op(S2K_OKAY, OP_EQ,
+            secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
+                                    pw1, strlen(pw1)));
+  tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
+  tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
+
+ done:
+  ;
+}
+
+static void
+test_crypto_s2k_general(void *arg)
+{
+  const char *which = arg;
+
+  if (!strcmp(which, "scrypt")) {
+    run_s2k_tests(0, 2, 19, 32, 0);
+  } else if (!strcmp(which, "scrypt-low")) {
+    run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0);
+  } else if (!strcmp(which, "pbkdf2")) {
+    run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0);
+  } else if (!strcmp(which, "rfc2440")) {
+    run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0);
+  } else if (!strcmp(which, "rfc2440-legacy")) {
+    run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1);
+  } else {
+    tt_fail();
+  }
+}
+
+static void
+test_crypto_s2k_errors(void *arg)
+{
+  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN];
+  size_t sz;
+
+  (void)arg;
+
+  /* Bogus specifiers: simple */
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"", 0, "ABC", 3));
+  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"\x10", 1, "ABC", 3));
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"\x01\x02", 2, "ABC", 3));
+
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
+  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
+            secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3));
+
+  /* too long gets "BAD_LEN" too */
+  memset(buf, 0, sizeof(buf));
+  buf[0] = 2;
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_derivekey(buf2, sizeof(buf2),
+                                    buf, sizeof(buf), "ABC", 3));
+
+  /* Truncated output */
+#ifdef HAVE_LIBSCRYPT_H
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
+                                                 "ABC", 3, 0));
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
+                                                 "ABC", 3, S2K_FLAG_LOW_MEM));
+#endif
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz,
+                                              "ABC", 3, S2K_FLAG_USE_PBKDF2));
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz,
+                                              "ABC", 3, S2K_FLAG_NO_SCRYPT));
+
+#ifdef HAVE_LIBSCRYPT_H
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0));
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18,
+                                                 S2K_FLAG_LOW_MEM));
+#endif
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17,
+                                                 S2K_FLAG_USE_PBKDF2));
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9,
+                                                 S2K_FLAG_NO_SCRYPT));
+
+  /* Now try using type-specific bogus specifiers. */
+
+  /* It's a bad pbkdf2 buffer if it has an iteration count that would overflow
+   * int32_t. */
+  memset(buf, 0, sizeof(buf));
+  buf[0] = 1; /* pbkdf2 */
+  buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */
+  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
+            secret_to_key_derivekey(buf2, sizeof(buf2),
+                                    buf, 18, "ABC", 3));
+
+#ifdef HAVE_LIBSCRYPT_H
+  /* It's a bad scrypt buffer if N would overflow uint64 */
+  memset(buf, 0, sizeof(buf));
+  buf[0] = 2; /* scrypt */
+  buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */
+  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
+            secret_to_key_derivekey(buf2, sizeof(buf2),
+                                    buf, 19, "ABC", 3));
+#endif
+
+ done:
+  ;
+}
+
+static void
+test_crypto_scrypt_vectors(void *arg)
+{
+  char *mem_op_hex_tmp = NULL;
+  uint8_t spec[64], out[64];
+
+  (void)arg;
+#ifndef HAVE_LIBSCRYPT_H
+  if (1)
+    tt_skip();
+#endif
+
+  /* Test vectors from
+     http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11.
+
+     Note that the names of 'r' and 'N' are switched in that section. Or
+     possibly in libscrypt.
+  */
+
+  base16_decode((char*)spec, sizeof(spec),
+                "0400", 4);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(64, OP_EQ,
+            secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2));
+  test_memeq_hex(out,
+                 "77d6576238657b203b19ca42c18a0497"
+                 "f16b4844e3074ae8dfdffa3fede21442"
+                 "fcd0069ded0948f8326a753a0fc81f17"
+                 "e8d3e0fb2e0d3628cf35e20c38d18906");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "4e61436c" "0A34", 12);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(64, OP_EQ,
+            secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2));
+  test_memeq_hex(out,
+                 "fdbabe1c9d3472007856e7190d01e9fe"
+                 "7c6ad7cbc8237830e77376634b373162"
+                 "2eaf30d92e22a3886ff109279d9830da"
+                 "c727afb94a83ee6d8360cbdfa2cc0640");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "536f6469756d43686c6f72696465" "0e30", 32);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(64, OP_EQ,
+            secret_to_key_compute_key(out, 64, spec, 16,
+                                      "pleaseletmein", 13, 2));
+  test_memeq_hex(out,
+                 "7023bdcb3afd7348461c06cd81fd38eb"
+                 "fda8fbba904f8e3ea9b543f6545da1f2"
+                 "d5432955613f0fcf62d49705242a9af9"
+                 "e61e85dc0d651e40dfcf017b45575887");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "536f6469756d43686c6f72696465" "1430", 32);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(64, OP_EQ,
+            secret_to_key_compute_key(out, 64, spec, 16,
+                                      "pleaseletmein", 13, 2));
+  test_memeq_hex(out,
+                 "2101cb9b6a511aaeaddbbe09cf70f881"
+                 "ec568d574a2ffd4dabe5ee9820adaa47"
+                 "8e56fd8f4ba5d09ffa1c6d927c40f4c3"
+                 "37304049e8a952fbcbf45c6fa77a41a4");
+
+ done:
+  tor_free(mem_op_hex_tmp);
+}
+
+static void
+test_crypto_pbkdf2_vectors(void *arg)
+{
+  char *mem_op_hex_tmp = NULL;
+  uint8_t spec[64], out[64];
+  (void)arg;
+
+  /* Test vectors from RFC6070, section 2 */
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c74" "00" , 10);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(20, OP_EQ,
+            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
+  test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c74" "01" , 10);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(20, OP_EQ,
+            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
+  test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c74" "0C" , 10);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(20, OP_EQ,
+            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
+  test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c74" "18" , 10);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(20, OP_EQ,
+            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
+  test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c7453414c5473616c7453414c5473616c745"
+                "3414c5473616c7453414c5473616c74" "0C" , 74);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(25, OP_EQ,
+            secret_to_key_compute_key(out, 25, spec, 37,
+                                      "passwordPASSWORDpassword", 24, 1));
+  test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "7361006c74" "0c" , 12);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(16, OP_EQ,
+            secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1));
+  test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3");
+
+ done:
+  tor_free(mem_op_hex_tmp);
+}
+
+static void
+test_crypto_pwbox(void *arg)
+{
+  uint8_t *boxed=NULL, *decoded=NULL;
+  size_t len, dlen;
+  unsigned i;
+  const char msg[] = "This bunny reminds you that you still have a "
+    "salamander in your sylladex. She is holding the bunny Dave got you. "
+    "It’s sort of uncanny how similar they are, aside from the knitted "
+    "enhancements. Seriously, what are the odds?? So weird.";
+  const char pw[] = "I'm a night owl and a wise bird too";
+
+  const unsigned flags[] = { 0,
+                             S2K_FLAG_NO_SCRYPT,
+                             S2K_FLAG_LOW_MEM,
+                             S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM,
+                             S2K_FLAG_USE_PBKDF2 };
+  (void)arg;
+
+  for (i = 0; i < ARRAY_LENGTH(flags); ++i) {
+    tt_int_op(0, OP_EQ, crypto_pwbox(&boxed, &len,
+                                  (const uint8_t*)msg, strlen(msg),
+                                  pw, strlen(pw), flags[i]));
+    tt_assert(boxed);
+    tt_assert(len > 128+32);
+
+    tt_int_op(0, OP_EQ, crypto_unpwbox(&decoded, &dlen, boxed, len,
+                                    pw, strlen(pw)));
+
+    tt_assert(decoded);
+    tt_uint_op(dlen, OP_EQ, strlen(msg));
+    tt_mem_op(decoded, OP_EQ, msg, dlen);
+
+    tor_free(decoded);
+
+    tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
+                                                     boxed, len,
+                                                     pw, strlen(pw)-1));
+    boxed[len-1] ^= 1;
+    tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
+                                                     boxed, len,
+                                                     pw, strlen(pw)));
+    boxed[0] = 255;
+    tt_int_op(UNPWBOX_CORRUPTED, OP_EQ, crypto_unpwbox(&decoded, &dlen,
+                                                    boxed, len,
+                                                    pw, strlen(pw)));
+
+    tor_free(boxed);
+  }
+
+ done:
+  tor_free(boxed);
+  tor_free(decoded);
+}
+
+#define CRYPTO_LEGACY(name)                                            \
+  { #name, test_crypto_ ## name , 0, NULL, NULL }
+
+struct testcase_t slow_crypto_tests[] = {
+  CRYPTO_LEGACY(s2k_rfc2440),
+#ifdef HAVE_LIBSCRYPT_H
+  { "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"scrypt" },
+  { "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"scrypt-low" },
+#endif
+  { "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"pbkdf2" },
+  { "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"rfc2440" },
+  { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"rfc2440-legacy" },
+  { "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL },
+  { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL },
+  { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
+  { "pwbox", test_crypto_pwbox, 0, NULL, NULL },
+  END_OF_TESTCASES
+};
+
diff --git a/src/test/test_slow.c b/src/test/test_slow.c
new file mode 100644
index 0000000..32386b4
--- /dev/null
+++ b/src/test/test_slow.c
@@ -0,0 +1,29 @@
+/* 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 */
+
+/**
+ * \file test_slow.c
+ * \brief Slower unit tests for many pieces of the lower level Tor modules.
+ **/
+
+#include "orconfig.h"
+
+#include <stdio.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "or.h"
+#include "test.h"
+
+extern struct testcase_t slow_crypto_tests[];
+extern struct testcase_t slow_util_tests[];
+
+struct testgroup_t testgroups[] = {
+  { "slow/crypto/", slow_crypto_tests },
+  { "slow/util/", slow_util_tests },
+  END_OF_GROUPS
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index b53c8fc..e5df5b4 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -3380,370 +3380,6 @@ test_util_fgets_eagain(void *ptr)
 }
 #endif
 
-#ifndef BUILDDIR
-#define BUILDDIR "."
-#endif
-
-#ifdef _WIN32
-#define notify_pending_waitpid_callbacks() STMT_NIL
-#define TEST_CHILD "test-child.exe"
-#define EOL "\r\n"
-#else
-#define TEST_CHILD (BUILDDIR "/src/test/test-child")
-#define EOL "\n"
-#endif
-
-#ifdef _WIN32
-/* I've assumed Windows doesn't have the gap between fork and exec
- * that causes the race condition on unix-like platforms */
-#define MATCH_PROCESS_STATUS(s1,s2)         ((s1) == (s2))
-
-#else
-/* work around a race condition of the timing of SIGCHLD handler updates
- * to the process_handle's fields, and checks of those fields
- *
- * TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to
- * PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */
-#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING  (PROCESS_STATUS_RUNNING+1)
-#define IS_RUNNING_OR_NOTRUNNING(s)           \
-  ((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING)
-/* well, this is ugly */
-#define MATCH_PROCESS_STATUS(s1,s2)           \
-  (  (s1) == (s2)                                         \
-     ||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING      \
-        && IS_RUNNING_OR_NOTRUNNING(s2))                  \
-     ||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING      \
-        && IS_RUNNING_OR_NOTRUNNING(s1)))
-
-#endif // _WIN32
-
-/** Helper function for testing tor_spawn_background */
-static void
-run_util_spawn_background(const char *argv[], const char *expected_out,
-                          const char *expected_err, int expected_exit,
-                          int expected_status)
-{
-  int retval, exit_code;
-  ssize_t pos;
-  process_handle_t *process_handle=NULL;
-  char stdout_buf[100], stderr_buf[100];
-  int status;
-
-  /* Start the program */
-#ifdef _WIN32
-  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
-  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
-
-  notify_pending_waitpid_callbacks();
-
-  /* the race condition doesn't affect status,
-   * because status isn't updated by the SIGCHLD handler,
-   * but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
-  tt_assert(MATCH_PROCESS_STATUS(expected_status, status));
-  if (status == PROCESS_STATUS_ERROR) {
-    tt_ptr_op(process_handle, OP_EQ, NULL);
-    return;
-  }
-
-  tt_assert(process_handle != NULL);
-
-  /* When a spawned process forks, fails, then exits very quickly,
-   * (this typically occurs when exec fails)
-   * there is a race condition between the SIGCHLD handler
-   * updating the process_handle's fields, and this test
-   * checking the process status in those fields.
-   * The SIGCHLD update can occur before or after the code below executes.
-   * This causes intermittent failures in spawn_background_fail(),
-   * typically when the machine is under load.
-   * We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */
-
-  /* the race condition affects the change in
-   * process_handle->status from RUNNING to NOTRUNNING */
-  tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status));
-
-#ifndef _WIN32
-  notify_pending_waitpid_callbacks();
-  /* the race condition affects the change in
-   * process_handle->waitpid_cb to NULL,
-   * so we skip the check if expected_status is ambiguous,
-   * that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
-  tt_assert(process_handle->waitpid_cb != NULL
-              || expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
-#endif
-
-#ifdef _WIN32
-  tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
-  tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
-#else
-  tt_assert(process_handle->stdout_pipe >= 0);
-  tt_assert(process_handle->stderr_pipe >= 0);
-#endif
-
-  /* Check stdout */
-  pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
-                                         sizeof(stdout_buf) - 1);
-  tt_assert(pos >= 0);
-  stdout_buf[pos] = '\0';
-  tt_int_op(strlen(expected_out),OP_EQ, pos);
-  tt_str_op(expected_out,OP_EQ, stdout_buf);
-
-  notify_pending_waitpid_callbacks();
-
-  /* Check it terminated correctly */
-  retval = tor_get_exit_code(process_handle, 1, &exit_code);
-  tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
-  tt_int_op(expected_exit,OP_EQ, exit_code);
-  // TODO: Make test-child exit with something other than 0
-
-#ifndef _WIN32
-  notify_pending_waitpid_callbacks();
-  tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
-#endif
-
-  /* Check stderr */
-  pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
-                                         sizeof(stderr_buf) - 1);
-  tt_assert(pos >= 0);
-  stderr_buf[pos] = '\0';
-  tt_str_op(expected_err,OP_EQ, stderr_buf);
-  tt_int_op(strlen(expected_err),OP_EQ, pos);
-
-  notify_pending_waitpid_callbacks();
-
- done:
-  if (process_handle)
-    tor_process_handle_destroy(process_handle, 1);
-}
-
-/** Check that we can launch a process and read the output */
-static void
-test_util_spawn_background_ok(void *ptr)
-{
-  const char *argv[] = {TEST_CHILD, "--test", NULL};
-  const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
-  const char *expected_err = "ERR"EOL;
-
-  (void)ptr;
-
-  run_util_spawn_background(argv, expected_out, expected_err, 0,
-      PROCESS_STATUS_RUNNING);
-}
-
-/** Check that failing to find the executable works as expected */
-static void
-test_util_spawn_background_fail(void *ptr)
-{
-  const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
-  const char *expected_err = "";
-  char expected_out[1024];
-  char code[32];
-#ifdef _WIN32
-  const int expected_status = PROCESS_STATUS_ERROR;
-#else
-  /* TODO: Once we can signal failure to exec, set this to be
-   * PROCESS_STATUS_RUNNING_OR_ERROR */
-  const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
-#endif
-
-  memset(expected_out, 0xf0, sizeof(expected_out));
-  memset(code, 0xf0, sizeof(code));
-
-  (void)ptr;
-
-  tor_snprintf(code, sizeof(code), "%x/%x",
-    9 /* CHILD_STATE_FAILEXEC */ , ENOENT);
-  tor_snprintf(expected_out, sizeof(expected_out),
-    "ERR: Failed to spawn background process - code %s\n", code);
-
-  run_util_spawn_background(argv, expected_out, expected_err, 255,
-      expected_status);
-}
-
-/** Test that reading from a handle returns a partial read rather than
- * blocking */
-static void
-test_util_spawn_background_partial_read_impl(int exit_early)
-{
-  const int expected_exit = 0;
-  const int expected_status = PROCESS_STATUS_RUNNING;
-
-  int retval, exit_code;
-  ssize_t pos = -1;
-  process_handle_t *process_handle=NULL;
-  int status;
-  char stdout_buf[100], stderr_buf[100];
-
-  const char *argv[] = {TEST_CHILD, "--test", NULL};
-  const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
-                                 "DONE" EOL,
-                                 NULL };
-  const char *expected_err = "ERR" EOL;
-
-#ifndef _WIN32
-  int eof = 0;
-#endif
-  int expected_out_ctr;
-
-  if (exit_early) {
-    argv[1] = "--hang";
-    expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
-  }
-
-  /* Start the program */
-#ifdef _WIN32
-  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
-  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
-  tt_int_op(expected_status,OP_EQ, status);
-  tt_assert(process_handle);
-  tt_int_op(expected_status,OP_EQ, process_handle->status);
-
-  /* Check stdout */
-  for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
-#ifdef _WIN32
-    pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
-                              sizeof(stdout_buf) - 1, NULL);
-#else
-    /* Check that we didn't read the end of file last time */
-    tt_assert(!eof);
-    pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
-                              sizeof(stdout_buf) - 1, NULL, &eof);
-#endif
-    log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
-
-    /* We would have blocked, keep on trying */
-    if (0 == pos)
-      continue;
-
-    tt_assert(pos > 0);
-    stdout_buf[pos] = '\0';
-    tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf);
-    tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos);
-    expected_out_ctr++;
-  }
-
-  if (exit_early) {
-    tor_process_handle_destroy(process_handle, 1);
-    process_handle = NULL;
-    goto done;
-  }
-
-  /* The process should have exited without writing more */
-#ifdef _WIN32
-  pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
-                            sizeof(stdout_buf) - 1,
-                            process_handle);
-  tt_int_op(0,OP_EQ, pos);
-#else
-  if (!eof) {
-    /* We should have got all the data, but maybe not the EOF flag */
-    pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
-                              sizeof(stdout_buf) - 1,
-                              process_handle, &eof);
-    tt_int_op(0,OP_EQ, pos);
-    tt_assert(eof);
-  }
-  /* Otherwise, we got the EOF on the last read */
-#endif
-
-  /* Check it terminated correctly */
-  retval = tor_get_exit_code(process_handle, 1, &exit_code);
-  tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
-  tt_int_op(expected_exit,OP_EQ, exit_code);
-
-  // TODO: Make test-child exit with something other than 0
-
-  /* Check stderr */
-  pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
-                                         sizeof(stderr_buf) - 1);
-  tt_assert(pos >= 0);
-  stderr_buf[pos] = '\0';
-  tt_str_op(expected_err,OP_EQ, stderr_buf);
-  tt_int_op(strlen(expected_err),OP_EQ, pos);
-
- done:
-  tor_process_handle_destroy(process_handle, 1);
-}
-
-static void
-test_util_spawn_background_partial_read(void *arg)
-{
-  (void)arg;
-  test_util_spawn_background_partial_read_impl(0);
-}
-
-static void
-test_util_spawn_background_exit_early(void *arg)
-{
-  (void)arg;
-  test_util_spawn_background_partial_read_impl(1);
-}
-
-static void
-test_util_spawn_background_waitpid_notify(void *arg)
-{
-  int retval, exit_code;
-  process_handle_t *process_handle=NULL;
-  int status;
-  int ms_timer;
-
-  const char *argv[] = {TEST_CHILD, "--fast", NULL};
-
-  (void) arg;
-
-#ifdef _WIN32
-  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
-  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
-
-  tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
-  tt_ptr_op(process_handle, OP_NE, NULL);
-
-  /* We're not going to look at the stdout/stderr output this time. Instead,
-   * we're testing whether notify_pending_waitpid_calbacks() can report the
-   * process exit (on unix) and/or whether tor_get_exit_code() can notice it
-   * (on windows) */
-
-#ifndef _WIN32
-  ms_timer = 30*1000;
-  tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL);
-  while (process_handle->waitpid_cb && ms_timer > 0) {
-    tor_sleep_msec(100);
-    ms_timer -= 100;
-    notify_pending_waitpid_callbacks();
-  }
-  tt_int_op(ms_timer, OP_GT, 0);
-  tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
-#endif
-
-  ms_timer = 30*1000;
-  while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
-                == PROCESS_EXIT_RUNNING) && ms_timer > 0) {
-    tor_sleep_msec(100);
-    ms_timer -= 100;
-  }
-  tt_int_op(ms_timer, OP_GT, 0);
-
-  tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED);
-
- done:
-  tor_process_handle_destroy(process_handle, 1);
-}
-
-#undef TEST_CHILD
-#undef EOL
-
-#undef MATCH_PROCESS_STATUS
-
-#ifndef _WIN32
-#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING
-#undef IS_RUNNING_OR_NOTRUNNING
-#endif
-
 /**
  * Test for format_hex_number_sigsafe()
  */
@@ -4796,11 +4432,6 @@ struct testcase_t util_tests[] = {
   UTIL_TEST(exit_status, 0),
   UTIL_TEST(fgets_eagain, 0),
 #endif
-  UTIL_TEST(spawn_background_ok, 0),
-  UTIL_TEST(spawn_background_fail, 0),
-  UTIL_TEST(spawn_background_partial_read, 0),
-  UTIL_TEST(spawn_background_exit_early, 0),
-  UTIL_TEST(spawn_background_waitpid_notify, 0),
   UTIL_TEST(format_hex_number, 0),
   UTIL_TEST(format_dec_number, 0),
   UTIL_TEST(join_win_cmdline, 0),
diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c
new file mode 100644
index 0000000..4adcbe1
--- /dev/null
+++ b/src/test/test_util_slow.c
@@ -0,0 +1,388 @@
+/* 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 */
+
+#include "orconfig.h"
+#define UTIL_PRIVATE
+#include "util.h"
+#include "util_process.h"
+#include "crypto.h"
+#include "torlog.h"
+#include "test.h"
+
+#ifndef BUILDDIR
+#define BUILDDIR "."
+#endif
+
+#ifdef _WIN32
+#define notify_pending_waitpid_callbacks() STMT_NIL
+#define TEST_CHILD "test-child.exe"
+#define EOL "\r\n"
+#else
+#define TEST_CHILD (BUILDDIR "/src/test/test-child")
+#define EOL "\n"
+#endif
+
+#ifdef _WIN32
+/* I've assumed Windows doesn't have the gap between fork and exec
+ * that causes the race condition on unix-like platforms */
+#define MATCH_PROCESS_STATUS(s1,s2)         ((s1) == (s2))
+
+#else
+/* work around a race condition of the timing of SIGCHLD handler updates
+ * to the process_handle's fields, and checks of those fields
+ *
+ * TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to
+ * PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */
+#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING  (PROCESS_STATUS_RUNNING+1)
+#define IS_RUNNING_OR_NOTRUNNING(s)           \
+  ((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING)
+/* well, this is ugly */
+#define MATCH_PROCESS_STATUS(s1,s2)           \
+  (  (s1) == (s2)                                         \
+     ||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING      \
+        && IS_RUNNING_OR_NOTRUNNING(s2))                  \
+     ||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING      \
+        && IS_RUNNING_OR_NOTRUNNING(s1)))
+
+#endif // _WIN32
+
+/** Helper function for testing tor_spawn_background */
+static void
+run_util_spawn_background(const char *argv[], const char *expected_out,
+                          const char *expected_err, int expected_exit,
+                          int expected_status)
+{
+  int retval, exit_code;
+  ssize_t pos;
+  process_handle_t *process_handle=NULL;
+  char stdout_buf[100], stderr_buf[100];
+  int status;
+
+  /* Start the program */
+#ifdef _WIN32
+  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+
+  notify_pending_waitpid_callbacks();
+
+  /* the race condition doesn't affect status,
+   * because status isn't updated by the SIGCHLD handler,
+   * but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
+  tt_assert(MATCH_PROCESS_STATUS(expected_status, status));
+  if (status == PROCESS_STATUS_ERROR) {
+    tt_ptr_op(process_handle, OP_EQ, NULL);
+    return;
+  }
+
+  tt_assert(process_handle != NULL);
+
+  /* When a spawned process forks, fails, then exits very quickly,
+   * (this typically occurs when exec fails)
+   * there is a race condition between the SIGCHLD handler
+   * updating the process_handle's fields, and this test
+   * checking the process status in those fields.
+   * The SIGCHLD update can occur before or after the code below executes.
+   * This causes intermittent failures in spawn_background_fail(),
+   * typically when the machine is under load.
+   * We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */
+
+  /* the race condition affects the change in
+   * process_handle->status from RUNNING to NOTRUNNING */
+  tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status));
+
+#ifndef _WIN32
+  notify_pending_waitpid_callbacks();
+  /* the race condition affects the change in
+   * process_handle->waitpid_cb to NULL,
+   * so we skip the check if expected_status is ambiguous,
+   * that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
+  tt_assert(process_handle->waitpid_cb != NULL
+              || expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
+#endif
+
+#ifdef _WIN32
+  tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
+  tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
+#else
+  tt_assert(process_handle->stdout_pipe >= 0);
+  tt_assert(process_handle->stderr_pipe >= 0);
+#endif
+
+  /* Check stdout */
+  pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
+                                         sizeof(stdout_buf) - 1);
+  tt_assert(pos >= 0);
+  stdout_buf[pos] = '\0';
+  tt_int_op(strlen(expected_out),OP_EQ, pos);
+  tt_str_op(expected_out,OP_EQ, stdout_buf);
+
+  notify_pending_waitpid_callbacks();
+
+  /* Check it terminated correctly */
+  retval = tor_get_exit_code(process_handle, 1, &exit_code);
+  tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
+  tt_int_op(expected_exit,OP_EQ, exit_code);
+  // TODO: Make test-child exit with something other than 0
+
+#ifndef _WIN32
+  notify_pending_waitpid_callbacks();
+  tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
+#endif
+
+  /* Check stderr */
+  pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
+                                         sizeof(stderr_buf) - 1);
+  tt_assert(pos >= 0);
+  stderr_buf[pos] = '\0';
+  tt_str_op(expected_err,OP_EQ, stderr_buf);
+  tt_int_op(strlen(expected_err),OP_EQ, pos);
+
+  notify_pending_waitpid_callbacks();
+
+ done:
+  if (process_handle)
+    tor_process_handle_destroy(process_handle, 1);
+}
+
+/** Check that we can launch a process and read the output */
+static void
+test_util_spawn_background_ok(void *ptr)
+{
+  const char *argv[] = {TEST_CHILD, "--test", NULL};
+  const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
+  const char *expected_err = "ERR"EOL;
+
+  (void)ptr;
+
+  run_util_spawn_background(argv, expected_out, expected_err, 0,
+      PROCESS_STATUS_RUNNING);
+}
+
+/** Check that failing to find the executable works as expected */
+static void
+test_util_spawn_background_fail(void *ptr)
+{
+  const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
+  const char *expected_err = "";
+  char expected_out[1024];
+  char code[32];
+#ifdef _WIN32
+  const int expected_status = PROCESS_STATUS_ERROR;
+#else
+  /* TODO: Once we can signal failure to exec, set this to be
+   * PROCESS_STATUS_RUNNING_OR_ERROR */
+  const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
+#endif
+
+  memset(expected_out, 0xf0, sizeof(expected_out));
+  memset(code, 0xf0, sizeof(code));
+
+  (void)ptr;
+
+  tor_snprintf(code, sizeof(code), "%x/%x",
+    9 /* CHILD_STATE_FAILEXEC */ , ENOENT);
+  tor_snprintf(expected_out, sizeof(expected_out),
+    "ERR: Failed to spawn background process - code %s\n", code);
+
+  run_util_spawn_background(argv, expected_out, expected_err, 255,
+      expected_status);
+}
+
+/** Test that reading from a handle returns a partial read rather than
+ * blocking */
+static void
+test_util_spawn_background_partial_read_impl(int exit_early)
+{
+  const int expected_exit = 0;
+  const int expected_status = PROCESS_STATUS_RUNNING;
+
+  int retval, exit_code;
+  ssize_t pos = -1;
+  process_handle_t *process_handle=NULL;
+  int status;
+  char stdout_buf[100], stderr_buf[100];
+
+  const char *argv[] = {TEST_CHILD, "--test", NULL};
+  const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
+                                 "DONE" EOL,
+                                 NULL };
+  const char *expected_err = "ERR" EOL;
+
+#ifndef _WIN32
+  int eof = 0;
+#endif
+  int expected_out_ctr;
+
+  if (exit_early) {
+    argv[1] = "--hang";
+    expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
+  }
+
+  /* Start the program */
+#ifdef _WIN32
+  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+  tt_int_op(expected_status,OP_EQ, status);
+  tt_assert(process_handle);
+  tt_int_op(expected_status,OP_EQ, process_handle->status);
+
+  /* Check stdout */
+  for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
+#ifdef _WIN32
+    pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
+                              sizeof(stdout_buf) - 1, NULL);
+#else
+    /* Check that we didn't read the end of file last time */
+    tt_assert(!eof);
+    pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
+                              sizeof(stdout_buf) - 1, NULL, &eof);
+#endif
+    log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
+
+    /* We would have blocked, keep on trying */
+    if (0 == pos)
+      continue;
+
+    tt_assert(pos > 0);
+    stdout_buf[pos] = '\0';
+    tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf);
+    tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos);
+    expected_out_ctr++;
+  }
+
+  if (exit_early) {
+    tor_process_handle_destroy(process_handle, 1);
+    process_handle = NULL;
+    goto done;
+  }
+
+  /* The process should have exited without writing more */
+#ifdef _WIN32
+  pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
+                            sizeof(stdout_buf) - 1,
+                            process_handle);
+  tt_int_op(0,OP_EQ, pos);
+#else
+  if (!eof) {
+    /* We should have got all the data, but maybe not the EOF flag */
+    pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
+                              sizeof(stdout_buf) - 1,
+                              process_handle, &eof);
+    tt_int_op(0,OP_EQ, pos);
+    tt_assert(eof);
+  }
+  /* Otherwise, we got the EOF on the last read */
+#endif
+
+  /* Check it terminated correctly */
+  retval = tor_get_exit_code(process_handle, 1, &exit_code);
+  tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
+  tt_int_op(expected_exit,OP_EQ, exit_code);
+
+  // TODO: Make test-child exit with something other than 0
+
+  /* Check stderr */
+  pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
+                                         sizeof(stderr_buf) - 1);
+  tt_assert(pos >= 0);
+  stderr_buf[pos] = '\0';
+  tt_str_op(expected_err,OP_EQ, stderr_buf);
+  tt_int_op(strlen(expected_err),OP_EQ, pos);
+
+ done:
+  tor_process_handle_destroy(process_handle, 1);
+}
+
+static void
+test_util_spawn_background_partial_read(void *arg)
+{
+  (void)arg;
+  test_util_spawn_background_partial_read_impl(0);
+}
+
+static void
+test_util_spawn_background_exit_early(void *arg)
+{
+  (void)arg;
+  test_util_spawn_background_partial_read_impl(1);
+}
+
+static void
+test_util_spawn_background_waitpid_notify(void *arg)
+{
+  int retval, exit_code;
+  process_handle_t *process_handle=NULL;
+  int status;
+  int ms_timer;
+
+  const char *argv[] = {TEST_CHILD, "--fast", NULL};
+
+  (void) arg;
+
+#ifdef _WIN32
+  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+
+  tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
+  tt_ptr_op(process_handle, OP_NE, NULL);
+
+  /* We're not going to look at the stdout/stderr output this time. Instead,
+   * we're testing whether notify_pending_waitpid_calbacks() can report the
+   * process exit (on unix) and/or whether tor_get_exit_code() can notice it
+   * (on windows) */
+
+#ifndef _WIN32
+  ms_timer = 30*1000;
+  tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL);
+  while (process_handle->waitpid_cb && ms_timer > 0) {
+    tor_sleep_msec(100);
+    ms_timer -= 100;
+    notify_pending_waitpid_callbacks();
+  }
+  tt_int_op(ms_timer, OP_GT, 0);
+  tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
+#endif
+
+  ms_timer = 30*1000;
+  while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
+                == PROCESS_EXIT_RUNNING) && ms_timer > 0) {
+    tor_sleep_msec(100);
+    ms_timer -= 100;
+  }
+  tt_int_op(ms_timer, OP_GT, 0);
+
+  tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED);
+
+ done:
+  tor_process_handle_destroy(process_handle, 1);
+}
+
+#undef TEST_CHILD
+#undef EOL
+
+#undef MATCH_PROCESS_STATUS
+
+#ifndef _WIN32
+#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING
+#undef IS_RUNNING_OR_NOTRUNNING
+#endif
+
+#define UTIL_TEST(name, flags)                          \
+  { #name, test_util_ ## name, flags, NULL, NULL }
+
+struct testcase_t slow_util_tests[] = {
+  UTIL_TEST(spawn_background_ok, 0),
+  UTIL_TEST(spawn_background_fail, 0),
+  UTIL_TEST(spawn_background_partial_read, 0),
+  UTIL_TEST(spawn_background_exit_early, 0),
+  UTIL_TEST(spawn_background_waitpid_notify, 0),
+  END_OF_TESTCASES
+};
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
new file mode 100644
index 0000000..d7d6dac
--- /dev/null
+++ b/src/test/testing_common.c
@@ -0,0 +1,298 @@
+/* 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)
+{
+#ifdef CACHE_GENERATED_KEYS
+  tor_assert(idx < N_PREGEN_KEYS);
+  if (! pregen_keys[idx]) {
+    pregen_keys[idx] = crypto_pk_new();
+    tor_assert(!crypto_pk_generate_key(pregen_keys[idx]));
+  }
+  return crypto_pk_dup_key(pregen_keys[idx]);
+#else
+  crypto_pk_t *result;
+  (void) idx;
+  result = crypto_pk_new();
+  tor_assert(!crypto_pk_generate_key(result));
+  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;
+}
+





More information about the tor-commits mailing list