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