text: /*
   * Check: a unit test framework for C
   * Copyright (C) 2001, 2002 Arien Malec
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 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
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the
   * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   * MA 02110-1301, USA.
   */
  
  #include "../lib/libcompat.h"
  
  #include <stdlib.h>
  #include <signal.h>
  #include <stdio.h>
  #include <string.h>
  #include <check.h>
  #include "check_error.h"
  #include "check_str.h"
  #include "check_check.h"
  
  static char errm[200];
  
  static void fixture_sub_setup (void)
  {
    ck_abort_msg("Test failure in fixture");
  }
  
  static SRunner *fixture_sr;
  
  void setup_fixture (void)
  {
    TCase *tc;
    Suite *fixture_s;
    
    fixture_s = suite_create("Fix Sub");
    tc = tcase_create("Fix Sub");
    tcase_add_unchecked_fixture(tc, fixture_sub_setup, NULL);
    suite_add_tcase (fixture_s, tc);
    fixture_sr = srunner_create(fixture_s);
    srunner_run_all(fixture_sr,CK_VERBOSE);
  }
  
  void teardown_fixture (void)
  {
    srunner_free(fixture_sr);
  }
  
  START_TEST(test_fixture_fail_counts)
  {
    int nrun, nfail;
  
    nrun = srunner_ntests_run(fixture_sr);
    nfail = srunner_ntests_failed(fixture_sr);
  
    ck_assert_msg (nrun == 1 && nfail == 1,
  	       "Counts for run and fail for fixture failure not correct");
  }
  END_TEST
  
  START_TEST(test_print_counts)
  {
    char *srstat = sr_stat_str(fixture_sr);
    const char *exp = "0%: Checks: 1, Failures: 1, Errors: 0";
  
    ck_assert_msg(strcmp(srstat, exp) == 0,
  	      "SRunner stat string incorrect with setup failure");
    free(srstat);
  }
  END_TEST
  
  START_TEST(test_setup_failure_msg)
  {
    TestResult **tra;
    char *trm;
    const char *trmexp = "check_check_fixture.c:36:S:Fix Sub:unchecked_setup:0: Test failure in fixture";
  
    tra = srunner_failures(fixture_sr);
    trm = tr_str(tra[0]);
    free(tra);
  
    if (strstr(trm, trmexp) == 0) {
      snprintf(errm, sizeof(errm),
  	     "Bad setup tr msg (%s)", trm);
      
      ck_abort_msg (errm);
    }
    free(trm);
  }
  END_TEST
  
  #if defined(HAVE_FORK) && HAVE_FORK==1
  int testval_up;
  int testval_down;
  
  static void sub_ch_setup_norm (void)
  {
    testval_up += 2;
  }
  
  static void sub_ch_teardown_norm(void)
  {
    testval_down += 2;
  }
  
  START_TEST(test_sub_ch_setup_norm)
  {
    if (testval_up == 1)
      ck_abort_msg("Setup not run correctly");
    else if (testval_up > 3)
      ck_abort_msg("Test side-effects persist across runs");
    testval_up++;
  }
  END_TEST
  
  START_TEST(test_ch_setup)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
  
    s = suite_create("Fixture Norm Sub");
    tc = tcase_create("Fixture Norm Sub");
    sr = srunner_create(s);
    suite_add_tcase(s, tc);
    tcase_add_test(tc,test_sub_ch_setup_norm);
    tcase_add_test(tc,test_sub_ch_setup_norm);
    tcase_add_checked_fixture(tc,sub_ch_setup_norm,sub_ch_teardown_norm);
    srunner_run_all(sr, CK_VERBOSE);
  
    ck_assert_msg(srunner_ntests_failed(sr) == 0,
  	      "Checked setup not being run correctly");
  
    srunner_free(sr);
  }
  END_TEST
  
  static void setup_sub_fail (void)
  {
    ck_abort_msg("Failed setup"); /* check_check_fixture.c:130 */
  }
  
  static void teardown_sub_fail (void)
  {
    ck_abort_msg("Failed teardown");
  }
  
  static void setup_sub_signal (void)
  {
    mark_point();
    raise(SIGFPE);
  }
  
  static void teardown_sub_signal(void)
  {
    mark_point();
    raise(SIGFPE);
  }
  
  START_TEST(test_sub_fail)
  {
    ck_abort_msg("Should never run");
  }
  END_TEST
  
  START_TEST(test_sub_pass)
  {
    ck_assert_msg(1 == 1, "Always pass");
  }
  END_TEST
  
  START_TEST(test_ch_setup_fail)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
    TestResult ** tr;
    char *strstat;
    char *trm;
  
    s = suite_create("Setup Fail");
    tc = tcase_create("Setup Fail");
    suite_add_tcase(s, tc);
    tcase_add_test(tc,test_sub_fail);
    tcase_add_checked_fixture(tc,setup_sub_fail, NULL);
    sr = srunner_create(s);
    srunner_run_all(sr,CK_VERBOSE);
  
    ck_assert_msg (srunner_ntests_run(sr) == 1,
  	       "Test run counts not correct for checked setup failure");
    ck_assert_msg (srunner_ntests_failed(sr) == 1,
  	       "Failure counts not correct for checked setup failure");
  
    strstat= sr_stat_str(sr);
  
    ck_assert_msg(strcmp(strstat,
  		     "0%: Checks: 1, Failures: 1, Errors: 0") == 0,
  	      "SRunner stat string incorrect with checked setup failure");
    free(strstat);
  
    tr = srunner_failures(sr);
    trm = tr_str(tr[0]);
     /* Search for check_check_fixture.c:150 if this fails. */
    if (strstr(trm,
  	     "check_check_fixture.c:150:S:Setup Fail:test_sub_fail:0: Failed setup")
        == 0) {
      snprintf(errm, sizeof(errm),
  	     "Bad failed checked setup tr msg (%s)", trm);
      
      ck_abort_msg (errm);
    }
    free(trm);
    free(tr);
  }
  END_TEST
  
  START_TEST(test_ch_setup_fail_nofork)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
  
    s = suite_create("Setup Fail Nofork");
    tc = tcase_create("Setup Fail Nofork");
    suite_add_tcase(s, tc);
    tcase_add_test(tc, test_sub_fail);
    tcase_add_checked_fixture(tc, setup_sub_fail, NULL);
    sr = srunner_create(s);
    srunner_set_fork_status(sr, CK_NOFORK);
    srunner_run_all(sr, CK_VERBOSE);
  
    ck_assert_msg (srunner_ntests_run(sr) == 1,
  	       "Test run counts not correct for checked setup failure");
    ck_assert_msg (srunner_ntests_failed(sr) == 1,
  	       "Failure counts not correct for checked setup failure");
    srunner_free(sr);
  }
  END_TEST
  
  START_TEST(test_ch_setup_fail_nofork_2)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
  
    s = suite_create("Setup Fail Nofork 2");
    tc = tcase_create("Setup Fail Nofork 2");
    suite_add_tcase(s, tc);
    tcase_add_test(tc, test_sub_fail);
    tcase_add_checked_fixture(tc, sub_ch_setup_norm, NULL);
    tcase_add_checked_fixture(tc, setup_sub_fail, NULL);
    sr = srunner_create(s);
    srunner_set_fork_status(sr, CK_NOFORK);
    srunner_run_all(sr, CK_VERBOSE);
  
    ck_assert_msg (srunner_ntests_run(sr) == 1,
  	       "Test run counts not correct for checked setup failure");
    ck_assert_msg (srunner_ntests_failed(sr) == 1,
  	       "Failure counts not correct for checked setup failure");
    srunner_free(sr);
  }
  END_TEST
  
  START_TEST(test_ch_setup_pass_nofork)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
  
    s = suite_create("Setup Pass Multiple fixtures");
    tc = tcase_create("Setup Pass Multiple fixtures");
    suite_add_tcase(s, tc);
    tcase_add_test(tc, test_sub_pass);
    tcase_add_checked_fixture(tc, sub_ch_setup_norm, sub_ch_teardown_norm);
    tcase_add_checked_fixture(tc, sub_ch_setup_norm, sub_ch_teardown_norm);
    tcase_add_checked_fixture(tc, sub_ch_setup_norm, sub_ch_teardown_norm);
    sr = srunner_create(s);
    srunner_set_fork_status(sr, CK_NOFORK);
    testval_up = 1;
    testval_down = 1;
    srunner_run_all(sr, CK_VERBOSE);
    ck_assert_msg(testval_up == 7, "Multiple setups failed");
    ck_assert_msg(testval_down == 7, "Multiple teardowns failed");
  
    ck_assert_msg (srunner_ntests_run(sr) == 1,
  	       "Test run counts not correct for checked setup failure");
    ck_assert_msg (srunner_ntests_failed(sr) == 0,
  	       "Failure counts not correct for checked setup failure");
    srunner_free(sr);
  }
  END_TEST
  
  /* This test currently does not work on Cygwin, as it results in a
   * SIGSEGV instead of a SIGFPE. However, a simple program that installs
   * a SIGFPE handler then raise(SIGFPE) works as expected. Further
   * investigation is necessary. */
  #if !defined(__CYGWIN__)
  /*
   * This test will fail without fork, as it results in a checked
   * fixture raising a signal, which terminates the test runner early.
   */
  START_TEST(test_ch_setup_sig)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
    TestResult **tr;
    char *strstat;
    char *trm;
  
    s = suite_create("Setup Sig");
    tc = tcase_create("Setup Sig");
    suite_add_tcase(s, tc);
    tcase_add_test(tc,test_sub_fail);
    tcase_add_checked_fixture(tc,setup_sub_signal, NULL);
    sr = srunner_create(s);
    srunner_run_all(sr,CK_VERBOSE);
  
    ck_assert_msg (srunner_ntests_failed(sr) == 1,
  	       "Failure counts not correct for checked setup signal");
    ck_assert_msg (srunner_ntests_run(sr) == 1,
  	       "Test run counts not correct for checked setup signal");
  
    strstat= sr_stat_str(sr);
  
    ck_assert_msg(strcmp(strstat,
  		     "0%: Checks: 1, Failures: 0, Errors: 1") == 0,
  	      "SRunner stat string incorrect with checked setup signal");
    free(strstat);
  
    tr = srunner_failures(sr);
    trm = tr_str(tr[0]);
  
    if (strstr(trm,
  	     "check_check_fixture.c:160:S:Setup Sig:test_sub_fail:0: "
  	     "(after this point) Received signal 8")
        == 0) {
      snprintf(errm, sizeof(errm),
  	     "Msg was (%s)", trm);
      
      ck_abort_msg (errm);
    }
    free(trm);
    srunner_free(sr);
    free(tr);
  }
  END_TEST
  #endif /* !defined(__CYGWIN__) */
  
  static void sub_ch_setup_dual_1(void)
  {
    ck_assert_msg(testval_up == 1, "Wrong start value");
    testval_up += 2;
  }
  
  static void sub_ch_setup_dual_2(void)
  {
    ck_assert_msg(testval_up == 3, "First setup failed");
    testval_up += 3;
  }
  
  START_TEST(test_sub_two_setups)
  {
    ck_assert_msg(testval_up == 6, "Multiple setups failed");
  }
  END_TEST
  
  /*
   * This test will not work without fork, as checked fixtures are
   * not supported
   */
  START_TEST(test_ch_setup_two_setups_fork)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
  
    s = suite_create("Fixture Two setups");
    tc = tcase_create("Fixture Two setups");
    sr = srunner_create(s);
    suite_add_tcase(s, tc);
    tcase_add_test(tc,test_sub_two_setups);
    tcase_add_checked_fixture(tc,sub_ch_setup_dual_1,NULL);
    tcase_add_checked_fixture(tc,sub_ch_setup_dual_2,NULL);
    testval_up = 1;
    srunner_run_all(sr, CK_VERBOSE);
  
    ck_assert_msg(srunner_ntests_failed(sr) == 0,
  	      "Problem with several setups");
  
    srunner_free(sr);
  }
  END_TEST
  
  START_TEST(test_ch_teardown_fail)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
    TestResult **tr;
    char *strstat;
    char *trm;
  
    s = suite_create("Teardown Fail");
    tc = tcase_create("Teardown Fail");
    suite_add_tcase(s, tc);
    tcase_add_test(tc,test_sub_pass);
    tcase_add_checked_fixture(tc,NULL, teardown_sub_fail);
    sr = srunner_create(s);
    srunner_run_all(sr,CK_VERBOSE);
  
    ck_assert_msg (srunner_ntests_failed(sr) == 1,
  	       "Failure counts not correct for checked teardown failure");
    ck_assert_msg (srunner_ntests_run(sr) == 1,
  	       "Test run counts not correct for checked teardown failure");
  
    strstat= sr_stat_str(sr);
  
    ck_assert_msg(strcmp(strstat,
  		     "0%: Checks: 1, Failures: 1, Errors: 0") == 0,
  	      "SRunner stat string incorrect with checked setup failure");
    free(strstat);
  
    tr = srunner_failures(sr);
    trm = tr_str(tr[0]);
  
    if (strstr(trm,
  	     "check_check_fixture.c:155:S:Teardown Fail:test_sub_pass:0: Failed teardown")
        == 0) {
      snprintf(errm, sizeof(errm),
  	     "Bad failed checked teardown tr msg (%s)", trm);
      
      ck_abort_msg (errm);
    }
    free(trm);
    free(tr);
  }
  END_TEST
  
  START_TEST(test_ch_teardown_fail_nofork)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
    TestResult **tr;
    char *strstat;
    char *trm;
  
    s = suite_create("Teardown Fail No Fork");
    tc = tcase_create("Teardown Fail No Fork");
    suite_add_tcase(s, tc);
    tcase_add_test(tc,test_sub_pass);
    tcase_add_checked_fixture(tc,NULL, teardown_sub_fail);
    sr = srunner_create(s);
    srunner_set_fork_status(sr, CK_NOFORK);
    srunner_run_all(sr,CK_VERBOSE);
  
    ck_assert_msg (srunner_ntests_failed(sr) == 1,
  	       "Failure counts not correct for checked teardown failure");
    ck_assert_msg (srunner_ntests_run(sr) == 1,
  	       "Test run counts not correct for checked teardown failure");
  
    strstat= sr_stat_str(sr);
  
    ck_assert_msg(strcmp(strstat,
  		     "0%: Checks: 1, Failures: 1, Errors: 0") == 0,
  	      "SRunner stat string incorrect with checked setup failure");
    free(strstat);
  
    tr = srunner_failures(sr);
    trm = tr_str(tr[0]);
  
    if (strstr(trm,
  	     "check_check_fixture.c:155:S:Teardown Fail No Fork:test_sub_pass:0: Failed teardown")
        == 0) {
      snprintf(errm, sizeof(errm),
  	     "Bad failed checked teardown tr msg (%s)", trm);
      
      ck_abort_msg (errm);
    }
    free(trm);
    free(tr);
  }
  END_TEST
  
  /* This test currently does not work on Cygwin, as it results in a
   * SIGSEGV instead of a SIGFPE. However, a simple program that installs
   * a SIGFPE handler then raise(SIGFPE) works as expected. Further
   * investigation is necessary. */
  #if !defined(__CYGWIN__)
  /*
   * This test will fail without fork, as it results in a checked
   * fixture raising a signal, which terminates the test runner early.
   */
  
  START_TEST(test_ch_teardown_sig)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
    TestResult **tr;
    char *strstat;
    char *trm;
  
    s = suite_create("Teardown Sig");
    tc = tcase_create("Teardown Sig");
    suite_add_tcase(s, tc);
    tcase_add_test(tc,test_sub_pass);
    tcase_add_checked_fixture(tc,NULL, teardown_sub_signal);
    sr = srunner_create(s);
    srunner_run_all(sr,CK_VERBOSE);
  
    ck_assert_msg (srunner_ntests_failed(sr) == 1,
  	       "Failure counts not correct for checked teardown signal");
    ck_assert_msg (srunner_ntests_run(sr) == 1,
  	       "Test run counts not correct for checked teardown signal");
  
    strstat= sr_stat_str(sr);
  
    ck_assert_msg(strcmp(strstat,
  		     "0%: Checks: 1, Failures: 0, Errors: 1") == 0,
  	      "SRunner stat string incorrect with checked teardown signal");
    free(strstat);
  
    tr = srunner_failures(sr);
    trm = tr_str(tr[0]);
  
    if (strstr(trm,
  	     "check_check_fixture.c:166:S:Teardown Sig:test_sub_pass:0: "
  	     "(after this point) Received signal 8")
        == 0) {
      snprintf(errm, sizeof(errm),
  	     "Bad msg (%s)", trm);
      
      ck_abort_msg (errm);
    }
    free(trm);
    srunner_free(sr);
    free(tr);
  }
  END_TEST
  #endif /* !defined(__CYGWIN__) */
  
  /* Teardowns are run in reverse order */
  static void sub_ch_teardown_dual_1(void)
  {
    ck_assert_msg(testval_down == 6, "Second teardown failed");
  }
  
  static void sub_ch_teardown_dual_2(void)
  {
    ck_assert_msg(testval_down == 3, "First teardown failed");
    testval_down += 3;
  }
  
  START_TEST(test_sub_two_teardowns)
  {
    testval_down += 2;
  }
  END_TEST
  
  /*
   * This test will not work without fork, as checked fixtures are
   * not supported
   */
  START_TEST(test_ch_teardown_two_teardowns_fork)
  {
    TCase *tc;
    Suite *s;
    SRunner *sr;
    int nr_of_failures;
    char errm[1024] = {0};
  
    s = suite_create("Fixture Two teardowns");
    tc = tcase_create("Fixture Two teardowns");
    sr = srunner_create(s);
    suite_add_tcase(s, tc);
    tcase_add_test(tc,test_sub_two_teardowns);
    tcase_add_checked_fixture(tc,NULL,sub_ch_teardown_dual_1);
    tcase_add_checked_fixture(tc,NULL,sub_ch_teardown_dual_2);
    testval_down = 1;
    srunner_run_all(sr, CK_VERBOSE);
    
    nr_of_failures = srunner_ntests_failed(sr);
    if (nr_of_failures > 0) {
      TestResult **tra = srunner_failures(sr);
      int i;
  
      for (i = 0; i < nr_of_failures; i++) {
        char *trm = tr_str(tra[i]);
        if (strlen(errm) + strlen(trm) > 1022) {
          free(trm);
          break;
        } 
        strcat(errm, trm);
        strcat(errm, "\n");
        free(trm);
      }
      free(tra);
    }
    ck_assert_msg(nr_of_failures == 0, "Problem with several teardowns\n %s",
                errm);
  
    srunner_free(sr);
  }
  END_TEST
  #endif /* HAVE_FORK */
  
  Suite *make_fixture_suite (void)
  {
  
    Suite *s;
    TCase *tc;
  
    s = suite_create("Fixture");
    tc = tcase_create("Core");
  
    suite_add_tcase (s, tc);
    tcase_add_test(tc,test_fixture_fail_counts);
    tcase_add_test(tc,test_print_counts);
    tcase_add_test(tc,test_setup_failure_msg);
  
  #if defined(HAVE_FORK) && HAVE_FORK==1
    /*
     * This test assumes that CK_FORK is being used,
     * as it tests that side effects from checked
     * fixtures do not persist between tests.
     */
    tcase_add_test(tc,test_ch_setup);
    
    tcase_add_test(tc,test_ch_setup_fail);
    tcase_add_test(tc,test_ch_setup_fail_nofork);
    tcase_add_test(tc,test_ch_setup_fail_nofork_2);
    tcase_add_test(tc,test_ch_setup_pass_nofork);
  #if !defined(__CYGWIN__)
    tcase_add_test(tc,test_ch_setup_sig);
  #endif /* !defined(__CYGWIN__) */
    tcase_add_test(tc,test_ch_setup_two_setups_fork);
    tcase_add_test(tc,test_ch_teardown_fail);
    tcase_add_test(tc,test_ch_teardown_fail_nofork);
  #if !defined(__CYGWIN__)
    tcase_add_test(tc,test_ch_teardown_sig);
  #endif /* !defined(__CYGWIN__) */
    tcase_add_test(tc,test_ch_teardown_two_teardowns_fork);
  #endif
  
    return s;
  }
  