diff --git a/changes/ticket32196 b/changes/ticket32196
new file mode 100644
index 0000000000000000000000000000000000000000..d642478fe4df99822d9a9cd9fc1a1c8d094f1583
--- /dev/null
+++ b/changes/ticket32196
@@ -0,0 +1,2 @@
+  o Testing (circuit, EWMA):
+    - Add unit tests for circuitmux and EWMA subsystems. Closes ticket 32196.
diff --git a/src/core/or/circuitmux.c b/src/core/or/circuitmux.c
index b2628bec3f2bbc72ac4fc7f7ba35b2cbaf6f1605..f92a53eb27e431799540b2f88f7fed55a510dd33 100644
--- a/src/core/or/circuitmux.c
+++ b/src/core/or/circuitmux.c
@@ -69,26 +69,20 @@
  *     made to attach all existing circuits to the new policy.
  **/
 
+#define CIRCUITMUX_PRIVATE
+
 #include "core/or/or.h"
 #include "core/or/channel.h"
 #include "core/or/circuitlist.h"
 #include "core/or/circuitmux.h"
 #include "core/or/relay.h"
 
-#include "core/or/cell_queue_st.h"
-#include "core/or/destroy_cell_queue_st.h"
 #include "core/or/or_circuit_st.h"
 
 /*
  * Private typedefs for circuitmux.c
  */
 
-/*
- * Map of muxinfos for circuitmux_t to use; struct is defined below (name
- * of struct must match HT_HEAD line).
- */
-typedef struct chanid_circid_muxinfo_map chanid_circid_muxinfo_map_t;
-
 /*
  * Hash table entry (yeah, calling it chanid_circid_muxinfo_s seems to
  * break the hash table code).
@@ -102,49 +96,6 @@ typedef struct chanid_circid_muxinfo_t chanid_circid_muxinfo_t;
 
 typedef struct circuit_muxinfo_s circuit_muxinfo_t;
 
-/*
- * Structures for circuitmux.c
- */
-
-struct circuitmux_s {
-  /* Keep count of attached, active circuits */
-  unsigned int n_circuits, n_active_circuits;
-
-  /* Total number of queued cells on all circuits */
-  unsigned int n_cells;
-
-  /*
-   * Map from (channel ID, circuit ID) pairs to circuit_muxinfo_t
-   */
-  chanid_circid_muxinfo_map_t *chanid_circid_map;
-
-  /** List of queued destroy cells */
-  destroy_cell_queue_t destroy_cell_queue;
-  /** Boolean: True iff the last cell to circuitmux_get_first_active_circuit
-   * returned the destroy queue. Used to force alternation between
-   * destroy/non-destroy cells.
-   *
-   * XXXX There is no reason to think that alternating is a particularly good
-   * approach -- it's just designed to prevent destroys from starving other
-   * cells completely.
-   */
-  unsigned int last_cell_was_destroy : 1;
-  /** Destroy counter: increment this when a destroy gets queued, decrement
-   * when we unqueue it, so we can test to make sure they don't starve.
-   */
-  int64_t destroy_ctr;
-
-  /*
-   * Circuitmux policy; if this is non-NULL, it can override the built-
-   * in round-robin active circuits behavior.  This is how EWMA works in
-   * the new circuitmux_t world.
-   */
-  const circuitmux_policy_t *policy;
-
-  /* Policy-specific data */
-  circuitmux_policy_data_t *policy_data;
-};
-
 /*
  * This struct holds whatever we want to store per attached circuit on a
  * circuitmux_t; right now, just the count of queued cells and the direction.
@@ -221,9 +172,6 @@ chanid_circid_entry_hash(chanid_circid_muxinfo_t *a)
             ((unsigned int)(a->chan_id & 0xffffffff)));
 }
 
-/* Declare the struct chanid_circid_muxinfo_map type */
-HT_HEAD(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t);
-
 /* Emit a bunch of hash table stuff */
 HT_PROTOTYPE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node,
              chanid_circid_entry_hash, chanid_circid_entries_eq)
diff --git a/src/core/or/circuitmux.h b/src/core/or/circuitmux.h
index 67cd9bcdd8a03dc9dbd5f4c8f69dc4d99e4266c0..c68c31b29af3a8c4b5ff93ecfa6205f312efe7d5 100644
--- a/src/core/or/circuitmux.h
+++ b/src/core/or/circuitmux.h
@@ -158,5 +158,61 @@ void circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux,
 MOCK_DECL(int, circuitmux_compare_muxes,
           (circuitmux_t *cmux_1, circuitmux_t *cmux_2));
 
+#ifdef CIRCUITMUX_PRIVATE
+
+#include "core/or/destroy_cell_queue_st.h"
+
+/*
+ * Map of muxinfos for circuitmux_t to use; struct is defined below (name
+ * of struct must match HT_HEAD line).
+ */
+typedef HT_HEAD(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t)
+  chanid_circid_muxinfo_map_t;
+
+/*
+ * Structures for circuitmux.c
+ */
+
+struct circuitmux_s {
+  /* Keep count of attached, active circuits */
+  unsigned int n_circuits, n_active_circuits;
+
+  /* Total number of queued cells on all circuits */
+  unsigned int n_cells;
+
+  /*
+   * Map from (channel ID, circuit ID) pairs to circuit_muxinfo_t
+   */
+  chanid_circid_muxinfo_map_t *chanid_circid_map;
+
+  /** List of queued destroy cells */
+  destroy_cell_queue_t destroy_cell_queue;
+  /** Boolean: True iff the last cell to circuitmux_get_first_active_circuit
+   * returned the destroy queue. Used to force alternation between
+   * destroy/non-destroy cells.
+   *
+   * XXXX There is no reason to think that alternating is a particularly good
+   * approach -- it's just designed to prevent destroys from starving other
+   * cells completely.
+   */
+  unsigned int last_cell_was_destroy : 1;
+  /** Destroy counter: increment this when a destroy gets queued, decrement
+   * when we unqueue it, so we can test to make sure they don't starve.
+   */
+  int64_t destroy_ctr;
+
+  /*
+   * Circuitmux policy; if this is non-NULL, it can override the built-
+   * in round-robin active circuits behavior.  This is how EWMA works in
+   * the new circuitmux_t world.
+   */
+  const circuitmux_policy_t *policy;
+
+  /* Policy-specific data */
+  circuitmux_policy_data_t *policy_data;
+};
+
+#endif /* CIRCUITMUX_PRIVATE */
+
 #endif /* !defined(TOR_CIRCUITMUX_H) */
 
diff --git a/src/core/or/circuitmux_ewma.c b/src/core/or/circuitmux_ewma.c
index 3f83c3fd5a7034f26e3c068f11e8f92fb46ec61d..5c9eac1c3fee073e122314eb323f31a0aad33b68 100644
--- a/src/core/or/circuitmux_ewma.c
+++ b/src/core/or/circuitmux_ewma.c
@@ -58,115 +58,6 @@
 /** The natural logarithm of 0.5. */
 #define LOG_ONEHALF -0.69314718055994529
 
