35065fdf913f838b713b8cc1dd20e23684f6f28b
[platform/framework/web/crosswalk.git] / src / components / dom_distiller / core / dom_distiller_service_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_service.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/containers/hash_tables.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "components/dom_distiller/core/article_entry.h"
13 #include "components/dom_distiller/core/dom_distiller_model.h"
14 #include "components/dom_distiller/core/dom_distiller_store.h"
15 #include "components/dom_distiller/core/dom_distiller_test_util.h"
16 #include "components/dom_distiller/core/fake_db.h"
17 #include "components/dom_distiller/core/fake_distiller.h"
18 #include "components/dom_distiller/core/task_tracker.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 using testing::Invoke;
23 using testing::Return;
24 using testing::_;
25
26 namespace dom_distiller {
27 namespace test {
28
29 namespace {
30
31 class FakeViewRequestDelegate : public ViewRequestDelegate {
32  public:
33   virtual ~FakeViewRequestDelegate() {}
34   MOCK_METHOD1(OnArticleReady, void(DistilledPageProto* proto));
35 };
36
37 class MockDistillerObserver : public DomDistillerObserver {
38  public:
39   MOCK_METHOD1(ArticleEntriesUpdated, void(const std::vector<ArticleUpdate>&));
40   virtual ~MockDistillerObserver() {}
41 };
42
43 class MockArticleAvailableCallback {
44  public:
45   MOCK_METHOD1(DistillationCompleted, void(bool));
46 };
47
48 DomDistillerService::ArticleAvailableCallback ArticleCallback(
49     MockArticleAvailableCallback* callback) {
50   return base::Bind(&MockArticleAvailableCallback::DistillationCompleted,
51                     base::Unretained(callback));
52 }
53
54 void RunDistillerCallback(FakeDistiller* distiller,
55                           scoped_ptr<DistilledPageProto> proto) {
56   distiller->RunDistillerCallback(proto.Pass());
57   base::RunLoop().RunUntilIdle();
58 }
59
60 }  // namespace
61
62 class DomDistillerServiceTest : public testing::Test {
63  public:
64   virtual void SetUp() {
65     main_loop_.reset(new base::MessageLoop());
66     FakeDB* fake_db = new FakeDB(&db_model_);
67     FakeDB::EntryMap store_model;
68     store_ = test::util::CreateStoreWithFakeDB(fake_db, store_model);
69     distiller_factory_ = new MockDistillerFactory();
70     service_.reset(new DomDistillerService(
71         scoped_ptr<DomDistillerStoreInterface>(store_),
72         scoped_ptr<DistillerFactory>(distiller_factory_)));
73     fake_db->InitCallback(true);
74     fake_db->LoadCallback(true);
75   }
76
77   virtual void TearDown() {
78     base::RunLoop().RunUntilIdle();
79     store_ = NULL;
80     distiller_factory_ = NULL;
81     service_.reset();
82   }
83
84  protected:
85   // store is owned by service_.
86   DomDistillerStoreInterface* store_;
87   MockDistillerFactory* distiller_factory_;
88   scoped_ptr<DomDistillerService> service_;
89   scoped_ptr<base::MessageLoop> main_loop_;
90   FakeDB::EntryMap db_model_;
91 };
92
93 TEST_F(DomDistillerServiceTest, TestViewEntry) {
94   FakeDistiller* distiller = new FakeDistiller(false);
95   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
96       .WillOnce(Return(distiller));
97
98   GURL url("http://www.example.com/p1");
99   std::string entry_id("id0");
100   ArticleEntry entry;
101   entry.set_entry_id(entry_id);
102   entry.add_pages()->set_url(url.spec());
103
104   store_->AddEntry(entry);
105
106   FakeViewRequestDelegate viewer_delegate;
107   scoped_ptr<ViewerHandle> handle =
108       service_->ViewEntry(&viewer_delegate, entry_id);
109
110   ASSERT_FALSE(distiller->GetCallback().is_null());
111
112   scoped_ptr<DistilledPageProto> proto(new DistilledPageProto);
113   EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get()));
114
115   RunDistillerCallback(distiller, proto.Pass());
116 }
117
118 TEST_F(DomDistillerServiceTest, TestViewUrl) {
119   FakeDistiller* distiller = new FakeDistiller(false);
120   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
121       .WillOnce(Return(distiller));
122
123   FakeViewRequestDelegate viewer_delegate;
124   GURL url("http://www.example.com/p1");
125   scoped_ptr<ViewerHandle> handle = service_->ViewUrl(&viewer_delegate, url);
126
127   ASSERT_FALSE(distiller->GetCallback().is_null());
128   EXPECT_EQ(url, distiller->GetUrl());
129
130   scoped_ptr<DistilledPageProto> proto(new DistilledPageProto);
131   EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get()));
132
133   RunDistillerCallback(distiller, proto.Pass());
134 }
135
136 TEST_F(DomDistillerServiceTest, TestMultipleViewUrl) {
137   FakeDistiller* distiller = new FakeDistiller(false);
138   FakeDistiller* distiller2 = new FakeDistiller(false);
139   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
140       .WillOnce(Return(distiller))
141       .WillOnce(Return(distiller2));
142
143   FakeViewRequestDelegate viewer_delegate;
144   FakeViewRequestDelegate viewer_delegate2;
145
146   GURL url("http://www.example.com/p1");
147   GURL url2("http://www.example.com/a/p1");
148
149   scoped_ptr<ViewerHandle> handle = service_->ViewUrl(&viewer_delegate, url);
150   scoped_ptr<ViewerHandle> handle2 = service_->ViewUrl(&viewer_delegate2, url2);
151
152   ASSERT_FALSE(distiller->GetCallback().is_null());
153   EXPECT_EQ(url, distiller->GetUrl());
154
155   scoped_ptr<DistilledPageProto> proto(new DistilledPageProto);
156   EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get()));
157
158   RunDistillerCallback(distiller, proto.Pass());
159
160   ASSERT_FALSE(distiller2->GetCallback().is_null());
161   EXPECT_EQ(url2, distiller2->GetUrl());
162
163   scoped_ptr<DistilledPageProto> proto2(new DistilledPageProto);
164   EXPECT_CALL(viewer_delegate2, OnArticleReady(proto2.get()));
165
166   RunDistillerCallback(distiller2, proto2.Pass());
167 }
168
169 TEST_F(DomDistillerServiceTest, TestViewUrlCancelled) {
170   FakeDistiller* distiller = new FakeDistiller(false);
171   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
172       .WillOnce(Return(distiller));
173
174   bool distiller_destroyed = false;
175   EXPECT_CALL(*distiller, Die())
176       .WillOnce(testing::Assign(&distiller_destroyed, true));
177
178   FakeViewRequestDelegate viewer_delegate;
179   GURL url("http://www.example.com/p1");
180   scoped_ptr<ViewerHandle> handle = service_->ViewUrl(&viewer_delegate, url);
181
182   ASSERT_FALSE(distiller->GetCallback().is_null());
183   EXPECT_EQ(url, distiller->GetUrl());
184
185   EXPECT_CALL(viewer_delegate, OnArticleReady(_)).Times(0);
186
187   EXPECT_FALSE(distiller_destroyed);
188
189   handle.reset();
190   base::RunLoop().RunUntilIdle();
191   EXPECT_TRUE(distiller_destroyed);
192 }
193
194 TEST_F(DomDistillerServiceTest, TestAddAndRemoveEntry) {
195   FakeDistiller* distiller = new FakeDistiller(false);
196   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
197       .WillOnce(Return(distiller));
198
199   GURL url("http://www.example.com/p1");
200
201   MockArticleAvailableCallback article_cb;
202   EXPECT_CALL(article_cb, DistillationCompleted(true));
203
204   std::string entry_id = service_->AddToList(url, ArticleCallback(&article_cb));
205
206   ArticleEntry entry;
207   EXPECT_TRUE(store_->GetEntryByUrl(url, &entry));
208   EXPECT_EQ(entry.entry_id(), entry_id);
209
210   ASSERT_FALSE(distiller->GetCallback().is_null());
211   EXPECT_EQ(url, distiller->GetUrl());
212
213   scoped_ptr<DistilledPageProto> proto(new DistilledPageProto);
214   RunDistillerCallback(distiller, proto.Pass());
215
216   EXPECT_TRUE(store_->GetEntryByUrl(url, &entry));
217   EXPECT_EQ(1u, store_->GetEntries().size());
218   service_->RemoveEntry(entry_id);
219   base::RunLoop().RunUntilIdle();
220   EXPECT_EQ(0u, store_->GetEntries().size());
221 }
222
223 TEST_F(DomDistillerServiceTest, TestCancellation) {
224   FakeDistiller* distiller = new FakeDistiller(false);
225   MockDistillerObserver observer;
226   service_->AddObserver(&observer);
227
228   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
229       .WillOnce(Return(distiller));
230   EXPECT_CALL(observer, ArticleEntriesUpdated(_));
231
232   MockArticleAvailableCallback article_cb;
233   EXPECT_CALL(article_cb, DistillationCompleted(false));
234
235   GURL url("http://www.example.com/p1");
236   std::string entry_id = service_->AddToList(url, ArticleCallback(&article_cb));
237
238   // Just remove the entry, there should only be a REMOVE update and no UPDATE
239   // update.
240   std::vector<DomDistillerObserver::ArticleUpdate> expected_updates;
241   DomDistillerObserver::ArticleUpdate update;
242   update.entry_id = entry_id;
243   update.update_type = DomDistillerObserver::ArticleUpdate::REMOVE;
244   expected_updates.push_back(update);
245   EXPECT_CALL(
246       observer,
247       ArticleEntriesUpdated(util::HasExpectedUpdates(expected_updates)));
248
249   // Remove entry will post a cancellation task.
250   service_->RemoveEntry(entry_id);
251   base::RunLoop().RunUntilIdle();
252 }
253
254 TEST_F(DomDistillerServiceTest, TestMultipleObservers) {
255   FakeDistiller* distiller = new FakeDistiller(false);
256   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
257       .WillOnce(Return(distiller));
258
259   const int kObserverCount = 5;
260   MockDistillerObserver observers[kObserverCount];
261   for (int i = 0; i < kObserverCount; ++i) {
262     service_->AddObserver(&observers[i]);
263     EXPECT_CALL(observers[i], ArticleEntriesUpdated(_));
264   }
265
266   DomDistillerService::ArticleAvailableCallback article_cb;
267   GURL url("http://www.example.com/p1");
268   std::string entry_id = service_->AddToList(url, article_cb);
269
270   // Distillation should notify all observers that article is updated.
271   std::vector<DomDistillerObserver::ArticleUpdate> expected_updates;
272   DomDistillerObserver::ArticleUpdate update;
273   update.entry_id = entry_id;
274   update.update_type = DomDistillerObserver::ArticleUpdate::UPDATE;
275   expected_updates.push_back(update);
276
277   for (int i = 0; i < kObserverCount; ++i) {
278     EXPECT_CALL(
279         observers[i],
280         ArticleEntriesUpdated(util::HasExpectedUpdates(expected_updates)));
281   }
282
283   scoped_ptr<DistilledPageProto> proto(new DistilledPageProto);
284   RunDistillerCallback(distiller, proto.Pass());
285
286   // Remove should notify all observers that article is removed.
287   update.update_type = DomDistillerObserver::ArticleUpdate::REMOVE;
288   expected_updates.clear();
289   expected_updates.push_back(update);
290   for (int i = 0; i < kObserverCount; ++i) {
291     EXPECT_CALL(
292         observers[i],
293         ArticleEntriesUpdated(util::HasExpectedUpdates(expected_updates)));
294   }
295
296   service_->RemoveEntry(entry_id);
297   base::RunLoop().RunUntilIdle();
298 }
299
300 TEST_F(DomDistillerServiceTest, TestMultipleCallbacks) {
301   FakeDistiller* distiller = new FakeDistiller(false);
302   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
303       .WillOnce(Return(distiller));
304
305   const int kClientsCount = 5;
306   MockArticleAvailableCallback article_cb[kClientsCount];
307   // Adding a URL and then distilling calls all clients.
308   GURL url("http://www.example.com/p1");
309   const std::string entry_id =
310       service_->AddToList(url, ArticleCallback(&article_cb[0]));
311   EXPECT_CALL(article_cb[0], DistillationCompleted(true));
312
313   for (int i = 1; i < kClientsCount; ++i) {
314     EXPECT_EQ(entry_id,
315               service_->AddToList(url, ArticleCallback(&article_cb[i])));
316     EXPECT_CALL(article_cb[i], DistillationCompleted(true));
317   }
318
319   scoped_ptr<DistilledPageProto> proto(new DistilledPageProto);
320   RunDistillerCallback(distiller, proto.Pass());
321
322   // Add the same url again, all callbacks should be called with true.
323   for (int i = 0; i < kClientsCount; ++i) {
324     EXPECT_CALL(article_cb[i], DistillationCompleted(true));
325     EXPECT_EQ(entry_id,
326               service_->AddToList(url, ArticleCallback(&article_cb[i])));
327   }
328
329   base::RunLoop().RunUntilIdle();
330 }
331
332 TEST_F(DomDistillerServiceTest, TestMultipleCallbacksOnRemove) {
333   FakeDistiller* distiller = new FakeDistiller(false);
334   EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
335       .WillOnce(Return(distiller));
336
337   const int kClientsCount = 5;
338   MockArticleAvailableCallback article_cb[kClientsCount];
339   // Adding a URL and remove the entry before distillation. Callback should be
340   // called with false.
341   GURL url("http://www.example.com/p1");
342   const std::string entry_id =
343       service_->AddToList(url, ArticleCallback(&article_cb[0]));
344
345   EXPECT_CALL(article_cb[0], DistillationCompleted(false));
346   for (int i = 1; i < kClientsCount; ++i) {
347     EXPECT_EQ(entry_id,
348               service_->AddToList(url, ArticleCallback(&article_cb[i])));
349     EXPECT_CALL(article_cb[i], DistillationCompleted(false));
350   }
351
352   service_->RemoveEntry(entry_id);
353   base::RunLoop().RunUntilIdle();
354 }
355
356 }  // namespace test
357 }  // namespace dom_distiller