text: /* Miniature re-implementation of the "check" library.
   *
   * This is intended to support just enough of check to run the Expat
   * tests.  This interface is based entirely on the portion of the
   * check library being used.
   */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <setjmp.h>
  #include <assert.h>
  
  #include "internal.h"  /* for UNUSED_P only */
  #include "minicheck.h"
  
  Suite *
  suite_create(const char *name)
  {
      Suite *suite = (Suite *) calloc(1, sizeof(Suite));
      if (suite != NULL) {
          suite->name = name;
      }
      return suite;
  }
  
  TCase *
  tcase_create(const char *name)
  {
      TCase *tc = (TCase *) calloc(1, sizeof(TCase));
      if (tc != NULL) {
          tc->name = name;
      }
      return tc;
  }
  
  void
  suite_add_tcase(Suite *suite, TCase *tc) 
  {
      assert(suite != NULL);
      assert(tc != NULL);
      assert(tc->next_tcase == NULL);
  
      tc->next_tcase = suite->tests;
      suite->tests = tc;
  }
  
  void
  tcase_add_checked_fixture(TCase *tc,
                            tcase_setup_function setup,
                            tcase_teardown_function teardown)
  {
      assert(tc != NULL);
      tc->setup = setup;
      tc->teardown = teardown;
  }
  
  void
  tcase_add_test(TCase *tc, tcase_test_function test)
  {
      assert(tc != NULL);
      if (tc->allocated == tc->ntests) {
          int nalloc = tc->allocated + 100;
          size_t new_size = sizeof(tcase_test_function) * nalloc;
          tcase_test_function *new_tests = realloc(tc->tests, new_size);
          assert(new_tests != NULL);
          if (new_tests != tc->tests) {
              free(tc->tests);
              tc->tests = new_tests;
          }
          tc->allocated = nalloc;
      }
      tc->tests[tc->ntests] = test;
      tc->ntests++;
  }
  
  SRunner *
  srunner_create(Suite *suite)
  {
      SRunner *runner = calloc(1, sizeof(SRunner));
      if (runner != NULL) {
          runner->suite = suite;
      }
      return runner;
  }
  
  static jmp_buf env;
  
  static char const *_check_current_function = NULL;
  static int _check_current_lineno = -1;
  static char const *_check_current_filename = NULL;
  
  void
  _check_set_test_info(char const *function, char const *filename, int lineno)
  {
      _check_current_function = function;
      _check_current_lineno = lineno;
      _check_current_filename = filename;
  }
  
  
  static void
  add_failure(SRunner *runner, int verbosity)
  {
      runner->nfailures++;
      if (verbosity >= CK_VERBOSE) {
          printf("%s:%d: %s\n", _check_current_filename,
                 _check_current_lineno, _check_current_function);
      }
  }
  
  void
  srunner_run_all(SRunner *runner, int verbosity)
  {
      Suite *suite;
      TCase *tc;
      assert(runner != NULL);
      suite = runner->suite;
      tc = suite->tests;
      while (tc != NULL) {
          int i;
          for (i = 0; i < tc->ntests; ++i) {
              runner->nchecks++;
  
              if (tc->setup != NULL) {
                  /* setup */
                  if (setjmp(env)) {
                      add_failure(runner, verbosity);
                      continue;
                  }
                  tc->setup();
              }
              /* test */
              if (setjmp(env)) {
                  add_failure(runner, verbosity);
                  continue;
              }
              (tc->tests[i])();
  
              /* teardown */
              if (tc->teardown != NULL) {
                  if (setjmp(env)) {
                      add_failure(runner, verbosity);
                      continue;
                  }
                  tc->teardown();
              }
          }
          tc = tc->next_tcase;
      }
      if (verbosity) {
          int passed = runner->nchecks - runner->nfailures;
          double percentage = ((double) passed) / runner->nchecks;
          int display = (int) (percentage * 100);
          printf("%d%%: Checks: %d, Failed: %d\n",
                 display, runner->nchecks, runner->nfailures);
      }
  }
  
  void
  _fail_unless(int UNUSED_P(condition), const char *UNUSED_P(file), int UNUSED_P(line), const char *msg)
  {
      /* Always print the error message so it isn't lost.  In this case,
         we have a failure, so there's no reason to be quiet about what
         it is.
      */
      if (msg != NULL)
          printf("%s", msg);
      longjmp(env, 1);
  }
  
  int
  srunner_ntests_failed(SRunner *runner)
  {
      assert(runner != NULL);
      return runner->nfailures;
  }
  
  void
  srunner_free(SRunner *runner)
  {
      free(runner->suite);
      free(runner);
  }
  