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
* 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)
/**
* 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
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
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,
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)
if(srunner_run_unchecked_setup(sr, tc))
{
srunner_iterate_tcase_tfuns(sr, tc);
- srunner_run_unchecked_teardown(tc);
+ srunner_run_unchecked_teardown(sr, tc);
}
}
}
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;
}
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.
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