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
|
||||
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
|
||||
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.
|
||||
|
@ -124,6 +124,8 @@ if (gtest_build_tests)
|
||||
test/gtest-param-test2_test.cc)
|
||||
cxx_test(gtest-port_test 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_prod_test gtest_main
|
||||
test/production.cc)
|
||||
|
@ -58,6 +58,7 @@ EXTRA_DIST += \
|
||||
test/gtest-param-test_test.cc \
|
||||
test/gtest-param-test_test.h \
|
||||
test/gtest-port_test.cc \
|
||||
test/gtest_premature_exit_test.cc \
|
||||
test/gtest-printers_test.cc \
|
||||
test/gtest-test-part_test.cc \
|
||||
test/gtest-tuple_test.cc \
|
||||
|
60
src/gtest.cc
60
src/gtest.cc
@ -3523,6 +3523,35 @@ const char* const
|
||||
OsStackTraceGetter::kElidedFramesMarker =
|
||||
"... " 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
|
||||
|
||||
// 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
|
||||
// from the main thread.
|
||||
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
|
||||
// used for the duration of the program.
|
||||
impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
|
||||
|
||||
#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
|
||||
// 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
|
||||
|
@ -66,21 +66,15 @@ EXE_PATH = gtest_test_utils.GetTestExecutablePath(
|
||||
'gtest_break_on_failure_unittest_')
|
||||
|
||||
|
||||
# Utilities.
|
||||
environ = gtest_test_utils.environ
|
||||
SetEnvVar = gtest_test_utils.SetEnvVar
|
||||
|
||||
|
||||
environ = os.environ.copy()
|
||||
|
||||
|
||||
def SetEnvVar(env_var, value):
|
||||
"""Sets an environment variable to a given value; unsets it when the
|
||||
given value is None.
|
||||
"""
|
||||
|
||||
if value is not None:
|
||||
environ[env_var] = value
|
||||
elif env_var in environ:
|
||||
del environ[env_var]
|
||||
# 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)
|
||||
|
||||
|
||||
def Run(command):
|
||||
|
@ -57,14 +57,27 @@ EX_EXE_PATH = gtest_test_utils.GetTestExecutablePath(
|
||||
EXE_PATH = gtest_test_utils.GetTestExecutablePath(
|
||||
'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
|
||||
|
||||
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.
|
||||
if SUPPORTS_SEH_EXCEPTIONS:
|
||||
@ -212,7 +225,8 @@ class CatchCxxExceptionsTest(gtest_test_utils.TestCase):
|
||||
uncaught_exceptions_ex_binary_output = gtest_test_utils.Subprocess(
|
||||
[EX_EXE_PATH,
|
||||
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'
|
||||
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_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
|
||||
# environment. The comment suppresses the 'Invalid variable name' lint
|
||||
# complaint.
|
||||
|
Loading…
Reference in New Issue
Block a user