diff --git a/util/mac/xattr_test.cc b/util/mac/xattr_test.cc index e6bbf7b1..c02aa08c 100644 --- a/util/mac/xattr_test.cc +++ b/util/mac/xattr_test.cc @@ -25,20 +25,18 @@ #include "base/strings/stringprintf.h" #include "gtest/gtest.h" #include "util/test/errors.h" +#include "util/test/scoped_temp_dir.h" namespace crashpad { namespace test { namespace { class Xattr : public testing::Test { - public: + protected: // testing::Test: void SetUp() override { - path_ = base::FilePath( - base::StringPrintf("/tmp/com.googlecode.crashpad.test.xattr.%d.%p", - getpid(), this)); - // TODO(rsesek): This should use something like ScopedTempDir. + path_ = temp_dir_.path().Append("xattr_file"); base::ScopedFD tmp(HANDLE_EINTR( open(path_.value().c_str(), O_CREAT | O_TRUNC, 0644))); EXPECT_GE(tmp.get(), 0) << ErrnoMessage("open"); @@ -48,9 +46,10 @@ class Xattr : public testing::Test { EXPECT_EQ(0, unlink(path_.value().c_str())) << ErrnoMessage("unlink"); } - base::FilePath path() { return path_; } + const base::FilePath& path() const { return path_; } private: + ScopedTempDir temp_dir_; base::FilePath path_; }; diff --git a/util/test/scoped_temp_dir.cc b/util/test/scoped_temp_dir.cc new file mode 100644 index 00000000..a2f8d251 --- /dev/null +++ b/util/test/scoped_temp_dir.cc @@ -0,0 +1,28 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/test/scoped_temp_dir.h" + +namespace crashpad { +namespace test { + +ScopedTempDir::ScopedTempDir() : path_(CreateTemporaryDirectory()) { +} + +ScopedTempDir::~ScopedTempDir() { + RecursivelyDeleteTemporaryDirectory(path()); +} + +} // namespace test +} // namespace crashpad diff --git a/util/test/scoped_temp_dir.h b/util/test/scoped_temp_dir.h new file mode 100644 index 00000000..0768d8d1 --- /dev/null +++ b/util/test/scoped_temp_dir.h @@ -0,0 +1,61 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_UTIL_TEST_SCOPED_TEMP_DIR_ +#define CRASHPAD_UTIL_TEST_SCOPED_TEMP_DIR_ + +#include "base/basictypes.h" +#include "base/files/file_path.h" + +namespace crashpad { +namespace test { + +//! \brief A RAII object that creates a temporary directory for testing. +//! +//! Upon construction, a temporary directory will be created. Failure to create +//! the directory is fatal. On destruction, the directory and all its contents +//! will be removed. +class ScopedTempDir { + public: + ScopedTempDir(); + ~ScopedTempDir(); + + //! \brief Returns the path of the temporary directory. + //! + //! \return The temporary directory path. + const base::FilePath& path() const { return path_; } + + private: + //! \brief Creates the temporary directory and asserts success of the + //! operation. + static base::FilePath CreateTemporaryDirectory(); + + //! \brief Removes all files and subdirectories at the given \a path, + //! including the \a path itself. + //! + //! Failures are recorded by gtest expectations. + //! + //! \param[in] path The path to delete, along with its contents. This must + //! reference a directory. + static void RecursivelyDeleteTemporaryDirectory(const base::FilePath& path); + + const base::FilePath path_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTempDir); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_UTIL_TEST_SCOPED_TEMP_DIR_ diff --git a/util/test/scoped_temp_dir_posix.cc b/util/test/scoped_temp_dir_posix.cc new file mode 100644 index 00000000..c8bb2a77 --- /dev/null +++ b/util/test/scoped_temp_dir_posix.cc @@ -0,0 +1,63 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/test/scoped_temp_dir.h" + +#include +#include +#include + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "util/test/errors.h" + +namespace crashpad { +namespace test { + +// static +base::FilePath ScopedTempDir::CreateTemporaryDirectory() { + char dir_tempalate[] = "/tmp/com.googlecode.crashpad.test.XXXXXX"; + PCHECK(mkdtemp(dir_tempalate)) << "mkdtemp " << dir_tempalate; + return base::FilePath(dir_tempalate); +} + +// static +void ScopedTempDir::RecursivelyDeleteTemporaryDirectory( + const base::FilePath& path) { + DIR* dir = opendir(path.value().c_str()); + ASSERT_TRUE(dir) << ErrnoMessage("opendir") << " " << path.value(); + + dirent* entry; + while ((entry = readdir(dir))) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + base::FilePath entry_path = path.Append(entry->d_name); + if (entry->d_type == DT_DIR) { + RecursivelyDeleteTemporaryDirectory(entry_path); + } else { + EXPECT_EQ(0, unlink(entry_path.value().c_str())) + << ErrnoMessage("unlink") << " " << entry_path.value(); + } + } + + EXPECT_EQ(0, closedir(dir)) + << ErrnoMessage("closedir") << " " << path.value(); + EXPECT_EQ(0, rmdir(path.value().c_str())) + << ErrnoMessage("rmdir") << " " << path.value(); +} + +} // namespace test +} // namespace crashpad diff --git a/util/test/scoped_temp_dir_test.cc b/util/test/scoped_temp_dir_test.cc new file mode 100644 index 00000000..1e80a828 --- /dev/null +++ b/util/test/scoped_temp_dir_test.cc @@ -0,0 +1,123 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/test/scoped_temp_dir.h" + +#include +#include +#include +#include +#include + +#include "base/posix/eintr_wrapper.h" +#include "gtest/gtest.h" +#include "util/test/errors.h" + +namespace crashpad { +namespace test { +namespace { + +bool FileExists(const base::FilePath& path) { +#if defined(OS_POSIX) + struct stat st; + int rv = lstat(path.value().c_str(), &st); + if (rv < 0) { + EXPECT_EQ(ENOENT, errno) << ErrnoMessage("lstat") << " " << path.value(); + return false; + } + return true; +#else +#error "Not implemented" +#endif +} + +void CreateFile(const base::FilePath& path) { +#if defined(OS_POSIX) + int fd = HANDLE_EINTR(creat(path.value().c_str(), 0644)); + ASSERT_GE(fd, 0) << ErrnoMessage("creat") << " " << path.value(); + ASSERT_EQ(0, IGNORE_EINTR(close(fd))) + << ErrnoMessage("close") << " " << path.value(); +#else +#error "Not implemented" +#endif + EXPECT_TRUE(FileExists(path)); +} + +void CreateDirectory(const base::FilePath& path) { +#if defined(OS_POSIX) + ASSERT_EQ(0, mkdir(path.value().c_str(), 0755)) + << ErrnoMessage("mkdir") << " " << path.value(); +#else +#error "Not implemented" +#endif + ASSERT_TRUE(FileExists(path)); +} + +TEST(ScopedTempDir, Empty) { + base::FilePath path; + { + ScopedTempDir dir; + path = dir.path(); + EXPECT_TRUE(FileExists(path)); + } + EXPECT_FALSE(FileExists(path)); +} + +TEST(ScopedTempDir, WithTwoFiles) { + base::FilePath parent, file1, file2; + + { + ScopedTempDir dir; + parent = dir.path(); + ASSERT_TRUE(FileExists(parent)); + + file1 = parent.Append("test1"); + CreateFile(file1); + + file2 = parent.Append("test 2"); + CreateFile(file2); + } + + EXPECT_FALSE(FileExists(file1)); + EXPECT_FALSE(FileExists(file2)); + EXPECT_FALSE(FileExists(parent)); +} + +TEST(ScopedTempDir, WithRecursiveDirectory) { + base::FilePath parent, file1, child_dir, file2; + + { + ScopedTempDir dir; + parent = dir.path(); + ASSERT_TRUE(FileExists(parent)); + + file1 = parent.Append(".first-level file"); + CreateFile(file1); + + child_dir = parent.Append("subdir"); + CreateDirectory(child_dir); + + file2 = child_dir.Append("second level file"); + CreateFile(file2); + } + + EXPECT_FALSE(FileExists(file1)); + EXPECT_FALSE(FileExists(file2)); + EXPECT_FALSE(FileExists(child_dir)); + EXPECT_FALSE(FileExists(parent)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/util.gyp b/util/util.gyp index 14b3cd2f..2bdb9cbe 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -214,6 +214,9 @@ 'test/multiprocess.h', 'test/multiprocess_exec.cc', 'test/multiprocess_exec.h', + 'test/scoped_temp_dir.cc', + 'test/scoped_temp_dir.h', + 'test/scoped_temp_dir_posix.cc', ], 'conditions': [ ['OS=="mac"', { @@ -285,6 +288,7 @@ 'test/mac/mach_multiprocess_test.cc', 'test/multiprocess_exec_test.cc', 'test/multiprocess_test.cc', + 'test/scoped_temp_dir_test.cc', ], 'conditions': [ ['OS=="mac"', {