1 /* Copyright (C) 2003 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.
23 #include <ccunit/CCUnitMakeSuite.h>
24 #include <ccunit/CCUnitLogMessage.h>
32 * @param test testdef to destroy.
34 static void destroyTestDef (CCUnitTestDef* test)
38 safe_free (test->name);
44 * @param test testdef.
45 * @param type test type.
46 * @param name test name.
48 static CCUnitTestDef* initTestDef (CCUnitTestDef* test,
49 CCUnitTestType_t type,
53 test->name = safe_strdup (name);
60 * @param test testdef to delete.
62 static void deleteTestDef (CCUnitTestDef* test)
74 * destroy test suite def.
76 * @param suite test suite def.
78 static void destroyTestSuiteDef (CCUnitTestSuiteDef* suite)
80 ccunit_deleteList (&suite->testdefs, (void(*)(void*))deleteTestDef);
81 destroyTestDef (&suite->testdef);
84 CCUnitTestSuiteDef* ccunit_newTestSuiteDef (const char* name)
86 CCUnitTestSuiteDef* suite = calloc (1, sizeof (*suite));
89 initTestDef (&suite->testdef, ccunitTypeSuite, name);
90 suite->testdef.dtor = (void(*)(CCUnitTestDef*))destroyTestSuiteDef;
91 ccunit_initList (&suite->testdefs);
96 * add test to test suite.
98 * @param suite test suite to add.
99 * @param test test group.
100 * @return added test.
102 static CCUnitTestDef* addTestDef (CCUnitTestSuiteDef* suite,
107 ccunit_log ("add test: %s", test->name);
108 ccunit_addList (&suite->testdefs, test);
113 * create new test fixture.
115 * @param type return type of fixture.
116 * @param name fixture name.
117 * @param desc description.
118 * @return new test fixture def.
120 static CCUnitTestFixtureDef* newTestFixtureDef (const char* type,
124 struct CCUnitTestFixtureDef* f = calloc (1, sizeof (*f));
125 ccunit_log ("create new test func: %s %s", type, name);
128 f->type = safe_strdup (type);
129 f->name = safe_strdup (name);
130 f->desc = !desc ? safe_strdup (name) : strdup (desc);
135 * delete test func def.
136 * @param func test func def to delete.
138 static void deleteTestFixtureDef (CCUnitTestFixtureDef* func)
142 safe_free (func->type);
143 safe_free (func->name);
144 safe_free (func->desc);
149 * destroy test case def.
150 * @param testCase test case def to destroy.
152 static void destroyTestCaseDef (CCUnitTestCaseDef* testCase)
154 ccunit_deleteList (&testCase->fixtures, (void(*)(void*))deleteTestFixtureDef);
155 deleteTestFixtureDef (testCase->setUp);
156 deleteTestFixtureDef (testCase->tearDown);
157 destroyTestDef (&testCase->testdef);
161 * create new test case def.
163 * @param name test case name.
164 * @param setUp test case setup func def.
165 * @param tearDown test case tearDown func def.
167 static CCUnitTestCaseDef* newTestCaseDef (const char* name)
169 CCUnitTestCaseDef* testCase = calloc (1, sizeof (*testCase));
170 ccunit_log ("create new test case: %s", name);
173 initTestDef (&testCase->testdef, ccunitTypeCase, name);
174 testCase->testdef.dtor = (void(*)(CCUnitTestDef*))destroyTestCaseDef;
175 ccunit_initList (&testCase->fixtures);
184 char* str; /**< read line buffer */
185 size_t length; /**< line length */
186 size_t capacity; /**< buffer capacity */
187 unsigned long lno; /**< line number */
188 FILE* ifp; /**< input stream */
189 const char* fname; /**< input file name */
193 * Current processing line.
195 static struct _CCUnitLine line;
198 * Get one line from stream.
199 * This fixture copies a read line on the global variable <code>line</code>.
201 * @return When reading succeeds, value except for the zero is
202 * returned. When an error occurs, a zero is returned.
204 static int readline ()
206 static const size_t MIN_LINE_BUF_LEN = 512;
207 /* buffer hasn't been allocate yet */
208 if (line.str == NULL)
210 line.capacity = MIN_LINE_BUF_LEN;
211 line.str = calloc (line.capacity, sizeof(line.str[0]));
215 /* shrink to minimum size */
216 else if (line.capacity > MIN_LINE_BUF_LEN)
218 line.capacity = MIN_LINE_BUF_LEN;
219 char* str = realloc (line.str, line.capacity);
223 char* insertAt = line.str;
224 size_t restSize = line.capacity;
227 while ((sp = fgets (insertAt, restSize, line.ifp)) != NULL)
229 line.length += strlen(insertAt);
230 /* read complete whole line */
231 if (line.str[line.length-1] == '\n'
232 || line.str[line.length-1] == '\r') /* for mac? */
238 /* to extend capacity for rest of line */
239 size_t newCapacity = line.capacity * 2 / 3;
240 char* str = realloc (line.str, newCapacity);
243 ccunit_log ("/* no more memory */");
247 restSize = newCapacity - line.capacity;
248 insertAt = str + line.capacity;
249 line.capacity = newCapacity;
255 char* tail = line.str + line.length - 1;
256 while (*tail == '\n' || *tail == '\r') /* for dos/mac? */
266 * read contents of doc comment.
268 * @return comment string. or NULL when error occurred.
270 static char* readDocCommentContents ()
272 bool eoc = false; /* reach end of comment */
273 char* content = NULL; /* comment content */
274 size_t length = 0; /* content length */
275 char* start = NULL; /* start of content */
276 char* end = NULL; /* end of content */
277 ccunit_log ("readDocCommentContent");
278 start = line.str + 2;
281 ccunit_dbg ("read from:%lu: \"%s\"", line.lno, start);
282 /* skip white spaces */
283 for (; *start && isspace (*start); start ++)
285 if (*start != '*') /* block comment '*' */
287 else if (start[1] == '/') /* eoc */
289 else /* skip white spaces */
290 for (start ++; *start && isspace (*start); start ++)
292 /* seek to eol or end of comment */
293 for (end = start; *end; end ++)
294 if (end[0] == '*' && end[1] == '/')
296 ccunit_log ("end of comment");
300 /* trim trailing white space */
301 for (end --; start < end; end --)
307 /* did a comment exist? */
310 int len = (int)(end - start);
311 char* newContent = realloc (content, length + len + 2);
314 ccunit_err ("no more memory");
318 newContent[length ++] = ' '; /* word space */
319 memcpy (newContent + length, start, len);
321 newContent[length] = '\0';
322 content = newContent;
323 ccunit_log ("get: \"%*.*s\"", len, len, start);
325 if (eoc || !readline ())
329 ccunit_dbg ("comment content: \"%s\"", content);
334 * read document comment.
336 * @return comment content if matched, or NULL if not matched.
338 static char* readDocComment ()
340 const char* cmnt = "/**";
341 if (strncmp (line.str, cmnt, strlen(cmnt)) != 0) /* not a comment */
343 else if (line.str[3] == '*' || line.str[3] == '/') /* not doc */
347 ccunit_dbg ("found doc comment:%lu: %s", line.lno, line.str);
348 return readDocCommentContents ();
356 * @param str comment string.
357 * @return group attribute object.
359 static char* getTestCaseName (const char* str)
361 static const char* const prefix = "TEST CASE:";
362 const size_t prefixLen = strlen (prefix);
363 const char* name = NULL;
364 if (strncasecmp (str, prefix, prefixLen) == 0)
366 for (name = str + prefixLen; *name; name ++)
367 if (!isspace (*name))
372 ccunit_err ("no test case name: %s. near line %lu",
377 ccunit_dbg ("not a test case name: %s", str);
382 * get end of case string.
384 * @return eoc string.
386 static char* getEndOfCase (const char* str)
388 static const char* const prefix = "END TEST CASE";
389 const size_t prefixLen = strlen (prefix);
390 const char* name = NULL;
391 if (strncasecmp (str, prefix, prefixLen) == 0)
393 name = str + prefixLen;
394 if (*name && !isspace (*name) && !ispunct (*name))
397 ccunit_dbg ("not a end of test case: %s", str);
401 for (; *name; name ++)
402 if (!isspace (*name))
407 ccunit_log ("end of test case: %s", name);
411 ccunit_dbg ("not a end of test case: %s", str);
416 * read test fixturedef.
418 * @param type required type string.
419 * @param prefix required fixture name prefix.
420 * @param desc description.
421 * @return funcdef object.
423 static CCUnitTestFixtureDef* readFixture (const char* type,
429 ccunit_dbg ("read fixture: %s %s... from '%s'", type, prefix, line.str);
430 for (typ = line.str; *typ; typ ++)
433 if (strncmp (typ, type, strlen (type)) != 0)
435 ccunit_dbg ("type mismatch: %s %s", type, typ);
438 name = typ + strlen (type);
439 if (*name && !isspace (*name))
441 ccunit_dbg ("type mismatch: %s %s", type, name);
446 for (; *name; name ++)
447 if (!isspace (*name))
453 ccunit_err ("unexpected EOF");
457 if (strncmp (name, prefix, strlen(prefix)) == 0)
460 for (tail = name + 1; *tail; tail ++)
461 if (isspace (*tail) || *tail == '(')
466 return newTestFixtureDef (type, name, desc);
469 ccunit_dbg ("name mismatch: %s %s", prefix, name);
474 * read test case function.
476 * @param parent parent suite.
477 * @param cname test case name to read.
479 static void readTestCase (CCUnitTestSuiteDef* parent, const char* cname)
481 CCUnitTestSuiteDef* suite;
482 CCUnitTestCaseDef* testCase;
483 CCUnitTestFixtureDef* f = NULL;
487 suite = ccunit_newTestSuiteDef (NULL);
490 testCase = newTestCaseDef (cname);
493 deleteTestDef (&suite->testdef);
496 addTestDef (parent, &suite->testdef);
497 addTestDef (suite, &testCase->testdef);
500 /* setUp function def */
501 if ((f = readFixture ("void", "setUp", desc)) != NULL)
503 if (!testCase->setUp)
507 ccunit_err ("%s:%lu: setUp multiply defined %s... ignored.\n"
508 " previous definition is %s\n"
509 " perhaps missing /** test case: ... */",
510 line.fname, line.lno, f->name, testCase->setUp->name);
511 deleteTestFixtureDef (f);
515 /* tearDown function def */
516 else if ((f = readFixture ("void", "tearDown", desc)) != NULL)
518 if (!testCase->tearDown)
519 testCase->tearDown = f;
522 ccunit_err ("%s:%lu: tearDown multiply defined %s... ignored.\n"
523 " previous definition is %s\n"
524 " perhaps missing /** test case: ... */",
525 line.fname, line.lno, f->name, testCase->tearDown->name);
526 deleteTestFixtureDef (f);
530 /* if test fixture function def, then read as test fixture. */
531 else if ((f = readFixture ("void", "test", desc)) != NULL)
533 ccunit_addList (&testCase->fixtures, f);
536 /* if current line is javaDoc comment, then read as description. */
537 else if ((doc = readDocComment ()) != NULL)
539 if ((name = getTestCaseName (doc)) != NULL)
541 ccunit_err ("%s:%lu: unbaranced end case comment '%s', "
542 "need /** end case: %s */",
543 line.fname, line.lno, doc, cname);
544 readTestCase (suite, name);
547 else if ((name = getEndOfCase (doc)) != NULL)
549 ccunit_log ("exit test case: %s", testCase->testdef.name);
562 * read test suite def.
564 * @param parent parent suitedef.
566 static void readSuite (CCUnitTestSuiteDef* parent)
568 CCUnitTestFixtureDef* f;
574 /* if current line is javaDoc comment, then read as description. */
575 if ((doc = readDocComment ()) != NULL)
577 if ((name = getTestCaseName (doc)) != NULL)
579 readTestCase (parent, name);
582 else if ((name = getEndOfCase (doc)) != NULL)
584 ccunit_err ("%s:%lu: invalid end test case comment '%s'",
585 line.fname, line.lno, doc);
591 else if ((f = readFixture ("void", "test", desc)) != NULL
592 || (f = readFixture ("void", "setUp", desc)) != NULL
593 || (f = readFixture ("void", "tearDown", desc)) != NULL)
595 ccunit_err ("%s:%lu: missing test case start comment '%s': ignored",
596 line.fname, line.lno, line.str);
597 deleteTestFixtureDef (f);
606 void ccunit_readSuite (const char* fname, CCUnitTestSuiteDef* parent)
608 if (strcmp (fname, "-") == 0) /* special file name '-' as stdin */
611 line.fname = "stdin";
615 line.ifp = fopen (fname, "r");
616 if (!line.ifp) /* open error */
618 ccunit_err ("can't open file '%s': %s. skipped.\n",
619 fname, strerror (errno));
625 safe_free (line.str);
626 if (line.ifp != NULL && line.ifp != stdin)
628 memset (&line, 0, sizeof (line));