-/*** EWMA structures ***/
-
-typedef struct cell_ewma_s cell_ewma_t;
-typedef struct ewma_policy_data_s ewma_policy_data_t;
-typedef struct ewma_policy_circ_data_s ewma_policy_circ_data_t;
-
-/**
- * The cell_ewma_t structure keeps track of how many cells a circuit has
- * transferred recently.  It keeps an EWMA (exponentially weighted moving
- * average) of the number of cells flushed from the circuit queue onto a
- * connection in channel_flush_from_first_active_circuit().
- */
-
-struct cell_ewma_s {
-  /** The last 'tick' at which we recalibrated cell_count.
-   *
-   * A cell sent at exactly the start of this tick has weight 1.0. Cells sent
-   * since the start of this tick have weight greater than 1.0; ones sent
-   * earlier have less weight. */
-  unsigned int last_adjusted_tick;
-  /** The EWMA of the cell count. */
-  double cell_count;
-  /** True iff this is the cell count for a circuit's previous
-   * channel. */
-  unsigned int is_for_p_chan : 1;
-  /** The position of the circuit within the OR connection's priority
-   * queue. */
-  int heap_index;
-};
-
-struct ewma_policy_data_s {
-  circuitmux_policy_data_t base_;
-
-  /**
-   * Priority queue of cell_ewma_t for circuits with queued cells waiting
-   * for room to free up on the channel that owns this circuitmux.  Kept
-   * in heap order according to EWMA.  This was formerly in channel_t, and
-   * in or_connection_t before that.
-   */
-  smartlist_t *active_circuit_pqueue;
-
-  /**
-   * The tick on which the cell_ewma_ts in active_circuit_pqueue last had
-   * their ewma values rescaled.  This was formerly in channel_t, and in
-   * or_connection_t before that.
-   */
-  unsigned int active_circuit_pqueue_last_recalibrated;
-};
-
-struct ewma_policy_circ_data_s {
-  circuitmux_policy_circ_data_t base_;
-
-  /**
-   * The EWMA count for the number of cells flushed from this circuit
-   * onto this circuitmux.  Used to determine which circuit to flush
-   * from next.  This was formerly in circuit_t and or_circuit_t.
-   */
-  cell_ewma_t cell_ewma;
-
-  /**
-   * Pointer back to the circuit_t this is for; since we're separating
-   * out circuit selection policy like this, we can't attach cell_ewma_t
-   * to the circuit_t any more, so we can't use SUBTYPE_P directly to a
-   * circuit_t like before; instead get it here.
-   */
-  circuit_t *circ;
-};
-
-#define EWMA_POL_DATA_MAGIC 0x2fd8b16aU
-#define EWMA_POL_CIRC_DATA_MAGIC 0x761e7747U
-
-/*** Downcasts for the above types ***/
-
-static ewma_policy_data_t *
-TO_EWMA_POL_DATA(circuitmux_policy_data_t *);
-
-static ewma_policy_circ_data_t *
-TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *);
-
-/**
- * Downcast a circuitmux_policy_data_t to an ewma_policy_data_t and assert
- * if the cast is impossible.
- */
-
-static inline ewma_policy_data_t *
-TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol)
-{
-  if (!pol) return NULL;
-  else {
-    tor_assert(pol->magic == EWMA_POL_DATA_MAGIC);
-    return DOWNCAST(ewma_policy_data_t, pol);
-  }
-}
-
-/**
- * Downcast a circuitmux_policy_circ_data_t to an ewma_policy_circ_data_t
- * and assert if the cast is impossible.
- */
-
-static inline ewma_policy_circ_data_t *
-TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol)
-{
-  if (!pol) return NULL;
-  else {
-    tor_assert(pol->magic == EWMA_POL_CIRC_DATA_MAGIC);
-    return DOWNCAST(ewma_policy_circ_data_t, pol);
-  }
-}
-
 /*** Static declarations for circuitmux_ewma.c ***/
 
 static void add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma);
diff --git a/src/core/or/circuitmux_ewma.h b/src/core/or/circuitmux_ewma.h
index b45ce1f916f5d66ed8616e2015a32e66838d789f..dcfbc17a8244dd6d4313671b221ddc558e1b9c9c 100644
--- a/src/core/or/circuitmux_ewma.h
+++ b/src/core/or/circuitmux_ewma.h
@@ -22,9 +22,114 @@ void cmux_ewma_set_options(const or_options_t *options,
 void circuitmux_ewma_free_all(void);
 
 #ifdef CIRCUITMUX_EWMA_PRIVATE
+
+/*** EWMA structures ***/
+
+typedef struct cell_ewma_s cell_ewma_t;
+typedef struct ewma_policy_data_s ewma_policy_data_t;
+typedef struct ewma_policy_circ_data_s ewma_policy_circ_data_t;
+
+/**
+ * The cell_ewma_t structure keeps track of how many cells a circuit has
+ * transferred recently.  It keeps an EWMA (exponentially weighted moving
+ * average) of the number of cells flushed from the circuit queue onto a
+ * connection in channel_flush_from_first_active_circuit().
+ */
+
+struct cell_ewma_s {
+  /** The last 'tick' at which we recalibrated cell_count.
+   *
+   * A cell sent at exactly the start of this tick has weight 1.0. Cells sent
+   * since the start of this tick have weight greater than 1.0; ones sent
+   * earlier have less weight. */
+  unsigned int last_adjusted_tick;
+  /** The EWMA of the cell count. */
+  double cell_count;
+  /** True iff this is the cell count for a circuit's previous
+   * channel. */
+  unsigned int is_for_p_chan : 1;
+  /** The position of the circuit within the OR connection's priority
+   * queue. */
+  int heap_index;
+};
+
+struct ewma_policy_data_s {
+  circuitmux_policy_data_t base_;
+
+  /**
+   * Priority queue of cell_ewma_t for circuits with queued cells waiting
+   * for room to free up on the channel that owns this circuitmux.  Kept
+   * in heap order according to EWMA.  This was formerly in channel_t, and
+   * in or_connection_t before that.
+   */
+  smartlist_t *active_circuit_pqueue;
+
+  /**
+   * The tick on which the cell_ewma_ts in active_circuit_pqueue last had
+   * their ewma values rescaled.  This was formerly in channel_t, and in
+   * or_connection_t before that.
+   */
+  unsigned int active_circuit_pqueue_last_recalibrated;
+};
+
+struct ewma_policy_circ_data_s {
+  circuitmux_policy_circ_data_t base_;
+
+  /**
+   * The EWMA count for the number of cells flushed from this circuit
+   * onto this circuitmux.  Used to determine which circuit to flush
+   * from next.  This was formerly in circuit_t and or_circuit_t.
+   */
+  cell_ewma_t cell_ewma;
+
+  /**
+   * Pointer back to the circuit_t this is for; since we're separating
+   * out circuit selection policy like this, we can't attach cell_ewma_t
+   * to the circuit_t any more, so we can't use SUBTYPE_P directly to a
+   * circuit_t like before; instead get it here.
+   */
+  circuit_t *circ;
+};
+
+#define EWMA_POL_DATA_MAGIC 0x2fd8b16aU
+#define EWMA_POL_CIRC_DATA_MAGIC 0x761e7747U
+
+/*** Downcasts for the above types ***/
+
+/**
+ * Downcast a circuitmux_policy_data_t to an ewma_policy_data_t and assert
+ * if the cast is impossible.
+ */
+
+static inline ewma_policy_data_t *
+TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol)
+{
+  if (!pol) return NULL;
+  else {
+    tor_assert(pol->magic == EWMA_POL_DATA_MAGIC);
+    return DOWNCAST(ewma_policy_data_t, pol);
+  }
+}
+
+/**
+ * Downcast a circuitmux_policy_circ_data_t to an ewma_policy_circ_data_t
+ * and assert if the cast is impossible.
+ */
+
+static inline ewma_policy_circ_data_t *
+TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol)
+{
+  if (!pol) return NULL;
+  else {
+    tor_assert(pol->magic == EWMA_POL_CIRC_DATA_MAGIC);
+    return DOWNCAST(ewma_policy_circ_data_t, pol);
+  }
+}
+
 STATIC unsigned cell_ewma_get_current_tick_and_fraction(double *remainder_out);
 STATIC void cell_ewma_initialize_ticks(void);
