From e691ab5250e8e46a92fc02d1f26064063d7854b5 Mon Sep 17 00:00:00 2001 From: st0012 Date: Fri, 11 Mar 2022 22:07:06 +0000 Subject: [PATCH 1/5] Implement assert_locals_result matcher --- test/protocol/break_test.rb | 12 ++++++++++-- test/support/protocol_utils.rb | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/test/protocol/break_test.rb b/test/protocol/break_test.rb index 1a65bdf04..66d1d93c9 100644 --- a/test/protocol/break_test.rb +++ b/test/protocol/break_test.rb @@ -15,12 +15,20 @@ class BreakTest1 < TestCase 8| bar = Bar.new 9| end RUBY - + def test_break_stops_at_correct_place run_protocol_scenario PROGRAM do req_add_breakpoint 5 req_continue assert_line_num 5 + + assert_locals_result( + [ + { name: "%self", value: "Foo::Bar", type: "Class" }, + { name: "_return", value: "hello", type: "String" } + ] + ) + req_add_breakpoint 8 req_continue assert_line_num 8 @@ -52,7 +60,7 @@ def bar2 end RUBY end - + def test_break_stops_at_the_extra_file with_extra_tempfile do |extra_file| run_protocol_scenario(program(extra_file.path), cdp: false) do diff --git a/test/support/protocol_utils.rb b/test/support/protocol_utils.rb index 7648504e8..1bf874f64 100644 --- a/test/support/protocol_utils.rb +++ b/test/support/protocol_utils.rb @@ -203,6 +203,41 @@ def assert_reattach end end + def assert_locals_result expected, frame_idx: 0 + case ENV['RUBY_DEBUG_TEST_UI'] + when 'vscode' + # get frameId + send_request 'stackTrace', + threadId: 1, + startFrame: 0, + levels: 20 + res = find_crt_dap_response + f_id = res.dig(:body, :stackFrames, frame_idx, :id) + + # get variablesReference + send_request 'scopes', frameId: f_id + res = find_crt_dap_response + locals_scope = res.dig(:body, :scopes).find { |d| d[:presentationHint] == "locals" } + locals_reference = locals_scope[:variablesReference] + + # get variables + send_request 'variables', variablesReference: locals_reference + res = find_crt_dap_response + + expected.each do |exp| + if exp[:type] == "String" + exp[:value] = exp[:value].inspect + end + end + + actual_locals = res.dig(:body, :variables).map { |loc| { name: loc[:name], value: loc[:value], type: loc[:type] } } + + assert_equal(expected, actual_locals) + when 'chrome' + omit "locals assertion from CDP protocol is not supported yet" + end + end + def assert_hover_result expected, expression: nil, frame_idx: 0 case ENV['RUBY_DEBUG_TEST_UI'] when 'vscode' From 9cee8fba1ef75fc557eeb6ad48da2b4ceb17e3c0 Mon Sep 17 00:00:00 2001 From: st0012 Date: Sat, 12 Mar 2022 12:50:40 +0000 Subject: [PATCH 2/5] Support assert_locals_result for Chrome as well --- test/support/protocol_utils.rb | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/test/support/protocol_utils.rb b/test/support/protocol_utils.rb index 1bf874f64..435a451b7 100644 --- a/test/support/protocol_utils.rb +++ b/test/support/protocol_utils.rb @@ -217,12 +217,15 @@ def assert_locals_result expected, frame_idx: 0 # get variablesReference send_request 'scopes', frameId: f_id res = find_crt_dap_response + assert_dap_response :ScopesResponse, res + locals_scope = res.dig(:body, :scopes).find { |d| d[:presentationHint] == "locals" } locals_reference = locals_scope[:variablesReference] # get variables send_request 'variables', variablesReference: locals_reference res = find_crt_dap_response + assert_dap_response :VariablesResponse, res expected.each do |exp| if exp[:type] == "String" @@ -231,11 +234,25 @@ def assert_locals_result expected, frame_idx: 0 end actual_locals = res.dig(:body, :variables).map { |loc| { name: loc[:name], value: loc[:value], type: loc[:type] } } - - assert_equal(expected, actual_locals) when 'chrome' - omit "locals assertion from CDP protocol is not supported yet" + current_frame = @crt_frames.first + locals_scope = current_frame[:scopeChain].find { |f| f[:type] == "local" } + object_id = locals_scope.dig(:object, :objectId) + + send_request "Runtime.getProperties", objectId: object_id + res = find_crt_cdp_response + assert_cdp_response 'Runtime.getProperties', res + + actual_locals = res.dig(:result, :result).map do |loc| + type = loc.dig(:value, :className) || loc.dig(:value, :type).capitalize # TODO: sync this with get_ruby_type + + { name: loc[:name], value: loc.dig(:value, :description), type: type } + end end + + failure_msg = FailureMessage.new{create_protocol_message "result:\n#{JSON.pretty_generate res}"} + + assert_equal(expected.sort_by { |h| h[:name] }, actual_locals.sort_by { |h| h[:name] }, failure_msg) end def assert_hover_result expected, expression: nil, frame_idx: 0 From c2a4d71228932f3affa04649eb2a36bde3ce985e Mon Sep 17 00:00:00 2001 From: st0012 Date: Sat, 12 Mar 2022 13:01:54 +0000 Subject: [PATCH 3/5] Apply locals check to control flow tests --- test/protocol/break_test.rb | 7 +++++++ test/protocol/next_test.rb | 29 ++++++++++++++++++++++++++++- test/protocol/step_back_test.rb | 19 +++++++++++++++++-- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/test/protocol/break_test.rb b/test/protocol/break_test.rb index 66d1d93c9..68bc93fbb 100644 --- a/test/protocol/break_test.rb +++ b/test/protocol/break_test.rb @@ -32,6 +32,13 @@ def test_break_stops_at_correct_place req_add_breakpoint 8 req_continue assert_line_num 8 + + assert_locals_result( + [ + { name: "%self", value: "Foo", type: "Module" }, + { name: "bar", value: "nil", type: "NilClass" } + ] + ) req_terminate_debuggee end end diff --git a/test/protocol/next_test.rb b/test/protocol/next_test.rb index 6faf8ced0..9b3f447a6 100644 --- a/test/protocol/next_test.rb +++ b/test/protocol/next_test.rb @@ -15,17 +15,44 @@ class NextTest < TestCase 8| bar = Bar.new 9| end RUBY - + def test_next_goes_to_the_next_statement run_protocol_scenario PROGRAM do req_next assert_line_num 2 + + assert_locals_result( + [ + { name: "%self", value: "Foo", type: "Module" }, + { name: "bar", value: "nil", type: "NilClass" } + ] + ) req_next assert_line_num 3 + + assert_locals_result( + [ + { name: "%self", value: "Foo::Bar", type: "Class" }, + ] + ) req_next assert_line_num 7 + + assert_locals_result( + [ + { name: "%self", value: "Foo", type: "Module" }, + { name: "bar", value: "nil", type: "NilClass" } + ] + ) req_next assert_line_num 8 + + assert_locals_result( + [ + { name: "%self", value: "Foo", type: "Module" }, + { name: "bar", value: "nil", type: "NilClass" } + ] + ) req_terminate_debuggee end end diff --git a/test/protocol/step_back_test.rb b/test/protocol/step_back_test.rb index a08d289b5..79d941d98 100644 --- a/test/protocol/step_back_test.rb +++ b/test/protocol/step_back_test.rb @@ -6,7 +6,7 @@ module DEBUGGER__ class StepBackTest < TestCase PROGRAM = <<~RUBY 1| binding.b do: 'record on' - 2| + 2| 3| module Foo 4| class Bar 5| def self.a @@ -17,17 +17,32 @@ class StepBackTest < TestCase 10| bar = Bar.new 11| end RUBY - + def test_step_back_goes_back_to_the_previous_statement run_protocol_scenario PROGRAM, cdp: false do req_add_breakpoint 9 req_continue req_step_back assert_line_num 9 + assert_locals_result( + [ + { name: "%self", value: "Foo", type: "Module" }, + { name: "bar", value: "nil", type: "NilClass" } + ] + ) req_step_back assert_line_num 5 + assert_locals_result([ + { name: "%self", value: "Foo::Bar", type: "Class" } + ]) req_step_back assert_line_num 4 + assert_locals_result( + [ + { name: "%self", value: "Foo", type: "Module" }, + { name: "bar", value: "nil", type: "NilClass" } + ] + ) req_terminate_debuggee end end From cfb057f7dfe266dda71b71141400ee00e733f8f8 Mon Sep 17 00:00:00 2001 From: st0012 Date: Wed, 23 Mar 2022 13:30:13 +0000 Subject: [PATCH 4/5] Mention assert_locals_result in the contribution guideline --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b7d7f8b36..26121b2db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -364,6 +364,14 @@ Passes if result of `expression` is equal to `expected`. Passes if `expected` is equal to the location where debugger stops. +- assert_locals_result(expected) + +Passes if all of `expected` local variable entries match the ones returned by debugger. + +An variable entry looks like this: `{ name: "bar", value: "nil", type: "NilClass" }`. + +Please note that both `value` and `type` need to be strings. + ## To Update README From c7f0f540a146598b3ebd7c653340178616c5271b Mon Sep 17 00:00:00 2001 From: st0012 Date: Mon, 28 Mar 2022 10:50:01 +0100 Subject: [PATCH 5/5] Support matching object instance with regexp --- test/protocol/break_test.rb | 6 +++--- test/support/protocol_utils.rb | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/test/protocol/break_test.rb b/test/protocol/break_test.rb index 68bc93fbb..f3cfe6d83 100644 --- a/test/protocol/break_test.rb +++ b/test/protocol/break_test.rb @@ -29,14 +29,14 @@ def test_break_stops_at_correct_place ] ) - req_add_breakpoint 8 + req_add_breakpoint 9 req_continue - assert_line_num 8 + assert_line_num 9 assert_locals_result( [ { name: "%self", value: "Foo", type: "Module" }, - { name: "bar", value: "nil", type: "NilClass" } + { name: "bar", value: /#