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/file_util.h" 10 #include "base/files/file_path.h" 11 #include "base/files/scoped_temp_dir.h" 12 #include "base/logging.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/run_loop.h" 15 #include "base/threading/thread.h" 16 #include "base/time/time.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 namespace base { 20 21 namespace { 22 23 std::string GetFileContent(const FilePath& path) { 24 std::string content; 25 if (!ReadFileToString(path, &content)) { 26 NOTREACHED(); 27 } 28 return content; 29 } 30 31 class DataSerializer : public ImportantFileWriter::DataSerializer { 32 public: 33 explicit DataSerializer(const std::string& data) : data_(data) { 34 } 35 36 virtual bool SerializeData(std::string* output) OVERRIDE { 37 output->assign(data_); 38 return true; 39 } 40 41 private: 42 const std::string data_; 43 }; 44 45 class SuccessfulWriteObserver { 46 public: 47 SuccessfulWriteObserver() : successful_write_observed_(false) {} 48 49 // Register on_successful_write() to be called on the next successful write 50 // of |writer|. 51 void ObserveNextSuccessfulWrite(ImportantFileWriter* writer); 52 53 // Returns true if a successful write was observed via on_successful_write() 54 // and resets the observation state to false regardless. 55 bool GetAndResetObservationState(); 56 57 private: 58 void on_successful_write() { 59 EXPECT_FALSE(successful_write_observed_); 60 successful_write_observed_ = true; 61 } 62 63 bool successful_write_observed_; 64 65 DISALLOW_COPY_AND_ASSIGN(SuccessfulWriteObserver); 66 }; 67 68 void SuccessfulWriteObserver::ObserveNextSuccessfulWrite( 69 ImportantFileWriter* writer) { 70 writer->RegisterOnNextSuccessfulWriteCallback(base::Bind( 71 &SuccessfulWriteObserver::on_successful_write, base::Unretained(this))); 72 } 73 74 bool SuccessfulWriteObserver::GetAndResetObservationState() { 75 bool was_successful_write_observed = successful_write_observed_; 76 successful_write_observed_ = false; 77 return was_successful_write_observed; 78 } 79 80 } // namespace 81 82 class ImportantFileWriterTest : public testing::Test { 83 public: 84 ImportantFileWriterTest() { } 85 virtual void SetUp() { 86 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 87 file_ = temp_dir_.path().AppendASCII("test-file"); 88 } 89 90 protected: 91 SuccessfulWriteObserver successful_write_observer_; 92 FilePath file_; 93 MessageLoop loop_; 94 95 private: 96 ScopedTempDir temp_dir_; 97 }; 98 99 TEST_F(ImportantFileWriterTest, Basic) { 100 ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); 101 EXPECT_FALSE(PathExists(writer.path())); 102 EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); 103 writer.WriteNow("foo"); 104 RunLoop().RunUntilIdle(); 105 106 EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); 107 ASSERT_TRUE(PathExists(writer.path())); 108 EXPECT_EQ("foo", GetFileContent(writer.path())); 109 } 110 111 TEST_F(ImportantFileWriterTest, BasicWithSuccessfulWriteObserver) { 112 ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); 113 EXPECT_FALSE(PathExists(writer.path())); 114 EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); 115 successful_write_observer_.ObserveNextSuccessfulWrite(&writer); 116 writer.WriteNow("foo"); 117 RunLoop().RunUntilIdle(); 118 119 // Confirm that the observer is invoked. 120 EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState()); 121 ASSERT_TRUE(PathExists(writer.path())); 122 EXPECT_EQ("foo", GetFileContent(writer.path())); 123 124 // Confirm that re-installing the observer works for another write. 125 EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); 126 successful_write_observer_.ObserveNextSuccessfulWrite(&writer); 127 writer.WriteNow("bar"); 128 RunLoop().RunUntilIdle(); 129 130 EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState()); 131 ASSERT_TRUE(PathExists(writer.path())); 132 EXPECT_EQ("bar", GetFileContent(writer.path())); 133 134 // Confirm that writing again without re-installing the observer doesn't 135 // result in a notification. 136 EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); 137 writer.WriteNow("baz"); 138 RunLoop().RunUntilIdle(); 139 140 EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); 141 ASSERT_TRUE(PathExists(writer.path())); 142 EXPECT_EQ("baz", GetFileContent(writer.path())); 143 } 144 145 TEST_F(ImportantFileWriterTest, ScheduleWrite) { 146 ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); 147 writer.set_commit_interval(TimeDelta::FromMilliseconds(25)); 148 EXPECT_FALSE(writer.HasPendingWrite()); 149 DataSerializer serializer("foo"); 150 writer.ScheduleWrite(&serializer); 151 EXPECT_TRUE(writer.HasPendingWrite()); 152 MessageLoop::current()->PostDelayedTask( 153 FROM_HERE, 154 MessageLoop::QuitWhenIdleClosure(), 155 TimeDelta::FromMilliseconds(100)); 156 MessageLoop::current()->Run(); 157 EXPECT_FALSE(writer.HasPendingWrite()); 158 ASSERT_TRUE(PathExists(writer.path())); 159 EXPECT_EQ("foo", GetFileContent(writer.path())); 160 } 161 162 TEST_F(ImportantFileWriterTest, DoScheduledWrite) { 163 ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); 164 EXPECT_FALSE(writer.HasPendingWrite()); 165 DataSerializer serializer("foo"); 166 writer.ScheduleWrite(&serializer); 167 EXPECT_TRUE(writer.HasPendingWrite()); 168 writer.DoScheduledWrite(); 169 MessageLoop::current()->PostDelayedTask( 170 FROM_HERE, 171 MessageLoop::QuitWhenIdleClosure(), 172 TimeDelta::FromMilliseconds(100)); 173 MessageLoop::current()->Run(); 174 EXPECT_FALSE(writer.HasPendingWrite()); 175 ASSERT_TRUE(PathExists(writer.path())); 176 EXPECT_EQ("foo", GetFileContent(writer.path())); 177 } 178 179 TEST_F(ImportantFileWriterTest, BatchingWrites) { 180 ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); 181 writer.set_commit_interval(TimeDelta::FromMilliseconds(25)); 182 DataSerializer foo("foo"), bar("bar"), baz("baz"); 183 writer.ScheduleWrite(&foo); 184 writer.ScheduleWrite(&bar); 185 writer.ScheduleWrite(&baz); 186 MessageLoop::current()->PostDelayedTask( 187 FROM_HERE, 188 MessageLoop::QuitWhenIdleClosure(), 189 TimeDelta::FromMilliseconds(100)); 190 MessageLoop::current()->Run(); 191 ASSERT_TRUE(PathExists(writer.path())); 192 EXPECT_EQ("baz", GetFileContent(writer.path())); 193 } 194 195 } // namespace base 196