diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
new file mode 100644
index 0000000000000000000000000000000000000000..a17bf0f0a2a05f90addb1b90e2af8cf118b2f0ae
--- /dev/null
+++ b/src/test/hs_test_helpers.c
@@ -0,0 +1,244 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "crypto_ed25519.h"
+#include "test.h"
+#include "torcert.h"
+
+#include "hs_test_helpers.h"
+
+hs_desc_intro_point_t *
+hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
+                            const char *addr, int legacy)
+{
+  int ret;
+  ed25519_keypair_t auth_kp;
+  hs_desc_intro_point_t *intro_point = NULL;
+  hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip));
+  ip->link_specifiers = smartlist_new();
+
+  {
+    hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls));
+    if (legacy) {
+      ls->type = LS_LEGACY_ID;
+      memcpy(ls->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8",
+             DIGEST_LEN);
+    } else {
+      ls->u.ap.port = 9001;
+      int family = tor_addr_parse(&ls->u.ap.addr, addr);
+      switch (family) {
+        case AF_INET:
+          ls->type = LS_IPV4;
+          break;
+        case AF_INET6:
+          ls->type = LS_IPV6;
+          break;
+        default:
+          /* Stop the test, not suppose to have an error. */
+          tt_int_op(family, OP_EQ, AF_INET);
+      }
+    }
+    smartlist_add(ip->link_specifiers, ls);
+  }
+
+  ret = ed25519_keypair_generate(&auth_kp, 0);
+  tt_int_op(ret, ==, 0);
+  ip->auth_key_cert = tor_cert_create(signing_kp, CERT_TYPE_AUTH_HS_IP_KEY,
+                                      &auth_kp.pubkey, now,
+                                      HS_DESC_CERT_LIFETIME,
+                                      CERT_FLAG_INCLUDE_SIGNING_KEY);
+  tt_assert(ip->auth_key_cert);
+
+  if (legacy) {
+    ip->enc_key.legacy = crypto_pk_new();
+    ip->enc_key_type = HS_DESC_KEY_TYPE_LEGACY;
+    tt_assert(ip->enc_key.legacy);
+    ret = crypto_pk_generate_key(ip->enc_key.legacy);
+    tt_int_op(ret, ==, 0);
+  } else {
+    ret = curve25519_keypair_generate(&ip->enc_key.curve25519, 0);
+    tt_int_op(ret, ==, 0);
+    ip->enc_key_type = HS_DESC_KEY_TYPE_CURVE25519;
+  }
+
+  intro_point = ip;
+ done:
+  return intro_point;
+}
+
+/* Return a valid hs_descriptor_t object. If no_ip is set, no introduction
+ * points are added. */
+static hs_descriptor_t *
+hs_helper_build_hs_desc_impl(unsigned int no_ip,
+                             const ed25519_keypair_t *signing_kp)
+{
+  int ret;
+  time_t now = time(NULL);
+  ed25519_keypair_t blinded_kp;
+  hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc));
+
+  desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX;
+
+  /* Copy only the public key into the descriptor. */
+  memcpy(&desc->plaintext_data.signing_pubkey, &signing_kp->pubkey,
+         sizeof(ed25519_public_key_t));
+
+  ret = ed25519_keypair_generate(&blinded_kp, 0);
+  tt_int_op(ret, ==, 0);
+  /* Copy only the public key into the descriptor. */
+  memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey,
+         sizeof(ed25519_public_key_t));
+
+  desc->plaintext_data.signing_key_cert =
+    tor_cert_create(&blinded_kp, CERT_TYPE_SIGNING_HS_DESC,
+                    &signing_kp->pubkey, now, 3600,
+                    CERT_FLAG_INCLUDE_SIGNING_KEY);
+  tt_assert(desc->plaintext_data.signing_key_cert);
+  desc->plaintext_data.revision_counter = 42;
+  desc->plaintext_data.lifetime_sec = 3 * 60 * 60;
+
+  /* Setup encrypted data section. */
+  desc->encrypted_data.create2_ntor = 1;
+  desc->encrypted_data.intro_auth_types = smartlist_new();
+  desc->encrypted_data.single_onion_service = 1;
+  smartlist_add(desc->encrypted_data.intro_auth_types, tor_strdup("ed25519"));
+  desc->encrypted_data.intro_points = smartlist_new();
+  if (!no_ip) {
+    /* Add four intro points. */
+    smartlist_add(desc->encrypted_data.intro_points,
+              hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0));
+    smartlist_add(desc->encrypted_data.intro_points,
+              hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0));
+    smartlist_add(desc->encrypted_data.intro_points,
+              hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1));
+    smartlist_add(desc->encrypted_data.intro_points,
+              hs_helper_build_intro_point(signing_kp, now, "", 1));
+  }
+
+  descp = desc;
+ done:
+  return descp;
+}
+
+/* Build a descriptor with introduction points. */
+hs_descriptor_t *
+hs_helper_build_hs_desc_with_ip(const ed25519_keypair_t *signing_kp)
+{
+  return hs_helper_build_hs_desc_impl(0, signing_kp);
+}
+
+/* Build a descriptor without any introduction points. */
+hs_descriptor_t *
+hs_helper_build_hs_desc_no_ip(const ed25519_keypair_t *signing_kp)
+{
+  return hs_helper_build_hs_desc_impl(1, signing_kp);
+}
+
+void
+hs_helper_desc_equal(const hs_descriptor_t *desc1,
+                     const hs_descriptor_t *desc2)
+{
+  char *addr1 = NULL, *addr2 = NULL;
+  /* Plaintext data section. */
+  tt_int_op(desc1->plaintext_data.version, OP_EQ,
+            desc2->plaintext_data.version);
+  tt_uint_op(desc1->plaintext_data.lifetime_sec, OP_EQ,
+             desc2->plaintext_data.lifetime_sec);
+  tt_assert(tor_cert_eq(desc1->plaintext_data.signing_key_cert,
+                        desc2->plaintext_data.signing_key_cert));
+  tt_mem_op(desc1->plaintext_data.signing_pubkey.pubkey, OP_EQ,
+            desc2->plaintext_data.signing_pubkey.pubkey,
+            ED25519_PUBKEY_LEN);
+  tt_mem_op(desc1->plaintext_data.blinded_pubkey.pubkey, OP_EQ,
+            desc2->plaintext_data.blinded_pubkey.pubkey,
+            ED25519_PUBKEY_LEN);
+  tt_u64_op(desc1->plaintext_data.revision_counter, ==,
+            desc2->plaintext_data.revision_counter);
+
+  /* NOTE: We can't compare the encrypted blob because when encoding the
+   * descriptor, the object is immutable thus we don't update it with the
+   * encrypted blob. As contrast to the decoding process where we populate a
+   * descriptor object. */
+
+  /* Encrypted data section. */
+  tt_uint_op(desc1->encrypted_data.create2_ntor, ==,
+             desc2->encrypted_data.create2_ntor);
+
+  /* Authentication type. */
+  tt_int_op(!!desc1->encrypted_data.intro_auth_types, ==,
+            !!desc2->encrypted_data.intro_auth_types);
+  if (desc1->encrypted_data.intro_auth_types &&
+      desc2->encrypted_data.intro_auth_types) {
+    tt_int_op(smartlist_len(desc1->encrypted_data.intro_auth_types), ==,
+              smartlist_len(desc2->encrypted_data.intro_auth_types));
+    for (int i = 0;
+         i < smartlist_len(desc1->encrypted_data.intro_auth_types);
+         i++) {
+      tt_str_op(smartlist_get(desc1->encrypted_data.intro_auth_types, i),OP_EQ,
+                smartlist_get(desc2->encrypted_data.intro_auth_types, i));
+    }
+  }
+
+  /* Introduction points. */
+  {
+    tt_assert(desc1->encrypted_data.intro_points);
+    tt_assert(desc2->encrypted_data.intro_points);
+    tt_int_op(smartlist_len(desc1->encrypted_data.intro_points), ==,
+              smartlist_len(desc2->encrypted_data.intro_points));
+    for (int i=0; i < smartlist_len(desc1->encrypted_data.intro_points); i++) {
+      hs_desc_intro_point_t *ip1 = smartlist_get(desc1->encrypted_data
+                                                 .intro_points, i),
+                            *ip2 = smartlist_get(desc2->encrypted_data
+                                                 .intro_points, i);
+      tt_assert(tor_cert_eq(ip1->auth_key_cert, ip2->auth_key_cert));
+      tt_int_op(ip1->enc_key_type, OP_EQ, ip2->enc_key_type);
+      tt_assert(ip1->enc_key_type == HS_DESC_KEY_TYPE_LEGACY ||
+                ip1->enc_key_type == HS_DESC_KEY_TYPE_CURVE25519);
+      switch (ip1->enc_key_type) {
+        case HS_DESC_KEY_TYPE_LEGACY:
+          tt_int_op(crypto_pk_cmp_keys(ip1->enc_key.legacy,
+                                       ip2->enc_key.legacy), OP_EQ, 0);
+          break;
+        case HS_DESC_KEY_TYPE_CURVE25519:
+          tt_mem_op(ip1->enc_key.curve25519.pubkey.public_key, OP_EQ,
+                    ip2->enc_key.curve25519.pubkey.public_key,
+                    CURVE25519_PUBKEY_LEN);
+          break;
+      }
+
+      tt_int_op(smartlist_len(ip1->link_specifiers), ==,
+                smartlist_len(ip2->link_specifiers));
+      for (int j = 0; j < smartlist_len(ip1->link_specifiers); j++) {
+        hs_desc_link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j),
+                                 *ls2 = smartlist_get(ip2->link_specifiers, j);
+        tt_int_op(ls1->type, ==, ls2->type);
+        switch (ls1->type) {
+          case LS_IPV4:
+          case LS_IPV6:
+            {
+              addr1 = tor_addr_to_str_dup(&ls1->u.ap.addr);
+              addr2 = tor_addr_to_str_dup(&ls2->u.ap.addr);
+              tt_str_op(addr1, OP_EQ, addr2);
+              tor_free(addr1);
+              tor_free(addr2);
+              tt_int_op(ls1->u.ap.port, ==, ls2->u.ap.port);
+            }
+            break;
+          case LS_LEGACY_ID:
+            tt_mem_op(ls1->u.legacy_id, OP_EQ, ls2->u.legacy_id,
+                      sizeof(ls1->u.legacy_id));
+            break;
+          default:
+            /* Unknown type, caught it and print its value. */
+            tt_int_op(ls1->type, OP_EQ, -1);
+        }
+      }
+    }
+  }
+
+ done:
+  tor_free(addr1);
+  tor_free(addr2);
+}
+
diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..a7fedab13600b0d22b5a8af970fa37760323065e
--- /dev/null
+++ b/src/test/hs_test_helpers.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_HS_TEST_HELPERS_H
+#define TOR_HS_TEST_HELPERS_H
+
+#include "ed25519_cert.h"
+#include "hs_descriptor.h"
+
+/* Set of functions to help build and test descriptors. */
+hs_desc_intro_point_t *hs_helper_build_intro_point(
+                          const ed25519_keypair_t *signing_kp, time_t now,
+                          const char *addr, int legacy);
+hs_descriptor_t *hs_helper_build_hs_desc_no_ip(
+                                 const ed25519_keypair_t *signing_kp);
+hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
+                                 const ed25519_keypair_t *signing_kp);
+void hs_helper_desc_equal(const hs_descriptor_t *desc1,
+                          const hs_descriptor_t *desc2);
+
+#endif /* TOR_HS_TEST_HELPERS_H */
+
diff --git a/src/test/include.am b/src/test/include.am
index ee14d0651b9fa6b4497fe5cfa5b9a279c73cd5dd..c166d3b0fe1181e6969ab444d9e2fbe9e5728659 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -69,6 +69,7 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
 
 src_test_test_SOURCES = \
 	src/test/log_test_helpers.c \
