Skip to content

Commit b577533

Browse files
committed
Add tests
1 parent 7df13bf commit b577533

16 files changed

Lines changed: 594 additions & 0 deletions

test/__init__.py

Whitespace-only changes.

test/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright 2017 Palantir Technologies, Inc.
2+
""" py.test configuration"""
3+
4+
pytest_plugins = [
5+
'test.fixtures'
6+
]

test/fixtures.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright 2017 Palantir Technologies, Inc.
2+
import os
3+
import pytest
4+
from pyls.python_ls import PythonLanguageServer
5+
from pyls.workspace import Workspace
6+
from StringIO import StringIO
7+
8+
9+
@pytest.fixture
10+
def pyls():
11+
""" Return an initialized python LS """
12+
rfile = StringIO()
13+
wfile = StringIO()
14+
ls = PythonLanguageServer(rfile, wfile)
15+
16+
ls.m_initialize(
17+
processId=1,
18+
rootPath=os.path.dirname(__file__),
19+
initializationOptions={}
20+
)
21+
22+
return ls
23+
24+
25+
@pytest.fixture
26+
def workspace():
27+
""" Return a workspace """
28+
return Workspace(os.path.dirname(__file__))

test/providers/__init__.py

Whitespace-only changes.

test/providers/test_completion.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2017 Palantir Technologies, Inc.
2+
from pyls.providers.completion import JediCompletionProvider
3+
4+
DOC_URI = __file__
5+
DOC = """import sys
6+
print sys.stdin.read()
7+
8+
def hello():
9+
pass
10+
11+
def _a_hello():
12+
pass
13+
14+
"""
15+
16+
17+
def test_completion(workspace):
18+
# Over 'r' in sys.stdin.read()
19+
com_position = {'line': 1, 'character': 17}
20+
21+
workspace.put_document(DOC_URI, DOC)
22+
provider = JediCompletionProvider(workspace)
23+
24+
items = provider.run(DOC_URI, com_position)['items']
25+
26+
assert len(items) > 0
27+
assert items[0]['label'] == 'read'
28+
29+
30+
def test_completion_ordering(workspace):
31+
# Over the blank line
32+
com_position = {'line': 8, 'character': 0}
33+
34+
workspace.put_document(DOC_URI, DOC)
35+
provider = JediCompletionProvider(workspace)
36+
37+
completions = provider.run(DOC_URI, com_position)['items']
38+
39+
items = {c['label']: c['sortText'] for c in completions}
40+
41+
# Assert that builtins come after our own functions even if alphabetically they're before
42+
assert items['hello'] < items['assert']
43+
# And that 'hidden' functions come after unhidden ones
44+
assert items['hello'] < items['_a_hello']

test/providers/test_definitions.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2017 Palantir Technologies, Inc.
2+
from pyls.providers.definition import JediDefinitionsProvider
3+
4+
DOC_URI = __file__
5+
DOC = """def a():
6+
pass
7+
8+
print a()
9+
"""
10+
11+
12+
def test_definitions(workspace):
13+
# Over 'a' in print a
14+
cursor_pos = {'line': 3, 'character': 6}
15+
16+
# The definition of 'a'
17+
range = {
18+
'start': {'line': 0, 'character': 4},
19+
'end': {'line': 0, 'character': 5}
20+
}
21+
22+
workspace.put_document(DOC_URI, DOC)
23+
provider = JediDefinitionsProvider(workspace)
24+
25+
assert [{'uri': DOC_URI, 'range': range}] == provider.run(DOC_URI, cursor_pos)

