File: message_channel_spec.rb

package info (click to toggle)
ruby-passenger 3.0.13debian-1%2Bdeb7u2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 15,920 kB
  • sloc: cpp: 99,104; ruby: 18,098; ansic: 9,846; sh: 8,632; python: 141; makefile: 30
file content (196 lines) | stat: -rw-r--r-- 5,360 bytes parent folder | download | duplicates (2)
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
require 'socket'
require 'phusion_passenger/message_channel'

module PhusionPassenger

describe MessageChannel do
	describe "scenarios with a single channel" do
		before :each do
			@reader_pipe, @writer_pipe = IO.pipe
			@reader = MessageChannel.new(@reader_pipe)
			@writer = MessageChannel.new(@writer_pipe)
		end
		
		after :each do
			@reader_pipe.close unless @reader_pipe.closed?
			@writer_pipe.close unless @writer_pipe.closed?
		end
		
		it "can read a single written array message" do
			@writer.write("hello")
			@reader.read.should == ["hello"]
		end
		
		it "can handle array messages that contain spaces" do
			@writer.write("hello world", "! ")
			@reader.read.should == ["hello world", "! "]
		end
		
		it "can handle array messages that have only a single empty string" do
			@writer.write("")
			@reader.read.should == [""]
		end
		
		it "can handle array messages with empty arguments" do
			@writer.write("hello", "", "world")
			@reader.read.should == ["hello", "", "world"]
			
			@writer.write("")
			@reader.read.should == [""]
			
			@writer.write(nil, "foo")
			@reader.read.should == ["", "foo"]
		end
		
		it "properly detects end-of-file when reading an array message" do
			@writer.close
			@reader.read.should be_nil
		end
		
		specify "#read_hash works" do
			@writer.write("hello", "world")
			@reader.read_hash.should == { "hello" => "world" }
			
			@writer.write("hello", "world", "foo", "bar", "", "...")
			@reader.read_hash.should == { "hello" => "world", "foo" => "bar", "" => "..." }
		end
		
		specify "#read_hash throws an exception if the array message doesn't have an even number of items" do
			@writer.write("foo")
			lambda { @reader.read_hash }.should raise_error(MessageChannel::InvalidHashError)
			
			@writer.write("foo", "bar", "baz")
			lambda { @reader.read_hash }.should raise_error(MessageChannel::InvalidHashError)
		end
		
		it "can read a single written scalar message" do
			@writer.write_scalar("hello world")
			@reader.read_scalar.should == "hello world"
		end
		
		it "can handle empty scalar messages" do
			@writer.write_scalar("")
			@reader.read_scalar.should == ""
		end
		
		it "properly detects end-of-file when reading a scalar message" do
			@writer.close
			@reader.read_scalar.should be_nil
		end
		
		it "puts the data into the given buffer" do
			buffer = ''
			@writer.write_scalar("x" * 100)
			result = @reader.read_scalar(buffer)
			result.object_id.should == buffer.object_id
			buffer.should == "x" * 100
		end
		
		it "raises SecurityError when a received scalar message's size is larger than a specified maximum" do
			@writer.write_scalar(" " * 100)
			lambda { @reader.read_scalar('', 99) }.should raise_error(SecurityError)
		end
	end
	
	describe "scenarios with 2 channels and 2 concurrent processes" do
		after :each do
			@parent_socket.close
			Process.waitpid(@pid) rescue nil
		end
		
		def spawn_process
			@parent_socket, @child_socket = UNIXSocket.pair
			@pid = fork do
				@parent_socket.close
				@channel = MessageChannel.new(@child_socket)
				begin
					yield
				rescue Exception => e
					print_exception("child", e)
				ensure
					@child_socket.close
					exit!
				end
			end
			@child_socket.close
			@channel = MessageChannel.new(@parent_socket)
		end
		
		it "both processes can read and write a single array message" do
			spawn_process do
				x = @channel.read
				@channel.write("#{x[0]}!")
			end
			@channel.write("hello")
			@channel.read.should == ["hello!"]
		end
		
		it "can handle scalar messages with arbitrary binary data" do
			garbage_files = ["garbage1.dat", "garbage2.dat", "garbage3.dat"]
			spawn_process do
				garbage_files.each do |name|
					data = File.binread("stub/#{name}")
					@channel.write_scalar(data)
				end
			end
			
			garbage_files.each do |name|
				data = File.binread("stub/#{name}")
				@channel.read_scalar.should == data
			end
		end
		
		it "supports IO object (file descriptor) passing" do
			spawn_process do
				writer = @channel.recv_io
				writer.write("it works")
				writer.close
			end
			reader, writer = IO.pipe
			@channel.send_io(writer)
			writer.close
			reader.read.should == "it works"
			reader.close
		end
		
		it "supports large amounts of data" do
			iterations = 1000
			blob = "123" * 1024
			spawn_process do
				iterations.times do |i|
					@channel.write(blob)
				end
			end
			iterations.times do
				@channel.read.should == [blob]
			end
		end
		
		it "has stream properties" do
			garbage = File.binread("stub/garbage1.dat")
			spawn_process do
				@channel.write("hello", "world")
				@channel.write_scalar(garbage)
				@channel.send_io(STDIN)
				@channel.write_scalar(":-)")
				
				a = @channel.read_scalar
				b = @channel.read
				b << a
				@channel.write(*b)
			end
			@channel.read.should == ["hello", "world"]
			@channel.read_scalar.should == garbage
			@channel.recv_io.close
			@channel.read_scalar.should == ":-)"
			
			@channel.write_scalar("TASTE MY WRATH! ULTIMATE SWORD TECHNIQUE!! DRAGON'S BREATH SL--")
			@channel.write("Uhm, watch your step.", "WAAHH?!", "Calm down, Motoko!!")
			@channel.read.should == ["Uhm, watch your step.", "WAAHH?!", "Calm down, Motoko!!",
				"TASTE MY WRATH! ULTIMATE SWORD TECHNIQUE!! DRAGON'S BREATH SL--"]
		end
	end
end

end # module PhusionPassenger