1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/files/important_file_writer.h"
6 
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/run_loop.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/test/metrics/histogram_tester.h"
20 #include "base/threading/thread.h"
21 #include "base/threading/thread_task_runner_handle.h"
22 #include "base/time/time.h"
23 #include "base/timer/mock_timer.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 namespace base {
27 
28 namespace {
29 
GetFileContent(const FilePath & path)30 std::string GetFileContent(const FilePath& path) {
31   std::string content;
32   if (!ReadFileToString(path, &content)) {
33     NOTREACHED();
34   }
35   return content;
36 }
37 
38 class DataSerializer : public ImportantFileWriter::DataSerializer {
39  public:
DataSerializer(const std::string & data)40   explicit DataSerializer(const std::string& data) : data_(data) {
41   }
42 
SerializeData(std::string * output)43   bool SerializeData(std::string* output) override {
44     output->assign(data_);
45     return true;
46   }
47 
48  private:
49   const std::string data_;
50 };
51 
52 class FailingDataSerializer : public ImportantFileWriter::DataSerializer {
53  public:
SerializeData(std::string * output)54   bool SerializeData(std::string* output) override { return false; }
55 };
56 
57 enum WriteCallbackObservationState {
58   NOT_CALLED,
59   CALLED_WITH_ERROR,
60   CALLED_WITH_SUCCESS,
61 };
62 
63 class WriteCallbacksObserver {
64  public:
65   WriteCallbacksObserver() = default;
66 
67   // Register OnBeforeWrite() and OnAfterWrite() to be called on the next write
68   // of |writer|.
69   void ObserveNextWriteCallbacks(ImportantFileWriter* writer);
70 
71   // Returns the |WriteCallbackObservationState| which was observed, then resets
72   // it to |NOT_CALLED|.
73   WriteCallbackObservationState GetAndResetObservationState();
74 
75  private:
OnBeforeWrite()76   void OnBeforeWrite() {
77     EXPECT_FALSE(before_write_called_);
78     before_write_called_ = true;
79   }
80 
OnAfterWrite(bool success)81   void OnAfterWrite(bool success) {
82     EXPECT_EQ(NOT_CALLED, after_write_observation_state_);
83     after_write_observation_state_ =
84         success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR;
85   }
86 
87   bool before_write_called_ = false;
88   WriteCallbackObservationState after_write_observation_state_ = NOT_CALLED;
89 
90   DISALLOW_COPY_AND_ASSIGN(WriteCallbacksObserver);
91 };
92 
ObserveNextWriteCallbacks(ImportantFileWriter * writer)93 void WriteCallbacksObserver::ObserveNextWriteCallbacks(
94     ImportantFileWriter* writer) {
95   writer->RegisterOnNextWriteCallbacks(
96       base::Bind(&WriteCallbacksObserver::OnBeforeWrite,
97                  base::Unretained(this)),
98       base::Bind(&WriteCallbacksObserver::OnAfterWrite,
99                  base::Unretained(this)));
100 }
101 
102 WriteCallbackObservationState
GetAndResetObservationState()103 WriteCallbacksObserver::GetAndResetObservationState() {
104   EXPECT_EQ(after_write_observation_state_ != NOT_CALLED, before_write_called_)
105       << "The before-write callback should always be called before the "
106          "after-write callback";
107 
108   WriteCallbackObservationState state = after_write_observation_state_;
109   before_write_called_ = false;
110   after_write_observation_state_ = NOT_CALLED;
111   return state;
112 }
113 
114 }  // namespace
115 
116 class ImportantFileWriterTest : public testing::Test {
117  public:
118   ImportantFileWriterTest() = default;
SetUp()119   void SetUp() override {
120     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
121     file_ = temp_dir_.GetPath().AppendASCII("test-file");
122   }
123 
124  protected:
125   WriteCallbacksObserver write_callback_observer_;
126   FilePath file_;
127   MessageLoop loop_;
128 
129  private:
130   ScopedTempDir temp_dir_;
131 };
132 
TEST_F(ImportantFileWriterTest,Basic)133 TEST_F(ImportantFileWriterTest, Basic) {
134   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
135   EXPECT_FALSE(PathExists(writer.path()));
136   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
137   writer.WriteNow(std::make_unique<std::string>("foo"));
138   RunLoop().RunUntilIdle();
139 
140   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
141   ASSERT_TRUE(PathExists(writer.path()));
142   EXPECT_EQ("foo", GetFileContent(writer.path()));
143 }
144 
TEST_F(ImportantFileWriterTest,WriteWithObserver)145 TEST_F(ImportantFileWriterTest, WriteWithObserver) {
146   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
147   EXPECT_FALSE(PathExists(writer.path()));
148   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
149 
150   // Confirm that the observer is invoked.
151   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
152   writer.WriteNow(std::make_unique<std::string>("foo"));
153   RunLoop().RunUntilIdle();
154 
155   EXPECT_EQ(CALLED_WITH_SUCCESS,
156             write_callback_observer_.GetAndResetObservationState());
157   ASSERT_TRUE(PathExists(writer.path()));
158   EXPECT_EQ("foo", GetFileContent(writer.path()));
159 
160   // Confirm that re-installing the observer works for another write.
161   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
162   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
163   writer.WriteNow(std::make_unique<std::string>("bar"));
164   RunLoop().RunUntilIdle();
165 
166   EXPECT_EQ(CALLED_WITH_SUCCESS,
167             write_callback_observer_.GetAndResetObservationState());
168   ASSERT_TRUE(PathExists(writer.path()));
169   EXPECT_EQ("bar", GetFileContent(writer.path()));
170 
171   // Confirm that writing again without re-installing the observer doesn't
172   // result in a notification.
173   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
174   writer.WriteNow(std::make_unique<std::string>("baz"));
175   RunLoop().RunUntilIdle();
176 
177   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
178   ASSERT_TRUE(PathExists(writer.path()));
179   EXPECT_EQ("baz", GetFileContent(writer.path()));
180 }
181 
TEST_F(ImportantFileWriterTest,FailedWriteWithObserver)182 TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) {
183   // Use an invalid file path (relative paths are invalid) to get a
184   // FILE_ERROR_ACCESS_DENIED error when trying to write the file.
185   ImportantFileWriter writer(FilePath().AppendASCII("bad/../path"),
186                              ThreadTaskRunnerHandle::Get());
187   EXPECT_FALSE(PathExists(writer.path()));
188   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
189   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
190   writer.WriteNow(std::make_unique<std::string>("foo"));
191   RunLoop().RunUntilIdle();
192 
193   // Confirm that the write observer was invoked with its boolean parameter set
194   // to false.
195   EXPECT_EQ(CALLED_WITH_ERROR,
196             write_callback_observer_.GetAndResetObservationState());
197   EXPECT_FALSE(PathExists(writer.path()));
198 }
199 
TEST_F(ImportantFileWriterTest,CallbackRunsOnWriterThread)200 TEST_F(ImportantFileWriterTest, CallbackRunsOnWriterThread) {
201   base::Thread file_writer_thread("ImportantFileWriter test thread");
202   file_writer_thread.Start();
203   ImportantFileWriter writer(file_, file_writer_thread.task_runner());
204   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
205 
206   // Block execution on |file_writer_thread| to verify that callbacks are
207   // executed on it.
208   base::WaitableEvent wait_helper(
209       base::WaitableEvent::ResetPolicy::MANUAL,
210       base::WaitableEvent::InitialState::NOT_SIGNALED);
211   file_writer_thread.task_runner()->PostTask(
212       FROM_HERE, base::BindOnce(&base::WaitableEvent::Wait,
213                                 base::Unretained(&wait_helper)));
214 
215   write_callback_observer_.ObserveNextWriteCallbacks(&writer);
216   writer.WriteNow(std::make_unique<std::string>("foo"));
217   RunLoop().RunUntilIdle();
218 
219   // Expect the callback to not have been executed before the
220   // |file_writer_thread| is unblocked.
221   EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
222 
223   wait_helper.Signal();
224   file_writer_thread.FlushForTesting();
225 
226   EXPECT_EQ(CALLED_WITH_SUCCESS,
227             write_callback_observer_.GetAndResetObservationState());
228   ASSERT_TRUE(PathExists(writer.path()));
229   EXPECT_EQ("foo", GetFileContent(writer.path()));
230 }
231 
TEST_F(ImportantFileWriterTest,ScheduleWrite)232 TEST_F(ImportantFileWriterTest, ScheduleWrite) {
233   constexpr TimeDelta kCommitInterval = TimeDelta::FromSeconds(12345);
234   MockOneShotTimer timer;
235   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get(),
236                              kCommitInterval);
237   writer.SetTimerForTesting(&timer);
238   EXPECT_FALSE(writer.HasPendingWrite());
239   DataSerializer serializer("foo");
240   writer.ScheduleWrite(&serializer);
241   EXPECT_TRUE(writer.HasPendingWrite());
242   ASSERT_TRUE(timer.IsRunning());
243   EXPECT_EQ(kCommitInterval, timer.GetCurrentDelay());
244   timer.Fire();
245   EXPECT_FALSE(writer.HasPendingWrite());
246   EXPECT_FALSE(timer.IsRunning());
247   RunLoop().RunUntilIdle();
248   ASSERT_TRUE(PathExists(writer.path()));
249   EXPECT_EQ("foo", GetFileContent(writer.path()));
250 }
251 
TEST_F(ImportantFileWriterTest,DoScheduledWrite)252 TEST_F(ImportantFileWriterTest, DoScheduledWrite) {
253   MockOneShotTimer timer;
254   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
255   writer.SetTimerForTesting(&timer);
256   EXPECT_FALSE(writer.HasPendingWrite());
257   DataSerializer serializer("foo");
258   writer.ScheduleWrite(&serializer);
259   EXPECT_TRUE(writer.HasPendingWrite());
260   writer.DoScheduledWrite();
261   EXPECT_FALSE(writer.HasPendingWrite());
262   RunLoop().RunUntilIdle();
263   ASSERT_TRUE(PathExists(writer.path()));
264   EXPECT_EQ("foo", GetFileContent(writer.path()));
265 }
266 
TEST_F(ImportantFileWriterTest,BatchingWrites)267 TEST_F(ImportantFileWriterTest, BatchingWrites) {
268   MockOneShotTimer timer;
269   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
270   writer.SetTimerForTesting(&timer);
271   DataSerializer foo("foo"), bar("bar"), baz("baz");
272   writer.ScheduleWrite(&foo);
273   writer.ScheduleWrite(&bar);
274   writer.ScheduleWrite(&baz);
275   ASSERT_TRUE(timer.IsRunning());
276   timer.Fire();
277   RunLoop().RunUntilIdle();
278   ASSERT_TRUE(PathExists(writer.path()));
279   EXPECT_EQ("baz", GetFileContent(writer.path()));
280 }
281 
TEST_F(ImportantFileWriterTest,ScheduleWrite_FailToSerialize)282 TEST_F(ImportantFileWriterTest, ScheduleWrite_FailToSerialize) {
283   MockOneShotTimer timer;
284   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
285   writer.SetTimerForTesting(&timer);
286   EXPECT_FALSE(writer.HasPendingWrite());
287   FailingDataSerializer serializer;
288   writer.ScheduleWrite(&serializer);
289   EXPECT_TRUE(writer.HasPendingWrite());
290   ASSERT_TRUE(timer.IsRunning());
291   timer.Fire();
292   EXPECT_FALSE(writer.HasPendingWrite());
293   RunLoop().RunUntilIdle();
294   EXPECT_FALSE(PathExists(writer.path()));
295 }
296 
TEST_F(ImportantFileWriterTest,ScheduleWrite_WriteNow)297 TEST_F(ImportantFileWriterTest, ScheduleWrite_WriteNow) {
298   MockOneShotTimer timer;
299   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
300   writer.SetTimerForTesting(&timer);
301   EXPECT_FALSE(writer.HasPendingWrite());
302   DataSerializer serializer("foo");
303   writer.ScheduleWrite(&serializer);
304   EXPECT_TRUE(writer.HasPendingWrite());
305   writer.WriteNow(std::make_unique<std::string>("bar"));
306   EXPECT_FALSE(writer.HasPendingWrite());
307   EXPECT_FALSE(timer.IsRunning());
308 
309   RunLoop().RunUntilIdle();
310   ASSERT_TRUE(PathExists(writer.path()));
311   EXPECT_EQ("bar", GetFileContent(writer.path()));
312 }
313 
TEST_F(ImportantFileWriterTest,DoScheduledWrite_FailToSerialize)314 TEST_F(ImportantFileWriterTest, DoScheduledWrite_FailToSerialize) {
315   MockOneShotTimer timer;
316   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
317   writer.SetTimerForTesting(&timer);
318   EXPECT_FALSE(writer.HasPendingWrite());
319   FailingDataSerializer serializer;
320   writer.ScheduleWrite(&serializer);
321   EXPECT_TRUE(writer.HasPendingWrite());
322 
323   writer.DoScheduledWrite();
324   EXPECT_FALSE(timer.IsRunning());
325   EXPECT_FALSE(writer.HasPendingWrite());
326   RunLoop().RunUntilIdle();
327   EXPECT_FALSE(PathExists(writer.path()));
328 }
329 
TEST_F(ImportantFileWriterTest,WriteFileAtomicallyHistogramSuffixTest)330 TEST_F(ImportantFileWriterTest, WriteFileAtomicallyHistogramSuffixTest) {
331   base::HistogramTester histogram_tester;
332   EXPECT_FALSE(PathExists(file_));
333   EXPECT_TRUE(ImportantFileWriter::WriteFileAtomically(file_, "baz", "test"));
334   EXPECT_TRUE(PathExists(file_));
335   EXPECT_EQ("baz", GetFileContent(file_));
336   histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError", 0);
337   histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError.test", 0);
338 
339   FilePath invalid_file_ = FilePath().AppendASCII("bad/../non_existent/path");
340   EXPECT_FALSE(PathExists(invalid_file_));
341   EXPECT_FALSE(
342       ImportantFileWriter::WriteFileAtomically(invalid_file_, nullptr));
343   histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError", 1);
344   histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError.test", 0);
345   EXPECT_FALSE(
346       ImportantFileWriter::WriteFileAtomically(invalid_file_, nullptr, "test"));
347   histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError", 1);
348   histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError.test", 1);
349 }
350 
351 }  // namespace base
352