1// Copyright 2014 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 <string>
6
7#include "base/compiler_specific.h"
8#include "components/invalidation/push_client_channel.h"
9#include "jingle/notifier/listener/fake_push_client.h"
10#include "jingle/notifier/listener/notification_defines.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13namespace syncer {
14namespace {
15
16class PushClientChannelTest
17    : public ::testing::Test,
18      public SyncNetworkChannel::Observer {
19 protected:
20  PushClientChannelTest()
21      : fake_push_client_(new notifier::FakePushClient()),
22        push_client_channel_(
23            scoped_ptr<notifier::PushClient>(fake_push_client_)),
24        last_invalidator_state_(DEFAULT_INVALIDATION_ERROR) {
25    push_client_channel_.AddObserver(this);
26    push_client_channel_.SetMessageReceiver(
27        invalidation::NewPermanentCallback(
28            this, &PushClientChannelTest::OnIncomingMessage));
29    push_client_channel_.SetSystemResources(NULL);
30  }
31
32  virtual ~PushClientChannelTest() {
33    push_client_channel_.RemoveObserver(this);
34  }
35
36  virtual void OnNetworkChannelStateChanged(
37      InvalidatorState invalidator_state) OVERRIDE {
38    last_invalidator_state_ = invalidator_state;
39  }
40
41  void OnIncomingMessage(std::string incoming_message) {
42    last_message_ = incoming_message;
43  }
44
45  notifier::FakePushClient* fake_push_client_;
46  PushClientChannel push_client_channel_;
47  std::string last_message_;
48  InvalidatorState last_invalidator_state_;
49};
50
51const char kMessage[] = "message";
52const char kServiceContext[] = "service context";
53const int64 kSchedulingHash = 100;
54
55// Make sure the channel subscribes to the correct notifications
56// channel on construction.
57TEST_F(PushClientChannelTest, Subscriptions) {
58  notifier::Subscription expected_subscription;
59  expected_subscription.channel = "tango_raw";
60  EXPECT_TRUE(notifier::SubscriptionListsEqual(
61      fake_push_client_->subscriptions(),
62      notifier::SubscriptionList(1, expected_subscription)));
63}
64
65// Call UpdateCredentials on the channel.  It should propagate it to
66// the push client.
67TEST_F(PushClientChannelTest, UpdateCredentials) {
68  const char kEmail[] = "foo@bar.com";
69  const char kToken[] = "token";
70  EXPECT_TRUE(fake_push_client_->email().empty());
71  EXPECT_TRUE(fake_push_client_->token().empty());
72  push_client_channel_.UpdateCredentials(kEmail, kToken);
73  EXPECT_EQ(kEmail, fake_push_client_->email());
74  EXPECT_EQ(kToken, fake_push_client_->token());
75}
76
77// Simulate push client state changes on the push client.  It should
78// propagate to the channel.
79TEST_F(PushClientChannelTest, OnPushClientStateChange) {
80  EXPECT_EQ(DEFAULT_INVALIDATION_ERROR, last_invalidator_state_);
81  fake_push_client_->EnableNotifications();
82  EXPECT_EQ(INVALIDATIONS_ENABLED, last_invalidator_state_);
83  fake_push_client_->DisableNotifications(
84      notifier::TRANSIENT_NOTIFICATION_ERROR);
85  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, last_invalidator_state_);
86  fake_push_client_->DisableNotifications(
87      notifier::NOTIFICATION_CREDENTIALS_REJECTED);
88  EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, last_invalidator_state_);
89}
90
91// Call SendMessage on the channel.  It should propagate it to the
92// push client.
93TEST_F(PushClientChannelTest, SendMessage) {
94  EXPECT_TRUE(fake_push_client_->sent_notifications().empty());
95  push_client_channel_.SendMessage(kMessage);
96  ASSERT_EQ(1u, fake_push_client_->sent_notifications().size());
97  std::string expected_encoded_message =
98      PushClientChannel::EncodeMessageForTest(
99          kMessage,
100          push_client_channel_.GetServiceContextForTest(),
101          push_client_channel_.GetSchedulingHashForTest());
102  ASSERT_EQ(expected_encoded_message,
103            fake_push_client_->sent_notifications()[0].data);
104}
105
106// Encode a message with some context and then decode it.  The decoded info
107// should match the original info.
108TEST_F(PushClientChannelTest, EncodeDecode) {
109  const std::string& data = PushClientChannel::EncodeMessageForTest(
110      kMessage, kServiceContext, kSchedulingHash);
111  std::string message;
112  std::string service_context;
113  int64 scheduling_hash = 0LL;
114  EXPECT_TRUE(PushClientChannel::DecodeMessageForTest(
115      data, &message, &service_context, &scheduling_hash));
116  EXPECT_EQ(kMessage, message);
117  EXPECT_EQ(kServiceContext, service_context);
118  EXPECT_EQ(kSchedulingHash, scheduling_hash);
119}
120
121// Encode a message with no context and then decode it.  The decoded message
122// should match the original message, but the context and hash should be
123// untouched.
124TEST_F(PushClientChannelTest, EncodeDecodeNoContext) {
125  const std::string& data = PushClientChannel::EncodeMessageForTest(
126      kMessage, std::string(), kSchedulingHash);
127  std::string message;
128  std::string service_context = kServiceContext;
129  int64 scheduling_hash = kSchedulingHash + 1;
130  EXPECT_TRUE(PushClientChannel::DecodeMessageForTest(
131      data, &message, &service_context, &scheduling_hash));
132  EXPECT_EQ(kMessage, message);
133  EXPECT_EQ(kServiceContext, service_context);
134  EXPECT_EQ(kSchedulingHash + 1, scheduling_hash);
135}
136
137// Decode an empty notification. It should result in an empty message
138// but should leave the context and hash untouched.
139TEST_F(PushClientChannelTest, DecodeEmpty) {
140  std::string message = kMessage;
141  std::string service_context = kServiceContext;
142  int64 scheduling_hash = kSchedulingHash;
143  EXPECT_TRUE(PushClientChannel::DecodeMessageForTest(
144      std::string(), &message, &service_context, &scheduling_hash));
145  EXPECT_TRUE(message.empty());
146  EXPECT_EQ(kServiceContext, service_context);
147  EXPECT_EQ(kSchedulingHash, scheduling_hash);
148}
149
150// Try to decode a garbage notification.  It should leave all its
151// arguments untouched and return false.
152TEST_F(PushClientChannelTest, DecodeGarbage) {
153  std::string data = "garbage";
154  std::string message = kMessage;
155  std::string service_context = kServiceContext;
156  int64 scheduling_hash = kSchedulingHash;
157  EXPECT_FALSE(PushClientChannel::DecodeMessageForTest(
158      data, &message, &service_context, &scheduling_hash));
159  EXPECT_EQ(kMessage, message);
160  EXPECT_EQ(kServiceContext, service_context);
161  EXPECT_EQ(kSchedulingHash, scheduling_hash);
162}
163
164// Simulate an incoming notification. It should be decoded properly
165// by the channel.
166TEST_F(PushClientChannelTest, OnIncomingMessage) {
167  notifier::Notification notification;
168  notification.data =
169      PushClientChannel::EncodeMessageForTest(
170          kMessage, kServiceContext, kSchedulingHash);
171  fake_push_client_->SimulateIncomingNotification(notification);
172
173  EXPECT_EQ(kServiceContext,
174            push_client_channel_.GetServiceContextForTest());
175  EXPECT_EQ(kSchedulingHash,
176            push_client_channel_.GetSchedulingHashForTest());
177  EXPECT_EQ(kMessage, last_message_);
178}
179
180// Simulate an incoming notification with no receiver. It should be dropped by
181// the channel.
182TEST_F(PushClientChannelTest, OnIncomingMessageNoReceiver) {
183  push_client_channel_.SetMessageReceiver(NULL);
184
185  notifier::Notification notification;
186  notification.data = PushClientChannel::EncodeMessageForTest(
187      kMessage, kServiceContext, kSchedulingHash);
188  fake_push_client_->SimulateIncomingNotification(notification);
189
190  EXPECT_TRUE(push_client_channel_.GetServiceContextForTest().empty());
191  EXPECT_EQ(static_cast<int64>(0),
192            push_client_channel_.GetSchedulingHashForTest());
193  EXPECT_TRUE(last_message_.empty());
194}
195
196// Simulate an incoming garbage notification. It should be dropped by
197// the channel.
198TEST_F(PushClientChannelTest, OnIncomingMessageGarbage) {
199  notifier::Notification notification;
200  notification.data = "garbage";
201  fake_push_client_->SimulateIncomingNotification(notification);
202  EXPECT_TRUE(push_client_channel_.GetServiceContextForTest().empty());
203  EXPECT_EQ(static_cast<int64>(0),
204            push_client_channel_.GetSchedulingHashForTest());
205  EXPECT_TRUE(last_message_.empty());
206}
207
208// Send a message, simulate an incoming message with context, and then
209// send the same message again.  The first sent message should not
210// have any context, but the second sent message should have the
211// context from the incoming emssage.
212TEST_F(PushClientChannelTest, PersistedMessageState) {
213  push_client_channel_.SendMessage(kMessage);
214  ASSERT_EQ(1u, fake_push_client_->sent_notifications().size());
215  {
216    std::string message;
217    std::string service_context;
218    int64 scheduling_hash = 0LL;
219    EXPECT_TRUE(PushClientChannel::DecodeMessageForTest(
220        fake_push_client_->sent_notifications()[0].data,
221        &message,
222        &service_context,
223        &scheduling_hash));
224    EXPECT_EQ(kMessage, message);
225    EXPECT_TRUE(service_context.empty());
226    EXPECT_EQ(0LL, scheduling_hash);
227  }
228
229  notifier::Notification notification;
230  notification.data = PushClientChannel::EncodeMessageForTest(
231      kMessage, kServiceContext, kSchedulingHash);
232  fake_push_client_->SimulateIncomingNotification(notification);
233
234  push_client_channel_.SendMessage(kMessage);
235  ASSERT_EQ(2u, fake_push_client_->sent_notifications().size());
236  {
237    std::string message;
238    std::string service_context;
239    int64 scheduling_hash = 0LL;
240    EXPECT_TRUE(PushClientChannel::DecodeMessageForTest(
241        fake_push_client_->sent_notifications()[1].data,
242        &message,
243        &service_context,
244        &scheduling_hash));
245    EXPECT_EQ(kMessage, message);
246    EXPECT_EQ(kServiceContext, service_context);
247    EXPECT_EQ(kSchedulingHash, scheduling_hash);
248  }
249}
250
251}  // namespace
252}  // namespace syncer
253