Automated.c 30.8 KB
Newer Older
anilsaharan's avatar
anilsaharan committed
1
/*
2
 *  BCUnit - A Unit testing framework library for C.
3 4
 *  Copyright (C) 2001       Anil Kumar
 *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
5
 *
anilsaharan's avatar
anilsaharan committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
22
 *  Implementation of the Automated Test Interface.
23
 *
24
 *  Feb 2002      Initial implementation. (AK)
anilsaharan's avatar
anilsaharan committed
25
 *
26 27
 *  13/Feb/2002   Added initial automated interface functions to generate
 *                HTML based Run report. (AK)
28
 *
29
 *  23/Jul/2002   Changed HTML to XML Format file generation for Automated Tests. (AK)
30
 *
31
 *  27/Jul/2003   Fixed a bug which hinders the listing of all failures. (AK)
32
 *
33 34 35 36 37
 *  17-Jul-2004   New interface, doxygen comments, eliminate compiler warnings,
 *                automated_run_tests now assigns a generic file name if
 *                none has been supplied. (JDS)
 *
 *  30-Apr-2005   Added notification of failed suite cleanup function. (JDS)
38 39
 *
 *  02-May-2006   Added internationalization hooks.  (JDS)
40 41
 *
 *  07-May-2011   Added patch to fix broken xml tags dur to spacial characters in the test name.  (AK)
42 43 44 45
 */

/** @file
 * Automated test interface with xml result output (implementation).
anilsaharan's avatar
anilsaharan committed
46
 */
47 48 49
/** @addtogroup Automated
 @{
*/
anilsaharan's avatar
anilsaharan committed
50 51 52 53 54 55 56 57 58

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
#include <time.h>

59
#include "BCUnit.h"
anilsaharan's avatar
anilsaharan committed
60
#include "TestDB.h"
61
#include "MyMem.h"
anilsaharan's avatar
anilsaharan committed
62 63 64
#include "Util.h"
#include "TestRun.h"
#include "Automated.h"
65
#include "BCUnit_intl.h"
anilsaharan's avatar
anilsaharan committed
66

anilsaharan's avatar
anilsaharan committed
67 68
#define MAX_FILENAME_LENGTH		1025

69 70 71
/*=================================================================
 *  Global / Static data definitions
 *=================================================================*/
72
static CU_pSuite f_pRunningSuite = NULL;                    /**< The running test suite. */
73
static char      f_szDefaultFileRoot[] = "BCUnitAutomated";  /**< Default filename root for automated output files. */
anilsaharan's avatar
anilsaharan committed
74 75
static char      f_szTestListFileName[MAX_FILENAME_LENGTH] = "";   /**< Current output file name for the test listing file. */
static char      f_szTestResultFileName[MAX_FILENAME_LENGTH] = ""; /**< Current output file name for the test results file. */
76
static FILE*     f_pTestResultFile = NULL;                  /**< FILE pointer the test results file. */
anilsaharan's avatar
anilsaharan committed
77

78
static CU_BOOL   f_bWriting_BCUNIT_RUN_SUITE = CU_FALSE;       /**< Flag for keeping track of when a closing xml tag is required. */
anilsaharan's avatar
anilsaharan committed
79

80
static CU_BOOL   bJUnitXmlOutput = CU_FALSE;                /**< Flag for toggling the xml junit output or keeping the original. Off is the default */
81 82 83

static CU_BOOL	 bPartialSuiteJUnitReport = CU_FALSE;		/** Flag for toggling englobing <testsuites> tags. Warning: allows imcomplete JUnit results file where only <testsuite></testsuite> will be present. This is to allow JUnit-xml output for a single suite */

84 85
static char _gPackageName[50] = "";

86 87 88 89
//static time_t    f_testStartTime = 0;                       /**< Start time of current running test suite. */

double startTime = 0.0;
double endTime = 0.0;
90

91 92 93
/*=================================================================
 *  Static function forward declarations
 *=================================================================*/
94
static CU_ErrorCode automated_list_all_tests(CU_pTestRegistry pRegistry, const char* szFilename);
95

96 97
static CU_ErrorCode initialize_result_file(const char* szFilename);
static CU_ErrorCode uninitialize_result_file(void);
anilsaharan's avatar
anilsaharan committed
98

99
static void automated_run_all_tests(CU_pTestRegistry pRegistry);
anilsaharan's avatar
anilsaharan committed
100

101 102 103 104
static void automated_test_start_message_handler(const CU_pTest pTest, const CU_pSuite pSuite);
static void automated_test_complete_message_handler(const CU_pTest pTest, const CU_pSuite pSuite, const CU_pFailureRecord pFailure);
static void automated_all_tests_complete_message_handler(const CU_pFailureRecord pFailure);
static void automated_suite_init_failure_message_handler(const CU_pSuite pSuite);
105
static void automated_suite_cleanup_failure_message_handler(const CU_pSuite pSuite);
anilsaharan's avatar
anilsaharan committed
106

107 108 109 110 111 112
CU_TestStartMessageHandler           test_start_handler;
CU_TestCompleteMessageHandler        test_complete_handler;
CU_AllTestsCompleteMessageHandler    all_test_complete_handler;
CU_SuiteInitFailureMessageHandler    suite_init_failure_handler;
CU_SuiteCleanupFailureMessageHandler suite_cleanup_failure_handler;

