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/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