1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #ifndef UPB_UTIL_DEF_TO_PROTO_TEST_H_
9 #define UPB_UTIL_DEF_TO_PROTO_TEST_H_
10
11 #include <string>
12
13 #include "google/protobuf/descriptor.pb.h"
14 #include "google/protobuf/descriptor.upb.h"
15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
17 #include "google/protobuf/descriptor.h"
18 #include "google/protobuf/dynamic_message.h"
19 #include "google/protobuf/util/field_comparator.h"
20 #include "upb/base/status.hpp"
21 #include "upb/mem/arena.hpp"
22 #include "upb/reflection/def.hpp"
23 #include "upb/util/def_to_proto.h"
24
25 namespace upb_test {
26
27 // A gtest matcher that verifies that a proto is equal to `proto`. Both `proto`
28 // and `arg` must be messages of type `msgdef_func` (a .upbdefs.h function that
29 // loads a known msgdef into the given defpool).
30 MATCHER_P(EqualsProtoTreatNansAsEqual, proto,
31 negation ? "are not equal" : "are equal") {
32 upb::DefPool defpool;
33 google::protobuf::DescriptorPool pool;
34 google::protobuf::DynamicMessageFactory factory;
35 std::string differences;
36 google::protobuf::util::DefaultFieldComparator comparator;
37 comparator.set_treat_nan_as_equal(true);
38 google::protobuf::util::MessageDifferencer differencer;
39 differencer.set_field_comparator(&comparator);
40 differencer.ReportDifferencesToString(&differences);
41 bool eq = differencer.Compare(proto, arg);
42 if (!eq) {
43 *result_listener << differences;
44 }
45 return eq;
46 }
47
48 class NullErrorCollector : public google::protobuf::DescriptorPool::ErrorCollector {
RecordError(absl::string_view filename,absl::string_view element_name,const google::protobuf::Message * descriptor,ErrorLocation location,absl::string_view message)49 void RecordError(absl::string_view filename, absl::string_view element_name,
50 const google::protobuf::Message* descriptor, ErrorLocation location,
51 absl::string_view message) override {}
RecordWarning(absl::string_view filename,absl::string_view element_name,const google::protobuf::Message * descriptor,ErrorLocation location,absl::string_view message)52 void RecordWarning(absl::string_view filename, absl::string_view element_name,
53 const google::protobuf::Message* descriptor, ErrorLocation location,
54 absl::string_view message) override {}
55 };
56
AddFile(google::protobuf::FileDescriptorProto & file,upb::DefPool * pool,google::protobuf::DescriptorPool * desc_pool)57 static void AddFile(google::protobuf::FileDescriptorProto& file, upb::DefPool* pool,
58 google::protobuf::DescriptorPool* desc_pool) {
59 NullErrorCollector collector;
60 const google::protobuf::FileDescriptor* file_desc =
61 desc_pool->BuildFileCollectingErrors(file, &collector);
62
63 if (file_desc != nullptr) {
64 // The file descriptor was valid according to proto2.
65 google::protobuf::FileDescriptorProto normalized_file;
66 file_desc->CopyTo(&normalized_file);
67 std::string serialized;
68 normalized_file.SerializeToString(&serialized);
69 upb::Arena arena;
70 upb::Status status;
71 google_protobuf_FileDescriptorProto* proto = google_protobuf_FileDescriptorProto_parse(
72 serialized.data(), serialized.size(), arena.ptr());
73 ASSERT_NE(proto, nullptr);
74 upb::FileDefPtr file_def = pool->AddFile(proto, &status);
75
76 // Ideally we could assert that file_def is present here. After all, any
77 // descriptor accepted by C++ should be by definition valid. However C++
78 // performs some of its validation at the .proto file parser level instead
79 // of when validating descriptors. As as result, C++ will accept some
80 // unreasonable descriptors like:
81 // file { name: "" package: "0" }
82 //
83 // There is no .proto file that will produce this descriptor, but
84 // BuildFile() accepts it. We should probably clean up these cases so C++
85 // will reject them too.
86 if (!file_def) return;
87
88 ASSERT_TRUE(status.ok()) << status.error_message();
89 google_protobuf_FileDescriptorProto* upb_proto =
90 upb_FileDef_ToProto(file_def.ptr(), arena.ptr());
91 size_t size;
92 const char* buf =
93 google_protobuf_FileDescriptorProto_serialize(upb_proto, arena.ptr(), &size);
94 google::protobuf::FileDescriptorProto google_proto;
95 bool ok = google_proto.ParseFromArray(buf, size);
96 ASSERT_TRUE(ok);
97 EXPECT_THAT(google_proto, EqualsProtoTreatNansAsEqual(normalized_file));
98 } else {
99 // This file was invalid according to proto2. When we parse it with upb,
100 // it may or may not be accepted, since upb does not perform as much
101 // validation as proto2. However it must not crash.
102 std::string serialized;
103 file.SerializeToString(&serialized);
104 upb::Arena arena;
105 upb::Status status;
106 google_protobuf_FileDescriptorProto* proto = google_protobuf_FileDescriptorProto_parse(
107 serialized.data(), serialized.size(), arena.ptr());
108 ASSERT_NE(proto, nullptr);
109 pool->AddFile(proto, &status);
110 }
111 }
112
RoundTripDescriptor(const google::protobuf::FileDescriptorSet & set)113 inline void RoundTripDescriptor(const google::protobuf::FileDescriptorSet& set) {
114 upb::DefPool defpool;
115 google::protobuf::DescriptorPool desc_pool;
116 desc_pool.EnforceWeakDependencies(true);
117 for (const auto& file : set.file()) {
118 google::protobuf::FileDescriptorProto mutable_file(file);
119 AddFile(mutable_file, &defpool, &desc_pool);
120 }
121 }
122
123 } // namespace upb_test
124
125 #endif // UPB_UTIL_DEF_TO_PROTO_TEST_H_
126