Skip to content

Commit 12a57ad

Browse files
committed
Implement conformance test binary
1 parent f725928 commit 12a57ad

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ members = [
1313
"protobuf-examples/issue-614",
1414
"protobuf-parse",
1515
"protobuf-support",
16+
"test-crates/conformance",
1617
"test-crates/perftest/bytes",
1718
"test-crates/perftest/misc",
1819
"test-crates/perftest/vs-cxx",

test-crates/conformance/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "conformance"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
anyhow = "1.0.68"
8+
protobuf = { path = "../../protobuf" }
9+
protobuf-json-mapping = { version = "4.0.0-alpha.0", path = "../../protobuf-json-mapping" }
10+
11+
[build-dependencies]
12+
protobuf-codegen = { path = "../../protobuf-codegen" }

test-crates/conformance/build.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
protobuf_codegen::Codegen::new()
3+
.includes(["../../google-protobuf-all-protos/protobuf/protobuf-git/conformance", "../../google-protobuf-all-protos/protobuf/protobuf-git/src/google/protobuf"])
4+
.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"])
5+
.cargo_out_dir("protos")
6+
.run_from_script();
7+
}

test-crates/conformance/src/main.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use std::io::{self, Read, Write};
2+
3+
use anyhow::{anyhow, bail, Context, Error, Result};
4+
5+
mod protos {
6+
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
7+
}
8+
9+
use protobuf::Message;
10+
use protos::conformance::{
11+
conformance_request::Payload, ConformanceRequest, ConformanceResponse, WireFormat,
12+
};
13+
14+
fn main() {
15+
loop {
16+
match serve_conformance_request() {
17+
Ok(false) => {}
18+
Ok(true) => break,
19+
Err(e) => {
20+
eprintln!("{:#}", e);
21+
std::process::exit(1)
22+
}
23+
}
24+
}
25+
}
26+
27+
fn serve_conformance_request() -> Result<bool> {
28+
let mut stdin = io::stdin().lock();
29+
let mut len = [0; 4];
30+
match stdin.read_exact(&mut len) {
31+
Ok(()) => {}
32+
Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => {
33+
return Ok(true);
34+
}
35+
Err(e) => return Err(Error::new(e).context("reading request length")),
36+
}
37+
let len = u32::from_le_bytes(len);
38+
let mut buf = vec![0; len as usize];
39+
stdin.read_exact(&mut buf).context("reading request")?;
40+
41+
let request = ConformanceRequest::parse_from_bytes(&buf).context("parsing request")?;
42+
43+
let response = run_test(&request).context("running test")?;
44+
45+
let response = response.write_to_bytes().context("serializing response")?;
46+
let mut stdout = io::stdout().lock();
47+
stdout
48+
.write_all(&(response.len() as u32).to_le_bytes())
49+
.context("writing response length")?;
50+
stdout.write_all(&response).context("writing response")?;
51+
52+
Ok(false)
53+
}
54+
55+
fn run_test(request: &ConformanceRequest) -> Result<ConformanceResponse> {
56+
let files = [
57+
protos::test_messages_proto3::file_descriptor(),
58+
protos::test_messages_proto2::file_descriptor(),
59+
protos::conformance::file_descriptor(),
60+
];
61+
let descriptor = files
62+
.into_iter()
63+
.flat_map(|file| file.message_by_full_name(&format!(".{}", request.message_type)))
64+
.next()
65+
.ok_or_else(|| anyhow!("couldn't find message type {}", request.message_type))?;
66+
67+
let Some(ref payload) = request.payload else { bail!("missing or unsupported payload"); };
68+
69+
let mut test_message = descriptor.new_instance();
70+
let mut response = ConformanceResponse::new();
71+
match *payload {
72+
Payload::ProtobufPayload(ref payload) => {
73+
if let Err(e) = test_message.merge_from_bytes_dyn(payload) {
74+
response.set_parse_error(e.to_string());
75+
}
76+
}
77+
Payload::JsonPayload(ref payload) => {
78+
if let Err(e) = protobuf_json_mapping::merge_from_str(&mut *test_message, payload) {
79+
response.set_parse_error(e.to_string());
80+
}
81+
}
82+
Payload::JspbPayload(_) => {
83+
response.set_skipped("JSPB input not supported".into());
84+
}
85+
Payload::TextPayload(ref payload) => {
86+
if let Err(e) = protobuf::text_format::merge_from_str(&mut *test_message, payload) {
87+
response.set_parse_error(e.to_string());
88+
}
89+
}
90+
}
91+
92+
let requested_output_format = request
93+
.requested_output_format
94+
.enum_value()
95+
.map_err(|i| anyhow!("unknown wire format {}", i))?;
96+
match requested_output_format {
97+
WireFormat::UNSPECIFIED => bail!("unspecified output format"),
98+
WireFormat::PROTOBUF => response.set_protobuf_payload(
99+
test_message
100+
.write_to_bytes_dyn()
101+
.context("serializing test message")?,
102+
),
103+
WireFormat::JSON => {
104+
response.set_json_payload(
105+
protobuf_json_mapping::print_to_string(&*test_message)
106+
.context("serializing test message as JSON")?,
107+
);
108+
}
109+
WireFormat::JSPB => response.set_skipped("JSPB output not supported".into()),
110+
WireFormat::TEXT_FORMAT => {
111+
response.set_text_payload(protobuf::text_format::print_to_string(&*test_message))
112+
}
113+
}
114+
115+
Ok(response)
116+
}

0 commit comments

Comments
 (0)