expect-pty 0.5.3 → 0.6.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +28 -19
- data/docs/API.md +88 -0
- data/docs/MIGRATION.md +15 -0
- data/lib/expect/cleanup.rb +33 -0
- data/lib/expect/configuration.rb +87 -66
- data/lib/expect/interaction.rb +226 -209
- data/lib/expect/logging.rb +157 -156
- data/lib/expect/matcher.rb +30 -29
- data/lib/expect/pattern.rb +8 -2
- data/lib/expect/pattern_list.rb +35 -5
- data/lib/expect/redactor.rb +11 -4
- data/lib/expect/relay.rb +97 -73
- data/lib/expect/relay_writer.rb +2 -1
- data/lib/expect/result.rb +32 -8
- data/lib/expect/session.rb +508 -0
- data/lib/expect/session_resources.rb +10 -2
- data/lib/expect/terminal.rb +78 -78
- data/lib/expect/version.rb +1 -1
- data/lib/expect.rb +333 -520
- data/sig/expect.rbs +355 -0
- metadata +13 -106
- data/.rubocop.yml +0 -66
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -28
- data/Rakefile +0 -38
- data/benchmark/matching.rb +0 -98
- data/benchmark/redactor.rb +0 -64
- data/benchmark/relay.rb +0 -48
- data/benchmark/scaling.rb +0 -70
- data/benchmark/send_slow.rb +0 -41
- data/benchmark/support.rb +0 -114
- data/docs/COMPATIBILITY.md +0 -98
- data/docs/INTERNAL_CONTRACTS.md +0 -154
- data/docs/PERFORMANCE.md +0 -165
- data/docs/RELEASING.md +0 -84
- data/docs/VERIFICATION.md +0 -456
- data/examples/dialogue.rb +0 -30
- data/examples/kibitz/README.md +0 -81
- data/examples/kibitz/kibitz.rb +0 -142
- data/examples/kibitz/test_kibitz.rb +0 -37
- data/examples/ssh_auto.rb +0 -94
- data/examples/ssh_interact.rb +0 -159
- data/examples/ssh_login.rb +0 -64
- data/expect-pty.gemspec +0 -33
- data/script/ci +0 -121
- data/script/release.rb +0 -319
- data/test/buffer_accounting_test.rb +0 -83
- data/test/cleanup_test.rb +0 -251
- data/test/compare_upstream.rb +0 -157
- data/test/configuration_test.rb +0 -133
- data/test/deadline_test.rb +0 -237
- data/test/diagnostics_test.rb +0 -353
- data/test/edge_case_test.rb +0 -262
- data/test/fixtures/ssh_scripts/01_identity.sh +0 -4
- data/test/fixtures/ssh_scripts/02_output.sh +0 -5
- data/test/fixtures/ssh_scripts/03_delayed.sh +0 -6
- data/test/fixtures/ssh_scripts/04_failure.sh +0 -2
- data/test/fixtures/ssh_scripts/05_recovery.sh +0 -3
- data/test/initialization_failure_test.rb +0 -92
- data/test/integration/README.md +0 -109
- data/test/integration/ssh_scripts.rb +0 -94
- data/test/interact_test.rb +0 -253
- data/test/interconnect_test.rb +0 -425
- data/test/io_test.rb +0 -321
- data/test/kibitz_test.rb +0 -45
- data/test/lifecycle_contract_test.rb +0 -71
- data/test/literal_scan_test.rb +0 -97
- data/test/matching_test.rb +0 -211
- data/test/multi_session_test.rb +0 -66
- data/test/ownership_sequence_test.rb +0 -208
- data/test/pattern_offset_test.rb +0 -43
- data/test/process_interruption_test.rb +0 -296
- data/test/process_test.rb +0 -291
- data/test/redactor_test.rb +0 -183
- data/test/relay_recovery_test.rb +0 -451
- data/test/relay_reentrancy_test.rb +0 -159
- data/test/release_test.rb +0 -375
- data/test/ruby_api_test.rb +0 -515
- data/test/scan_reuse_test.rb +0 -109
- data/test/script_logging_test.rb +0 -124
- data/test/support/interact_probe.rb +0 -125
- data/test/support/kibitz_probe.rb +0 -177
- data/test/support/script_probe.rb +0 -158
- data/test/terminal_cleanup_test.rb +0 -349
- data/test/test_helper.rb +0 -58
- data/test/timeout_test.rb +0 -289
- data/test/write_contract_test.rb +0 -105
data/test/ruby_api_test.rb
DELETED
|
@@ -1,515 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "test_helper"
|
|
4
|
-
|
|
5
|
-
class RubyAPITest < ExpectTest
|
|
6
|
-
def test_result_uses_native_struct_conversion_and_pattern_matching
|
|
7
|
-
session, = pipe_session
|
|
8
|
-
session.buffer = "before value=42 after"
|
|
9
|
-
session.expect(/value=(\d+)/, timeout: 0)
|
|
10
|
-
result = session.last_result
|
|
11
|
-
values = [1, nil, "value=42", "before ", " after", session, ["42"]]
|
|
12
|
-
|
|
13
|
-
assert_equal values, result.to_a
|
|
14
|
-
assert_equal values, result.deconstruct
|
|
15
|
-
assert_equal values, Array(result)
|
|
16
|
-
assert_equal ["42"], result.to_h.fetch(:captures)
|
|
17
|
-
refute_respond_to result, :to_ary
|
|
18
|
-
number, error, match, before, after, connection, captures = result.to_a
|
|
19
|
-
assert_equal values, [number, error, match, before, after, connection, captures]
|
|
20
|
-
assert_pattern { result => { number: 1, captures: ["42"] } }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def test_send_retains_ruby_reflection_and_write_sends_bytes
|
|
24
|
-
client, server = Socket.pair(:UNIX, :STREAM, 0)
|
|
25
|
-
@ios.push(client, server)
|
|
26
|
-
Expect.open(client) do |session|
|
|
27
|
-
session.buffer = "ready"
|
|
28
|
-
assert_equal "ready", session.send(:buffer)
|
|
29
|
-
assert_nil session.send(:timeout=, nil)
|
|
30
|
-
assert_equal 3, session.send(:write, "abc")
|
|
31
|
-
assert_equal("abc", bounded { server.read(3) })
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def test_concise_expect_dsl_drives_a_dialogue_without_a_result_object
|
|
36
|
-
session = child('print "name: "; puts "hello " + gets.strip', raw_pty: true)
|
|
37
|
-
name = "Ruby"
|
|
38
|
-
matched = session.expect(timeout: 2) do
|
|
39
|
-
on("name: ") do |connection|
|
|
40
|
-
connection.puts(name)
|
|
41
|
-
connection.continue
|
|
42
|
-
end
|
|
43
|
-
on(/hello (\w+)/)
|
|
44
|
-
end
|
|
45
|
-
assert_equal 2, matched
|
|
46
|
-
assert_equal "hello Ruby", session.match
|
|
47
|
-
assert_equal ["Ruby"], session.captures
|
|
48
|
-
assert_equal 0, session.wait(timeout: 1).exitstatus
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def test_concise_dsl_supports_events_multi_session_and_result_objects
|
|
52
|
-
first, writer = pipe_session
|
|
53
|
-
second, = pipe_session
|
|
54
|
-
writer.close
|
|
55
|
-
ended = []
|
|
56
|
-
timed_out = []
|
|
57
|
-
result = Expect.expect_result(timeout: 0.02) do
|
|
58
|
-
eof(from: first) do |connection|
|
|
59
|
-
ended << connection
|
|
60
|
-
connection.continue(reset_timeout: false)
|
|
61
|
-
end
|
|
62
|
-
on("missing", from: second)
|
|
63
|
-
timeout { |sessions| timed_out.concat(sessions) }
|
|
64
|
-
end
|
|
65
|
-
assert result.timeout?
|
|
66
|
-
assert_equal [first], ended
|
|
67
|
-
assert_equal [second], timed_out
|
|
68
|
-
assert_same second, result.session
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def test_explicit_block_parameter_preserves_the_callers_self
|
|
72
|
-
session, = pipe_session
|
|
73
|
-
session.buffer = "ready"
|
|
74
|
-
owner = self
|
|
75
|
-
matched = session.expect(timeout: 0) do |patterns|
|
|
76
|
-
assert_same owner, self
|
|
77
|
-
patterns.on("ready") { assert_same owner, self }
|
|
78
|
-
end
|
|
79
|
-
assert_equal 1, matched
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def test_optional_block_parameter_still_receives_the_pattern_builder
|
|
83
|
-
session, = pipe_session
|
|
84
|
-
session.buffer = "ready"
|
|
85
|
-
matched = session.expect(timeout: 0) { |patterns = nil| patterns.on("ready") }
|
|
86
|
-
assert_equal 1, matched
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def test_break_from_a_concise_definition_does_not_consume_input
|
|
90
|
-
session, writer = pipe_session
|
|
91
|
-
writer.write("ready")
|
|
92
|
-
result = session.expect(timeout: 0) do
|
|
93
|
-
on("ready")
|
|
94
|
-
break :cancelled
|
|
95
|
-
end
|
|
96
|
-
assert_equal :cancelled, result
|
|
97
|
-
assert_nil session.last_result
|
|
98
|
-
assert_equal "ready", session.to_io.read_nonblock(5)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def test_exception_in_a_concise_definition_does_not_consume_input
|
|
102
|
-
session, writer = pipe_session
|
|
103
|
-
writer.write("ready")
|
|
104
|
-
assert_raises(RuntimeError) do
|
|
105
|
-
session.expect(timeout: 0) do
|
|
106
|
-
on("ready")
|
|
107
|
-
raise "definition failed"
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
assert_equal "ready", session.to_io.read_nonblock(5)
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def test_block_patterns_drive_a_real_dialogue_with_captured_variables
|
|
114
|
-
session = child(<<~'RUBY', raw_pty: true)
|
|
115
|
-
print "name: "
|
|
116
|
-
name = gets.strip
|
|
117
|
-
print "code: "
|
|
118
|
-
puts "hello #{name}:#{gets.strip}"
|
|
119
|
-
RUBY
|
|
120
|
-
name = "Ruby"
|
|
121
|
-
result = session.expect_result(timeout: 2) do |patterns|
|
|
122
|
-
patterns.on("name: ") do |connection|
|
|
123
|
-
connection.puts(name)
|
|
124
|
-
connection.continue
|
|
125
|
-
end
|
|
126
|
-
patterns.on("code: ") do |connection|
|
|
127
|
-
connection.send_slow("123\n", delay: 0)
|
|
128
|
-
connection.continue(reset_timeout: false)
|
|
129
|
-
end
|
|
130
|
-
patterns.on(/hello (\w+):(\d+)/)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
assert_equal 3, result.number
|
|
134
|
-
assert_equal %w[Ruby 123], result.captures
|
|
135
|
-
assert_same session, result.session
|
|
136
|
-
assert_equal 0, session.wait(timeout: 1).exitstatus
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def test_keyword_timeout_and_default_timeout
|
|
140
|
-
session, writer = pipe_session
|
|
141
|
-
session.timeout = 0
|
|
142
|
-
assert session.expect_result("missing").timeout?
|
|
143
|
-
assert session.expect_result("missing", timeout: 0.01).timeout?
|
|
144
|
-
background do
|
|
145
|
-
sleep 0.02
|
|
146
|
-
writer.write("ready")
|
|
147
|
-
end
|
|
148
|
-
assert_equal(1, bounded { session.expect("ready", timeout: nil) })
|
|
149
|
-
assert_equal 0, session.timeout
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def test_block_uses_keyword_timeout_and_returns_pattern_number
|
|
153
|
-
session, = pipe_session
|
|
154
|
-
session.buffer = "ready"
|
|
155
|
-
number = session.expect(timeout: 0) { |patterns| patterns.on("ready") }
|
|
156
|
-
assert_equal 1, number
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def test_literal_patterns_are_unambiguous_even_with_callbacks
|
|
160
|
-
%w[-i -ex -unknown timeout eof a.c].each do |literal|
|
|
161
|
-
session, = pipe_session
|
|
162
|
-
session.buffer = "prefix #{literal} suffix"
|
|
163
|
-
observed = nil
|
|
164
|
-
result = session.expect_result(timeout: 0) do |patterns|
|
|
165
|
-
patterns.on(literal) { |connection| observed = connection.match }
|
|
166
|
-
end
|
|
167
|
-
assert result.matched?, literal
|
|
168
|
-
assert_equal literal, observed
|
|
169
|
-
assert_equal "prefix ", result.before
|
|
170
|
-
assert_equal " suffix", session.buffer
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def test_positional_strings_are_literal_and_do_not_parse_command_line_flags
|
|
175
|
-
%w[-i -ex -re timeout eof].each do |literal|
|
|
176
|
-
session, = pipe_session
|
|
177
|
-
session.buffer = "before #{literal} after"
|
|
178
|
-
result = session.expect_result(literal, timeout: 0)
|
|
179
|
-
assert result.matched?
|
|
180
|
-
assert_equal literal, result.match
|
|
181
|
-
assert_equal " after", session.buffer
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def test_duplicate_timeout_callbacks_are_rejected_before_reading
|
|
186
|
-
session, writer = pipe_session
|
|
187
|
-
writer.write("ready")
|
|
188
|
-
assert_raises(ArgumentError) do
|
|
189
|
-
session.expect(timeout: 0) do
|
|
190
|
-
timeout { nil }
|
|
191
|
-
timeout { nil }
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
assert_equal "ready", session.to_io.read_nonblock(5)
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def test_timeout_callback_receives_all_active_sessions
|
|
198
|
-
first, = pipe_session
|
|
199
|
-
second, = pipe_session
|
|
200
|
-
observed = nil
|
|
201
|
-
Expect.expect(timeout: 0) do
|
|
202
|
-
on("first", from: first)
|
|
203
|
-
on("second", from: second)
|
|
204
|
-
timeout { |sessions| observed = sessions }
|
|
205
|
-
end
|
|
206
|
-
assert_equal [first, second], observed
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
def test_patterns_keep_declaration_priority
|
|
210
|
-
session, = pipe_session
|
|
211
|
-
session.buffer = "second first"
|
|
212
|
-
result = session.expect_result(timeout: 0) do |patterns|
|
|
213
|
-
patterns.on("first")
|
|
214
|
-
patterns.on("second")
|
|
215
|
-
end
|
|
216
|
-
assert_equal 1, result.number
|
|
217
|
-
assert_equal "second ", result.before
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def test_invalid_patterns_and_positional_timeouts_do_not_consume_input
|
|
221
|
-
session, writer = pipe_session
|
|
222
|
-
writer.write("ready")
|
|
223
|
-
[nil, 0].each do |timeout|
|
|
224
|
-
assert_raises(ArgumentError) { session.expect(timeout, "ready", timeout: 1) }
|
|
225
|
-
assert_raises(ArgumentError) { Expect.expect(timeout, "-i", session, "ready", timeout: 1) }
|
|
226
|
-
end
|
|
227
|
-
assert_raises(ArgumentError) { session.expect("ready", timeout: 0) { |patterns| patterns.on("ready") } }
|
|
228
|
-
assert_raises(ArgumentError) { session.expect(timeout: 0) { |patterns| patterns.on(:eof) } }
|
|
229
|
-
assert_raises(ArgumentError) { session.expect(timeout: 0) { |patterns| patterns.on("ready", from: []) } }
|
|
230
|
-
assert_raises(ArgumentError) { Expect.expect(timeout: 0) { |patterns| patterns.on("ready") } }
|
|
231
|
-
assert_raises(ArgumentError) { Expect.expect(timeout: 0) { |patterns| patterns.timeout { nil } } }
|
|
232
|
-
assert_raises(ArgumentError) { session.expect(timeout: -1) { flunk "invalid timeout must fail before the block" } }
|
|
233
|
-
assert_empty session.buffer
|
|
234
|
-
assert_equal "ready", session.to_io.read_nonblock(5)
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def test_definition_exception_does_not_run_callbacks_or_consume_input
|
|
238
|
-
session, writer = pipe_session
|
|
239
|
-
writer.write("ready")
|
|
240
|
-
assert_raises(RuntimeError) do
|
|
241
|
-
session.expect(timeout: 0) do |patterns|
|
|
242
|
-
patterns.on("ready") { flunk "callbacks must wait until configuration finishes" }
|
|
243
|
-
raise "configuration failed"
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
assert_nil session.last_result
|
|
247
|
-
assert_equal "ready", session.to_io.read_nonblock(5)
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def test_callback_exception_preserves_the_match_and_unread_buffer
|
|
251
|
-
session, = pipe_session
|
|
252
|
-
session.buffer = "ready tail"
|
|
253
|
-
error = assert_raises(RuntimeError) do
|
|
254
|
-
session.expect(timeout: 0) do |patterns|
|
|
255
|
-
patterns.on("ready") { raise "callback failed" }
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
assert_equal "callback failed", error.message
|
|
259
|
-
assert_equal "ready", session.match
|
|
260
|
-
assert_equal " tail", session.buffer
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def test_timeout_callback_can_continue_with_active_sessions
|
|
264
|
-
session, = pipe_session
|
|
265
|
-
observed = nil
|
|
266
|
-
result = session.expect_result(timeout: 0) do |patterns|
|
|
267
|
-
patterns.on("ready")
|
|
268
|
-
patterns.timeout do |sessions|
|
|
269
|
-
observed = sessions
|
|
270
|
-
session.buffer = "ready"
|
|
271
|
-
Expect.continue
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
assert_equal [session], observed
|
|
275
|
-
assert_equal 1, result.number
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
def test_continuation_without_reset_observes_the_deadline
|
|
279
|
-
session, = pipe_session
|
|
280
|
-
session.buffer = "ready"
|
|
281
|
-
result = bounded(1) do
|
|
282
|
-
session.expect_result(timeout: 0.01) do |patterns|
|
|
283
|
-
patterns.on(/(?=ready)/) { Expect.continue(reset_timeout: false) }
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
assert result.timeout?
|
|
287
|
-
assert_equal "ready", session.buffer
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def test_eof_block_receives_the_session_and_remaining_bytes
|
|
291
|
-
session, writer = pipe_session
|
|
292
|
-
writer.write("tail")
|
|
293
|
-
writer.close
|
|
294
|
-
observed = nil
|
|
295
|
-
result = session.expect_result(timeout: 1) do |patterns|
|
|
296
|
-
patterns.eof { |connection| observed = [connection, connection.before] }
|
|
297
|
-
end
|
|
298
|
-
assert result.eof?
|
|
299
|
-
assert_equal [session, "tail"], observed
|
|
300
|
-
assert_empty session.buffer
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
def test_class_block_routes_patterns_to_multiple_sessions
|
|
304
|
-
first, = pipe_session
|
|
305
|
-
second, writer = pipe_session
|
|
306
|
-
writer.write("ready:42")
|
|
307
|
-
result = Expect.expect_result(timeout: 1) do |patterns|
|
|
308
|
-
patterns.on("missing", from: first)
|
|
309
|
-
patterns.on(/ready:(\d+)/, from: [first, second])
|
|
310
|
-
end
|
|
311
|
-
assert_equal 2, result.number
|
|
312
|
-
assert_same second, result.session
|
|
313
|
-
assert_equal ["42"], result.captures
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
def test_class_keyword_from_selects_a_session
|
|
317
|
-
session, writer = pipe_session
|
|
318
|
-
writer.write("ready")
|
|
319
|
-
assert_equal 1, Expect.expect("ready", from: session, timeout: 1)
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
def test_eof_continuation_keeps_other_sessions_active
|
|
323
|
-
first, first_writer = pipe_session
|
|
324
|
-
second, second_writer = pipe_session
|
|
325
|
-
first_writer.close
|
|
326
|
-
observed = []
|
|
327
|
-
result = Expect.expect_result(timeout: 1) do |patterns|
|
|
328
|
-
patterns.eof(from: first) do |connection|
|
|
329
|
-
observed << connection
|
|
330
|
-
second_writer.write("ready")
|
|
331
|
-
connection.continue(reset_timeout: false)
|
|
332
|
-
end
|
|
333
|
-
patterns.on("ready", from: second)
|
|
334
|
-
end
|
|
335
|
-
assert_equal [first], observed
|
|
336
|
-
assert_same second, result.session
|
|
337
|
-
assert_equal 2, result.number
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
def test_timeout_before_class_patterns_receives_active_sessions
|
|
341
|
-
session, = pipe_session
|
|
342
|
-
observed = nil
|
|
343
|
-
result = Expect.expect_result(timeout: 0) do |patterns|
|
|
344
|
-
patterns.timeout { |sessions| observed = sessions }
|
|
345
|
-
patterns.on("missing", from: session)
|
|
346
|
-
end
|
|
347
|
-
assert result.timeout?
|
|
348
|
-
assert_equal [session], observed
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
def test_open_returns_block_value_and_preserves_borrowed_io
|
|
352
|
-
reader, writer = IO.pipe
|
|
353
|
-
@ios.push(reader, writer)
|
|
354
|
-
connection = nil
|
|
355
|
-
result = Expect.open(reader) do |session|
|
|
356
|
-
connection = session
|
|
357
|
-
:finished
|
|
358
|
-
end
|
|
359
|
-
assert_equal :finished, result
|
|
360
|
-
assert connection.closed?
|
|
361
|
-
refute reader.closed?
|
|
362
|
-
refute writer.closed?
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
def test_open_closes_both_owned_handles_on_exception
|
|
366
|
-
reader, writer = IO.pipe
|
|
367
|
-
@ios.push(reader, writer)
|
|
368
|
-
assert_raises(RuntimeError) do
|
|
369
|
-
Expect.open(reader, writer: writer, own: true) { raise "stop" }
|
|
370
|
-
end
|
|
371
|
-
assert reader.closed?
|
|
372
|
-
assert writer.closed?
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
def test_invalid_open_options_close_owned_io_and_preserve_borrowed_io
|
|
376
|
-
[false, true].each do |own|
|
|
377
|
-
reader, writer = IO.pipe
|
|
378
|
-
@ios.push(reader, writer)
|
|
379
|
-
assert_raises(ArgumentError) do
|
|
380
|
-
Expect.open(reader, writer: writer, own: own, unknown: true) { flunk "invalid options" }
|
|
381
|
-
end
|
|
382
|
-
assert_equal own, reader.closed?
|
|
383
|
-
assert_equal own, writer.closed?
|
|
384
|
-
end
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
def test_boolean_predicates_use_ruby_truthiness_and_have_no_write_arguments
|
|
388
|
-
session, = pipe_session
|
|
389
|
-
refute session.log_stdout?
|
|
390
|
-
session.log_stdout = true
|
|
391
|
-
assert session.log_stdout?
|
|
392
|
-
session.log_stdout = 0
|
|
393
|
-
assert session.log_stdout?
|
|
394
|
-
session.log_stdout = nil
|
|
395
|
-
refute session.log_stdout?
|
|
396
|
-
assert_raises(ArgumentError) { session.log_stdout?(true) }
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
def test_readiness_accepts_an_empty_group_with_a_keyword_timeout
|
|
400
|
-
assert_empty Expect.readable_sessions(timeout: 0)
|
|
401
|
-
end
|
|
402
|
-
|
|
403
|
-
def test_spawn_closes_on_nonlocal_block_exit
|
|
404
|
-
connection = nil
|
|
405
|
-
result = Expect.spawn(RbConfig.ruby, "--disable-gems", "-e", "sleep 60", log_stdout: false) do |session|
|
|
406
|
-
connection = session
|
|
407
|
-
break :finished
|
|
408
|
-
end
|
|
409
|
-
assert_equal :finished, result
|
|
410
|
-
assert connection.closed?
|
|
411
|
-
refute connection.alive?
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
def test_buffer_setter_copies_bytes_and_applies_the_limit
|
|
415
|
-
session, = pipe_session(buffer_limit: 4)
|
|
416
|
-
value = +"before tail"
|
|
417
|
-
session.buffer = value
|
|
418
|
-
value.replace("changed")
|
|
419
|
-
session.buffer.clear
|
|
420
|
-
assert_equal "tail", session.buffer
|
|
421
|
-
assert_equal Encoding::BINARY, session.buffer.encoding
|
|
422
|
-
assert_equal "tail", session.clear_buffer
|
|
423
|
-
assert_empty session.buffer
|
|
424
|
-
assert_raises(ArgumentError) { session.buffer = nil }
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
def test_listeners_setter_takes_a_snapshot_and_validates_before_replacement
|
|
428
|
-
session, writer = pipe_session
|
|
429
|
-
output = StringIO.new
|
|
430
|
-
listeners = [output]
|
|
431
|
-
session.listeners = listeners
|
|
432
|
-
listeners.clear
|
|
433
|
-
session.listeners.clear
|
|
434
|
-
assert_raises(ArgumentError) { session.listeners = [Object.new] }
|
|
435
|
-
writer.write("ready")
|
|
436
|
-
session.expect("ready", timeout: 1)
|
|
437
|
-
assert_equal "ready", output.string
|
|
438
|
-
session.listeners = []
|
|
439
|
-
assert_empty session.listeners
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
def test_log_block_and_setter_receive_only_read_bytes
|
|
443
|
-
session, writer = pipe_session
|
|
444
|
-
chunks = []
|
|
445
|
-
session.log_to { |bytes| chunks << bytes }
|
|
446
|
-
assert_raises(ArgumentError) { session.log_to(StringIO.new) { nil } }
|
|
447
|
-
writer.write("first")
|
|
448
|
-
session.expect("first", timeout: 1)
|
|
449
|
-
output = StringIO.new
|
|
450
|
-
session.log_output = output
|
|
451
|
-
writer.write("second")
|
|
452
|
-
session.expect("second", timeout: 1)
|
|
453
|
-
session.log_output = nil
|
|
454
|
-
assert_equal "first", chunks.join
|
|
455
|
-
assert_equal "second", output.string
|
|
456
|
-
refute output.closed?
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
def test_puts_matches_standard_ruby_io_for_nested_and_recursive_arrays
|
|
460
|
-
client, server = Socket.pair(:UNIX, :STREAM, 0)
|
|
461
|
-
@ios.push(client, server)
|
|
462
|
-
recursive = ["once"]
|
|
463
|
-
recursive << recursive
|
|
464
|
-
values = [nil, 42, "中文", "already\n", ["nested", ["last"]], recursive, "\xff".b]
|
|
465
|
-
expected = StringIO.new("".b)
|
|
466
|
-
expected.puts
|
|
467
|
-
expected.puts(*values)
|
|
468
|
-
Expect.open(client) do |session|
|
|
469
|
-
assert_nil session.puts
|
|
470
|
-
assert_nil session.puts(*values)
|
|
471
|
-
end
|
|
472
|
-
assert_equal(expected.string, bounded { server.read(expected.string.bytesize) })
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
def test_assigning_the_current_log_preserves_file_ownership
|
|
476
|
-
session, = pipe_session
|
|
477
|
-
Dir.mktmpdir do |directory|
|
|
478
|
-
log = session.log_to(File.join(directory, "session.log"))
|
|
479
|
-
session.log_output = log
|
|
480
|
-
session.close
|
|
481
|
-
assert log.closed?
|
|
482
|
-
end
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
def test_write_converts_objects_and_append_returns_the_session
|
|
486
|
-
client, server = Socket.pair(:UNIX, :STREAM, 0)
|
|
487
|
-
@ios.push(client, server)
|
|
488
|
-
values = [nil, 42, %w[a b]]
|
|
489
|
-
expected = StringIO.new
|
|
490
|
-
count = expected.write(*values)
|
|
491
|
-
Expect.open(client) do |session|
|
|
492
|
-
assert_equal count, session.write(*values)
|
|
493
|
-
assert_same session, session << "tail" << "\n"
|
|
494
|
-
end
|
|
495
|
-
expected << "tail\n"
|
|
496
|
-
assert_equal(expected.string, bounded { server.read(expected.string.bytesize) })
|
|
497
|
-
end
|
|
498
|
-
|
|
499
|
-
def test_sequence_block_filters_escape_and_keeps_trailing_bytes
|
|
500
|
-
session, writer = pipe_session
|
|
501
|
-
output = StringIO.new
|
|
502
|
-
session.listeners = [output]
|
|
503
|
-
observed = []
|
|
504
|
-
value = :done
|
|
505
|
-
session.on_sequence("STOP") do
|
|
506
|
-
observed << value
|
|
507
|
-
false
|
|
508
|
-
end
|
|
509
|
-
writer.write("beforeSTOPafter")
|
|
510
|
-
assert_same session, Expect.interconnect(session, timeout: 1)
|
|
511
|
-
assert_equal [:done], observed
|
|
512
|
-
assert_equal "before", output.string
|
|
513
|
-
assert_equal "after", session.buffer
|
|
514
|
-
end
|
|
515
|
-
end
|
data/test/scan_reuse_test.rb
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "test_helper"
|
|
4
|
-
|
|
5
|
-
class ScanReuseTest < ExpectTest
|
|
6
|
-
def test_each_scan_snapshots_a_session_once_across_non_adjacent_groups
|
|
7
|
-
first, = pipe_session
|
|
8
|
-
second, = pipe_session
|
|
9
|
-
first.buffer = "ready"
|
|
10
|
-
list = Expect::PatternList.new
|
|
11
|
-
list.on("missing", from: first).on("other", from: second).on("ready", from: first)
|
|
12
|
-
matcher = Expect::Matcher.new(list, 0)
|
|
13
|
-
snapshots = 0
|
|
14
|
-
original = first.method(:buffer)
|
|
15
|
-
first.stub(:buffer, lambda {
|
|
16
|
-
snapshots += 1
|
|
17
|
-
original.call
|
|
18
|
-
}) do
|
|
19
|
-
2.times do
|
|
20
|
-
result = matcher.__send__(:find_match)
|
|
21
|
-
assert_same first, result[0]
|
|
22
|
-
assert_equal 3, result[1].number
|
|
23
|
-
assert_equal [0, 5, []], result[2]
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
assert_equal 2, snapshots
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def test_callback_replacement_and_nested_match_are_visible_on_the_next_scan
|
|
30
|
-
first, = pipe_session
|
|
31
|
-
second, = pipe_session
|
|
32
|
-
first.buffer = "start"
|
|
33
|
-
events = []
|
|
34
|
-
result = Expect.expect_result(timeout: 0) do
|
|
35
|
-
on("start", from: first) do |session|
|
|
36
|
-
events << session.match
|
|
37
|
-
session.buffer = "nested finish"
|
|
38
|
-
session.expect("nested ", timeout: 0)
|
|
39
|
-
events << session.match
|
|
40
|
-
session.continue
|
|
41
|
-
end
|
|
42
|
-
on("missing", from: second)
|
|
43
|
-
on("finish", from: first) { |session| events << session.match }
|
|
44
|
-
end
|
|
45
|
-
assert_equal 3, result.number
|
|
46
|
-
assert_equal ["start", "nested ", "finish"], events
|
|
47
|
-
assert_empty first.buffer
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def test_shared_io_keeps_first_session_and_identity_despite_equal_hashes
|
|
51
|
-
first, writer = pipe_session
|
|
52
|
-
second = Expect.open(first.to_io)
|
|
53
|
-
@sessions << second
|
|
54
|
-
other, other_writer = pipe_session
|
|
55
|
-
# IO 的业务相等性不能替代 IO.select 返回对象的身份。
|
|
56
|
-
[first.to_io, other.to_io].each do |io|
|
|
57
|
-
io.define_singleton_method(:hash) { 0 }
|
|
58
|
-
io.define_singleton_method(:eql?) { |_other| true }
|
|
59
|
-
end
|
|
60
|
-
writer.write("first")
|
|
61
|
-
other_writer.write("other")
|
|
62
|
-
result = Expect.expect_result(timeout: 0) do
|
|
63
|
-
on("missing", from: [first, second, other])
|
|
64
|
-
on("other", from: other)
|
|
65
|
-
end
|
|
66
|
-
assert_same other, result.session
|
|
67
|
-
assert_equal "first", first.buffer
|
|
68
|
-
assert_empty second.buffer
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def test_regexps_share_one_combined_text_and_literal_only_does_not_build_it
|
|
72
|
-
session, = pipe_session
|
|
73
|
-
session.listeners = [StringIO.new]
|
|
74
|
-
[0, 4].each do |count|
|
|
75
|
-
session.__send__(:sequences=, {})
|
|
76
|
-
count.times { |index| session.on_sequence(/missing#{index}/) }
|
|
77
|
-
session.on_sequence("STOP")
|
|
78
|
-
history = session.__send__(:relay_history)
|
|
79
|
-
concatenations = 0
|
|
80
|
-
original = history.method(:+)
|
|
81
|
-
history.define_singleton_method(:+) do |other|
|
|
82
|
-
concatenations += 1
|
|
83
|
-
original.call(other)
|
|
84
|
-
end
|
|
85
|
-
assert Expect.__send__(:relay_buffer, session, { session => "payload".b })
|
|
86
|
-
assert_equal count.zero? ? 0 : 1, concatenations
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def test_escape_callback_can_change_rules_before_rescanning
|
|
91
|
-
session, = pipe_session
|
|
92
|
-
output = StringIO.new
|
|
93
|
-
session.listeners = [output]
|
|
94
|
-
events = []
|
|
95
|
-
session.on_sequence(/ONE/) do
|
|
96
|
-
events << :one
|
|
97
|
-
session.on_sequence(/TWO/) do
|
|
98
|
-
events << :two
|
|
99
|
-
false
|
|
100
|
-
end
|
|
101
|
-
true
|
|
102
|
-
end
|
|
103
|
-
buffers = { session => "aONEbTWOtail".b }
|
|
104
|
-
refute Expect.__send__(:relay_buffer, session, buffers)
|
|
105
|
-
assert_equal %i[one two], events
|
|
106
|
-
assert_equal "ab", output.string
|
|
107
|
-
assert_equal "tail", buffers.fetch(session)
|
|
108
|
-
end
|
|
109
|
-
end
|