Skip to content

Commit 0ecef9d

Browse files
committed
Compile files for conformance test
1 parent c2ca015 commit 0ecef9d

File tree

5 files changed

+594
-2
lines changed

5 files changed

+594
-2
lines changed

.github/workflows/ci.yml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,5 +175,28 @@ jobs:
175175
- name: Install conformance runner
176176
run: npm install -g protobuf-conformance
177177

178-
- name: Verify runner
179-
run: conformance_test_runner --help
178+
# - name: Verify runner
179+
# run: conformance_test_runner --help
180+
181+
- name: Install uv with Python 3.10
182+
uses: astral-sh/setup-uv@v5
183+
with:
184+
version: "0.7.5"
185+
python-version: 3.10
186+
187+
- name: Install betterproto2
188+
working-directory: ./betterproto2
189+
run: uv sync --locked --all-extras --all-groups
190+
191+
- name: Install betterproto2_compiler
192+
working-directory: ./betterproto2_compiler
193+
run: uv sync --locked --all-extras --all-groups
194+
195+
- name: Compile test files
196+
working-directory: ./betterproto2_compiler
197+
shell: bash
198+
run: uv run poe generate
199+
200+
- name: Move compiled files to betterproto2
201+
shell: bash
202+
run: cp -r betterproto2_compiler/tests/outputs betterproto2/tests
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import betterproto2
2+
3+
import struct
4+
import sys
5+
6+
from tests.outputs.conformance.conformance import (
7+
ConformanceResponse,
8+
ConformanceRequest,
9+
TestCategory,
10+
WireFormat,
11+
)
12+
from tests.outputs.conformance.protobuf_test_messages.proto3 import TestAllTypesProto3
13+
14+
15+
test_count = 0
16+
verbose = False
17+
18+
19+
class ProtocolError(Exception):
20+
pass
21+
22+
23+
def do_test(request: ConformanceRequest) -> ConformanceResponse:
24+
response = ConformanceResponse()
25+
26+
is_json = betterproto2.which_one_of(request, "payload")[0] == "json_payload"
27+
28+
if request.message_type != "protobuf_test_messages.proto3.TestAllTypesProto3":
29+
return ConformanceResponse(skipped="non proto3 tests not supported")
30+
31+
try:
32+
if betterproto2.which_one_of(request, "payload")[0] == "protobuf_payload":
33+
try:
34+
test_message = TestAllTypesProto3.parse(request.protobuf_payload)
35+
except Exception as e:
36+
response.parse_error = str(e)
37+
return response
38+
39+
elif betterproto2.which_one_of(request, "payload")[0] == "json_payload":
40+
try:
41+
ignore_unknown_fields = request.test_category == TestCategory.JSON_IGNORE_UNKNOWN_PARSING_TEST
42+
test_message = TestAllTypesProto3.from_json(request.json_payload, ignore_unknown_fields=ignore_unknown_fields)
43+
except Exception as e:
44+
response.parse_error = str(e)
45+
return response
46+
47+
elif betterproto2.which_one_of(request, "payload")[0] == "text_payload":
48+
return ConformanceResponse(skipped="text input not supported")
49+
try:
50+
text_format.Parse(request.text_payload, test_message)
51+
except Exception as e:
52+
response.parse_error = str(e)
53+
return response
54+
55+
else:
56+
raise ProtocolError("Request didn't have payload.")
57+
58+
if request.requested_output_format == WireFormat.UNSPECIFIED:
59+
raise ProtocolError("Unspecified output format")
60+
61+
elif request.requested_output_format == WireFormat.PROTOBUF:
62+
response.protobuf_payload = bytes(test_message)
63+
64+
elif request.requested_output_format == WireFormat.JSON:
65+
try:
66+
response.json_payload = test_message.to_json()
67+
except Exception as e:
68+
response.serialize_error = str(e)
69+
return response
70+
71+
elif request.requested_output_format == WireFormat.TEXT_FORMAT:
72+
return ConformanceResponse(skipped="text output not supported")
73+
# response.text_payload = text_format.MessageToString(
74+
# test_message, print_unknown_fields=request.print_unknown_fields
75+
# )
76+
77+
except Exception as e:
78+
response.runtime_error = str(e)
79+
80+
return response
81+
82+
83+
def do_test_io():
84+
length_bytes = sys.stdin.buffer.read(4)
85+
if len(length_bytes) == 0:
86+
return False # EOF
87+
elif len(length_bytes) != 4:
88+
raise IOError("I/O error")
89+
90+
length = struct.unpack("<I", length_bytes)[0]
91+
serialized_request = sys.stdin.buffer.read(length)
92+
if len(serialized_request) != length:
93+
raise IOError("I/O error")
94+
95+
request = ConformanceRequest.parse(serialized_request)
96+
97+
response = do_test(request)
98+
99+
serialized_response = bytes(response)
100+
sys.stdout.buffer.write(struct.pack("<I", len(serialized_response)))
101+
sys.stdout.buffer.write(serialized_response)
102+
sys.stdout.buffer.flush()
103+
104+
# if verbose:
105+
# sys.stderr.write(
106+
# "conformance_python: request=%s, response=%s\n"
107+
# % (
108+
# request.ShortDebugString().c_str(),
109+
# response.ShortDebugString().c_str(),
110+
# )
111+
# )
112+
113+
global test_count
114+
test_count += 1
115+
116+
return True
117+
118+
119+
while True:
120+
# with open("/Users/adrienvannson/Documents/python-betterproto2/betterproto2/debug.txt", "a") as f:
121+
# f.write("Hello!")
122+
if not do_test_io():
123+
sys.stderr.write(
124+
"conformance_python: received EOF from test runner "
125+
+ "after %s tests, exiting\n" % (test_count,)
126+
)
127+
sys.exit(0)

