blob: c00801a8b92f145a301670cf1d3f61aee42a2255 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// 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 "./rpc_fuzzing/proto_field_path.h"
#include <cstdint>
#include <vector>
#include "google/protobuf/descriptor.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "./fuzztest/internal/test_protobuf.pb.h"
#include "./rpc_fuzzing/testdata/mini_blogger.pb.h"
namespace fuzztest::internal {
using ::testing::ElementsAre;
using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::UnorderedElementsAre;
namespace {
TEST(FieldPathTest, GetFieldPathParseStringIntoAFieldPath) {
const FieldPath field_path =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.session_id");
EXPECT_THAT(
field_path.GetAllFields(),
ElementsAre(
LogOutUserRequest::GetDescriptor()->FindFieldByName("log_out_info"),
LogOutInfo::GetDescriptor()->FindFieldByName("session_info"),
SessionInfo::GetDescriptor()->FindFieldByName("session_id")));
}
TEST(FieldPathTest, GetFieldPathAbortsAtInvalidField) {
EXPECT_DEATH_IF_SUPPORTED(
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.invalid"),
"Invalid field name!");
}
MATCHER_P(FieldPathAsString, path_str, "") {
if (arg.GetAllFields().empty()) return path_str == "";
std::string result;
for (const google::protobuf::FieldDescriptor* part : arg.GetAllFields()) {
absl::StrAppend(&result, part->name(), ".");
}
return result.substr(0, result.size() - 1) == path_str;
}
TEST(FieldPathTest, HashAndEqualityCheck) {
const FieldPath path1 = GetFieldPath<LogInUserResponse>("session_id");
const FieldPath path2 = GetFieldPath<LogInUserResponse>("session_id");
EXPECT_EQ(path1, path2);
}
TEST(FieldPathTest, CopyFieldOfSameTypeAndSameNameInMessagesSucceeds) {
// Test copying LogInUserResponse.session_id to
// LogOutUserRequest.log_out_info.session_info.session_id.
LogInUserResponse log_in_response;
const FieldPath session_id_source_path =
GetFieldPath<LogInUserResponse>("session_id");
LogOutUserRequest log_out_request;
const FieldPath session_id_sink_path =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.session_id");
constexpr int64_t kSessionId = 0xdeadbeef;
log_in_response.set_session_id(kSessionId);
CopyField(session_id_source_path, log_in_response, session_id_sink_path,
log_out_request);
EXPECT_EQ(kSessionId,
log_out_request.log_out_info().session_info().session_id());
}
TEST(FieldPathTest, CopyingRepeatedFieldToNonRepeatedCopiesTheFirstElement) {
// Copy repeated `{val1, val2}` to non repeated `val2` results in `val2`.
constexpr int64_t kValue1 = 123;
constexpr int64_t kValue2 = 234;
MessageContainingRepeatedFields message_containing_repeated_fields;
message_containing_repeated_fields.add_field(kValue1);
message_containing_repeated_fields.add_field(kValue2);
const FieldPath source_path =
GetFieldPath<MessageContainingRepeatedFields>("field");
MessageNotContainingRepeatedFields message_not_containing_repeated_fields;
message_not_containing_repeated_fields.set_field(kValue2);
const FieldPath sink_path =
GetFieldPath<MessageNotContainingRepeatedFields>("field");
CopyField(source_path, message_containing_repeated_fields, sink_path,
message_not_containing_repeated_fields);
MessageNotContainingRepeatedFields expected_result;
expected_result.set_field(kValue1);
EXPECT_EQ(message_not_containing_repeated_fields.DebugString(),
expected_result.DebugString());
}
TEST(FieldPathTest,
CopyFieldDoesNothingIfSourceContainsEmptyRepeatedFieldInTheMiddle) {
MessageContainingRepeatedFields message_containing_repeated_fields;
const FieldPath source_path = GetFieldPath<MessageContainingRepeatedFields>(
"repeated_msg_field.field2");
MessageNotContainingRepeatedFields message_not_containing_repeated_fields;
message_not_containing_repeated_fields.set_field2(123);
const MessageNotContainingRepeatedFields expected_result =
message_not_containing_repeated_fields;
const FieldPath sink_path =
GetFieldPath<MessageNotContainingRepeatedFields>("field2");
CopyField(source_path, message_containing_repeated_fields, sink_path,
message_not_containing_repeated_fields);
EXPECT_EQ(message_not_containing_repeated_fields.DebugString(),
expected_result.DebugString());
}
TEST(FieldPathTest,
CopyFieldSucceedsIfSinkContainEmptyRepeatedFieldInTheMiddle) {
constexpr int64_t kValue1 = 123;
MessageContainingRepeatedFields message_containing_repeated_fields;
const FieldPath sink_path = GetFieldPath<MessageContainingRepeatedFields>(
"repeated_msg_field.field2");
MessageNotContainingRepeatedFields message_not_containing_repeated_fields;
message_not_containing_repeated_fields.set_field2(kValue1);
const FieldPath source_path =
GetFieldPath<MessageNotContainingRepeatedFields>("field2");
CopyField(source_path, message_not_containing_repeated_fields, sink_path,
message_containing_repeated_fields);
MessageContainingRepeatedFields expected_result;
expected_result.add_repeated_msg_field()->set_field2(kValue1);
EXPECT_EQ(message_containing_repeated_fields.DebugString(),
expected_result.DebugString());
}
TEST(FieldPathTest, CopyingEmptyRepeatedFieldToNonRepeatedClearsTheField) {
// Copy empty repeated `{}` to non repeated `val2` results in cleared field.
MessageContainingRepeatedFields message_containing_repeated_fields;
const FieldPath source_path =
GetFieldPath<MessageContainingRepeatedFields>("field");
MessageNotContainingRepeatedFields message_not_containing_repeated_fields;
message_not_containing_repeated_fields.set_field(123);
const FieldPath sink_path =
GetFieldPath<MessageNotContainingRepeatedFields>("field");
CopyField(source_path, message_containing_repeated_fields, sink_path,
message_not_containing_repeated_fields);
EXPECT_EQ(message_not_containing_repeated_fields.DebugString(),
MessageNotContainingRepeatedFields{}.DebugString());
}
TEST(FieldPathTest, CopyingNonRepeatedFieldToRepeatedFieldSetsFirstElement) {
// Copy non repeated `val2` to repeated `{val1, val2}` results in `{val2}`.
constexpr int64_t kValue1 = 123;
constexpr int64_t kValue2 = 234;
MessageNotContainingRepeatedFields message_not_containing_repeated_fields;
message_not_containing_repeated_fields.set_field(kValue2);
const FieldPath source_path =
GetFieldPath<MessageNotContainingRepeatedFields>("field");
MessageContainingRepeatedFields message_containing_repeated_fields;
message_containing_repeated_fields.add_field(kValue1);
message_containing_repeated_fields.add_field(kValue2);
const FieldPath sink_path =
GetFieldPath<MessageContainingRepeatedFields>("field");
CopyField(source_path, message_not_containing_repeated_fields, sink_path,
message_containing_repeated_fields);
MessageContainingRepeatedFields expected_result;
expected_result.add_field(kValue2);
EXPECT_EQ(message_containing_repeated_fields.DebugString(),
expected_result.DebugString());
}
TEST(FieldPathTest, CopyingRepeatedFieldHandlesFieldOfMessageTypeCorrectly) {
MessageContainingRepeatedFields source_message, sink_message;
source_message.add_repeated_msg_field()->set_field2(0x123);
source_message.add_repeated_msg_field()->set_field2(0x234);
const FieldPath field_path =
GetFieldPath<MessageContainingRepeatedFields>("repeated_msg_field");
CopyField(field_path, source_message, field_path, sink_message);
EXPECT_EQ(sink_message.DebugString(), source_message.DebugString());
}
TEST(FieldPathTest,
CopyingSIngularSrcToRepeatedDstHandlesFieldOfMessageTypeCorrectly) {
MessageWithSingleInnerRepeated source_message;
source_message.mutable_repeated_msg_field()->set_field2(123);
MessageContainingRepeatedFields sink_message;
sink_message.add_repeated_msg_field()->set_field2(1);
sink_message.add_repeated_msg_field()->set_field2(1);
const FieldPath sink_field_path =
GetFieldPath<MessageContainingRepeatedFields>("repeated_msg_field");
const FieldPath source_field_path =
GetFieldPath<MessageWithSingleInnerRepeated>("repeated_msg_field");
CopyField(source_field_path, source_message, sink_field_path, sink_message);
MessageContainingRepeatedFields expected_result;
expected_result.add_repeated_msg_field()->set_field2(123);
EXPECT_EQ(sink_message.DebugString(), expected_result.DebugString());
}
TEST(FieldPathTest, CopyingRepeatedFieldToRepeatedFieldCopiesTheWholeVector) {
// Copy repeated `{val1, val2}` to repeated `{}` results in `{val1, val2}`.
constexpr int64_t kValue1 = 123;
constexpr int64_t kValue2 = 234;
MessageContainingRepeatedFields source, sink;
source.add_field(kValue1);
source.add_field(kValue2);
const FieldPath source_path =
GetFieldPath<MessageContainingRepeatedFields>("field");
const FieldPath sink_path =
GetFieldPath<MessageContainingRepeatedFields>("field");
CopyField(source_path, source, sink_path, sink);
MessageContainingRepeatedFields expected_result;
expected_result.add_field(kValue1);
expected_result.add_field(kValue2);
EXPECT_EQ(sink.DebugString(), expected_result.DebugString());
}
TEST(FieldPathTest, CopyFieldOfSameTypeAndDifferentNameInMessagesFails) {
// Test copying GetUserPostsOptions.order to
// GetUserPostsRequest.options.
GetUserPostsOptions options;
const FieldPath order_source_path =
GetFieldPath<GetUserPostsOptions>("order");
GetUserPostsOptionsWithDifferentFieldNames options_with_different_field_names;
const FieldPath ordering_sink_path =
GetFieldPath<GetUserPostsOptionsWithDifferentFieldNames>("ordering");
EXPECT_DEATH_IF_SUPPORTED(
CopyField(order_source_path, options, ordering_sink_path,
options_with_different_field_names),
"Fields of mismatch names cannot be copied!");
}
TEST(FieldPathTest, CopyFieldOfDifferentTypeInMessagesFails) {
// Test copying LogInUserResponse.session_id to
// GetUserPostsRequest.options.
const LogInUserResponse log_in_response;
const FieldPath session_id_source_path =
GetFieldPath<LogInUserResponse>("session_id");
GetUserPostsRequest get_user_posts_request;
const FieldPath session_id_sink_path =
GetFieldPath<GetUserPostsRequest>("options");
EXPECT_DEATH_IF_SUPPORTED(
CopyField(session_id_source_path, log_in_response, session_id_sink_path,
get_user_posts_request),
"Fields of mismatch types cannot be copied!");
}
TEST(FieldPathTest, CopyFieldHandlesEnumCorrectly) {
GetUserPostsOptions source_options, sink_options;
const FieldPath field_path = GetFieldPath<GetUserPostsOptions>("order");
source_options.set_order(SortingOrder::ASCENDING);
CopyField(field_path, source_options, field_path, sink_options);
EXPECT_EQ(sink_options.order(), SortingOrder::ASCENDING);
}
TEST(FieldPathTest, GetContainingMessageOfLastFieldReturnsContainingMessage) {
const LogOutUserRequest log_out_request;
const FieldPath session_id_sink_path =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.session_id");
const LogOutInfo& log_out_info = log_out_request.log_out_info();
const SessionInfo& session_info = log_out_info.session_info();
const google::protobuf::Message* parent_msg =
session_id_sink_path.GetContainingMessageOfLastField(log_out_request);
EXPECT_EQ(parent_msg, &session_info);
}
TEST(FieldPathTest,
GetContainingMessageReturnsNonNullForUnsetMiddleSingularField) {
LogOutUserRequest log_out_request;
const FieldPath session_id_sink_path =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.session_id");
EXPECT_THAT(
session_id_sink_path.GetContainingMessageOfLastField(log_out_request),
NotNull());
}
TEST(FieldPathTest,
GetContainingMessageReturnsNullForMissingMiddleRepeatedField) {
MessageContainingRepeatedFields message_containing_repeated_fields;
const FieldPath message_containing_repeated_fields_sink_path =
GetFieldPath<MessageContainingRepeatedFields>(
"repeated_msg_field.field2");
EXPECT_THAT(
message_containing_repeated_fields_sink_path
.GetContainingMessageOfLastField(message_containing_repeated_fields),
IsNull());
}
TEST(FieldPathTest,
MutableContainingMessageAddMissingRepeatedFieldAndReturnsNonNull) {
MessageContainingRepeatedFields message_containing_repeated_fields;
const FieldPath message_containing_repeated_fields_sink_path =
GetFieldPath<MessageContainingRepeatedFields>(
"repeated_msg_field.field2");
EXPECT_THAT(message_containing_repeated_fields_sink_path
.MutableContainingMessageOfLastField(
message_containing_repeated_fields),
NotNull());
}
TEST(FieldPathTest,
MutableContainingMessageAtNonEmptyRepeatedFieldWillNotAddField) {
MessageContainingRepeatedFields message_containing_repeated_fields;
message_containing_repeated_fields.add_repeated_msg_field()->set_field2(
0x123);
const FieldPath message_containing_repeated_fields_sink_path =
GetFieldPath<MessageContainingRepeatedFields>(
"repeated_msg_field.field2");
(void)message_containing_repeated_fields_sink_path
.MutableContainingMessageOfLastField(message_containing_repeated_fields);
EXPECT_EQ(
message_containing_repeated_fields.GetReflection()->FieldSize(
message_containing_repeated_fields,
message_containing_repeated_fields.GetDescriptor()->FindFieldByName(
"repeated_msg_field")),
1);
}
TEST(FieldPathTest, MutableContainingMessageAtEmptyRepeatedFieldWillAddField) {
MessageContainingRepeatedFields message_containing_repeated_fields;
const FieldPath message_containing_repeated_fields_sink_path =
GetFieldPath<MessageContainingRepeatedFields>(
"repeated_msg_field.field2");
(void)message_containing_repeated_fields_sink_path
.MutableContainingMessageOfLastField(message_containing_repeated_fields);
EXPECT_EQ(
message_containing_repeated_fields.GetReflection()->FieldSize(
message_containing_repeated_fields,
message_containing_repeated_fields.GetDescriptor()->FindFieldByName(
"repeated_msg_field")),
1);
}
TEST(FieldPathTest,
GetContainingMessageOfLastFieldReturnsFirstIndexOfRepeatedFields) {
MessageContainingRepeatedFields message;
message.add_repeated_msg_field();
message.add_repeated_msg_field();
FieldPath field_path = GetFieldPath<MessageContainingRepeatedFields>(
"repeated_msg_field.field2");
const google::protobuf::Message* parent_msg =
field_path.GetContainingMessageOfLastField(message);
EXPECT_EQ(parent_msg, &message.repeated_msg_field(0));
}
TEST(FieldPathTest, MutableContainingMessageOfLastFieldReturnsMutableMessage) {
LogOutUserRequest log_out_request;
const FieldPath session_id_sink_path =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.session_id");
const LogOutInfo& log_out_info = log_out_request.log_out_info();
const SessionInfo& session_info = log_out_info.session_info();
google::protobuf::Message* parent_msg =
session_id_sink_path.MutableContainingMessageOfLastField(log_out_request);
// MutableContainingMessageOfLastField might create new message so the address
// will be different.
EXPECT_NE(parent_msg, &session_info);
}
TEST(FieldPathTest, GetLastFieldReturnsLastFieldOfThePath) {
const LogOutUserRequest log_out_request;
const FieldPath session_id_sink_path =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.session_id");
EXPECT_EQ(&session_id_sink_path.GetLastField(),
SessionInfo::GetDescriptor()->FindFieldByName("session_id"));
}
TEST(FieldPathTest,
AppendFieldSuccessIfTheLastFieldIsTheAppendedFieldParentType) {
const FieldPath ground_truth =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info");
FieldPath field_path = GetFieldPath<LogOutUserRequest>("log_out_info");
field_path.AppendField(
*LogOutInfo::GetDescriptor()->FindFieldByName("session_info"));
EXPECT_EQ(ground_truth, field_path);
}
TEST(FieldPathTest,
AppendFieldFailsIfTheLastFieldIsNotTheAppendedFieldParentType) {
FieldPath field_path = GetFieldPath<LogInUserResponse>("success");
EXPECT_DEATH_IF_SUPPORTED(
field_path.AppendField(field_path.GetLastField()),
"The current last field in the path must be a message "
"containing the appended field.");
}
TEST(FieldPathTest,
AppendPathSuccessIfLastFieldOfAppendedPathIsContainedByTheCurrentPath) {
const FieldPath ground_truth =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.session_id");
FieldPath field_path1 = GetFieldPath<LogOutUserRequest>("log_out_info");
const FieldPath field_path2 =
GetFieldPath<LogOutInfo>("session_info.session_id");
field_path1.AppendPath(field_path2);
EXPECT_EQ(field_path1, ground_truth);
}
TEST(FieldPathTest,
AppendPathFailsIfLastFieldOfAppendedPathIsNotContainedByTheCurrentPath) {
FieldPath field_path1 = GetFieldPath<LogOutUserRequest>("log_out_info");
const FieldPath field_path2 = GetFieldPath<SessionInfo>("session_id");
EXPECT_DEATH_IF_SUPPORTED(
field_path1.AppendPath(field_path2),
"The current last field in the path must be a message "
"containing the first field in the other path.");
}
TEST(FieldPathTest, CollectAllFieldsReturnAllInnerFields) {
std::vector<FieldPath> all_fields =
CollectAllFields(*LogOutUserRequest::descriptor());
EXPECT_THAT(all_fields,
UnorderedElementsAre(
FieldPathAsString("log_out_info"),
FieldPathAsString("log_out_info.session_info"),
FieldPathAsString("log_out_info.session_id"),
FieldPathAsString("log_out_info.session_info.session_id")));
}
TEST(FieldPathTest, CollectAllFieldsSupportsGroupField) {
std::vector<FieldPath> all_fields =
CollectAllFields(*MessageWithGroup::descriptor());
EXPECT_THAT(all_fields,
UnorderedElementsAre(FieldPathAsString("groupfield"),
FieldPathAsString("groupfield.field1"),
FieldPathAsString("groupfield.field2")));
}
TEST(FieldPathTest, CopyFieldsSupportsGroupField) {
MessageWithGroup message_with_group1, message_with_group2;
message_with_group1.mutable_groupfield()->set_field1(123);
CopyField(GetFieldPath<MessageWithGroup>("groupfield"), message_with_group1,
GetFieldPath<MessageWithGroup>("groupfield"), message_with_group2);
EXPECT_EQ(message_with_group2.groupfield().field1(), 123);
}
TEST(FieldPathTest, CollectAllFieldsInRecursiveMessageSkipVisitedMessages) {
std::vector<FieldPath> all_fields =
CollectAllFields(*RecursiveNode::descriptor());
EXPECT_THAT(all_fields, ElementsAre(FieldPathAsString("value"),
FieldPathAsString("children")));
}
TEST(FieldPathTest, ToStringConcatAllFieldPartWithDot) {
const FieldPath field_path =
GetFieldPath<LogOutUserRequest>("log_out_info.session_info.session_id");
EXPECT_EQ(field_path.ToString(), "log_out_info.session_info.session_id");
}
TEST(FieldPathTest, AlternativeToReturnsTrueForFieldsWithinTheSameOneOf) {
EXPECT_TRUE(
AreOneOfAltearnatives(GetFieldPath<OneOfMessage>("oneof1_field1.v1"),
GetFieldPath<OneOfMessage>("oneof1_field2")));
EXPECT_TRUE(
AreOneOfAltearnatives(GetFieldPath<OneOfMessage>("oneof1_field1.v2"),
GetFieldPath<OneOfMessage>("oneof1_field2")));
}
TEST(FieldPathTest, AlternativeToReturnsFalseForFieldsNotWithinTheSameOneOf) {
EXPECT_FALSE(
AreOneOfAltearnatives(GetFieldPath<OneOfMessage>("oneof1_field1"),
GetFieldPath<OneOfMessage>("oneof1_field1.v1")));
EXPECT_FALSE(
AreOneOfAltearnatives(GetFieldPath<OneOfMessage>("oneof1_field1.v1"),
GetFieldPath<OneOfMessage>("oneof1_field1.v1")));
EXPECT_FALSE(
AreOneOfAltearnatives(GetFieldPath<OneOfMessage>("oneof1_field2"),
GetFieldPath<OneOfMessage>("oneof2_field2")));
}
} // namespace
} // namespace fuzztest::internal