// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Tests for ProtobufWireAdapter. #include <gmock/gmock.h> #include <gtest/gtest.h> #include <polo/util/poloutil.h> #include <polo/wire/protobuf/protobufwireadapter.h> #include "polo/wire/mocks.h" using ::testing::InSequence; using ::testing::Mock; using ::testing::Return; using ::testing::StrictMock; namespace polo { namespace wire { namespace protobuf { // A mock MessageListener. class MockMessageListener : public pairing::message::MessageListener { MOCK_METHOD1(OnConfigurationMessage, void(const pairing::message::ConfigurationMessage& message)); MOCK_METHOD1(OnConfigurationAckMessage, void(const pairing::message::ConfigurationAckMessage& message)); MOCK_METHOD1(OnOptionsMessage, void(const pairing::message::OptionsMessage& message)); MOCK_METHOD1(OnPairingRequestMessage, void(const pairing::message::PairingRequestMessage& message)); MOCK_METHOD1(OnPairingRequestAckMessage, void(const pairing::message::PairingRequestAckMessage& message)); MOCK_METHOD1(OnSecretMessage, void(const pairing::message::SecretMessage& message)); MOCK_METHOD1(OnSecretAckMessage, void(const pairing::message::SecretAckMessage& message)); MOCK_METHOD1(OnError, void(pairing::PoloError error)); }; // Test fixture for ProtobufWireAdapter tests. class ProtobufWireAdapterTest : public ::testing::Test { public: ProtobufWireAdapterTest() : interface_(), adapter_(&interface_) {} protected: virtual void SetUp() { adapter_.set_listener(&listener_); } // Expects that a call to GetNextMessage will be made which triggers a read // for the 4 byte preamble. void ExpectGetPreamble() { EXPECT_CALL(interface_, Receive(4)); adapter_.GetNextMessage(); } // Expects that a call to GetNextMessage will be made, and the preamble will // be read containing the given message size. This will trigger another read // for the full message. void ExpectReadPreamble(uint32_t message_size) { ExpectGetPreamble(); unsigned char* size_bytes; util::PoloUtil::IntToBigEndianBytes(message_size, size_bytes); EXPECT_CALL(interface_, Receive(message_size)); adapter_.OnBytesReceived( std::vector<uint8_t>(size_bytes, size_bytes + 4)); } // Expects that the given OuterMessage will be sent over the interface. void ExpectSend(const OuterMessage& message) { std::string outer_string = message.SerializeAsString(); unsigned char* size_bytes; util::PoloUtil::IntToBigEndianBytes(outer_string.length(), size_bytes); std::vector<unsigned char> data(outer_string.length() + 4); unsigned char* buffer = &data[0]; memcpy(buffer, size_bytes, 4); memcpy((buffer + 4), &outer_string[0], outer_string.length()); EXPECT_CALL(interface_, Send(data)); } StrictMock<MockWireInterface> interface_; StrictMock<MockMessageListener> listener_; ProtobufWireAdapter adapter_; }; // Verifies that a call to GetNextMessage will trigger a read for the 4 byte // preamble. TEST_F(ProtobufWireAdapterTest, GetNextMessage) { ExpectGetPreamble(); } // Verifies that once the preamble is received, a read will be triggered for // the full message. TEST_F(ProtobufWireAdapterTest, OnBytesReceivedPreamble) { InSequence sequence; ExpectReadPreamble(0xAABBCCDD); } // Verifies that a ConfigurationMessage is successfully sent over the interface. TEST_F(ProtobufWireAdapterTest, SendConfigurationMessage) { InSequence sequence; Configuration proto; proto.set_client_role(Options_RoleType_ROLE_TYPE_OUTPUT); proto.mutable_encoding()->set_type( Options_Encoding_EncodingType_ENCODING_TYPE_QRCODE); proto.mutable_encoding()->set_symbol_length(64); OuterMessage outer; outer.set_type(OuterMessage_MessageType_MESSAGE_TYPE_CONFIGURATION); outer.set_payload(proto.SerializeAsString()); outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_OK); ExpectSend(outer); pairing::message::ConfigurationMessage message( encoding::EncodingOption(encoding::EncodingOption::kQRCode, 64), pairing::message::OptionsMessage::kDisplayDevice); adapter_.SendConfigurationMessage(message); } // Verifies that a ConfigurationAckMessage is successfully sent over the // interface. TEST_F(ProtobufWireAdapterTest, SendConfigurationAckMessage) { InSequence sequence; ConfigurationAck proto; OuterMessage outer; outer.set_type(OuterMessage_MessageType_MESSAGE_TYPE_CONFIGURATION_ACK); outer.set_payload(proto.SerializeAsString()); outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_OK); ExpectSend(outer); pairing::message::ConfigurationAckMessage message; adapter_.SendConfigurationAckMessage(message); } // Verifies that an OptionsMessage is successfully sent over the interface. TEST_F(ProtobufWireAdapterTest, SendOptionsMessage) { InSequence sequence; Options proto; proto.set_preferred_role(Options_RoleType_ROLE_TYPE_INPUT); Options_Encoding* encoding = proto.add_input_encodings(); encoding->set_type(Options_Encoding_EncodingType_ENCODING_TYPE_NUMERIC); encoding->set_symbol_length(16); encoding = proto.add_input_encodings(); encoding->set_type(Options_Encoding_EncodingType_ENCODING_TYPE_ALPHANUMERIC); encoding->set_symbol_length(32); encoding = proto.add_output_encodings(); encoding->set_type(Options_Encoding_EncodingType_ENCODING_TYPE_HEXADECIMAL); encoding->set_symbol_length(128); encoding = proto.add_output_encodings(); encoding->set_type(Options_Encoding_EncodingType_ENCODING_TYPE_QRCODE); encoding->set_symbol_length(512); OuterMessage outer; outer.set_type(OuterMessage_MessageType_MESSAGE_TYPE_OPTIONS); outer.set_payload(proto.SerializeAsString()); outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_OK); ExpectSend(outer); pairing::message::OptionsMessage message; message.set_protocol_role_preference( pairing::message::OptionsMessage::kInputDevice); // Note, the input and output encoding sets are sorted by complexity, so these // should be in the same order as the encodings added to the proto above to // ensure the assert matches. message.AddInputEncoding( encoding::EncodingOption(encoding::EncodingOption::kNumeric, 16)); message.AddInputEncoding( encoding::EncodingOption(encoding::EncodingOption::kAlphaNumeric, 32)); message.AddOutputEncoding( encoding::EncodingOption(encoding::EncodingOption::kHexadecimal, 128)); message.AddOutputEncoding( encoding::EncodingOption(encoding::EncodingOption::kQRCode, 512)); adapter_.SendOptionsMessage(message); } // Verifies that a PairingRequestMessage is successfully sent over the // interface. TEST_F(ProtobufWireAdapterTest, SendPairingRequestMessage) { InSequence sequence; PairingRequest proto; proto.set_client_name("foo-client"); proto.set_service_name("foo-service"); OuterMessage outer; outer.set_type(OuterMessage_MessageType_MESSAGE_TYPE_PAIRING_REQUEST); outer.set_payload(proto.SerializeAsString()); outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_OK); ExpectSend(outer); pairing::message::PairingRequestMessage message("foo-service", "foo-client"); adapter_.SendPairingRequestMessage(message); } // Verifies that a SendPairingRequestAckMesssage is successfully sent over the // interface. TEST_F(ProtobufWireAdapterTest, SendPairingRequestAckMessage) { InSequence sequence; PairingRequestAck proto; proto.set_server_name("foo-server"); OuterMessage outer; outer.set_type(OuterMessage_MessageType_MESSAGE_TYPE_PAIRING_REQUEST_ACK); outer.set_payload(proto.SerializeAsString()); outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_OK); ExpectSend(outer); pairing::message::PairingRequestAckMessage message("foo-server"); adapter_.SendPairingRequestAckMessage(message); } // Verifies that a SecretMessage is successfully sent over the interface. TEST_F(ProtobufWireAdapterTest, SendSecretMessage) { InSequence sequence; std::vector<unsigned char> secret(4); secret[0] = 0xAA; secret[1] = 0xBB; secret[2] = 0xCC; secret[3] = 0xDD; Secret proto; proto.set_secret(&secret[0], secret.size()); OuterMessage outer; outer.set_type(OuterMessage_MessageType_MESSAGE_TYPE_SECRET); outer.set_payload(proto.SerializeAsString()); outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_OK); ExpectSend(outer); pairing::message::SecretMessage message(secret); adapter_.SendSecretMessage(message); } // Verifies that a SecretAckMessage is successfully sent over the interface. TEST_F(ProtobufWireAdapterTest, SendSecretAckMessage) { InSequence sequence; std::vector<unsigned char> secret(4); secret[0] = 0xAA; secret[1] = 0xBB; secret[2] = 0xCC; secret[3] = 0xDD; SecretAck proto; proto.set_secret(&secret[0], secret.size()); OuterMessage outer; outer.set_type(OuterMessage_MessageType_MESSAGE_TYPE_SECRET_ACK); outer.set_payload(proto.SerializeAsString()); outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_OK); ExpectSend(outer); pairing::message::SecretAckMessage message(secret); adapter_.SendSecretAckMessage(message); } // Verifies that an ErrorMessage is successfully sent over the interface. TEST_F(ProtobufWireAdapterTest, SendErrorMessage) { InSequence sequence; OuterMessage outer; outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_BAD_SECRET); ExpectSend(outer); adapter_.SendErrorMessage(pairing::kErrorInvalidChallengeResponse); } } // namespace protobuf } // namespace wire } // namespace polo