+	src/test/hs_test_helpers.c \
 	src/test/rend_test_helpers.c \
 	src/test/test.c \
 	src/test/test_accounting.c \
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index bc4bfabde91e0be2d43fa040a8c348fe8691a727..40f50b322a491aab39614392222f95c91c1084b2 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -15,96 +15,10 @@
 #include "directory.h"
 #include "connection.h"
 
+#include "hs_test_helpers.h"
 #include "test_helpers.h"
 #include "test.h"
 
-/* Build an intro point using a blinded key and an address. */
-static hs_desc_intro_point_t *
-helper_build_intro_point(const ed25519_keypair_t *blinded_kp,
-                         const char *addr)
-{
-  int ret;
-  ed25519_keypair_t auth_kp;
-  hs_desc_intro_point_t *intro_point = NULL;
-  hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip));
-  ip->link_specifiers = smartlist_new();
-
-  {
-    hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls));
-    ls->u.ap.port = 9001;
-    int family = tor_addr_parse(&ls->u.ap.addr, addr);
-    switch (family) {
-    case AF_INET:
-      ls->type = LS_IPV4;
-      break;
-    case AF_INET6:
-      ls->type = LS_IPV6;
-      break;
-    default:
-      /* Stop the test, not suppose to have an error. */
-      tt_int_op(family, OP_EQ, AF_INET);
-    }
-    smartlist_add(ip->link_specifiers, ls);
-  }
-
-  ret = ed25519_keypair_generate(&auth_kp, 0);
-  tt_int_op(ret, ==, 0);
-  ip->auth_key_cert = tor_cert_create(blinded_kp, CERT_TYPE_AUTH_HS_IP_KEY,
-                                      &auth_kp.pubkey, time(NULL),
-                                      HS_DESC_CERT_LIFETIME,
-                                      CERT_FLAG_INCLUDE_SIGNING_KEY);
-  tt_assert(ip->auth_key_cert);
-
-  ret = curve25519_keypair_generate(&ip->enc_key.curve25519, 0);
-  tt_int_op(ret, ==, 0);
-  ip->enc_key_type = HS_DESC_KEY_TYPE_CURVE25519;
-  intro_point = ip;
- done:
-  return intro_point;
-}
-
-/* Return a valid hs_descriptor_t object. */
-static hs_descriptor_t *
-helper_build_hs_desc(uint64_t revision_counter, uint32_t lifetime,
-                     ed25519_public_key_t *signing_pubkey)
-{
-  int ret;
-  ed25519_keypair_t blinded_kp;
-  hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc));
-
-  desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX;
-
-  /* Copy only the public key into the descriptor. */
-  memcpy(&desc->plaintext_data.signing_pubkey, signing_pubkey,
-         sizeof(ed25519_public_key_t));
-
-  ret = ed25519_keypair_generate(&blinded_kp, 0);
-  tt_int_op(ret, ==, 0);
-  /* Copy only the public key into the descriptor. */
-  memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey,
-         sizeof(ed25519_public_key_t));
-
-  desc->plaintext_data.signing_key_cert =
-    tor_cert_create(&blinded_kp, CERT_TYPE_SIGNING_HS_DESC, signing_pubkey,
-                    time(NULL), 3600, CERT_FLAG_INCLUDE_SIGNING_KEY);
-  tt_assert(desc->plaintext_data.signing_key_cert);
-  desc->plaintext_data.revision_counter = revision_counter;
-  desc->plaintext_data.lifetime_sec = lifetime;
-
-  /* Setup encrypted data section. */
-  desc->encrypted_data.create2_ntor = 1;
-  desc->encrypted_data.intro_auth_types = smartlist_new();
-  smartlist_add(desc->encrypted_data.intro_auth_types, tor_strdup("ed25519"));
-  desc->encrypted_data.intro_points = smartlist_new();
-  /* Add an intro point. */
-  smartlist_add(desc->encrypted_data.intro_points,
-                helper_build_intro_point(&blinded_kp, "1.2.3.4"));
-
-  descp = desc;
- done:
-  return descp;
-}
-
 /* Static variable used to encoded the HSDir query. */
 static char query_b64[256];
 
