Tue, 09 Dec 2025 17:27:58 +0100
fix return value of cxPropertiesLoad() for non-empty files that do not contain keys
relates to #775
| src/properties.c | file | annotate | diff | comparison | revisions | |
| tests/test_properties.c | file | annotate | diff | comparison | revisions |
--- a/src/properties.c Mon Dec 08 23:09:11 2025 +0100 +++ b/src/properties.c Tue Dec 09 17:27:58 2025 +0100 @@ -283,6 +283,7 @@ // read/fill/parse loop status = CX_PROPERTIES_NO_DATA; + size_t keys_found = 0; while (true) { size_t r = fread(fillbuf, 1, cx_properties_load_fill_size, f); if (ferror(f)) { @@ -313,13 +314,11 @@ status = CX_PROPERTIES_MAP_ERROR; break; } + keys_found++; } } if (status > CX_PROPERTIES_OK) { break; - } else if (status == CX_PROPERTIES_NO_DATA) { - // we want to report this case differently in this function - status = CX_PROPERTIES_NO_ERROR; } } @@ -327,5 +326,9 @@ fclose(f); cxPropertiesDestroy(&parser); cx_strfree(&fname); - return status; + if (status == CX_PROPERTIES_NO_DATA && keys_found > 0) { + return CX_PROPERTIES_NO_ERROR; + } else { + return status; + } }
--- a/tests/test_properties.c Mon Dec 08 23:09:11 2025 +0100 +++ b/tests/test_properties.c Tue Dec 09 17:27:58 2025 +0100 @@ -451,6 +451,62 @@ remove(fname); } +CX_TEST(test_properties_load_empty_file) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + char fname[16] = "ucxtestXXXXXX"; + int tmpfd = mkstemp(fname); + FILE *f = tmpfd < 0 ? NULL : fdopen(tmpfd, "w"); + CX_TEST_DO { + CX_TEST_ASSERTM(f, "test file cannot be opened, test aborted"); + fclose(f); + f = NULL; + + CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); + // store something that we don't want to be deleted + cxMapPut(map, "test", "value"); + CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map); + CX_TEST_ASSERT(status == CX_PROPERTIES_NO_DATA); + CX_TEST_ASSERT(cxMapSize(map) == 1); + + char *v = cxMapGet(map, "test"); + CX_TEST_ASSERTM(v, "value was removed"); + CX_TEST_ASSERT(!strcmp(v, "value")); + CX_TEST_ASSERT(!cx_testing_allocator_used(&talloc)); + cxMapFree(map); + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + } + cx_testing_allocator_destroy(&talloc); + if (f) fclose(f); + remove(fname); +} + +CX_TEST(test_properties_load_only_comments) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + char fname[16] = "ucxtestXXXXXX"; + int tmpfd = mkstemp(fname); + FILE *f = tmpfd < 0 ? NULL : fdopen(tmpfd, "w"); + CX_TEST_DO { + CX_TEST_ASSERTM(f, "test file cannot be opened, test aborted"); + fputs("# test file\n\n# contains only comments\n\n# key = value\n", f); + fclose(f); + f = NULL; + + CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); + CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map); + CX_TEST_ASSERT(status == CX_PROPERTIES_NO_DATA); + CX_TEST_ASSERT(cxMapSize(map) == 0); + + CX_TEST_ASSERT(!cx_testing_allocator_used(&talloc)); + cxMapFree(map); + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + } + cx_testing_allocator_destroy(&talloc); + if (f) fclose(f); + remove(fname); +} + CX_TEST(test_properties_multiple_fill) { const char *props1 = "key1 = value1\n"; const char *props2 = "key2 = value2\n"; @@ -559,7 +615,8 @@ cx_test_register(suite, test_properties_next_part); cx_test_register(suite, test_properties_next_long_lines); cx_test_register(suite, test_properties_load); - // TODO: test_properties_load_empty_file + cx_test_register(suite, test_properties_load_empty_file); + cx_test_register(suite, test_properties_load_only_comments); // TODO: test_properties_load_invalid_key // TODO: test_properties_load_missing_delimiter // TODO: test_properties_load_unexpected_end