]> granicus.if.org Git - check/commitdiff
Fix checked teardown calls going into infinate loop in CK_NOFORK mode
authorbrarcher <brarcher@64e312b2-a51f-0410-8e61-82d0ca0eb02a>
Mon, 23 Jun 2014 04:13:03 +0000 (04:13 +0000)
committerbrarcher <brarcher@64e312b2-a51f-0410-8e61-82d0ca0eb02a>
Mon, 23 Jun 2014 04:13:03 +0000 (04:13 +0000)
A test can be created with a checked teardown, where the teardown
would call some Check asserts. If the test was run in CK_NOFORK
mode and one of the asserts in the teardown failed, longjmp()
would be called, which would result in the setjmp() being returned
to which monitored the test function, and the checked teardowns
executed again. This lead to an infinate loop.

The fix* for this is to execute the checked teardowns in their
own setjmp() block. A test is added to ensure that CK_NOFORK
with a failed checked teardown does not go into an infinate loop.

In addition, the comments for the checked and unchecked
fixture API are updated to be more clear as to what occurs
for CK_FORK and CK_NOFORK mode.

* This reveals another problem: a failed checked teardown
reacts differently for CK_FORK and CK_NOFORK mode.

CK_FORK: the test is marked as failed
CK_NOFORK: the test is marked as passed

More work will need to be done to allow CK_NOFORK mode to also
cause a test to fail if the checked teardown fails.

git-svn-id: svn+ssh://svn.code.sf.net/p/check/code/trunk@1150 64e312b2-a51f-0410-8e61-82d0ca0eb02a

NEWS
src/check.h.in
src/check_run.c
tests/check_check_fixture.c

diff --git a/NEWS b/NEWS
index 31472df314dc4ba28184ba38f96c32020d301ec3..4ebffd65836e6b90454625abb6e73cccf16f608e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,12 @@ In Development:
 
 * Changes to (unofficially for now) support Solaris and AIX platforms.
 
+* Fix issue with checked teardown functions in CK_NOFORK mode. If
+  the teardown function called an assertion that failed, an
+  infinate loop would result. Bug#98. As a result of the change, for now
+  checked teardown functions that fail in CK_NOFORK mode will
+  not cause the test to fail.
+
 
 Fri May 30, 2014: Released Check 0.9.13
   based on r1137 (2014-05-26 21:03:09 +0000)
