PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/content/browser/content_index/content_index_database_unittest.cc

https://github.com/chromium/chromium
C++ | 538 lines | 420 code | 94 blank | 24 comment | 5 complexity | e7d4cbe9591d94efe40c4b6d18ac9bc3 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. // Copyright 2019 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. #include "content/browser/content_index/content_index_database.h"
  5. #include <string>
  6. #include "base/run_loop.h"
  7. #include "base/test/bind.h"
  8. #include "base/test/metrics/histogram_tester.h"
  9. #include "content/browser/service_worker/embedded_worker_test_helper.h"
  10. #include "content/public/browser/content_index_provider.h"
  11. #include "content/public/test/browser_task_environment.h"
  12. #include "content/public/test/test_browser_context.h"
  13. #include "testing/gmock/include/gmock/gmock.h"
  14. #include "testing/gtest/include/gtest/gtest.h"
  15. #include "third_party/blink/public/common/storage_key/storage_key.h"
  16. #include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
  17. #include "third_party/skia/include/core/SkBitmap.h"
  18. #include "url/origin.h"
  19. namespace content {
  20. namespace {
  21. using ::testing::_;
  22. class MockContentIndexProvider : public ContentIndexProvider {
  23. public:
  24. MOCK_METHOD1(GetIconSizes,
  25. std::vector<gfx::Size>(blink::mojom::ContentCategory));
  26. MOCK_METHOD1(OnContentAdded, void(ContentIndexEntry entry));
  27. MOCK_METHOD3(OnContentDeleted,
  28. void(int64_t service_Worker_registration_id,
  29. const url::Origin& origin,
  30. const std::string& description_id));
  31. };
  32. class ContentIndexTestBrowserContext : public TestBrowserContext {
  33. public:
  34. ContentIndexTestBrowserContext()
  35. : delegate_(std::make_unique<MockContentIndexProvider>()) {}
  36. ~ContentIndexTestBrowserContext() override = default;
  37. MockContentIndexProvider* GetContentIndexProvider() override {
  38. return delegate_.get();
  39. }
  40. private:
  41. std::unique_ptr<MockContentIndexProvider> delegate_;
  42. };
  43. void DidRegisterServiceWorker(int64_t* out_service_worker_registration_id,
  44. base::OnceClosure quit_closure,
  45. blink::ServiceWorkerStatusCode status,
  46. const std::string& status_message,
  47. int64_t service_worker_registration_id) {
  48. DCHECK(out_service_worker_registration_id);
  49. EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status) << status_message;
  50. *out_service_worker_registration_id = service_worker_registration_id;
  51. std::move(quit_closure).Run();
  52. }
  53. void DidFindServiceWorkerRegistration(
  54. scoped_refptr<ServiceWorkerRegistration>* out_service_worker_registration,
  55. base::OnceClosure quit_closure,
  56. blink::ServiceWorkerStatusCode status,
  57. scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
  58. DCHECK(out_service_worker_registration);
  59. EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
  60. << blink::ServiceWorkerStatusToString(status);
  61. *out_service_worker_registration = service_worker_registration;
  62. std::move(quit_closure).Run();
  63. }
  64. void DatabaseErrorCallback(base::OnceClosure quit_closure,
  65. blink::mojom::ContentIndexError* out_error,
  66. blink::mojom::ContentIndexError error) {
  67. *out_error = error;
  68. std::move(quit_closure).Run();
  69. }
  70. void GetDescriptionsCallback(
  71. base::OnceClosure quit_closure,
  72. blink::mojom::ContentIndexError* out_error,
  73. std::vector<blink::mojom::ContentDescriptionPtr>* out_descriptions,
  74. blink::mojom::ContentIndexError error,
  75. std::vector<blink::mojom::ContentDescriptionPtr> descriptions) {
  76. if (out_error)
  77. *out_error = error;
  78. DCHECK(out_descriptions);
  79. *out_descriptions = std::move(descriptions);
  80. std::move(quit_closure).Run();
  81. }
  82. std::vector<SkBitmap> CreateTestIcons() {
  83. std::vector<SkBitmap> icons(2);
  84. icons[0].allocN32Pixels(42, 42);
  85. icons[1].allocN32Pixels(24, 24);
  86. return icons;
  87. }
  88. } // namespace
  89. class ContentIndexDatabaseTest : public ::testing::Test {
  90. public:
  91. ContentIndexDatabaseTest()
  92. : task_environment_(BrowserTaskEnvironment::IO_MAINLOOP),
  93. embedded_worker_test_helper_(base::FilePath() /* in memory */) {}
  94. ContentIndexDatabaseTest(const ContentIndexDatabaseTest&) = delete;
  95. ContentIndexDatabaseTest& operator=(const ContentIndexDatabaseTest&) = delete;
  96. ~ContentIndexDatabaseTest() override = default;
  97. void SetUp() override {
  98. // Register Service Worker.
  99. service_worker_registration_id_ = RegisterServiceWorker(origin_);
  100. ASSERT_NE(service_worker_registration_id_,
  101. blink::mojom::kInvalidServiceWorkerRegistrationId);
  102. database_ = std::make_unique<ContentIndexDatabase>(
  103. &browser_context_, embedded_worker_test_helper_.context_wrapper());
  104. }
  105. blink::mojom::ContentDescriptionPtr CreateDescription(const std::string& id) {
  106. auto icon_definition = blink::mojom::ContentIconDefinition::New();
  107. icon_definition->src = "https://example.com/image.png";
  108. icon_definition->type = "image/png";
  109. std::vector<blink::mojom::ContentIconDefinitionPtr> icons;
  110. icons.push_back(std::move(icon_definition));
  111. return blink::mojom::ContentDescription::New(
  112. id, "title", "description", blink::mojom::ContentCategory::HOME_PAGE,
  113. std::move(icons), "https://example.com");
  114. }
  115. blink::mojom::ContentIndexError AddEntry(
  116. blink::mojom::ContentDescriptionPtr description,
  117. std::vector<SkBitmap> icons = CreateTestIcons()) {
  118. base::RunLoop run_loop;
  119. blink::mojom::ContentIndexError error;
  120. database_->AddEntry(
  121. service_worker_registration_id_, origin_,
  122. /* is_top_level_context= */ true, std::move(description),
  123. std::move(icons), launch_url(),
  124. base::BindOnce(&DatabaseErrorCallback, run_loop.QuitClosure(), &error));
  125. run_loop.Run();
  126. return error;
  127. }
  128. blink::mojom::ContentIndexError DeleteEntry(const std::string& id) {
  129. base::RunLoop run_loop;
  130. blink::mojom::ContentIndexError error;
  131. database_->DeleteEntry(
  132. service_worker_registration_id_, origin_, id,
  133. base::BindOnce(&DatabaseErrorCallback, run_loop.QuitClosure(), &error));
  134. run_loop.Run();
  135. return error;
  136. }
  137. std::vector<blink::mojom::ContentDescriptionPtr> GetDescriptions(
  138. blink::mojom::ContentIndexError* out_error = nullptr) {
  139. base::RunLoop run_loop;
  140. std::vector<blink::mojom::ContentDescriptionPtr> descriptions;
  141. database_->GetDescriptions(
  142. service_worker_registration_id_, origin_,
  143. base::BindOnce(&GetDescriptionsCallback, run_loop.QuitClosure(),
  144. out_error, &descriptions));
  145. run_loop.Run();
  146. return descriptions;
  147. }
  148. std::vector<SkBitmap> GetIcons(const std::string& id) {
  149. base::RunLoop run_loop;
  150. std::vector<SkBitmap> out_icons;
  151. database_->GetIcons(
  152. service_worker_registration_id_, id,
  153. base::BindLambdaForTesting([&](std::vector<SkBitmap> icons) {
  154. out_icons = std::move(icons);
  155. run_loop.Quit();
  156. }));
  157. run_loop.Run();
  158. return out_icons;
  159. }
  160. std::vector<ContentIndexEntry> GetAllEntries() {
  161. base::RunLoop run_loop;
  162. std::vector<ContentIndexEntry> out_entries;
  163. database_->GetAllEntries(
  164. base::BindLambdaForTesting([&](blink::mojom::ContentIndexError error,
  165. std::vector<ContentIndexEntry> entries) {
  166. ASSERT_EQ(error, blink::mojom::ContentIndexError::NONE);
  167. out_entries = std::move(entries);
  168. run_loop.Quit();
  169. }));
  170. run_loop.Run();
  171. return out_entries;
  172. }
  173. std::unique_ptr<ContentIndexEntry> GetEntry(
  174. const std::string& description_id) {
  175. base::RunLoop run_loop;
  176. std::unique_ptr<ContentIndexEntry> out_entry;
  177. database_->GetEntry(service_worker_registration_id_, description_id,
  178. base::BindLambdaForTesting(
  179. [&](absl::optional<ContentIndexEntry> entry) {
  180. if (entry)
  181. out_entry = std::make_unique<ContentIndexEntry>(
  182. std::move(*entry));
  183. run_loop.Quit();
  184. }));
  185. run_loop.Run();
  186. return out_entry;
  187. }
  188. MockContentIndexProvider* provider() {
  189. return browser_context_.GetContentIndexProvider();
  190. }
  191. int64_t service_worker_registration_id() {
  192. return service_worker_registration_id_;
  193. }
  194. void set_service_worker_registration_id(
  195. int64_t service_worker_registration_id) {
  196. service_worker_registration_id_ = service_worker_registration_id;
  197. }
  198. ContentIndexDatabase* database() { return database_.get(); }
  199. BrowserTaskEnvironment& task_environment() { return task_environment_; }
  200. const url::Origin& origin() { return origin_; }
  201. GURL launch_url() { return origin_.GetURL(); }
  202. int64_t RegisterServiceWorker(const url::Origin& origin) {
  203. GURL script_url(origin.GetURL().spec() + "sw.js");
  204. int64_t service_worker_registration_id =
  205. blink::mojom::kInvalidServiceWorkerRegistrationId;
  206. {
  207. blink::mojom::ServiceWorkerRegistrationOptions options;
  208. options.scope = origin.GetURL();
  209. blink::StorageKey key(origin);
  210. base::RunLoop run_loop;
  211. embedded_worker_test_helper_.context()->RegisterServiceWorker(
  212. script_url, key, options,
  213. blink::mojom::FetchClientSettingsObject::New(),
  214. base::BindOnce(&DidRegisterServiceWorker,
  215. &service_worker_registration_id,
  216. run_loop.QuitClosure()),
  217. /*requesting_frame_id=*/GlobalRenderFrameHostId());
  218. run_loop.Run();
  219. }
  220. if (service_worker_registration_id ==
  221. blink::mojom::kInvalidServiceWorkerRegistrationId) {
  222. ADD_FAILURE() << "Could not obtain a valid Service Worker registration";
  223. return blink::mojom::kInvalidServiceWorkerRegistrationId;
  224. }
  225. {
  226. base::RunLoop run_loop;
  227. embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
  228. service_worker_registration_id, blink::StorageKey(origin),
  229. base::BindOnce(&DidFindServiceWorkerRegistration,
  230. &service_worker_registration_,
  231. run_loop.QuitClosure()));
  232. run_loop.Run();
  233. }
  234. // Wait for the worker to be activated.
  235. base::RunLoop().RunUntilIdle();
  236. if (!service_worker_registration_) {
  237. ADD_FAILURE() << "Could not find the new Service Worker registration.";
  238. return blink::mojom::kInvalidServiceWorkerRegistrationId;
  239. }
  240. return service_worker_registration_id;
  241. }
  242. private:
  243. BrowserTaskEnvironment task_environment_; // Must be first member.
  244. ContentIndexTestBrowserContext browser_context_;
  245. url::Origin origin_ = url::Origin::Create(GURL("https://example.com"));
  246. int64_t service_worker_registration_id_ =
  247. blink::mojom::kInvalidServiceWorkerRegistrationId;
  248. EmbeddedWorkerTestHelper embedded_worker_test_helper_;
  249. scoped_refptr<ServiceWorkerRegistration> service_worker_registration_;
  250. std::unique_ptr<ContentIndexDatabase> database_;
  251. };
  252. TEST_F(ContentIndexDatabaseTest, DatabaseOperations) {
  253. // Initially database will be empty.
  254. {
  255. blink::mojom::ContentIndexError error;
  256. auto descriptions = GetDescriptions(&error);
  257. EXPECT_TRUE(descriptions.empty());
  258. EXPECT_EQ(error, blink::mojom::ContentIndexError::NONE);
  259. }
  260. // Insert entries and expect to find them.
  261. EXPECT_EQ(AddEntry(CreateDescription("id1")),
  262. blink::mojom::ContentIndexError::NONE);
  263. EXPECT_EQ(AddEntry(CreateDescription("id2")),
  264. blink::mojom::ContentIndexError::NONE);
  265. EXPECT_EQ(GetDescriptions().size(), 2u);
  266. // Remove an entry.
  267. EXPECT_EQ(DeleteEntry("id2"), blink::mojom::ContentIndexError::NONE);
  268. // Inspect the last remaining element.
  269. auto descriptions = GetDescriptions();
  270. ASSERT_EQ(descriptions.size(), 1u);
  271. auto expected_description = CreateDescription("id1");
  272. EXPECT_TRUE(descriptions[0]->Equals(*expected_description));
  273. }
  274. TEST_F(ContentIndexDatabaseTest, DatabaseOperationsBadSWID) {
  275. url::Origin other_origin = url::Origin::Create(GURL("https://other.com"));
  276. int64_t other_service_worker_registration_id =
  277. RegisterServiceWorker(other_origin);
  278. ASSERT_NE(other_service_worker_registration_id,
  279. blink::mojom::kInvalidServiceWorkerRegistrationId);
  280. set_service_worker_registration_id(other_service_worker_registration_id);
  281. blink::mojom::ContentIndexError error;
  282. auto descriptions = GetDescriptions(&error);
  283. EXPECT_TRUE(descriptions.empty());
  284. EXPECT_EQ(error, blink::mojom::ContentIndexError::STORAGE_ERROR);
  285. EXPECT_EQ(AddEntry(CreateDescription("id1")),
  286. blink::mojom::ContentIndexError::STORAGE_ERROR);
  287. EXPECT_EQ(DeleteEntry("id2"), blink::mojom::ContentIndexError::STORAGE_ERROR);
  288. }
  289. TEST_F(ContentIndexDatabaseTest, AddDuplicateIdWillOverwrite) {
  290. auto description1 = CreateDescription("id");
  291. description1->title = "title1";
  292. auto description2 = CreateDescription("id");
  293. description2->title = "title2";
  294. EXPECT_EQ(AddEntry(std::move(description1)),
  295. blink::mojom::ContentIndexError::NONE);
  296. EXPECT_EQ(AddEntry(std::move(description2)),
  297. blink::mojom::ContentIndexError::NONE);
  298. auto descriptions = GetDescriptions();
  299. ASSERT_EQ(descriptions.size(), 1u);
  300. EXPECT_EQ(descriptions[0]->id, "id");
  301. EXPECT_EQ(descriptions[0]->title, "title2");
  302. }
  303. TEST_F(ContentIndexDatabaseTest, DeleteNonExistentEntry) {
  304. auto descriptions = GetDescriptions();
  305. EXPECT_TRUE(descriptions.empty());
  306. EXPECT_EQ(DeleteEntry("id"), blink::mojom::ContentIndexError::NONE);
  307. }
  308. TEST_F(ContentIndexDatabaseTest, ProviderUpdated) {
  309. {
  310. std::unique_ptr<ContentIndexEntry> out_entry;
  311. EXPECT_CALL(*provider(), OnContentAdded(_))
  312. .WillOnce(testing::Invoke([&](auto entry) {
  313. out_entry = std::make_unique<ContentIndexEntry>(std::move(entry));
  314. }));
  315. EXPECT_EQ(AddEntry(CreateDescription("id")),
  316. blink::mojom::ContentIndexError::NONE);
  317. // Wait for the provider to receive the OnContentAdded event.
  318. task_environment().RunUntilIdle();
  319. ASSERT_TRUE(out_entry);
  320. ASSERT_TRUE(out_entry->description);
  321. EXPECT_EQ(out_entry->service_worker_registration_id,
  322. service_worker_registration_id());
  323. EXPECT_EQ(out_entry->description->id, "id");
  324. EXPECT_EQ(out_entry->launch_url, launch_url());
  325. EXPECT_FALSE(out_entry->registration_time.is_null());
  326. }
  327. {
  328. EXPECT_CALL(*provider(), OnContentDeleted(service_worker_registration_id(),
  329. origin(), "id"));
  330. EXPECT_EQ(DeleteEntry("id"), blink::mojom::ContentIndexError::NONE);
  331. task_environment().RunUntilIdle();
  332. }
  333. }
  334. TEST_F(ContentIndexDatabaseTest, IconLifetimeTiedToEntry) {
  335. // Initially we don't have an icon.
  336. EXPECT_TRUE(GetIcons("id").empty());
  337. EXPECT_EQ(AddEntry(CreateDescription("id")),
  338. blink::mojom::ContentIndexError::NONE);
  339. auto icons = GetIcons("id");
  340. ASSERT_EQ(icons.size(), 2u);
  341. if (icons[0].width() > icons[1].width())
  342. std::swap(icons[0], icons[1]);
  343. EXPECT_FALSE(icons[0].isNull());
  344. EXPECT_FALSE(icons[0].drawsNothing());
  345. EXPECT_EQ(icons[0].width(), 24);
  346. EXPECT_EQ(icons[0].height(), 24);
  347. EXPECT_FALSE(icons[1].isNull());
  348. EXPECT_FALSE(icons[1].drawsNothing());
  349. EXPECT_EQ(icons[1].width(), 42);
  350. EXPECT_EQ(icons[1].height(), 42);
  351. EXPECT_EQ(DeleteEntry("id"), blink::mojom::ContentIndexError::NONE);
  352. EXPECT_TRUE(GetIcons("id").empty());
  353. }
  354. TEST_F(ContentIndexDatabaseTest, NoIconsAreSupported) {
  355. // Initially we don't have an icon.
  356. EXPECT_TRUE(GetIcons("id").empty());
  357. // Create an entry with no icons.
  358. EXPECT_EQ(AddEntry(CreateDescription("id"), /* icons= */ {}),
  359. blink::mojom::ContentIndexError::NONE);
  360. // No icons should be found.
  361. EXPECT_TRUE(GetIcons("id").empty());
  362. }
  363. TEST_F(ContentIndexDatabaseTest, GetEntries) {
  364. // Initially there are no entries.
  365. EXPECT_FALSE(GetEntry("any-id"));
  366. EXPECT_TRUE(GetAllEntries().empty());
  367. std::unique_ptr<ContentIndexEntry> added_entry;
  368. {
  369. EXPECT_CALL(*provider(), OnContentAdded(_))
  370. .WillOnce(testing::Invoke([&](auto entry) {
  371. added_entry = std::make_unique<ContentIndexEntry>(std::move(entry));
  372. }));
  373. EXPECT_EQ(AddEntry(CreateDescription("id")),
  374. blink::mojom::ContentIndexError::NONE);
  375. base::RunLoop().RunUntilIdle();
  376. ASSERT_TRUE(added_entry);
  377. }
  378. // Check the notified entries match the queried entries.
  379. {
  380. auto entry = GetEntry("id");
  381. ASSERT_TRUE(entry);
  382. EXPECT_TRUE(entry->description->Equals(*added_entry->description));
  383. auto entries = GetAllEntries();
  384. ASSERT_EQ(entries.size(), 1u);
  385. EXPECT_TRUE(entries[0].description->Equals(*added_entry->description));
  386. }
  387. // Add one more entry.
  388. {
  389. EXPECT_CALL(*provider(), OnContentAdded(_));
  390. EXPECT_EQ(AddEntry(CreateDescription("id-2")),
  391. blink::mojom::ContentIndexError::NONE);
  392. auto entries = GetAllEntries();
  393. EXPECT_EQ(entries.size(), 2u);
  394. }
  395. }
  396. TEST_F(ContentIndexDatabaseTest, BlockedOriginsCannotRegisterContent) {
  397. // Initially adding is fine.
  398. EXPECT_EQ(AddEntry(CreateDescription("id1")),
  399. blink::mojom::ContentIndexError::NONE);
  400. // Two delete events were dispatched.
  401. database()->BlockOrigin(origin());
  402. database()->BlockOrigin(origin());
  403. // Content can't be registered while the origin is blocked.
  404. EXPECT_EQ(AddEntry(CreateDescription("id2")),
  405. blink::mojom::ContentIndexError::STORAGE_ERROR);
  406. // First event dispatch completed.
  407. database()->UnblockOrigin(origin());
  408. // Content still can't be registered.
  409. EXPECT_EQ(AddEntry(CreateDescription("id3")),
  410. blink::mojom::ContentIndexError::STORAGE_ERROR);
  411. // Last event dispatch completed.
  412. database()->UnblockOrigin(origin());
  413. // Registering is OK now.
  414. EXPECT_EQ(AddEntry(CreateDescription("id4")),
  415. blink::mojom::ContentIndexError::NONE);
  416. }
  417. TEST_F(ContentIndexDatabaseTest, UmaRecorded) {
  418. base::HistogramTester histogram_tester;
  419. EXPECT_EQ(AddEntry(CreateDescription("id")),
  420. blink::mojom::ContentIndexError::NONE);
  421. histogram_tester.ExpectBucketCount("ContentIndex.Database.Add",
  422. blink::ServiceWorkerStatusCode::kOk, 1);
  423. EXPECT_FALSE(GetIcons("id").empty());
  424. histogram_tester.ExpectBucketCount("ContentIndex.Database.GetIcon",
  425. blink::ServiceWorkerStatusCode::kOk, 1);
  426. EXPECT_EQ(GetAllEntries().size(), 1u);
  427. histogram_tester.ExpectBucketCount("ContentIndex.Database.GetAllEntries",
  428. blink::ServiceWorkerStatusCode::kOk, 1);
  429. EXPECT_EQ(GetDescriptions().size(), 1u);
  430. histogram_tester.ExpectBucketCount("ContentIndex.Database.GetDescriptions",
  431. blink::ServiceWorkerStatusCode::kOk, 1);
  432. EXPECT_TRUE(GetEntry("id"));
  433. histogram_tester.ExpectBucketCount("ContentIndex.Database.GetEntry",
  434. blink::ServiceWorkerStatusCode::kOk, 1);
  435. EXPECT_EQ(DeleteEntry("id"), blink::mojom::ContentIndexError::NONE);
  436. histogram_tester.ExpectBucketCount("ContentIndex.Database.Delete",
  437. blink::ServiceWorkerStatusCode::kOk, 1);
  438. database()->BlockOrigin(origin());
  439. AddEntry(CreateDescription("id"));
  440. histogram_tester.ExpectBucketCount("ContentIndex.RegistrationBlocked",
  441. blink::mojom::ContentCategory::HOME_PAGE,
  442. 1);
  443. }
  444. } // namespace content