113 114 115
/*=================================================================
 *  Public Interface functions
 *=================================================================*/
116
void CU_automated_run_tests(void)
anilsaharan's avatar
anilsaharan committed
117
{
118
  assert(NULL != CU_get_registry());
119 120 121 122 123 124

  /* Ensure output makes it to screen at the moment of a SIGSEGV. */
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);

  /* if a filename root hasn't been set, use the default one */
125
  if (0 == strlen(f_szTestResultFileName)) {
126
    CU_set_output_filename(f_szDefaultFileRoot);
127
  }
128

129
  if (CUE_SUCCESS != initialize_result_file(f_szTestResultFileName)) {
130
    fprintf(stderr, "\n%s", _("ERROR - Failed to create/initialize the result file."));
131 132
  }
  else {
133 134 135 136 137 138
    /* get previous handlers in case there was something here */
    test_start_handler = CU_get_test_start_handler();
    test_complete_handler = CU_get_test_complete_handler();
    all_test_complete_handler = CU_get_all_test_complete_handler();
    suite_init_failure_handler = CU_get_suite_init_failure_handler();
    suite_cleanup_failure_handler = CU_get_suite_cleanup_failure_handler();
139 140 141 142 143
    /* set up the message handlers for writing xml output */
    CU_set_test_start_handler(automated_test_start_message_handler);
    CU_set_test_complete_handler(automated_test_complete_message_handler);
    CU_set_all_test_complete_handler(automated_all_tests_complete_message_handler);
    CU_set_suite_init_failure_handler(automated_suite_init_failure_message_handler);
tlh2000's avatar
tlh2000 committed
144
    CU_set_suite_cleanup_failure_handler(automated_suite_cleanup_failure_message_handler);
145

146
    f_bWriting_BCUNIT_RUN_SUITE = CU_FALSE;
147

148
    automated_run_all_tests(NULL);
tlh2000's avatar
tlh2000 committed
149

150
    if (CUE_SUCCESS != uninitialize_result_file()) {
151
      fprintf(stderr, "\n%s", _("ERROR - Failed to close/uninitialize the result files."));
152 153
    }
  }
anilsaharan's avatar
anilsaharan committed
154 155
}

156
/*------------------------------------------------------------------------*/
157
void CU_set_output_filename(const char* szFilenameRoot)
anilsaharan's avatar
anilsaharan committed
158
{
159 160 161
  const char* szListEnding = "-Listing.xml";
  const char* szResultEnding = "-Results.xml";

162 163
  /* Construct the name for the listing file */
  if (NULL != szFilenameRoot) {
anilsaharan's avatar
anilsaharan committed
164
    strncpy(f_szTestListFileName, szFilenameRoot, MAX_FILENAME_LENGTH - strlen(szListEnding) - 1);
165 166
  }
  else {
anilsaharan's avatar
anilsaharan committed
167
    strncpy(f_szTestListFileName, f_szDefaultFileRoot, MAX_FILENAME_LENGTH - strlen(szListEnding) - 1);
168 169
  }

anilsaharan's avatar
anilsaharan committed
170
  f_szTestListFileName[MAX_FILENAME_LENGTH - strlen(szListEnding) - 1] = '\0';
171
  strcat(f_szTestListFileName, szListEnding);
172

173 174
  /* Construct the name for the result file */
  if (NULL != szFilenameRoot) {
anilsaharan's avatar
anilsaharan committed
175
    strncpy(f_szTestResultFileName, szFilenameRoot, MAX_FILENAME_LENGTH - strlen(szResultEnding) - 1);
176 177
  }
  else {
anilsaharan's avatar
anilsaharan committed
178
    strncpy(f_szTestResultFileName, f_szDefaultFileRoot, MAX_FILENAME_LENGTH - strlen(szResultEnding) - 1);
179
  }
180

anilsaharan's avatar
anilsaharan committed
181
  f_szTestResultFileName[MAX_FILENAME_LENGTH - strlen(szResultEnding) - 1] = '\0';
182
  strcat(f_szTestResultFileName, szResultEnding);
anilsaharan's avatar
anilsaharan committed
183 184
}

185 186
/*------------------------------------------------------------------------*/
CU_ErrorCode CU_list_tests_to_file()
anilsaharan's avatar
anilsaharan committed
187
{
188
  /* if a filename root hasn't been set, use the default one */
189
  if (0 == strlen(f_szTestListFileName)) {
190
    CU_set_output_filename(f_szDefaultFileRoot);
191
  }
anilsaharan's avatar
anilsaharan committed
192

193
  return automated_list_all_tests(CU_get_registry(), f_szTestListFileName);
anilsaharan's avatar
anilsaharan committed
194 195
}

196 197 198 199
/*=================================================================
 *  Static function implementation
 *=================================================================*/
/** Runs the registered tests using the automated interface.
tlh2000's avatar
tlh2000 committed
200
 *  If non-NULL. the specified registry is set as the active
201
 *  registry for running the tests.  If NULL, then the default
202
 *  BCUnit test registry is used.  The actual test running is
203 204
 *  performed by CU_run_all_tests().
 *  @param pRegistry The test registry to run.
205 206
 */
