blob: 168ed57a7da12b5956ed79f3c9cb8fadbbb7e278 [file] [log] [blame]
// Copyright 2021 gRPC authors.
//
// 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.
#include "src/core/ext/transport/binder/wire_format/wire_writer.h"
#include <string>
#include <utility>
#include <gtest/gtest.h>
#include "absl/memory/memory.h"
#include <grpcpp/impl/grpc_library.h>
#include "test/core/transport/binder/mock_objects.h"
#include "test/core/util/test_config.h"
namespace grpc_binder {
using ::testing::Return;
MATCHER_P(StrEqInt8Ptr, target, "") {
return std::string(reinterpret_cast<const char*>(arg), target.size()) ==
target;
}
TEST(WireWriterTest, RpcCall) {
grpc::internal::GrpcLibrary init_lib;
// Required because wire writer uses combiner internally.
grpc_core::ExecCtx exec_ctx;
auto mock_binder = std::make_unique<MockBinder>();
MockBinder& mock_binder_ref = *mock_binder;
MockWritableParcel mock_writable_parcel;
ON_CALL(mock_binder_ref, GetWritableParcel)
.WillByDefault(Return(&mock_writable_parcel));
WireWriterImpl wire_writer(std::move(mock_binder));
auto ExpectWriteByteArray = [&](const std::string& target) {
// length
EXPECT_CALL(mock_writable_parcel, WriteInt32(target.size()));
if (!target.empty()) {
// content
EXPECT_CALL(mock_writable_parcel,
WriteByteArray(StrEqInt8Ptr(target), target.size()));
}
};
::testing::InSequence sequence;
int sequence_number = 0;
{
// flag
EXPECT_CALL(mock_writable_parcel, WriteInt32(0));
// sequence number
EXPECT_CALL(mock_writable_parcel, WriteInt32(sequence_number));
EXPECT_CALL(mock_binder_ref, Transact(BinderTransportTxCode(kFirstCallId)));
auto tx = std::make_unique<Transaction>(kFirstCallId, /*is_client=*/true);
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok());
sequence_number++;
grpc_core::ExecCtx::Get()->Flush();
}
{
// flag
EXPECT_CALL(mock_writable_parcel, WriteInt32(kFlagPrefix));
// sequence number. This is another stream so the sequence number starts
// with 0.
EXPECT_CALL(mock_writable_parcel, WriteInt32(0));
EXPECT_CALL(mock_writable_parcel,
WriteString(absl::string_view("/example/method/ref")));
const std::vector<std::pair<std::string, std::string>> kMetadata = {
{"", ""},
{"", "value"},
{"key", ""},
{"key", "value"},
{"another-key", "another-value"},
};
// Number of metadata
EXPECT_CALL(mock_writable_parcel, WriteInt32(kMetadata.size()));
for (const auto& md : kMetadata) {
ExpectWriteByteArray(md.first);
ExpectWriteByteArray(md.second);
}
EXPECT_CALL(mock_binder_ref,
Transact(BinderTransportTxCode(kFirstCallId + 1)));
auto tx =
std::make_unique<Transaction>(kFirstCallId + 1, /*is_client=*/true);
tx->SetPrefix(kMetadata);
tx->SetMethodRef("/example/method/ref");
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok());
grpc_core::ExecCtx::Get()->Flush();
}
{
// flag
EXPECT_CALL(mock_writable_parcel, WriteInt32(kFlagMessageData));
// sequence number
EXPECT_CALL(mock_writable_parcel, WriteInt32(sequence_number));
ExpectWriteByteArray("data");
EXPECT_CALL(mock_binder_ref, Transact(BinderTransportTxCode(kFirstCallId)));
auto tx = std::make_unique<Transaction>(kFirstCallId, /*is_client=*/true);
tx->SetData("data");
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok());
sequence_number++;
grpc_core::ExecCtx::Get()->Flush();
}
{
// flag
EXPECT_CALL(mock_writable_parcel, WriteInt32(kFlagSuffix));
// sequence number
EXPECT_CALL(mock_writable_parcel, WriteInt32(sequence_number));
EXPECT_CALL(mock_binder_ref, Transact(BinderTransportTxCode(kFirstCallId)));
auto tx = std::make_unique<Transaction>(kFirstCallId, /*is_client=*/true);
tx->SetSuffix({});
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok());
sequence_number++;
grpc_core::ExecCtx::Get()->Flush();
}
{
// flag
EXPECT_CALL(mock_writable_parcel,
WriteInt32(kFlagPrefix | kFlagMessageData | kFlagSuffix));
// sequence number
EXPECT_CALL(mock_writable_parcel, WriteInt32(sequence_number));
EXPECT_CALL(mock_writable_parcel,
WriteString(absl::string_view("/example/method/ref")));
const std::vector<std::pair<std::string, std::string>> kMetadata = {
{"", ""},
{"", "value"},
{"key", ""},
{"key", "value"},
{"another-key", "another-value"},
};
// Number of metadata
EXPECT_CALL(mock_writable_parcel, WriteInt32(kMetadata.size()));
for (const auto& md : kMetadata) {
ExpectWriteByteArray(md.first);
ExpectWriteByteArray(md.second);
}
// Empty message data
ExpectWriteByteArray("");
EXPECT_CALL(mock_binder_ref, Transact(BinderTransportTxCode(kFirstCallId)));
auto tx = std::make_unique<Transaction>(kFirstCallId, /*is_client=*/true);
// TODO(waynetu): Implement a helper function that automatically creates
// EXPECT_CALL based on the tx object.
tx->SetPrefix(kMetadata);
tx->SetMethodRef("/example/method/ref");
tx->SetData("");
tx->SetSuffix({});
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok());
sequence_number++;
grpc_core::ExecCtx::Get()->Flush();
}
// Really large message
{
EXPECT_CALL(mock_writable_parcel,
WriteInt32(kFlagMessageData | kFlagMessageDataIsPartial));
EXPECT_CALL(mock_writable_parcel, WriteInt32(0));
ExpectWriteByteArray(std::string(WireWriterImpl::kBlockSize, 'a'));
EXPECT_CALL(mock_writable_parcel, GetDataSize)
.WillOnce(Return(WireWriterImpl::kBlockSize));
EXPECT_CALL(mock_binder_ref,
Transact(BinderTransportTxCode(kFirstCallId + 2)));
EXPECT_CALL(mock_writable_parcel,
WriteInt32(kFlagMessageData | kFlagMessageDataIsPartial));
EXPECT_CALL(mock_writable_parcel, WriteInt32(1));
ExpectWriteByteArray(std::string(WireWriterImpl::kBlockSize, 'a'));
EXPECT_CALL(mock_writable_parcel, GetDataSize)
.WillOnce(Return(WireWriterImpl::kBlockSize));
EXPECT_CALL(mock_binder_ref,
Transact(BinderTransportTxCode(kFirstCallId + 2)));
EXPECT_CALL(mock_writable_parcel, WriteInt32(kFlagMessageData));
EXPECT_CALL(mock_writable_parcel, WriteInt32(2));
ExpectWriteByteArray("a");
EXPECT_CALL(mock_writable_parcel, GetDataSize).WillOnce(Return(1));
EXPECT_CALL(mock_binder_ref,
Transact(BinderTransportTxCode(kFirstCallId + 2)));
// Use a new stream.
auto tx =
std::make_unique<Transaction>(kFirstCallId + 2, /*is_client=*/true);
tx->SetData(std::string(2 * WireWriterImpl::kBlockSize + 1, 'a'));
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok());
grpc_core::ExecCtx::Get()->Flush();
}
// Really large message with metadata
{
EXPECT_CALL(
mock_writable_parcel,
WriteInt32(kFlagPrefix | kFlagMessageData | kFlagMessageDataIsPartial));
EXPECT_CALL(mock_writable_parcel, WriteInt32(0));
EXPECT_CALL(mock_writable_parcel, WriteString(absl::string_view("123")));
EXPECT_CALL(mock_writable_parcel, WriteInt32(0));
ExpectWriteByteArray(std::string(WireWriterImpl::kBlockSize, 'a'));
EXPECT_CALL(mock_writable_parcel, GetDataSize)
.WillOnce(Return(WireWriterImpl::kBlockSize));
EXPECT_CALL(mock_binder_ref,
Transact(BinderTransportTxCode(kFirstCallId + 3)));
EXPECT_CALL(mock_writable_parcel,
WriteInt32(kFlagMessageData | kFlagMessageDataIsPartial));
EXPECT_CALL(mock_writable_parcel, WriteInt32(1));
ExpectWriteByteArray(std::string(WireWriterImpl::kBlockSize, 'a'));
EXPECT_CALL(mock_writable_parcel, GetDataSize)
.WillOnce(Return(WireWriterImpl::kBlockSize));
EXPECT_CALL(mock_binder_ref,
Transact(BinderTransportTxCode(kFirstCallId + 3)));
EXPECT_CALL(mock_writable_parcel,
WriteInt32(kFlagMessageData | kFlagSuffix));
EXPECT_CALL(mock_writable_parcel, WriteInt32(2));
ExpectWriteByteArray("a");
EXPECT_CALL(mock_writable_parcel, GetDataSize).WillOnce(Return(1));
EXPECT_CALL(mock_binder_ref,
Transact(BinderTransportTxCode(kFirstCallId + 3)));
// Use a new stream.
auto tx =
std::make_unique<Transaction>(kFirstCallId + 3, /*is_client=*/true);
tx->SetPrefix({});
tx->SetMethodRef("123");
tx->SetData(std::string(2 * WireWriterImpl::kBlockSize + 1, 'a'));
tx->SetSuffix({});
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok());
grpc_core::ExecCtx::Get()->Flush();
}
grpc_core::ExecCtx::Get()->Flush();
}
} // namespace grpc_binder
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
return RUN_ALL_TESTS();
}