xref: /freebsd/contrib/expat/tests/hash_tests.c (revision c7b67985633c408cae69703ca443cbfd84d326a8)
1*c7b67985SPhilip Paeps /* Tests related to the hash tables used inside Expat
2*c7b67985SPhilip Paeps __  __            _
3*c7b67985SPhilip Paeps                          ___\ \/ /_ __   __ _| |_
4*c7b67985SPhilip Paeps                         / _ \\  /| '_ \ / _` | __|
5*c7b67985SPhilip Paeps                        |  __//  \| |_) | (_| | |_
6*c7b67985SPhilip Paeps                         \___/_/\_\ .__/ \__,_|\__|
7*c7b67985SPhilip Paeps                                  |_| XML parser
8*c7b67985SPhilip Paeps 
9*c7b67985SPhilip Paeps    Copyright (c) 2026 Sebastian Pipping <sebastian@pipping.org>
10*c7b67985SPhilip Paeps    Licensed under the MIT license:
11*c7b67985SPhilip Paeps 
12*c7b67985SPhilip Paeps    Permission is  hereby granted,  free of charge,  to any  person obtaining
13*c7b67985SPhilip Paeps    a  copy  of  this  software   and  associated  documentation  files  (the
14*c7b67985SPhilip Paeps    "Software"),  to  deal in  the  Software  without restriction,  including
15*c7b67985SPhilip Paeps    without  limitation the  rights  to use,  copy,  modify, merge,  publish,
16*c7b67985SPhilip Paeps    distribute, sublicense, and/or sell copies of the Software, and to permit
17*c7b67985SPhilip Paeps    persons  to whom  the Software  is  furnished to  do so,  subject to  the
18*c7b67985SPhilip Paeps    following conditions:
19*c7b67985SPhilip Paeps 
20*c7b67985SPhilip Paeps    The above copyright  notice and this permission notice  shall be included
21*c7b67985SPhilip Paeps    in all copies or substantial portions of the Software.
22*c7b67985SPhilip Paeps 
23*c7b67985SPhilip Paeps    THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
24*c7b67985SPhilip Paeps    EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
25*c7b67985SPhilip Paeps    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
26*c7b67985SPhilip Paeps    NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
27*c7b67985SPhilip Paeps    DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
28*c7b67985SPhilip Paeps    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
29*c7b67985SPhilip Paeps    USE OR OTHER DEALINGS IN THE SOFTWARE.
30*c7b67985SPhilip Paeps 
31*c7b67985SPhilip Paeps    SPDX-License-Identifier: MIT
32*c7b67985SPhilip Paeps */
33*c7b67985SPhilip Paeps 
34*c7b67985SPhilip Paeps #include "hash_tests.h"
35*c7b67985SPhilip Paeps 
36*c7b67985SPhilip Paeps #include "common.h" // for XCS
37*c7b67985SPhilip Paeps #include "expat.h"
38*c7b67985SPhilip Paeps #include "hash_table.h"
39*c7b67985SPhilip Paeps #include "minicheck.h"
40*c7b67985SPhilip Paeps 
41*c7b67985SPhilip Paeps #include <stdbool.h>
42*c7b67985SPhilip Paeps #include <string.h> // for memcmp
43*c7b67985SPhilip Paeps 
START_TEST(test_hash_table)44*c7b67985SPhilip Paeps START_TEST(test_hash_table) {
45*c7b67985SPhilip Paeps   // The test is not doing any parsing, so a single run
46*c7b67985SPhilip Paeps   // (with `g_chunkSize == 0`) is enough
47*c7b67985SPhilip Paeps   if (g_chunkSize != 0)
48*c7b67985SPhilip Paeps     return;
49*c7b67985SPhilip Paeps 
50*c7b67985SPhilip Paeps   typedef struct {
51*c7b67985SPhilip Paeps     const XML_Char *name;
52*c7b67985SPhilip Paeps     bool initialized;
53*c7b67985SPhilip Paeps   } NAME_AND_FLAG;
54*c7b67985SPhilip Paeps 
55*c7b67985SPhilip Paeps   HASH_TABLE table;
56*c7b67985SPhilip Paeps   XML_Parser parser = XML_ParserCreate(NULL);
57*c7b67985SPhilip Paeps   hashTableInit(&table, parser);
58*c7b67985SPhilip Paeps 
59*c7b67985SPhilip Paeps   const XML_Char *const key1 = XCS("key1");
60*c7b67985SPhilip Paeps   const XML_Char *const key2 = XCS("key2");
61*c7b67985SPhilip Paeps   const XML_Char *const key3 = XCS("key1overlap");
62*c7b67985SPhilip Paeps 
63*c7b67985SPhilip Paeps   // Self-test: `key3` starts with `key1` but is different from it
64*c7b67985SPhilip Paeps   assert_true(memcmp(key1, key3, keylen(key1)) == 0);
65*c7b67985SPhilip Paeps   assert_true(keyeq(key1, keylen(key1), key3) == XML_FALSE);
66*c7b67985SPhilip Paeps 
67*c7b67985SPhilip Paeps   // Test: Look up false for all keys because the table is empty
68*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key1, 0) == NULL);
69*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key2, 0) == NULL);
70*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key3, 0) == NULL);
71*c7b67985SPhilip Paeps 
72*c7b67985SPhilip Paeps   // Test: Iteration yields 0 items initially
73*c7b67985SPhilip Paeps   {
74*c7b67985SPhilip Paeps     HASH_TABLE_ITER iter1;
75*c7b67985SPhilip Paeps     hashTableIterInit(&iter1, &table);
76*c7b67985SPhilip Paeps     assert_true(hashTableIterNext(&iter1) == NULL);
77*c7b67985SPhilip Paeps   }
78*c7b67985SPhilip Paeps 
79*c7b67985SPhilip Paeps   // Test: Insertion works (including initialization to zero)
80*c7b67985SPhilip Paeps   NAME_AND_FLAG *const inserted1
81*c7b67985SPhilip Paeps       = (NAME_AND_FLAG *)lookup(parser, &table, key1, sizeof(NAME_AND_FLAG));
82*c7b67985SPhilip Paeps   assert_true(inserted1 != NULL);
83*c7b67985SPhilip Paeps   assert_true(inserted1->name == key1);
84*c7b67985SPhilip Paeps   assert_true(! inserted1->initialized);
85*c7b67985SPhilip Paeps 
86*c7b67985SPhilip Paeps   // Make it possible to tell the struct apart from a freshly inserted version
87*c7b67985SPhilip Paeps   inserted1->initialized = true;
88*c7b67985SPhilip Paeps 
89*c7b67985SPhilip Paeps   // Test: Only present keys can be looked up
90*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key1, 0) != NULL);
91*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key2, 0) == NULL);
92*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key3, 0) == NULL);
93*c7b67985SPhilip Paeps 
94*c7b67985SPhilip Paeps   // Test: Key length works without false positives
95*c7b67985SPhilip Paeps   assert_true(lookupWithLength(parser, &table, key3, /*nameLen=*/3, 0) == NULL);
96*c7b67985SPhilip Paeps   assert_true(lookupWithLength(parser, &table, key3, /*nameLen=*/4, 0) != NULL);
97*c7b67985SPhilip Paeps   assert_true(lookupWithLength(parser, &table, key3, /*nameLen=*/5, 0) == NULL);
98*c7b67985SPhilip Paeps 
99*c7b67985SPhilip Paeps   // TEST: Lookup does not reset existing entries to zeros
100*c7b67985SPhilip Paeps   NAME_AND_FLAG *const found
101*c7b67985SPhilip Paeps       = (NAME_AND_FLAG *)lookup(parser, &table, key1, sizeof(NAME_AND_FLAG));
102*c7b67985SPhilip Paeps   assert_true(found != NULL);
103*c7b67985SPhilip Paeps   assert_true(found->name == key1);
104*c7b67985SPhilip Paeps   assert_true(found->initialized); // this is key
105*c7b67985SPhilip Paeps 
106*c7b67985SPhilip Paeps   // Test: Insertion of a second item works
107*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key2, 0) == NULL);
108*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key2, sizeof(NAME_AND_FLAG)) != NULL);
109*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key2, 0) != NULL);
110*c7b67985SPhilip Paeps 
111*c7b67985SPhilip Paeps   // Test: Iteration yields nothing but the two expected items
112*c7b67985SPhilip Paeps   {
113*c7b67985SPhilip Paeps     HASH_TABLE_ITER iter2;
114*c7b67985SPhilip Paeps     hashTableIterInit(&iter2, &table);
115*c7b67985SPhilip Paeps     size_t itemCount = 0;
116*c7b67985SPhilip Paeps     while (true) {
117*c7b67985SPhilip Paeps       const NAME_AND_FLAG *const item
118*c7b67985SPhilip Paeps           = (const NAME_AND_FLAG *)hashTableIterNext(&iter2);
119*c7b67985SPhilip Paeps       if (item == NULL)
120*c7b67985SPhilip Paeps         break;
121*c7b67985SPhilip Paeps 
122*c7b67985SPhilip Paeps       itemCount++;
123*c7b67985SPhilip Paeps 
124*c7b67985SPhilip Paeps       if (keyeq(key1, keylen(key1), item->name) == XML_TRUE)
125*c7b67985SPhilip Paeps         assert_true(item->initialized);
126*c7b67985SPhilip Paeps       else if (keyeq(key2, keylen(key2), item->name) == XML_TRUE)
127*c7b67985SPhilip Paeps         assert_true(! item->initialized);
128*c7b67985SPhilip Paeps       else
129*c7b67985SPhilip Paeps         fail("unexpected item .name");
130*c7b67985SPhilip Paeps     }
131*c7b67985SPhilip Paeps     assert_true(itemCount == 2);
132*c7b67985SPhilip Paeps   }
133*c7b67985SPhilip Paeps 
134*c7b67985SPhilip Paeps   // Test: After clearing all lookups fail
135*c7b67985SPhilip Paeps   hashTableClear(&table);
136*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key1, 0) == NULL);
137*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key2, 0) == NULL);
138*c7b67985SPhilip Paeps   assert_true(lookup(parser, &table, key3, 0) == NULL);
139*c7b67985SPhilip Paeps 
140*c7b67985SPhilip Paeps   // Test: After clearing iteration yields 0 items again
141*c7b67985SPhilip Paeps   {
142*c7b67985SPhilip Paeps     HASH_TABLE_ITER iter3;
143*c7b67985SPhilip Paeps     hashTableIterInit(&iter3, &table);
144*c7b67985SPhilip Paeps     assert_true(hashTableIterNext(&iter3) == NULL);
145*c7b67985SPhilip Paeps   }
146*c7b67985SPhilip Paeps 
147*c7b67985SPhilip Paeps   hashTableDestroy(&table);
148*c7b67985SPhilip Paeps   XML_ParserFree(parser);
149*c7b67985SPhilip Paeps }
150*c7b67985SPhilip Paeps END_TEST
151*c7b67985SPhilip Paeps 
152*c7b67985SPhilip Paeps void
make_hash_test_case(Suite * s)153*c7b67985SPhilip Paeps make_hash_test_case(Suite *s) {
154*c7b67985SPhilip Paeps   TCase *const tc_hash = tcase_create("hash tests");
155*c7b67985SPhilip Paeps   suite_add_tcase(s, tc_hash);
156*c7b67985SPhilip Paeps   tcase_add_test(tc_hash, test_hash_table);
157*c7b67985SPhilip Paeps }
158