// 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