/* * Copyright (c) 2009-2021, Google LLC * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Google LLC nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "upb/util/def_to_proto.h" #include "gmock/gmock.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/descriptor.upbdefs.h" #include "google/protobuf/dynamic_message.h" #include "google/protobuf/util/message_differencer.h" #include "gtest/gtest.h" #include "upb/def.hpp" #include "upb/upb.hpp" #include "upb/util/def_to_proto_test.upbdefs.h" // Loads and retrieves a descriptor for `msgdef` into the given `pool`. const google::protobuf::Descriptor* AddMessageDescriptor( upb::MessageDefPtr msgdef, google::protobuf::DescriptorPool* pool) { upb::Arena tmp_arena; upb::FileDefPtr file = msgdef.file(); google_protobuf_FileDescriptorProto* upb_proto = upb_FileDef_ToProto(file.ptr(), tmp_arena.ptr()); size_t size; const char* buf = google_protobuf_FileDescriptorProto_serialize( upb_proto, tmp_arena.ptr(), &size); google::protobuf::FileDescriptorProto google_proto; google_proto.ParseFromArray(buf, size); const google::protobuf::FileDescriptor* file_desc = pool->BuildFile(google_proto); EXPECT_TRUE(file_desc != nullptr); return pool->FindMessageTypeByName(msgdef.full_name()); } // Converts a upb `msg` (with type `msgdef`) into a protobuf Message object from // the given factory and descriptor. std::unique_ptr ToProto( const upb_Message* msg, const upb_MessageDef* msgdef, const google::protobuf::Descriptor* desc, google::protobuf::MessageFactory* factory) { upb::Arena arena; EXPECT_TRUE(desc != nullptr); std::unique_ptr google_msg( factory->GetPrototype(desc)->New()); size_t size; const char* buf = upb_Encode(msg, upb_MessageDef_MiniTable(msgdef), 0, arena.ptr(), &size); google_msg->ParseFromArray(buf, size); return google_msg; } // 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 symtab). MATCHER_P2(EqualsUpbProto, proto, msgdef_func, negation ? "are not equal" : "are equal") { upb::SymbolTable symtab; google::protobuf::DescriptorPool pool; google::protobuf::DynamicMessageFactory factory; upb::MessageDefPtr msgdef(msgdef_func(symtab.ptr())); EXPECT_TRUE(msgdef.ptr() != nullptr); const google::protobuf::Descriptor* desc = AddMessageDescriptor(msgdef, &pool); EXPECT_TRUE(desc != nullptr); std::unique_ptr m1( ToProto(proto, msgdef.ptr(), desc, &factory)); std::unique_ptr m2( ToProto(arg, msgdef.ptr(), desc, &factory)); std::string differences; google::protobuf::util::MessageDifferencer differencer; differencer.ReportDifferencesToString(&differences); bool eq = differencer.Compare(*m2, *m1); if (!eq) { *result_listener << differences; } return eq; } // Verifies that the given upb FileDef can be converted to a proto that matches // `proto`. void CheckFile(const upb::FileDefPtr file, const google_protobuf_FileDescriptorProto* proto) { upb::Arena arena; google_protobuf_FileDescriptorProto* proto2 = upb_FileDef_ToProto(file.ptr(), arena.ptr()); ASSERT_THAT( proto, EqualsUpbProto(proto2, google_protobuf_FileDescriptorProto_getmsgdef)); } // Verifies that upb/util/def_to_proto_test.proto can round-trip: // serialized descriptor -> upb def -> serialized descriptor TEST(DefToProto, Test) { upb::Arena arena; upb::SymbolTable symtab; upb_StringView test_file_desc = upb_util_def_to_proto_test_proto_upbdefinit.descriptor; const auto* file_desc = google_protobuf_FileDescriptorProto_parse( test_file_desc.data, test_file_desc.size, arena.ptr()); upb::MessageDefPtr msgdef(pkg_Message_getmsgdef(symtab.ptr())); upb::FileDefPtr file = msgdef.file(); CheckFile(file, file_desc); } // Like the previous test, but uses a message layout built at runtime. TEST(DefToProto, TestRuntimeReflection) { upb::Arena arena; upb::SymbolTable symtab; upb_StringView test_file_desc = upb_util_def_to_proto_test_proto_upbdefinit.descriptor; const auto* file_desc = google_protobuf_FileDescriptorProto_parse( test_file_desc.data, test_file_desc.size, arena.ptr()); _upb_DefPool_LoadDefInitEx( symtab.ptr(), &upb_util_def_to_proto_test_proto_upbdefinit, true); upb::FileDefPtr file = symtab.FindFileByName( upb_util_def_to_proto_test_proto_upbdefinit.filename); CheckFile(file, file_desc); }