1 /* 2 * test-serialize.c 3 * Tests for spdxtool's JSON serialization value model. 4 * 5 * SPDX-License-Identifier: pkgconf 6 * 7 * Copyright (c) 2026 pkgconf authors (see AUTHORS). 8 * 9 * Permission to use, copy, modify, and/or distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * This software is provided 'as is' and without any warranty, express or 14 * implied. In no event shall the authors be liable for any damages arising 15 * from the use of this software. 16 */ 17 18 #include <libpkgconf/stdinc.h> 19 #include <libpkgconf/libpkgconf.h> 20 #include "test-api.h" 21 #include "serialize.h" 22 23 // Render a value to a freshly-allocated C string (caller frees) 24 static char * 25 render(spdxtool_serialize_value_t *value) 26 { 27 pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER; 28 TEST_ASSERT_TRUE(spdxtool_serialize_value_to_buf(&buf, value, 0)); 29 char *s = strdup(pkgconf_buffer_str_or_empty(&buf)); 30 pkgconf_buffer_finalize(&buf); 31 return s; 32 } 33 34 // Render a container without taking ownership of it 35 static char * 36 render_object(spdxtool_serialize_object_list_t *o) 37 { 38 spdxtool_serialize_value_t wrap = { .type = SPDXTOOL_SERIALIZE_TYPE_OBJECT, .value = { .o = o } }; 39 return render(&wrap); 40 } 41 42 static char * 43 render_array(spdxtool_serialize_array_t *a) 44 { 45 spdxtool_serialize_value_t wrap = { .type = SPDXTOOL_SERIALIZE_TYPE_ARRAY, .value = { .a = a } }; 46 return render(&wrap); 47 } 48 49 // Scalar value types: string / int / bool / null 50 static void 51 test_serialize_value_string(void) 52 { 53 spdxtool_serialize_value_t *v = spdxtool_serialize_value_string("hi"); 54 TEST_ASSERT_NONNULL(v); 55 char *s = render(v); 56 TEST_ASSERT_STRCMP_EQ(s, "\"hi\""); 57 free(s); 58 spdxtool_serialize_value_free(v); 59 } 60 61 static void 62 test_serialize_value_int(void) 63 { 64 spdxtool_serialize_value_t *v = spdxtool_serialize_value_int(123); 65 TEST_ASSERT_NONNULL(v); 66 char *s = render(v); 67 TEST_ASSERT_STRCMP_EQ(s, "123"); 68 free(s); 69 spdxtool_serialize_value_free(v); 70 } 71 72 static void 73 test_serialize_value_bool(void) 74 { 75 spdxtool_serialize_value_t *t = spdxtool_serialize_value_bool(true); 76 spdxtool_serialize_value_t *f = spdxtool_serialize_value_bool(false); 77 char *st = render(t); 78 char *sf = render(f); 79 TEST_ASSERT_STRCMP_EQ(st, "true"); 80 TEST_ASSERT_STRCMP_EQ(sf, "false"); 81 free(st); 82 free(sf); 83 spdxtool_serialize_value_free(t); 84 spdxtool_serialize_value_free(f); 85 } 86 87 static void 88 test_serialize_value_null(void) 89 { 90 spdxtool_serialize_value_t *v = spdxtool_serialize_value_null(); 91 TEST_ASSERT_NONNULL(v); 92 char *s = render(v); 93 TEST_ASSERT_STRCMP_EQ(s, "null"); 94 free(s); 95 spdxtool_serialize_value_free(v); 96 } 97 98 // All JSON string escape sequences, including control characters 99 static void 100 test_serialize_escape_sequences(void) 101 { 102 spdxtool_serialize_value_t *v = spdxtool_serialize_value_string("\"\\\b\f\n\r\t\x01" "Z"); 103 char *s = render(v); 104 105 TEST_ASSERT_STRSTR(s, "\\\""); // quote 106 TEST_ASSERT_STRSTR(s, "\\\\"); // backslash 107 TEST_ASSERT_STRSTR(s, "\\b"); // backspace 108 TEST_ASSERT_STRSTR(s, "\\f"); // form feed 109 TEST_ASSERT_STRSTR(s, "\\n"); // newline 110 TEST_ASSERT_STRSTR(s, "\\r"); // carriage return 111 TEST_ASSERT_STRSTR(s, "\\t"); // tab 112 TEST_ASSERT_STRSTR(s, "\\u0001"); // other ctrl 113 TEST_ASSERT_STRSTR(s, "Z"); // printable passthrough 114 115 free(s); 116 spdxtool_serialize_value_free(v); 117 } 118 119 // Mixed-type object exercises the int/bool/null add helpers 120 static void 121 test_serialize_object_mixed_types(void) 122 { 123 spdxtool_serialize_object_list_t *o = spdxtool_serialize_object_list_new(); 124 TEST_ASSERT_NONNULL(o); 125 126 TEST_ASSERT_NONNULL(spdxtool_serialize_object_add_string(o, "s", "x")); 127 TEST_ASSERT_NONNULL(spdxtool_serialize_object_add_int(o, "i", 42)); 128 TEST_ASSERT_NONNULL(spdxtool_serialize_object_add_bool(o, "b", true)); 129 TEST_ASSERT_NONNULL(spdxtool_serialize_object_add_null(o, "n")); 130 131 char *s = render_object(o); 132 TEST_ASSERT_STRSTR(s, "\"s\": \"x\""); 133 TEST_ASSERT_STRSTR(s, "\"i\": 42"); 134 TEST_ASSERT_STRSTR(s, "\"b\": true"); 135 TEST_ASSERT_STRSTR(s, "\"n\": null"); 136 137 free(s); 138 spdxtool_serialize_object_list_free(o); 139 } 140 141 // Mixed-type array exercises the int/bool/null array helpers 142 static void 143 test_serialize_array_mixed_types(void) 144 { 145 spdxtool_serialize_array_t *a = spdxtool_serialize_array_new(); 146 TEST_ASSERT_NONNULL(a); 147 148 TEST_ASSERT_NONNULL(spdxtool_serialize_array_add_int(a, 7)); 149 TEST_ASSERT_NONNULL(spdxtool_serialize_array_add_bool(a, false)); 150 TEST_ASSERT_NONNULL(spdxtool_serialize_array_add_null(a)); 151 152 char *s = render_array(a); 153 TEST_ASSERT_STRSTR(s, "7"); 154 TEST_ASSERT_STRSTR(s, "false"); 155 TEST_ASSERT_STRSTR(s, "null"); 156 157 free(s); 158 spdxtool_serialize_array_free(a); 159 } 160 161 // Defensive NULL handling that the CLI path never reaches 162 static void 163 test_serialize_null_guards(void) 164 { 165 pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER; 166 spdxtool_serialize_value_t *v = spdxtool_serialize_value_null(); 167 168 // value_to_buf rejects NULL buffer or NULL value 169 TEST_ASSERT_FALSE(spdxtool_serialize_value_to_buf(NULL, v, 0)); 170 TEST_ASSERT_FALSE(spdxtool_serialize_value_to_buf(&buf, NULL, 0)); 171 pkgconf_buffer_finalize(&buf); 172 173 // value_string(NULL) yields NULL 174 TEST_ASSERT_NULL(spdxtool_serialize_value_string(NULL)); 175 176 // add_take with a NULL container frees the value and returns NULL 177 TEST_ASSERT_NULL(spdxtool_serialize_object_add_take(NULL, "k", v)); 178 TEST_ASSERT_NULL(spdxtool_serialize_array_add_take(NULL, spdxtool_serialize_value_null())); 179 180 // free routines are NULL-safe 181 spdxtool_serialize_value_free(NULL); 182 spdxtool_serialize_object_free(NULL); 183 spdxtool_serialize_object_list_free(NULL); 184 spdxtool_serialize_array_free(NULL); 185 } 186 187 int 188 main(int argc, const char **argv) 189 { 190 (void) argc; 191 const char *basename = pkgconf_path_find_basename(argv[0]); 192 193 TEST_RUN(basename, test_serialize_value_string); 194 TEST_RUN(basename, test_serialize_value_int); 195 TEST_RUN(basename, test_serialize_value_bool); 196 TEST_RUN(basename, test_serialize_value_null); 197 TEST_RUN(basename, test_serialize_escape_sequences); 198 TEST_RUN(basename, test_serialize_object_mixed_types); 199 TEST_RUN(basename, test_serialize_array_mixed_types); 200 TEST_RUN(basename, test_serialize_null_guards); 201 202 return EXIT_SUCCESS; 203 } 204