Index: log_unit_tests.cc |
diff --git a/log_unit_tests.cc b/log_unit_tests.cc |
index 2d279eef679bd45cde569aea19b20bd30aa6dc7b..b12718a20fad934aa44b3e99f67d9aad510bf8da 100644 |
--- a/log_unit_tests.cc |
+++ b/log_unit_tests.cc |
@@ -3,8 +3,13 @@ |
// found in the LICENSE file. |
#include "bootstat.h" |
+#include "bootstat_test.h" |
#include <errno.h> |
+#include <stdlib.h> |
+#include <sys/fcntl.h> |
+#include <sys/stat.h> |
+#include <sys/types.h> |
#include <unistd.h> |
#include <string> |
@@ -16,40 +21,245 @@ namespace { |
using std::string; |
-static const char kMostVoluminousEventName[] = |
- // 16 32 48 64 |
- "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 64 |
- "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 128 |
- "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 191 |
- "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 256 |
- ; |
+static void RemoveFile(const string& file_path) { |
+ EXPECT_EQ(0, unlink(file_path.c_str())) |
+ << "can't unlink file " << file_path << ": " |
+ << strerror(errno); |
+} |
+ |
+ |
+// 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. |
+class EventTracker { |
+ public: |
+ EventTracker(const string& name, const string& uptime_prefix, |
+ const string& disk_prefix); |
+ void TestLogEvent(const string& uptime, const string& diskstats); |
+ void Reset(); |
+ |
+ private: |
+ string event_name_; |
+ string uptime_file_name_; |
+ string uptime_content_; |
+ string diskstats_file_name_; |
+ string diskstats_content_; |
+}; |
+ |
+ |
+EventTracker::EventTracker(const string& name, |
+ const string& uptime_prefix, |
+ const string& diskstats_prefix) |
+ : 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; |
+} |
+ |
+ |
+// 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) { |
+ int rv = access(file_name.c_str(), W_OK); |
+ EXPECT_EQ(0, rv) << file_name << " is not writable: " |
+ << strerror(errno); |
+ rv = access(file_name.c_str(), R_OK); |
+ ASSERT_EQ(0, rv) << file_name << " is not readable: " |
+ << 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()); |
+ EXPECT_EQ(file_contents.length(), rv); |
+ buffer[file_contents.length()] = '\0'; |
+ string actual_contents(buffer); |
+ EXPECT_EQ(file_contents, actual_contents); |
+ rv = read(fd, buffer, 1); |
+ EXPECT_EQ(0, rv) << "found data in event file past expected EOF"; |
+ (void)close(fd); |
+ delete buffer; |
+} |
-static const string kUptimeFileNamePrefix("/tmp/uptime-"); |
-static const string kDiskStatFileNamePrefix("/tmp/disk-"); |
-static void TestEventFileAccess(const string& file_name) { |
- int rv = access(file_name.c_str(), R_OK | W_OK); |
- EXPECT_EQ(rv, 0) << "access to " << file_name |
- << " failed: " << strerror(errno); |
- (void) unlink(file_name.c_str()); |
+// 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) { |
+ bootstat_log(event_name_.c_str()); |
+ uptime_content_ += uptime; |
+ diskstats_content_ += diskstats; |
+ ValidateEventFileContents(uptime_file_name_, uptime_content_); |
+ ValidateEventFileContents(diskstats_file_name_, diskstats_content_); |
} |
-static void TestEventByName(const string& event_name) { |
- bootstat_log(event_name.c_str()); |
- string truncated_event(event_name.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1)); |
- TestEventFileAccess(kUptimeFileNamePrefix + truncated_event); |
- TestEventFileAccess(kDiskStatFileNamePrefix + truncated_event); |
+ |
+// 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(); |
+ RemoveFile(diskstats_file_name_); |
+ RemoveFile(uptime_file_name_); |
+} |
+ |
+ |
+// Bootstat test class. We use this class to override the |
+// dependencies in bootstat_log() on the file paths for /proc/uptime |
+// and /sys/block/<device>/stat. |
+// |
+// The class uses test-specific interfaces that change the default |
+// paths from the kernel statistics psuedo-files to temporary paths |
+// selected by this test. This class also redirects the location for |
+// the event files created by bootstat_log() to a temporary directory. |
+class BootstatTest : public ::testing::Test { |
+ protected: |
+ virtual void SetUp(); |
+ virtual void TearDown(); |
+ |
+ EventTracker MakeEvent(const string& event_name) { |
+ return EventTracker(event_name, uptime_event_prefix_, |
+ disk_event_prefix_); |
+ } |
+ |
+ void SetStatsContent(const char* uptime_content, |
+ const char* disk_content); |
+ void TestLogEvent(EventTracker& event); |
+ |
+ 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()); |
} |
-// Tests that name truncation of logged events works as advertised |
-TEST(BoostatTest, EventNameTruncation) { |
+ |
+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, 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) { |
+ 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) |
+ << "write to stats file " << file_path |
+ << " failed: " << strerror(errno); |
+ (void)close(fd); |
+} |
+ |
+ |
+// Set the content of the files mocking the contents of the kernel's |
+// statistics pseudo-files. The strings provided here will be the |
+// ones recorded for subsequent calls to bootstat_log() for all |
+// 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_); |
+} |
+ |
+ |
+void BootstatTest::TestLogEvent(EventTracker& event) { |
+ event.TestLogEvent(uptime_stats_content_, disk_stats_content_); |
+} |
+ |
+ |
+// Test data to be used as input to SetStatsContent(). |
+// |
+// The structure of this array is pairs of strings, terminated by a |
+// single NULL. The first string in the pair is content for |
+// /proc/uptime, the second for /sys/block/<device>/stat. |
+// |
+// This data is taken directly from a development system, and is |
+// representative of valid stats content, though not typical of what |
+// would be seen immediately after boot. |
+static const char* bootstat_data[] = { |
+/* 0 */ |
+/* uptime */ "691448.42 11020440.26\n", |
+/* disk */ " 1417116 14896 55561564 10935990 4267850 78379879" |
+ " 661568738 1635920520 158 17856450 1649520570\n", |
+/* 1 */ |
+/* uptime */ "691623.71 11021372.99\n", |
+/* disk */ " 1420714 14918 55689988 11006390 4287385 78594261" |
+ " 663441564 1651579200 152 17974280 1665255160\n", |
+/* EOT */ NULL |
+}; |
+ |
+ |
+// Tests 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); |
+ i += 2; |
+ } |
+ ev.Reset(); |
+} |
+ |
+ |
+// Tests that name truncation of logged events works as advertised. |
+TEST_F(BootstatTest, EventNameTruncation) { |
+ static const char kMostVoluminousEventName[] = |
+ // 16 32 48 64 |
+ "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 64 |
+ "=064+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 128 |
+ "=128+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 191 |
+ "=191+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 256 |
+ ; |
+ |
string very_long(kMostVoluminousEventName); |
+ SetStatsContent(bootstat_data[0], bootstat_data[1]); |
- TestEventByName(very_long); |
- TestEventByName(very_long.substr(0, 1)); |
- TestEventByName(very_long.substr(0, 16)); |
- TestEventByName(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1)); |
- TestEventByName(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN)); |
+ EventTracker ev = MakeEvent(very_long); |
+ TestLogEvent(ev); |
+ ev.Reset(); |
+ ev = MakeEvent(very_long.substr(0, 1)); |
+ TestLogEvent(ev); |
+ ev.Reset(); |
+ ev = MakeEvent(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1)); |
+ TestLogEvent(ev); |
+ ev.Reset(); |
+ ev = MakeEvent(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN)); |
+ TestLogEvent(ev); |
+ ev.Reset(); |
} |
} // namespace |