Skip to content

Commit 6dd3500

Browse files
committed
Fix test_implementations by using real on-FS file
1 parent b7fb666 commit 6dd3500

File tree

2 files changed

+101
-100
lines changed

2 files changed

+101
-100
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from abc import ABC, abstractmethod
2+
3+
class Animal(ABC):
4+
@abstractmethod
5+
def breathe(self):
6+
pass
7+
8+
@property
9+
@abstractmethod
10+
def size(self) -> str:
11+
pass
12+
13+
class WingedAnimal(Animal):
14+
@abstractmethod
15+
def fly(self, destination):
16+
pass
17+
18+
class Bird(WingedAnimal):
19+
def breathe(self):
20+
print("*inhales like a bird*")
21+
22+
def fly(self, destination):
23+
print("*flies like a bird*")
24+
25+
@property
26+
def size(self) -> str:
27+
return "bird-sized"
28+
29+
print("not a method at all")

test/plugins/test_implementations.py

Lines changed: 72 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,114 @@
11
# Copyright 2017-2020 Palantir Technologies, Inc.
22
# Copyright 2021- Python Language Server Contributors.
33

4-
import os
4+
from collections.abc import Iterable
5+
from importlib.resources import as_file, files
6+
from pathlib import Path
57

68
import pytest
9+
from rope.base.exceptions import BadIdentifierError
710

811
from pylsp import uris
12+
from pylsp.config.config import Config
913
from pylsp.plugins.rope_implementation import pylsp_implementations
10-
from pylsp.workspace import Document
11-
12-
DOC_URI = uris.from_fs_path(__file__)
13-
DOC = """
14-
from abc import ABC, abstractmethod
15-
16-
class Animal(ABC):
17-
@abstractmethod
18-
def breathe(self):
19-
...
20-
21-
@property
22-
@abstractmethod
23-
def size(self) -> str:
24-
...
25-
26-
class WingedAnimal(Animal):
27-
@abstractmethod
28-
def fly(self, destination):
29-
...
30-
31-
class Bird(WingedAnimal):
32-
def breathe(self):
33-
print("*inhales like a bird*")
34-
35-
def fly(self, destination):
36-
print("*flies like a bird*")
37-
38-
@property
39-
def size(self) -> str:
40-
return "bird-sized"
41-
42-
print("not a method at all")
43-
"""
14+
from pylsp.workspace import Workspace
15+
16+
17+
# We use a real file because the part of Rope that this feature uses
18+
# (`rope.findit.find_implementations`) *always* loads files from the
19+
# filesystem, in contrast to e.g. `code_assist` which takes a `source` argument
20+
# that can be more easily faked.
21+
# An alternative to using real files would be `unittest.mock.patch`, but that
22+
# ends up being more trouble than it's worth...
23+
@pytest.fixture
24+
def examples_dir_path() -> Iterable[Path]:
25+
with as_file(files("test.data.implementations_examples")) as path:
26+
yield path
27+
28+
29+
@pytest.fixture
30+
def doc_uri(examples_dir_path: Path) -> str:
31+
return uris.from_fs_path(str(examples_dir_path / "example.py"))
32+
33+
34+
# Similarly to the above, we need our workspace & config to point to the actual
35+
# location on the filesystem containing the example modules, so we override the
36+
# root fixtures:
37+
@pytest.fixture
38+
def workspace(examples_dir_path: Path, endpoint) -> None:
39+
ws = Workspace(uris.from_fs_path(str(examples_dir_path)), endpoint)
40+
ws._config = Config(ws.root_uri, {}, 0, {})
41+
yield ws
42+
ws.close()
43+
44+
45+
@pytest.fixture
46+
def config(workspace):
47+
cfg = Config(workspace.root_uri, {}, 0, {})
48+
cfg._plugin_settings = {
49+
"plugins": {"pylint": {"enabled": False, "args": [], "executable": None}}
50+
}
51+
return cfg
4452

4553

46-
def test_implementations(config, workspace) -> None:
54+
def test_implementations(config, workspace, doc_uri) -> None:
4755
# Over 'fly' in WingedAnimal.fly
48-
cursor_pos = {"line": 15, "character": 9}
56+
cursor_pos = {"line": 14, "character": 8}
4957

5058
# The implementation of 'Bird.fly'
5159
def_range = {
52-
"start": {"line": 22, "character": 0},
53-
"end": {"line": 22, "character": 999},
60+
"start": {"line": 21, "character": 0},
61+
"end": {"line": 21, "character": 999},
5462
}
5563

