| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "bootstat.h" | 5 #include "bootstat.h" |
| 6 #include "bootstat_test.h" | 6 #include "bootstat_test.h" |
| 7 | 7 |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <stdio.h> |
| 9 #include <stdlib.h> | 10 #include <stdlib.h> |
| 10 #include <sys/fcntl.h> | 11 #include <sys/fcntl.h> |
| 11 #include <sys/stat.h> | 12 #include <sys/stat.h> |
| 12 #include <sys/types.h> | 13 #include <sys/types.h> |
| 13 #include <unistd.h> | 14 #include <unistd.h> |
| 14 | 15 |
| 15 #include <string> | 16 #include <string> |
| 16 #include <iostream> | 17 #include <iostream> |
| 17 | 18 |
| 18 #include <gtest/gtest.h> | 19 #include <gtest/gtest.h> |
| 19 | 20 |
| 20 namespace { | 21 namespace { |
| 21 | 22 |
| 22 using std::string; | 23 using std::string; |
| 23 | 24 |
| 24 static void RemoveFile(const string& file_path) { | 25 static void RemoveFile(const string& file_path) { |
| 25 EXPECT_EQ(0, unlink(file_path.c_str())) | 26 EXPECT_EQ(0, unlink(file_path.c_str())) |
| 26 << "can't unlink file " << file_path << ": " | 27 << "can't unlink file " << file_path << ": " |
| 27 << strerror(errno); | 28 << strerror(errno); |
| 28 } | 29 } |
| 29 | 30 |
| 30 | 31 |
| 31 // Class to track and test the data associated with a single event. | 32 // Class to track and test the data associated with a single event. |
| 32 // The primary function is TestLogEvent(): This method wraps calls | 33 // The methods TestEventContent() and TestEventCreation() wrap calls |
| 33 // to bootstat_log() with code to track the expected contents of the | 34 // to bootstat_log() to test various assertions regarding the |
| 34 // event files. After logging, the expected content is tested | 35 // function. |
| 35 // against the actual content. | |
| 36 class EventTracker { | 36 class EventTracker { |
| 37 public: | 37 public: |
| 38 EventTracker(const string& name, const string& uptime_prefix, | 38 EventTracker(const string& name, const string& output_dir); |
| 39 const string& disk_prefix); | 39 void TestEventContent(const char* uptime, const char* diskstats); |
| 40 void TestLogEvent(const string& uptime, const string& diskstats); | 40 void TestEventCreation(bool expect_success); |
| 41 void Reset(); | 41 void Reset(); |
| 42 | 42 |
| 43 private: | 43 private: |
| 44 string event_name_; | 44 string event_name_; |
| 45 string uptime_file_name_; | 45 string uptime_file_name_; |
| 46 string uptime_content_; | 46 string uptime_content_; |
| 47 string diskstats_file_name_; | 47 string diskstats_file_name_; |
| 48 string diskstats_content_; | 48 string diskstats_content_; |
| 49 }; | 49 }; |
| 50 | 50 |
| 51 | 51 |
| 52 EventTracker::EventTracker(const string& name, | 52 EventTracker::EventTracker(const string& name, |
| 53 const string& uptime_prefix, | 53 const string& output_dir) |
| 54 const string& diskstats_prefix) | |
| 55 : event_name_(name), | 54 : event_name_(name), |
| 56 uptime_content_(""), | 55 uptime_content_(""), |
| 57 diskstats_content_("") { | 56 diskstats_content_("") { |
| 58 string truncated_name = | 57 string truncated_name = |
| 59 event_name_.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1); | 58 event_name_.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1); |
| 60 uptime_file_name_ = uptime_prefix + truncated_name; | 59 uptime_file_name_ = output_dir + "/uptime-" + truncated_name; |
| 61 diskstats_file_name_ = diskstats_prefix + truncated_name; | 60 diskstats_file_name_ = output_dir + "/disk-" + truncated_name; |
| 62 } | 61 } |
| 63 | 62 |
| 64 | 63 |
| 65 // Basic helper function to test whether the contents of the | 64 // Basic helper function to test whether the contents of the |
| 66 // specified file exactly match the given contents string. | 65 // specified file exactly match the given contents string. |
| 67 static void ValidateEventFileContents(const string& file_name, | 66 static void ValidateEventFileContent(const string& file_name, |
| 68 const string& file_contents) { | 67 const string& file_contents) { |
| 69 int rv = access(file_name.c_str(), W_OK); | 68 int rv = access(file_name.c_str(), W_OK); |
| 70 EXPECT_EQ(0, rv) << file_name << " is not writable: " | 69 EXPECT_EQ(0, rv) << file_name << " is not writable: " |
| 71 << strerror(errno); | 70 << strerror(errno) << "."; |
| 72 rv = access(file_name.c_str(), R_OK); | 71 rv = access(file_name.c_str(), R_OK); |
| 73 ASSERT_EQ(0, rv) << file_name << " is not readable: " | 72 ASSERT_EQ(0, rv) << file_name << " is not readable: " |
| 74 << strerror(errno); | 73 << strerror(errno) << "."; |
| 75 char *buffer = new char[file_contents.length() + 1]; | 74 char *buffer = new char[file_contents.length() + 1]; |
| 76 int fd = open(file_name.c_str(), O_RDONLY); | 75 int fd = open(file_name.c_str(), O_RDONLY); |
| 77 rv = read(fd, buffer, file_contents.length()); | 76 rv = read(fd, buffer, file_contents.length()); |
| 78 EXPECT_EQ(file_contents.length(), rv); | 77 EXPECT_EQ(file_contents.length(), rv); |
| 79 buffer[file_contents.length()] = '\0'; | 78 buffer[file_contents.length()] = '\0'; |
| 80 string actual_contents(buffer); | 79 string actual_contents(buffer); |
| 81 EXPECT_EQ(file_contents, actual_contents); | 80 EXPECT_EQ(file_contents, actual_contents); |
| 82 rv = read(fd, buffer, 1); | 81 rv = read(fd, buffer, 1); |
| 83 EXPECT_EQ(0, rv) << "found data in event file past expected EOF"; | 82 EXPECT_EQ(0, rv) << "found data in event file past expected EOF"; |
| 84 (void)close(fd); | 83 (void)close(fd); |
| 85 delete buffer; | 84 delete buffer; |
| 86 } | 85 } |
| 87 | 86 |
| 88 | 87 |
| 89 // Call bootstat_log() once, and update the expected content for | 88 // Call bootstat_log() once, and update the expected content for |
| 90 // this event. Test that the new content of the event's files | 89 // this event. Test that the new content of the event's files |
| 91 // matches the updated expected content. | 90 // matches the updated expected content. |
| 92 void EventTracker::TestLogEvent(const string& uptime, | 91 void EventTracker::TestEventContent(const char* uptime, |
| 93 const string& diskstats) { | 92 const char* diskstats) { |
| 94 bootstat_log(event_name_.c_str()); | 93 bootstat_log(event_name_.c_str()); |
| 95 uptime_content_ += uptime; | 94 uptime_content_ += string(uptime); |
| 96 diskstats_content_ += diskstats; | 95 diskstats_content_ += string(diskstats); |
| 97 ValidateEventFileContents(uptime_file_name_, uptime_content_); | 96 ValidateEventFileContent(uptime_file_name_, uptime_content_); |
| 98 ValidateEventFileContents(diskstats_file_name_, diskstats_content_); | 97 ValidateEventFileContent(diskstats_file_name_, diskstats_content_); |
| 99 } | 98 } |
| 100 | 99 |
| 101 | 100 |
| 102 // Reset event state back to initial conditions, by deleting the | 101 // Basic helper function to test whether the contents of the |
| 103 // associated event files, and clearing the expected contents. | 102 // specified file exactly match the given contents string. |
| 103 // A return value of true indicates that the file contents can |
| 104 // be read. |
| 105 static void ValidateEventFileExistence(const string& file_name, |
| 106 bool expect_success) { |
| 107 int rv = access(file_name.c_str(), F_OK); |
| 108 if (expect_success) { |
| 109 EXPECT_EQ(0, rv) << "Cannot verify existence of " << file_name |
| 110 << ": " << strerror(errno) << "."; |
| 111 } else { |
| 112 EXPECT_LT(rv, 0) << file_name << " exists, but it shouldn't."; |
| 113 } |
| 114 |
| 115 if (rv >= 0) { |
| 116 RemoveFile(file_name); |
| 117 } |
| 118 } |
| 119 |
| 120 |
| 121 // Call bootstat_log() once, and confirm creation of the event |
| 122 // files. The content of the files isn't checked. |
| 123 void EventTracker::TestEventCreation(bool expect_success) { |
| 124 bootstat_log(event_name_.c_str()); |
| 125 ValidateEventFileExistence(uptime_file_name_, expect_success); |
| 126 ValidateEventFileExistence(diskstats_file_name_, expect_success); |
| 127 } |
| 128 |
| 129 |
| 130 // Reset event state back to initial conditions by deleting the |
| 131 // associated event files and clearing the expected contents. |
| 104 void EventTracker::Reset() { | 132 void EventTracker::Reset() { |
| 105 uptime_content_.clear(); | 133 uptime_content_.clear(); |
| 106 diskstats_content_.clear(); | 134 diskstats_content_.clear(); |
| 107 RemoveFile(diskstats_file_name_); | 135 RemoveFile(diskstats_file_name_); |
| 108 RemoveFile(uptime_file_name_); | 136 RemoveFile(uptime_file_name_); |
| 109 } | 137 } |
| 110 | 138 |
| 111 | 139 |
| 112 // Bootstat test class. We use this class to override the | 140 // Bootstat test class. We use this class to override the |
| 113 // dependencies in bootstat_log() on the file paths for /proc/uptime | 141 // dependencies in bootstat_log() on the file paths for /proc/uptime |
| 114 // and /sys/block/<device>/stat. | 142 // and /sys/block/<device>/stat. |
| 115 // | 143 // |
| 116 // The class uses test-specific interfaces that change the default | 144 // The class uses test-specific interfaces that change the default |
| 117 // paths from the kernel statistics psuedo-files to temporary paths | 145 // paths from the kernel statistics psuedo-files to temporary paths |
| 118 // selected by this test. This class also redirects the location for | 146 // selected by this test. This class also redirects the location for |
| 119 // the event files created by bootstat_log() to a temporary directory. | 147 // the event files created by bootstat_log() to a temporary directory. |
| 120 class BootstatTest : public ::testing::Test { | 148 class BootstatTest : public ::testing::Test { |
| 121 protected: | 149 protected: |
| 122 virtual void SetUp(); | 150 virtual void SetUp(); |
| 123 virtual void TearDown(); | 151 virtual void TearDown(); |
| 124 | 152 |
| 153 EventTracker MakeEvent(const string& event_name, |
| 154 const string& output_dir_name) { |
| 155 return EventTracker(event_name, output_dir_name); |
| 156 } |
| 125 EventTracker MakeEvent(const string& event_name) { | 157 EventTracker MakeEvent(const string& event_name) { |
| 126 return EventTracker(event_name, uptime_event_prefix_, | 158 return EventTracker(event_name, stats_output_dir_); |
| 127 disk_event_prefix_); | |
| 128 } | 159 } |
| 129 | 160 |
| 130 void SetStatsContent(const char* uptime_content, | 161 void SetStatsContent(const char* uptime_content, |
| 131 const char* disk_content); | 162 const char* disk_content); |
| 132 void TestLogEvent(EventTracker& event); | 163 void ClearStatsContent(); |
| 164 void TestSetOutputDirectory(const string& testdir, |
| 165 const string& testevent, |
| 166 bool expect_success, |
| 167 int expected_errno); |
| 168 void TestSetOutputDirectoryAccess(const string& testdir, mode_t testmode); |
| 169 void TestLogAccess(const string& testdir, mode_t testmode); |
| 133 | 170 |
| 134 private: | 171 private: |
| 135 string stats_output_dir_; | 172 string stats_output_dir_; |
| 136 string uptime_event_prefix_; | |
| 137 string disk_event_prefix_; | |
| 138 | 173 |
| 139 string uptime_stats_file_name_; | 174 string uptime_stats_file_name_; |
| 140 string uptime_stats_content_; | |
| 141 string disk_stats_file_name_; | 175 string disk_stats_file_name_; |
| 142 string disk_stats_content_; | |
| 143 }; | 176 }; |
| 144 | 177 |
| 145 | 178 |
| 146 void BootstatTest::SetUp() { | 179 void BootstatTest::SetUp() { |
| 147 char dir_template[] = "bootstat_test_XXXXXX"; | 180 char dir_template[] = "bootstat_test_XXXXXX"; |
| 148 stats_output_dir_ = string(mkdtemp(dir_template)); | 181 stats_output_dir_ = string(mkdtemp(dir_template)); |
| 149 uptime_event_prefix_ = stats_output_dir_ + "/uptime-"; | |
| 150 disk_event_prefix_ = stats_output_dir_ + "/disk-"; | |
| 151 uptime_stats_file_name_ = stats_output_dir_ + "/proc_uptime"; | 182 uptime_stats_file_name_ = stats_output_dir_ + "/proc_uptime"; |
| 152 disk_stats_file_name_ = stats_output_dir_ + "/block_stats"; | 183 disk_stats_file_name_ = stats_output_dir_ + "/block_stats"; |
| 153 bootstat_set_output_directory(stats_output_dir_.c_str()); | 184 EXPECT_EQ(0, bootstat_set_output_directory(stats_output_dir_.c_str())) |
| 154 bootstat_set_uptime_file_name(uptime_stats_file_name_.c_str()); | 185 << "Failed to select output directory " << stats_output_dir_ |
| 155 bootstat_set_disk_file_name(disk_stats_file_name_.c_str()); | 186 << ": " << strerror(errno) << "."; |
| 156 } | 187 } |
| 157 | 188 |
| 158 | 189 |
| 159 void BootstatTest::TearDown() { | 190 void BootstatTest::TearDown() { |
| 160 bootstat_set_uptime_file_name(NULL); | 191 EXPECT_EQ(0, bootstat_set_output_directory(NULL)) |
| 161 bootstat_set_disk_file_name(NULL); | 192 << "Failed to set output directory back to default: " |
| 162 bootstat_set_output_directory(NULL); | 193 << strerror(errno) << "."; |
| 163 RemoveFile(uptime_stats_file_name_); | |
| 164 RemoveFile(disk_stats_file_name_); | |
| 165 EXPECT_EQ(0, rmdir(stats_output_dir_.c_str())) | 194 EXPECT_EQ(0, rmdir(stats_output_dir_.c_str())) |
| 166 << "Can't remove directory " << stats_output_dir_ | 195 << "Can't remove directory " << stats_output_dir_ |
| 167 << ": " << strerror(errno) << "."; | 196 << ": " << strerror(errno) << "."; |
| 168 } | 197 } |
| 169 | 198 |
| 170 | 199 |
| 171 static void WriteStatsContent(const string& content, const string& file_path) { | 200 static void WriteStatsContent(const char *content, const string& file_path) { |
| 172 const int kFileOpenFlags = O_WRONLY | O_TRUNC | O_CREAT; | 201 const int kFileOpenFlags = O_WRONLY | O_TRUNC | O_CREAT; |
| 173 const mode_t kFileCreationMode = | 202 const mode_t kFileCreationMode = |
| 174 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; | 203 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; |
| 175 int fd = open(file_path.c_str(), kFileOpenFlags, kFileCreationMode); | 204 int fd = open(file_path.c_str(), kFileOpenFlags, kFileCreationMode); |
| 176 int nwrite = write(fd, content.c_str(), content.length()); | 205 int nwrite = write(fd, content, strlen(content)); |
| 177 EXPECT_EQ(content.length(), nwrite) | 206 EXPECT_EQ(strlen(content), nwrite) |
| 178 << "write to stats file " << file_path | 207 << "write to stats file " << file_path |
| 179 << " failed: " << strerror(errno); | 208 << " failed: " << strerror(errno); |
| 180 (void)close(fd); | 209 (void)close(fd); |
| 181 } | 210 } |
| 182 | 211 |
| 183 | 212 |
| 184 // Set the content of the files mocking the contents of the kernel's | 213 // Set the content of the files mocking the contents of the kernel's |
| 185 // statistics pseudo-files. The strings provided here will be the | 214 // statistics pseudo-files. The strings provided here will be the |
| 186 // ones recorded for subsequent calls to bootstat_log() for all | 215 // ones recorded for subsequent calls to bootstat_log() for all |
| 187 // events. | 216 // events. |
| 188 void BootstatTest::SetStatsContent(const char* uptime_stats, | 217 void BootstatTest::SetStatsContent(const char* uptime_stats, |
| 189 const char* disk_stats) { | 218 const char* disk_stats) { |
| 190 uptime_stats_content_ = string(uptime_stats); | 219 WriteStatsContent(uptime_stats, uptime_stats_file_name_); |
| 191 WriteStatsContent(uptime_stats_content_, uptime_stats_file_name_); | 220 WriteStatsContent(disk_stats, disk_stats_file_name_); |
| 192 disk_stats_content_ = string(disk_stats); | 221 bootstat_set_uptime_file_name(uptime_stats_file_name_.c_str()); |
| 193 WriteStatsContent(disk_stats_content_, disk_stats_file_name_); | 222 bootstat_set_disk_file_name(disk_stats_file_name_.c_str()); |
| 194 } | 223 } |
| 195 | 224 |
| 196 | 225 |
| 197 void BootstatTest::TestLogEvent(EventTracker& event) { | 226 void BootstatTest::ClearStatsContent() { |
| 198 event.TestLogEvent(uptime_stats_content_, disk_stats_content_); | 227 bootstat_set_uptime_file_name(NULL); |
| 228 bootstat_set_disk_file_name(NULL); |
| 229 RemoveFile(uptime_stats_file_name_); |
| 230 RemoveFile(disk_stats_file_name_); |
| 199 } | 231 } |
| 200 | 232 |
| 201 | 233 |
| 202 // Test data to be used as input to SetStatsContent(). | 234 // Test data to be used as input to SetStatsContent(). |
| 203 // | 235 // |
| 204 // The structure of this array is pairs of strings, terminated by a | 236 // The structure of this array is pairs of strings, terminated by a |
| 205 // single NULL. The first string in the pair is content for | 237 // single NULL. The first string in the pair is content for |
| 206 // /proc/uptime, the second for /sys/block/<device>/stat. | 238 // /proc/uptime, the second for /sys/block/<device>/stat. |
| 207 // | 239 // |
| 208 // This data is taken directly from a development system, and is | 240 // This data is taken directly from a development system, and is |
| 209 // representative of valid stats content, though not typical of what | 241 // representative of valid stats content, though not typical of what |
| 210 // would be seen immediately after boot. | 242 // would be seen immediately after boot. |
| 211 static const char* bootstat_data[] = { | 243 static const char* bootstat_data[] = { |
| 212 /* 0 */ | 244 /* 0 */ |
| 213 /* uptime */ "691448.42 11020440.26\n", | 245 /* uptime */ "691448.42 11020440.26\n", |
| 214 /* disk */ " 1417116 14896 55561564 10935990 4267850 78379879" | 246 /* disk */ " 1417116 14896 55561564 10935990 4267850 78379879" |
| 215 " 661568738 1635920520 158 17856450 1649520570\n", | 247 " 661568738 1635920520 158 17856450 1649520570\n", |
| 216 /* 1 */ | 248 /* 1 */ |
| 217 /* uptime */ "691623.71 11021372.99\n", | 249 /* uptime */ "691623.71 11021372.99\n", |
| 218 /* disk */ " 1420714 14918 55689988 11006390 4287385 78594261" | 250 /* disk */ " 1420714 14918 55689988 11006390 4287385 78594261" |
| 219 " 663441564 1651579200 152 17974280 1665255160\n", | 251 " 663441564 1651579200 152 17974280 1665255160\n", |
| 220 /* EOT */ NULL | 252 /* EOT */ NULL |
| 221 }; | 253 }; |
| 222 | 254 |
| 223 | 255 |
| 224 // Tests that event file content matches expectations when an | 256 // This is to test that event file content matches expectations when |
| 225 // event is logged multiple times. | 257 // an event is logged multiple times. |
| 226 TEST_F(BootstatTest, ContentGeneration) { | 258 TEST_F(BootstatTest, ContentGeneration) { |
| 227 EventTracker ev = MakeEvent(string("test_event")); | 259 EventTracker ev = MakeEvent(string("test_event")); |
| 228 int i = 0; | 260 int i = 0; |
| 229 while (bootstat_data[i] != NULL) { | 261 while (bootstat_data[i] != NULL) { |
| 230 SetStatsContent(bootstat_data[i], bootstat_data[i+1]); | 262 SetStatsContent(bootstat_data[i], bootstat_data[i+1]); |
| 231 TestLogEvent(ev); | 263 ev.TestEventContent(bootstat_data[i], bootstat_data[i+1]); |
| 232 i += 2; | 264 i += 2; |
| 233 } | 265 } |
| 266 ClearStatsContent(); |
| 234 ev.Reset(); | 267 ev.Reset(); |
| 235 } | 268 } |
| 236 | 269 |
| 237 | 270 |
| 238 // Tests that name truncation of logged events works as advertised. | 271 // This is to test that name truncation of logged events works as |
| 272 // advertised. |
| 239 TEST_F(BootstatTest, EventNameTruncation) { | 273 TEST_F(BootstatTest, EventNameTruncation) { |
| 240 static const char kMostVoluminousEventName[] = | 274 static const char kMostVoluminousEventName[] = |
| 241 // 16 32 48 64 | 275 // 16 32 48 64 |
| 242 "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 64 | 276 "event-6789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 64 |
| 243 "=064+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 128 | 277 "=064+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 128 |
| 244 "=128+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 191 | 278 "=128+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 191 |
| 245 "=191+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 256 | 279 "=191+56789abcdef_123456789ABCDEF.123456789abcdef0123456789abcdef" // 256 |
| 246 ; | 280 ; |
| 247 | 281 |
| 248 string very_long(kMostVoluminousEventName); | 282 string very_long(kMostVoluminousEventName); |
| 249 SetStatsContent(bootstat_data[0], bootstat_data[1]); | 283 SetStatsContent(bootstat_data[0], bootstat_data[1]); |
| 250 | 284 |
| 251 EventTracker ev = MakeEvent(very_long); | 285 EventTracker ev = MakeEvent(very_long); |
| 252 TestLogEvent(ev); | 286 ev.TestEventContent(bootstat_data[0], bootstat_data[1]); |
| 253 ev.Reset(); | 287 ev.Reset(); |
| 254 ev = MakeEvent(very_long.substr(0, 1)); | 288 ev = MakeEvent(very_long.substr(0, 1)); |
| 255 TestLogEvent(ev); | 289 ev.TestEventContent(bootstat_data[0], bootstat_data[1]); |
| 256 ev.Reset(); | 290 ev.Reset(); |
| 257 ev = MakeEvent(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1)); | 291 ev = MakeEvent(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN - 1)); |
| 258 TestLogEvent(ev); | 292 ev.TestEventContent(bootstat_data[0], bootstat_data[1]); |
| 259 ev.Reset(); | 293 ev.Reset(); |
| 260 ev = MakeEvent(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN)); | 294 ev = MakeEvent(very_long.substr(0, BOOTSTAT_MAX_EVENT_LEN)); |
| 261 TestLogEvent(ev); | 295 ev.TestEventContent(bootstat_data[0], bootstat_data[1]); |
| 262 ev.Reset(); | 296 ev.Reset(); |
| 297 |
| 298 ClearStatsContent(); |
| 263 } | 299 } |
| 264 | 300 |
| 301 |
| 302 // Test a call to bootstat_set_output_directory(). Check that the |
| 303 // operation succeeds or fails as indicated by the input parameters. |
| 304 // Also tests that bootstat_log() logs in the expected place after |
| 305 // the call: after success, logging should go to the new directory; |
| 306 // after a failure, logging should continue to go to the previous |
| 307 // directory. |
| 308 void BootstatTest::TestSetOutputDirectory(const string& testdir, |
| 309 const string& testevent, |
| 310 bool expect_success, |
| 311 int expected_errno) { |
| 312 ASSERT_EQ(0, bootstat_set_output_directory(stats_output_dir_.c_str())) |
| 313 << "bootstat_set_output_directory() failed for " << stats_output_dir_ |
| 314 << ": " << strerror(errno) << "."; |
| 315 bool set_output_success = |
| 316 (bootstat_set_output_directory(testdir.c_str()) == 0); |
| 317 |
| 318 if (expect_success) { |
| 319 ASSERT_TRUE(set_output_success) |
| 320 << "bootstat_set_output_directory() failed unexpectedly for " |
| 321 << testdir << ": " << strerror(errno) << "."; |
| 322 } else { |
| 323 ASSERT_FALSE(set_output_success) |
| 324 << "bootstat_set_output_directory() succeeded for " |
| 325 << testdir << "; expected error: " << strerror(expected_errno) << "."; |
| 326 EXPECT_EQ(expected_errno, errno); |
| 327 } |
| 328 |
| 329 string eventdir; |
| 330 if (set_output_success) { |
| 331 eventdir = testdir; |
| 332 } else { |
| 333 eventdir = stats_output_dir_; |
| 334 } |
| 335 string eventname = string("test-set-directory-") + testevent; |
| 336 EventTracker ev = MakeEvent(eventname, eventdir); |
| 337 ev.TestEventCreation(true); |
| 338 ASSERT_EQ(0, bootstat_set_output_directory(NULL)) |
| 339 << "bootstat_set_output_directory(NULL) failed: " |
| 340 << strerror(errno) << "."; |
| 341 } |
| 342 |
| 343 |
| 344 // Test that bootstat_set_output_directory() will return ENOENT |
| 345 // when required. |
| 346 TEST_F(BootstatTest, SetOutputDirectoryEnoent) { |
| 347 TestSetOutputDirectory(string("/this-directory-does-not-exist"), |
| 348 string("ENOENT"), false, ENOENT); |
| 349 } |
| 350 |
| 351 |
| 352 // Test that bootstat_set_output_directory() will return ENOTDIR |
| 353 // when required. |
| 354 TEST_F(BootstatTest, SetOutputDirectoryEnotdir) { |
| 355 string ordinary_file_name = string("ordinary-file"); |
| 356 const mode_t kFileCreationMode = |
| 357 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; |
| 358 int fd = creat(ordinary_file_name.c_str(), kFileCreationMode); |
| 359 EXPECT_GE(fd, 0) |
| 360 << "failed to create test file " << ordinary_file_name |
| 361 << ": " << strerror(errno) << "."; |
| 362 if (fd >= 0) { |
| 363 TestSetOutputDirectory(ordinary_file_name, string("ENOTDIR"), |
| 364 false, ENOTDIR); |
| 365 (void) close(fd); |
| 366 RemoveFile(ordinary_file_name); |
| 367 } |
| 368 } |
| 369 |
| 370 |
| 371 // Test that bootstat_set_output_directory() succeeds or fails with |
| 372 // EACCES as expected. |
| 373 void BootstatTest::TestSetOutputDirectoryAccess(const string& testdir, |
| 374 mode_t testmode) { |
| 375 char buffer[8]; |
| 376 sprintf(buffer, "0%03o", testmode); |
| 377 string modestring = string(buffer); |
| 378 bool expect_success = |
| 379 ((testmode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)); |
| 380 |
| 381 ASSERT_EQ(0, chmod(testdir.c_str(), testmode)) |
| 382 << "Unable to change directory mode for " << testdir |
| 383 << " to " << modestring << ": " << strerror(errno) << "."; |
| 384 string eventname = string("EACCES-") + modestring; |
| 385 TestSetOutputDirectory(testdir, eventname, expect_success, EACCES); |
| 386 } |
| 387 |
| 388 |
| 389 // Test that the actual access mode requirements enforced by |
| 390 // bootstat_log() match the requirements enforced by |
| 391 // bootstat_set_output_directory(). |
| 392 void BootstatTest::TestLogAccess(const string& testdir, |
| 393 mode_t testmode) { |
| 394 char buffer[8]; |
| 395 sprintf(buffer, "0%03o", testmode); |
| 396 string modestring = string(buffer); |
| 397 bool expect_success = |
| 398 ((testmode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)); |
| 399 |
| 400 ASSERT_EQ(0, chmod(testdir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR)) |
| 401 << "Unable to change directory mode for " << testdir |
| 402 << " to 0700: " << strerror(errno) << "."; |
| 403 ASSERT_EQ(0, bootstat_set_output_directory(testdir.c_str())) |
| 404 << "bootstat_set_output_directory() failed for " << testdir |
| 405 << ": " << strerror(errno) << "."; |
| 406 ASSERT_EQ(0, chmod(testdir.c_str(), testmode)) |
| 407 << "Unable to change directory mode for " << testdir |
| 408 << " to " << modestring << ": " << strerror(errno) << "."; |
| 409 |
| 410 string event_name = string("test-log-access-") + modestring; |
| 411 EventTracker ev = MakeEvent(event_name, testdir); |
| 412 ev.TestEventCreation(expect_success); |
| 413 |
| 414 ASSERT_EQ(0, bootstat_set_output_directory(NULL)) |
| 415 << "bootstat_set_output_directory(NULL) failed: " |
| 416 << strerror(errno) << "."; |
| 417 } |
| 418 |
| 419 |
| 420 // Test that bootstat_set_output_directory() will return EACCES |
| 421 // as expected for all directory modes. The test here is |
| 422 // two-pronged. First, the test confirms that EACCES is |
| 423 // returned exactly when the caller has both W and X access. |
| 424 // Second, the test confirms that bootstat_log() fails when |
| 425 // EACCES is returned, but not when it isn't. |
| 426 TEST_F(BootstatTest, SetOutputDirectoryEacces) { |
| 427 char dir_template[] = "bootstat_access_test_XXXXXX"; |
| 428 string testdir = string(mkdtemp(dir_template)); |
| 429 mode_t lobit = ((S_IRUSR - 1) & (S_IWUSR - 1) & (S_IXUSR - 1)) + 1; |
| 430 mode_t allbits = (S_IRUSR | S_IWUSR | S_IXUSR); |
| 431 mode_t dirmode = allbits + lobit; |
| 432 while (dirmode > 0) { |
| 433 dirmode -= lobit; |
| 434 TestSetOutputDirectoryAccess(testdir, dirmode); |
| 435 TestLogAccess(testdir, dirmode); |
| 436 } |
| 437 EXPECT_EQ(0, rmdir(testdir.c_str())) |
| 438 << "Can't remove directory " << testdir |
| 439 << ": " << strerror(errno) << "."; |
| 440 } |
| 441 |
| 442 |
| 265 } // namespace | 443 } // namespace |
| 266 | 444 |
| 267 int main(int argc, char** argv) { | 445 int main(int argc, char** argv) { |
| 268 testing::InitGoogleTest(&argc, argv); | 446 testing::InitGoogleTest(&argc, argv); |
| 269 return RUN_ALL_TESTS(); | 447 return RUN_ALL_TESTS(); |
| 270 } | 448 } |
| OLD | NEW |