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/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
19namespace base {
20
21namespace {
22
23std::string GetFileContent(const FilePath& path) {
24  std::string content;
25  if (!ReadFileToString(path, &content)) {
26    NOTREACHED();
27  }
28  return content;
29}
30
31class 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
45class 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
68void SuccessfulWriteObserver::ObserveNextSuccessfulWrite(
69    ImportantFileWriter* writer) {
70  writer->RegisterOnNextSuccessfulWriteCallback(base::Bind(
71      &SuccessfulWriteObserver::on_successful_write, base::Unretained(this)));
72}
73
74bool 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
82class 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
99TEST_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
111TEST_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
145TEST_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
162TEST_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
179TEST_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