scriptty 0.5.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/.gitattributes +1 -0
  2. data/.gitignore +3 -0
  3. data/COPYING +674 -0
  4. data/COPYING.LESSER +165 -0
  5. data/README.rdoc +31 -0
  6. data/Rakefile +49 -0
  7. data/VERSION +1 -0
  8. data/bin/scriptty-capture +5 -0
  9. data/bin/scriptty-dump-screens +4 -0
  10. data/bin/scriptty-replay +5 -0
  11. data/bin/scriptty-term-test +4 -0
  12. data/bin/scriptty-transcript-parse +4 -0
  13. data/examples/captures/xterm-overlong-line-prompt.bin +9 -0
  14. data/examples/captures/xterm-vim-session.bin +262 -0
  15. data/examples/demo-capture.rb +19 -0
  16. data/examples/telnet-nego.rb +55 -0
  17. data/lib/scriptty/apps/capture_app/console.rb +104 -0
  18. data/lib/scriptty/apps/capture_app/password_prompt.rb +65 -0
  19. data/lib/scriptty/apps/capture_app.rb +213 -0
  20. data/lib/scriptty/apps/dump_screens_app.rb +166 -0
  21. data/lib/scriptty/apps/replay_app.rb +229 -0
  22. data/lib/scriptty/apps/term_test_app.rb +124 -0
  23. data/lib/scriptty/apps/transcript_parse_app.rb +143 -0
  24. data/lib/scriptty/cursor.rb +39 -0
  25. data/lib/scriptty/exception.rb +38 -0
  26. data/lib/scriptty/expect.rb +392 -0
  27. data/lib/scriptty/multiline_buffer.rb +192 -0
  28. data/lib/scriptty/net/event_loop.rb +610 -0
  29. data/lib/scriptty/screen_pattern/generator.rb +398 -0
  30. data/lib/scriptty/screen_pattern/parser.rb +558 -0
  31. data/lib/scriptty/screen_pattern.rb +104 -0
  32. data/lib/scriptty/term/dg410/dg410-client-escapes.txt +37 -0
  33. data/lib/scriptty/term/dg410/dg410-escapes.txt +82 -0
  34. data/lib/scriptty/term/dg410/parser.rb +162 -0
  35. data/lib/scriptty/term/dg410.rb +489 -0
  36. data/lib/scriptty/term/xterm/xterm-escapes.txt +73 -0
  37. data/lib/scriptty/term/xterm.rb +661 -0
  38. data/lib/scriptty/term.rb +40 -0
  39. data/lib/scriptty/util/fsm/definition_parser.rb +111 -0
  40. data/lib/scriptty/util/fsm/scriptty_fsm_definition.treetop +189 -0
  41. data/lib/scriptty/util/fsm.rb +177 -0
  42. data/lib/scriptty/util/transcript/reader.rb +96 -0
  43. data/lib/scriptty/util/transcript/writer.rb +111 -0
  44. data/test/apps/capture_app_test.rb +123 -0
  45. data/test/apps/transcript_parse_app_test.rb +118 -0
  46. data/test/cursor_test.rb +51 -0
  47. data/test/fsm_definition_parser_test.rb +220 -0
  48. data/test/fsm_test.rb +322 -0
  49. data/test/multiline_buffer_test.rb +275 -0
  50. data/test/net/event_loop_test.rb +402 -0
  51. data/test/screen_pattern/generator_test.rb +408 -0
  52. data/test/screen_pattern/parser_test/explicit_cursor_pattern.txt +14 -0
  53. data/test/screen_pattern/parser_test/explicit_fields.txt +22 -0
  54. data/test/screen_pattern/parser_test/multiple_patterns.txt +42 -0
  55. data/test/screen_pattern/parser_test/simple_pattern.txt +14 -0
  56. data/test/screen_pattern/parser_test/truncated_heredoc.txt +12 -0
  57. data/test/screen_pattern/parser_test/utf16bebom_pattern.bin +0 -0
  58. data/test/screen_pattern/parser_test/utf16lebom_pattern.bin +0 -0
  59. data/test/screen_pattern/parser_test/utf8_pattern.bin +14 -0
  60. data/test/screen_pattern/parser_test/utf8_unix_pattern.bin +14 -0
  61. data/test/screen_pattern/parser_test/utf8bom_pattern.bin +14 -0
  62. data/test/screen_pattern/parser_test.rb +266 -0
  63. data/test/term/dg410/parser_test.rb +139 -0
  64. data/test/term/xterm_test.rb +327 -0
  65. data/test/test_helper.rb +3 -0
  66. data/test/util/transcript/reader_test.rb +131 -0
  67. data/test/util/transcript/writer_test.rb +126 -0
  68. data/test.watchr +29 -0
  69. metadata +175 -0
