1 /*
2 * Copyright (c) 2020, Masatake YAMATO
3 *
4 * This source code is released into the public domain.
5 *
6 * Testing tagsFind() and tagsFindNext() API functions
7 */
8
9 #include "readtags.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16
17 struct expectation {
18 char *name;
19 char *file;
20 char *pattern;
21 char *kind;
22 char *scope_kind;
23 char *scope_name;
24 char *typeref;
25 int fileScope;
26
27 tagResult result;
28 int err;
29 };
30
31 #define COUNT(x) (sizeof(x)/sizeof(x[0]))
32
33 enum extraTest { TESTX_NO_REMAIN, TESTX_INVALID_ARG };
34
35 static int
check_finding0(tagFile * t,const char * name,const int options,struct expectation * expectations,int count,enum extraTest xtest)36 check_finding0 (tagFile *t, const char *name, const int options,
37 struct expectation *expectations, int count, enum extraTest xtest)
38 {
39 tagEntry e;
40 struct expectation *x;
41 int err;
42
43 for (int i = 0; i < count; i++)
44 {
45 fprintf (stderr, "[%d/%d] finding \"%s\" (%d)...", i + 1, count, name, options);
46 tagResult r = (i == 0)
47 ? tagsFind (t, &e, name, options)
48 : tagsFindNext (t, &e);
49 x = expectations + i;
50 if (r == TagSuccess)
51 {
52 if (x->result == TagFailure)
53 {
54 fprintf (stderr, "found unexpectedly\n");
55 return 1;
56 }
57 else
58 fprintf (stderr, "found as expected\n");
59 }
60 else
61 {
62 if (x->result == TagFailure)
63 {
64 err = tagsGetErrno (t);
65 if (err == x->err)
66 {
67 if (err == 0)
68 fprintf (stderr, "not found, and it is expected\n");
69 else
70 fprintf (stderr, "error as expected: %d\n", err);
71 continue;
72 }
73 else
74 {
75 fprintf (stderr, "error number doesn't match: %d (expected: %d)\n",
76 err, x->err);
77 return 1;
78 }
79 }
80 else
81 {
82 if ((err = tagsGetErrno (t)))
83 fprintf (stderr, "error: %d\n", err);
84 else
85 fprintf (stderr, "not found\n");
86 return 1;
87 }
88 }
89
90 fprintf (stderr, "checking name field...");
91 if (!(e.name && strcmp (x->name, e.name) == 0))
92 {
93 fprintf (stderr, "unexpected: %s\n", e.name? e.name: "<NULL>");
94 return 1;
95 }
96 fprintf (stderr, "ok\n");
97
98 fprintf (stderr, "checking file field...");
99 if (!(e.file && strcmp (x->file, e.file) == 0))
100 {
101 fprintf (stderr, "unexpected\n");
102 return 1;
103 }
104 fprintf (stderr, "ok\n");
105
106 fprintf (stderr, "checking pattern field...");
107 if (!(e.address.pattern && strcmp (x->pattern, e.address.pattern) == 0))
108 {
109 fprintf (stderr, "unexpected: %s\n",
110 e.address.pattern? e.address.pattern: "<NULL>");
111 return 1;
112 }
113 fprintf (stderr, "ok\n");
114
115 fprintf (stderr, "checking kind field...");
116 if (!(e.kind && strcmp (x->kind, e.kind) == 0))
117 {
118 fprintf (stderr, "unexpected\n");
119 return 1;
120 }
121 fprintf (stderr, "ok\n");
122
123 if (x->scope_kind)
124 {
125 fprintf (stderr, "checking scope field...");
126 const char *scope = tagsField (&e, x->scope_kind);
127 if (scope == NULL || strcmp (scope, x->scope_name) != 0)
128 {
129 fprintf (stderr, "unexpected: %s\n", scope? scope: "<NULL>");
130 return 1;
131 }
132 fprintf (stderr, "ok\n");
133 }
134
135 if (x->typeref)
136 {
137 fprintf (stderr, "checking typeref field...");
138 const char *typeref = tagsField (&e, "typeref");
139 if (typeref == NULL || strcmp (typeref, x->typeref) != 0)
140 {
141 fprintf (stderr, "unexpected: %s\n", typeref? typeref: "<NULL>");
142 return 1;
143 }
144 fprintf (stderr, "ok\n");
145 }
146
147 fprintf (stderr, "checking file field...");
148 if (x->fileScope != e.fileScope)
149 {
150 fprintf (stderr, "unexpected\n");
151 return 1;
152 }
153 fprintf (stderr, "ok\n");
154 }
155
156 if (xtest == TESTX_NO_REMAIN)
157 {
158 fprintf (stderr, "verifying no remain....");
159 if (tagsFindNext (t, &e) == TagSuccess)
160 {
161 fprintf (stderr, "still existing\n");
162 return 1;
163 }
164 else if ((err = tagsGetErrno (t)))
165 {
166 fprintf (stderr, "unexpected error: %d\n", err);
167 return 1;
168 }
169 fprintf (stderr, "ok\n");
170 }
171 else if (xtest == TESTX_NO_REMAIN)
172 {
173 fprintf (stderr, "call tagsFindNext after getting an error....");
174 if (tagsFindNext (t, &e) == TagSuccess)
175 {
176 fprintf (stderr, "unexpectedly successful\n");
177 return 1;
178 }
179
180 err = tagsGetErrno (t);
181 if (err != TagErrnoInvalidArgument)
182 {
183 fprintf (stderr, "errer number doesn't match: %d (expected: %d)\n",
184 err, TagErrnoInvalidArgument);
185 return 1;
186 }
187 fprintf (stderr, "get TagErrnoInvalidArgument expectedly\n");
188 }
189
190 return 0;
191 }
192
193 static int
check_finding(const char * tags,const char * name,const int options,struct expectation * expectations,int count,enum extraTest xtest)194 check_finding (const char *tags, const char *name, const int options,
195 struct expectation *expectations, int count, enum extraTest xtest)
196 {
197 tagFile *t;
198 tagFileInfo info;
199
200 fprintf (stderr, "opening %s...", tags);
201 t = tagsOpen (tags, &info);
202 if (!t)
203 {
204 fprintf (stderr, "unexpected result (t: %p, opened: %d, error_number: %d)\n",
205 t, info.status.opened, info.status.error_number);
206 return 1;
207 }
208 fprintf (stderr, "ok\n");
209
210 if (check_finding0 (t, name, options, expectations, count, xtest) != 0)
211 return 1;
212
213 fprintf (stderr, "closing the tag file...");
214 if (tagsClose (t) != TagSuccess)
215 {
216 fprintf (stderr, "unexpected result\n");
217 return 1;
218 }
219 fprintf (stderr, "ok\n");
220 return 0;
221 }
222
223 int
main(void)224 main (void)
225 {
226 char *srcdir = getenv ("srcdir");
227 if (srcdir)
228 {
229 if (chdir (srcdir) == -1)
230 {
231 perror ("chdir");
232 return 99;
233 }
234 }
235
236 /*
237 * sorted=yes
238 */
239 const char *tags_sorted_yes = "./duplicated-names--sorted-yes.tags";
240 struct expectation sorted_yes_FULLMATCH_OBSERVECASE [] = {
241 {
242 .name = "n",
243 .file = "input.c",
244 .pattern = "/^ int n;$/",
245 .kind = "l",
246 .scope_kind = "function",
247 .scope_name = "main",
248 .typeref = "typename:int",
249 .fileScope = 1,
250 .result = TagSuccess,
251 },
252 {
253 .name = "n",
254 .file = "input.c",
255 .pattern = "/^ for (int n = 0; n < 1; n++)$/",
256 .kind = "l",
257 .scope_kind = "function",
258 .scope_name = "main",
259 .typeref = "typename:int",
260 .fileScope = 1,
261 .result = TagSuccess,
262 },
263 {
264 .name = "n",
265 .file = "input.c",
266 .pattern = "/^ int n;$/",
267 .kind = "m",
268 .scope_kind = "struct",
269 .scope_name = "n",
270 .typeref = "typename:int",
271 .fileScope = 1,
272 .result = TagSuccess,
273 },
274 {
275 .name = "n",
276 .file = "input.c",
277 .pattern = "/^int main(int n)$/",
278 .kind = "z",
279 .scope_kind = "function",
280 .scope_name = "main",
281 .typeref = "typename:int",
282 .fileScope = 1,
283 .result = TagSuccess,
284 },
285 {
286 .name = "n",
287 .file = "input.c",
288 .pattern = "/^struct n {$/",
289 .kind = "s",
290 .scope_kind = NULL,
291 .scope_name = NULL,
292 .typeref = NULL,
293 .fileScope = 1,
294 .result = TagSuccess,
295 },
296 {
297 .name = "n",
298 .file = "input.c",
299 .pattern = "/^typedef int n;$/",
300 .kind = "t",
301 .scope_kind = NULL,
302 .scope_name = NULL,
303 .typeref = "typename:int",
304 .fileScope = 1,
305 .result = TagSuccess,
306 },
307 };
308
309 struct expectation sorted_yes_FULLMATCH_IGNORECASE [COUNT(sorted_yes_FULLMATCH_OBSERVECASE) + 1] = {
310 [0] = {
311 .name = "N",
312 .file = "input.c",
313 .pattern = "/^int N;$/",
314 .kind = "v",
315 .scope_kind = NULL,
316 .scope_name = NULL,
317 .typeref = "typename:int",
318 .fileScope = 0,
319 .result = TagSuccess,
320 },
321 };
322 for (int i = 0; i < COUNT(sorted_yes_FULLMATCH_OBSERVECASE); i++)
323 sorted_yes_FULLMATCH_IGNORECASE [i + 1] = sorted_yes_FULLMATCH_OBSERVECASE [i];
324
325 struct expectation sorted_yes_PARTIALMATCH_OBSERVECASE [] = {
326 {
327 .name = "m",
328 .file = "input.c",
329 .pattern = "/^int m;$/",
330 .kind = "v",
331 .scope_kind = NULL,
332 .scope_name = NULL,
333 .typeref = "typename:int",
334 .fileScope = 0,
335 .result = TagSuccess,
336 },
337 {
338 .name = "main",
339 .file = "input.c",
340 .pattern = "/^int main(int n)$/",
341 .kind = "f",
342 .scope_kind = NULL,
343 .scope_name = NULL,
344 .typeref = "typename:int",
345 .fileScope = 0,
346 .result = TagSuccess,
347 },
348 };
349
350 struct expectation sorted_yes_PARTIALMATCH_IGNORECASE [COUNT(sorted_yes_PARTIALMATCH_OBSERVECASE) + 1] = {
351 [0] = {
352 .name = "M",
353 .file = "input.c",
354 .pattern = "/^int M (void) { return 0; }$/",
355 .kind = "f",
356 .scope_kind = NULL,
357 .scope_name = NULL,
358 .typeref = "typename:int",
359 .fileScope = 0,
360 .result = TagSuccess,
361 },
362 };
363 for (int i = 0; i < COUNT(sorted_yes_PARTIALMATCH_OBSERVECASE); i++)
364 sorted_yes_PARTIALMATCH_IGNORECASE [i + 1] = sorted_yes_PARTIALMATCH_OBSERVECASE [i];
365
366 if (check_finding (tags_sorted_yes, "n", TAG_FULLMATCH|TAG_OBSERVECASE,
367 sorted_yes_FULLMATCH_OBSERVECASE, COUNT(sorted_yes_FULLMATCH_OBSERVECASE),
368 TESTX_NO_REMAIN) != 0)
369 return 1;
370
371 if (check_finding (tags_sorted_yes, "n", TAG_FULLMATCH|TAG_IGNORECASE,
372 sorted_yes_FULLMATCH_IGNORECASE, COUNT(sorted_yes_FULLMATCH_IGNORECASE),
373 TESTX_NO_REMAIN) != 0)
374 return 1;
375
376 if (check_finding (tags_sorted_yes, "m", TAG_PARTIALMATCH|TAG_OBSERVECASE,
377 sorted_yes_PARTIALMATCH_OBSERVECASE, COUNT(sorted_yes_PARTIALMATCH_OBSERVECASE),
378 TESTX_NO_REMAIN) != 0)
379 return 1;
380
381 if (check_finding (tags_sorted_yes, "m", TAG_PARTIALMATCH|TAG_IGNORECASE,
382 sorted_yes_PARTIALMATCH_IGNORECASE, COUNT(sorted_yes_PARTIALMATCH_IGNORECASE),
383 TESTX_NO_REMAIN) != 0)
384 return 1;
385
386
387 /*
388 * sorted=no
389 */
390 const char *tags_sorted_no = "./duplicated-names--sorted-no.tags";
391 struct expectation sorted_no_FULLMATCH_OBSERVECASE [] = {
392 {
393 .name = "n",
394 .file = "input.c",
395 .pattern = "/^struct n {$/",
396 .kind = "s",
397 .scope_kind = NULL,
398 .scope_name = NULL,
399 .typeref = NULL,
400 .fileScope = 1,
401 .result = TagSuccess,
402 },
403 {
404 .name = "n",
405 .file = "input.c",
406 .pattern = "/^ int n;$/",
407 .kind = "m",
408 .scope_kind = "struct",
409 .scope_name = "n",
410 .typeref = "typename:int",
411 .fileScope = 1,
412 .result = TagSuccess,
413 },
414 {
415 .name = "n",
416 .file = "input.c",
417 .pattern = "/^typedef int n;$/",
418 .kind = "t",
419 .scope_kind = NULL,
420 .scope_name = NULL,
421 .typeref = "typename:int",
422 .fileScope = 1,
423 .result = TagSuccess,
424 },
425 {
426 .name = "n",
427 .file = "input.c",
428 .pattern = "/^int main(int n)$/",
429 .kind = "z",
430 .scope_kind = "function",
431 .scope_name = "main",
432 .typeref = "typename:int",
433 .fileScope = 1,
434 .result = TagSuccess,
435 },
436 {
437 .name = "n",
438 .file = "input.c",
439 .pattern = "/^ for (int n = 0; n < 1; n++)$/",
440 .kind = "l",
441 .scope_kind = "function",
442 .scope_name = "main",
443 .typeref = "typename:int",
444 .fileScope = 1,
445 .result = TagSuccess,
446 },
447 {
448 .name = "n",
449 .file = "input.c",
450 .pattern = "/^ int n;$/",
451 .kind = "l",
452 .scope_kind = "function",
453 .scope_name = "main",
454 .typeref = "typename:int",
455 .fileScope = 1,
456 .result = TagSuccess,
457 },
458 };
459
460 struct expectation sorted_no_FULLMATCH_IGNORECASE [COUNT(sorted_no_FULLMATCH_OBSERVECASE) + 1] = {
461 [0] = {
462 .name = "N",
463 .file = "input.c",
464 .pattern = "/^int N;$/",
465 .kind = "v",
466 .scope_kind = NULL,
467 .scope_name = NULL,
468 .typeref = "typename:int",
469 .fileScope = 0,
470 .result = TagSuccess,
471 },
472 };
473 for (int i = 0; i < COUNT(sorted_no_FULLMATCH_OBSERVECASE); i++)
474 sorted_no_FULLMATCH_IGNORECASE [i + 1] = sorted_no_FULLMATCH_OBSERVECASE [i];
475
476 struct expectation sorted_no_PARTIALMATCH_OBSERVECASE [] = {
477 {
478 .name = "main",
479 .file = "input.c",
480 .pattern = "/^int main(int n)$/",
481 .kind = "f",
482 .scope_kind = NULL,
483 .scope_name = NULL,
484 .typeref = "typename:int",
485 .fileScope = 0,
486 .result = TagSuccess,
487 },
488 {
489 .name = "m",
490 .file = "input.c",
491 .pattern = "/^int m;$/",
492 .kind = "v",
493 .scope_kind = NULL,
494 .scope_name = NULL,
495 .typeref = "typename:int",
496 .fileScope = 0,
497 .result = TagSuccess,
498 },
499 };
500
501 struct expectation sorted_no_PARTIALMATCH_IGNORECASE [COUNT(sorted_no_PARTIALMATCH_OBSERVECASE) + 1] = {
502 [COUNT(sorted_no_PARTIALMATCH_OBSERVECASE)] = {
503 .name = "M",
504 .file = "input.c",
505 .pattern = "/^int M (void) { return 0; }$/",
506 .kind = "f",
507 .scope_kind = NULL,
508 .scope_name = NULL,
509 .typeref = "typename:int",
510 .fileScope = 0,
511 .result = TagSuccess,
512 },
513 };
514 for (int i = 0; i < COUNT(sorted_no_PARTIALMATCH_OBSERVECASE); i++)
515 sorted_no_PARTIALMATCH_IGNORECASE [i] = sorted_no_PARTIALMATCH_OBSERVECASE [i];
516
517 if (check_finding (tags_sorted_no, "n", TAG_FULLMATCH|TAG_OBSERVECASE,
518 sorted_no_FULLMATCH_OBSERVECASE, COUNT(sorted_no_FULLMATCH_OBSERVECASE),
519 TESTX_NO_REMAIN) != 0)
520 return 1;
521
522 if (check_finding (tags_sorted_no, "n", TAG_FULLMATCH|TAG_IGNORECASE,
523 sorted_no_FULLMATCH_IGNORECASE, COUNT(sorted_no_FULLMATCH_IGNORECASE),
524 TESTX_NO_REMAIN) != 0)
525 return 1;
526
527 if (check_finding (tags_sorted_no, "m", TAG_PARTIALMATCH|TAG_OBSERVECASE,
528 sorted_no_PARTIALMATCH_OBSERVECASE, COUNT(sorted_no_PARTIALMATCH_OBSERVECASE),
529 TESTX_NO_REMAIN) != 0)
530 return 1;
531
532 if (check_finding (tags_sorted_no, "m", TAG_PARTIALMATCH|TAG_IGNORECASE,
533 sorted_no_PARTIALMATCH_IGNORECASE, COUNT(sorted_no_PARTIALMATCH_IGNORECASE),
534 TESTX_NO_REMAIN) != 0)
535 return 1;
536
537
538 /*
539 * sorted=foldcase
540 */
541 const char *tags_sorted_foldcase = "./duplicated-names--sorted-foldcase.tags";
542 struct expectation sorted_foldcase_FULLMATCH_OBSERVECASE [COUNT(sorted_yes_FULLMATCH_OBSERVECASE)];
543 for (int i = 0; i < COUNT(sorted_foldcase_FULLMATCH_OBSERVECASE); i++)
544 sorted_foldcase_FULLMATCH_OBSERVECASE [i] = sorted_yes_FULLMATCH_OBSERVECASE [i];
545
546 struct expectation sorted_foldcase_FULLMATCH_IGNORECASE [COUNT(sorted_foldcase_FULLMATCH_OBSERVECASE) + 1] = {
547 [4] = {
548 .name = "N",
549 .file = "input.c",
550 .pattern = "/^int N;$/",
551 .kind = "v",
552 .scope_kind = NULL,
553 .scope_name = NULL,
554 .typeref = "typename:int",
555 .fileScope = 0,
556 .result = TagSuccess,
557 },
558 [5] = sorted_foldcase_FULLMATCH_OBSERVECASE[4],
559 [6] = sorted_foldcase_FULLMATCH_OBSERVECASE[5],
560 };
561 for (int i = 0; i < 4; i++)
562 sorted_foldcase_FULLMATCH_IGNORECASE [i] = sorted_foldcase_FULLMATCH_OBSERVECASE [i];
563
564 struct expectation sorted_foldcase_PARTIALMATCH_OBSERVECASE [COUNT(sorted_yes_PARTIALMATCH_OBSERVECASE)];
565 for (int i = 0; i < COUNT(sorted_foldcase_PARTIALMATCH_OBSERVECASE); i++)
566 sorted_foldcase_PARTIALMATCH_OBSERVECASE [i] = sorted_yes_PARTIALMATCH_OBSERVECASE [i];
567
568 struct expectation sorted_foldcase_PARTIALMATCH_IGNORECASE [COUNT(sorted_yes_PARTIALMATCH_IGNORECASE)];
569 for (int i = 0; i < COUNT(sorted_foldcase_PARTIALMATCH_IGNORECASE); i++)
570 sorted_foldcase_PARTIALMATCH_IGNORECASE [i] = sorted_yes_PARTIALMATCH_IGNORECASE [i];
571
572
573 if (check_finding (tags_sorted_foldcase, "n", TAG_FULLMATCH|TAG_OBSERVECASE,
574 sorted_foldcase_FULLMATCH_OBSERVECASE, COUNT(sorted_foldcase_FULLMATCH_OBSERVECASE),
575 TESTX_NO_REMAIN) != 0)
576 return 1;
577
578 if (check_finding (tags_sorted_foldcase, "n", TAG_FULLMATCH|TAG_IGNORECASE,
579 sorted_foldcase_FULLMATCH_IGNORECASE, COUNT(sorted_foldcase_FULLMATCH_IGNORECASE),
580 TESTX_NO_REMAIN) != 0)
581 return 1;
582
583 if (check_finding (tags_sorted_foldcase, "m", TAG_PARTIALMATCH|TAG_OBSERVECASE,
584 sorted_foldcase_PARTIALMATCH_OBSERVECASE, COUNT(sorted_foldcase_PARTIALMATCH_OBSERVECASE),
585 TESTX_NO_REMAIN) != 0)
586 return 1;
587
588 if (check_finding (tags_sorted_foldcase, "m", TAG_PARTIALMATCH|TAG_IGNORECASE,
589 sorted_foldcase_PARTIALMATCH_IGNORECASE, COUNT(sorted_foldcase_PARTIALMATCH_IGNORECASE),
590 TESTX_NO_REMAIN) != 0)
591 return 1;
592
593 /*
594 * Not found
595 */
596 const char *tags_not_found = "./duplicated-names--sorted-yes.tags";
597 struct expectation not_found_case = {
598 .result = TagFailure,
599 .err = 0,
600 };
601 if (check_finding (tags_not_found, "noSuchItem", TAG_FULLMATCH|TAG_IGNORECASE,
602 ¬_found_case, 1, TESTX_NO_REMAIN) != 0)
603 return 1;
604
605 /*
606 * Broken line:
607 */
608 const char *tags_broken_line_field = "./broken-line-field-in-middle.tags";
609 struct expectation broken_line_case = {
610 .result = TagFailure,
611 .err = TagErrnoUnexpectedLineno,
612 };
613 if (check_finding (tags_broken_line_field, "n", TAG_FULLMATCH|TAG_OBSERVECASE,
614 &broken_line_case, 1, TESTX_INVALID_ARG) != 0)
615 return 1;
616
617 struct expectation broken_line_cases[] = {
618 {
619 .name = "N",
620 .file = "duplicated-names.c",
621 .pattern = "/^int N;$/",
622 .kind = "v",
623 .scope_kind = NULL,
624 .scope_name = NULL,
625 .typeref = "typename:int",
626 .fileScope = 0,
627 .result = TagSuccess,
628 },
629 {
630 .result = TagFailure,
631 .err = TagErrnoUnexpectedLineno,
632 },
633 };
634 if (check_finding (tags_broken_line_field, "n", TAG_FULLMATCH|TAG_IGNORECASE,
635 broken_line_cases, COUNT(broken_line_cases), TESTX_INVALID_ARG) != 0)
636 return 1;
637
638 return 0;
639 }
640