| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // https://developers.google.com/protocol-buffers/ |
| // |
| // 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: kenton@google.com (Kenton Varda) |
| // emulates google3/testing/base/public/googletest.cc |
| |
| #include <google/protobuf/testing/googletest.h> |
| #include <google/protobuf/testing/file.h> |
| #include <google/protobuf/stubs/strutil.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #ifdef _MSC_VER |
| #include <io.h> |
| #include <direct.h> |
| #else |
| #include <unistd.h> |
| #endif |
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <iostream> |
| #include <fstream> |
| |
| namespace google { |
| namespace protobuf { |
| |
| #ifdef _WIN32 |
| #define mkdir(name, mode) mkdir(name) |
| #endif |
| |
| #ifndef O_BINARY |
| #ifdef _O_BINARY |
| #define O_BINARY _O_BINARY |
| #else |
| #define O_BINARY 0 // If this isn't defined, the platform doesn't need it. |
| #endif |
| #endif |
| |
| string TestSourceDir() { |
| #ifndef GOOGLE_THIRD_PARTY_PROTOBUF |
| #ifndef _MSC_VER |
| // automake sets the "srcdir" environment variable. |
| char* result = getenv("srcdir"); |
| if (result != NULL) { |
| return result; |
| } |
| #endif // _MSC_VER |
| |
| // Look for the "src" directory. |
| string prefix = "."; |
| |
| while (!File::Exists(prefix + "/src/google/protobuf")) { |
| if (!File::Exists(prefix)) { |
| GOOGLE_LOG(FATAL) |
| << "Could not find protobuf source code. Please run tests from " |
| "somewhere within the protobuf source package."; |
| } |
| prefix += "/.."; |
| } |
| return prefix + "/src"; |
| #else |
| return "third_party/protobuf/src"; |
| #endif // GOOGLE_THIRD_PARTY_PROTOBUF |
| } |
| |
| namespace { |
| |
| string GetTemporaryDirectoryName() { |
| // Tests run under Bazel "should not" use /tmp. Bazel sets this environment |
| // variable for tests to use instead. |
| char *from_environment = getenv("TEST_TMPDIR"); |
| if (from_environment != NULL && from_environment[0] != '\0') { |
| return string(from_environment) + "/protobuf_tmpdir"; |
| } |
| |
| // tmpnam() is generally not considered safe but we're only using it for |
| // testing. We cannot use tmpfile() or mkstemp() since we're creating a |
| // directory. |
| char b[L_tmpnam + 1]; // HPUX multithread return 0 if s is 0 |
| string result = tmpnam(b); |
| #ifdef _WIN32 |
| // On Win32, tmpnam() returns a file prefixed with '\', but which is supposed |
| // to be used in the current working directory. WTF? |
| if (HasPrefixString(result, "\\")) { |
| result.erase(0, 1); |
| } |
| // The Win32 API accepts forward slashes as a path delimiter even though |
| // backslashes are standard. Let's avoid confusion and use only forward |
| // slashes. |
| result = StringReplace(result, "\\", "/", true); |
| #endif // _WIN32 |
| return result; |
| } |
| |
| // Creates a temporary directory on demand and deletes it when the process |
| // quits. |
| class TempDirDeleter { |
| public: |
| TempDirDeleter() {} |
| ~TempDirDeleter() { |
| if (!name_.empty()) { |
| File::DeleteRecursively(name_, NULL, NULL); |
| } |
| } |
| |
| string GetTempDir() { |
| if (name_.empty()) { |
| name_ = GetTemporaryDirectoryName(); |
| GOOGLE_CHECK(mkdir(name_.c_str(), 0777) == 0) << strerror(errno); |
| |
| // Stick a file in the directory that tells people what this is, in case |
| // we abort and don't get a chance to delete it. |
| File::WriteStringToFileOrDie("", name_ + "/TEMP_DIR_FOR_PROTOBUF_TESTS"); |
| } |
| return name_; |
| } |
| |
| private: |
| string name_; |
| }; |
| |
| TempDirDeleter temp_dir_deleter_; |
| |
| } // namespace |
| |
| string TestTempDir() { |
| return temp_dir_deleter_.GetTempDir(); |
| } |
| |
| // TODO(kenton): Share duplicated code below. Too busy/lazy for now. |
| |
| static string stdout_capture_filename_; |
| static string stderr_capture_filename_; |
| static int original_stdout_ = -1; |
| static int original_stderr_ = -1; |
| |
| void CaptureTestStdout() { |
| GOOGLE_CHECK_EQ(original_stdout_, -1) << "Already capturing."; |
| |
| stdout_capture_filename_ = TestTempDir() + "/captured_stdout"; |
| |
| int fd = open(stdout_capture_filename_.c_str(), |
| O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777); |
| GOOGLE_CHECK(fd >= 0) << "open: " << strerror(errno); |
| |
| original_stdout_ = dup(1); |
| close(1); |
| dup2(fd, 1); |
| close(fd); |
| } |
| |
| void CaptureTestStderr() { |
| GOOGLE_CHECK_EQ(original_stderr_, -1) << "Already capturing."; |
| |
| stderr_capture_filename_ = TestTempDir() + "/captured_stderr"; |
| |
| int fd = open(stderr_capture_filename_.c_str(), |
| O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777); |
| GOOGLE_CHECK(fd >= 0) << "open: " << strerror(errno); |
| |
| original_stderr_ = dup(2); |
| close(2); |
| dup2(fd, 2); |
| close(fd); |
| } |
| |
| string GetCapturedTestStdout() { |
| GOOGLE_CHECK_NE(original_stdout_, -1) << "Not capturing."; |
| |
| close(1); |
| dup2(original_stdout_, 1); |
| original_stdout_ = -1; |
| |
| string result; |
| File::ReadFileToStringOrDie(stdout_capture_filename_, &result); |
| |
| remove(stdout_capture_filename_.c_str()); |
| |
| return result; |
| } |
| |
| string GetCapturedTestStderr() { |
| GOOGLE_CHECK_NE(original_stderr_, -1) << "Not capturing."; |
| |
| close(2); |
| dup2(original_stderr_, 2); |
| original_stderr_ = -1; |
| |
| string result; |
| File::ReadFileToStringOrDie(stderr_capture_filename_, &result); |
| |
| remove(stderr_capture_filename_.c_str()); |
| |
| return result; |
| } |
| |
| ScopedMemoryLog* ScopedMemoryLog::active_log_ = NULL; |
| |
| ScopedMemoryLog::ScopedMemoryLog() { |
| GOOGLE_CHECK(active_log_ == NULL); |
| active_log_ = this; |
| old_handler_ = SetLogHandler(&HandleLog); |
| } |
| |
| ScopedMemoryLog::~ScopedMemoryLog() { |
| SetLogHandler(old_handler_); |
| active_log_ = NULL; |
| } |
| |
| const vector<string>& ScopedMemoryLog::GetMessages(LogLevel level) { |
| GOOGLE_CHECK(level == ERROR || |
| level == WARNING); |
| return messages_[level]; |
| } |
| |
| void ScopedMemoryLog::HandleLog(LogLevel level, const char* filename, |
| int line, const string& message) { |
| GOOGLE_CHECK(active_log_ != NULL); |
| if (level == ERROR || level == WARNING) { |
| active_log_->messages_[level].push_back(message); |
| } |
| } |
| |
| namespace { |
| |
| // Force shutdown at process exit so that we can test for memory leaks. To |
| // actually check for leaks, I suggest using the heap checker included with |
| // google-perftools. Set it to "draconian" mode to ensure that every last |
| // call to malloc() has a corresponding free(). |
| struct ForceShutdown { |
| ~ForceShutdown() { |
| ShutdownProtobufLibrary(); |
| } |
| } force_shutdown; |
| |
| } // namespace |
| |
| } // namespace protobuf |
| } // namespace google |