Skip to content

Commit 37eb656

Browse files
committed
Add tests for type/call hierarchies
1 parent 70589f9 commit 37eb656

File tree

10 files changed

+479
-57
lines changed

10 files changed

+479
-57
lines changed

ycmd/completers/language_server/language_server_completer.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,7 +1685,7 @@ def GetDetailedDiagnostic( self, request_data ):
16851685
message = diagnostic[ 'message' ]
16861686
try:
16871687
code = diagnostic[ 'code' ]
1688-
message += f' [{ code }]'
1688+
message += f' [{ code }]' # noqa
16891689
except KeyError:
16901690
pass
16911691

@@ -2739,7 +2739,11 @@ def Hierarchy( self, request_data, args ):
27392739
*_LspLocationToLocationAndDescription( request_data, location ) )
27402740
for location in [ item ] ]
27412741
return result
2742-
raise RuntimeError( f'No { direction } { kind } found.' )
2742+
if kind == 'call':
2743+
raise RuntimeError(
2744+
f'No { direction.rstrip( "Calls" ) } { kind }s found.' )
2745+
else:
2746+
raise RuntimeError( f'No { direction } found.' )
27432747

27442748

27452749
def CallHierarchy( self, request_data, args ):

ycmd/tests/clangd/subcommands_test.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from hamcrest import ( assert_that,
1919
has_items,
2020
contains_exactly,
21+
contains_inanyorder,
2122
contains_string,
2223
equal_to,
2324
has_entries,
@@ -98,6 +99,39 @@ def RunGoToTest_all( app, folder, command, test ):
9899
} )
99100

100101