static void automated_run_all_tests(CU_pTestRegistry pRegistry)
anilsaharan's avatar
anilsaharan committed
207
{
208
  CU_pTestRegistry pOldRegistry = NULL;
anilsaharan's avatar
anilsaharan committed
209

210
  assert(NULL != f_pTestResultFile);
211 212 213

  f_pRunningSuite = NULL;

214 215 216
  if (NULL != pRegistry) {
    pOldRegistry = CU_set_registry(pRegistry);
  }
217
  if (bJUnitXmlOutput == CU_FALSE) {
218
    fprintf(f_pTestResultFile, "  <BCUNIT_RESULT_LISTING> \n");
219
  }
220
  CU_run_all_tests();
221 222 223
  if (NULL != pRegistry) {
    CU_set_registry(pOldRegistry);
  }
anilsaharan's avatar
anilsaharan committed
224 225
}

226
/*------------------------------------------------------------------------*/
227
/** Initializes the test results file generated by the automated interface.
228
 *  A file stream is opened and header information is written.
229 230
 */
static CU_ErrorCode initialize_result_file(const char* szFilename)
231
{
232 233 234 235 236 237 238 239 240 241 242
  CU_set_error(CUE_SUCCESS);

  if ((NULL == szFilename) || (strlen(szFilename) == 0)) {
    CU_set_error(CUE_BAD_FILENAME);
  }
  else if (NULL == (f_pTestResultFile = fopen(szFilename, "w"))) {
    CU_set_error(CUE_FOPEN_FAILED);
  }
  else {
    setvbuf(f_pTestResultFile, NULL, _IONBF, 0);

243
    if (bJUnitXmlOutput == CU_TRUE) {
244 245 246 247 248
	    if (bPartialSuiteJUnitReport == CU_FALSE) {
		    fprintf(f_pTestResultFile,
			    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
			    "<testsuites> \n");
	    }
249 250 251
    } else {
      fprintf(f_pTestResultFile,
              "<?xml version=\"1.0\" ?> \n"
252 253 254 255
              "<?xml-stylesheet type=\"text/xsl\" href=\"BCUnit-Run.xsl\" ?> \n"
              "<!DOCTYPE BCUNIT_TEST_RUN_REPORT SYSTEM \"BCUnit-Run.dtd\"> \n"
              "<BCUNIT_TEST_RUN_REPORT> \n"
              "  <BCUNIT_HEADER/> \n");
256
    }
257 258 259
  }

  return CU_get_error();
anilsaharan's avatar
anilsaharan committed
260 261
}

262 263
/*------------------------------------------------------------------------*/
/** Handler function called at start of each test.
264 265 266 267
 *  The test result file must have been opened before this
 *  function is called (i.e. f_pTestResultFile non-NULL).
 *  @param pTest  The test being run (non-NULL).
 *  @param pSuite The suite containing the test (non-NULL).
268 269
 */
static void automated_test_start_message_handler(const CU_pTest pTest, const CU_pSuite pSuite)
anilsaharan's avatar
anilsaharan committed
270
{
271 272 273
	char *szTempName = NULL;
	size_t szTempName_len = 0;

274 275 276 277 278 279
  //Gathering information about current time for the suite start time
  time_t suiteStartTime = time(NULL);
  struct tm tm = *localtime(&suiteStartTime);

  //Getting the start time of the test to compute test duration
  startTime = CU_get_wall_time();
280

281 282 283 284
  if( test_start_handler ){
    (*test_start_handler)(pTest, pSuite);
  }

jds2's avatar
jds2 committed
285
  CU_UNREFERENCED_PARAMETER(pTest);   /* not currently used */
286

287 288
  assert(NULL != pTest);
  assert(NULL != pSuite);
289
  assert(NULL != pSuite->pName);
290
  assert(NULL != f_pTestResultFile);
291 292

  /* write suite close/open tags if this is the 1st test for this szSuite */
293
  if ((NULL == f_pRunningSuite) || (f_pRunningSuite != pSuite)) {
294
    if (CU_TRUE == f_bWriting_BCUNIT_RUN_SUITE) {
295 296 297 298 299 300
      if (bJUnitXmlOutput == CU_TRUE) {
        fprintf(f_pTestResultFile,
                "    </testsuite>\n");
      }
      else {
        fprintf(f_pTestResultFile,
301 302
                "      </BCUNIT_RUN_SUITE_SUCCESS> \n"
                "    </BCUNIT_RUN_SUITE> \n");
303
      }
304 305
    }

306 307 308 309
  /* translate suite name that may contain XML control characters */
  szTempName = (char *)CU_MALLOC((szTempName_len = CU_translated_strlen(pSuite->pName) + 1));
  CU_translate_special_characters(pSuite->pName, szTempName, szTempName_len);

310 311
    if (bJUnitXmlOutput == CU_TRUE) {
      fprintf(f_pTestResultFile,
312
              "  <testsuite name=\"%s\" tests=\"%u\" time=\"0\" failures=\"%u\" errors=\"%u\" skipped=\"0\" timestamp=\"%d-%02d-%02dT%02d:%02d:%02d\"> \n",
Erwan Croze's avatar
Erwan Croze committed
313
              (NULL != szTempName) ? szTempName : "", /* Name */
Erwan Croze's avatar
Erwan Croze committed
314 315
              pSuite->uiNumberOfTests, /* Tests */
              pSuite->uiNumberOfTestsFailed, /* Tests failure */
316 317 318 319 320 321 322 323
              0,
              tm.tm_year + 1900,
              tm.tm_mon + 1,
              tm.tm_mday,
              tm.tm_hour,
              tm.tm_min,
              tm.tm_sec
              ); /* Errors */
324 325
    } else {
      fprintf(f_pTestResultFile,
326 327
              "    <BCUNIT_RUN_SUITE> \n"
              "      <BCUNIT_RUN_SUITE_SUCCESS> \n"
328
              "        <SUITE_NAME> %s </SUITE_NAME> \n",
329
              (NULL != szTempName ? szTempName : ""));
330
    }
331

332
    f_bWriting_BCUNIT_RUN_SUITE = CU_TRUE;
333 334
    f_pRunningSuite = pSuite;
  }
335 336 337 338

  if (NULL != szTempName) {
    CU_FREE(szTempName);
  }
anilsaharan's avatar
anilsaharan committed
339 340
}

