Home | History | Annotate | Download | only in files
      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 
     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:
     40   explicit DataSerializer(const std::string& data) : data_(data) {
     41   }
     42 
     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:
     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:
     76   void OnBeforeWrite() {
     77     EXPECT_FALSE(before_write_called_);
     78     before_write_called_ = true;
     79   }
     80 
     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 
     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
    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;
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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