supports a protocol for catching tests that prematurely exit
This commit is contained in:
parent
492986a5d0
commit
c306ef2e14
4
CHANGES
4
CHANGES
|
@ -2,6 +2,10 @@ Changes for 1.7.0:
|
||||||
|
|
||||||
* New feature: death tests are supported on OpenBSD and in iOS
|
* New feature: death tests are supported on OpenBSD and in iOS
|
||||||
simulator now.
|
simulator now.
|
||||||
|
* New feature: Google Test now implements a protocol to allow
|
||||||
|
a test runner to detect that a test program has exited
|
||||||
|
prematurely and report it as a failure (before it would be
|
||||||
|
falsely reported as a success if the exit code is 0).
|
||||||
* New feature: Test::RecordProperty() can now be used outside of the
|
* New feature: Test::RecordProperty() can now be used outside of the
|
||||||
lifespan of a test method, in which case it will be attributed to
|
lifespan of a test method, in which case it will be attributed to
|
||||||
the current test case or the test program in the XML report.
|
the current test case or the test program in the XML report.
|
||||||
|
|
|
@ -124,6 +124,8 @@ if (gtest_build_tests)
|
||||||
test/gtest-param-test2_test.cc)
|
test/gtest-param-test2_test.cc)
|
||||||
cxx_test(gtest-port_test gtest_main)
|
cxx_test(gtest-port_test gtest_main)
|
||||||
cxx_test(gtest_pred_impl_unittest gtest_main)
|
cxx_test(gtest_pred_impl_unittest gtest_main)
|
||||||
|
cxx_test(gtest_premature_exit_test gtest
|
||||||
|
test/gtest_premature_exit_test.cc)
|
||||||
cxx_test(gtest-printers_test gtest_main)
|
cxx_test(gtest-printers_test gtest_main)
|
||||||
cxx_test(gtest_prod_test gtest_main
|
cxx_test(gtest_prod_test gtest_main
|
||||||
test/production.cc)
|
test/production.cc)
|
||||||
|
|
|
@ -58,6 +58,7 @@ EXTRA_DIST += \
|
||||||
test/gtest-param-test_test.cc \
|
test/gtest-param-test_test.cc \
|
||||||
test/gtest-param-test_test.h \
|
test/gtest-param-test_test.h \
|
||||||
test/gtest-port_test.cc \
|
test/gtest-port_test.cc \
|
||||||
|
test/gtest_premature_exit_test.cc \
|
||||||
test/gtest-printers_test.cc \
|
test/gtest-printers_test.cc \
|
||||||
test/gtest-test-part_test.cc \
|
test/gtest-test-part_test.cc \
|
||||||
test/gtest-tuple_test.cc \
|
test/gtest-tuple_test.cc \
|
||||||
|
|
60
src/gtest.cc
60
src/gtest.cc
|
@ -3523,6 +3523,35 @@ const char* const
|
||||||
OsStackTraceGetter::kElidedFramesMarker =
|
OsStackTraceGetter::kElidedFramesMarker =
|
||||||
"... " GTEST_NAME_ " internal frames ...";
|
"... " GTEST_NAME_ " internal frames ...";
|
||||||
|
|
||||||
|
// A helper class that creates the premature-exit file in its
|
||||||
|
// constructor and deletes the file in its destructor.
|
||||||
|
class ScopedPrematureExitFile {
|
||||||
|
public:
|
||||||
|
explicit ScopedPrematureExitFile(const char* premature_exit_filepath)
|
||||||
|
: premature_exit_filepath_(premature_exit_filepath) {
|
||||||
|
// If a path to the premature-exit file is specified...
|
||||||
|
if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') {
|
||||||
|
// create the file with a single "0" character in it. I/O
|
||||||
|
// errors are ignored as there's nothing better we can do and we
|
||||||
|
// don't want to fail the test because of this.
|
||||||
|
FILE* pfile = posix::FOpen(premature_exit_filepath, "w");
|
||||||
|
fwrite("0", 1, 1, pfile);
|
||||||
|
fclose(pfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedPrematureExitFile() {
|
||||||
|
if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') {
|
||||||
|
remove(premature_exit_filepath_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* const premature_exit_filepath_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// class TestEventListeners
|
// class TestEventListeners
|
||||||
|
@ -3823,14 +3852,39 @@ void UnitTest::RecordProperty(const std::string& key,
|
||||||
// We don't protect this under mutex_, as we only support calling it
|
// We don't protect this under mutex_, as we only support calling it
|
||||||
// from the main thread.
|
// from the main thread.
|
||||||
int UnitTest::Run() {
|
int UnitTest::Run() {
|
||||||
|
const bool in_death_test_child_process =
|
||||||
|
internal::GTEST_FLAG(internal_run_death_test).length() > 0;
|
||||||
|
|
||||||
|
// Google Test implements this protocol for catching that a test
|
||||||
|
// program exits before returning control to Google Test:
|
||||||
|
//
|
||||||
|
// 1. Upon start, Google Test creates a file whose absolute path
|
||||||
|
// is specified by the environment variable
|
||||||
|
// TEST_PREMATURE_EXIT_FILE.
|
||||||
|
// 2. When Google Test has finished its work, it deletes the file.
|
||||||
|
//
|
||||||
|
// This allows a test runner to set TEST_PREMATURE_EXIT_FILE before
|
||||||
|
// running a Google-Test-based test program and check the existence
|
||||||
|
// of the file at the end of the test execution to see if it has
|
||||||
|
// exited prematurely.
|
||||||
|
|
||||||
|
// If we are in the child process of a death test, don't
|
||||||
|
// create/delete the premature exit file, as doing so is unnecessary
|
||||||
|
// and will confuse the parent process. Otherwise, create/delete
|
||||||
|
// the file upon entering/leaving this function. If the program
|
||||||
|
// somehow exits before this function has a chance to return, the
|
||||||
|
// premature-exit file will be left undeleted, causing a test runner
|
||||||
|
// that understands the premature-exit-file protocol to report the
|
||||||
|
// test as having failed.
|
||||||
|
const internal::ScopedPrematureExitFile premature_exit_file(
|
||||||
|
in_death_test_child_process ?
|
||||||
|
NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE"));
|
||||||
|
|
||||||
// Captures the value of GTEST_FLAG(catch_exceptions). This value will be
|
// Captures the value of GTEST_FLAG(catch_exceptions). This value will be
|
||||||
// used for the duration of the program.
|
// used for the duration of the program.
|
||||||
impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
|
impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
|
||||||
|
|
||||||
#if GTEST_HAS_SEH
|
#if GTEST_HAS_SEH
|
||||||
const bool in_death_test_child_process =
|
|
||||||
internal::GTEST_FLAG(internal_run_death_test).length() > 0;
|
|
||||||
|
|
||||||
// Either the user wants Google Test to catch exceptions thrown by the
|
// Either the user wants Google Test to catch exceptions thrown by the
|
||||||
// tests or this is executing in the context of death test child
|
// tests or this is executing in the context of death test child
|
||||||
// process. In either case the user does not want to see pop-up dialogs
|
// process. In either case the user does not want to see pop-up dialogs
|
||||||
|
|
|
@ -66,21 +66,15 @@ EXE_PATH = gtest_test_utils.GetTestExecutablePath(
|
||||||
'gtest_break_on_failure_unittest_')
|
'gtest_break_on_failure_unittest_')
|
||||||
|
|
||||||
|
|
||||||
# Utilities.
|
environ = gtest_test_utils.environ
|
||||||
|
SetEnvVar = gtest_test_utils.SetEnvVar
|
||||||
|
|
||||||
|
# Tests in this file run a Google-Test-based test program and expect it
|
||||||
environ = os.environ.copy()
|
# to terminate prematurely. Therefore they are incompatible with
|
||||||
|
# the premature-exit-file protocol by design. Unset the
|
||||||
|
# premature-exit filepath to prevent Google Test from creating
|
||||||
def SetEnvVar(env_var, value):
|
# the file.
|
||||||
"""Sets an environment variable to a given value; unsets it when the
|
SetEnvVar(gtest_test_utils.PREMATURE_EXIT_FILE_ENV_VAR, None)
|
||||||
given value is None.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if value is not None:
|
|
||||||
environ[env_var] = value
|
|
||||||
elif env_var in environ:
|
|
||||||
del environ[env_var]
|
|
||||||
|
|
||||||
|
|
||||||
def Run(command):
|
def Run(command):
|
||||||
|
|
|
@ -57,14 +57,27 @@ EX_EXE_PATH = gtest_test_utils.GetTestExecutablePath(
|
||||||
EXE_PATH = gtest_test_utils.GetTestExecutablePath(
|
EXE_PATH = gtest_test_utils.GetTestExecutablePath(
|
||||||
'gtest_catch_exceptions_no_ex_test_')
|
'gtest_catch_exceptions_no_ex_test_')
|
||||||
|
|
||||||
TEST_LIST = gtest_test_utils.Subprocess([EXE_PATH, LIST_TESTS_FLAG]).output
|
environ = gtest_test_utils.environ
|
||||||
|
SetEnvVar = gtest_test_utils.SetEnvVar
|
||||||
|
|
||||||
|
# Tests in this file run a Google-Test-based test program and expect it
|
||||||
|
# to terminate prematurely. Therefore they are incompatible with
|
||||||
|
# the premature-exit-file protocol by design. Unset the
|
||||||
|
# premature-exit filepath to prevent Google Test from creating
|
||||||
|
# the file.
|
||||||
|
SetEnvVar(gtest_test_utils.PREMATURE_EXIT_FILE_ENV_VAR, None)
|
||||||
|
|
||||||
|
TEST_LIST = gtest_test_utils.Subprocess(
|
||||||
|
[EXE_PATH, LIST_TESTS_FLAG], env=environ).output
|
||||||
|
|
||||||
SUPPORTS_SEH_EXCEPTIONS = 'ThrowsSehException' in TEST_LIST
|
SUPPORTS_SEH_EXCEPTIONS = 'ThrowsSehException' in TEST_LIST
|
||||||
|
|
||||||
if SUPPORTS_SEH_EXCEPTIONS:
|
if SUPPORTS_SEH_EXCEPTIONS:
|
||||||
BINARY_OUTPUT = gtest_test_utils.Subprocess([EXE_PATH]).output
|
BINARY_OUTPUT = gtest_test_utils.Subprocess([EXE_PATH], env=environ).output
|
||||||
|
|
||||||
|
EX_BINARY_OUTPUT = gtest_test_utils.Subprocess(
|
||||||
|
[EX_EXE_PATH], env=environ).output
|
||||||
|
|
||||||
EX_BINARY_OUTPUT = gtest_test_utils.Subprocess([EX_EXE_PATH]).output
|
|
||||||
|
|
||||||
# The tests.
|
# The tests.
|
||||||
if SUPPORTS_SEH_EXCEPTIONS:
|
if SUPPORTS_SEH_EXCEPTIONS:
|
||||||
|
@ -212,7 +225,8 @@ class CatchCxxExceptionsTest(gtest_test_utils.TestCase):
|
||||||
uncaught_exceptions_ex_binary_output = gtest_test_utils.Subprocess(
|
uncaught_exceptions_ex_binary_output = gtest_test_utils.Subprocess(
|
||||||
[EX_EXE_PATH,
|
[EX_EXE_PATH,
|
||||||
NO_CATCH_EXCEPTIONS_FLAG,
|
NO_CATCH_EXCEPTIONS_FLAG,
|
||||||
FITLER_OUT_SEH_TESTS_FLAG]).output
|
FITLER_OUT_SEH_TESTS_FLAG],
|
||||||
|
env=environ).output
|
||||||
|
|
||||||
self.assert_('Unhandled C++ exception terminating the program'
|
self.assert_('Unhandled C++ exception terminating the program'
|
||||||
in uncaught_exceptions_ex_binary_output)
|
in uncaught_exceptions_ex_binary_output)
|
||||||
|
|
141
test/gtest_premature_exit_test.cc
Normal file
141
test/gtest_premature_exit_test.cc
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright 2013, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Author: wan@google.com (Zhanyong Wan)
|
||||||
|
//
|
||||||
|
// Tests that Google Test manipulates the premature-exit-detection
|
||||||
|
// file correctly.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using ::testing::InitGoogleTest;
|
||||||
|
using ::testing::Test;
|
||||||
|
using ::testing::internal::posix::GetEnv;
|
||||||
|
using ::testing::internal::posix::Stat;
|
||||||
|
using ::testing::internal::posix::StatStruct;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Is the TEST_PREMATURE_EXIT_FILE environment variable expected to be
|
||||||
|
// set?
|
||||||
|
const bool kTestPrematureExitFileEnvVarShouldBeSet = false;
|
||||||
|
|
||||||
|
class PrematureExitTest : public Test {
|
||||||
|
public:
|
||||||
|
// Returns true iff the given file exists.
|
||||||
|
static bool FileExists(const char* filepath) {
|
||||||
|
StatStruct stat;
|
||||||
|
return Stat(filepath, &stat) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PrematureExitTest() {
|
||||||
|
premature_exit_file_path_ = GetEnv("TEST_PREMATURE_EXIT_FILE");
|
||||||
|
|
||||||
|
// Normalize NULL to "" for ease of handling.
|
||||||
|
if (premature_exit_file_path_ == NULL) {
|
||||||
|
premature_exit_file_path_ = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true iff the premature-exit file exists.
|
||||||
|
bool PrematureExitFileExists() const {
|
||||||
|
return FileExists(premature_exit_file_path_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* premature_exit_file_path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef PrematureExitTest PrematureExitDeathTest;
|
||||||
|
|
||||||
|
// Tests that:
|
||||||
|
// - the premature-exit file exists during the execution of a
|
||||||
|
// death test (EXPECT_DEATH*), and
|
||||||
|
// - a death test doesn't interfere with the main test process's
|
||||||
|
// handling of the premature-exit file.
|
||||||
|
TEST_F(PrematureExitDeathTest, FileExistsDuringExecutionOfDeathTest) {
|
||||||
|
if (*premature_exit_file_path_ == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_DEATH_IF_SUPPORTED({
|
||||||
|
// If the file exists, crash the process such that the main test
|
||||||
|
// process will catch the (expected) crash and report a success;
|
||||||
|
// otherwise don't crash, which will cause the main test process
|
||||||
|
// to report that the death test has failed.
|
||||||
|
if (PrematureExitFileExists()) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that TEST_PREMATURE_EXIT_FILE is set where it's expected to
|
||||||
|
// be set.
|
||||||
|
TEST_F(PrematureExitTest, TestPrematureExitFileEnvVarIsSet) {
|
||||||
|
if (kTestPrematureExitFileEnvVarShouldBeSet) {
|
||||||
|
const char* const filepath = GetEnv("TEST_PREMATURE_EXIT_FILE");
|
||||||
|
ASSERT_TRUE(filepath != NULL);
|
||||||
|
ASSERT_NE(*filepath, '\0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the premature-exit file exists during the execution of a
|
||||||
|
// normal (non-death) test.
|
||||||
|
TEST_F(PrematureExitTest, PrematureExitFileExistsDuringTestExecution) {
|
||||||
|
if (*premature_exit_file_path_ == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(PrematureExitFileExists())
|
||||||
|
<< " file " << premature_exit_file_path_
|
||||||
|
<< " should exist during test execution, but doesn't.";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
InitGoogleTest(&argc, argv);
|
||||||
|
const int exit_code = RUN_ALL_TESTS();
|
||||||
|
|
||||||
|
// Test that the premature-exit file is deleted upon return from
|
||||||
|
// RUN_ALL_TESTS().
|
||||||
|
const char* const filepath = GetEnv("TEST_PREMATURE_EXIT_FILE");
|
||||||
|
if (filepath != NULL && *filepath != '\0') {
|
||||||
|
if (PrematureExitTest::FileExists(filepath)) {
|
||||||
|
printf(
|
||||||
|
"File %s shouldn't exist after the test program finishes, but does.",
|
||||||
|
filepath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return exit_code;
|
||||||
|
}
|
|
@ -56,6 +56,21 @@ GTEST_OUTPUT_VAR_NAME = 'GTEST_OUTPUT'
|
||||||
IS_WINDOWS = os.name == 'nt'
|
IS_WINDOWS = os.name == 'nt'
|
||||||
IS_CYGWIN = os.name == 'posix' and 'CYGWIN' in os.uname()[0]
|
IS_CYGWIN = os.name == 'posix' and 'CYGWIN' in os.uname()[0]
|
||||||
|
|
||||||
|
# The environment variable for specifying the path to the premature-exit file.
|
||||||
|
PREMATURE_EXIT_FILE_ENV_VAR = 'TEST_PREMATURE_EXIT_FILE'
|
||||||
|
|
||||||
|
environ = os.environ.copy()
|
||||||
|
|
||||||
|
|
||||||
|
def SetEnvVar(env_var, value):
|
||||||
|
"""Sets/unsets an environment variable to a given value."""
|
||||||
|
|
||||||
|
if value is not None:
|
||||||
|
environ[env_var] = value
|
||||||
|
elif env_var in environ:
|
||||||
|
del environ[env_var]
|
||||||
|
|
||||||
|
|
||||||
# Here we expose a class from a particular module, depending on the
|
# Here we expose a class from a particular module, depending on the
|
||||||
# environment. The comment suppresses the 'Invalid variable name' lint
|
# environment. The comment suppresses the 'Invalid variable name' lint
|
||||||
# complaint.
|
# complaint.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user