341 342
/*------------------------------------------------------------------------*/
/** Handler function called at completion of each test.
343 344
 * @param pTest   The test being run (non-NULL).
 * @param pSuite  The suite containing the test (non-NULL).
345 346 347 348 349
 * @param pFailure Pointer to the 1st failure record for this test.
 */
static void automated_test_complete_message_handler(const CU_pTest pTest,
                                                    const CU_pSuite pSuite,
                                                    const CU_pFailureRecord pFailure)
anilsaharan's avatar
anilsaharan committed
350
{
351 352 353
  char *szTemp = NULL;
  size_t szTemp_len = 0;
  size_t cur_len = 0;
354
  CU_pFailureRecord pTempFailure = pFailure;
355
  const char *pPackageName = CU_automated_package_name_get();
356

357 358 359
  //Getting the end time of the test to compute test duration
  endTime = CU_get_wall_time();

360 361 362
  if( test_complete_handler ){
    (*test_complete_handler)(pTest, pSuite, pFailure);
  }
363

jds2's avatar
jds2 committed
364
  CU_UNREFERENCED_PARAMETER(pSuite);  /* pSuite is not used except in assertion */
365

366
  assert(NULL != pTest);
367
  assert(NULL != pTest->pName);
368
  assert(NULL != pSuite);
369
  assert(NULL != pSuite->pName);
370
  assert(NULL != f_pTestResultFile);
371

372
  if (NULL != pTempFailure) {
373

374 375 376 377 378 379
    if(NULL != pTempFailure) {
      if (bJUnitXmlOutput == CU_TRUE) {
        assert((NULL != pTempFailure->pSuite) && (pTempFailure->pSuite == pSuite));
        assert((NULL != pTempFailure->pTest) && (pTempFailure->pTest == pTest));

        if (NULL != pTempFailure->strCondition) {
380 381 382
          szTemp_len = CU_translated_strlen(pTempFailure->strCondition) + 1;
          szTemp = (char *)CU_MALLOC(szTemp_len);
          size_t test = CU_translate_special_characters(pTempFailure->strCondition, szTemp, szTemp_len);
383 384
        }
        else {
385
          //!!!!!!
386 387 388
          szTemp[0] = '\0';
        }

389
        fprintf(f_pTestResultFile, "        <testcase classname=\"%s.%s\" name=\"%s\" time=\"%f\">\n",
390 391
                pPackageName,
                pSuite->pName,
392
                (NULL != pTest->pName) ? pTest->pName : "",
393 394 395 396 397 398 399 400 401 402 403
                endTime - startTime
                );

        if(NULL != pTempFailure->pNext) {
          fprintf(f_pTestResultFile, "            <failure message=\"Multiple asserts failed ...\" type=\"Failure\">\n");
        }
        else {
          fprintf(f_pTestResultFile, "            <failure message=\"%s\" type=\"Failure\">\n", szTemp);
        }


404 405 406
      } /* if */
    }

407
    while (NULL != pTempFailure) {
408

409 410 411
      assert((NULL != pTempFailure->pSuite) && (pTempFailure->pSuite == pSuite));
      assert((NULL != pTempFailure->pTest) && (pTempFailure->pTest == pTest));

412
      /* expand temporary char buffer if need more room */
413
      if (NULL != pTempFailure->strCondition) {
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
        cur_len = CU_translated_strlen(pTempFailure->strCondition) + 1;
      }
      else {
        cur_len = 1;
      }
      if (cur_len > szTemp_len) {
        szTemp_len = cur_len;
        if (NULL != szTemp) {
          CU_FREE(szTemp);
        }
        szTemp = (char *)CU_MALLOC(szTemp_len);
      }

      /* convert xml entities in strCondition (if present) */
      if (NULL != pTempFailure->strCondition) {
        CU_translate_special_characters(pTempFailure->strCondition, szTemp, szTemp_len);
430 431 432 433
      }
      else {
        szTemp[0] = '\0';
      }
434

435 436 437
      if (bJUnitXmlOutput == CU_TRUE) {
        fprintf(f_pTestResultFile, "                     Condition: %s\n", szTemp);
        fprintf(f_pTestResultFile, "                     File     : %s\n", (NULL != pTempFailure->strFileName) ? pTempFailure->strFileName : "");
438
        fprintf(f_pTestResultFile, "                     Line     : %u\n", pTempFailure->uiLineNumber);
439 440
      } else {
        fprintf(f_pTestResultFile,
441 442
              "        <BCUNIT_RUN_TEST_RECORD> \n"
              "          <BCUNIT_RUN_TEST_FAILURE> \n"
443 444 445 446
              "            <TEST_NAME> %s </TEST_NAME> \n"
              "            <FILE_NAME> %s </FILE_NAME> \n"
              "            <LINE_NUMBER> %u </LINE_NUMBER> \n"
              "            <CONDITION> %s </CONDITION> \n"
447 448
              "          </BCUNIT_RUN_TEST_FAILURE> \n"
              "        </BCUNIT_RUN_TEST_RECORD> \n",
449
              pTest->pName,
450 451 452
              (NULL != pTempFailure->strFileName) ? pTempFailure->strFileName : "",
              pTempFailure->uiLineNumber,
              szTemp);
453
      } /* if */
454
      pTempFailure = pTempFailure->pNext;
455 456 457 458 459 460
    } /* while */

    if (bJUnitXmlOutput == CU_TRUE) {
      fprintf(f_pTestResultFile, "            </failure>\n");
      fprintf(f_pTestResultFile, "        </testcase>\n");
    } /* if */
461 462
  }
  else {
463
    if (bJUnitXmlOutput == CU_TRUE) {
464
      fprintf(f_pTestResultFile,  "        <testcase classname=\"%s.%s\" name=\"%s\" time=\"%f\"/>\n",
465 466
              pPackageName,
              pSuite->pName,
467
              (NULL != pTest->pName) ? pTest->pName : "",
468 469
              endTime - startTime
              );
470 471
    } else {
      fprintf(f_pTestResultFile,
472 473
              "        <BCUNIT_RUN_TEST_RECORD> \n"
              "          <BCUNIT_RUN_TEST_SUCCESS> \n"
474
              "            <TEST_NAME> %s </TEST_NAME> \n"
475 476
              "          </BCUNIT_RUN_TEST_SUCCESS> \n"
              "        </BCUNIT_RUN_TEST_RECORD> \n",
477 478
              pTest->pName);
    }
479
  }
480 481 482 483

  if (NULL != szTemp) {
    CU_FREE(szTemp);
  }
anilsaharan's avatar
anilsaharan committed
484 485
}

