blob: ac0dcf03ff83978e26a26b6b8265c6d9bdd30232 [file]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
// This file contains a program for running the test suite in a separate
// process. The other alternative is to run the suite in-process. See
// conformance.proto for pros/cons of these two options.
//
// This program will fork the process under test and communicate with it over
// its stdin/stdout:
//
// +--------+ pipe +----------+
// | tester | <------> | testee |
// | | | |
// | C++ | | any lang |
// +--------+ +----------+
//
// The tester contains all of the test cases and their expected output.
// The testee is a simple program written in the target language that reads
// each test case and attempts to produce acceptable output for it.
//
// Every test consists of a ConformanceRequest/ConformanceResponse
// request/reply pair. The protocol on the pipe is simply:
//
// 1. tester sends 4-byte length N (little endian)
// 2. tester sends N bytes representing a ConformanceRequest proto
// 3. testee sends 4-byte length M (little endian)
// 4. testee sends M bytes representing a ConformanceResponse proto
#include "conformance/fork_pipe_runner.h"
#include <cstdint>
#include <string>
#include "absl/log/absl_log.h"
#include "absl/strings/string_view.h"
#include "conformance/conformance.pb.h"
#include "google/protobuf/endian.h"
namespace google {
namespace protobuf {
namespace conformance {
// The other constructor and the destructor are defined by the platform's
// implementation file: they create and destroy `state_`, whose type is only
// complete there.
ForkPipeRunner::ForkPipeRunner(absl::string_view executable)
: ForkPipeRunner(executable, {}) {}
std::string ForkPipeRunner::RunTest(absl::string_view test_name,
absl::string_view request) {
if (!IsTestProgramRunning()) {
SpawnTestProgram();
}
current_test_name_ = std::string(test_name);
uint32_t len = ::google::protobuf::internal::little_endian::FromHost(
static_cast<uint32_t>(request.size()));
if (!TryWrite(&len, sizeof(uint32_t)) ||
!TryWrite(request.data(), request.size())) {
return ReportFailure(/*timed_out=*/false, "error writing to child");
}
ReadResult read_result = TryRead(&len, sizeof(uint32_t));
if (read_result != ReadResult::kOk) return ReportReadFailure(read_result);
len = ::google::protobuf::internal::little_endian::ToHost(len);
std::string response(len, '\0');
read_result = TryRead(&response[0], len);
if (read_result != ReadResult::kOk) return ReportReadFailure(read_result);
return response;
}
std::string ForkPipeRunner::ReportReadFailure(ReadResult read_result) {
absl::string_view what_failed;
switch (read_result) {
case ReadResult::kTimeout:
what_failed = "child timed out";
break;
case ReadResult::kEof:
what_failed = "child closed its output without responding";
break;
default:
what_failed = "error reading from child";
break;
}
return ReportFailure(read_result == ReadResult::kTimeout, what_failed);
}
std::string ForkPipeRunner::ReportFailure(bool timed_out,
absl::string_view what_failed) {
// The exchange with the testee failed: it exited, crashed, or hung. It is
// shut down and the outcome classified by the platform's implementation;
// the next RunTest() call will spawn a fresh testee.
const std::string error_msg = GetTestProgramFailure(what_failed);
ABSL_LOG(INFO) << error_msg;
::conformance::ConformanceResponse response;
if (timed_out) {
response.set_timeout_error(error_msg);
} else {
response.set_runtime_error(error_msg);
}
return response.SerializeAsString();
}
} // namespace conformance
} // namespace protobuf
} // namespace google