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 "jingle/notifier/communicator/single_login_attempt.h" 6 7 #include <cstddef> 8 9 #include "base/compiler_specific.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/message_loop/message_loop.h" 12 #include "jingle/notifier/base/const_communicator.h" 13 #include "jingle/notifier/base/fake_base_task.h" 14 #include "jingle/notifier/communicator/login_settings.h" 15 #include "net/dns/mock_host_resolver.h" 16 #include "net/url_request/url_request_test_util.h" 17 #include "talk/xmpp/constants.h" 18 #include "talk/xmpp/xmppengine.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h" 21 22 namespace buzz { 23 class XmppTaskParentInterface; 24 } // namespace buzz 25 26 namespace notifier { 27 28 namespace { 29 30 enum DelegateState { 31 IDLE, CONNECTED, REDIRECTED, CREDENTIALS_REJECTED, SETTINGS_EXHAUSTED 32 }; 33 34 class FakeDelegate : public SingleLoginAttempt::Delegate { 35 public: 36 FakeDelegate() : state_(IDLE) {} 37 38 virtual void OnConnect( 39 base::WeakPtr<buzz::XmppTaskParentInterface> base_task) OVERRIDE { 40 state_ = CONNECTED; 41 base_task_ = base_task; 42 } 43 44 virtual void OnRedirect(const ServerInformation& redirect_server) OVERRIDE { 45 state_ = REDIRECTED; 46 redirect_server_ = redirect_server; 47 } 48 49 virtual void OnCredentialsRejected() OVERRIDE { 50 state_ = CREDENTIALS_REJECTED; 51 } 52 53 virtual void OnSettingsExhausted() OVERRIDE { 54 state_ = SETTINGS_EXHAUSTED; 55 } 56 57 DelegateState state() const { return state_; } 58 59 base::WeakPtr<buzz::XmppTaskParentInterface> base_task() const { 60 return base_task_; 61 } 62 63 const ServerInformation& redirect_server() const { 64 return redirect_server_; 65 } 66 67 private: 68 DelegateState state_; 69 base::WeakPtr<buzz::XmppTaskParentInterface> base_task_; 70 ServerInformation redirect_server_; 71 }; 72 73 class MyTestURLRequestContext : public net::TestURLRequestContext { 74 public: 75 MyTestURLRequestContext() : TestURLRequestContext(true) { 76 context_storage_.set_host_resolver( 77 scoped_ptr<net::HostResolver>(new net::HangingHostResolver())); 78 Init(); 79 } 80 virtual ~MyTestURLRequestContext() {} 81 }; 82 83 class SingleLoginAttemptTest : public ::testing::Test { 84 protected: 85 SingleLoginAttemptTest() 86 : login_settings_( 87 buzz::XmppClientSettings(), 88 new net::TestURLRequestContextGetter( 89 base::MessageLoopProxy::current(), 90 scoped_ptr<net::TestURLRequestContext>( 91 new MyTestURLRequestContext())), 92 ServerList( 93 1, 94 ServerInformation( 95 net::HostPortPair("example.com", 100), SUPPORTS_SSLTCP)), 96 false /* try_ssltcp_first */, 97 "auth_mechanism"), 98 attempt_(new SingleLoginAttempt(login_settings_, &fake_delegate_)) {} 99 100 virtual void TearDown() OVERRIDE { 101 message_loop_.RunUntilIdle(); 102 } 103 104 void FireRedirect(buzz::XmlElement* redirect_error) { 105 attempt_->OnError(buzz::XmppEngine::ERROR_STREAM, 0, redirect_error); 106 } 107 108 virtual ~SingleLoginAttemptTest() { 109 attempt_.reset(); 110 message_loop_.RunUntilIdle(); 111 } 112 113 private: 114 base::MessageLoop message_loop_; 115 const LoginSettings login_settings_; 116 117 protected: 118 scoped_ptr<SingleLoginAttempt> attempt_; 119 FakeDelegate fake_delegate_; 120 FakeBaseTask fake_base_task_; 121 }; 122 123 // Fire OnConnect and make sure the base task gets passed to the 124 // delegate properly. 125 TEST_F(SingleLoginAttemptTest, Basic) { 126 attempt_->OnConnect(fake_base_task_.AsWeakPtr()); 127 EXPECT_EQ(CONNECTED, fake_delegate_.state()); 128 EXPECT_EQ(fake_base_task_.AsWeakPtr().get(), 129 fake_delegate_.base_task().get()); 130 } 131 132 // Fire OnErrors and make sure the delegate gets the 133 // OnSettingsExhausted() event. 134 TEST_F(SingleLoginAttemptTest, Error) { 135 for (int i = 0; i < 2; ++i) { 136 EXPECT_EQ(IDLE, fake_delegate_.state()); 137 attempt_->OnError(buzz::XmppEngine::ERROR_NONE, 0, NULL); 138 } 139 EXPECT_EQ(SETTINGS_EXHAUSTED, fake_delegate_.state()); 140 } 141 142 // Fire OnErrors but replace the last one with OnConnect, and make 143 // sure the delegate still gets the OnConnect message. 144 TEST_F(SingleLoginAttemptTest, ErrorThenSuccess) { 145 attempt_->OnError(buzz::XmppEngine::ERROR_NONE, 0, NULL); 146 attempt_->OnConnect(fake_base_task_.AsWeakPtr()); 147 EXPECT_EQ(CONNECTED, fake_delegate_.state()); 148 EXPECT_EQ(fake_base_task_.AsWeakPtr().get(), 149 fake_delegate_.base_task().get()); 150 } 151 152 buzz::XmlElement* MakeRedirectError(const std::string& redirect_server) { 153 buzz::XmlElement* stream_error = 154 new buzz::XmlElement(buzz::QN_STREAM_ERROR, true); 155 stream_error->AddElement( 156 new buzz::XmlElement(buzz::QN_XSTREAM_SEE_OTHER_HOST, true)); 157 buzz::XmlElement* text = 158 new buzz::XmlElement(buzz::QN_XSTREAM_TEXT, true); 159 stream_error->AddElement(text); 160 text->SetBodyText(redirect_server); 161 return stream_error; 162 } 163 164 // Fire a redirect and make sure the delegate gets the proper redirect 165 // server info. 166 TEST_F(SingleLoginAttemptTest, Redirect) { 167 const ServerInformation redirect_server( 168 net::HostPortPair("example.com", 1000), 169 SUPPORTS_SSLTCP); 170 171 scoped_ptr<buzz::XmlElement> redirect_error( 172 MakeRedirectError(redirect_server.server.ToString())); 173 FireRedirect(redirect_error.get()); 174 175 EXPECT_EQ(REDIRECTED, fake_delegate_.state()); 176 EXPECT_TRUE(fake_delegate_.redirect_server().Equals(redirect_server)); 177 } 178 179 // Fire a redirect with the host only and make sure the delegate gets 180 // the proper redirect server info with the default XMPP port. 181 TEST_F(SingleLoginAttemptTest, RedirectHostOnly) { 182 const ServerInformation redirect_server( 183 net::HostPortPair("example.com", kDefaultXmppPort), 184 SUPPORTS_SSLTCP); 185 186 scoped_ptr<buzz::XmlElement> redirect_error( 187 MakeRedirectError(redirect_server.server.host())); 188 FireRedirect(redirect_error.get()); 189 190 EXPECT_EQ(REDIRECTED, fake_delegate_.state()); 191 EXPECT_TRUE(fake_delegate_.redirect_server().Equals(redirect_server)); 192 } 193 194 // Fire a redirect with a zero port and make sure the delegate gets 195 // the proper redirect server info with the default XMPP port. 196 TEST_F(SingleLoginAttemptTest, RedirectZeroPort) { 197 const ServerInformation redirect_server( 198 net::HostPortPair("example.com", kDefaultXmppPort), 199 SUPPORTS_SSLTCP); 200 201 scoped_ptr<buzz::XmlElement> redirect_error( 202 MakeRedirectError(redirect_server.server.host() + ":0")); 203 FireRedirect(redirect_error.get()); 204 205 EXPECT_EQ(REDIRECTED, fake_delegate_.state()); 206 EXPECT_TRUE(fake_delegate_.redirect_server().Equals(redirect_server)); 207 } 208 209 // Fire a redirect with an invalid port and make sure the delegate 210 // gets the proper redirect server info with the default XMPP port. 211 TEST_F(SingleLoginAttemptTest, RedirectInvalidPort) { 212 const ServerInformation redirect_server( 213 net::HostPortPair("example.com", kDefaultXmppPort), 214 SUPPORTS_SSLTCP); 215 216 scoped_ptr<buzz::XmlElement> redirect_error( 217 MakeRedirectError(redirect_server.server.host() + ":invalidport")); 218 FireRedirect(redirect_error.get()); 219 220 EXPECT_EQ(REDIRECTED, fake_delegate_.state()); 221 EXPECT_TRUE(fake_delegate_.redirect_server().Equals(redirect_server)); 222 } 223 224 // Fire an empty redirect and make sure the delegate does not get a 225 // redirect. 226 TEST_F(SingleLoginAttemptTest, RedirectEmpty) { 227 scoped_ptr<buzz::XmlElement> redirect_error(MakeRedirectError(std::string())); 228 FireRedirect(redirect_error.get()); 229 EXPECT_EQ(IDLE, fake_delegate_.state()); 230 } 231 232 // Fire a redirect with a missing text element and make sure the 233 // delegate does not get a redirect. 234 TEST_F(SingleLoginAttemptTest, RedirectMissingText) { 235 scoped_ptr<buzz::XmlElement> redirect_error(MakeRedirectError(std::string())); 236 redirect_error->RemoveChildAfter(redirect_error->FirstChild()); 237 FireRedirect(redirect_error.get()); 238 EXPECT_EQ(IDLE, fake_delegate_.state()); 239 } 240 241 // Fire a redirect with a missing see-other-host element and make sure 242 // the delegate does not get a redirect. 243 TEST_F(SingleLoginAttemptTest, RedirectMissingSeeOtherHost) { 244 scoped_ptr<buzz::XmlElement> redirect_error(MakeRedirectError(std::string())); 245 redirect_error->RemoveChildAfter(NULL); 246 FireRedirect(redirect_error.get()); 247 EXPECT_EQ(IDLE, fake_delegate_.state()); 248 } 249 250 // Fire 'Unauthorized' errors and make sure the delegate gets the 251 // OnCredentialsRejected() event. 252 TEST_F(SingleLoginAttemptTest, CredentialsRejected) { 253 attempt_->OnError(buzz::XmppEngine::ERROR_UNAUTHORIZED, 0, NULL); 254 EXPECT_EQ(CREDENTIALS_REJECTED, fake_delegate_.state()); 255 } 256 257 } // namespace 258 259 } // namespace notifier 260