-#endif
+
+#endif /* CIRCUITMUX_EWMA_PRIVATE */
 
 #endif /* !defined(TOR_CIRCUITMUX_EWMA_H) */
 
diff --git a/src/core/or/destroy_cell_queue_st.h b/src/core/or/destroy_cell_queue_st.h
index fc817c1b429ea4944007d6b83670f533ee6b6423..3c4df050c23bb6ea8aec9583ce9a379978f4b367 100644
--- a/src/core/or/destroy_cell_queue_st.h
+++ b/src/core/or/destroy_cell_queue_st.h
@@ -12,6 +12,8 @@
 #ifndef DESTROY_CELL_QUEUE_ST_H
 #define DESTROY_CELL_QUEUE_ST_H
 
+#include "core/or/cell_queue_st.h"
+
 /** A single queued destroy cell. */
 struct destroy_cell_t {
   TOR_SIMPLEQ_ENTRY(destroy_cell_t) next;
diff --git a/src/test/fakecircs.c b/src/test/fakecircs.c
new file mode 100644
index 0000000000000000000000000000000000000000..62027e03391a58a73436132f470288b6111a1286
--- /dev/null
+++ b/src/test/fakecircs.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file fakecircs.c
+ * \brief Fake circuits API for unit test.
+ **/
+
+#define CHANNEL_PRIVATE
+#define CIRCUITBUILD_PRIVATE
+#define CIRCUITLIST_PRIVATE
+#define CRYPT_PATH_PRIVATE
+
+#include "core/or/or.h"
+
+#include "core/crypto/relay_crypto.h"
+#include "core/or/channel.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuitpadding.h"
+#include "core/or/crypt_path.h"
+#include "core/or/relay.h"
+#include "core/or/relay_crypto_st.h"
+
+#include "fakecircs.h"
+
+/** Return newly allocated OR circuit using the given nchan and pchan. It must
+ * be freed with the free_fake_orcirc(). */
+or_circuit_t *
+new_fake_orcirc(channel_t *nchan, channel_t *pchan)
+{
+  or_circuit_t *orcirc = NULL;
+  circuit_t *circ = NULL;
+  crypt_path_t tmp_cpath;
+  char whatevs_key[CPATH_KEY_MATERIAL_LEN];
+
+  orcirc = tor_malloc_zero(sizeof(*orcirc));
+  circ = &(orcirc->base_);
+  circ->magic = OR_CIRCUIT_MAGIC;
+
+  circuit_set_n_circid_chan(circ, get_unique_circ_id_by_chan(nchan), nchan);
+  cell_queue_init(&(circ->n_chan_cells));
+
+  circ->n_hop = NULL;
+  circ->streams_blocked_on_n_chan = 0;
+  circ->streams_blocked_on_p_chan = 0;
+  circ->n_delete_pending = 0;
+  circ->p_delete_pending = 0;
+  circ->received_destroy = 0;
+  circ->state = CIRCUIT_STATE_OPEN;
+  circ->purpose = CIRCUIT_PURPOSE_OR;
+  circ->package_window = CIRCWINDOW_START_MAX;
+  circ->deliver_window = CIRCWINDOW_START_MAX;
+  circ->n_chan_create_cell = NULL;
+
+  circuit_set_p_circid_chan(orcirc, get_unique_circ_id_by_chan(pchan), pchan);
+  cell_queue_init(&(orcirc->p_chan_cells));
+
+  memset(&tmp_cpath, 0, sizeof(tmp_cpath));
+  if (cpath_init_circuit_crypto(&tmp_cpath, whatevs_key,
+                                sizeof(whatevs_key), 0, 0)<0) {
+    log_warn(LD_BUG,"Circuit initialization failed");
+    return NULL;
+  }
+  orcirc->crypto = tmp_cpath.pvt_crypto;
+
+  return orcirc;
+}
+
+/** Free fake OR circuit which MUST be created by new_fake_orcirc(). */
+void
+free_fake_orcirc(or_circuit_t *orcirc)
+{
+  circuit_t *circ = TO_CIRCUIT(orcirc);
+
+  relay_crypto_clear(&orcirc->crypto);
+
+  circpad_circuit_free_all_machineinfos(circ);
+
+  if (orcirc->p_chan && orcirc->p_chan->cmux) {
+    circuitmux_detach_circuit(orcirc->p_chan->cmux, circ);
+  }
+  if (circ->n_chan && circ->n_chan->cmux) {
+    circuitmux_detach_circuit(circ->n_chan->cmux, circ);
+  }
+
+  tor_free_(circ);
+}
diff --git a/src/test/fakecircs.h b/src/test/fakecircs.h
new file mode 100644
index 0000000000000000000000000000000000000000..5fd02027f0d4c306b6f6af0cb0ab2c967d37706a
--- /dev/null
+++ b/src/test/fakecircs.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file fakecircs.h
+ * \brief Declarations for fake circuits for test suite use.
+ **/
+
+#ifndef TOR_FAKECIRCS_H
+#define TOR_FAKECIRCS_H
+
+#include "core/or/or_circuit_st.h"
+
+or_circuit_t *new_fake_orcirc(channel_t *nchan, channel_t *pchan);
+void free_fake_orcirc(or_circuit_t *orcirc);
+
+#endif /* TOR_FAKECIRCS_H */
diff --git a/src/test/include.am b/src/test/include.am
index d8e25dea9fb87d1c95feb70a342e83c266497540..667bbf53686a240ced5915dace58b3e8618e4419 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -99,6 +99,7 @@ if UNITTESTS_ENABLED
 
 # ADD_C_FILE: INSERT SOURCES HERE.
 src_test_test_SOURCES += \
+	src/test/fakecircs.c \
 	src/test/log_test_helpers.c \
 	src/test/hs_test_helpers.c \
 	src/test/rend_test_helpers.c \
@@ -122,6 +123,7 @@ src_test_test_SOURCES += \
 	src/test/test_checkdir.c \
 	src/test/test_circuitlist.c \
 	src/test/test_circuitmux.c \
+	src/test/test_circuitmux_ewma.c \
 	src/test/test_circuitbuild.c \
 	src/test/test_circuituse.c \
 	src/test/test_circuitstats.c \
@@ -339,6 +341,7 @@ src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS)
 # ADD_C_FILE: INSERT HEADERS HERE.
 noinst_HEADERS+= \
 	src/test/fakechans.h \
+	src/test/fakecircs.h \
 	src/test/hs_test_helpers.h \
 	src/test/log_test_helpers.h \
 	src/test/rend_test_helpers.h \