@@ -141,7 +55,7 @@ test_directory(void *arg)
   /* Generate a valid descriptor with normal values. */
   ret = ed25519_keypair_generate(&signing_kp1, 0);
   tt_int_op(ret, ==, 0);
-  desc1 = helper_build_hs_desc(42, 3 * 60 * 60, &signing_kp1.pubkey);
+  desc1 = hs_helper_build_hs_desc_with_ip(&signing_kp1);
   tt_assert(desc1);
   ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str);
   tt_int_op(ret, OP_EQ, 0);
@@ -175,8 +89,10 @@ test_directory(void *arg)
     ret = ed25519_keypair_generate(&signing_kp_zero, 0);
     tt_int_op(ret, ==, 0);
     hs_descriptor_t *desc_zero_lifetime;
-    desc_zero_lifetime = helper_build_hs_desc(1, 0, &signing_kp_zero.pubkey);
+    desc_zero_lifetime = hs_helper_build_hs_desc_with_ip(&signing_kp_zero);
     tt_assert(desc_zero_lifetime);
+    desc_zero_lifetime->plaintext_data.revision_counter = 1;
+    desc_zero_lifetime->plaintext_data.lifetime_sec = 0;
     char *desc_zero_lifetime_str;
     ret = hs_desc_encode_descriptor(desc_zero_lifetime, &signing_kp_zero,
                                     &desc_zero_lifetime_str);
