Skip to content

Commit b912881

Browse files
Implement type hints tests (#25)
* Add initial tests * Add variable types tests
1 parent 8440b40 commit b912881

7 files changed

Lines changed: 499 additions & 4 deletions

File tree

.github/workflows/build.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ jobs:
7474
echo "::set-output name=pluginVerifierHomeDir::~/.pluginVerifier"
7575
./gradlew listProductsReleases # prepare list of IDEs for Plugin Verifier
7676
77+
# Run tests
78+
- name: Run Tests
79+
run: ./gradlew test
80+
81+
# Collect Tests Result of failed tests
82+
- name: Collect Tests Result
83+
if: ${{ failure() }}
84+
uses: actions/upload-artifact@v3
85+
with:
86+
name: tests-result
87+
path: ${{ github.workspace }}/build/reports/tests
88+
7789
# Cache Plugin Verifier IDEs
7890
- name: Setup Plugin Verifier IDEs Cache
7991
uses: actions/cache@v3
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package space.whitememory.pythoninlayparams
2+
3+
import com.intellij.testFramework.utils.inlays.InlayHintsProviderTestCase
4+
import com.jetbrains.python.psi.LanguageLevel
5+
import space.whitememory.pythoninlayparams.python.PyLightProjectDescriptor
6+
7+
8+
abstract class PythonAbstractInlayHintsTestCase : InlayHintsProviderTestCase() {
9+
override fun getProjectDescriptor() = PyLightProjectDescriptor(LanguageLevel.getLatest())
10+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package space.whitememory.pythoninlayparams
2+
3+
import space.whitememory.pythoninlayparams.types.functions.PythonFunctionInlayTypeHintsProvider
4+
5+
class PythonFunctionReturnTypesTest : PythonAbstractInlayHintsTestCase() {
6+
7+
fun testSimple() = doTest(
8+
"""
9+
class A:
10+
...
11+
12+
def get_int()<# [-> int] #>:
13+
return 1
14+
15+
def get_instance()<# [-> A] #>:
16+
return A()
17+
18+
def get_type()<# [-> [Type [ A ]]] #>:
19+
return A
20+
21+
def get_union()<# [-> [int | str]] #>:
22+
return 1 or ""
23+
24+
def get_optional()<# [-> [[list [ A ]] | None]] #>:
25+
if True:
26+
return [A()]
27+
return None
28+
""".trimIndent()
29+
)
30+
31+
fun testNoHint() = doTest(
32+
"""
33+
def get_implicit_none():
34+
return
35+
36+
def get_explicit_none():
37+
return None
38+
39+
def check_ellipsis():
40+
...
41+
42+
def check_pass():
43+
pass
44+
45+
async def async_get_implicit_none():
46+
return
47+
48+
async def async_get_explicit_none():
49+
return None
50+
51+
async def async_check_ellipsis():
52+
...
53+
54+
async def async_check_pass():
55+
pass
56+
57+
def check_filled_hint() -> str:
58+
return ""
59+
60+
def async_check_filled_hint() -> str:
61+
return ""
62+
""".trimIndent()
63+
)
64+
65+
fun testCollections() = doTest(
66+
"""
67+
class A:
68+
...
69+
70+
def get_set()<# [-> [set [ str ]]] #>:
71+
return {"foo", "bar"}
72+
73+
def get_tuple()<# [-> [tuple [ int , A ]]] #>:
74+
return 1, A()
75+
76+
def get_nested_tuples()<# [-> [tuple [ [tuple [ int , int ]] , [tuple [ int , int ]] ]]] #>:
77+
return ((1, 1), (1, 1))
78+
79+
def get_list_any()<# [-> list] #>:
80+
return []
81+
82+
def get_list_of_union_types()<# [-> [list [ [A | int] ]]] #>:
83+
return [A(), 1]
84+
""".trimIndent()
85+
)
86+
87+
fun testDictionaries() = doTest(
88+
"""
89+
def get_dict_any()<# [-> dict] #>:
90+
return {}
91+
92+
def get_dict_single()<# [-> [dict [ str , int ]]] #>:
93+
return {"": 1}
94+
95+
def get_dict_union()<# [-> [dict [ str , [str | int] ]]] #>:
96+
return {"foo": 1, "bar": ""}
97+
98+
def get_nested_dict()<# [-> [dict [ int , [dict [ str , str ]] ]]] #>:
99+
return {1: {"": ""}}
100+
""".trimIndent()
101+
)
102+
103+
fun testAsync() = doTest(
104+
"""
105+
async def coro()<# [-> int] #>:
106+
return 1
107+
108+
async def get_coro()<# [-> [Coroutine [ Any , Any , int ]]] #>:
109+
return coro()
110+
111+
async def get_coro_result()<# [-> int] #>:
112+
return await coro()
113+
114+
async def test_nested_coro<# [-> [Coroutine [ Any , Any , [Coroutine [ Any , Any , int ]] ]]] #>:
115+
return get_coro()
116+
""".trimIndent()
117+
)
118+
119+
fun testGenerators() = doTest(
120+
"""
121+
def sync_generator()<# [-> [Generator [ int , Any , list ]]] #>:
122+
for i in range(10):
123+
yield i
124+
125+
return []
126+
127+
async def async_generator()<# [-> [AsyncGenerator [ int , Any ]]] #>:
128+
for i in range(10):
129+
yield i
130+
""".trimIndent()
131+
)
132+
133+
fun testClassMethods() = doTest(
134+
"""
135+
class A:
136+
def check_method_hint()<# [-> int] #>:
137+
return 1
138+
139+
def check_not_return():
140+
pass
141+
""".trimIndent()
142+
)
143+
144+
fun testCallable() = doTest(
145+
"""
146+
def test_callable(foo: int, bar: str) -> int:
147+
return 1
148+
149+
def get_callable()<# [-> [(foo: int, bar: str) -> ( int )]] #>:
150+
return test_callable
151+
152+
def get_lambda()<# [-> [(x, y) -> ( Any )]] #>:
153+
return lambda x, y: x * y
154+
""".trimIndent()
155+
)
156+
157+
fun testIncompleteDefs() = doTest(
158+
"""
159+
def
160+
161+
def test
162+
163+
def test()
164+
165+
def test:
166+
167+
def test() ->
168+
169+
def test() ->:
170+
""".trimIndent()
171+
)
172+
173+
private fun doTest(text: String) {
174+
testProvider(
175+
"foo.py", text, PythonFunctionInlayTypeHintsProvider()
176+
)
177+
}
178+
}

src/test/kotlin/space/whitememory/pythoninlayparams/PythonInlayHintsTest.kt

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package space.whitememory.pythoninlayparams
2+
3+
import space.whitememory.pythoninlayparams.types.variables.PythonVariablesInlayTypeHintsProvider
4+
5+
class PythonVariableTypesTest : PythonAbstractInlayHintsTestCase() {
6+
private val testObjects = """
7+
import datetime
8+
from typing import Union, TypedDict
9+
10+
def get_int():
11+
return 1
12+
13+
class TestTyped(TypedDict):
14+
id: int
15+
name: str
16+
17+
class UnionChild(Union):
18+
...
19+
20+
class A:
21+
def __init__(self, val: int):
22+
self.val = val
23+
24+
@classmethod
25+
def from_val(cls, val: int) -> "A":
26+
return cls(val)
27+
28+
def get_a():
29+
return A()
30+
31+
def get_a_type():
32+
return A
33+
34+
def get_optional():
35+
if True:
36+
return [A(1)]
37+
38+
return None
39+
40+
def get_callable():
41+
return get_int
42+
43+
def get_lambda():
44+
return lambda x, y: x * y
45+
46+
def get_typed_dict() -> TestTyped:
47+
return {"id": 1, name: "test"}
48+
49+
async def coro():
50+
return 1
51+
52+
async def get_coro():
53+
return coro()
54+
55+
def get_now():
56+
return datetime.datetime.now()
57+
""".trimIndent()
58+
59+
fun testSimple() = doTest(
60+
"""
61+
x1<# [: int] #> = get_int()
62+
x2<# [: [[list [ A ]] | None]] #> = get_optional()
63+
x3<# [: TestTyped] #> = get_typed_dict()
64+
65+
x7<# [: [A | None]] #> = A() or None
66+
x8<# [: [A | None]] #> = A() and None
67+
x9<# [: A] #> = A() or get_a()
68+
x10<# [: [A | None]] #> = get_a() or None
69+
x11<# [: [A | None]] #> = A.from_val(10) or None
70+
x12<# [: [[Type [ A ]] | A]] #> = get_a_type() or A()
71+
x13<# [: [Type [ A ]]] #> = get_a_type()
72+
73+
b1<# [: datetime] #> = get_now()
74+
""".trimIndent()
75+
)
76+
77+
fun testNoHint() = doTest(
78+
"""
79+
x1 = 1
80+
x2 = -1
81+
x3 = +1
82+
x4 = 1_000
83+
x5 = 1 or 2
84+
x6 = 1 and 2
85+
x7 = 1 if True else 1
86+
x8 = 1 + 1
87+
x9 = 1 - 1
88+
89+
b = (111)
90+
c = (1,)
91+
d = ("1", "2", 3)
92+
e = []
93+
94+
f: int = get_int()
95+
# g1 = A
96+
g2 = A(1)
97+
g3 = A(1) or A(2)
98+
g4 = A.from_val(1)
99+
g5 = Union[int, str]
100+
# g6 = UnionChild[int, str]
101+
g7 = {k: None for k, _ in range(10)}
102+
103+
h1 = datetime.datetime.now()
104+
h2 = datetime.datetime()
105+
""".trimIndent()
106+
)
107+
108+
fun testReassignments() = doTest(
109+
"""
110+
x<# [: [Coroutine [ Any , Any , int ]]] #> = coro()
111+
y<# [: [Coroutine [ Any , Any , int ]]] #> = x
112+
z<# [: int] #> = await x
113+
114+
f = 1
115+
g = f
116+
h<# [: int] #> = 1 + g
117+
""".trimIndent()
118+
)
119+
120+
fun testComplex() = doTest(
121+
"""
122+
x1<# [: [[list [ A ]] | None | list | ...]] #> = get_optional() or [] or {}
123+
long_types<# [: [list [ [int | [list [ [list | int | ...] ]] | ...] ]]] #> = [1, [[], 1, '3'], '', []]
124+
125+
coro_callable<# [: [() -> ( [Coroutine [ Any , Any , int ]] )]] #> = coro
126+
one_coro<# [: [Coroutine [ Any , Any , int ]]] #> = await get_coro()
127+
nested_coro<# [: [Coroutine [ Any , Any , [Coroutine [ Any , Any , int ]] ]]] #> = get_coro()
128+
coro_value<# [: int] #> = await one_coro
129+
130+
x4<# [: [() -> ( int )]] #> = get_callable()
131+
x5<# [: [(x, y) -> ( Any )]] #> = get_lambda()
132+
x6<# [: [(val: int) -> ( A )]] #> = A.from_val
133+
134+
def get_generator():
135+
for i<# [: int] #> in range(10):
136+
yield i
137+
return []
138+
139+
async def get_async_generator():
140+
for i<# [: int] #> in range(10):
141+
yield i
142+
143+
g1<# [: [Generator [ int , Any , list ]]] #> = get_generator()
144+
g2<# [: [AsyncGenerator [ int , Any ]]] #> = get_async_generator()
145+
""".trimIndent()
146+
)
147+
148+
fun testClassAttrs() = doTest(
149+
"""
150+
class TestAttrs:
151+
x<# [: int] #> = get_int()
152+
y<# [: [[list [ A ]] | None]] #> = get_optional()
153+
x = 1
154+
""".trimIndent()
155+
)
156+
157+
fun testLoopHints() = doTest(
158+
"""
159+
dt = {"test": 1}
160+
161+
d3 = {u: n for u<# [: str] #>, n<# [: int] #> in dt.items()}
162+
d<# [: [list [ int ]]] #> = [i for i<# [: int] #> in range(10)]
163+
s2 = {i for i<# [: int] #> in range(10)}
164+
d2 = {k: v for k<# [: str] #>, v<# [: int] #> in {"a": 1, "b": 2}.items()}
165+
c<# [: [list [ A ]]] #> = [get_a() for i<# [: int] #> in range(10)]
166+
s = {get_a_type() for i<# [: int] #> in range(10)}
167+
v = {k: get_a() for k<# [: int] #> in range(10)}
168+
s1<# [: [frozenset [ A ]]] #> = frozenset(get_a() for i<# [: int] #> in range(10))
169+
""".trimIndent()
170+
)
171+
172+
private fun doTest(text: String) {
173+
testProvider(
174+
"foo.py",
175+
"$testObjects\n$text",
176+
PythonVariablesInlayTypeHintsProvider(),
177+
PythonVariablesInlayTypeHintsProvider.Settings(showClassAttributeHints = true, showGeneralHints = true)
178+
)
179+
}
180+
}

0 commit comments

Comments
 (0)