486 487
/*------------------------------------------------------------------------*/
/** Handler function called at completion of all tests in a suite.
488
 *  @param pFailure Pointer to the test failure record list.
489 490
 */
static void automated_all_tests_complete_message_handler(const CU_pFailureRecord pFailure)
anilsaharan's avatar
anilsaharan committed
491
{
492 493 494
  CU_pTestRegistry pRegistry = CU_get_registry();
  CU_pRunSummary pRunSummary = CU_get_run_summary();

495 496 497 498
  if( all_test_complete_handler ){
    (*all_test_complete_handler)(pFailure);
  }

jds2's avatar
jds2 committed
499
  CU_UNREFERENCED_PARAMETER(pFailure);  /* not used */
500

501 502 503
  assert(NULL != pRegistry);
  assert(NULL != pRunSummary);
  assert(NULL != f_pTestResultFile);
504

505
  if ((NULL != f_pRunningSuite) && (CU_TRUE == f_bWriting_BCUNIT_RUN_SUITE)) {
506 507
    if (bJUnitXmlOutput == CU_FALSE) {
      fprintf(f_pTestResultFile,
508 509
              "      </BCUNIT_RUN_SUITE_SUCCESS> \n"
              "    </BCUNIT_RUN_SUITE> \n");
510
    }
511 512
  }

513 514
  if (bJUnitXmlOutput == CU_FALSE) {
    fprintf(f_pTestResultFile,
515 516
            "  </BCUNIT_RESULT_LISTING>\n"
            "  <BCUNIT_RUN_SUMMARY> \n");
jds2's avatar
jds2 committed
517

518
    fprintf(f_pTestResultFile,
519
            "    <BCUNIT_RUN_SUMMARY_RECORD> \n"
520 521 522 523 524 525
            "      <TYPE> %s </TYPE> \n"
            "      <TOTAL> %u </TOTAL> \n"
            "      <RUN> %u </RUN> \n"
            "      <SUCCEEDED> - NA - </SUCCEEDED> \n"
            "      <FAILED> %u </FAILED> \n"
            "      <INACTIVE> %u </INACTIVE> \n"
526
            "    </BCUNIT_RUN_SUMMARY_RECORD> \n",
527 528 529 530 531
            _("Suites"),
            pRegistry->uiNumberOfSuites,
            pRunSummary->nSuitesRun,
            pRunSummary->nSuitesFailed,
            pRunSummary->nSuitesInactive);
jds2's avatar
jds2 committed
532

533
    fprintf(f_pTestResultFile,
534
            "    <BCUNIT_RUN_SUMMARY_RECORD> \n"
535 536 537 538 539 540
            "      <TYPE> %s </TYPE> \n"
            "      <TOTAL> %u </TOTAL> \n"
            "      <RUN> %u </RUN> \n"
            "      <SUCCEEDED> %u </SUCCEEDED> \n"
            "      <FAILED> %u </FAILED> \n"
            "      <INACTIVE> %u </INACTIVE> \n"
541
            "    </BCUNIT_RUN_SUMMARY_RECORD> \n",
542 543 544 545 546 547
            _("Test Cases"),
            pRegistry->uiNumberOfTests,
            pRunSummary->nTestsRun,
            pRunSummary->nTestsRun - pRunSummary->nTestsFailed,
            pRunSummary->nTestsFailed,
            pRunSummary->nTestsInactive);
jds2's avatar
jds2 committed
548

549
    fprintf(f_pTestResultFile,
550
            "    <BCUNIT_RUN_SUMMARY_RECORD> \n"
551 552 553 554 555 556
            "      <TYPE> %s </TYPE> \n"
            "      <TOTAL> %u </TOTAL> \n"
            "      <RUN> %u </RUN> \n"
            "      <SUCCEEDED> %u </SUCCEEDED> \n"
            "      <FAILED> %u </FAILED> \n"
            "      <INACTIVE> %s </INACTIVE> \n"
557 558
            "    </BCUNIT_RUN_SUMMARY_RECORD> \n"
            "  </BCUNIT_RUN_SUMMARY> \n",
559 560 561 562 563 564 565
            _("Assertions"),
            pRunSummary->nAsserts,
            pRunSummary->nAsserts,
            pRunSummary->nAsserts - pRunSummary->nAssertsFailed,
            pRunSummary->nAssertsFailed,
            _("n/a"));
    }
anilsaharan's avatar
anilsaharan committed
566 567
}

