• 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 "base/compiler_specific.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/functional/bind.h"
12 #include "base/location.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/notreached.h"
15 #include "base/run_loop.h"
16 #include "base/sequence_checker.h"
17 #include "base/task/single_thread_task_runner.h"
18 #include "base/test/bind.h"
19 #include "base/test/metrics/histogram_tester.h"
20 #include "base/test/task_environment.h"
21 #include "base/threading/thread.h"
22 #include "base/time/time.h"
23 #include "base/timer/mock_timer.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "third_party/abseil-cpp/absl/types/optional.h"
26 
27 namespace base {
28 
29 namespace {
30 
GetFileContent(const FilePath & path)31 std::string GetFileContent(const FilePath& path) {
32   std::string content;
33   if (!ReadFileToString(path, &content)) {
34     NOTREACHED();
35   }
36   return content;
37 }
38 
39 class DataSerializer : public ImportantFileWriter::DataSerializer {
40  public:
DataSerializer(const std::string & data)41   explicit DataSerializer(const std::string& data) : data_(data) {
42   }
43 
SerializeData()44   absl::optional<std::string> SerializeData() override {
45     EXPECT_TRUE(sequence_checker_.CalledOnValidSequence());
46     return data_;
47   }
48 
49  private:
50   const base::SequenceChecker sequence_checker_;
51   const std::string data_;
52 };
53 
54 class FailingDataSerializer : public ImportantFileWriter::DataSerializer {
55  public:
SerializeData()56   absl::optional<std::string> SerializeData() override { return absl::nullopt; }
57 };
58 
59 class BackgroundDataSerializer
60     : public ImportantFileWriter::BackgroundDataSerializer {
61  public:
BackgroundDataSerializer(ImportantFileWriter::BackgroundDataProducerCallback data_producer_callback)62   explicit BackgroundDataSerializer(
63       ImportantFileWriter::BackgroundDataProducerCallback
64           data_producer_callback)
65       : data_producer_callback_(std::move(data_producer_callback)) {
66     DCHECK(data_producer_callback_);
67   }
68 
69   ImportantFileWriter::BackgroundDataProducerCallback
GetSerializedDataProducerForBackgroundSequence()70   GetSerializedDataProducerForBackgroundSequence() override {
71     EXPECT_TRUE(sequence_checker_.CalledOnValidSequence());
72     return std::move(data_producer_callback_);
73   }
74 
producer_callback_obtained() const75   bool producer_callback_obtained() const {
76     return data_producer_callback_.is_null();
77   }
78 
79  private:
80   const base::SequenceChecker sequence_checker_;
81   ImportantFileWriter::BackgroundDataProducerCallback data_producer_callback_;
82 };
83 
84 enum WriteCallbackObservationState {
85   NOT_CALLED,
86   CALLED_WITH_ERROR,
87   CALLED_WITH_SUCCESS,
88 };
89 
90 class WriteCallbacksObserver {
91  public:
92   WriteCallbacksObserver() = default;
93   WriteCallbacksObserver(const WriteCallbacksObserver&) = delete;
94   WriteCallbacksObserver& operator=(const WriteCallbacksObserver&) = delete;
95 
96   // Register OnBeforeWrite() and OnAfterWrite() to be called on the next write
97   // of |writer|.
98   void ObserveNextWriteCallbacks(ImportantFileWriter* writer);
99 
100   // Returns the |WriteCallbackObservationState| which was observed, then resets
101   // it to |NOT_CALLED|.
102   WriteCallbackObservationState GetAndResetObservationState();
103 
104  private:
OnBeforeWrite()105   void OnBeforeWrite() {
106     EXPECT_FALSE(before_write_called_);
107     before_write_called_ = true;
108   }
109 
OnAfterWrite(bool success)110   void OnAfterWrite(bool success) {
111     EXPECT_EQ(NOT_CALLED, after_write_observation_state_);
112     after_write_observation_state_ =
113         success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR;
114   }
115 
116   bool before_write_called_ = false;
117   WriteCallbackObservationState after_write_observation_state_ = NOT_CALLED;
118 };
119 
ObserveNextWriteCallbacks(ImportantFileWriter * writer)120 void WriteCallbacksObserver::ObserveNextWriteCallbacks(
121     ImportantFileWriter* writer) {
122   writer->RegisterOnNextWriteCallbacks(
123       base::BindOnce(&WriteCallbacksObserver::OnBeforeWrite,
124                      base::Unretained(this)),
125       base::BindOnce(&WriteCallbacksObserver::OnAfterWrite,
126                      base::Unretained(this)));
127 }
128 
129 WriteCallbackObservationState
GetAndResetObservationState()130 WriteCallbacksObserver::GetAndResetObservationState() {
131   EXPECT_EQ(after_write_observation_state_ != NOT_CALLED, before_write_called_)
132       << "The before-write callback should always be called before the "
133          "after-write callback";
134 
135   WriteCallbackObservationState state = after_write_observation_state_;
136   before_write_called_ = false;
137   after_write_observation_state_ = NOT_CALLED;
138   return state;
139 }
140 
141 }  // namespace
142 
143 class ImportantFileWriterTest : public testing::Test {
144  public:
145   ImportantFileWriterTest() = default;
SetUp()146   void SetUp() override {
147     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
148     file_ = temp_dir_.GetPath().AppendASCII("test-file");
149   }
150 
151  protected:
152   WriteCallbacksObserver write_callback_observer_;
153   FilePath file_;
154   test::TaskEnvironment task_environment_;
155 
156  private:
157   ScopedTempDir temp_dir_;
158 };
159 
TEST_F(ImportantFileWriterTest,Basic)160 TEST_F(ImportantFileWriterTest, Basic) {
161   ImportantFileWriter writer(file_,
162                              SingleThreadTaskRunner::GetCurrentDefault());
163   EXPECT_FALSE(PathExists(writer.path()));
164   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
165   writer.WriteNow("foo");
166   RunLoop().RunUntilIdle();
167 
168   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
169   ASSERT_TRUE(PathExists(writer.path()));
170   EXPECT_EQ("foo", GetFileContent(writer.path()));
171 }
172 
TEST_F(ImportantFileWriterTest,WriteWithObserver)173 TEST_F(ImportantFileWriterTest, WriteWithObserver) {
174   ImportantFileWriter writer(file_,
175                              SingleThreadTaskRunner::GetCurrentDefault());
176   EXPECT_FALSE(PathExists(writer.path()));
177   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
178 
179   // Confirm that the observer is invoked.
180   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
181   writer.WriteNow("foo");
182   RunLoop().RunUntilIdle();
183 
184   EXPECT_EQ(CALLED_WITH_SUCCESS,
185             write_callback_observer_.GetAndResetObservationState());
186   ASSERT_TRUE(PathExists(writer.path()));
187   EXPECT_EQ("foo", GetFileContent(writer.path()));
188 
189   // Confirm that re-installing the observer works for another write.
190   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
191   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
192   writer.WriteNow("bar");
193   RunLoop().RunUntilIdle();
194 
195   EXPECT_EQ(CALLED_WITH_SUCCESS,
196             write_callback_observer_.GetAndResetObservationState());
197   ASSERT_TRUE(PathExists(writer.path()));
198   EXPECT_EQ("bar", GetFileContent(writer.path()));
199 
200   // Confirm that writing again without re-installing the observer doesn't
201   // result in a notification.
202   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
203   writer.WriteNow("baz");
204   RunLoop().RunUntilIdle();
205 
206   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
207   ASSERT_TRUE(PathExists(writer.path()));
208   EXPECT_EQ("baz", GetFileContent(writer.path()));
209 }
210 
TEST_F(ImportantFileWriterTest,FailedWriteWithObserver)211 TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) {
212   // Use an invalid file path (relative paths are invalid) to get a
213   // FILE_ERROR_ACCESS_DENIED error when trying to write the file.
214   ImportantFileWriter writer(FilePath().AppendASCII("bad/../path"),
215                              SingleThreadTaskRunner::GetCurrentDefault());
216   EXPECT_FALSE(PathExists(writer.path()));
217   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
218   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
219   writer.WriteNow("foo");
220   RunLoop().RunUntilIdle();
221 
222   // Confirm that the write observer was invoked with its boolean parameter set
223   // to false.
224   EXPECT_EQ(CALLED_WITH_ERROR,
225             write_callback_observer_.GetAndResetObservationState());
226   EXPECT_FALSE(PathExists(writer.path()));
227 }
228 
TEST_F(ImportantFileWriterTest,CallbackRunsOnWriterThread)229 TEST_F(ImportantFileWriterTest, CallbackRunsOnWriterThread) {
230   base::Thread file_writer_thread("ImportantFileWriter test thread");
231   file_writer_thread.Start();
232   ImportantFileWriter writer(file_, file_writer_thread.task_runner());
233   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
234 
235   // Block execution on |file_writer_thread| to verify that callbacks are
236   // executed on it.
237   base::WaitableEvent wait_helper(
238       base::WaitableEvent::ResetPolicy::MANUAL,
239       base::WaitableEvent::InitialState::NOT_SIGNALED);
240   file_writer_thread.task_runner()->PostTask(
241       FROM_HERE, base::BindOnce(&base::WaitableEvent::Wait,
242                                 base::Unretained(&wait_helper)));
243 
244   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
245   writer.WriteNow("foo");
246   RunLoop().RunUntilIdle();
247 
248   // Expect the callback to not have been executed before the
249   // |file_writer_thread| is unblocked.
250   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
251 
252   wait_helper.Signal();
253   file_writer_thread.FlushForTesting();
254 
255   EXPECT_EQ(CALLED_WITH_SUCCESS,
256             write_callback_observer_.GetAndResetObservationState());
257   ASSERT_TRUE(PathExists(writer.path()));
258   EXPECT_EQ("foo", GetFileContent(writer.path()));
259 }
260 
TEST_F(ImportantFileWriterTest,ScheduleWrite)261 TEST_F(ImportantFileWriterTest, ScheduleWrite) {
262   constexpr TimeDelta kCommitInterval = Seconds(12345);
263   MockOneShotTimer timer;
264   ImportantFileWriter writer(file_, SingleThreadTaskRunner::GetCurrentDefault(),
265                              kCommitInterval);
266   EXPECT_EQ(0u, writer.previous_data_size());
267   writer.SetTimerForTesting(&timer);
268   EXPECT_FALSE(writer.HasPendingWrite());
269   DataSerializer serializer("foo");
270   writer.ScheduleWrite(&serializer);
271   EXPECT_TRUE(writer.HasPendingWrite());
272   ASSERT_TRUE(timer.IsRunning());
273   EXPECT_EQ(kCommitInterval, timer.GetCurrentDelay());
274   timer.Fire();
275   EXPECT_FALSE(writer.HasPendingWrite());
276   EXPECT_FALSE(timer.IsRunning());
277   RunLoop().RunUntilIdle();
278   ASSERT_TRUE(PathExists(writer.path()));
279   EXPECT_EQ("foo", GetFileContent(writer.path()));
280   EXPECT_EQ(3u, writer.previous_data_size());
281 }
282 
TEST_F(ImportantFileWriterTest,DoScheduledWrite)283 TEST_F(ImportantFileWriterTest, DoScheduledWrite) {
284   MockOneShotTimer timer;
285   ImportantFileWriter writer(file_,
286                              SingleThreadTaskRunner::GetCurrentDefault());
287   writer.SetTimerForTesting(&timer);
288   EXPECT_FALSE(writer.HasPendingWrite());
289   DataSerializer serializer("foo");
290   writer.ScheduleWrite(&serializer);
291   EXPECT_TRUE(writer.HasPendingWrite());
292   writer.DoScheduledWrite();
293   EXPECT_FALSE(writer.HasPendingWrite());
294   RunLoop().RunUntilIdle();
295   ASSERT_TRUE(PathExists(writer.path()));
296   EXPECT_EQ("foo", GetFileContent(writer.path()));
297 }
298 
TEST_F(ImportantFileWriterTest,BatchingWrites)299 TEST_F(ImportantFileWriterTest, BatchingWrites) {
300   MockOneShotTimer timer;
301   ImportantFileWriter writer(file_,
302                              SingleThreadTaskRunner::GetCurrentDefault());
303   writer.SetTimerForTesting(&timer);
304   DataSerializer foo("foo"), bar("bar"), baz("baz");
305   writer.ScheduleWrite(&foo);
306   writer.ScheduleWrite(&bar);
307   writer.ScheduleWrite(&baz);
308   ASSERT_TRUE(timer.IsRunning());
309   timer.Fire();
310   RunLoop().RunUntilIdle();
311   ASSERT_TRUE(PathExists(writer.path()));
312   EXPECT_EQ("baz", GetFileContent(writer.path()));
313 }
314 
TEST_F(ImportantFileWriterTest,ScheduleWrite_FailToSerialize)315 TEST_F(ImportantFileWriterTest, ScheduleWrite_FailToSerialize) {
316   MockOneShotTimer timer;
317   ImportantFileWriter writer(file_,
318                              SingleThreadTaskRunner::GetCurrentDefault());
319   writer.SetTimerForTesting(&timer);
320   EXPECT_FALSE(writer.HasPendingWrite());
321   FailingDataSerializer serializer;
322   writer.ScheduleWrite(&serializer);
323   EXPECT_TRUE(writer.HasPendingWrite());
324   ASSERT_TRUE(timer.IsRunning());
325   timer.Fire();
326   EXPECT_FALSE(writer.HasPendingWrite());
327   RunLoop().RunUntilIdle();
328   EXPECT_FALSE(PathExists(writer.path()));
329 }
330 
TEST_F(ImportantFileWriterTest,ScheduleWrite_WriteNow)331 TEST_F(ImportantFileWriterTest, ScheduleWrite_WriteNow) {
332   MockOneShotTimer timer;
333   ImportantFileWriter writer(file_,
334                              SingleThreadTaskRunner::GetCurrentDefault());
335   writer.SetTimerForTesting(&timer);
336   EXPECT_FALSE(writer.HasPendingWrite());
337   DataSerializer serializer("foo");
338   writer.ScheduleWrite(&serializer);
339   EXPECT_TRUE(writer.HasPendingWrite());
340   writer.WriteNow("bar");
341   EXPECT_FALSE(writer.HasPendingWrite());
342   EXPECT_FALSE(timer.IsRunning());
343 
344   RunLoop().RunUntilIdle();
345   ASSERT_TRUE(PathExists(writer.path()));
346   EXPECT_EQ("bar", GetFileContent(writer.path()));
347 }
348 
TEST_F(ImportantFileWriterTest,DoScheduledWrite_FailToSerialize)349 TEST_F(ImportantFileWriterTest, DoScheduledWrite_FailToSerialize) {
350   base::HistogramTester histogram_tester;
351   MockOneShotTimer timer;
352   ImportantFileWriter writer(file_,
353                              SingleThreadTaskRunner::GetCurrentDefault());
354   writer.SetTimerForTesting(&timer);
355   EXPECT_FALSE(writer.HasPendingWrite());
356   FailingDataSerializer serializer;
357   writer.ScheduleWrite(&serializer);
358   EXPECT_TRUE(writer.HasPendingWrite());
359 
360   writer.DoScheduledWrite();
361   EXPECT_FALSE(timer.IsRunning());
362   EXPECT_FALSE(writer.HasPendingWrite());
363   RunLoop().RunUntilIdle();
364   EXPECT_FALSE(PathExists(writer.path()));
365   // We don't record metrics in case the serialization fails.
366   histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 0);
367   histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 0);
368 }
369 
TEST_F(ImportantFileWriterTest,ScheduleWriteWithBackgroundDataSerializer)370 TEST_F(ImportantFileWriterTest, ScheduleWriteWithBackgroundDataSerializer) {
371   base::HistogramTester histogram_tester;
372   base::Thread file_writer_thread("ImportantFileWriter test thread");
373   file_writer_thread.Start();
374   constexpr TimeDelta kCommitInterval = Seconds(12345);
375   MockOneShotTimer timer;
376   ImportantFileWriter writer(file_, file_writer_thread.task_runner(),
377                              kCommitInterval);
378   EXPECT_EQ(0u, writer.previous_data_size());
379   writer.SetTimerForTesting(&timer);
380   EXPECT_FALSE(writer.HasPendingWrite());
381   ASSERT_FALSE(file_writer_thread.task_runner()->RunsTasksInCurrentSequence());
382   BackgroundDataSerializer serializer(
383       base::BindLambdaForTesting([&]() -> absl::optional<std::string> {
384         EXPECT_TRUE(
385             file_writer_thread.task_runner()->RunsTasksInCurrentSequence());
386         return "foo";
387       }));
388   writer.ScheduleWriteWithBackgroundDataSerializer(&serializer);
389   EXPECT_TRUE(writer.HasPendingWrite());
390   EXPECT_FALSE(serializer.producer_callback_obtained());
391   ASSERT_TRUE(timer.IsRunning());
392   EXPECT_EQ(kCommitInterval, timer.GetCurrentDelay());
393 
394   timer.Fire();
395   EXPECT_FALSE(writer.HasPendingWrite());
396   EXPECT_TRUE(serializer.producer_callback_obtained());
397   EXPECT_FALSE(timer.IsRunning());
398   file_writer_thread.FlushForTesting();
399   ASSERT_TRUE(PathExists(writer.path()));
400   EXPECT_EQ("foo", GetFileContent(writer.path()));
401   histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 1);
402   histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 1);
403 }
404 
TEST_F(ImportantFileWriterTest,ScheduleWriteWithBackgroundDataSerializer_FailToSerialize)405 TEST_F(ImportantFileWriterTest,
406        ScheduleWriteWithBackgroundDataSerializer_FailToSerialize) {
407   base::HistogramTester histogram_tester;
408   base::Thread file_writer_thread("ImportantFileWriter test thread");
409   file_writer_thread.Start();
410   constexpr TimeDelta kCommitInterval = Seconds(12345);
411   MockOneShotTimer timer;
412   ImportantFileWriter writer(file_, file_writer_thread.task_runner(),
413                              kCommitInterval);
414   EXPECT_EQ(0u, writer.previous_data_size());
415   writer.SetTimerForTesting(&timer);
416   EXPECT_FALSE(writer.HasPendingWrite());
417   ASSERT_FALSE(file_writer_thread.task_runner()->RunsTasksInCurrentSequence());
418   BackgroundDataSerializer serializer(
419       base::BindLambdaForTesting([&]() -> absl::optional<std::string> {
420         EXPECT_TRUE(
421             file_writer_thread.task_runner()->RunsTasksInCurrentSequence());
422         return absl::nullopt;
423       }));
424   writer.ScheduleWriteWithBackgroundDataSerializer(&serializer);
425   EXPECT_TRUE(writer.HasPendingWrite());
426   EXPECT_FALSE(serializer.producer_callback_obtained());
427   EXPECT_TRUE(timer.IsRunning());
428 
429   timer.Fire();
430   EXPECT_FALSE(timer.IsRunning());
431   EXPECT_TRUE(serializer.producer_callback_obtained());
432   EXPECT_FALSE(writer.HasPendingWrite());
433   file_writer_thread.FlushForTesting();
434   EXPECT_FALSE(PathExists(writer.path()));
435   // We record the foreground serialization metric despite later failure in
436   // background sequence.
437   histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 1);
438   histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 0);
439 }
440 
441 // Test that the chunking to avoid very large writes works.
TEST_F(ImportantFileWriterTest,WriteLargeFile)442 TEST_F(ImportantFileWriterTest, WriteLargeFile) {
443   // One byte larger than kMaxWriteAmount.
444   const std::string large_data(8 * 1024 * 1024 + 1, 'g');
445   EXPECT_FALSE(PathExists(file_));
446   EXPECT_TRUE(ImportantFileWriter::WriteFileAtomically(file_, large_data));
447   std::string actual;
448   EXPECT_TRUE(ReadFileToString(file_, &actual));
449   EXPECT_EQ(large_data, actual);
450 }
451 
452 // Verify that a UMA metric for the serialization duration is recorded.
TEST_F(ImportantFileWriterTest,SerializationDuration)453 TEST_F(ImportantFileWriterTest, SerializationDuration) {
454   base::HistogramTester histogram_tester;
455   ImportantFileWriter writer(file_,
456                              SingleThreadTaskRunner::GetCurrentDefault());
457   DataSerializer serializer("foo");
458   writer.ScheduleWrite(&serializer);
459   writer.DoScheduledWrite();
460   RunLoop().RunUntilIdle();
461   histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration", 1);
462   histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration", 1);
463 }
464 
465 // Verify that a UMA metric for the serialization duration is recorded if the
466 // ImportantFileWriter has a custom histogram suffix.
TEST_F(ImportantFileWriterTest,SerializationDurationWithCustomSuffix)467 TEST_F(ImportantFileWriterTest, SerializationDurationWithCustomSuffix) {
468   base::HistogramTester histogram_tester;
469   ImportantFileWriter writer(file_, SingleThreadTaskRunner::GetCurrentDefault(),
470                              "Foo");
471   DataSerializer serializer("foo");
472   writer.ScheduleWrite(&serializer);
473   writer.DoScheduledWrite();
474   RunLoop().RunUntilIdle();
475   histogram_tester.ExpectTotalCount("ImportantFile.SerializationDuration.Foo",
476                                     1);
477   histogram_tester.ExpectTotalCount("ImportantFile.WriteDuration.Foo", 1);
478 }
479 
480 }  // namespace base
481