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