@@ -262,7 +178,7 @@ test_clean_as_dir(void *arg)
   /* Generate a valid descriptor with values. */
   ret = ed25519_keypair_generate(&signing_kp1, 0);
   tt_int_op(ret, ==, 0);
-  desc1 = helper_build_hs_desc(42, 3 * 60 * 60, &signing_kp1.pubkey);
+  desc1 = hs_helper_build_hs_desc_with_ip(&signing_kp1);
   tt_assert(desc1);
   ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str);
   tt_int_op(ret, OP_EQ, 0);
@@ -375,7 +291,7 @@ test_upload_and_download_hs_desc(void *arg)
     ed25519_keypair_t signing_kp;
     retval = ed25519_keypair_generate(&signing_kp, 0);
     tt_int_op(retval, ==, 0);
-    published_desc = helper_build_hs_desc(42, 3 * 60 * 60, &signing_kp.pubkey);
+    published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
     tt_assert(published_desc);
     retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
                                        &published_desc_str);
@@ -438,8 +354,7 @@ test_hsdir_revision_counter_check(void *arg)
   {
     retval = ed25519_keypair_generate(&signing_kp, 0);
     tt_int_op(retval, ==, 0);
-    published_desc = helper_build_hs_desc(1312, 3 * 60 * 60,
-                                          &signing_kp.pubkey);
+    published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
     tt_assert(published_desc);
     retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
                                        &published_desc_str);
