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