1// Copyright (c) 2011 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/sync/util/extensions_activity_monitor.h"
6
7#include "base/file_path.h"
8#include "base/string_util.h"
9#include "base/synchronization/waitable_event.h"
10#include "base/values.h"
11#include "chrome/browser/extensions/extension_bookmarks_module.h"
12#include "chrome/common/extensions/extension.h"
13#include "chrome/common/extensions/extension_constants.h"
14#include "content/browser/browser_thread.h"
15#include "content/common/notification_service.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18using browser_sync::ExtensionsActivityMonitor;
19namespace keys = extension_manifest_keys;
20
21namespace {
22
23const FilePath::CharType kTestExtensionPath1[] =
24#if defined(OS_POSIX)
25    FILE_PATH_LITERAL("/testextension1");
26#elif defined(OS_WIN)
27    FILE_PATH_LITERAL("c:\\testextension1");
28#endif
29
30const FilePath::CharType kTestExtensionPath2[] =
31#if defined(OS_POSIX)
32    FILE_PATH_LITERAL("/testextension2");
33#elif defined(OS_WIN)
34    FILE_PATH_LITERAL("c:\\testextension2");
35#endif
36
37const char* kTestExtensionVersion = "1.0.0.0";
38const char* kTestExtensionName = "foo extension";
39
40template <class FunctionType>
41class BookmarkAPIEventTask : public Task {
42 public:
43  BookmarkAPIEventTask(FunctionType* t, Extension* e, size_t repeats,
44                       base::WaitableEvent* done) :
45       extension_(e), function_(t), repeats_(repeats), done_(done) {}
46   virtual void Run() {
47     for (size_t i = 0; i < repeats_; i++) {
48       NotificationService::current()->Notify(
49           NotificationType::EXTENSION_BOOKMARKS_API_INVOKED,
50           Source<Extension>(extension_.get()),
51           Details<const BookmarksFunction>(function_.get()));
52     }
53     done_->Signal();
54   }
55 private:
56  scoped_refptr<Extension> extension_;
57  scoped_refptr<FunctionType> function_;
58  size_t repeats_;
59  base::WaitableEvent* done_;
60
61  DISALLOW_COPY_AND_ASSIGN(BookmarkAPIEventTask);
62};
63
64class BookmarkAPIEventGenerator {
65 public:
66  BookmarkAPIEventGenerator() {}
67  virtual ~BookmarkAPIEventGenerator() {}
68  template <class T>
69  void NewEvent(const FilePath::StringType& extension_path,
70      T* bookmarks_function, size_t repeats) {
71    std::string error;
72    DictionaryValue input;
73    input.SetString(keys::kVersion, kTestExtensionVersion);
74    input.SetString(keys::kName, kTestExtensionName);
75    scoped_refptr<Extension> extension(Extension::Create(
76        FilePath(extension_path), Extension::INVALID, input,
77        Extension::STRICT_ERROR_CHECKS, &error));
78    bookmarks_function->set_name(T::function_name());
79    base::WaitableEvent done_event(false, false);
80    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
81        new BookmarkAPIEventTask<T>(bookmarks_function, extension,
82                                    repeats, &done_event));
83    done_event.Wait();
84  }
85
86 private:
87  DISALLOW_COPY_AND_ASSIGN(BookmarkAPIEventGenerator);
88};
89}  // namespace
90
91class DoUIThreadSetupTask : public Task {
92 public:
93  DoUIThreadSetupTask(NotificationService** service,
94                      base::WaitableEvent* done)
95      : service_(service), signal_when_done_(done) {}
96  virtual ~DoUIThreadSetupTask() {}
97  virtual void Run() {
98    *service_ = new NotificationService();
99    signal_when_done_->Signal();
100  }
101 private:
102  NotificationService** service_;
103  base::WaitableEvent* signal_when_done_;
104  DISALLOW_COPY_AND_ASSIGN(DoUIThreadSetupTask);
105};
106
107class ExtensionsActivityMonitorTest : public testing::Test {
108 public:
109  ExtensionsActivityMonitorTest() : service_(NULL),
110      ui_thread_(BrowserThread::UI) { }
111  virtual ~ExtensionsActivityMonitorTest() {}
112
113  virtual void SetUp() {
114    ui_thread_.Start();
115    base::WaitableEvent service_created(false, false);
116    ui_thread_.message_loop()->PostTask(FROM_HERE,
117        new DoUIThreadSetupTask(&service_, &service_created));
118    service_created.Wait();
119  }
120
121  virtual void TearDown() {
122    ui_thread_.message_loop()->DeleteSoon(FROM_HERE, service_);
123    ui_thread_.Stop();
124  }
125
126  MessageLoop* ui_loop() { return ui_thread_.message_loop(); }
127
128  static std::string GetExtensionIdForPath(
129      const FilePath::StringType& extension_path) {
130    std::string error;
131    DictionaryValue input;
132    input.SetString(keys::kVersion, kTestExtensionVersion);
133    input.SetString(keys::kName, kTestExtensionName);
134    scoped_refptr<Extension> extension(Extension::Create(
135        FilePath(extension_path), Extension::INVALID, input,
136        Extension::STRICT_ERROR_CHECKS, &error));
137    EXPECT_EQ("", error);
138    return extension->id();
139  }
140 private:
141  NotificationService* service_;
142  BrowserThread ui_thread_;
143};
144
145TEST_F(ExtensionsActivityMonitorTest, Basic) {
146  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
147  BookmarkAPIEventGenerator generator;
148
149  generator.NewEvent<RemoveBookmarkFunction>(kTestExtensionPath1,
150      new RemoveBookmarkFunction(), 1);
151  generator.NewEvent<MoveBookmarkFunction>(kTestExtensionPath1,
152      new MoveBookmarkFunction(), 1);
153  generator.NewEvent<UpdateBookmarkFunction>(kTestExtensionPath1,
154      new UpdateBookmarkFunction(), 2);
155  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
156      new CreateBookmarkFunction(), 3);
157  generator.NewEvent<SearchBookmarksFunction>(kTestExtensionPath1,
158      new SearchBookmarksFunction(), 5);
159  const uint32 writes_by_extension1 = 1 + 1 + 2 + 3;
160
161  generator.NewEvent<RemoveTreeBookmarkFunction>(kTestExtensionPath2,
162      new RemoveTreeBookmarkFunction(), 8);
163  generator.NewEvent<GetBookmarkTreeFunction>(kTestExtensionPath2,
164      new GetBookmarkTreeFunction(), 13);
165  generator.NewEvent<GetBookmarkChildrenFunction>(kTestExtensionPath2,
166      new GetBookmarkChildrenFunction(), 21);
167  generator.NewEvent<GetBookmarksFunction>(kTestExtensionPath2,
168      new GetBookmarksFunction(), 33);
169  const uint32 writes_by_extension2 = 8;
170
171  ExtensionsActivityMonitor::Records results;
172  monitor->GetAndClearRecords(&results);
173
174  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);
175  std::string id2 = GetExtensionIdForPath(kTestExtensionPath2);
176
177  EXPECT_EQ(2U, results.size());
178  EXPECT_TRUE(results.end() != results.find(id1));
179  EXPECT_TRUE(results.end() != results.find(id2));
180  EXPECT_EQ(writes_by_extension1, results[id1].bookmark_write_count);
181  EXPECT_EQ(writes_by_extension2, results[id2].bookmark_write_count);
182
183  ui_loop()->DeleteSoon(FROM_HERE, monitor);
184}
185
186TEST_F(ExtensionsActivityMonitorTest, Put) {
187  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
188  BookmarkAPIEventGenerator generator;
189  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);
190  std::string id2 = GetExtensionIdForPath(kTestExtensionPath2);
191
192  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
193      new CreateBookmarkFunction(), 5);
194  generator.NewEvent<MoveBookmarkFunction>(kTestExtensionPath2,
195      new MoveBookmarkFunction(), 8);
196
197  ExtensionsActivityMonitor::Records results;
198  monitor->GetAndClearRecords(&results);
199
200  EXPECT_EQ(2U, results.size());
201  EXPECT_EQ(5U, results[id1].bookmark_write_count);
202  EXPECT_EQ(8U, results[id2].bookmark_write_count);
203
204  generator.NewEvent<GetBookmarksFunction>(kTestExtensionPath2,
205      new GetBookmarksFunction(), 3);
206  generator.NewEvent<UpdateBookmarkFunction>(kTestExtensionPath2,
207      new UpdateBookmarkFunction(), 2);
208
209  // Simulate a commit failure, which augments the active record set with the
210  // refugee records.
211  monitor->PutRecords(results);
212  ExtensionsActivityMonitor::Records new_records;
213  monitor->GetAndClearRecords(&new_records);
214
215  EXPECT_EQ(2U, results.size());
216  EXPECT_EQ(id1, new_records[id1].extension_id);
217  EXPECT_EQ(id2, new_records[id2].extension_id);
218  EXPECT_EQ(5U, new_records[id1].bookmark_write_count);
219  EXPECT_EQ(8U + 2U, new_records[id2].bookmark_write_count);
220  ui_loop()->DeleteSoon(FROM_HERE, monitor);
221}
222
223TEST_F(ExtensionsActivityMonitorTest, MultiGet) {
224  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
225  BookmarkAPIEventGenerator generator;
226  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);
227
228  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
229      new CreateBookmarkFunction(), 5);
230
231  ExtensionsActivityMonitor::Records results;
232  monitor->GetAndClearRecords(&results);
233
234  EXPECT_EQ(1U, results.size());
235  EXPECT_EQ(5U, results[id1].bookmark_write_count);
236
237  monitor->GetAndClearRecords(&results);
238  EXPECT_TRUE(results.empty());
239
240  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
241      new CreateBookmarkFunction(), 3);
242  monitor->GetAndClearRecords(&results);
243
244  EXPECT_EQ(1U, results.size());
245  EXPECT_EQ(3U, results[id1].bookmark_write_count);
246
247  ui_loop()->DeleteSoon(FROM_HERE, monitor);
248}
249