56-
workspace.put_document(DOC_URI, DOC)
57-
doc = workspace.get_document(DOC_URI)
58-
assert [{"uri": DOC_URI, "range": def_range}] == pylsp_implementations(
64+
doc = workspace.get_document(doc_uri)
65+
assert [{"uri": doc_uri, "range": def_range}] == pylsp_implementations(
5966
config, workspace, doc, cursor_pos
6067
)
6168

6269

63-
def test_implementations_skipping_one_class(config, workspace) -> None:
70+
def test_implementations_skipping_one_class(config, workspace, doc_uri) -> None:
6471
# Over 'Animal.breathe'
65-
cursor_pos = {"line": 15, "character": 9}
72+
cursor_pos = {"line": 4, "character": 8}
6673

6774
# The implementation of 'breathe', skipping intermediate classes
6875
def_range = {
69-
"start": {"line": 19, "character": 0},
70-
"end": {"line": 19, "character": 999},
76+
"start": {"line": 18, "character": 0},
77+
"end": {"line": 18, "character": 999},
7178
}
7279

73-
doc = Document(DOC_URI, workspace, DOC)
74-
assert [{"uri": DOC_URI, "range": def_range}] == pylsp_implementations(
80+
doc = workspace.get_document(doc_uri)
81+
assert [{"uri": doc_uri, "range": def_range}] == pylsp_implementations(
7582
config, workspace, doc, cursor_pos
7683
)
7784

7885

79-
@pytest.mark.xfail(reason="not implemented upstream (Rope)", strict=True)
80-
def test_property_implementations(config, workspace) -> None:
86+
@pytest.mark.xfail(
87+
reason="not implemented upstream (Rope)", strict=True, raises=BadIdentifierError
88+
)
89+
def test_property_implementations(config, workspace, doc_uri) -> None:
8190
# Over 'Animal.size'
82-
cursor_pos = {"line": 10, "character": 9}
91+
cursor_pos = {"line": 9, "character": 9}
8392

8493
# The property implementation 'Bird.size'
8594
def_range = {
86-
"start": {"line": 26, "character": 0},
87-
"end": {"line": 26, "character": 999},
95+
"start": {"line": 25, "character": 0},
96+
"end": {"line": 25, "character": 999},
8897
}
8998

90-
doc = Document(DOC_URI, workspace, DOC)
91-
assert [{"uri": DOC_URI, "range": def_range}] == pylsp_implementations(
99+
doc = workspace.get_document(doc_uri)
100+
assert [{"uri": doc_uri, "range": def_range}] == pylsp_implementations(
92101
config, workspace, doc, cursor_pos
93102
)
94103

95104

96-
def test_no_implementations_if_not_a_method(config, workspace) -> None:
97-
# Over 'print(...)' call
98-
cursor_pos = {"line": 26, "character": 0}
99-
100-
doc = Document(DOC_URI, workspace, DOC)
101-
assert [] == pylsp_implementations(config, workspace, doc, cursor_pos)
105+
def test_implementations_not_a_method(config, workspace, doc_uri) -> None:
106+
# Over 'print(...)' call => Rope error because not a method.
107+
cursor_pos = {"line": 28, "character": 0}
102108

109+
doc = workspace.get_document(doc_uri)
103110

104-
def test_document_path_implementations(
105-
config, workspace, workspace_other_root_path, tmpdir
106-
) -> None:
107-
# Create a dummy module out of the workspace's root_path and try to get
108-
# a implementation on it in another file placed next to it.
109-
module_content = """
110-
class A:
111-
def f():
112-
"""
113-
114-
p = tmpdir.join("mymodule.py")
115-
p.write(module_content)
116-
117-
# Content of doc to test implementation
118-
doc_content = """
119-
from mymodule import A
120-
class B(A):
121-
def f():
122-
"""
123-
doc_path = str(tmpdir) + os.path.sep + "myfile.py"
124-
doc_uri = uris.from_fs_path(doc_path)
125-
doc = Document(doc_uri, workspace_other_root_path, doc_content)
126-
127-
# The range where f is defined in mymodule.py
128-
def_range = {
129-
"start": {"line": 2, "character": 0},
130-
"end": {"line": 2, "character": 999},
131-
}
132-
133-
# The position where foo is called in myfile.py
134-
cursor_pos = {"line": 3, "character": 9}
135-
136-
# The uri for mymodule.py
137-
module_path = str(p)
138-
module_uri = uris.from_fs_path(module_path)
139-
140-
assert [{"uri": module_uri, "range": def_range}] == pylsp_implementations(
141-
config, workspace, doc, cursor_pos
142-
)
111+
# This exception is turned into an empty result set automatically in upper
112+
# layers, so we just check that it is raised to document this behavior:
113+
with pytest.raises(BadIdentifierError):
114+
pylsp_implementations(config, workspace, doc, cursor_pos)

0 commit comments

Comments
 (0)