fix return value of cxPropertiesLoad() for non-empty files that do not contain keys

Tue, 09 Dec 2025 17:27:58 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 09 Dec 2025 17:27:58 +0100
changeset 1558
fc863c877a75
parent 1557
03fbf1c99e73
child 1559
9e50f45f8736

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

mercurial