568 569
/*------------------------------------------------------------------------*/
/** Handler function called when suite initialization fails.
570
 *  @param pSuite The suite for which initialization failed.
571 572
 */
static void automated_suite_init_failure_message_handler(const CU_pSuite pSuite)
anilsaharan's avatar
anilsaharan committed
573
{
574 575 576 577 578

  if( suite_init_failure_handler ){
    (*suite_init_failure_handler)(pSuite);
  }

579
  assert(NULL != pSuite);
580
  assert(NULL != pSuite->pName);
581
  assert(NULL != f_pTestResultFile);
582

583
  if (CU_TRUE == f_bWriting_BCUNIT_RUN_SUITE) {
584
    if (bJUnitXmlOutput == CU_TRUE) {
585
      f_bWriting_BCUNIT_RUN_SUITE = CU_FALSE;
586 587 588 589
      fprintf(f_pTestResultFile,
              "    </testsuite>\n");
    } else {
      fprintf(f_pTestResultFile,
590 591 592
              "      </BCUNIT_RUN_SUITE_SUCCESS> \n"
              "    </BCUNIT_RUN_SUITE> \n");
      f_bWriting_BCUNIT_RUN_SUITE = CU_FALSE;
593
    }
594 595
  }

596 597
  if (bJUnitXmlOutput == CU_FALSE) {
    fprintf(f_pTestResultFile,
598 599
            "    <BCUNIT_RUN_SUITE> \n"
            "      <BCUNIT_RUN_SUITE_FAILURE> \n"
600 601
            "        <SUITE_NAME> %s </SUITE_NAME> \n"
            "        <FAILURE_REASON> %s </FAILURE_REASON> \n"
602 603
            "      </BCUNIT_RUN_SUITE_FAILURE> \n"
            "    </BCUNIT_RUN_SUITE>  \n",
604 605 606
            pSuite->pName,
            _("Suite Initialization Failed"));
  }
607 608 609 610
}

/*------------------------------------------------------------------------*/
/** Handler function called when suite cleanup fails.
611
 *  @param pSuite The suite for which cleanup failed.
612 613 614
 */
static void automated_suite_cleanup_failure_message_handler(const CU_pSuite pSuite)
{
615 616 617 618 619

  if( suite_cleanup_failure_handler ){
    (*suite_cleanup_failure_handler)(pSuite);
  }

620
  assert(NULL != pSuite);
621
  assert(NULL != pSuite->pName);
622
  assert(NULL != f_pTestResultFile);
623

624
  if (CU_TRUE == f_bWriting_BCUNIT_RUN_SUITE) {
625
    if (bJUnitXmlOutput == CU_TRUE) {
626
      f_bWriting_BCUNIT_RUN_SUITE = CU_FALSE;
627 628 629 630
      fprintf(f_pTestResultFile,
              "    </testsuite>\n");
    } else {
      fprintf(f_pTestResultFile,
631 632 633
              "      </BCUNIT_RUN_SUITE_SUCCESS> \n"
              "    </BCUNIT_RUN_SUITE> \n");
      f_bWriting_BCUNIT_RUN_SUITE = CU_FALSE;
634
    }
635 636
  }

637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
  if (bJUnitXmlOutput == CU_TRUE) {
    fprintf(f_pTestResultFile,
            "    <testsuite name=\"Suite Cleanup\"> \n"
            "        <testcase name=\"%s\" result=\"failure\"> \n"
            "            <error> \"Cleanup of suite failed.\" </error> \n"
            "          <variation name=\"error\"> \n"
            "            <severity>fail</severity> \n"
            "            <description> \"Cleanup of suite failed.\" </description> \n"
            "            <resource> SuiteCleanup </resource> \n"
            "          </variation> \n"
            "       </testcase> \n"
            "    </testsuite>\n",
            (NULL != pSuite->pName) ? pSuite->pName : "");
  } else {
    fprintf(f_pTestResultFile,
652 653
            "    <BCUNIT_RUN_SUITE> \n"
            "      <BCUNIT_RUN_SUITE_FAILURE> \n"
654 655
            "        <SUITE_NAME> %s </SUITE_NAME> \n"
            "        <FAILURE_REASON> %s </FAILURE_REASON> \n"
656 657
            "      </BCUNIT_RUN_SUITE_FAILURE> \n"
            "    </BCUNIT_RUN_SUITE>  \n",
658 659 660
            pSuite->pName,
            _("Suite Cleanup Failed"));
  }
anilsaharan's avatar
anilsaharan committed
661 662
}

