/* * libfyaml-test-generic.c - libfyaml generics tests * * Copyright (c) 2023 Pantelis Antoniou * * SPDX-License-Identifier: MIT */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifndef _WIN32 #include #endif #include #include #include #include "fy-generic.h" #include "fy-generic-decoder.h" #include "fy-generic-encoder.h" #include "fy-parse.h" #include "fy-check.h" /* make it less noisy, this is a test-suite after all */ FY_DIAG_PUSH FY_DIAG_IGNORE_UNUSED_VARIABLE FY_DIAG_IGNORE_UNUSED_PARAMETER /* Test: Basic generic types, sanity testing */ START_TEST(generic_basics) { fy_generic v; fy_generic_sequence_handle seqh; fy_generic_mapping_handle maph; int i; const char *str; v = fy_invalid; ck_assert(!fy_generic_is_valid(v)); /* null */ v = fy_null; ck_assert(fy_generic_is_null_type(v)); ck_assert(fy_generic_is_in_place(v)); /* bool */ v = fy_local_bool(true); ck_assert(fy_generic_is_bool_type(v)); ck_assert(fy_generic_is_in_place(v)); /* int (in place) */ v = fy_local_int(100); ck_assert(fy_generic_is_int_type(v)); ck_assert(fy_generic_is_in_place(v)); /* int (out of place) */ v = fy_local_int(FYGT_INT_INPLACE_MAX+1); ck_assert(fy_get_type(v) == FYGT_INT); ck_assert(!fy_generic_is_in_place(v)); /* float (in place in 64 bit), out of place for 32 bit */ v = fy_local_float(100.0); ck_assert(fy_generic_is_float_type(v)); #ifdef FYGT_GENERIC_64 ck_assert(fy_generic_is_in_place(v)); #else ck_assert(!fy_generic_is_in_place(v)); #endif /* double (out of place for both) */ v = fy_local_float(DBL_MIN); ck_assert(fy_generic_is_float_type(v)); ck_assert(!fy_generic_is_in_place(v)); /* string of length 2 (3 with \0) in place always */ v = fy_local_string("sh"); ck_assert(fy_generic_is_string(v)); ck_assert(fy_generic_is_in_place(v)); /* string of length 6 (7 with \0) in place for 64 bit */ v = fy_local_string("short1"); ck_assert(fy_generic_is_string(v)); #ifdef FYGT_GENERIC_64 ck_assert(fy_generic_is_in_place(v)); #else ck_assert(!fy_generic_is_in_place(v)); #endif /* long string is always out of place */ v = fy_local_string("long string out of place"); ck_assert(fy_generic_is_string(v)); ck_assert(!fy_generic_is_in_place(v)); /* sequence */ v = fy_local_sequence(fy_local_int(10), fy_local_string("a string to test")); ck_assert(fy_generic_is_sequence(v)); ck_assert(!fy_generic_is_in_place(v)); /* check that the sequence is correct */ seqh = fy_cast(v, fy_seq_handle_null); ck_assert_ptr_ne(seqh, NULL); ck_assert(seqh->count == 2); ck_assert(fy_len(seqh) == 2); i = fy_cast(seqh->items[0], -1); ck_assert(i == 10); str = fy_castp(&seqh->items[1], ""); ck_assert(!strcmp(str, "a string to test")); /* mapping */ v = fy_local_mapping(fy_local_int(10), fy_local_string("a string to test")); ck_assert(fy_generic_is_mapping(v)); ck_assert(!fy_generic_is_in_place(v)); /* check that the mapping is correct */ maph = fy_cast(v, fy_map_handle_null); ck_assert_ptr_ne(maph, NULL); ck_assert(maph->count == 1); ck_assert(fy_len(maph) == 1); i = fy_cast(maph->pairs[0].key, -1); ck_assert(i == 10); str = fy_castp(&maph->pairs[0].value, ""); ck_assert(!strcmp(str, "a string to test")); } END_TEST /* Test: testing bool range */ START_TEST(generic_bool_range) { static const bool btable[2] = { false, true, }; bool pass, test_fail; unsigned int i; fy_generic v; bool res, bv; test_fail = false; for (i = 0; i < ARRAY_SIZE(btable); i++) { bv = btable[i]; v = fy_bool(bv); res = fy_cast(v, (_Bool)false); pass = bv == res; test_fail |= !pass; printf("boolean/%s = %016llx %s - %s\n", bv ? "true" : "false", (unsigned long long)v.v, res ? "true" : "false", pass ? "PASS" : "FAIL"); } ck_assert(!test_fail); } END_TEST /* Test: testing int range */ START_TEST(generic_int_range) { static const long long itable[] = { 0, 1, -1, LLONG_MAX, LLONG_MIN, FYGT_INT_INPLACE_MAX, FYGT_INT_INPLACE_MIN, FYGT_INT_INPLACE_MAX+1, FYGT_INT_INPLACE_MIN-1, }; bool pass, test_fail; unsigned int i; fy_generic v; long long res, iv; test_fail = false; for (i = 0; i < ARRAY_SIZE(itable); i++) { iv = itable[i]; v = fy_int(iv); res = fy_cast(v, (long long)0); pass = iv == res; test_fail |= !pass; printf("int/%lld = %016llx %lld - %s\n", iv, (unsigned long long)v.v, res, pass ? "PASS" : "FAIL"); } ck_assert(!test_fail); } END_TEST /* Test: testing float range */ START_TEST(generic_float_range) { static const double ftable[] = { 0.0, -0,0, 1.0, -1.0, 0.1, -0.1, 128.0, -128.0, 256.1, -256.1, INFINITY, -INFINITY, NAN, -NAN, 100000.00001, /* does not fit in 32 bit float */ FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX, }; bool pass, test_fail; unsigned int i; fy_generic v; double res, fv; test_fail = false; for (i = 0; i < ARRAY_SIZE(ftable); i++) { fv = ftable[i]; v = fy_float(fv); res = fy_cast(v, (double)0.0); if (isfinite(fv)) pass = fv == res; else if (isinf(fv)) pass = isinf(fv) == isinf(res); else if (isnan(fv)) pass = isnan(fv) == isnan(res); else pass = false; test_fail |= !pass; printf("float/%g = %016llx %g - %s\n", fv, (unsigned long long)v.v, res, pass ? "PASS" : "FAIL"); } ck_assert(!test_fail); } END_TEST /* Test: testing size encoding */ START_TEST(generic_size_encoding) { static const size_t sztable[] = { 0, ((size_t)1 << 7) - 1, ((size_t)1 << 7), ((size_t)1 << 7) + 1, ((size_t)1 << 14) - 1, ((size_t)1 << 14), ((size_t)1 << 14) + 1, ((size_t)1 << 21) - 1, ((size_t)1 << 21), ((size_t)1 << 21) + 1, ((size_t)1 << 28) - 1, ((size_t)1 << 28), ((size_t)1 << 28) + 1, ((size_t)1 << 29) - 1, ((size_t)1 << 29), ((size_t)1 << 29) + 1, #if SIZE_MAX > UINT32_MAX ((size_t)1 << 35) - 1, ((size_t)1 << 35), ((size_t)1 << 35) + 1, ((size_t)1 << 42) - 1, ((size_t)1 << 42), ((size_t)1 << 42) + 1, ((size_t)1 << 49) - 1, ((size_t)1 << 49), ((size_t)1 << 49) + 1, ((size_t)1 << 56) - 1, ((size_t)1 << 56), ((size_t)1 << 56) + 1, ((size_t)1 << 57) - 1, ((size_t)1 << 57), ((size_t)1 << 57) + 1, (size_t)UINT64_MAX, #endif (size_t)UINT32_MAX, }; uint8_t size_buf[FYGT_SIZE_ENCODING_MAX_64]; unsigned int i, j, k; size_t sz, szd; uint32_t sz32d; uint8_t *szp; for (i = 0; i < ARRAY_SIZE(sztable); i++) { sz = sztable[i]; printf("size_t/%zx =", sz); j = fy_encode_size_bytes(sz); ck_assert(j <= sizeof(size_buf)); printf(" (%d)", j); memset(size_buf, 0, sizeof(size_buf)); szp = fy_encode_size(size_buf, sizeof(size_buf), sz); ck_assert_ptr_ne(szp, NULL); ck_assert((unsigned int)(szp - size_buf) == j); for (k = 0; k < j; k++) printf(" %02x", size_buf[k] & 0xff); szd = 0; fy_decode_size(size_buf, sizeof(size_buf), &szd); printf(" decoded=%zx", szd); szd = 0; fy_decode_size_nocheck(size_buf, &szd); printf(" decoded_nocheck=%zx", szd); printf("\n"); /* decoding must match */ ck_assert(szd == sz); } for (i = 0; i < ARRAY_SIZE(sztable); i++) { sz = sztable[i]; if (sz > UINT32_MAX) continue; printf("uint32_t/%zx =", sz); j = fy_encode_size32_bytes((uint32_t)sz); ck_assert(j <= sizeof(size_buf)); printf(" (%d)", j); memset(size_buf, 0, sizeof(size_buf)); szp = fy_encode_size32(size_buf, sizeof(size_buf), (uint32_t)sz); ck_assert_ptr_ne(szp, NULL); ck_assert((unsigned int)(szp - size_buf) == j); for (k = 0; k < j; k++) printf(" %02x", size_buf[k] & 0xff); sz32d = 0; fy_decode_size32(size_buf, sizeof(size_buf), &sz32d); printf(" decoded=%zx", (size_t)sz32d); printf("\n"); /* decoding must match */ ck_assert(sz32d == (uint32_t)sz); } } END_TEST /* Test: testing string encoding */ START_TEST(generic_string_range) { static const char *stable[] = { "", /* empty string */ "0", "01", "012", "0123", "01234", "012345", "0123456", "01234567", "This is a string", "invoice", /* a longer than 128 characters string */ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, " "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo " "consequat. Duis aute irure dolor in reprehenderit in voluptate velit " "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat " "cupidatat non proident, sunt in culpa qui officia deserunt mollit anim " "id est laborum." }; bool pass, test_fail; unsigned int i; fy_generic v; const char *res, *sv; /* try normal cast at first */ test_fail = false; for (i = 0; i < ARRAY_SIZE(stable); i++) { sv = stable[i]; v = fy_string(sv); res = fy_cast(v, "invalid"); pass = !strcmp(sv, res); test_fail |= !pass; printf("cast(v) string/%s = %016llx %s - %s\n", sv, (unsigned long long)v.v, res, pass ? "PASS" : "FAIL"); } /* now use castp */ for (i = 0; i < ARRAY_SIZE(stable); i++) { sv = stable[i]; v = fy_string(sv); res = fy_castp(&v, "invalid"); pass = !strcmp(sv, res); test_fail |= !pass; printf("cast(&v) string/%s = %016llx %s - %s\n", sv, (unsigned long long)v.v, res, pass ? "PASS" : "FAIL"); } ck_assert(!test_fail); } END_TEST /* Test: Automatic generic type promotion */ START_TEST(generic_type_promotion) { fy_generic v; fy_generic_sequence_handle seqh; fy_generic_mapping_handle maph; int i; const char *str; bool ok; /* almost the same as basic test, but with auto type promotion now */ /* null */ v = fy_value((void *)NULL); ck_assert(fy_generic_is_null_type(v)); ck_assert(fy_generic_is_in_place(v)); /* bool */ v.v = fy_generic_in_place_bool((_Bool)true); ck_assert(fy_generic_is_bool_type(v)); ck_assert(fy_generic_is_in_place(v)); ck_assert(fy_cast(v, (_Bool)false) == true); ck_assert(fy_castp(&v, (_Bool)false) == true); v = fy_value((_Bool)false); ck_assert(fy_generic_is_bool_type(v)); ck_assert(fy_generic_is_in_place(v)); ck_assert(fy_cast(v, (_Bool)true) == false); ck_assert(fy_castp(&v, (_Bool)true) == false); /* int (in place) */ v = fy_value(100); ck_assert(fy_generic_is_int_type(v)); ck_assert(fy_generic_is_in_place(v)); ck_assert(fy_cast(v, -1) == 100); ck_assert(fy_castp(&v, -1) == 100); /* int (out of place) */ v = fy_value((long long)(FYGT_INT_INPLACE_MAX+1)); ck_assert(fy_get_type(v) == FYGT_INT); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_cast(v, (long long)-1) == FYGT_INT_INPLACE_MAX+1); ck_assert(fy_castp(&v, (long long)-1) == FYGT_INT_INPLACE_MAX+1); /* float (in place in 64 bit), out of place for 32 bit */ v = fy_value((float)100.0f); ck_assert(fy_generic_is_float_type(v)); #ifdef FYGT_GENERIC_64 ck_assert(fy_generic_is_in_place(v)); #else ck_assert(!fy_generic_is_in_place(v)); #endif ck_assert(fy_cast(v, (float)NAN) == 100.0); ck_assert(fy_castp(&v, (float)NAN) == 100.0); /* double (out of place for both) */ v = fy_value((double)DBL_MIN); ck_assert(fy_generic_is_float_type(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_cast(v, (double)NAN) == DBL_MIN); ck_assert(fy_castp(&v, (double)NAN) == DBL_MIN); /* string of length 2 (3 with \0) in place always */ v = fy_value("sh"); ck_assert(fy_generic_is_string(v)); ck_assert(fy_generic_is_in_place(v)); ok = !strcmp(fy_cast(v, ""), "sh"); ck_assert(ok); ok = !strcmp(fy_castp(&v, ""), "sh"); ck_assert(ok); /* string of length 6 (7 with \0) in place for 64 bit */ v = fy_value("short1"); ck_assert(fy_generic_is_string(v)); #ifdef FYGT_GENERIC_64 ck_assert(fy_generic_is_in_place(v)); #else ck_assert(!fy_generic_is_in_place(v)); #endif ok = !strcmp(fy_cast(v, ""), "short1"); ck_assert(ok); ok = !strcmp(fy_castp(&v, ""), "short1"); ck_assert(ok); /* long string is always out of place */ v = fy_value("long string out of place"); ck_assert(fy_generic_is_string(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(!strcmp(fy_cast(v, ""), "long string out of place")); ck_assert(!strcmp(fy_castp(&v, ""), "long string out of place")); /* sequence */ v = fy_local_sequence(10, "a string to test"); ck_assert(fy_generic_is_sequence(v)); ck_assert(!fy_generic_is_in_place(v)); /* check that the sequence is correct */ seqh = fy_cast(v, fy_seq_handle_null); ck_assert_ptr_ne(seqh, NULL); ck_assert(seqh->count == 2); ck_assert(fy_len(seqh) == 2); i = fy_cast(seqh->items[0], -1); ck_assert(i == 10); i = fy_castp(&seqh->items[0], -1); ck_assert(i == 10); str = fy_cast(seqh->items[1], ""); ck_assert(!strcmp(str, "a string to test")); str = fy_castp(&seqh->items[1], ""); ck_assert(!strcmp(str, "a string to test")); /* mapping */ v = fy_local_mapping(10, "a string to test"); ck_assert(fy_generic_is_mapping(v)); ck_assert(!fy_generic_is_in_place(v)); /* check that the mapping is correct */ maph = fy_cast(v, fy_map_handle_null); ck_assert_ptr_ne(maph, NULL); ck_assert(maph->count == 1); ck_assert(fy_len(maph) == 1); i = fy_cast(maph->pairs[0].key, -1); ck_assert(i == 10); i = fy_castp(&maph->pairs[0].key, -1); ck_assert(i == 10); str = fy_cast(maph->pairs[0].value, ""); ck_assert(!strcmp(str, "a string to test")); str = fy_castp(&maph->pairs[0].value, ""); ck_assert(!strcmp(str, "a string to test")); } END_TEST /* Test: testing valid, invalid propagation */ START_TEST(generic_invalid_propagation) { fy_generic v, vv; /* valid value passes through */ v = fy_value(100); vv = fy_validate(v); ck_assert(fy_generic_is_valid(vv)); ck_assert(v.v == vv.v); /* invalid value is invalid */ v = fy_invalid; vv = fy_validate(v); ck_assert(fy_generic_is_invalid(vv)); /* a sequence with all valid items is valid */ v = fy_local_sequence(true, false, "string", 100000); ck_assert(fy_generic_is_sequence(v)); vv = fy_validate(v); ck_assert(fy_generic_is_valid(vv)); ck_assert(v.v == vv.v); /* a sequence with an invalid item is invalid */ v = fy_local_sequence(true, false, fy_invalid, 100000); ck_assert(fy_generic_is_sequence(v)); vv = fy_validate(v); ck_assert(fy_generic_is_invalid(vv)); /* a mapping with all valid items is valid */ v = fy_local_mapping("foo", false, "bar", true); ck_assert(fy_generic_is_mapping(v)); vv = fy_validate(v); ck_assert(fy_generic_is_valid(vv)); ck_assert(v.v == vv.v); /* a mapping with an invalid key is invalid */ v = fy_local_mapping("foo", false, fy_invalid, true); ck_assert(fy_generic_is_mapping(v)); vv = fy_validate(v); ck_assert(fy_generic_is_invalid(vv)); /* a mapping with an invalid value is invalid */ v = fy_local_mapping("foo", false, "bar", fy_invalid); ck_assert(fy_generic_is_mapping(v)); vv = fy_validate(v); ck_assert(fy_generic_is_invalid(vv)); /* a sequence with all deep items valid is valid */ v = fy_local_sequence(true, false, fy_local_mapping("foo", "bar"), 100000); ck_assert(fy_generic_is_sequence(v)); vv = fy_validate(v); ck_assert(fy_generic_is_valid(vv)); ck_assert(v.v == vv.v); /* a sequence with a deep invalid is invalid */ v = fy_local_sequence(true, false, fy_local_mapping("foo", fy_invalid), 100000); ck_assert(fy_generic_is_sequence(v)); vv = fy_validate(v); ck_assert(fy_generic_is_invalid(vv)); } END_TEST /* Test: sized string */ START_TEST(generic_sized_string) { const char *str0_short = "H\0A"; const size_t str0_short_sz = 3; const char *str0_long = "Hello\0There\0Long\0String"; const size_t str0_long_sz = 23; const fy_generic_sized_string szstr0_short = { .data = str0_short, .size = str0_short_sz }; const fy_generic_sized_string szstr0_long = { .data = str0_long, .size = str0_long_sz }; fy_generic_sized_string szstr; fy_generic v; /* test short sized string */ v = fy_value(szstr0_short); ck_assert(fy_generic_is_string(v)); szstr = fy_cast(v, fy_szstr_empty); /* check it roundtripped */ ck_assert(szstr.size == szstr0_short.size); ck_assert(!memcmp(szstr.data, szstr0_short.data, szstr0_short.size)); /* test long sized string */ v = fy_value(szstr0_long); ck_assert(fy_generic_is_string(v)); szstr = fy_cast(v, fy_szstr_empty); /* check it roundtripped */ ck_assert(szstr.size == szstr0_long.size); ck_assert(!memcmp(szstr.data, szstr0_long.data, szstr0_long.size)); } END_TEST /* Test: decorated int (full range int) */ START_TEST(generic_decorated_int) { fy_generic_decorated_int dint; unsigned long long ullv; fy_generic v; /* first test in place */ v = fy_value(1LLU); ck_assert(fy_generic_is_int_type(v)); ullv = fy_cast(v, 0LLU); ck_assert(ullv == 1LLU); dint = fy_cast(v, fy_dint_empty); ck_assert(dint.uv == 1LLU); /* test out of place, but still in signed range */ v = fy_value((unsigned long long)LLONG_MAX); ck_assert(fy_generic_is_int_type(v)); ullv = fy_cast(v, 0LLU); ck_assert(ullv == (unsigned long long)LLONG_MAX); dint = fy_cast(v, fy_dint_empty); ck_assert(dint.uv == (unsigned long long)LLONG_MAX); /* test out of place, but now in unsigned range */ v = fy_value((unsigned long long)LLONG_MAX + 1); ck_assert(fy_generic_is_int_type(v)); ullv = fy_cast(v, 0LLU); ck_assert(ullv == (unsigned long long)LLONG_MAX + 1); dint = fy_cast(v, fy_dint_empty); ck_assert(dint.uv == (unsigned long long)LLONG_MAX + 1); ck_assert((dint.flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* must be marked as unsigned */ /* test maximum */ v = fy_value(ULLONG_MAX); ck_assert(fy_generic_is_int_type(v)); ullv = fy_cast(v, 0LLU); ck_assert(ullv == ULLONG_MAX); dint = fy_cast(v, fy_dint_empty); ck_assert(dint.uv == ULLONG_MAX); ck_assert((dint.flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* must be marked as unsigned */ } END_TEST /* Test: casting checks */ START_TEST(generic_casts) { fy_generic v; fy_generic_sequence_handle seqh; fy_generic_mapping_handle maph; /* first test casts that should succeed */ /* null */ v = fy_value((void *)NULL); ck_assert(fy_cast(v, (void *)NULL) == (void *)NULL); /* bool */ v = fy_value((_Bool)true); ck_assert(fy_cast(v, (_Bool)false) == true); v = fy_value((_Bool)false); ck_assert(fy_cast(v, (_Bool)true) == false); /* char */ v = fy_value('a'); ck_assert(fy_cast(v, '\0') == 'a'); v = fy_value(CHAR_MIN); ck_assert(fy_cast(v, '1') == CHAR_MIN); v = fy_value(CHAR_MAX); ck_assert(fy_cast(v, '1') == CHAR_MAX); v = fy_value((signed char)0x61); ck_assert(fy_cast(v, (signed char)0x00) == 0x61); v = fy_value(SCHAR_MIN); ck_assert(fy_cast(v, '1') == SCHAR_MIN); v = fy_value(SCHAR_MAX); ck_assert(fy_cast(v, '1') == SCHAR_MAX); v = fy_value((unsigned char)0xf1); ck_assert(fy_cast(v, (unsigned char)0x00) == 0xf1); v = fy_value(UCHAR_MAX); ck_assert(fy_cast(v, '1') == UCHAR_MAX); /* short */ v = fy_value(SHRT_MIN); ck_assert(fy_cast(v, (short)0) == SHRT_MIN); v = fy_value(SHRT_MAX); ck_assert(fy_cast(v, (short)0) == SHRT_MAX); v = fy_value(USHRT_MAX); ck_assert(fy_cast(v, (unsigned short)0) == USHRT_MAX); /* int */ v = fy_value(INT_MIN); ck_assert(fy_cast(v, (int)0) == INT_MIN); v = fy_value(INT_MAX); ck_assert(fy_cast(v, (int)0) == INT_MAX); v = fy_value(UINT_MAX); ck_assert(fy_cast(v, (unsigned int)0) == UINT_MAX); /* long */ v = fy_value(LONG_MIN); ck_assert(fy_cast(v, (long)0) == LONG_MIN); v = fy_value(LONG_MAX); ck_assert(fy_cast(v, (long)0) == LONG_MAX); v = fy_value(ULONG_MAX); ck_assert(fy_cast(v, (unsigned long)0) == ULONG_MAX); /* long long */ v = fy_value(LLONG_MIN); ck_assert(fy_cast(v, (long long)0) == LLONG_MIN); v = fy_value(LLONG_MAX); ck_assert(fy_cast(v, (long long)0) == LLONG_MAX); v = fy_value(ULLONG_MAX); ck_assert(fy_cast(v, (unsigned long long)0) == ULLONG_MAX); /* float */ v = fy_value((float)FLT_MIN); ck_assert(fy_cast(v, (float)NAN) == (float)FLT_MIN); v = fy_value((float)FLT_MAX); ck_assert(fy_cast(v, (float)NAN) == (float)FLT_MAX); v = fy_value((float)-FLT_MIN); ck_assert(fy_cast(v, (float)NAN) == (float)-FLT_MIN); v = fy_value((float)-FLT_MAX); ck_assert(fy_cast(v, (float)NAN) == (float)-FLT_MAX); /* double */ v = fy_value((double)DBL_MIN); ck_assert(fy_cast(v, (double)NAN) == (double)DBL_MIN); v = fy_value((double)DBL_MAX); ck_assert(fy_cast(v, (double)NAN) == (double)DBL_MAX); v = fy_value((double)-DBL_MIN); ck_assert(fy_cast(v, (double)NAN) == (double)-DBL_MIN); v = fy_value((double)-DBL_MAX); ck_assert(fy_cast(v, (double)NAN) == (double)-DBL_MAX); /* string */ v = fy_value("This is a string"); ck_assert(!strcmp(fy_cast(v, ""), "This is a string")); /* sequence */ v = fy_local_sequence(1, 2, 3); seqh = fy_cast(v, fy_seq_handle_null); ck_assert(seqh != fy_seq_handle_null); /* mapping */ v = fy_local_mapping("foo", "bar", "baz", (_Bool)true); maph = fy_cast(v, fy_map_handle_null); ck_assert(maph != fy_map_handle_null); /* now test the invalid type casts */ v = fy_value((_Bool)true); ck_assert(fy_cast(v, 0) == 0); ck_assert(!strcmp(fy_cast(v, ""), "")); ck_assert(fy_cast(v, 0.0f) == 0.0f); ck_assert(fy_cast(v, fy_seq_handle_null) == fy_seq_handle_null); /* onwards to the range casts */ v = fy_value(CHAR_MIN - 1); ck_assert(fy_cast(v, (char)'0') == '0'); v = fy_value(CHAR_MAX + 1); ck_assert(fy_cast(v, (char)'0') == '0'); v = fy_value(SCHAR_MIN - 1); ck_assert(fy_cast(v, (signed char)0) == 0); v = fy_value(SCHAR_MAX + 1); ck_assert(fy_cast(v, (signed char)0) == 0); v = fy_value(-1); ck_assert(fy_cast(v, (unsigned char)0) == 0); v = fy_value(UCHAR_MAX + 1); ck_assert(fy_cast(v, (unsigned char)0) == 0); v = fy_value(SHRT_MIN - 1); ck_assert(fy_cast(v, (signed short)0) == 0); v = fy_value(SHRT_MAX + 1); ck_assert(fy_cast(v, (signed short)0) == 0); v = fy_value(-1); ck_assert(fy_cast(v, (unsigned short)0) == 0); v = fy_value(USHRT_MAX + 1); ck_assert(fy_cast(v, (unsigned short)0) == 0); v = fy_value((signed long long)INT_MIN - 1); ck_assert(fy_cast(v, (signed int)0) == 0); v = fy_value((signed long long)INT_MAX + 1); ck_assert(fy_cast(v, (signed int)0) == 0); v = fy_value(-1); ck_assert(fy_cast(v, (unsigned int)0) == 0); v = fy_value((signed long long)UINT_MAX + 1); ck_assert(fy_cast(v, (unsigned int)0) == 0); /* LONG and LLONG are at the range limit, so don't try to be smart */ } END_TEST /* Test: get api */ START_TEST(generic_get) { char buf[16384]; struct fy_generic_builder *gb; fy_generic seq, map, v; fy_generic_sequence_handle seqh; fy_generic_mapping_handle maph; int iv; _Bool bv; const char *strv; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* sequence */ seq = fy_local_sequence(-100, (_Bool)true, "sh", "long string"); ck_assert(fy_generic_is_sequence(seq)); /* manual access through seq generic value */ iv = fy_get(seq, 0, -1); ck_assert(iv == -100); bv = fy_get(seq, 1, (_Bool)false); ck_assert(bv == (_Bool)true); strv = fy_get(seq, 2, ""); ck_assert(!strcmp(strv, "sh")); strv = fy_get(seq, 3, ""); ck_assert(!strcmp(strv, "long string")); /* manual access through the seq handle (somewhat faster) */ seqh = fy_cast(seq, fy_seq_handle_null); ck_assert(seqh != NULL); iv = fy_get(seqh, 0, -1); ck_assert(iv == -100); bv = fy_get(seqh, 1, (_Bool)false); ck_assert(bv == (_Bool)true); strv = fy_get(seqh, 2, ""); ck_assert(!strcmp(strv, "sh")); strv = fy_get(seqh, 3, ""); ck_assert(!strcmp(strv, "long string")); /* try to access something that does not exist */ iv = fy_get(seq, -1, -1); ck_assert(iv == -1); iv = fy_get(seq, "foo", -1); ck_assert(iv == -1); iv = fy_get(seq, 1000, -1); ck_assert(iv == -1); /* mapping */ map = fy_local_mapping("foo", 100, "bar", 200); ck_assert(fy_generic_is_mapping(map)); /* manual access through map generic value */ iv = fy_get(map, "foo", -1); ck_assert(iv == 100); iv = fy_get(map, "bar", -1); ck_assert(iv == 200); /* manual access through the map handle (somewhat faster) */ maph = fy_cast(map, fy_map_handle_null); ck_assert(maph != NULL); iv = fy_get(maph, "foo", -1); ck_assert(iv == 100); iv = fy_get(maph, "bar", -1); ck_assert(iv == 200); /* try to access something that does not exist */ iv = fy_get(maph, "dummy", -1); ck_assert(iv == -1); /* manual access through generic op */ seq = fy_local_sequence(-100, (_Bool)true, "sh", "long string"); v = fy_generic_op(gb, FYGBOPF_GET, seq, 1, (fy_generic [1]) { fy_value(0) } ); ck_assert(fy_cast(v, -1) == -100); v = fy_generic_op(gb, FYGBOPF_GET, seq, 1, (fy_generic [1]) { fy_value(1) } ); ck_assert(fy_cast(v, (_Bool)false) == true); v = fy_generic_op(gb, FYGBOPF_GET, seq, 1, (fy_generic [1]) { fy_value(2) } ); ck_assert(!strcmp(fy_cast(v, ""), "sh")); v = fy_generic_op(gb, FYGBOPF_GET, seq, 1, (fy_generic [1]) { fy_value(3) } ); ck_assert(!strcmp(fy_cast(v, ""), "long string")); /* manual access through generic op */ map = fy_local_mapping("foo", 100, "bar", 200); v = fy_generic_op(gb, FYGBOPF_GET, map, 1, (fy_generic [1]) { fy_value("foo") } ); ck_assert(fy_cast(v, -1) == 100); v = fy_generic_op(gb, FYGBOPF_GET, map, 1, (fy_generic [1]) { fy_value("bar") } ); ck_assert(fy_cast(v, -1) == 200); } END_TEST /* Test: get_at */ START_TEST(generic_get_at) { char buf[16384]; struct fy_generic_builder *gb; fy_generic seq, map, v; fy_generic_sequence_handle seqh; fy_generic_map_pair mp; const fy_generic_map_pair *mpp; const char *key; size_t i, len; int val, sum; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); seq = fy_sequence(10, 100, 1000); ck_assert(fy_generic_is_sequence(seq)); len = fy_len(seq); ck_assert(len == 3); sum = 0; for (i = 0; i < len; i++) { val = fy_get_at(seq, i, -1); ck_assert(val != -1); sum += val; printf("%s: [%zu]=%d\n", __func__, i, val); } printf("%s: sum=%d\n", __func__, sum); ck_assert(sum == 1110); seqh = fy_cast(seq, fy_seq_handle_null); ck_assert(seqh != NULL); ck_assert(fy_generic_is_sequence(seq)); len = fy_len(seqh); ck_assert(len == 3); sum = 0; for (i = 0; i < len; i++) { val = fy_get_at(seqh, i, -1); ck_assert(val != -1); sum += val; printf("%s: [%zu]=%d\n", __func__, i, val); } printf("%s: sum=%d\n", __func__, sum); ck_assert(sum == 1110); seqh = fy_cast(fy_sequence(800, 900, 2000, 1), fy_seq_handle_null); ck_assert(seqh); len = fy_len(seqh); ck_assert(len == 4); sum = 0; for (i = 0; i < len; i++) { v = fy_get_at(seqh, i, fy_invalid); ck_assert(v.v != fy_invalid_value); val = fy_cast(v, -1); ck_assert(val != -1); sum += val; printf("%s: [%zu]=%d\n", __func__, i, val); } printf("%s: sum=%d\n", __func__, sum); ck_assert(sum == 3701); map = fy_mapping("foo", 10, "bar", 20); ck_assert(fy_generic_is_mapping(map)); len = fy_len(map); ck_assert(len == 2); sum = 0; for (i = 0; i < len; i++) { val = fy_get_at(map, i, -1); ck_assert(val != -1); sum += val; printf("%s: [%zu]=%d\n", __func__, i, val); } printf("%s: sum=%d\n", __func__, sum); ck_assert(sum == 30); mp = fy_get_at(map, 0, fy_map_pair_invalid); ck_assert(fy_generic_is_valid(mp.key)); ck_assert(fy_generic_is_valid(mp.value)); key = fy_cast(mp.key, ""); val = fy_cast(mp.value, -1); printf("%s: [0] mp: key=%s value=%d\n", __func__, key, val); ck_assert(!strcmp(key, "foo")); ck_assert(val == 10); mpp = fy_get_at(map, 1, ((fy_generic_map_pair *)NULL)); ck_assert(mpp); ck_assert(fy_generic_is_valid(mpp->key)); ck_assert(fy_generic_is_valid(mpp->value)); key = fy_cast(mpp->key, ""); val = fy_cast(mpp->value, -1); printf("%s: [1] *mp: key=%s value=%d\n", __func__, key, val); ck_assert(!strcmp(key, "bar")); ck_assert(val == 20); printf("checking get_key_at\n"); key = fy_get_key_at(map, 0, ""); printf(">[%d] key=%s\n", 0, key); ck_assert(!strcmp(key, "foo")); key = fy_get_key_at(map, 1, ""); printf(">[%d] key=%s\n", 1, key); ck_assert(!strcmp(key, "bar")); /* using op */ seq = fy_sequence(10, 100, 1000); len = fy_len(seq); ck_assert(len == 3); sum = 0; for (i = 0; i < len; i++) { v = fy_generic_op(gb, FYGBOPF_GET_AT, seq, 0, NULL, i); val = fy_cast(v, -1); ck_assert(val != -1); sum += val; printf("%s: [%zu]=%d\n", __func__, i, val); } printf("%s: sum=%d\n", __func__, sum); ck_assert(sum == 1110); map = fy_mapping("foo", 10, "bar", 20); ck_assert(fy_generic_is_mapping(map)); len = fy_len(map); ck_assert(len == 2); sum = 0; for (i = 0; i < len; i++) { v = fy_generic_op(gb, FYGBOPF_GET_AT, map, 0, NULL, i); val = fy_cast(v, -1); ck_assert(val != -1); sum += val; printf("%s: [%zu]=%d\n", __func__, i, val); } printf("%s: sum=%d\n", __func__, sum); ck_assert(sum == 30); } END_TEST /* Test: comparisons */ START_TEST(generic_compare) { fy_generic v1, v2; fy_generic_sequence_handle seqh1, seqh2; fy_generic_mapping_handle maph1, maph2; int ret; /* nulls match always */ v1 = fy_value((void *)NULL); v2 = fy_value((void *)NULL); ck_assert(fy_compare(v1, v2) == 0); ck_assert(fy_compare(v1, (void *)NULL) == 0); ck_assert(fy_compare((void *)NULL, (void *)NULL) == 0); /* false == false */ v1 = fy_value((_Bool)false); v2 = fy_value((_Bool)false); ck_assert(fy_compare(v1, v2) == 0); ck_assert(fy_compare(v1, (_Bool)false) == 0); ck_assert(fy_compare((_Bool)false, (_Bool)false) == 0); /* true == true */ v1 = fy_value((_Bool)true); v2 = fy_value((_Bool)true); ck_assert(fy_compare(v1, v2) == 0); ck_assert(fy_compare(v1, (_Bool)true) == 0); ck_assert(fy_compare((_Bool)true, (_Bool)true) == 0); /* false < true */ v1 = fy_value((_Bool)false); v2 = fy_value((_Bool)true); ck_assert(fy_compare(v1, v2) < 0); ck_assert(fy_compare(v1, (_Bool)true) < 0); ck_assert(fy_compare((_Bool)false, (_Bool)true) < 0); /* true > false */ v1 = fy_value((_Bool)true); v2 = fy_value((_Bool)false); ck_assert(fy_compare(v1, v2) > 0); ck_assert(fy_compare(v1, (_Bool)false) > 0); ck_assert(fy_compare((_Bool)true, (_Bool)false) > 0); /* 0 == 0 */ v1 = fy_value(0); v2 = fy_value(0); ck_assert(fy_compare(v1, v2) == 0); ck_assert(fy_compare(v1, 0) == 0); ck_assert(fy_compare(0, 0) == 0); /* 100 > -10 */ v1 = fy_value(100); v2 = fy_value(-10); ck_assert(fy_compare(v1, v2) > 0); ck_assert(fy_compare(v1, -10) > 0); ck_assert(fy_compare(100, -10) > 0); /* 100 < 999 */ v1 = fy_value(100); v2 = fy_value(999); ck_assert(fy_compare(v1, v2) < 0); ck_assert(fy_compare(v1, 999) < 0); ck_assert(fy_compare(100, 990) < 0); /* unsigned LLONG_MAX + 1 > LLONG_MAX */ v1 = fy_value((unsigned long long)LLONG_MAX + 1); v2 = fy_value((long long)LLONG_MAX); ck_assert(fy_compare(v1, v2) > 0); ck_assert(fy_compare(v1, (long long)LONG_MAX) > 0); ck_assert(fy_compare((unsigned long long)LLONG_MAX + 1, (long long)LLONG_MAX) > 0); /* abc == abc */ v1 = fy_value("abc"); v2 = fy_value("abc"); ck_assert(fy_compare(v1, v2) == 0); ck_assert(fy_compare(v1, "abc") == 0); ck_assert(fy_compare("abc", "abc") == 0); /* abc < zxc */ v1 = fy_value("abc"); v2 = fy_value("zxc"); ck_assert(fy_compare(v1, v2) < 0); ck_assert(fy_compare(v1, "zxc") < 0); ck_assert(fy_compare("abc", "zxc") < 0); /* zxc > abc */ v1 = fy_value("zxc"); v2 = fy_value("abc"); ck_assert(fy_compare(v1, v2) > 0); ck_assert(fy_compare(v1, "abc") > 0); ck_assert(fy_compare("zxc", "abc") > 0); /* zxc000 > zxc */ v1 = fy_value("zxc000"); v2 = fy_value("zxc"); ck_assert(fy_compare(v1, v2) > 0); ck_assert(fy_compare(v1, "zxc") > 0); ck_assert(fy_compare("zxc000", "zxc") > 0); /* "" == "" */ v1 = fy_value(""); v2 = fy_value(""); ck_assert(fy_compare(v1, v2) == 0); ck_assert(fy_compare(v1, "") == 0); ck_assert(fy_compare("", "") == 0); /* "a" > "" */ v1 = fy_value("a"); v2 = fy_value(""); ck_assert(fy_compare(v1, v2) > 0); ck_assert(fy_compare(v1, "") > 0); ck_assert(fy_compare("a", "") > 0); /* sequence equality */ v1 = fy_local_sequence(1, 2, 3); v2 = fy_local_sequence(1, 2, 3); ret = fy_compare(v1, v2); ck_assert(ret == 0); seqh1 = fy_cast(v1, fy_seq_handle_null); seqh2 = fy_cast(v2, fy_seq_handle_null); ret = fy_compare(seqh1, v2); ck_assert(ret == 0); ret = fy_compare(v1, seqh2); ck_assert(ret == 0); ret = fy_compare(seqh1, seqh2); ck_assert(ret == 0); /* sequence > */ v1 = fy_local_sequence(1, 8, 3); v2 = fy_local_sequence(1, 2, 10); ret = fy_compare(v1, v2); ck_assert(ret > 0); ret = fy_compare(v1, fy_cast(fy_local_sequence(1, 2, 10), fy_seq_handle_null)); ck_assert(ret > 0); seqh1 = fy_cast(v1, fy_seq_handle_null); seqh2 = fy_cast(v2, fy_seq_handle_null); ret = fy_compare(seqh1, v2); ck_assert(ret > 0); ret = fy_compare(v1, seqh2); ck_assert(ret > 0); ret = fy_compare(seqh1, seqh2); ck_assert(ret > 0); /* mapping equality (easy) */ v1 = fy_local_mapping("foo", 10, "bar", 100); v2 = fy_local_mapping("foo", 10, "bar", 100); ret = fy_compare(v1, v2); ck_assert(ret == 0); maph1 = fy_cast(v1, fy_map_handle_null); maph2 = fy_cast(v2, fy_map_handle_null); ret = fy_compare(maph1, v2); ck_assert(ret == 0); ret = fy_compare(v1, maph2); ck_assert(ret == 0); ret = fy_compare(maph1, maph2); ck_assert(ret == 0); /* mapping equality (reorders) */ v1 = fy_local_mapping("foo", 10, "bar", 100); v2 = fy_local_mapping("bar", 100, "foo", 10); ret = fy_compare(v1, v2); ck_assert(ret == 0); maph1 = fy_cast(v1, fy_map_handle_null); maph2 = fy_cast(v2, fy_map_handle_null); ret = fy_compare(maph1, v2); ck_assert(ret == 0); ret = fy_compare(v1, maph2); ck_assert(ret == 0); ret = fy_compare(maph1, maph2); ck_assert(ret == 0); /* mapping inequality (reorders) */ v1 = fy_local_mapping("foo", 10, "bar", 101); v2 = fy_local_mapping("bar", 100, "foo", 10); ret = fy_compare(v1, v2); ck_assert(ret > 0); maph1 = fy_cast(v1, fy_map_handle_null); maph2 = fy_cast(v2, fy_map_handle_null); ret = fy_compare(maph1, v2); ck_assert(ret > 0); ret = fy_compare(v1, maph2); ck_assert(ret > 0); ret = fy_compare(maph1, maph2); ck_assert(ret > 0); /* NOTE: GCC miscompiles the complicated compare cast sequences */ /* file a bug report? */ } END_TEST /* Test: Basic builder operation */ START_TEST(gb_basics) { char buf[8192]; struct fy_generic_builder *gb; fy_generic v, seq; unsigned int i, len; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* verify that creating inplace still makes it inplace */ /* int (in place) */ v = fy_to_generic(gb, 100); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(fy_generic_is_in_place(v)); /* int (out of place) */ v = fy_to_generic(gb, FYGT_INT_INPLACE_MAX+1); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_get_type(v) == FYGT_INT); ck_assert(!fy_generic_is_in_place(v)); /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* same but using polymorphic fy_value() */ v = fy_value(gb, 100); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(fy_generic_is_in_place(v)); /* int (out of place) */ v = fy_value(gb, FYGT_INT_INPLACE_MAX+1); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_get_type(v) == FYGT_INT); ck_assert(!fy_generic_is_in_place(v)); /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); // long string v = fy_value(gb, "long string out of place"); ck_assert(fy_generic_is_string(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(!strcmp(fy_cast(v, ""), "long string out of place")); ck_assert(!strcmp(fy_castp(&v, ""), "long string out of place")); /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* a sequence is always out of place */ v = fy_gb_sequence(gb, 1, 10, 100); ck_assert(fy_generic_is_sequence(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_len(v) == 3); /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* same with mapping */ v = fy_gb_mapping(gb, "foo", 100, "bar", 200); ck_assert(fy_generic_is_mapping(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_len(v) == 2); /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* now verify that everyhing in a collection is contained in the builder */ seq = fy_gb_sequence(gb, 1, 10, 100, "Long string that should belong in the builder"); len = fy_len(seq); for (i = 0; i < len; i++) { v = fy_get(seq, i, fy_invalid); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_builder_contains(gb, v)); } } END_TEST /* Test: testing whether is_unsigned for outofplace ints is proper */ START_TEST(generic_is_unsigned) { char buf[8192]; struct fy_generic_builder *gb; const struct fy_generic_decorated_int *p; fy_generic v; static const unsigned long long tab[] = { FYGT_INT_INPLACE_MAX+1, (unsigned long long)LLONG_MAX, (unsigned long long)LLONG_MAX + 1, ULLONG_MAX }; size_t i; unsigned long long uv; bool is_unsigned; /* first try constant regular signed in place */ v = fy_value(100); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(fy_generic_is_in_place(v)); /* now this is out of place */ v = fy_value((long long)FYGT_INT_INPLACE_MAX+1); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->sv == (long long)FYGT_INT_INPLACE_MAX+1); ck_assert(!(p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* again for LLONG_MAX (should still be signed */ v = fy_value((long long)LLONG_MAX); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->sv == LLONG_MAX); ck_assert(!(p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* now try LLONG_MAX+1 but unsigned */ v = fy_value((unsigned long long)LLONG_MAX + 1); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->uv == (unsigned long long)LLONG_MAX + 1); ck_assert((p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* finally try ULLONG_MAX */ v = fy_value(ULLONG_MAX); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->uv == ULLONG_MAX); ck_assert((p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* again but using a variable */ for (i = 0; i < ARRAY_SIZE(tab); i++) { uv = tab[i]; is_unsigned = uv > (unsigned long long)LLONG_MAX; printf("uv=0x%llx is_unsigned=%s\n", uv, is_unsigned ? "true" : "false"); v = fy_value(uv); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->uv == uv); ck_assert(!!(p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND) == is_unsigned); } /* the same, but with a builder... */ gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* first try constant regular signed in place */ v = fy_value(gb, 100); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(fy_generic_is_in_place(v)); /* now this is out of place */ v = fy_value(gb, (long long)FYGT_INT_INPLACE_MAX+1); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_generic_builder_contains(gb, v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->sv == (long long)FYGT_INT_INPLACE_MAX+1); ck_assert(!(p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* again for LLONG_MAX (should still be signed */ v = fy_value(gb, (long long)LLONG_MAX); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_generic_builder_contains(gb, v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->sv == LLONG_MAX); ck_assert(!(p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* now try LLONG_MAX+1 but unsigned */ v = fy_value(gb, (unsigned long long)LLONG_MAX + 1); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_generic_builder_contains(gb, v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->uv == (unsigned long long)LLONG_MAX + 1); ck_assert((p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* finally try ULLONG_MAX */ v = fy_value(gb, ULLONG_MAX); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_generic_builder_contains(gb, v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->uv == ULLONG_MAX); ck_assert((p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND)); /* again but using a variable */ for (i = 0; i < ARRAY_SIZE(tab); i++) { uv = tab[i]; is_unsigned = uv > (unsigned long long)LLONG_MAX; printf("uv=0x%llx is_unsigned=%s\n", uv, is_unsigned ? "true" : "false"); v = fy_value(gb, uv); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_generic_builder_contains(gb, v)); /* manually resolve and check */ p = fy_generic_resolve_ptr(v); ck_assert(p->uv == uv); ck_assert(!!(p->flags & FYGDIF_UNSIGNED_RANGE_EXTEND) == is_unsigned); } } END_TEST /* Test: Basic dedup builder operation */ START_TEST(gb_dedup_basics) { char buf[8192]; struct fy_generic_builder *gb; fy_generic v, seq, map; fy_generic v_int_out_of_place, v_str_out_of_place, vseq1, vseq2, vmap1; unsigned int i, len; (void)vseq1; (void)vseq2; (void)vmap1; gb = fy_generic_builder_create_in_place( FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER | FYGBCF_DEDUP_ENABLED, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* verify that creating inplace still makes it inplace */ /* int (in place) */ v = fy_to_generic(gb, 100); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(fy_generic_is_in_place(v)); v = fy_to_generic(gb, FYGT_INT_INPLACE_MAX+1); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_get_type(v) == FYGT_INT); ck_assert(!fy_generic_is_in_place(v)); /* save this */ v_int_out_of_place = v; /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* same but using polymorphic fy_value() */ v = fy_value(gb, 100); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_int_type(v)); ck_assert(fy_generic_is_in_place(v)); /* int (out of place) */ v = fy_value(gb, (long long)FYGT_INT_INPLACE_MAX+1); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_get_type(v) == FYGT_INT); ck_assert(!fy_generic_is_in_place(v)); /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* verify that the value is the same one as previously */ if (v.v != v_int_out_of_place.v) { printf("======================================================================================\n"); printf("v.v=0x%016llx v_int_out_of_place.v=0x%016llx\n", (unsigned long long)v.v, (unsigned long long)v_int_out_of_place.v); printf("======================================================================================\n"); } ck_assert(v.v == v_int_out_of_place.v); // printf("0x%016lx 0x%016lx\n", v.v, v_int_out_of_place.v); ck_assert(v.v == v_int_out_of_place.v); /* long string */ v = fy_value(gb, "long string out of place"); ck_assert(fy_generic_is_string(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(!strcmp(fy_cast(v, ""), "long string out of place")); ck_assert(!strcmp(fy_castp(&v, ""), "long string out of place")); /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* save this */ v_str_out_of_place = v; /* a sequence is always out of place */ v = fy_gb_sequence(gb, 1, 10, 100); ck_assert(fy_generic_is_sequence(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_len(v) == 3); /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* save it for later */ vseq1 = v; /* same with mapping */ v = fy_gb_mapping(gb, "foo", 100, "bar", 200); ck_assert(fy_generic_is_mapping(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_len(v) == 2); /* save it for later */ vmap1 = v; /* verify that the builder contains this out of place v */ ck_assert(fy_generic_builder_contains(gb, v)); /* now verify that everyhing in a collection is contained in the builder */ seq = fy_gb_sequence(gb, 1, 10, 100, "Long string that should belong in the builder", "long string out of place"); len = fy_len(seq); for (i = 0; i < len; i++) { v = fy_get(seq, i, fy_invalid); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_builder_contains(gb, v)); } /* get the 4th item, it should be the one that was stored earlier */ v = fy_get(seq, 4, fy_invalid); ck_assert(fy_generic_is_valid(v)); ck_assert(v_str_out_of_place.v == v.v); /* verify that an internalized local sequence is deduped */ seq = fy_local_sequence(1, 10, 100); v = fy_gb_internalize(gb, seq); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_sequence(v)); /* it must be the exact same value */ ck_assert(vseq1.v == v.v); /* verify that an internalized local mapping is deduped */ map = fy_local_mapping("foo", 100, "bar", 200); v = fy_gb_internalize(gb, map); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_generic_is_mapping(v)); /* it must be the exact same value */ ck_assert(vmap1.v == v.v); /* verify that values are stored once */ v = fy_value(gb, FYGT_INT_INPLACE_MAX+1); ck_assert(fy_generic_is_valid(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_get_type(v) == FYGT_INT); ck_assert(v.v == v_int_out_of_place.v); v = fy_gb_to_generic(gb, "long string out of place"); ck_assert(fy_generic_is_valid(v)); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_get_type(v) == FYGT_STRING); ck_assert(v.v == v_str_out_of_place.v); } END_TEST static fy_generic calculate_seq_sum(enum fy_gb_cfg_flags flags, struct fy_generic_builder *parent_gb, fy_generic seq) { char buf[8192]; struct fy_generic_builder *gb; fy_generic v; size_t i, len; long long val, sum; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER | flags, parent_gb, buf, sizeof(buf)); ck_assert(gb != NULL); len = fy_len(seq); sum = 0; for (i = 0; i < len; i++) { val = fy_get(seq, i, (long long)-1); ck_assert(val != -1); sum += val; } /* we know that the value must be out of place */ printf("%s: fy_value before\n", __func__); v = fy_value(gb, sum); printf("%s: fy_value after\n", __func__); ck_assert(!fy_generic_is_in_place(v)); ck_assert(fy_generic_builder_contains(gb, v)); /* export the value back */ return fy_generic_builder_export(gb, v); } /* Test: Builder scoping */ START_TEST(gb_scoping) { char buf[8192]; struct fy_generic_builder *gb; fy_generic seq, v; long long expected, result; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); seq = fy_gb_sequence(gb, 100, 200, 300, FYGT_INT_INPLACE_MAX+1); ck_assert(fy_generic_is_sequence(seq)); expected = 100 + 200 + 300 + FYGT_INT_INPLACE_MAX+1; /* calculate */ v = calculate_seq_sum(0, gb, seq); /* verify the result is out of place */ ck_assert(!fy_generic_is_in_place(v)); /* verify that our builder contains it */ ck_assert(fy_generic_builder_contains(gb, v)); result = fy_cast(v, (long long)0); printf("expected=%lld result=%lld\n", expected, result); ck_assert(expected == result); } END_TEST /* Test: Builder dedup scoping */ START_TEST(gb_dedup_scoping) { char buf[8192]; struct fy_generic_builder *gb; fy_generic seq, v, vexp; long long expected, result; gb = fy_generic_builder_create_in_place( FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER | FYGBCF_DEDUP_ENABLED | FYGBCF_TRACE, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); printf("%s: decorated int size=%zu\n", __func__, sizeof(struct fy_generic_decorated_int)); printf("%s: sequence creating\n", __func__); seq = fy_gb_sequence(gb, 100, 200, 300, FYGT_INT_INPLACE_MAX+1); printf("%s: sequence created\n", __func__); ck_assert(fy_generic_is_sequence(seq)); expected = 100 + 200 + 300 + FYGT_INT_INPLACE_MAX+1; /* internalize the expected value */ printf("%s: internalizing\n", __func__); vexp = fy_gb_internalize(gb, fy_value(expected)); printf("%s: internalized\n", __func__); ck_assert(fy_generic_is_long_long(vexp)); fprintf(stderr, "vexp=0x%016llx\n", (unsigned long long)vexp.v); /* verify the result is out of place */ ck_assert(!fy_generic_is_in_place(vexp)); /* calculate (but with dedup enabled) */ printf("%s: sum calculating\n", __func__); v = calculate_seq_sum(FYGBCF_DEDUP_ENABLED | FYGBCF_TRACE, gb, seq); printf("%s: sum calculated\n", __func__); /* verify correct result */ printf("%s: long long casting\n", __func__); result = fy_cast(v, (long long)0); printf("%s: long long casted\n", __func__); printf("expected=%lld result=%lld\n", expected, result); ck_assert(expected == result); fprintf(stderr, "v=0x%016llx\n", (unsigned long long)v.v); /* verify the result is out of place */ ck_assert(!fy_generic_is_in_place(v)); /* verify that the result is exactly the same one that we internalized */ ck_assert(vexp.v == v.v); } END_TEST /* Test: Builder dedup scoping2 */ START_TEST(gb_dedup_scoping2) { char buf1[8192], buf2[8192]; struct fy_generic_builder *gb1, *gb2; fy_generic v1, v2; /* create two stacked dedup builders */ gb1 = fy_generic_builder_create_in_place( FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER | FYGBCF_DEDUP_ENABLED, NULL, buf1, sizeof(buf1)); ck_assert(gb1 != NULL); gb2 = fy_generic_builder_create_in_place( FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER | FYGBCF_DEDUP_ENABLED, gb1, buf2, sizeof(buf2)); ck_assert(gb2 != NULL); /* verify that the top one has dedup chain enabled */ ck_assert((gb2->flags & FYGBF_DEDUP_CHAIN) != 0); /* store a long string in the first builder */ v1 = fy_value(gb1, "This is a long string"); ck_assert(fy_generic_is_valid(v1)); ck_assert(fy_generic_builder_contains(gb1, v1)); /* store the same string in the second */ v2 = fy_value(gb2, "This is a long string"); ck_assert(fy_generic_is_valid(v2)); ck_assert(fy_generic_builder_contains(gb2, v2)); /* both of them should now contain it */ ck_assert(fy_generic_builder_contains(gb1, v2)); /* and it must be the same value */ ck_assert(v1.v == v2.v); /* do the same but with a collection */ v1 = fy_sequence(gb1, 100, 200, "This is a long string"); ck_assert(fy_generic_is_valid(v1)); ck_assert(fy_generic_is_sequence(v1)); ck_assert(fy_generic_builder_contains(gb1, v1)); v2 = fy_sequence(gb2, 100, 200, "This is a long string"); ck_assert(fy_generic_is_valid(v2)); ck_assert(fy_generic_is_sequence(v2)); ck_assert(fy_generic_builder_contains(gb1, v2)); ck_assert(fy_generic_builder_contains(gb1, v2)); /* and it must be the same value */ ck_assert(v1.v == v2.v); } END_TEST /* Test: polymorphics */ START_TEST(gb_polymorphics) { char buf[8192]; struct fy_generic_builder *gb; fy_generic seq, map; /* empty sequence */ seq = fy_sequence(); ck_assert(fy_generic_is_sequence(seq)); ck_assert(fy_len(seq) == 0); /* empty mapping */ map = fy_mapping(); ck_assert(fy_generic_is_mapping(map)); ck_assert(fy_len(map) == 0); seq = fy_sequence(10); ck_assert(fy_generic_is_sequence(seq)); seq = fy_sequence(10, true, "Hello there", fy_sequence(-1, -2)); ck_assert(fy_generic_is_sequence(seq)); gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* empty sequence */ seq = fy_sequence(gb); ck_assert(fy_generic_is_sequence(seq)); ck_assert(fy_len(seq) == 0); /* empty mapping */ map = fy_mapping(gb); ck_assert(fy_generic_is_mapping(map)); ck_assert(fy_len(map) == 0); seq = fy_sequence(1, 2, 3); ck_assert(fy_generic_is_sequence(seq)); /* verify that our builder does not contain it */ ck_assert(!fy_generic_builder_contains(gb, seq)); seq = fy_sequence(gb, 1, 2, 3); ck_assert(fy_generic_is_sequence(seq)); /* verify that our builder now contains it */ ck_assert(fy_generic_builder_contains(gb, seq)); } END_TEST /* Test: basic collection operations */ START_TEST(gb_basic_collection_ops) { char buf[16384]; struct fy_generic_builder *gb; fy_generic items[16]; fy_generic v; fy_generic seq, map; size_t len; int val; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* manual sequence creation */ items[0] = fy_value(1000); items[1] = fy_value("Hello"); items[2] = fy_value(false); v = fy_generic_op(gb, FYGBOPF_CREATE_SEQ, 3, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 1000); ck_assert(!strcmp(fy_get(v, 1, ""), "Hello")); ck_assert(fy_get(v, 2, true) == false); printf("> seq-create: "); fy_generic_emit_default(v); /* manual mapping creation */ items[0] = fy_value("foo"); items[1] = fy_value(100); items[2] = fy_value("bar"); items[3] = fy_value(200); v = fy_generic_op(gb, FYGBOPF_CREATE_MAP, 2, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 100); ck_assert(fy_get(v, "bar", -1) == 200); printf("> map-create: "); fy_generic_emit_default(v); /* testing insert (start with empty sequence) () -> (99) */ seq = fy_sequence(gb); items[0] = fy_value(99); v = fy_generic_op(gb, FYGBOPF_INSERT, seq, 1, items, 0); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 99); printf("> seq-insert-1: "); fy_generic_emit_default(v); /* testing insert (start with empty sequence) () -> (99, 999) */ seq = fy_sequence(gb); items[0] = fy_value(99); items[1] = fy_value(999); v = fy_generic_op(gb, FYGBOPF_INSERT, seq, 2, items, 0); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, 0, -1) == 99); ck_assert(fy_get(v, 1, -1) == 999); printf("> seq-insert-2: "); fy_generic_emit_default(v); /* testing insert at the beginning (1, 2) -> (100, 1, 2) */ seq = fy_sequence(gb, 1, 2); items[0] = fy_value(100); v = fy_generic_op(gb, FYGBOPF_INSERT, seq, 1, items, 0); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 100); ck_assert(fy_get(v, 1, -1) == 1); ck_assert(fy_get(v, 2, -1) == 2); printf("> seq-insert-begin: "); fy_generic_emit_default(v); /* testing insert at the end (1, 2) -> (1, 2, 100) */ seq = fy_sequence(gb, 1, 2); items[0] = fy_value(100); v = fy_generic_op(gb, FYGBOPF_INSERT, seq, 1, items, 2); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 100); printf("> seq-insert-end: "); fy_generic_emit_default(v); /* testing insert at the middle (1, 2) -> (1, 300, 2) */ seq = fy_sequence(gb, 1, 2); items[0] = fy_value(300); v = fy_generic_op(gb, FYGBOPF_INSERT, seq, 1, items, 1); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 300); ck_assert(fy_get(v, 2, -1) == 2); printf("> seq-insert-middle: "); fy_generic_emit_default(v); /* testing insert (start with empty map) () -> ("foo": 10) */ map = fy_mapping(gb); items[0] = fy_value("foo"); items[1] = fy_value(10); v = fy_generic_op(gb, FYGBOPF_INSERT, map, 1, items, 0); ck_assert(fy_generic_is_mapping(v)); len = fy_len(v); ck_assert(len == 1); val = fy_get(v, "foo", -1); ck_assert(val == 10); val = fy_get_at(v, 0, -1); ck_assert(val == 10); printf("> map-insert-1: "); fy_generic_emit_default(v); /* testing insert (start with empty mapping) () -> ("foo": 100, "baz": -100) */ map = fy_mapping(gb); items[0] = fy_value("foo"); items[1] = fy_value(100); items[2] = fy_value("baz"); items[3] = fy_value(-100); v = fy_generic_op(gb, FYGBOPF_INSERT, map, 2, items, 0); ck_assert(fy_generic_is_mapping(v)); len = fy_len(v); ck_assert(len == 2); val = fy_get(v, "foo", -1); ck_assert(val == 100); val = fy_get_at(v, 0, -1); ck_assert(val == 100); val = fy_get(v, "baz", -1); ck_assert(val == -100); val = fy_get_at(v, 1, -1); ck_assert(val == -100); printf("> map-insert-2: "); fy_generic_emit_default(v); /* testing insert at the beginning ("foo": 1, "bar": 2) -> ("baz": 100, "foo": 1, "bar": 2) */ map = fy_mapping(gb, "foo", 1, "bar", 2); items[0] = fy_value("baz"); items[1] = fy_value(100); v = fy_generic_op(gb, FYGBOPF_INSERT, map, 1, items, 0); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, "foo", -1) == 1); ck_assert(fy_get_at(v, 1, -1) == 1); ck_assert(fy_get(v, "bar", -1) == 2); ck_assert(fy_get_at(v, 2, -1) == 2); ck_assert(fy_get(v, "baz", -1) == 100); ck_assert(fy_get_at(v, 0, -1) == 100); printf("> map-insert-begin: "); fy_generic_emit_default(v); /* testing insert at the end ("foo": 1, "bar": 2 ) -> ("foo" 1, "bar":2, "baz": 99) */ map = fy_mapping(gb, "foo", 1, "bar", 2); items[0] = fy_value("baz"); items[1] = fy_value(99); v = fy_generic_op(gb, FYGBOPF_INSERT, map, 1, items, 2); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, "foo", -1) == 1); ck_assert(fy_get_at(v, 0, -1) == 1); ck_assert(fy_get(v, "bar", -1) == 2); ck_assert(fy_get_at(v, 1, -1) == 2); ck_assert(fy_get(v, "baz", -1) == 99); ck_assert(fy_get_at(v, 2, -1) == 99); printf("> map-insert-end: "); fy_generic_emit_default(v); /* testing insert at the middle ("foo": 1, "bar": 2) -> ("foo": 1, "baz": 300, "bar": 2) */ map = fy_mapping(gb, "foo", 1, "bar", 2); items[0] = fy_value("baz"); items[1] = fy_value(300); v = fy_generic_op(gb, FYGBOPF_INSERT, map, 1, items, 1); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, "foo", -1) == 1); ck_assert(fy_get_at(v, 0, -1) == 1); ck_assert(fy_get(v, "bar", -1) == 2); ck_assert(fy_get_at(v, 2, -1) == 2); ck_assert(fy_get(v, "baz", -1) == 300); ck_assert(fy_get_at(v, 1, -1) == 300); printf("> map-insert-middle: "); fy_generic_emit_default(v); /* testing replace (start with empty sequence) () -> (99) */ seq = fy_sequence(gb); items[0] = fy_value(99); v = fy_generic_op(gb, FYGBOPF_REPLACE, seq, 1, items, 0); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 99); printf("> seq-replace-1: "); fy_generic_emit_default(v); /* testing replace (single item, replace it) (100) -> (99) */ seq = fy_sequence(gb, 100); items[0] = fy_value(99); v = fy_generic_op(gb, FYGBOPF_REPLACE, seq, 1, items, 0); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 99); printf("> seq-replace-2: "); fy_generic_emit_default(v); /* testing replace (single item, replace in the beginning) (100, 200) -> (98, 200) */ seq = fy_sequence(gb, 100, 200); items[0] = fy_value(98); v = fy_generic_op(gb, FYGBOPF_REPLACE, seq, 1, items, 0); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, 0, -1) == 98); ck_assert(fy_get(v, 1, -1) == 200); printf("> seq-replace-3: "); fy_generic_emit_default(v); /* testing replace (single item, replace in the middle) (100, 1000, 200) -> (100, 1, 200) */ seq = fy_sequence(gb, 100, 1000, 200); items[0] = fy_value(1); v = fy_generic_op(gb, FYGBOPF_REPLACE, seq, 1, items, 1); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 100); ck_assert(fy_get(v, 1, -1) == 1); ck_assert(fy_get(v, 2, -1) == 200); printf("> seq-replace-3: "); fy_generic_emit_default(v); /* testing append (single item) (100, 1000, 200) -> (100, 1000, 200, 1) */ seq = fy_sequence(gb, 100, 1000, 200); items[0] = fy_value(1); v = fy_generic_op(gb, FYGBOPF_APPEND, seq, 1, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 100); ck_assert(fy_get(v, 1, -1) == 1000); ck_assert(fy_get(v, 2, -1) == 200); ck_assert(fy_get(v, 3, -1) == 1); printf("> seq-append-1: "); fy_generic_emit_default(v); } END_TEST /* Test: assoc/deassoc map operations */ START_TEST(gb_assoc_deassoc) { char buf[16384]; struct fy_generic_builder *gb; fy_generic items[16]; fy_generic map, v; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* associate, on empty map () -> ( "foo": 10 ) */ map = fy_mapping(); items[0] = fy_value("foo"); items[1] = fy_value(10); v = fy_generic_op(gb, FYGBOPF_ASSOC, map, 1, items); fy_generic_emit_default(v); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, "foo", -1) == 10); ck_assert(fy_get_at(v, 0, -1) == 10); printf("> assoc-on-empty: "); fy_generic_emit_default(v); /* associate, on non empty map ("bar": 100) -> ( "foo": 10 ) */ map = fy_mapping("bar", 100); items[0] = fy_value("foo"); items[1] = fy_value(10); v = fy_generic_op(gb, FYGBOPF_ASSOC, map, 1, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "bar", -1) == 100); ck_assert(fy_get_at(v, 0, -1) == 100); ck_assert(fy_get(v, "foo", -1) == 10); ck_assert(fy_get_at(v, 1, -1) == 10); printf("> assoc-on-non-empty: "); fy_generic_emit_default(v); /* associate, replace value ("bar": 100) -> ( "bar": 10 ) */ map = fy_mapping("bar", 100); items[0] = fy_value("bar"); items[1] = fy_value(10); v = fy_generic_op(gb, FYGBOPF_ASSOC, map, 1, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, "bar", -1) == 10); ck_assert(fy_get_at(v, 0, -1) == 10); printf("> assoc-replace: "); fy_generic_emit_default(v); /* associate, replace and append value ("foo": 1, "bar": 100) -> ( "foo": 10, "bar": 100, "baz": 1000 ) */ map = fy_mapping("foo", 1, "bar", 100); items[0] = fy_value("foo"); items[1] = fy_value(10); items[2] = fy_value("baz"); items[3] = fy_value(1000); v = fy_generic_op(gb, FYGBOPF_ASSOC, map, 2, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, "foo", -1) == 10); ck_assert(fy_get_at(v, 0, -1) == 10); ck_assert(fy_get(v, "bar", -1) == 100); ck_assert(fy_get_at(v, 1, -1) == 100); ck_assert(fy_get(v, "baz", -1) == 1000); ck_assert(fy_get_at(v, 2, -1) == 1000); printf("> assoc-replace-append: "); fy_generic_emit_default(v); /* simple replace */ map = fy_mapping("foo", 10, "bar", 20); v = fy_generic_op(gb, FYGBOPF_ASSOC, map, 1, (fy_generic []){ fy_value("bar"), fy_value(200)} ); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 10); ck_assert(fy_get(v, "bar", -1) == 200); /* disassociate, mapping goes empty ("bar": 100) -> () */ map = fy_mapping("bar", 100); items[0] = fy_value("bar"); v = fy_generic_op(gb, FYGBOPF_DISASSOC, map, 1, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 0); /* must be exactly empty */ ck_assert(v.v == fy_map_empty_value); printf("> disassoc-goes-empty: "); fy_generic_emit_default(v); /* disassociate, mapping is unchanged ("bar": 100) -> ("bar": 100) */ map = fy_mapping("bar", 100); items[0] = fy_value("foo"); v = fy_generic_op(gb, FYGBOPF_DISASSOC, map, 1, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, "bar", -1) == 100); ck_assert(fy_get_at(v, 0, -1) == 100); /* must be exactly the same */ /* will not be the same value, but it will be the same content */ ck_assert(!fy_generic_compare(map, v)); printf("> disassoc-nop: "); fy_generic_emit_default(v); } END_TEST /* Test: map utilities */ START_TEST(gb_map_utils) { char buf[16384]; struct fy_generic_builder *gb; fy_generic items[16]; fy_generic map, v, seq; const char *str; int ival; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* empty map, return keys, values, items as an empty sequence */ map = fy_mapping(); v = fy_generic_op(gb, FYGBOPF_KEYS, map); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> map-keys-on-empty: "); fy_generic_emit_default(v); v = fy_generic_op(gb, FYGBOPF_VALUES, map); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> map-values-on-empty: "); fy_generic_emit_default(v); v = fy_generic_op(gb, FYGBOPF_ITEMS, map); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> map-items-on-empty: "); fy_generic_emit_default(v); /* single pair map, return keys, values, items as a single item sequence */ map = fy_mapping("foo", 100); v = fy_generic_op(gb, FYGBOPF_KEYS, map); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); str = fy_get(v, 0, ""); ck_assert(!strcmp(str, "foo")); printf("> map-keys-on-1: "); fy_generic_emit_default(v); v = fy_generic_op(gb, FYGBOPF_VALUES, map); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ival = fy_get(v, 0, -1); ck_assert(ival == 100); printf("> map-values-on-1: "); fy_generic_emit_default(v); v = fy_generic_op(gb, FYGBOPF_ITEMS, map); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); seq = fy_get(v, 0, fy_invalid); ck_assert(fy_generic_is_sequence(seq)); str = fy_get(seq, 0, ""); ck_assert(!strcmp(str, "foo")); ival = fy_get(seq, 1, -1); ck_assert(ival == 100); printf("> map-values-on-1: "); fy_generic_emit_default(v); /* bigger map */ map = fy_mapping("foo", 100, "bar", 200, "baz", 300, "long string key", false); v = fy_generic_op(gb, FYGBOPF_KEYS, map); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); str = fy_get(v, 0, ""); ck_assert(!strcmp(str, "foo")); str = fy_get(v, 1, ""); ck_assert(!strcmp(str, "bar")); str = fy_get(v, 2, ""); ck_assert(!strcmp(str, "baz")); str = fy_get(v, 3, ""); ck_assert(!strcmp(str, "long string key")); printf("> map-key-on-4: "); fy_generic_emit_default(v); /* contains on an empty map */ map = fy_mapping(); items[0] = fy_value("foo"); v = fy_generic_op(gb, FYGBOPF_CONTAINS, map, 1, items); ck_assert(v.v == fy_false_value); /* contains on an map which does not contain the key */ map = fy_mapping("bar", 10); items[0] = fy_value("foo"); v = fy_generic_op(gb, FYGBOPF_CONTAINS, map, 1, items); ck_assert(v.v == fy_false_value); /* contains on an map which contains the key */ map = fy_mapping("foo", 100); items[0] = fy_value("foo"); v = fy_generic_op(gb, FYGBOPF_CONTAINS, map, 1, items); ck_assert(v.v == fy_true_value); /* contains on an map which does not contain any of the keys */ map = fy_mapping("foo", 100, "bar", 1000, "baz", 5); items[0] = fy_value("nope"); items[1] = fy_value("neither"); v = fy_generic_op(gb, FYGBOPF_CONTAINS, map, 2, items); ck_assert(v.v == fy_false_value); /* contains on an map which contains the second key */ map = fy_mapping("foo", 100, "bar", 1000, "baz", 5); items[0] = fy_value("nope"); items[1] = fy_value("bar"); v = fy_generic_op(gb, FYGBOPF_CONTAINS, map, 2, items); ck_assert(v.v == fy_true_value); /* merge, on empty map () -> merge nothing */ map = fy_map_empty; // fy_mapping(); v = fy_generic_op(gb, FYGBOPF_MERGE, map, 0, NULL); ck_assert(fy_generic_is_mapping(v)); ck_assert(v.v == fy_map_empty_value); printf("> merge-on-empty-0: "); fy_generic_emit_default(v); /* merge, on empty map () -> merge nothing */ map = fy_mapping(); items[0] = fy_mapping(); v = fy_generic_op(gb, FYGBOPF_MERGE, map, 1, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(v.v == fy_map_empty_value); printf("> merge-on-empty-empty-0: "); fy_generic_emit_default(v); /* merge, on simple map ("foo": 10) -> ("foo": 10) */ map = fy_mapping("foo", 10); v = fy_generic_op(gb, FYGBOPF_MERGE, map, 0, NULL); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, "foo", -1) == 10); ck_assert(fy_get_at(v, 0, -1) == 10); printf("> merge-on-unchanged: "); fy_generic_emit_default(v); /* merge, on map with duplicate * ("foo": 10, "bar": 100, "foo": 200, "baz": 1) -> ("foo": 200, "bar": 100, "baz": 1) */ map = fy_mapping("foo", 10, "bar", 100, "foo", 200, "baz", 1); v = fy_generic_op(gb, FYGBOPF_MERGE, map, 0, NULL); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, "foo", -1) == 200); ck_assert(fy_get_at(v, 0, -1) == 200); ck_assert(fy_get(v, "bar", -1) == 100); ck_assert(fy_get_at(v, 1, -1) == 100); ck_assert(fy_get(v, "baz", -1) == 1); ck_assert(fy_get_at(v, 2, -1) == 1); printf("> merge-dup-filter: "); fy_generic_emit_default(v); /* merge, with duplicates * ("foo": 10, "bar": 100) + ("bar":99) + ("foo":9) -> ("foo": 9, "bar": 99) */ map = fy_mapping("foo", 10, "bar", 100); items[0] = fy_mapping("bar", 99); items[1] = fy_mapping("foo", 9); v = fy_generic_op(gb, FYGBOPF_MERGE, map, 2, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 9); ck_assert(fy_get_at(v, 0, -1) == 9); ck_assert(fy_get(v, "bar", -1) == 99); ck_assert(fy_get_at(v, 1, -1) == 99); printf("> merge-with-dups: "); fy_generic_emit_default(v); /* sort a bunch of mappings */ map = fy_mapping("bar", 10, "foo", 100); items[0] = fy_mapping("z", 1000); items[1] = fy_mapping(); items[2] = fy_mapping("a", 0); v = fy_generic_op(gb, FYGBOPF_SORT, map, 3, items); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, "a", -1) == 0); ck_assert(fy_get_at(v, 0, -1) == 0); ck_assert(fy_get(v, "bar", -1) == 10); ck_assert(fy_get_at(v, 1, -1) == 10); ck_assert(fy_get(v, "foo", -1) == 100); ck_assert(fy_get_at(v, 2, -1) == 100); ck_assert(fy_get(v, "z", -1) == 1000); ck_assert(fy_get_at(v, 3, -1) == 1000); printf("> map-sort-complicated: "); fy_generic_emit_default(v); } END_TEST /* Test: seq utilities */ START_TEST(gb_seq_utils) { char buf[16384]; struct fy_generic_builder *gb; fy_generic items[16]; fy_generic seq, v; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* empty sequence, concat with nothing -> empty sequence */ seq = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_CONCAT, seq, 0, NULL); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> seq-empty-concat-0: "); fy_generic_emit_default(v); /* empty sequence, concat with empty sequences -> empty sequence */ seq = fy_sequence(); items[0] = fy_sequence(); items[1] = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_CONCAT, seq, 2, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> seq-empty-concat-empty: "); fy_generic_emit_default(v); /* empty sequence, concat with non empty sequences -> non empty sequences */ seq = fy_sequence(); items[0] = fy_sequence(1, 2); items[1] = fy_sequence(3); v = fy_generic_op(gb, FYGBOPF_CONCAT, seq, 2, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 3); printf("> seq-empty-concat-non-empty: "); fy_generic_emit_default(v); /* a non empty sequence, concat with empty sequences -> same sequence */ seq = fy_sequence(1, 2, 3); items[0] = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_CONCAT, seq, 1, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(!fy_generic_compare(seq, v)); printf("> seq-concat-empty: "); fy_generic_emit_default(v); /* a non empty sequence, concat with two non empty sequences */ seq = fy_sequence(1, 2, 3); items[0] = fy_sequence(4, 5); items[1] = fy_sequence(6, 7); v = fy_generic_op(gb, FYGBOPF_CONCAT, seq, 2, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 7); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 3); ck_assert(fy_get(v, 3, -1) == 4); ck_assert(fy_get(v, 4, -1) == 5); ck_assert(fy_get(v, 5, -1) == 6); ck_assert(fy_get(v, 6, -1) == 7); printf("> seq-concat-regular: "); fy_generic_emit_default(v); /* a non empty sequence, concat with two non empty sequences and one empty in the middle*/ seq = fy_sequence(1, 2, 3); items[0] = fy_sequence(4, 5); items[1] = fy_sequence(); items[2] = fy_sequence(6, 7); v = fy_generic_op(gb, FYGBOPF_CONCAT, seq, 3, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 7); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 3); ck_assert(fy_get(v, 3, -1) == 4); ck_assert(fy_get(v, 4, -1) == 5); ck_assert(fy_get(v, 5, -1) == 6); ck_assert(fy_get(v, 6, -1) == 7); printf("> seq-concat-regular-empty: "); fy_generic_emit_default(v); /* empty sequence, reverse with nothing -> empty sequence */ seq = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_REVERSE, seq, 0, NULL); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> seq-empty-reverse-0: "); fy_generic_emit_default(v); /* empty sequence, reverse with empty sequences -> empty sequence */ seq = fy_sequence(); items[0] = fy_sequence(); items[1] = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_REVERSE, seq, 2, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> seq-empty-reverse-empty: "); fy_generic_emit_default(v); /* simple sequence reverse */ seq = fy_sequence(1, 2, 3); v = fy_generic_op(gb, FYGBOPF_REVERSE, seq, 0, NULL); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 3); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 1); printf("> seq-simple-reverse: "); fy_generic_emit_default(v); /* empty sequence, reverse with non empty sequences -> non empty sequences */ seq = fy_sequence(); items[0] = fy_sequence(1, 2); items[1] = fy_sequence(3); v = fy_generic_op(gb, FYGBOPF_REVERSE, seq, 2, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 3); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 1); printf("> seq-empty-reverse-non-empty: "); fy_generic_emit_default(v); /* a non empty sequence, reverse with empty sequences */ seq = fy_sequence(1, 2, 3); items[0] = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_REVERSE, seq, 1, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 3); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 1); printf("> seq-reverse-empty: "); fy_generic_emit_default(v); /* a non empty sequence, reverse with two non empty sequences */ seq = fy_sequence(1, 2, 3); items[0] = fy_sequence(4, 5); items[1] = fy_sequence(6, 7); v = fy_generic_op(gb, FYGBOPF_REVERSE, seq, 2, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 7); ck_assert(fy_get(v, 0, -1) == 7); ck_assert(fy_get(v, 1, -1) == 6); ck_assert(fy_get(v, 2, -1) == 5); ck_assert(fy_get(v, 3, -1) == 4); ck_assert(fy_get(v, 4, -1) == 3); ck_assert(fy_get(v, 5, -1) == 2); ck_assert(fy_get(v, 6, -1) == 1); printf("> seq-reverse-regular: "); fy_generic_emit_default(v); /* a non empty sequence, reverse with two non empty sequences and one empty in the middle*/ seq = fy_sequence(1, 2, 3); items[0] = fy_sequence(4, 5); items[1] = fy_sequence(); items[2] = fy_sequence(6, 7); v = fy_generic_op(gb, FYGBOPF_REVERSE, seq, 3, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 7); ck_assert(fy_get(v, 0, -1) == 7); ck_assert(fy_get(v, 1, -1) == 6); ck_assert(fy_get(v, 2, -1) == 5); ck_assert(fy_get(v, 3, -1) == 4); ck_assert(fy_get(v, 4, -1) == 3); ck_assert(fy_get(v, 5, -1) == 2); ck_assert(fy_get(v, 6, -1) == 1); printf("> seq-reverse-regular-empty: "); fy_generic_emit_default(v); /* a simple sequence to be uniq'ed */ seq = fy_sequence(1, 2, 1); v = fy_generic_op(gb, FYGBOPF_UNIQUE, seq, 0, NULL); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 2); printf("> seq-unique-simple: "); fy_generic_emit_default(v); /* a convoluted sequence to be uniq'ed */ seq = fy_sequence(1, 2, 1); items[0] = fy_sequence(1, 1); items[1] = fy_sequence(); items[2] = fy_sequence(2, 3); v = fy_generic_op(gb, FYGBOPF_UNIQUE, seq, 3, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 3); printf("> seq-unique-complicated: "); fy_generic_emit_default(v); /* sort a simple sequence */ seq = fy_sequence(3, 2, 1); v = fy_generic_op(gb, FYGBOPF_SORT, seq, 0, NULL); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 3); printf("> seq-sort-simple: "); fy_generic_emit_default(v); /* sort a non empty sequence, reverse with two non empty sequences and one empty in the middle */ seq = fy_sequence(7, 6, 5); items[0] = fy_sequence(4, 3); items[1] = fy_sequence(); items[2] = fy_sequence(2, 1); v = fy_generic_op(gb, FYGBOPF_SORT, seq, 3, items); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 7); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 1, -1) == 2); ck_assert(fy_get(v, 2, -1) == 3); ck_assert(fy_get(v, 3, -1) == 4); ck_assert(fy_get(v, 4, -1) == 5); ck_assert(fy_get(v, 5, -1) == 6); ck_assert(fy_get(v, 6, -1) == 7); printf("> seq-sort-complicated: "); fy_generic_emit_default(v); } END_TEST static bool test_filter_over_100(struct fy_generic_builder *gb, fy_generic v) { return fy_cast(v, INT_MIN) > 100; } /* Test: filter */ START_TEST(gb_filter) { char buf[65536]; struct fy_generic_builder *gb; fy_generic items[16]; fy_generic seq, map, v; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* filter an empty sequence */ seq = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_FILTER, seq, 0, NULL, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> filter-seq-empty: "); fy_generic_emit_default(v); /* filter a single sequence for items > 100 (none exist) */ seq = fy_sequence(7, 6, 5, 8, -100); v = fy_generic_op(gb, FYGBOPF_FILTER, seq, 0, NULL, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> filter-seq-over-100 (empty): "); fy_generic_emit_default(v); /* filter a single sequence for items > 100 (all exist) */ seq = fy_sequence(107, 106, 105, 108, 1000); v = fy_generic_op(gb, FYGBOPF_FILTER, seq, 0, NULL, test_filter_over_100); fy_generic_emit_default(v); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 5); ck_assert(fy_get(v, 0, -1) == 107); ck_assert(fy_get(v, 1, -1) == 106); ck_assert(fy_get(v, 2, -1) == 105); ck_assert(fy_get(v, 3, -1) == 108); ck_assert(fy_get(v, 4, -1) == 1000); printf("> filter-seq-over-100 (all): "); fy_generic_emit_default(v); /* filter a single sequence for items > 100 (one exists) */ seq = fy_sequence(7, 6, 5, 101, 8, -100); v = fy_generic_op(gb, FYGBOPF_FILTER, seq, 0, NULL, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 101); printf("> filter-seq-over-100 (101): "); fy_generic_emit_default(v); /* filter a complex sequence for items > 100 (two exist) */ seq = fy_sequence(7, 6, 5, 101, 8, -100); items[0] = fy_sequence(1, 102); items[1] = fy_sequence(1, 3); v = fy_generic_op(gb, FYGBOPF_FILTER, seq, 2, items, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, 0, -1) == 101); ck_assert(fy_get(v, 1, -1) == 102); printf("> filter-seq-over-100 (101/102): "); fy_generic_emit_default(v); /* filter an empty map */ map = fy_mapping(); v = fy_generic_op(gb, FYGBOPF_FILTER, map, 0, NULL, test_filter_over_100); ck_assert(fy_generic_is_mapping(v)); ck_assert(v.v == fy_map_empty_value); printf("> filter-map-empty: "); fy_generic_emit_default(v); /* filter a single mapping for items > 100 (none exist) */ map = fy_mapping("foo", 7, "bar", 6, "frooz", 5, "whoa", 8, "last", -100); v = fy_generic_op(gb, FYGBOPF_FILTER, map, 0, NULL, test_filter_over_100); ck_assert(fy_generic_is_mapping(v)); ck_assert(v.v == fy_map_empty_value); printf("> filter-map-over-100 (empty): "); fy_generic_emit_default(v); /* filter a single mapping for items > 100 (all exist) */ map = fy_mapping("foo", 107, "bar", 106, "baz", 1000); v = fy_generic_op(gb, FYGBOPF_FILTER, map, 0, NULL, test_filter_over_100); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, "foo", -1) == 107); ck_assert(fy_get_at(v, 0, -1) == 107); ck_assert(fy_get(v, "bar", -1) == 106); ck_assert(fy_get_at(v, 1, -1) == 106); ck_assert(fy_get(v, "baz", -1) == 1000); ck_assert(fy_get_at(v, 2, -1) == 1000); printf("> filter-map-over-100 (all): "); fy_generic_emit_default(v); /* filter a single mapping for items > 100 (one exists) */ map = fy_mapping("a", 7, "baz", 6, "whoa", 5, "foo", 101, "z", 8, "zz", -100); v = fy_generic_op(gb, FYGBOPF_FILTER, map, 0, NULL, test_filter_over_100); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, "foo", -1) == 101); ck_assert(fy_get_at(v, 0, -1) == 101); printf("> filter-map-over-100 (101): "); fy_generic_emit_default(v); /* filter a complex mapping for items > 100 (two exist) */ map = fy_mapping("a", 7, "b", 6, "c", 5, "foo", 101, "d", 8, "e", -100); items[0] = fy_mapping("aa", 1, "bar", 102); items[1] = fy_mapping("aaa", 1, "bbb", 3); v = fy_generic_op(gb, FYGBOPF_FILTER, map, 2, items, test_filter_over_100); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 101); ck_assert(fy_get_at(v, 0, -1) == 101); ck_assert(fy_get(v, "bar", -1) == 102); ck_assert(fy_get_at(v, 1, -1) == 102); printf("> filter-map-over-100 (101/102): "); fy_generic_emit_default(v); } END_TEST static fy_generic test_map_double(struct fy_generic_builder *gb, fy_generic v) { return fy_value(gb, fy_cast(v, 0) * 2); } static fy_generic test_map_double2(struct fy_generic_builder *gb, fy_generic v) { int i = fy_cast(v, 0); int j; j = i * 2; printf("i=%d -> %d\n", i, j); return fy_value(j); } static fy_generic test_map_filter_double_if_over_10(struct fy_generic_builder *gb, fy_generic v) { int iv; iv = fy_cast(v, 0); return iv >= 10 ? fy_value(gb, iv * 2) : fy_invalid; } static fy_generic test_map_prefix_str(struct fy_generic_builder *gb, fy_generic v) { /* verify that export to the builder works */ return fy_value(gb, fy_sprintfa("long-prefix-%s", fy_cast(v, ""))); } /* Test: map */ START_TEST(gb_map) { char buf[65536]; struct fy_generic_builder *gb; fy_generic seq, map, v; const char *str; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* map over an empty sequence */ seq = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_MAP, seq, 0, NULL, test_map_double); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> map-seq-empty: "); fy_generic_emit_default(v); /* map a single sequence of numbers */ seq = fy_sequence(7, 6, 5, 8); v = fy_generic_op(gb, FYGBOPF_MAP, seq, 0, NULL, test_map_double2); printf("> map-seq-double (1): "); fy_generic_emit_default(v); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 14); ck_assert(fy_get(v, 1, -1) == 12); ck_assert(fy_get(v, 2, -1) == 10); ck_assert(fy_get(v, 3, -1) == 16); printf("> map-seq-double (1): "); fy_generic_emit_default(v); /* map to prefix a sequence of strings */ seq = fy_sequence("foo", "bar", "baz"); v = fy_generic_op(gb, FYGBOPF_MAP, seq, 0, NULL, test_map_prefix_str); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); str = fy_get(v, 0, ""); ck_assert(!strcmp(str, "long-prefix-foo")); str = fy_get(v, 1, ""); ck_assert(!strcmp(str, "long-prefix-bar")); str = fy_get(v, 2, ""); ck_assert(!strcmp(str, "long-prefix-baz")); printf("> map-seq-str-prefix (3): "); fy_generic_emit_default(v); /* map a single map of numbers */ map = fy_mapping("foo", 7, "bar", 6); v = fy_generic_op(gb, FYGBOPF_MAP, map, 0, NULL, test_map_double); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 14); ck_assert(fy_get_at(v, 0, -1) == 14); ck_assert(fy_get(v, "bar", -1) == 12); ck_assert(fy_get_at(v, 1, -1) == 12); printf("> map-map-double (1): "); fy_generic_emit_default(v); } END_TEST static fy_generic fy_generic_test_sum_reducer(struct fy_generic_builder *gb, fy_generic accv, fy_generic v) { return fy_value(gb, fy_cast(accv, 0) + fy_cast(v, 0)); } static fy_generic fy_generic_test_concat_str_reducer(struct fy_generic_builder *gb, fy_generic accv, fy_generic v) { return fy_value(gb, fy_sprintfa("%s%s", fy_cast(accv, ""), fy_cast(v, ""))); } static fy_generic fy_generic_test_map_reducer(struct fy_generic_builder *gb, fy_generic accv, fy_generic v) { const fy_generic_map_pair *pairs; fy_generic kv[2]; size_t i, len; int new_val, old_val; if (!fy_generic_is_mapping(accv)) return fy_invalid; /* nothing? */ pairs = fy_generic_mapping_get_pairs(v, &len); if (!len) return accv; /* populate with new values */ for (i = 0; i < len; i++) { new_val = fy_cast(pairs[i].value, 0); old_val = fy_get(accv, pairs[i].key, 0); kv[0] = pairs[i].key; kv[1] = fy_value(gb, old_val + new_val); accv = fy_generic_op(gb, FYGBOPF_ASSOC, accv, 1, kv); } return accv; } /* Test: reduce */ START_TEST(gb_reduce) { char buf[65536]; struct fy_generic_builder *gb; fy_generic seq, map, v; int sum; const char *str; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* sum an empty sequence */ seq = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_REDUCE, seq, 0, NULL, fy_generic_test_sum_reducer, fy_value(gb, 0)); sum = fy_cast(v, INT_MIN); ck_assert(sum == 0); printf("> reduce-seq-sum (empty): "); fy_generic_emit_default(v); /* sum a sequence of numbers */ seq = fy_sequence(7, 6, 5, 8); v = fy_generic_op(gb, FYGBOPF_REDUCE, seq, 0, NULL, fy_generic_test_sum_reducer, fy_value(gb, 0)); fy_generic_emit_default(v); sum = fy_cast(v, INT_MIN); ck_assert(sum == 26); printf("> reduce-seq-sum (1): "); fy_generic_emit_default(v); /* concat a number of strings */ seq = fy_sequence("0123", "45", "6", "789"); v = fy_generic_op(gb, FYGBOPF_REDUCE, seq, 0, NULL, fy_generic_test_concat_str_reducer, fy_value(gb, "")); str = fy_cast(v, ""); ck_assert(!strcmp(str, "0123456789")); printf("> reduce-seq-concat (1): "); fy_generic_emit_default(v); /* sum the values of a mapping */ map = fy_mapping("a", 7, "b", 6, "c", 5, "d", 8); v = fy_generic_op(gb, FYGBOPF_REDUCE, map, 0, NULL, fy_generic_test_sum_reducer, fy_value(gb, 0)); sum = fy_cast(v, 0); ck_assert(sum == 26); printf("> reduce-map-sum (1): "); fy_generic_emit_default(v); /* sum the values of a mapping */ seq = fy_sequence( fy_mapping("foo", 10, "bar", 100), fy_mapping("baz", 1), fy_mapping("foo", 1, "baz", 3)); v = fy_generic_op(gb, FYGBOPF_REDUCE, seq, 0, NULL, fy_generic_test_map_reducer, fy_mapping()); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, "foo", -1) == 11); ck_assert(fy_get_at(v, 0, -1) == 11); ck_assert(fy_get(v, "bar", -1) == 100); ck_assert(fy_get_at(v, 1, -1) == 100); ck_assert(fy_get(v, "baz", -1) == 4); ck_assert(fy_get_at(v, 2, -1) == 4); printf("> reduce-complex (1): "); fy_generic_emit_default(v); } END_TEST /* Test: parallel filter */ START_TEST(gb_pfilter) { struct fy_generic_builder_cfg cfg; struct fy_generic_builder *gb; fy_generic seq, map, v; size_t i, vlen, loop, num_items; int iv, ivbegin, ivmid, ivend; /* 10000 items to force parallelization to kick in */ num_items = 10000; /* allocate a 10000 element buffer */ fy_generic *items = malloc(sizeof(*items) * num_items); ck_assert(items != NULL); /* Run tests twice: once without dedup, once with dedup */ for (loop = 0; loop < 2; loop++) { memset(&cfg, 0, sizeof(cfg)); cfg.flags = FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER; if (loop == 1) cfg.flags |= FYGBCF_DEDUP_ENABLED; gb = fy_generic_builder_create(&cfg); ck_assert(gb != NULL); printf("\n=== pfilter tests %s dedup ===\n", loop == 0 ? "WITHOUT" : "WITH"); /* parallel filter an empty sequence */ seq = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_FILTER | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> pfilter-empty: "); fy_generic_emit_default(v); /* parallel filter a large sequence (forces parallel execution) */ for (i = 0; i < num_items; i++) items[i] = fy_value(gb, (int)i); seq = fy_gb_sequence_create(gb, num_items, items); v = fy_generic_op(gb, FYGBOPF_FILTER | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); vlen = fy_len(v); ck_assert(vlen == num_items - 101); /* 101.. */ /* Check that all values are > 100 */ for (i = 0; i < vlen; i++) { iv = fy_get(v, i, -1); ck_assert(iv > 100); ck_assert(iv == (101 + (int)i)); } ivbegin = fy_get(v, 0, -1); ivmid = fy_get(v, vlen / 2, -1); ivend = fy_get(v, vlen - 1, -1); printf("> pfilter-large-seq-over-100 (%zu items): len=%zu, first=%d, mid=%d, last=%d\n", num_items, vlen, ivbegin, ivmid, ivend); /* parallel filter where nothing passes */ for (i = 0; i < num_items; i++) items[i] = fy_value(gb, ((int)i % 100)); seq = fy_gb_sequence_create(gb, num_items, items); v = fy_generic_op(gb, FYGBOPF_FILTER | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> pfilter-none-pass: "); fy_generic_emit_default(v); /* parallel filter on a mapping */ map = fy_mapping("a", 50, "b", 101, "c", 200, "d", 75); v = fy_generic_op(gb, FYGBOPF_FILTER | FYGBOPF_PARALLEL, map, 0, NULL, NULL, test_filter_over_100); ck_assert(fy_generic_is_mapping(v)); size_t maplen = fy_len(v); ck_assert(maplen == 2); /* Only "b" and "c" pass */ int vb = fy_get(v, "b", -1); int vc = fy_get(v, "c", -1); ck_assert(vb == 101); ck_assert(vc == 200); printf("> pfilter-map-over-100: "); fy_generic_emit_default(v); fy_generic_builder_destroy(gb); } free(items); } END_TEST /* Test: parallel map */ START_TEST(gb_pmap) { struct fy_generic_builder_cfg cfg; struct fy_generic_builder *gb; fy_generic seq, map, v; size_t i, vlen, loop, num_items; int iv, ivbegin, ivmid, ivend; /* 10000 items to force parallelization to kick in */ num_items = 10000; /* allocate a 10000 element buffer */ fy_generic *items = malloc(sizeof(*items) * num_items); ck_assert(items != NULL); /* Run tests twice: once without dedup, once with dedup */ for (loop = 0; loop < 2; loop++) { memset(&cfg, 0, sizeof(cfg)); cfg.flags = FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER; if (loop == 1) cfg.flags |= FYGBCF_DEDUP_ENABLED; gb = fy_generic_builder_create(&cfg); ck_assert(gb != NULL); printf("\n=== pmap tests %s dedup ===\n", loop == 0 ? "WITHOUT" : "WITH"); /* parallel map an empty sequence */ seq = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_MAP | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, test_map_double); ck_assert(fy_generic_is_sequence(v)); ck_assert(v.v == fy_seq_empty_value); printf("> pmap-empty: "); fy_generic_emit_default(v); /* parallel map a large sequence (forces parallel execution) */ for (i = 0; i < num_items; i++) items[i] = fy_value(gb, (int)i); seq = fy_gb_sequence_create(gb, num_items, items); v = fy_generic_op(gb, FYGBOPF_MAP | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, test_map_double); ck_assert(fy_generic_is_sequence(v)); vlen = fy_len(v); ck_assert(vlen == num_items); /* Check that all values are double */ for (i = 0; i < vlen; i++) { iv = fy_get(v, i, -1); ck_assert(iv == (int)(i * 2)); } ivbegin = fy_get(v, 0, -1); ivmid = fy_get(v, vlen / 2, -1); ivend = fy_get(v, vlen - 1, -1); printf("> pmap-large-seq-double (%zu items): len=%zu, first=%d, mid=%d, last=%d\n", num_items, vlen, ivbegin, ivmid, ivend); /* parallel map on a mapping */ map = fy_mapping("a", 5, "b", 10, "c", 15, "d", 20); v = fy_generic_op(gb, FYGBOPF_MAP | FYGBOPF_PARALLEL, map, 0, NULL, NULL, test_map_double); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_get(v, "a", -1) == 10); ck_assert(fy_get(v, "b", -1) == 20); ck_assert(fy_get(v, "c", -1) == 30); ck_assert(fy_get(v, "d", -1) == 40); printf("> pmap-map-double: "); fy_generic_emit_default(v); fy_generic_builder_destroy(gb); } free(items); } END_TEST /* Test: parallel reduce */ START_TEST(gb_preduce) { struct fy_generic_builder_cfg cfg; struct fy_generic_builder *gb; fy_generic seq, map, v; int sum, exp_sum; size_t i, num_items; fy_generic *items; /* 10000 items to force parallelization to kick in */ num_items = 10000; /* allocate a 10000 element buffer */ items = malloc(sizeof(*items) * num_items); ck_assert(items != NULL); /* Run tests twice: without dedup, then with dedup */ for (int loop = 0; loop < 2; loop++) { memset(&cfg, 0, sizeof(cfg)); cfg.flags = FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER; if (loop == 1) cfg.flags |= FYGBCF_DEDUP_ENABLED; gb = fy_generic_builder_create(&cfg); ck_assert(gb != NULL); printf("\n=== preduce tests %s dedup ===\n", loop == 0 ? "WITHOUT" : "WITH"); /* parallel reduce an empty sequence */ seq = fy_sequence(); v = fy_generic_op(gb, FYGBOPF_REDUCE | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, fy_generic_test_sum_reducer, fy_value(gb, 0)); sum = fy_cast(v, INT_MIN); // ck_assert(sum == 0); printf("> preduce-seq-sum (empty): %d", sum); fy_generic_emit_default(v); /* parallel reduce a large sequence (forces parallel execution) */ for (i = 0; i < num_items; i++) items[i] = fy_value(gb, i); seq = fy_gb_sequence_create(gb, num_items, items); v = fy_generic_op(gb, FYGBOPF_REDUCE | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, fy_generic_test_sum_reducer, fy_value(gb, 0)); sum = fy_cast(v, INT_MIN); /* Sum of 0..9999 = n*(n-1)/2 = 10000*9999/2 = 49995000 */ exp_sum = num_items * (num_items - 1) / 2; ck_assert(sum == exp_sum); printf("> preduce-large-seq-sum (%zu items): %d\n", num_items, sum); /* parallel reduce a small sequence (uses serial) */ seq = fy_sequence(7, 6, 5, 8); v = fy_generic_op(gb, FYGBOPF_REDUCE | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, fy_generic_test_sum_reducer, fy_value(gb, 0)); sum = fy_cast(v, INT_MIN); ck_assert(sum == 26); printf("> preduce-small-seq-sum: "); fy_generic_emit_default(v); v = fy_generic_op(gb, FYGBOPF_REDUCE | FYGBOPF_PARALLEL, seq, 0, NULL, NULL, fy_generic_test_sum_reducer, fy_value(gb, 10)); sum = fy_cast(v, INT_MIN); ck_assert(sum == 36); printf("> preduce-small-seq-sum-acc10: "); fy_generic_emit_default(v); map = fy_mapping("a", 7, "b", 6, "c", 5, "d", 8); v = fy_generic_op(gb, FYGBOPF_REDUCE | FYGBOPF_PARALLEL, map, 0, NULL, NULL, fy_generic_test_sum_reducer, fy_value(gb, 10)); sum = fy_cast(v, INT_MIN); ck_assert(sum == 36); printf("> preduce-map-sum-acc10: "); fy_generic_emit_default(v); fy_generic_builder_destroy(gb); } free(items); } END_TEST /* Test: parallel reduce */ START_TEST(simple_ops) { char buf[65536]; struct fy_generic_builder *gb; fy_generic v, vexp; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); v = fy_gb_create_sequence(gb, 1, 5, 10); vexp = fy_sequence(1, 5, 10); ck_assert(!fy_generic_compare(v, vexp)); printf("> simple-seq-create: "); fy_generic_emit_default(v); v = fy_gb_create_mapping(gb, "foo", 100, "bar", 200, "baz", 300); vexp = fy_mapping("foo", 100, "bar", 200, "baz", 300); ck_assert(!fy_generic_compare(v, vexp)); printf("> simple-map-create: "); fy_generic_emit_default(v); /* now do operations using the user friendly interface */ v = fy_gb_assoc(gb, fy_mapping("bar", 100), "foo", 10); vexp = fy_mapping("bar", 100, "foo", 10); ck_assert(!fy_generic_compare(v, vexp)); printf("> simple-assoc: "); fy_generic_emit_default(v); /* disassociate, mapping goes empty ("bar": 100) -> () */ v = fy_gb_disassoc(gb, fy_mapping("bar", 100), "bar"); vexp = fy_map_empty; ck_assert(!fy_generic_compare(v, vexp)); printf("> simple-disassoc: "); fy_generic_emit_default(v); /* testing insert at the middle (1, 2) -> (1, 300, 2) */ v = fy_gb_insert(gb, fy_sequence(gb, 1, 2), 1, 300); vexp = fy_sequence(1, 300, 2); ck_assert(!fy_generic_compare(v, vexp)); printf("> simple-seq-insert: "); fy_generic_emit_default(v); } END_TEST /* Test: iterators */ START_TEST(iterators) { char buf[65536]; struct fy_generic_builder *gb; fy_generic v, seq, map; fy_generic_map_pair mp; int ival, idx; const char *sval; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* first try simple sequences */ seq = fy_sequence(0, 10, 20); printf("fy_foreach seq generic\n"); idx = 0; fy_foreach(v, seq) { ck_assert(idx < 3); ival = fy_cast(v, -1); ck_assert(ival == (idx * 10)); idx++; printf("> %d\n", ival); } printf("fy_foreach seq int\n"); idx = 0; fy_foreach(ival, seq) { ck_assert(idx < 3); ck_assert(ival == (idx * 10)); idx++; printf("> %d\n", ival); } /* sequences of strings now */ printf("fy_foreach seq strings\n"); seq = fy_sequence("Hello", "World", "!"); idx = 0; fy_foreach(sval, seq) { ck_assert(idx < 3); if (idx == 0) ck_assert(!strcmp(sval, "Hello")); else if (idx == 1) ck_assert(!strcmp(sval, "World")); else if (idx == 2) ck_assert(!strcmp(sval, "!")); idx++; printf("> %s\n", sval); } /* now try over mappings */ printf("fy_foreach map strings\n"); map = fy_mapping("foo", 100, "bar", 200); idx = 0; fy_foreach(sval, map) { ival = fy_get(map, sval, -1); if (idx == 0) { ck_assert(!strcmp(sval, "foo")); ck_assert(ival == 100); } else if (idx == 1) { ck_assert(!strcmp(sval, "bar")); ck_assert(ival == 200); } idx++; printf("> %s: %d\n", sval, ival); } /* iterate using mapping pairs */ printf("fy_foreach map pairs\n"); idx = 0; fy_foreach(mp, map) { sval = fy_cast(mp.key, ""); ival = fy_cast(mp.value, -1); if (idx == 0) { ck_assert(!strcmp(sval, "foo")); ck_assert(ival == 100); } else if (idx == 1) { ck_assert(!strcmp(sval, "bar")); ck_assert(ival == 200); } idx++; printf("> %s: %d\n", sval, ival); } } END_TEST /* Test: local ops */ START_TEST(local_ops) { fy_generic seq, map; fy_generic v; /* first try simple sequence */ seq = fy_create_local_sequence(0, 10, 20, 30, 40, 50, 60, 70); printf("> create local seq: "); fy_generic_emit_default(seq); map = fy_create_local_mapping("foo", 10, "bar", 20, "baz", 30, "frooz long string", 40); printf("> create local map: "); fy_generic_emit_default(map); v = fy_local_assoc(map, "new", 100); printf("> assoc local: "); fy_generic_emit_default(v); } END_TEST /* Test: local ops retry mechanism - create structures that exceed initial buffer size * The FY_LOCAL_OP starts with ~384 bytes and doubles until 65536. * We force retries by creating large structures. */ START_TEST(local_ops_retry) { fy_generic seq, map, v; int i; printf("> Building large sequence incrementally...\n"); seq = fy_sequence(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); for (i = 1; i < 5; i++) { seq = fy_concat(seq, fy_sequence( i*10+0, i*10+1, i*10+2, i*10+3, i*10+4, i*10+5, i*10+6, i*10+7, i*10+8, i*10+9)); } ck_assert(fy_generic_is_valid(seq)); ck_assert(fy_len(seq) == 50); printf("> Large sequence length: %zu\n", fy_len(seq)); /* Verify first and last elements */ ck_assert(fy_get_at(seq, 0, -1) == 0); ck_assert(fy_get_at(seq, 49, -1) == 49); fy_generic_emit_default(seq); /* Test 2: Create a mapping with long string keys * Long strings force out-of-place allocation */ printf("> Creating mapping with long string keys...\n"); map = fy_merge(fy_map_empty, fy_mapping( "this_is_a_very_long_key_that_exceeds_inline_storage_01", 1, "this_is_a_very_long_key_that_exceeds_inline_storage_02", 2, "this_is_a_very_long_key_that_exceeds_inline_storage_03", 3, "this_is_a_very_long_key_that_exceeds_inline_storage_04", 4, "this_is_a_very_long_key_that_exceeds_inline_storage_05", 5, "this_is_a_very_long_key_that_exceeds_inline_storage_06", 6, "this_is_a_very_long_key_that_exceeds_inline_storage_07", 7, "this_is_a_very_long_key_that_exceeds_inline_storage_08", 8, "this_is_a_very_long_key_that_exceeds_inline_storage_09", 9, "this_is_a_very_long_key_that_exceeds_inline_storage_10", 10 )); ck_assert(fy_generic_is_valid(map)); ck_assert(fy_len(map) == 10); printf("> Long-key mapping length: %zu\n", fy_len(map)); fy_generic_emit_default(map); /* Test 3: Chain operations that accumulate size */ printf("> Chaining operations to accumulate size...\n"); seq = fy_sequence(1, 2, 3); for (i = 0; i < 10; i++) { seq = fy_append(seq, i * 10, i * 10 + 1, i * 10 + 2); } ck_assert(fy_generic_is_valid(seq)); ck_assert(fy_len(seq) == 33); /* 3 + 10*3 = 33 */ printf("> Chained sequence length: %zu\n", fy_len(seq)); /* Test 5: Large concat operation */ printf("> Large concat operation...\n"); seq = fy_sequence(0); v = fy_concat(seq, fy_sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), fy_sequence(11, 12, 13, 14, 15, 16, 17, 18, 19, 20), fy_sequence(21, 22, 23, 24, 25, 26, 27, 28, 29, 30) ); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_len(v) == 31); /* 1 + 10 + 10 + 10 */ printf("> Concat result length: %zu\n", fy_len(v)); /* Test 6: Large merge operation */ printf("> Large merge operation...\n"); map = fy_mapping("base", 0); v = fy_merge(map, fy_mapping("a1", 1, "a2", 2, "a3", 3), fy_mapping("b1", 4, "b2", 5, "b3", 6), fy_mapping("c1", 7, "c2", 8, "c3", 9) ); ck_assert(fy_generic_is_valid(v)); ck_assert(fy_len(v) == 10); /* 1 + 3 + 3 + 3 */ printf("> Merge result length: %zu\n", fy_len(v)); } /* Test: unified ops - auto-dispatch based on first argument type */ START_TEST(unified_ops) { char buf[4096]; struct fy_generic_builder *gb; fy_generic seq, map, v, vexp; int sum; bool ok; gb = fy_generic_builder_create_in_place( FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); seq = fy_sequence(gb, 1, 2, 3, 4, 5); printf("> fy_sequence(gb, ...): "); fy_generic_emit_default(seq); ck_assert(fy_len(seq) == 5); /* append, with and without gb */ v = fy_append(gb, seq, 6, 7, 8); printf("> fy_append(gb, seq, ...): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 8); v = fy_append(seq, 6, 7, 8); printf("> fy_append(seq, ...): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 8); /* insert, with and without gb */ v = fy_insert(gb, seq, 0, 100); printf("> fy_insert(gb, seq, 0, 100): "); fy_generic_emit_default(v); ck_assert(fy_get_at(v, 0, -1) == 100); v = fy_insert(seq, 0, 100); printf("> fy_insert(gb, seq, 0, 100): "); fy_generic_emit_default(v); ck_assert(fy_get_at(v, 0, -1) == 100); /* replace, with and without gb */ v = fy_replace(gb, seq, 0, 999); printf("> fy_replace(gb, seq, 0, 999): "); fy_generic_emit_default(v); ck_assert(fy_get_at(v, 0, -1) == 999); v = fy_replace(seq, 0, 999); printf("> fy_replace(seq, 0, 999): "); fy_generic_emit_default(v); ck_assert(fy_get_at(v, 0, -1) == 999); /* go with a map */ map = fy_mapping(gb, "a", 1, "b", 2); printf("> fy_mapping(gb, ...): "); fy_generic_emit_default(map); ck_assert(fy_len(map) == 2); /* assoc with and without gb */ v = fy_assoc(gb, map, "c", 3); printf("> fy_assoc(gb, map, ...): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 3); v = fy_assoc(map, "c", 3); printf("> fy_assoc(map, ...): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 3); /* disassoc with and without gb */ v = fy_disassoc(gb, map, "a"); printf("> fy_disassoc(gb, map, 'a'): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 1); v = fy_disassoc(map, "a"); printf("> fy_disassoc(map, 'a'): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 1); /* keys, values, and items with and without gb */ v = fy_keys(gb, map); printf("> fy_keys(gb, map): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 2); v = fy_keys(map); printf("> fy_keys(map): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 2); v = fy_values(gb, map); printf("> fy_values(gb, map): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 2); v = fy_values(map); printf("> fy_values(map): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 2); v = fy_items(gb, map); printf("> fy_items(gb, map): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 2); v = fy_items(map); printf("> fy_items(map): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 2); /* concat */ v = fy_concat(gb, seq, fy_sequence(100, 200)); printf("> fy_concat(gb, seq, ...): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 7); v = fy_concat(seq, fy_sequence(100, 200)); printf("> fy_concat(seq, ...): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 7); /* Test contains */ vexp = fy_sequence(1, 2, 3); v = fy_contains(gb, vexp, 2); ok = v.v == fy_true_value; printf("> fy_contains(gb, [1,2,3], 2): "); fy_generic_emit_default(v); ck_assert(ok == true); v = fy_contains(vexp, 2); ok = v.v == fy_true_value; printf("> fy_contains([1,2,3], 2): "); fy_generic_emit_default(v); ck_assert(ok == true); v = fy_contains(gb, vexp, 99); ok = v.v == fy_true_value; printf("> fy_contains(gb, [1,2,3], 99): "); fy_generic_emit_default(v); ck_assert(ok == false); v = fy_contains(vexp, 99); ok = v.v == fy_true_value; printf("> fy_contains([1,2,3], 99): "); fy_generic_emit_default(v); ck_assert(ok == false); /* Test unique */ vexp = fy_sequence(1, 2, 2, 3, 3, 3); v = fy_unique(gb, vexp); printf("> fy_unique(gb, [1,2,2,3,3,3]): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 3); v = fy_unique(vexp); printf("> fy_unique([1,2,2,3,3,3]): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 3); /* Test merge */ map = fy_mapping("a", 1); v = fy_merge(gb, map, fy_mapping("b", 2, "c", 3)); printf("> fy_merge(gb, {a:1}, {b:2,c:3}): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 3); map = fy_mapping("a", 1); v = fy_merge(map, fy_mapping("b", 2, "c", 3)); printf("> fy_merge({a:1}, {b:2,c:3}): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 3); /* Test sort */ vexp = fy_sequence(4, 3, 1, 2); v = fy_sort(gb, vexp); printf("> fy_sort(gb, [4,3,1,2]): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 3, -1) == 4); v = fy_sort(vexp); printf("> fy_sort([4,3,1,2]): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 3, -1) == 4); /* Test reverse */ vexp = fy_sequence(4, 3, 2, 1); v = fy_reverse(gb, vexp); printf("> fy_reverse(gb, [4,3,2,1]): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 3, -1) == 4); v = fy_reverse(vexp); printf("> fy_reverse([4,3,1,2]): "); fy_generic_emit_default(v); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 1); ck_assert(fy_get(v, 3, -1) == 4); /* filter a single sequence for items > 100 (one exists) */ seq = fy_sequence(7, 6, 5, 101, 8, -100); v = fy_filter(gb, seq, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 101); printf("> fy_filter(gb, [7,6,5,101,8,-100]: "); fy_generic_emit_default(v); v = fy_filter(seq, test_filter_over_100); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 101); printf("> fy_filter([7,6,5,101,8,-100]: "); fy_generic_emit_default(v); /* map a single sequence of numbers */ seq = fy_sequence(7, 6, 5, 8); v = fy_map(gb, seq, test_map_double); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 14); ck_assert(fy_get(v, 1, -1) == 12); ck_assert(fy_get(v, 2, -1) == 10); ck_assert(fy_get(v, 3, -1) == 16); printf("> fy_map(gb, [7,6,5,8]): "); fy_generic_emit_default(v); v = fy_map(seq, test_map_double); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 14); ck_assert(fy_get(v, 1, -1) == 12); ck_assert(fy_get(v, 2, -1) == 10); ck_assert(fy_get(v, 3, -1) == 16); printf("> fy_map([7,6,5,8]) - double: "); fy_generic_emit_default(v); /* sum a sequence of numbers */ seq = fy_sequence(7, 6, 5, 8); v = fy_reduce(gb, seq, 0, fy_generic_test_sum_reducer); sum = fy_cast(v, 0); ck_assert(sum == 26); printf("> fy_reduce(gb, [7,6,5,8]) - sum): %d", sum); fy_generic_emit_default(v); v = fy_reduce(seq, 0, fy_generic_test_sum_reducer); sum = fy_cast(v, 0); ck_assert(sum == 26); printf("> fy_reduce([7,6,5,8]) - sum): %d", sum); fy_generic_emit_default(v); } END_TEST #ifdef FY_HAVE_LAMBDAS /* Test: lambda operations using FY_PRED/FY_XFORM/FY_REDUCER macros */ START_TEST(lambda_ops) { char buf[16384]; struct fy_generic_builder *gb; fy_generic seq, v; int sum; (void)sum; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); /* Create a test sequence */ seq = fy_sequence(7, 6, 5, 101, 8, -100); /* Test fy_local_filter_lambda - filter values > 100 */ v = fy_local_filter_lambda(seq, (fy_cast(v, 0) > 100) ); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 101); printf("> fy_local_filter_lambda (> 100): "); fy_generic_emit_default(v); /* Test fy_gb_filter_lambda - filter values > 100 */ v = fy_gb_filter_lambda(gb, seq, (fy_cast(v, 0) > 100) ); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 101); printf("> fy_gb_filter_lambda (> 100): "); fy_generic_emit_default(v); /* Test fy_filter_lambda - filter values > 100 */ v = fy_filter_lambda(seq, fy_cast(v, 0) > 100 ); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 101); printf("> fy_filter_lambda (> 100): "); fy_generic_emit_default(v); /* Test fy_filter_lambda with builder */ v = fy_filter_lambda(gb, seq, fy_cast(v, 0) > 100); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 1); ck_assert(fy_get(v, 0, -1) == 101); printf("> fy_filter_lambda(gb, ...) (> 100): "); fy_generic_emit_default(v); /* Test fy_map_lambda - double values */ seq = fy_sequence(7, 6, 5, 8); v = fy_map_lambda(seq, fy_value(fy_cast(v, 0) * 2)); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 14); ck_assert(fy_get(v, 1, -1) == 12); ck_assert(fy_get(v, 2, -1) == 10); ck_assert(fy_get(v, 3, -1) == 16); printf("> fy_map_lambda (x2): "); fy_generic_emit_default(v); /* Test fy_map_lambda with builder */ v = fy_map_lambda(gb, seq, fy_value(fy_cast(v, 0) * 2)); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); ck_assert(fy_get(v, 0, -1) == 14); printf("> fy_map_lambda(gb, ...) (x2): "); fy_generic_emit_default(v); #ifdef FY_HAVE_NESTED_FUNC_LAMBDAS /* Test fy_reduce_lambda - sum values */ seq = fy_sequence(7, 6, 5, 8); v = fy_reduce_lambda(seq, 0, fy_value(fy_cast(acc, 0) + fy_cast(v, 0)) ); sum = fy_cast(v, 0); ck_assert(sum == 26); printf("> fy_reduce_lambda (sum): %d\n", sum); /* Test fy_reduce_lambda with builder */ v = fy_reduce_lambda(gb, seq, 0, fy_value(fy_cast(acc, 0) + fy_cast(v, 0))); sum = fy_cast(v, 0); ck_assert(sum == 26); printf("> fy_reduce_lambda(gb, ...) (sum): %d\n", sum); /* Test chained lambda operations: filter then map */ seq = fy_sequence(1, 50, 101, 200, 3); v = fy_map_lambda( fy_filter_lambda(seq, fy_cast(v, 0) > 100), fy_value(fy_cast(v, 0) * 10)); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, 0, -1) == 1010); /* 101 * 10 */ ck_assert(fy_get(v, 1, -1) == 2000); /* 200 * 10 */ printf("> chained filter->map with lambdas: "); fy_generic_emit_default(v); /* Test parallel lambdas - fy_pfilter_lambda */ seq = fy_sequence(7, 6, 5, 101, 8, 200, 150); v = fy_pfilter_lambda(seq, NULL, fy_cast(v, 0) > 100); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); /* 101, 200, 150 */ printf("> fy_pfilter_lambda (> 100): "); fy_generic_emit_default(v); /* Test fy_pfilter_lambda with builder */ v = fy_pfilter_lambda(gb, seq, NULL, fy_cast(v, 0) > 100); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); printf("> fy_pfilter_lambda(gb, ...) (> 100): "); fy_generic_emit_default(v); /* Test parallel lambdas - fy_pmap_lambda */ seq = fy_sequence(7, 6, 5, 8); v = fy_pmap_lambda(seq, NULL, fy_value(fy_cast(v, 0) * 2)); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); printf("> fy_pmap_lambda (x2): "); fy_generic_emit_default(v); /* Test fy_pmap_lambda with builder */ v = fy_pmap_lambda(gb, seq, NULL, fy_value(fy_cast(v, 0) * 2)); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 4); printf("> fy_pmap_lambda(gb, ...) (x2): "); fy_generic_emit_default(v); /* Test parallel lambdas - fy_preduce_lambda */ seq = fy_sequence(7, 6, 5, 8); v = fy_preduce_lambda(seq, 0, NULL, fy_value(fy_cast(acc, 0) + fy_cast(v, 0))); sum = fy_cast(v, 0); ck_assert(sum == 26); printf("> fy_preduce_lambda (sum): %d\n", sum); /* Test fy_preduce_lambda with builder */ v = fy_preduce_lambda(gb, seq, 0, NULL, fy_value(fy_cast(acc, 0) + fy_cast(v, 0))); sum = fy_cast(v, 0); ck_assert(sum == 26); printf("> fy_preduce_lambda(gb, ...) (sum): %d\n", sum); #endif } END_TEST #endif START_TEST(get_at_path) { char buf[16384]; struct fy_generic_builder *gb; fy_generic seq, map, nested_map, v; (void)nested_map; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* Test 1: fy_local_lookup with sequence */ seq = fy_local_sequence(10, 20, 30, 40, 50); fy_generic_emit_default(seq); v = fy_local_get_at_path(seq, 2); /* index 2 -> value 30 */ ck_assert(fy_cast(v, -1) == 30); printf("> fy_local_get_at_path(seq, 2) = 30\n"); fy_generic_emit_default(v); /* Test 2: fy_local_lookup with mapping */ map = fy_local_mapping("a", 1, "b", 2, "c", 3); fy_generic_emit_default(map); v = fy_local_get_at_path(map, "b"); /* key "b" -> value 2 */ ck_assert(fy_cast(v, -1) == 2); printf("> fy_local_get_at_path(map, \"b\") = 2\n"); fy_generic_emit_default(v); /* Test 3: fy_local_lookup with nested path */ seq = fy_local_sequence(10, 20, 30); nested_map = fy_local_mapping("data", seq, "name", "test"); v = fy_local_get_at_path(nested_map, "data", 1); /* nested_map["data"][1] -> 20 */ ck_assert(fy_cast(v, -1) == 20); printf("> fy_local_get_at_path(nested_map, \"data\", 1) = 20\n"); /* Test 4: fy_gb_lookup with sequence */ seq = fy_gb_sequence(gb, 100, 200, 300); v = fy_gb_get_at_path(gb, seq, 1); /* index 1 -> value 200 */ ck_assert(fy_cast(v, -1) == 200); printf("> fy_gb_get_at_path(gb, seq, 1) = 200\n"); /* Test 5: fy_gb_lookup with mapping */ map = fy_gb_mapping(gb, "x", 10, "y", 20, "z", 30); v = fy_gb_get_at_path(gb, map, "z"); /* key "z" -> value 30 */ ck_assert(fy_cast(v, -1) == 30); printf("> fy_gb_get_at_path(gb, map, \"z\") = 30\n"); /* Test 6: fy_lookup auto-detection (local) */ seq = fy_local_sequence(5, 15, 25); v = fy_local_get_at_path(seq, 2); /* local, index 2 -> value 25 */ ck_assert(fy_cast(v, -1) == 25); printf("> fy_local_get_at_path(seq, 2) = 25 [local]\n"); /* Test 7: fy_lookup auto-detection (builder) */ map = fy_gb_mapping(gb, "p", 7, "q", 8, "r", 9); v = fy_gb_get_at_path(gb, map, "q"); /* builder, key "q" -> value 8 */ ck_assert(fy_cast(v, -1) == 8); printf("> fy_gb_get_at_path(gb, map, \"q\") = 8 [builder]\n"); /* Test 8: invalid get at returns fy_invalid */ seq = fy_local_sequence(1, 2, 3); v = fy_local_get_at_path(seq, "not_an_index"); /* wrong type for sequence */ fy_generic_emit_default(v); ck_assert(!fy_generic_is_valid(v)); printf("> fy_local_lookup with wrong type returns fy_invalid\n"); /* Test 9: out of bounds get at returns fy_invalid */ v = fy_local_get_at_path(seq, 99); /* index out of bounds */ ck_assert(!fy_generic_is_valid(v)); printf("> fy_local_lookup out of bounds returns fy_invalid\n"); } END_TEST START_TEST(set_ops) { char buf[16384]; struct fy_generic_builder *gb; fy_generic seq, map, v, v2; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* testing set, simple seq */ seq = fy_sequence(10, 20, 30); v = fy_generic_op(gb, FYGBOPF_SET, seq, 2, (fy_generic []){ fy_value(1), fy_value(200)} ); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 10); ck_assert(fy_get(v, 1, -1) == 200); ck_assert(fy_get(v, 2, -1) == 30); printf("> simple seq-set: "); fy_generic_emit_default(v); /* testing set, simple map */ map = fy_mapping("foo", 10, "bar", 20); v = fy_generic_op(gb, FYGBOPF_SET, map, 2, (fy_generic []){ fy_value("bar"), fy_value(200)} ); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 10); ck_assert(fy_get(v, "bar", -1) == 200); printf("> simple map-set: "); fy_generic_emit_default(v); /* testing set, multiple seq */ seq = fy_sequence(10, 20, 30); v = fy_generic_op(gb, FYGBOPF_SET, seq, 6, (fy_generic []){ fy_value(0), fy_value(100), fy_value(1), fy_value(200), fy_value(2), fy_value(300) } ); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 100); ck_assert(fy_get(v, 1, -1) == 200); ck_assert(fy_get(v, 2, -1) == 300); printf("> multiple seq-set: "); fy_generic_emit_default(v); /* testing set_at, simple seq */ seq = fy_sequence(10, 20, 30); v = fy_generic_op(gb, FYGBOPF_SET_AT, seq, 1, (fy_generic []){ fy_value(200)}, 1); ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 10); ck_assert(fy_get(v, 1, -1) == 200); ck_assert(fy_get(v, 2, -1) == 30); printf("> simple seq-set-at: "); fy_generic_emit_default(v); /* testing set-at, simple map */ map = fy_mapping("foo", 10, "bar", 20); v = fy_generic_op(gb, FYGBOPF_SET_AT, map, 1, (fy_generic []){ fy_value("bar"), fy_value(200)}, 1); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 10); ck_assert(fy_get(v, "bar", -1) == 200); printf("> simple map-set-at: "); fy_generic_emit_default(v); /* testing set_at_path, simple */ seq = fy_sequence(10, 20, 30); v = fy_generic_op(gb, FYGBOPF_SET_AT_PATH, seq, 2, (fy_generic []){ fy_value(1), fy_value(200)}); // /1 ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 10); ck_assert(fy_get(v, 1, -1) == 200); ck_assert(fy_get(v, 2, -1) == 30); printf("> simple seq-set-at-path /1=200: "); fy_generic_emit_default(v); /* testing set_at_path, nested */ seq = fy_sequence(10, 20, fy_sequence(1000, 2000)); v = fy_generic_op(gb, FYGBOPF_SET_AT_PATH, seq, 3, (fy_generic []){ fy_value(2), fy_value(0), fy_value(1)}); // /2/1 ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 10); ck_assert(fy_get(v, 1, -1) == 20); v2 = fy_get(v, 2, fy_invalid); ck_assert(fy_generic_is_sequence(v2)); ck_assert(fy_get(v2, 0, -1) == 1); ck_assert(fy_get(v2, 1, -1) == 2000); printf("> nested seq-set-at-path /2/1=1: "); fy_generic_emit_default(v2); /* testing set_at with map, simple */ seq = fy_mapping("foo", 10, "bar", 20); v = fy_generic_op(gb, FYGBOPF_SET_AT_PATH, map, 2, (fy_generic []) { fy_value("bar"), fy_value(100) } ); ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 10); ck_assert(fy_get(v, "bar", -1) == 100); printf("> simple map-set-at-path /bar=100: "); fy_generic_emit_default(v); /* testing set_at_path, nested */ map = fy_mapping("foo", 10, "bar", fy_mapping("baz", 100)); v = fy_generic_op(gb, FYGBOPF_SET_AT_PATH, map, 3, (fy_generic []){ fy_value("bar"), fy_value("baz"), fy_value(1)}); // /bar/baz ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 10); v2 = fy_get(v, "bar", fy_invalid); ck_assert(fy_generic_is_mapping(v2)); ck_assert(fy_get(v2, "baz", -1) == 1); printf("> nested map-set-at-path /bar/baz=1: "); fy_generic_emit_default(v); /* testing set_at_path, simple polymorphic */ seq = fy_sequence(10, 20, 30); v = fy_set_at_path(seq, 1, 200); // /1 ck_assert(fy_generic_is_sequence(v)); ck_assert(fy_len(v) == 3); ck_assert(fy_get(v, 0, -1) == 10); ck_assert(fy_get(v, 1, -1) == 200); ck_assert(fy_get(v, 2, -1) == 30); printf("> simple seq-set-at-path (poly) /1=200: "); fy_generic_emit_default(v); /* testing set_at_path, nested, polymorphic */ map = fy_mapping("foo", 10, "bar", fy_mapping("baz", 100)); v = fy_set_at_path(gb, map, "bar", "baz", 1); // /bar/baz = 1 ck_assert(fy_generic_is_mapping(v)); ck_assert(fy_len(v) == 2); ck_assert(fy_get(v, "foo", -1) == 10); v2 = fy_get(v, "bar", fy_invalid); ck_assert(fy_generic_is_mapping(v2)); ck_assert(fy_get(v2, "baz", -1) == 1); printf("> nested map-set-at-path (poly) /bar/baz=1: "); fy_generic_emit_default(v); } END_TEST START_TEST(parse_emit_ops) { char buf[65536]; struct fy_generic_builder *gb; fy_generic yaml_str, json_str, parsed, emitted; fy_generic seq, map; const char *result_str; struct fy_generic_op_args args; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* Test PARSE operation with YAML */ yaml_str = fy_gb_string_create(gb, "- item1\n- item2\n- item3"); ck_assert(fy_generic_is_valid(yaml_str)); memset(&args, 0, sizeof(args)); args.parse.flags = FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING | FYOPPF_MODE_AUTO; parsed = fy_generic_op_args(gb, FYGBOPF_PARSE, yaml_str, &args); fy_generic_emit_default(parsed); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_sequence(parsed)); ck_assert(fy_len(parsed) == 3); ck_assert_str_eq(fy_get(parsed, 0, ""), "item1"); ck_assert_str_eq(fy_get(parsed, 1, ""), "item2"); ck_assert_str_eq(fy_get(parsed, 2, ""), "item3"); printf("> PARSE YAML sequence: OK\n"); fy_generic_emit_default(parsed); /* Test PARSE operation with JSON using args */ json_str = fy_gb_string_create(gb, "{\"key1\": 100, \"key2\": 200}"); ck_assert(fy_generic_is_valid(json_str)); memset(&args, 0, sizeof(args)); args.parse.flags = FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING | FYOPPF_MODE_JSON; parsed = fy_generic_op_args(gb, FYGBOPF_PARSE, json_str, &args); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_mapping(parsed)); ck_assert(fy_len(parsed) == 2); ck_assert(fy_get(parsed, "key1", 0) == 100); ck_assert(fy_get(parsed, "key2", 0) == 200); printf("> PARSE JSON mapping with args: OK\n"); fy_generic_emit_default(parsed); /* Test EMIT operation - emit to YAML block */ seq = fy_sequence(1, 2, 3, 4, 5); memset(&args, 0, sizeof(args)); args.emit.flags = FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_BLOCK; emitted = fy_generic_op_args(gb, FYGBOPF_EMIT, seq, &args); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); result_str = fy_generic_cast_sized_string_default(emitted, fy_szstr_empty).data; ck_assert_ptr_ne(result_str, NULL); printf("> EMIT sequence to YAML:\n%s\n", result_str); /* Test EMIT operation - emit to JSON using args */ map = fy_mapping("name", "Alice", "age", 30); fy_generic_emit_default(map); memset(&args, 0, sizeof(args)); args.emit.flags = FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_JSON | FYOPEF_STYLE_DEFAULT; emitted = fy_generic_op_args(gb, FYGBOPF_EMIT, map, &args); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); result_str = fy_generic_cast_sized_string_default(emitted, fy_szstr_empty).data; ck_assert_ptr_ne(result_str, NULL); printf("> EMIT mapping to JSON:\n%s\n", result_str); /* Test EMIT operation - emit to flow YAML using args */ memset(&args, 0, sizeof(args)); args.emit.flags = FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_FLOW; emitted = fy_generic_op_args(gb, FYGBOPF_EMIT, seq, &args); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); result_str = fy_generic_cast_sized_string_default(emitted, fy_szstr_empty).data; ck_assert_ptr_ne(result_str, NULL); printf("> EMIT sequence to YAML flow:\n%s\n", result_str); /* Test EMIT operation - emit using args */ memset(&args, 0, sizeof(args)); args.emit.flags = FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_JSON | FYOPEF_STYLE_DEFAULT | FYOPEF_INDENT_2; emitted = fy_generic_op_args(gb, FYGBOPF_EMIT, map, &args); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); result_str = fy_generic_cast_sized_string_default(emitted, fy_szstr_empty).data; ck_assert_ptr_ne(result_str, NULL); printf("> EMIT mapping to JSON with args:\n%s\n", result_str); /* Test round-trip: YAML -> parse -> emit -> parse */ yaml_str = fy_gb_string_create(gb, "foo: bar\nbaz: 42"); memset(&args, 0, sizeof(args)); args.parse.flags = FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING | FYOPPF_MODE_YAML_1_2; parsed = fy_generic_op_args(gb, FYGBOPF_PARSE, yaml_str, &args); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_mapping(parsed)); memset(&args, 0, sizeof(args)); args.emit.flags = FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_BLOCK; emitted = fy_generic_op_args(gb, FYGBOPF_EMIT, parsed, &args); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); memset(&args, 0, sizeof(args)); args.parse.flags = FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING | FYOPPF_MODE_YAML_1_2; parsed = fy_generic_op_args(gb, FYGBOPF_PARSE, emitted, &args); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_mapping(parsed)); ck_assert_str_eq(fy_cast(fy_get(parsed, "foo", fy_null), ""), "bar"); ck_assert(fy_cast(fy_get(parsed, "baz", fy_null), 0) == 42); printf("> Round-trip YAML parse->emit->parse: OK\n"); /* Test round-trip: JSON -> parse -> emit -> parse */ json_str = fy_gb_string_create(gb, "[1, 2, 3, 4]"); memset(&args, 0, sizeof(args)); args.parse.flags = FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING | FYOPPF_MODE_JSON; parsed = fy_generic_op_args(gb, FYGBOPF_PARSE, json_str, &args); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_sequence(parsed)); ck_assert(fy_len(parsed) == 4); memset(&args, 0, sizeof(args)); args.emit.flags = FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_JSON | FYOPEF_STYLE_BLOCK; emitted = fy_generic_op_args(gb, FYGBOPF_EMIT, parsed, &args); ck_assert(fy_generic_is_valid(emitted)); memset(&args, 0, sizeof(args)); args.parse.flags = FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING | FYOPPF_MODE_JSON; parsed = fy_generic_op_args(gb, FYGBOPF_PARSE, emitted, &args); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_sequence(parsed)); ck_assert(fy_len(parsed) == 4); ck_assert(fy_cast(fy_get(parsed, 0, fy_null), 0) == 1); ck_assert(fy_cast(fy_get(parsed, 3, fy_null), 0) == 4); printf("> Round-trip JSON parse->emit->parse: OK\n"); /* test one liners without a newline */ map = fy_mapping("name", "Alice", "age", 30); fy_generic_emit_default(map); memset(&args, 0, sizeof(args)); args.emit.flags = FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_ONELINE; emitted = fy_generic_op_args(gb, FYGBOPF_EMIT, map, &args); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); result_str = fy_generic_cast_sized_string_default(emitted, fy_szstr_empty).data; ck_assert_ptr_ne(result_str, NULL); printf("> EMIT mapping to JSON (oneline):\n%s\n", result_str); fy_generic_emit_default(parsed); /* Test PARSE operation with YAML and fy_gb_parse */ yaml_str = fy_gb_string_create(gb, "- item1\n- item2\n- item3"); ck_assert(fy_generic_is_valid(yaml_str)); parsed = fy_gb_parse(gb, "- item1\n- item2\n- item3", FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING, NULL); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_sequence(parsed)); ck_assert(fy_len(parsed) == 3); ck_assert_str_eq(fy_get(parsed, 0, ""), "item1"); ck_assert_str_eq(fy_get(parsed, 1, ""), "item2"); ck_assert_str_eq(fy_get(parsed, 2, ""), "item3"); printf("> PARSE YAML sequence (with fy_gb_parse): OK\n"); fy_generic_emit_default(parsed); parsed = fy_local_parse("- item1\n- item2\n- item3", FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING, NULL); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_sequence(parsed)); ck_assert(fy_len(parsed) == 3); ck_assert_str_eq(fy_get(parsed, 0, ""), "item1"); ck_assert_str_eq(fy_get(parsed, 1, ""), "item2"); ck_assert_str_eq(fy_get(parsed, 2, ""), "item3"); printf("> PARSE YAML sequence (with fy_local_parse): OK\n"); fy_generic_emit_default(parsed); parsed = fy_parse("- item1\n- item2\n- item3", FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING, NULL); ck_assert(fy_generic_is_valid(parsed)); ck_assert(fy_generic_is_sequence(parsed)); ck_assert(fy_len(parsed) == 3); ck_assert_str_eq(fy_get(parsed, 0, ""), "item1"); ck_assert_str_eq(fy_get(parsed, 1, ""), "item2"); ck_assert_str_eq(fy_get(parsed, 2, ""), "item3"); printf("> PARSE YAML sequence (with fy_parse): OK\n"); fy_generic_emit_default(parsed); seq = fy_sequence(1, 2, 3, 4, 5); emitted = fy_gb_emit(gb, seq, FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_BLOCK, NULL); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); result_str = fy_generic_cast_sized_string_default(emitted, fy_szstr_empty).data; ck_assert_ptr_ne(result_str, NULL); printf("> EMIT sequence to YAML (with fy_gb_emit):\n%s\n", result_str); seq = fy_sequence(1, 2, 3, 4, 5); emitted = fy_local_emit(seq, FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_BLOCK, NULL); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); result_str = fy_generic_cast_sized_string_default(emitted, fy_szstr_empty).data; ck_assert_ptr_ne(result_str, NULL); printf("> EMIT sequence to YAML (with fy_local_emit):\n%s\n", result_str); seq = fy_sequence(1, 2, 3, 4, 5); emitted = fy_emit(seq, FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_BLOCK, NULL); ck_assert(fy_generic_is_valid(emitted)); ck_assert(fy_generic_is_string(emitted)); result_str = fy_generic_cast_sized_string_default(emitted, fy_szstr_empty).data; ck_assert_ptr_ne(result_str, NULL); printf("> EMIT sequence to YAML (with fy_emit):\n%s\n", result_str); #if 0 seq = fy_parse_file(FYOPPF_DISABLE_DIRECTORY, "x.yaml"); fy_generic_emit_default(parsed); emitted = fy_emit_file(seq, FYOPEF_DISABLE_DIRECTORY, "y.yaml"); fy_generic_emit_default(emitted); #endif } END_TEST START_TEST(generic_failsafe_schema_preserves_empty_scalars) { char buf[16384]; struct fy_generic_builder *gb; fy_generic parsed, value, seq, item; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_YAML1_2_FAILSAFE | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); parsed = fy_parse(gb, "a:\n" "seq:\n" " - \n", FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING, NULL); ck_assert(fy_generic_is_valid(parsed)); value = fy_get(parsed, "a", fy_invalid); ck_assert(fy_generic_is_string(value)); ck_assert_str_eq(fy_cast(value, ""), ""); seq = fy_get(parsed, "seq", fy_invalid); ck_assert(fy_generic_is_sequence(seq)); item = fy_get(seq, 0, fy_invalid); ck_assert(fy_generic_is_string(item)); ck_assert_str_eq(fy_cast(item, ""), ""); } END_TEST static void do_generic_resolved_parse_drops_anchors(enum fy_op_emit_flags extra_emit_flags) { char buf[65536]; struct fy_generic_builder *gb; fy_generic parsed; fy_generic emitted; const char *emitted_str; const char *expected = "a: b\nc: b\nd: e\n"; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); parsed = fy_gb_parse(gb, "a: &anchor b\n" "c: *anchor\n" "d: e\n", FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING, NULL); ck_assert(fy_generic_is_valid(parsed)); emitted = fy_gb_emit(gb, parsed, FYOPEF_DISABLE_DIRECTORY | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_BLOCK | extra_emit_flags, NULL); ck_assert(fy_generic_is_string(emitted)); emitted_str = fy_cast(emitted, ""); ck_assert_str_eq(emitted_str, expected); ck_assert_ptr_eq(strstr(emitted_str, "&anchor"), NULL); ck_assert_ptr_eq(strstr(emitted_str, "*anchor"), NULL); } START_TEST(generic_resolved_parse_drops_anchors_encoder) { do_generic_resolved_parse_drops_anchors(0); } END_TEST static void do_generic_emit_preserves_document_markers(enum fy_op_emit_flags extra_emit_flags) { char buf[65536]; struct fy_generic_builder *gb; fy_generic parsed; fy_generic emitted; const char *emitted_str; const char *yaml = "---\nfoo: bar\n---\nbaz: frooz\n...\n"; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); parsed = fy_gb_parse(gb, yaml, FYOPPF_INPUT_TYPE_STRING | FYOPPF_MULTI_DOCUMENT, NULL); ck_assert(fy_generic_is_valid(parsed)); emitted = fy_gb_emit(gb, parsed, FYOPEF_MULTI_DOCUMENT | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_BLOCK | extra_emit_flags, NULL); ck_assert(fy_generic_is_string(emitted)); emitted_str = fy_cast(emitted, ""); ck_assert_str_eq(emitted_str, yaml); } START_TEST(generic_emit_preserves_document_markers_encoder) { do_generic_emit_preserves_document_markers(0); } END_TEST static void do_generic_emit_preserves_tag_directive_spelling(enum fy_op_emit_flags extra_emit_flags) { char buf[65536]; struct fy_generic_builder *gb; fy_generic parsed; fy_generic emitted; const char *emitted_str; const char *yaml = "%TAG !e! tag:example.com,2024:\n---\n!e!thing foo\n"; const char *directive = "%TAG !e! tag:example.com,2024:\n"; const char *short_tag = "!e!thing"; const char *expanded_tag = "tag:example.com,2024:thing"; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); parsed = fy_gb_parse(gb, yaml, FYOPPF_INPUT_TYPE_STRING | FYOPPF_MULTI_DOCUMENT, NULL); ck_assert(fy_generic_is_valid(parsed)); emitted = fy_gb_emit(gb, parsed, FYOPEF_MULTI_DOCUMENT | FYOPEF_MODE_YAML_1_2 | FYOPEF_STYLE_BLOCK | extra_emit_flags, NULL); ck_assert(fy_generic_is_string(emitted)); emitted_str = fy_cast(emitted, ""); (void)yaml; ck_assert_ptr_ne(strstr(emitted_str, directive), NULL); ck_assert_ptr_ne(strstr(emitted_str, short_tag), NULL); ck_assert_ptr_eq(strstr(emitted_str, expanded_tag), NULL); } START_TEST(generic_emit_preserves_tag_directive_spelling_encoder) { do_generic_emit_preserves_tag_directive_spelling(0); } END_TEST START_TEST(generic_document_builder_pull_mode) { char buf[65536]; struct fy_generic_builder *gb; struct fy_generic_document_builder_cfg cfg; struct fy_generic_document_builder *fygdb; struct fy_parser *fyp; const char *yaml = "---\nfoo: 1\n---\nbar: 2\n"; fy_generic doc1, doc2; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); memset(&cfg, 0, sizeof(cfg)); cfg.parse_cfg.flags = FYPCF_DEFAULT_PARSE | FYPCF_RESOLVE_DOCUMENT; cfg.gb = gb; cfg.flags = FYGDBF_DISABLE_DIRECTORY; fyp = fy_parser_create(&cfg.parse_cfg); ck_assert_ptr_ne(fyp, NULL); ck_assert_int_eq(fy_parser_set_string(fyp, yaml, FY_NT), 0); fygdb = fy_generic_document_builder_create(&cfg); ck_assert_ptr_ne(fygdb, NULL); doc1 = fy_generic_document_builder_load_document(fygdb, fyp); ck_assert(fy_generic_is_mapping(doc1)); ck_assert_int_eq(fy_get(doc1, "foo", -1), 1); doc2 = fy_generic_document_builder_load_document(fygdb, fyp); ck_assert(fy_generic_is_mapping(doc2)); ck_assert_int_eq(fy_get(doc2, "bar", -1), 2); ck_assert(fy_generic_is_invalid(fy_generic_document_builder_load_document(fygdb, fyp))); fy_generic_document_builder_destroy(fygdb); fy_parser_destroy(fyp); } END_TEST START_TEST(generic_document_builder_push_mode_metadata) { char buf_actual[65536]; char buf_expected[65536]; struct fy_generic_builder *gb_actual, *gb_expected; struct fy_generic_document_builder_cfg cfg; struct fy_generic_document_builder *fygdb; struct fy_parser *fyp; struct fy_eventp *fyep; const char *yaml = "---\nfoo: \"bar\" # trailing\n"; fy_generic actual, expected, value, comment, style, marker; int rc; gb_actual = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf_actual, sizeof(buf_actual)); gb_expected = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf_expected, sizeof(buf_expected)); ck_assert_ptr_ne(gb_actual, NULL); ck_assert_ptr_ne(gb_expected, NULL); memset(&cfg, 0, sizeof(cfg)); cfg.parse_cfg.flags = FYPCF_DEFAULT_PARSE | FYPCF_RESOLVE_DOCUMENT | FYPCF_KEEP_COMMENTS | FYPCF_CREATE_MARKERS | FYPCF_KEEP_STYLE; cfg.gb = gb_actual; cfg.flags = FYGDBF_DISABLE_DIRECTORY | FYGDBF_KEEP_COMMENTS | FYGDBF_CREATE_MARKERS | FYGDBF_KEEP_STYLE; fyp = fy_parser_create(&cfg.parse_cfg); ck_assert_ptr_ne(fyp, NULL); ck_assert_int_eq(fy_parser_set_string(fyp, yaml, FY_NT), 0); fygdb = fy_generic_document_builder_create(&cfg); ck_assert_ptr_ne(fygdb, NULL); fy_generic_document_builder_set_in_stream(fygdb); while ((fyep = fy_parse_private(fyp)) != NULL) { if (fyep->e.type == FYET_STREAM_START) { fy_parse_eventp_recycle(fyp, fyep); continue; } rc = fy_generic_document_builder_process_event(fygdb, &fyep->e); fy_parse_eventp_recycle(fyp, fyep); ck_assert_int_ne(rc, -1); if (fy_generic_document_builder_is_document_complete(fygdb)) break; } ck_assert(fy_generic_document_builder_is_document_complete(fygdb)); ck_assert(fy_generic_is_mapping(fy_generic_document_builder_peek_document(fygdb))); actual = fy_generic_document_builder_take_document(fygdb); ck_assert(fy_generic_is_mapping(actual)); expected = fy_gb_parse(gb_expected, yaml, FYOPPF_DISABLE_DIRECTORY | FYOPPF_INPUT_TYPE_STRING | FYOPPF_KEEP_COMMENTS | FYOPPF_CREATE_MARKERS | FYOPPF_KEEP_STYLE, NULL); ck_assert(fy_generic_is_valid(expected)); ck_assert_int_eq(fy_generic_compare(actual, expected), 0); value = fy_get(actual, "foo", fy_invalid); ck_assert(fy_generic_is_valid(value)); comment = fy_generic_get_comment(value); style = fy_generic_get_style(value); marker = fy_generic_get_marker(value); ck_assert(fy_generic_is_string(comment)); ck_assert(strstr(fy_cast(comment, ""), "trailing") != NULL); ck_assert(fy_generic_is_int_type(style)); ck_assert_int_eq(fy_cast(style, -1), FYSS_DOUBLE_QUOTED); ck_assert(fy_generic_is_sequence(marker)); ck_assert_int_eq((int)fy_len(marker), 6); fy_generic_document_builder_destroy(fygdb); fy_parser_destroy(fyp); } END_TEST START_TEST(generic_document_builder_directory_mode) { char buf_actual[65536]; char buf_expected[65536]; struct fy_generic_builder *gb_actual, *gb_expected; struct fy_generic_document_builder_cfg cfg; struct fy_generic_document_builder *fygdb; struct fy_parser *fyp; const char *yaml = "---\nfoo: 1\n"; fy_generic actual_vds, expected_vds, root; gb_actual = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf_actual, sizeof(buf_actual)); gb_expected = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf_expected, sizeof(buf_expected)); ck_assert_ptr_ne(gb_actual, NULL); ck_assert_ptr_ne(gb_expected, NULL); memset(&cfg, 0, sizeof(cfg)); cfg.parse_cfg.flags = FYPCF_DEFAULT_PARSE | FYPCF_RESOLVE_DOCUMENT; cfg.gb = gb_actual; cfg.flags = 0; fyp = fy_parser_create(&cfg.parse_cfg); ck_assert_ptr_ne(fyp, NULL); ck_assert_int_eq(fy_parser_set_string(fyp, yaml, FY_NT), 0); fygdb = fy_generic_document_builder_create(&cfg); ck_assert_ptr_ne(fygdb, NULL); actual_vds = fy_generic_document_builder_load_document(fygdb, fyp); ck_assert(fy_generic_is_valid(actual_vds)); expected_vds = fy_gb_parse(gb_expected, yaml, FYOPPF_INPUT_TYPE_STRING, NULL); ck_assert(fy_generic_is_valid(expected_vds)); ck_assert_int_eq(fy_generic_compare(actual_vds, expected_vds), 0); root = fy_generic_vds_get_root(actual_vds); ck_assert(fy_generic_is_mapping(root)); ck_assert_int_eq(fy_get(root, "foo", -1), 1); fy_generic_document_builder_destroy(fygdb); fy_parser_destroy(fyp); } END_TEST START_TEST(slice_ops) { char buf[16384]; struct fy_generic_builder *gb; struct fy_generic_op_args args; fy_generic seq, sliced; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* Create test sequence: ["a", "b", "c", "d", "e"] */ seq = fy_sequence("a", "b", "c", "d", "e"); ck_assert(fy_generic_is_sequence(seq)); ck_assert(fy_len(seq) == 5); printf("> Original sequence: "); fy_generic_emit_default(seq); /* Test SLICE: middle elements [1:4] -> ["b", "c", "d"] */ memset(&args, 0, sizeof(args)); args.slice.start = 1; args.slice.end = 4; sliced = fy_generic_op_args(gb, FYGBOPF_SLICE, seq, &args); ck_assert(fy_generic_is_sequence(sliced)); ck_assert(fy_len(sliced) == 3); ck_assert_str_eq(fy_get(sliced, 0, ""), "b"); ck_assert_str_eq(fy_get(sliced, 1, ""), "c"); ck_assert_str_eq(fy_get(sliced, 2, ""), "d"); printf("> Slice [1:4]: "); fy_generic_emit_default(sliced); /* Test SLICE: first 3 elements [0:3] -> ["a", "b", "c"] */ memset(&args, 0, sizeof(args)); args.slice.start = 0; args.slice.end = 3; sliced = fy_generic_op_args(gb, FYGBOPF_SLICE, seq, &args); ck_assert(fy_generic_is_sequence(sliced)); ck_assert(fy_len(sliced) == 3); ck_assert_str_eq(fy_get(sliced, 0, ""), "a"); ck_assert_str_eq(fy_get(sliced, 1, ""), "b"); ck_assert_str_eq(fy_get(sliced, 2, ""), "c"); printf("> Slice [0:3]: "); fy_generic_emit_default(sliced); /* Test SLICE: from index 2 to end [2:SIZE_MAX] -> ["c", "d", "e"] */ memset(&args, 0, sizeof(args)); args.slice.start = 2; args.slice.end = SIZE_MAX; sliced = fy_generic_op_args(gb, FYGBOPF_SLICE, seq, &args); ck_assert(fy_generic_is_sequence(sliced)); ck_assert(fy_len(sliced) == 3); ck_assert_str_eq(fy_get(sliced, 0, ""), "c"); ck_assert_str_eq(fy_get(sliced, 1, ""), "d"); ck_assert_str_eq(fy_get(sliced, 2, ""), "e"); printf("> Slice [2:end]: "); fy_generic_emit_default(sliced); /* Test SLICE: empty slice [2:2] -> [] */ memset(&args, 0, sizeof(args)); args.slice.start = 2; args.slice.end = 2; sliced = fy_generic_op_args(gb, FYGBOPF_SLICE, seq, &args); ck_assert(fy_generic_is_sequence(sliced)); ck_assert(fy_len(sliced) == 0); printf("> Slice [2:2] (empty): "); fy_generic_emit_default(sliced); /* Test SLICE_PY: negative indices [-2:-1] -> ["d"] */ memset(&args, 0, sizeof(args)); args.slice_py.start = -2; args.slice_py.end = -1; sliced = fy_generic_op_args(gb, FYGBOPF_SLICE_PY, seq, &args); ck_assert(fy_generic_is_sequence(sliced)); ck_assert(fy_len(sliced) == 1); ck_assert_str_eq(fy_get(sliced, 0, ""), "d"); printf("> Slice_py [-2:-1]: "); fy_generic_emit_default(sliced); /* Test SLICE_PY: all but last [0:-1] -> ["a", "b", "c", "d"] */ memset(&args, 0, sizeof(args)); args.slice_py.start = 0; args.slice_py.end = -1; sliced = fy_generic_op_args(gb, FYGBOPF_SLICE_PY, seq, &args); ck_assert(fy_generic_is_sequence(sliced)); ck_assert(fy_len(sliced) == 4); ck_assert_str_eq(fy_get(sliced, 0, ""), "a"); ck_assert_str_eq(fy_get(sliced, 1, ""), "b"); ck_assert_str_eq(fy_get(sliced, 2, ""), "c"); ck_assert_str_eq(fy_get(sliced, 3, ""), "d"); printf("> Slice_py [0:-1]: "); fy_generic_emit_default(sliced); /* Test slice with integers */ seq = fy_sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); memset(&args, 0, sizeof(args)); args.slice.start = 3; args.slice.end = 7; sliced = fy_generic_op_args(gb, FYGBOPF_SLICE, seq, &args); ck_assert(fy_generic_is_sequence(sliced)); ck_assert(fy_len(sliced) == 4); ck_assert(fy_get(sliced, 0, -1) == 4); ck_assert(fy_get(sliced, 1, -1) == 5); ck_assert(fy_get(sliced, 2, -1) == 6); ck_assert(fy_get(sliced, 3, -1) == 7); printf("> Slice integers [3:7]: "); fy_generic_emit_default(sliced); printf("> All slice tests passed!\n"); } END_TEST START_TEST(take_drop_ops) { char buf[16384]; struct fy_generic_builder *gb; struct fy_generic_op_args args; fy_generic seq, result, elem; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* Create test sequence: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] */ seq = fy_sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); ck_assert(fy_generic_is_sequence(seq)); ck_assert(fy_len(seq) == 10); printf("> Original sequence: "); fy_generic_emit_default(seq); /* Test TAKE: first 5 elements -> [1, 2, 3, 4, 5] */ memset(&args, 0, sizeof(args)); args.take.n = 5; result = fy_generic_op_args(gb, FYGBOPF_TAKE, seq, &args); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 5); ck_assert(fy_get(result, 0, -1) == 1); ck_assert(fy_get(result, 1, -1) == 2); ck_assert(fy_get(result, 2, -1) == 3); ck_assert(fy_get(result, 3, -1) == 4); ck_assert(fy_get(result, 4, -1) == 5); printf("> Take 5: "); fy_generic_emit_default(result); /* Test TAKE: take 0 -> [] */ memset(&args, 0, sizeof(args)); args.take.n = 0; result = fy_generic_op_args(gb, FYGBOPF_TAKE, seq, &args); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 0); printf("> Take 0 (empty): "); fy_generic_emit_default(result); /* Test TAKE: take more than length -> all elements */ memset(&args, 0, sizeof(args)); args.take.n = 100; result = fy_generic_op_args(gb, FYGBOPF_TAKE, seq, &args); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 10); printf("> Take 100 (all): "); fy_generic_emit_default(result); /* Test DROP: skip first 3 elements -> [4, 5, 6, 7, 8, 9, 10] */ memset(&args, 0, sizeof(args)); args.drop.n = 3; result = fy_generic_op_args(gb, FYGBOPF_DROP, seq, &args); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 7); ck_assert(fy_get(result, 0, -1) == 4); ck_assert(fy_get(result, 1, -1) == 5); ck_assert(fy_get(result, 2, -1) == 6); ck_assert(fy_get(result, 3, -1) == 7); printf("> Drop 3: "); fy_generic_emit_default(result); /* Test DROP: drop 0 -> all elements */ memset(&args, 0, sizeof(args)); args.drop.n = 0; result = fy_generic_op_args(gb, FYGBOPF_DROP, seq, &args); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 10); printf("> Drop 0 (all): "); fy_generic_emit_default(result); /* Test DROP: drop more than length -> empty */ memset(&args, 0, sizeof(args)); args.drop.n = 100; result = fy_generic_op_args(gb, FYGBOPF_DROP, seq, &args); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 0); printf("> Drop 100 (empty): "); fy_generic_emit_default(result); /* Test FIRST: get first element -> 1 */ elem = fy_generic_op_args(gb, FYGBOPF_FIRST, seq, NULL); ck_assert(fy_generic_is_int(elem)); ck_assert(fy_cast(elem, -1) == 1); printf("> First: "); fy_generic_emit_default(elem); /* Test LAST: get last element -> 10 */ elem = fy_generic_op_args(gb, FYGBOPF_LAST, seq, NULL); ck_assert(fy_generic_is_int(elem)); ck_assert(fy_cast(elem, -1) == 10); printf("> Last: "); fy_generic_emit_default(elem); /* Test REST: all but first -> [2, 3, 4, 5, 6, 7, 8, 9, 10] */ result = fy_generic_op_args(gb, FYGBOPF_REST, seq, NULL); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 9); ck_assert(fy_get(result, 0, -1) == 2); ck_assert(fy_get(result, 1, -1) == 3); ck_assert(fy_get(result, 8, -1) == 10); printf("> Rest: "); fy_generic_emit_default(result); /* Test with strings */ seq = fy_sequence("alice", "bob", "charlie", "dave"); printf("> String sequence: "); fy_generic_emit_default(seq); /* TAKE with strings */ memset(&args, 0, sizeof(args)); args.take.n = 2; result = fy_generic_op_args(gb, FYGBOPF_TAKE, seq, &args); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 2); ck_assert_str_eq(fy_get(result, 0, ""), "alice"); ck_assert_str_eq(fy_get(result, 1, ""), "bob"); printf("> Take 2: "); fy_generic_emit_default(result); /* DROP with strings */ memset(&args, 0, sizeof(args)); args.drop.n = 1; result = fy_generic_op_args(gb, FYGBOPF_DROP, seq, &args); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert_str_eq(fy_get(result, 0, ""), "bob"); ck_assert_str_eq(fy_get(result, 1, ""), "charlie"); ck_assert_str_eq(fy_get(result, 2, ""), "dave"); printf("> Drop 1: "); fy_generic_emit_default(result); /* FIRST with strings */ elem = fy_generic_op_args(gb, FYGBOPF_FIRST, seq, NULL); ck_assert(fy_generic_is_string(elem)); ck_assert_str_eq(fy_cast(elem, ""), "alice"); printf("> First: "); fy_generic_emit_default(elem); /* LAST with strings */ elem = fy_generic_op_args(gb, FYGBOPF_LAST, seq, NULL); ck_assert(fy_generic_is_string(elem)); ck_assert_str_eq(fy_cast(elem, ""), "dave"); printf("> Last: "); fy_generic_emit_default(elem); /* REST with strings */ result = fy_generic_op_args(gb, FYGBOPF_REST, seq, NULL); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert_str_eq(fy_get(result, 0, ""), "bob"); ck_assert_str_eq(fy_get(result, 1, ""), "charlie"); ck_assert_str_eq(fy_get(result, 2, ""), "dave"); printf("> Rest: "); fy_generic_emit_default(result); /* Test edge cases: empty sequence */ seq = fy_sequence(); ck_assert(fy_len(seq) == 0); /* FIRST on empty -> invalid */ elem = fy_generic_op_args(gb, FYGBOPF_FIRST, seq, NULL); ck_assert(fy_generic_is_invalid(elem)); printf("> First on empty: invalid (expected)\n"); /* LAST on empty -> invalid */ elem = fy_generic_op_args(gb, FYGBOPF_LAST, seq, NULL); ck_assert(fy_generic_is_invalid(elem)); printf("> Last on empty: invalid (expected)\n"); /* REST on empty -> empty */ result = fy_generic_op_args(gb, FYGBOPF_REST, seq, NULL); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 0); printf("> Rest on empty: empty sequence (expected)\n"); /* Test edge cases: single element */ seq = fy_sequence("only"); /* FIRST on single */ elem = fy_generic_op_args(gb, FYGBOPF_FIRST, seq, NULL); ck_assert(fy_generic_is_string(elem)); ck_assert_str_eq(fy_cast(elem, ""), "only"); printf("> First on single: "); fy_generic_emit_default(elem); /* LAST on single */ elem = fy_generic_op_args(gb, FYGBOPF_LAST, seq, NULL); ck_assert(fy_generic_is_string(elem)); ck_assert_str_eq(fy_cast(elem, ""), "only"); printf("> Last on single: "); fy_generic_emit_default(elem); /* REST on single -> empty */ result = fy_generic_op_args(gb, FYGBOPF_REST, seq, NULL); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 0); printf("> Rest on single: empty sequence\n"); printf("> All take/drop/first/last/rest tests passed!\n"); } END_TEST START_TEST(polymorphic_slice_ops) { char buf[16384]; struct fy_generic_builder *gb; fy_generic seq, result, elem; printf("\n> Testing polymorphic wrapper macros for slice operations\n"); gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* Create test sequence */ seq = fy_sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); printf("> Test sequence: "); fy_generic_emit_default(seq); /* Test fy_slice() - local allocation */ result = fy_slice(seq, 2, 5); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert(fy_get(result, 0, -1) == 3); ck_assert(fy_get(result, 1, -1) == 4); ck_assert(fy_get(result, 2, -1) == 5); printf("> fy_slice(seq, 2, 5) local: "); fy_generic_emit_default(result); /* Test fy_slice() - builder allocation */ result = fy_slice(gb, seq, 2, 5); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert(fy_get(result, 0, -1) == 3); printf("> fy_slice(gb, seq, 2, 5) builder: "); fy_generic_emit_default(result); /* Test fy_slice_py() - local allocation with negative indices */ result = fy_slice_py(seq, -5, -2); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert(fy_get(result, 0, -1) == 6); ck_assert(fy_get(result, 1, -1) == 7); ck_assert(fy_get(result, 2, -1) == 8); printf("> fy_slice_py(seq, -5, -2) local: "); fy_generic_emit_default(result); /* Test fy_slice_py() - builder allocation */ result = fy_slice_py(gb, seq, -5, -2); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert(fy_get(result, 0, -1) == 6); printf("> fy_slice_py(gb, seq, -5, -2) builder: "); fy_generic_emit_default(result); /* Test fy_take() - local allocation */ result = fy_take(seq, 3); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert(fy_get(result, 0, -1) == 1); ck_assert(fy_get(result, 1, -1) == 2); ck_assert(fy_get(result, 2, -1) == 3); printf("> fy_take(seq, 3) local: "); fy_generic_emit_default(result); /* Test fy_take() - builder allocation */ result = fy_take(gb, seq, 3); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert(fy_get(result, 0, -1) == 1); printf("> fy_take(gb, seq, 3) builder: "); fy_generic_emit_default(result); /* Test fy_drop() - local allocation */ result = fy_drop(seq, 7); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert(fy_get(result, 0, -1) == 8); ck_assert(fy_get(result, 1, -1) == 9); ck_assert(fy_get(result, 2, -1) == 10); printf("> fy_drop(seq, 7) local: "); fy_generic_emit_default(result); /* Test fy_drop() - builder allocation */ result = fy_drop(gb, seq, 7); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert(fy_get(result, 0, -1) == 8); printf("> fy_drop(gb, seq, 7) builder: "); fy_generic_emit_default(result); /* Test fy_first() - local allocation */ elem = fy_first(seq); ck_assert(fy_generic_is_int(elem)); ck_assert(fy_cast(elem, -1) == 1); printf("> fy_first(seq) local: "); fy_generic_emit_default(elem); /* Test fy_first() - builder allocation */ elem = fy_first(gb, seq); ck_assert(fy_generic_is_int(elem)); ck_assert(fy_cast(elem, -1) == 1); printf("> fy_first(gb, seq) builder: "); fy_generic_emit_default(elem); /* Test fy_last() - local allocation */ elem = fy_last(seq); ck_assert(fy_generic_is_int(elem)); ck_assert(fy_cast(elem, -1) == 10); printf("> fy_last(seq) local: "); fy_generic_emit_default(elem); /* Test fy_last() - builder allocation */ elem = fy_last(gb, seq); ck_assert(fy_generic_is_int(elem)); ck_assert(fy_cast(elem, -1) == 10); printf("> fy_last(gb, seq) builder: "); fy_generic_emit_default(elem); /* Test fy_rest() - local allocation */ result = fy_rest(seq); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 9); ck_assert(fy_get(result, 0, -1) == 2); ck_assert(fy_get(result, 8, -1) == 10); printf("> fy_rest(seq) local: "); fy_generic_emit_default(result); /* Test fy_rest() - builder allocation */ result = fy_rest(gb, seq); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 9); ck_assert(fy_get(result, 0, -1) == 2); printf("> fy_rest(gb, seq) builder: "); fy_generic_emit_default(result); /* Test with strings */ seq = fy_sequence("alpha", "beta", "gamma", "delta"); printf("> String sequence: "); fy_generic_emit_default(seq); /* fy_take with strings - local */ result = fy_take(seq, 2); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 2); ck_assert_str_eq(fy_get(result, 0, ""), "alpha"); ck_assert_str_eq(fy_get(result, 1, ""), "beta"); printf("> fy_take(seq, 2) strings local: "); fy_generic_emit_default(result); /* fy_drop with strings - builder */ result = fy_drop(gb, seq, 1); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 3); ck_assert_str_eq(fy_get(result, 0, ""), "beta"); printf("> fy_drop(gb, seq, 1) strings builder: "); fy_generic_emit_default(result); /* fy_first with strings - local */ elem = fy_first(seq); ck_assert(fy_generic_is_string(elem)); ck_assert_str_eq(fy_cast(elem, ""), "alpha"); printf("> fy_first(seq) strings local: "); fy_generic_emit_default(elem); /* fy_last with strings - builder */ elem = fy_last(gb, seq); ck_assert(fy_generic_is_string(elem)); ck_assert_str_eq(fy_cast(elem, ""), "delta"); printf("> fy_last(gb, seq) strings builder: "); fy_generic_emit_default(elem); /* Edge cases */ seq = fy_sequence(); /* empty */ elem = fy_first(seq); ck_assert(fy_generic_is_invalid(elem)); printf("> fy_first on empty: invalid (expected)\n"); elem = fy_last(gb, seq); ck_assert(fy_generic_is_invalid(elem)); printf("> fy_last on empty: invalid (expected)\n"); result = fy_rest(seq); ck_assert(fy_generic_is_sequence(result)); ck_assert(fy_len(result) == 0); printf("> fy_rest on empty: empty sequence (expected)\n"); /* Single element */ seq = fy_sequence(42); elem = fy_first(seq); ck_assert(fy_cast(elem, -1) == 42); printf("> fy_first on single element: "); fy_generic_emit_default(elem); result = fy_rest(gb, seq); ck_assert(fy_len(result) == 0); printf("> fy_rest on single: empty sequence\n"); printf("> All polymorphic wrapper macro tests passed!\n"); } END_TEST /* Test: Type conversion to int */ START_TEST(conversion_to_int) { char buf[16384]; struct fy_generic_builder *gb; fy_generic v, result; printf("\n> Testing fy_gb_to_int() conversions\n"); gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* INT → INT (identity) */ v = fy_to_generic(42); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_int(result)); ck_assert(fy_cast(result, 0) == 42); printf("> INT 42 → INT 42: "); fy_generic_emit_default(result); /* BOOL → INT (true → 1, false → 0) */ v = fy_to_generic((_Bool)true); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_int(result)); ck_assert(fy_cast(result, 0) == 1); printf("> BOOL true → INT 1: "); fy_generic_emit_default(result); v = fy_to_generic((_Bool)false); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_int(result)); ck_assert(fy_cast(result, -1) == 0); printf("> BOOL false → INT 0: "); fy_generic_emit_default(result); /* FLOAT → INT (truncate toward zero) */ v = fy_to_generic(3.7); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_int(result)); ck_assert(fy_cast(result, 0) == 3); printf("> FLOAT 3.7 → INT 3: "); fy_generic_emit_default(result); v = fy_to_generic(-2.9); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_int(result)); ck_assert(fy_cast(result, 0) == -2); printf("> FLOAT -2.9 → INT -2: "); fy_generic_emit_default(result); /* STRING → INT (parse base-10) */ v = fy_gb_string_create(gb, "12345"); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_int(result)); ck_assert(fy_cast(result, 0) == 12345); printf("> STRING \"12345\" → INT 12345: "); fy_generic_emit_default(result); v = fy_gb_string_create(gb, "-9876"); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_int(result)); ck_assert(fy_cast(result, 0) == -9876); printf("> STRING \"-9876\" → INT -9876: "); fy_generic_emit_default(result); /* STRING → INT (invalid - should return fy_invalid) */ v = fy_gb_string_create(gb, "not a number"); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> STRING \"not a number\" → INVALID (expected)\n"); v = fy_gb_string_create(gb, ""); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> STRING \"\" → INVALID (expected)\n"); /* FLOAT NaN/Infinity → INT (error) */ v = fy_to_generic(0.0 / 0.0); /* NaN */ result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> FLOAT NaN → INVALID (expected)\n"); /* NULL → INT (error) */ v = fy_null; result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> NULL → INVALID (expected)\n"); /* SEQUENCE → INT (error) */ v = fy_sequence(1, 2, 3); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> SEQUENCE → INVALID (expected)\n"); /* MAPPING → INT (error) */ v = fy_mapping("key", "value"); result = fy_gb_to_int(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> MAPPING → INVALID (expected)\n"); printf("> All fy_gb_to_int() tests passed!\n"); } END_TEST /* Test: Type conversion to float */ START_TEST(conversion_to_float) { char buf[16384]; struct fy_generic_builder *gb; fy_generic v, result; double dv; printf("\n> Testing fy_gb_to_float() conversions\n"); gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* FLOAT → FLOAT (identity) */ v = fy_to_generic(3.14159); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_float(result)); dv = fy_cast(result, 0.0); ck_assert(dv > 3.14158 && dv < 3.14160); printf("> FLOAT 3.14159 → FLOAT 3.14159: "); fy_generic_emit_default(result); /* INT → FLOAT */ v = fy_to_generic(42); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_float(result)); ck_assert(fy_cast(result, 0.0) == 42.0); printf("> INT 42 → FLOAT 42.0: "); fy_generic_emit_default(result); /* BOOL → FLOAT (true → 1.0, false → 0.0) */ v = fy_to_generic((_Bool)true); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_float(result)); ck_assert(fy_cast(result, -1.0) == 1.0); printf("> BOOL true → FLOAT 1.0: "); fy_generic_emit_default(result); v = fy_to_generic((_Bool)false); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_float(result)); ck_assert(fy_cast(result, -1.0) == 0.0); printf("> BOOL false → FLOAT 0.0: "); fy_generic_emit_default(result); /* STRING → FLOAT */ v = fy_gb_string_create(gb, "2.71828"); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_float(result)); dv = fy_cast(result, 0.0); ck_assert(dv > 2.71827 && dv < 2.71829); printf("> STRING \"2.71828\" → FLOAT 2.71828: "); fy_generic_emit_default(result); v = fy_gb_string_create(gb, "-123.456"); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_float(result)); dv = fy_cast(result, 0.0); ck_assert(dv > -123.457 && dv < -123.455); printf("> STRING \"-123.456\" → FLOAT -123.456: "); fy_generic_emit_default(result); /* STRING → FLOAT (invalid) */ v = fy_gb_string_create(gb, "not a float"); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> STRING \"not a float\" → INVALID (expected)\n"); /* NULL → FLOAT (error) */ v = fy_null; result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> NULL → INVALID (expected)\n"); /* SEQUENCE → FLOAT (error) */ v = fy_sequence(1.0, 2.0); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> SEQUENCE → INVALID (expected)\n"); /* MAPPING → FLOAT (error) */ v = fy_mapping("pi", 3.14); result = fy_gb_to_float(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> MAPPING → INVALID (expected)\n"); printf("> All fy_gb_to_float() tests passed!\n"); } END_TEST /* Test: Type conversion to string (schema-aware) */ START_TEST(conversion_to_string) { char buf[16384]; struct fy_generic_builder *gb; fy_generic v, result; const char *str; printf("\n> Testing fy_gb_to_string() conversions\n"); gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* STRING → STRING (identity) */ v = fy_gb_string_create(gb, "hello"); result = fy_gb_to_string(gb, v); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "hello"); printf("> STRING \"hello\" → STRING \"hello\": "); fy_generic_emit_default(result); /* INT → STRING */ v = fy_to_generic(42); result = fy_gb_to_string(gb, v); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "42"); printf("> INT 42 → STRING \"42\": "); fy_generic_emit_default(result); v = fy_to_generic(-123); result = fy_gb_to_string(gb, v); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "-123"); printf("> INT -123 → STRING \"-123\": "); fy_generic_emit_default(result); /* FLOAT → STRING */ v = fy_to_generic(3.14); result = fy_gb_to_string(gb, v); ck_assert(fy_generic_is_string(result)); str = fy_cast(result, ""); printf("> FLOAT 3.14 → STRING \"%s\": ", str); fy_generic_emit_default(result); /* BOOL → STRING (YAML schema: lowercase) */ v = fy_to_generic((_Bool)true); result = fy_gb_to_string(gb, v); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "true"); printf("> BOOL true → STRING \"true\" (YAML): "); fy_generic_emit_default(result); v = fy_to_generic((_Bool)false); result = fy_gb_to_string(gb, v); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "false"); printf("> BOOL false → STRING \"false\" (YAML): "); fy_generic_emit_default(result); /* NULL → STRING (YAML schema: "null") */ v = fy_null; result = fy_gb_to_string(gb, v); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "null"); printf("> NULL → STRING \"null\" (YAML): "); fy_generic_emit_default(result); /* Test with PYTHON schema - use separate buffer */ { char buf2[16384]; struct fy_generic_builder *gb2; gb2 = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_PYTHON | FYGBCF_SCOPE_LEADER, NULL, buf2, sizeof(buf2)); ck_assert_ptr_ne(gb2, NULL); enum fy_generic_schema schema = fy_gb_get_schema(gb2); ck_assert(schema == FYGS_PYTHON); /* BOOL → STRING (PYTHON schema: capitalized) */ v = fy_to_generic((_Bool)true); result = fy_gb_to_string(gb2, v); ck_assert(fy_generic_is_string(result)); str = fy_cast(result, ""); ck_assert_str_eq(str, "True"); printf("> BOOL true → STRING \"True\" (PYTHON): "); fy_generic_emit_default(result); v = fy_to_generic((_Bool)false); result = fy_gb_to_string(gb2, v); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "False"); printf("> BOOL false → STRING \"False\" (PYTHON): "); fy_generic_emit_default(result); /* NULL → STRING (PYTHON schema: "None") */ v = fy_null; result = fy_gb_to_string(gb2, v); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "None"); printf("> NULL → STRING \"None\" (PYTHON): "); fy_generic_emit_default(result); /* SEQUENCE → STRING (flow format) */ v = fy_sequence(1, 2, 3); result = fy_gb_to_string(gb2, v); ck_assert(fy_generic_is_string(result)); str = fy_cast(result, ""); printf("> SEQUENCE [1,2,3] → STRING \"%s\": ", str); fy_generic_emit_default(result); /* MAPPING → STRING (flow format) */ v = fy_mapping("a", 1, "b", 2); result = fy_gb_to_string(gb2, v); ck_assert(fy_generic_is_string(result)); str = fy_cast(result, ""); printf("> MAPPING {a:1,b:2} → STRING \"%s\": ", str); fy_generic_emit_default(result); } printf("> All fy_gb_to_string() tests passed!\n"); } END_TEST /* Test: Type conversion to bool (schema-aware) */ START_TEST(conversion_to_bool) { char buf[16384]; struct fy_generic_builder *gb; fy_generic v, result; _Bool bv; printf("\n> Testing fy_gb_to_bool() conversions\n"); /* YAML schema tests */ gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_YAML1_2_CORE | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert_ptr_ne(gb, NULL); /* BOOL → BOOL (identity) */ v = fy_to_generic((_Bool)true); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> BOOL true → BOOL true: "); fy_generic_emit_default(result); v = fy_to_generic((_Bool)false); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)true) == (_Bool)false); printf("> BOOL false → BOOL false: "); fy_generic_emit_default(result); /* INT → BOOL (0 → false, non-zero → true) */ v = fy_to_generic(0); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)true) == (_Bool)false); printf("> INT 0 → BOOL false: "); fy_generic_emit_default(result); v = fy_to_generic(42); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> INT 42 → BOOL true: "); fy_generic_emit_default(result); v = fy_to_generic(-1); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> INT -1 → BOOL true: "); fy_generic_emit_default(result); /* FLOAT → BOOL (0.0 → false, non-zero → true) */ v = fy_to_generic(0.0); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)true) == (_Bool)false); printf("> FLOAT 0.0 → BOOL false: "); fy_generic_emit_default(result); v = fy_to_generic(3.14); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> FLOAT 3.14 → BOOL true: "); fy_generic_emit_default(result); /* STRING → BOOL (YAML 1.2 Core parsing) */ v = fy_gb_string_create(gb, "true"); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> STRING \"true\" → BOOL true (YAML): "); fy_generic_emit_default(result); v = fy_gb_string_create(gb, "False"); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)true) == (_Bool)false); printf("> STRING \"False\" → BOOL false (YAML): "); fy_generic_emit_default(result); v = fy_gb_string_create(gb, "TRUE"); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> STRING \"TRUE\" → BOOL true (YAML): "); fy_generic_emit_default(result); /* STRING → BOOL (invalid for YAML) */ v = fy_gb_string_create(gb, "not a bool"); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> STRING \"not a bool\" → INVALID (YAML, expected)\n"); /* NULL → BOOL (error for YAML) */ v = fy_null; result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> NULL → INVALID (YAML, expected)\n"); /* SEQUENCE → BOOL (error for YAML) */ v = fy_sequence(1, 2); result = fy_gb_to_bool(gb, v); ck_assert(fy_generic_is_invalid(result)); printf("> SEQUENCE → INVALID (YAML, expected)\n"); /* Test with PYTHON schema (truthiness) - use separate buffer */ { char buf2[16384]; struct fy_generic_builder *gb2; gb2 = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_PYTHON | FYGBCF_SCOPE_LEADER, NULL, buf2, sizeof(buf2)); ck_assert_ptr_ne(gb2, NULL); /* STRING → BOOL (Python: empty → False, non-empty → True) */ v = fy_gb_string_create(gb2, ""); result = fy_gb_to_bool(gb2, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)true) == (_Bool)false); printf("> STRING \"\" → BOOL false (PYTHON): "); fy_generic_emit_default(result); v = fy_gb_string_create(gb2, "hello"); result = fy_gb_to_bool(gb2, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> STRING \"hello\" → BOOL true (PYTHON): "); fy_generic_emit_default(result); /* Note: even "False" as a string is True in Python! */ v = fy_gb_string_create(gb2, "False"); result = fy_gb_to_bool(gb2, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> STRING \"False\" → BOOL true (PYTHON truthiness): "); fy_generic_emit_default(result); /* NULL → BOOL (Python: False) */ v = fy_null; result = fy_gb_to_bool(gb2, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)true) == (_Bool)false); printf("> NULL → BOOL false (PYTHON): "); fy_generic_emit_default(result); /* SEQUENCE → BOOL (Python: empty → False, non-empty → True) */ v = fy_sequence(); result = fy_gb_to_bool(gb2, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)true) == (_Bool)false); printf("> SEQUENCE [] → BOOL false (PYTHON): "); fy_generic_emit_default(result); v = fy_sequence(1, 2, 3); result = fy_gb_to_bool(gb2, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> SEQUENCE [1,2,3] → BOOL true (PYTHON): "); fy_generic_emit_default(result); /* MAPPING → BOOL (Python: empty → False, non-empty → True) */ v = fy_mapping(); result = fy_gb_to_bool(gb2, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)true) == (_Bool)false); printf("> MAPPING {} → BOOL false (PYTHON): "); fy_generic_emit_default(result); v = fy_mapping("key", "value"); result = fy_gb_to_bool(gb2, v); ck_assert(fy_generic_is_bool(result)); ck_assert(fy_cast(result, (_Bool)false) == (_Bool)true); printf("> MAPPING {key:value} → BOOL true (PYTHON): "); fy_generic_emit_default(result); } printf("> All fy_gb_to_bool() tests passed!\n"); } END_TEST /* Test: convert op */ START_TEST(convert_op) { fy_generic v, result; const char *str; printf("\n> Testing convert op\n"); /* INT → STRING */ v = fy_to_generic(42); result = fy_convert(v, FYGT_STRING); ck_assert(fy_generic_is_string(result)); ck_assert_str_eq(fy_cast(result, ""), "42"); printf("> convert 42 → STRING \"42\": "); fy_generic_emit_default(result); /* SEQUENCE → STRING (flow format) */ v = fy_sequence(1, 2, 3); result = fy_convert(v, FYGT_STRING); ck_assert(fy_generic_is_string(result)); str = fy_cast(result, ""); printf("> convert SEQUENCE [1,2,3] → STRING \"%s\": ", str); fy_generic_emit_default(result); printf("> All convert op tests passed!\n"); } END_TEST /* Test: Generic iterator functionality */ START_TEST(generic_iterator) { char buf[16384]; struct fy_generic_builder *gb; struct fy_generic_iterator *fygi; fy_generic vroot, vdir, v; int count; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); vdir = fy_parse(gb, "root:\n" " scalar: value\n" " seq:\n" " - item1\n" " - item2\n" " map:\n" " key: val\n", FYOPPF_INPUT_TYPE_STRING, NULL); ck_assert(fy_generic_is_valid(vdir)); fy_generic_emit_default(vdir); vroot = fy_get(vdir, "root", fy_invalid); ck_assert(fy_generic_is_valid(vroot)); fy_generic_emit_default(vroot); /* Create iterator */ fygi = fy_generic_iterator_create(); ck_assert_ptr_ne(fygi, NULL); /* Start iteration from root */ fy_generic_iterator_generic_start(fygi, vroot); /* Count all nodes */ count = 0; while (fy_generic_is_valid(v = fy_generic_iterator_generic_next(fygi))) { printf("[%d]: ", count); fy_generic_emit_default(v); count++; } /* We should have iterated through multiple nodes */ ck_assert_int_eq(count, 13); /* Cleanup */ fy_generic_iterator_destroy(fygi); } END_TEST /* Test: Generic iterator functionality */ START_TEST(generic_iterator_events) { char buf[16384]; struct fy_generic_builder *gb; struct fy_generic_iterator_cfg cfg; struct fy_generic_iterator *fygi; fy_generic vdir; struct fy_event *fye; char *evtxt; int count; gb = fy_generic_builder_create_in_place(FYGBCF_SCHEMA_AUTO | FYGBCF_SCOPE_LEADER, NULL, buf, sizeof(buf)); ck_assert(gb != NULL); vdir = fy_parse(gb, "root:\n" " scalar: value\n" " seq:\n" " - item1\n" " - item2\n" " map:\n" " key: val\n", FYOPPF_INPUT_TYPE_STRING, NULL); ck_assert(fy_generic_is_valid(vdir)); fy_generic_emit_default(vdir); /* Create iterator */ memset(&cfg, 0, sizeof(cfg)); cfg.flags = FYGICF_WANT_STREAM_DOCUMENT_BODY_EVENTS; cfg.vdir = vdir; fygi = fy_generic_iterator_create_cfg(&cfg); ck_assert_ptr_ne(fygi, NULL); count = 0; while ((fye = fy_generic_iterator_generate_next(fygi)) != NULL) { evtxt = fy_event_to_string(fye); ck_assert_ptr_ne(evtxt, NULL); printf("%s\n", evtxt); free(evtxt); fy_generic_iterator_event_free(fygi, fye); count++; } /* We should have iterated through multiple nodes */ ck_assert_int_eq(count, 21); /* Cleanup */ fy_generic_iterator_destroy(fygi); } END_TEST void libfyaml_case_generic(struct fy_check_suite *cs) { struct fy_check_testcase *ctc; ctc = fy_check_suite_add_test_case(cs, "generic"); /* baby steps first */ fy_check_testcase_add_test(ctc, generic_basics); fy_check_testcase_add_test(ctc, generic_bool_range); fy_check_testcase_add_test(ctc, generic_int_range); fy_check_testcase_add_test(ctc, generic_float_range); fy_check_testcase_add_test(ctc, generic_size_encoding); fy_check_testcase_add_test(ctc, generic_string_range); fy_check_testcase_add_test(ctc, generic_type_promotion); /* invalid propagation tests */ fy_check_testcase_add_test(ctc, generic_invalid_propagation); /* sized string (any kind of data including zeroes) */ fy_check_testcase_add_test(ctc, generic_sized_string); /* decorated int */ fy_check_testcase_add_test(ctc, generic_decorated_int); /* casts */ fy_check_testcase_add_test(ctc, generic_casts); /* get */ fy_check_testcase_add_test(ctc, generic_get); /* get_at testing */ fy_check_testcase_add_test(ctc, generic_get_at); /* compare */ fy_check_testcase_add_test(ctc, generic_compare); /* builders */ fy_check_testcase_add_test(ctc, gb_basics); /* special for is_unsigned */ fy_check_testcase_add_test(ctc, generic_is_unsigned); /* continue with builders */ fy_check_testcase_add_test(ctc, gb_dedup_basics); fy_check_testcase_add_test(ctc, gb_scoping); fy_check_testcase_add_test(ctc, gb_dedup_scoping); fy_check_testcase_add_test(ctc, gb_dedup_scoping2); /* the polymorphic stuff */ fy_check_testcase_add_test(ctc, gb_polymorphics); /* time to do operations on collection */ fy_check_testcase_add_test(ctc, gb_basic_collection_ops); fy_check_testcase_add_test(ctc, gb_assoc_deassoc); /* map utils */ fy_check_testcase_add_test(ctc, gb_map_utils); /* seq utils */ fy_check_testcase_add_test(ctc, gb_seq_utils); /* filter/map/reduce */ fy_check_testcase_add_test(ctc, gb_filter); fy_check_testcase_add_test(ctc, gb_map); fy_check_testcase_add_test(ctc, gb_reduce); /* parallel filter/map/reduce */ fy_check_testcase_add_test(ctc, gb_pfilter); fy_check_testcase_add_test(ctc, gb_pmap); fy_check_testcase_add_test(ctc, gb_preduce); /* now time to do collection operations using the simple intefaces */ fy_check_testcase_add_test(ctc, simple_ops); /* iterator test */ fy_check_testcase_add_test(ctc, iterators); /* local operations */ fy_check_testcase_add_test(ctc, local_ops); /* local ops retry mechanism */ fy_check_testcase_add_test(ctc, local_ops_retry); /* unified operations */ fy_check_testcase_add_test(ctc, unified_ops); #ifdef FY_HAVE_LAMBDAS /* lambda operations */ fy_check_testcase_add_test(ctc, lambda_ops); #endif /* get_at_path macros */ fy_check_testcase_add_test(ctc, get_at_path); /* set ops */ fy_check_testcase_add_test(ctc, set_ops); /* parse and emit operations */ fy_check_testcase_add_test(ctc, parse_emit_ops); fy_check_testcase_add_test(ctc, generic_failsafe_schema_preserves_empty_scalars); fy_check_testcase_add_test(ctc, generic_resolved_parse_drops_anchors_encoder); fy_check_testcase_add_test(ctc, generic_emit_preserves_document_markers_encoder); fy_check_testcase_add_test(ctc, generic_emit_preserves_tag_directive_spelling_encoder); fy_check_testcase_add_test(ctc, generic_document_builder_pull_mode); fy_check_testcase_add_test(ctc, generic_document_builder_push_mode_metadata); fy_check_testcase_add_test(ctc, generic_document_builder_directory_mode); /* slice operations */ fy_check_testcase_add_test(ctc, slice_ops); /* take/drop/first/last/rest operations */ fy_check_testcase_add_test(ctc, take_drop_ops); /* polymorphic wrapper macros for slice operations */ fy_check_testcase_add_test(ctc, polymorphic_slice_ops); /* type conversion operations */ fy_check_testcase_add_test(ctc, conversion_to_int); fy_check_testcase_add_test(ctc, conversion_to_float); fy_check_testcase_add_test(ctc, conversion_to_string); fy_check_testcase_add_test(ctc, conversion_to_bool); fy_check_testcase_add_test(ctc, convert_op); /* generic iterator */ fy_check_testcase_add_test(ctc, generic_iterator); fy_check_testcase_add_test(ctc, generic_iterator_events); } FY_DIAG_POP