@@ -470,7 +385,7 @@ test_hsdir_revision_counter_check(void *arg)
     tt_assert(received_desc);
 
     /* Check that the revision counter is correct */
-    tt_u64_op(received_desc->plaintext_data.revision_counter, ==, 1312);
+    tt_u64_op(received_desc->plaintext_data.revision_counter, ==, 42);
 
     hs_descriptor_free(received_desc);
     received_desc = NULL;
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index a1a1f14fb8527e2b47bd523332f4eadd54c02450..9b364dbc299b27882188e80c96f304754cd22e18 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -15,227 +15,10 @@
 #include "test.h"
 #include "torcert.h"
 
+#include "hs_test_helpers.h"
 #include "test_helpers.h"
 #include "log_test_helpers.h"
 
-static hs_desc_intro_point_t *
-helper_build_intro_point(const ed25519_keypair_t *blinded_kp, time_t now,
-                         const char *addr, int legacy)
-{
-  int ret;
-  ed25519_keypair_t auth_kp;
-  hs_desc_intro_point_t *intro_point = NULL;
-  hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip));
-  ip->link_specifiers = smartlist_new();
-
-  {
-    hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls));
-    if (legacy) {
-      ls->type = LS_LEGACY_ID;
-      memcpy(ls->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8",
-             DIGEST_LEN);
-    } else {
-      ls->u.ap.port = 9001;
-      int family = tor_addr_parse(&ls->u.ap.addr, addr);
-      switch (family) {
-      case AF_INET:
-        ls->type = LS_IPV4;
-        break;
-      case AF_INET6:
-        ls->type = LS_IPV6;
-        break;
-      default:
-        /* Stop the test, not suppose to have an error. */
-        tt_int_op(family, OP_EQ, AF_INET);
-      }
-    }
-    smartlist_add(ip->link_specifiers, ls);
-  }
-
-  ret = ed25519_keypair_generate(&auth_kp, 0);
-  tt_int_op(ret, ==, 0);
-  ip->auth_key_cert = tor_cert_create(blinded_kp, CERT_TYPE_AUTH_HS_IP_KEY,
-                                      &auth_kp.pubkey, now,
-                                      HS_DESC_CERT_LIFETIME,
-                                      CERT_FLAG_INCLUDE_SIGNING_KEY);
-  tt_assert(ip->auth_key_cert);
-
-  if (legacy) {
-    ip->enc_key.legacy = crypto_pk_new();
-    ip->enc_key_type = HS_DESC_KEY_TYPE_LEGACY;
-    tt_assert(ip->enc_key.legacy);
-    ret = crypto_pk_generate_key(ip->enc_key.legacy);
-    tt_int_op(ret, ==, 0);
-  } else {
-    ret = curve25519_keypair_generate(&ip->enc_key.curve25519, 0);
-    tt_int_op(ret, ==, 0);
-    ip->enc_key_type = HS_DESC_KEY_TYPE_CURVE25519;
-  }
-
-  intro_point = ip;
- done:
-  return intro_point;
-}
-
-/* Return a valid hs_descriptor_t object. If no_ip is set, no introduction
- * points are added. */
-static hs_descriptor_t *
-helper_build_hs_desc(unsigned int no_ip, ed25519_public_key_t *signing_pubkey)
-{
-  int ret;
-  time_t now = time(NULL);
-  ed25519_keypair_t blinded_kp;
-  hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc));
-
-  desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX;
-
-  /* Copy only the public key into the descriptor. */
-  memcpy(&desc->plaintext_data.signing_pubkey, signing_pubkey,
-         sizeof(ed25519_public_key_t));
-
-  ret = ed25519_keypair_generate(&blinded_kp, 0);
-  tt_int_op(ret, ==, 0);
-  /* Copy only the public key into the descriptor. */
-  memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey,
-         sizeof(ed25519_public_key_t));
-
-  desc->plaintext_data.signing_key_cert =
-    tor_cert_create(&blinded_kp, CERT_TYPE_SIGNING_HS_DESC, signing_pubkey,
-                    now, 3600, CERT_FLAG_INCLUDE_SIGNING_KEY);
-  tt_assert(desc->plaintext_data.signing_key_cert);
-  desc->plaintext_data.revision_counter = 42;
-  desc->plaintext_data.lifetime_sec = 3 * 60 * 60;
-
-  /* Setup encrypted data section. */
-  desc->encrypted_data.create2_ntor = 1;
-  desc->encrypted_data.intro_auth_types = smartlist_new();
-  desc->encrypted_data.single_onion_service = 1;
-  smartlist_add(desc->encrypted_data.intro_auth_types, tor_strdup("ed25519"));
-  desc->encrypted_data.intro_points = smartlist_new();
-  if (!no_ip) {
-    /* Add four intro points. */
-    smartlist_add(desc->encrypted_data.intro_points,
-                helper_build_intro_point(&blinded_kp, now, "1.2.3.4", 0));
-    smartlist_add(desc->encrypted_data.intro_points,
-                helper_build_intro_point(&blinded_kp, now, "[2600::1]", 0));
-    smartlist_add(desc->encrypted_data.intro_points,
-                helper_build_intro_point(&blinded_kp, now, "3.2.1.4", 1));
-    smartlist_add(desc->encrypted_data.intro_points,
-                helper_build_intro_point(&blinded_kp, now, "", 1));
-  }
-
-  descp = desc;
- done:
-  return descp;
-}
-
-static void
-helper_compare_hs_desc(const hs_descriptor_t *desc1,
-                       const hs_descriptor_t *desc2)
-{
-  char *addr1 = NULL, *addr2 = NULL;
-  /* Plaintext data section. */
-  tt_int_op(desc1->plaintext_data.version, OP_EQ,
-            desc2->plaintext_data.version);
-  tt_uint_op(desc1->plaintext_data.lifetime_sec, OP_EQ,
-             desc2->plaintext_data.lifetime_sec);
-  tt_assert(tor_cert_eq(desc1->plaintext_data.signing_key_cert,
-                        desc2->plaintext_data.signing_key_cert));
-  tt_mem_op(desc1->plaintext_data.signing_pubkey.pubkey, OP_EQ,
-            desc2->plaintext_data.signing_pubkey.pubkey,
-            ED25519_PUBKEY_LEN);
-  tt_mem_op(desc1->plaintext_data.blinded_pubkey.pubkey, OP_EQ,
-            desc2->plaintext_data.blinded_pubkey.pubkey,
-            ED25519_PUBKEY_LEN);
-  tt_u64_op(desc1->plaintext_data.revision_counter, ==,
-             desc2->plaintext_data.revision_counter);
-
-  /* NOTE: We can't compare the encrypted blob because when encoding the
-   * descriptor, the object is immutable thus we don't update it with the
-   * encrypted blob. As contrast to the decoding process where we populate a
-   * descriptor object. */
-
-  /* Encrypted data section. */
-  tt_uint_op(desc1->encrypted_data.create2_ntor, ==,
-             desc2->encrypted_data.create2_ntor);
-
-  /* Authentication type. */
-  tt_int_op(!!desc1->encrypted_data.intro_auth_types, ==,
-            !!desc2->encrypted_data.intro_auth_types);
-  if (desc1->encrypted_data.intro_auth_types &&
-      desc2->encrypted_data.intro_auth_types) {
-    tt_int_op(smartlist_len(desc1->encrypted_data.intro_auth_types), ==,
-              smartlist_len(desc2->encrypted_data.intro_auth_types));
-    for (int i = 0;
-         i < smartlist_len(desc1->encrypted_data.intro_auth_types);
-         i++) {
-      tt_str_op(smartlist_get(desc1->encrypted_data.intro_auth_types, i),OP_EQ,
-                smartlist_get(desc2->encrypted_data.intro_auth_types, i));
-    }
-  }
-
-  /* Introduction points. */
-  {
-    tt_assert(desc1->encrypted_data.intro_points);
-    tt_assert(desc2->encrypted_data.intro_points);
-    tt_int_op(smartlist_len(desc1->encrypted_data.intro_points), ==,
-              smartlist_len(desc2->encrypted_data.intro_points));
-    for (int i=0; i < smartlist_len(desc1->encrypted_data.intro_points); i++) {
-      hs_desc_intro_point_t *ip1 = smartlist_get(desc1->encrypted_data
-                                                 .intro_points, i),
-                            *ip2 = smartlist_get(desc2->encrypted_data
-                                                 .intro_points, i);
-      tt_assert(tor_cert_eq(ip1->auth_key_cert, ip2->auth_key_cert));
-      tt_int_op(ip1->enc_key_type, OP_EQ, ip2->enc_key_type);
-      tt_assert(ip1->enc_key_type == HS_DESC_KEY_TYPE_LEGACY ||
-                ip1->enc_key_type == HS_DESC_KEY_TYPE_CURVE25519);
-      switch (ip1->enc_key_type) {
-      case HS_DESC_KEY_TYPE_LEGACY:
-        tt_int_op(crypto_pk_cmp_keys(ip1->enc_key.legacy, ip2->enc_key.legacy),
-                  OP_EQ, 0);
-        break;
-      case HS_DESC_KEY_TYPE_CURVE25519:
-        tt_mem_op(ip1->enc_key.curve25519.pubkey.public_key, OP_EQ,
-                  ip2->enc_key.curve25519.pubkey.public_key,
-                  CURVE25519_PUBKEY_LEN);
-        break;
-      }
-
-      tt_int_op(smartlist_len(ip1->link_specifiers), ==,
-                smartlist_len(ip2->link_specifiers));
-      for (int j = 0; j < smartlist_len(ip1->link_specifiers); j++) {
-        hs_desc_link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j),
-                                 *ls2 = smartlist_get(ip2->link_specifiers, j);
-        tt_int_op(ls1->type, ==, ls2->type);
-        switch (ls1->type) {
-        case LS_IPV4:
-        case LS_IPV6:
-        {
-          addr1 = tor_addr_to_str_dup(&ls1->u.ap.addr);
-          addr2 = tor_addr_to_str_dup(&ls2->u.ap.addr);
-          tt_str_op(addr1, OP_EQ, addr2);
-          tor_free(addr1);
-          tor_free(addr2);
-          tt_int_op(ls1->u.ap.port, ==, ls2->u.ap.port);
-        }
-        break;
-        case LS_LEGACY_ID:
-          tt_mem_op(ls1->u.legacy_id, OP_EQ, ls2->u.legacy_id,
-                    sizeof(ls1->u.legacy_id));
-          break;
-        default:
-          /* Unknown type, caught it and print its value. */
-          tt_int_op(ls1->type, OP_EQ, -1);
-        }
-      }
-    }
-  }
-
- done:
-  tor_free(addr1);
-  tor_free(addr2);
-}
-
 /* Test certificate encoding put in a descriptor. */
 static void
 test_cert_encoding(void *arg)