663
/*------------------------------------------------------------------------*/
664
/** Finalizes and closes the results output file generated
665
 *  by the automated interface.
666 667
 */
static CU_ErrorCode uninitialize_result_file(void)
anilsaharan's avatar
anilsaharan committed
668
{
669 670
  char* szTime;
  time_t tTime = 0;
anilsaharan's avatar
anilsaharan committed
671

672
  assert(NULL != f_pTestResultFile);
anilsaharan's avatar
anilsaharan committed
673

674
  CU_set_error(CUE_SUCCESS);
anilsaharan's avatar
anilsaharan committed
675

676 677
  time(&tTime);
  szTime = ctime(&tTime);
678
  if (szTime) szTime[24] = '\0';
679 680 681 682 683 684 685 686 687 688 689 690 691 692
  if (bJUnitXmlOutput == CU_TRUE) {
	  fprintf(f_pTestResultFile,
		  "    </testsuite>\n");
	  if (bPartialSuiteJUnitReport == CU_FALSE) {
		  fprintf(f_pTestResultFile, "</testsuites>\n");
	  }
  }
  else {
	  fprintf(f_pTestResultFile,
		  "  <BCUNIT_FOOTER> %s" CU_VERSION " - %s </BCUNIT_FOOTER> \n"
		  "</BCUNIT_TEST_RUN_REPORT>\n",
		  _("File Generated By BCUnit v"),
		  (NULL != szTime) ? szTime : "");
  }
anilsaharan's avatar
anilsaharan committed
693

694
  if (0 != fclose(f_pTestResultFile)) {
695
    CU_set_error(CUE_FCLOSE_FAILED);
696
  }
anilsaharan's avatar
anilsaharan committed
697

698
  return CU_get_error();
anilsaharan's avatar
anilsaharan committed
699
}
700

701
/*------------------------------------------------------------------------*/
702
/** Generates an xml listing of all tests in all suites for the
703 704 705 706 707 708
 *  specified test registry.  The output is directed to a file
 *  having the specified name.
 *  @param pRegistry   Test registry for which to generate list (non-NULL).
 *  @param szFilename  Non-NULL, non-empty string containing name for
 *                     listing file.
 *  @return  A CU_ErrorCode indicating the error status.
709 710
 */
