1 // Copyright 2012 The Chromium Authors
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 "base/files/important_file_writer.h"
6
7 #include <optional>
8 #include <utility>
9
10 #include "base/compiler_specific.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/functional/bind.h"
15 #include "base/functional/callback.h"
16 #include "base/functional/callback_helpers.h"
17 #include "base/location.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/notreached.h"
20 #include "base/run_loop.h"
21 #include "base/sequence_checker.h"
22 #include "base/task/single_thread_task_runner.h"
23 #include "base/test/bind.h"
24 #include "base/test/metrics/histogram_tester.h"
25 #include "base/test/task_environment.h"
26 #include "base/threading/thread.h"
27 #include "base/time/time.h"
28 #include "base/timer/mock_timer.h"
29 #include "build/build_config.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace base {
33
34 namespace {
35
GetFileContent(const FilePath & path)36 std::string GetFileContent(const FilePath& path) {
37 std::string content;
38 if (!ReadFileToString(path, &content)) {
39 NOTREACHED();
40 }
41 return content;
42 }
43
44 class DataSerializer : public ImportantFileWriter::DataSerializer {
45 public:
DataSerializer(const std::string & data)46 explicit DataSerializer(const std::string& data) : data_(data) {
47 }
48
SerializeData()49 std::optional<std::string> SerializeData() override {
50 EXPECT_TRUE(sequence_checker_.CalledOnValidSequence());
51 return data_;
52 }
53
54 private:
55 const base::SequenceChecker sequence_checker_;
56 const std::string data_;
57 };
58
59 class FailingDataSerializer : public ImportantFileWriter::DataSerializer {
60 public:
SerializeData()61 std::optional<std::string> SerializeData() override { return std::nullopt; }
62 };
63
64 class BackgroundDataSerializer
65 : public ImportantFileWriter::BackgroundDataSerializer {
66 public:
BackgroundDataSerializer(ImportantFileWriter::BackgroundDataProducerCallback data_producer_callback)67 explicit BackgroundDataSerializer(
68 ImportantFileWriter::BackgroundDataProducerCallback
69 data_producer_callback)
70 : data_producer_callback_(std::move(data_producer_callback)) {
71 DCHECK(data_producer_callback_);
72 }
73
74 ImportantFileWriter::BackgroundDataProducerCallback
GetSerializedDataProducerForBackgroundSequence()75 GetSerializedDataProducerForBackgroundSequence() override {
76 EXPECT_TRUE(sequence_checker_.CalledOnValidSequence());
77 return std::move(data_producer_callback_);
78 }
79
producer_callback_obtained() const80 bool producer_callback_obtained() const {
81 return data_producer_callback_.is_null();
82 }
83
84 private:
85 const base::SequenceChecker sequence_checker_;
86 ImportantFileWriter::BackgroundDataProducerCallback data_producer_callback_;
87 };
88
89 enum WriteCallbackObservationState {
90 NOT_CALLED,
91 CALLED_WITH_ERROR,
92 CALLED_WITH_SUCCESS,
93 };
94
95 class WriteCallbacksObserver {
96 public:
97 WriteCallbacksObserver() = default;
98 WriteCallbacksObserver(const WriteCallbacksObserver&) = delete;
99 WriteCallbacksObserver& operator=(const WriteCallbacksObserver&) = delete;
100
101 // Register OnBeforeWrite() and OnAfterWrite() to be called on the next write
102 // of |writer|. `after_write_closure` will also be invoked from
103 // OnAfterWrite().
104 void ObserveNextWriteCallbacks(
105 ImportantFileWriter* writer,
106 base::OnceClosure after_write_closure = base::DoNothing());
107
108 // Returns the |WriteCallbackObservationState| which was observed, then resets
109 // it to |NOT_CALLED|.
110 WriteCallbackObservationState GetAndResetObservationState();
111
112 private:
OnBeforeWrite()113 void OnBeforeWrite() {
114 EXPECT_FALSE(before_write_called_);
115 before_write_called_ = true;
116 }
117
OnAfterWrite(bool success)118 void OnAfterWrite(bool success) {
119 EXPECT_EQ(NOT_CALLED, after_write_observation_state_);
120 after_write_observation_state_ =
121 success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR;
122 std::move(after_write_closure_).Run();
123 }
124
125 bool before_write_called_ = false;
126 WriteCallbackObservationState after_write_observation_state_ = NOT_CALLED;
127 base::OnceClosure after_write_closure_ = base::DoNothing();
128 };
129
ObserveNextWriteCallbacks(ImportantFileWriter * writer,base::OnceClosure after_write_closure)130 void WriteCallbacksObserver::ObserveNextWriteCallbacks(
131 ImportantFileWriter* writer,
132 base::OnceClosure after_write_closure) {
133 after_write_closure_ = std::move(after_write_closure);
134 writer->RegisterOnNextWriteCallbacks(
135 base::BindOnce(&WriteCallbacksObserver::OnBeforeWrite,
136 base::Unretained(this)),
137 base::BindOnce(&WriteCallbacksObserver::OnAfterWrite,
138 base::Unretained(this)));
139 }
140
141 WriteCallbackObservationState
GetAndResetObservationState()142 WriteCallbacksObserver::GetAndResetObservationState() {
143 EXPECT_EQ(after_write_observation_state_ != NOT_CALLED, before_write_called_)
144 << "The before-write callback should always be called before the "
145 "after-write callback";
146
147 WriteCallbackObservationState state = after_write_observation_state_;
148 before_write_called_ = false;
149 after_write_observation_state_ = NOT_CALLED;
150 return state;
151 }
152
153 } // namespace
154
155 class ImportantFileWriterTest : public testing::Test {
156 public:
157 ImportantFileWriterTest() = default;
SetUp()158 void SetUp() override {
159 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
160 file_ = temp_dir_.GetPath().AppendASCII("test-file");
161 }
162
163 protected:
164 WriteCallbacksObserver write_callback_observer_;
165 FilePath file_;
166 test::TaskEnvironment task_environment_;
167
168 private:
169 ScopedTempDir temp_dir_;
170 };
171
TEST_F(ImportantFileWriterTest,Basic)172 TEST_F(ImportantFileWriterTest, Basic) {
173 ImportantFileWriter writer(file_,
174 SingleThreadTaskRunner::GetCurrentDefault());
175 EXPECT_FALSE(PathExists(writer.path()));
176 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
177 writer.WriteNow("foo");
178 RunLoop().RunUntilIdle();
179
180 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
181 ASSERT_TRUE(PathExists(writer.path()));
182 EXPECT_EQ("foo", GetFileContent(writer.path()));
183 }
184
TEST_F(ImportantFileWriterTest,WriteWithObserver)185 TEST_F(ImportantFileWriterTest, WriteWithObserver) {
186 ImportantFileWriter writer(file_,
187 SingleThreadTaskRunner::GetCurrentDefault());
188 EXPECT_FALSE(PathExists(writer.path()));
189 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
190
191 // Confirm that the observer is invoked.
192 write_callback_observer_.ObserveNextWriteCallbacks(&writer);
193 writer.WriteNow("foo");
194 RunLoop().RunUntilIdle();
195
196 EXPECT_EQ(CALLED_WITH_SUCCESS,
197 write_callback_observer_.GetAndResetObservationState());
198 ASSERT_TRUE(PathExists(writer.path()));
199 EXPECT_EQ("foo", GetFileContent(writer.path()));
200
201 // Confirm that re-installing the observer works for another write.
202 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
203 write_callback_observer_.ObserveNextWriteCallbacks(&writer);
204 writer.WriteNow("bar");
205 RunLoop().RunUntilIdle();
206
207 EXPECT_EQ(CALLED_WITH_SUCCESS,
208 write_callback_observer_.GetAndResetObservationState());
209 ASSERT_TRUE(PathExists(writer.path()));
210 EXPECT_EQ("bar", GetFileContent(writer.path()));
211
212 // Confirm that writing again without re-installing the observer doesn't
213 // result in a notification.
214 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
215 writer.WriteNow("baz");
216 RunLoop().RunUntilIdle();
217
218 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
219 ASSERT_TRUE(PathExists(writer.path()));
220 EXPECT_EQ("baz", GetFileContent(writer.path()));
221 }
222
TEST_F(ImportantFileWriterTest,FailedWriteWithObserver)223 TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) {
224 // Use an invalid file path (relative paths are invalid) to get a
225 // FILE_ERROR_ACCESS_DENIED error when trying to write the file.
226 ImportantFileWriter writer(FilePath().AppendASCII("bad/../path"),
227 SingleThreadTaskRunner::GetCurrentDefault());
228 EXPECT_FALSE(PathExists(writer.path()));
229 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
230 write_callback_observer_.ObserveNextWriteCallbacks(&writer);
231 writer.WriteNow("foo");
232 RunLoop().RunUntilIdle();
233
234 // Confirm that the write observer was invoked with its boolean parameter set
235 // to false.
236 EXPECT_EQ(CALLED_WITH_ERROR,
237 write_callback_observer_.GetAndResetObservationState());
238 EXPECT_FALSE(PathExists(writer.path()));
239 }
240
TEST_F(ImportantFileWriterTest,CallbackRunsOnWriterThread)241 TEST_F(ImportantFileWriterTest, CallbackRunsOnWriterThread) {
242 base::Thread file_writer_thread("ImportantFileWriter test thread");
243 file_writer_thread.Start();
244 ImportantFileWriter writer(file_, file_writer_thread.task_runner());
245 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
246
247 // Block execution on |file_writer_thread| to verify that callbacks are
248 // executed on it.
249 base::WaitableEvent wait_helper(
250 base::WaitableEvent::ResetPolicy::MANUAL,
251 base::WaitableEvent::InitialState::NOT_SIGNALED);
252 file_writer_thread.task_runner()->PostTask(
253 FROM_HERE, base::BindOnce(&base::WaitableEvent::Wait,
254 base::Unretained(&wait_helper)));
255
256 write_callback_observer_.ObserveNextWriteCallbacks(&writer);
257 writer.WriteNow("foo");
258 RunLoop().RunUntilIdle();
259
260 // Expect the callback to not have been executed before the
261 // |file_writer_thread| is unblocked.
262 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
263
264 wait_helper.Signal();
265 file_writer_thread.FlushForTesting();
266
267 EXPECT_EQ(CALLED_WITH_SUCCESS,
268 write_callback_observer_.GetAndResetObservationState());
269 ASSERT_TRUE(PathExists(writer.path()));
270 EXPECT_EQ("foo", GetFileContent(writer.path()));
271 }
272
TEST_F(ImportantFileWriterTest,ScheduleWrite)273 TEST_F(ImportantFileWriterTest, ScheduleWrite) {
274 constexpr TimeDelta kCommitInterval = Seconds(12345);
275 MockOneShotTimer timer;
276 ImportantFileWriter writer(file_, SingleThreadTaskRunner::GetCurrentDefault(),
277 kCommitInterval);
278 EXPECT_EQ(0u, writer.previous_data_size());
279 writer.SetTimerForTesting(&timer);
280 EXPECT_FALSE(writer.HasPendingWrite());
281 DataSerializer serializer("foo");
282 writer.ScheduleWrite(&serializer);
283 EXPECT_TRUE(writer.HasPendingWrite());
284 ASSERT_TRUE(timer.IsRunning());
285 EXPECT_EQ(kCommitInterval, timer.GetCurrentDelay());
286 timer.Fire();
287 EXPECT_FALSE(writer.HasPendingWrite());
288 EXPECT_FALSE(timer.IsRunning());
289 RunLoop().RunUntilIdle();
290 ASSERT_TRUE(PathExists(writer.path()));
291 EXPECT_EQ("foo", GetFileContent(writer.path()));
292 EXPECT_EQ(3u, writer.previous_data_size());
293 }
294
TEST_F(ImportantFileWriterTest,DoScheduledWrite)295 TEST_F(ImportantFileWriterTest, DoScheduledWrite) {
296 MockOneShotTimer timer;
297 ImportantFileWriter writer(file_,
298 SingleThreadTaskRunner::GetCurrentDefault());
299 writer.SetTimerForTesting(&timer);
300 EXPECT_FALSE(writer.HasPendingWrite());
301 DataSerializer serializer("foo");
302 writer.ScheduleWrite(&serializer);
303 EXPECT_TRUE(writer.HasPendingWrite());
304 writer.DoScheduledWrite();
305 EXPECT_FALSE(writer.HasPendingWrite());
306 RunLoop().RunUntilIdle();
307 ASSERT_TRUE(PathExists(writer.path()));
308 EXPECT_EQ("foo", GetFileContent(writer.path()));
309 }
310
TEST_F(ImportantFileWriterTest,BatchingWrites)311 TEST_F(ImportantFileWriterTest, BatchingWrites) {
312 MockOneShotTimer timer;
313 ImportantFileWriter writer(file_,
314 SingleThreadTaskRunner::GetCurrentDefault());
315 writer.SetTimerForTesting(&timer);
316 DataSerializer foo("foo"), bar("bar"), baz("baz");
317 writer.ScheduleWrite(&foo);
318 writer.ScheduleWrite(&bar);
319 writer.ScheduleWrite(&baz);
320 ASSERT_TRUE(timer.IsRunning());
321 timer.Fire();
322 RunLoop().RunUntilIdle();
323 ASSERT_TRUE(PathExists(writer.path()));
324 EXPECT_EQ("baz", GetFileContent(writer.path()));
325 }
326
TEST_F(ImportantFileWriterTest,ScheduleWrite_FailToSerialize)327 TEST_F(ImportantFileWriterTest, ScheduleWrite_FailToSerialize) {
328 MockOneShotTimer timer;
329 ImportantFileWriter writer(file_,
330 SingleThreadTaskRunner::GetCurrentDefault());
331 writer.SetTimerForTesting(&timer);
332 EXPECT_FALSE(writer.HasPendingWrite());
333 FailingDataSerializer serializer;
334 writer.ScheduleWrite(&serializer);
335 EXPECT_TRUE(writer.HasPendingWrite());
336 ASSERT_TRUE(timer.IsRunning());
337 timer.Fire();
338 EXPECT_FALSE(writer.HasPendingWrite());
339 RunLoop().RunUntilIdle();
340 EXPECT_FALSE(PathExists(writer.path()));
341 }
342
TEST_F(ImportantFileWriterTest,ScheduleWrite_WriteNow)343 TEST_F(ImportantFileWriterTest, ScheduleWrite_WriteNow) {
344 MockOneShotTimer timer;
345 ImportantFileWriter writer(file_,
346 SingleThreadTaskRunner::GetCurrentDefault());
347 writer.SetTimerForTesting(&timer);
348 EXPECT_FALSE(writer.HasPendingWrite());
349 DataSerializer serializer("foo");
350 writer.ScheduleWrite(&serializer);
351 EXPECT_TRUE(writer.HasPendingWrite());
352 writer.WriteNow("bar");
353 EXPECT_FALSE(writer.HasPendingWrite());
354 EXPECT_FALSE(timer.IsRunning());
355
356 RunLoop().RunUntilIdle();
357 ASSERT_TRUE(PathExists(writer.path()));
358 EXPECT_EQ("bar", GetFileContent(writer.path()));
359 }
360
TEST_F(ImportantFileWriterTest,DoScheduledWrite_FailToSerialize)361 TEST_F(ImportantFileWriterTest, DoScheduledWrite_FailToSerialize) {
362 base::HistogramTester histogram_tester;
363 MockOneShotTimer timer;
364 ImportantFileWriter writer(file_,
365 SingleThreadTaskRunner::GetCurrentDefault());
366 writer.SetTimerForTesting(&timer);
367 EXPECT_FALSE(writer.HasPendingWrite());
368 FailingDataSerializer serializer;
369 writer.ScheduleWrite(&serializer);
370 EXPECT_TRUE(writer.HasPendingWrite());
371
372 writer.DoScheduledWrite();
373 EXPECT_FALSE(timer.IsRunning());
374 EXPECT_FALSE(writer.HasPendingWrite());
375 RunLoop().RunUntilIdle();
376 EXPECT_FALSE(PathExists(writer.path()));
377 // We don't record metrics in case the serialization fails.
378 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 0);
379 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration.All",
380 0);
381 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 0);
382 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration.All", 0);
383 }
384
TEST_F(ImportantFileWriterTest,ScheduleWriteWithBackgroundDataSerializer)385 TEST_F(ImportantFileWriterTest, ScheduleWriteWithBackgroundDataSerializer) {
386 base::HistogramTester histogram_tester;
387 base::Thread file_writer_thread("ImportantFileWriter test thread");
388 file_writer_thread.Start();
389 constexpr TimeDelta kCommitInterval = Seconds(12345);
390 MockOneShotTimer timer;
391 ImportantFileWriter writer(file_, file_writer_thread.task_runner(),
392 kCommitInterval);
393 EXPECT_EQ(0u, writer.previous_data_size());
394 writer.SetTimerForTesting(&timer);
395 EXPECT_FALSE(writer.HasPendingWrite());
396 ASSERT_FALSE(file_writer_thread.task_runner()->RunsTasksInCurrentSequence());
397 BackgroundDataSerializer serializer(
398 base::BindLambdaForTesting([&]() -> std::optional<std::string> {
399 EXPECT_TRUE(
400 file_writer_thread.task_runner()->RunsTasksInCurrentSequence());
401 return "foo";
402 }));
403 writer.ScheduleWriteWithBackgroundDataSerializer(&serializer);
404 EXPECT_TRUE(writer.HasPendingWrite());
405 EXPECT_FALSE(serializer.producer_callback_obtained());
406 ASSERT_TRUE(timer.IsRunning());
407 EXPECT_EQ(kCommitInterval, timer.GetCurrentDelay());
408
409 timer.Fire();
410 EXPECT_FALSE(writer.HasPendingWrite());
411 EXPECT_TRUE(serializer.producer_callback_obtained());
412 EXPECT_FALSE(timer.IsRunning());
413 file_writer_thread.FlushForTesting();
414 ASSERT_TRUE(PathExists(writer.path()));
415 EXPECT_EQ("foo", GetFileContent(writer.path()));
416 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 1);
417 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration.All",
418 1);
419 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 1);
420 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration.All", 1);
421 }
422
TEST_F(ImportantFileWriterTest,ScheduleWriteWithBackgroundDataSerializer_FailToSerialize)423 TEST_F(ImportantFileWriterTest,
424 ScheduleWriteWithBackgroundDataSerializer_FailToSerialize) {
425 base::HistogramTester histogram_tester;
426 base::Thread file_writer_thread("ImportantFileWriter test thread");
427 file_writer_thread.Start();
428 constexpr TimeDelta kCommitInterval = Seconds(12345);
429 MockOneShotTimer timer;
430 ImportantFileWriter writer(file_, file_writer_thread.task_runner(),
431 kCommitInterval);
432 EXPECT_EQ(0u, writer.previous_data_size());
433 writer.SetTimerForTesting(&timer);
434 EXPECT_FALSE(writer.HasPendingWrite());
435 ASSERT_FALSE(file_writer_thread.task_runner()->RunsTasksInCurrentSequence());
436 BackgroundDataSerializer serializer(
437 base::BindLambdaForTesting([&]() -> std::optional<std::string> {
438 EXPECT_TRUE(
439 file_writer_thread.task_runner()->RunsTasksInCurrentSequence());
440 return std::nullopt;
441 }));
442 writer.ScheduleWriteWithBackgroundDataSerializer(&serializer);
443 EXPECT_TRUE(writer.HasPendingWrite());
444 EXPECT_FALSE(serializer.producer_callback_obtained());
445 EXPECT_TRUE(timer.IsRunning());
446
447 timer.Fire();
448 EXPECT_FALSE(timer.IsRunning());
449 EXPECT_TRUE(serializer.producer_callback_obtained());
450 EXPECT_FALSE(writer.HasPendingWrite());
451 file_writer_thread.FlushForTesting();
452 EXPECT_FALSE(PathExists(writer.path()));
453 // We record the foreground serialization metric despite later failure in
454 // background sequence.
455 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 1);
456 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration.All",
457 1);
458 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 0);
459 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration.All", 0);
460 }
461
462 // Test that the chunking to avoid very large writes works.
TEST_F(ImportantFileWriterTest,WriteLargeFile)463 TEST_F(ImportantFileWriterTest, WriteLargeFile) {
464 // One byte larger than kMaxWriteAmount.
465 const std::string large_data(8 * 1024 * 1024 + 1, 'g');
466 EXPECT_FALSE(PathExists(file_));
467 EXPECT_TRUE(ImportantFileWriter::WriteFileAtomically(file_, large_data));
468 std::string actual;
469 EXPECT_TRUE(ReadFileToString(file_, &actual));
470 EXPECT_EQ(large_data, actual);
471 }
472
473 // Verify that a UMA metric for the serialization duration is recorded.
TEST_F(ImportantFileWriterTest,SerializationDuration)474 TEST_F(ImportantFileWriterTest, SerializationDuration) {
475 base::HistogramTester histogram_tester;
476 ImportantFileWriter writer(file_,
477 SingleThreadTaskRunner::GetCurrentDefault());
478 DataSerializer serializer("foo");
479 writer.ScheduleWrite(&serializer);
480 writer.DoScheduledWrite();
481 RunLoop().RunUntilIdle();
482 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 1);
483 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration.All",
484 1);
485 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 1);
486 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration.All", 1);
487 }
488
489 // Verify that a UMA metric for the serialization duration is recorded if the
490 // ImportantFileWriter has a custom histogram suffix.
TEST_F(ImportantFileWriterTest,SerializationDurationWithCustomSuffix)491 TEST_F(ImportantFileWriterTest, SerializationDurationWithCustomSuffix) {
492 base::HistogramTester histogram_tester;
493 ImportantFileWriter writer(file_, SingleThreadTaskRunner::GetCurrentDefault(),
494 "Foo");
495 DataSerializer serializer("foo");
496 writer.ScheduleWrite(&serializer);
497 writer.DoScheduledWrite();
498 RunLoop().RunUntilIdle();
499 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration.Foo",
500 1);
501 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration.Foo", 1);
502
503 // Should not be written to the unsuffixed ("unknown") histogram.
504 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 0);
505 histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration.All",
506 1);
507 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 0);
508 histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration.All", 1);
509 }
510
511 #if BUILDFLAG(IS_WIN)
512 // Tests that failures of ReplaceFile are handled. These don't call the OS
513 // ReplaceFile because they count the exact number of calls, which could be
514 // flaky if the test runs on a machine with file scanners.
TEST_F(ImportantFileWriterTest,ReplaceFileSuccess)515 TEST_F(ImportantFileWriterTest, ReplaceFileSuccess) {
516 base::HistogramTester histogram_tester;
517 ImportantFileWriter writer(file_,
518 SingleThreadTaskRunner::GetCurrentDefault());
519
520 // Unconditional success in ReplaceFile.
521 writer.SetReplaceFileCallbackForTesting(base::BindRepeating(
522 [](const FilePath&, const FilePath&, File::Error* error) {
523 *error = File::FILE_OK;
524 return true;
525 }));
526
527 DataSerializer serializer("foo");
528 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
529 base::RunLoop run_loop;
530 write_callback_observer_.ObserveNextWriteCallbacks(&writer,
531 run_loop.QuitClosure());
532 writer.WriteNow("foo");
533 run_loop.Run();
534
535 EXPECT_EQ(CALLED_WITH_SUCCESS,
536 write_callback_observer_.GetAndResetObservationState());
537
538 // 0 means no retries were needed.
539 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceRetryCount", 0,
540 1);
541
542 // FileReplaceRetryCount2 is only recorded if retries were needed.
543 histogram_tester.ExpectTotalCount("ImportantFile.FileReplaceRetryCount2", 0);
544 histogram_tester.ExpectTotalCount("ImportantFile.FileReplaceRetryCount2.All",
545 0);
546
547 // 0 means no retries were needed.
548 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceResult", 0, 1);
549 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceResult.All", 0,
550 1);
551 }
552
TEST_F(ImportantFileWriterTest,ReplaceFileRetry)553 TEST_F(ImportantFileWriterTest, ReplaceFileRetry) {
554 base::HistogramTester histogram_tester;
555 ImportantFileWriter writer(file_,
556 SingleThreadTaskRunner::GetCurrentDefault());
557
558 // Fake a failure on the first two calls to ReplaceFile.
559 size_t retry_count = 0;
560 writer.SetReplaceFileCallbackForTesting(base::BindLambdaForTesting(
561 [&retry_count](const FilePath&, const FilePath&, File::Error* error) {
562 if (retry_count < 2) {
563 retry_count += 1;
564 *error = File::FILE_ERROR_IN_USE;
565 return false;
566 }
567 *error = File::FILE_OK;
568 return true;
569 }));
570
571 DataSerializer serializer("foo");
572 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
573 base::RunLoop run_loop;
574 write_callback_observer_.ObserveNextWriteCallbacks(&writer,
575 run_loop.QuitClosure());
576 writer.WriteNow("foo");
577 run_loop.Run();
578
579 EXPECT_EQ(CALLED_WITH_SUCCESS,
580 write_callback_observer_.GetAndResetObservationState());
581 EXPECT_EQ(retry_count, 2u);
582
583 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceRetryCount", 2,
584 1);
585
586 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceRetryCount2", 2,
587 1);
588 histogram_tester.ExpectUniqueSample(
589 "ImportantFile.FileReplaceRetryCount2.All", 2, 1);
590
591 // 1 means succeeded with retries.
592 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceResult", 1, 1);
593 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceResult.All", 1,
594 1);
595 }
596
TEST_F(ImportantFileWriterTest,ReplaceFileFails)597 TEST_F(ImportantFileWriterTest, ReplaceFileFails) {
598 base::HistogramTester histogram_tester;
599 ImportantFileWriter writer(file_,
600 SingleThreadTaskRunner::GetCurrentDefault());
601
602 // Unconditional failure in ReplaceFile.
603 writer.SetReplaceFileCallbackForTesting(base::BindRepeating(
604 [](const FilePath&, const FilePath&, File::Error* error) {
605 *error = File::FILE_ERROR_IN_USE;
606 return false;
607 }));
608
609 DataSerializer serializer("foo");
610 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
611 base::RunLoop run_loop;
612 write_callback_observer_.ObserveNextWriteCallbacks(&writer,
613 run_loop.QuitClosure());
614 writer.WriteNow("foo");
615 run_loop.Run();
616
617 EXPECT_EQ(CALLED_WITH_ERROR,
618 write_callback_observer_.GetAndResetObservationState());
619 // 10 means ReplaceFile never succeeded.
620 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceRetryCount", 10,
621 1);
622
623 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceRetryCount2", 5,
624 1);
625 histogram_tester.ExpectUniqueSample(
626 "ImportantFile.FileReplaceRetryCount2.All", 5, 1);
627
628 // 2 means ReplaceFile never succeeded.
629 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceResult", 2, 1);
630 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceResult.All", 2,
631 1);
632 }
633
TEST_F(ImportantFileWriterTest,ReplaceFileFailsWithSuffix)634 TEST_F(ImportantFileWriterTest, ReplaceFileFailsWithSuffix) {
635 base::HistogramTester histogram_tester;
636 ImportantFileWriter writer(file_, SingleThreadTaskRunner::GetCurrentDefault(),
637 "Foo");
638
639 // Unconditional failure in ReplaceFile.
640 writer.SetReplaceFileCallbackForTesting(base::BindRepeating(
641 [](const FilePath&, const FilePath&, File::Error* error) {
642 *error = File::FILE_ERROR_IN_USE;
643 return false;
644 }));
645
646 DataSerializer serializer("foo");
647 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
648 base::RunLoop run_loop;
649 write_callback_observer_.ObserveNextWriteCallbacks(&writer,
650 run_loop.QuitClosure());
651 writer.WriteNow("foo");
652 run_loop.Run();
653
654 EXPECT_EQ(CALLED_WITH_ERROR,
655 write_callback_observer_.GetAndResetObservationState());
656
657 // 10 means ReplaceFile never succeeded.
658 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceRetryCount", 10,
659 1);
660
661 histogram_tester.ExpectUniqueSample(
662 "ImportantFile.FileReplaceRetryCount2.Foo", 5, 1);
663 histogram_tester.ExpectUniqueSample(
664 "ImportantFile.FileReplaceRetryCount2.All", 5, 1);
665
666 // 2 means ReplaceFile never succeeded.
667 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceResult.Foo", 2,
668 1);
669 histogram_tester.ExpectUniqueSample("ImportantFile.FileReplaceResult.All", 2,
670 1);
671 }
672 #endif
673
674 } // namespace base
675