blob: bbbc1bee8f81826aa275fe54f150878f7f637651 [file] [log] [blame]
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -07001// Copyright 2015 The Chromium OS 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 "buffet/notification/xmpp_channel.h"
6
7#include <queue>
8
9#include <base/test/simple_test_clock.h>
10#include <chromeos/bind_lambda.h>
11#include <chromeos/streams/fake_stream.h>
12#include <gmock/gmock.h>
13#include <gtest/gtest.h>
14
15namespace buffet {
16
17using ::testing::DoAll;
18using ::testing::Return;
19using ::testing::SetArgPointee;
20using ::testing::_;
21
22namespace {
23
24constexpr char kAccountName[] = "Account@Name";
25constexpr char kAccessToken[] = "AccessToken";
26
27constexpr char kStartStreamResponse[] =
28 "<stream:stream from=\"clouddevices.gserviceaccount.com\" "
29 "id=\"0CCF520913ABA04B\" version=\"1.0\" "
30 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
31 "xmlns=\"jabber:client\">"
32 "<stream:features><starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">"
33 "<required/></starttls><mechanisms "
34 "xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"><mechanism>X-OAUTH2</mechanism>"
35 "<mechanism>X-GOOGLE-TOKEN</mechanism></mechanisms></stream:features>";
36constexpr char kAuthenticationSucceededResponse[] =
37 "<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>";
38constexpr char kAuthenticationFailedResponse[] =
39 "<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"><not-authorized/>"
40 "</failure></stream:stream>";
41constexpr char kRestartStreamResponse[] =
42 "<stream:stream from=\"clouddevices.gserviceaccount.com\" "
43 "id=\"BE7D34E0B7589E2A\" version=\"1.0\" "
44 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
45 "xmlns=\"jabber:client\">"
46 "<stream:features><bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
47 "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
48 "</stream:features>";
49constexpr char kBindResponse[] =
50 "<iq id=\"0\" type=\"result\">"
51 "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">"
52 "<jid>110cc78f78d7032cc7bf2c6e14c1fa7d@clouddevices.gserviceaccount.com"
53 "/19853128</jid></bind></iq>";
54constexpr char kSessionResponse[] =
55 "<iq type=\"result\" id=\"1\"/>";
56constexpr char kSubscribedResponse[] =
57 "<iq to=\""
58 "110cc78f78d7032cc7bf2c6e14c1fa7d@clouddevices.gserviceaccount.com/"
59 "19853128\" from=\""
60 "110cc78f78d7032cc7bf2c6e14c1fa7d@clouddevices.gserviceaccount.com\" "
61 "id=\"pushsubscribe1\" type=\"result\"/>";
62constexpr char kStartStreamMessage[] =
63 "<stream:stream to='clouddevices.gserviceaccount.com' "
64 "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='*' "
65 "version='1.0' xmlns='jabber:client'>";
66constexpr char kAuthenticationMessage[] =
67 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='X-OAUTH2' "
68 "auth:service='oauth2' auth:allow-non-google-login='true' "
69 "auth:client-uses-full-bind-result='true' "
70 "xmlns:auth='http://www.google.com/talk/protocol/auth'>"
71 "AEFjY291bnRATmFtZQBBY2Nlc3NUb2tlbg==</auth>";
72constexpr char kBindMessage[] =
73 "<iq type='set' id='0'><bind "
74 "xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>";
75constexpr char kSessionMessage[] =
76 "<iq type='set' id='1'><session "
77 "xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>";
78constexpr char kSubscribeMessage[] =
79 "<iq type='set' to='Account@Name' id='pushsubscribe1'>"
80 "<subscribe xmlns='google:push'><item channel='cloud_devices' from=''/>"
81 "</subscribe></iq>";
82} // namespace
83
84// Mock-like task runner that allow the tests to inspect the calls to
85// TaskRunner::PostDelayedTask and verify the delays.
86class TestTaskRunner : public base::TaskRunner {
87 public:
88 MOCK_METHOD3(PostDelayedTask, bool(const tracked_objects::Location&,
89 const base::Closure&,
90 base::TimeDelta));
91 bool RunsTasksOnCurrentThread() const { return true; }
92};
93
94class FakeXmppChannel : public XmppChannel {
95 public:
96 FakeXmppChannel(const scoped_refptr<base::TaskRunner>& task_runner,
97 base::Clock* clock)
98 : XmppChannel{kAccountName, kAccessToken, task_runner},
99 fake_stream_{chromeos::Stream::AccessMode::READ_WRITE, task_runner,
100 clock} {}
101
102 XmppState state() const { return state_; }
103 void set_state(XmppState state) { state_ = state; }
104
105 void Connect(const std::string& host, uint16_t port,
106 const base::Closure& callback) override {
107 stream_ = &fake_stream_;
108 callback.Run();
109 }
110
111 chromeos::FakeStream fake_stream_;
112};
113
114class XmppChannelTest : public ::testing::Test {
115 protected:
116 void SetUp() override {
117 task_runner_ = new TestTaskRunner;
118
119 auto callback = [this](const tracked_objects::Location& from_here,
120 const base::Closure& task,
121 base::TimeDelta delay) -> bool {
122 clock_.Advance(delay);
123 message_queue_.push(task);
124 return true;
125 };
126
127 EXPECT_CALL(*task_runner_, PostDelayedTask(_, _, _))
128 .WillRepeatedly(testing::Invoke(callback));
129
130 xmpp_client_.reset(new FakeXmppChannel{task_runner_, &clock_});
131 clock_.SetNow(base::Time::Now());
132 }
133
134 void StartWithState(XmppChannel::XmppState state) {
135 xmpp_client_->fake_stream_.ExpectWritePacketString({}, kStartStreamMessage);
136 xmpp_client_->Start(nullptr);
137 RunTasks(1);
138 xmpp_client_->set_state(state);
139 }
140
141 void RunTasks(size_t count) {
142 while (count > 0) {
143 base::Closure task = message_queue_.front();
144 message_queue_.pop();
145 task.Run();
146 count--;
147 }
148 }
149 std::unique_ptr<FakeXmppChannel> xmpp_client_;
150 base::SimpleTestClock clock_;
151 scoped_refptr<TestTaskRunner> task_runner_;
152 std::queue<base::Closure> message_queue_;
153};
154
155TEST_F(XmppChannelTest, StartStream) {
156 EXPECT_EQ(XmppChannel::XmppState::kNotStarted, xmpp_client_->state());
157 xmpp_client_->fake_stream_.ExpectWritePacketString({}, kStartStreamMessage);
158 xmpp_client_->Start(nullptr);
159 RunTasks(1);
160 EXPECT_EQ(XmppChannel::XmppState::kStarted, xmpp_client_->state());
161}
162
163TEST_F(XmppChannelTest, HandleStartedResponse) {
164 StartWithState(XmppChannel::XmppState::kStarted);
165 xmpp_client_->fake_stream_.AddReadPacketString({}, kStartStreamResponse);
166 xmpp_client_->fake_stream_.ExpectWritePacketString({},
167 kAuthenticationMessage);
168 RunTasks(2);
169 EXPECT_EQ(XmppChannel::XmppState::kAuthenticationStarted,
170 xmpp_client_->state());
171}
172
173TEST_F(XmppChannelTest, HandleAuthenticationSucceededResponse) {
174 StartWithState(XmppChannel::XmppState::kAuthenticationStarted);
175 xmpp_client_->fake_stream_.AddReadPacketString(
176 {}, kAuthenticationSucceededResponse);
177 xmpp_client_->fake_stream_.ExpectWritePacketString({}, kStartStreamMessage);
178 RunTasks(2);
179 EXPECT_EQ(XmppChannel::XmppState::kStreamRestartedPostAuthentication,
180 xmpp_client_->state());
181}
182
183TEST_F(XmppChannelTest, HandleAuthenticationFailedResponse) {
184 StartWithState(XmppChannel::XmppState::kAuthenticationStarted);
185 xmpp_client_->fake_stream_.AddReadPacketString(
186 {}, kAuthenticationFailedResponse);
187 RunTasks(1);
188 EXPECT_EQ(XmppChannel::XmppState::kAuthenticationFailed,
189 xmpp_client_->state());
190 EXPECT_TRUE(message_queue_.empty());
191}
192
193TEST_F(XmppChannelTest, HandleStreamRestartedResponse) {
194 StartWithState(XmppChannel::XmppState::kStreamRestartedPostAuthentication);
195 xmpp_client_->fake_stream_.AddReadPacketString({}, kRestartStreamResponse);
196 xmpp_client_->fake_stream_.ExpectWritePacketString({}, kBindMessage);
197 RunTasks(2);
198 EXPECT_EQ(XmppChannel::XmppState::kBindSent,
199 xmpp_client_->state());
200}
201
202TEST_F(XmppChannelTest, HandleBindResponse) {
203 StartWithState(XmppChannel::XmppState::kBindSent);
204 xmpp_client_->fake_stream_.AddReadPacketString({}, kBindResponse);
205 xmpp_client_->fake_stream_.ExpectWritePacketString({}, kSessionMessage);
206 RunTasks(2);
207 EXPECT_EQ(XmppChannel::XmppState::kSessionStarted,
208 xmpp_client_->state());
209}
210
211TEST_F(XmppChannelTest, HandleSessionResponse) {
212 StartWithState(XmppChannel::XmppState::kSessionStarted);
213 xmpp_client_->fake_stream_.AddReadPacketString({}, kSessionResponse);
214 xmpp_client_->fake_stream_.ExpectWritePacketString({}, kSubscribeMessage);
215 RunTasks(2);
216 EXPECT_EQ(XmppChannel::XmppState::kSubscribeStarted,
217 xmpp_client_->state());
218}
219
220TEST_F(XmppChannelTest, HandleSubscribeResponse) {
221 StartWithState(XmppChannel::XmppState::kSubscribeStarted);
222 xmpp_client_->fake_stream_.AddReadPacketString({}, kSubscribedResponse);
223 RunTasks(1);
224 EXPECT_EQ(XmppChannel::XmppState::kSubscribed,
225 xmpp_client_->state());
226}
227
228} // namespace buffet