Index: log_unit_tests.cc |
diff --git a/log_unit_tests.cc b/log_unit_tests.cc |
index b12718a20fad934aa44b3e99f67d9aad510bf8da..9c5c54f430a88e8ddbb30275d1d2be1f5daa8fcb 100644 |
--- a/log_unit_tests.cc |
+++ b/log_unit_tests.cc |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
@@ -6,6 +6,7 @@ |
#include "bootstat_test.h" |
#include <errno.h> |
+#include <stdio.h> |
#include <stdlib.h> |
#include <sys/fcntl.h> |
#include <sys/stat.h> |
@@ -29,15 +30,14 @@ static void RemoveFile(const string& file_path) { |
// Class to track and test the data associated with a single event. |
-// The primary function is TestLogEvent(): This method wraps calls |
-// to bootstat_log() with code to track the expected contents of the |
-// event files. After logging, the expected content is tested |
-// against the actual content. |
+// The methods TestEventContent() and TestEventCreation() wrap calls |
+// to bootstat_log() to test various assertions regarding the |
+// function. |
class EventTracker { |
public: |
- EventTracker(const string& name, const string& uptime_prefix, |
- const string& disk_prefix); |
- void TestLogEvent(const string& uptime, const string& diskstats); |
+ EventTracker(const string& name, const string& output_dir); |
+ void TestEventContent(const char* uptime, const char* diskstats); |
+ void TestEventCreation(bool expect_success); |
void Reset(); |
private: |
@@ -50,28 +50,27 @@ class EventTracker { |
EventTracker::EventTracker(const string& name, |
- const string& uptime_prefix, |
- const string& diskstats_prefix) |
+ const string& output_dir) |
: event_name_(name), |
uptime_content_(""), |
diskstats_content_("") { |
string truncated_name = |
event_name_.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1); |
- uptime_file_name_ = uptime_prefix + truncated_name; |
- diskstats_file_name_ = diskstats_prefix + truncated_name; |
+ uptime_file_name_ = output_dir + "/uptime-" + truncated_name; |
+ diskstats_file_name_ = output_dir + "/disk-" + truncated_name; |
} |
// Basic helper function to test whether the contents of the |
// specified file exactly match the given contents string. |
-static void ValidateEventFileContents(const string& file_name, |
- const string& file_contents) { |
+static void ValidateEventFileContent(const string& file_name, |
+ const string& file_contents) { |
int rv = access(file_name.c_str(), W_OK); |
EXPECT_EQ(0, rv) << file_name << " is not writable: " |
- << strerror(errno); |
+ << strerror(errno) << "."; |
rv = access(file_name.c_str(), R_OK); |
ASSERT_EQ(0, rv) << file_name << " is not readable: " |
- << strerror(errno); |
+ << strerror(errno) << "."; |
char *buffer = new char[file_contents.length() + 1]; |
int fd = open(file_name.c_str(), O_RDONLY); |
rv = read(fd, buffer, file_contents.length()); |
@@ -89,18 +88,47 @@ static void ValidateEventFileContents(const string& file_name, |
// Call bootstat_log() once, and update the expected content for |
// this event. Test that the new content of the event's files |
// matches the updated expected content. |
-void EventTracker::TestLogEvent(const string& uptime, |
- const string& diskstats) { |
+void EventTracker::TestEventContent(const char* uptime, |
+ const char* diskstats) { |
bootstat_log(event_name_.c_str()); |
- uptime_content_ += uptime; |
- diskstats_content_ += diskstats; |
- ValidateEventFileContents(uptime_file_name_, uptime_content_); |
- ValidateEventFileContents(diskstats_file_name_, diskstats_content_); |
+ uptime_content_ += string(uptime); |
+ diskstats_content_ += string(diskstats); |
+ ValidateEventFileContent(uptime_file_name_, uptime_content_); |
+ ValidateEventFileContent(diskstats_file_name_, diskstats_content_); |
} |
-// Reset event state back to initial conditions, by deleting the |
-// associated event files, and clearing the expected contents. |
+// Basic helper function to test whether the contents of the |
+// specified file exactly match the given contents string. |
+// A return value of true indicates that the file contents can |
+// be read. |
+static void ValidateEventFileExistence(const string& file_name, |
+ bool expect_success) { |
+ int rv = access(file_name.c_str(), F_OK); |
+ if (expect_success) { |
+ EXPECT_EQ(0, rv) << "Cannot verify existence of " << file_name |
+ << ": " << strerror(errno) << "."; |
+ } else { |
+ EXPECT_LT(rv, 0) << file_name << " exists, but it shouldn't."; |
+ } |
+ |
+ if (rv >= 0) { |
+ RemoveFile(file_name); |
+ } |
+} |
+ |
+ |
+// Call bootstat_log() once, and confirm creation of the event |
+// files. The content of the files isn't checked. |
+void EventTracker::TestEventCreation(bool expect_success) { |
+ bootstat_log(event_name_.c_str()); |
+ ValidateEventFileExistence(uptime_file_name_, expect_success); |
+ ValidateEventFileExistence(diskstats_file_name_, expect_success); |
+} |
+ |
+ |
+// Reset event state back to initial conditions by deleting the |
+// associated event files and clearing the expected contents. |
void EventTracker::Reset() { |
uptime_content_.clear(); |
diskstats_content_.clear(); |
@@ -122,59 +150,60 @@ class BootstatTest : public ::testing::Test { |
virtual void SetUp(); |
virtual void TearDown(); |
+ EventTracker MakeEvent(const string& event_name, |
+ const string& output_dir_name) { |
+ return EventTracker(event_name, output_dir_name); |
+ } |
EventTracker MakeEvent(const string& event_name) { |
- return EventTracker(event_name, uptime_event_prefix_, |
- disk_event_prefix_); |
+ return EventTracker(event_name, stats_output_dir_); |
} |
void SetStatsContent(const char* uptime_content, |
const char* disk_content); |
- void TestLogEvent(EventTracker& event); |
+ void ClearStatsContent(); |
+ void TestSetOutputDirectory(const string& testdir, |
+ const string& testevent, |
+ bool expect_success, |
+ int expected_errno); |
+ void TestSetOutputDirectoryAccess(const string& testdir, mode_t testmode); |
+ void TestLogAccess(const string& testdir, mode_t testmode); |
private: |
string stats_output_dir_; |
- string uptime_event_prefix_; |
- string disk_event_prefix_; |
string uptime_stats_file_name_; |
- string uptime_stats_content_; |
string disk_stats_file_name_; |
- string disk_stats_content_; |
}; |
void BootstatTest::SetUp() { |
char dir_template[] = "bootstat_test_XXXXXX"; |
stats_output_dir_ = string(mkdtemp(dir_template)); |
- uptime_event_prefix_ = stats_output_dir_ + "/uptime-"; |
- disk_event_prefix_ = stats_output_dir_ + "/disk-"; |
uptime_stats_file_name_ = stats_output_dir_ + "/proc_uptime"; |
disk_stats_file_name_ = stats_output_dir_ + "/block_stats"; |
- bootstat_set_output_directory(stats_output_dir_.c_str()); |
- bootstat_set_uptime_file_name(uptime_stats_file_name_.c_str()); |
- bootstat_set_disk_file_name(disk_stats_file_name_.c_str()); |
+ EXPECT_EQ(0, bootstat_set_output_directory(stats_output_dir_.c_str())) |
+ << "Failed to select output directory " << stats_output_dir_ |
+ << ": " << strerror(errno) << "."; |
} |
void BootstatTest::TearDown() { |
- bootstat_set_uptime_file_name(NULL); |
- bootstat_set_disk_file_name(NULL); |
- bootstat_set_output_directory(NULL); |
- RemoveFile(uptime_stats_file_name_); |
- RemoveFile(disk_stats_file_name_); |
+ EXPECT_EQ(0, bootstat_set_output_directory(NULL)) |
+ << "Failed to set output directory back to default: " |
+ << strerror(errno) << "."; |
EXPECT_EQ(0, rmdir(stats_output_dir_.c_str())) |
<< "Can't remove directory " << stats_output_dir_ |
<< ": " << strerror(errno) << "."; |
} |
-static void WriteStatsContent(const string& content, const string& file_path) { |
+static void WriteStatsContent(const char *content, const string& file_path) { |
const int kFileOpenFlags = O_WRONLY | O_TRUNC | O_CREAT; |
const mode_t kFileCreationMode = |
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; |
int fd = open(file_path.c_str(), kFileOpenFlags, kFileCreationMode); |
- int nwrite = write(fd, content.c_str(), content.length()); |
- EXPECT_EQ(content.length(), nwrite) |
+ int nwrite = write(fd, content, strlen(content)); |
+ EXPECT_EQ(strlen(content), nwrite) |
<< "write to stats file " << file_path |
<< " failed: " << strerror(errno); |
(void)close(fd); |
@@ -187,15 +216,18 @@ static void WriteStatsContent(const string& content, const string& file_path) { |
// events. |
void BootstatTest::SetStatsContent(const char* uptime_stats, |
const char* disk_stats) { |
- uptime_stats_content_ = string(uptime_stats); |
- WriteStatsContent(uptime_stats_content_, uptime_stats_file_name_); |
- disk_stats_content_ = string(disk_stats); |
- WriteStatsContent(disk_stats_content_, disk_stats_file_name_); |
+ WriteStatsContent(uptime_stats, uptime_stats_file_name_); |
+ WriteStatsContent(disk_stats, disk_stats_file_name_); |
+ bootstat_set_uptime_file_name(uptime_stats_file_name_.c_str()); |
+ bootstat_set_disk_file_name(disk_stats_file_name_.c_str()); |
} |
-void BootstatTest::TestLogEvent(EventTracker& event) { |
- event.TestLogEvent(uptime_stats_content_, disk_stats_content_); |
+void BootstatTest::ClearStatsContent() { |
+ bootstat_set_uptime_file_name(NULL); |
+ bootstat_set_disk_file_name(NULL); |
+ RemoveFile(uptime_stats_file_name_); |
+ RemoveFile(disk_stats_file_name_); |
} |
@@ -221,21 +253,23 @@ static const char* bootstat_data[] = { |
}; |
-// Tests that event file content matches expectations when an |
-// event is logged multiple times. |
+// This is to test that event file content matches expectations when |
+// an event is logged multiple times. |
TEST_F(BootstatTest, ContentGeneration) { |
EventTracker ev = MakeEvent(string("test_event")); |
int i = 0; |
while (bootstat_data[i] != NULL) { |
SetStatsContent(bootstat_data[i], bootstat_data[i+1]); |
- TestLogEvent(ev); |
+ ev.TestEventContent(bootstat_data[i], bootstat_data[i+1]); |
i += 2; |
} |
+ ClearStatsContent(); |
ev.Reset(); |
} |
-// Tests that name truncation of logged events works as advertised. |
+// This is to test that name truncation of logged events works as |
+// advertised. |
TEST_F(BootstatTest, EventNameTruncation) { |
static const char kMostVoluminousEventName[] = |
// 16 32 48 64 |
@@ -249,19 +283,163 @@ TEST_F(BootstatTest, EventNameTruncation) { |
SetStatsContent(bootstat_data[0], bootstat_data[1]); |
EventTracker ev = MakeEvent(very_long); |
- TestLogEvent(ev); |
+ ev.TestEventContent(bootstat_data[0], bootstat_data[1]); |
ev.Reset(); |
ev = MakeEvent(very_long.substr(0, 1)); |
- TestLogEvent(ev); |
+ ev.TestEventContent(bootstat_data[0], bootstat_data[1]); |
ev.Reset(); |
ev = MakeEvent(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1)); |
- TestLogEvent(ev); |
+ ev.TestEventContent(bootstat_data[0], bootstat_data[1]); |
ev.Reset(); |
ev = MakeEvent(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN)); |
- TestLogEvent(ev); |
+ ev.TestEventContent(bootstat_data[0], bootstat_data[1]); |
ev.Reset(); |
+ |
+ ClearStatsContent(); |
+} |
+ |
+ |
+// Test a call to bootstat_set_output_directory(). Check that the |
+// operation succeeds or fails as indicated by the input parameters. |
+// Also tests that bootstat_log() logs in the expected place after |
+// the call: after success, logging should go to the new directory; |
+// after a failure, logging should continue to go to the previous |
+// directory. |
+void BootstatTest::TestSetOutputDirectory(const string& testdir, |
+ const string& testevent, |
+ bool expect_success, |
+ int expected_errno) { |
+ ASSERT_EQ(0, bootstat_set_output_directory(stats_output_dir_.c_str())) |
+ << "bootstat_set_output_directory() failed for " << stats_output_dir_ |
+ << ": " << strerror(errno) << "."; |
+ bool set_output_success = |
+ (bootstat_set_output_directory(testdir.c_str()) == 0); |
+ |
+ if (expect_success) { |
+ ASSERT_TRUE(set_output_success) |
+ << "bootstat_set_output_directory() failed unexpectedly for " |
+ << testdir << ": " << strerror(errno) << "."; |
+ } else { |
+ ASSERT_FALSE(set_output_success) |
+ << "bootstat_set_output_directory() succeeded for " |
+ << testdir << "; expected error: " << strerror(expected_errno) << "."; |
+ EXPECT_EQ(expected_errno, errno); |
+ } |
+ |
+ string eventdir; |
+ if (set_output_success) { |
+ eventdir = testdir; |
+ } else { |
+ eventdir = stats_output_dir_; |
+ } |
+ string eventname = string("test-set-directory-") + testevent; |
+ EventTracker ev = MakeEvent(eventname, eventdir); |
+ ev.TestEventCreation(true); |
+ ASSERT_EQ(0, bootstat_set_output_directory(NULL)) |
+ << "bootstat_set_output_directory(NULL) failed: " |
+ << strerror(errno) << "."; |
+} |
+ |
+ |
+// Test that bootstat_set_output_directory() will return ENOENT |
+// when required. |
+TEST_F(BootstatTest, SetOutputDirectoryEnoent) { |
+ TestSetOutputDirectory(string("/this-directory-does-not-exist"), |
+ string("ENOENT"), false, ENOENT); |
} |
+ |
+// Test that bootstat_set_output_directory() will return ENOTDIR |
+// when required. |
+TEST_F(BootstatTest, SetOutputDirectoryEnotdir) { |
+ string ordinary_file_name = string("ordinary-file"); |
+ const mode_t kFileCreationMode = |
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; |
+ int fd = creat(ordinary_file_name.c_str(), kFileCreationMode); |
+ EXPECT_GE(fd, 0) |
+ << "failed to create test file " << ordinary_file_name |
+ << ": " << strerror(errno) << "."; |
+ if (fd >= 0) { |
+ TestSetOutputDirectory(ordinary_file_name, string("ENOTDIR"), |
+ false, ENOTDIR); |
+ (void) close(fd); |
+ RemoveFile(ordinary_file_name); |
+ } |
+} |
+ |
+ |
+// Test that bootstat_set_output_directory() succeeds or fails with |
+// EACCES as expected. |
+void BootstatTest::TestSetOutputDirectoryAccess(const string& testdir, |
+ mode_t testmode) { |
+ char buffer[8]; |
+ sprintf(buffer, "0%03o", testmode); |
+ string modestring = string(buffer); |
+ bool expect_success = |
+ ((testmode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)); |
+ |
+ ASSERT_EQ(0, chmod(testdir.c_str(), testmode)) |
+ << "Unable to change directory mode for " << testdir |
+ << " to " << modestring << ": " << strerror(errno) << "."; |
+ string eventname = string("EACCES-") + modestring; |
+ TestSetOutputDirectory(testdir, eventname, expect_success, EACCES); |
+} |
+ |
+ |
+// Test that the actual access mode requirements enforced by |
+// bootstat_log() match the requirements enforced by |
+// bootstat_set_output_directory(). |
+void BootstatTest::TestLogAccess(const string& testdir, |
+ mode_t testmode) { |
+ char buffer[8]; |
+ sprintf(buffer, "0%03o", testmode); |
+ string modestring = string(buffer); |
+ bool expect_success = |
+ ((testmode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)); |
+ |
+ ASSERT_EQ(0, chmod(testdir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR)) |
+ << "Unable to change directory mode for " << testdir |
+ << " to 0700: " << strerror(errno) << "."; |
+ ASSERT_EQ(0, bootstat_set_output_directory(testdir.c_str())) |
+ << "bootstat_set_output_directory() failed for " << testdir |
+ << ": " << strerror(errno) << "."; |
+ ASSERT_EQ(0, chmod(testdir.c_str(), testmode)) |
+ << "Unable to change directory mode for " << testdir |
+ << " to " << modestring << ": " << strerror(errno) << "."; |
+ |
+ string event_name = string("test-log-access-") + modestring; |
+ EventTracker ev = MakeEvent(event_name, testdir); |
+ ev.TestEventCreation(expect_success); |
+ |
+ ASSERT_EQ(0, bootstat_set_output_directory(NULL)) |
+ << "bootstat_set_output_directory(NULL) failed: " |
+ << strerror(errno) << "."; |
+} |
+ |
+ |
+// Test that bootstat_set_output_directory() will return EACCES |
+// as expected for all directory modes. The test here is |
+// two-pronged. First, the test confirms that EACCES is |
+// returned exactly when the caller has both W and X access. |
+// Second, the test confirms that bootstat_log() fails when |
+// EACCES is returned, but not when it isn't. |
+TEST_F(BootstatTest, SetOutputDirectoryEacces) { |
+ char dir_template[] = "bootstat_access_test_XXXXXX"; |
+ string testdir = string(mkdtemp(dir_template)); |
+ mode_t lobit = ((S_IRUSR - 1) & (S_IWUSR - 1) & (S_IXUSR - 1)) + 1; |
+ mode_t allbits = (S_IRUSR | S_IWUSR | S_IXUSR); |
+ mode_t dirmode = allbits + lobit; |
+ while (dirmode > 0) { |
+ dirmode -= lobit; |
+ TestSetOutputDirectoryAccess(testdir, dirmode); |
+ TestLogAccess(testdir, dirmode); |
+ } |
+ EXPECT_EQ(0, rmdir(testdir.c_str())) |
+ << "Can't remove directory " << testdir |
+ << ": " << strerror(errno) << "."; |
+} |
+ |
+ |
} // namespace |
int main(int argc, char** argv) { |