Skip to content

Commit 8031912

Browse files
authored
Ensure that tests that crash before running emit finish event (#3487)
1 parent 271e3e9 commit 8031912

File tree

5 files changed

+176
-107
lines changed

5 files changed

+176
-107
lines changed

lib/ruby_lsp/test_reporters/lsp_reporter.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# typed: strict
22
# frozen_string_literal: true
33

4+
require "English"
45
require "json"
56
require "socket"
67
require "singleton"
@@ -211,3 +212,11 @@ def send_message(method_name, **params)
211212
require "coverage"
212213
Coverage.start(:all)
213214
end
215+
216+
if RubyLsp::LspReporter.executed_under_test_runner?
217+
at_exit do
218+
# Regular finish events are registered per test reporter. However, if the test crashes during loading the files
219+
# (e.g.: a bad require), we need to ensure that the execution is finalized so that the extension is not left hanging
220+
RubyLsp::LspReporter.instance.at_exit if $ERROR_INFO
221+
end
222+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
require "minitest/autorun"
4+
5+
Minitest::Test.i_suck_and_my_tests_are_order_dependent!
6+
7+
class SampleTest < Minitest::Test
8+
raise "oh, no"
9+
10+
def test_that_passes
11+
assert_equal(1, 1)
12+
end
13+
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# typed: false
2+
# frozen_string_literal: true
3+
4+
require "test-unit"
5+
6+
class SampleTest < Test::Unit::TestCase
7+
raise "oh, no"
8+
9+
def test_that_passes
10+
assert_equal(1, 1)
11+
end
12+
end

test/test_reporters/minitest_reporter_test.rb

Lines changed: 70 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,56 +6,16 @@
66
module RubyLsp
77
class MinitestReporterTest < Minitest::Test
88
def test_minitest_output
9-
plugin_path = File.expand_path("lib/ruby_lsp/test_reporters/minitest_reporter.rb")
10-
uri = URI::Generic.from_path(path: "#{Dir.pwd}/test/fixtures/minitest_example.rb").to_s
11-
12-
server = TCPServer.new("localhost", 0)
13-
port = server.addr[1].to_s
14-
events = []
15-
socket = nil #: Socket?
16-
17-
receiver = Thread.new do
18-
socket = server.accept
19-
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
20-
21-
loop do
22-
headers = socket.gets("\r\n\r\n")
23-
break unless headers
24-
25-
content_length = headers[/Content-Length: (\d+)/i, 1].to_i
26-
raw_message = socket.read(content_length)
27-
28-
event = JSON.parse(raw_message)
29-
events << event
30-
31-
break if event["method"] == "finish"
32-
end
33-
end
34-
35-
_stdin, stdout, _stderr, wait_thr = Open3 #: as untyped
36-
.popen3(
37-
{
38-
"RUBYOPT" => "-rbundler/setup -r#{plugin_path}",
39-
"RUBY_LSP_TEST_RUNNER" => "run",
40-
"RUBY_LSP_REPORTER_PORT" => port,
41-
},
42-
"bundle",
43-
"exec",
44-
"ruby",
45-
"-Itest",
46-
"test/fixtures/minitest_example.rb",
47-
)
48-
49-
receiver.join
50-
wait_thr.join
51-
socket&.close
9+
uri = URI::Generic.from_path(path: "#{Dir.pwd}/test/fixtures/minitest_example.rb")
10+
string_uri = uri.to_s
11+
events = gather_events(uri)
5212

5313
expected = [
5414
{
5515
"method" => "start",
5616
"params" => {
5717
"id" => "SampleTest#test_that_fails",
58-
"uri" => uri,
18+
"uri" => string_uri,
5919
"line" => 14,
6020
},
6121
},
@@ -64,44 +24,44 @@ def test_minitest_output
6424
"params" => {
6525
"id" => "SampleTest#test_that_fails",
6626
"message" => "Expected: 1\n Actual: 2",
67-
"uri" => uri,
27+
"uri" => string_uri,
6828
},
6929
},
7030
{
7131
"method" => "start",
7232
"params" => {
7333
"id" => "SampleTest#test_that_is_pending",
74-
"uri" => uri,
34+
"uri" => string_uri,
7535
"line" => 18,
7636
},
7737
},
7838
{
7939
"method" => "skip",
8040
"params" => {
8141
"id" => "SampleTest#test_that_is_pending",
82-
"uri" => uri,
42+
"uri" => string_uri,
8343
},
8444
},
8545
{
8646
"method" => "start",
8747
"params" => {
8848
"id" => "SampleTest#test_that_passes",
89-
"uri" => uri,
49+
"uri" => string_uri,
9050
"line" => 9,
9151
},
9252
},
9353
{
9454
"method" => "pass",
9555
"params" => {
9656
"id" => "SampleTest#test_that_passes",
97-
"uri" => uri,
57+
"uri" => string_uri,
9858
},
9959
},
10060
{
10161
"method" => "start",
10262
"params" => {
10363
"id" => "SampleTest#test_that_raises",
104-
"uri" => uri,
64+
"uri" => string_uri,
10565
"line" => 22,
10666
},
10767
},
@@ -110,22 +70,22 @@ def test_minitest_output
11070
"params" => {
11171
"id" => "SampleTest#test_that_raises",
11272
"message" => "RuntimeError: oops\n test/fixtures/minitest_example.rb:24:in #{error_location}",
113-
"uri" => uri,
73+
"uri" => string_uri,
11474
},
11575
},
11676
{
11777
"method" => "start",
11878
"params" => {
11979
"id" => "SampleTest#test_with_output",
120-
"uri" => uri,
80+
"uri" => string_uri,
12181
"line" => 26,
12282
},
12383
},
12484
{
12585
"method" => "pass",
12686
"params" => {
12787
"id" => "SampleTest#test_with_output",
128-
"uri" => uri,
88+
"uri" => string_uri,
12989
},
13090
},
13191
{
@@ -135,11 +95,67 @@ def test_minitest_output
13595
]
13696

