Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / components / dom_distiller / core / dom_distiller_store_unittest.cc
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 "components/dom_distiller/core/dom_distiller_store.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/time/time.h"
13 #include "components/dom_distiller/core/article_entry.h"
14 #include "components/dom_distiller/core/dom_distiller_test_util.h"
15 #include "components/dom_distiller/core/fake_db.h"
16 #include "sync/api/attachments/attachment_id.h"
17 #include "sync/api/attachments/attachment_service_proxy_for_test.h"
18 #include "sync/protocol/sync.pb.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 using base::Time;
23 using dom_distiller::test::FakeDB;
24 using sync_pb::EntitySpecifics;
25 using syncer::ModelType;
26 using syncer::SyncChange;
27 using syncer::SyncChangeList;
28 using syncer::SyncChangeProcessor;
29 using syncer::SyncData;
30 using syncer::SyncDataList;
31 using syncer::SyncError;
32 using syncer::SyncErrorFactory;
33 using testing::AssertionFailure;
34 using testing::AssertionResult;
35 using testing::AssertionSuccess;
36
37 namespace dom_distiller {
38
39 namespace {
40
41 const ModelType kDomDistillerModelType = syncer::ARTICLES;
42
43 typedef base::hash_map<std::string, ArticleEntry> EntryMap;
44
45 void AddEntry(const ArticleEntry& e, EntryMap* map) {
46   (*map)[e.entry_id()] = e;
47 }
48
49 class FakeSyncErrorFactory : public syncer::SyncErrorFactory {
50  public:
51   virtual syncer::SyncError CreateAndUploadError(
52       const tracked_objects::Location& location,
53       const std::string& message) OVERRIDE {
54     return syncer::SyncError();
55   }
56 };
57
58 class FakeSyncChangeProcessor : public syncer::SyncChangeProcessor {
59  public:
60   explicit FakeSyncChangeProcessor(EntryMap* model) : model_(model) {}
61
62   virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
63       OVERRIDE {
64     ADD_FAILURE() << "FakeSyncChangeProcessor::GetAllSyncData not implemented.";
65     return syncer::SyncDataList();
66   }
67
68   virtual SyncError ProcessSyncChanges(
69       const tracked_objects::Location&,
70       const syncer::SyncChangeList& changes) OVERRIDE {
71     for (SyncChangeList::const_iterator it = changes.begin();
72          it != changes.end();
73          ++it) {
74       AddEntry(GetEntryFromChange(*it), model_);
75     }
76     return SyncError();
77   }
78
79  private:
80   EntryMap* model_;
81 };
82
83 ArticleEntry CreateEntry(std::string entry_id,
84                          std::string page_url1,
85                          std::string page_url2,
86                          std::string page_url3) {
87   ArticleEntry entry;
88   entry.set_entry_id(entry_id);
89   if (!page_url1.empty()) {
90     ArticleEntryPage* page = entry.add_pages();
91     page->set_url(page_url1);
92   }
93   if (!page_url2.empty()) {
94     ArticleEntryPage* page = entry.add_pages();
95     page->set_url(page_url2);
96   }
97   if (!page_url3.empty()) {
98     ArticleEntryPage* page = entry.add_pages();
99     page->set_url(page_url3);
100   }
101   return entry;
102 }
103
104 ArticleEntry GetSampleEntry(int id) {
105   static ArticleEntry entries[] = {
106       CreateEntry("entry0", "example.com/1", "example.com/2", "example.com/3"),
107       CreateEntry("entry1", "example.com/1", "", ""),
108       CreateEntry("entry2", "example.com/p1", "example.com/p2", ""),
109       CreateEntry("entry3", "example.com/something/all", "", ""),
110       CreateEntry("entry4", "example.com/somethingelse/1", "", ""),
111       CreateEntry("entry5", "rock.example.com/p1", "rock.example.com/p2", ""),
112       CreateEntry("entry7", "example.com/entry7/1", "example.com/entry7/2", ""),
113       CreateEntry("entry8", "example.com/entry8/1", "", ""),
114       CreateEntry("entry9", "example.com/entry9/all", "", ""), };
115   EXPECT_LT(id, 9);
116   return entries[id % 9];
117 }
118
119 class MockDistillerObserver : public DomDistillerObserver {
120  public:
121   MOCK_METHOD1(ArticleEntriesUpdated, void(const std::vector<ArticleUpdate>&));
122   virtual ~MockDistillerObserver() {}
123 };
124
125 }  // namespace
126
127 class DomDistillerStoreTest : public testing::Test {
128  public:
129   virtual void SetUp() {
130     db_model_.clear();
131     sync_model_.clear();
132     store_model_.clear();
133     next_sync_id_ = 1;
134   }
135
136   virtual void TearDown() {
137     store_.reset();
138     fake_db_ = NULL;
139     fake_sync_processor_ = NULL;
140   }
141
142   // Creates a simple DomDistillerStore initialized with |store_model_| and
143   // with a FakeDB backed by |db_model_|.
144   void CreateStore() {
145     fake_db_ = new FakeDB(&db_model_);
146     store_.reset(test::util::CreateStoreWithFakeDB(fake_db_, store_model_));
147   }
148
149   void StartSyncing() {
150     fake_sync_processor_ = new FakeSyncChangeProcessor(&sync_model_);
151
152     store_->MergeDataAndStartSyncing(
153         kDomDistillerModelType,
154         SyncDataFromEntryMap(sync_model_),
155         make_scoped_ptr<SyncChangeProcessor>(fake_sync_processor_),
156         scoped_ptr<SyncErrorFactory>(new FakeSyncErrorFactory()));
157   }
158
159  protected:
160   SyncData CreateSyncData(const ArticleEntry& entry) {
161     EntitySpecifics specifics = SpecificsFromEntry(entry);
162     return SyncData::CreateRemoteData(
163         next_sync_id_++,
164         specifics,
165         Time::UnixEpoch(),
166         syncer::AttachmentIdList(),
167         syncer::AttachmentServiceProxyForTest::Create());
168   }
169
170   SyncDataList SyncDataFromEntryMap(const EntryMap& model) {
171     SyncDataList data;
172     for (EntryMap::const_iterator it = model.begin(); it != model.end(); ++it) {
173       data.push_back(CreateSyncData(it->second));
174     }
175     return data;
176   }
177
178   base::MessageLoop message_loop_;
179
180   EntryMap db_model_;
181   EntryMap sync_model_;
182   FakeDB::EntryMap store_model_;
183
184   scoped_ptr<DomDistillerStore> store_;
185
186   // Both owned by |store_|.
187   FakeDB* fake_db_;
188   FakeSyncChangeProcessor* fake_sync_processor_;
189
190   int64 next_sync_id_;
191 };
192
193 AssertionResult AreEntriesEqual(const EntryVector& entries,
194                                 EntryMap expected_entries) {
195   if (entries.size() != expected_entries.size())
196     return AssertionFailure() << "Expected " << expected_entries.size()
197                               << " entries but found " << entries.size();
198
199   for (EntryVector::const_iterator it = entries.begin(); it != entries.end();
200        ++it) {
201     EntryMap::iterator expected_it = expected_entries.find(it->entry_id());
202     if (expected_it == expected_entries.end()) {
203       return AssertionFailure() << "Found unexpected entry with id <"
204                                 << it->entry_id() << ">";
205     }
206     if (!AreEntriesEqual(expected_it->second, *it)) {
207       return AssertionFailure() << "Mismatched entry with id <"
208                                 << it->entry_id() << ">";
209     }
210     expected_entries.erase(expected_it);
211   }
212   return AssertionSuccess();
213 }
214
215 AssertionResult AreEntryMapsEqual(const EntryMap& left, const EntryMap& right) {
216   EntryVector entries;
217   for (EntryMap::const_iterator it = left.begin(); it != left.end(); ++it) {
218     entries.push_back(it->second);
219   }
220   return AreEntriesEqual(entries, right);
221 }
222
223 TEST_F(DomDistillerStoreTest, TestDatabaseLoad) {
224   AddEntry(GetSampleEntry(0), &db_model_);
225   AddEntry(GetSampleEntry(1), &db_model_);
226   AddEntry(GetSampleEntry(2), &db_model_);
227
228   CreateStore();
229
230   fake_db_->InitCallback(true);
231   EXPECT_EQ(fake_db_->GetDirectory(), FakeDB::DirectoryForTestDB());
232
233   fake_db_->LoadCallback(true);
234   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), db_model_));
235 }
236
237 TEST_F(DomDistillerStoreTest, TestDatabaseLoadMerge) {
238   AddEntry(GetSampleEntry(0), &db_model_);
239   AddEntry(GetSampleEntry(1), &db_model_);
240   AddEntry(GetSampleEntry(2), &db_model_);
241
242   AddEntry(GetSampleEntry(2), &store_model_);
243   AddEntry(GetSampleEntry(3), &store_model_);
244   AddEntry(GetSampleEntry(4), &store_model_);
245
246   EntryMap expected_model(db_model_);
247   AddEntry(GetSampleEntry(3), &expected_model);
248   AddEntry(GetSampleEntry(4), &expected_model);
249
250   CreateStore();
251   fake_db_->InitCallback(true);
252   fake_db_->LoadCallback(true);
253
254   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), expected_model));
255   EXPECT_TRUE(AreEntryMapsEqual(db_model_, expected_model));
256 }
257
258 TEST_F(DomDistillerStoreTest, TestAddAndRemoveEntry) {
259   CreateStore();
260   fake_db_->InitCallback(true);
261   fake_db_->LoadCallback(true);
262
263   EXPECT_TRUE(store_->GetEntries().empty());
264   EXPECT_TRUE(db_model_.empty());
265
266   store_->AddEntry(GetSampleEntry(0));
267
268   EntryMap expected_model;
269   AddEntry(GetSampleEntry(0), &expected_model);
270
271   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), expected_model));
272   EXPECT_TRUE(AreEntryMapsEqual(db_model_, expected_model));
273
274   store_->RemoveEntry(GetSampleEntry(0));
275   expected_model.clear();
276
277   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), expected_model));
278   EXPECT_TRUE(AreEntryMapsEqual(db_model_, expected_model));
279 }
280
281 TEST_F(DomDistillerStoreTest, TestAddAndUpdateEntry) {
282   CreateStore();
283   fake_db_->InitCallback(true);
284   fake_db_->LoadCallback(true);
285
286   EXPECT_TRUE(store_->GetEntries().empty());
287   EXPECT_TRUE(db_model_.empty());
288
289   store_->AddEntry(GetSampleEntry(0));
290
291   EntryMap expected_model;
292   AddEntry(GetSampleEntry(0), &expected_model);
293
294   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), expected_model));
295   EXPECT_TRUE(AreEntryMapsEqual(db_model_, expected_model));
296
297   EXPECT_FALSE(store_->UpdateEntry(GetSampleEntry(0)));
298
299   ArticleEntry updated_entry(GetSampleEntry(0));
300   updated_entry.set_title("updated title.");
301   EXPECT_TRUE(store_->UpdateEntry(updated_entry));
302   expected_model.clear();
303   AddEntry(updated_entry, &expected_model);
304
305   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), expected_model));
306   EXPECT_TRUE(AreEntryMapsEqual(db_model_, expected_model));
307
308   store_->RemoveEntry(updated_entry);
309   EXPECT_FALSE(store_->UpdateEntry(updated_entry));
310   EXPECT_FALSE(store_->UpdateEntry(GetSampleEntry(0)));
311 }
312
313 TEST_F(DomDistillerStoreTest, TestSyncMergeWithEmptyDatabase) {
314   AddEntry(GetSampleEntry(0), &sync_model_);
315   AddEntry(GetSampleEntry(1), &sync_model_);
316   AddEntry(GetSampleEntry(2), &sync_model_);
317
318   CreateStore();
319   fake_db_->InitCallback(true);
320   fake_db_->LoadCallback(true);
321
322   StartSyncing();
323
324   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), sync_model_));
325   EXPECT_TRUE(AreEntryMapsEqual(db_model_, sync_model_));
326 }
327
328 TEST_F(DomDistillerStoreTest, TestSyncMergeAfterDatabaseLoad) {
329   AddEntry(GetSampleEntry(0), &db_model_);
330   AddEntry(GetSampleEntry(1), &db_model_);
331   AddEntry(GetSampleEntry(2), &db_model_);
332
333   AddEntry(GetSampleEntry(2), &sync_model_);
334   AddEntry(GetSampleEntry(3), &sync_model_);
335   AddEntry(GetSampleEntry(4), &sync_model_);
336
337   EntryMap expected_model(db_model_);
338   AddEntry(GetSampleEntry(3), &expected_model);
339   AddEntry(GetSampleEntry(4), &expected_model);
340
341   CreateStore();
342   fake_db_->InitCallback(true);
343   fake_db_->LoadCallback(true);
344
345   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), db_model_));
346
347   StartSyncing();
348
349   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), expected_model));
350   EXPECT_TRUE(AreEntryMapsEqual(db_model_, expected_model));
351   EXPECT_TRUE(AreEntryMapsEqual(sync_model_, expected_model));
352 }
353
354 TEST_F(DomDistillerStoreTest, TestGetAllSyncData) {
355   AddEntry(GetSampleEntry(0), &db_model_);
356   AddEntry(GetSampleEntry(1), &db_model_);
357   AddEntry(GetSampleEntry(2), &db_model_);
358
359   AddEntry(GetSampleEntry(2), &sync_model_);
360   AddEntry(GetSampleEntry(3), &sync_model_);
361   AddEntry(GetSampleEntry(4), &sync_model_);
362
363   EntryMap expected_model(db_model_);
364   AddEntry(GetSampleEntry(3), &expected_model);
365   AddEntry(GetSampleEntry(4), &expected_model);
366
367   CreateStore();
368
369   fake_db_->InitCallback(true);
370   fake_db_->LoadCallback(true);
371
372   StartSyncing();
373
374   SyncDataList data = store_->GetAllSyncData(kDomDistillerModelType);
375   EntryVector entries;
376   for (SyncDataList::iterator it = data.begin(); it != data.end(); ++it) {
377     entries.push_back(EntryFromSpecifics(it->GetSpecifics()));
378   }
379   EXPECT_TRUE(AreEntriesEqual(entries, expected_model));
380 }
381
382 TEST_F(DomDistillerStoreTest, TestProcessSyncChanges) {
383   AddEntry(GetSampleEntry(0), &db_model_);
384   AddEntry(GetSampleEntry(1), &db_model_);
385   sync_model_ = db_model_;
386
387   EntryMap expected_model(db_model_);
388   AddEntry(GetSampleEntry(2), &expected_model);
389   AddEntry(GetSampleEntry(3), &expected_model);
390
391   CreateStore();
392
393   fake_db_->InitCallback(true);
394   fake_db_->LoadCallback(true);
395
396   StartSyncing();
397
398   SyncChangeList changes;
399   changes.push_back(SyncChange(
400       FROM_HERE, SyncChange::ACTION_ADD, CreateSyncData(GetSampleEntry(2))));
401   changes.push_back(SyncChange(
402       FROM_HERE, SyncChange::ACTION_ADD, CreateSyncData(GetSampleEntry(3))));
403
404   store_->ProcessSyncChanges(FROM_HERE, changes);
405
406   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), expected_model));
407   EXPECT_TRUE(AreEntryMapsEqual(db_model_, expected_model));
408 }
409
410 TEST_F(DomDistillerStoreTest, TestSyncMergeWithSecondDomDistillerStore) {
411   AddEntry(GetSampleEntry(0), &db_model_);
412   AddEntry(GetSampleEntry(1), &db_model_);
413   AddEntry(GetSampleEntry(2), &db_model_);
414
415   EntryMap other_db_model;
416   AddEntry(GetSampleEntry(2), &other_db_model);
417   AddEntry(GetSampleEntry(3), &other_db_model);
418   AddEntry(GetSampleEntry(4), &other_db_model);
419
420   EntryMap expected_model(db_model_);
421   AddEntry(GetSampleEntry(3), &expected_model);
422   AddEntry(GetSampleEntry(4), &expected_model);
423
424   CreateStore();
425
426   fake_db_->InitCallback(true);
427   fake_db_->LoadCallback(true);
428
429   FakeDB* other_fake_db = new FakeDB(&other_db_model);
430   scoped_ptr<DomDistillerStore> owned_other_store(new DomDistillerStore(
431       scoped_ptr<DomDistillerDatabaseInterface>(other_fake_db),
432       std::vector<ArticleEntry>(),
433       base::FilePath(FILE_PATH_LITERAL("/fake/other/path"))));
434   DomDistillerStore* other_store = owned_other_store.get();
435   other_fake_db->InitCallback(true);
436   other_fake_db->LoadCallback(true);
437
438   EXPECT_FALSE(AreEntriesEqual(store_->GetEntries(), expected_model));
439   EXPECT_FALSE(AreEntriesEqual(other_store->GetEntries(), expected_model));
440   ASSERT_TRUE(AreEntriesEqual(other_store->GetEntries(), other_db_model));
441
442   FakeSyncErrorFactory* other_error_factory = new FakeSyncErrorFactory();
443   store_->MergeDataAndStartSyncing(
444       kDomDistillerModelType,
445       SyncDataFromEntryMap(other_db_model),
446       owned_other_store.PassAs<SyncChangeProcessor>(),
447       make_scoped_ptr<SyncErrorFactory>(other_error_factory));
448
449   EXPECT_TRUE(AreEntriesEqual(store_->GetEntries(), expected_model));
450   EXPECT_TRUE(AreEntriesEqual(other_store->GetEntries(), expected_model));
451 }
452
453 TEST_F(DomDistillerStoreTest, TestObserver) {
454   CreateStore();
455   MockDistillerObserver observer;
456   store_->AddObserver(&observer);
457   fake_db_->InitCallback(true);
458   fake_db_->LoadCallback(true);
459   std::vector<DomDistillerObserver::ArticleUpdate> expected_updates;
460   DomDistillerObserver::ArticleUpdate update;
461   update.entry_id = GetSampleEntry(0).entry_id();
462   update.update_type = DomDistillerObserver::ArticleUpdate::ADD;
463   expected_updates.push_back(update);
464   EXPECT_CALL(
465       observer,
466       ArticleEntriesUpdated(test::util::HasExpectedUpdates(expected_updates)));
467   store_->AddEntry(GetSampleEntry(0));
468
469   expected_updates.clear();
470   update.entry_id = GetSampleEntry(1).entry_id();
471   update.update_type = DomDistillerObserver::ArticleUpdate::ADD;
472   expected_updates.push_back(update);
473   EXPECT_CALL(
474       observer,
475       ArticleEntriesUpdated(test::util::HasExpectedUpdates(expected_updates)));
476   store_->AddEntry(GetSampleEntry(1));
477
478   expected_updates.clear();
479   update.entry_id = GetSampleEntry(0).entry_id();
480   update.update_type = DomDistillerObserver::ArticleUpdate::REMOVE;
481   expected_updates.clear();
482   expected_updates.push_back(update);
483   EXPECT_CALL(
484       observer,
485       ArticleEntriesUpdated(test::util::HasExpectedUpdates(expected_updates)));
486   store_->RemoveEntry(GetSampleEntry(0));
487
488   // Add entry_id = 3 and update entry_id = 1.
489   expected_updates.clear();
490   SyncDataList change_data;
491   change_data.push_back(CreateSyncData(GetSampleEntry(3)));
492   ArticleEntry updated_entry(GetSampleEntry(1));
493   updated_entry.set_title("changed_title");
494   change_data.push_back(CreateSyncData(updated_entry));
495   update.entry_id = GetSampleEntry(3).entry_id();
496   update.update_type = DomDistillerObserver::ArticleUpdate::ADD;
497   expected_updates.push_back(update);
498   update.entry_id = GetSampleEntry(1).entry_id();
499   update.update_type = DomDistillerObserver::ArticleUpdate::UPDATE;
500   expected_updates.push_back(update);
501   EXPECT_CALL(
502       observer,
503       ArticleEntriesUpdated(test::util::HasExpectedUpdates(expected_updates)));
504
505   FakeSyncErrorFactory* fake_error_factory = new FakeSyncErrorFactory();
506   EntryMap fake_model;
507   FakeSyncChangeProcessor* fake_sync_change_processor =
508       new FakeSyncChangeProcessor(&fake_model);
509   store_->MergeDataAndStartSyncing(
510       kDomDistillerModelType,
511       change_data,
512       make_scoped_ptr<SyncChangeProcessor>(fake_sync_change_processor),
513       make_scoped_ptr<SyncErrorFactory>(fake_error_factory));
514 }
515
516 }  // namespace dom_distiller