1 /*
2     INI LIBRARY
3 
4     Unit test for the value object.
5 
6     Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 3 of the License, or
11     (at your option) any later version.
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "config.h"
21 #include <errno.h>  /* for errors */
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <limits.h>
27 
28 #include "ini_valueobj.h"
29 #include "ini_defines.h"
30 #define TRACE_HOME
31 #include "trace.h"
32 
33 #define TEST_SIZE 80
34 
35 int verbose = 0;
36 
37 #define VOOUT(foo) \
38     do { \
39         if (verbose) foo; \
40     } while(0)
41 
42 
43 typedef int (*test_fn)(void);
44 
create_comment(int i,struct ini_comment ** ic)45 static int create_comment(int i, struct ini_comment **ic)
46 {
47     int error = EOK;
48     const char *template = ";Line 0 of the value %d";
49     char comment[TEST_SIZE];
50     struct ini_comment *new_ic = NULL;
51 
52     TRACE_FLOW_ENTRY();
53 
54     snprintf(comment, TEST_SIZE, template, i);
55 
56 
57     if ((error = ini_comment_create(&new_ic)) ||
58         (error = ini_comment_build(new_ic, comment)) ||
59         (error = ini_comment_build(new_ic, NULL)) ||
60         (error = ini_comment_build(new_ic, "#This is the second line")) ||
61         (error = ini_comment_build(new_ic, ";This is the third line")) ||
62         (error = ini_comment_build(new_ic, ""))) {
63         printf("Failed to create comment object. Error %d.\n", error);
64         ini_comment_destroy(new_ic);
65         return -1;
66     }
67 
68     *ic = new_ic;
69 
70     TRACE_FLOW_EXIT();
71     return EOK;
72 }
73 
74 /* Save value to the file */
75 /* NOTE: might be moved into the API in future */
save_value(FILE * ff,const char * key,struct value_obj * vo)76 static int save_value(FILE *ff, const char *key, struct value_obj *vo)
77 {
78 
79     int error = EOK;
80     struct simplebuffer *sbobj = NULL;
81     uint32_t left = 0;
82 
83     TRACE_FLOW_ENTRY();
84 
85     error = simplebuffer_alloc(&sbobj);
86     if (error) {
87         TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error);
88         return error;
89     }
90 
91     /* Serialize */
92     error = value_serialize(vo, key, sbobj);
93     if (error) {
94         printf("Failed to serialize a value object %d.\n", error);
95         simplebuffer_free(sbobj);
96         return error;
97     }
98 
99     /* Add CR */
100     error = simplebuffer_add_cr(sbobj);
101     if (error) {
102         TRACE_ERROR_NUMBER("Failed to add CR", error);
103         simplebuffer_free(sbobj);
104         return error;
105     }
106 
107     /* Save */
108     left = simplebuffer_get_len(sbobj);
109     while (left > 0) {
110         error = simplebuffer_write(fileno(ff), sbobj, &left);
111         if (error) {
112             printf("Failed to write value object %d.\n", error);
113             simplebuffer_free(sbobj);
114             return error;
115         }
116     }
117 
118     simplebuffer_free(sbobj);
119 
120     TRACE_FLOW_EXIT();
121     return EOK;
122 }
123 
124 /* Test to create value object using arrays */
other_create_test(FILE * ff,struct value_obj ** vo)125 static int other_create_test(FILE *ff, struct value_obj **vo)
126 {
127     int error = EOK;
128     struct value_obj *new_vo = NULL;
129     struct ref_array *raw_lines;
130     struct ref_array *raw_lengths;
131     struct ini_comment *ic = NULL;
132     struct ini_comment *ic2 = NULL;
133     char *val;
134     const char *vallines[] = { "Domain1,",
135                                "  Domain2 ,",
136                                "  Domain3   " };
137     const char *fullstr;
138     const char *expected = "Domain1,  Domain2 ,  Domain3";
139     int i;
140     uint32_t origin = 0;
141     uint32_t line = 0;
142     uint32_t len = 0;
143     uint32_t expected_len = 0;
144 
145 
146     TRACE_FLOW_ENTRY();
147 
148     /* Create a pair of arrays */
149     error = value_create_arrays(&raw_lines,
150                                 &raw_lengths);
151     if (error) {
152         printf("Failed to create arrays %d.\n", error);
153         return error;
154     }
155 
156     for (i=0; i< 3; i++) {
157         errno = 0;
158         val = strdup(vallines[i]);
159         if (val == NULL) {
160             error = errno;
161             printf("Failed to dup memory %d.\n", error);
162             value_destroy_arrays(raw_lines,
163                                 raw_lengths);
164             return error;
165         }
166 
167         /* Add line to the arrays */
168         error = value_add_to_arrays(val,
169                                     strlen(val),
170                                     raw_lines,
171                                     raw_lengths);
172         if (error) {
173             printf("Failed to add to arrays %d.\n", error);
174             value_destroy_arrays(raw_lines,
175                                 raw_lengths);
176             return error;
177         }
178 
179     }
180 
181     /* Create a comment */
182     error = create_comment(1000, &ic);
183     if (error) {
184         printf("Failed to create comment %d.\n", error);
185         value_destroy_arrays(raw_lines,
186                             raw_lengths);
187         return error;
188     }
189 
190     /* Create object */
191     error = value_create_from_refarray(raw_lines,
192                                        raw_lengths,
193                                        1,
194                                        INI_VALUE_READ,
195                                        3,
196                                        70,
197                                        ic,
198                                        &new_vo);
199 
200     if (error) {
201         printf("Failed to create comment %d.\n", error);
202         value_destroy_arrays(raw_lines,
203                             raw_lengths);
204         ini_comment_destroy(ic);
205         return error;
206     }
207 
208     /* Save value to the file */
209     error = save_value(ff, "baz", new_vo);
210     if (error) {
211         printf("Failed to save value to file %d.\n", error);
212         value_destroy(new_vo);
213         return error;
214     }
215 
216     /* Now do assertions and modifications to the object */
217 
218     /* NOTE: Below this line do not need to free arrays or comment
219      * they became internal parts of the value object
220      * and will be freed as a part of it.
221      */
222 
223     /* Get concatenated value */
224     error = value_get_concatenated(new_vo,
225                                    &fullstr);
226 
227     if (error) {
228         printf("Failed to get the string %d.\n", error);
229         value_destroy(new_vo);
230         return error;
231     }
232 
233     /* Get length of the concatenated value */
234     value_get_concatenated_len(new_vo, &len);
235     expected_len = strlen(expected);
236 
237     if ((len != expected_len) ||
238         (strncmp(fullstr, expected, expected_len + 1) != 0)) {
239         printf("The expected value is different.\n");
240         printf("The expected value is %s\n", expected);
241         printf("The real value is %s\n", fullstr);
242         printf("The expected len is %d, real %d.\n", expected_len, len);
243         value_destroy(new_vo);
244         return EINVAL;
245     }
246 
247     /* Get value's origin */
248     error = value_get_origin(new_vo, &origin);
249     if (error) {
250         printf("Failed to get origin %d.\n", error);
251         value_destroy(new_vo);
252         return error;
253     }
254 
255     if (origin != INI_VALUE_READ) {
256         printf("The expected origin is different.\n%d\n", origin);
257         value_destroy(new_vo);
258         return EINVAL;
259     }
260 
261     /* Get value's line */
262     error = value_get_line(new_vo, &line);
263     if (error) {
264         printf("Failed to get origin %d.\n", error);
265         value_destroy(new_vo);
266         return error;
267     }
268 
269     if (line != 1) {
270         printf("The expected line is different.\n%d\n", origin);
271         value_destroy(new_vo);
272         return EINVAL;
273     }
274 
275     /* Get comment from the value */
276     ic = NULL;
277     error = value_extract_comment(new_vo, &ic);
278     if (error) {
279         printf("Failed to extract comment %d.\n", error);
280         value_destroy(new_vo);
281         return error;
282     }
283 
284     if (ic == NULL) {
285         printf("The expected comment to be there.\n");
286         value_destroy(new_vo);
287         return error;
288     }
289 
290     VOOUT(ini_comment_print(ic, stdout));
291 
292     /* Get comment again */
293     ic2 = NULL;
294     error = value_extract_comment(new_vo, &ic2);
295     if (error) {
296         printf("Failed to extract comment %d.\n", error);
297         value_destroy(new_vo);
298         ini_comment_destroy(ic);
299         return error;
300     }
301 
302     if (ic2 != NULL) {
303         printf("The expected NO comment to be there.\n");
304         value_destroy(new_vo);
305         ini_comment_destroy(ic);
306         /* No free for ic2 since it is the same object */
307 
308         /* But this should not happen anyways -
309          * it will be coding error.
310          */
311         return EINVAL;
312     }
313 
314     /* Put comment back */
315     error = value_put_comment(new_vo, ic);
316     if (error) {
317         printf("Failed to put comment back %d.\n", error);
318         value_destroy(new_vo);
319         ini_comment_destroy(ic);
320         return error;
321     }
322 
323     /* Save value to the file */
324     error = save_value(ff, "bar", new_vo);
325     if (error) {
326         printf("Failed to save value to file %d.\n", error);
327         value_destroy(new_vo);
328         return error;
329     }
330 
331     *vo = new_vo;
332 
333     TRACE_FLOW_EXIT();
334     return EOK;
335 }
336 
337 /* Modify the value object */
modify_test(FILE * ff,struct value_obj * vo)338 static int modify_test(FILE *ff, struct value_obj *vo)
339 {
340     int error = EOK;
341     const char *strval = "Domain100, Domain200, Domain300";
342 
343     TRACE_FLOW_ENTRY();
344 
345 
346     /* Update key length */
347     error = value_set_keylen(vo, strlen("foobar"));
348     if (error) {
349         printf("Failed to change key length %d.\n", error);
350         return error;
351     }
352 
353     /* Update value */
354     error = value_update(vo,
355                          strval,
356                          strlen(strval),
357                          INI_VALUE_CREATED,
358                          10);
359     if (error) {
360         printf("Failed to update value %d.\n", error);
361         return error;
362     }
363 
364 
365     /* Save value to the file */
366     error = save_value(ff, "foobar", vo);
367     if (error) {
368         printf("Failed to save value to file %d.\n", error);
369         return error;
370     }
371 
372     TRACE_FLOW_EXIT();
373     return EOK;
374 }
375 
376 
vo_basic_test(void)377 static int vo_basic_test(void)
378 {
379     int error = EOK;
380     const char *strvalue = "Test multi_word_value_that_will_"
381                            "be_split_between_several_lines_!";
382 
383     /* Other testing can be done with the following string:
384      * const char *strvalue = "Test multi word value that "
385      *                        "will be split between several lines";
386      */
387 
388     struct value_obj *vo = NULL;
389     uint32_t wrap = 0;
390     struct ini_comment *ic = NULL;
391     FILE *ff = NULL;
392 
393     TRACE_FLOW_ENTRY();
394 
395     errno = 0;
396     ff = fopen("test.ini","wt");
397     if (ff == NULL) {
398         error = errno;
399         printf("Failed to open file. Error %d.\n", error);
400         return error;
401     }
402 
403 
404     for (wrap = 0; wrap < 80; wrap++) {
405 
406         ic = NULL;
407         error = create_comment(wrap, &ic);
408         if (error) {
409             printf("Failed to create a new comment object %d.\n", error);
410             fclose(ff);
411             return error;
412         }
413 
414         vo = NULL;
415         error = value_create_new(strvalue,
416                                  strlen(strvalue),
417                                  INI_VALUE_CREATED,
418                                  3,
419                                  wrap,
420                                  ic,
421                                  &vo);
422         if (error) {
423             printf("Failed to create a new value object %d.\n", error);
424             ini_comment_destroy(ic);
425             fclose(ff);
426             return error;
427         }
428 
429         error = save_value(ff, "key", vo);
430         if (error) {
431             printf("Failed to save value to file %d.\n", error);
432             value_destroy(vo);
433             fclose(ff);
434             return error;
435         }
436 
437         value_destroy(vo);
438     }
439 
440     vo = NULL;
441 
442     /* Run other create test here */
443     error = other_create_test(ff, &vo);
444     if (error) {
445         printf("Create test failed %d.\n", error);
446         fclose(ff);
447         return error;
448     }
449 
450     /* Run modify test here */
451     error = modify_test(ff, vo);
452     if (error) {
453         printf("Modify test failed %d.\n", error);
454         fclose(ff);
455         value_destroy(vo);
456         return error;
457     }
458 
459     value_destroy(vo);
460 
461 
462     ic = NULL;
463     error = create_comment(100, &ic);
464     if (error) {
465         printf("Failed to create a new comment object %d.\n", error);
466         fclose(ff);
467         return error;
468     }
469 
470     ini_comment_print(ic, ff);
471 
472     ini_comment_destroy(ic);
473 
474     fclose(ff);
475 
476     TRACE_FLOW_EXIT();
477     return EOK;
478 }
479 
vo_copy_test(void)480 static int vo_copy_test(void)
481 {
482     int error = EOK;
483     const char *strvalue = "Test multi word value that "
484                            "will be split between several lines";
485 
486     struct value_obj *vo = NULL;
487     struct value_obj *vo_copy = NULL;
488     uint32_t wrap = 0;
489     struct ini_comment *ic = NULL;
490     FILE *ff = NULL;
491     char comment[100];
492 
493     TRACE_FLOW_ENTRY();
494 
495     VOOUT(printf("Copy test\n"));
496 
497     errno = 0;
498     ff = fopen("test.ini","a");
499     if (ff == NULL) {
500         error = errno;
501         printf("Failed to open file. Error %d.\n", error);
502         return error;
503     }
504 
505     error = ini_comment_create(&ic);
506     if (error) {
507         printf("Failed to create comment object\n");
508         fclose(ff);
509         return -1;
510     }
511 
512     error = ini_comment_append(ic, "#This is a copy test!");
513     if (error) {
514         printf("Failed to add a line to the comment %d.\n", error);
515         ini_comment_destroy(ic);
516         fclose(ff);
517         return error;
518     }
519 
520     error = ini_comment_append(ic, "#Replacable comment line");
521     if (error) {
522         printf("Failed to add a line to the comment %d.\n", error);
523         ini_comment_destroy(ic);
524         fclose(ff);
525         return error;
526     }
527 
528     error = value_create_new(strvalue,
529                              strlen(strvalue),
530                              INI_VALUE_CREATED,
531                              3,
532                              20,
533                              ic,
534                              &vo);
535     if (error) {
536         printf("Failed to create a new value object %d.\n", error);
537         ini_comment_destroy(ic);
538         fclose(ff);
539         return error;
540     }
541 
542     error = save_value(ff, "key", vo);
543     if (error) {
544         printf("Failed to save value to file %d.\n", error);
545         value_destroy(vo);
546         fclose(ff);
547         return error;
548     }
549 
550     for (wrap = 0; wrap < 80; wrap++) {
551 
552         TRACE_INFO_NUMBER("Iteration:", wrap);
553 
554         vo_copy = NULL;
555         error = value_copy(vo, &vo_copy);
556         if (error) {
557             printf("Failed to create a new value object %d.\n", error);
558             value_destroy(vo);
559             fclose(ff);
560             return error;
561         }
562 
563         error = value_set_boundary(vo_copy, wrap);
564         if (error) {
565             printf("Failed to set boundary %d.\n", error);
566             value_destroy(vo);
567             value_destroy(vo_copy);
568             fclose(ff);
569             return error;
570         }
571 
572         /* Get comment from the value */
573         ic = NULL;
574         error = value_extract_comment(vo_copy, &ic);
575         if (error) {
576             printf("Failed to extract comment %d.\n", error);
577             value_destroy(vo);
578             value_destroy(vo_copy);
579             fclose(ff);
580             return error;
581         }
582 
583         /* Replace comment in the value */
584         snprintf(comment, TEST_SIZE, ";This is value with boundary %d", wrap);
585         VOOUT(printf("Comment: %s\n", comment));
586         error = ini_comment_replace(ic, 1, comment);
587         if (error) {
588             printf("Failed to replace comment %d.\n", error);
589             value_destroy(vo);
590             value_destroy(vo_copy);
591             fclose(ff);
592             return error;
593         }
594 
595         /* Set comment into the value */
596         error = value_put_comment(vo_copy, ic);
597         if (error) {
598             printf("Failed to set comment %d.\n", error);
599             value_destroy(vo);
600             value_destroy(vo_copy);
601             fclose(ff);
602             return error;
603         }
604 
605         error = save_value(ff, "key", vo_copy);
606         if (error) {
607             printf("Failed to save value to file %d.\n", error);
608             value_destroy(vo);
609             value_destroy(vo_copy);
610             fclose(ff);
611             return error;
612         }
613 
614         value_destroy(vo_copy);
615     }
616 
617     value_destroy(vo);
618     fclose(ff);
619     TRACE_FLOW_EXIT();
620     return EOK;
621 }
622 
vo_show_test(void)623 static int vo_show_test(void)
624 {
625     VOOUT(system("cat test.ini"));
626     return EOK;
627 }
628 
vo_mc_test(void)629 static int vo_mc_test(void)
630 {
631     int error = EOK;
632     struct value_obj *vo1 = NULL;
633     struct value_obj *vo2 = NULL;
634     struct ini_comment *ic = NULL;
635 
636     TRACE_FLOW_ENTRY();
637 
638     VOOUT(printf("<=== Merge Comment Test ===>\n"));
639 
640     error = create_comment(1, &ic);
641     if (error) {
642         printf("Failed to create a new comment object %d.\n", error);
643         return error;
644     }
645 
646     error = value_create_new("test1",
647                              5,
648                              INI_VALUE_CREATED,
649                              3,
650                              80,
651                              ic,
652                              &vo1);
653     if (error) {
654         printf("Failed to create the first value object %d.\n", error);
655         ini_comment_destroy(ic);
656         return error;
657     }
658 
659     error = create_comment(2, &ic);
660     if (error) {
661         printf("Failed to create a new comment object %d.\n", error);
662         value_destroy(vo1);
663         return error;
664     }
665 
666     error = value_create_new("test2",
667                              5,
668                              INI_VALUE_CREATED,
669                              3,
670                              80,
671                              ic,
672                              &vo2);
673     if (error) {
674         printf("Failed to create the second value object %d.\n", error);
675         ini_comment_destroy(ic);
676         value_destroy(vo1);
677         return error;
678     }
679 
680     /* Merge comment from one value into another */
681     error = value_merge_comment(vo2, vo1);
682     if (error) {
683         printf("Failed to merge comments %d.\n", error);
684         value_destroy(vo1);
685         value_destroy(vo2);
686         return error;
687     }
688 
689     value_destroy(vo2);
690 
691     VOOUT(printf("<=== Key ===>\n"));
692     VOOUT(value_print("key", vo1));
693 
694     error = value_extract_comment(vo1, &ic);
695     if (error) {
696         printf("Failed to extract comment %d.\n", error);
697         value_destroy(vo1);
698         return error;
699     }
700 
701     value_destroy(vo1);
702 
703     VOOUT(printf("<=== Comment ===>\n"));
704     VOOUT(ini_comment_print(ic, stdout));
705     ini_comment_destroy(ic);
706 
707     TRACE_FLOW_EXIT();
708     return EOK;
709 }
710 
711 
712 /* Main function of the unit test */
main(int argc,char * argv[])713 int main(int argc, char *argv[])
714 {
715     int error = 0;
716     test_fn tests[] = { vo_basic_test,
717                         vo_copy_test,
718                         vo_show_test,
719                         vo_mc_test,
720                         NULL };
721     test_fn t;
722     int i = 0;
723     char *var;
724 
725     if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1;
726     else {
727         var = getenv("COMMON_TEST_VERBOSE");
728         if (var) verbose = 1;
729     }
730 
731     VOOUT(printf("Start\n"));
732 
733     while ((t = tests[i++])) {
734         error = t();
735         if (error) {
736             VOOUT(printf("Failed with error %d!\n", error));
737             return error;
738         }
739     }
740 
741     VOOUT(printf("Success!\n"));
742     return 0;
743 }
744