diff --git a/src/test/test.c b/src/test/test.c
index 6dbec26fa80e24832b7d44b7716951fc79cb5733..c4227dca50b6db0e589940158bbceedc49c945e1 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -836,6 +836,7 @@ struct testgroup_t testgroups[] = {
   { "circuitpadding/", circuitpadding_tests },
   { "circuitlist/", circuitlist_tests },
   { "circuitmux/", circuitmux_tests },
+  { "circuitmux_ewma/", circuitmux_ewma_tests },
   { "circuitstats/", circuitstats_tests },
   { "circuituse/", circuituse_tests },
   { "compat/libevent/", compat_libevent_tests },
diff --git a/src/test/test.h b/src/test/test.h
index feaa13a3a5c78eff341d8ce9d5cf5803c5ef8623..b2dc552c82c5c5b3f82e6fddfecd503bd3ff41e8 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -199,6 +199,7 @@ extern struct testcase_t checkdir_tests[];
 extern struct testcase_t circuitbuild_tests[];
 extern struct testcase_t circuitlist_tests[];
 extern struct testcase_t circuitmux_tests[];
+extern struct testcase_t circuitmux_ewma_tests[];
 extern struct testcase_t circuitstats_tests[];
 extern struct testcase_t circuituse_tests[];
 extern struct testcase_t compat_libevent_tests[];
diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c
index a2b3e62fe8c7756aecc2ff50c6703d8ff1996a4d..6d9e5c472d5c0cc66b577186679e11bc3780a054 100644
--- a/src/test/test_circuitmux.c
+++ b/src/test/test_circuitmux.c
@@ -5,29 +5,23 @@
 #define CIRCUITMUX_PRIVATE
 #define CIRCUITMUX_EWMA_PRIVATE
 #define RELAY_PRIVATE
+
 #include "core/or/or.h"
 #include "core/or/channel.h"
 #include "core/or/circuitmux.h"
 #include "core/or/circuitmux_ewma.h"
+#include "core/or/destroy_cell_queue_st.h"
 #include "core/or/relay.h"
 #include "core/or/scheduler.h"
-#include "test/test.h"
 
-#include "core/or/destroy_cell_queue_st.h"
+#include "test/fakechans.h"
+#include "test/fakecircs.h"
+#include "test/test.h"
 
 #include <math.h>
 
-/* XXXX duplicated function from test_circuitlist.c */
-static channel_t *
-new_fake_channel(void)
-{
-  channel_t *chan = tor_malloc_zero(sizeof(channel_t));
-  channel_init(chan);
-  return chan;
-}
-
 static int
-has_queued_writes(channel_t *c)
+mock_has_queued_writes_true(channel_t *c)
 {
   (void) c;
   return 1;
@@ -44,16 +38,14 @@ test_cmux_destroy_cell_queue(void *arg)
   packed_cell_t *pc = NULL;
   destroy_cell_t *dc = NULL;
 
-  scheduler_init();
+  MOCK(scheduler_release_channel, scheduler_release_channel_mock);
 
   (void) arg;
 
-  cmux = circuitmux_alloc();
-  tt_assert(cmux);
   ch = new_fake_channel();
-  circuitmux_set_policy(cmux, &ewma_policy);
-  ch->has_queued_writes = has_queued_writes;
+  ch->has_queued_writes = mock_has_queued_writes_true;
   ch->wide_circ_ids = 1;
+  cmux = ch->cmux;
 
   circ = circuitmux_get_first_active_circuit(cmux, &cq);
   tt_ptr_op(circ, OP_EQ, NULL);
@@ -78,10 +70,11 @@ test_cmux_destroy_cell_queue(void *arg)
   tt_int_op(circuitmux_num_cells(cmux), OP_EQ, 2);
 
  done:
-  circuitmux_free(cmux);
-  channel_free(ch);
+  free_fake_channel(ch);
   packed_cell_free(pc);
   tor_free(dc);
+
+  UNMOCK(scheduler_release_channel);
 }
 
 static void
@@ -125,9 +118,364 @@ test_cmux_compute_ticks(void *arg)
   ;
 }
 
