qa_robusta 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. data/.autotest +23 -0
  2. data/.gemtest +0 -0
  3. data/History.txt +6 -0
  4. data/Manifest.txt +101 -0
  5. data/README.txt +48 -0
  6. data/Rakefile +18 -0
  7. data/bin/qa_robusta +14 -0
  8. data/common/Rakefile +95 -0
  9. data/common/conf/monkey_patch.yaml +8 -0
  10. data/common/conf/monkey_patch.yaml.ex +10 -0
  11. data/common/lib/array.rb +9 -0
  12. data/common/lib/cache.rb +12 -0
  13. data/common/lib/constants.rb +35 -0
  14. data/common/lib/error_defns.rb +13 -0
  15. data/common/lib/format_html_tmp.html +34 -0
  16. data/common/lib/formatters.rb +18 -0
  17. data/common/lib/gem_helpers.rb +29 -0
  18. data/common/lib/gems/.svn/entries +43 -0
  19. data/common/lib/gems/cache/.svn/entries +28 -0
  20. data/common/lib/gems/doc/.svn/entries +28 -0
  21. data/common/lib/gems/gems/.svn/entries +28 -0
  22. data/common/lib/gems/installed/.svn/entries +40 -0
  23. data/common/lib/gems/installed/cache/.svn/entries +28 -0
  24. data/common/lib/gems/installed/doc/.svn/entries +28 -0
  25. data/common/lib/gems/installed/gems/.svn/entries +28 -0
  26. data/common/lib/gems/installed/specifications/.svn/entries +28 -0
  27. data/common/lib/gems/specifications/.svn/entries +28 -0
  28. data/common/lib/gen_suite_doc.rb +52 -0
  29. data/common/lib/load_test_data.rb +18 -0
  30. data/common/lib/monkey_patch.rb +149 -0
  31. data/common/lib/navigate_mech.rb +79 -0
  32. data/common/lib/update_element.rb +12 -0
  33. data/common/monkey_patches/mechanize.rb +589 -0
  34. data/common/monkey_patches/table.rb +363 -0
  35. data/common/monkey_patches/telnet.rb +420 -0
  36. data/common/monkey_patches/unit.rb +538 -0
  37. data/demo/demo_site.rb +38 -0
  38. data/demo/public/javascripts/jquery-1.6.2.min.js +18 -0
  39. data/demo/views/index.erb +35 -0
  40. data/lib/monkey_patch.rb +26 -0
  41. data/lib/qa_robusta.rb +3 -0
  42. data/mechanize_interface/conf/app.yaml +10 -0
  43. data/mechanize_interface/lib/agent.rb +18 -0
  44. data/mechanize_interface/lib/app_require.rb +19 -0
  45. data/mechanize_interface/lib/get_page.rb +20 -0
  46. data/mechanize_interface/lib/login.rb +30 -0
  47. data/mechanize_interface/lib/navigation_paths.rb +12 -0
  48. data/mechanize_interface/test/lib/mech_unit_test.rb +19 -0
  49. data/qa_observer/conf/dev_users.yaml +9 -0
  50. data/qa_observer/conf/development.yaml +3 -0
  51. data/qa_observer/conf/qa_observer_links.yaml +10 -0
  52. data/qa_observer/generators/site/site_generator.rb +61 -0
  53. data/qa_observer/generators/site/templates/base_urls.yaml.erb +6 -0
  54. data/qa_observer/generators/site/templates/create_flow.rb.erb +54 -0
  55. data/qa_observer/generators/site/templates/elements.rb.erb +61 -0
  56. data/qa_observer/generators/site/templates/html_elements.yaml +22 -0
  57. data/qa_observer/generators/site/templates/navigate.rb +32 -0
  58. data/qa_observer/generators/site/templates/reports.rb +7 -0
  59. data/qa_observer/generators/site/templates/users_list.yaml +21 -0
  60. data/qa_observer/generators/test_case/templates/test_case.rb.erb +62 -0
  61. data/qa_observer/generators/test_case/test_case_generator.rb +29 -0
  62. data/qa_observer/lib/doc.rb +103 -0
  63. data/qa_observer/lib/env_details.rb +2 -0
  64. data/qa_observer/lib/error_defns.rb +3 -0
  65. data/qa_observer/lib/form_helpers.rb +52 -0
  66. data/qa_observer/lib/reports.rb +7 -0
  67. data/qa_observer/lib/requires.rb +23 -0
  68. data/qa_observer/lib/system_test.rb +146 -0
  69. data/qa_observer/lib/test_extention.rb +29 -0
  70. data/qa_observer/lib/update_element.rb +12 -0
  71. data/qa_observer/lib/watir.rb +42 -0
  72. data/qa_observer/script/destroy +14 -0
  73. data/qa_observer/script/generate +14 -0
  74. data/qa_observer/sites/demo/conf/base_urls.yaml +6 -0
  75. data/qa_observer/sites/demo/conf/html_elements.yaml +22 -0
  76. data/qa_observer/sites/demo/conf/remote_machine.yaml +12 -0
  77. data/qa_observer/sites/demo/conf/users_list.yaml +21 -0
  78. data/qa_observer/sites/demo/elements/demo_elements.rb +9 -0
  79. data/qa_observer/sites/demo/flows/demo_flows.rb +37 -0
  80. data/qa_observer/sites/demo/helpers/.placeholder +0 -0
  81. data/qa_observer/sites/demo/lib/navigate.rb +32 -0
  82. data/qa_observer/sites/demo/lib/reports.rb +7 -0
  83. data/qa_observer/sites/demo/results/.placeholder +0 -0
  84. data/qa_observer/sites/demo/results/images/.placeholder +0 -0
  85. data/qa_observer/sites/demo/test_cases/home_page.rb +106 -0
  86. data/qa_observer/suites/demo_lg_chrome/lg.yaml +11 -0
  87. data/qa_observer/suites/demo_md_chrome/md.yaml +11 -0
  88. data/qa_observer/suites/demo_sm_chrome/sm.yaml +11 -0
  89. data/qa_observer/suites/init.rb +41 -0
  90. data/qa_observer/test_runner.rb +125 -0
  91. data/remote_unix/helpers/constants.rb +8 -0
  92. data/remote_unix/helpers/out_xforms.rb +48 -0
  93. data/remote_unix/lib/common.rb +52 -0
  94. data/remote_unix/lib/error_defn.rb +4 -0
  95. data/remote_unix/lib/general_unix.rb +308 -0
  96. data/remote_unix/lib/network_commands.rb +41 -0
  97. data/remote_unix/lib/network_info.rb +98 -0
  98. data/remote_unix/lib/postfix_commands.rb +87 -0
  99. data/remote_unix/lib/postfix_info.rb +30 -0
  100. data/remote_unix/lib/requires.rb +25 -0
  101. data/remote_unix/lib/scp.rb +22 -0
  102. data/remote_unix/lib/ssh.rb +46 -0
  103. data/test/test_qa_robusta.rb +8 -0
  104. metadata +219 -0
