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/run_loop.h" 17 #include "base/single_thread_task_runner.h" 18 #include "base/threading/thread.h" 19 #include "base/threading/thread_task_runner_handle.h" 20 #include "base/time/time.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 23 namespace base { 24 25 namespace { 26 27 std::string GetFileContent(const FilePath& path) { 28 std::string content; 29 if (!ReadFileToString(path, &content)) { 30 NOTREACHED(); 31 } 32 return content; 33 } 34 35 class DataSerializer : public ImportantFileWriter::DataSerializer { 36 public: 37 explicit DataSerializer(const std::string& data) : data_(data) { 38 } 39 40 bool SerializeData(std::string* output) override { 41 output->assign(data_); 42 return true; 43 } 44 45 private: 46 const std::string data_; 47 }; 48 49 enum WriteCallbackObservationState { 50 NOT_CALLED, 51 CALLED_WITH_ERROR, 52 CALLED_WITH_SUCCESS, 53 }; 54 55 class WriteCallbacksObserver { 56 public: 57 WriteCallbacksObserver() = default; 58 59 // Register OnBeforeWrite() and OnAfterWrite() to be called on the next write 60 // of |writer|. 61 void ObserveNextWriteCallbacks(ImportantFileWriter* writer); 62 63 // Returns the |WriteCallbackObservationState| which was observed, then resets 64 // it to |NOT_CALLED|. 65 WriteCallbackObservationState GetAndResetObservationState(); 66 67 private: 68 void OnBeforeWrite() { 69 EXPECT_FALSE(before_write_called_); 70 before_write_called_ = true; 71 } 72 73 void OnAfterWrite(bool success) { 74 EXPECT_EQ(NOT_CALLED, after_write_observation_state_); 75 after_write_observation_state_ = 76 success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR; 77 } 78 79 bool before_write_called_ = false; 80 WriteCallbackObservationState after_write_observation_state_ = NOT_CALLED; 81 82 DISALLOW_COPY_AND_ASSIGN(WriteCallbacksObserver); 83 }; 84 85 void WriteCallbacksObserver::ObserveNextWriteCallbacks( 86 ImportantFileWriter* writer) { 87 writer->RegisterOnNextWriteCallbacks( 88 base::Bind(&WriteCallbacksObserver::OnBeforeWrite, 89 base::Unretained(this)), 90 base::Bind(&WriteCallbacksObserver::OnAfterWrite, 91 base::Unretained(this))); 92 } 93 94 WriteCallbackObservationState 95 WriteCallbacksObserver::GetAndResetObservationState() { 96 EXPECT_EQ(after_write_observation_state_ != NOT_CALLED, before_write_called_) 97 << "The before-write callback should always be called before the " 98 "after-write callback"; 99 100 WriteCallbackObservationState state = after_write_observation_state_; 101 before_write_called_ = false; 102 after_write_observation_state_ = NOT_CALLED; 103 return state; 104 } 105 106 } // namespace 107 108 class ImportantFileWriterTest : public testing::Test { 109 public: 110 ImportantFileWriterTest() { } 111 void SetUp() override { 112 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 113 file_ = temp_dir_.GetPath().AppendASCII("test-file"); 114 } 115 116 protected: 117 WriteCallbacksObserver write_callback_observer_; 118 FilePath file_; 119 MessageLoop loop_; 120 121 private: 122 ScopedTempDir temp_dir_; 123 }; 124 125 TEST_F(ImportantFileWriterTest, Basic) { 126 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); 127 EXPECT_FALSE(PathExists(writer.path())); 128 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 129 writer.WriteNow(MakeUnique<std::string>("foo")); 130 RunLoop().RunUntilIdle(); 131 132 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 133 ASSERT_TRUE(PathExists(writer.path())); 134 EXPECT_EQ("foo", GetFileContent(writer.path())); 135 } 136 137 TEST_F(ImportantFileWriterTest, WriteWithObserver) { 138 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); 139 EXPECT_FALSE(PathExists(writer.path())); 140 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 141 142 // Confirm that the observer is invoked. 143 write_callback_observer_.ObserveNextWriteCallbacks(&writer); 144 writer.WriteNow(MakeUnique<std::string>("foo")); 145 RunLoop().RunUntilIdle(); 146 147 EXPECT_EQ(CALLED_WITH_SUCCESS, 148 write_callback_observer_.GetAndResetObservationState()); 149 ASSERT_TRUE(PathExists(writer.path())); 150 EXPECT_EQ("foo", GetFileContent(writer.path())); 151 152 // Confirm that re-installing the observer works for another write. 153 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 154 write_callback_observer_.ObserveNextWriteCallbacks(&writer); 155 writer.WriteNow(MakeUnique<std::string>("bar")); 156 RunLoop().RunUntilIdle(); 157 158 EXPECT_EQ(CALLED_WITH_SUCCESS, 159 write_callback_observer_.GetAndResetObservationState()); 160 ASSERT_TRUE(PathExists(writer.path())); 161 EXPECT_EQ("bar", GetFileContent(writer.path())); 162 163 // Confirm that writing again without re-installing the observer doesn't 164 // result in a notification. 165 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 166 writer.WriteNow(MakeUnique<std::string>("baz")); 167 RunLoop().RunUntilIdle(); 168 169 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 170 ASSERT_TRUE(PathExists(writer.path())); 171 EXPECT_EQ("baz", GetFileContent(writer.path())); 172 } 173 174 TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) { 175 // Use an invalid file path (relative paths are invalid) to get a 176 // FILE_ERROR_ACCESS_DENIED error when trying to write the file. 177 ImportantFileWriter writer(FilePath().AppendASCII("bad/../path"), 178 ThreadTaskRunnerHandle::Get()); 179 EXPECT_FALSE(PathExists(writer.path())); 180 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 181 write_callback_observer_.ObserveNextWriteCallbacks(&writer); 182 writer.WriteNow(MakeUnique<std::string>("foo")); 183 RunLoop().RunUntilIdle(); 184 185 // Confirm that the write observer was invoked with its boolean parameter set 186 // to false. 187 EXPECT_EQ(CALLED_WITH_ERROR, 188 write_callback_observer_.GetAndResetObservationState()); 189 EXPECT_FALSE(PathExists(writer.path())); 190 } 191 192 TEST_F(ImportantFileWriterTest, CallbackRunsOnWriterThread) { 193 base::Thread file_writer_thread("ImportantFileWriter test thread"); 194 file_writer_thread.Start(); 195 ImportantFileWriter writer(file_, file_writer_thread.task_runner()); 196 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 197 198 // Block execution on |file_writer_thread| to verify that callbacks are 199 // executed on it. 200 base::WaitableEvent wait_helper( 201 base::WaitableEvent::ResetPolicy::MANUAL, 202 base::WaitableEvent::InitialState::NOT_SIGNALED); 203 file_writer_thread.task_runner()->PostTask( 204 FROM_HERE, 205 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&wait_helper))); 206 207 write_callback_observer_.ObserveNextWriteCallbacks(&writer); 208 writer.WriteNow(MakeUnique<std::string>("foo")); 209 RunLoop().RunUntilIdle(); 210 211 // Expect the callback to not have been executed before the 212 // |file_writer_thread| is unblocked. 213 EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); 214 215 wait_helper.Signal(); 216 file_writer_thread.FlushForTesting(); 217 218 EXPECT_EQ(CALLED_WITH_SUCCESS, 219 write_callback_observer_.GetAndResetObservationState()); 220 ASSERT_TRUE(PathExists(writer.path())); 221 EXPECT_EQ("foo", GetFileContent(writer.path())); 222 } 223 224 TEST_F(ImportantFileWriterTest, ScheduleWrite) { 225 ImportantFileWriter writer(file_, 226 ThreadTaskRunnerHandle::Get(), 227 TimeDelta::FromMilliseconds(25)); 228 EXPECT_FALSE(writer.HasPendingWrite()); 229 DataSerializer serializer("foo"); 230 writer.ScheduleWrite(&serializer); 231 EXPECT_TRUE(writer.HasPendingWrite()); 232 ThreadTaskRunnerHandle::Get()->PostDelayedTask( 233 FROM_HERE, MessageLoop::QuitWhenIdleClosure(), 234 TimeDelta::FromMilliseconds(100)); 235 RunLoop().Run(); 236 EXPECT_FALSE(writer.HasPendingWrite()); 237 ASSERT_TRUE(PathExists(writer.path())); 238 EXPECT_EQ("foo", GetFileContent(writer.path())); 239 } 240 241 TEST_F(ImportantFileWriterTest, DoScheduledWrite) { 242 ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); 243 EXPECT_FALSE(writer.HasPendingWrite()); 244 DataSerializer serializer("foo"); 245 writer.ScheduleWrite(&serializer); 246 EXPECT_TRUE(writer.HasPendingWrite()); 247 writer.DoScheduledWrite(); 248 ThreadTaskRunnerHandle::Get()->PostDelayedTask( 249 FROM_HERE, MessageLoop::QuitWhenIdleClosure(), 250 TimeDelta::FromMilliseconds(100)); 251 RunLoop().Run(); 252 EXPECT_FALSE(writer.HasPendingWrite()); 253 ASSERT_TRUE(PathExists(writer.path())); 254 EXPECT_EQ("foo", GetFileContent(writer.path())); 255 } 256 257 TEST_F(ImportantFileWriterTest, BatchingWrites) { 258 ImportantFileWriter writer(file_, 259 ThreadTaskRunnerHandle::Get(), 260 TimeDelta::FromMilliseconds(25)); 261 DataSerializer foo("foo"), bar("bar"), baz("baz"); 262 writer.ScheduleWrite(&foo); 263 writer.ScheduleWrite(&bar); 264 writer.ScheduleWrite(&baz); 265 ThreadTaskRunnerHandle::Get()->PostDelayedTask( 266 FROM_HERE, MessageLoop::QuitWhenIdleClosure(), 267 TimeDelta::FromMilliseconds(100)); 268 RunLoop().Run(); 269 ASSERT_TRUE(PathExists(writer.path())); 270 EXPECT_EQ("baz", GetFileContent(writer.path())); 271 } 272 273 } // namespace base 274