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 18 using browser_sync::ExtensionsActivityMonitor; 19 namespace keys = extension_manifest_keys; 20 21 namespace { 22 23 const 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 30 const 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 37 const char* kTestExtensionVersion = "1.0.0.0"; 38 const char* kTestExtensionName = "foo extension"; 39 40 template <class FunctionType> 41 class 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 64 class 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 91 class 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 107 class 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 145 TEST_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 186 TEST_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 223 TEST_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