Skip to content

Commit 56fbe4f

Browse files
authored
Merge pull request #63 from benlangmuir/sourcekit-lsp-test
Add a test for sourcekit-lsp
2 parents e949519 + 1060ea1 commit 56fbe4f

File tree

7 files changed

+192
-0
lines changed

7 files changed

+192
-0
lines changed

lit.cfg

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ repl_swift_dummy_path = lit_config.params.get(
187187
"repl_swift",
188188
os.path.join(package_path, "usr", "bin", "repl_swift"))
189189
lit_config.note("testing using 'repl_swift': {}".format(repl_swift_dummy_path))
190+
191+
sourcekit_lsp_path = lit_config.params.get(
192+
"sourcekit-lsp",
193+
os.path.join(package_path, "usr", "bin", "sourcekit-lsp"))
190194

191195
# Verify they exist.
192196
if not os.path.exists(swift_path):
@@ -204,6 +208,12 @@ if not os.path.exists(repl_swift_dummy_path):
204208
if platform.system() == 'Linux':
205209
lit_config.fatal("repl_swift does not exist!")
206210

211+
if os.path.exists(sourcekit_lsp_path):
212+
config.available_features.add("have-sourcekit-lsp")
213+
lit_config.note("testing using 'sourcekit-lsp': {}".format(sourcekit_lsp_path))
214+
else:
215+
lit_config.note("'sourcekit-lsp' unavailable, skipping related tests")
216+
207217
# Define our supported substitutions.
208218
config.substitutions.append( ('%{package_path}', package_path) )
209219
config.substitutions.append( ('%{python}', sys.executable) )
@@ -215,6 +225,7 @@ config.substitutions.append( ('%{swift}', swift_path) )
215225
config.substitutions.append( ('%{swiftc}', swiftc_path) )
216226
config.substitutions.append( ('%{FileCheck}', filecheck_path) )
217227
config.substitutions.append( ('%{readelf}', readelf_path) )
228+
config.substitutions.append( ('%{sourcekit-lsp}', sourcekit_lsp_path) )
218229

219230
# Add substitutions for swiftpm executables.
220231
swiftpm_build = lit_config.params.get("swiftpm-build")