betterproto2_compiler/tests/generate.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ async def main_async():
7575
generate_test("casing", semaphore, reference=True),
7676
generate_test("casing", semaphore),
7777
generate_test("compiler_lib", semaphore),
78+
generate_test("conformance", semaphore),
7879
generate_test("deprecated", semaphore, reference=True),
7980
generate_test("deprecated", semaphore, client_generation="async"),
8081
generate_test("documentation", semaphore, client_generation="async"),
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Protocol Buffers - Google's data interchange format
2+
// Copyright 2008 Google Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file or at
6+
// https://developers.google.com/open-source/licenses/bsd
7+
8+
syntax = "proto3";
9+
10+
package conformance;
11+
12+
option java_package = "com.google.protobuf.conformance";
13+
option objc_class_prefix = "Conformance";
14+
15+
// This defines the conformance testing protocol. This protocol exists between
16+
// the conformance test suite itself and the code being tested. For each test,
17+
// the suite will send a ConformanceRequest message and expect a
18+
// ConformanceResponse message.
19+
//
20+
// You can either run the tests in two different ways:
21+
//
22+
// 1. in-process (using the interface in conformance_test.h).
23+
//
24+
// 2. as a sub-process communicating over a pipe. Information about how to
25+
// do this is in conformance_test_runner.cc.
26+
//
27+
// Pros/cons of the two approaches:
28+
//
29+
// - running as a sub-process is much simpler for languages other than C/C++.
30+
//
31+
// - running as a sub-process may be more tricky in unusual environments like
32+
// iOS apps, where fork/stdin/stdout are not available.
33+
34+
enum WireFormat {
35+
UNSPECIFIED = 0;
36+
PROTOBUF = 1;
37+
JSON = 2;
38+
JSPB = 3; // Only used inside Google. Opensource testees just skip it.
39+
TEXT_FORMAT = 4;
40+
}
41+
42+
enum TestCategory {
43+
UNSPECIFIED_TEST = 0;
44+
BINARY_TEST = 1; // Test binary wire format.
45+
JSON_TEST = 2; // Test json wire format.
46+
// Similar to JSON_TEST. However, during parsing json, testee should ignore
47+
// unknown fields. This feature is optional. Each implementation can decide
48+
// whether to support it. See
49+
// https://developers.google.com/protocol-buffers/docs/proto3#json_options
50+
// for more detail.
51+
JSON_IGNORE_UNKNOWN_PARSING_TEST = 3;
52+
// Test jspb wire format. Only used inside Google. Opensource testees just
53+
// skip it.
54+
JSPB_TEST = 4;
55+
// Test text format. For cpp, java and python, testees can already deal with
56+
// this type. Testees of other languages can simply skip it.
57+
TEXT_FORMAT_TEST = 5;
58+
}
59+
60+
// Meant to encapsulate all types of tests: successes, skips, failures, etc.
61+
// Therefore, this may or may not have a failure message. Failure messages
62+
// may be truncated for our failure lists.
63+
message TestStatus {
64+
string name = 1;
65+
string failure_message = 2;
66+
// What an actual test name matched to in a failure list. Can be wildcarded or
67+
// an exact match without wildcards.
68+
string matched_name = 3;
69+
}
70+
71+
// The conformance runner will request a list of failures as the first request.
72+
// This will be known by message_type == "conformance.FailureSet", a conformance
73+
// test should return a serialized FailureSet in protobuf_payload.
74+
message FailureSet {
75+
repeated TestStatus test = 2;
76+
reserved 1;
77+
}
78+
79+
// Represents a single test case's input. The testee should:
80+
//
81+
// 1. parse this proto (which should always succeed)
82+
// 2. parse the protobuf or JSON payload in "payload" (which may fail)
83+
// 3. if the parse succeeded, serialize the message in the requested format.
84+
message ConformanceRequest {
85+
// The payload (whether protobuf of JSON) is always for a
86+
// protobuf_test_messages.proto3.TestAllTypes proto (as defined in
87+
// src/google/protobuf/proto3_test_messages.proto).
88+
oneof payload {
89+
bytes protobuf_payload = 1;
90+
string json_payload = 2;
91+
// Only used inside Google. Opensource testees just skip it.
92+
string jspb_payload = 7;
93+
string text_payload = 8;
94+
}
95+
96+
// Which format should the testee serialize its message to?
97+
WireFormat requested_output_format = 3;
98+
99+
// The full name for the test message to use; for the moment, either:
100+
// protobuf_test_messages.proto3.TestAllTypesProto3 or
101+
// protobuf_test_messages.proto2.TestAllTypesProto2 or
102+
// protobuf_test_messages.editions.proto2.TestAllTypesProto2 or
103+
// protobuf_test_messages.editions.proto3.TestAllTypesProto3 or
104+
// protobuf_test_messages.editions.TestAllTypesEdition2023.
105+
string message_type = 4;
106+
107+
// Each test is given a specific test category. Some category may need
108+
// specific support in testee programs. Refer to the definition of
109+
// TestCategory for more information.
110+
TestCategory test_category = 5;
111+
112+
// Specify details for how to encode jspb.
113+
JspbEncodingConfig jspb_encoding_options = 6;
114+
115+
// This can be used in json and text format. If true, testee should print
116+
// unknown fields instead of ignore. This feature is optional.
117+
bool print_unknown_fields = 9;
118+
}
119+
120+
// Represents a single test case's output.
121+
message ConformanceResponse {
122+
oneof result {
123+
// This string should be set to indicate parsing failed. The string can
124+
// provide more information about the parse error if it is available.
125+
//
126+
// Setting this string does not necessarily mean the testee failed the
127+
// test. Some of the test cases are intentionally invalid input.
128+
string parse_error = 1;
129+
130+
// If the input was successfully parsed but errors occurred when
131+
// serializing it to the requested output format, set the error message in
132+
// this field.
133+
string serialize_error = 6;
134+
135+
// This should be set if the test program timed out. The string should
136+
// provide more information about what the child process was doing when it
137+
// was killed.
138+
string timeout_error = 9;
139+
140+
// This should be set if some other error occurred. This will always
141+
// indicate that the test failed. The string can provide more information
142+
// about the failure.
143+
string runtime_error = 2;
144+
145+
// If the input was successfully parsed and the requested output was
146+
// protobuf, serialize it to protobuf and set it in this field.
147+
bytes protobuf_payload = 3;
148+
149+
// If the input was successfully parsed and the requested output was JSON,
150+
// serialize to JSON and set it in this field.
151+
string json_payload = 4;
152+
153+
// For when the testee skipped the test, likely because a certain feature
154+
// wasn't supported, like JSON input/output.
155+
string skipped = 5;
156+
157+
// If the input was successfully parsed and the requested output was JSPB,
158+
// serialize to JSPB and set it in this field. JSPB is only used inside
159+
// Google. Opensource testees can just skip it.
160+
string jspb_payload = 7;
161+
162+
// If the input was successfully parsed and the requested output was
163+
// TEXT_FORMAT, serialize to TEXT_FORMAT and set it in this field.
164+
string text_payload = 8;
165+
}
166+
}
167+
168+
// Encoding options for jspb format.
169+
message JspbEncodingConfig {
170+
// Encode the value field of Any as jspb array if true, otherwise binary.
171+
bool use_jspb_array_any_format = 1;
172+
}

0 commit comments

Comments
 (0)