+static void
+test_cmux_allocate(void *arg)
+{
+  circuitmux_t *cmux = NULL;
+
+  (void) arg;
+
+  cmux = circuitmux_alloc();
+  tt_assert(cmux);
+  tt_assert(cmux->chanid_circid_map);
+  tt_int_op(HT_SIZE(cmux->chanid_circid_map), OP_EQ, 0);
+  tt_uint_op(cmux->n_circuits, OP_EQ, 0);
+  tt_uint_op(cmux->n_active_circuits, OP_EQ, 0);
+  tt_uint_op(cmux->n_cells, OP_EQ, 0);
+  tt_uint_op(cmux->last_cell_was_destroy, OP_EQ, 0);
+  tt_int_op(cmux->destroy_ctr, OP_EQ, 0);
+  tt_ptr_op(cmux->policy, OP_EQ, NULL);
+  tt_ptr_op(cmux->policy_data, OP_EQ, NULL);
+
+  tt_assert(TOR_SIMPLEQ_EMPTY(&cmux->destroy_cell_queue.head));
+
+ done:
+  circuitmux_free(cmux);
+}
+
+static void
+test_cmux_attach_circuit(void *arg)
+{
+  circuit_t *circ = NULL;
+  or_circuit_t *orcirc = NULL;
+  channel_t *pchan = NULL, *nchan = NULL;
+  cell_direction_t cdir;
+  unsigned int n_cells;
+
+  (void) arg;
+
+  pchan = new_fake_channel();
+  tt_assert(pchan);
+  nchan = new_fake_channel();
+  tt_assert(nchan);
+
+  orcirc = new_fake_orcirc(nchan, pchan);
+  tt_assert(orcirc);
+  circ = TO_CIRCUIT(orcirc);
+
+  /* While assigning a new circuit IDs, the circuitmux_attach_circuit() is
+   * called for a new channel on the circuit. This means, we should now have
+   * the created circuit attached on both the pchan and nchan cmux. */
+  tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1);
+  tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 1);
+
+  /* There should be _no_ active circuit due to no queued cells. */
+  tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 0);
+  tt_uint_op(circuitmux_num_active_circuits(nchan->cmux), OP_EQ, 0);
+
+  /* Circuit should not be active on the cmux. */
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_active(nchan->cmux, circ), OP_EQ, 0);
+
+  /* Not active so no cells. */
+  n_cells = circuitmux_num_cells_for_circuit(pchan->cmux, circ);
+  tt_uint_op(n_cells, OP_EQ, 0);
+  n_cells = circuitmux_num_cells(pchan->cmux);
+  tt_uint_op(n_cells, OP_EQ, 0);
+  n_cells = circuitmux_num_cells_for_circuit(nchan->cmux, circ);
+  tt_uint_op(n_cells, OP_EQ, 0);
+  n_cells = circuitmux_num_cells(nchan->cmux);
+  tt_uint_op(n_cells, OP_EQ, 0);
+
+  /* So it should be attached :) */
+  tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1);
+  tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 1);
+
+  /* Query the chanid<->circid map in the cmux subsytem with what we just
+   * created and validate the cell direction. */
+  cdir = circuitmux_attached_circuit_direction(pchan->cmux, circ);
+  tt_int_op(cdir, OP_EQ, CELL_DIRECTION_IN);
+  cdir = circuitmux_attached_circuit_direction(nchan->cmux, circ);
+  tt_int_op(cdir, OP_EQ, CELL_DIRECTION_OUT);
+
+  /*
+   * We'll activate->deactivate->activate to test all code paths of
+   * circuitmux_set_num_cells().
+   */
+
+  /* Activate circuit. */
+  circuitmux_set_num_cells(pchan->cmux, circ, 4);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
+
+  /* Deactivate. */
+  circuitmux_clear_num_cells(pchan->cmux, circ);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
+  tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 0);
+
+  /* Re-activate. */
+  circuitmux_set_num_cells(pchan->cmux, circ, 4);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
+
+  /* Once re-attached, it should become inactive because the circuit has no
+   * cells while the chanid<->circid object has some. The attach code will
+   * reset the count on the cmux for that circuit:
+   *
+   * if (chanid_circid_muxinfo_t->muxinfo.cell_count > 0 && cell_count == 0) {
+   */
+  circuitmux_attach_circuit(pchan->cmux, circ, CELL_DIRECTION_IN);
+  n_cells = circuitmux_num_cells_for_circuit(pchan->cmux, circ);
+  tt_uint_op(n_cells, OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
+  tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 0);
+
+  /* Lets queue a cell on the circuit now so it becomes active when
+   * re-attaching:
+   *
+   * else if (chanid_circid_muxinfo_t->muxinfo.cell_count == 0 &&
+   *          cell_count > 0) {
+   */
+  orcirc->p_chan_cells.n = 1;
+  circuitmux_attach_circuit(pchan->cmux, circ, CELL_DIRECTION_IN);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
+
+ done:
+  free_fake_orcirc(orcirc);
+  free_fake_channel(pchan);
+  free_fake_channel(nchan);
+}
+
+static void
+test_cmux_detach_circuit(void *arg)
+{
+  circuit_t *circ = NULL;
+  or_circuit_t *orcirc = NULL;
+  channel_t *pchan = NULL, *nchan = NULL;
+
+  (void) arg;
+
+  pchan = new_fake_channel();
+  tt_assert(pchan);
+  nchan = new_fake_channel();
+  tt_assert(nchan);
+
+  orcirc = new_fake_orcirc(nchan, pchan);
+  tt_assert(orcirc);
+  circ = TO_CIRCUIT(orcirc);
+
+  /* While assigning a new circuit IDs, the circuitmux_attach_circuit() is
+   * called for a new channel on the circuit. This means, we should now have
+   * the created circuit attached on both the pchan and nchan cmux. */
+  tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1);
+  tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 1);
+  tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1);
+  tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 1);
+
+  /* Now, detach the circuit from pchan and then nchan. */
+  circuitmux_detach_circuit(pchan->cmux, circ);
+  tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 0);
+  circuitmux_detach_circuit(nchan->cmux, circ);
+  tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 0);
+
+ done:
+  free_fake_orcirc(orcirc);
+  free_fake_channel(pchan);
+  free_fake_channel(nchan);
+}
+
+static void
+test_cmux_detach_all_circuits(void *arg)
+{
+  circuit_t *circ = NULL;
+  or_circuit_t *orcirc = NULL;
+  channel_t *pchan = NULL, *nchan = NULL;
+  smartlist_t *detached_out = smartlist_new();
+
+  (void) arg;
+
+  /* Channels need to be registered in order for the detach all circuit
+   * function to find them. */
+  pchan = new_fake_channel();
+  tt_assert(pchan);
+  channel_register(pchan);
+  nchan = new_fake_channel();
+  tt_assert(nchan);
+  channel_register(nchan);
+
+  orcirc = new_fake_orcirc(nchan, pchan);
+  tt_assert(orcirc);
+  circ = TO_CIRCUIT(orcirc);
+
+  /* Just make sure it is attached. */
+  tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1);
+  tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 1);
+  tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1);
+  tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 1);
+
+  /* Queue some cells so we can test if the circuit becomes inactive on the
+   * cmux after the mass detach. */
+  circuitmux_set_num_cells(pchan->cmux, circ, 4);
+  circuitmux_set_num_cells(nchan->cmux, circ, 4);
+
+  /* Detach all on pchan and then nchan. */
+  circuitmux_detach_all_circuits(pchan->cmux, detached_out);
+  tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
+  tt_int_op(smartlist_len(detached_out), OP_EQ, 1);
+  circuitmux_detach_all_circuits(nchan->cmux, NULL);
+  tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_active(nchan->cmux, circ), OP_EQ, 0);
+
+ done:
+  smartlist_free(detached_out);
+  free_fake_orcirc(orcirc);
+  free_fake_channel(pchan);
+  free_fake_channel(nchan);
+}
+
+static void
+test_cmux_policy(void *arg)
+{
+  circuit_t *circ = NULL;
+  or_circuit_t *orcirc = NULL;
+  channel_t *pchan = NULL, *nchan = NULL;
+
+  (void) arg;
+
+  pchan = new_fake_channel();
+  tt_assert(pchan);
+  channel_register(pchan);
+  nchan = new_fake_channel();
+  tt_assert(nchan);
+  channel_register(nchan);
+
+  orcirc = new_fake_orcirc(nchan, pchan);
+  tt_assert(orcirc);
+  circ = TO_CIRCUIT(orcirc);
+
+  /* Confirm we have the EWMA policy by default for new channels. */
+  tt_ptr_op(circuitmux_get_policy(pchan->cmux), OP_EQ, &ewma_policy);
+  tt_ptr_op(circuitmux_get_policy(nchan->cmux), OP_EQ, &ewma_policy);
+
+  /* Putting cell on the cmux means will make the notify policy code path to
+   * trigger. */
+  circuitmux_set_num_cells(pchan->cmux, circ, 4);
+
+  /* Clear it out. */
+  circuitmux_clear_policy(pchan->cmux);
+
+  /* Set back the EWMA policy. */
+  circuitmux_set_policy(pchan->cmux, &ewma_policy);
+
+ done:
+  free_fake_orcirc(orcirc);
+  free_fake_channel(pchan);
+  free_fake_channel(nchan);
+}
+
+static void
+test_cmux_xmit_cell(void *arg)
+{
+  circuit_t *circ = NULL;
+  or_circuit_t *orcirc = NULL;
+  channel_t *pchan = NULL, *nchan = NULL;
+
+  (void) arg;
+
+  pchan = new_fake_channel();
+  tt_assert(pchan);
+  nchan = new_fake_channel();
+  tt_assert(nchan);
+
+  orcirc = new_fake_orcirc(nchan, pchan);
+  tt_assert(orcirc);
+  circ = TO_CIRCUIT(orcirc);
+
+  /* Queue 4 cells on the circuit. */
+  circuitmux_set_num_cells(pchan->cmux, circ, 4);
+  tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 4);
+  tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 4);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
+  tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 1);
+
+  /* Emit the first cell. Circuit should still be active. */
+  circuitmux_notify_xmit_cells(pchan->cmux, circ, 1);
+  tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 3);
+  tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 3);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
+  tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 1);
+
+  /* Emit the last 3 cells. Circuit should become inactive. */
+  circuitmux_notify_xmit_cells(pchan->cmux, circ, 3);
+  tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 0);
+  tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 0);
+  tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
+  tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 0);
+
+  /* Queue a DESTROY cell. */
+  pchan->has_queued_writes = mock_has_queued_writes_true;
+  circuitmux_append_destroy_cell(pchan, pchan->cmux, orcirc->p_circ_id, 0);
+  tt_int_op(pchan->cmux->destroy_ctr, OP_EQ, 1);
+  tt_int_op(pchan->cmux->destroy_cell_queue.n, OP_EQ, 1);
+  tt_int_op(circuitmux_count_queued_destroy_cells(pchan, pchan->cmux),
+            OP_EQ, 1);
+
+  /* Emit the DESTROY cell. */
+  circuitmux_notify_xmit_destroy(pchan->cmux);
+  tt_int_op(pchan->cmux->destroy_ctr, OP_EQ, 0);
+
+ done:
+  free_fake_orcirc(orcirc);
+  free_fake_channel(pchan);
+  free_fake_channel(nchan);
+}
+
+static void *
+cmux_setup_test(const struct testcase_t *tc)
+{
+  static int whatever;
+
+  (void) tc;
+
+  cell_ewma_initialize_ticks();
+  return &whatever;
+}
+
+static int
+cmux_cleanup_test(const struct testcase_t *tc, void *ptr)
+{
+  (void) tc;
+  (void) ptr;
+
+  circuitmux_ewma_free_all();
+
+  return 1;
+}
+
+static struct testcase_setup_t cmux_test_setup = {
+  .setup_fn = cmux_setup_test,
+  .cleanup_fn = cmux_cleanup_test,
+};
+
+#define TEST_CMUX(name) \
+  { #name, test_cmux_##name, TT_FORK, &cmux_test_setup, NULL }
+
 struct testcase_t circuitmux_tests[] = {
-  { "destroy_cell_queue", test_cmux_destroy_cell_queue, TT_FORK, NULL, NULL },
-  { "compute_ticks", test_cmux_compute_ticks, TT_FORK, NULL, NULL },
+  /* Test circuitmux_t object */
+  TEST_CMUX(allocate),
+  TEST_CMUX(attach_circuit),
+  TEST_CMUX(detach_circuit),
+  TEST_CMUX(detach_all_circuits),
+  TEST_CMUX(policy),
+  TEST_CMUX(xmit_cell),
+
+  /* Misc. */
+  TEST_CMUX(compute_ticks),
+  TEST_CMUX(destroy_cell_queue),
+
   END_OF_TESTCASES
 };
 
diff --git a/src/test/test_circuitmux_ewma.c b/src/test/test_circuitmux_ewma.c
new file mode 100644
index 0000000000000000000000000000000000000000..8b3edf2b0634218841e30cebd61ab8ef9e4bdc0f
--- /dev/null
+++ b/src/test/test_circuitmux_ewma.c
@@ -0,0 +1,228 @@
+/* Copyright (c) 2013-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CIRCUITMUX_PRIVATE
+#define CIRCUITMUX_EWMA_PRIVATE
+
+#include "core/or/or.h"
+#include "core/or/circuitmux.h"
+#include "core/or/circuitmux_ewma.h"
+
+#include "test/fakechans.h"
+#include "test/fakecircs.h"
+#include "test/test.h"
+
+static void
+test_cmux_ewma_active_circuit(void *arg)
+{
+  circuitmux_t cmux; /* garbage */
+  circuitmux_policy_data_t *pol_data = NULL;
+  circuit_t circ; /* garbage */
+  circuitmux_policy_circ_data_t *circ_data = NULL;
+
+  (void) arg;
+
+  pol_data = ewma_policy.alloc_cmux_data(&cmux);
+  tt_assert(pol_data);
+  circ_data = ewma_policy.alloc_circ_data(&cmux, pol_data, &circ,
+                                          CELL_DIRECTION_OUT, 42);
+  tt_assert(circ_data);
+
+  /* Get EWMA specific objects. */
+
+  /* Make circuit active. */
+  ewma_policy.notify_circ_active(&cmux, pol_data, &circ, circ_data);
+
+  circuit_t *entry = ewma_policy.pick_active_circuit(&cmux, pol_data);
+  tt_mem_op(entry, OP_EQ, &circ, sizeof(circ));
+
+ done:
+  ewma_policy.free_circ_data(&cmux, pol_data, &circ, circ_data);
+  ewma_policy.free_cmux_data(&cmux, pol_data);
+}
+
+static void
+test_cmux_ewma_xmit_cell(void *arg)
+{
+  circuitmux_t cmux; /* garbage */
+  circuitmux_policy_data_t *pol_data = NULL;
+  circuit_t circ; /* garbage */
+  circuitmux_policy_circ_data_t *circ_data = NULL;
+  ewma_policy_data_t *ewma_pol_data;
+  ewma_policy_circ_data_t *ewma_data;
+  double old_cell_count;
+
+  (void) arg;
+
+  pol_data = ewma_policy.alloc_cmux_data(&cmux);
+  tt_assert(pol_data);
+  circ_data = ewma_policy.alloc_circ_data(&cmux, pol_data, &circ,
+                                          CELL_DIRECTION_OUT, 42);
+  tt_assert(circ_data);
+  ewma_pol_data = TO_EWMA_POL_DATA(pol_data);
+  ewma_data = TO_EWMA_POL_CIRC_DATA(circ_data);
+
+  /* Make circuit active. */
+  ewma_policy.notify_circ_active(&cmux, pol_data, &circ, circ_data);
+
+  /* Move back in time the last time we calibrated so we scale the active
+   * circuit when emitting a cell. */
+  ewma_pol_data->active_circuit_pqueue_last_recalibrated -= 100;
+  ewma_data->cell_ewma.last_adjusted_tick =
+    ewma_pol_data->active_circuit_pqueue_last_recalibrated;
+
+  /* Grab old cell count. */
+  old_cell_count = ewma_data->cell_ewma.cell_count;
+
+  ewma_policy.notify_xmit_cells(&cmux, pol_data, &circ, circ_data, 1);
+
+  /* Our old cell count should be lower to what we have since we just emitted
+   * a cell and thus we scale. */
+  tt_double_op(old_cell_count, OP_LT, ewma_data->cell_ewma.cell_count);
+
+ done:
+  ewma_policy.free_circ_data(&cmux, pol_data, &circ, circ_data);
+  ewma_policy.free_cmux_data(&cmux, pol_data);
+}
+
+static void
+test_cmux_ewma_notify_circ(void *arg)
+{
+  circuitmux_t cmux; /* garbage */
+  circuitmux_policy_data_t *pol_data = NULL;
+  circuit_t circ; /* garbage */
+  circuitmux_policy_circ_data_t *circ_data = NULL;
+  const ewma_policy_data_t *ewma_pol_data;
+
+  (void) arg;
+
+  pol_data = ewma_policy.alloc_cmux_data(&cmux);
+  tt_assert(pol_data);
+  circ_data = ewma_policy.alloc_circ_data(&cmux, pol_data, &circ,
+                                          CELL_DIRECTION_OUT, 42);
+  tt_assert(circ_data);
+
+  /* Currently, notify_circ_active() ignores cmux and circ. They can not be
+   * NULL so it is fine to pass garbage. */
+  ewma_policy.notify_circ_active(&cmux, pol_data, &circ, circ_data);
+
+  /* We should have an active circuit in the queue so its EWMA value can be
+   * tracked. */
+  ewma_pol_data = TO_EWMA_POL_DATA(pol_data);
+  tt_int_op(smartlist_len(ewma_pol_data->active_circuit_pqueue), OP_EQ, 1);
+  tt_uint_op(ewma_pol_data->active_circuit_pqueue_last_recalibrated, OP_NE, 0);
+
+  ewma_policy.notify_circ_inactive(&cmux, pol_data, &circ, circ_data);
+  /* Should be removed from the active queue. */
+  ewma_pol_data = TO_EWMA_POL_DATA(pol_data);
+  tt_int_op(smartlist_len(ewma_pol_data->active_circuit_pqueue), OP_EQ, 0);
+  tt_uint_op(ewma_pol_data->active_circuit_pqueue_last_recalibrated, OP_NE, 0);
+
+ done:
+  ewma_policy.free_circ_data(&cmux, pol_data, &circ, circ_data);
+  ewma_policy.free_cmux_data(&cmux, pol_data);
+}
+
+static void
+test_cmux_ewma_policy_circ_data(void *arg)
+{
+  circuitmux_t cmux; /* garbage */
+  circuitmux_policy_data_t pol_data; /* garbage */
+  circuit_t circ; /* garbage */
+  circuitmux_policy_circ_data_t *circ_data = NULL;
+  const ewma_policy_circ_data_t *ewma_data;
+
+  (void) arg;
+
+  /* Currently, alloc_circ_data() ignores every parameter _except_ the cell
+   * direction so it is OK to pass garbage. They can not be NULL. */
+  circ_data = ewma_policy.alloc_circ_data(&cmux, &pol_data, &circ,
+                                          CELL_DIRECTION_OUT, 42);
+  tt_assert(circ_data);
+  tt_uint_op(circ_data->magic, OP_EQ, EWMA_POL_CIRC_DATA_MAGIC);
+
+  ewma_data = TO_EWMA_POL_CIRC_DATA(circ_data);
+  tt_mem_op(ewma_data->circ, OP_EQ, &circ, sizeof(circuit_t));
+  tt_double_op(ewma_data->cell_ewma.cell_count, OP_LE, 0.0);
+  tt_int_op(ewma_data->cell_ewma.heap_index, OP_EQ, -1);
+  tt_uint_op(ewma_data->cell_ewma.is_for_p_chan, OP_EQ, 0);
+  ewma_policy.free_circ_data(&cmux, &pol_data, &circ, circ_data);
+
+  circ_data = ewma_policy.alloc_circ_data(&cmux, &pol_data, &circ,
+                                          CELL_DIRECTION_IN, 42);
+  tt_assert(circ_data);
+  tt_uint_op(circ_data->magic, OP_EQ, EWMA_POL_CIRC_DATA_MAGIC);
+
+  ewma_data = TO_EWMA_POL_CIRC_DATA(circ_data);
+  tt_mem_op(ewma_data->circ, OP_EQ, &circ, sizeof(circuit_t));
+  tt_double_op(ewma_data->cell_ewma.cell_count, OP_LE, 0.0);
+  tt_int_op(ewma_data->cell_ewma.heap_index, OP_EQ, -1);
+  tt_uint_op(ewma_data->cell_ewma.is_for_p_chan, OP_EQ, 1);
+
+ done:
+  ewma_policy.free_circ_data(&cmux, &pol_data, &circ, circ_data);
+}
+
+static void
+test_cmux_ewma_policy_data(void *arg)
+{
+  circuitmux_t cmux; /* garbage. */
+  circuitmux_policy_data_t *pol_data = NULL;
+  const ewma_policy_data_t *ewma_pol_data;
+
+  (void) arg;
+
+  pol_data = ewma_policy.alloc_cmux_data(&cmux);
+  tt_assert(pol_data);
+  tt_uint_op(pol_data->magic, OP_EQ, EWMA_POL_DATA_MAGIC);
+
+  /* Test EWMA object. */
+  ewma_pol_data = TO_EWMA_POL_DATA(pol_data);
+  tt_assert(ewma_pol_data->active_circuit_pqueue);
+  tt_uint_op(ewma_pol_data->active_circuit_pqueue_last_recalibrated, OP_NE, 0);
+
+ done:
+  ewma_policy.free_cmux_data(&cmux, pol_data);
+}
+
+static void *
+cmux_ewma_setup_test(const struct testcase_t *tc)
+{
+  static int whatever;
+
+  (void) tc;
+
+  cell_ewma_initialize_ticks();
+  cmux_ewma_set_options(NULL, NULL);
+
+  return &whatever;
+}
+
+static int
+cmux_ewma_cleanup_test(const struct testcase_t *tc, void *ptr)
+{
+  (void) tc;
+  (void) ptr;
+
+  circuitmux_ewma_free_all();
+
+  return 1;
+}
+
+static struct testcase_setup_t cmux_ewma_test_setup = {
+  .setup_fn = cmux_ewma_setup_test,
+  .cleanup_fn = cmux_ewma_cleanup_test,
+};
+
+#define TEST_CMUX_EWMA(name) \
+  { #name, test_cmux_ewma_##name, TT_FORK, &cmux_ewma_test_setup, NULL }
+
+struct testcase_t circuitmux_ewma_tests[] = {
+  TEST_CMUX_EWMA(active_circuit),
+  TEST_CMUX_EWMA(policy_data),
+  TEST_CMUX_EWMA(policy_circ_data),
+  TEST_CMUX_EWMA(notify_circ),
+  TEST_CMUX_EWMA(xmit_cell),
+
+  END_OF_TESTCASES
+};
diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c
index 934ddb0208d23ad57e6ef0808a8dfbc9a16c7705..70e2081c55a36a808fd4166ea22f00b52c8e3a61 100644
--- a/src/test/test_circuitpadding.c
+++ b/src/test/test_circuitpadding.c
@@ -38,6 +38,7 @@
 #include "core/or/or_circuit_st.h"
 #include "core/or/origin_circuit_st.h"
 
+#include "test/fakecircs.h"
 #include "test/rng_test_helpers.h"
 
 /* Start our monotime mocking at 1 second past whatever monotime_init()
@@ -53,7 +54,6 @@ circid_t get_unique_circ_id_by_chan(channel_t *chan);
 void helper_create_basic_machine(void);
 static void helper_create_conditional_machines(void);
 
-static or_circuit_t * new_fake_orcirc(channel_t *nchan, channel_t *pchan);
 channel_t *new_fake_channel(void);
 void test_circuitpadding_negotiation(void *arg);
 void test_circuitpadding_wronghop(void *arg);
@@ -67,7 +67,6 @@ void test_circuitpadding_state_length(void *arg);
 static void
 simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay,
                            int padding);
-void free_fake_orcirc(circuit_t *circ);
 void free_fake_origin_circuit(origin_circuit_t *circ);
 
 static int deliver_negotiated = 1;
@@ -127,62 +126,6 @@ circuit_get_nth_node_mock(origin_circuit_t *circ, int hop)
   return &padding_node;
 }
 
-static or_circuit_t *
-new_fake_orcirc(channel_t *nchan, channel_t *pchan)
-{
-  or_circuit_t *orcirc = NULL;
-  circuit_t *circ = NULL;
-  crypt_path_t tmp_cpath;
-  char whatevs_key[CPATH_KEY_MATERIAL_LEN];
-
-  orcirc = tor_malloc_zero(sizeof(*orcirc));
-  circ = &(orcirc->base_);
-  circ->magic = OR_CIRCUIT_MAGIC;
-
-  //circ->n_chan = nchan;
-  circ->n_circ_id = get_unique_circ_id_by_chan(nchan);
-  cell_queue_init(&(circ->n_chan_cells));
-  circ->n_hop = NULL;
-  circ->streams_blocked_on_n_chan = 0;
-  circ->streams_blocked_on_p_chan = 0;
-  circ->n_delete_pending = 0;
-  circ->p_delete_pending = 0;
-  circ->received_destroy = 0;
-  circ->state = CIRCUIT_STATE_OPEN;
-  circ->purpose = CIRCUIT_PURPOSE_OR;
-  circ->package_window = CIRCWINDOW_START_MAX;
-  circ->deliver_window = CIRCWINDOW_START_MAX;
-  circ->n_chan_create_cell = NULL;
-
-  //orcirc->p_chan = pchan;
-  orcirc->p_circ_id = get_unique_circ_id_by_chan(pchan);
-  cell_queue_init(&(orcirc->p_chan_cells));
-
-  circuit_set_p_circid_chan(orcirc, orcirc->p_circ_id, pchan);
-  circuit_set_n_circid_chan(circ, circ->n_circ_id, nchan);
-
-  memset(&tmp_cpath, 0, sizeof(tmp_cpath));
-  if (cpath_init_circuit_crypto(&tmp_cpath, whatevs_key,
-                                sizeof(whatevs_key), 0, 0)<0) {
-    log_warn(LD_BUG,"Circuit initialization failed");
-    return NULL;
-  }
-  orcirc->crypto = tmp_cpath.pvt_crypto;
-
-  return orcirc;
-}
-
-void
-free_fake_orcirc(circuit_t *circ)
-{
-  or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
-
-  relay_crypto_clear(&orcirc->crypto);
-
-  circpad_circuit_free_all_machineinfos(circ);
-  tor_free(circ);
-}
-
 void
 free_fake_origin_circuit(origin_circuit_t *circ)
 {
@@ -413,7 +356,7 @@ test_circuitpadding_rtt(void *arg)
             circpad_machine_current_state(
                 client_side->padding_info[0])->histogram_edges[0]);
  done:
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
   circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
   circuitmux_free(dummy_channel.cmux);
   timers_shutdown();
@@ -1439,7 +1382,7 @@ test_circuitpadding_wronghop(void *arg)
 
   /* Test 2: Test no padding */
   free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
 
   client_side = TO_CIRCUIT(origin_circuit_new());
   relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
@@ -1484,7 +1427,7 @@ test_circuitpadding_wronghop(void *arg)
 
  done:
   free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
   circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
   circuitmux_free(dummy_channel.cmux);
   monotime_disable_test_mocking();
@@ -1553,7 +1496,7 @@ test_circuitpadding_negotiation(void *arg)
 
   /* Test 2: Test no padding */
   free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
 
   client_side = TO_CIRCUIT(origin_circuit_new());
   relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
@@ -1591,7 +1534,7 @@ test_circuitpadding_negotiation(void *arg)
 
   /* 3. Test failure to negotiate a machine due to desync */
   free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
 
   client_side = TO_CIRCUIT(origin_circuit_new());
   relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
@@ -1619,7 +1562,7 @@ test_circuitpadding_negotiation(void *arg)
 
  done:
   free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
   circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
   circuitmux_free(dummy_channel.cmux);
   monotime_disable_test_mocking();
@@ -1939,7 +1882,7 @@ test_circuitpadding_state_length(void *arg)
   tor_free(client_machine);
 
   free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
 
   circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
   circuitmux_free(dummy_channel.cmux);
@@ -2312,7 +2255,7 @@ test_circuitpadding_circuitsetup_machine(void *arg)
   tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
             OP_NE, 0);
   circuit_mark_for_close(client_side, END_CIRC_REASON_FLAG_REMOTE);
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
   timers_advance_and_run(5000);
 
   /* No cells sent */
