1// Copyright (c) 2012 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 "chrome/browser/extensions/api/push_messaging/push_messaging_invalidation_handler.h"
6
7#include "base/basictypes.h"
8#include "base/callback.h"
9#include "base/memory/scoped_ptr.h"
10#include "chrome/browser/extensions/api/push_messaging/push_messaging_invalidation_handler_delegate.h"
11#include "components/invalidation/invalidation_logger.h"
12#include "components/invalidation/invalidation_service.h"
13#include "components/invalidation/object_id_invalidation_map.h"
14#include "google/cacheinvalidation/types.pb.h"
15#include "testing/gmock/include/gmock/gmock.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18using ::testing::NotNull;
19using ::testing::SaveArg;
20using ::testing::StrictMock;
21using ::testing::_;
22
23namespace extensions {
24
25namespace {
26
27class MockInvalidationService : public invalidation::InvalidationService {
28 public:
29  MockInvalidationService();
30  ~MockInvalidationService();
31  MOCK_METHOD1(RegisterInvalidationHandler,
32               void(syncer::InvalidationHandler*));
33  MOCK_METHOD2(UpdateRegisteredInvalidationIds,
34               void(syncer::InvalidationHandler*, const syncer::ObjectIdSet&));
35  MOCK_METHOD1(UnregisterInvalidationHandler,
36               void(syncer::InvalidationHandler*));
37  MOCK_CONST_METHOD0(GetInvalidatorState, syncer::InvalidatorState());
38  MOCK_CONST_METHOD0(GetInvalidatorClientId, std::string());
39  MOCK_METHOD0(GetInvalidationLogger, invalidation::InvalidationLogger*());
40  MOCK_CONST_METHOD1(RequestDetailedStatus,
41                     void(base::Callback<void(const base::DictionaryValue&)>));
42  MOCK_METHOD0(GetIdentityProvider, IdentityProvider*());
43
44 private:
45  DISALLOW_COPY_AND_ASSIGN(MockInvalidationService);
46};
47
48MockInvalidationService::MockInvalidationService() {}
49MockInvalidationService::~MockInvalidationService() {}
50
51class MockInvalidationHandlerDelegate
52    : public PushMessagingInvalidationHandlerDelegate {
53 public:
54  MockInvalidationHandlerDelegate();
55  ~MockInvalidationHandlerDelegate();
56  MOCK_METHOD3(OnMessage,
57               void(const std::string&, int, const std::string&));
58
59 private:
60  DISALLOW_COPY_AND_ASSIGN(MockInvalidationHandlerDelegate);
61};
62
63MockInvalidationHandlerDelegate::MockInvalidationHandlerDelegate() {}
64MockInvalidationHandlerDelegate::~MockInvalidationHandlerDelegate() {}
65
66}  // namespace
67
68class PushMessagingInvalidationHandlerTest : public ::testing::Test {
69 protected:
70  virtual void SetUp() OVERRIDE {
71    syncer::InvalidationHandler* handler = NULL;
72    EXPECT_CALL(service_, RegisterInvalidationHandler(NotNull()))
73        .WillOnce(SaveArg<0>(&handler));
74    handler_.reset(new PushMessagingInvalidationHandler(
75        &service_, &delegate_));
76    EXPECT_EQ(handler_.get(), handler);
77  }
78  virtual void TearDown() OVERRIDE {
79    EXPECT_CALL(service_, UnregisterInvalidationHandler(handler_.get()));
80    handler_.reset();
81  }
82  StrictMock<MockInvalidationService> service_;
83  StrictMock<MockInvalidationHandlerDelegate> delegate_;
84  scoped_ptr<PushMessagingInvalidationHandler> handler_;
85};
86
87TEST_F(PushMessagingInvalidationHandlerTest, RegisterUnregisterExtension) {
88  syncer::ObjectIdSet expected_ids;
89  expected_ids.insert(invalidation::ObjectId(
90      ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
91      "U/cccccccccccccccccccccccccccccccc/0"));
92  expected_ids.insert(invalidation::ObjectId(
93      ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
94      "U/cccccccccccccccccccccccccccccccc/1"));
95  expected_ids.insert(invalidation::ObjectId(
96      ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
97      "U/cccccccccccccccccccccccccccccccc/2"));
98  expected_ids.insert(invalidation::ObjectId(
99      ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
100      "U/cccccccccccccccccccccccccccccccc/3"));
101  EXPECT_CALL(service_,
102              UpdateRegisteredInvalidationIds(handler_.get(), expected_ids));
103  handler_->RegisterExtension("cccccccccccccccccccccccccccccccc");
104  EXPECT_CALL(service_,
105              UpdateRegisteredInvalidationIds(handler_.get(),
106                                              syncer::ObjectIdSet()));
107  handler_->UnregisterExtension("cccccccccccccccccccccccccccccccc");
108}
109
110TEST_F(PushMessagingInvalidationHandlerTest, Dispatch) {
111  syncer::ObjectIdInvalidationMap invalidation_map;
112  // A normal invalidation.
113  invalidation_map.Insert(
114      syncer::Invalidation::Init(
115          invalidation::ObjectId(
116              ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
117              "U/dddddddddddddddddddddddddddddddd/0"),
118          10,
119          "payload"));
120
121  // An unknown version invalidation.
122  invalidation_map.Insert(syncer::Invalidation::InitUnknownVersion(
123      invalidation::ObjectId(
124        ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
125        "U/dddddddddddddddddddddddddddddddd/3")));
126
127  EXPECT_CALL(delegate_,
128              OnMessage("dddddddddddddddddddddddddddddddd", 0, "payload"));
129  EXPECT_CALL(delegate_,
130              OnMessage("dddddddddddddddddddddddddddddddd", 3, ""));
131  handler_->OnIncomingInvalidation(invalidation_map);
132}
133
134// Tests that malformed object IDs don't trigger spurious callbacks.
135TEST_F(PushMessagingInvalidationHandlerTest, DispatchInvalidObjectIds) {
136  syncer::ObjectIdInvalidationMap invalidation_map;
137  // Completely incorrect format.
138  invalidation_map.Insert(syncer::Invalidation::InitUnknownVersion(
139          invalidation::ObjectId(
140              ipc::invalidation::ObjectSource::TEST,
141              "Invalid")));
142  // Incorrect source.
143  invalidation_map.Insert(syncer::Invalidation::InitUnknownVersion(
144          invalidation::ObjectId(
145              ipc::invalidation::ObjectSource::TEST,
146              "U/dddddddddddddddddddddddddddddddd/3")));
147  // Incorrect format type.
148  invalidation_map.Insert(syncer::Invalidation::InitUnknownVersion(
149          invalidation::ObjectId(
150              ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
151              "V/dddddddddddddddddddddddddddddddd/3")));
152  // Invalid extension ID length.
153  invalidation_map.Insert(syncer::Invalidation::InitUnknownVersion(
154          invalidation::ObjectId(
155              ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
156              "U/ddddddddddddddddddddddddddddddddd/3")));
157  // Non-numeric subchannel.
158  invalidation_map.Insert(syncer::Invalidation::InitUnknownVersion(
159          invalidation::ObjectId(
160              ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
161              "U/dddddddddddddddddddddddddddddddd/z")));
162  // Subchannel out of range.
163  invalidation_map.Insert(syncer::Invalidation::InitUnknownVersion(
164          invalidation::ObjectId(
165              ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
166              "U/dddddddddddddddddddddddddddddddd/4")));
167  handler_->OnIncomingInvalidation(invalidation_map);
168}
169
170// Test version filtering of incoming invalidations.
171TEST_F(PushMessagingInvalidationHandlerTest, InvalidationVersionsOutOfOrder) {
172  const invalidation::ObjectId id0(
173      ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
174      "U/dddddddddddddddddddddddddddddddd/0");
175  const invalidation::ObjectId id3(
176      ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
177      "U/dddddddddddddddddddddddddddddddd/3");
178
179  // The first received invalidation should get through.
180  syncer::ObjectIdInvalidationMap map1;
181  map1.Insert(syncer::Invalidation::Init(id0, 5, "5"));
182  EXPECT_CALL(delegate_, OnMessage("dddddddddddddddddddddddddddddddd", 0, "5"));
183  handler_->OnIncomingInvalidation(map1);
184  testing::Mock::VerifyAndClearExpectations(&delegate_);
185
186  // Invalid versions are always allowed through.
187  syncer::ObjectIdInvalidationMap map2;
188  map2.Insert(syncer::Invalidation::InitUnknownVersion(id0));
189  EXPECT_CALL(delegate_, OnMessage("dddddddddddddddddddddddddddddddd", 0, ""));
190  handler_->OnIncomingInvalidation(map2);
191  testing::Mock::VerifyAndClearExpectations(&delegate_);
192
193  // An older version should not make it through.
194  syncer::ObjectIdInvalidationMap map3;
195  map3.Insert(syncer::Invalidation::Init(id0, 4, "4"));
196  handler_->OnIncomingInvalidation(map3);
197
198  // A newer version will make it through.
199  syncer::ObjectIdInvalidationMap map4;
200  map4.Insert(syncer::Invalidation::Init(id0, 6, "6"));
201  EXPECT_CALL(delegate_, OnMessage("dddddddddddddddddddddddddddddddd", 0, "6"));
202  handler_->OnIncomingInvalidation(map4);
203  testing::Mock::VerifyAndClearExpectations(&delegate_);
204
205  // An unrelated object should be unaffected by all the above.
206  syncer::ObjectIdInvalidationMap map5;
207  map5.Insert(syncer::Invalidation::Init(id3, 1, "1"));
208  EXPECT_CALL(delegate_, OnMessage("dddddddddddddddddddddddddddddddd", 3, "1"));
209  handler_->OnIncomingInvalidation(map5);
210  testing::Mock::VerifyAndClearExpectations(&delegate_);
211}
212
213}  // namespace extensions
214