Support nested messages, fix casing. Support test-cases in packages.
This commit is contained in:
@@ -10,6 +10,7 @@ message Test {
|
||||
int32 camelCase = 1;
|
||||
my_enum snake_case = 2;
|
||||
snake_case_message snake_case_message = 3;
|
||||
int32 UPPERCASE = 4;
|
||||
}
|
||||
|
||||
message snake_case_message {
|
||||
|
||||
@@ -8,6 +8,7 @@ def test_message_attributes():
|
||||
message, "snake_case_message"
|
||||
), "snake_case field name is same in python"
|
||||
assert hasattr(message, "camel_case"), "CamelCase field is snake_case in python"
|
||||
assert hasattr(message, "uppercase"), "UPPERCASE field is lowercase in python"
|
||||
|
||||
|
||||
def test_message_casing():
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
# 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
|
||||
"import_circular_dependency",
|
||||
"oneof_enum", # 63
|
||||
"casing_message_field_uppercase", # 11
|
||||
"namespace_keywords", # 70
|
||||
@@ -15,6 +10,16 @@ tests = {
|
||||
"googletypes_value", # 9
|
||||
}
|
||||
|
||||
|
||||
# Defines where the main package for this test resides.
|
||||
# Needed to test relative package imports.
|
||||
packages = {
|
||||
"import_root_package_from_child": ".child",
|
||||
"import_parent_package_from_child": ".parent.child",
|
||||
"repeatedmessage": ".repeatedmessage",
|
||||
"service": ".service",
|
||||
}
|
||||
|
||||
services = {
|
||||
"googletypes_response",
|
||||
"googletypes_response_embedded",
|
||||
|
||||
@@ -3,9 +3,9 @@ syntax = "proto3";
|
||||
import "root.proto";
|
||||
import "other.proto";
|
||||
|
||||
// This test-case verifies that future implementations will support circular dependencies in the generated python files.
|
||||
// This test-case verifies support for circular dependencies in the generated python files.
|
||||
//
|
||||
// This becomes important when generating 1 python file/module per package, rather than 1 file per proto file.
|
||||
// This is important because we generate 1 python file/module per package, rather than 1 file per proto file.
|
||||
//
|
||||
// Scenario:
|
||||
//
|
||||
@@ -24,5 +24,5 @@ import "other.proto";
|
||||
// (root: Test & RootPackageMessage) <-------> (other: OtherPackageMessage)
|
||||
message Test {
|
||||
RootPackageMessage message = 1;
|
||||
other.OtherPackageMessage other =2;
|
||||
other.OtherPackageMessage other = 2;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package child;
|
||||
|
||||
import "root.proto";
|
||||
|
||||
// Verify that we can import root message from child package
|
||||
|
||||
message Test {
|
||||
RootMessage message = 1;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "root.proto";
|
||||
|
||||
package child;
|
||||
|
||||
// Tests generated imports when a message inside a child-package refers to a message defined in the root.
|
||||
|
||||
message Test {
|
||||
RootMessage message = 1;
|
||||
}
|
||||
@@ -10,7 +10,7 @@ message Test {
|
||||
|
||||
Nested nested = 1;
|
||||
Sibling sibling = 2;
|
||||
Sibling sibling2 = 3;
|
||||
// Sibling sibling2 = 3;
|
||||
}
|
||||
|
||||
message Sibling {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"root": {
|
||||
"top": {
|
||||
"name": "double-nested",
|
||||
"parent": {
|
||||
"child": [{"foo": "hello"}],
|
||||
"enumChild": ["A"],
|
||||
"rootParentChild": [{"a": "hello"}],
|
||||
"middle": {
|
||||
"bottom": [{"foo": "hello"}],
|
||||
"enumBottom": ["A"],
|
||||
"topMiddleBottom": [{"a": "hello"}],
|
||||
"bar": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message Test {
|
||||
message Root {
|
||||
message Parent {
|
||||
message RootParentChild {
|
||||
message Top {
|
||||
message Middle {
|
||||
message TopMiddleBottom {
|
||||
string a = 1;
|
||||
}
|
||||
enum EnumChild{
|
||||
enum EnumBottom{
|
||||
A = 0;
|
||||
B = 1;
|
||||
}
|
||||
message Child {
|
||||
message Bottom {
|
||||
string foo = 1;
|
||||
}
|
||||
reserved 1;
|
||||
repeated Child child = 2;
|
||||
repeated EnumChild enumChild=3;
|
||||
repeated RootParentChild rootParentChild=4;
|
||||
repeated Bottom bottom = 2;
|
||||
repeated EnumBottom enumBottom=3;
|
||||
repeated TopMiddleBottom topMiddleBottom=4;
|
||||
bool bar = 5;
|
||||
}
|
||||
string name = 1;
|
||||
Parent parent = 2;
|
||||
Middle middle = 2;
|
||||
}
|
||||
Root root = 1;
|
||||
Top top = 1;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ref;
|
||||
|
||||
import "repeatedmessage.proto";
|
||||
|
||||
message Test {
|
||||
|
||||
89
betterproto/tests/test_casing.py
Normal file
89
betterproto/tests/test_casing.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import pytest
|
||||
|
||||
from betterproto.casing import camel_case, pascal_case, snake_case
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["value", "expected"],
|
||||
[
|
||||
("", ""),
|
||||
("a", "A"),
|
||||
("foobar", "Foobar"),
|
||||
("FooBar", "FooBar"),
|
||||
("foo.bar", "FooBar"),
|
||||
("foo_bar", "FooBar"),
|
||||
("FOOBAR", "Foobar"),
|
||||
("FOOBar", "FooBar"),
|
||||
("UInt32", "UInt32"),
|
||||
("FOO_BAR", "FooBar"),
|
||||
("FOOBAR1", "Foobar1"),
|
||||
("FOOBAR_1", "Foobar1"),
|
||||
("FOO1BAR2", "Foo1Bar2"),
|
||||
("foo__bar", "FooBar"),
|
||||
("_foobar", "Foobar"),
|
||||
("foobaR", "FoobaR"),
|
||||
("foo~bar", "FooBar"),
|
||||
("foo:bar", "FooBar"),
|
||||
("1foobar", "1Foobar"),
|
||||
],
|
||||
)
|
||||
def test_pascal_case(value, expected):
|
||||
actual = pascal_case(value)
|
||||
assert actual == expected, f"{value} => {expected} (actual: {actual})"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["value", "expected"],
|
||||
[
|
||||
("", ""),
|
||||
("a", "a"),
|
||||
("foobar", "foobar"),
|
||||
("FooBar", "fooBar"),
|
||||
("foo.bar", "fooBar"),
|
||||
("foo_bar", "fooBar"),
|
||||
("FOOBAR", "foobar"),
|
||||
("FOO_BAR", "fooBar"),
|
||||
("FOOBAR1", "foobar1"),
|
||||
("FOOBAR_1", "foobar1"),
|
||||
("FOO1BAR2", "foo1Bar2"),
|
||||
("foo__bar", "fooBar"),
|
||||
("_foobar", "foobar"),
|
||||
("foobaR", "foobaR"),
|
||||
("foo~bar", "fooBar"),
|
||||
("foo:bar", "fooBar"),
|
||||
("1foobar", "1Foobar"),
|
||||
],
|
||||
)
|
||||
def test_camel_case(value, expected):
|
||||
actual = camel_case(value)
|
||||
assert actual == expected, f"{value} => {expected} (actual: {actual})"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["value", "expected"],
|
||||
[
|
||||
("", ""),
|
||||
("a", "a"),
|
||||
("foobar", "foobar"),
|
||||
("FooBar", "foo_bar"),
|
||||
("foo.bar", "foo_bar"),
|
||||
("foo_bar", "foo_bar"),
|
||||
("FOOBAR", "foobar"),
|
||||
("FOOBar", "foo_bar"),
|
||||
("UInt32", "u_int32"),
|
||||
("FOO_BAR", "foo_bar"),
|
||||
("FOOBAR1", "foobar1"),
|
||||
("FOOBAR_1", "foobar_1"),
|
||||
("FOOBAR_123", "foobar_123"),
|
||||
("FOO1BAR2", "foo1_bar2"),
|
||||
("foo__bar", "foo_bar"),
|
||||
("_foobar", "foobar"),
|
||||
("foobaR", "fooba_r"),
|
||||
("foo~bar", "foo_bar"),
|
||||
("foo:bar", "foo_bar"),
|
||||
("1foobar", "1_foobar"),
|
||||
],
|
||||
)
|
||||
def test_snake_case(value, expected):
|
||||
actual = snake_case(value)
|
||||
assert actual == expected, f"{value} => {expected} (actual: {actual})"
|
||||
@@ -1,9 +1,8 @@
|
||||
import pytest
|
||||
|
||||
from ..compile.importing import get_ref_type
|
||||
from ..compile.importing import get_ref_type, parse_source_type_name
|
||||
|
||||
|
||||
@pytest.mark.skip
|
||||
@pytest.mark.parametrize(
|
||||
["google_type", "expected_name", "expected_import"],
|
||||
[
|
||||
@@ -33,13 +32,14 @@ def test_import_google_wellknown_types_non_wrappers(
|
||||
google_type: str, expected_name: str, expected_import: str
|
||||
):
|
||||
imports = set()
|
||||
name = get_ref_type(package="", imports=imports, type_name=google_type)
|
||||
name = get_ref_type(package="", imports=imports, source_type=google_type)
|
||||
|
||||
assert name == expected_name
|
||||
assert imports.__contains__(expected_import)
|
||||
assert imports.__contains__(
|
||||
expected_import
|
||||
), f"{expected_import} not found in {imports}"
|
||||
|
||||
|
||||
@pytest.mark.skip
|
||||
@pytest.mark.parametrize(
|
||||
["google_type", "expected_name"],
|
||||
[
|
||||
@@ -56,13 +56,12 @@ def test_import_google_wellknown_types_non_wrappers(
|
||||
)
|
||||
def test_importing_google_wrappers_unwraps_them(google_type: str, expected_name: str):
|
||||
imports = set()
|
||||
name = get_ref_type(package="", imports=imports, type_name=google_type)
|
||||
name = get_ref_type(package="", imports=imports, source_type=google_type)
|
||||
|
||||
assert name == expected_name
|
||||
assert imports == set()
|
||||
|
||||
|
||||
@pytest.mark.skip
|
||||
@pytest.mark.parametrize(
|
||||
["google_type", "expected_name"],
|
||||
[
|
||||
@@ -80,7 +79,9 @@ def test_importing_google_wrappers_unwraps_them(google_type: str, expected_name:
|
||||
def test_importing_google_wrappers_without_unwrapping(
|
||||
google_type: str, expected_name: str
|
||||
):
|
||||
name = get_ref_type(package="", imports=set(), type_name=google_type, unwrap=False)
|
||||
name = get_ref_type(
|
||||
package="", imports=set(), source_type=google_type, unwrap=False
|
||||
)
|
||||
|
||||
assert name == expected_name
|
||||
|
||||
@@ -88,7 +89,7 @@ def test_importing_google_wrappers_without_unwrapping(
|
||||
def test_import_child_package_from_package():
|
||||
imports = set()
|
||||
name = get_ref_type(
|
||||
package="package", imports=imports, type_name="package.child.Message"
|
||||
package="package", imports=imports, source_type="package.child.Message"
|
||||
)
|
||||
|
||||
assert imports == {"from . import child"}
|
||||
@@ -97,7 +98,7 @@ def test_import_child_package_from_package():
|
||||
|
||||
def test_import_child_package_from_root():
|
||||
imports = set()
|
||||
name = get_ref_type(package="", imports=imports, type_name="child.Message")
|
||||
name = get_ref_type(package="", imports=imports, source_type="child.Message")
|
||||
|
||||
assert imports == {"from . import child"}
|
||||
assert name == "child.Message"
|
||||
@@ -106,7 +107,7 @@ def test_import_child_package_from_root():
|
||||
def test_import_camel_cased():
|
||||
imports = set()
|
||||
name = get_ref_type(
|
||||
package="", imports=imports, type_name="child_package.example_message"
|
||||
package="", imports=imports, source_type="child_package.example_message"
|
||||
)
|
||||
|
||||
assert imports == {"from . import child_package"}
|
||||
@@ -115,7 +116,7 @@ def test_import_camel_cased():
|
||||
|
||||
def test_import_nested_child_from_root():
|
||||
imports = set()
|
||||
name = get_ref_type(package="", imports=imports, type_name="nested.child.Message")
|
||||
name = get_ref_type(package="", imports=imports, source_type="nested.child.Message")
|
||||
|
||||
assert imports == {"from .nested import child as nested_child"}
|
||||
assert name == "nested_child.Message"
|
||||
@@ -124,7 +125,7 @@ def test_import_nested_child_from_root():
|
||||
def test_import_deeply_nested_child_from_root():
|
||||
imports = set()
|
||||
name = get_ref_type(
|
||||
package="", imports=imports, type_name="deeply.nested.child.Message"
|
||||
package="", imports=imports, source_type="deeply.nested.child.Message"
|
||||
)
|
||||
|
||||
assert imports == {"from .deeply.nested import child as deeply_nested_child"}
|
||||
@@ -136,7 +137,7 @@ def test_import_deeply_nested_child_from_package():
|
||||
name = get_ref_type(
|
||||
package="package",
|
||||
imports=imports,
|
||||
type_name="package.deeply.nested.child.Message",
|
||||
source_type="package.deeply.nested.child.Message",
|
||||
)
|
||||
|
||||
assert imports == {"from .deeply.nested import child as deeply_nested_child"}
|
||||
@@ -145,32 +146,32 @@ def test_import_deeply_nested_child_from_package():
|
||||
|
||||
def test_import_root_sibling():
|
||||
imports = set()
|
||||
name = get_ref_type(package="", imports=imports, type_name="Message")
|
||||
name = get_ref_type(package="", imports=imports, source_type="Message")
|
||||
|
||||
assert imports == {"from . import Message"}
|
||||
assert name == "Message"
|
||||
assert imports == set()
|
||||
assert name == '"Message"'
|
||||
|
||||
|
||||
def test_import_nested_siblings():
|
||||
imports = set()
|
||||
name = get_ref_type(package="foo", imports=imports, type_name="foo.Message")
|
||||
name = get_ref_type(package="foo", imports=imports, source_type="foo.Message")
|
||||
|
||||
assert imports == {"from . import Message"}
|
||||
assert name == "Message"
|
||||
assert name == '"Message"'
|
||||
|
||||
|
||||
def test_import_deeply_nested_siblings():
|
||||
imports = set()
|
||||
name = get_ref_type(package="foo.bar", imports=imports, type_name="foo.bar.Message")
|
||||
name = get_ref_type(
|
||||
package="foo.bar", imports=imports, source_type="foo.bar.Message"
|
||||
)
|
||||
|
||||
assert imports == {"from . import Message"}
|
||||
assert name == "Message"
|
||||
assert name == '"Message"'
|
||||
|
||||
|
||||
def test_import_parent_package_from_child():
|
||||
imports = set()
|
||||
name = get_ref_type(
|
||||
package="package.child", imports=imports, type_name="package.Message"
|
||||
package="package.child", imports=imports, source_type="package.Message"
|
||||
)
|
||||
|
||||
assert imports == {"from .. import Message"}
|
||||
@@ -182,7 +183,7 @@ def test_import_parent_package_from_deeply_nested_child():
|
||||
name = get_ref_type(
|
||||
package="package.deeply.nested.child",
|
||||
imports=imports,
|
||||
type_name="package.deeply.nested.Message",
|
||||
source_type="package.deeply.nested.Message",
|
||||
)
|
||||
|
||||
assert imports == {"from .. import Message"}
|
||||
@@ -191,7 +192,7 @@ def test_import_parent_package_from_deeply_nested_child():
|
||||
|
||||
def test_import_root_package_from_child():
|
||||
imports = set()
|
||||
name = get_ref_type(package="package.child", imports=imports, type_name="Message")
|
||||
name = get_ref_type(package="package.child", imports=imports, source_type="Message")
|
||||
|
||||
assert imports == {"from ... import Message"}
|
||||
assert name == "Message"
|
||||
@@ -200,7 +201,7 @@ def test_import_root_package_from_child():
|
||||
def test_import_root_package_from_deeply_nested_child():
|
||||
imports = set()
|
||||
name = get_ref_type(
|
||||
package="package.deeply.nested.child", imports=imports, type_name="Message"
|
||||
package="package.deeply.nested.child", imports=imports, source_type="Message"
|
||||
)
|
||||
|
||||
assert imports == {"from ..... import Message"}
|
||||
@@ -209,7 +210,7 @@ def test_import_root_package_from_deeply_nested_child():
|
||||
|
||||
def test_import_unrelated_package():
|
||||
imports = set()
|
||||
name = get_ref_type(package="a", imports=imports, type_name="p.Message")
|
||||
name = get_ref_type(package="a", imports=imports, source_type="p.Message")
|
||||
|
||||
assert imports == {"from .. import p as _p"}
|
||||
assert name == "_p.Message"
|
||||
@@ -217,7 +218,7 @@ def test_import_unrelated_package():
|
||||
|
||||
def test_import_unrelated_nested_package():
|
||||
imports = set()
|
||||
name = get_ref_type(package="a.b", imports=imports, type_name="p.q.Message")
|
||||
name = get_ref_type(package="a.b", imports=imports, source_type="p.q.Message")
|
||||
|
||||
assert imports == {"from ...p import q as __p_q"}
|
||||
assert name == "__p_q.Message"
|
||||
@@ -225,7 +226,9 @@ def test_import_unrelated_nested_package():
|
||||
|
||||
def test_import_unrelated_deeply_nested_package():
|
||||
imports = set()
|
||||
name = get_ref_type(package="a.b.c.d", imports=imports, type_name="p.q.r.s.Message")
|
||||
name = get_ref_type(
|
||||
package="a.b.c.d", imports=imports, source_type="p.q.r.s.Message"
|
||||
)
|
||||
|
||||
assert imports == {"from .....p.q.r import s as ____p_q_r_s"}
|
||||
assert name == "____p_q_r_s.Message"
|
||||
@@ -233,7 +236,7 @@ def test_import_unrelated_deeply_nested_package():
|
||||
|
||||
def test_import_cousin_package():
|
||||
imports = set()
|
||||
name = get_ref_type(package="a.x", imports=imports, type_name="a.y.Message")
|
||||
name = get_ref_type(package="a.x", imports=imports, source_type="a.y.Message")
|
||||
|
||||
assert imports == {"from .. import y as _y"}
|
||||
assert name == "_y.Message"
|
||||
@@ -241,7 +244,7 @@ def test_import_cousin_package():
|
||||
|
||||
def test_import_far_cousin_package():
|
||||
imports = set()
|
||||
name = get_ref_type(package="a.x.y", imports=imports, type_name="a.b.c.Message")
|
||||
name = get_ref_type(package="a.x.y", imports=imports, source_type="a.b.c.Message")
|
||||
|
||||
assert imports == {"from ...b import c as __b_c"}
|
||||
assert name == "__b_c.Message"
|
||||
@@ -249,7 +252,22 @@ def test_import_far_cousin_package():
|
||||
|
||||
def test_import_far_far_cousin_package():
|
||||
imports = set()
|
||||
name = get_ref_type(package="a.x.y.z", imports=imports, type_name="a.b.c.d.Message")
|
||||
name = get_ref_type(
|
||||
package="a.x.y.z", imports=imports, source_type="a.b.c.d.Message"
|
||||
)
|
||||
|
||||
assert imports == {"from ....b.c import d as ___b_c_d"}
|
||||
assert name == "___b_c_d.Message"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["full_name", "expected_output"],
|
||||
[
|
||||
("package.SomeMessage.NestedType", ("package", "SomeMessage.NestedType")),
|
||||
(".package.SomeMessage.NestedType", ("package", "SomeMessage.NestedType")),
|
||||
(".service.ExampleRequest", ("service", "ExampleRequest")),
|
||||
(".package.lower_case_message", ("package", "lower_case_message")),
|
||||
],
|
||||
)
|
||||
def test_parse_field_type_name(full_name, expected_output):
|
||||
assert parse_source_type_name(full_name) == expected_output
|
||||
|
||||
@@ -57,7 +57,9 @@ plugin_output_package = "betterproto.tests.output_betterproto"
|
||||
reference_output_package = "betterproto.tests.output_reference"
|
||||
|
||||
|
||||
TestData = namedtuple("TestData", "plugin_module, reference_module, json_data")
|
||||
TestData = namedtuple(
|
||||
"TestData", ["plugin_module", "reference_module", "json_data", "entry_point"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -75,15 +77,18 @@ def test_data(request):
|
||||
|
||||
sys.path.append(reference_module_root)
|
||||
|
||||
test_package = test_case_name + test_input_config.packages.get(test_case_name, "")
|
||||
|
||||
yield (
|
||||
TestData(
|
||||
plugin_module=importlib.import_module(
|
||||
f"{plugin_output_package}.{test_case_name}.{test_case_name}"
|
||||
f"{plugin_output_package}.{test_package}"
|
||||
),
|
||||
reference_module=lambda: importlib.import_module(
|
||||
f"{reference_output_package}.{test_case_name}.{test_case_name}_pb2"
|
||||
),
|
||||
json_data=get_test_case_json_data(test_case_name),
|
||||
entry_point=test_package,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -106,7 +111,7 @@ def test_message_equality(test_data: TestData) -> None:
|
||||
|
||||
@pytest.mark.parametrize("test_data", test_cases.messages_with_json, indirect=True)
|
||||
def test_message_json(repeat, test_data: TestData) -> None:
|
||||
plugin_module, _, json_data = test_data
|
||||
plugin_module, _, json_data, entry_point = test_data
|
||||
|
||||
for _ in range(repeat):
|
||||
message: betterproto.Message = plugin_module.Test()
|
||||
@@ -119,13 +124,13 @@ def test_message_json(repeat, test_data: TestData) -> None:
|
||||
|
||||
@pytest.mark.parametrize("test_data", test_cases.services, indirect=True)
|
||||
def test_service_can_be_instantiated(test_data: TestData) -> None:
|
||||
plugin_module, _, json_data = test_data
|
||||
plugin_module, _, json_data, entry_point = test_data
|
||||
plugin_module.TestStub(MockChannel())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("test_data", test_cases.messages_with_json, indirect=True)
|
||||
def test_binary_compatibility(repeat, test_data: TestData) -> None:
|
||||
plugin_module, reference_module, json_data = test_data
|
||||
plugin_module, reference_module, json_data, entry_point = test_data
|
||||
|
||||
reference_instance = Parse(json_data, reference_module().Test())
|
||||
reference_binary_output = reference_instance.SerializeToString()
|
||||
|
||||
Reference in New Issue
Block a user