blob: 05a3bd28fd99f4514e8e3276674a3987e7086ed5 [file] [log] [blame]
[email protected]64bb4cb32012-01-05 19:17:161// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]6faa0e0d2009-04-28 06:50:362// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]43486252012-10-24 16:33:365#include "base/files/important_file_writer.h"
[email protected]6faa0e0d2009-04-28 06:50:366
[email protected]e33c9512014-05-12 02:24:137#include "base/bind.h"
[email protected]6faa0e0d2009-04-28 06:50:368#include "base/compiler_specific.h"
[email protected]57999812013-02-24 05:40:529#include "base/files/file_path.h"
[email protected]e3177dd52014-08-13 20:22:1410#include "base/files/file_util.h"
[email protected]ea1a3f62012-11-16 20:34:2311#include "base/files/scoped_temp_dir.h"
skyostil054861d2015-04-30 19:06:1512#include "base/location.h"
[email protected]6faa0e0d2009-04-28 06:50:3613#include "base/logging.h"
avi543540e2015-12-24 05:15:3214#include "base/macros.h"
dcheng093de9b2016-04-04 21:25:5115#include "base/memory/ptr_util.h"
Gabriel Charette629ba73c2018-05-01 22:53:5416#include "base/message_loop/message_loop.h"
[email protected]7ff48ca2013-02-06 16:56:1917#include "base/run_loop.h"
skyostil054861d2015-04-30 19:06:1518#include "base/single_thread_task_runner.h"
Devlin Cronin15291e9c2018-06-07 21:37:4819#include "base/test/metrics/histogram_tester.h"
[email protected]34b99632011-01-01 01:01:0620#include "base/threading/thread.h"
gab6cce2f32016-05-11 19:53:4321#include "base/threading/thread_task_runner_handle.h"
[email protected]99084f62013-06-28 00:49:0722#include "base/time/time.h"
Sam McNally365428e2017-05-22 05:25:2223#include "base/timer/mock_timer.h"
[email protected]6faa0e0d2009-04-28 06:50:3624#include "testing/gtest/include/gtest/gtest.h"
25
[email protected]43486252012-10-24 16:33:3626namespace base {
27
[email protected]6faa0e0d2009-04-28 06:50:3628namespace {
29
30std::string GetFileContent(const FilePath& path) {
31 std::string content;
[email protected]82f84b92013-08-30 18:23:5032 if (!ReadFileToString(path, &content)) {
[email protected]6faa0e0d2009-04-28 06:50:3633 NOTREACHED();
34 }
35 return content;
36}
37
[email protected]6c1164042009-05-08 14:41:0838class DataSerializer : public ImportantFileWriter::DataSerializer {
39 public:
40 explicit DataSerializer(const std::string& data) : data_(data) {
41 }
42
dcheng56488182014-10-21 10:54:5143 bool SerializeData(std::string* output) override {
[email protected]6c1164042009-05-08 14:41:0844 output->assign(data_);
45 return true;
46 }
47
48 private:
49 const std::string data_;
50};
51
Sam McNally8f50faa2017-05-19 05:08:0052class FailingDataSerializer : public ImportantFileWriter::DataSerializer {
53 public:
54 bool SerializeData(std::string* output) override { return false; }
55};
56
probergefc46ac12016-09-21 18:03:0057enum WriteCallbackObservationState {
58 NOT_CALLED,
59 CALLED_WITH_ERROR,
60 CALLED_WITH_SUCCESS,
[email protected]e33c9512014-05-12 02:24:1361};
62
probergec503d692016-09-28 19:51:0563class WriteCallbacksObserver {
probergefc46ac12016-09-21 18:03:0064 public:
probergec503d692016-09-28 19:51:0565 WriteCallbacksObserver() = default;
probergefc46ac12016-09-21 18:03:0066
probergec503d692016-09-28 19:51:0567 // Register OnBeforeWrite() and OnAfterWrite() to be called on the next write
68 // of |writer|.
69 void ObserveNextWriteCallbacks(ImportantFileWriter* writer);
probergefc46ac12016-09-21 18:03:0070
probergec503d692016-09-28 19:51:0571 // Returns the |WriteCallbackObservationState| which was observed, then resets
72 // it to |NOT_CALLED|.
probergefc46ac12016-09-21 18:03:0073 WriteCallbackObservationState GetAndResetObservationState();
74
75 private:
probergec503d692016-09-28 19:51:0576 void OnBeforeWrite() {
77 EXPECT_FALSE(before_write_called_);
78 before_write_called_ = true;
probergefc46ac12016-09-21 18:03:0079 }
80
probergec503d692016-09-28 19:51:0581 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 }
probergefc46ac12016-09-21 18:03:0086
probergec503d692016-09-28 19:51:0587 bool before_write_called_ = false;
88 WriteCallbackObservationState after_write_observation_state_ = NOT_CALLED;
89
90 DISALLOW_COPY_AND_ASSIGN(WriteCallbacksObserver);
probergefc46ac12016-09-21 18:03:0091};
92
probergec503d692016-09-28 19:51:0593void WriteCallbacksObserver::ObserveNextWriteCallbacks(
[email protected]e33c9512014-05-12 02:24:1394 ImportantFileWriter* writer) {
probergec503d692016-09-28 19:51:0595 writer->RegisterOnNextWriteCallbacks(
96 base::Bind(&WriteCallbacksObserver::OnBeforeWrite,
97 base::Unretained(this)),
98 base::Bind(&WriteCallbacksObserver::OnAfterWrite,
99 base::Unretained(this)));
[email protected]e33c9512014-05-12 02:24:13100}
101
probergefc46ac12016-09-21 18:03:00102WriteCallbackObservationState
probergec503d692016-09-28 19:51:05103WriteCallbacksObserver::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;
probergefc46ac12016-09-21 18:03:00111 return state;
[email protected]e33c9512014-05-12 02:24:13112}
113
[email protected]6faa0e0d2009-04-28 06:50:36114} // namespace
115
116class ImportantFileWriterTest : public testing::Test {
117 public:
Chris Watkinsbb7211c2017-11-29 07:16:38118 ImportantFileWriterTest() = default;
dcheng8aef37612014-12-23 02:56:47119 void SetUp() override {
[email protected]6faa0e0d2009-04-28 06:50:36120 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
vabr411f4fc2016-09-08 09:26:27121 file_ = temp_dir_.GetPath().AppendASCII("test-file");
[email protected]6faa0e0d2009-04-28 06:50:36122 }
123
124 protected:
probergec503d692016-09-28 19:51:05125 WriteCallbacksObserver write_callback_observer_;
[email protected]6faa0e0d2009-04-28 06:50:36126 FilePath file_;
[email protected]6fad2632009-11-02 05:59:37127 MessageLoop loop_;
[email protected]6faa0e0d2009-04-28 06:50:36128
129 private:
[email protected]6faa0e0d2009-04-28 06:50:36130 ScopedTempDir temp_dir_;
131};
132
[email protected]6fad2632009-11-02 05:59:37133TEST_F(ImportantFileWriterTest, Basic) {
skyostil054861d2015-04-30 19:06:15134 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
[email protected]7567484142013-07-11 17:36:07135 EXPECT_FALSE(PathExists(writer.path()));
probergefc46ac12016-09-21 18:03:00136 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
Jeremy Roman9532f252017-08-16 23:27:24137 writer.WriteNow(std::make_unique<std::string>("foo"));
[email protected]7ff48ca2013-02-06 16:56:19138 RunLoop().RunUntilIdle();
[email protected]6faa0e0d2009-04-28 06:50:36139
probergefc46ac12016-09-21 18:03:00140 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
[email protected]7567484142013-07-11 17:36:07141 ASSERT_TRUE(PathExists(writer.path()));
[email protected]6faa0e0d2009-04-28 06:50:36142 EXPECT_EQ("foo", GetFileContent(writer.path()));
143}
144
probergefc46ac12016-09-21 18:03:00145TEST_F(ImportantFileWriterTest, WriteWithObserver) {
skyostil054861d2015-04-30 19:06:15146 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
[email protected]e33c9512014-05-12 02:24:13147 EXPECT_FALSE(PathExists(writer.path()));
probergefc46ac12016-09-21 18:03:00148 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
149
150 // Confirm that the observer is invoked.
probergec503d692016-09-28 19:51:05151 write_callback_observer_.ObserveNextWriteCallbacks(&writer);
Jeremy Roman9532f252017-08-16 23:27:24152 writer.WriteNow(std::make_unique<std::string>("foo"));
[email protected]e33c9512014-05-12 02:24:13153 RunLoop().RunUntilIdle();
154
probergefc46ac12016-09-21 18:03:00155 EXPECT_EQ(CALLED_WITH_SUCCESS,
156 write_callback_observer_.GetAndResetObservationState());
[email protected]e33c9512014-05-12 02:24:13157 ASSERT_TRUE(PathExists(writer.path()));
158 EXPECT_EQ("foo", GetFileContent(writer.path()));
159
160 // Confirm that re-installing the observer works for another write.
probergefc46ac12016-09-21 18:03:00161 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
probergec503d692016-09-28 19:51:05162 write_callback_observer_.ObserveNextWriteCallbacks(&writer);
Jeremy Roman9532f252017-08-16 23:27:24163 writer.WriteNow(std::make_unique<std::string>("bar"));
[email protected]e33c9512014-05-12 02:24:13164 RunLoop().RunUntilIdle();
165
probergefc46ac12016-09-21 18:03:00166 EXPECT_EQ(CALLED_WITH_SUCCESS,
167 write_callback_observer_.GetAndResetObservationState());
[email protected]e33c9512014-05-12 02:24:13168 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.
probergefc46ac12016-09-21 18:03:00173 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
Jeremy Roman9532f252017-08-16 23:27:24174 writer.WriteNow(std::make_unique<std::string>("baz"));
[email protected]e33c9512014-05-12 02:24:13175 RunLoop().RunUntilIdle();
176
probergefc46ac12016-09-21 18:03:00177 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
[email protected]e33c9512014-05-12 02:24:13178 ASSERT_TRUE(PathExists(writer.path()));
179 EXPECT_EQ("baz", GetFileContent(writer.path()));
180}
181
probergefc46ac12016-09-21 18:03:00182TEST_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());
probergec503d692016-09-28 19:51:05189 write_callback_observer_.ObserveNextWriteCallbacks(&writer);
Jeremy Roman9532f252017-08-16 23:27:24190 writer.WriteNow(std::make_unique<std::string>("foo"));
probergefc46ac12016-09-21 18:03:00191 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
200TEST_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
probergec503d692016-09-28 19:51:05206 // 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(
tzik92b7a422017-04-11 15:00:44212 FROM_HERE, base::BindOnce(&base::WaitableEvent::Wait,
213 base::Unretained(&wait_helper)));
probergec503d692016-09-28 19:51:05214
215 write_callback_observer_.ObserveNextWriteCallbacks(&writer);
Jeremy Roman9532f252017-08-16 23:27:24216 writer.WriteNow(std::make_unique<std::string>("foo"));
probergefc46ac12016-09-21 18:03:00217 RunLoop().RunUntilIdle();
218
probergec503d692016-09-28 19:51:05219 // Expect the callback to not have been executed before the
220 // |file_writer_thread| is unblocked.
probergefc46ac12016-09-21 18:03:00221 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState());
222
probergec503d692016-09-28 19:51:05223 wait_helper.Signal();
probergefc46ac12016-09-21 18:03:00224 file_writer_thread.FlushForTesting();
probergec503d692016-09-28 19:51:05225
probergefc46ac12016-09-21 18:03:00226 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
[email protected]6faa0e0d2009-04-28 06:50:36232TEST_F(ImportantFileWriterTest, ScheduleWrite) {
Sam McNally365428e2017-05-22 05:25:22233 constexpr TimeDelta kCommitInterval = TimeDelta::FromSeconds(12345);
234 MockTimer timer(true, false);
Sam McNally8f50faa2017-05-19 05:08:00235 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get(),
Sam McNally365428e2017-05-22 05:25:22236 kCommitInterval);
237 writer.SetTimerForTesting(&timer);
[email protected]6c1164042009-05-08 14:41:08238 EXPECT_FALSE(writer.HasPendingWrite());
239 DataSerializer serializer("foo");
240 writer.ScheduleWrite(&serializer);
241 EXPECT_TRUE(writer.HasPendingWrite());
Sam McNally365428e2017-05-22 05:25:22242 ASSERT_TRUE(timer.IsRunning());
243 EXPECT_EQ(kCommitInterval, timer.GetCurrentDelay());
244 timer.Fire();
[email protected]6c1164042009-05-08 14:41:08245 EXPECT_FALSE(writer.HasPendingWrite());
Sam McNally365428e2017-05-22 05:25:22246 EXPECT_FALSE(timer.IsRunning());
247 RunLoop().RunUntilIdle();
[email protected]7567484142013-07-11 17:36:07248 ASSERT_TRUE(PathExists(writer.path()));
[email protected]6c1164042009-05-08 14:41:08249 EXPECT_EQ("foo", GetFileContent(writer.path()));
250}
251
252TEST_F(ImportantFileWriterTest, DoScheduledWrite) {
Sam McNally365428e2017-05-22 05:25:22253 MockTimer timer(true, false);
skyostil054861d2015-04-30 19:06:15254 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
Sam McNally365428e2017-05-22 05:25:22255 writer.SetTimerForTesting(&timer);
[email protected]6c1164042009-05-08 14:41:08256 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());
Sam McNally365428e2017-05-22 05:25:22262 RunLoop().RunUntilIdle();
[email protected]7567484142013-07-11 17:36:07263 ASSERT_TRUE(PathExists(writer.path()));
[email protected]6faa0e0d2009-04-28 06:50:36264 EXPECT_EQ("foo", GetFileContent(writer.path()));
265}
266
[email protected]b9a8ee4d2012-11-08 04:29:12267TEST_F(ImportantFileWriterTest, BatchingWrites) {
Sam McNally365428e2017-05-22 05:25:22268 MockTimer timer(true, false);
269 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
270 writer.SetTimerForTesting(&timer);
[email protected]6c1164042009-05-08 14:41:08271 DataSerializer foo("foo"), bar("bar"), baz("baz");
272 writer.ScheduleWrite(&foo);
273 writer.ScheduleWrite(&bar);
274 writer.ScheduleWrite(&baz);
Sam McNally365428e2017-05-22 05:25:22275 ASSERT_TRUE(timer.IsRunning());
276 timer.Fire();
Sam McNally8f50faa2017-05-19 05:08:00277 RunLoop().RunUntilIdle();
[email protected]7567484142013-07-11 17:36:07278 ASSERT_TRUE(PathExists(writer.path()));
[email protected]6faa0e0d2009-04-28 06:50:36279 EXPECT_EQ("baz", GetFileContent(writer.path()));
280}
[email protected]43486252012-10-24 16:33:36281
Sam McNally8f50faa2017-05-19 05:08:00282TEST_F(ImportantFileWriterTest, ScheduleWrite_FailToSerialize) {
Sam McNally365428e2017-05-22 05:25:22283 MockTimer timer(true, false);
284 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
285 writer.SetTimerForTesting(&timer);
Sam McNally8f50faa2017-05-19 05:08:00286 EXPECT_FALSE(writer.HasPendingWrite());
287 FailingDataSerializer serializer;
288 writer.ScheduleWrite(&serializer);
289 EXPECT_TRUE(writer.HasPendingWrite());
Sam McNally365428e2017-05-22 05:25:22290 ASSERT_TRUE(timer.IsRunning());
291 timer.Fire();
Sam McNally8f50faa2017-05-19 05:08:00292 EXPECT_FALSE(writer.HasPendingWrite());
Sam McNally365428e2017-05-22 05:25:22293 RunLoop().RunUntilIdle();
Sam McNally8f50faa2017-05-19 05:08:00294 EXPECT_FALSE(PathExists(writer.path()));
295}
296
297TEST_F(ImportantFileWriterTest, ScheduleWrite_WriteNow) {
Sam McNally365428e2017-05-22 05:25:22298 MockTimer timer(true, false);
299 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
300 writer.SetTimerForTesting(&timer);
Sam McNally8f50faa2017-05-19 05:08:00301 EXPECT_FALSE(writer.HasPendingWrite());
302 DataSerializer serializer("foo");
303 writer.ScheduleWrite(&serializer);
304 EXPECT_TRUE(writer.HasPendingWrite());
Jeremy Roman9532f252017-08-16 23:27:24305 writer.WriteNow(std::make_unique<std::string>("bar"));
Sam McNally8f50faa2017-05-19 05:08:00306 EXPECT_FALSE(writer.HasPendingWrite());
Sam McNally365428e2017-05-22 05:25:22307 EXPECT_FALSE(timer.IsRunning());
Sam McNally8f50faa2017-05-19 05:08:00308
309 RunLoop().RunUntilIdle();
Sam McNally8f50faa2017-05-19 05:08:00310 ASSERT_TRUE(PathExists(writer.path()));
311 EXPECT_EQ("bar", GetFileContent(writer.path()));
312}
313
314TEST_F(ImportantFileWriterTest, DoScheduledWrite_FailToSerialize) {
Sam McNally365428e2017-05-22 05:25:22315 MockTimer timer(true, false);
316 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get());
317 writer.SetTimerForTesting(&timer);
Sam McNally8f50faa2017-05-19 05:08:00318 EXPECT_FALSE(writer.HasPendingWrite());
319 FailingDataSerializer serializer;
320 writer.ScheduleWrite(&serializer);
321 EXPECT_TRUE(writer.HasPendingWrite());
322
323 writer.DoScheduledWrite();
Sam McNally365428e2017-05-22 05:25:22324 EXPECT_FALSE(timer.IsRunning());
Sam McNally8f50faa2017-05-19 05:08:00325 EXPECT_FALSE(writer.HasPendingWrite());
Sam McNally8f50faa2017-05-19 05:08:00326 RunLoop().RunUntilIdle();
Sam McNally8f50faa2017-05-19 05:08:00327 EXPECT_FALSE(PathExists(writer.path()));
328}
329
xaerox4ae8d172017-06-20 10:20:12330TEST_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
Scott Grahamd2c3c292017-06-20 19:28:47339 FilePath invalid_file_ = FilePath().AppendASCII("bad/../non_existent/path");
xaerox4ae8d172017-06-20 10:20:12340 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
[email protected]43486252012-10-24 16:33:36351} // namespace base