blob: 519a09ba26a17e56c1bb416616777d34d5093b33 [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 Inc. 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 THE COPYRIGHT
// OWNER OR CONTRIBUTORS 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 <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <memory>
#include <string>
#include <utility>
#include "google/protobuf/message.h"
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/json_util.h"
#include "google/protobuf/util/type_resolver_util.h"
#include "google/protobuf/stubs/logging.h"
#include "google/protobuf/stubs/logging.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "conformance/conformance.pb.h"
#include "conformance/conformance.pb.h"
#include "google/protobuf/test_messages_proto2.pb.h"
#include "google/protobuf/test_messages_proto3.pb.h"
#include "google/protobuf/test_messages_proto3.pb.h"
#include "google/protobuf/util/type_resolver.h"
#include "google/protobuf/stubs/status_macros.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace {
using ::conformance::ConformanceRequest;
using ::conformance::ConformanceResponse;
using ::google::protobuf::util::BinaryToJsonString;
using ::google::protobuf::util::JsonParseOptions;
using ::google::protobuf::util::JsonToBinaryString;
using ::google::protobuf::util::NewTypeResolverForDescriptorPool;
using ::google::protobuf::util::TypeResolver;
using ::protobuf_test_messages::proto2::TestAllTypesProto2;
using ::protobuf_test_messages::proto3::TestAllTypesProto3;
absl::Status ReadFd(int fd, char* buf, size_t len) {
while (len > 0) {
ssize_t bytes_read = read(fd, buf, len);
if (bytes_read == 0) {
return absl::DataLossError("unexpected EOF");
}
if (bytes_read < 0) {
return absl::ErrnoToStatus(errno, "error reading from test runner");
}
len -= bytes_read;
buf += bytes_read;
}
return absl::OkStatus();
}
absl::Status WriteFd(int fd, const void* buf, size_t len) {
if (static_cast<size_t>(write(fd, buf, len)) != len) {
return absl::ErrnoToStatus(errno, "error reading to test runner");
}
return absl::OkStatus();
}
class Harness {
public:
Harness() {
google::protobuf::LinkMessageReflection<TestAllTypesProto2>();
google::protobuf::LinkMessageReflection<TestAllTypesProto3>();
resolver_.reset(NewTypeResolverForDescriptorPool(
"type.googleapis.com", DescriptorPool::generated_pool()));
type_url_ = absl::StrCat("type.googleapis.com/",
TestAllTypesProto3::GetDescriptor()->full_name());
}
absl::StatusOr<ConformanceResponse> RunTest(
const ConformanceRequest& request);
// Returns Ok(true) if we're done processing requests.
absl::StatusOr<bool> ServeConformanceRequest();
private:
bool verbose_ = false;
std::unique_ptr<TypeResolver> resolver_;
std::string type_url_;
};
absl::StatusOr<ConformanceResponse> Harness::RunTest(
const ConformanceRequest& request) {
const Descriptor* descriptor =
DescriptorPool::generated_pool()->FindMessageTypeByName(
request.message_type());
if (descriptor == nullptr) {
return absl::NotFoundError(
absl::StrCat("No such message type: ", request.message_type()));
}
std::unique_ptr<Message> test_message(
MessageFactory::generated_factory()->GetPrototype(descriptor)->New());
ConformanceResponse response;
switch (request.payload_case()) {
case ConformanceRequest::kProtobufPayload: {
if (!test_message->ParseFromString(request.protobuf_payload())) {
response.set_parse_error("parse error (no more details available)");
return response;
}
break;
}
case ConformanceRequest::kJsonPayload: {
JsonParseOptions options;
options.ignore_unknown_fields =
(request.test_category() ==
conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST);
std::string proto_binary;
absl::Status status =
JsonToBinaryString(resolver_.get(), type_url_, request.json_payload(),
&proto_binary, options);
if (!status.ok()) {
response.set_parse_error(
absl::StrCat("parse error: ", status.message()));
return response;
}
if (!test_message->ParseFromString(proto_binary)) {
response.set_runtime_error(
"parsing JSON generated invalid proto output");
return response;
}
break;
}
case ConformanceRequest::kTextPayload: {
if (!TextFormat::ParseFromString(request.text_payload(),
test_message.get())) {
response.set_parse_error("parse error (no more details available)");
return response;
}
break;
}
case ConformanceRequest::PAYLOAD_NOT_SET:
return absl::InvalidArgumentError("request didn't have payload");
default:
return absl::InvalidArgumentError(
absl::StrCat("unknown payload type", request.payload_case()));
}
switch (request.requested_output_format()) {
case conformance::UNSPECIFIED:
return absl::InvalidArgumentError("unspecified output format");
case conformance::PROTOBUF: {
GOOGLE_ABSL_CHECK(
test_message->SerializeToString(response.mutable_protobuf_payload()));
break;
}
case conformance::JSON: {
std::string proto_binary;
GOOGLE_ABSL_CHECK(test_message->SerializeToString(&proto_binary));
absl::Status status =
BinaryToJsonString(resolver_.get(), type_url_, proto_binary,
response.mutable_json_payload());
if (!status.ok()) {
response.set_serialize_error(absl::StrCat(
"failed to serialize JSON output: ", status.message()));
}
break;
}
case conformance::TEXT_FORMAT: {
TextFormat::Printer printer;
printer.SetHideUnknownFields(!request.print_unknown_fields());
GOOGLE_ABSL_CHECK(printer.PrintToString(*test_message,
response.mutable_text_payload()));
break;
}
default:
return absl::InvalidArgumentError(absl::StrCat(
"unknown output format", request.requested_output_format()));
}
return response;
}
absl::StatusOr<bool> Harness::ServeConformanceRequest() {
uint32_t in_len;
if (!ReadFd(STDIN_FILENO, reinterpret_cast<char*>(&in_len), sizeof(in_len))
.ok()) {
// EOF means we're done.
return true;
}
std::string serialized_input;
serialized_input.resize(in_len);
RETURN_IF_ERROR(ReadFd(STDIN_FILENO, &serialized_input[0], in_len));
ConformanceRequest request;
GOOGLE_ABSL_CHECK(request.ParseFromString(serialized_input));
absl::StatusOr<ConformanceResponse> response = RunTest(request);
RETURN_IF_ERROR(response.status());
std::string serialized_output;
response->SerializeToString(&serialized_output);
uint32_t out_len = static_cast<uint32_t>(serialized_output.size());
RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, &out_len, sizeof(out_len)));
RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, serialized_output.data(), out_len));
if (verbose_) {
GOOGLE_ABSL_LOG(INFO) << "conformance-cpp: request=" << request.ShortDebugString()
<< ", response=" << response->ShortDebugString();
}
return false;
}
} // namespace
} // namespace protobuf
} // namespace google
int main() {
google::protobuf::Harness harness;
int total_runs = 0;
while (true) {
auto is_done = harness.ServeConformanceRequest();
if (!is_done.ok()) {
GOOGLE_ABSL_LOG(FATAL) << is_done.status();
}
if (*is_done) {
break;
}
total_runs++;
}
GOOGLE_ABSL_LOG(INFO) << "conformance-cpp: received EOF from test runner after "
<< total_runs << " tests";
}