test-sourcekit-lsp/pkg/Package.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// swift-tools-version:5.1
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "pkg",
6+
targets: [
7+
.target(name: "exec", dependencies: ["lib", "clib"]),
8+
.target(name: "lib", dependencies: []),
9+
.target(name: "clib", dependencies: []),
10+
]
11+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "clib.h"
2+
3+
void clib_func(void) {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef CLIB_H
2+
#define CLIB_H
3+
4+
void clib_func(void);
5+
void clib_other(void);
6+
7+
#endif // CLIB_H
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import lib
2+
import clib
3+
4+
Lib().foo()
5+
clib_func()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public struct Lib {
2+
public func foo() {}
3+
public init() {}
4+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Canary test for sourcekit-lsp, covering interaction with swiftpm and toolchain
2+
# language services.
3+
4+
# REQUIRES: have-sourcekit-lsp
5+
6+
# Make a sandbox dir.
7+
# RUN: rm -rf %t.dir
8+
# RUN: mkdir -p %t.dir
9+
# RUN: cp -r %S/pkg %t.dir/
10+
11+
# RUN: env SWIFTPM_ENABLE_CLANG_INDEX_STORE=1 %{swift-build} --package-path %t.dir/pkg -Xswiftc -index-ignore-system-modules -v 2>&1 | tee %t.build-log
12+
# RUN: %{FileCheck} --check-prefix CHECK-BUILD-LOG --input-file %t.build-log %s
13+
# CHECK-BUILD-LOG-NOT: error:
14+
15+
# RUN: %{python} -u %s %{sourcekit-lsp} %t.dir/pkg 2>&1 | tee %t.run-log
16+
# RUN: %{FileCheck} --input-file %t.run-log %s
17+
18+
import argparse
19+
import json
20+
import os
21+
import subprocess
22+
import sys
23+
24+
class LspScript(object):
25+
def __init__(self):
26+
self.request_id = 0
27+
self.script = ''
28+
29+
def request(self, method, params):
30+
body = json.dumps({
31+
'jsonrpc': '2.0',
32+
'id': self.request_id,
33+
'method': method,
34+
'params': params
35+
})
36+
self.request_id += 1
37+
self.script += 'Content-Length: {}\r\n\r\n{}'.format(len(body), body)
38+
39+
def note(self, method, params):
40+
body = json.dumps({
41+
'jsonrpc': '2.0',
42+
'method': method,
43+
'params': params
44+
})
45+
self.script += 'Content-Length: {}\r\n\r\n{}'.format(len(body), body)
46+
47+
def main():
48+
parser = argparse.ArgumentParser()
49+
parser.add_argument('sourcekit_lsp')
50+
parser.add_argument('package')
51+
args = parser.parse_args()
52+
53+
lsp = LspScript()
54+
lsp.request('initialize', {
55+
'rootPath': args.package,
56+
'capabilities': {},
57+
'initializationOptions': {
58+
'listenToUnitEvents': False,
59+
}
60+
})
61+
62+
main_swift = os.path.join(args.package, 'Sources', 'exec', 'main.swift')
63+
with open(main_swift, 'r') as f:
64+
main_swift_content = f.read()
65+
66+
lsp.note('textDocument/didOpen', {
67+
'textDocument': {
68+
'uri': 'file://' + main_swift,
69+
'languageId': 'swift',
70+
'version': 0,
71+
'text': main_swift_content,
72+
}
73+
})
74+
75+
lsp.request('workspace/_pollIndex', {})
76+
lsp.request('textDocument/definition', {
77+
'textDocument': { 'uri': 'file://' + main_swift },
78+
'position': { 'line': 3, 'character': 6}, ## zero-based
79+
})
80+
81+
# CHECK: "result":[
82+
# CHECK-DAG: lib.swift
83+
# CHECK-DAG: "line":1
84+
# CHECK-DAG: "character":14
85+
# CHECK: ]
86+
87+
lsp.request('textDocument/definition', {
88+
'textDocument': { 'uri': 'file://' + main_swift },
89+
'position': { 'line': 4, 'character': 0}, ## zero-based
90+
})
91+
92+
# CHECK: "result":[
93+
# CHECK-DAG: clib.c
94+
# CHECK-DAG: "line":2
95+
# CHECK-DAG: "character":5
96+
# CHECK: ]
97+
98+
lsp.request('textDocument/completion', {
99+
'textDocument': { 'uri': 'file://' + main_swift },
100+
'position': { 'line': 3, 'character': 6}, ## zero-based
101+
})
102+
# CHECK: "items":[
103+
# CHECK-DAG: "label":"foo()"
104+
# CHECK-DAG: "label":"self"
105+
# CHECK: ]
106+
107+
clib_c = os.path.join(args.package, 'Sources', 'clib', 'clib.c')
108+
with open(clib_c, 'r') as f:
109+
clib_c_content = f.read()
110+
111+
lsp.note('textDocument/didOpen', {
112+
'textDocument': {
113+
'uri': 'file://' + clib_c,
114+
'languageId': 'c',
115+
'version': 0,
116+
'text': clib_c_content,
117+
}
118+
})
119+
120+
lsp.request('textDocument/completion', {
121+
'textDocument': { 'uri': 'file://' + clib_c },
122+
'position': { 'line': 2, 'character': 22}, ## zero-based
123+
})
124+
# CHECK: "items":[
125+
# FIXME: Extra space?
126+
# CHECK-DAG: "label":" clib_func()"
127+
# CHECK-DAG: "label":" clib_other()"
128+
# CHECK: ]
129+
130+
lsp.request('shutdown', {})
131+
lsp.note('exit', {})
132+
133+
print('==== INPUT ====')
134+
print(lsp.script)
135+
print('')
136+
print('==== OUTPUT ====')
137+
138+
p = subprocess.Popen([args.sourcekit_lsp, '--sync'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
139+
out, _ = p.communicate(lsp.script)
140+
print(out)
141+
print('')
142+
143+
if p.returncode == 0:
144+
print('OK')
145+
else:
146+
print('error: sourcekit-lsp exited with code {}'.format(p.returncode))
147+
sys.exit(1)
148+
# CHECK: OK
149+
150+
if __name__ == "__main__":
151+
main()

0 commit comments

Comments
 (0)