OSDN Git Service

Added '2013' in copyrights
[ccunit/ccunit.git] / src / ccunit / CCUnitReadSuite.c
1 /* Copyright (C) 2003, 2010, 2013 TSUTSUMI Kikuo.
2    This file is part of the CCUnit Library.
3
4    The CCUnit Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public License
6    as published by the Free Software Foundation; either version 2.1 of
7    the License, or (at your option) any later version.
8
9    The CCUnit Library is distributed in the hope that it will be
10    useful, but WITHOUT ANY WARRANTY; without even the implied warranty
11    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the CCUnit Library; see the file COPYING.LESSER.
16    If not, write to the Free Software Foundation, Inc., 59 Temple
17    Place - Suite 330, Boston, MA 02111-1307, USA.  
18 */
19 /*
20  * $Id$
21  */
22
23 /** @file
24  * ReadSuite module implementation.
25  */
26 #include <ccunit/CCUnitMakeSuite.h>
27 #include <ccunit/CCUnitLogMessage.h>
28 #include <ctype.h>
29 #include <errno.h>
30
31 /**
32  * @addtogroup CCUnitReadSuite
33  * @{
34  */
35
36 /**
37  * destroy test def.
38  *
39  * @param test testdef to destroy.
40  */
41 static void destroyTestDef (_CCUnitTestDef* test)
42 {
43   if (!test)
44     return;
45   safe_free (test->name);
46   safe_free (test->idname);
47 }
48
49 /**
50  * init test def.
51  *
52  * @param test testdef.
53  * @param type test type.
54  * @param name test name.
55  */
56 static _CCUnitTestDef* initTestDef (_CCUnitTestDef* test,
57                                     CCUnitTestType_t type,
58                                     const char* name)
59 {
60   test->type = type;
61   test->name = safe_strdup (name);
62   test->idname = NULL;
63   return test;
64 }
65
66 /**
67  * delete test def.
68  *
69  * @param test testdef to delete.
70  */
71 static void deleteTestDef (_CCUnitTestDef* test)
72 {
73   if (!test)
74     return;
75   if (!test->dtor)
76     ;
77   else
78     test->dtor (test);
79   safe_free (test);
80 }
81
82 /**
83  * destroy test suite def.
84  *
85  * @param suite test suite def.
86  */
87 static void destroyTestSuiteDef (_CCUnitTestSuiteDef* suite)
88 {
89   ccunit_deleteList (&suite->testdefs, (void(*)(void*))deleteTestDef);
90   destroyTestDef (&suite->testdef);
91 }
92
93 _CCUnitTestSuiteDef* ccunit_newTestSuiteDef (const char* name)
94 {
95   _CCUnitTestSuiteDef* suite = calloc (1, sizeof (*suite));
96   if (!suite)
97     return suite;
98   initTestDef (&suite->testdef, ccunitTypeSuite, name);
99   suite->testdef.dtor = (void(*)(_CCUnitTestDef*))destroyTestSuiteDef;
100   ccunit_initList (&suite->testdefs);
101   return suite;
102 }
103
104 inline void ccunit_deleteTestSuiteDef (_CCUnitTestSuiteDef* suite)
105 {
106   deleteTestDef (&suite->testdef);
107 }
108
109 /**
110  * add test to test suite.
111  *
112  * @param suite test suite to add.
113  * @param test test group.
114  * @return added test.
115  */
116 static _CCUnitTestDef* addTestDef (_CCUnitTestSuiteDef* suite,
117                                    _CCUnitTestDef* test)
118 {
119   if (!suite || !test)
120     return NULL;
121   ccunit_addList (&suite->testdefs, test);
122   return test;
123 }
124
125 /**
126  * add test suite to test suite.
127  *
128  * @param suite test suite to add.
129  * @param test test suite.
130  * @return added test.
131  */
132 static inline _CCUnitTestDef* addTestSuiteDef (_CCUnitTestSuiteDef* suite,
133                                                _CCUnitTestSuiteDef* test)
134 {
135   const char* name;
136   if (!suite || !test)
137     return NULL;
138   name = test->testdef.name;
139   if (!name)
140     name = "";
141   ccunit_log ("add test suite: %s", name);
142   return addTestDef (suite, &test->testdef);
143 }
144
145 /**
146  * add test case to test suite.
147  *
148  * @param suite test suite to add.
149  * @param test test case.
150  * @return added test.
151  */
152 static inline _CCUnitTestDef* addTestCaseDef (_CCUnitTestSuiteDef* suite,
153                                                  _CCUnitTestCaseDef* test)
154 {
155   const char* name;
156   if (!suite || !test)
157     return NULL;
158   name = test->testdef.name;
159   if (!name)
160     name = "";
161   ccunit_log ("add test case: %s", name);
162   return addTestDef (suite, &test->testdef);
163 }
164
165 /**
166  * create new test func.
167  *
168  * @param scope scope.
169  * @param type return type of func.
170  * @param name func name.
171  * @param desc description.
172  * @return new test func def.
173  */
174 static _CCUnitFuncDef* newFuncDef (const char* scope,
175                                    const char* type,
176                                    const char* name,
177                                    const char* desc)
178 {
179   struct _CCUnitFuncDef* f = calloc (1, sizeof (*f));
180   ccunit_log ("create new test func: %s %s", type, name);
181   if (!f)
182     return f;
183   f->scope = !scope ? strdup ("extern") : safe_strdup (scope);
184   f->type = safe_strdup (type);
185   f->name = safe_strdup (name);
186   f->desc = !desc ? safe_strdup (name) : strdup (desc);
187   return f;
188 }
189
190 /**
191  * delete test func def.
192  * @param func test func def to delete.
193  */
194 static void deleteFuncDef (_CCUnitFuncDef* func)
195 {
196   if (!func)
197     return;
198   safe_free (func->scope);
199   safe_free (func->type);
200   safe_free (func->name);
201   safe_free (func->desc);
202   free (func);
203 }
204
205 /**
206  * destroy test case def.
207  * @param testCase test case def to destroy.
208  */
209 static void destroyTestCaseDef (_CCUnitTestCaseDef* testCase)
210 {
211   ccunit_deleteList (&testCase->testFuncs, (void(*)(void*))deleteFuncDef);
212   destroyTestDef (&testCase->testdef);
213 }
214
215 /**
216  * create new test case def.
217  *
218  * @param name test case name.
219  */
220 static _CCUnitTestCaseDef* newTestCaseDef (const char* name)
221 {
222   _CCUnitTestCaseDef* testCase = calloc (1, sizeof (*testCase));
223   ccunit_log ("create new test case: %s", name);
224   if (!testCase)
225     return NULL;
226   initTestDef (&testCase->testdef, ccunitTypeTestCase, name);
227   testCase->testdef.dtor = (void(*)(_CCUnitTestDef*))destroyTestCaseDef;
228   ccunit_initList (&testCase->testFuncs);
229   return testCase;
230 }
231
232 /**
233  * @defgroup _CCUnitLine _Line
234  * Read one line module.
235  * @{
236  */
237
238 /**
239  * Read line.
240  */
241 struct _CCUnitLine
242 {
243   char* str;                                    /**< read line buffer */
244   size_t length;                                /**< line length */
245   size_t capacity;                              /**< buffer capacity */
246   unsigned long lno;                            /**< line number */
247   FILE* ifp;                                    /**< input stream */
248   const char* fname;                            /**< input file name */
249 };
250
251 /**
252  * Current processing line.
253  */
254 static struct _CCUnitLine line;
255
256 /**
257  * Get one line from stream.
258  * This func copies a read line on the global variable <code>line</code>.
259  *
260  * @return When reading succeeds, value except for the zero is
261  * returned. When an error occurs, a zero is returned.
262  */
263 static int readline ()
264 {
265   static const size_t MIN_LINE_BUF_LEN = 512;
266   char* insertAt;
267   size_t restSize;
268   char* sp;
269   char* tail;
270   /* buffer hasn't been allocate yet */
271   if (line.str == NULL)
272     {
273       line.capacity = MIN_LINE_BUF_LEN;
274       line.str = calloc (line.capacity, sizeof(line.str[0]));
275       line.length = 0;
276       line.lno = 0;
277     }
278   /* shrink to minimum size */
279   else if (line.capacity > MIN_LINE_BUF_LEN)
280     {
281       char* str;
282       line.capacity = MIN_LINE_BUF_LEN;
283       str = realloc (line.str, line.capacity);
284       if (str)
285         line.str = str;
286     }
287   insertAt = line.str;
288   restSize = line.capacity;
289   line.length = 0;
290   sp = 0;
291   while ((sp = fgets (insertAt, restSize, line.ifp)) != NULL)
292     {
293       line.length += strlen(insertAt);
294       /* read complete whole line */
295       if (line.str[line.length-1] == '\n'
296           || line.str[line.length-1] == '\r')   /* for mac? */
297         {
298           break;
299         }
300       else
301         {
302           /* to extend capacity for rest of line */
303           size_t newCapacity = line.capacity * 2 / 3;
304           char* str = realloc (line.str, newCapacity);
305           if (!str)
306             {
307               ccunit_log ("/* no more memory */");
308               return 0;
309             }
310           line.str = str;
311           restSize = newCapacity - line.capacity;
312           insertAt = str + line.capacity;
313           line.capacity = newCapacity;
314         }
315     }
316   if (!sp)
317     return 0;
318   /* chomp CR/LF */
319   if (line.length > 0)
320     {
321       tail = line.str + line.length - 1;
322       if (*tail == '\n')
323         {
324           line.length --;
325           *tail = '\0';
326           if (line.length > 0)
327             tail --;
328         }
329       if (*tail == '\r')        /* for dos and/or mac? */
330         {
331           line.length --;
332           *tail = '\0';
333           if (line.length > 0)
334             tail --;
335         }
336     }
337   line.lno ++;
338   return 1;
339 }
340
341 /**
342  * read contents of doc comment.
343  *
344  * @return comment string. or NULL when error occurred.
345  */
346 static char* readDocCommentContents ()
347 {
348   bool eoc = false;                             /* reach end of comment */
349   char* content = NULL;                         /* comment content */
350   size_t length = 0;                            /* content length */
351   char* start = NULL;                           /* start of content */
352   char* end = NULL;                             /* end of content */
353   ccunit_log ("readDocCommentContent");
354   start = line.str + 2;
355   while (!eoc)
356     {
357       ccunit_dbg ("read from:%lu: \"%s\"", line.lno, start);
358       /* skip white spaces */
359       for (; *start && isspace ((int)*start); start ++)
360         ;
361       /* skip leading block comment '*'<WSP>... */
362       if (*start != '*')
363         ;
364       else if (start[1] == '/')                 /* eoc */
365         ;
366       else                                      /* skip white spaces */
367         for (start ++; *start && isspace ((int)*start); start ++)
368           ;
369       /* skip doxygen command line */
370       if (*start == '@')
371         {
372           ccunit_log ("skip doxygen javadoc style comment");
373           for (end = start + 1; *end; end ++)
374             if (end[0] == '*' && end[1] == '/')
375               {
376                 ccunit_log ("end of comment");
377                 eoc = true;
378                 break;
379               }
380           start = end;
381         }
382       /* seek to eol or end of comment */
383       for (end = start; *end; end ++)
384         if (end[0] == '*' && end[1] == '/')
385           {
386             ccunit_log ("end of comment");
387             eoc = true;
388             break;
389           }
390       /* trim trailing white space */
391       for (end --; start < end; end --)
392         if (!isspace ((int)*end))
393           {
394             end ++;
395             break;
396           }
397       /* did a comment exist? */
398       if (start < end)
399         {
400           int len = (int)(end - start);
401           char* newContent = realloc (content, length + len + 2);
402           if (!newContent)
403             {
404               ccunit_err ("no more memory");
405               break;
406             }
407           if (length > 0)
408             newContent[length ++] = ' ';        /* word space */
409           memcpy (newContent + length, start, len);
410           length += len;
411           newContent[length] = '\0';
412           content = newContent;
413           ccunit_log ("get: \"%*.*s\"", len, len, start);
414         }
415       if (eoc || !readline ())
416         break;
417       start = line.str;
418     }
419   ccunit_log ("comment content: \"%s\"", !content ? "" : content);
420   return content;
421 }
422
423 /**
424  * read document comment.
425  *
426  * @return comment content if matched, or NULL if not matched.
427  */
428 static char* readDocComment ()
429 {
430   static const char cmnt[] = "/**";
431   if (strncmp (line.str, cmnt, sizeof (cmnt) - 1) != 0) /* not a comment */
432     ;
433   else if (line.str[3] == '*' || line.str[3] == '/') /* not doc */
434     ;
435   else
436     {
437       ccunit_dbg ("found doc comment:%lu: %s", line.lno, line.str);
438       return readDocCommentContents ();
439     }
440   return NULL;
441 }
442
443 static const char* testTypeStr[] = {
444   "case", "suite"
445 };
446
447 /**
448  * get test def.
449  *
450  * @param type test type.
451  * @param str comment string.
452  * @return test name.
453  */
454 static const char* getTestName (CCUnitTestType_t type, const char* str)
455 {
456   static const char* prefixStr[] = {
457     "TEST CASE:", "TEST SUITE:",
458   };
459   const int typeid = (type == ccunitTypeSuite) ? 1 : 0;
460   const char* testType = testTypeStr[typeid];
461   const char* prefix = prefixStr[typeid];
462   const size_t prefixLen = strlen (prefix);
463   const char* name = NULL;
464   if (strncasecmp (str, prefix, prefixLen) == 0)
465     {
466       for (name = str + prefixLen; *name; name ++)
467         if (!isspace ((int)*name))
468           break;
469       if (!*name)
470         {
471           name = NULL;
472           ccunit_err ("no test %s name: %s. near line %lu",
473                       testType, str, line.lno);
474         }
475     }
476   else
477     ccunit_dbg ("not a test %s name: %s", testType, str);
478   return name;
479 }
480
481 /**
482  * get end of test string.
483  * @param type test type.
484  * @param str string.
485  * @return name of test.
486  */
487 static const char*
488 getEndOfTest (CCUnitTestType_t type, const char* str)
489 {
490   static const char* prefixStr[] = {
491     "END TEST CASE", "END TEST SUITE"
492   };
493   const int typeid = (type == ccunitTypeSuite) ? 1 : 0;
494   const char* testType = testTypeStr[typeid];
495   const char* prefix = prefixStr[typeid];
496   const size_t prefixLen = strlen (prefix);
497   const char* name = NULL;
498   if (strncasecmp (str, prefix, prefixLen) == 0)
499     {
500       name = str + prefixLen;
501       if (*name && !isspace ((int)*name) && !ispunct ((int)*name))
502         {
503           name = NULL;
504           ccunit_dbg ("not an end of test %s: %s", testType, str);
505         }
506       else
507         {
508           for (; *name; name ++)
509             if (!isspace ((int)*name))
510               break;
511           if (!*name)
512             ;
513           else
514             ccunit_log ("end of test %s: %s", testType, name);
515         }
516     }
517   else
518     ccunit_dbg ("not an end of test %s: %s", testType, str);
519   return name;
520 }
521
522 /**
523  * read test funcdef.
524  *
525  * @param type required type string.
526  * @param prefix required func name prefix.
527  * @param desc description.
528  * @return funcdef object.
529  */
530 static _CCUnitFuncDef* readTestFunc (const char* type,
531                                     const char* prefix,
532                                     const char* desc)
533 {
534   const char* scope = "static";
535   char* typ;
536   char* name;
537   ccunit_dbg ("read func: %s %s... from '%s'", type, prefix, line.str);
538   for (typ = line.str; *typ; typ ++)
539     if (!isspace ((int)*typ))
540       break;
541   if (strncmp (typ, scope, strlen (scope)) != 0)
542     scope = "extern";
543   else
544     {
545       typ += strlen (scope);
546       if (*typ && !isspace ((int)*typ))
547         {
548           ccunit_dbg ("type mismatch: %s %s", type, typ);
549           return NULL;
550         }
551       for (;;)
552         {
553           for (; *typ; typ ++)
554             if (!isspace ((int)*typ))
555               break;
556           if (*typ)
557             break;
558           if (!readline ())
559             {
560               ccunit_err ("unexpected EOF");
561               return NULL;
562             }
563           typ = line.str;
564         }
565     }
566   if (strncmp (typ, type, strlen (type)) != 0)
567     {
568       ccunit_dbg ("type mismatch: %s %s", type, typ);
569       return NULL;
570     }
571   name = typ + strlen (type);
572   if (*name && !isspace ((int)*name))
573     {
574       ccunit_dbg ("type mismatch: %s %s", type, name);
575       return NULL;
576     }
577   for (;;)
578     {
579       for (; *name; name ++)
580         if (!isspace ((int)*name))
581           break;
582       if (*name)
583         break;
584       if (!readline ())
585         {
586           ccunit_err ("unexpected EOF");
587           return NULL;
588         }
589       name = line.str;
590     }
591   if (strncmp (name, prefix, strlen(prefix)) == 0)
592     {
593       char* tail;
594       for (tail = name + 1; *tail; tail ++)
595         if (isspace ((int)*tail) || *tail == '(')
596           {
597             *tail = '\0';
598             break;
599           }
600       return newFuncDef (scope, type, name, desc);
601     }
602   else
603     ccunit_dbg ("name mismatch: %s %s", prefix, name);
604   return NULL;
605 }
606
607 /**
608  * read test case function.
609  *
610  * @param suite parent suite.
611  * @param cname test case name to read.
612  */
613 static void readTestCase (_CCUnitTestSuiteDef* suite, const char* cname)
614 {
615   _CCUnitTestCaseDef* testCase;
616   _CCUnitFuncDef* f = NULL;
617   const char* name;
618   char* doc;
619   char* desc = NULL;
620   if (!suite)
621     return;
622   testCase = newTestCaseDef (cname);
623   if (!testCase)
624     return;
625   addTestCaseDef (suite, testCase);
626   while (readline ())
627     {
628       /* setUpBeforeClass function def */
629       if ((f = readTestFunc ("void", "setUpBeforeClass", desc)) != NULL)
630         {
631           ccunit_addList (&testCase->testFuncs, f);
632           safe_free (desc);
633         }
634       /* tearDownAfterClass function def */
635       else if ((f = readTestFunc ("void", "tearDownAfterClass", desc)) != NULL)
636         {
637           ccunit_addList (&testCase->testFuncs, f);
638           safe_free (desc);
639         }
640       /* setUp function def */
641       else if ((f = readTestFunc ("void", "setUp", desc)) != NULL)
642         {
643           ccunit_addList (&testCase->testFuncs, f);
644           safe_free (desc);
645         }
646       /* tearDown function def */
647       else if ((f = readTestFunc ("void", "tearDown", desc)) != NULL)
648         {
649           ccunit_addList (&testCase->testFuncs, f);
650           safe_free (desc);
651         }
652       /* if test case function def, then read as test func. */
653       else if ((f = readTestFunc ("void", "test", desc)) != NULL)
654         {
655           ccunit_addList (&testCase->testFuncs, f);
656           safe_free (desc);
657         }
658       /* if current line is javaDoc comment, then read as description. */
659       else if ((doc = readDocComment ()) != NULL)
660         {
661           if ((name = getTestName (ccunitTypeTestCase, doc)) != NULL)
662             {
663               readTestCase (suite, name);
664               safe_free (doc);
665             }
666           else if ((name = getEndOfTest (ccunitTypeTestCase, doc))
667                    != NULL)
668             {
669               ccunit_log ("end test case: %s", testCase->testdef.name);
670               safe_free (doc);
671               break;
672             }
673           safe_free (desc);
674           desc = doc;
675           doc = NULL;
676         }
677       else
678         ;
679     }
680   safe_free (desc);
681 }
682
683 /**
684  * read test suite def.
685  *
686  * @param suite test suitedef.
687  */
688 static void readSuite (_CCUnitTestSuiteDef* suite)
689 {
690   _CCUnitFuncDef* f;
691   const char* name;
692   char* doc = NULL;
693   char* desc = NULL;
694   while (readline ())
695     {
696       /* if current line is javaDoc comment, then read as description. */
697       if ((doc = readDocComment ()) != NULL)
698         {
699           if ((name = getTestName (ccunitTypeTestCase, doc)) != NULL)
700             {
701               readTestCase (suite, name);
702               safe_free (doc);
703             }
704           else if ((name = getEndOfTest (ccunitTypeTestCase, doc)) != NULL)
705             {
706               ccunit_err ("%s:%lu: invalid end test case comment '%s'",
707                           line.fname, line.lno, doc);
708               safe_free (doc);
709             }
710           else if ((name = getTestName (ccunitTypeSuite, doc)) != NULL)
711             {
712               _CCUnitTestSuiteDef* newSuite;
713               newSuite = ccunit_newTestSuiteDef (name);
714               if (!newSuite)
715                 break;
716               addTestSuiteDef (suite, newSuite);
717               readSuite (newSuite);
718               safe_free (doc);
719             }
720           else if ((name = getEndOfTest (ccunitTypeSuite, doc)) != NULL)
721             {
722               break;
723             }
724           safe_free (desc);
725           desc = doc;
726           doc = NULL;
727         }
728       else if ((f = readTestFunc ("void", "test", desc)) != NULL
729                || (f = readTestFunc ("void", "setUpBeforeClass", desc)) != NULL
730                || (f = readTestFunc ("void", "tearDownAfterClass", desc)) != NULL
731                || (f = readTestFunc ("void", "setUp", desc)) != NULL
732                || (f = readTestFunc ("void", "tearDown", desc)) != NULL)
733         {
734           ccunit_err ("%s:%lu: missing test case start comment '%s': ignored",
735                       line.fname, line.lno, line.str);
736           deleteFuncDef (f);
737           safe_free (desc);
738         }
739       else
740         ;
741     }
742   safe_free (doc);
743   safe_free (desc);
744 }
745
746 /** @} */
747
748 void ccunit_readSuite (const char* fname, _CCUnitTestSuiteDef* suite)
749 {
750   if (strcmp (fname, "-") == 0) /* special file name '-' as stdin  */
751     {
752       line.ifp = stdin;
753       line.fname = "(stdin)";
754     }
755   else
756     {
757       line.ifp = fopen (fname, "r");
758       if (!line.ifp)                            /* open error */
759         {
760           ccunit_err ("can't open file '%s': %s.  skipped.\n",
761                       fname, strerror (errno));
762           return;
763         }
764       line.fname = fname;
765     }
766   readSuite (suite);
767   safe_free (line.str);
768   if (line.ifp != NULL && line.ifp != stdin)
769     fclose (line.ifp);
770   memset (&line, 0, sizeof (line));
771 }
772
773 /** @} */