1 /* Copyright (C) 2003, 2010, 2013 TSUTSUMI Kikuo.
2 This file is part of the CCUnit Library.
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.
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.
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.
24 * ReadSuite module implementation.
26 #include <ccunit/CCUnitMakeSuite.h>
27 #include <ccunit/CCUnitLogMessage.h>
32 * @addtogroup CCUnitReadSuite
39 * @param test testdef to destroy.
41 static void destroyTestDef (_CCUnitTestDef* test)
45 safe_free (test->name);
46 safe_free (test->idname);
52 * @param test testdef.
53 * @param type test type.
54 * @param name test name.
56 static _CCUnitTestDef* initTestDef (_CCUnitTestDef* test,
57 CCUnitTestType_t type,
61 test->name = safe_strdup (name);
69 * @param test testdef to delete.
71 static void deleteTestDef (_CCUnitTestDef* test)
83 * destroy test suite def.
85 * @param suite test suite def.
87 static void destroyTestSuiteDef (_CCUnitTestSuiteDef* suite)
89 ccunit_deleteList (&suite->testdefs, (void(*)(void*))deleteTestDef);
90 destroyTestDef (&suite->testdef);
93 _CCUnitTestSuiteDef* ccunit_newTestSuiteDef (const char* name)
95 _CCUnitTestSuiteDef* suite = calloc (1, sizeof (*suite));
98 initTestDef (&suite->testdef, ccunitTypeSuite, name);
99 suite->testdef.dtor = (void(*)(_CCUnitTestDef*))destroyTestSuiteDef;
100 ccunit_initList (&suite->testdefs);
104 inline void ccunit_deleteTestSuiteDef (_CCUnitTestSuiteDef* suite)
106 deleteTestDef (&suite->testdef);
110 * add test to test suite.
112 * @param suite test suite to add.
113 * @param test test group.
114 * @return added test.
116 static _CCUnitTestDef* addTestDef (_CCUnitTestSuiteDef* suite,
117 _CCUnitTestDef* test)
121 ccunit_addList (&suite->testdefs, test);
126 * add test suite to test suite.
128 * @param suite test suite to add.
129 * @param test test suite.
130 * @return added test.
132 static inline _CCUnitTestDef* addTestSuiteDef (_CCUnitTestSuiteDef* suite,
133 _CCUnitTestSuiteDef* test)
138 name = test->testdef.name;
141 ccunit_log ("add test suite: %s", name);
142 return addTestDef (suite, &test->testdef);
146 * add test case to test suite.
148 * @param suite test suite to add.
149 * @param test test case.
150 * @return added test.
152 static inline _CCUnitTestDef* addTestCaseDef (_CCUnitTestSuiteDef* suite,
153 _CCUnitTestCaseDef* test)
158 name = test->testdef.name;
161 ccunit_log ("add test case: %s", name);
162 return addTestDef (suite, &test->testdef);
166 * create new test func.
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.
174 static _CCUnitFuncDef* newFuncDef (const char* scope,
179 struct _CCUnitFuncDef* f = calloc (1, sizeof (*f));
180 ccunit_log ("create new test func: %s %s", type, name);
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);
191 * delete test func def.
192 * @param func test func def to delete.
194 static void deleteFuncDef (_CCUnitFuncDef* func)
198 safe_free (func->scope);
199 safe_free (func->type);
200 safe_free (func->name);
201 safe_free (func->desc);
206 * destroy test case def.
207 * @param testCase test case def to destroy.
209 static void destroyTestCaseDef (_CCUnitTestCaseDef* testCase)
211 ccunit_deleteList (&testCase->testFuncs, (void(*)(void*))deleteFuncDef);
212 destroyTestDef (&testCase->testdef);
216 * create new test case def.
218 * @param name test case name.
220 static _CCUnitTestCaseDef* newTestCaseDef (const char* name)
222 _CCUnitTestCaseDef* testCase = calloc (1, sizeof (*testCase));
223 ccunit_log ("create new test case: %s", name);
226 initTestDef (&testCase->testdef, ccunitTypeTestCase, name);
227 testCase->testdef.dtor = (void(*)(_CCUnitTestDef*))destroyTestCaseDef;
228 ccunit_initList (&testCase->testFuncs);
233 * @defgroup _CCUnitLine _Line
234 * Read one line module.
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 */
252 * Current processing line.
254 static struct _CCUnitLine line;
257 * Get one line from stream.
258 * This func copies a read line on the global variable <code>line</code>.
260 * @return When reading succeeds, value except for the zero is
261 * returned. When an error occurs, a zero is returned.
263 static int readline ()
265 static const size_t MIN_LINE_BUF_LEN = 512;
270 /* buffer hasn't been allocate yet */
271 if (line.str == NULL)
273 line.capacity = MIN_LINE_BUF_LEN;
274 line.str = calloc (line.capacity, sizeof(line.str[0]));
278 /* shrink to minimum size */
279 else if (line.capacity > MIN_LINE_BUF_LEN)
282 line.capacity = MIN_LINE_BUF_LEN;
283 str = realloc (line.str, line.capacity);
288 restSize = line.capacity;
291 while ((sp = fgets (insertAt, restSize, line.ifp)) != NULL)
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? */
302 /* to extend capacity for rest of line */
303 size_t newCapacity = line.capacity * 2 / 3;
304 char* str = realloc (line.str, newCapacity);
307 ccunit_log ("/* no more memory */");
311 restSize = newCapacity - line.capacity;
312 insertAt = str + line.capacity;
313 line.capacity = newCapacity;
321 tail = line.str + line.length - 1;
329 if (*tail == '\r') /* for dos and/or mac? */
342 * read contents of doc comment.
344 * @return comment string. or NULL when error occurred.
346 static char* readDocCommentContents ()
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;
357 ccunit_dbg ("read from:%lu: \"%s\"", line.lno, start);
358 /* skip white spaces */
359 for (; *start && isspace ((int)*start); start ++)
361 /* skip leading block comment '*'<WSP>... */
364 else if (start[1] == '/') /* eoc */
366 else /* skip white spaces */
367 for (start ++; *start && isspace ((int)*start); start ++)
369 /* skip doxygen command line */
372 ccunit_log ("skip doxygen javadoc style comment");
373 for (end = start + 1; *end; end ++)
374 if (end[0] == '*' && end[1] == '/')
376 ccunit_log ("end of comment");
382 /* seek to eol or end of comment */
383 for (end = start; *end; end ++)
384 if (end[0] == '*' && end[1] == '/')
386 ccunit_log ("end of comment");
390 /* trim trailing white space */
391 for (end --; start < end; end --)
392 if (!isspace ((int)*end))
397 /* did a comment exist? */
400 int len = (int)(end - start);
401 char* newContent = realloc (content, length + len + 2);
404 ccunit_err ("no more memory");
408 newContent[length ++] = ' '; /* word space */
409 memcpy (newContent + length, start, len);
411 newContent[length] = '\0';
412 content = newContent;
413 ccunit_log ("get: \"%*.*s\"", len, len, start);
415 if (eoc || !readline ())
419 ccunit_log ("comment content: \"%s\"", !content ? "" : content);
424 * read document comment.
426 * @return comment content if matched, or NULL if not matched.
428 static char* readDocComment ()
430 static const char cmnt[] = "/**";
431 if (strncmp (line.str, cmnt, sizeof (cmnt) - 1) != 0) /* not a comment */
433 else if (line.str[3] == '*' || line.str[3] == '/') /* not doc */
437 ccunit_dbg ("found doc comment:%lu: %s", line.lno, line.str);
438 return readDocCommentContents ();
443 static const char* testTypeStr[] = {
450 * @param type test type.
451 * @param str comment string.
454 static const char* getTestName (CCUnitTestType_t type, const char* str)
456 static const char* prefixStr[] = {
457 "TEST CASE:", "TEST SUITE:",
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)
466 for (name = str + prefixLen; *name; name ++)
467 if (!isspace ((int)*name))
472 ccunit_err ("no test %s name: %s. near line %lu",
473 testType, str, line.lno);
477 ccunit_dbg ("not a test %s name: %s", testType, str);
482 * get end of test string.
483 * @param type test type.
485 * @return name of test.
488 getEndOfTest (CCUnitTestType_t type, const char* str)
490 static const char* prefixStr[] = {
491 "END TEST CASE", "END TEST SUITE"
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)
500 name = str + prefixLen;
501 if (*name && !isspace ((int)*name) && !ispunct ((int)*name))
504 ccunit_dbg ("not an end of test %s: %s", testType, str);
508 for (; *name; name ++)
509 if (!isspace ((int)*name))
514 ccunit_log ("end of test %s: %s", testType, name);
518 ccunit_dbg ("not an end of test %s: %s", testType, str);
525 * @param type required type string.
526 * @param prefix required func name prefix.
527 * @param desc description.
528 * @return funcdef object.
530 static _CCUnitFuncDef* readTestFunc (const char* type,
534 const char* scope = "static";
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))
541 if (strncmp (typ, scope, strlen (scope)) != 0)
545 typ += strlen (scope);
546 if (*typ && !isspace ((int)*typ))
548 ccunit_dbg ("type mismatch: %s %s", type, typ);
554 if (!isspace ((int)*typ))
560 ccunit_err ("unexpected EOF");
566 if (strncmp (typ, type, strlen (type)) != 0)
568 ccunit_dbg ("type mismatch: %s %s", type, typ);
571 name = typ + strlen (type);
572 if (*name && !isspace ((int)*name))
574 ccunit_dbg ("type mismatch: %s %s", type, name);
579 for (; *name; name ++)
580 if (!isspace ((int)*name))
586 ccunit_err ("unexpected EOF");
591 if (strncmp (name, prefix, strlen(prefix)) == 0)
594 for (tail = name + 1; *tail; tail ++)
595 if (isspace ((int)*tail) || *tail == '(')
600 return newFuncDef (scope, type, name, desc);
603 ccunit_dbg ("name mismatch: %s %s", prefix, name);
608 * read test case function.
610 * @param suite parent suite.
611 * @param cname test case name to read.
613 static void readTestCase (_CCUnitTestSuiteDef* suite, const char* cname)
615 _CCUnitTestCaseDef* testCase;
616 _CCUnitFuncDef* f = NULL;
622 testCase = newTestCaseDef (cname);
625 addTestCaseDef (suite, testCase);
628 /* setUpBeforeClass function def */
629 if ((f = readTestFunc ("void", "setUpBeforeClass", desc)) != NULL)
631 ccunit_addList (&testCase->testFuncs, f);
634 /* tearDownAfterClass function def */
635 else if ((f = readTestFunc ("void", "tearDownAfterClass", desc)) != NULL)
637 ccunit_addList (&testCase->testFuncs, f);
640 /* setUp function def */
641 else if ((f = readTestFunc ("void", "setUp", desc)) != NULL)
643 ccunit_addList (&testCase->testFuncs, f);
646 /* tearDown function def */
647 else if ((f = readTestFunc ("void", "tearDown", desc)) != NULL)
649 ccunit_addList (&testCase->testFuncs, f);
652 /* if test case function def, then read as test func. */
653 else if ((f = readTestFunc ("void", "test", desc)) != NULL)
655 ccunit_addList (&testCase->testFuncs, f);
658 /* if current line is javaDoc comment, then read as description. */
659 else if ((doc = readDocComment ()) != NULL)
661 if ((name = getTestName (ccunitTypeTestCase, doc)) != NULL)
663 readTestCase (suite, name);
666 else if ((name = getEndOfTest (ccunitTypeTestCase, doc))
669 ccunit_log ("end test case: %s", testCase->testdef.name);
684 * read test suite def.
686 * @param suite test suitedef.
688 static void readSuite (_CCUnitTestSuiteDef* suite)
696 /* if current line is javaDoc comment, then read as description. */
697 if ((doc = readDocComment ()) != NULL)
699 if ((name = getTestName (ccunitTypeTestCase, doc)) != NULL)
701 readTestCase (suite, name);
704 else if ((name = getEndOfTest (ccunitTypeTestCase, doc)) != NULL)
706 ccunit_err ("%s:%lu: invalid end test case comment '%s'",
707 line.fname, line.lno, doc);
710 else if ((name = getTestName (ccunitTypeSuite, doc)) != NULL)
712 _CCUnitTestSuiteDef* newSuite;
713 newSuite = ccunit_newTestSuiteDef (name);
716 addTestSuiteDef (suite, newSuite);
717 readSuite (newSuite);
720 else if ((name = getEndOfTest (ccunitTypeSuite, doc)) != NULL)
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)
734 ccunit_err ("%s:%lu: missing test case start comment '%s': ignored",
735 line.fname, line.lno, line.str);
748 void ccunit_readSuite (const char* fname, _CCUnitTestSuiteDef* suite)
750 if (strcmp (fname, "-") == 0) /* special file name '-' as stdin */
753 line.fname = "(stdin)";
757 line.ifp = fopen (fname, "r");
758 if (!line.ifp) /* open error */
760 ccunit_err ("can't open file '%s': %s. skipped.\n",
761 fname, strerror (errno));
767 safe_free (line.str);
768 if (line.ifp != NULL && line.ifp != stdin)
770 memset (&line, 0, sizeof (line));