commit
5c4969ff1c
@ -87,4 +87,4 @@ betterproto/tests/test_inputs.py ..x...x..x...x.X........xx........x.....x......
|
|||||||
- `x` — XFAIL: expected failure
|
- `x` — XFAIL: expected failure
|
||||||
- `X` — XPASS: expected failure, but still passed
|
- `X` — XPASS: expected failure, but still passed
|
||||||
|
|
||||||
Test cases marked for expected failure are declared in [inputs/xfail.py](inputs.xfail.py)
|
Test cases marked for expected failure are declared in [inputs/config.py](inputs/config.py)
|
@ -2,6 +2,7 @@
|
|||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
@ -33,6 +34,8 @@ def generate(whitelist: Set[str]):
|
|||||||
|
|
||||||
test_case_names = set(get_directories(inputs_path))
|
test_case_names = set(get_directories(inputs_path))
|
||||||
|
|
||||||
|
failed_test_cases = []
|
||||||
|
|
||||||
for test_case_name in sorted(test_case_names):
|
for test_case_name in sorted(test_case_names):
|
||||||
test_case_input_path = os.path.realpath(
|
test_case_input_path = os.path.realpath(
|
||||||
os.path.join(inputs_path, test_case_name)
|
os.path.join(inputs_path, test_case_name)
|
||||||
@ -45,22 +48,39 @@ def generate(whitelist: Set[str]):
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
test_case_output_path_reference = os.path.join(
|
|
||||||
output_path_reference, test_case_name
|
|
||||||
)
|
|
||||||
test_case_output_path_betterproto = os.path.join(
|
|
||||||
output_path_betterproto, test_case_name
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"Generating output for {test_case_name}")
|
print(f"Generating output for {test_case_name}")
|
||||||
os.makedirs(test_case_output_path_reference, exist_ok=True)
|
try:
|
||||||
os.makedirs(test_case_output_path_betterproto, exist_ok=True)
|
generate_test_case_output(test_case_name, test_case_input_path)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
failed_test_cases.append(test_case_name)
|
||||||
|
|
||||||
clear_directory(test_case_output_path_reference)
|
if failed_test_cases:
|
||||||
clear_directory(test_case_output_path_betterproto)
|
sys.stderr.write("\nFailed to generate the following test cases:\n")
|
||||||
|
for failed_test_case in failed_test_cases:
|
||||||
|
sys.stderr.write(f"- {failed_test_case}\n")
|
||||||
|
|
||||||
protoc_reference(test_case_input_path, test_case_output_path_reference)
|
|
||||||
protoc_plugin(test_case_input_path, test_case_output_path_betterproto)
|
def generate_test_case_output(test_case_name, test_case_input_path=None):
|
||||||
|
if not test_case_input_path:
|
||||||
|
test_case_input_path = os.path.realpath(
|
||||||
|
os.path.join(inputs_path, test_case_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
test_case_output_path_reference = os.path.join(
|
||||||
|
output_path_reference, test_case_name
|
||||||
|
)
|
||||||
|
test_case_output_path_betterproto = os.path.join(
|
||||||
|
output_path_betterproto, test_case_name
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(test_case_output_path_reference, exist_ok=True)
|
||||||
|
os.makedirs(test_case_output_path_betterproto, exist_ok=True)
|
||||||
|
|
||||||
|
clear_directory(test_case_output_path_reference)
|
||||||
|
clear_directory(test_case_output_path_betterproto)
|
||||||
|
|
||||||
|
protoc_reference(test_case_input_path, test_case_output_path_reference)
|
||||||
|
protoc_plugin(test_case_input_path, test_case_output_path_betterproto)
|
||||||
|
|
||||||
|
|
||||||
HELP = "\n".join(
|
HELP = "\n".join(
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"UPPERCASE": 10,
|
||||||
|
"UPPERCASE_V2": 10,
|
||||||
|
"UPPER_CAMEL_CASE": 10
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
message Test {
|
||||||
|
int32 UPPERCASE = 1;
|
||||||
|
int32 UPPERCASE_V2 = 2;
|
||||||
|
int32 UPPER_CAMEL_CASE = 3;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
from betterproto.tests.output_betterproto.casing_message_field_uppercase.casing_message_field_uppercase import (
|
||||||
|
Test,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_message_casing():
|
||||||
|
message = Test()
|
||||||
|
assert hasattr(
|
||||||
|
message, "uppercase"
|
||||||
|
), "UPPERCASE attribute is converted to 'uppercase' in python"
|
||||||
|
assert hasattr(
|
||||||
|
message, "uppercase_v2"
|
||||||
|
), "UPPERCASE_V2 attribute is converted to 'uppercase_v2' in python"
|
||||||
|
assert hasattr(
|
||||||
|
message, "upper_camel_case"
|
||||||
|
), "UPPER_CAMEL_CASE attribute is converted to upper_camel_case in python"
|
23
betterproto/tests/inputs/config.py
Normal file
23
betterproto/tests/inputs/config.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Test cases that are expected to fail, e.g. unimplemented features or bug-fixes.
|
||||||
|
# Remove from list when fixed.
|
||||||
|
tests = {
|
||||||
|
"import_root_sibling", # 61
|
||||||
|
"import_child_package_from_package", # 58
|
||||||
|
"import_root_package_from_child", # 60
|
||||||
|
"import_parent_package_from_child", # 59
|
||||||
|
"import_circular_dependency", # failing because of other bugs now
|
||||||
|
"import_packages_same_name", # 25
|
||||||
|
"oneof_enum", # 63
|
||||||
|
"googletypes_service_returns_empty", # 9
|
||||||
|
"casing_message_field_uppercase", # 11
|
||||||
|
"namespace_keywords", # 70
|
||||||
|
"namespace_builtin_types" # 53
|
||||||
|
}
|
||||||
|
|
||||||
|
services = {
|
||||||
|
"googletypes_response",
|
||||||
|
"googletypes_response_embedded",
|
||||||
|
"service",
|
||||||
|
"import_service_input_message",
|
||||||
|
"googletypes_service_returns_empty",
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
service Test {
|
||||||
|
rpc Send (RequestMessage) returns (google.protobuf.Empty) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequestMessage {
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "users_v1.proto";
|
||||||
|
import "posts_v1.proto";
|
||||||
|
|
||||||
|
// Tests generated message can correctly reference two packages with the same leaf-name
|
||||||
|
|
||||||
|
message Test {
|
||||||
|
users.v1.User user = 1;
|
||||||
|
posts.v1.Post post = 2;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package posts.v1;
|
||||||
|
|
||||||
|
message Post {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package users.v1;
|
||||||
|
|
||||||
|
message User {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "request_message.proto";
|
||||||
|
|
||||||
|
// Tests generated service correctly imports the RequestMessage
|
||||||
|
|
||||||
|
service Test {
|
||||||
|
rpc DoThing (RequestMessage) returns (RequestResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message RequestResponse {
|
||||||
|
int32 value = 1;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
message RequestMessage {
|
||||||
|
int32 argument = 1;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from betterproto.tests.mocks import MockChannel
|
||||||
|
from betterproto.tests.output_betterproto.import_service_input_message.import_service_input_message import (
|
||||||
|
RequestResponse,
|
||||||
|
TestStub,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reason="#68 Request Input Messages are not imported for service")
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_service_correctly_imports_reference_message():
|
||||||
|
mock_response = RequestResponse(value=10)
|
||||||
|
service = TestStub(MockChannel([mock_response]))
|
||||||
|
response = await service.do_thing()
|
||||||
|
assert mock_response == response
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"for": 1,
|
|
||||||
"with": 2,
|
|
||||||
"as": 3
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
message Test {
|
|
||||||
int32 for = 1;
|
|
||||||
int32 with = 2;
|
|
||||||
int32 as = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
service TestService {
|
|
||||||
rpc GetTest(Test) returns (Test) {}
|
|
||||||
}
|
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"int": "value-for-int",
|
||||||
|
"float": "value-for-float",
|
||||||
|
"complex": "value-for-complex",
|
||||||
|
"list": "value-for-list",
|
||||||
|
"tuple": "value-for-tuple",
|
||||||
|
"range": "value-for-range",
|
||||||
|
"str": "value-for-str",
|
||||||
|
"bytearray": "value-for-bytearray",
|
||||||
|
"bytes": "value-for-bytes",
|
||||||
|
"memoryview": "value-for-memoryview",
|
||||||
|
"set": "value-for-set",
|
||||||
|
"frozenset": "value-for-frozenset",
|
||||||
|
"map": "value-for-map",
|
||||||
|
"bool": "value-for-bool"
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
// Tests that messages may contain fields with names that are python types
|
||||||
|
|
||||||
|
message Test {
|
||||||
|
// https://docs.python.org/2/library/stdtypes.html#numeric-types-int-float-long-complex
|
||||||
|
string int = 1;
|
||||||
|
string float = 2;
|
||||||
|
string complex = 3;
|
||||||
|
|
||||||
|
// https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
|
||||||
|
string list = 4;
|
||||||
|
string tuple = 5;
|
||||||
|
string range = 6;
|
||||||
|
|
||||||
|
// https://docs.python.org/3/library/stdtypes.html#str
|
||||||
|
string str = 7;
|
||||||
|
|
||||||
|
// https://docs.python.org/3/library/stdtypes.html#bytearray-objects
|
||||||
|
string bytearray = 8;
|
||||||
|
|
||||||
|
// https://docs.python.org/3/library/stdtypes.html#bytes-and-bytearray-operations
|
||||||
|
string bytes = 9;
|
||||||
|
|
||||||
|
// https://docs.python.org/3/library/stdtypes.html#memory-views
|
||||||
|
string memoryview = 10;
|
||||||
|
|
||||||
|
// https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset
|
||||||
|
string set = 11;
|
||||||
|
string frozenset = 12;
|
||||||
|
|
||||||
|
// https://docs.python.org/3/library/stdtypes.html#dict
|
||||||
|
string map = 13;
|
||||||
|
string dict = 14;
|
||||||
|
|
||||||
|
// https://docs.python.org/3/library/stdtypes.html#boolean-values
|
||||||
|
string bool = 15;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"False": 1,
|
||||||
|
"None": 2,
|
||||||
|
"True": 3,
|
||||||
|
"and": 4,
|
||||||
|
"as": 5,
|
||||||
|
"assert": 6,
|
||||||
|
"async": 7,
|
||||||
|
"await": 8,
|
||||||
|
"break": 9,
|
||||||
|
"class": 10,
|
||||||
|
"continue": 11,
|
||||||
|
"def": 12,
|
||||||
|
"del": 13,
|
||||||
|
"elif": 14,
|
||||||
|
"else": 15,
|
||||||
|
"except": 16,
|
||||||
|
"finally": 17,
|
||||||
|
"for": 18,
|
||||||
|
"from": 19,
|
||||||
|
"global": 20,
|
||||||
|
"if": 21,
|
||||||
|
"import": 22,
|
||||||
|
"in": 23,
|
||||||
|
"is": 24,
|
||||||
|
"lambda": 25,
|
||||||
|
"nonlocal": 26,
|
||||||
|
"not": 27,
|
||||||
|
"or": 28,
|
||||||
|
"pass": 29,
|
||||||
|
"raise": 30,
|
||||||
|
"return": 31,
|
||||||
|
"try": 32,
|
||||||
|
"while": 33,
|
||||||
|
"with": 34,
|
||||||
|
"yield": 35
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
// Tests that messages may contain fields that are Python keywords
|
||||||
|
//
|
||||||
|
// Generated with Python 3.7.6
|
||||||
|
// print('\n'.join(f'string {k} = {i+1};' for i,k in enumerate(keyword.kwlist)))
|
||||||
|
|
||||||
|
message Test {
|
||||||
|
string False = 1;
|
||||||
|
string None = 2;
|
||||||
|
string True = 3;
|
||||||
|
string and = 4;
|
||||||
|
string as = 5;
|
||||||
|
string assert = 6;
|
||||||
|
string async = 7;
|
||||||
|
string await = 8;
|
||||||
|
string break = 9;
|
||||||
|
string class = 10;
|
||||||
|
string continue = 11;
|
||||||
|
string def = 12;
|
||||||
|
string del = 13;
|
||||||
|
string elif = 14;
|
||||||
|
string else = 15;
|
||||||
|
string except = 16;
|
||||||
|
string finally = 17;
|
||||||
|
string for = 18;
|
||||||
|
string from = 19;
|
||||||
|
string global = 20;
|
||||||
|
string if = 21;
|
||||||
|
string import = 22;
|
||||||
|
string in = 23;
|
||||||
|
string is = 24;
|
||||||
|
string lambda = 25;
|
||||||
|
string nonlocal = 26;
|
||||||
|
string not = 27;
|
||||||
|
string or = 28;
|
||||||
|
string pass = 29;
|
||||||
|
string raise = 30;
|
||||||
|
string return = 31;
|
||||||
|
string try = 32;
|
||||||
|
string while = 33;
|
||||||
|
string with = 34;
|
||||||
|
string yield = 35;
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
# Test cases that are expected to fail, e.g. unimplemented features or bug-fixes.
|
|
||||||
# Remove from list when fixed.
|
|
||||||
tests = {
|
|
||||||
"import_root_sibling",
|
|
||||||
"import_child_package_from_package",
|
|
||||||
"import_root_package_from_child",
|
|
||||||
"import_parent_package_from_child",
|
|
||||||
"import_circular_dependency",
|
|
||||||
"oneof_enum",
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ from typing import Set
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import betterproto
|
import betterproto
|
||||||
from betterproto.tests.inputs import xfail
|
from betterproto.tests.inputs import config as test_input_config
|
||||||
from betterproto.tests.mocks import MockChannel
|
from betterproto.tests.mocks import MockChannel
|
||||||
from betterproto.tests.util import get_directories, get_test_case_json_data, inputs_path
|
from betterproto.tests.util import get_directories, get_test_case_json_data, inputs_path
|
||||||
|
|
||||||
@ -45,9 +45,8 @@ class TestCases:
|
|||||||
|
|
||||||
test_cases = TestCases(
|
test_cases = TestCases(
|
||||||
path=inputs_path,
|
path=inputs_path,
|
||||||
# test cases for services
|
services=test_input_config.services,
|
||||||
services={"googletypes_response", "googletypes_response_embedded", "service"},
|
xfail=test_input_config.tests,
|
||||||
xfail=xfail.tests,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
plugin_output_package = "betterproto.tests.output_betterproto"
|
plugin_output_package = "betterproto.tests.output_betterproto"
|
||||||
|
@ -36,10 +36,11 @@ def read_relative(file: str, path: str):
|
|||||||
return fh.read()
|
return fh.read()
|
||||||
|
|
||||||
|
|
||||||
def protoc_plugin(path: str, output_dir: str):
|
def protoc_plugin(path: str, output_dir: str) -> subprocess.CompletedProcess:
|
||||||
subprocess.run(
|
return subprocess.run(
|
||||||
f"protoc --plugin=protoc-gen-custom={plugin_path} --custom_out={output_dir} --proto_path={path} {path}/*.proto",
|
f"protoc --plugin=protoc-gen-custom={plugin_path} --custom_out={output_dir} --proto_path={path} {path}/*.proto",
|
||||||
shell=True,
|
shell=True,
|
||||||
|
check=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user