-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_e2e_inheritance.lua
More file actions
214 lines (181 loc) · 10.1 KB
/
test_e2e_inheritance.lua
File metadata and controls
214 lines (181 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
-- E2E tests for inheritance chain resolution.
-- Tests local multi-level inheritance AND cross-file parent resolution.
--
-- Usage:
-- cargo build --release
-- PERL5LIB=$PWD/test_files/lib nvim --headless --clean -u test_nvim_init.lua -l test_e2e_inheritance.lua
vim.opt.rtp:prepend(".")
local t = require("test.runner")
local lsp = require("test.lsp")
local b = require("test.buf")
local buf = lsp.open_and_attach("test_files/inheritance.pl")
-- Wait for cross-file types (BaseWorker) to resolve.
-- Poll by checking completion on $worker-> for "process" from BaseWorker.
local function wait_for_cross_file(max_secs)
for _ = 1, max_secs * 4 do
local line, col = b.find_pos(buf, "$worker->run()")
if line then
local labels = lsp.completion_labels(buf, line, col + 9)
for _, l in ipairs(labels) do
if l == "process" then return true end
end
end
vim.wait(250)
end
return false
end
local cross_file_ready = wait_for_cross_file(15)
if not cross_file_ready then
io.write("\27[33mWARN: cross-file inheritance did not resolve within 15s — some tests may fail\27[0m\n")
io.write(" Make sure PERL5LIB includes test_files/lib\n\n")
end
-- ── 1. Local: completion through inheritance ─────────────────────────
t.test("completion: $dog-> offers own method fetch", function()
local N = "completion: $dog-> offers own method fetch"
local line, col = b.find_pos(buf, "$dog->fetch()")
if not t.ok(N, line, "couldn't find '$dog->fetch()'") then return end
local labels = lsp.completion_labels(buf, line, col + 6)
if t.contains(N, labels, "fetch", "completions") then t.pass(N) end
end)
t.test("completion: $dog-> offers inherited speak from Animal (overridden in Dog)", function()
local N = "completion: $dog-> offers inherited speak from Animal (overridden in Dog)"
local line, col = b.find_pos(buf, "$dog->speak()")
if not t.ok(N, line, "couldn't find '$dog->speak()'") then return end
local labels = lsp.completion_labels(buf, line, col + 6)
if t.contains(N, labels, "speak", "completions") then t.pass(N) end
end)
t.test("completion: $golden-> offers multi-level inherited breathe from Animal", function()
local N = "completion: $golden-> offers multi-level inherited breathe from Animal"
local line, col = b.find_pos(buf, "$golden->breathe()")
if not t.ok(N, line, "couldn't find '$golden->breathe()'") then return end
local labels = lsp.completion_labels(buf, line, col + 10)
if not t.ok(N, #labels > 0, "no completions") then return end
local ok = t.contains(N, labels, "breathe", "completions")
ok = t.contains(N, labels, "be_friendly", "completions") and ok
ok = t.contains(N, labels, "speak", "completions") and ok
if ok then t.pass(N) end
end)
-- ── 2. Local: goto-def to inherited method ───────────────────────────
t.test("goto-def: $golden->breathe() jumps to Animal::breathe", function()
local N = "goto-def: $golden->breathe() jumps to Animal::breathe"
local line, col = b.find_pos(buf, "$golden->breathe()")
if not t.ok(N, line, "couldn't find '$golden->breathe()'") then return end
local def = lsp.def_line(buf, line, col + 10)
local expected = b.find_line(buf, "^sub breathe")
if t.eq(N, expected, def, "definition line") then t.pass(N) end
end)
t.test("goto-def: $dog->speak() jumps to Dog::speak (override, not Animal)", function()
local N = "goto-def: $dog->speak() jumps to Dog::speak (override, not Animal)"
local line, col = b.find_pos(buf, "$dog->speak()")
if not t.ok(N, line, "couldn't find '$dog->speak()'") then return end
local def = lsp.def_line(buf, line, col + 6)
-- Dog::speak is the second "sub speak", Animal::speak is the first
local animal_speak = b.find_line(buf, "^sub speak")
local dog_speak = b.find_line(buf, "^sub speak", (animal_speak or 0) + 2)
if not t.ok(N, dog_speak, "couldn't find Dog::speak line") then return end
if t.eq(N, dog_speak, def, "definition line (should be Dog, not Animal)") then t.pass(N) end
end)
-- ── 3. Local: hover shows provenance ─────────────────────────────────
t.test("hover: $golden->breathe() shows '(from Animal)'", function()
local N = "hover: $golden->breathe() shows '(from Animal)'"
local line, col = b.find_pos(buf, "$golden->breathe()")
if not t.ok(N, line, "couldn't find '$golden->breathe()'") then return end
local text = lsp.hover_text(buf, line, col + 10)
if not t.ok(N, text, "no hover result") then return end
if t.ok(N, text:find("Animal", 1, true), "hover should mention 'Animal', got: " .. text) then
t.pass(N)
end
end)
t.test("hover: $dog->speak() does NOT show '(from Animal)' since Dog overrides", function()
local N = "hover: $dog->speak() does NOT show '(from Animal)' since Dog overrides"
local line, col = b.find_pos(buf, "$dog->speak()")
if not t.ok(N, line, "couldn't find '$dog->speak()'") then return end
local text = lsp.hover_text(buf, line, col + 6)
if not t.ok(N, text, "no hover result") then return end
if t.ok(N, not text:find("from Animal", 1, true),
"hover should NOT mention 'from Animal' since Dog overrides, got: " .. text) then
t.pass(N)
end
end)
-- ── 4. Cross-file: completion from inherited BaseWorker ──────────────
t.test("completion: $worker-> offers process from BaseWorker", function()
local N = "completion: $worker-> offers process from BaseWorker"
local line, col = b.find_pos(buf, "$worker->process()")
if not t.ok(N, line, "couldn't find '$worker->process()'") then return end
local labels = lsp.completion_labels(buf, line, col + 10)
if not t.ok(N, #labels > 0, "no completions") then return end
local ok = t.contains(N, labels, "process", "completions")
ok = t.contains(N, labels, "run", "completions") and ok
if ok then t.pass(N) end
end)
t.test("completion: $worker-> offers status from BaseWorker", function()
local N = "completion: $worker-> offers status from BaseWorker"
local line, col = b.find_pos(buf, "$worker->process()")
if not t.ok(N, line, "couldn't find '$worker->process()'") then return end
local labels = lsp.completion_labels(buf, line, col + 10)
if t.contains(N, labels, "status", "completions") then t.pass(N) end
end)
-- ── 5. Cross-file: goto-def to BaseWorker::process ───────────────────
t.test("goto-def: $worker->process() jumps to BaseWorker.pm", function()
local N = "goto-def: $worker->process() jumps to BaseWorker.pm"
local line, col = b.find_pos(buf, "$worker->process()")
if not t.ok(N, line, "couldn't find '$worker->process()'") then return end
local loc = lsp.def_location(buf, line, col + 10)
if not t.ok(N, loc, "no definition result") then return end
if not t.ok(N, loc.uri and loc.uri:find("BaseWorker.pm", 1, true),
"uri should point to BaseWorker.pm, got: " .. tostring(loc.uri)) then return end
-- process is defined around line 12 in BaseWorker.pm (0-indexed)
if t.ok(N, loc.line and loc.line >= 10 and loc.line <= 15,
"def_line should be ~12, got: " .. tostring(loc.line)) then
t.pass(N)
end
end)
-- ── 6. Cross-file: hover shows provenance ────────────────────────────
t.test("hover: $worker->process() shows BaseWorker provenance", function()
local N = "hover: $worker->process() shows BaseWorker provenance"
local line, col = b.find_pos(buf, "$worker->process()")
if not t.ok(N, line, "couldn't find '$worker->process()'") then return end
local text = lsp.hover_text(buf, line, col + 10)
if not t.ok(N, text, "no hover result") then return end
if t.ok(N, text:find("BaseWorker", 1, true),
"hover should mention 'BaseWorker', got: " .. text) then
t.pass(N)
end
end)
-- ── 7. Goto-def on parent class name in use parent ──────────────────
t.test("goto-def: 'BaseWorker' in use parent opens BaseWorker.pm", function()
local N = "goto-def: 'BaseWorker' in use parent opens BaseWorker.pm"
local line, col = b.find_pos(buf, "use parent 'BaseWorker'")
if not t.ok(N, line, "couldn't find 'use parent BaseWorker'") then return end
-- Position cursor on "BaseWorker" inside the string (col + 12 = inside the string content)
local loc = lsp.def_location(buf, line, col + 12)
if not t.ok(N, loc, "no definition result") then return end
if t.ok(N, loc.uri and loc.uri:find("BaseWorker.pm", 1, true),
"uri should point to BaseWorker.pm, got: " .. tostring(loc.uri)) then
t.pass(N)
end
end)
-- ── 8. Cross-file rename ─────────────────────────────────────────────
t.test("rename: process → execute produces edits in both files", function()
local N = "rename: process → execute produces edits in both files"
local line, col = b.find_pos(buf, "$worker->process()")
if not t.ok(N, line, "couldn't find '$worker->process()'") then return end
-- Request rename but DON'T apply — just check the edit structure
local edit = lsp.rename(buf, line, col + 10, "execute")
if not t.ok(N, edit, "rename returned no edit") then return end
if not t.ok(N, edit.changes, "rename has no changes") then return end
-- Should have edits in at least the current file
local has_local = false
local has_cross_file = false
for uri, _ in pairs(edit.changes) do
if uri:find("inheritance.pl", 1, true) then has_local = true end
if uri:find("BaseWorker.pm", 1, true) then has_cross_file = true end
end
local ok = t.ok(N, has_local, "should have edits in inheritance.pl")
ok = t.ok(N, has_cross_file, "should have edits in BaseWorker.pm (cross-file)") and ok
if ok then t.pass(N) end
end)
-- ── diagnostics ──────────────────────────────────────────────────────
lsp.assert_no_diagnostics(t, buf)
-- ── done ─────────────────────────────────────────────────────────────
t.finish()