@@ -494,7 +277,7 @@ test_encode_descriptor(void *arg)
 
   ret = ed25519_keypair_generate(&signing_kp, 0);
   tt_int_op(ret, ==, 0);
-  desc = helper_build_hs_desc(0, &signing_kp.pubkey);
+  desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
   ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded);
   tt_int_op(ret, ==, 0);
   tt_assert(encoded);
@@ -518,7 +301,7 @@ test_decode_descriptor(void *arg)
 
   ret = ed25519_keypair_generate(&signing_kp, 0);
   tt_int_op(ret, ==, 0);
-  desc = helper_build_hs_desc(0, &signing_kp.pubkey);
+  desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
 
   /* Give some bad stuff to the decoding function. */
   ret = hs_desc_decode_descriptor("hladfjlkjadf", NULL, &decoded);
@@ -532,14 +315,14 @@ test_decode_descriptor(void *arg)
   tt_int_op(ret, ==, 0);
   tt_assert(decoded);
 
-  helper_compare_hs_desc(desc, decoded);
+  hs_helper_desc_equal(desc, decoded);
 
   /* Decode a descriptor with _no_ introduction points. */
   {
     ed25519_keypair_t signing_kp_no_ip;
     ret = ed25519_keypair_generate(&signing_kp_no_ip, 0);
     tt_int_op(ret, ==, 0);
-    desc_no_ip = helper_build_hs_desc(1, &signing_kp_no_ip.pubkey);
+    desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip);
     tt_assert(desc_no_ip);
     tor_free(encoded);
     ret = hs_desc_encode_descriptor(desc_no_ip, &signing_kp_no_ip, &encoded);
@@ -660,7 +443,7 @@ test_decode_intro_point(void *arg)
     char *line;
     ret = ed25519_keypair_generate(&signing_kp, 0);
     tt_int_op(ret, ==, 0);
-    desc = helper_build_hs_desc(0, &signing_kp.pubkey);
+    desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
     tt_assert(desc);
     /* Only try to decode an incomplete introduction point section. */
     tor_asprintf(&line, "\n%s", intro_point);
@@ -690,7 +473,7 @@ test_decode_intro_point(void *arg)
     desc = NULL;
     ret = ed25519_keypair_generate(&signing_kp, 0);
     tt_int_op(ret, ==, 0);
-    desc = helper_build_hs_desc(0, &signing_kp.pubkey);
+    desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
     const char *junk = "this is not a descriptor";
     ip = decode_introduction_point(desc, junk);
     tt_assert(!ip);