static CU_ErrorCode automated_list_all_tests(CU_pTestRegistry pRegistry, const char* szFilename)
711
{
712 713 714 715
  CU_pSuite pSuite = NULL;
  CU_pTest  pTest = NULL;
  FILE* pTestListFile = NULL;
  char* szTime;
716
  time_t tTime = 0;
717 718 719

  CU_set_error(CUE_SUCCESS);

720
  if (NULL == pRegistry) {
721 722
    CU_set_error(CUE_NOREGISTRY);
  }
723
  else if ((NULL == szFilename) || (0 == strlen(szFilename))) {
724 725 726 727 728 729 730 731 732 733
    CU_set_error(CUE_BAD_FILENAME);
  }
  else if (NULL == (pTestListFile = fopen(f_szTestListFileName, "w"))) {
    CU_set_error(CUE_FOPEN_FAILED);
  }
  else {
    setvbuf(pTestListFile, NULL, _IONBF, 0);

    fprintf(pTestListFile,
            "<?xml version=\"1.0\" ?> \n"
734 735 736 737 738
            "<?xml-stylesheet type=\"text/xsl\" href=\"BCUnit-List.xsl\" ?> \n"
            "<!DOCTYPE BCUNIT_TEST_LIST_REPORT SYSTEM \"BCUnit-List.dtd\"> \n"
            "<BCUNIT_TEST_LIST_REPORT> \n"
            "  <BCUNIT_HEADER/> \n"
            "  <BCUNIT_LIST_TOTAL_SUMMARY> \n");
jds2's avatar
jds2 committed
739 740

    fprintf(pTestListFile,
741 742 743 744
            "    <BCUNIT_LIST_TOTAL_SUMMARY_RECORD> \n"
            "      <BCUNIT_LIST_TOTAL_SUMMARY_RECORD_TEXT> %s </BCUNIT_LIST_TOTAL_SUMMARY_RECORD_TEXT> \n"
            "      <BCUNIT_LIST_TOTAL_SUMMARY_RECORD_VALUE> %u </BCUNIT_LIST_TOTAL_SUMMARY_RECORD_VALUE> \n"
            "    </BCUNIT_LIST_TOTAL_SUMMARY_RECORD> \n",
745
            _("Total Number of Suites"),
jds2's avatar
jds2 committed
746 747 748
            pRegistry->uiNumberOfSuites);

    fprintf(pTestListFile,
749 750 751 752 753
            "    <BCUNIT_LIST_TOTAL_SUMMARY_RECORD> \n"
            "      <BCUNIT_LIST_TOTAL_SUMMARY_RECORD_TEXT> %s </BCUNIT_LIST_TOTAL_SUMMARY_RECORD_TEXT> \n"
            "      <BCUNIT_LIST_TOTAL_SUMMARY_RECORD_VALUE> %u </BCUNIT_LIST_TOTAL_SUMMARY_RECORD_VALUE> \n"
            "    </BCUNIT_LIST_TOTAL_SUMMARY_RECORD> \n"
            "  </BCUNIT_LIST_TOTAL_SUMMARY> \n",
754
            _("Total Number of Test Cases"),
755 756 757
            pRegistry->uiNumberOfTests);

    fprintf(pTestListFile,
758
            "  <BCUNIT_ALL_TEST_LISTING> \n");
759 760

    pSuite = pRegistry->pSuite;
761
    while (NULL != pSuite) {
762
      assert(NULL != pSuite->pName);
763 764 765
      pTest = pSuite->pTest;

      fprintf(pTestListFile,
766 767
              "    <BCUNIT_ALL_TEST_LISTING_SUITE> \n"
              "      <BCUNIT_ALL_TEST_LISTING_SUITE_DEFINITION> \n"
768 769
              "        <SUITE_NAME> %s </SUITE_NAME> \n"
              "        <INITIALIZE_VALUE> %s </INITIALIZE_VALUE> \n"
770 771
              "        <CLEANUP_VALUE> %s </CLEANUP_VALUE> \n"
              "        <ACTIVE_VALUE> %s </ACTIVE_VALUE> \n"
772
              "        <TEST_COUNT_VALUE> %u </TEST_COUNT_VALUE> \n"
773
              "      </BCUNIT_ALL_TEST_LISTING_SUITE_DEFINITION> \n",
774 775 776 777
               pSuite->pName,
              (NULL != pSuite->pInitializeFunc) ? _("Yes") : _("No"),
              (NULL != pSuite->pCleanupFunc) ? _("Yes") : _("No"),
              (CU_FALSE != pSuite->fActive) ? _("Yes") : _("No"),
778 779 780
              pSuite->uiNumberOfTests);

      fprintf(pTestListFile,
781
              "      <BCUNIT_ALL_TEST_LISTING_SUITE_TESTS> \n");
782
      while (NULL != pTest) {
783
        assert(NULL != pTest->pName);
784
        fprintf(pTestListFile,
785 786 787
                "        <TEST_CASE_DEFINITION> \n"
                "          <TEST_CASE_NAME> %s </TEST_CASE_NAME> \n"
                "          <TEST_ACTIVE_VALUE> %s </TEST_ACTIVE_VALUE> \n"
tlh2000's avatar
tlh2000 committed
788
                "        </TEST_CASE_DEFINITION> \n",
789 790
                pTest->pName,
                (CU_FALSE != pSuite->fActive) ? _("Yes") : _("No"));
791 792 793 794
        pTest = pTest->pNext;
      }

      fprintf(pTestListFile,
795 796
              "      </BCUNIT_ALL_TEST_LISTING_SUITE_TESTS> \n"
              "    </BCUNIT_ALL_TEST_LISTING_SUITE> \n");
797 798 799 800

      pSuite = pSuite->pNext;
    }

801
    fprintf(pTestListFile, "  </BCUNIT_ALL_TEST_LISTING> \n");
802 803 804

    time(&tTime);
    szTime = ctime(&tTime);
805
    if (szTime) szTime[24] = '\0';
806
    fprintf(pTestListFile,
807 808 809
            "  <BCUNIT_FOOTER> %s" CU_VERSION " - %s </BCUNIT_FOOTER> \n"
            "</BCUNIT_TEST_LIST_REPORT>\n",
            _("File Generated By BCUnit v"),
810
            (NULL != szTime) ? szTime : "");
811

812
    if (0 != fclose(pTestListFile)) {
813
      CU_set_error(CUE_FCLOSE_FAILED);
814
    }
815 816 817
  }

  return CU_get_error();
818
}
819

820 821 822 823 824 825 826 827 828 829 830
/*------------------------------------------------------------------------*/
/** Enable or Disable the XML output format to JUnit-like. When enabled (CU_TRUE)
 *  then the Results xml that is produced can be read by cruisecontrol and displayed
 *  in the test results page.
 */
void CU_automated_enable_junit_xml(CU_BOOL bFlag)
{
  bJUnitXmlOutput = bFlag;
}
/** @} */

831 832 833 834 835 836 837 838 839
/*------------------------------------------------------------------------*/
/** Enable or Disable the englobing XML tags for <testsuites> in the JUnit file report
 *  This allows to create partial JUnit results file that will need to be merged.
 */
void CU_automated_enable_partial_junit(CU_BOOL bFlag)
{
  bPartialSuiteJUnitReport = bFlag;
}

840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
/*------------------------------------------------------------------------*/
/** Set tests suites package name
 */
void CU_automated_package_name_set(const char *pName)
{
  memset(_gPackageName, 0, sizeof(_gPackageName));

  /* Is object valid? */
  if (pName) {
    strncpy(_gPackageName, pName, sizeof(_gPackageName) - 1);
    _gPackageName[sizeof(_gPackageName) - 1] = '\0';
  }
}

/*------------------------------------------------------------------------*/
/** Get tests suites package name
 */
const char *CU_automated_package_name_get()
{
 return _gPackageName;
}
861
/** @} */