reading_list_store_unittest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // Copyright 2016 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 "components/reading_list/core/reading_list_store.h"
  5. #include <map>
  6. #include <set>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/run_loop.h"
  10. #include "base/test/simple_test_clock.h"
  11. #include "base/test/task_environment.h"
  12. #include "components/reading_list/core/reading_list_model_impl.h"
  13. #include "components/sync/test/mock_model_type_change_processor.h"
  14. #include "components/sync/test/model_type_store_test_util.h"
  15. #include "testing/gmock/include/gmock/gmock.h"
  16. #include "testing/gtest/include/gtest/gtest.h"
  17. namespace {
  18. using testing::_;
  19. MATCHER_P3(MatchesSpecifics,
  20. expected_title,
  21. expected_url,
  22. expected_status,
  23. "") {
  24. const sync_pb::ReadingListSpecifics& specifics =
  25. arg->specifics.reading_list();
  26. if (specifics.title() != expected_title) {
  27. *result_listener << "which has title \"" << specifics.title();
  28. return false;
  29. }
  30. if (specifics.url() != expected_url) {
  31. *result_listener << "which has URL " << specifics.url();
  32. return false;
  33. }
  34. if (specifics.status() != expected_status) {
  35. *result_listener << "which has unexpected status";
  36. return false;
  37. }
  38. return true;
  39. }
  40. // Tests that the transition from |entryA| to |entryB| is possible (|possible|
  41. // is true) or not.
  42. void ExpectAB(const sync_pb::ReadingListSpecifics& entryA,
  43. const sync_pb::ReadingListSpecifics& entryB,
  44. bool possible) {
  45. EXPECT_EQ(ReadingListStore::CompareEntriesForSync(entryA, entryB), possible);
  46. std::unique_ptr<ReadingListEntry> a =
  47. ReadingListEntry::FromReadingListSpecifics(entryA,
  48. base::Time::FromTimeT(10));
  49. std::unique_ptr<ReadingListEntry> b =
  50. ReadingListEntry::FromReadingListSpecifics(entryB,
  51. base::Time::FromTimeT(10));
  52. a->MergeWithEntry(*b);
  53. std::unique_ptr<sync_pb::ReadingListSpecifics> mergedEntry =
  54. a->AsReadingListSpecifics();
  55. if (possible) {
  56. // If transition is possible, the merge should be B.
  57. EXPECT_EQ(entryB.SerializeAsString(), mergedEntry->SerializeAsString());
  58. } else {
  59. // If transition is not possible, the transition shold be possible to the
  60. // merged state.
  61. EXPECT_TRUE(ReadingListStore::CompareEntriesForSync(entryA, *mergedEntry));
  62. EXPECT_TRUE(ReadingListStore::CompareEntriesForSync(entryB, *mergedEntry));
  63. }
  64. }
  65. base::Time AdvanceAndGetTime(base::SimpleTestClock* clock) {
  66. clock->Advance(base::Milliseconds(10));
  67. return clock->Now();
  68. }
  69. } // namespace
  70. class FakeModelTypeChangeProcessorObserver {
  71. public:
  72. virtual void Put(const std::string& client_tag,
  73. std::unique_ptr<syncer::EntityData> entity_data,
  74. syncer::MetadataChangeList* metadata_change_list) = 0;
  75. virtual void Delete(const std::string& client_tag,
  76. syncer::MetadataChangeList* metadata_change_list) = 0;
  77. };
  78. class ReadingListStoreTest : public testing::Test,
  79. public ReadingListStoreDelegate {
  80. protected:
  81. ReadingListStoreTest()
  82. : store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {
  83. ON_CALL(processor_, IsTrackingMetadata())
  84. .WillByDefault(testing::Return(true));
  85. ClearState();
  86. reading_list_store_ = std::make_unique<ReadingListStore>(
  87. syncer::ModelTypeStoreTestUtil::MoveStoreToFactory(std::move(store_)),
  88. processor_.CreateForwardingProcessor());
  89. model_ = std::make_unique<ReadingListModelImpl>(nullptr, nullptr, &clock_);
  90. reading_list_store_->SetReadingListModel(model_.get(), this, &clock_);
  91. base::RunLoop().RunUntilIdle();
  92. }
  93. void AssertCounts(int sync_add_called,
  94. int sync_remove_called,
  95. int sync_merge_called) {
  96. EXPECT_EQ(sync_add_called, sync_add_called_);
  97. EXPECT_EQ(sync_remove_called, sync_remove_called_);
  98. EXPECT_EQ(sync_merge_called, sync_merge_called_);
  99. }
  100. void ClearState() {
  101. sync_add_called_ = 0;
  102. sync_remove_called_ = 0;
  103. sync_merge_called_ = 0;
  104. sync_added_.clear();
  105. sync_removed_.clear();
  106. sync_merged_.clear();
  107. }
  108. // These three mathods handle callbacks from a ReadingListStore.
  109. void StoreLoaded(std::unique_ptr<ReadingListEntries> entries) override {}
  110. // Handle sync events.
  111. void SyncAddEntry(std::unique_ptr<ReadingListEntry> entry) override {
  112. sync_add_called_++;
  113. sync_added_[entry->URL().spec()] = entry->IsRead();
  114. }
  115. void SyncRemoveEntry(const GURL& gurl) override {
  116. sync_remove_called_++;
  117. sync_removed_.insert(gurl.spec());
  118. }
  119. ReadingListEntry* SyncMergeEntry(
  120. std::unique_ptr<ReadingListEntry> entry) override {
  121. sync_merge_called_++;
  122. sync_merged_[entry->URL().spec()] = entry->IsRead();
  123. return model_->SyncMergeEntry(std::move(entry));
  124. }
  125. // In memory model type store needs to be able to post tasks.
  126. base::test::SingleThreadTaskEnvironment task_environment_;
  127. testing::NiceMock<syncer::MockModelTypeChangeProcessor> processor_;
  128. std::unique_ptr<syncer::ModelTypeStore> store_;
  129. std::unique_ptr<ReadingListModelImpl> model_;
  130. base::SimpleTestClock clock_;
  131. std::unique_ptr<ReadingListStore> reading_list_store_;
  132. int sync_add_called_;
  133. int sync_remove_called_;
  134. int sync_merge_called_;
  135. std::map<std::string, bool> sync_added_;
  136. std::set<std::string> sync_removed_;
  137. std::map<std::string, bool> sync_merged_;
  138. };
  139. TEST_F(ReadingListStoreTest, CheckEmpties) {
  140. EXPECT_EQ(0ul, model_->size());
  141. }
  142. TEST_F(ReadingListStoreTest, SaveOneRead) {
  143. ReadingListEntry entry(GURL("http://read.example.com/"), "read title",
  144. AdvanceAndGetTime(&clock_));
  145. entry.SetRead(true, AdvanceAndGetTime(&clock_));
  146. AdvanceAndGetTime(&clock_);
  147. EXPECT_CALL(processor_,
  148. Put("http://read.example.com/",
  149. MatchesSpecifics("read title", "http://read.example.com/",
  150. sync_pb::ReadingListSpecifics::READ),
  151. _));
  152. reading_list_store_->SaveEntry(entry);
  153. AssertCounts(0, 0, 0);
  154. }
  155. TEST_F(ReadingListStoreTest, SaveOneUnread) {
  156. ReadingListEntry entry(GURL("http://unread.example.com/"), "unread title",
  157. AdvanceAndGetTime(&clock_));
  158. EXPECT_CALL(processor_,
  159. Put("http://unread.example.com/",
  160. MatchesSpecifics("unread title", "http://unread.example.com/",
  161. sync_pb::ReadingListSpecifics::UNSEEN),
  162. _));
  163. reading_list_store_->SaveEntry(entry);
  164. AssertCounts(0, 0, 0);
  165. }
  166. TEST_F(ReadingListStoreTest, SyncMergeOneEntry) {
  167. EXPECT_CALL(processor_, Put(_, _, _)).Times(0);
  168. syncer::EntityChangeList remote_input;
  169. ReadingListEntry entry(GURL("http://read.example.com/"), "read title",
  170. AdvanceAndGetTime(&clock_));
  171. entry.SetRead(true, AdvanceAndGetTime(&clock_));
  172. std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
  173. entry.AsReadingListSpecifics();
  174. syncer::EntityData data;
  175. *data.specifics.mutable_reading_list() = *specifics;
  176. remote_input.push_back(syncer::EntityChange::CreateAdd(
  177. "http://read.example.com/", std::move(data)));
  178. std::unique_ptr<syncer::MetadataChangeList> metadata_changes(
  179. reading_list_store_->CreateMetadataChangeList());
  180. auto error = reading_list_store_->MergeSyncData(std::move(metadata_changes),
  181. std::move(remote_input));
  182. AssertCounts(1, 0, 0);
  183. EXPECT_EQ(sync_added_.size(), 1u);
  184. EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u);
  185. EXPECT_EQ(sync_added_["http://read.example.com/"], true);
  186. }
  187. TEST_F(ReadingListStoreTest, ApplySyncChangesOneAdd) {
  188. EXPECT_CALL(processor_, Put(_, _, _)).Times(0);
  189. ReadingListEntry entry(GURL("http://read.example.com/"), "read title",
  190. AdvanceAndGetTime(&clock_));
  191. entry.SetRead(true, AdvanceAndGetTime(&clock_));
  192. std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
  193. entry.AsReadingListSpecifics();
  194. syncer::EntityData data;
  195. *data.specifics.mutable_reading_list() = *specifics;
  196. syncer::EntityChangeList add_changes;
  197. add_changes.push_back(syncer::EntityChange::CreateAdd(
  198. "http://read.example.com/", std::move(data)));
  199. auto error = reading_list_store_->ApplySyncChanges(
  200. reading_list_store_->CreateMetadataChangeList(), std::move(add_changes));
  201. AssertCounts(1, 0, 0);
  202. EXPECT_EQ(sync_added_.size(), 1u);
  203. EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u);
  204. EXPECT_EQ(sync_added_["http://read.example.com/"], true);
  205. }
  206. TEST_F(ReadingListStoreTest, ApplySyncChangesOneMerge) {
  207. AdvanceAndGetTime(&clock_);
  208. model_->AddEntry(GURL("http://unread.example.com/"), "unread title",
  209. reading_list::ADDED_VIA_CURRENT_APP);
  210. ReadingListEntry new_entry(GURL("http://unread.example.com/"), "unread title",
  211. AdvanceAndGetTime(&clock_));
  212. new_entry.SetRead(true, AdvanceAndGetTime(&clock_));
  213. std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
  214. new_entry.AsReadingListSpecifics();
  215. syncer::EntityData data;
  216. *data.specifics.mutable_reading_list() = *specifics;
  217. // ApplySyncChanges() must *not* result in any Put() calls - that would risk
  218. // triggering ping-pong between two syncing devices.
  219. EXPECT_CALL(processor_, Put(_, _, _)).Times(0);
  220. syncer::EntityChangeList add_changes;
  221. add_changes.push_back(syncer::EntityChange::CreateAdd(
  222. "http://unread.example.com/", std::move(data)));
  223. auto error = reading_list_store_->ApplySyncChanges(
  224. reading_list_store_->CreateMetadataChangeList(), std::move(add_changes));
  225. AssertCounts(0, 0, 1);
  226. EXPECT_EQ(sync_merged_.size(), 1u);
  227. EXPECT_EQ(sync_merged_.count("http://unread.example.com/"), 1u);
  228. EXPECT_EQ(sync_merged_["http://unread.example.com/"], true);
  229. }
  230. TEST_F(ReadingListStoreTest, ApplySyncChangesOneIgnored) {
  231. // Read entry but with unread URL as it must update the other one.
  232. ReadingListEntry old_entry(GURL("http://unread.example.com/"),
  233. "old unread title", AdvanceAndGetTime(&clock_));
  234. old_entry.SetRead(true, AdvanceAndGetTime(&clock_));
  235. AdvanceAndGetTime(&clock_);
  236. model_->AddEntry(GURL("http://unread.example.com/"), "new unread title",
  237. reading_list::ADDED_VIA_CURRENT_APP);
  238. AssertCounts(0, 0, 0);
  239. std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
  240. old_entry.AsReadingListSpecifics();
  241. syncer::EntityData data;
  242. *data.specifics.mutable_reading_list() = *specifics;
  243. // ApplySyncChanges() must *not* result in any Put() calls - that would risk
  244. // triggering ping-pong between two syncing devices.
  245. EXPECT_CALL(processor_, Put(_, _, _)).Times(0);
  246. syncer::EntityChangeList add_changes;
  247. add_changes.push_back(syncer::EntityChange::CreateAdd(
  248. "http://unread.example.com/", std::move(data)));
  249. auto error = reading_list_store_->ApplySyncChanges(
  250. reading_list_store_->CreateMetadataChangeList(), std::move(add_changes));
  251. AssertCounts(0, 0, 1);
  252. EXPECT_EQ(sync_merged_.size(), 1u);
  253. }
  254. TEST_F(ReadingListStoreTest, ApplySyncChangesOneRemove) {
  255. syncer::EntityChangeList delete_changes;
  256. delete_changes.push_back(
  257. syncer::EntityChange::CreateDelete("http://read.example.com/"));
  258. auto error = reading_list_store_->ApplySyncChanges(
  259. reading_list_store_->CreateMetadataChangeList(),
  260. std::move(delete_changes));
  261. AssertCounts(0, 1, 0);
  262. EXPECT_EQ(sync_removed_.size(), 1u);
  263. EXPECT_EQ(sync_removed_.count("http://read.example.com/"), 1u);
  264. }
  265. TEST_F(ReadingListStoreTest, CompareEntriesForSync) {
  266. sync_pb::ReadingListSpecifics entryA;
  267. sync_pb::ReadingListSpecifics entryB;
  268. entryA.set_entry_id("http://foo.bar/");
  269. entryB.set_entry_id("http://foo.bar/");
  270. entryA.set_url("http://foo.bar/");
  271. entryB.set_url("http://foo.bar/");
  272. entryA.set_title("Foo Bar");
  273. entryB.set_title("Foo Bar");
  274. entryA.set_status(sync_pb::ReadingListSpecifics::UNREAD);
  275. entryB.set_status(sync_pb::ReadingListSpecifics::UNREAD);
  276. entryA.set_creation_time_us(10);
  277. entryB.set_creation_time_us(10);
  278. entryA.set_estimated_read_time_seconds(420);
  279. entryB.set_estimated_read_time_seconds(420);
  280. entryA.set_first_read_time_us(50);
  281. entryB.set_first_read_time_us(50);
  282. entryA.set_update_time_us(100);
  283. entryB.set_update_time_us(100);
  284. entryA.set_update_title_time_us(110);
  285. entryB.set_update_title_time_us(110);
  286. // Equal entries can be submitted.
  287. ExpectAB(entryA, entryB, true);
  288. ExpectAB(entryB, entryA, true);
  289. // Try to update each field.
  290. // You cannot change the URL of an entry.
  291. entryA.set_url("http://foo.foo/");
  292. EXPECT_FALSE(ReadingListStore::CompareEntriesForSync(entryA, entryB));
  293. EXPECT_FALSE(ReadingListStore::CompareEntriesForSync(entryB, entryA));
  294. entryA.set_url("http://foo.bar/");
  295. // You can set a title to a title later in alphabetical order if the
  296. // update_title_time is the same. If a title has been more recently updated,
  297. // the only possible transition is to this one.
  298. entryA.set_title("");
  299. ExpectAB(entryA, entryB, true);
  300. ExpectAB(entryB, entryA, false);
  301. entryA.set_update_title_time_us(109);
  302. ExpectAB(entryA, entryB, true);
  303. ExpectAB(entryB, entryA, false);
  304. entryA.set_update_title_time_us(110);
  305. entryA.set_title("Foo Aar");
  306. ExpectAB(entryA, entryB, true);
  307. ExpectAB(entryB, entryA, false);
  308. entryA.set_update_title_time_us(109);
  309. ExpectAB(entryA, entryB, true);
  310. ExpectAB(entryB, entryA, false);
  311. entryA.set_update_title_time_us(110);
  312. entryA.set_title("Foo Ba");
  313. ExpectAB(entryA, entryB, true);
  314. ExpectAB(entryB, entryA, false);
  315. entryA.set_update_title_time_us(109);
  316. ExpectAB(entryA, entryB, true);
  317. ExpectAB(entryB, entryA, false);
  318. entryA.set_update_title_time_us(110);
  319. entryA.set_title("Foo Bas");
  320. ExpectAB(entryA, entryB, false);
  321. ExpectAB(entryB, entryA, true);
  322. entryA.set_update_title_time_us(109);
  323. ExpectAB(entryA, entryB, true);
  324. ExpectAB(entryB, entryA, false);
  325. entryA.set_update_title_time_us(110);
  326. entryA.set_title("Foo Bar");
  327. // Update times.
  328. entryA.set_creation_time_us(9);
  329. ExpectAB(entryA, entryB, true);
  330. ExpectAB(entryB, entryA, false);
  331. entryA.set_first_read_time_us(51);
  332. ExpectAB(entryA, entryB, true);
  333. ExpectAB(entryB, entryA, false);
  334. entryA.set_first_read_time_us(49);
  335. ExpectAB(entryA, entryB, true);
  336. ExpectAB(entryB, entryA, false);
  337. entryA.set_first_read_time_us(0);
  338. ExpectAB(entryA, entryB, true);
  339. ExpectAB(entryB, entryA, false);
  340. entryA.set_first_read_time_us(50);
  341. entryB.set_first_read_time_us(0);
  342. ExpectAB(entryA, entryB, true);
  343. ExpectAB(entryB, entryA, false);
  344. entryB.set_first_read_time_us(50);
  345. entryA.set_creation_time_us(10);
  346. entryA.set_first_read_time_us(51);
  347. ExpectAB(entryA, entryB, true);
  348. ExpectAB(entryB, entryA, false);
  349. entryA.set_first_read_time_us(0);
  350. ExpectAB(entryA, entryB, true);
  351. ExpectAB(entryB, entryA, false);
  352. entryA.set_first_read_time_us(50);
  353. entryA.set_creation_time_us(11);
  354. entryA.set_estimated_read_time_seconds(400);
  355. ExpectAB(entryB, entryA, true);
  356. ExpectAB(entryA, entryB, false);
  357. entryA.set_estimated_read_time_seconds(420);
  358. entryA.set_creation_time_us(10);
  359. entryA.set_update_time_us(99);
  360. ExpectAB(entryA, entryB, true);
  361. ExpectAB(entryB, entryA, false);
  362. sync_pb::ReadingListSpecifics::ReadingListEntryStatus status_oder[3] = {
  363. sync_pb::ReadingListSpecifics::UNSEEN,
  364. sync_pb::ReadingListSpecifics::UNREAD,
  365. sync_pb::ReadingListSpecifics::READ};
  366. for (int index_a = 0; index_a < 3; index_a++) {
  367. entryA.set_status(status_oder[index_a]);
  368. for (int index_b = 0; index_b < 3; index_b++) {
  369. entryB.set_status(status_oder[index_b]);
  370. ExpectAB(entryA, entryB, true);
  371. ExpectAB(entryB, entryA, false);
  372. }
  373. }
  374. entryA.set_update_time_us(100);
  375. for (int index_a = 0; index_a < 3; index_a++) {
  376. entryA.set_status(status_oder[index_a]);
  377. entryB.set_status(status_oder[index_a]);
  378. ExpectAB(entryA, entryB, true);
  379. ExpectAB(entryB, entryA, true);
  380. for (int index_b = index_a + 1; index_b < 3; index_b++) {
  381. entryB.set_status(status_oder[index_b]);
  382. ExpectAB(entryA, entryB, true);
  383. ExpectAB(entryB, entryA, false);
  384. }
  385. }
  386. }