1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 * Copyright (C) 2000-2009, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 *******************************************************************************
8 * Date Name Description
9 * 03/22/00 aliu Creation.
10 * 07/13/00 Madhu Added more tests
11 *******************************************************************************
12 */
13
14 #include <stdbool.h>
15 #include "cintltst.h"
16 #include "uhash.h"
17 #include "unicode/ctest.h"
18 #include "unicode/ustring.h"
19 #include "cstring.h"
20
21 /**********************************************************************
22 * Prototypes
23 *********************************************************************/
24
25 static void TestBasic(void);
26 static void TestAllowZero(void);
27 static void TestOtherAPI(void);
28 static void hashIChars(void);
29
30 static int32_t U_EXPORT2 U_CALLCONV hashChars(const UHashTok key);
31
32 static UBool U_EXPORT2 U_CALLCONV isEqualChars(const UHashTok key1, const UHashTok key2);
33
34 static void _put(UHashtable* hash,
35 const char* key,
36 int32_t value,
37 int32_t expectedOldValue);
38
39 static void _get(UHashtable* hash,
40 const char* key,
41 int32_t expectedValue);
42
43 static void _remove(UHashtable* hash,
44 const char* key,
45 int32_t expectedValue);
46
47 void addHashtableTest(TestNode** root);
48
49 /**********************************************************************
50 * UHashTok wrapper functions
51 *********************************************************************/
52
53 static UBool
_compareChars(const void * a,const void * b)54 _compareChars(const void* a, const void* b) {
55 UHashTok s, t;
56 s.pointer = (void *)a;
57 t.pointer = (void *)b;
58 return uhash_compareChars(s, t);
59 }
60
61 static UBool
_compareIChars(const void * a,const void * b)62 _compareIChars(const void* a, const void* b) {
63 UHashTok s, t;
64 s.pointer = (void *)a;
65 t.pointer = (void *)b;
66 return uhash_compareIChars(s, t);
67 }
68
69 static UBool
_compareUChars(const void * a,const void * b)70 _compareUChars(const void* a, const void* b) {
71 UHashTok s, t;
72 s.pointer = (void *)a;
73 t.pointer = (void *)b;
74 return uhash_compareUChars(s, t);
75 }
76
77 static UBool
_compareLong(int32_t a,int32_t b)78 _compareLong(int32_t a, int32_t b) {
79 UHashTok s, t;
80 s.integer = a;
81 t.integer = b;
82 return uhash_compareLong(s, t);
83 }
84
85 /**********************************************************************
86 * FW Registration
87 *********************************************************************/
88
addHashtableTest(TestNode ** root)89 void addHashtableTest(TestNode** root) {
90
91 addTest(root, &TestBasic, "tsutil/chashtst/TestBasic");
92 addTest(root, &TestAllowZero, "tsutil/chashtst/TestAllowZero");
93 addTest(root, &TestOtherAPI, "tsutil/chashtst/TestOtherAPI");
94 addTest(root, &hashIChars, "tsutil/chashtst/hashIChars");
95
96 }
97
98 /**********************************************************************
99 * Test Functions
100 *********************************************************************/
101
TestBasic(void)102 static void TestBasic(void) {
103 static const char one[4] = {0x6F, 0x6E, 0x65, 0}; /* "one" */
104 static const char one2[4] = {0x6F, 0x6E, 0x65, 0}; /* Get around compiler optimizations */
105 static const char two[4] = {0x74, 0x77, 0x6F, 0}; /* "two" */
106 static const char three[6] = {0x74, 0x68, 0x72, 0x65, 0x65, 0}; /* "three" */
107 static const char omega[6] = {0x6F, 0x6D, 0x65, 0x67, 0x61, 0}; /* "omega" */
108 UErrorCode status = U_ZERO_ERROR;
109 UHashtable *hash;
110
111 hash = uhash_open(hashChars, isEqualChars, NULL, &status);
112 if (U_FAILURE(status)) {
113 log_err("FAIL: uhash_open failed with %s and returned 0x%08x\n",
114 u_errorName(status), hash);
115 return;
116 }
117 if (hash == NULL) {
118 log_err("FAIL: uhash_open returned NULL\n");
119 return;
120 }
121 log_verbose("Ok: uhash_open returned 0x%08X\n", hash);
122
123 _put(hash, one, 1, 0);
124 _put(hash, omega, 24, 0);
125 _put(hash, two, 2, 0);
126 _put(hash, three, 3, 0);
127 _put(hash, one, -1, 1);
128 _put(hash, two, -2, 2);
129 _put(hash, omega, 48, 24);
130 _put(hash, one, 100, -1);
131 _get(hash, three, 3);
132 _remove(hash, two, -2);
133 _get(hash, two, 0);
134 _get(hash, one, 100);
135 _put(hash, two, 200, 0);
136 _get(hash, omega, 48);
137 _get(hash, two, 200);
138
139 // puti(key, value==0) removes the key's element.
140 _put(hash, two, 0, 200);
141
142 if(_compareChars((void*)one, (void*)three) == true ||
143 _compareChars((void*)one, (void*)one2) != true ||
144 _compareChars((void*)one, (void*)one) != true ||
145 _compareChars((void*)one, NULL) == true ) {
146 log_err("FAIL: compareChars failed\n");
147 }
148 if(_compareIChars((void*)one, (void*)three) == true ||
149 _compareIChars((void*)one, (void*)one) != true ||
150 _compareIChars((void*)one, (void*)one2) != true ||
151 _compareIChars((void*)one, NULL) == true ) {
152 log_err("FAIL: compareIChars failed\n");
153 }
154
155 uhash_close(hash);
156 }
157
TestAllowZero(void)158 static void TestAllowZero(void) {
159 UErrorCode status = U_ZERO_ERROR;
160 UHashtable *hash = uhash_open(hashChars, isEqualChars, NULL, &status);
161 if (U_FAILURE(status)) {
162 log_err("FAIL: uhash_open failed with %s and returned 0x%08x\n",
163 u_errorName(status), hash);
164 return;
165 }
166 if (hash == NULL) {
167 log_err("FAIL: uhash_open returned NULL\n");
168 return;
169 }
170 log_verbose("Ok: uhash_open returned 0x%08X\n", hash);
171
172 int32_t oldValue = uhash_putiAllowZero(hash, (char *)"one", 1, &status);
173 UBool found = false;
174 if (U_FAILURE(status) || oldValue != 0 || !uhash_containsKey(hash, "one") ||
175 uhash_geti(hash, "one") != 1 ||
176 uhash_getiAndFound(hash, "one", &found) != 1 || !found) {
177 log_err("FAIL: uhash_putiAllowZero(one, 1)");
178 }
179 oldValue = uhash_putiAllowZero(hash, (char *)"zero", 0, &status);
180 found = false;
181 if (U_FAILURE(status) || oldValue != 0 || !uhash_containsKey(hash, "zero") ||
182 uhash_geti(hash, "zero") != 0 ||
183 uhash_getiAndFound(hash, "zero", &found) != 0 || !found) {
184 log_err("FAIL: uhash_putiAllowZero(zero, 0)");
185 }
186 // Overwrite "one" to 0.
187 oldValue = uhash_putiAllowZero(hash, (char *)"one", 0, &status);
188 found = false;
189 if (U_FAILURE(status) || oldValue != 1 || !uhash_containsKey(hash, "one") ||
190 uhash_geti(hash, "one") != 0 ||
191 uhash_getiAndFound(hash, "one", &found) != 0 || !found) {
192 log_err("FAIL: uhash_putiAllowZero(one, 0)");
193 }
194 // Remove "zero" using puti(zero, 0).
195 oldValue = uhash_puti(hash, (char *)"zero", 0, &status);
196 found = true;
197 if (U_FAILURE(status) || oldValue != 0 || uhash_containsKey(hash, "zero") ||
198 uhash_geti(hash, "zero") != 0 ||
199 uhash_getiAndFound(hash, "zero", &found) != 0 || found) {
200 log_err("FAIL: uhash_puti(zero, 0)");
201 }
202
203 uhash_close(hash);
204 }
205
TestOtherAPI(void)206 static void TestOtherAPI(void){
207
208 UErrorCode status = U_ZERO_ERROR;
209 UHashtable *hash;
210
211 /* Use the correct type when cast to void * */
212 static const UChar one[4] = {0x006F, 0x006E, 0x0065, 0}; /* L"one" */
213 static const UChar one2[4] = {0x006F, 0x006E, 0x0065, 0}; /* Get around compiler optimizations */
214 static const UChar two[4] = {0x0074, 0x0077, 0x006F, 0}; /* L"two" */
215 static const UChar two2[4] = {0x0074, 0x0077, 0x006F, 0}; /* L"two" */
216 static const UChar three[6] = {0x0074, 0x0068, 0x0072, 0x0065, 0x0065, 0}; /* L"three" */
217 static const UChar four[6] = {0x0066, 0x006F, 0x0075, 0x0072, 0}; /* L"four" */
218 static const UChar five[6] = {0x0066, 0x0069, 0x0076, 0x0065, 0}; /* L"five" */
219 static const UChar five2[6] = {0x0066, 0x0069, 0x0076, 0x0065, 0}; /* L"five" */
220
221 hash = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
222 if (U_FAILURE(status)) {
223 log_err("FAIL: uhash_open failed with %s and returned 0x%08x\n",
224 u_errorName(status), hash);
225 return;
226 }
227 if (hash == NULL) {
228 log_err("FAIL: uhash_open returned NULL\n");
229 return;
230 }
231 log_verbose("Ok: uhash_open returned 0x%08X\n", hash);
232
233 uhash_puti(hash, (void*)one, 1, &status);
234 if(uhash_count(hash) != 1){
235 log_err("FAIL: uhas_count() failed. Expected: 1, Got: %d\n", uhash_count(hash));
236 }
237 if(uhash_find(hash, (void*)two) != NULL){
238 log_err("FAIL: uhash_find failed\n");
239 }
240 uhash_puti(hash, (void*)two, 2, &status);
241 uhash_puti(hash, (void*)three, 3, &status);
242 uhash_puti(hash, (void*)four, 4, &status);
243 uhash_puti(hash, (void*)five, 5, &status);
244
245 if(uhash_count(hash) != 5){
246 log_err("FAIL: uhas_count() failed. Expected: 5, Got: %d\n", uhash_count(hash));
247 }
248
249 if(uhash_geti(hash, (void*)two2) != 2){
250 log_err("FAIL: uhash_geti failed\n");
251 }
252
253 if(uhash_find(hash, (void*)two2) == NULL){
254 log_err("FAIL: uhash_find of \"two\" failed\n");
255 }
256
257 if(uhash_removei(hash, (void*)five2) != 5){
258 log_err("FAIL: uhash_remove() failed\n");
259 }
260 if(uhash_count(hash) != 4){
261 log_err("FAIL: uhas_count() failed. Expected: 4, Got: %d\n", uhash_count(hash));
262 }
263
264 uhash_put(hash, (void*)one, NULL, &status);
265 if(uhash_count(hash) != 3){
266 log_err("FAIL: uhash_put() with value=NULL didn't remove the key value pair\n");
267 }
268 status=U_ILLEGAL_ARGUMENT_ERROR;
269 uhash_puti(hash, (void*)one, 1, &status);
270 if(uhash_count(hash) != 3){
271 log_err("FAIL: uhash_put() with value!=NULL should fail when status != U_ZERO_ERROR \n");
272 }
273
274 status=U_ZERO_ERROR;
275 uhash_puti(hash, (void*)one, 1, &status);
276 if(uhash_count(hash) != 4){
277 log_err("FAIL: uhash_put() with value!=NULL didn't replace the key value pair\n");
278 }
279
280 if(_compareUChars((void*)one, (void*)two) == true ||
281 _compareUChars((void*)one, (void*)one) != true ||
282 _compareUChars((void*)one, (void*)one2) != true ||
283 _compareUChars((void*)one, NULL) == true ) {
284 log_err("FAIL: compareUChars failed\n");
285 }
286
287 uhash_removeAll(hash);
288 if(uhash_count(hash) != 0){
289 log_err("FAIL: uhas_count() failed. Expected: 0, Got: %d\n", uhash_count(hash));
290 }
291
292 uhash_setKeyComparator(hash, uhash_compareLong);
293 uhash_setKeyHasher(hash, uhash_hashLong);
294 uhash_iputi(hash, 1001, 1, &status);
295 uhash_iputi(hash, 1002, 2, &status);
296 uhash_iputi(hash, 1003, 3, &status);
297 if(_compareLong(1001, 1002) == true ||
298 _compareLong(1001, 1001) != true ||
299 _compareLong(1001, 0) == true ) {
300 log_err("FAIL: compareLong failed\n");
301 }
302 /*set the resize policy to just GROW and SHRINK*/
303 /*how to test this??*/
304 uhash_setResizePolicy(hash, U_GROW_AND_SHRINK);
305 uhash_iputi(hash, 1004, 4, &status);
306 uhash_iputi(hash, 1005, 5, &status);
307 uhash_iputi(hash, 1006, 6, &status);
308 if(uhash_count(hash) != 6){
309 log_err("FAIL: uhash_count() failed. Expected: 6, Got: %d\n", uhash_count(hash));
310 }
311 if(uhash_iremovei(hash, 1004) != 4){
312 log_err("FAIL: uhash_remove failed\n");
313 }
314 if(uhash_iremovei(hash, 1004) != 0){
315 log_err("FAIL: uhash_remove failed\n");
316 }
317
318 uhash_removeAll(hash);
319 uhash_iput(hash, 2004, (void*)one, &status);
320 uhash_iput(hash, 2005, (void*)two, &status);
321 if(uhash_count(hash) != 2){
322 log_err("FAIL: uhash_count() failed. Expected: 2, Got: %d\n", uhash_count(hash));
323 }
324 if(uhash_iremove(hash, 2004) != (void*)one){
325 log_err("FAIL: uhash_remove failed\n");
326 }
327 if(uhash_iremove(hash, 2004) != NULL){
328 log_err("FAIL: uhash_remove failed\n");
329 }
330 if(uhash_count(hash) != 1){
331 log_err("FAIL: uhash_count() failed. Expected: 1, Got: %d\n", uhash_count(hash));
332 }
333
334 uhash_close(hash);
335
336 }
337
hashIChars(void)338 static void hashIChars(void) {
339 static const char which[] = "which";
340 static const char WHICH2[] = "WHICH";
341 static const char where[] = "where";
342 UErrorCode status = U_ZERO_ERROR;
343 UHashtable *hash;
344
345 hash = uhash_open(uhash_hashIChars, uhash_compareIChars, NULL, &status);
346 if (U_FAILURE(status)) {
347 log_err("FAIL: uhash_open failed with %s and returned 0x%08x\n",
348 u_errorName(status), hash);
349 return;
350 }
351 if (hash == NULL) {
352 log_err("FAIL: uhash_open returned NULL\n");
353 return;
354 }
355 log_verbose("Ok: uhash_open returned 0x%08X\n", hash);
356
357 _put(hash, which, 1, 0);
358 _put(hash, WHICH2, 2, 1);
359 _put(hash, where, 3, 0);
360 if(uhash_count(hash) != 2){
361 log_err("FAIL: uhas_count() failed. Expected: 1, Got: %d\n", uhash_count(hash));
362 }
363 _remove(hash, which, 2);
364
365 uhash_close(hash);
366 }
367
368
369 /**********************************************************************
370 * uhash Callbacks
371 *********************************************************************/
372
373 /**
374 * This hash function is designed to collide a lot to test key equality
375 * resolution. It only uses the first char.
376 */
hashChars(const UHashTok key)377 static int32_t U_EXPORT2 U_CALLCONV hashChars(const UHashTok key) {
378 return *(const char*) key.pointer;
379 }
380
isEqualChars(const UHashTok key1,const UHashTok key2)381 static UBool U_EXPORT2 U_CALLCONV isEqualChars(const UHashTok key1, const UHashTok key2) {
382 return (UBool)((key1.pointer != NULL) &&
383 (key2.pointer != NULL) &&
384 (uprv_strcmp((const char*)key1.pointer, (const char*)key2.pointer) == 0));
385 }
386
387 /**********************************************************************
388 * Wrapper Functions
389 *********************************************************************/
390
_put(UHashtable * hash,const char * key,int32_t value,int32_t expectedOldValue)391 static void _put(UHashtable* hash,
392 const char* key,
393 int32_t value,
394 int32_t expectedOldValue) {
395 UErrorCode status = U_ZERO_ERROR;
396 int32_t oldValue =
397 uhash_puti(hash, (void*) key, value, &status);
398 if (U_FAILURE(status)) {
399 log_err("FAIL: uhash_puti(%s) failed with %s and returned %ld\n",
400 key, u_errorName(status), oldValue);
401 } else if (oldValue != expectedOldValue) {
402 log_err("FAIL: uhash_puti(%s) returned old value %ld; expected %ld\n",
403 key, oldValue, expectedOldValue);
404 } else {
405 log_verbose("Ok: uhash_puti(%s, %d) returned old value %ld\n",
406 key, value, oldValue);
407 }
408 int32_t newValue = uhash_geti(hash, key);
409 if (newValue != value) {
410 log_err("FAIL: uhash_puti(%s) failed to set the intended value %ld: "
411 "uhash_geti() returns %ld\n",
412 key, value, newValue);
413 }
414 UBool contained = uhash_containsKey(hash, key);
415 if (value == 0) {
416 if (contained) {
417 log_err("FAIL: uhash_puti(%s, zero) failed to remove the key item: "
418 "uhash_containsKey() returns true\n",
419 key);
420 }
421 } else {
422 if (!contained) {
423 log_err("FAIL: uhash_puti(%s, not zero) appears to have removed the key item: "
424 "uhash_containsKey() returns false\n",
425 key);
426 }
427 }
428 }
429
_get(UHashtable * hash,const char * key,int32_t expectedValue)430 static void _get(UHashtable* hash,
431 const char* key,
432 int32_t expectedValue) {
433 int32_t value = uhash_geti(hash, key);
434 if (value != expectedValue) {
435 log_err("FAIL: uhash_geti(%s) returned %ld; expected %ld\n",
436 key, value, expectedValue);
437 } else {
438 log_verbose("Ok: uhash_geti(%s) returned value %ld\n",
439 key, value);
440 }
441 }
442
_remove(UHashtable * hash,const char * key,int32_t expectedValue)443 static void _remove(UHashtable* hash,
444 const char* key,
445 int32_t expectedValue) {
446 int32_t value = uhash_removei(hash, key);
447 if (value != expectedValue) {
448 log_err("FAIL: uhash_removei(%s) returned %ld; expected %ld\n",
449 key, value, expectedValue);
450 } else {
451 log_verbose("Ok: uhash_removei(%s) returned old value %ld\n",
452 key, value);
453 }
454 if (uhash_containsKey(hash, key)) {
455 log_err("FAIL: uhash_removei(%s) failed to remove the key item: "
456 "uhash_containsKey() returns false\n",
457 key);
458 }
459 }
460