Skip to content

Commit be1cb07

Browse files
committed
Implement conformance test binary
1 parent f725928 commit be1cb07

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-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: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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_ne_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_ne_bytes())
49+
.context("writing response length")?;
50+
stdout.write_all(&response).context("writing response")?;
51+
stdout.flush()?;
52+
53+
Ok(false)
54+
}
55+
56+
fn run_test(request: &ConformanceRequest) -> Result<ConformanceResponse> {
57+
let files = [
58+
protos::test_messages_proto3::file_descriptor(),
59+
protos::test_messages_proto2::file_descriptor(),
60+
protos::conformance::file_descriptor(),
61+
];
62+
let descriptor = files
63+
.into_iter()
64+
.flat_map(|file| file.message_by_full_name(&format!(".{}", request.message_type)))
65+
.next()
66+
.ok_or_else(|| anyhow!("couldn't find message type {}", request.message_type))?;
67+
68+
let Some(ref payload) = request.payload else { bail!("missing or unsupported payload"); };
69+
70+
let mut test_message = descriptor.new_instance();
71+
let mut response = ConformanceResponse::new();
72+
match *payload {
73+
Payload::ProtobufPayload(ref payload) => {
74+
if let Err(e) = test_message.merge_from_bytes_dyn(payload) {
75+
response.set_parse_error(e.to_string());
76+
}
77+
}
78+
Payload::JsonPayload(ref payload) => {
79+
if let Err(e) = protobuf_json_mapping::merge_from_str(&mut *test_message, payload) {
80+
response.set_parse_error(e.to_string());
81+
}
82+
}
83+
Payload::JspbPayload(_) => {
84+
response.set_skipped("JSPB input not supported".into());
85+
}
86+
Payload::TextPayload(ref payload) => {
87+
if let Err(e) = protobuf::text_format::merge_from_str(&mut *test_message, payload) {
88+
response.set_parse_error(e.to_string());
89+
}
90+
}
91+
}
92+
93+
let requested_output_format = request
94+
.requested_output_format
95+
.enum_value()
96+
.map_err(|i| anyhow!("unknown wire format {}", i))?;
97+
match requested_output_format {
98+
WireFormat::UNSPECIFIED => bail!("unspecified output format"),
99+
WireFormat::PROTOBUF => response.set_protobuf_payload(
100+
test_message
101+
.write_to_bytes_dyn()
102+
.context("serializing test message")?,
103+
),
104+
WireFormat::JSON => {
105+
response.set_json_payload(
106+
protobuf_json_mapping::print_to_string(&*test_message)
107+
.context("serializing test message as JSON")?,
108+
);
109+
}
110+
WireFormat::JSPB => response.set_skipped("JSPB output not supported".into()),
111+
WireFormat::TEXT_FORMAT => {
112+
response.set_text_payload(protobuf::text_format::print_to_string(&*test_message))
113+
}
114+
}
115+
116+
Ok(response)
117+
}

0 commit comments

Comments
 (0)