#include "cx/test.h"
#include "util_allocator.h"
#include "cx/properties.h"
#include "cx/hash_map.h"
CX_TEST(test_properties_init) {
CxProperties prop;
CX_TEST_DO {
cxPropertiesInitDefault(&prop);
CX_TEST_ASSERT(prop.config.delimiter ==
'=');
CX_TEST_ASSERT(prop.config.comment1 ==
'#');
CX_TEST_ASSERT(prop.config.comment2 ==
0);
CX_TEST_ASSERT(prop.config.comment3 ==
0);
CX_TEST_ASSERT(prop.config.continuation ==
'\\');
CX_TEST_ASSERT(prop.input.space ==
NULL);
CX_TEST_ASSERT(prop.buffer.space ==
NULL);
cxPropertiesDestroy(&prop);
}
}
CX_TEST(test_properties_next) {
const char *tests[] = {
"name = value\n",
"name=value\n",
"n=value\n",
"name=v\n",
"n=v\n",
"name = value # comment\n",
"#comment\nn=v\n",
"# comment1\n# comment2\n\n \n\nname = value\n",
" name = value\n",
"name = value\n\n"
};
const char *keys[] = {
"name",
"name",
"n",
"name",
"n",
"name",
"n",
"name",
"name",
"name"
};
const char *values[] = {
"value",
"value",
"value",
"v",
"v",
"value",
"v",
"value",
"value",
"value"
};
CxProperties prop;
cxPropertiesInitDefault(&prop);
CxPropertiesStatus result;
cxstring key;
cxstring value;
CX_TEST_DO {
for (
int i =
0; i <
10; i++) {
cxPropertiesFill(&prop, tests[i]);
CX_TEST_ASSERT(prop.input.space == tests[i]);
CX_TEST_ASSERT(prop.input.size == strlen(tests[i]));
CX_TEST_ASSERT(prop.input.pos ==
0);
result = cxPropertiesNext(&prop, &key, &value);
cxstring k = cx_str(keys[i]);
cxstring v = cx_str(values[i]);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, k));
CX_TEST_ASSERT(
0 == cx_strcmp(value, v));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
}
}
cxPropertiesDestroy(&prop);
}
CX_TEST_SUBROUTINE(test_properties_next_multi_check, CxProperties *prop) {
const char *keys[] = {
"a",
"b",
"c",
"uap",
"name",
"key1",
"key2",
"key3"
};
const char *values[] = {
"a value",
"b value",
"core",
"core",
"ucx",
"value1",
"value2",
"value3"
};
CxPropertiesStatus result;
cxstring key;
cxstring value;
for (
int i =
0; i <
8; i++) {
result = cxPropertiesNext(prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key, cx_str(keys[i])));
CX_TEST_ASSERT(!cx_strcmp(value, cx_str(values[i])));
}
result = cxPropertiesNext(prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
}
CX_TEST(test_properties_next_multi) {
const char *str =
"#\n"
"# properties\n"
"# contains key/value pairs\n"
"#\n"
"a = a value\n"
"b = b value\n"
"c = core\n"
"\n# test\n"
"uap = core\n"
"name = ucx\n"
"# no = property\n"
"key1 = value1\n"
"#key1 = wrong value\n"
"#key2 = not value 2\n"
"key2 = value2\n"
"\n\n\n \n key3=value3\n";
CxProperties prop;
cxPropertiesInitDefault(&prop);
CX_TEST_DO {
CxPropertiesStatus result;
cxstring key;
cxstring value;
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NULL_INPUT);
cxPropertiesFill(&prop, str);
CX_TEST_CALL_SUBROUTINE(test_properties_next_multi_check, &prop);
cxPropertiesFill(&prop, cx_str(str));
CX_TEST_CALL_SUBROUTINE(test_properties_next_multi_check, &prop);
cxPropertiesFill(&prop, cx_mutstr((
char*)str));
CX_TEST_CALL_SUBROUTINE(test_properties_next_multi_check, &prop);
}
cxPropertiesDestroy(&prop);
}
CX_TEST(test_properties_next_part) {
CxProperties prop;
cxPropertiesInitDefault(&prop);
CxPropertiesStatus result;
cxstring key;
cxstring value;
const char *str;
CX_TEST_DO {
str =
"";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
str =
" \n";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
str =
"name";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
" ";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"= ";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
str =
"value";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"\n";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"name")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value")));
str =
"#comment\n";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
str =
"#comment\nname2 = ";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"value2\na = b\n";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"name2")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value2")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"a")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"b")));
str =
"# comment\n#\n#\ntests = ";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"test1 ";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"test2 test3 test4\n";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"tests")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"test1 test2 test3 test4")));
str =
"# just a comment";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
" in 3";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
" parts\nx = 1\n";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"x")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"1")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
}
cxPropertiesDestroy(&prop);
}
CX_TEST(test_properties_next_long_lines) {
CxProperties prop;
cxPropertiesInitDefault(&prop);
CxPropertiesStatus result;
cxstring key;
cxstring value;
size_t key_len =
512;
char *long_key = (
char*)malloc(key_len);
memset(long_key,
'a',
70);
memset(long_key +
70,
'b',
242);
memset(long_key +
312,
'c',
200);
size_t value_len =
2048;
char *long_value = (
char*)malloc(value_len);
memset(long_value,
'x',
1024);
memset(long_value
+1024,
'y',
1024);
CX_TEST_DO {
cxPropertiesFilln(&prop, long_key,
10);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
cxPropertiesFilln(&prop, long_key +
10,
202);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
cxPropertiesFilln(&prop, long_key +
212,
200);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
cxPropertiesFilln(&prop, long_key +
412,
100);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
const char *str =
" = ";
cxPropertiesFill(&prop, str);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
cxPropertiesFilln(&prop, long_value,
512);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
cxPropertiesFilln(&prop, long_value +
512,
1024);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
cxPropertiesFilln(&prop, long_value +
1536,
512);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"\n#comment\nkey = value\n";
cxPropertiesFill(&prop, str);
result = cxPropertiesNext(&prop, &key, &value);
cxstring k = cx_strn(long_key, key_len);
cxstring v = cx_strn(long_value, value_len);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, k));
CX_TEST_ASSERT(
0 == cx_strcmp(value, v));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"key")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
CX_TEST_ASSERT(prop.buffer.capacity >
0);
CX_TEST_ASSERT(cxBufferEof(&prop.buffer));
CX_TEST_ASSERT(cxBufferEof(&prop.input));
cxPropertiesDestroy(&prop);
CX_TEST_ASSERT(prop.buffer.capacity ==
0);
CX_TEST_ASSERT(prop.buffer.size ==
0);
CX_TEST_ASSERT(prop.buffer.pos ==
0);
}
free(long_key);
free(long_value);
}
CX_TEST(test_properties_next_line_continuation) {
const char *str =
"key1 = multiline \\\nvalue\n"
"key2 = normal\n"
"key3 = multiline \\\n trim \n"
"key4 = m1\\\nm2\\\n m3\\\nm4 \n"
"key5 = no\\continuation\n"
"key6 = multiple \\\n \\\n \\\n continuations\n";
CxProperties prop;
cxPropertiesInitDefault(&prop);
cxstring key;
cxstring value;
CX_TEST_DO {
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key,
"key1"));
CX_TEST_ASSERT(!cx_strcmp(value,
"multiline value"));
CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key,
"key2"));
CX_TEST_ASSERT(!cx_strcmp(value,
"normal"));
CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key,
"key3"));
CX_TEST_ASSERT(!cx_strcmp(value,
"multiline trim"));
CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key,
"key4"));
CX_TEST_ASSERT(!cx_strcmp(value,
"m1m2m3m4"));
CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key,
"key5"));
CX_TEST_ASSERT(!cx_strcmp(value,
"no\\continuation"));
CX_TEST_ASSERT(cxPropertiesNext(&prop, &key, &value) ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key,
"key6"));
CX_TEST_ASSERT(!cx_strcmp(value,
"multiple continuations"));
}
cxPropertiesDestroy(&prop);
}
CX_TEST(test_properties_next_line_continuation_part) {
CxProperties prop;
cxPropertiesInitDefault(&prop);
cxstring key;
cxstring value;
CxPropertiesStatus result;
const char *str;
CX_TEST_DO {
str =
"key1 ";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"= continue \\";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"\ncontinue \\\n";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"...";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
str =
"line\nkey2 = value2\n";
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, str));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key,
"key1"));
CX_TEST_ASSERT(!cx_strcmp(value,
"continue continue ...line"));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(!cx_strcmp(key,
"key2"));
CX_TEST_ASSERT(!cx_strcmp(value,
"value2"));
str =
"key3=\\\ncontinue-\\\n line";
size_t len = strlen(str);
for(
size_t i=
0;i<len;i++) {
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop, cx_strn(str+i,
1)));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
}
CX_TEST_ASSERT(
0 == cxPropertiesFill(&prop,
"\n"));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(!cx_strcmp(key,
"key3"));
CX_TEST_ASSERT(!cx_strcmp(value,
"continue-line"));
}
cxPropertiesDestroy(&prop);
}
CX_TEST(test_properties_load) {
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");
fprintf(f,
"# properties file\n\nkey1 = value1\nkey2 = value2\n");
fprintf(f,
"\n\nkey3 = value3\n\n");
size_t key_len =
512;
char *long_key = (
char *) malloc(key_len);
memset(long_key,
'k',
512);
size_t value_len =
2048;
char *long_value = (
char *) malloc(value_len);
memset(long_value,
'v',
2048);
fwrite(long_key,
1, key_len, f);
fprintf(f,
" = ");
fwrite(long_value,
1, value_len, f);
fprintf(f,
" \n");
fprintf(f,
"\n\n\n\nlast_key = property value\n");
fclose(f);
f =
NULL;
CxMap *map = cxHashMapCreateSimple(
CX_STORE_POINTERS);
cxDefineAdvancedDestructor(map, cxFree, &talloc);
CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map);
CX_TEST_ASSERT(status ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(cxMapSize(map) ==
5);
char *v1 = cxMapGet(map,
"key1");
char *v2 = cxMapGet(map, cx_str(
"key2"));
char *v3 = cxMapGet(map,
"key3");
char *lv = cxMapGet(map, cx_strn(long_key, key_len));
char *lk = cxMapGet(map,
"last_key");
CX_TEST_ASSERTM(v1,
"value for key1 not found");
CX_TEST_ASSERTM(v2,
"value for key2 not found");
CX_TEST_ASSERTM(v3,
"value for key3 not found");
CX_TEST_ASSERTM(lv,
"value for long key not found");
CX_TEST_ASSERTM(lk,
"value for last_key not found");
CX_TEST_ASSERT(!strcmp(v1,
"value1"));
CX_TEST_ASSERT(!strcmp(v2,
"value2"));
CX_TEST_ASSERT(!strcmp(v3,
"value3"));
cxstring expected = cx_strn(long_value, value_len);
cxstring actual = cx_str(lv);
CX_TEST_ASSERT(!cx_strcmp(expected, actual));
CX_TEST_ASSERT(!strcmp(lk,
"property value"));
free(long_key);
free(long_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_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);
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_load_error) {
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\ntest = value\n = value2\n", f);
fclose(f);
f =
NULL;
CxMap *map = cxHashMapCreateSimple(
CX_STORE_POINTERS);
cxDefineAdvancedDestructor(map, cxFree, &talloc);
CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map);
CX_TEST_ASSERT(status ==
CX_PROPERTIES_INVALID_EMPTY_KEY);
CX_TEST_ASSERT(cxMapSize(map) ==
1);
char *v = cxMapGet(map,
"test");
CX_TEST_ASSERT(v !=
NULL);
CX_TEST_ASSERT(!strcmp(v,
"value"));
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_file_not_exists) {
CX_TEST_DO {
CxMap *map = cxHashMapCreateSimple(
CX_STORE_POINTERS);
CxPropertiesStatus status = cxPropertiesLoadDefault(
NULL,
"does-definitely-not-exist", map);
CX_TEST_ASSERT(status ==
CX_PROPERTIES_FILE_ERROR);
cxMapFree(map);
}
}
CX_TEST(test_properties_load_cxmutstr_map) {
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\ntest = value\ntest2 = value2\n", f);
fclose(f);
f =
NULL;
CxMap *map = cxHashMapCreateSimple(
sizeof(cxmutstr));
cxDefineAdvancedDestructor(map, cx_strfree_a, &talloc);
CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map);
CX_TEST_ASSERT(status ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(cxMapSize(map) ==
2);
cxstring v1 =
CX_STR(
"value");
cxstring v2 =
CX_STR(
"value2");
CX_TEST_ASSERT(cx_strcmp_p(cxMapGet(map,
"test"), &v1) ==
0);
CX_TEST_ASSERT(cx_strcmp_p(cxMapGet(map,
"test2"), &v2) ==
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_load_incompatible_map) {
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\ntest = value\ntest2 = value2\n", f);
fclose(f);
f =
NULL;
CxMap *map = cxHashMapCreateSimple(
sizeof(CxBuffer));
cxDefineAdvancedDestructor(map, cxFree, &talloc);
CxPropertiesStatus status = cxPropertiesLoadDefault(&talloc.base, fname, map);
CX_TEST_ASSERT(status ==
CX_PROPERTIES_MAP_ERROR);
CX_TEST_ASSERT(cxMapSize(map) ==
0);
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";
const char *props3 =
"key3 = value3\n";
CxProperties prop;
cxPropertiesInitDefault(&prop);
CxPropertiesStatus result;
cxstring key;
cxstring value;
CX_TEST_DO {
cxPropertiesFill(&prop, props1);
cxPropertiesFill(&prop, props2);
cxPropertiesFill(&prop, props3);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"key1")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value1")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"key2")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value2")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"key3")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value3")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
}
cxPropertiesDestroy(&prop);
}
CX_TEST(test_properties_use_stack) {
const char *props1 =
"key1 = val";
const char *props2 =
"ue1\nkey2 = value2";
const char *props3 =
"\nkey3 = value3\n";
char stackmem[
16];
CxProperties prop;
cxPropertiesInitDefault(&prop);
cxPropertiesUseStack(&prop, stackmem,
16);
CxPropertiesStatus result;
cxstring key;
cxstring value;
CX_TEST_DO {
cxPropertiesFill(&prop, props1);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
cxPropertiesFill(&prop, props2);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"key1")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value1")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INCOMPLETE_DATA);
cxPropertiesFill(&prop, props3);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"key2")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value2")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"key3")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"value3")));
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_DATA);
}
cxPropertiesDestroy(&prop);
}
CX_TEST(test_properties_empty_key) {
const char *fail1 =
"= val\n";
const char *fail2 =
" = val\n";
const char *good =
" key = val\n";
CxProperties prop;
CxPropertiesStatus result;
cxstring key;
cxstring value;
CX_TEST_DO {
cxPropertiesInitDefault(&prop);
cxPropertiesFill(&prop, fail1);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INVALID_EMPTY_KEY);
cxPropertiesReset(&prop);
cxPropertiesFill(&prop, fail2);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_INVALID_EMPTY_KEY);
cxPropertiesReset(&prop);
cxPropertiesFill(&prop, good);
result = cxPropertiesNext(&prop, &key, &value);
CX_TEST_ASSERT(result ==
CX_PROPERTIES_NO_ERROR);
CX_TEST_ASSERT(
0 == cx_strcmp(key, cx_str(
"key")));
CX_TEST_ASSERT(
0 == cx_strcmp(value, cx_str(
"val")));
cxPropertiesDestroy(&prop);
}
}
CxTestSuite *cx_test_suite_properties(
void) {
CxTestSuite *suite = cx_test_suite_new(
"properties");
cx_test_register(suite, test_properties_init);
cx_test_register(suite, test_properties_next);
cx_test_register(suite, test_properties_next_multi);
cx_test_register(suite, test_properties_next_part);
cx_test_register(suite, test_properties_next_long_lines);
cx_test_register(suite, test_properties_next_line_continuation);
cx_test_register(suite, test_properties_next_line_continuation_part);
cx_test_register(suite, test_properties_load);
cx_test_register(suite, test_properties_load_empty_file);
cx_test_register(suite, test_properties_load_only_comments);
cx_test_register(suite, test_properties_load_error);
cx_test_register(suite, test_properties_load_file_not_exists);
cx_test_register(suite, test_properties_load_cxmutstr_map);
cx_test_register(suite, test_properties_load_incompatible_map);
cx_test_register(suite, test_properties_multiple_fill);
cx_test_register(suite, test_properties_use_stack);
cx_test_register(suite, test_properties_empty_key);
return suite;
}