debugger-xml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/.gitignore +20 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +24 -0
  5. data/Rakefile +10 -0
  6. data/bin/rdebug-ide +90 -0
  7. data/debugger-xml.gemspec +25 -0
  8. data/lib/debugger/printers/texts/xml.yml +121 -0
  9. data/lib/debugger/printers/xml.rb +195 -0
  10. data/lib/debugger/xml.rb +3 -0
  11. data/lib/debugger/xml/extensions/commands/edit.rb +13 -0
  12. data/lib/debugger/xml/extensions/commands/frame.rb +14 -0
  13. data/lib/debugger/xml/extensions/commands/help.rb +13 -0
  14. data/lib/debugger/xml/extensions/commands/info.rb +13 -0
  15. data/lib/debugger/xml/extensions/commands/irb.rb +13 -0
  16. data/lib/debugger/xml/extensions/commands/kill.rb +13 -0
  17. data/lib/debugger/xml/extensions/commands/tmate.rb +13 -0
  18. data/lib/debugger/xml/extensions/commands/trace.rb +13 -0
  19. data/lib/debugger/xml/extensions/commands/variables.rb +16 -0
  20. data/lib/debugger/xml/extensions/debugger.rb +28 -0
  21. data/lib/debugger/xml/extensions/processor.rb +9 -0
  22. data/lib/debugger/xml/ide_processor.rb +149 -0
  23. data/lib/debugger/xml/interface.rb +65 -0
  24. data/lib/debugger/xml/version.rb +5 -0
  25. data/test/breakpoints_test.rb +190 -0
  26. data/test/conditions_test.rb +32 -0
  27. data/test/continue_test.rb +12 -0
  28. data/test/display_test.rb +25 -0
  29. data/test/edit_test.rb +12 -0
  30. data/test/eval_test.rb +20 -0
  31. data/test/examples/breakpoint1.rb +15 -0
  32. data/test/examples/breakpoint2.rb +7 -0
  33. data/test/examples/conditions.rb +4 -0
  34. data/test/examples/continue.rb +4 -0
  35. data/test/examples/display.rb +5 -0
  36. data/test/examples/edit.rb +3 -0
  37. data/test/examples/eval.rb +4 -0
  38. data/test/examples/frame.rb +31 -0
  39. data/test/examples/help.rb +2 -0
  40. data/test/examples/info.rb +48 -0
  41. data/test/examples/irb.rb +6 -0
  42. data/test/examples/jump.rb +14 -0
  43. data/test/examples/kill.rb +2 -0
  44. data/test/examples/method.rb +15 -0
  45. data/test/examples/reload.rb +6 -0
  46. data/test/examples/restart.rb +6 -0
  47. data/test/examples/set.rb +3 -0
  48. data/test/examples/stepping.rb +21 -0
  49. data/test/examples/thread.rb +32 -0
  50. data/test/examples/tmate.rb +10 -0
  51. data/test/examples/trace.rb +7 -0
  52. data/test/examples/variables.rb +26 -0
  53. data/test/examples/variables_xml.rb +31 -0
  54. data/test/frame_test.rb +29 -0
  55. data/test/help_test.rb +13 -0
  56. data/test/ide_control_command_processor_test.rb +62 -0
  57. data/test/ide_processor_test.rb +118 -0
  58. data/test/info_test.rb +12 -0
  59. data/test/irb_test.rb +12 -0
  60. data/test/jump_test.rb +22 -0
  61. data/test/kill_test.rb +13 -0
  62. data/test/method_test.rb +36 -0
  63. data/test/printers/xml_test.rb +193 -0
  64. data/test/reload_test.rb +14 -0
  65. data/test/restart_test.rb +50 -0
  66. data/test/set_test.rb +12 -0
  67. data/test/stepping_test.rb +15 -0
  68. data/test/test_helper.rb +6 -0
  69. data/test/thread_test.rb +20 -0
  70. data/test/tmate_test.rb +15 -0
  71. data/test/trace_test.rb +12 -0
  72. data/test/variables_test.rb +84 -0
  73. metadata +253 -0
