Skip to content

Commit 44f685c

Browse files
author
ono-max
committed
Move utility methods for DAP to the new file
1 parent 5166893 commit 44f685c

File tree

3 files changed

+295
-287
lines changed

3 files changed

+295
-287
lines changed

test/support/dap_utils.rb

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
# frozen_string_literal: true
2+
3+
module DEBUGGER__
4+
module DAP_TestUtils
5+
class RetryBecauseCantRead < Exception
6+
end
7+
8+
def recv_request sock, backlog
9+
case sock.gets
10+
when /Content-Length: (\d+)/
11+
b = sock.read(2)
12+
raise b.inspect unless b == "\r\n"
13+
14+
l = sock.read $1.to_i
15+
backlog << "V<D #{l}"
16+
JSON.parse l, symbolize_names: true
17+
when nil
18+
nil
19+
when /out/, /input/
20+
recv_request sock
21+
else
22+
raise "unrecognized line: #{header} (#{l.nil?} bytes)"
23+
end
24+
rescue RetryBecauseCantRead
25+
retry
26+
end
27+
28+
def create_protocol_msg msgs, remote_info, fail_msg
29+
all_protocol_msg = <<~DEBUGGER_MSG.chomp
30+
-------------------------
31+
| All Protocol Messages |
32+
-------------------------
33+
34+
#{msgs.join("\n")}
35+
DEBUGGER_MSG
36+
37+
last_msg = msgs.reverse[0..3].map{|m|
38+
h = m.sub /(D|V)(<|>)(D|V)\s/, ''
39+
JSON.pretty_generate(JSON.parse h)
40+
}.reverse.join("\n")
41+
42+
last_protocol_msg = <<~DEBUGGER_MSG.chomp
43+
--------------------------
44+
| Last Protocol Messages |
45+
--------------------------
46+
47+
#{last_msg}
48+
DEBUGGER_MSG
49+
50+
debuggee_msg =
51+
<<~DEBUGGEE_MSG.chomp
52+
--------------------
53+
| Debuggee Session |
54+
--------------------
55+
56+
> #{remote_info.debuggee_backlog.join('> ')}
57+
DEBUGGEE_MSG
58+
59+
failure_msg = <<~FAILURE_MSG.chomp
60+
-------------------
61+
| Failure Message |
62+
-------------------
63+
64+
#{fail_msg}
65+
FAILURE_MSG
66+
67+
<<~MSG.chomp
68+
#{all_protocol_msg}
69+
70+
#{last_protocol_msg}
71+
72+
#{debuggee_msg}
73+
74+
#{failure_msg}
75+
MSG
76+
end
77+
78+
CFG_DAP = [
79+
{
80+
seq: 1,
81+
command: "initialize",
82+
arguments: {
83+
clientID: "vscode",
84+
clientName: "Visual Studio Code",
85+
adapterID: "rdbg",
86+
pathFormat: "path",
87+
linesStartAt1: true,
88+
columnsStartAt1: true,
89+
supportsVariableType: true,
90+
supportsVariablePaging: true,
91+
supportsRunInTerminalRequest: true,
92+
locale: "en-us",
93+
supportsProgressReporting: true,
94+
supportsInvalidatedEvent: true,
95+
supportsMemoryReferences: true
96+
},
97+
type: "request"
98+
},
99+
{
100+
seq: 2,
101+
command: "attach",
102+
arguments: {
103+
type: "rdbg",
104+
name: "Attach with rdbg",
105+
request: "attach",
106+
rdbgPath: File.expand_path('../../exe/rdbg', __dir__),
107+
debugPort: "/var/folders/kv/w1k6nh1x5fl7vx47b2pd005w0000gn/T/ruby-debug-sock-501/ruby-debug-naotto-8845",
108+
autoAttach: true,
109+
__sessionId: "141d9c79-3669-43ec-ac1f-e62598c5a65a"
110+
},
111+
type: "request"
112+
},
113+
{
114+
seq: 3,
115+
command: "setFunctionBreakpoints",
116+
arguments: {
117+
breakpoints: [
118+
119+
]
120+
},
121+
type: "request"
122+
},
123+
{
124+
seq: 4,
125+
command: "setExceptionBreakpoints",
126+
arguments: {
127+
filters: [
128+
129+
],
130+
filterOptions: [
131+
{
132+
filterId: "RuntimeError"
133+
}
134+
]
135+
},
136+
type: "request"
137+
},
138+
{
139+
seq: 5,
140+
command: "configurationDone",
141+
type: "request"
142+
}
143+
]
144+
145+
DAP_TestInfo = Struct.new(:res_backlog, :backlog, :failed_process)
146+
147+
class Detach < StandardError
148+
end
149+
150+
def connect_to_dap_server path, test_info
151+
sock = Socket.unix path
152+
reader_thread = Thread.new(sock, test_info) do |s, info|
153+
while res = recv_request(s, info.backlog)
154+
info.res_backlog << res
155+
end
156+
rescue Detach
157+
end
158+
sleep 0.001 while reader_thread.status != 'sleep'
159+
reader_thread.run
160+
[sock, reader_thread]
161+
end
162+
163+
TIMEOUT_SEC = (ENV['RUBY_DEBUG_TIMEOUT_SEC'] || 10).to_i
164+
165+
def run_dap_scenario program, &msgs
166+
write_temp_file(strip_line_num(program))
167+
168+
test_info = DAP_TestInfo.new([], [])
169+
remote_info = setup_unix_doman_socket_remote_debuggee
170+
sock = nil
171+
reader_thread = nil
172+
res_log = test_info.res_backlog
173+
backlog = test_info.backlog
174+
target_msg = nil
175+
176+
msgs.call.each{|msg|
177+
case msg[:type]
178+
when 'request'
179+
if msg[:command] == 'initialize'
180+
sock, reader_thread = connect_to_dap_server remote_info.sock_path, test_info
181+
end
182+
str = JSON.dump(msg)
183+
sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
184+
backlog << "V>D #{str}"
185+
when 'response'
186+
result = nil
187+
target_msg = msg
188+
Timeout.timeout(TIMEOUT_SEC) do
189+
loop do
190+
res_log.each{|r|
191+
if r[:request_seq] == msg[:request_seq]
192+
result = r
193+
break
194+
end
195+
}
196+
break unless result.nil?
197+
198+
sleep 0.01
199+
end
200+
end
201+
202+
msg.delete :seq
203+
hash = ProtocolParser.new.parse msg
204+
hash.each{|r|
205+
k, v = r
206+
case v
207+
when Regexp
208+
assert_match v, result.dig(*k).to_s, FailureMessage.new{create_protocol_msg backlog, remote_info, "expected:\n#{JSON.pretty_generate msg}\n\nresult:\n#{JSON.pretty_generate result}"}
209+
else
210+
assert_equal v, result.dig(*k), FailureMessage.new{create_protocol_msg backlog, remote_info, "expected:\n#{JSON.pretty_generate msg}\n\nresult:\n#{JSON.pretty_generate result}"}
211+
end
212+
}
213+
if msg[:command] == 'disconnect'
214+
res_log.clear
215+
reader_thread.raise Detach
216+
sock.close
217+
end
218+
when 'event'
219+
result = nil
220+
target_msg = msg
221+
Timeout.timeout(TIMEOUT_SEC) do
222+
loop do
223+
res_log.each{|r|
224+
if r[:event] == msg[:event]
225+
result = r
226+
break
227+
end
228+
}
229+
break unless result.nil?
230+
231+
sleep 0.01
232+
end
233+
end
234+
235+
msg.delete :seq
236+
hash = ProtocolParser.new.parse msg
237+
hash.each{|r|
238+
k, v = r
239+
case v
240+
when Regexp
241+
assert_match v, result.dig(*k).to_s, FailureMessage.new{create_protocol_msg backlog, remote_info, "expected:\n#{JSON.pretty_generate msg}\n\nresult:\n#{JSON.pretty_generate result}"}
242+
else
243+
assert_equal v, result.dig(*k), FailureMessage.new{create_protocol_msg backlog, remote_info, "expected:\n#{JSON.pretty_generate msg}\n\nresult:\n#{JSON.pretty_generate result}"}
244+
end
245+
}
246+
res_log.delete result
247+
end
248+
}
249+
rescue Timeout::Error
250+
flunk create_protocol_msg backlog, remote_info, "TIMEOUT ERROR (#{TIMEOUT_SEC} sec) while waiting for the following response.\n#{JSON.pretty_generate target_msg}"
251+
ensure
252+
reader_thread.kill
253+
sock.close
254+
kill_safely remote_info.pid, :debuggee, test_info
255+
if test_info.failed_process
256+
flunk create_protocol_msg backlog, remote_info, "Expected the debuggee program to finish"
257+
end
258+
end
259+
end
260+
end
261+
262+
class ProtocolParser
263+
def initialize
264+
@result = []
265+
@keys = []
266+
end
267+
268+
def parse objs
269+
objs.each{|k, v|
270+
parse_ k, v
271+
@keys.pop
272+
}
273+
@result
274+
end
275+
276+
def parse_ k, v
277+
@keys << k
278+
case v
279+
when Array
280+
v.each.with_index{|v, i|
281+
parse_ i, v
282+
@keys.pop
283+
}
284+
when Hash
285+
v.each{|k, v|
286+
parse_ k, v
287+
@keys.pop
288+
}
289+
else
290+
@result << [@keys.dup, v]
291+
end
292+
end
293+
end

test/support/test_case.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
require 'securerandom'
66

77
require_relative 'utils'
8+
require_relative 'dap_utils'
89
require_relative 'assertions'
910

1011
module DEBUGGER__
1112
class TestCase < Test::Unit::TestCase
1213
include TestUtils
14+
include DAP_TestUtils
1315
include AssertionHelpers
1416

1517
def setup

0 commit comments

Comments
 (0)