blob: f8e017cead3e0873e6badcecb15830da1dff738a [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef UPB_UTIL_DEF_TO_PROTO_TEST_H_
#define UPB_UTIL_DEF_TO_PROTO_TEST_H_
#include <string>
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/descriptor.upb.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "google/protobuf/descriptor.h"
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/util/field_comparator.h"
#include "upb/base/status.hpp"
#include "upb/mem/arena.hpp"
#include "upb/reflection/def.hpp"
#include "upb/util/def_to_proto.h"
namespace upb_test {
// A gtest matcher that verifies that a proto is equal to `proto`. Both `proto`
// and `arg` must be messages of type `msgdef_func` (a .upbdefs.h function that
// loads a known msgdef into the given defpool).
MATCHER_P(EqualsProtoTreatNansAsEqual, proto,
negation ? "are not equal" : "are equal") {
upb::DefPool defpool;
google::protobuf::DescriptorPool pool;
google::protobuf::DynamicMessageFactory factory;
std::string differences;
google::protobuf::util::DefaultFieldComparator comparator;
comparator.set_treat_nan_as_equal(true);
google::protobuf::util::MessageDifferencer differencer;
differencer.set_field_comparator(&comparator);
differencer.ReportDifferencesToString(&differences);
bool eq = differencer.Compare(proto, arg);
if (!eq) {
*result_listener << differences;
}
return eq;
}
class NullErrorCollector : public google::protobuf::DescriptorPool::ErrorCollector {
void RecordError(absl::string_view filename, absl::string_view element_name,
const google::protobuf::Message* descriptor, ErrorLocation location,
absl::string_view message) override {}
void RecordWarning(absl::string_view filename, absl::string_view element_name,
const google::protobuf::Message* descriptor, ErrorLocation location,
absl::string_view message) override {}
};
static void AddFile(google::protobuf::FileDescriptorProto& file, upb::DefPool* pool,
google::protobuf::DescriptorPool* desc_pool) {
NullErrorCollector collector;
const google::protobuf::FileDescriptor* file_desc =
desc_pool->BuildFileCollectingErrors(file, &collector);
if (file_desc != nullptr) {
// The file descriptor was valid according to proto2.
google::protobuf::FileDescriptorProto normalized_file;
file_desc->CopyTo(&normalized_file);
std::string serialized;
normalized_file.SerializeToString(&serialized);
upb::Arena arena;
upb::Status status;
google_protobuf_FileDescriptorProto* proto = google_protobuf_FileDescriptorProto_parse(
serialized.data(), serialized.size(), arena.ptr());
ASSERT_NE(proto, nullptr);
upb::FileDefPtr file_def = pool->AddFile(proto, &status);
// Ideally we could assert that file_def is present here. After all, any
// descriptor accepted by C++ should be by definition valid. However C++
// performs some of its validation at the .proto file parser level instead
// of when validating descriptors. As as result, C++ will accept some
// unreasonable descriptors like:
// file { name: "" package: "0" }
//
// There is no .proto file that will produce this descriptor, but
// BuildFile() accepts it. We should probably clean up these cases so C++
// will reject them too.
if (!file_def) return;
ASSERT_TRUE(status.ok()) << status.error_message();
google_protobuf_FileDescriptorProto* upb_proto =
upb_FileDef_ToProto(file_def.ptr(), arena.ptr());
size_t size;
const char* buf =
google_protobuf_FileDescriptorProto_serialize(upb_proto, arena.ptr(), &size);
google::protobuf::FileDescriptorProto google_proto;
bool ok = google_proto.ParseFromArray(buf, size);
ASSERT_TRUE(ok);
EXPECT_THAT(google_proto, EqualsProtoTreatNansAsEqual(normalized_file));
} else {
// This file was invalid according to proto2. When we parse it with upb,
// it may or may not be accepted, since upb does not perform as much
// validation as proto2. However it must not crash.
std::string serialized;
file.SerializeToString(&serialized);
upb::Arena arena;
upb::Status status;
google_protobuf_FileDescriptorProto* proto = google_protobuf_FileDescriptorProto_parse(
serialized.data(), serialized.size(), arena.ptr());
ASSERT_NE(proto, nullptr);
pool->AddFile(proto, &status);
}
}
inline void RoundTripDescriptor(const google::protobuf::FileDescriptorSet& set) {
upb::DefPool defpool;
google::protobuf::DescriptorPool desc_pool;
desc_pool.EnforceWeakDependencies(true);
for (const auto& file : set.file()) {
google::protobuf::FileDescriptorProto mutable_file(file);
AddFile(mutable_file, &defpool, &desc_pool);
}
}
} // namespace upb_test
#endif // UPB_UTIL_DEF_TO_PROTO_TEST_H_