13797
assert_equal(expected, events)
138-
refute_empty(stdout.read)
98+
end
99+
100+
def test_crashing_example
101+
uri = URI::Generic.from_path(path: "#{Dir.pwd}/test/fixtures/minitest_crash_example.rb")
102+
events = gather_events(uri, output: :stderr)
103+
104+
expected = [{ "method" => "finish", "params" => {} }]
105+
assert_equal(expected, events)
139106
end
140107

141108
private
142109

110+
#: (URI::Generic, ?output: Symbol) -> Array[Hash[untyped, untyped]]
111+
def gather_events(uri, output: :stdout)
112+
plugin_path = File.expand_path("lib/ruby_lsp/test_reporters/minitest_reporter.rb")
113+
114+
server = TCPServer.new("localhost", 0)
115+
port = server.addr[1].to_s
116+
events = []
117+
socket = nil #: Socket?
118+
119+
receiver = Thread.new do
120+
socket = server.accept
121+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
122+
123+
loop do
124+
headers = socket.gets("\r\n\r\n")
125+
break unless headers
126+
127+
content_length = headers[/Content-Length: (\d+)/i, 1].to_i
128+
raw_message = socket.read(content_length)
129+
130+
event = JSON.parse(raw_message)
131+
events << event
132+
133+
break if event["method"] == "finish"
134+
end
135+
end
136+
137+
_stdin, stdout, stderr, wait_thr = Open3.popen3(
138+
{
139+
"RUBYOPT" => "-rbundler/setup -r#{plugin_path}",
140+
"RUBY_LSP_TEST_RUNNER" => "run",
141+
"RUBY_LSP_REPORTER_PORT" => port,
142+
"RUBY_LSP_ENV" => "production",
143+
},
144+
"bundle",
145+
"exec",
146+
"ruby",
147+
"-Itest",
148+
uri.to_standardized_path, #: as !nil
149+
)
150+
151+
receiver.join
152+
wait_thr.join
153+
socket&.close
154+
io = output == :stdout ? stdout : stderr
155+
refute_empty(io.read)
156+
events
157+
end
158+
143159
def error_location
144160
ruby_version = Gem::Version.new(RUBY_VERSION)
145161

0 commit comments

Comments
 (0)