index 47cff97002984920be8620a74d84434da5c46abd..56a1bdafa0c5354e06940b97f3df0c3d57c08fc8 100644 (file)
@@ -279,16 +279,21 @@ CK_DLL_EXP void CK_EXPORT _tcase_add_test(TCase * tc, TFun tf,
 /**
  * Add unchecked fixture setup/teardown functions to a test case
  *
- * If unchecked fixture functions are run at the start and end of the
- * test case, and not before and after unit tests. Note that unchecked
- * setup/teardown functions are not run in a separate address space,
+ * Unchecked fixture functions are run at the start and end of the
+ * test case, and not before and after unit tests. Further,
+ * unchecked fixture functions are not run in a separate address space,
  * like test functions, and so must not exit or signal (e.g.,
- * segfault)
+ * segfault).
  *
  * Also, when run in CK_NOFORK mode, unchecked fixture functions may
- * lead to different unit test behavior IF unit tests change data
+ * lead to different unit test behavior if unit tests change data
  * setup by the fixture functions.
  *
+ * Note that if a setup function fails, the remaining setup functions
+ * will be omitted, as will the test case and the teardown functions.
+ * If a teardown function fails the remaining teardown functins will be
+ * omitted.
+ *
  * @param tc test case to add unchecked fixture setup/teardown to
  * @param setup function to add to be executed before the test case;
  *               if NULL no setup function is added
@@ -300,19 +305,23 @@ CK_DLL_EXP void CK_EXPORT tcase_add_unchecked_fixture(TCase * tc, SFun setup,
                                                       SFun teardown);
 
 /**
- *  Add fixture setup/teardown functions to a test case
+ * Add checked fixture setup/teardown functions to a test case
  *
- * Checked fixture functions are run before and after unit
- * tests. Unlike unchecked fixture functions, checked fixture
- * functions are run in the same separate address space as the test
- * program, and thus the test function will survive signals or
- * unexpected exits in the fixture function. Also, IF the setup
+ * Checked fixture functions are run before and after each unit test inside
+ * of the address space of the test. Thus, if using CK_FORK
+ * mode the separate process running the unit test will survive signals
+ * or unexpected exits in the fixture function. Also, if the setup
  * function is idempotent, unit test behavior will be the same in
  * CK_FORK and CK_NOFORK modes.
  *
  * However, since fixture functions are run before and after each unit
  * test, they should not be expensive code.
  *
+ * Note that if a setup function fails, the remaining setup functions
+ * will be omitted, as will the test and the teardown functions. If a
+ * teardown function fails the remaining teardown functins will be
+ * omitted.
+ *
  * @param tc test case to add checked fixture setup/teardown to
  * @param setup function to add to be executed before each unit test in
  *               the test case;  if NULL no setup function is added
index d8263ca7d1ca0897bf70b24c22329bbc17064034..449626cc89b984d819199be40f739de9bf00b557 100644 (file)
@@ -68,8 +68,8 @@ static TestResult * srunner_run_setup(List * func_list,
     const char * setup_name);
 static int srunner_run_unchecked_setup(SRunner * sr, TCase * tc);
 static TestResult *tcase_run_checked_setup(SRunner * sr, TCase * tc);
-static void srunner_run_teardown(List * l);
-static void srunner_run_unchecked_teardown(TCase * tc);
+static void srunner_run_teardown(List * fixture_list, enum fork_status fork_usage);
+static void srunner_run_unchecked_teardown(SRunner * sr, TCase * tc);
 static void tcase_run_checked_teardown(TCase * tc);
 static void srunner_run_tcase(SRunner * sr, TCase * tc);
 static TestResult *tcase_run_tfun_nofork(SRunner * sr, TCase * tc, TF * tf,
@@ -300,26 +300,43 @@ static TestResult *tcase_run_checked_setup(SRunner * sr, TCase * tc)
     return tr;
 }
 
-static void srunner_run_teardown(List * l)
+static void srunner_run_teardown(List * fixture_list, enum fork_status fork_usage)
 {
-    Fixture *f;
+    Fixture * fixture;
 
-    for(check_list_front(l); !check_list_at_end(l); check_list_advance(l))
+    for(check_list_front(fixture_list); !check_list_at_end(fixture_list);
+        check_list_advance(fixture_list))
     {
-        f = check_list_val(l);
+        fixture = check_list_val(fixture_list);
         send_ctx_info(CK_CTX_TEARDOWN);
-        f->fun();
+
+        if(fork_usage == CK_NOFORK)
+        {
+            if(0 == setjmp(error_jmp_buffer))
+            {
+                fixture->fun();
+            }
+            else
+            {
+                /* Abort the remaining teardowns */
+                break;
+            }
+        }
+        else
+        {
+            fixture->fun();
+        }
     }
 }
 
-static void srunner_run_unchecked_teardown(TCase * tc)
+static void srunner_run_unchecked_teardown(SRunner * sr, TCase * tc)
 {
-    srunner_run_teardown(tc->unch_tflst);
+    srunner_run_teardown(tc->unch_tflst, srunner_fork_status(sr));
 }
 
 static void tcase_run_checked_teardown(TCase * tc)
 {
-    srunner_run_teardown(tc->ch_tflst);
+    srunner_run_teardown(tc->ch_tflst, CK_NOFORK);
 }
 
 static void srunner_run_tcase(SRunner * sr, TCase * tc)
@@ -327,7 +344,7 @@ static void srunner_run_tcase(SRunner * sr, TCase * tc)
     if(srunner_run_unchecked_setup(sr, tc))
     {
         srunner_iterate_tcase_tfuns(sr, tc);
-        srunner_run_unchecked_teardown(tc);
+        srunner_run_unchecked_teardown(sr, tc);
     }
 }
 
index b3f373cef801af937cde2be46f3f1a1d1b4606c8..9bd8ea26f411ad38e5a4ae7cd784fbcd1e75c46a 100644 (file)
@@ -376,14 +376,6 @@ START_TEST(test_ch_setup_two_setups_fork)
 }
 END_TEST
 
-/*
- * This test will fail without fork. It expects a checked teardown 
- * fixture to call ck_abort_msg. In fork mode this results in exit() 
- * being called, which signals to the parent process that the test 
- * failed. However, without fork, ck_abort_msg call longjmp, which 
- * jumps to right before the checked teardown fixtures are called.
- * This results in an infinate loop.
- */
 START_TEST(test_ch_teardown_fail)
 {
   TCase *tc;
@@ -429,6 +421,53 @@ START_TEST(test_ch_teardown_fail)
 }
 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:135: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 will fail without fork, as it results in a checked
  * fixture raising a signal, which terminates the test runner early.
@@ -569,6 +608,7 @@ Suite *make_fixture_suite (void)
   tcase_add_test(tc,test_ch_setup_sig);
   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);
   tcase_add_test(tc,test_ch_teardown_sig);
   tcase_add_test(tc,test_ch_teardown_two_teardowns_fork);
 #endif