0000-basics.c (12495B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #include "assert.h" 6 #include "cnfparse.h" 7 8 static FILE *open_string(const char *text) { 9 return fmemopen((void *)text, strlen(text), "r"); 10 } 11 12 void test_empty_file() { 13 FILE *fd = open_string(""); 14 ASSERT("fmemopen succeeds", fd != NULL); 15 struct cnf_directive *dir = cnf_directive_read(fd); 16 ASSERT("empty file returns NULL", dir == NULL); 17 fclose(fd); 18 } 19 20 void test_blank_lines_only() { 21 FILE *fd = open_string("\n\n \t\r\n"); 22 ASSERT("fmemopen succeeds", fd != NULL); 23 struct cnf_directive *dir = cnf_directive_read(fd); 24 ASSERT("blank lines return NULL", dir == NULL); 25 fclose(fd); 26 } 27 28 void test_comment_only_line() { 29 FILE *fd = open_string("# this is a comment line\n"); 30 ASSERT("fmemopen succeeds", fd != NULL); 31 struct cnf_directive *dir = cnf_directive_read(fd); 32 ASSERT("comment-only line returns NULL", dir == NULL); 33 fclose(fd); 34 } 35 36 void test_single_word_no_args() { 37 FILE *fd = open_string("directive\n"); 38 ASSERT("fmemopen succeeds", fd != NULL); 39 struct cnf_directive *dir = cnf_directive_read(fd); 40 ASSERT("non-empty directive returned", dir != NULL); 41 ASSERT_STRING_EQUALS("directive", dir->name); 42 ASSERT_EQUALS(0, dir->argc); 43 cnf_directive_free(dir); 44 fclose(fd); 45 } 46 47 void test_leading_whitespace() { 48 FILE *fd = open_string(" \t leading_spaces\n"); 49 ASSERT("fmemopen succeeds", fd != NULL); 50 struct cnf_directive *dir = cnf_directive_read(fd); 51 ASSERT("directive with leading whitespace returned", dir != NULL); 52 ASSERT_STRING_EQUALS("leading_spaces", dir->name); 53 ASSERT_EQUALS(0, dir->argc); 54 cnf_directive_free(dir); 55 fclose(fd); 56 } 57 58 void test_trailing_whitespace() { 59 FILE *fd = open_string("trailing \t\n"); 60 ASSERT("fmemopen succeeds", fd != NULL); 61 struct cnf_directive *dir = cnf_directive_read(fd); 62 ASSERT("directive with trailing whitespace returned", dir != NULL); 63 ASSERT_STRING_EQUALS("trailing", dir->name); 64 ASSERT_EQUALS(0, dir->argc); 65 cnf_directive_free(dir); 66 fclose(fd); 67 } 68 69 void test_multiple_space_separated_args() { 70 FILE *fd = open_string("add 10.0.0.0/8 via 192.168.1.1\n"); 71 ASSERT("fmemopen succeeds", fd != NULL); 72 struct cnf_directive *dir = cnf_directive_read(fd); 73 ASSERT("directive with args returned", dir != NULL); 74 ASSERT_STRING_EQUALS("add", dir->name); 75 ASSERT_EQUALS(3, dir->argc); 76 ASSERT_STRING_EQUALS("10.0.0.0/8", dir->argv[0]); 77 ASSERT_STRING_EQUALS("via", dir->argv[1]); 78 ASSERT_STRING_EQUALS("192.168.1.1", dir->argv[2]); 79 cnf_directive_free(dir); 80 fclose(fd); 81 } 82 83 void test_multiple_spaces_between_args() { 84 FILE *fd = open_string(" del 10.1.0.0/16\n"); 85 ASSERT("fmemopen succeeds", fd != NULL); 86 struct cnf_directive *dir = cnf_directive_read(fd); 87 ASSERT("directive with extra spaces returned", dir != NULL); 88 ASSERT_STRING_EQUALS("del", dir->name); 89 ASSERT_EQUALS(1, dir->argc); 90 ASSERT_STRING_EQUALS("10.1.0.0/16", dir->argv[0]); 91 cnf_directive_free(dir); 92 fclose(fd); 93 } 94 95 void test_quoted_double_args() { 96 FILE *fd = open_string("set note \"hello world\" and \"foo bar\"\n"); 97 ASSERT("fmemopen succeeds", fd != NULL); 98 struct cnf_directive *dir = cnf_directive_read(fd); 99 ASSERT("directive with quoted args returned", dir != NULL); 100 ASSERT_STRING_EQUALS("set", dir->name); 101 ASSERT_EQUALS(4, dir->argc); 102 ASSERT_STRING_EQUALS("note", dir->argv[0]); 103 ASSERT_STRING_EQUALS("hello world", dir->argv[1]); 104 ASSERT_STRING_EQUALS("and", dir->argv[2]); 105 ASSERT_STRING_EQUALS("foo bar", dir->argv[3]); 106 fclose(fd); 107 } 108 109 void test_quoted_single_args() { 110 FILE *fd = open_string("op 'single quoted'\n"); 111 ASSERT("fmemopen succeeds", fd != NULL); 112 struct cnf_directive *dir = cnf_directive_read(fd); 113 ASSERT("directive with single-quoted arg returned", dir != NULL); 114 ASSERT_STRING_EQUALS("op", dir->name); 115 ASSERT_EQUALS(1, dir->argc); 116 ASSERT_STRING_EQUALS("single quoted", dir->argv[0]); 117 cnf_directive_free(dir); 118 fclose(fd); 119 } 120 121 void test_escaped_quote_in_double() { 122 FILE *fd = open_string("op arg \"a \\\" b\"\n"); 123 ASSERT("fmemopen succeeds", fd != NULL); 124 struct cnf_directive *dir = cnf_directive_read(fd); 125 ASSERT("directive with escaped quote returned", dir != NULL); 126 ASSERT_STRING_EQUALS("op", dir->name); 127 ASSERT_EQUALS(2, dir->argc); 128 ASSERT_STRING_EQUALS("arg", dir->argv[0]); 129 ASSERT_STRING_EQUALS("a \" b", dir->argv[1]); 130 cnf_directive_free(dir); 131 fclose(fd); 132 } 133 134 void test_escaped_newline_in_double_quotes() { 135 FILE *fd = open_string("op \"a\\nb\"\n"); // config text: op "a\nb" 136 ASSERT("fmemopen succeeds", fd != NULL); 137 struct cnf_directive *dir = cnf_directive_read(fd); 138 ASSERT("directive with escaped newline returned", dir != NULL); 139 ASSERT_STRING_EQUALS("op", dir->name); 140 ASSERT_EQUALS(1, dir->argc); 141 ASSERT_STRING_EQUALS("a\nb", dir->argv[0]); 142 cnf_directive_free(dir); 143 fclose(fd); 144 } 145 146 void test_escaped_tab_cr_in_double_quotes() { 147 FILE *fd = open_string("op \"a\\tb\\rc\"\n"); // config text: op "a\tb\rc" 148 ASSERT("fmemopen succeeds", fd != NULL); 149 struct cnf_directive *dir = cnf_directive_read(fd); 150 ASSERT("directive with escaped tab/cr returned", dir != NULL); 151 ASSERT_EQUALS(1, dir->argc); 152 ASSERT_STRING_EQUALS("a\tb\rc", dir->argv[0]); 153 cnf_directive_free(dir); 154 fclose(fd); 155 } 156 157 void test_escaped_backslash_in_double_quotes() { 158 FILE *fd = open_string("op \"a\\\\b\"\n"); // config text: op "a\\b" 159 ASSERT("fmemopen succeeds", fd != NULL); 160 struct cnf_directive *dir = cnf_directive_read(fd); 161 ASSERT("directive with escaped backslash returned", dir != NULL); 162 ASSERT_EQUALS(1, dir->argc); 163 ASSERT_STRING_EQUALS("a\\b", dir->argv[0]); 164 cnf_directive_free(dir); 165 fclose(fd); 166 } 167 168 void test_unknown_escape_kept_verbatim() { 169 FILE *fd = open_string("op \"a\\zb\"\n"); // config text: op "a\zb" 170 ASSERT("fmemopen succeeds", fd != NULL); 171 struct cnf_directive *dir = cnf_directive_read(fd); 172 ASSERT("directive with unknown escape returned", dir != NULL); 173 ASSERT_EQUALS(1, dir->argc); 174 ASSERT_STRING_EQUALS("azb", dir->argv[0]); 175 cnf_directive_free(dir); 176 fclose(fd); 177 } 178 179 void test_null_escape_becomes_zero() { 180 FILE *fd = open_string("op \"a\\0b\"\n"); // config text: op "a\0b" 181 ASSERT("fmemopen succeeds", fd != NULL); 182 struct cnf_directive *dir = cnf_directive_read(fd); 183 ASSERT("directive with null escape returned", dir != NULL); 184 ASSERT_EQUALS(1, dir->argc); 185 ASSERT_STRING_EQUALS("a0b", dir->argv[0]); 186 cnf_directive_free(dir); 187 fclose(fd); 188 } 189 190 void test_single_quotes_keep_backslash_literal() { 191 FILE *fd = open_string("op 'a\\nb'\n"); // config text: op 'a\nb' 192 ASSERT("fmemopen succeeds", fd != NULL); 193 struct cnf_directive *dir = cnf_directive_read(fd); 194 ASSERT("directive with literal backslash in single quotes returned", dir != NULL); 195 ASSERT_EQUALS(1, dir->argc); 196 ASSERT_STRING_EQUALS("a\\nb", dir->argv[0]); 197 cnf_directive_free(dir); 198 fclose(fd); 199 } 200 201 void test_escaped_single_quote_in_single_quotes() { 202 FILE *fd = open_string("op 'it\\'s'\n"); // config text: op 'it\'s' 203 ASSERT("fmemopen succeeds", fd != NULL); 204 struct cnf_directive *dir = cnf_directive_read(fd); 205 ASSERT("directive with escaped single quote returned", dir != NULL); 206 ASSERT_EQUALS(1, dir->argc); 207 ASSERT_STRING_EQUALS("it's", dir->argv[0]); 208 cnf_directive_free(dir); 209 fclose(fd); 210 } 211 212 void test_empty_double_quote_first_token() { 213 FILE *fd = open_string("\"\" foo\n"); // config text: "" foo 214 ASSERT("fmemopen succeeds", fd != NULL); 215 struct cnf_directive *dir = cnf_directive_read(fd); 216 ASSERT("directive with empty first token returned", dir != NULL); 217 ASSERT_STRING_EQUALS("", dir->name); 218 ASSERT_EQUALS(1, dir->argc); 219 ASSERT_STRING_EQUALS("foo", dir->argv[0]); 220 cnf_directive_free(dir); 221 fclose(fd); 222 } 223 224 void test_empty_double_quote_alone() { 225 FILE *fd = open_string("\"\"\n"); // config text: "" 226 ASSERT("fmemopen succeeds", fd != NULL); 227 struct cnf_directive *dir = cnf_directive_read(fd); 228 ASSERT("directive with only an empty token returned", dir != NULL); 229 ASSERT_STRING_EQUALS("", dir->name); 230 ASSERT_EQUALS(0, dir->argc); 231 cnf_directive_free(dir); 232 fclose(fd); 233 } 234 235 void test_hash_in_quoted_string() { 236 FILE *fd = open_string("set note \"a # b\"\n"); 237 ASSERT("fmemopen succeeds", fd != NULL); 238 struct cnf_directive *dir = cnf_directive_read(fd); 239 ASSERT("directive with hash in quotes returned", dir != NULL); 240 ASSERT_STRING_EQUALS("set", dir->name); 241 ASSERT_EQUALS(2, dir->argc); 242 ASSERT_STRING_EQUALS("note", dir->argv[0]); 243 ASSERT_STRING_EQUALS("a # b", dir->argv[1]); 244 cnf_directive_free(dir); 245 fclose(fd); 246 } 247 248 void test_trailing_comment() { 249 FILE *fd = open_string("op arg # trailing comment\n"); 250 ASSERT("fmemopen succeeds", fd != NULL); 251 struct cnf_directive *dir = cnf_directive_read(fd); 252 ASSERT("directive before trailing comment returned", dir != NULL); 253 ASSERT_STRING_EQUALS("op", dir->name); 254 ASSERT_EQUALS(1, dir->argc); 255 ASSERT_STRING_EQUALS("arg", dir->argv[0]); 256 cnf_directive_free(dir); 257 fclose(fd); 258 } 259 260 void test_comment_no_space() { 261 FILE *fd = open_string("route 172.16.0.0/12 #comment with no space\n"); 262 ASSERT("fmemopen succeeds", fd != NULL); 263 struct cnf_directive *dir = cnf_directive_read(fd); 264 ASSERT("directive before tight comment returned", dir != NULL); 265 ASSERT_STRING_EQUALS("route", dir->name); 266 ASSERT_EQUALS(1, dir->argc); 267 ASSERT_STRING_EQUALS("172.16.0.0/12", dir->argv[0]); 268 cnf_directive_free(dir); 269 fclose(fd); 270 } 271 272 void test_multiple_directives_sequence() { 273 FILE *fd = open_string("first one\nsecond two three\n"); 274 ASSERT("fmemopen succeeds", fd != NULL); 275 276 struct cnf_directive *d1 = cnf_directive_read(fd); 277 ASSERT("first directive returned", d1 != NULL); 278 ASSERT_STRING_EQUALS("first", d1->name); 279 ASSERT_EQUALS(1, d1->argc); 280 ASSERT_STRING_EQUALS("one", d1->argv[0]); 281 cnf_directive_free(d1); 282 283 struct cnf_directive *d2 = cnf_directive_read(fd); 284 ASSERT("second directive returned", d2 != NULL); 285 ASSERT_STRING_EQUALS("second", d2->name); 286 ASSERT_EQUALS(2, d2->argc); 287 ASSERT_STRING_EQUALS("two", d2->argv[0]); 288 ASSERT_STRING_EQUALS("three", d2->argv[1]); 289 cnf_directive_free(d2); 290 291 struct cnf_directive *d3 = cnf_directive_read(fd); 292 ASSERT("exhausted file returns NULL", d3 == NULL); 293 fclose(fd); 294 } 295 296 void test_blank_lines_between_directives() { 297 FILE *fd = open_string("\n\nfirst arg\n\n\nsecond\n"); 298 ASSERT("fmemopen succeeds", fd != NULL); 299 300 struct cnf_directive *d1 = cnf_directive_read(fd); 301 ASSERT("directive after blank lines returned", d1 != NULL); 302 ASSERT_STRING_EQUALS("first", d1->name); 303 ASSERT_EQUALS(1, d1->argc); 304 cnf_directive_free(d1); 305 306 struct cnf_directive *d2 = cnf_directive_read(fd); 307 ASSERT("directive after more blank lines returned", d2 != NULL); 308 ASSERT_STRING_EQUALS("second", d2->name); 309 ASSERT_EQUALS(0, d2->argc); 310 cnf_directive_free(d2); 311 312 fclose(fd); 313 } 314 315 void test_no_trailing_newline() { 316 FILE *fd = open_string("final one two three"); 317 ASSERT("fmemopen succeeds", fd != NULL); 318 struct cnf_directive *dir = cnf_directive_read(fd); 319 ASSERT("directive without trailing newline returned", dir != NULL); 320 ASSERT_STRING_EQUALS("final", dir->name); 321 ASSERT_EQUALS(3, dir->argc); 322 ASSERT_STRING_EQUALS("one", dir->argv[0]); 323 ASSERT_STRING_EQUALS("two", dir->argv[1]); 324 ASSERT_STRING_EQUALS("three", dir->argv[2]); 325 cnf_directive_free(dir); 326 fclose(fd); 327 } 328 329 void test_directive_free_null() { 330 cnf_directive_free(NULL); 331 ASSERT("free NULL does not crash", 1); 332 } 333 334 int main(int argc, const char *argv[]) { 335 RUN(test_empty_file); 336 RUN(test_blank_lines_only); 337 RUN(test_comment_only_line); 338 RUN(test_single_word_no_args); 339 RUN(test_leading_whitespace); 340 RUN(test_trailing_whitespace); 341 RUN(test_multiple_space_separated_args); 342 RUN(test_multiple_spaces_between_args); 343 RUN(test_quoted_double_args); 344 RUN(test_quoted_single_args); 345 RUN(test_escaped_quote_in_double); 346 RUN(test_escaped_newline_in_double_quotes); 347 RUN(test_escaped_tab_cr_in_double_quotes); 348 RUN(test_escaped_backslash_in_double_quotes); 349 RUN(test_unknown_escape_kept_verbatim); 350 RUN(test_null_escape_becomes_zero); 351 RUN(test_single_quotes_keep_backslash_literal); 352 RUN(test_escaped_single_quote_in_single_quotes); 353 RUN(test_empty_double_quote_first_token); 354 RUN(test_empty_double_quote_alone); 355 RUN(test_hash_in_quoted_string); 356 RUN(test_trailing_comment); 357 RUN(test_comment_no_space); 358 RUN(test_multiple_directives_sequence); 359 RUN(test_blank_lines_between_directives); 360 RUN(test_no_trailing_newline); 361 RUN(test_directive_free_null); 362 return TEST_REPORT(); 363 }