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