@@ -2616,7 +2559,7 @@ test_circuitpadding_global_rate_limiting(void *arg)
   tt_int_op(retval, OP_EQ, 0);
 
  done:
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
   circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
   circuitmux_free(dummy_channel.cmux);
   SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
@@ -2769,7 +2712,7 @@ test_circuitpadding_reduce_disable(void *arg)
   tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
 
  done:
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
   circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
   circuitmux_free(dummy_channel.cmux);
   testing_disable_reproducible_rng();
@@ -3075,7 +3018,7 @@ helper_test_hs_machines(bool test_intro_circs)
   }
 
  done:
-  free_fake_orcirc(relay_side);
+  free_fake_orcirc(TO_OR_CIRCUIT(relay_side));
   circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
   circuitmux_free(dummy_channel.cmux);
   free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
diff --git a/src/test/test_relay.c b/src/test/test_relay.c
index 0b7a7be3327a28e235dfbeefaae4e46b8026e637..f7809b47efa3ab89549b0526a750d824aff16ead 100644
--- a/src/test/test_relay.c
+++ b/src/test/test_relay.c
@@ -21,42 +21,10 @@
 /* Test suite stuff */
 #include "test/test.h"
 #include "test/fakechans.h"
-
-static or_circuit_t * new_fake_orcirc(channel_t *nchan, channel_t *pchan);
+#include "test/fakecircs.h"
 
 static void test_relay_append_cell_to_circuit_queue(void *arg);
 
