rpc_handler_unittest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 "components/copresence/rpc/rpc_handler.h" 6 7#include <map> 8#include <string> 9#include <vector> 10 11#include "base/bind.h" 12#include "base/bind_helpers.h" 13#include "base/message_loop/message_loop.h" 14#include "components/copresence/handlers/directive_handler.h" 15#include "components/copresence/proto/data.pb.h" 16#include "components/copresence/proto/enums.pb.h" 17#include "components/copresence/proto/rpcs.pb.h" 18#include "net/http/http_status_code.h" 19#include "testing/gtest/include/gtest/gtest.h" 20 21using google::protobuf::MessageLite; 22using google::protobuf::RepeatedPtrField; 23 24namespace copresence { 25 26namespace { 27 28const char kChromeVersion[] = "Chrome Version String"; 29 30void AddMessageWithStrategy(ReportRequest* report, 31 BroadcastScanConfiguration strategy) { 32 report->mutable_manage_messages_request()->add_message_to_publish() 33 ->mutable_token_exchange_strategy()->set_broadcast_scan_configuration( 34 strategy); 35} 36 37void AddSubscriptionWithStrategy(ReportRequest* report, 38 BroadcastScanConfiguration strategy) { 39 report->mutable_manage_subscriptions_request()->add_subscription() 40 ->mutable_token_exchange_strategy()->set_broadcast_scan_configuration( 41 strategy); 42} 43 44void CreateSubscribedMessage(const std::vector<std::string>& subscription_ids, 45 const std::string& message_string, 46 SubscribedMessage* message_proto) { 47 message_proto->mutable_published_message()->set_payload(message_string); 48 for (std::vector<std::string>::const_iterator subscription_id = 49 subscription_ids.begin(); 50 subscription_id != subscription_ids.end(); 51 ++subscription_id) { 52 message_proto->add_subscription_id(*subscription_id); 53 } 54} 55 56// TODO(ckehoe): Make DirectiveHandler an interface. 57class FakeDirectiveHandler : public DirectiveHandler { 58 public: 59 FakeDirectiveHandler() {} 60 virtual ~FakeDirectiveHandler() {} 61 62 const std::vector<Directive>& added_directives() const { 63 return added_directives_; 64 } 65 66 virtual void Initialize( 67 const AudioRecorder::DecodeSamplesCallback& decode_cb, 68 const AudioDirectiveHandler::EncodeTokenCallback& encode_cb) OVERRIDE {} 69 70 virtual void AddDirective(const Directive& directive) OVERRIDE { 71 added_directives_.push_back(directive); 72 } 73 74 virtual void RemoveDirectives(const std::string& op_id) OVERRIDE { 75 // TODO(ckehoe): Add a parallel implementation when prod has one. 76 } 77 78 private: 79 std::vector<Directive> added_directives_; 80 81 DISALLOW_COPY_AND_ASSIGN(FakeDirectiveHandler); 82}; 83 84} // namespace 85 86class RpcHandlerTest : public testing::Test, public CopresenceDelegate { 87 public: 88 RpcHandlerTest() : rpc_handler_(this), status_(SUCCESS), api_key_("API key") { 89 rpc_handler_.server_post_callback_ = 90 base::Bind(&RpcHandlerTest::CaptureHttpPost, base::Unretained(this)); 91 rpc_handler_.device_id_ = "Device ID"; 92 } 93 94 void CaptureHttpPost( 95 net::URLRequestContextGetter* url_context_getter, 96 const std::string& rpc_name, 97 scoped_ptr<MessageLite> request_proto, 98 const RpcHandler::PostCleanupCallback& response_callback) { 99 rpc_name_ = rpc_name; 100 request_proto_ = request_proto.Pass(); 101 } 102 103 void CaptureStatus(CopresenceStatus status) { 104 status_ = status; 105 } 106 107 inline const ReportRequest* GetReportSent() { 108 return static_cast<ReportRequest*>(request_proto_.get()); 109 } 110 111// TODO(ckehoe): Fix this on Windows. See rpc_handler.cc. 112#ifndef OS_WIN 113 const TokenTechnology& GetTokenTechnologyFromReport() { 114 return GetReportSent()->update_signals_request().state().capabilities() 115 .token_technology(0); 116 } 117#endif 118 119 const RepeatedPtrField<PublishedMessage>& GetMessagesPublished() { 120 return GetReportSent()->manage_messages_request().message_to_publish(); 121 } 122 123 const RepeatedPtrField<Subscription>& GetSubscriptionsSent() { 124 return GetReportSent()->manage_subscriptions_request().subscription(); 125 } 126 127 void SetDeviceId(const std::string& device_id) { 128 rpc_handler_.device_id_ = device_id; 129 } 130 131 const std::string& GetDeviceId() { 132 return rpc_handler_.device_id_; 133 } 134 135 void AddInvalidToken(const std::string& token) { 136 rpc_handler_.invalid_audio_token_cache_.Add(token, true); 137 } 138 139 bool TokenIsInvalid(const std::string& token) { 140 return rpc_handler_.invalid_audio_token_cache_.HasKey(token); 141 } 142 143 FakeDirectiveHandler* InstallFakeDirectiveHandler() { 144 FakeDirectiveHandler* handler = new FakeDirectiveHandler; 145 rpc_handler_.directive_handler_.reset(handler); 146 return handler; 147 } 148 149 void InvokeReportResponseHandler(int status_code, 150 const std::string& response) { 151 rpc_handler_.ReportResponseHandler( 152 base::Bind(&RpcHandlerTest::CaptureStatus, base::Unretained(this)), 153 NULL, 154 status_code, 155 response); 156 } 157 158 // CopresenceDelegate implementation 159 160 virtual void HandleMessages( 161 const std::string& app_id, 162 const std::string& subscription_id, 163 const std::vector<Message>& messages) OVERRIDE { 164 // app_id is unused for now, pending a server fix. 165 messages_by_subscription_[subscription_id] = messages; 166 } 167 168 virtual net::URLRequestContextGetter* GetRequestContext() const OVERRIDE { 169 return NULL; 170 } 171 172 virtual const std::string GetPlatformVersionString() const OVERRIDE { 173 return kChromeVersion; 174 } 175 176 virtual const std::string GetAPIKey() const OVERRIDE { 177 return api_key_; 178 } 179 180 virtual WhispernetClient* GetWhispernetClient() OVERRIDE { 181 return NULL; 182 } 183 184 protected: 185 // For rpc_handler_.invalid_audio_token_cache_ 186 base::MessageLoop message_loop_; 187 188 RpcHandler rpc_handler_; 189 CopresenceStatus status_; 190 std::string api_key_; 191 192 std::string rpc_name_; 193 scoped_ptr<MessageLite> request_proto_; 194 std::map<std::string, std::vector<Message> > messages_by_subscription_; 195}; 196 197TEST_F(RpcHandlerTest, Initialize) { 198 SetDeviceId(""); 199 rpc_handler_.Initialize(RpcHandler::SuccessCallback()); 200 RegisterDeviceRequest* registration = 201 static_cast<RegisterDeviceRequest*>(request_proto_.get()); 202 Identity identity = registration->device_identifiers().registrant(); 203 EXPECT_EQ(CHROME, identity.type()); 204 EXPECT_FALSE(identity.chrome_id().empty()); 205} 206 207// TODO(ckehoe): Fix this on Windows. See rpc_handler.cc. 208#ifndef OS_WIN 209 210TEST_F(RpcHandlerTest, GetDeviceCapabilities) { 211 // Empty request. 212 rpc_handler_.SendReportRequest(make_scoped_ptr(new ReportRequest)); 213 EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); 214 const TokenTechnology* token_technology = &GetTokenTechnologyFromReport(); 215 EXPECT_EQ(AUDIO_ULTRASOUND_PASSBAND, token_technology->medium()); 216 EXPECT_EQ(TRANSMIT, token_technology->instruction_type(0)); 217 EXPECT_EQ(RECEIVE, token_technology->instruction_type(1)); 218 219 // Request with broadcast only. 220 scoped_ptr<ReportRequest> report(new ReportRequest); 221 AddMessageWithStrategy(report.get(), BROADCAST_ONLY); 222 rpc_handler_.SendReportRequest(report.Pass()); 223 token_technology = &GetTokenTechnologyFromReport(); 224 EXPECT_EQ(1, token_technology->instruction_type_size()); 225 EXPECT_EQ(TRANSMIT, token_technology->instruction_type(0)); 226 EXPECT_FALSE(GetReportSent()->has_manage_subscriptions_request()); 227 228 // Request with scan only. 229 report.reset(new ReportRequest); 230 AddSubscriptionWithStrategy(report.get(), SCAN_ONLY); 231 AddSubscriptionWithStrategy(report.get(), SCAN_ONLY); 232 rpc_handler_.SendReportRequest(report.Pass()); 233 token_technology = &GetTokenTechnologyFromReport(); 234 EXPECT_EQ(1, token_technology->instruction_type_size()); 235 EXPECT_EQ(RECEIVE, token_technology->instruction_type(0)); 236 EXPECT_FALSE(GetReportSent()->has_manage_messages_request()); 237 238 // Request with both scan and broadcast only (conflict). 239 report.reset(new ReportRequest); 240 AddMessageWithStrategy(report.get(), SCAN_ONLY); 241 AddMessageWithStrategy(report.get(), BROADCAST_ONLY); 242 AddSubscriptionWithStrategy(report.get(), BROADCAST_ONLY); 243 rpc_handler_.SendReportRequest(report.Pass()); 244 token_technology = &GetTokenTechnologyFromReport(); 245 EXPECT_EQ(TRANSMIT, token_technology->instruction_type(0)); 246 EXPECT_EQ(RECEIVE, token_technology->instruction_type(1)); 247 248 // Request with broadcast and scan. 249 report.reset(new ReportRequest); 250 AddMessageWithStrategy(report.get(), SCAN_ONLY); 251 AddSubscriptionWithStrategy(report.get(), BROADCAST_AND_SCAN); 252 rpc_handler_.SendReportRequest(report.Pass()); 253 token_technology = &GetTokenTechnologyFromReport(); 254 EXPECT_EQ(TRANSMIT, token_technology->instruction_type(0)); 255 EXPECT_EQ(RECEIVE, token_technology->instruction_type(1)); 256} 257#endif 258 259TEST_F(RpcHandlerTest, CreateRequestHeader) { 260 SetDeviceId("CreateRequestHeader Device ID"); 261 rpc_handler_.SendReportRequest(make_scoped_ptr(new ReportRequest), 262 "CreateRequestHeader App ID", 263 StatusCallback()); 264 EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); 265 ReportRequest* report = static_cast<ReportRequest*>(request_proto_.get()); 266 EXPECT_EQ(kChromeVersion, 267 report->header().framework_version().version_name()); 268 EXPECT_EQ("CreateRequestHeader App ID", 269 report->header().client_version().client()); 270 EXPECT_EQ("CreateRequestHeader Device ID", 271 report->header().registered_device_id()); 272 EXPECT_EQ(CHROME_PLATFORM_TYPE, 273 report->header().device_fingerprint().type()); 274} 275 276TEST_F(RpcHandlerTest, ReportTokens) { 277 std::vector<AudioToken> test_tokens; 278 test_tokens.push_back(AudioToken("token 1", false)); 279 test_tokens.push_back(AudioToken("token 2", true)); 280 test_tokens.push_back(AudioToken("token 3", false)); 281 AddInvalidToken("token 2"); 282 283 rpc_handler_.ReportTokens(test_tokens); 284 EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); 285 ReportRequest* report = static_cast<ReportRequest*>(request_proto_.get()); 286 google::protobuf::RepeatedPtrField<TokenObservation> tokens_sent = 287 report->update_signals_request().token_observation(); 288 ASSERT_EQ(2, tokens_sent.size()); 289 EXPECT_EQ("token 1", tokens_sent.Get(0).token_id()); 290 EXPECT_EQ("token 3", tokens_sent.Get(1).token_id()); 291} 292 293TEST_F(RpcHandlerTest, ReportResponseHandler) { 294 // Fail on HTTP status != 200. 295 ReportResponse empty_response; 296 empty_response.mutable_header()->mutable_status()->set_code(OK); 297 std::string serialized_empty_response; 298 ASSERT_TRUE(empty_response.SerializeToString(&serialized_empty_response)); 299 status_ = SUCCESS; 300 InvokeReportResponseHandler(net::HTTP_BAD_REQUEST, serialized_empty_response); 301 EXPECT_EQ(FAIL, status_); 302 303 std::vector<std::string> subscription_1(1, "Subscription 1"); 304 std::vector<std::string> subscription_2(1, "Subscription 2"); 305 std::vector<std::string> both_subscriptions; 306 both_subscriptions.push_back("Subscription 1"); 307 both_subscriptions.push_back("Subscription 2"); 308 309 ReportResponse test_response; 310 test_response.mutable_header()->mutable_status()->set_code(OK); 311 UpdateSignalsResponse* update_response = 312 test_response.mutable_update_signals_response(); 313 update_response->set_status(util::error::OK); 314 Token* invalid_token = update_response->add_token(); 315 invalid_token->set_id("bad token"); 316 invalid_token->set_status(INVALID); 317 CreateSubscribedMessage( 318 subscription_1, "Message A", update_response->add_message()); 319 CreateSubscribedMessage( 320 subscription_2, "Message B", update_response->add_message()); 321 CreateSubscribedMessage( 322 both_subscriptions, "Message C", update_response->add_message()); 323 update_response->add_directive()->set_subscription_id("Subscription 1"); 324 update_response->add_directive()->set_subscription_id("Subscription 2"); 325 326 messages_by_subscription_.clear(); 327 FakeDirectiveHandler* directive_handler = InstallFakeDirectiveHandler(); 328 std::string serialized_proto; 329 ASSERT_TRUE(test_response.SerializeToString(&serialized_proto)); 330 status_ = FAIL; 331 InvokeReportResponseHandler(net::HTTP_OK, serialized_proto); 332 333 EXPECT_EQ(SUCCESS, status_); 334 EXPECT_TRUE(TokenIsInvalid("bad token")); 335 ASSERT_EQ(2U, messages_by_subscription_.size()); 336 ASSERT_EQ(2U, messages_by_subscription_["Subscription 1"].size()); 337 ASSERT_EQ(2U, messages_by_subscription_["Subscription 2"].size()); 338 EXPECT_EQ("Message A", 339 messages_by_subscription_["Subscription 1"][0].payload()); 340 EXPECT_EQ("Message B", 341 messages_by_subscription_["Subscription 2"][0].payload()); 342 EXPECT_EQ("Message C", 343 messages_by_subscription_["Subscription 1"][1].payload()); 344 EXPECT_EQ("Message C", 345 messages_by_subscription_["Subscription 2"][1].payload()); 346 347 ASSERT_EQ(2U, directive_handler->added_directives().size()); 348 EXPECT_EQ("Subscription 1", 349 directive_handler->added_directives()[0].subscription_id()); 350 EXPECT_EQ("Subscription 2", 351 directive_handler->added_directives()[1].subscription_id()); 352} 353 354} // namespace copresence 355