1// Copyright 2014 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 "chrome/browser/extensions/api/file_system/entry_watcher_service.h"
6
7#include <string>
8#include <vector>
9
10#include "base/files/file.h"
11#include "base/files/file_path.h"
12#include "base/files/scoped_temp_dir.h"
13#include "base/memory/scoped_vector.h"
14#include "base/run_loop.h"
15#include "chrome/common/extensions/api/file_system.h"
16#include "chrome/test/base/testing_profile.h"
17#include "content/public/test/test_browser_thread_bundle.h"
18#include "content/public/test/test_file_system_context.h"
19#include "extensions/browser/event_router.h"
20#include "storage/browser/fileapi/file_system_url.h"
21#include "storage/common/fileapi/file_system_types.h"
22
23namespace extensions {
24namespace {
25
26const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
27
28void LogStatus(std::vector<base::File::Error>* log, base::File::Error status) {
29  log->push_back(status);
30}
31
32}  // namespace
33
34class EntryWatcherServiceTest : public testing::Test {
35 protected:
36  EntryWatcherServiceTest() {}
37  virtual ~EntryWatcherServiceTest() {}
38
39  virtual void SetUp() OVERRIDE {
40    profile_.reset(new TestingProfile);
41    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
42    file_system_context_ =
43        content::CreateFileSystemContextForTesting(NULL, data_dir_.path());
44    service_.reset(new EntryWatcherService(profile_.get()));
45    service_->SetDispatchEventImplForTesting(base::Bind(
46        &EntryWatcherServiceTest::DispatchEventImpl, base::Unretained(this)));
47    service_->SetGetFileSystemContextImplForTesting(
48        base::Bind(&EntryWatcherServiceTest::GetFileSystemContextImpl,
49                   base::Unretained(this)));
50    testing_url_ = file_system_context_->CreateCrackedFileSystemURL(
51        GURL(std::string("chrome-extension://") + kExtensionId),
52        storage::kFileSystemTypeTest,
53        base::FilePath::FromUTF8Unsafe("/x/y/z"));
54  }
55
56  virtual void TearDown() OVERRIDE {
57    dispatch_event_log_targets_.clear();
58    dispatch_event_log_events_.clear();
59  }
60
61  void DispatchEventImpl(const std::string& extension_id,
62                         scoped_ptr<Event> event) {
63    dispatch_event_log_targets_.push_back(extension_id);
64    dispatch_event_log_events_.push_back(event.release());
65  }
66
67  storage::FileSystemContext* GetFileSystemContextImpl(
68      const std::string& extension_id,
69      content::BrowserContext* context) {
70    EXPECT_EQ(kExtensionId, extension_id);
71    EXPECT_EQ(profile_.get(), context);
72    return file_system_context_.get();
73  }
74
75  content::TestBrowserThreadBundle thread_bundle_;
76  scoped_ptr<TestingProfile> profile_;
77  base::ScopedTempDir data_dir_;
78  scoped_refptr<storage::FileSystemContext> file_system_context_;
79  scoped_ptr<EntryWatcherService> service_;
80  storage::FileSystemURL testing_url_;
81  std::vector<std::string> dispatch_event_log_targets_;
82  ScopedVector<Event> dispatch_event_log_events_;
83};
84
85TEST_F(EntryWatcherServiceTest, GetWatchedEntries) {
86  std::vector<base::File::Error> log;
87
88  const bool recursive = false;
89  service_->WatchDirectory(
90      kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log));
91  base::RunLoop().RunUntilIdle();
92
93  ASSERT_EQ(1u, log.size());
94  EXPECT_EQ(base::File::FILE_OK, log[0]);
95
96  {
97    const std::vector<storage::FileSystemURL> watched_entries =
98        service_->GetWatchedEntries(kExtensionId);
99    ASSERT_EQ(1u, watched_entries.size());
100    EXPECT_EQ(testing_url_, watched_entries[0]);
101  }
102
103  {
104    const std::string wrong_extension_id = "abcabcabcabcabcabcabcabcabcabcab";
105    const std::vector<storage::FileSystemURL> watched_entries =
106        service_->GetWatchedEntries(wrong_extension_id);
107    EXPECT_EQ(0u, watched_entries.size());
108  }
109}
110
111TEST_F(EntryWatcherServiceTest, WatchDirectory) {
112  std::vector<base::File::Error> log;
113
114  const bool recursive = false;
115  service_->WatchDirectory(
116      kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log));
117  base::RunLoop().RunUntilIdle();
118
119  ASSERT_EQ(1u, log.size());
120  EXPECT_EQ(base::File::FILE_OK, log[0]);
121
122  // The testing WatcherManager implementation emits two hard-coded fake
123  // notifications as soon as the watcher is set properly. See:
124  // TestWatcherManager::WatchDirectory() for details.
125  ASSERT_LE(1u, dispatch_event_log_targets_.size());
126  ASSERT_LE(1u, dispatch_event_log_events_.size());
127
128  EXPECT_EQ(kExtensionId, dispatch_event_log_targets_[0]);
129  EXPECT_EQ(api::file_system::OnEntryChanged::kEventName,
130            dispatch_event_log_events_[0]->event_name);
131
132  ASSERT_LE(2u, dispatch_event_log_targets_.size());
133  ASSERT_LE(2u, dispatch_event_log_events_.size());
134  EXPECT_EQ(kExtensionId, dispatch_event_log_targets_[1]);
135  EXPECT_EQ(api::file_system::OnEntryRemoved::kEventName,
136            dispatch_event_log_events_[1]->event_name);
137
138  // No unexpected events.
139  ASSERT_EQ(2u, dispatch_event_log_targets_.size());
140  ASSERT_EQ(2u, dispatch_event_log_events_.size());
141
142  const std::vector<storage::FileSystemURL> watched_entries =
143      service_->GetWatchedEntries(kExtensionId);
144  ASSERT_EQ(1u, watched_entries.size());
145  EXPECT_EQ(testing_url_, watched_entries[0]);
146}
147
148TEST_F(EntryWatcherServiceTest, WatchDirectory_AlreadyExists) {
149  std::vector<base::File::Error> log;
150
151  const bool recursive = false;
152  service_->WatchDirectory(
153      kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log));
154  base::RunLoop().RunUntilIdle();
155
156  ASSERT_EQ(1u, log.size());
157  EXPECT_EQ(base::File::FILE_OK, log[0]);
158
159  ASSERT_EQ(2u, dispatch_event_log_targets_.size());
160  ASSERT_EQ(2u, dispatch_event_log_events_.size());
161
162  {
163    const std::vector<storage::FileSystemURL> watched_entries =
164        service_->GetWatchedEntries(kExtensionId);
165    EXPECT_EQ(1u, watched_entries.size());
166  }
167
168  service_->WatchDirectory(
169      kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log));
170  base::RunLoop().RunUntilIdle();
171
172  ASSERT_EQ(2u, log.size());
173  EXPECT_EQ(base::File::FILE_ERROR_EXISTS, log[1]);
174
175  // No new unexpected events.
176  ASSERT_EQ(2u, dispatch_event_log_targets_.size());
177  ASSERT_EQ(2u, dispatch_event_log_events_.size());
178
179  {
180    const std::vector<storage::FileSystemURL> watched_entries =
181        service_->GetWatchedEntries(kExtensionId);
182    EXPECT_EQ(1u, watched_entries.size());
183  }
184}
185
186TEST_F(EntryWatcherServiceTest, WatchDirectory_Recursive) {
187  std::vector<base::File::Error> log;
188
189  const bool recursive = true;
190  service_->WatchDirectory(
191      kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log));
192  base::RunLoop().RunUntilIdle();
193
194  // Recursive watchers are not supported yet.
195  ASSERT_EQ(1u, log.size());
196  EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, log[0]);
197
198  // No unexpected events.
199  ASSERT_EQ(0u, dispatch_event_log_targets_.size());
200  ASSERT_EQ(0u, dispatch_event_log_events_.size());
201
202  const std::vector<storage::FileSystemURL> watched_entries =
203      service_->GetWatchedEntries(kExtensionId);
204  EXPECT_EQ(0u, watched_entries.size());
205}
206
207TEST_F(EntryWatcherServiceTest, UnwatchEntry) {
208  std::vector<base::File::Error> watch_log;
209
210  const bool recursive = false;
211  service_->WatchDirectory(kExtensionId,
212                           testing_url_,
213                           recursive,
214                           base::Bind(&LogStatus, &watch_log));
215  base::RunLoop().RunUntilIdle();
216
217  ASSERT_EQ(1u, watch_log.size());
218  EXPECT_EQ(base::File::FILE_OK, watch_log[0]);
219
220  ASSERT_EQ(2u, dispatch_event_log_targets_.size());
221  ASSERT_EQ(2u, dispatch_event_log_events_.size());
222
223  {
224    const std::vector<storage::FileSystemURL> watched_entries =
225        service_->GetWatchedEntries(kExtensionId);
226    EXPECT_EQ(1u, watched_entries.size());
227  }
228
229  std::vector<base::File::Error> unwatch_log;
230  service_->UnwatchEntry(
231      kExtensionId, testing_url_, base::Bind(&LogStatus, &unwatch_log));
232  base::RunLoop().RunUntilIdle();
233
234  ASSERT_EQ(1u, unwatch_log.size());
235  EXPECT_EQ(base::File::FILE_OK, unwatch_log[0]);
236
237  {
238    const std::vector<storage::FileSystemURL> watched_entries =
239        service_->GetWatchedEntries(kExtensionId);
240    EXPECT_EQ(0u, watched_entries.size());
241  }
242}
243
244TEST_F(EntryWatcherServiceTest, UnwatchEntry_NotFound) {
245  std::vector<base::File::Error> log;
246  service_->UnwatchEntry(
247      kExtensionId, testing_url_, base::Bind(&LogStatus, &log));
248  base::RunLoop().RunUntilIdle();
249
250  ASSERT_EQ(1u, log.size());
251  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, log[0]);
252}
253
254}  // namespace extensions
255