102+
def RunHierarchyTest( app,
103+
kind,
104+
direction,
105+
location,
106+
expected,
107+
code ):
108+
file, line, column = location
109+
contents = ReadFile( file )
110+
request = {
111+
'completer_target' : 'filetype_default',
112+
'command_arguments': [ f'{ kind.title() }Hierarchy' ],
113+
'line_num' : line,
114+
'column_num' : column,
115+
'filepath' : file,
116+
'contents' : contents,
117+
'filetype' : 'cpp'
118+
}
119+
test = { 'request': request,
120+
'route': '/run_completer_command' }
121+
prepare_hierarchy_response = RunAfterInitialized( app, test )
122+
request[ 'command_arguments' ] = [
123+
f'Resolve{ kind.title() }HierarchyItem',
124+
prepare_hierarchy_response[ 0 ],
125+
direction
126+
]
127+
test[ 'expect' ] = {
128+
'response': code,
129+
'data': expected
130+
}
131+
RunAfterInitialized( app, test )
132+
133+
134+
101135
def RunGetSemanticTest( app,
102136
filepath,
103137
filetype,
@@ -605,6 +639,8 @@ def test_Subcommands_ServerNotInitialized( self, app ):
605639
'GoToType',
606640
'RefactorRename',
607641
'GoToAlternateFile',
642+
'CallHierarchy',
643+
'TypeHierarchy',
608644
]:
609645
with self.subTest( cmd = cmd ):
610646
completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] )
@@ -1285,3 +1321,122 @@ def test_Subcommands_RefactorRename( self, app ):
12851321
'route': '/run_completer_command'
12861322
}
12871323
RunAfterInitialized( app, test )
1324+
1325+
1326+
@SharedYcmd
1327+
def test_Subcommands_SupertypeHierarchy( self, app ):
1328+
filepath = PathToTestFile( 'hierarchies.cc' )
1329+
for location, response, code in [
1330+
[ ( filepath, 16, 8 ),
1331+
contains_inanyorder(
1332+
has_entry( 'locations',
1333+
contains_exactly(
1334+
LocationMatcher( filepath, 13, 8 )
1335+
) ),
1336+
has_entry( 'locations',
1337+
contains_exactly(
1338+
LocationMatcher( filepath, 12, 8 )
1339+
) ),
1340+
),
1341+
requests.codes.ok ],
1342+
[ ( filepath, 13, 8 ),
1343+
contains_inanyorder(
1344+
has_entry( 'locations',
1345+
contains_exactly(
1346+
LocationMatcher( filepath, 12, 8 )
1347+
) ),
1348+
),
1349+
requests.codes.ok ],
1350+
[ ( filepath, 12, 8 ),
1351+
ErrorMatcher( RuntimeError, 'No supertypes found.' ),
1352+
requests.codes.server_error ]
1353+
]:
1354+
with self.subTest( location = location, response = response ):
1355+
RunHierarchyTest( app, 'type', 'supertypes', location, response, code )
1356+
1357+
1358+
@SharedYcmd
1359+
def test_Subcommands_SubtypeHierarchy( self, app ):
1360+
filepath = PathToTestFile( 'hierarchies.cc' )
1361+
for location, response, code in [
1362+
[ ( filepath, 12, 8 ),
1363+
contains_inanyorder(
1364+
has_entry( 'locations',
1365+
contains_exactly(
1366+
LocationMatcher( filepath, 13, 8 )
1367+
) ),
1368+
has_entry( 'locations',
1369+
contains_exactly(
1370+
LocationMatcher( filepath, 15, 8 )
1371+
) ),
1372+
has_entry( 'locations',
1373+
contains_exactly(
1374+
LocationMatcher( filepath, 16, 8 )
1375+
) ) ),
1376+
requests.codes.ok ],
1377+
[ ( filepath, 13, 8 ),
1378+
contains_inanyorder(
1379+
has_entry( 'locations',
1380+
contains_exactly(
1381+
LocationMatcher( filepath, 16, 8 )
1382+
) ) ),
1383+
requests.codes.ok ],
1384+
[ ( filepath, 16, 8 ),
1385+
ErrorMatcher( RuntimeError, 'No subtypes found.' ),
1386+
requests.codes.server_error ]
1387+
]:
1388+
with self.subTest( location = location, response = response ):
1389+
RunHierarchyTest( app, 'type', 'subtypes', location, response, code )
1390+
1391+
1392+
@SharedYcmd
1393+
def test_Subcommands_IncomingCallHierarchy( self, app ):
1394+
filepath = PathToTestFile( 'hierarchies.cc' )
1395+
for location, response, code in [
1396+
[ ( filepath, 1, 5 ),
1397+
contains_inanyorder(
1398+
has_entry( 'locations',
1399+
contains_exactly(
1400+
LocationMatcher( filepath, 4, 12 ),
1401+
LocationMatcher( filepath, 4, 18 )
1402+
) ),
1403+
has_entry( 'locations',
1404+
contains_exactly(
1405+
LocationMatcher( filepath, 9, 12 )
1406+
) ) ),
1407+
requests.codes.ok ],
1408+
[ ( filepath, 3, 5 ),
1409+
contains_inanyorder(
1410+
has_entry( 'locations',
1411+
contains_exactly(
1412+
LocationMatcher( filepath, 8, 13 )
1413+
) ) ),
1414+
requests.codes.ok ],
1415+
[ ( filepath, 7, 5 ),
1416+
ErrorMatcher( RuntimeError, 'No incoming calls found.' ),
1417+
requests.codes.server_error ]
1418+
]:
1419+
with self.subTest( location = location, response = response ):
1420+
RunHierarchyTest( app, 'call', 'incoming', location, response, code )
1421+
1422+
1423+
@SharedYcmd
1424+
def test_Subcommands_NoHierarchyFound( self, app ):
1425+
for kind in [ 'call', 'type' ]:
1426+
with self.subTest( kind = kind ):
1427+
filepath = PathToTestFile( 'hierarchies.cc' )
1428+
request = {
1429+
'completer_target' : 'filetype_default',
1430+
'command_arguments': [ f'{ kind.title() }Hierarchy' ],
1431+
'line_num' : 2,
1432+
'column_num' : 1,
1433+
'filepath' : filepath,
1434+
'filetype' : 'cpp'
1435+
}
1436+
test = { 'request': request,
1437+
'route': '/run_completer_command',
1438+
'expect': {
1439+
'response': requests.codes.server_error,
1440+
'data': ErrorMatcher( RuntimeError,
1441+
f'No { kind } hierarchy found.' ) } }
1442+
RunAfterInitialized( app, test )
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
int f();
2+
3+
int g() {
4+
return f() + f();
5+
}
6+
7+
int h() {
8+
int x = g();
9+
return f() + x;
10+
}
11+
12+
struct B0 {};
13+
struct B1 : B0 {};
14+
15+
struct D0 : B0 {};
16+
struct D1 : B0, B1 {};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package main
2+
3+
func f() (int) {
4+
return 5;
5+
}
6+
func g() (int) {
7+
return f() + f();
8+
}
9+
func h() (int) {
10+
var x = g();
11+
return f() + x;
12+
}