test/providers/test_format.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright 2017 Palantir Technologies, Inc.
2+
from pyls.providers.format import YapfFormatter
3+
4+
DOC_URI = __file__
5+
DOC = """A = [
6+
'h', 'w',
7+
8+
'a'
9+
]
10+
11+
B = ['h',
12+
13+
14+
'w']
15+
"""
16+
17+
GOOD_DOC = """A = ['hello', 'world']\n"""
18+
19+
20+
def test_format(workspace):
21+
workspace.put_document(DOC_URI, DOC)
22+
provider = YapfFormatter(workspace)
23+
24+
res = provider.run(DOC_URI)
25+
26+
assert len(res) == 1
27+
assert res[0]['newText'] == "A = ['h', 'w', 'a']\n\nB = ['h', 'w']\n"
28+
29+
30+
def test_range_format(workspace):
31+
workspace.put_document(DOC_URI, DOC)
32+
provider = YapfFormatter(workspace)
33+
34+
range = {
35+
'start': {'line': 0, 'character': 0},
36+
'end': {'line': 4, 'character': 10}
37+
}
38+
39+
res = provider.run(DOC_URI, range)
40+
41+
assert len(res) == 1
42+
43+
# Make sure B is still badly formatted
44+
assert res[0]['newText'] == "A = ['h', 'w', 'a']\n\nB = ['h',\n\n\n'w']\n"
45+
46+
47+
def test_no_change(workspace):
48+
workspace.put_document(DOC_URI, GOOD_DOC)
49+
provider = YapfFormatter(workspace)
50+
51+
assert len(provider.run(DOC_URI)) == 0

test/providers/test_hover.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2017 Palantir Technologies, Inc.
2+
from pyls.providers.hover import JediDocStringHoverProvider
3+
4+
DOC_URI = __file__
5+
DOC = """import sys
6+
7+
def main():
8+
print sys.stdin.read()
9+
raise Exception()
10+
"""
11+
12+
13+
def test_hover(workspace):
14+
# Over 'Exception' in raise Exception()
15+
hov_position = {'line': 4, 'character': 17}
16+
# Over the blank second line
17+
no_hov_position = {'line': 1, 'character': 0}
18+
19+
workspace.put_document(DOC_URI, DOC)
20+
provider = JediDocStringHoverProvider(workspace)
21+
22+
assert {
23+
'contents': 'Common base class for all non-exit exceptions.'
24+
} == provider.run(DOC_URI, hov_position)
25+
26+
assert {'contents': ''} == provider.run(DOC_URI, no_hov_position)

test/providers/test_lint.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Copyright 2017 Palantir Technologies, Inc.
2+
import os
3+
import shutil
4+
import tempfile
5+
from pyls.workspace import Workspace
6+
from pyls.providers.lint import PyCodeStyleLinter, PyflakesLinter
7+
8+
DOC_URI = __file__
9+
DOC = """import sys
10+
11+
def hello():
12+
\tpass
13+
14+
import json
15+
"""
16+
17+
DOC_SYNTAX_ERR = """def hello()
18+
pass
19+
"""
20+
21+
22+
def test_pycodestyle(workspace):
23+
workspace.put_document(DOC_URI, DOC)
24+
provider = PyCodeStyleLinter(workspace)
25+
26+
diags = provider.run(DOC_URI)
27+
28+
assert all([d['source'] == 'pycodestyle' for d in diags])
29+
30+
# One we're expecting is:
31+
msg = 'E402 module level import not at top of file'
32+
mod_import = filter(lambda d: d['message'] == msg, diags)[0]
33+
34+
assert mod_import['code'] == 'E402'
35+
assert mod_import['range']['start'] == {'line': 5, 'character': 0}
36+
37+
38+
def test_pycodestyle_config():
39+
""" Test that we load config files properly.
40+
41+
Config files are loaded in the following order:
42+
tox.ini pep8.cfg setup.cfg pycodestyle.cfg
43+
44+
Each overriding the values in the last.
45+
46+
These files are first looked for in the current document's
47+
directory and then each parent directory until any one is found
48+
terminating at the workspace root.
49+
50+
If any section called 'pycodestyle' exists that will be solely used
51+
and any config in a 'pep8' section will be ignored
52+
"""
53+
# Create a workspace in tmp
54+
tmp = tempfile.mkdtemp()
55+
workspace = Workspace(tmp)
56+
doc_uri = 'file://' + tmp + '/' + 'test.py'
57+
58+
provider = PyCodeStyleLinter(workspace)
59+
workspace.put_document(doc_uri, DOC)
60+
61+
# Make sure we get a warning for 'indentation contains tabs'
62+
diags = provider.run(doc_uri)
63+
assert len(filter(lambda d: d['code'] == 'W191', diags)) > 0
64+
65+
content = {
66+
'setup.cfg': ('[pycodestyle]\nignore = W191', True),
67+
'pep8.cfg': ('[pep8]\nignore = W191', True),
68+
'tox.ini': ('', False)
69+
}
70+
71+
for conf_file, (content, working) in content.items():
72+
# Now we'll add config file to ignore it
73+
with open(os.path.join(tmp, conf_file), 'w+') as f:
74+
f.write(content)
75+
76+
# And make sure we don't get any warnings
77+
diags = provider.run(doc_uri)
78+
assert len(filter(lambda d: d['code'] == 'W191', diags)) == 0 if working else 1
79+
80+
os.unlink(os.path.join(tmp, conf_file))
81+
82+
shutil.rmtree(tmp)
83+
84+
85+
def test_pyflakes(workspace):
86+
workspace.put_document(DOC_URI, DOC)
87+
provider = PyflakesLinter(workspace)
88+
89+
diags = provider.run(DOC_URI)
90+
91+
# One we're expecting is:
92+
msg = '\'sys\' imported but unused'
93+
unused_import = filter(lambda d: d['message'] == msg, diags)[0]
94+
95+
assert unused_import['range']['start'] == {'line': 0, 'character': 0}
96+
97+
98+
def test_syntax_error_pyflakes(workspace):
99+
workspace.put_document(DOC_URI, DOC_SYNTAX_ERR)
100+
provider = PyflakesLinter(workspace)
101+
102+
diag = provider.run(DOC_URI)[0]
103+
104+
assert diag['message'] == 'invalid syntax'
105+
assert diag['range']['start'] == {'line': 0, 'character': 12}

