| // 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 |