UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "cx/test.h" 30 #include "util_allocator.h" 31 32 #include "cx/properties.h" 33 #include "cx/hash_map.h" 34 35 CX_TEST(test_properties_init) { 36 CxProperties prop; 37 CX_TEST_DO { 38 cxPropertiesInitDefault(&prop); 39 40 CX_TEST_ASSERT(prop.config.delimiter == '='); 41 CX_TEST_ASSERT(prop.config.comment1 == '#'); 42 CX_TEST_ASSERT(prop.config.comment2 == 0); 43 CX_TEST_ASSERT(prop.config.comment3 == 0); 44 CX_TEST_ASSERT(prop.config.continuation == '\\'); 45 CX_TEST_ASSERT(prop.input.space == NULL); 46 CX_TEST_ASSERT(prop.buffer.space == NULL); 47 48 cxPropertiesDestroy(&prop); 49 } 50 } 51 52 CX_TEST(test_properties_next) { 53 const char *tests[] = { 54 "name = value\n", 55 "name=value\n", 56 "n=value\n", 57 "name=v\n", 58 "n=v\n", 59 "name = value # comment\n", 60 "#comment\nn=v\n", 61 "# comment1\n# comment2\n\n \n\nname = value\n", 62 " name = value\n", 63 "name = value\n\n" 64 }; 65 66 const char *keys[] = { 67 "name", 68 "name", 69 "n", 70 "name", 71 "n", 72 "name", 73 "n", 74 "name", 75 "name", 76 "name" 77 }; 78 79 const char *values[] = { 80 "value", 81 "value", 82 "value", 83 "v", 84 "v", 85 "value", 86 "v", 87 "value", 88 "value", 89 "value" 90 }; 91 92 CxProperties prop; 93 cxPropertiesInitDefault(&prop); 94 CxPropertiesStatus result; 95 cxstring key; 96 cxstring value; 97 CX_TEST_DO { 98 for (int i = 0; i < 10; i++) { 99 cxPropertiesFill(&prop, tests[i]); 100 CX_TEST_ASSERT(prop.input.space == tests[i]); 101 CX_TEST_ASSERT(prop.input.size == strlen(tests[i])); 102 CX_TEST_ASSERT(prop.input.pos == 0); 103 104 result = cxPropertiesNext(&prop, &key, &value); 105 cxstring k = cx_str(keys[i]); 106 cxstring v = cx_str(values[i]); 107 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 108 CX_TEST_ASSERT(0 == cx_strcmp(key, k)); 109 CX_TEST_ASSERT(0 == cx_strcmp(value, v)); 110 111 result = cxPropertiesNext(&prop, &key, &value); 112 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 113 } 114 } 115 cxPropertiesDestroy(&prop); 116 } 117 118 CX_TEST_SUBROUTINE(test_properties_next_multi_check, CxProperties *prop) { 119 const char *keys[] = { 120 "a", 121 "b", 122 "c", 123 "uap", 124 "name", 125 "key1", 126 "key2", 127 "key3" 128 }; 129 130 const char *values[] = { 131 "a value", 132 "b value", 133 "core", 134 "core", 135 "ucx", 136 "value1", 137 "value2", 138 "value3" 139 }; 140 CxPropertiesStatus result; 141 cxstring key; 142 cxstring value; 143 for (int i = 0; i < 8; i++) { 144 result = cxPropertiesNext(prop, &key, &value); 145 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 146 CX_TEST_ASSERT(!cx_strcmp(key, cx_str(keys[i]))); 147 CX_TEST_ASSERT(!cx_strcmp(value, cx_str(values[i]))); 148 } 149 result = cxPropertiesNext(prop, &key, &value); 150 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 151 } 152 153 CX_TEST(test_properties_next_multi) { 154 const char *str = "#\n" 155 "# properties\n" 156 "# contains key/value pairs\n" 157 "#\n" 158 "a = a value\n" 159 "b = b value\n" 160 "c = core\n" 161 "\n# test\n" 162 "uap = core\n" 163 "name = ucx\n" 164 "# no = property\n" 165 "key1 = value1\n" 166 "#key1 = wrong value\n" 167 "#key2 = not value 2\n" 168 "key2 = value2\n" 169 "\n\n\n \n key3=value3\n"; 170 171 CxProperties prop; 172 cxPropertiesInitDefault(&prop); 173 174 CX_TEST_DO { 175 CxPropertiesStatus result; 176 cxstring key; 177 cxstring value; 178 result = cxPropertiesNext(&prop, &key, &value); 179 CX_TEST_ASSERT(result == CX_PROPERTIES_NULL_INPUT); 180 181 // check for C string 182 cxPropertiesFill(&prop, str); 183 CX_TEST_CALL_SUBROUTINE(test_properties_next_multi_check, &prop); 184 185 // check for cxstring 186 cxPropertiesFill(&prop, cx_str(str)); 187 CX_TEST_CALL_SUBROUTINE(test_properties_next_multi_check, &prop); 188 189 // check for mutstr 190 cxPropertiesFill(&prop, cx_mutstr((char*)str)); 191 CX_TEST_CALL_SUBROUTINE(test_properties_next_multi_check, &prop); 192 } 193 cxPropertiesDestroy(&prop); 194 } 195 196 CX_TEST(test_properties_next_part) { 197 CxProperties prop; 198 cxPropertiesInitDefault(&prop); 199 CxPropertiesStatus result; 200 cxstring key; 201 cxstring value; 202 const char *str; 203 204 CX_TEST_DO { 205 str = ""; 206 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 207 result = cxPropertiesNext(&prop, &key, &value); 208 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 209 210 str = " \n"; 211 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 212 result = cxPropertiesNext(&prop, &key, &value); 213 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 214 215 str = "name"; 216 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 217 result = cxPropertiesNext(&prop, &key, &value); 218 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 219 220 str = " "; 221 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 222 result = cxPropertiesNext(&prop, &key, &value); 223 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 224 225 // call fill twice in a row 226 str = "= "; 227 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 228 str = "value"; 229 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 230 result = cxPropertiesNext(&prop, &key, &value); 231 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 232 233 str = "\n"; 234 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 235 result = cxPropertiesNext(&prop, &key, &value); 236 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 237 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("name"))); 238 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value"))); 239 240 // second round 241 str = "#comment\n"; 242 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 243 result = cxPropertiesNext(&prop, &key, &value); 244 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 245 246 str = "#comment\nname2 = "; 247 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 248 result = cxPropertiesNext(&prop, &key, &value); 249 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 250 251 str = "value2\na = b\n"; 252 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 253 result = cxPropertiesNext(&prop, &key, &value); 254 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 255 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("name2"))); 256 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value2"))); 257 258 result = cxPropertiesNext(&prop, &key, &value); 259 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 260 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("a"))); 261 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("b"))); 262 263 str = "# comment\n#\n#\ntests = "; 264 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 265 result = cxPropertiesNext(&prop, &key, &value); 266 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 267 268 str = "test1 "; 269 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 270 result = cxPropertiesNext(&prop, &key, &value); 271 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 272 273 str = "test2 test3 test4\n"; 274 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 275 result = cxPropertiesNext(&prop, &key, &value); 276 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 277 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("tests"))); 278 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("test1 test2 test3 test4"))); 279 280 // test if cxPropertiesNext finds a name/value after a comment 281 str = "# just a comment"; 282 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 283 result = cxPropertiesNext(&prop, &key, &value); 284 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 285 286 str = " in 3"; 287 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 288 result = cxPropertiesNext(&prop, &key, &value); 289 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 290 291 str = " parts\nx = 1\n"; 292 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 293 result = cxPropertiesNext(&prop, &key, &value); 294 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 295 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("x"))); 296 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("1"))); 297 298 // finally we are done 299 result = cxPropertiesNext(&prop, &key, &value); 300 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 301 } 302 cxPropertiesDestroy(&prop); 303 } 304 305 CX_TEST(test_properties_next_long_lines) { 306 CxProperties prop; 307 cxPropertiesInitDefault(&prop); 308 CxPropertiesStatus result; 309 cxstring key; 310 cxstring value; 311 312 size_t key_len = 512; 313 char *long_key = (char*)malloc(key_len); 314 memset(long_key, 'a', 70); 315 memset(long_key + 70, 'b', 242); 316 memset(long_key + 312, 'c', 200); 317 318 size_t value_len = 2048; 319 char *long_value = (char*)malloc(value_len); 320 memset(long_value, 'x', 1024); 321 memset(long_value+1024, 'y', 1024); 322 323 CX_TEST_DO { 324 cxPropertiesFilln(&prop, long_key, 10); 325 result = cxPropertiesNext(&prop, &key, &value); 326 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 327 328 cxPropertiesFilln(&prop, long_key + 10, 202); 329 result = cxPropertiesNext(&prop, &key, &value); 330 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 331 332 cxPropertiesFilln(&prop, long_key + 212, 200); 333 result = cxPropertiesNext(&prop, &key, &value); 334 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 335 336 cxPropertiesFilln(&prop, long_key + 412, 100); 337 result = cxPropertiesNext(&prop, &key, &value); 338 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 339 340 const char *str = " = "; 341 cxPropertiesFill(&prop, str); 342 result = cxPropertiesNext(&prop, &key, &value); 343 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 344 345 cxPropertiesFilln(&prop, long_value, 512); 346 result = cxPropertiesNext(&prop, &key, &value); 347 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 348 349 cxPropertiesFilln(&prop, long_value + 512, 1024); 350 result = cxPropertiesNext(&prop, &key, &value); 351 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 352 353 cxPropertiesFilln(&prop, long_value + 1536, 512); 354 result = cxPropertiesNext(&prop, &key, &value); 355 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 356 357 str = "\n#comment\nkey = value\n"; 358 cxPropertiesFill(&prop, str); 359 result = cxPropertiesNext(&prop, &key, &value); 360 cxstring k = cx_strn(long_key, key_len); 361 cxstring v = cx_strn(long_value, value_len); 362 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 363 CX_TEST_ASSERT(0 == cx_strcmp(key, k)); 364 CX_TEST_ASSERT(0 == cx_strcmp(value, v)); 365 366 result = cxPropertiesNext(&prop, &key, &value); 367 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 368 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key"))); 369 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value"))); 370 371 result = cxPropertiesNext(&prop, &key, &value); 372 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 373 374 CX_TEST_ASSERT(prop.buffer.capacity > 0); 375 CX_TEST_ASSERT(cxBufferEof(&prop.buffer)); 376 CX_TEST_ASSERT(cxBufferEof(&prop.input)); 377 cxPropertiesDestroy(&prop); 378 CX_TEST_ASSERT(prop.buffer.capacity == 0); 379 CX_TEST_ASSERT(prop.buffer.size == 0); 380 CX_TEST_ASSERT(prop.buffer.pos == 0); 381 } 382 383 free(long_key); 384 free(long_value); 385 } 386 387 CX_TEST(test_properties_next_line_continuation) { 388 const char *str = 389 "key1 = multiline \\\nvalue\n" 390 "key2 = normal\n" 391 "key3 = multiline \\\n trim \n" 392 "key4 = m1\\\nm2\\\n m3\\\nm4 \n" 393 "key5 = no\\continuation\n" 394 "key6 = multiple \\\n \\\n \\\n continuations\n"; 395 396 CxProperties prop; 397 cxPropertiesInitDefault(&prop); 398 399 cxstring key; 400 cxstring value; 401 402 CX_TEST_DO { 403 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 404 405 CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) == CX_PROPERTIES_NO_ERROR); 406 CX_TEST_ASSERT(!cx_strcmp(key, "key1")); 407 CX_TEST_ASSERT(!cx_strcmp(value, "multiline value")); 408 409 CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) == CX_PROPERTIES_NO_ERROR); 410 CX_TEST_ASSERT(!cx_strcmp(key, "key2")); 411 CX_TEST_ASSERT(!cx_strcmp(value, "normal")); 412 413 CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) == CX_PROPERTIES_NO_ERROR); 414 CX_TEST_ASSERT(!cx_strcmp(key, "key3")); 415 CX_TEST_ASSERT(!cx_strcmp(value, "multiline trim")); 416 417 CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) == CX_PROPERTIES_NO_ERROR); 418 CX_TEST_ASSERT(!cx_strcmp(key, "key4")); 419 CX_TEST_ASSERT(!cx_strcmp(value, "m1m2m3m4")); 420 421 CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) == CX_PROPERTIES_NO_ERROR); 422 CX_TEST_ASSERT(!cx_strcmp(key, "key5")); 423 CX_TEST_ASSERT(!cx_strcmp(value, "no\\continuation")); 424 425 CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) == CX_PROPERTIES_NO_ERROR); 426 CX_TEST_ASSERT(!cx_strcmp(key, "key6")); 427 CX_TEST_ASSERT(!cx_strcmp(value, "multiple continuations")); 428 } 429 430 cxPropertiesDestroy(&prop); 431 } 432 433 CX_TEST(test_properties_next_line_continuation_part) { 434 CxProperties prop; 435 cxPropertiesInitDefault(&prop); 436 437 cxstring key; 438 cxstring value; 439 CxPropertiesStatus result; 440 const char *str; 441 442 CX_TEST_DO { 443 // key1 = continue continue ...line 444 str = "key1 "; 445 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 446 result = cxPropertiesNext(&prop, &key, &value); 447 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 448 449 str = "= continue \\"; 450 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 451 result = cxPropertiesNext(&prop, &key, &value); 452 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 453 454 str = "\ncontinue \\\n"; 455 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 456 result = cxPropertiesNext(&prop, &key, &value); 457 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 458 459 str = "..."; 460 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 461 result = cxPropertiesNext(&prop, &key, &value); 462 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 463 464 str = "line\nkey2 = value2\n"; 465 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, str)); 466 result = cxPropertiesNext(&prop, &key, &value); 467 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 468 CX_TEST_ASSERT(!cx_strcmp(key, "key1")); 469 CX_TEST_ASSERT(!cx_strcmp(value, "continue continue ...line")); 470 471 // key2 = value2 472 result = cxPropertiesNext(&prop, &key, &value); 473 CX_TEST_ASSERT(!cx_strcmp(key, "key2")); 474 CX_TEST_ASSERT(!cx_strcmp(value, "value2")); 475 476 // key3 = continue-line 477 str = "key3=\\\ncontinue-\\\n line"; 478 size_t len = strlen(str); 479 for(size_t i=0;i<len;i++) { 480 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, cx_strn(str+i, 1))); 481 result = cxPropertiesNext(&prop, &key, &value); 482 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 483 } 484 CX_TEST_ASSERT(0 == cxPropertiesFill(&prop, "\n")); 485 result = cxPropertiesNext(&prop, &key, &value); 486 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 487 CX_TEST_ASSERT(!cx_strcmp(key, "key3")); 488 CX_TEST_ASSERT(!cx_strcmp(value, "continue-line")); 489 } 490 491 cxPropertiesDestroy(&prop); 492 } 493 494 CX_TEST(test_properties_load) { 495 CxTestingAllocator talloc; 496 cx_testing_allocator_init(&talloc); 497 char fname[16] = "ucxtestXXXXXX"; 498 int tmpfd = mkstemp(fname); 499 FILE *f = tmpfd < 0 ? NULL : fdopen(tmpfd, "w"); 500 CX_TEST_DO { 501 CX_TEST_ASSERTM(f, "test file cannot be opened, test aborted"); 502 fprintf(f, "# properties file\n\nkey1 = value1\nkey2 = value2\n"); 503 fprintf(f, "\n\nkey3 = value3\n\n"); 504 505 size_t key_len = 512; 506 char *long_key = (char *) malloc(key_len); 507 memset(long_key, 'k', 512); 508 509 size_t value_len = 2048; 510 char *long_value = (char *) malloc(value_len); 511 memset(long_value, 'v', 2048); 512 513 fwrite(long_key, 1, key_len, f); 514 fprintf(f, " = "); 515 fwrite(long_value, 1, value_len, f); 516 fprintf(f, " \n"); 517 518 fprintf(f, "\n\n\n\nlast_key = property value\n"); 519 fclose(f); 520 f = NULL; 521 // preparation of test file complete 522 523 // we want to load the properties into a map of char* pointers 524 CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); 525 cxDefineAdvancedDestructor(map, cxFree, &talloc); 526 CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map); 527 528 CX_TEST_ASSERT(status == CX_PROPERTIES_NO_ERROR); 529 CX_TEST_ASSERT(cxMapSize(map) == 5); 530 531 char *v1 = cxMapGet(map, "key1"); 532 char *v2 = cxMapGet(map, cx_str("key2")); 533 char *v3 = cxMapGet(map, "key3"); 534 char *lv = cxMapGet(map, cx_strn(long_key, key_len)); 535 char *lk = cxMapGet(map, "last_key"); 536 537 CX_TEST_ASSERTM(v1, "value for key1 not found"); 538 CX_TEST_ASSERTM(v2, "value for key2 not found"); 539 CX_TEST_ASSERTM(v3, "value for key3 not found"); 540 CX_TEST_ASSERTM(lv, "value for long key not found"); 541 CX_TEST_ASSERTM(lk, "value for last_key not found"); 542 543 CX_TEST_ASSERT(!strcmp(v1, "value1")); 544 CX_TEST_ASSERT(!strcmp(v2, "value2")); 545 CX_TEST_ASSERT(!strcmp(v3, "value3")); 546 cxstring expected = cx_strn(long_value, value_len); 547 cxstring actual = cx_str(lv); 548 CX_TEST_ASSERT(!cx_strcmp(expected, actual)); 549 CX_TEST_ASSERT(!strcmp(lk, "property value")); 550 551 free(long_key); 552 free(long_value); 553 CX_TEST_ASSERT(cx_testing_allocator_used(&talloc)); 554 cxMapFree(map); 555 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 556 } 557 cx_testing_allocator_destroy(&talloc); 558 if (f) fclose(f); 559 remove(fname); 560 } 561 562 CX_TEST(test_properties_load_empty_file) { 563 CxTestingAllocator talloc; 564 cx_testing_allocator_init(&talloc); 565 char fname[16] = "ucxtestXXXXXX"; 566 int tmpfd = mkstemp(fname); 567 FILE *f = tmpfd < 0 ? NULL : fdopen(tmpfd, "w"); 568 CX_TEST_DO { 569 CX_TEST_ASSERTM(f, "test file cannot be opened, test aborted"); 570 fclose(f); 571 f = NULL; 572 573 CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); 574 // store something that we don't want to be deleted 575 cxMapPut(map, "test", "value"); 576 CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map); 577 CX_TEST_ASSERT(status == CX_PROPERTIES_NO_DATA); 578 CX_TEST_ASSERT(cxMapSize(map) == 1); 579 580 char *v = cxMapGet(map, "test"); 581 CX_TEST_ASSERTM(v, "value was removed"); 582 CX_TEST_ASSERT(!strcmp(v, "value")); 583 CX_TEST_ASSERT(!cx_testing_allocator_used(&talloc)); 584 cxMapFree(map); 585 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 586 } 587 cx_testing_allocator_destroy(&talloc); 588 if (f) fclose(f); 589 remove(fname); 590 } 591 592 CX_TEST(test_properties_load_only_comments) { 593 CxTestingAllocator talloc; 594 cx_testing_allocator_init(&talloc); 595 char fname[16] = "ucxtestXXXXXX"; 596 int tmpfd = mkstemp(fname); 597 FILE *f = tmpfd < 0 ? NULL : fdopen(tmpfd, "w"); 598 CX_TEST_DO { 599 CX_TEST_ASSERTM(f, "test file cannot be opened, test aborted"); 600 fputs("# test file\n\n# contains only comments\n\n# key = value\n", f); 601 fclose(f); 602 f = NULL; 603 604 CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); 605 CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map); 606 CX_TEST_ASSERT(status == CX_PROPERTIES_NO_DATA); 607 CX_TEST_ASSERT(cxMapSize(map) == 0); 608 609 CX_TEST_ASSERT(!cx_testing_allocator_used(&talloc)); 610 cxMapFree(map); 611 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 612 } 613 cx_testing_allocator_destroy(&talloc); 614 if (f) fclose(f); 615 remove(fname); 616 } 617 618 CX_TEST(test_properties_load_error) { 619 CxTestingAllocator talloc; 620 cx_testing_allocator_init(&talloc); 621 char fname[16] = "ucxtestXXXXXX"; 622 int tmpfd = mkstemp(fname); 623 FILE *f = tmpfd < 0 ? NULL : fdopen(tmpfd, "w"); 624 CX_TEST_DO { 625 CX_TEST_ASSERTM(f, "test file cannot be opened, test aborted"); 626 fputs("# test file\n\ntest = value\n = value2\n", f); 627 fclose(f); 628 f = NULL; 629 630 CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); 631 cxDefineAdvancedDestructor(map, cxFree, &talloc); 632 CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map); 633 CX_TEST_ASSERT(status == CX_PROPERTIES_INVALID_EMPTY_KEY); 634 // all keys that could be successfully parsed are added 635 CX_TEST_ASSERT(cxMapSize(map) == 1); 636 char *v = cxMapGet(map, "test"); 637 CX_TEST_ASSERT(v != NULL); 638 CX_TEST_ASSERT(!strcmp(v, "value")); 639 640 cxMapFree(map); 641 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 642 } 643 cx_testing_allocator_destroy(&talloc); 644 if (f) fclose(f); 645 remove(fname); 646 } 647 648 CX_TEST(test_properties_load_file_not_exists) { 649 CX_TEST_DO { 650 CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); 651 CxPropertiesStatus status = cxPropertiesLoadDefault(NULL, "does-definitely-not-exist", map); 652 CX_TEST_ASSERT(status == CX_PROPERTIES_FILE_ERROR); 653 cxMapFree(map); 654 } 655 } 656 657 CX_TEST(test_properties_load_cxmutstr_map) { 658 CxTestingAllocator talloc; 659 cx_testing_allocator_init(&talloc); 660 char fname[16] = "ucxtestXXXXXX"; 661 int tmpfd = mkstemp(fname); 662 FILE *f = tmpfd < 0 ? NULL : fdopen(tmpfd, "w"); 663 CX_TEST_DO { 664 CX_TEST_ASSERTM(f, "test file cannot be opened, test aborted"); 665 fputs("# test file\n\ntest = value\ntest2 = value2\n", f); 666 fclose(f); 667 f = NULL; 668 669 CxMap *map = cxHashMapCreateSimple(sizeof(cxmutstr)); 670 cxDefineAdvancedDestructor(map, cx_strfree_a, &talloc); 671 CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map); 672 CX_TEST_ASSERT(status == CX_PROPERTIES_NO_ERROR); 673 CX_TEST_ASSERT(cxMapSize(map) == 2); 674 cxstring v1 = CX_STR("value"); 675 cxstring v2 = CX_STR("value2"); 676 CX_TEST_ASSERT(cx_strcmp_p(cxMapGet(map, "test"), &v1) == 0); 677 CX_TEST_ASSERT(cx_strcmp_p(cxMapGet(map, "test2"), &v2) == 0); 678 679 CX_TEST_ASSERT(cx_testing_allocator_used(&talloc)); 680 cxMapFree(map); 681 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 682 } 683 cx_testing_allocator_destroy(&talloc); 684 if (f) fclose(f); 685 remove(fname); 686 } 687 688 CX_TEST(test_properties_load_incompatible_map) { 689 CxTestingAllocator talloc; 690 cx_testing_allocator_init(&talloc); 691 char fname[16] = "ucxtestXXXXXX"; 692 int tmpfd = mkstemp(fname); 693 FILE *f = tmpfd < 0 ? NULL : fdopen(tmpfd, "w"); 694 CX_TEST_DO { 695 CX_TEST_ASSERTM(f, "test file cannot be opened, test aborted"); 696 fputs("# test file\n\ntest = value\ntest2 = value2\n", f); 697 fclose(f); 698 f = NULL; 699 700 CxMap *map = cxHashMapCreateSimple(sizeof(CxBuffer)); 701 cxDefineAdvancedDestructor(map, cxFree, &talloc); 702 CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map); 703 CX_TEST_ASSERT(status == CX_PROPERTIES_MAP_ERROR); 704 CX_TEST_ASSERT(cxMapSize(map) == 0); 705 706 cxMapFree(map); 707 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 708 } 709 cx_testing_allocator_destroy(&talloc); 710 if (f) fclose(f); 711 remove(fname); 712 } 713 714 CX_TEST(test_properties_multiple_fill) { 715 const char *props1 = "key1 = value1\n"; 716 const char *props2 = "key2 = value2\n"; 717 const char *props3 = "key3 = value3\n"; 718 719 CxProperties prop; 720 cxPropertiesInitDefault(&prop); 721 CxPropertiesStatus result; 722 cxstring key; 723 cxstring value; 724 CX_TEST_DO { 725 cxPropertiesFill(&prop, props1); 726 cxPropertiesFill(&prop, props2); 727 cxPropertiesFill(&prop, props3); 728 result = cxPropertiesNext(&prop, &key, &value); 729 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 730 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key1"))); 731 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value1"))); 732 result = cxPropertiesNext(&prop, &key, &value); 733 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 734 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key2"))); 735 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value2"))); 736 result = cxPropertiesNext(&prop, &key, &value); 737 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 738 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key3"))); 739 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value3"))); 740 741 result = cxPropertiesNext(&prop, &key, &value); 742 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 743 } 744 cxPropertiesDestroy(&prop); 745 } 746 747 CX_TEST(test_properties_use_stack) { 748 const char *props1 = "key1 = val"; 749 const char *props2 = "ue1\nkey2 = value2"; 750 const char *props3 = "\nkey3 = value3\n"; 751 char stackmem[16]; 752 753 CxProperties prop; 754 cxPropertiesInitDefault(&prop); 755 cxPropertiesUseStack(&prop, stackmem, 16); 756 CxPropertiesStatus result; 757 cxstring key; 758 cxstring value; 759 CX_TEST_DO { 760 cxPropertiesFill(&prop, props1); 761 result = cxPropertiesNext(&prop, &key, &value); 762 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 763 cxPropertiesFill(&prop, props2); 764 result = cxPropertiesNext(&prop, &key, &value); 765 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 766 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key1"))); 767 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value1"))); 768 result = cxPropertiesNext(&prop, &key, &value); 769 CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); 770 cxPropertiesFill(&prop, props3); 771 result = cxPropertiesNext(&prop, &key, &value); 772 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 773 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key2"))); 774 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value2"))); 775 result = cxPropertiesNext(&prop, &key, &value); 776 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 777 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key3"))); 778 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value3"))); 779 result = cxPropertiesNext(&prop, &key, &value); 780 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); 781 } 782 cxPropertiesDestroy(&prop); 783 } 784 785 CX_TEST(test_properties_empty_key) { 786 const char *fail1 = "= val\n"; 787 const char *fail2 = " = val\n"; 788 const char *good = " key = val\n"; 789 790 CxProperties prop; 791 CxPropertiesStatus result; 792 cxstring key; 793 cxstring value; 794 CX_TEST_DO { 795 cxPropertiesInitDefault(&prop); 796 cxPropertiesFill(&prop, fail1); 797 result = cxPropertiesNext(&prop, &key, &value); 798 CX_TEST_ASSERT(result == CX_PROPERTIES_INVALID_EMPTY_KEY); 799 cxPropertiesReset(&prop); 800 cxPropertiesFill(&prop, fail2); 801 result = cxPropertiesNext(&prop, &key, &value); 802 CX_TEST_ASSERT(result == CX_PROPERTIES_INVALID_EMPTY_KEY); 803 cxPropertiesReset(&prop); 804 cxPropertiesFill(&prop, good); 805 result = cxPropertiesNext(&prop, &key, &value); 806 CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); 807 CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key"))); 808 CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("val"))); 809 cxPropertiesDestroy(&prop); 810 } 811 } 812 813 CxTestSuite *cx_test_suite_properties(void) { 814 CxTestSuite *suite = cx_test_suite_new("properties"); 815 816 cx_test_register(suite, test_properties_init); 817 cx_test_register(suite, test_properties_next); 818 cx_test_register(suite, test_properties_next_multi); 819 cx_test_register(suite, test_properties_next_part); 820 cx_test_register(suite, test_properties_next_long_lines); 821 cx_test_register(suite, test_properties_next_line_continuation); 822 cx_test_register(suite, test_properties_next_line_continuation_part); 823 cx_test_register(suite, test_properties_load); 824 cx_test_register(suite, test_properties_load_empty_file); 825 cx_test_register(suite, test_properties_load_only_comments); 826 cx_test_register(suite, test_properties_load_error); 827 cx_test_register(suite, test_properties_load_file_not_exists); 828 cx_test_register(suite, test_properties_load_cxmutstr_map); 829 cx_test_register(suite, test_properties_load_incompatible_map); 830 cx_test_register(suite, test_properties_multiple_fill); 831 cx_test_register(suite, test_properties_use_stack); 832 cx_test_register(suite, test_properties_empty_key); 833 834 return suite; 835 } 836