@@ -0,0 +1,29 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Frame Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must print current stack frame when without arguments" do
8
+ enter 'break 25', 'cont', 'up', 'frame'
9
+ debug_file('frame')
10
+ check_output_includes %{<frame no="0" file="#{fullpath('frame')}" line="25" current="false"/>}
11
+ end
12
+
13
+ describe "full path settings" do
14
+ temporary_change_hash_value(Debugger::Command.settings, :full_path, false)
15
+
16
+ it "must display current backtrace with full path = true" do
17
+ enter 'set fullpath', 'break 25', 'cont', 'where'
18
+ debug_file('frame')
19
+ check_output_includes(Regexp.new(
20
+ "<frames>" +
21
+ %{<frame no="0" file="#{fullpath('frame')}" line="25" current="true"/>} +
22
+ %{<frame no="1" file="#{fullpath('frame')}" line="21" current="false"/>} +
23
+ %{<frame no="2" file="#{fullpath('frame')}" line="17" current="false"/>} +
24
+ %{<frame no="3" file="#{fullpath('frame')}" line="14" current="false"/>.*} +
25
+ "</frames>",
26
+ Regexp::MULTILINE))
27
+ end
28
+ end
29
+ end
data/test/help_test.rb ADDED
@@ -0,0 +1,13 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Help Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must be unsupported for XML printer" do
8
+ enter 'help'
9
+ debug_file 'help'
10
+ check_output_includes "<error>Unsupported command 'help'</error>", interface.error_queue
11
+ end
12
+
13
+ end
@@ -0,0 +1,62 @@
1
+ require_relative 'test_helper'
2
+ require 'debugger/xml/ide_processor'
3
+
4
+ describe Debugger::Xml::IdeControlCommandProcessor do
5
+ include TestDsl
6
+
7
+ let(:klass) { Debugger::Xml::IdeControlCommandProcessor }
8
+ let(:interface) { Debugger.handler.interface }
9
+ let(:file) { fullpath('jump') }
10
+ let(:context) { stub(frame_binding: stub, stop_reason: nil, thread: stub, thnum: 1, stack_size: 2, dead?: false) }
11
+ subject { klass.new(interface) }
12
+ temporary_change_method_value(Debugger, :handler, Debugger::Xml::IdeProcessor.new(TestInterface.new))
13
+
14
+ before do
15
+ Thread.stubs(:stop)
16
+ Debugger.handler.instance_variable_set("@context", context)
17
+ Debugger.handler.instance_variable_set("@file", file)
18
+ Debugger.handler.instance_variable_set("@line", 30)
19
+ end
20
+
21
+ it "must process a control command" do
22
+ Debugger::AddBreakpoint.any_instance.expects(:execute).with()
23
+ enter 'break 5'
24
+ subject.process_commands
25
+ end
26
+
27
+ it "must process several commands, separated by ;" do
28
+ Debugger::AddBreakpoint.any_instance.expects(:execute).with()
29
+ Debugger::DeleteBreakpointCommand.any_instance.expects(:execute).with()
30
+ enter 'break 5; delete 1'
31
+ subject.process_commands
32
+ end
33
+
34
+ it "must show error if there is no such command" do
35
+ enter 'bla'
36
+ subject.process_commands
37
+ check_output_includes "Unknown command: bla"
38
+ end
39
+
40
+ it "must show error if context is dead" do
41
+ context.expects(:dead?).returns(true)
42
+ enter 'step'
43
+ subject.process_commands
44
+ check_output_includes "Command is unavailable"
45
+ end
46
+
47
+ it "must show error if no suspended thread" do
48
+ Debugger.handler.stubs(:at_line?).returns(false)
49
+ enter 'step'
50
+ subject.process_commands
51
+ check_output_includes(
52
+ "There is no thread suspended at the time and therefore no context to execute 'step'",
53
+ interface.error_queue)
54
+ end
55
+
56
+ it "must run stopped thread after stepping command" do
57
+ context.expects(:step).with(1, false)
58
+ context.thread.expects(:run)
59
+ enter 'step'
60
+ subject.process_commands
61
+ end
62
+ end
@@ -0,0 +1,118 @@
1
+ require_relative 'test_helper'
2
+ require 'debugger/xml/ide_processor'
3
+
4
+ describe Debugger::Xml::IdeProcessor do
5
+ include TestDsl
6
+
7
+ before { Thread.stubs(:stop) }
8
+
9
+ let(:klass) { Debugger::Xml::IdeProcessor }
10
+ let(:interface) { TestInterface.new }
11
+ let(:breakpoint) { stub }
12
+ let(:context) { stub(thread: nil, stop_reason: nil, thnum: 1, stack_size: 2) }
13
+ let(:file) { fullpath('jump') }
14
+ subject { klass.new(interface) }
15
+
16
+ describe "#at_breakpoint" do
17
+ it "must assign breakpoint to instance variable" do
18
+ subject.at_breakpoint(context, breakpoint)
19
+ subject.instance_variable_get("@last_breakpoint").must_equal breakpoint
20
+ end
21
+
22
+ it "must raise error if @last_breakpoint is already assigned" do
23
+ subject.instance_variable_set("@last_breakpoint", breakpoint)
24
+ subject.at_breakpoint(context, breakpoint)
25
+ check_output_includes /INTERNAL ERROR!!!/
26
+ end
27
+
28
+ it "must not print anything" do
29
+ subject.at_breakpoint(context, breakpoint)
30
+ interface.must_be_empty
31
+ end
32
+ end
33
+
34
+ describe "#at_line" do
35
+ describe "print current position" do
36
+ it "must print if context is nil" do
37
+ subject.at_line(nil, file, 30)
38
+ check_output_includes "#{file}:30"
39
+ end
40
+
41
+ it "must print in xml" do
42
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new) do
43
+ subject.at_line(nil, file, 30)
44
+ check_output_includes %{<suspended file="#{file}" line="30" threadId="" frames=""/>}
45
+ end
46
+ end
47
+
48
+ it "must print if stop reason is :step" do
49
+ context.stubs(:stop_reason).returns(:step)
50
+ subject.at_line(context, file, 30)
51
+ check_output_includes "#{file}:30"
52
+ end
53
+
54
+ it "must clear instance variables after resuming thread" do
55
+ subject.instance_variable_set("@line", 10)
56
+ subject.at_line(context, file, 30)
57
+ subject.instance_variable_get("@line").must_be_nil
58
+ end
59
+
60
+ describe "print breakpoint after at_breakpoint" do
61
+ before do
62
+ Debugger.stubs(:breakpoints).returns([breakpoint])
63
+ Debugger.stubs(:current_context).returns(stub(thnum: 1))
64
+ subject.instance_variable_set("@last_breakpoint", breakpoint)
65
+ end
66
+
67
+ it "must print in plain text" do
68
+ subject.at_line(context, file, 30)
69
+ check_output_includes "Breakpoint 1 at #{file}:30"
70
+ end
71
+
72
+ it "must print in xml" do
73
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new) do
74
+ subject.at_line(context, file, 30)
75
+ check_output_includes %{<breakpoint file="#{file}" line="30" threadId="1"/>}
76
+ end
77
+ end
78
+ end
79
+
80
+ it "must show error if current thread is DebugThread" do
81
+ context.stubs(:thread).returns(Debugger::DebugThread.new {})
82
+ subject.at_line(context, file, 30)
83
+ check_output_includes /DebuggerThread are not supposed to be traced/
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#at_line?" do
89
+ it "returns false if #at_line was not called yet" do
90
+ subject.at_line?.must_equal false
91
+ end
92
+
93
+ it "returns true if #at_line was called already" do
94
+ subject.instance_variable_set("@line", 10)
95
+ subject.at_line?.must_equal true
96
+ end
97
+ end
98
+
99
+ describe "#at_return?" do
100
+ before { context.stubs(:stop_frame=).with(-1) }
101
+
102
+ it "sets stop_frame to -1" do
103
+ context.expects(:stop_frame=).with(-1)
104
+ subject.at_return(context, file, 30)
105
+ end
106
+
107
+ it "prints current file and line" do
108
+ subject.at_return(context, file, 30)
109
+ check_output_includes "#{file}:30"
110
+ end
111
+
112
+ it "stops the thread" do
113
+ Thread.expects(:stop)
114
+ subject.at_return(context, file, 30)
115
+ end
116
+ end
117
+
118
+ end
data/test/info_test.rb ADDED
@@ -0,0 +1,12 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Info Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must be unsupported for XML printer" do
8
+ enter 'info line'
9
+ debug_file 'info'
10
+ check_output_includes "<error>Unsupported command 'info'</error>", interface.error_queue
11
+ end
12
+ end
data/test/irb_test.rb ADDED
@@ -0,0 +1,12 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Irb Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must be unsupported for XML printer" do
8
+ enter 'irb'
9
+ debug_file 'irb'
10
+ check_output_includes "<error>Unsupported command 'irb'</error>", interface.error_queue
11
+ end
12
+ end
data/test/jump_test.rb ADDED
@@ -0,0 +1,22 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Jump Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ describe "successful" do
8
+ describe "jumping to the same line" do
9
+ it "must show show the same position" do
10
+ enter 'break 6', 'cont', "jump 6 #{fullpath('jump')}"
11
+ debug_file('jump')
12
+ check_output_includes /<suspended file="#{fullpath('jump')}" line="6" threadId="\d+" frames="\d+"\/>/
13
+ end
14
+ end
15
+
16
+ it "must show message after jump" do
17
+ enter 'break 6', 'cont', "jump 8 #{fullpath('jump')}"
18
+ debug_file('jump')
19
+ check_output_includes /<suspended file="#{fullpath('jump')}" line="8" threadId="\d+" frames="\d+"\/>/
20
+ end
21
+ end
22
+ end
data/test/kill_test.rb ADDED
@@ -0,0 +1,13 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Kill Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must be unsupported for XML printer" do
8
+ enter 'kill'
9
+ debug_file 'kill'
10
+ check_output_includes "<error>Unsupported command 'kill'</error>", interface.error_queue
11
+ end
12
+
13
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Method Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ describe "show instance method of a class" do
8
+ it "must show using full command name" do
9
+ enter 'break 15', 'cont', 'm MethodEx'
10
+ debug_file 'method'
11
+ check_output_includes '<methods><method name="bla"/></methods>'
12
+ end
13
+ end
14
+
15
+ describe "show methods of an object" do
16
+ it "must show using full command name" do
17
+ enter 'break 15', 'cont', 'method instance a'
18
+ debug_file 'method'
19
+ check_output_includes /<methods>.*<method name="bla"\/>.*<\/methods>/
20
+ end
21
+ end
22
+
23
+ describe "show instance variables of an object" do
24
+ it "must show using full name command" do
25
+ enter 'break 15', 'cont', 'method iv a'
26
+ debug_file 'method'
27
+ check_output_includes(Regexp.new(
28
+ %{<variables>} +
29
+ %{<variable name="@a" kind="instance" value="b" type="String" hasChildren="false" objectId=".*?"/>} +
30
+ %{<variable name="@c" kind="instance" value="d" type="String" hasChildren="false" objectId=".*?"/>} +
31
+ %{</variables>}
32
+ ))
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,193 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe "Printers::Xml" do
4
+ include PrinterHelpers
5
+
6
+ let(:klass) { Printers::Xml }
7
+ let(:printer) { klass.new }
8
+ let(:yaml_xml) do
9
+ {
10
+ "foo" => {
11
+ "errors" => {
12
+ "bad" => "bad behavior"
13
+ },
14
+ "confirmations" => {
15
+ "okay" => "Okay?"
16
+ },
17
+ "debug" => {
18
+ "dbg" => "Debug message"
19
+ },
20
+ "bar" => {
21
+ "tag" => "xmltag",
22
+ "attributes" => {
23
+ "boo" => "{zee} > {uga}",
24
+ "agu" => "bew"
25
+ }
26
+ }
27
+ },
28
+ "variable" => {
29
+ "variable" => {
30
+ "tag" => "variable",
31
+ "attributes" => {
32
+ "name" => "{name}",
33
+ "kind" => "{kind}",
34
+ "value" => "{value}",
35
+ "type" => "{type}",
36
+ "hasChildren" => "{has_children}",
37
+ "objectId" => "{id}"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ end
43
+
44
+ before do
45
+ YAML.stubs(:load_file).with(yaml_file_path('xml')).returns(yaml_xml)
46
+ YAML.stubs(:load_file).with(yaml_file_path('base')).returns({})
47
+ end
48
+
49
+ describe "#print" do
50
+ it "must return correctly translated string" do
51
+ xml = ::Builder::XmlMarkup.new.xmltag(boo: "zuu > aga", agu: "bew")
52
+ printer.print("foo.bar", zee: "zuu", uga: "aga").must_equal xml
53
+ end
54
+
55
+ it "must return error string" do
56
+ printer.print("foo.errors.bad").must_equal "<error>bad behavior</error>"
57
+ end
58
+
59
+ it "must return confirmation string" do
60
+ printer.print("foo.confirmations.okay").must_equal "<confirmation>Okay?</confirmation>"
61
+ end
62
+
63
+ it "must return debug string" do
64
+ printer.print("foo.debug.dbg").must_equal "Debug message"
65
+ end
66
+ end
67
+
68
+ describe "#print_collection" do
69
+ it "must print collection" do
70
+ expected = ::Builder::XmlMarkup.new.xmltags do |x|
71
+ x.xmltag(boo: "0 > a", agu: "bew") + x.xmltag(boo: "1 > b", agu: "bew")
72
+ end
73
+ result = printer.print_collection("foo.bar", [{uga: 'a'}, {uga: 'b'}]) do |item, index|
74
+ item.merge(zee: index)
75
+ end
76
+ result.must_equal expected
77
+ end
78
+ end
79
+
80
+ describe "#print_variables" do
81
+ it "must print variables" do
82
+ vars = [["a", "b"], ["c", "d"]]
83
+ expected = ::Builder::XmlMarkup.new.variables do |x|
84
+ vars.map do |key, value|
85
+ x.variable(name: key, kind: "instance", value: value, type: "String", hasChildren: "false", objectId: "%#+x" % value.object_id)
86
+ end.join("")
87
+ end
88
+ result = printer.print_variables(vars, 'instance')
89
+ result.must_equal expected
90
+ end
91
+ end
92
+
93
+ describe "Printers::Xml::Variable" do
94
+ let(:klass) { Printers::Xml::Variable }
95
+
96
+ describe "#has_children?" do
97
+ describe "value is Array" do
98
+ it("must be true for non-empty") { klass.new('bla', ['a']).has_children?.must_equal(true) }
99
+ it("must be false for empty") { klass.new('bla', []).has_children?.must_equal(false) }
100
+ end
101
+
102
+ describe "value is Hash" do
103
+ it("must be true for non-empty") { klass.new('bla', {a: 'b'}).has_children?.must_equal(true) }
104
+ it("must be false for empty") { klass.new('bla', {}).has_children?.must_equal(false) }
105
+ end
106
+
107
+ describe "value is some random class" do
108
+ unless const_defined?("VariableExampleWithInstanceVars")
109
+ class VariableExampleWithInstanceVars; def initialize; @a = '1'; end; end
110
+ end
111
+ unless const_defined?("VariableExampleWithClassVars")
112
+ class VariableExampleWithClassVars; def initialize; @@a = '1'; end; end
113
+ end
114
+ unless const_defined?("VariableExampleWithoutVars")
115
+ class VariableExampleWithoutVars; end
116
+ end
117
+ it("must be true if has instance variables") { klass.new('bla', VariableExampleWithInstanceVars.new).has_children?.must_equal(true) }
118
+ it("must be true if has class variables") { klass.new('bla', VariableExampleWithClassVars.new).has_children?.must_equal(true) }
119
+ it("must be false if has no any variables") { klass.new('bla', VariableExampleWithoutVars.new).has_children?.must_equal(false) }
120
+ it("must be false as a fallback") { klass.new('bla', BasicObject.new).has_children?.must_equal(false) }
121
+ end
122
+ end
123
+
124
+ describe "#value" do
125
+ describe "value is a Array" do
126
+ it("must return string for empty") { klass.new('bla', []).value.must_equal("Empty Array") }
127
+ it("must return result for non-empty") { klass.new('bla', [1, 2]).value.must_equal("Array (2 element(s))") }
128
+ end
129
+
130
+ describe "value is a Hash" do
131
+ it("must return string for empty") { klass.new('bla', {}).value.must_equal("Empty Hash") }
132
+ it("must return result for non-empty") { klass.new('bla', {a: 'b', c: 'd'}).value.must_equal("Hash (2 element(s))") }
133
+ end
134
+
135
+ describe "value is some random class" do
136
+ unless const_defined?("ToSReturnNotAString")
137
+ class ToSReturnNotAString; def to_s; {}; end; end
138
+ end
139
+ it("must return nil for nil") { klass.new('bla', nil).value.must_equal("nil") }
140
+ it("must return #to_s for any other class") do
141
+ klass.new('bla', OpenStruct.new(a: 'b')).value.must_equal '#<OpenStruct a="b">'
142
+ end
143
+ it("must be able to show error") do
144
+ klass.new('bla', BasicObject.new).value.must_match /<raised exception: undefined method/
145
+ end
146
+ it("must get rid of quotes") { klass.new('bla', '"foo"').value.must_equal 'foo' }
147
+ it("must return special message for binary") { klass.new('bla', "\xFF\x12").value.must_equal '[Binary Data]' }
148
+ it("must show error if returned value is not a string") do
149
+ klass.new('bla', ToSReturnNotAString.new).value.must_equal(
150
+ 'ERROR: ToSReturnNotAString.to_s method returns Hash. Should return String.'
151
+ )
152
+ end
153
+ end
154
+ end
155
+
156
+ describe "#id" do
157
+ it "must show object_id" do
158
+ object = Object.new
159
+ klass.new('bla', object).id.must_equal("%#+x" % object.object_id)
160
+ end
161
+
162
+ it "must return nil as a fallback" do
163
+ klass.new('bla', BasicObject.new).id.must_be_nil
164
+ end
165
+ end
166
+
167
+ describe "#type" do
168
+ it "must return class" do
169
+ klass.new('bla', Object.new).type.must_equal(Object.new.class)
170
+ end
171
+
172
+ it "must return 'Undefined' as a callback" do
173
+ klass.new('bla', BasicObject.new).type.must_equal "Undefined"
174
+ end
175
+ end
176
+
177
+ describe "#name" do
178
+ it "must return name as a string" do
179
+ klass.new(:bla, "value").name.must_equal "bla"
180
+ end
181
+ end
182
+
183
+ describe "#to_hash" do
184
+ it "must return a hash with values" do
185
+ var = "foo"
186
+ klass.new(:bla, var).to_hash.must_equal(
187
+ {name: "bla", kind: nil, value: var, type: String, has_children: false, id: "%#+x" % var.object_id}
188
+ )
189
+ end
190
+ end
191
+
192
+ end
193
+ end