test/providers/test_references.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright 2017 Palantir Technologies, Inc.
2+
import os
3+
import pytest
4+
import shutil
5+
import tempfile
6+
from pyls.workspace import Workspace
7+
from pyls.providers.references import JediReferencesProvider
8+
9+
10+
DOC1_NAME = 'test1.py'
11+
DOC2_NAME = 'test2.py'
12+
13+
DOC1 = """class Test1():
14+
pass
15+
"""
16+
17+
DOC2 = """from test1 import Test1
18+
19+
Test1()
20+
"""
21+
22+
23+
@pytest.fixture
24+
def tmp_workspace():
25+
tmp = tempfile.mkdtemp()
26+
workspace = Workspace(tmp)
27+
28+
def create_file(name, content):
29+
fn = os.path.join(tmp, name)
30+
with open(fn, 'w') as f:
31+
f.write(content)
32+
workspace.put_document('file://' + fn, content)
33+
34+
create_file(DOC1_NAME, DOC1)
35+
create_file(DOC2_NAME, DOC2)
36+
37+
yield workspace
38+
shutil.rmtree(tmp)
39+
40+
41+
def test_references(tmp_workspace):
42+
provider = JediReferencesProvider(tmp_workspace)
43+
44+
# Over 'Test1' in class Test1():
45+
position = {'line': 0, 'character': 8}
46+
DOC1_URI = 'file://' + os.path.join(tmp_workspace.root, DOC1_NAME)
47+
48+
refs = provider.run(DOC1_URI, position)
49+
50+
# Definition, the import and the instantiation
51+
assert len(refs) == 3
52+
53+
# Briefly check excluding the definitions (also excludes imports, only counts uses)
54+
no_def_refs = provider.run(DOC1_URI, position, exclude_declaration=True)
55+
assert len(no_def_refs) == 1
56+
57+
# Make sure our definition is correctly located
58+
doc1_ref = filter(lambda u: u['uri'] == DOC1_URI, refs)[0]
59+
assert doc1_ref['range']['start'] == {'line': 0, 'character': 6}
60+
assert doc1_ref['range']['end'] == {'line': 0, 'character': 11}
61+
62+
# Make sure our import is correctly located
63+
doc2_import_ref = filter(lambda u: u['uri'] != DOC1_URI, refs)[0]
64+
assert doc2_import_ref['range']['start'] == {'line': 0, 'character': 18}
65+
assert doc2_import_ref['range']['end'] == {'line': 0, 'character': 23}
66+
67+
doc2_usage_ref = filter(lambda u: u['uri'] != DOC1_URI, refs)[1]
68+
assert doc2_usage_ref['range']['start'] == {'line': 2, 'character': 0}
69+
assert doc2_usage_ref['range']['end'] == {'line': 2, 'character': 5}

0 commit comments

Comments
 (0)