| 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) {
|
|
|