From 3f946dffa504ac9a75a8457d8591b53ab2b81495 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Tue, 20 Dec 2022 17:18:17 -0800 Subject: [PATCH] Implement conformance test binary --- Cargo.toml | 1 + test-crates/conformance/Cargo.toml | 12 +++ test-crates/conformance/build.rs | 7 ++ test-crates/conformance/src/main.rs | 113 ++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 test-crates/conformance/Cargo.toml create mode 100644 test-crates/conformance/build.rs create mode 100644 test-crates/conformance/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 812e5f2c8..9e20d9af6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "protobuf-examples/issue-614", "protobuf-parse", "protobuf-support", + "test-crates/conformance", "test-crates/perftest/bytes", "test-crates/perftest/misc", "test-crates/perftest/vs-cxx", diff --git a/test-crates/conformance/Cargo.toml b/test-crates/conformance/Cargo.toml new file mode 100644 index 000000000..8fd4b1c4e --- /dev/null +++ b/test-crates/conformance/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "conformance" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.68" +protobuf = { path = "../../protobuf" } +protobuf-json-mapping = { version = "4.0.0-alpha.0", path = "../../protobuf-json-mapping" } + +[build-dependencies] +protobuf-codegen = { path = "../../protobuf-codegen" } diff --git a/test-crates/conformance/build.rs b/test-crates/conformance/build.rs new file mode 100644 index 000000000..d82740b8a --- /dev/null +++ b/test-crates/conformance/build.rs @@ -0,0 +1,7 @@ +fn main() { + protobuf_codegen::Codegen::new() + .includes(["../../google-protobuf-all-protos/protobuf/protobuf-git/conformance", "../../google-protobuf-all-protos/protobuf/protobuf-git/src/google/protobuf"]) + .inputs(["../../google-protobuf-all-protos/protobuf/protobuf-git/conformance/conformance.proto", "../../google-protobuf-all-protos/protobuf/protobuf-git/src/google/protobuf/test_messages_proto2.proto", "../../google-protobuf-all-protos/protobuf/protobuf-git/src/google/protobuf/test_messages_proto3.proto"]) + .cargo_out_dir("protos") + .run_from_script(); +} diff --git a/test-crates/conformance/src/main.rs b/test-crates/conformance/src/main.rs new file mode 100644 index 000000000..acb614cad --- /dev/null +++ b/test-crates/conformance/src/main.rs @@ -0,0 +1,113 @@ +use std::io::{self, Read, Write}; + +use anyhow::{anyhow, bail, Context, Error, Result}; + +mod protos { + include!(concat!(env!("OUT_DIR"), "/protos/mod.rs")); +} + +use protobuf::Message; +use protos::conformance::{ + conformance_request::Payload, ConformanceRequest, ConformanceResponse, WireFormat, +}; + +fn main() { + loop { + match serve_conformance_request() { + Ok(false) => {} + Ok(true) => break, + Err(e) => { + eprintln!("{:#}", e); + std::process::exit(1) + } + } + } +} + +fn serve_conformance_request() -> Result { + let mut stdin = io::stdin().lock(); + let mut len = [0; 4]; + match stdin.read_exact(&mut len) { + Ok(()) => {} + Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => { + return Ok(true); + } + Err(e) => return Err(Error::new(e).context("reading request length")), + } + let len = u32::from_le_bytes(len); + let mut buf = vec![0; len as usize]; + stdin.read_exact(&mut buf).context("reading request")?; + + let request = ConformanceRequest::parse_from_bytes(&buf).context("parsing request")?; + + let response = run_test(&request).context("running test")?; + + let response = response.write_to_bytes().context("serializing response")?; + io::stdout() + .write_all(&response) + .context("writing response")?; + + Ok(false) +} + +fn run_test(request: &ConformanceRequest) -> Result { + let files = [ + protos::test_messages_proto3::file_descriptor(), + protos::test_messages_proto2::file_descriptor(), + ]; + let descriptor = files + .into_iter() + .flat_map(|file| file.message_by_full_name(&request.message_type)) + .next() + .ok_or_else(|| anyhow!("couldn't find message type {}", request.message_type))?; + + let Some(ref payload) = request.payload else { bail!("missing or unsupported payload"); }; + + let mut test_message = descriptor.new_instance(); + let mut response = ConformanceResponse::new(); + match *payload { + Payload::ProtobufPayload(ref payload) => { + if let Err(e) = test_message.merge_from_bytes_dyn(payload) { + response.set_parse_error(e.to_string()); + } + } + Payload::JsonPayload(ref payload) => { + if let Err(e) = protobuf_json_mapping::merge_from_str(&mut *test_message, payload) { + response.set_parse_error(e.to_string()); + } + } + Payload::JspbPayload(_) => { + response.set_skipped("JSPB input not supported".into()); + } + Payload::TextPayload(ref payload) => { + if let Err(e) = protobuf::text_format::merge_from_str(&mut *test_message, payload) { + response.set_parse_error(e.to_string()); + } + } + } + + let requested_output_format = request + .requested_output_format + .enum_value() + .map_err(|i| anyhow!("unknown wire format {}", i))?; + match requested_output_format { + WireFormat::UNSPECIFIED => bail!("unspecified output format"), + WireFormat::PROTOBUF => response.set_protobuf_payload( + test_message + .write_to_bytes_dyn() + .context("serializing test message")?, + ), + WireFormat::JSON => { + response.set_json_payload( + protobuf_json_mapping::print_to_string(&*test_message) + .context("serializing test message as JSON")?, + ); + } + WireFormat::JSPB => response.set_skipped("JSPB output not supported".into()), + WireFormat::TEXT_FORMAT => { + response.set_text_payload(protobuf::text_format::print_to_string(&*test_message)) + } + } + + Ok(response) +}