@@ -0,0 +1,363 @@
1
+ module Watir
2
+
3
+ # This class is used for dealing with tables.
4
+ # Normally a user would not need to create this object as it is returned by the Watir::Container#table method
5
+ #
6
+ # many of the methods available to this object are inherited from the Element class
7
+ #
8
+ class Table < Element
9
+ include Container
10
+
11
+ # Returns the table object containing the element
12
+ # * container - an instance of an IE object
13
+ # * anElement - a Watir object (TextField, Button, etc.)
14
+ def Table.create_from_element(container, element)
15
+ element.locate if defined?(element.locate)
16
+ o = element.ole_object.parentElement
17
+ o = o.parentElement until o.tagName == 'TABLE'
18
+ new container, :ole_object, o
19
+ end
20
+
21
+ # Returns an initialized instance of a table object
22
+ # * container - the container
23
+ # * how - symbol - how we access the table
24
+ # * what - what we use to access the table - id, name index etc
25
+ def initialize(container, how, what)
26
+ set_container container
27
+ @how = how
28
+ @what = what
29
+ super nil
30
+ end
31
+
32
+ def locate
33
+ if @how == :xpath
34
+ @o = @container.element_by_xpath(@what)
35
+ elsif @how == :ole_object
36
+ @o = @what
37
+ else
38
+ @o = @container.locate_tagged_element('TABLE', @how, @what)
39
+ end
40
+ end
41
+
42
+ # override the highlight method, as if the tables rows are set to have a background color,
43
+ # this will override the table background color, and the normal flash method won't work
44
+ def highlight(set_or_clear)
45
+ if set_or_clear == :set
46
+ begin
47
+ @original_border = @o.border.to_i
48
+ if @o.border.to_i==1
49
+ @o.border = 2
50
+ else
51
+ @o.border = 1
52
+ end
53
+ rescue
54
+ @original_border = nil
55
+ end
56
+ else
57
+ begin
58
+ @o.border= @original_border unless @original_border == nil
59
+ @original_border = nil
60
+ rescue
61
+ # we could be here for a number of reasons...
62
+ ensure
63
+ @original_border = nil
64
+ end
65
+ end
66
+ super
67
+ end
68
+
69
+ # this method is used to populate the properties in the to_s method
70
+ def table_string_creator
71
+ n = []
72
+ n << "rows:".ljust(TO_S_SIZE) + self.row_count.to_s
73
+ n << "cols:".ljust(TO_S_SIZE) + self.column_count.to_s
74
+ return n
75
+ end
76
+ private :table_string_creator
77
+
78
+ # returns the properties of the object in a string
79
+ # raises an ObjectNotFound exception if the object cannot be found
80
+ def to_s
81
+ assert_exists
82
+ r = string_creator
83
+ r += table_string_creator
84
+ return r.join("\n")
85
+ end
86
+
87
+ # iterates through the rows in the table. Yields a TableRow object
88
+ def each
89
+ assert_exists
90
+ 0.upto(@o.getElementsByTagName("TR").length) do |i|
91
+ yield TableRow.new(@container, :ole_object, _row(i))
92
+ end
93
+ end
94
+
95
+ # Returns a row in the table
96
+ # * index - the index of the row
97
+ def [](index)
98
+ assert_exists
99
+ return TableRow.new(@container, :ole_object, _row(index))
100
+ end
101
+
102
+ # Returns the number of rows inside the table, including rows in nested tables.
103
+ def row_count
104
+ assert_exists
105
+ #return table_body.children.length
106
+ return @o.getElementsByTagName("TR").length
107
+ end
108
+
109
+ # Returns the number of rows in the table, not including rows in nested tables.
110
+ def row_count_excluding_nested_tables
111
+ assert_exists
112
+ return @o.rows.length
113
+ end
114
+
115
+ # This method returns the number of columns in a row of the table.
116
+ # Raises an UnknownObjectException if the table doesn't exist.
117
+ # * index - the index of the row
118
+ def column_count(index=0)
119
+ assert_exists
120
+ _row(index).cells.length
121
+ end
122
+
123
+ # This method returns the table as a 2 dimensional array.
124
+ # Don't expect too much if there are nested tables, colspan etc.
125
+ # Raises an UnknownObjectException if the table doesn't exist.
126
+ # http://www.w3.org/TR/html4/struct/tables.html
127
+ def to_a
128
+ assert_exists
129
+ y = []
130
+ table_rows = @o.getElementsByTagName("TR")
131
+ for row in table_rows
132
+ x = []
133
+ for td in row.getElementsbyTagName("TD")
134
+ x << td.innerText.strip
135
+ end
136
+ y << x
137
+ end
138
+ return y
139
+ end
140
+
141
+ def table_body(index=1)
142
+ return @o.getElementsByTagName('TBODY')[index]
143
+ end
144
+ private :table_body
145
+
146
+ # returns a watir object
147
+ def body(how, what)
148
+ return TableBody.new(@container, how, what, self)
149
+ end
150
+
151
+ # returns a watir object
152
+ def bodies
153
+ assert_exists
154
+ return TableBodies.new(@container, @o)
155
+ end
156
+
157
+ # returns an ole object
158
+ def _row(index)
159
+ return @o.invoke("rows")[(index - 1).to_s]
160
+ end
161
+ private :_row
162
+
163
+ # Returns an array containing all the text values in the specified column
164
+ # Raises an UnknownCellException if the specified column does not exist in every
165
+ # Raises an UnknownObjectException if the table doesn't exist.
166
+ # row of the table
167
+ # * columnnumber - column index to extract values from
168
+ def column_values(columnnumber)
169
+ return (1..row_count).collect {|i| self[i][columnnumber].text}
170
+ end
171
+
172
+ # Returns an array containing all the text values in the specified row
173
+ # Raises an UnknownObjectException if the table doesn't exist.
174
+ # * rownumber - row index to extract values from
175
+ def row_values(rownumber)
176
+ return (1..column_count(rownumber)).collect {|i| self[rownumber][i].text}
177
+ end
178
+
179
+ end
180
+
181
+ # this class is a collection of the table body objects that exist in the table
182
+ # it wouldnt normally be created by a user, but gets returned by the bodies method of the Table object
183
+ # many of the methods available to this object are inherited from the Element class
184
+ #
185
+ class TableBodies < Element
186
+ def initialize(container, parent_table)
187
+ set_container container
188
+ @o = parent_table # in this case, @o is the parent table
189
+ end
190
+
191
+ # returns the number of TableBodies that exist in the table
192
+ def length
193
+ assert_exists
194
+ return @o.tBodies.length
195
+ end
196
+
197
+ # returns the n'th Body as a Watir TableBody object
198
+ def []n
199
+ assert_exists
200
+ return TableBody.new(@container, :ole_object, ole_table_body_at_index(n))
201
+ end
202
+
203
+ # returns an ole table body
204
+ def ole_table_body_at_index(n)
205
+ return @o.tBodies[(n-1).to_s]
206
+ end
207
+
208
+ # iterates through each of the TableBodies in the Table. Yields a TableBody object
209
+ def each
210
+ 1.upto(@o.tBodies.length) do |i|
211
+ yield TableBody.new(@container, :ole_object, ole_table_body_at_index(i))
212
+ end
213
+ end
214
+
215
+ end
216
+
217
+ # this class is a table body
218
+ class TableBody < Element
219
+ def locate
220
+ @o = nil
221
+ if @how == :ole_object
222
+ @o = @what # in this case, @o is the table body
223
+ elsif @how == :index
224
+ @o = @parent_table.bodies.ole_table_body_at_index(@what)
225
+ end
226
+ @rows = []
227
+ if @o
228
+ @o.rows.each do |oo|
229
+ @rows << TableRow.new(@container, :ole_object, oo)
230
+ end
231
+ end
232
+ end
233
+
234
+ def initialize(container, how, what, parent_table=nil)
235
+ set_container container
236
+ @how = how
237
+ @what = what
238
+ @parent_table = parent_table
239
+ super nil
240
+ end
241
+
242
+ # returns the specified row as a TableRow object
243
+ def [](n)
244
+ assert_exists
245
+ return @rows[n - 1]
246
+ end
247
+
248
+ # iterates through all the rows in the table body
249
+ def each
250
+ locate
251
+ 0.upto(@rows.length - 1) { |i| yield @rows[i] }
252
+ end
253
+
254
+ # returns the number of rows in this table body.
255
+ def length
256
+ return @rows.length
257
+ end
258
+ end
259
+
260
+ class TableRow < Element
261
+
262
+ def locate
263
+ @o = nil
264
+ if @how == :ole_object
265
+ @o = @what
266
+ elsif @how == :xpath
267
+ @o = @container.element_by_xpath(@what)
268
+ else
269
+ @o = @container.locate_tagged_element("TR", @how, @what)
270
+ end
271
+ if @o # cant call the assert_exists here, as an exists? method call will fail
272
+ @cells = []
273
+ @o.cells.each do |oo|
274
+ @cells << TableCell.new(@container, :ole_object, oo)
275
+ end
276
+ end
277
+ end
278
+
279
+ # Returns an initialized instance of a table row
280
+ # * o - the object contained in the row
281
+ # * container - an instance of an IE object
282
+ # * how - symbol - how we access the row
283
+ # * what - what we use to access the row - id, index etc. If how is :ole_object then what is a Internet Explorer Raw Row
284
+ def initialize(container, how, what)
285
+ set_container container
286
+ @how = how
287
+ @what = what
288
+ super nil
289
+ end
290
+
291
+ # this method iterates through each of the cells in the row. Yields a TableCell object
292
+ def each
293
+ locate
294
+ 0.upto(@cells.length) { |i| yield @cells[i] }
295
+ end
296
+
297
+ # Returns an element from the row as a TableCell object
298
+ def [](index)
299
+ assert_exists
300
+ if @cells.length < index
301
+ raise UnknownCellException, "Unable to locate a cell at index #{index}"
302
+ end
303
+ return @cells[(index)]
304
+ end
305
+
306
+ # defaults all missing methods to the array of elements, to be able to
307
+ # use the row as an array
308
+ # def method_missing(aSymbol, *args)
309
+ # return @o.send(aSymbol, *args)
310
+ # end
311
+ def column_count
312
+ locate
313
+ @cells.length
314
+ end
315
+ end
316
+
317
+ # this class is a table cell - when called via the Table object
318
+ class TableCell < Element
319
+ include Watir::Exception
320
+ include Container
321
+
322
+ def locate
323
+ if @how == :xpath
324
+ @o = @container.element_by_xpath(@what)
325
+ elsif @how == :ole_object
326
+ @o = @what
327
+ else
328
+ @o = @container.locate_tagged_element("TD", @how, @what)
329
+ end
330
+ end
331
+
332
+ # Returns an initialized instance of a table cell
333
+ # * container - an IE object
334
+ # * how - symbol - how we access the cell
335
+ # * what - what we use to access the cell - id, name index etc
336
+ def initialize(container, how, what)
337
+ set_container container
338
+ @how = how
339
+ @what = what
340
+ super nil
341
+ end
342
+
343
+ def ole_inner_elements
344
+ locate
345
+ return @o.all
346
+ end
347
+ private :ole_inner_elements
348
+
349
+ def document
350
+ locate
351
+ return @o
352
+ end
353
+
354
+ alias to_s text
355
+
356
+ def colspan
357
+ locate
358
+ @o.colSpan
359
+ end
360
+
361
+ end
362
+
363
+ end
@@ -0,0 +1,420 @@
1
+ require 'rubygems'
2
+ require 'net/ssh'
3
+
4
+ module Net
5
+ module SSH
6
+
7
+ # == Net::SSH::Telnet
8
+ #
9
+ # Provides a simple send/expect interface with an API almost
10
+ # identical to Net::Telnet. Please see Net::Telnet for main documentation.
11
+ # Only the differences are documented here.
12
+
13
+ class Telnet
14
+
15
+ CR = "\015"
16
+ LF = "\012"
17
+ EOL = CR + LF
18
+ VERSION = '0.0.1'
19
+
20
+ # Wrapper to emulate the behaviour of Net::Telnet "Proxy" option, where
21
+ # the user passes in an already-open socket
22
+
23
+ class TinyFactory
24
+ def initialize(sock)
25
+ @sock = sock
26
+ end
27
+ def open(host, port)
28
+ s = @sock
29
+ @sock = nil
30
+ s
31
+ end
32
+ end
33
+
34
+ # Creates a new Net::SSH::Telnet object.
35
+ #
36
+ # The API is similar to Net::Telnet, although you will need to pass in
37
+ # either an existing Net::SSH::Session object or a Username and Password,
38
+ # as shown below.
39
+ #
40
+ # Note that unlike Net::Telnet there is no preprocess method automatically
41
+ # setting up options related to proper character translations, so if your
42
+ # remote ptty is configured differently than the typical linux one you may
43
+ # need to pass in a different terminator or call 'stty' remotely to set it
44
+ # into an expected mode. This is better explained by the author of perl's
45
+ # Net::SSH::Expect here:
46
+ #
47
+ # http://search.cpan.org/~bnegrao/Net-SSH-Expect-1.04/lib/Net/SSH/Expect.pod
48
+ # #IMPORTANT_NOTES_ABOUT_DEALING_WITH_SSH_AND_PSEUDO-TERMINALS
49
+ #
50
+ # though for most installs the default LF should be fine. See example 5
51
+ # below.
52
+ #
53
+ # A new option is added to correct a misfeature of Net::Telnet. If you
54
+ # pass "FailEOF" => true, then if the remote end disconnects while you
55
+ # are still waiting for your match pattern then an EOFError is raised.
56
+ # Otherwise, it reverts to the same behaviour as Net::Telnet, which is
57
+ # just to return whatever data was sent so far, or nil if no data was
58
+ # returned so far. (This is a poor design because you can't tell whether
59
+ # the expected pattern was successfully matched or the remote end
60
+ # disconnected unexpectedly, unless you perform a second match on the
61
+ # return string). See
62
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/11373
63
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/11380
64
+ #
65
+ # Example 1 - pass existing Net::SSH::Session object
66
+ #
67
+ # ssh = Net::SSH.start("127.0.0.1",
68
+ # :username=>"test123",
69
+ # :password=>"pass456"
70
+ # )
71
+ # s = Net::SSH::Telnet.new(
72
+ # "Dump_log" => "/dev/stdout",
73
+ # "Session" => ssh
74
+ # )
75
+ # puts "Logged in"
76
+ # p s.cmd("echo hello")
77
+ #
78
+ # This is the most flexible way as it allows you to set up the SSH
79
+ # session using whatever authentication system you like. When done this
80
+ # way, calling Net::SSH::Telnet.new multiple times will create
81
+ # multiple channels, and #close will only close one channel.
82
+ #
83
+ # In all later examples, calling #close will close the entire
84
+ # Net::SSH::Session object (and therefore drop the TCP connection)
85
+ #
86
+ # Example 2 - pass host, username and password
87
+ #
88
+ # s = Net::SSH::Telnet.new(
89
+ # "Dump_log" => "/dev/stdout",
90
+ # "Host" => "127.0.0.1",
91
+ # "Username" => "test123",
92
+ # "Password" => "pass456"
93
+ # )
94
+ # puts "Logged in"
95
+ # puts s.cmd("echo hello")
96
+ #
97
+ # Example 3 - pass open IO object, username and password (this is really
98
+ # just for compatibility with Net::Telnet Proxy feature)
99
+ #
100
+ # require 'socket'
101
+ # sock = TCPSocket.open("127.0.0.1",22)
102
+ # s = Net::SSH::Telnet.new(
103
+ # "Dump_log" => "/dev/stdout",
104
+ # "Proxy" => sock,
105
+ # "Username" => "test123",
106
+ # "Password" => "pass456"
107
+ # )
108
+ # puts "Logged in"
109
+ # puts s.cmd("echo hello")
110
+ #
111
+ # Example 4 - pass a connection factory, host, username and password;
112
+ # Net::SSH will call #open(host,port) on this object. Included just
113
+ # because it was easy :-)
114
+ #
115
+ # require 'socket'
116
+ # s = Net::SSH::Telnet.new(
117
+ # "Dump_log" => "/dev/stdout",
118
+ # "Factory" => TCPSocket,
119
+ # "Host" => "127.0.0.1",
120
+ # "Username" => "test123",
121
+ # "Password" => "pass456"
122
+ # )
123
+ # puts "Logged in"
124
+ # puts s.cmd("echo hello")
125
+ #
126
+ # Example 5 - connection to a SAN device running a customized NetBSD with
127
+ # different ptty defaults, it doesn't convert LF -> CR+LF (see the man
128
+ # page for 'stty') and uses a custom prompt
129
+ #
130
+ # s = Net::SSH::Telnet.new(
131
+ # "Host" => "192.168.1.1",
132
+ # "Username" => "test123",
133
+ # "Password" => "pass456",
134
+ # "Prompt" => %r{^\S+>\s.*$},
135
+ # "Terminator" => "\r"
136
+ # )
137
+ # puts "Logged in"
138
+ # puts s.cmd("show alerts")
139
+
140
+ def initialize(options, &blk) # :yield: mesg
141
+ @options = options
142
+ @options["Host"] = "localhost" unless @options.has_key?("Host")
143
+ @options["Port"] = 22 unless @options.has_key?("Port")
144
+ @options["Prompt"] = /[$%#>] \z/n unless @options.has_key?("Prompt")
145
+ @options["Timeout"] = 10 unless @options.has_key?("Timeout")
146
+ @options["Waittime"] = 0 unless @options.has_key?("Waittime")
147
+ @options["Terminator"] = LF unless @options.has_key?("Terminator")
148
+
149
+ unless @options.has_key?("Binmode")
150
+ @options["Binmode"] = false
151
+ else
152
+ unless (true == @options["Binmode"] or false == @options["Binmode"])
153
+ raise ArgumentError, "Binmode option must be true or false"
154
+ end
155
+ end
156
+
157
+ if @options.has_key?("Output_log")
158
+ @log = File.open(@options["Output_log"], 'a+')
159
+ @log.sync = true
160
+ @log.binmode
161
+ end
162
+
163
+ if @options.has_key?("Dump_log")
164
+ @dumplog = File.open(@options["Dump_log"], 'a+')
165
+ @dumplog.sync = true
166
+ @dumplog.binmode
167
+ def @dumplog.log_dump(dir, x) # :nodoc:
168
+ len = x.length
169
+ addr = 0
170
+ offset = 0
171
+ while 0 < len
172
+ if len < 16
173
+ line = x[offset, len]
174
+ else
175
+ line = x[offset, 16]
176
+ end
177
+ hexvals = line.unpack('H*')[0]
178
+ hexvals += ' ' * (32 - hexvals.length)
179
+ hexvals = format("%s %s %s %s " * 4, *hexvals.unpack('a2' * 16))
180
+ line = line.gsub(/[\000-\037\177-\377]/n, '.')
181
+ printf "%s 0x%5.5x: %s%s\n", dir, addr, hexvals, line
182
+ addr += 16
183
+ offset += 16
184
+ len -= 16
185
+ end
186
+ print "\n"
187
+ end
188
+ end
189
+
190
+ if @options.has_key?("Session")
191
+ @ssh = @options["Session"]
192
+ @close_all = false
193
+ elsif @options.has_key?("Proxy")
194
+ @ssh = Net::SSH.start(nil, nil,
195
+ :host_name => @options["Host"], # ignored
196
+ :port => @options["Port"], # ignored
197
+ :user => @options["Username"],
198
+ :password => @options["Password"],
199
+ :timeout => @options["Timeout"],
200
+ :proxy => TinyFactory.new(@options["Proxy"])
201
+ )
202
+ @close_all = true
203
+ else
204
+ message = "Trying " + @options["Host"] + "...\n"
205
+ yield(message) if block_given?
206
+ @log.write(message) if @options.has_key?("Output_log")
207
+ @dumplog.log_dump('#', message) if @options.has_key?("Dump_log")
208
+
209
+ begin
210
+ @ssh = Net::SSH.start(nil, nil,
211
+ :host_name => @options["Host"],
212
+ :port => @options["Port"],
213
+ :user => @options["Username"],
214
+ :password => @options["Password"],
215
+ :timeout => @options["Timeout"],
216
+ :proxy => @options["Factory"]
217
+ )
218
+ @close_all = true
219
+ rescue TimeoutError
220
+ raise TimeoutError, "timed out while opening a connection to the host"
221
+ rescue
222
+ @log.write($ERROR_INFO.to_s + "\n") if @options.has_key?("Output_log")
223
+ @dumplog.log_dump('#', $ERROR_INFO.to_s + "\n") if @options.has_key?("Dump_log")
224
+ raise
225
+ end
226
+
227
+ message = "Connected to " + @options["Host"] + ".\n"
228
+ yield(message) if block_given?
229
+ @log.write(message) if @options.has_key?("Output_log")
230
+ @dumplog.log_dump('#', message) if @options.has_key?("Dump_log")
231
+ end
232
+
233
+ @buf = ""
234
+ @eof = false
235
+ @channel = nil
236
+ @ssh.open_channel do |channel|
237
+ channel.request_pty { |ch,success|
238
+ if success == false
239
+ raise "Failed to open ssh pty"
240
+ end
241
+ }
242
+ channel.send_channel_request("shell") { |ch, success|
243
+ if success
244
+ @channel = ch
245
+ waitfor(@options['Prompt'], &blk)
246
+ return
247
+ else
248
+ raise "Failed to open ssh shell"
249
+ end
250
+ }
251
+ channel.on_data { |ch,data| @buf << data }
252
+ channel.on_extended_data { |ch,type,data| @buf << data if type == 1 }
253
+ channel.on_close { @eof = true }
254
+ end
255
+ @ssh.loop
256
+ end # initialize
257
+
258
+ # Close the ssh channel, and also the entire ssh session if we
259
+ # opened it.
260
+
261
+ def close
262
+ @channel.close if @channel
263
+ @channel = nil
264
+ @ssh.close if @close_all and @ssh
265
+ end
266
+
267
+ # The ssh session and channel we are using.
268
+ attr_reader :ssh, :channel
269
+
270
+ # Turn newline conversion on (+mode+ == false) or off (+mode+ == true),
271
+ # or return the current value (+mode+ is not specified).
272
+ def binmode(mode = nil)
273
+ case mode
274
+ when nil
275
+ @options["Binmode"]
276
+ when true, false
277
+ @options["Binmode"] = mode
278
+ else
279
+ raise ArgumentError, "argument must be true or false"
280
+ end
281
+ end
282
+
283
+ # Turn newline conversion on (false) or off (true).
284
+ def binmode=(mode)
285
+ if (true == mode or false == mode)
286
+ @options["Binmode"] = mode
287
+ else
288
+ raise ArgumentError, "argument must be true or false"
289
+ end
290
+ end
291
+
292
+ # Read data from the host until a certain sequence is matched.
293
+
294
+ def waitfor(options) # :yield: recvdata
295
+ time_out = @options["Timeout"]
296
+ waittime = @options["Waittime"]
297
+ fail_eof = @options["FailEOF"]
298
+
299
+ if options.kind_of?(Hash)
300
+ prompt = if options.has_key?("Match")
301
+ options["Match"]
302
+ elsif options.has_key?("Prompt")
303
+ options["Prompt"]
304
+ elsif options.has_key?("String")
305
+ Regexp.new( Regexp.quote(options["String"]) )
306
+ end
307
+ time_out = options["Timeout"] if options.has_key?("Timeout")
308
+ waittime = options["Waittime"] if options.has_key?("Waittime")
309
+ fail_eof = options["FailEOF"] if options.has_key?("FailEOF")
310
+ else
311
+ prompt = options
312
+ end
313
+
314
+ if time_out == false
315
+ time_out = nil
316
+ end
317
+ line = ''
318
+ buf = ''
319
+ rest = ''
320
+ sock = @ssh.transport.socket
321
+ init_time = Time.now.tv_sec
322
+ until prompt === line and ((@eof and @buf == "") or not IO::select([sock], nil, nil, waittime))
323
+ while @buf == "" and !@eof
324
+ # timeout is covered by net-ssh
325
+ @channel.connection.process(0.1)
326
+ # if waittime.to_i > 0
327
+ # exec_time = Time.now.tv_sec - init_time
328
+ # raise Timeout::Error if exec_time > waittime
329
+ # end
330
+ end
331
+ if @buf != ""
332
+ # if waittime.to_i > 0
333
+ # exec_time = Time.now.tv_sec - init_time
334
+ # raise Timeout::Error if exec_time > waittime
335
+ # end
336
+
337
+ c = @buf; @buf = ""
338
+ @dumplog.log_dump('<', c) if @options.has_key?("Dump_log")
339
+ buf = c
340
+ buf.gsub!(/#{EOL}/no, "\n") unless @options["Binmode"]
341
+ rest = ''
342
+ @log.print(buf) if @options.has_key?("Output_log")
343
+ line += buf
344
+ yield buf if block_given?
345
+ elsif @eof # End of file reached
346
+ break if prompt === line
347
+ raise EOFError if fail_eof
348
+ if line == ''
349
+ line = nil
350
+ yield nil if block_given?
351
+ end
352
+ break
353
+ end
354
+ end
355
+ line
356
+ end
357
+
358
+ # Write +string+ to the host.
359
+ #
360
+ # Does not perform any conversions on +string+. Will log +string+ to the
361
+ # dumplog, if the Dump_log option is set.
362
+ def write(string)
363
+ @dumplog.log_dump('>', string) if @options.has_key?("Dump_log")
364
+ @channel.send_data string
365
+ end
366
+
367
+ # Sends a string to the host.
368
+ #
369
+ # This does _not_ automatically append a newline to the string. Embedded
370
+ # newlines may be converted depending upon the values of binmode or
371
+ # terminator.
372
+ def print(string)
373
+ terminator = @options["Terminator"]
374
+
375
+ if @options["Binmode"]
376
+ self.write(string)
377
+ else
378
+ self.write(string.gsub(/\n/n, terminator))
379
+ end
380
+ end
381
+
382
+ # Sends a string to the host.
383
+ #
384
+ # Same as #print(), but appends a newline to the string.
385
+ def puts(string)
386
+ self.print(string + "\n")
387
+ end
388
+
389
+ # Send a command to the host.
390
+ #
391
+ # More exactly, sends a string to the host, and reads in all received
392
+ # data until is sees the prompt or other matched sequence.
393
+ #
394
+ # The command or other string will have the newline sequence appended
395
+ # to it.
396
+
397
+ def cmd(options) # :yield: recvdata
398
+ match = @options["Prompt"]
399
+ time_out = @options["Timeout"]
400
+
401
+ if options.kind_of?(Hash)
402
+ string = options["String"]
403
+ match = options["Match"] if options.has_key?("Match")
404
+ time_out = options["Timeout"] if options.has_key?("Timeout")
405
+ waittime = options["Waittime"] if options.has_key?("Waittime")
406
+ else
407
+ string = options
408
+ end
409
+
410
+ self.puts(string)
411
+ if block_given?
412
+ waitfor({"Prompt" => match, "Timeout" => time_out, "Waittime" => waittime}){|c| yield c }
413
+ else
414
+ waitfor({"Prompt" => match, "Timeout" => time_out, "Waittime" => waittime})
415
+ end
416
+ end
417
+
418
+ end # class Telnet
419
+ end # module SSH
420
+ end # module Net