1// Copyright 2013 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/chromeos/file_manager/file_watcher.h"
6
7#include "base/files/file_util.h"
8#include "base/files/scoped_temp_dir.h"
9#include "base/message_loop/message_loop.h"
10#include "base/run_loop.h"
11#include "content/public/test/test_browser_thread_bundle.h"
12#include "google_apis/drive/test_util.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15namespace file_manager {
16namespace {
17
18using google_apis::test_util::CreateQuitCallback;
19using google_apis::test_util::CreateCopyResultCallback;
20
21class FileManagerFileWatcherTest : public testing::Test {
22 public:
23  // Use IO_MAINLOOP so FilePathWatcher works in the fake FILE thread, which
24  // is actually shared with the main thread.
25  FileManagerFileWatcherTest()
26      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
27  }
28
29 private:
30  content::TestBrowserThreadBundle thread_bundle_;
31};
32
33TEST_F(FileManagerFileWatcherTest, AddAndRemoveOneExtensionId) {
34  const base::FilePath kVirtualPath =
35      base::FilePath::FromUTF8Unsafe("foo/bar.txt");
36  const char kExtensionId[] = "extension-id";
37
38  FileWatcher file_watcher(kVirtualPath);
39  file_watcher.AddExtension(kExtensionId);
40  std::vector<std::string> extension_ids = file_watcher.GetExtensionIds();
41
42  ASSERT_EQ(1U, extension_ids.size());
43  ASSERT_EQ(kExtensionId, extension_ids[0]);
44
45  file_watcher.RemoveExtension(kExtensionId);
46  extension_ids = file_watcher.GetExtensionIds();
47  ASSERT_EQ(0U, extension_ids.size());
48}
49
50TEST_F(FileManagerFileWatcherTest, AddAndRemoveMultipleExtensionIds) {
51  const base::FilePath kVirtualPath =
52      base::FilePath::FromUTF8Unsafe("foo/bar.txt");
53  const char kExtensionFooId[] = "extension-foo-id";
54  const char kExtensionBarId[] = "extension-bar-id";
55
56  FileWatcher file_watcher(kVirtualPath);
57  file_watcher.AddExtension(kExtensionFooId);
58  file_watcher.AddExtension(kExtensionBarId);
59  std::vector<std::string> extension_ids = file_watcher.GetExtensionIds();
60
61  // The list should be sorted.
62  ASSERT_EQ(2U, extension_ids.size());
63  ASSERT_EQ(kExtensionBarId, extension_ids[0]);
64  ASSERT_EQ(kExtensionFooId, extension_ids[1]);
65
66  // Remove Foo. Bar should remain.
67  file_watcher.RemoveExtension(kExtensionFooId);
68  extension_ids = file_watcher.GetExtensionIds();
69  ASSERT_EQ(1U, extension_ids.size());
70  ASSERT_EQ(kExtensionBarId, extension_ids[0]);
71
72  // Remove Bar. Nothing should remain.
73  file_watcher.RemoveExtension(kExtensionBarId);
74  extension_ids = file_watcher.GetExtensionIds();
75  ASSERT_EQ(0U, extension_ids.size());
76}
77
78TEST_F(FileManagerFileWatcherTest, AddSameExtensionMultipleTimes) {
79  const base::FilePath kVirtualPath =
80      base::FilePath::FromUTF8Unsafe("foo/bar.txt");
81  const char kExtensionId[] = "extension-id";
82
83  FileWatcher file_watcher(kVirtualPath);
84  // Add three times.
85  file_watcher.AddExtension(kExtensionId);
86  file_watcher.AddExtension(kExtensionId);
87  file_watcher.AddExtension(kExtensionId);
88
89  std::vector<std::string> extension_ids = file_watcher.GetExtensionIds();
90  ASSERT_EQ(1U, extension_ids.size());
91  ASSERT_EQ(kExtensionId, extension_ids[0]);
92
93  // Remove 1st time.
94  file_watcher.RemoveExtension(kExtensionId);
95  extension_ids = file_watcher.GetExtensionIds();
96  ASSERT_EQ(1U, extension_ids.size());
97
98  // Remove 2nd time.
99  file_watcher.RemoveExtension(kExtensionId);
100  extension_ids = file_watcher.GetExtensionIds();
101  ASSERT_EQ(1U, extension_ids.size());
102
103  // Remove 3rd time. The extension ID should be gone now.
104  file_watcher.RemoveExtension(kExtensionId);
105  extension_ids = file_watcher.GetExtensionIds();
106  ASSERT_EQ(0U, extension_ids.size());
107}
108
109TEST_F(FileManagerFileWatcherTest, WatchLocalFile) {
110  const base::FilePath kVirtualPath =
111      base::FilePath::FromUTF8Unsafe("foo/bar.txt");
112  const char kExtensionId[] = "extension-id";
113
114  // Create a temporary directory.
115  base::ScopedTempDir temp_dir;
116  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
117
118  // See the comment at the end of this function for why scoped_ptr is used.
119  scoped_ptr<FileWatcher> file_watcher(
120      new FileWatcher(kVirtualPath));
121  file_watcher->AddExtension(kExtensionId);
122
123  // Start watching changes in the temporary directory.
124  base::FilePath changed_path;
125  bool watcher_created = false;
126  bool on_change_error = false;
127  base::RunLoop run_loop;
128  file_watcher->WatchLocalFile(
129      temp_dir.path(),
130      CreateQuitCallback(
131          &run_loop,
132          CreateCopyResultCallback(&changed_path, &on_change_error)),
133      CreateCopyResultCallback(&watcher_created));
134  // Spin the message loop so the base::FilePathWatcher is created.
135  base::RunLoop().RunUntilIdle();
136  ASSERT_TRUE(watcher_created);
137
138  // Create a temporary file in the temporary directory. The file watcher
139  // should detect the change in the directory.
140  base::FilePath temp_file_path;
141  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &temp_file_path));
142  // Wait until the directory change is notified.
143  run_loop.Run();
144  ASSERT_FALSE(on_change_error);
145  ASSERT_EQ(temp_dir.path().value(), changed_path.value());
146
147  // This is ugly, but FileWatcher should be deleted explicitly here, and
148  // spin the message loop so the base::FilePathWatcher is deleted.
149  // Otherwise, base::FilePathWatcher may detect a change when the temporary
150  // directory is deleted, which may result in crash.
151  file_watcher.reset();
152  base::RunLoop().RunUntilIdle();
153}
154
155}  // namespace
156}  // namespace file_manager.
157