ycmd/tests/go/subcommands_test.py

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@ def CombineRequest( request, data ):
8484

8585
print( f'completer response: { pformat( response.json ) }' )
8686

87-
assert_that( response.status_code,
88-
equal_to( test[ 'expect' ][ 'response' ] ) )
89-
assert_that( response.json, test[ 'expect' ][ 'data' ] )
87+
if 'expect' in test:
88+
assert_that( response.status_code,
89+
equal_to( test[ 'expect' ][ 'response' ] ) )
90+
assert_that( response.json, test[ 'expect' ][ 'data' ] )
91+
return response.json
9092

9193

9294
def RunFixItTest( app, description, filepath, line, col, fixits_for_line ):
@@ -105,6 +107,32 @@ def RunFixItTest( app, description, filepath, line, col, fixits_for_line ):
105107
} )
106108

107109

110+
def RunHierarchyTest( app, kind, direction, location, expected, code ):
111+
file, line, column = location
112+
request = {
113+
'completer_target' : 'filetype_default',
114+
'command': f'{ kind.title() }Hierarchy',
115+
'line_num' : line,
116+
'column_num' : column,
117+
'filepath' : file,
118+
}
119+
test = { 'request': request,
120+
'route': '/run_completer_command' }
121+
prepare_hierarchy_response = RunTest( app, test )
122+
request.update( {
123+
'command': f'Resolve{ kind.title() }HierarchyItem',
124+
'arguments': [
125+
prepare_hierarchy_response[ 0 ],
126+
direction
127+
]
128+
} )
129+
test[ 'expect' ] = {
130+
'response': code,
131+
'data': expected
132+
}
133+
RunTest( app, test )
134+
135+
108136
def RunGoToTest( app, command, test ):
109137
folder = PathToTestFile()
110138
filepath = PathToTestFile( test[ 'req' ][ 0 ] )
@@ -207,6 +235,7 @@ def Test( app, cmd, arguments, *args ):
207235
Test( app, 'GoTo', [] )
208236
Test( app, 'GoToDeclaration', [] )
209237
Test( app, 'GoToDefinition', [] )
238+
Test( app, 'CallHierarchy', [] )
210239
Test( app, 'GoToType', [] )
211240
Test( app, 'FixIt', [] )
212241

