1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// http://code.google.com/p/protobuf/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9//     * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11//     * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15//     * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: kenton@google.com (Kenton Varda)
32// emulates google3/testing/base/public/googletest.cc
33
34#include <google/protobuf/testing/googletest.h>
35#include <google/protobuf/testing/file.h>
36#include <google/protobuf/stubs/strutil.h>
37#include <sys/stat.h>
38#include <sys/types.h>
39#include <errno.h>
40#include <stdlib.h>
41#ifdef _MSC_VER
42#include <io.h>
43#include <direct.h>
44#else
45#include <unistd.h>
46#endif
47#include <stdio.h>
48#include <fcntl.h>
49#include <iostream>
50#include <fstream>
51
52namespace google {
53namespace protobuf {
54
55#ifdef _WIN32
56#define mkdir(name, mode) mkdir(name)
57#endif
58
59#ifndef O_BINARY
60#ifdef _O_BINARY
61#define O_BINARY _O_BINARY
62#else
63#define O_BINARY 0     // If this isn't defined, the platform doesn't need it.
64#endif
65#endif
66
67string TestSourceDir() {
68#ifdef _MSC_VER
69  // Look for the "src" directory.
70  string prefix = ".";
71
72  while (!File::Exists(prefix + "/src/google/protobuf")) {
73    if (!File::Exists(prefix)) {
74      GOOGLE_LOG(FATAL)
75        << "Could not find protobuf source code.  Please run tests from "
76           "somewhere within the protobuf source package.";
77    }
78    prefix += "/..";
79  }
80  return prefix + "/src";
81#else
82  // automake sets the "srcdir" environment variable.
83  char* result = getenv("srcdir");
84  if (result == NULL) {
85    // Otherwise, the test must be run from the source directory.
86    return ".";
87  } else {
88    return result;
89  }
90#endif
91}
92
93namespace {
94
95string GetTemporaryDirectoryName() {
96  // tmpnam() is generally not considered safe but we're only using it for
97  // testing.  We cannot use tmpfile() or mkstemp() since we're creating a
98  // directory.
99  char b[L_tmpnam + 1];     // HPUX multithread return 0 if s is 0
100  string result = tmpnam(b);
101#ifdef _WIN32
102  // On Win32, tmpnam() returns a file prefixed with '\', but which is supposed
103  // to be used in the current working directory.  WTF?
104  if (HasPrefixString(result, "\\")) {
105    result.erase(0, 1);
106  }
107#endif  // _WIN32
108  return result;
109}
110
111// Creates a temporary directory on demand and deletes it when the process
112// quits.
113class TempDirDeleter {
114 public:
115  TempDirDeleter() {}
116  ~TempDirDeleter() {
117    if (!name_.empty()) {
118      File::DeleteRecursively(name_, NULL, NULL);
119    }
120  }
121
122  string GetTempDir() {
123    if (name_.empty()) {
124      name_ = GetTemporaryDirectoryName();
125      GOOGLE_CHECK(mkdir(name_.c_str(), 0777) == 0) << strerror(errno);
126
127      // Stick a file in the directory that tells people what this is, in case
128      // we abort and don't get a chance to delete it.
129      File::WriteStringToFileOrDie("", name_ + "/TEMP_DIR_FOR_PROTOBUF_TESTS");
130    }
131    return name_;
132  }
133
134 private:
135  string name_;
136};
137
138TempDirDeleter temp_dir_deleter_;
139
140}  // namespace
141
142string TestTempDir() {
143  return temp_dir_deleter_.GetTempDir();
144}
145
146// TODO(kenton):  Share duplicated code below.  Too busy/lazy for now.
147
148static string stdout_capture_filename_;
149static string stderr_capture_filename_;
150static int original_stdout_ = -1;
151static int original_stderr_ = -1;
152
153void CaptureTestStdout() {
154  GOOGLE_CHECK_EQ(original_stdout_, -1) << "Already capturing.";
155
156  stdout_capture_filename_ = TestTempDir() + "/captured_stdout";
157
158  int fd = open(stdout_capture_filename_.c_str(),
159                O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777);
160  GOOGLE_CHECK(fd >= 0) << "open: " << strerror(errno);
161
162  original_stdout_ = dup(1);
163  close(1);
164  dup2(fd, 1);
165  close(fd);
166}
167
168void CaptureTestStderr() {
169  GOOGLE_CHECK_EQ(original_stderr_, -1) << "Already capturing.";
170
171  stderr_capture_filename_ = TestTempDir() + "/captured_stderr";
172
173  int fd = open(stderr_capture_filename_.c_str(),
174                O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777);
175  GOOGLE_CHECK(fd >= 0) << "open: " << strerror(errno);
176
177  original_stderr_ = dup(2);
178  close(2);
179  dup2(fd, 2);
180  close(fd);
181}
182
183string GetCapturedTestStdout() {
184  GOOGLE_CHECK_NE(original_stdout_, -1) << "Not capturing.";
185
186  close(1);
187  dup2(original_stdout_, 1);
188  original_stdout_ = -1;
189
190  string result;
191  File::ReadFileToStringOrDie(stdout_capture_filename_, &result);
192
193  remove(stdout_capture_filename_.c_str());
194
195  return result;
196}
197
198string GetCapturedTestStderr() {
199  GOOGLE_CHECK_NE(original_stderr_, -1) << "Not capturing.";
200
201  close(2);
202  dup2(original_stderr_, 2);
203  original_stderr_ = -1;
204
205  string result;
206  File::ReadFileToStringOrDie(stderr_capture_filename_, &result);
207
208  remove(stderr_capture_filename_.c_str());
209
210  return result;
211}
212
213ScopedMemoryLog* ScopedMemoryLog::active_log_ = NULL;
214
215ScopedMemoryLog::ScopedMemoryLog() {
216  GOOGLE_CHECK(active_log_ == NULL);
217  active_log_ = this;
218  old_handler_ = SetLogHandler(&HandleLog);
219}
220
221ScopedMemoryLog::~ScopedMemoryLog() {
222  SetLogHandler(old_handler_);
223  active_log_ = NULL;
224}
225
226const vector<string>& ScopedMemoryLog::GetMessages(LogLevel level) {
227  GOOGLE_CHECK(level == ERROR ||
228               level == WARNING);
229  return messages_[level];
230}
231
232void ScopedMemoryLog::HandleLog(LogLevel level, const char* filename,
233                                int line, const string& message) {
234  GOOGLE_CHECK(active_log_ != NULL);
235  if (level == ERROR || level == WARNING) {
236    active_log_->messages_[level].push_back(message);
237  }
238}
239
240namespace {
241
242// Force shutdown at process exit so that we can test for memory leaks.  To
243// actually check for leaks, I suggest using the heap checker included with
244// google-perftools.  Set it to "draconian" mode to ensure that every last
245// call to malloc() has a corresponding free().
246struct ForceShutdown {
247  ~ForceShutdown() {
248    ShutdownProtobufLibrary();
249  }
250} force_shutdown;
251
252}  // namespace
253
254}  // namespace protobuf
255}  // namespace google
256