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

Side by Side Diff: chrome/browser/chromeos/system_logs/single_log_source_unittest.cc

Issue 2956513004: Rename SingleLogSource to SingleLogFileLogSource (Closed)
Patch Set: Rebased Created 3 years, 5 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/system_logs/single_log_source.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/macros.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/run_loop.h"
16 #include "base/strings/string_split.h"
17 #include "base/test/scoped_task_environment.h"
18 #include "base/time/time.h"
19 #include "content/public/test/test_browser_thread_bundle.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 namespace system_logs {
23
24 namespace {
25
26 // Counts number of lines in a string.
27 size_t GetNumLinesInString(const std::string& string) {
28 size_t split_size = base::SplitString(string, "\n", base::KEEP_WHITESPACE,
29 base::SPLIT_WANT_ALL)
30 .size();
31 // If there is an extra newline at the end, it will generate an extra empty
32 // string. Do not count this as an extra line.
33 if (!string.empty() && string.back() == '\n')
34 --split_size;
35
36 return split_size;
37 }
38
39 } // namespace
40
41 class SingleLogSourceTest : public ::testing::Test {
42 public:
43 SingleLogSourceTest()
44 : scoped_task_environment_(
45 base::test::ScopedTaskEnvironment::MainThreadType::UI),
46 num_callback_calls_(0) {
47 InitializeTestLogDir();
48 }
49
50 ~SingleLogSourceTest() override {
51 SingleLogSource::SetChromeStartTimeForTesting(nullptr);
52 }
53
54 protected:
55 // Sets up a dummy system log directory.
56 void InitializeTestLogDir() {
57 ASSERT_TRUE(log_dir_.CreateUniqueTempDir());
58
59 // Create file "messages".
60 const base::FilePath messages_path = log_dir_.GetPath().Append("messages");
61 base::WriteFile(messages_path, "", 0);
62 EXPECT_TRUE(base::PathExists(messages_path)) << messages_path.value();
63
64 // Create file "ui/ui.LATEST".
65 const base::FilePath ui_dir_path = log_dir_.GetPath().Append("ui");
66 ASSERT_TRUE(base::CreateDirectory(ui_dir_path)) << ui_dir_path.value();
67
68 const base::FilePath ui_latest_path = ui_dir_path.Append("ui.LATEST");
69 base::WriteFile(ui_latest_path, "", 0);
70 ASSERT_TRUE(base::PathExists(ui_latest_path)) << ui_latest_path.value();
71 }
72
73 // Initializes the unit under test, |source_| to read a file from the dummy
74 // system log directory.
75 void InitializeSource(SingleLogSource::SupportedSource source_type) {
76 source_ = base::MakeUnique<SingleLogSource>(source_type);
77 source_->log_file_dir_path_ = log_dir_.GetPath();
78 log_file_path_ = source_->log_file_dir_path_.Append(source_->source_name());
79 ASSERT_TRUE(base::PathExists(log_file_path_)) << log_file_path_.value();
80 }
81
82 // Writes/appends (respectively) a string |input| to file indicated by
83 // |relative_path| under |log_dir_|.
84 bool WriteFile(const base::FilePath& relative_path,
85 const std::string& input) {
86 return base::WriteFile(log_dir_.GetPath().Append(relative_path),
87 input.data(),
88 input.size()) == static_cast<int>(input.size());
89 }
90 bool AppendToFile(const base::FilePath& relative_path,
91 const std::string& input) {
92 return base::AppendToFile(log_dir_.GetPath().Append(relative_path),
93 input.data(), input.size());
94 }
95
96 // Moves source file to destination path, then creates an empty file at the
97 // path of the original source file.
98 //
99 // |src_relative_path|: Source file path relative to |log_dir_|.
100 // |dest_relative_path|: Destination path relative to |log_dir_|.
101 bool RotateFile(const base::FilePath& src_relative_path,
102 const base::FilePath& dest_relative_path) {
103 return base::Move(log_dir_.GetPath().Append(src_relative_path),
104 log_dir_.GetPath().Append(dest_relative_path)) &&
105 WriteFile(src_relative_path, "");
106 }
107
108 // Calls source_.Fetch() to start a logs fetch operation. Passes in
109 // OnFileRead() as a callback. Runs until Fetch() has completed.
110 void FetchFromSource() {
111 source_->Fetch(
112 base::Bind(&SingleLogSourceTest::OnFileRead, base::Unretained(this)));
113 scoped_task_environment_.RunUntilIdle();
114 }
115
116 // Callback for fetching logs from |source_|. Overwrites the previous stored
117 // value of |latest_response_|.
118 void OnFileRead(SystemLogsResponse* response) {
119 ++num_callback_calls_;
120 if (response->empty())
121 return;
122
123 // Since |source_| represents a single log source, it should only return a
124 // single string result.
125 EXPECT_EQ(1U, response->size());
126 latest_response_ = std::move(response->begin()->second);
127 }
128
129 int num_callback_calls() const { return num_callback_calls_; }
130
131 const std::string& latest_response() const { return latest_response_; }
132
133 const base::FilePath& log_file_path() const { return log_file_path_; }
134
135 private:
136 // For running scheduled tasks.
137 base::test::ScopedTaskEnvironment scoped_task_environment_;
138
139 // Creates the necessary browser threads. Defined after
140 // |scoped_task_environment_| in order to use the MessageLoop it created.
141 content::TestBrowserThreadBundle browser_thread_bundle_;
142
143 // Unit under test.
144 std::unique_ptr<SingleLogSource> source_;
145
146 // Counts the number of times that |source_| has invoked the callback.
147 int num_callback_calls_;
148
149 // Stores the string response returned from |source_| the last time it invoked
150 // OnFileRead.
151 std::string latest_response_;
152
153 // Temporary dir for creating a dummy log file.
154 base::ScopedTempDir log_dir_;
155
156 // Path to the dummy log file in |log_dir_|.
157 base::FilePath log_file_path_;
158
159 DISALLOW_COPY_AND_ASSIGN(SingleLogSourceTest);
160 };
161
162 TEST_F(SingleLogSourceTest, EmptyFile) {
163 InitializeSource(SingleLogSource::SupportedSource::kMessages);
164 FetchFromSource();
165
166 EXPECT_EQ(1, num_callback_calls());
167 EXPECT_EQ("", latest_response());
168 }
169
170 TEST_F(SingleLogSourceTest, SingleRead) {
171 InitializeSource(SingleLogSource::SupportedSource::kUiLatest);
172
173 EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"), "Hello world!\n"));
174 FetchFromSource();
175
176 EXPECT_EQ(1, num_callback_calls());
177 EXPECT_EQ("Hello world!\n", latest_response());
178 }
179
180 TEST_F(SingleLogSourceTest, IncrementalReads) {
181 InitializeSource(SingleLogSource::SupportedSource::kMessages);
182
183 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "Hello world!\n"));
184 FetchFromSource();
185
186 EXPECT_EQ(1, num_callback_calls());
187 EXPECT_EQ("Hello world!\n", latest_response());
188
189 EXPECT_TRUE(AppendToFile(base::FilePath("messages"),
190 "The quick brown fox jumps over the lazy dog\n"));
191 FetchFromSource();
192
193 EXPECT_EQ(2, num_callback_calls());
194 EXPECT_EQ("The quick brown fox jumps over the lazy dog\n", latest_response());
195
196 EXPECT_TRUE(AppendToFile(base::FilePath("messages"),
197 "Some like it hot.\nSome like it cold\n"));
198 FetchFromSource();
199
200 EXPECT_EQ(3, num_callback_calls());
201 EXPECT_EQ("Some like it hot.\nSome like it cold\n", latest_response());
202
203 // As a sanity check, read entire contents of file separately to make sure it
204 // was written incrementally, and hence read incrementally.
205 std::string file_contents;
206 EXPECT_TRUE(base::ReadFileToString(log_file_path(), &file_contents));
207 EXPECT_EQ(
208 "Hello world!\nThe quick brown fox jumps over the lazy dog\n"
209 "Some like it hot.\nSome like it cold\n",
210 file_contents);
211 }
212
213 // The log files read by SingleLogSource are not expected to be overwritten.
214 // This test is just to ensure that the SingleLogSource class is robust enough
215 // not to break in the event of an overwrite.
216 TEST_F(SingleLogSourceTest, FileOverwrite) {
217 InitializeSource(SingleLogSource::SupportedSource::kUiLatest);
218
219 EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"), "0123456789\n"));
220 FetchFromSource();
221
222 EXPECT_EQ(1, num_callback_calls());
223 EXPECT_EQ("0123456789\n", latest_response());
224
225 // Overwrite the file.
226 EXPECT_TRUE(WriteFile(base::FilePath("ui/ui.LATEST"), "abcdefg\n"));
227 FetchFromSource();
228
229 // Should re-read from the beginning.
230 EXPECT_EQ(2, num_callback_calls());
231 EXPECT_EQ("abcdefg\n", latest_response());
232
233 // Append to the file to make sure incremental read still works.
234 EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"), "hijk\n"));
235 FetchFromSource();
236
237 EXPECT_EQ(3, num_callback_calls());
238 EXPECT_EQ("hijk\n", latest_response());
239
240 // Overwrite again, this time with a longer length than the existing file.
241 // Previous contents:
242 // abcdefg~hijk~ <-- "~" is a single-char representation of newline.
243 // New contents:
244 // lmnopqrstuvwxyz~ <-- excess text beyond end of prev contents: "yz~"
245 EXPECT_TRUE(WriteFile(base::FilePath("ui/ui.LATEST"), "lmnopqrstuvwxyz\n"));
246 FetchFromSource();
247
248 EXPECT_EQ(4, num_callback_calls());
249 EXPECT_EQ("yz\n", latest_response());
250 }
251
252 TEST_F(SingleLogSourceTest, IncompleteLines) {
253 InitializeSource(SingleLogSource::SupportedSource::kMessages);
254
255 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "0123456789"));
256 FetchFromSource();
257
258 EXPECT_EQ(1, num_callback_calls());
259 EXPECT_EQ("", latest_response());
260
261 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "abcdefg"));
262 FetchFromSource();
263
264 EXPECT_EQ(2, num_callback_calls());
265 EXPECT_EQ("", latest_response());
266
267 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "hijk\n"));
268 FetchFromSource();
269
270 EXPECT_EQ(3, num_callback_calls());
271 // All the previously written text should be read this time.
272 EXPECT_EQ("0123456789abcdefghijk\n", latest_response());
273
274 // Check ability to read whole lines while leaving the remainder for later.
275 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "Hello world\n"));
276 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "Goodbye world"));
277 FetchFromSource();
278
279 EXPECT_EQ(4, num_callback_calls());
280 EXPECT_EQ("Hello world\n", latest_response());
281
282 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "\n"));
283 FetchFromSource();
284
285 EXPECT_EQ(5, num_callback_calls());
286 EXPECT_EQ("Goodbye world\n", latest_response());
287 }
288
289 TEST_F(SingleLogSourceTest, Anonymize) {
290 InitializeSource(SingleLogSource::SupportedSource::kUiLatest);
291
292 EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"),
293 "My MAC address is: 11:22:33:44:55:66\n"));
294 FetchFromSource();
295
296 EXPECT_EQ(1, num_callback_calls());
297 EXPECT_EQ("My MAC address is: 11:22:33:00:00:01\n", latest_response());
298
299 // Suppose the write operation is not atomic, and the MAC address is written
300 // across two separate writes.
301 EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"),
302 "Your MAC address is: AB:88:C"));
303 FetchFromSource();
304
305 EXPECT_EQ(2, num_callback_calls());
306 EXPECT_EQ("", latest_response());
307
308 EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"), "D:99:EF:77\n"));
309 FetchFromSource();
310
311 EXPECT_EQ(3, num_callback_calls());
312 EXPECT_EQ("Your MAC address is: ab:88:cd:00:00:02\n", latest_response());
313 }
314
315 TEST_F(SingleLogSourceTest, HandleLogFileRotation) {
316 InitializeSource(SingleLogSource::SupportedSource::kMessages);
317
318 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "1st log file\n"));
319 FetchFromSource();
320 EXPECT_EQ(1, num_callback_calls());
321 EXPECT_EQ("1st log file\n", latest_response());
322
323 // Rotate file. Make sure the rest of the old file and the contents of the new
324 // file are both read.
325 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "More 1st log file\n"));
326 EXPECT_TRUE(
327 RotateFile(base::FilePath("messages"), base::FilePath("messages.1")));
328 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "2nd log file\n"));
329
330 FetchFromSource();
331 EXPECT_EQ(2, num_callback_calls());
332 EXPECT_EQ("More 1st log file\n2nd log file\n", latest_response());
333
334 // Rotate again, but this time omit the newline before rotating.
335 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "No newline here..."));
336 EXPECT_TRUE(
337 RotateFile(base::FilePath("messages"), base::FilePath("messages.1")));
338 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "3rd log file\n"));
339 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "Also no newline here"));
340
341 FetchFromSource();
342 EXPECT_EQ(3, num_callback_calls());
343 // Make sure the rotation didn't break anything: the last part of the new file
344 // does not end with a newline; thus the new file should not be read.
345 EXPECT_EQ("No newline here...3rd log file\n", latest_response());
346
347 // Finish the previous read attempt by adding the missing newline.
348 EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "...yet\n"));
349 FetchFromSource();
350 EXPECT_EQ(4, num_callback_calls());
351 EXPECT_EQ("Also no newline here...yet\n", latest_response());
352 }
353
354 TEST_F(SingleLogSourceTest, ReadRecentLinesFromMessages) {
355 // Write some lines to messages. Use various timestamp styles. Some lines have
356 // timestamps, Some do not. All timestamps are in chronological order.
357 const base::FilePath messages_path = base::FilePath("messages");
358 // All timestamps below include time zone info. Some are EDT (-0400) and
359 // others are PDT (-0700). These make sure that SingleLogSource is able to
360 // read various standard timestamp formats.
361 EXPECT_TRUE(
362 AppendToFile(messages_path, "13 Jun 2017 15:00:00 -0400 : Alpha\n"));
363 EXPECT_TRUE(
364 AppendToFile(messages_path, "13-Jun-2017 15:30 -04:00 : Bravo\n"));
365 EXPECT_TRUE(AppendToFile(messages_path, "Missing timestamp #1\n"));
366 EXPECT_TRUE(
367 AppendToFile(messages_path, "2017-06-13 3:30pm -04:00 : Charlie\n"));
368 EXPECT_TRUE(AppendToFile(messages_path, "\n"));
369 EXPECT_TRUE(
370 AppendToFile(messages_path, "13 Jun 2017 13:30:00 -0700 : Delta\n"));
371 EXPECT_TRUE(
372 AppendToFile(messages_path, "Tue Jun 13 17:00 EDT 2017 : Echo\n"));
373 EXPECT_TRUE(AppendToFile(messages_path, "Missing timestamp #2\n"));
374 EXPECT_TRUE(
375 AppendToFile(messages_path, "2017-06-13T17:30:00.125-04:00 : Foxtrot\n"));
376 EXPECT_TRUE(
377 AppendToFile(messages_path, "2017-06-13 3:00:00 PM PDT : Golf\n"));
378
379 // The timestamp of the first message in file "messages".
380 base::Time first_line_time;
381 EXPECT_TRUE(
382 base::Time::FromString("13 Jun 2017 15:00:00 EDT", &first_line_time));
383
384 // Confirm that it was correctly parsed.
385 base::Time::Exploded exploded;
386 first_line_time.UTCExplode(&exploded);
387 EXPECT_EQ(2017, exploded.year);
388 EXPECT_EQ(6, exploded.month);
389 EXPECT_EQ(2 /* Tue */, exploded.day_of_week);
390 EXPECT_EQ(13, exploded.day_of_month);
391 EXPECT_EQ(15 + 4 /* Convert from EDT to UTC */, exploded.hour);
392 EXPECT_EQ(0, exploded.minute);
393 EXPECT_EQ(0, exploded.second);
394 EXPECT_EQ(0, exploded.millisecond);
395
396 // Provide a fake Chrome start time for testing: 15:00 EDT
397 base::Time chrome_start_time = first_line_time;
398 SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
399
400 // Read from messages. The first line of messages should not have been read.
401 InitializeSource(SingleLogSource::SupportedSource::kMessages);
402 FetchFromSource();
403 EXPECT_EQ(1, num_callback_calls());
404 EXPECT_EQ(10U, GetNumLinesInString(latest_response())) << latest_response();
405
406 // Update Chrome start time to: 15:15 EDT
407 chrome_start_time += base::TimeDelta::FromMinutes(15);
408 SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
409
410 InitializeSource(SingleLogSource::SupportedSource::kMessages);
411 FetchFromSource();
412 EXPECT_EQ(2, num_callback_calls());
413 EXPECT_EQ(9U, GetNumLinesInString(latest_response())) << latest_response();
414
415 // Update Chrome start time: 15:45 EDT
416 chrome_start_time += base::TimeDelta::FromMinutes(30);
417 SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
418
419 InitializeSource(SingleLogSource::SupportedSource::kMessages);
420 FetchFromSource();
421 EXPECT_EQ(3, num_callback_calls());
422 EXPECT_EQ(5U, GetNumLinesInString(latest_response())) << latest_response();
423
424 // Update Chrome start time: 17:10 EDT
425 chrome_start_time = first_line_time + base::TimeDelta::FromHours(2) +
426 base::TimeDelta::FromMinutes(10);
427 SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
428
429 InitializeSource(SingleLogSource::SupportedSource::kMessages);
430 FetchFromSource();
431 EXPECT_EQ(4, num_callback_calls());
432 EXPECT_EQ(4U, GetNumLinesInString(latest_response())) << latest_response();
433
434 // Update Chrome start time: 17:40:00.125 EDT
435 chrome_start_time +=
436 base::TimeDelta::FromMinutes(30) + base::TimeDelta::FromMilliseconds(125);
437 SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
438
439 InitializeSource(SingleLogSource::SupportedSource::kMessages);
440 FetchFromSource();
441 EXPECT_EQ(5, num_callback_calls());
442 EXPECT_EQ(2U, GetNumLinesInString(latest_response())) << latest_response();
443
444 // Update Chrome start time: 17:40:00.126 EDT
445 chrome_start_time += base::TimeDelta::FromMilliseconds(1);
446 SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
447
448 InitializeSource(SingleLogSource::SupportedSource::kMessages);
449 FetchFromSource();
450 EXPECT_EQ(6, num_callback_calls());
451 EXPECT_EQ(1U, GetNumLinesInString(latest_response())) << latest_response();
452
453 // Update Chrome start time: 18:10 EDT
454 chrome_start_time = first_line_time + base::TimeDelta::FromHours(3) +
455 base::TimeDelta::FromMinutes(10);
456 SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
457
458 InitializeSource(SingleLogSource::SupportedSource::kMessages);
459 FetchFromSource();
460 EXPECT_EQ(7, num_callback_calls());
461 EXPECT_EQ(1U, GetNumLinesInString(latest_response())) << latest_response();
462
463 // Update Chrome start time: 18:15 EDT
464 chrome_start_time += base::TimeDelta::FromMinutes(5);
465 SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
466
467 InitializeSource(SingleLogSource::SupportedSource::kMessages);
468 FetchFromSource();
469 EXPECT_EQ(8, num_callback_calls());
470 EXPECT_EQ(0U, GetNumLinesInString(latest_response())) << latest_response();
471 }
472
473 } // namespace system_logs
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698