aboutsummaryrefslogtreecommitdiffstats
path: root/cicn-plugin/cicn/test/test_cicn_hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'cicn-plugin/cicn/test/test_cicn_hash.c')
-rw-r--r--cicn-plugin/cicn/test/test_cicn_hash.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/cicn-plugin/cicn/test/test_cicn_hash.c b/cicn-plugin/cicn/test/test_cicn_hash.c
new file mode 100644
index 00000000..a8ffac21
--- /dev/null
+++ b/cicn-plugin/cicn/test/test_cicn_hash.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * UT for cicn plugin hash function, included in UT framework.
+ */
+
+#include "test_cicn_hash.h"
+
+static void test_hash_cdata_dump_all (void); // forward decl
+
+/*
+ * test cases
+ */
+test_cicn_hash_namedata_t thash_data[] = {
+ TEST_CICN_HASH_NAMEDATA ("/"),
+ TEST_CICN_HASH_NAMEDATA ("/1"),
+ TEST_CICN_HASH_NAMEDATA ("/1/2"),
+ TEST_CICN_HASH_NAMEDATA ("/1/2/3"),
+ TEST_CICN_HASH_NAMEDATA ("/1/2/3/4/5/6/7"),
+ TEST_CICN_HASH_NAMEDATA ("/1/2/3/4/5/6/7.1"),
+ TEST_CICN_HASH_NAMEDATA ("/1/2/3/4/5/6/7/8"),
+ TEST_CICN_HASH_NAMEDATA ("/1/2/3/4/5/6/7/8/9"),
+ TEST_CICN_HASH_NAMEDATA
+ ("/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20"),
+ TEST_CICN_HASH_NAMEDATA_FULL ("/ccnx/list/\001", 1 /*is_chunk_name */ ),
+};
+
+#define TEST_CICN_VERIFY(expr, assert_on_mismatch) \
+ do { \
+ if (assert_on_mismatch) { assert(expr); } \
+ else if (!(expr)) { goto done; } \
+ } while (0);
+
+
+static int
+test_cicn_hash_pfx_inf_compare (const uint8_t * name, uint16_t namelen,
+ const cicn_prefix_hashinf_t * pfx_info1,
+ const cicn_prefix_hashinf_t * pfx_info2,
+ int is_full_name, int assert_on_mismatch)
+{
+ int i;
+
+ int ret = EINVAL;
+ TEST_CICN_VERIFY (pfx_info1->pfx_count == pfx_info2->pfx_count,
+ assert_on_mismatch);
+ TEST_CICN_VERIFY (pfx_info1->pfx_overflow == pfx_info2->pfx_overflow,
+ assert_on_mismatch);
+
+ for (i = 0; i < pfx_info1->pfx_count; i++)
+ {
+ TEST_CICN_VERIFY (pfx_info1->pfx_lens[i] == pfx_info2->pfx_lens[i],
+ assert_on_mismatch);
+ TEST_CICN_VERIFY (pfx_info1->pfx_hashes[i] == pfx_info2->pfx_hashes[i],
+ assert_on_mismatch);
+ if (i == pfx_info1->pfx_count - 1)
+ { // verify chunk comp handling
+ if (pfx_info1->pfx_lens[i] == pfx_info1->pfx_len)
+ {
+ break; // parsed whole name
+ }
+ if (pfx_info1->pfx_overflow)
+ {
+ break; // quit early on overflow
+ }
+ /* quit early on (hashed component before) chunk component */
+ int chunk_comp_idx =
+ pfx_info1->pfx_lens[i] + (is_full_name ? CICN_TLV_HDR_LEN : 0);
+ uint16_t type;
+ C_GETINT16 (type, &name[chunk_comp_idx]);
+ TEST_CICN_VERIFY (type == CICN_NAME_COMP_CHUNK, assert_on_mismatch);
+ }
+ }
+
+ if (is_full_name)
+ {
+ TEST_CICN_VERIFY (pfx_info1->pfx_full_hash == pfx_info2->pfx_full_hash,
+ assert_on_mismatch);
+ }
+
+ ret = AOK;
+
+done:
+ return (ret);
+}
+
+/*
+ * Version of cicn_hashtb_hash_prefixes() that calculates hash of
+ * each prefix by doing an independent hash from the beginning of the
+ * bytestring.
+ */
+static int
+test_cicn_hashtb_hash_prefixes_nonincr (const uint8_t * name,
+ uint16_t namelen, int is_full_name,
+ cicn_prefix_hashinf_t * pfx,
+ int limit)
+{
+ int i, ret = EINVAL;
+ const uint8_t *name_end, *pfx_start, *pfx_end;
+ uint16_t type = CICN_NAME_COMP, tlen;
+
+ /* Must point to something, and it must be at least as long
+ * as an empty name or name-comp
+ */
+ if ((name == NULL) || (namelen < 4) || (pfx == NULL))
+ {
+ goto done;
+ }
+
+ /* Establish sane limit on number of comps */
+ if (limit == 0 || limit > CICN_HASHTB_MAX_NAME_COMPS)
+ {
+ limit = CICN_HASHTB_MAX_NAME_COMPS;
+ }
+
+ name_end = name + namelen;
+
+ /* Hash the name-comp prefixes first */
+ if (is_full_name)
+ {
+ pfx_start = name + 4;
+
+ /* Capture tlv pointer and len in the context struct */
+ pfx->pfx_ptr = name + 4;
+ pfx->pfx_len = namelen - 4;
+ }
+ else
+ {
+ pfx_start = name;
+
+ /* Capture tlv pointer and len in the context struct */
+ pfx->pfx_ptr = name;
+ pfx->pfx_len = namelen;
+ }
+
+ pfx_end = pfx_start;
+
+ /* We hash each sub-set of the input name, all the way to the end. This
+ * means that in the case of a fib prefix, for example, the _last_
+ * sub-prefix hash is the hash of the full prefix.
+ */
+ for (i = 0; (i < limit) && (pfx_end < name_end); i++)
+ {
+ /* Double-check: in order to advance 'end' to another component tlv,
+ * there must be a min number of bytes left
+ */
+ if ((name_end - pfx_end) < 4)
+ {
+ /* Whoops - that's not valid */
+ goto done;
+ }
+
+ /* Advance 'end' to the next name-comp */
+ C_GETINT16 (type, pfx_end);
+ if (type == CICN_NAME_COMP_CHUNK)
+ {
+ /* Special-case, chunk/sequence not part of routeable prefix */
+ break;
+ }
+
+ pfx_end += 2;
+
+ C_GETINT16 (tlen, pfx_end);
+
+ pfx_end += (2 + tlen);
+ if (pfx_end > name_end)
+ {
+ /* Whoops - that's bad, sub-tlv shouldn't have overrun */
+ break;
+ }
+
+ /* And compute prefix's hash */
+ pfx->pfx_lens[i] = pfx_end - pfx_start;
+ pfx->pfx_hashes[i] =
+ cicn_hashtb_hash_bytestring (pfx_start, pfx_end - pfx_start);
+ }
+
+ if (pfx_end > name_end)
+ {
+ /* Whoops - that's bad, sub-tlv shouldn't have overrun */
+ goto done;
+ }
+
+ pfx->pfx_count = i;
+ pfx->pfx_overflow =
+ (pfx_end < name_end && type != CICN_NAME_COMP_CHUNK) ? 1 : 0;
+
+ /* If needed, compute the full-name hash */
+ if (is_full_name)
+ {
+ pfx->pfx_full_hash = cicn_hashtb_hash_name (name, namelen);
+ }
+
+ if (pfx->pfx_overflow && limit == CICN_HASHTB_MAX_NAME_COMPS)
+ {
+ ret = ENOSPC;
+ goto done;
+ }
+
+ ret = AOK;
+
+done:
+ return (ret);
+}
+
+/*
+ * Run test on a single case
+ */
+int
+test_cicn_hash_hd (test_cicn_hash_namedata_t * hnd,
+ const test_cicn_hash_namedata_t * hn_cdata)
+{
+ int ret = EINVAL;
+ int len;
+ cicn_rd_t cicn_rd;
+
+ uint8_t buf[1024];
+ cicn_prefix_hashinf_t *pfx_hi1 = &hnd->th_pfx_hi;
+ cicn_prefix_hashinf_t pfx_hi2;
+
+ len =
+ cicn_parse_name_from_str (buf, sizeof (buf), hnd->th_name,
+ hnd->th_is_chunk_name, &cicn_rd);
+ if (len <= 0)
+ {
+ goto done;
+ }
+
+ int ret1 =
+ cicn_hashtb_hash_prefixes (buf, len, TRUE /*fullname */ , pfx_hi1,
+ 0 /*!limit */ );
+ switch (ret1)
+ {
+ case AOK:
+ break;
+ case ENOSPC:
+ if (pfx_hi1->pfx_count != ARRAY_LEN (pfx_hi1->pfx_hashes))
+ {
+ goto done;
+ }
+ break;
+ default:
+ goto done;
+ }
+
+ int ret2 =
+ test_cicn_hashtb_hash_prefixes_nonincr (buf, len, TRUE /*fullname */ ,
+ &pfx_hi2, 0 /*!limit */ );
+ switch (ret2)
+ {
+ case AOK:
+ break;
+ case ENOSPC:
+ if (pfx_hi2.pfx_count != ARRAY_LEN (pfx_hi2.pfx_hashes))
+ {
+ goto done;
+ }
+ break;
+ default:
+ goto done;
+ }
+
+ if (ret1 != ret2)
+ {
+ goto done;
+ }
+ ret = test_cicn_hash_pfx_inf_compare (buf, len,
+ pfx_hi1, &hn_cdata->th_pfx_hi,
+ 1 /*is_full_name */ ,
+ 0 /*!assert_on_mismatch */ );
+
+ if (ret != AOK)
+ {
+ goto done;
+ }
+
+ ret = test_cicn_hash_pfx_inf_compare (buf, len,
+ &pfx_hi2, &hn_cdata->th_pfx_hi,
+ 1 /*is_full_name */ ,
+ 0 /*!assert_on_mismatch */ );
+
+ if (ret != AOK)
+ {
+ goto done;
+ }
+
+done:
+ return (ret);
+}
+
+/*
+ * Run all test cases
+ */
+int
+test_cicn_hash_suite (cicn_api_test_suite_results_t * tr,
+ test_cicn_running_t * running)
+{
+ int i;
+ if (0)
+ { // temporarily enable for adding new test cases
+ test_hash_cdata_dump_all ();
+ return (AOK);
+ }
+ for (i = 0; i < ARRAY_LEN (thash_data); i++)
+ {
+ int ret = test_cicn_hash_hd (&thash_data[i], &hash_namedata_cdata[i]);
+ test_cicn_result_record (tr, ret, running);
+ }
+ return (AOK);
+}
+
+/*
+ * Part of routines to generate "known good" output in test_cicn_hash_cdata.c.
+ */
+static void
+test_hash_cdata_dump_hnd (test_cicn_hash_namedata_t * hdn, int indent)
+{
+ cicn_prefix_hashinf_t *pfx_hi = &hdn->th_pfx_hi;
+ int i;
+
+ printf ("%*s{ .th_name = \"", indent, "");
+ for (i = 0; i < hdn->th_namebytes; i++)
+ {
+ uint8_t c = hdn->th_name[i];
+ if (isprint (c))
+ {
+ printf ("%c", c);
+ }
+ else
+ {
+ printf ("\\%3.3o", c);
+ }
+ }
+ printf ("\",\n");
+ printf ("%*s .th_namebytes = %u,\n", indent, "", hdn->th_namebytes);
+ if (hdn->th_is_chunk_name)
+ {
+ printf ("%*s .th_is_chunk_name = %d,\n",
+ indent, "", hdn->th_is_chunk_name);
+ }
+ printf ("%*s .th_pfx_hi = {\n", indent, "");
+ printf ("%*s "
+ ".pfx_len = %" PRIu16 ", "
+ ".pfx_count = %" PRIu16 ", "
+ ".pfx_overflow = %" PRIu16 ", "
+ ".pfx_full_hash = %#" PRIx64 ",\n",
+ indent, "",
+ pfx_hi->pfx_len, pfx_hi->pfx_count, pfx_hi->pfx_overflow,
+ pfx_hi->pfx_full_hash);
+
+ printf ("%*s .pfx_lens = { ", indent, "");
+ for (i = 0; i < hdn->th_pfx_hi.pfx_count; i++)
+ {
+ printf ("%" PRIu16 ", ", pfx_hi->pfx_lens[i]);
+ }
+ printf ("},\n");
+
+ printf ("%*s .pfx_hashes = { ", indent, "");
+ for (i = 0; i < hdn->th_pfx_hi.pfx_count; i++)
+ {
+ printf ("%#" PRIx64 ", ", pfx_hi->pfx_hashes[i]);
+ }
+ printf ("}, },\n"); // terminate pfx_hashes, th_pfx_hi
+
+ printf ("%*s},\n", indent, ""); // terminate hdn
+}
+
+/*
+ * Part of routines to generate "known good" compare data in
+ * test_cicn_hash_cdata.c.
+ * Not called during normal UT execution, only when adding/changing
+ * test cases.
+ */
+static void
+test_hash_cdata_dump_all (void)
+{
+ int i;
+
+ printf ("\n"); // skip debug cli prompt, for easier cut-and-paste
+ printf ("test_cicn_hash_namedata_t hash_namedata_cdata[] = {\n");
+
+ for (i = 0; i < ARRAY_LEN (thash_data); i++)
+ {
+ test_cicn_hash_namedata_t *hnd = &thash_data[i];
+ uint8_t buf[1024];
+ int len;
+ int ret;
+ cicn_rd_t cicn_rd;
+
+ len =
+ cicn_parse_name_from_str (buf, sizeof (buf), hnd->th_name,
+ hnd->th_is_chunk_name, &cicn_rd);
+ ASSERT (len > 0);
+
+ ret =
+ test_cicn_hashtb_hash_prefixes_nonincr (buf, len, TRUE /*fullname */ ,
+ &hnd->th_pfx_hi,
+ 0 /*!limit */ );
+ ASSERT (ret == AOK || ret == ENOSPC);
+ test_hash_cdata_dump_hnd (hnd, 4 /*indent */ );
+ }
+
+ printf ("};\n");
+}