Pull up revision 2f7a426c0f4149d59a7f3717ebedd6c55998e8bc from upstream:
---------- Fix detection of crashed test cases Prevent cross-test-case contamination that occured when a first test case passed and the second crashed. The second could pick up the result of the first test case and not be reported as failed. Similarly, change the way timed out test cases are reported back to the caller. The creation of a temporary results file was just a really stupid way of passing information around and introduced false positives if the test case creates a results file before timing out. Fixes ticket #35. ---------- Problem originally reported by pooka@.
This commit is contained in:
parent
3ab6a60f8f
commit
21e717d349
|
@ -97,7 +97,7 @@ class atf_run : public atf::application::app {
|
|||
const atf::tests::vars_map&,
|
||||
const atf::fs::path&);
|
||||
|
||||
atf::tests::tcr get_tcr(const atf::process::status&,
|
||||
atf::tests::tcr get_tcr(const std::string&, const atf::process::status&,
|
||||
const atf::fs::path&) const;
|
||||
|
||||
public:
|
||||
|
@ -212,12 +212,16 @@ atf_run::run_test_directory(const atf::fs::path& tp,
|
|||
}
|
||||
|
||||
atf::tests::tcr
|
||||
atf_run::get_tcr(const atf::process::status& s,
|
||||
atf_run::get_tcr(const std::string& broken_reason,
|
||||
const atf::process::status& s,
|
||||
const atf::fs::path& resfile)
|
||||
const
|
||||
{
|
||||
using atf::tests::tcr;
|
||||
|
||||
if (!broken_reason.empty())
|
||||
return tcr(tcr::failed_state, broken_reason);
|
||||
|
||||
if (s.exited()) {
|
||||
try {
|
||||
const tcr ret = tcr::read(resfile);
|
||||
|
@ -244,14 +248,10 @@ atf_run::get_tcr(const atf::process::status& s,
|
|||
std::string(e.what()));
|
||||
}
|
||||
} else if (s.signaled()) {
|
||||
try {
|
||||
return tcr::read(resfile);
|
||||
} catch (...) {
|
||||
return tcr(tcr::failed_state,
|
||||
"Test program received signal " +
|
||||
atf::text::to_string(s.termsig()) +
|
||||
(s.coredump() ? " (core dumped)" : ""));
|
||||
}
|
||||
return tcr(tcr::failed_state,
|
||||
"Test program received signal " +
|
||||
atf::text::to_string(s.termsig()) +
|
||||
(s.coredump() ? " (core dumped)" : ""));
|
||||
} else {
|
||||
UNREACHABLE;
|
||||
throw std::runtime_error("Unknown exit status");
|
||||
|
@ -311,6 +311,7 @@ atf_run::run_test_program(const atf::fs::path& tp,
|
|||
}
|
||||
|
||||
const atf::fs::path resfile = resdir.get_path() / "tcr";
|
||||
INV(!atf::fs::exists(resfile));
|
||||
try {
|
||||
const bool use_fs = atf::text::to_bool(
|
||||
(*tcmd.find("use.fs")).second);
|
||||
|
@ -321,34 +322,35 @@ atf_run::run_test_program(const atf::fs::path& tp,
|
|||
atf::fs::temp_dir workdir(atf::fs::path(atf::config::get(
|
||||
"atf_workdir")) / "atf-run.XXXXXX");
|
||||
|
||||
const atf::process::status body_status =
|
||||
std::pair< std::string, const atf::process::status > s =
|
||||
impl::run_test_case(tp, tcname, "body", tcmd, config,
|
||||
resfile, workdir.get_path(), w);
|
||||
const atf::process::status cleanup_status =
|
||||
impl::run_test_case(tp, tcname, "cleanup", tcmd, config,
|
||||
resfile, workdir.get_path(), w);
|
||||
(void)impl::run_test_case(tp, tcname, "cleanup", tcmd, config,
|
||||
resfile, workdir.get_path(), w);
|
||||
|
||||
// TODO: Force deletion of workdir.
|
||||
|
||||
tcr = get_tcr(body_status, resfile);
|
||||
tcr = get_tcr(s.first, s.second, resfile);
|
||||
} else {
|
||||
const atf::process::status body_status =
|
||||
std::pair< std::string, const atf::process::status > s =
|
||||
impl::run_test_case(tp, tcname, "body", tcmd, config,
|
||||
resfile, ro_workdir, w);
|
||||
const atf::process::status cleanup_status =
|
||||
impl::run_test_case(tp, tcname, "cleanup", tcmd, config,
|
||||
resfile, ro_workdir, w);
|
||||
(void)impl::run_test_case(tp, tcname, "cleanup", tcmd, config,
|
||||
resfile, ro_workdir, w);
|
||||
|
||||
tcr = get_tcr(body_status, resfile);
|
||||
tcr = get_tcr(s.first, s.second, resfile);
|
||||
}
|
||||
|
||||
w.end_tc(tcr);
|
||||
if (tcr.get_state() == atf::tests::tcr::failed_state)
|
||||
errcode = EXIT_FAILURE;
|
||||
} catch (...) {
|
||||
atf::fs::remove(resfile);
|
||||
if (atf::fs::exists(resfile))
|
||||
atf::fs::remove(resfile);
|
||||
throw;
|
||||
}
|
||||
if (atf::fs::exists(resfile))
|
||||
atf::fs::remove(resfile);
|
||||
|
||||
}
|
||||
w.end_tp("");
|
||||
}
|
||||
|
|
|
@ -186,23 +186,6 @@ exec_or_exit(const atf::fs::path& executable,
|
|||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
create_timeout_resfile(const atf::fs::path& path, const unsigned int timeout)
|
||||
{
|
||||
std::ofstream os(path.c_str());
|
||||
if (!os)
|
||||
throw std::runtime_error("Failed to create " + path.str());
|
||||
|
||||
const std::string reason = "Test case timed out after " +
|
||||
atf::text::to_string(timeout) + " " +
|
||||
(timeout == 1 ? "second" : "seconds");
|
||||
|
||||
atf::formats::atf_tcr_writer writer(os);
|
||||
writer.result("failed");
|
||||
writer.reason(reason);
|
||||
}
|
||||
|
||||
static
|
||||
std::vector< std::string >
|
||||
config_to_args(const atf::tests::vars_map& config)
|
||||
|
@ -309,7 +292,7 @@ impl::get_metadata(const atf::fs::path& executable,
|
|||
return metadata(parser.get_tcs());
|
||||
}
|
||||
|
||||
atf::process::status
|
||||
std::pair< std::string, atf::process::status >
|
||||
impl::run_test_case(const atf::fs::path& executable,
|
||||
const std::string& test_case_name,
|
||||
const std::string& test_case_part,
|
||||
|
@ -357,11 +340,14 @@ impl::run_test_case(const atf::fs::path& executable,
|
|||
atf::process::status status = child.wait();
|
||||
::killpg(child_pid, SIGKILL);
|
||||
|
||||
std::string reason;
|
||||
|
||||
if (timeout_timer.fired()) {
|
||||
INV(status.signaled());
|
||||
INV(status.termsig() == SIGKILL);
|
||||
create_timeout_resfile(resfile, timeout);
|
||||
reason = "Test case timed out after " + atf::text::to_string(timeout) +
|
||||
" " + (timeout == 1 ? "second" : "seconds");
|
||||
}
|
||||
|
||||
return status;
|
||||
return std::make_pair(reason, status);
|
||||
}
|
||||
|
|
|
@ -53,14 +53,10 @@ struct metadata {
|
|||
};
|
||||
|
||||
metadata get_metadata(const atf::fs::path&, const atf::tests::vars_map&);
|
||||
atf::process::status run_test_case(const atf::fs::path&,
|
||||
const std::string&,
|
||||
const std::string&,
|
||||
const atf::tests::vars_map&,
|
||||
const atf::tests::vars_map&,
|
||||
const atf::fs::path&,
|
||||
const atf::fs::path&,
|
||||
atf::formats::atf_tps_writer&);
|
||||
std::pair< std::string, atf::process::status > run_test_case(const atf::fs::path&,
|
||||
const std::string&, const std::string&, const atf::tests::vars_map&,
|
||||
const atf::tests::vars_map&, const atf::fs::path&, const atf::fs::path&,
|
||||
atf::formats::atf_tps_writer&);
|
||||
|
||||
} // namespace atf_run
|
||||
} // namespace atf
|
||||
|
|
|
@ -49,6 +49,7 @@ create_helper()
|
|||
|
||||
create_helper_stdin()
|
||||
{
|
||||
# TODO: This really, really, really must use real test programs.
|
||||
cat >${1} <<EOF
|
||||
#! $(atf-config -t atf_shell)
|
||||
while [ \${#} -gt 0 ]; do
|
||||
|
@ -57,12 +58,11 @@ while [ \${#} -gt 0 ]; do
|
|||
echo 'Content-Type: application/X-atf-tp; version="1"'
|
||||
echo
|
||||
EOF
|
||||
cnt=${2}
|
||||
while [ ${cnt} -gt 0 ]; do
|
||||
cnt=1
|
||||
while [ ${cnt} -le ${2} ]; do
|
||||
echo "echo 'ident: tc${cnt}'" >>${1}
|
||||
cnt=$((${cnt} - 1))
|
||||
|
||||
[ ${cnt} -gt 0 ] && echo
|
||||
[ ${cnt} -lt ${2} ] && echo "echo" >>${1}
|
||||
cnt=$((${cnt} + 1))
|
||||
done
|
||||
cat >>${1} <<EOF
|
||||
exit 0
|
||||
|
@ -71,6 +71,7 @@ cat >>${1} <<EOF
|
|||
resfile=\$(echo \${1} | cut -d r -f 2-)
|
||||
;;
|
||||
esac
|
||||
testcase=\$(echo \${1} | cut -d : -f 1)
|
||||
shift
|
||||
done
|
||||
EOF
|
||||
|
@ -412,20 +413,21 @@ signaled_head()
|
|||
}
|
||||
signaled_body()
|
||||
{
|
||||
create_helper_stdin helper 1 <<EOF
|
||||
echo 'Content-Type: application/X-atf-tcs; version="1"' 1>&9
|
||||
echo 1>&9
|
||||
echo "tcs-count: 1" 1>&9
|
||||
echo "tc-start: tc1" 1>&9
|
||||
echo "tc-end: tc1, failed, Will fail" 1>&9
|
||||
kill -9 \$\$
|
||||
create_helper_stdin helper 2 <<EOF
|
||||
echo 'Content-Type: application/X-atf-tcr; version="1"' >\${resfile}
|
||||
echo >>\${resfile}
|
||||
echo "result: passed" >>\${resfile}
|
||||
case \${testcase} in
|
||||
tc1) ;;
|
||||
tc2) echo "Killing myself!" ; kill -9 \$\$ ;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x helper
|
||||
|
||||
create_atffile helper
|
||||
|
||||
atf_check -s eq:1 -o save:stdout -e empty atf-run
|
||||
atf_check -s eq:0 -o save:stdout -e empty grep '^tc-end: tc1, ' stdout
|
||||
atf_check -s eq:0 -o save:stdout -e empty grep '^tc-end: tc2, ' stdout
|
||||
atf_check -s eq:0 -o ignore -e empty grep 'received signal 9' stdout
|
||||
}
|
||||
|
||||
|
@ -451,7 +453,6 @@ EOF
|
|||
create_atffile helper
|
||||
|
||||
atf_check -s eq:1 -o save:stdout -e empty atf-run
|
||||
cat stdout
|
||||
atf_check -s eq:0 -o ignore -e empty \
|
||||
grep '^tc-end: tc1,.*No reason specified' stdout
|
||||
done
|
||||
|
|
Loading…
Reference in New Issue