-static or_circuit_t *
-new_fake_orcirc(channel_t *nchan, channel_t *pchan)
-{
-  or_circuit_t *orcirc = NULL;
-  circuit_t *circ = NULL;
-
-  orcirc = tor_malloc_zero(sizeof(*orcirc));
-  circ = &(orcirc->base_);
-  circ->magic = OR_CIRCUIT_MAGIC;
-
-  circuit_set_n_circid_chan(circ, get_unique_circ_id_by_chan(nchan), nchan);
-  cell_queue_init(&(circ->n_chan_cells));
-
-  circ->n_hop = NULL;
-  circ->streams_blocked_on_n_chan = 0;
-  circ->streams_blocked_on_p_chan = 0;
-  circ->n_delete_pending = 0;
-  circ->p_delete_pending = 0;
-  circ->received_destroy = 0;
-  circ->state = CIRCUIT_STATE_OPEN;
-  circ->purpose = CIRCUIT_PURPOSE_OR;
-  circ->package_window = CIRCWINDOW_START_MAX;
-  circ->deliver_window = CIRCWINDOW_START_MAX;
-  circ->n_chan_create_cell = NULL;
-
-  circuit_set_p_circid_chan(orcirc, get_unique_circ_id_by_chan(pchan), pchan);
-  cell_queue_init(&(orcirc->p_chan_cells));
-
-  return orcirc;
-}
-
 static void
 assert_circuit_ok_mock(const circuit_t *c)
 {
@@ -145,7 +113,7 @@ test_relay_close_circuit(void *arg)
     cell_queue_clear(&orcirc->base_.n_chan_cells);
     cell_queue_clear(&orcirc->p_chan_cells);
   }
-  tor_free(orcirc);
+  free_fake_orcirc(orcirc);
   free_fake_channel(nchan);
   free_fake_channel(pchan);
   UNMOCK(assert_circuit_ok);
@@ -218,7 +186,7 @@ test_relay_append_cell_to_circuit_queue(void *arg)
     cell_queue_clear(&orcirc->base_.n_chan_cells);
     cell_queue_clear(&orcirc->p_chan_cells);
   }
-  tor_free(orcirc);
+  free_fake_orcirc(orcirc);
   free_fake_channel(nchan);
   free_fake_channel(pchan);