• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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