/
streaming_test.rb
150 lines (128 loc) · 3.55 KB
/
streaming_test.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# frozen_string_literal: true
require File.expand_path('../helper', __FILE__)
class StreamingTest < Minitest::Test
Stream = Sinatra::Helpers::Stream
it 'returns the concatenated body' do
mock_app do
get('/') do
stream do |out|
out << "Hello" << " "
out << "World!"
end
end
end
get('/')
assert_body "Hello World!"
end
it 'always yields strings' do
stream = Stream.new { |out| out << :foo }
stream.each { |str| assert_equal 'foo', str }
end
it 'postpones body generation' do
step = 0
stream = Stream.new do |out|
10.times do
out << step
step += 1
end
end
stream.each do |s|
assert_equal s, step.to_s
step += 1
end
end
it 'calls the callback after it is done' do
step = 0
final = 0
stream = Stream.new { |_| 10.times { step += 1 }}
stream.callback { final = step }
stream.each {|_|}
assert_equal 10, final
end
it 'does not trigger the callback if close is set to :keep_open' do
step = 0
final = 0
stream = Stream.new(Stream, :keep_open) { |_| 10.times { step += 1 } }
stream.callback { final = step }
stream.each {|_|}
assert_equal 0, final
end
it 'allows adding more than one callback' do
a = b = false
stream = Stream.new { }
stream.callback { a = true }
stream.callback { b = true }
stream.each {|_| }
assert a, 'should trigger first callback'
assert b, 'should trigger second callback'
end
class MockScheduler
def initialize(*) @schedule, @defer = [], [] end
def schedule(&block) @schedule << block end
def defer(&block) @defer << block end
def schedule!(*) @schedule.pop.call until @schedule.empty? end
def defer!(*) @defer.pop.call until @defer.empty? end
end
it 'allows dropping in another scheduler' do
scheduler = MockScheduler.new
processing = sending = done = false
stream = Stream.new(scheduler) do |out|
processing = true
out << :foo
end
stream.each { sending = true}
stream.callback { done = true }
scheduler.schedule!
assert !processing
assert !sending
assert !done
scheduler.defer!
assert processing
assert !sending
assert !done
scheduler.schedule!
assert sending
assert done
end
it 'schedules exceptions to be raised on the main thread/event loop/...' do
scheduler = MockScheduler.new
Stream.new(scheduler) { fail 'should be caught' }.each { }
scheduler.defer!
assert_raises(RuntimeError) { scheduler.schedule! }
end
it 'does not trigger an infinite loop if you call close in a callback' do
stream = Stream.new { |out| out.callback { out.close }}
stream.each { |_| }
end
it 'gives access to route specific params' do
mock_app do
get('/:name') do
stream { |o| o << params[:name] }
end
end
get '/foo'
assert_body 'foo'
end
it 'sets up async.close if available' do
ran = false
mock_app do
get('/') do
close = Object.new
def close.callback; yield end
def close.errback; end
env['async.close'] = close
stream(:keep_open) do |out|
out.callback { ran = true }
end
end
end
get '/'
assert ran
end
it 'has a public interface to inspect its open/closed state' do
stream = Stream.new(Stream) { |out| out << :foo }
assert !stream.closed?
stream.close
assert stream.closed?
end
end