• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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