1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. 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 #include <errno.h>
9 #include <stdarg.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12
13 #include <cstddef>
14 #include <cstdint>
15 #include <cstdio>
16 #include <memory>
17 #include <string>
18
19 #include "absl/log/absl_check.h"
20 #include "absl/log/absl_log.h"
21 #include "absl/status/status.h"
22 #include "absl/status/statusor.h"
23 #include "absl/strings/str_cat.h"
24 #include "conformance/conformance.pb.h"
25 #include "conformance/test_protos/test_messages_edition2023.pb.h"
26 #include "editions/golden/test_messages_proto2_editions.pb.h"
27 #include "editions/golden/test_messages_proto3_editions.pb.h"
28 #include "google/protobuf/endian.h"
29 #include "google/protobuf/json/json.h"
30 #include "google/protobuf/message.h"
31 #include "google/protobuf/test_messages_proto2.pb.h"
32 #include "google/protobuf/test_messages_proto3.pb.h"
33 #include "google/protobuf/text_format.h"
34 #include "google/protobuf/util/json_util.h"
35 #include "google/protobuf/util/type_resolver.h"
36 #include "google/protobuf/util/type_resolver_util.h"
37 #include "google/protobuf/stubs/status_macros.h"
38
39 // Must be included last.
40 #include "google/protobuf/port_def.inc"
41
42 namespace google {
43 namespace protobuf {
44 namespace {
45 using ::conformance::ConformanceRequest;
46 using ::conformance::ConformanceResponse;
47 using ::google::protobuf::util::BinaryToJsonString;
48 using ::google::protobuf::util::JsonParseOptions;
49 using ::google::protobuf::util::JsonToBinaryString;
50 using ::google::protobuf::util::NewTypeResolverForDescriptorPool;
51 using ::google::protobuf::util::TypeResolver;
52 using ::protobuf_test_messages::editions::TestAllTypesEdition2023;
53 using ::protobuf_test_messages::proto2::TestAllTypesProto2;
54 using ::protobuf_test_messages::proto3::TestAllTypesProto3;
55 using TestAllTypesProto2Editions =
56 ::protobuf_test_messages::editions::proto2::TestAllTypesProto2;
57 using TestAllTypesProto3Editions =
58 ::protobuf_test_messages::editions::proto3::TestAllTypesProto3;
59
ReadFd(int fd,char * buf,size_t len)60 absl::Status ReadFd(int fd, char* buf, size_t len) {
61 while (len > 0) {
62 ssize_t bytes_read = read(fd, buf, len);
63
64 if (bytes_read == 0) {
65 return absl::DataLossError("unexpected EOF");
66 }
67
68 if (bytes_read < 0) {
69 return absl::ErrnoToStatus(errno, "error reading from test runner");
70 }
71
72 len -= bytes_read;
73 buf += bytes_read;
74 }
75 return absl::OkStatus();
76 }
77
WriteFd(int fd,const void * buf,size_t len)78 absl::Status WriteFd(int fd, const void* buf, size_t len) {
79 if (static_cast<size_t>(write(fd, buf, len)) != len) {
80 return absl::ErrnoToStatus(errno, "error reading to test runner");
81 }
82 return absl::OkStatus();
83 }
84
85 class Harness {
86 public:
Harness()87 Harness() {
88 google::protobuf::LinkMessageReflection<TestAllTypesProto2>();
89 google::protobuf::LinkMessageReflection<TestAllTypesProto3>();
90 google::protobuf::LinkMessageReflection<TestAllTypesEdition2023>();
91 google::protobuf::LinkMessageReflection<TestAllTypesProto2Editions>();
92 google::protobuf::LinkMessageReflection<TestAllTypesProto3Editions>();
93
94 resolver_.reset(NewTypeResolverForDescriptorPool(
95 "type.googleapis.com", DescriptorPool::generated_pool()));
96 }
97
98 absl::StatusOr<ConformanceResponse> RunTest(
99 const ConformanceRequest& request);
100
101 // Returns Ok(true) if we're done processing requests.
102 absl::StatusOr<bool> ServeConformanceRequest();
103
104 private:
105 bool verbose_ = false;
106 std::unique_ptr<TypeResolver> resolver_;
107 };
108
RunTest(const ConformanceRequest & request)109 absl::StatusOr<ConformanceResponse> Harness::RunTest(
110 const ConformanceRequest& request) {
111 const Descriptor* descriptor =
112 DescriptorPool::generated_pool()->FindMessageTypeByName(
113 request.message_type());
114 if (descriptor == nullptr) {
115 return absl::NotFoundError(
116 absl::StrCat("No such message type: ", request.message_type()));
117 }
118 std::string type_url =
119 absl::StrCat("type.googleapis.com/", request.message_type());
120
121 std::unique_ptr<Message> test_message(
122 MessageFactory::generated_factory()->GetPrototype(descriptor)->New());
123 ConformanceResponse response;
124
125 switch (request.payload_case()) {
126 case ConformanceRequest::kProtobufPayload: {
127 if (!test_message->ParseFromString(request.protobuf_payload())) {
128 response.set_parse_error("parse error (no more details available)");
129 return response;
130 }
131 break;
132 }
133
134 case ConformanceRequest::kJsonPayload: {
135 JsonParseOptions options;
136 options.ignore_unknown_fields =
137 (request.test_category() ==
138 conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST);
139
140 std::string proto_binary;
141 absl::Status status =
142 JsonToBinaryString(resolver_.get(), type_url, request.json_payload(),
143 &proto_binary, options);
144 if (!status.ok()) {
145 response.set_parse_error(
146 absl::StrCat("parse error: ", status.message()));
147 return response;
148 }
149
150 if (!test_message->ParseFromString(proto_binary)) {
151 response.set_runtime_error(
152 "parsing JSON generated invalid proto output");
153 return response;
154 }
155
156 break;
157 }
158
159 case ConformanceRequest::kTextPayload: {
160 if (!TextFormat::ParseFromString(request.text_payload(),
161 test_message.get())) {
162 response.set_parse_error("parse error (no more details available)");
163 return response;
164 }
165 break;
166 }
167
168 case ConformanceRequest::PAYLOAD_NOT_SET:
169 return absl::InvalidArgumentError("request didn't have payload");
170
171 default:
172 return absl::InvalidArgumentError(
173 absl::StrCat("unknown payload type", request.payload_case()));
174 }
175
176 switch (request.requested_output_format()) {
177 case conformance::UNSPECIFIED:
178 return absl::InvalidArgumentError("unspecified output format");
179
180 case conformance::PROTOBUF: {
181 ABSL_CHECK(
182 test_message->SerializeToString(response.mutable_protobuf_payload()));
183 break;
184 }
185
186 case conformance::JSON: {
187 std::string proto_binary;
188 ABSL_CHECK(test_message->SerializeToString(&proto_binary));
189 absl::Status status =
190 BinaryToJsonString(resolver_.get(), type_url, proto_binary,
191 response.mutable_json_payload());
192 if (!status.ok()) {
193 response.set_serialize_error(absl::StrCat(
194 "failed to serialize JSON output: ", status.message()));
195 }
196 break;
197 }
198
199 case conformance::TEXT_FORMAT: {
200 TextFormat::Printer printer;
201 printer.SetHideUnknownFields(!request.print_unknown_fields());
202 ABSL_CHECK(printer.PrintToString(*test_message,
203 response.mutable_text_payload()));
204 break;
205 }
206
207 default:
208 return absl::InvalidArgumentError(absl::StrCat(
209 "unknown output format", request.requested_output_format()));
210 }
211
212 return response;
213 }
214
ServeConformanceRequest()215 absl::StatusOr<bool> Harness::ServeConformanceRequest() {
216 uint32_t in_len;
217 if (!ReadFd(STDIN_FILENO, reinterpret_cast<char*>(&in_len), sizeof(in_len))
218 .ok()) {
219 // EOF means we're done.
220 return true;
221 }
222 in_len = internal::little_endian::ToHost(in_len);
223
224 std::string serialized_input;
225 serialized_input.resize(in_len);
226 RETURN_IF_ERROR(ReadFd(STDIN_FILENO, &serialized_input[0], in_len));
227
228 ConformanceRequest request;
229 ABSL_CHECK(request.ParseFromString(serialized_input));
230
231 absl::StatusOr<ConformanceResponse> response = RunTest(request);
232 RETURN_IF_ERROR(response.status());
233
234 std::string serialized_output;
235 response->SerializeToString(&serialized_output);
236
237 uint32_t out_len = internal::little_endian::FromHost(
238 static_cast<uint32_t>(serialized_output.size()));
239
240 RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, &out_len, sizeof(out_len)));
241 RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, serialized_output.data(),
242 serialized_output.size()));
243
244 if (verbose_) {
245 ABSL_LOG(INFO) << "conformance-cpp: request="
246 << google::protobuf::ShortFormat(request)
247 << ", response=" << google::protobuf::ShortFormat(*response);
248 }
249 return false;
250 }
251 } // namespace
252 } // namespace protobuf
253 } // namespace google
254
main()255 int main() {
256 google::protobuf::Harness harness;
257 int total_runs = 0;
258 while (true) {
259 auto is_done = harness.ServeConformanceRequest();
260 if (!is_done.ok()) {
261 ABSL_LOG(FATAL) << is_done.status();
262 }
263 if (*is_done) {
264 break;
265 }
266 total_runs++;
267 }
268 ABSL_LOG(INFO) << "conformance-cpp: received EOF from test runner after "
269 << total_runs << " tests";
270 }
271
272 #include "google/protobuf/port_undef.inc"
273