Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5464)

Unified Diff: chromecast/crash/linux/synchronized_minidump_manager_unittest.cc

Issue 1154383006: Adding crash utilities to chromecast/crash. (Closed) Base URL: https://eureka-internal.googlesource.com/chromium/src@master
Patch Set: cast_shell_unittests not built for android Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chromecast/crash/linux/synchronized_minidump_manager.cc ('k') | chromecast/media/BUILD.gn » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chromecast/crash/linux/synchronized_minidump_manager_unittest.cc
diff --git a/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc b/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ea3e908b3e9979d35389209690a2f25d0849c955
--- /dev/null
+++ b/chromecast/crash/linux/synchronized_minidump_manager_unittest.cc
@@ -0,0 +1,388 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <sys/stat.h> // mkdir
+#include <sys/types.h> //
+#include <stdio.h> // perror
+#include <time.h>
+
+#include <fstream>
+
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/process/launch.h"
+#include "base/test/scoped_path_override.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "chromecast/crash/linux/dump_info.h"
+#include "chromecast/crash/linux/synchronized_minidump_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace {
+
+const char kLockfileName[] = "lockfile";
+const char kMinidumpSubdir[] = "minidumps";
+
+ScopedVector<DumpInfo> GetCurrentDumps(const std::string& logfile_path) {
+ ScopedVector<DumpInfo> dumps;
+ std::string entry;
+
+ std::ifstream in(logfile_path);
+ DCHECK(in.is_open());
+ while (std::getline(in, entry)) {
+ scoped_ptr<DumpInfo> info(new DumpInfo(entry));
+ dumps.push_back(info.Pass());
+ }
+ return dumps.Pass();
+}
+
+// A trivial implementation of SynchronizedMinidumpManager, which does no work
+// to the
+// minidump and exposes its protected members for testing.
+class SynchronizedMinidumpManagerSimple : public SynchronizedMinidumpManager {
+ public:
+ SynchronizedMinidumpManagerSimple()
+ : SynchronizedMinidumpManager(),
+ work_done_(false),
+ add_entry_return_code_(-1),
+ lockfile_path_(dump_path_.Append(kLockfileName).value()) {}
+ ~SynchronizedMinidumpManagerSimple() override {}
+
+ void SetDumpInfoToWrite(scoped_ptr<DumpInfo> dump_info) {
+ dump_info_ = dump_info.Pass();
+ }
+
+ int DoWorkLocked() { return AcquireLockAndDoWork(); }
+
+ // SynchronizedMinidumpManager implementation:
+ int DoWork() override {
+ if (dump_info_)
+ add_entry_return_code_ = AddEntryToLockFile(*dump_info_);
+ work_done_ = true;
+ return 0;
+ }
+
+ // Accessors for testing.
+ const std::string& dump_path() { return dump_path_.value(); }
+ const std::string& lockfile_path() { return lockfile_path_; }
+ bool work_done() { return work_done_; }
+ int add_entry_return_code() { return add_entry_return_code_; }
+
+ private:
+ bool work_done_;
+ int add_entry_return_code_;
+ std::string lockfile_path_;
+ scoped_ptr<DumpInfo> dump_info_;
+};
+
+void DoWorkLockedTask(SynchronizedMinidumpManagerSimple* manager) {
+ manager->DoWorkLocked();
+}
+
+class SleepySynchronizedMinidumpManagerSimple
+ : public SynchronizedMinidumpManagerSimple {
+ public:
+ SleepySynchronizedMinidumpManagerSimple(int sleep_duration_ms)
+ : SynchronizedMinidumpManagerSimple(),
+ sleep_duration_ms_(sleep_duration_ms) {}
+ ~SleepySynchronizedMinidumpManagerSimple() override {}
+
+ // SynchronizedMinidumpManager implementation:
+ int DoWork() override {
+ // The lock has been acquired. Fall asleep for |kSleepDurationMs|, then
+ // write the file.
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(sleep_duration_ms_));
+ return SynchronizedMinidumpManagerSimple::DoWork();
+ }
+
+ private:
+ const int sleep_duration_ms_;
+};
+
+class SynchronizedMinidumpManagerTest : public testing::Test {
+ public:
+ SynchronizedMinidumpManagerTest() {}
+ ~SynchronizedMinidumpManagerTest() override {}
+
+ void SetUp() override {
+ // Set up a temporary directory which will be used as our fake home dir.
+ ASSERT_TRUE(base::CreateNewTempDirectory("", &fake_home_dir_));
+ path_override_.reset(
+ new base::ScopedPathOverride(base::DIR_HOME, fake_home_dir_));
+ minidump_dir_ = fake_home_dir_.Append(kMinidumpSubdir);
+ lockfile_ = minidump_dir_.Append(kLockfileName);
+
+ // Create a minidump directory.
+ ASSERT_TRUE(base::CreateDirectory(minidump_dir_));
+ ASSERT_TRUE(base::IsDirectoryEmpty(minidump_dir_));
+
+ // Create a lockfile in that directory.
+ base::File lockfile(
+ lockfile_, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ ASSERT_TRUE(lockfile.IsValid());
+ }
+
+ void TearDown() override {
+ // Remove the temp directory.
+ path_override_.reset();
+ ASSERT_TRUE(base::DeleteFile(fake_home_dir_, true));
+ }
+
+ protected:
+ base::FilePath fake_home_dir_; // Path to the test home directory.
+ base::FilePath minidump_dir_; // Path the the minidump directory.
+ base::FilePath lockfile_; // Path to the lockfile in |minidump_dir_|.
+
+ private:
+ scoped_ptr<base::ScopedPathOverride> path_override_;
+};
+
+} // namespace
+
+TEST_F(SynchronizedMinidumpManagerTest, FilePathsAreCorrect) {
+ SynchronizedMinidumpManagerSimple manager;
+
+ // Verify file paths for directory and lock file.
+ ASSERT_EQ(minidump_dir_.value(), manager.dump_path());
+ ASSERT_EQ(lockfile_.value(), manager.lockfile_path());
+}
+
+TEST_F(SynchronizedMinidumpManagerTest, AcquireLockOnNonExistentDirectory) {
+ // The directory was created in SetUp(). Delete it and its contents.
+ ASSERT_TRUE(base::DeleteFile(minidump_dir_, true));
+ ASSERT_FALSE(base::PathExists(minidump_dir_));
+
+ SynchronizedMinidumpManagerSimple manager;
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_TRUE(manager.work_done());
+
+ // Verify the directory and the lockfile both exist.
+ ASSERT_TRUE(base::DirectoryExists(minidump_dir_));
+ ASSERT_TRUE(base::PathExists(lockfile_));
+}
+
+TEST_F(SynchronizedMinidumpManagerTest, AcquireLockOnExistingEmptyDirectory) {
+ // The lockfile was created in SetUp(). Delete it.
+ ASSERT_TRUE(base::DeleteFile(lockfile_, false));
+ ASSERT_FALSE(base::PathExists(lockfile_));
+
+ SynchronizedMinidumpManagerSimple manager;
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_TRUE(manager.work_done());
+
+ // Verify the directory and the lockfile both exist.
+ ASSERT_TRUE(base::DirectoryExists(minidump_dir_));
+ ASSERT_TRUE(base::PathExists(lockfile_));
+}
+
+TEST_F(SynchronizedMinidumpManagerTest,
+ AcquireLockOnExistingDirectoryWithLockfile) {
+ SynchronizedMinidumpManagerSimple manager;
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_TRUE(manager.work_done());
+
+ // Verify the directory and the lockfile both exist.
+ ASSERT_TRUE(base::DirectoryExists(minidump_dir_));
+ ASSERT_TRUE(base::PathExists(lockfile_));
+}
+
+TEST_F(SynchronizedMinidumpManagerTest,
+ AddEntryToLockFile_FailsWithInvalidEntry) {
+ // Test that the manager tried to log the entry and failed.
+ SynchronizedMinidumpManagerSimple manager;
+ manager.SetDumpInfoToWrite(make_scoped_ptr(new DumpInfo("")));
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_EQ(-1, manager.add_entry_return_code());
+
+ // Verify the lockfile is untouched.
+ ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
+ ASSERT_EQ(0u, dumps.size());
+}
+
+TEST_F(SynchronizedMinidumpManagerTest,
+ AddEntryToLockFile_SucceedsWithValidEntries) {
+ // Sample parameters.
+ time_t now = time(0);
+ MinidumpParams params;
+ params.process_name = "process";
+
+ // Write the first entry.
+ SynchronizedMinidumpManagerSimple manager;
+ manager.SetDumpInfoToWrite(
+ make_scoped_ptr(new DumpInfo("dump1", "log1", now, params)));
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_EQ(0, manager.add_entry_return_code());
+
+ // Test that the manager was successful in logging the entry.
+ ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
+ ASSERT_EQ(1u, dumps.size());
+
+ // Write the second entry.
+ manager.SetDumpInfoToWrite(
+ make_scoped_ptr(new DumpInfo("dump2", "log2", now, params)));
+ ASSERT_EQ(0, manager.DoWorkLocked());
+ ASSERT_EQ(0, manager.add_entry_return_code());
+
+ // Test that the second entry is also valid.
+ dumps = GetCurrentDumps(lockfile_.value());
+ ASSERT_EQ(2u, dumps.size());
+
+ // TODO(slan): Weird time incosistencies making this fail.
+ // ASSERT_EQ(dumps[0]->entry(), DumpInfo("dump", "log", now, params).entry());
+}
+
+TEST_F(SynchronizedMinidumpManagerTest,
+ AcquireLockFile_FailsWhenNonBlockingAndFileLocked) {
+ // Lock the lockfile here. Note that the Chromium base::File tools permit
+ // multiple locks on the same process to succeed, so we must use POSIX system
+ // calls to accomplish this.
+ int fd = open(lockfile_.value().c_str(), O_RDWR | O_CREAT, 0660);
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(0, flock(fd, LOCK_EX));
+
+ SynchronizedMinidumpManagerSimple manager;
+ manager.set_non_blocking(true);
+ ASSERT_EQ(-1, manager.DoWorkLocked());
+ ASSERT_FALSE(manager.work_done());
+
+ // Test that the manager was not able to log the crash dump.
+ ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
+ ASSERT_EQ(0u, dumps.size());
+}
+
+TEST_F(SynchronizedMinidumpManagerTest,
+ AcquireLockFile_WaitsForOtherThreadWhenBlocking) {
+ // Create some parameters for a minidump.
+ time_t now = time(0);
+ MinidumpParams params;
+ params.process_name = "process";
+
+ // Create a manager that grabs the lock then sleeps. Post a DoWork task to
+ // another thread. |sleepy_manager| will grab the lock and hold it for
+ // |sleep_time_ms|. It will then write a dump and release the lock.
+ const int sleep_time_ms = 100;
+ SleepySynchronizedMinidumpManagerSimple sleepy_manager(sleep_time_ms);
+ sleepy_manager.SetDumpInfoToWrite(
+ make_scoped_ptr(new DumpInfo("dump", "log", now, params)));
+ base::Thread sleepy_thread("sleepy");
+ sleepy_thread.Start();
+ sleepy_thread.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&DoWorkLockedTask, base::Unretained(&sleepy_manager)));
+
+ // Meanwhile, this thread should wait brielfy to allow the other thread to
+ // grab the lock.
+ const int concurrency_delay = 50;
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(concurrency_delay));
+
+ // |sleepy_manager| has the lock by now, but has not released it. Attempt to
+ // grab it. DoWorkLocked() should block until |manager| has a chance to write
+ // the dump.
+ SynchronizedMinidumpManagerSimple manager;
+ manager.SetDumpInfoToWrite(
+ make_scoped_ptr(new DumpInfo("dump", "log", now, params)));
+ manager.set_non_blocking(false);
+
+ EXPECT_EQ(0, manager.DoWorkLocked());
+ EXPECT_EQ(0, manager.add_entry_return_code());
+ EXPECT_TRUE(manager.work_done());
+
+ // Check that the other manager was also successful.
+ EXPECT_EQ(0, sleepy_manager.add_entry_return_code());
+ EXPECT_TRUE(sleepy_manager.work_done());
+
+ // Test that both entries were logged.
+ ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
+ EXPECT_EQ(2u, dumps.size());
+}
+
+// TODO(slan): These tests are passing but forking them is creating duplicates
+// of all tests in this thread. Figure out how to lock the file more cleanly
+// from another process.
+TEST_F(SynchronizedMinidumpManagerTest,
+ DISABLED_AcquireLockFile_FailsWhenNonBlockingAndLockedFromOtherProcess) {
+ // Fork the process.
+ pid_t pid = base::ForkWithFlags(0u, nullptr, nullptr);
+ if (pid != 0) {
+ // The child process should instantiate a manager which immediately grabs
+ // the lock, and falls aleep for some period of time, then writes a dump,
+ // and finally releases the lock.
+ SleepySynchronizedMinidumpManagerSimple sleepy_manager(100);
+ ASSERT_EQ(0, sleepy_manager.DoWorkLocked());
+ ASSERT_TRUE(sleepy_manager.work_done());
+ return;
+ }
+
+ // Meanwhile, this process should wait brielfy to allow the other thread to
+ // grab the lock.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
+
+ SynchronizedMinidumpManagerSimple manager;
+ manager.set_non_blocking(true);
+ ASSERT_EQ(-1, manager.DoWorkLocked());
+ ASSERT_FALSE(manager.work_done());
+
+ // Test that the manager was not able to log the crash dump.
+ ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
+ ASSERT_EQ(0u, dumps.size());
+}
+
+// TODO(slan): These tests are passing but forking them is creating duplicates
+// of all tests in this thread. Figure out how to lock the file more cleanly
+// from another process.
+TEST_F(SynchronizedMinidumpManagerTest,
+ DISABLED_AcquireLockFile_WaitsForOtherProcessWhenBlocking) {
+ // Create some parameters for a minidump.
+ time_t now = time(0);
+ MinidumpParams params;
+ params.process_name = "process";
+
+ // Fork the process.
+ pid_t pid = base::ForkWithFlags(0u, nullptr, nullptr);
+ if (pid != 0) {
+ // The child process should instantiate a manager which immediately grabs
+ // the lock, and falls aleep for some period of time, then writes a dump,
+ // and finally releases the lock.
+ SleepySynchronizedMinidumpManagerSimple sleepy_manager(100);
+ sleepy_manager.SetDumpInfoToWrite(
+ make_scoped_ptr(new DumpInfo("dump", "log", now, params)));
+ ASSERT_EQ(0, sleepy_manager.DoWorkLocked());
+ ASSERT_TRUE(sleepy_manager.work_done());
+ return;
+ }
+
+ // Meanwhile, this process should wait brielfy to allow the other thread to
+ // grab the lock.
+ const int concurrency_delay = 50;
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(concurrency_delay));
+
+ // |sleepy_manager| has the lock by now, but has not released it. Attempt to
+ // grab it. DoWorkLocked() should block until |manager| has a chance to write
+ // the dump.
+ SynchronizedMinidumpManagerSimple manager;
+ manager.SetDumpInfoToWrite(
+ make_scoped_ptr(new DumpInfo("dump", "log", now, params)));
+ manager.set_non_blocking(false);
+
+ EXPECT_EQ(0, manager.DoWorkLocked());
+ EXPECT_EQ(0, manager.add_entry_return_code());
+ EXPECT_TRUE(manager.work_done());
+
+ // Test that both entries were logged.
+ ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
+ EXPECT_EQ(2u, dumps.size());
+}
+
+} // namespace chromecast
« no previous file with comments | « chromecast/crash/linux/synchronized_minidump_manager.cc ('k') | chromecast/media/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698