@@ -530,3 +559,85 @@ def test_Subcommands_GoTo_WorksAfterSwitchingProjects( self, app ):
530559
]:
531560
with self.subTest( test = test ):
532561
RunGoToTest( app, 'GoTo', test )
562+
563+
564+
@SharedYcmd
565+
def test_Subcommands_OutgoingCallHierarchy( self, app ):
566+
filepath = PathToTestFile( 'hierarchies.go' )
567+
for location, response, code in [
568+
[ ( filepath, 9, 6 ),
569+
contains_inanyorder(
570+
has_entry( 'locations',
571+
contains_exactly(
572+
LocationMatcher( filepath, 10, 13 ),
573+
) ),
574+
has_entry( 'locations',
575+
contains_exactly(
576+
LocationMatcher( filepath, 11, 12 )
577+
) ) ),
578+
requests.codes.ok ],
579+
[ ( filepath, 6, 6 ),
580+
contains_inanyorder(
581+
has_entry( 'locations',
582+
contains_exactly(
583+
LocationMatcher( filepath, 7, 12 ),
584+
LocationMatcher( filepath, 7, 18 )
585+
) ) ),
586+
requests.codes.ok ],
587+
[ ( filepath, 3, 6 ),
588+
ErrorMatcher( RuntimeError, 'No outgoing calls found.' ),
589+
requests.codes.server_error ]
590+
]:
591+
with self.subTest( location = location, response = response ):
592+
RunHierarchyTest( app, 'call', 'outgoing', location, response, code )
593+
594+
595+
@SharedYcmd
596+
def test_Subcommands_IncomingCallHierarchy( self, app ):
597+
filepath = PathToTestFile( 'hierarchies.go' )
598+
for location, response, code in [
599+
[ ( filepath, 3, 6 ),
600+
contains_inanyorder(
601+
has_entry( 'locations',
602+
contains_exactly(
603+
LocationMatcher( filepath, 7, 12 ),
604+
LocationMatcher( filepath, 7, 18 )
605+
) ),
606+
has_entry( 'locations',
607+
contains_exactly(
608+
LocationMatcher( filepath, 11, 12 )
609+
) ) ),
610+
requests.codes.ok ],
611+
[ ( filepath, 6, 6 ),
612+
contains_inanyorder(
613+
has_entry( 'locations',
614+
contains_exactly(
615+
LocationMatcher( filepath, 10, 13 )
616+
) ) ),
617+
requests.codes.ok ],
618+
[ ( filepath, 9, 6 ),
619+
ErrorMatcher( RuntimeError, 'No incoming calls found.' ),
620+
requests.codes.server_error ]
621+
]:
622+
with self.subTest( location = location, response = response ):
623+
RunHierarchyTest( app, 'call', 'incoming', location, response, code )
624+
625+
626+
@SharedYcmd
627+
def test_Subcommands_NoHierarchyFound( self, app ):
628+
filepath = PathToTestFile( 'hierarchies.go' )
629+
request = {
630+
'completer_target' : 'filetype_default',
631+
'command': 'CallHierarchy',
632+
'line_num' : 2,
633+
'column_num' : 1,
634+
'filepath' : filepath,
635+
'filetype' : 'go'
636+
}
637+
test = { 'request': request,
638+
'route': '/run_completer_command',
639+
'expect': {
640+
'response': requests.codes.server_error,
641+
'data': ErrorMatcher(
642+
RuntimeError, 'No call hierarchy found.' ) } }
643+
RunTest( app, test )

ycmd/tests/rust/diagnostics_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@
4545
'kind': 'ERROR',
4646
'text':
4747
'no field `build_` on type `test::Builder`\nunknown field [E0609]',
48-
'location': LocationMatcher( MAIN_FILEPATH, 14, 13 ),
49-
'location_extent': RangeMatcher( MAIN_FILEPATH, ( 14, 13 ), ( 14, 19 ) ),
48+
'location': LocationMatcher( MAIN_FILEPATH, 15, 13 ),
49+
'location_extent': RangeMatcher( MAIN_FILEPATH, ( 15, 13 ), ( 15, 19 ) ),
5050
'ranges': contains_exactly( RangeMatcher( MAIN_FILEPATH,
51-
( 14, 13 ),
52-
( 14, 19 ) ) ),
51+
( 15, 13 ),
52+
( 15, 19 ) ) ),
5353
'fixit_available': False
5454
} ),
5555
has_entries( {
@@ -122,7 +122,7 @@ def test_Diagnostics_DetailedDiags( self, app ):
122122
request_data = BuildRequest( contents = contents,
123123
filepath = filepath,
124124
filetype = 'rust',
125-
line_num = 14,
125+
line_num = 15,
126126
column_num = 13 )
127127

128128
results = app.post_json( '/detailed_diagnostic', request_data ).json

0 commit comments

Comments
 (0)