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