Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion test/dap/boot_config_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class BootConfigTest1638611290 < TestCase
def test_boot_configuration_works_correctly
run_dap_scenario PROGRAM do
[
*CFG_DAP,
*INITIALIZE_MSG,
{
seq: 1,
type: "response",
Expand Down
2 changes: 1 addition & 1 deletion test/dap/break_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class BreakTest1638674577 < TestCase
def test_break_works_correctly
run_dap_scenario PROGRAM do
[
*CFG_DAP,
*INITIALIZE_MSG,
{
seq: 7,
type: "event",
Expand Down
2 changes: 1 addition & 1 deletion test/dap/detach_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class DetachTest1639218122 < TestCase
def test_1639218122
run_dap_scenario PROGRAM do
[
*CFG_DAP,
*INITIALIZE_MSG,
{
seq: 7,
type: "event",
Expand Down
2 changes: 1 addition & 1 deletion test/dap/finish_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class FinishTest1638674323 < TestCase
def test_finish_works_correctly
run_dap_scenario PROGRAM do
[
*CFG_DAP,
*INITIALIZE_MSG,
{
seq: 7,
type: "event",
Expand Down
2 changes: 1 addition & 1 deletion test/dap/hover_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class HoverTest1638791703 < TestCase
def test_hover_works_correctly
run_dap_scenario PROGRAM do
[
*CFG_DAP,
*INITIALIZE_MSG,
{
seq: 7,
type: "event",
Expand Down
2 changes: 1 addition & 1 deletion test/dap/next_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class NextTest1638676688 < TestCase
def test_next_works_correctly
run_dap_scenario PROGRAM do
[
*CFG_DAP,
*INITIALIZE_MSG,
{
seq: 7,
type: "event",
Expand Down
2 changes: 1 addition & 1 deletion test/dap/step_back_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class StepBackTest1638698086 < TestCase
def test_step_back_works_correctly
run_dap_scenario PROGRAM do
[
*CFG_DAP,
*INITIALIZE_MSG,
{
seq: 7,
type: "event",
Expand Down
2 changes: 1 addition & 1 deletion test/dap/step_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class StepTest1638676609 < TestCase
def test_step_works_correctly
run_dap_scenario PROGRAM do
[
*CFG_DAP,
*INITIALIZE_MSG,
{
seq: 7,
type: "event",
Expand Down
299 changes: 299 additions & 0 deletions test/support/dap_utils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
# frozen_string_literal: true

module DEBUGGER__
module DAP_TestUtils
class RetryBecauseCantRead < Exception
end

def recv_request sock, backlog
case sock.gets
when /Content-Length: (\d+)/
b = sock.read(2)
raise b.inspect unless b == "\r\n"

l = sock.read $1.to_i
backlog << "V<D #{l}"
JSON.parse l, symbolize_names: true
when nil
nil
when /out/, /input/
recv_request sock
else
raise "unrecognized line: #{header} (#{l.nil?} bytes)"
end
rescue RetryBecauseCantRead
retry
end

def create_protocol_msg msgs, remote_info, fail_msg
all_protocol_msg = <<~DEBUGGER_MSG.chomp
-------------------------
| All Protocol Messages |
-------------------------

#{msgs.join("\n")}
DEBUGGER_MSG

last_msg = msgs.reverse[0..3].map{|m|
h = m.sub /(D|V)(<|>)(D|V)\s/, ''
JSON.pretty_generate(JSON.parse h)
}.reverse.join("\n")

last_protocol_msg = <<~DEBUGGER_MSG.chomp
--------------------------
| Last Protocol Messages |
--------------------------

#{last_msg}
DEBUGGER_MSG

debuggee_msg =
<<~DEBUGGEE_MSG.chomp
--------------------
| Debuggee Session |
--------------------

> #{remote_info.debuggee_backlog.join('> ')}
DEBUGGEE_MSG

failure_msg = <<~FAILURE_MSG.chomp
-------------------
| Failure Message |
-------------------

#{fail_msg}
FAILURE_MSG

<<~MSG.chomp
#{all_protocol_msg}

#{last_protocol_msg}

#{debuggee_msg}

#{failure_msg}
MSG
end

INITIALIZE_MSG = [
{
seq: 1,
command: "initialize",
arguments: {
clientID: "vscode",
clientName: "Visual Studio Code",
adapterID: "rdbg",
pathFormat: "path",
linesStartAt1: true,
columnsStartAt1: true,
supportsVariableType: true,
supportsVariablePaging: true,
supportsRunInTerminalRequest: true,
locale: "en-us",
supportsProgressReporting: true,
supportsInvalidatedEvent: true,
supportsMemoryReferences: true
},
type: "request"
},
{
seq: 2,
command: "attach",
arguments: {
type: "rdbg",
name: "Attach with rdbg",
request: "attach",
rdbgPath: File.expand_path('../../exe/rdbg', __dir__),
debugPort: "/var/folders/kv/w1k6nh1x5fl7vx47b2pd005w0000gn/T/ruby-debug-sock-501/ruby-debug-naotto-8845",
autoAttach: true,
__sessionId: "141d9c79-3669-43ec-ac1f-e62598c5a65a"
},
type: "request"
},
{
seq: 3,
command: "setFunctionBreakpoints",
arguments: {
breakpoints: [

]
},
type: "request"
},
{
seq: 4,
command: "setExceptionBreakpoints",
arguments: {
filters: [

],
filterOptions: [
{
filterId: "RuntimeError"
}
]
},
type: "request"
},
{
seq: 5,
command: "configurationDone",
type: "request"
}
]

DAP_TestInfo = Struct.new(:res_backlog, :backlog, :failed_process)

class Detach < StandardError
end

def connect_to_dap_server path, test_info
sock = Socket.unix path
reader_thread = Thread.new(sock, test_info) do |s, info|
while res = recv_request(s, info.backlog)
info.res_backlog << res
end
rescue Detach
end
sleep 0.001 while reader_thread.status != 'sleep'
reader_thread.run
[sock, reader_thread]
end

TIMEOUT_SEC = (ENV['RUBY_DEBUG_TIMEOUT_SEC'] || 10).to_i
dt = ENV['RUBY_DEBUG_DAP_TEST']
DAP_TEST = dt == 'true' || dt == '1'

def run_dap_scenario program, &msgs
pend 'Tests for DAP were skipped. You can enable them with RUBY_DEBUG_DAP_TEST=1.' unless DAP_TEST

begin
write_temp_file(strip_line_num(program))

test_info = DAP_TestInfo.new([], [])
remote_info = setup_unix_doman_socket_remote_debuggee
sock = nil
reader_thread = nil
res_log = test_info.res_backlog
backlog = test_info.backlog
target_msg = nil

msgs.call.each{|msg|
case msg[:type]
when 'request'
if msg[:command] == 'initialize'
sock, reader_thread = connect_to_dap_server remote_info.sock_path, test_info
end
str = JSON.dump(msg)
sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
backlog << "V>D #{str}"
when 'response'
result = nil
target_msg = msg
Timeout.timeout(TIMEOUT_SEC) do
loop do
res_log.each{|r|
if r[:request_seq] == msg[:request_seq]
result = r
break
end
}
break unless result.nil?

sleep 0.01
end
end

msg.delete :seq
hash = ProtocolParser.new.parse msg
hash.each{|r|
k, v = r
case v
when Regexp
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}"}
else
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}"}
end
}
if msg[:command] == 'disconnect'
res_log.clear
reader_thread.raise Detach
sock.close
end
when 'event'
result = nil
target_msg = msg
Timeout.timeout(TIMEOUT_SEC) do
loop do
res_log.each{|r|
if r[:event] == msg[:event]
result = r
break
end
}
break unless result.nil?

sleep 0.01
end
end

msg.delete :seq
hash = ProtocolParser.new.parse msg
hash.each{|r|
k, v = r
case v
when Regexp
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}"}
else
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}"}
end
}
res_log.delete result
end
}
rescue Timeout::Error
flunk create_protocol_msg backlog, remote_info, "TIMEOUT ERROR (#{TIMEOUT_SEC} sec) while waiting for the following response.\n#{JSON.pretty_generate target_msg}"
ensure
reader_thread.kill
sock.close
kill_safely remote_info.pid, :debuggee, test_info
if test_info.failed_process
flunk create_protocol_msg backlog, remote_info, "Expected the debuggee program to finish"
end
end
end
end
end

class ProtocolParser
def initialize
@result = []
@keys = []
end

def parse objs
objs.each{|k, v|
parse_ k, v
@keys.pop
}
@result
end

def parse_ k, v
@keys << k
case v
when Array
v.each.with_index{|v, i|
parse_ i, v
@keys.pop
}
when Hash
v.each{|k, v|
parse_ k, v
@keys.pop
}
else
@result << [@keys.dup, v]
end
end
end
2 changes: 2 additions & 0 deletions test/support/test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
require 'securerandom'

require_relative 'utils'
require_relative 'dap_utils'
require_relative 'assertions'

module DEBUGGER__
class TestCase < Test::Unit::TestCase
include TestUtils
include DAP_TestUtils
include AssertionHelpers

def setup
Expand Down
Loading