@@ -0,0 +1,402 @@
1
+ # = Tests for ScripTTY::Net::EventLoop
2
+ # Copyright (C) 2010 Infonium Inc.
3
+ #
4
+ # This file is part of ScripTTY.
5
+ #
6
+ # ScripTTY is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # ScripTTY is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with ScripTTY. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require File.dirname(__FILE__) + "/../test_helper.rb"
20
+
21
+ class EventLoopTest < Test::Unit::TestCase
22
+
23
+ if !defined?(Java)
24
+
25
+ # This test gets executed when JRuby is not detected
26
+ def test_dummy_no_jruby
27
+ raise LoadError.new("Cannot test ScripTTY::Net::EventLoop: Not running under JRuby")
28
+ end
29
+
30
+ else # defined?(Java)
31
+
32
+ # XXX - These tests are ugly, using a mix of different styles. Clean them up (only if you know what you are doing!)
33
+
34
+ require 'scriptty/net/event_loop'
35
+ require 'thread'
36
+ require 'stringio'
37
+
38
+ CONNECTION_REFUSE_ADDR = ['localhost', 2] # address on which connections will be refused
39
+
40
+ def setup
41
+ raise "EventLoopTest disabled" # FIXME
42
+ end
43
+
44
+ def test_callback_error_handling_on_connect_error
45
+ result = []
46
+ evloop = ScripTTY::Net::EventLoop.new
47
+ evloop.connect(CONNECTION_REFUSE_ADDR) { |c|
48
+ c.on_callback_error { result << :callback_error }
49
+ c.on_connect_error { |e| raise "FOO" }
50
+ }
51
+ evloop.main
52
+ assert_equal [:callback_error], result
53
+ end
54
+
55
+ def test_connect_error_handling
56
+ result = []
57
+ connect_error = nil
58
+ evloop = ScripTTY::Net::EventLoop.new
59
+ evloop.connect(CONNECTION_REFUSE_ADDR) { |c|
60
+ c.on_connect { result << :connect }
61
+ c.on_connect_error { |e| result << :connect_error; connect_error = e }
62
+ c.on_receive_bytes { |bytes| result << [:receive_bytes, bytes] }
63
+ c.on_close { result << :closed }
64
+ }
65
+ evloop.main
66
+ assert_equal [:connect_error], result
67
+ assert_match /^java.net.ConnectException: Connection refused/, connect_error.message
68
+ end
69
+
70
+ def test_channel_gets_deregistered_after_close
71
+ selector_keys = nil
72
+ evloop = ScripTTY::Net::EventLoop.new
73
+ evloop.connect(CONNECTION_REFUSE_ADDR) { |c|
74
+ c.on_connect_error { |e|
75
+ # On the next call to select(), the SelectionKey should be gone.
76
+ evloop.timer(0) {
77
+ selector_keys = evloop.instance_eval("@selector").keys.to_a
78
+ }
79
+ }
80
+ }
81
+ evloop.main
82
+ assert_equal [], selector_keys, "Channels should be deregistered with the Selector when a channel is closed"
83
+ end
84
+
85
+ # An listening socket should be closed when the event loop finishes
86
+ def test_listening_socket_gets_closed_on_exit
87
+ # Create an event loop, bind a socket, then exit the event loop.
88
+ evloop = ScripTTY::Net::EventLoop.new
89
+ bind_addr = evloop.listen(['localhost', 0]) { |server| server.local_address }
90
+ evloop.timer(0) { evloop.exit }
91
+ evloop.main
92
+
93
+ # Create another event loop, and attempt to connect to the socket.
94
+ connected = false
95
+ error = false
96
+ evloop = ScripTTY::Net::EventLoop.new
97
+ evloop.connect(bind_addr) { |c|
98
+ c.on_connect { |conn| evloop.exit }
99
+ c.on_connect_error { |e|
100
+ assert_match /^java\.net\.ConnectException: Connection refused/, e.message
101
+ error = true
102
+ }
103
+ }
104
+ evloop.main
105
+ assert error, "sockets should be closed when event loop exits"
106
+ end
107
+
108
+ def test_simple_echo_server
109
+ server_log = []
110
+ client_log = []
111
+ timeout_log = []
112
+ evloop = ScripTTY::Net::EventLoop.new
113
+ bytes_received = ""
114
+ echo_addr = evloop.on_accept(['localhost', 0]) { |conn|
115
+ conn.on_close { server_log << :echo_closed }
116
+ conn.on_receive_bytes { |bytes| bytes_received += bytes; conn.write(bytes) }
117
+ }.local_address
118
+ bytes_to_send = "Hello, world!".split("")
119
+ evloop.on_connect(echo_addr) { |conn|
120
+ client_log << :client_open
121
+ conn.on_close {
122
+ client_log << :client_close
123
+ evloop.exit
124
+ }
125
+ write_next = Proc.new {
126
+ if bytes_to_send.empty?
127
+ client_log << :client_close_graceful
128
+ conn.close
129
+ else
130
+ client_log << :client_write unless client_log.last == :client_write
131
+ conn.write(bytes_to_send.shift, &write_next)
132
+ end
133
+ }
134
+ write_next.call
135
+ }
136
+ evloop.timer(5, :daemon => true) { timeout_log << :TIMEOUT; evloop.exit } # Set 5-second hard timeout for this test
137
+ evloop.main
138
+
139
+ assert_equal "Hello, world!", bytes_received
140
+
141
+ expected_logs = {
142
+ :client => [:client_open, :client_write, :client_close_graceful, :client_close],
143
+ :server => [:echo_closed],
144
+ :timeout => []
145
+ }
146
+
147
+ actual_logs = {
148
+ :client => client_log,
149
+ :server => server_log,
150
+ :timeout => timeout_log,
151
+ }
152
+
153
+ assert_equal expected_logs, actual_logs
154
+ end
155
+
156
+ # Start two sockets and make them talk to each other
157
+ def test_chatter
158
+ evloop = ScripTTY::Net::EventLoop.new
159
+ expected_logs = {}
160
+
161
+ alice_done = bob_done = false # XXX - We should do graceful TCP shutdown here instead.
162
+
163
+ # Alice
164
+ expected_logs[:alice] = [ "accepted", "said hello", "received", "said goodbye", "closed" ]
165
+ alice_log = []
166
+ alice_addr = evloop.on_accept(["localhost", 0]) { |conn|
167
+ alice_log << "accepted"
168
+ conn.write("Hello, my name is Alice. What is your name?\n") { alice_log << "said hello" }
169
+ buffer = ""
170
+ conn.on_receive_bytes { |bytes|
171
+ alice_log << "received" unless alice_log.last == "received"
172
+ buffer += bytes
173
+ if buffer =~ /\A(My name is ([^.]*)\.)$/
174
+ name = $2
175
+ buffer = "" # this would be buggy if we wanted to do more than this
176
+ conn.write("Goodbye, #{name}!\n") { alice_log << "said goodbye"; conn.close }
177
+ end
178
+ }
179
+ conn.on_close { alice_log << "closed"; alice_done = true; evloop.exit if bob_done }
180
+ }.local_address
181
+
182
+ # Bob
183
+ expected_logs[:bob] = [ "connected", "received", "said name", "received", "closed" ]
184
+ bob_log = []
185
+ evloop.on_connect(alice_addr) { |conn|
186
+ bob_log << "connected"
187
+ buffer = ""
188
+ conn.on_receive_bytes { |bytes|
189
+ bob_log << "received" unless bob_log.last == "received"
190
+ buffer += bytes
191
+ if buffer =~ /What is your name\?/
192
+ buffer = "" # this would be buggy if we wanted to do more than this
193
+ conn.write("My name is Bob.\n") { bob_log << "said name" }
194
+ end
195
+ }
196
+ conn.on_close { bob_log << "closed"; bob_done = true; evloop.exit if alice_done }
197
+ }
198
+
199
+ # Set 5-second hard timeout for this test
200
+ timeout = false
201
+ expected_logs[:timeout] = false
202
+ evloop.timer(5, :daemon => true) { timeout = true; evloop.exit }
203
+
204
+ # Execute
205
+ evloop.main
206
+
207
+ # Assertions
208
+ assert_equal(expected_logs, {:timeout => timeout, :alice => alice_log, :bob => bob_log}, "logs not what was expected")
209
+ end
210
+
211
+ def test_run_empty
212
+ net = ScripTTY::Net::EventLoop.new
213
+ net.main
214
+ end
215
+
216
+ # Test ConnectionWrapper#local_address and ConnectionWrapper#remote_address
217
+ def test_local_and_remote_address
218
+ evloop = ScripTTY::Net::EventLoop.new
219
+
220
+ client_log = StringIO.new
221
+ server_log = StringIO.new
222
+ timeout_log = StringIO.new
223
+ #client_log = server_log = timeout_log = $stdout # for debugging
224
+
225
+ server_local_addr = nil
226
+ server_remote_addr = nil
227
+ server = evloop.listen(['localhost', 0])
228
+ server_addr = server.local_address
229
+ server.on_accept { |conn|
230
+ server_log.puts "server_accept"
231
+ conn.on_close { server_log.puts "server_conn_close" }
232
+ server_local_addr = conn.local_address
233
+ server_remote_addr = conn.remote_address
234
+ conn.close
235
+ server.close
236
+ }
237
+ server.on_close { server_log.puts "server_close" }
238
+
239
+ client_local_addr = nil
240
+ client_remote_addr = nil
241
+ client = evloop.connect(server_addr)
242
+ client.on_connect { |conn|
243
+ client_log.puts "client_connect"
244
+ client_local_addr = conn.local_address
245
+ client_remote_addr = conn.remote_address
246
+ conn.close
247
+ }
248
+ client.on_close { client_log.puts "client_close" }
249
+
250
+ # Set 5-second hard timeout for this test
251
+ evloop.timer(5, :daemon => true) { timeout_log.puts "TIMEOUT"; evloop.exit }
252
+
253
+ evloop.main
254
+
255
+ expected_logs = {}
256
+ expected_logs[:server] = %w( server_accept server_close server_conn_close )
257
+ expected_logs[:client] = %w( client_connect client_close )
258
+ expected_logs[:timeout] = []
259
+
260
+ actual_logs = {
261
+ :server => server_log.string.split("\n"),
262
+ :client => client_log.string.split("\n"),
263
+ :timeout => timeout_log.string.split("\n"),
264
+ }
265
+
266
+ assert_equal expected_logs, actual_logs
267
+
268
+ # Checl that the addresses are what we expect
269
+ assert_equal server_addr, server_local_addr, "server_addr should== server_local_addr"
270
+ assert_equal server_addr, client_remote_addr, "server_addr should== client_remote_addr"
271
+ assert_equal server_remote_addr, client_local_addr, "server_remote_addr should== client_local_addr"
272
+ end
273
+
274
+ # Regression test: The event loop should exit on its own when a
275
+ # connection is closed, even if there are no on_close or on_receive_bytes
276
+ # callbacks defined.
277
+ def test_client_and_server_exit_with_minimal_callbacks
278
+ evloop = ScripTTY::Net::EventLoop.new
279
+ server = evloop.listen(['localhost', 0])
280
+ #server.on_accept { |conn| server.close } # stop accepting new connections, but keep the current connection open
281
+ server.on_accept { |conn| server.close; conn.close } # stop accepting new connections, and close the connection
282
+
283
+ client = evloop.connect(server.local_address)
284
+
285
+ # Timeout
286
+ timeout = false
287
+ evloop.timer(5, :daemon => true) { timeout = true; evloop.exit }
288
+
289
+ evloop.main
290
+
291
+ assert !timeout, "TIMEOUT"
292
+ end
293
+
294
+ def test_accept_multiple
295
+ evloop = ScripTTY::Net::EventLoop.new
296
+
297
+ # Listen on two different ports (chosen by the operating system)
298
+ alice_accepted = []
299
+ alice_addrs = evloop.on_accept([['localhost', 0], ['localhost', 0]], :multiple => true) { |conn|
300
+ alice_accepted << conn.local_address
301
+ conn.on_close { conn.master.exit if alice_accepted.length == 2 }
302
+ conn.close
303
+ }.map{|lw| lw.local_address}
304
+
305
+ bob_connected = nil
306
+ bob_addr = evloop.on_connect(alice_addrs[0]) { |conn|
307
+ bob_connected = conn.remote_address
308
+ conn.close
309
+ }.local_address
310
+
311
+ carol_connected = nil
312
+ carol_addr = evloop.on_connect(alice_addrs[1]) { |conn|
313
+ carol_connected = conn.remote_address
314
+ conn.close
315
+ }.local_address
316
+
317
+ # Execute
318
+ evloop.main
319
+
320
+ assert_equal alice_addrs.sort, alice_accepted.sort, "alice should accepted a connection on both ports"
321
+ assert_equal alice_addrs[0], bob_connected, "bob should connect to alice's first port"
322
+ assert_equal alice_addrs[1], carol_connected, "carol should connect to alice's second port"
323
+ end
324
+
325
+ def test_graceful_close
326
+ sequence = []
327
+ evloop = ScripTTY::Net::EventLoop.new
328
+ alice = evloop.listen(['localhost', 0])
329
+ alice.on_accept { |conn|
330
+ alice.close
331
+ conn.on_receive_bytes { |bytes| sequence << :alice_received }
332
+ conn.on_close { sequence << :alice_closed }
333
+ }
334
+ alice.on_close { sequence << :server_closed }
335
+
336
+ bob = evloop.connect(alice.local_address)
337
+ bob.on_connect { |conn|
338
+ conn.on_close { sequence << :bob_closed }
339
+ conn.write("Hello world!") {
340
+ sequence << :bob_wrote
341
+ evloop.timer(0.2) {
342
+ sequence << :bob_closing; conn.close
343
+ }
344
+ }
345
+ }
346
+
347
+ evloop.timer(5, :daemon => true) { sequence << :TIMEOUT; evloop.exit }
348
+
349
+ evloop.main
350
+ # :server_closed and :bob_wrote might happen in reverse order. If that happens, make this assertion smarter.
351
+ assert_equal [:server_closed, :bob_wrote, :alice_received, :bob_closing, :alice_closed, :bob_closed], sequence
352
+ end
353
+
354
+ # The timer should work, and not be too fast (or too slow).
355
+ def test_timer_works
356
+ evloop = ScripTTY::Net::EventLoop.new
357
+ t0 = Time.now
358
+ evloop.timer(1) { true }
359
+ evloop.main
360
+ t1 = Time.now
361
+ delta = t1 - t0
362
+ assert delta >= 1.0, "Timeout too short: #{delta.inspect}" # This should never fail
363
+ assert delta < 5.0, "warning: Timeout too long: #{delta.inspect}" # This might fail on a slow machine
364
+ end
365
+
366
+ # Daemon timers should not prevent the main loop from exiting
367
+ def test_daemon_timers
368
+ evloop = ScripTTY::Net::EventLoop.new
369
+ user_timer_fired = false
370
+ daemon_timer_fired = false
371
+ t0 = Time.now
372
+ evloop.timer(0.002) { user_timer_fired = true }
373
+ evloop.timer(10, :daemon => true) { daemon_timer_fired = true ; evloop.exit }
374
+ evloop.main
375
+ t1 = Time.now
376
+ delta = t1 - t0
377
+ assert user_timer_fired, "user timer should have fired"
378
+ assert !daemon_timer_fired, "daemon timer should not have fired"
379
+ end
380
+
381
+ # A timer with a zero-second delay should get executed immediately.
382
+ def test_timer_with_zero_delay
383
+ result = []
384
+ evloop = ScripTTY::Net::EventLoop.new
385
+ evloop.timer(0) { result << :t }
386
+ evloop.main
387
+ assert_equal [:t], result
388
+ end
389
+
390
+ # Timers should be able to be cancelled.
391
+ def test_timer_cancel
392
+ result = []
393
+ evloop = ScripTTY::Net::EventLoop.new
394
+ b = nil
395
+ a = evloop.timer(0.00001) { result << :a; b.cancel }
396
+ b = evloop.timer(0.00002) { result << :b }
397
+ c = evloop.timer(0.00003) { result << :c }
398
+ evloop.main
399
+ assert_equal [:a, :c], result
400
+ end
401
+ end # defined?(Java)
402
+ end # class