OSDN Git Service

Adjust sleep intervals to get more stable test results
[shogi-server/shogi-server.git] / test / baseclient.rb
1 require 'socket'
2 require 'stringio'
3 require 'thread'
4 require 'test/unit'
5
6 class SocketPlayer
7   def initialize(game_name, name, sente)
8     @game_name = game_name
9     @name = "%s_%s" % [game_name, name]
10     if sente == "*"
11       @turn_mark = sente
12     else
13       @turn_mark = sente ? "+" : "-"
14     end
15     @received_moves = 0
16     @socket = nil
17     @message = ""
18     @mutex = Mutex.new
19     @login_command = "LOGIN #{@name} dummy x1"
20   end
21   attr_reader :message
22   attr_accessor :login_command
23
24   def connect
25     port = 4000
26     @socket = TCPSocket.open("localhost", port)
27     @socket.sync = true
28     @message = ""
29     reader
30   end
31
32   def close
33     @socket.close if @socket && !@socket.closed?
34   end
35
36   def reader
37     @thread = Thread.new do
38       begin
39         Thread.pass
40         loop do 
41           break if @socket.closed?
42           if r = select([@socket], nil, nil, 10)
43             str = r[0].first.gets
44             break if str.nil?
45             @mutex.synchronize do
46               if %r!^[\+\-]\d{4}\w{2},T\d+$! =~ str
47                   @received_moves += 1
48               end
49               @message << str
50             end
51           else
52             raise "timed out"
53           end
54         end
55       rescue IOError
56         $stderr.puts "\nReader thread interrupted"
57       end
58     end
59   end
60
61   def stop_reader
62     @thread.kill if @thread
63   end
64
65   def wait(reg)
66     loop do 
67       @mutex.synchronize do
68         return if reg =~ @message
69       end
70       sleep 0.01
71     end
72   end
73
74   def wait_nmoves(n)
75     loop do
76       @mutex.synchronize do
77         return if @received_moves == n
78       end
79       sleep 0.01
80     end
81   end
82
83   def login
84     str = @login_command
85     $stderr.puts str if $DEBUG
86     @socket.puts str
87     wait %r!^LOGIN!
88   end
89
90   def game
91     str = "%%GAME #{@game_name}-1500-0 #{@turn_mark}"
92     $stderr.puts str if $DEBUG
93     @socket.puts str
94   end
95
96   def challenge
97     str = "%%CHALLENGE #{@game_name}-1500-0 #{@turn_mark}"
98     $stderr.puts str if $DEBUG
99     @socket.puts str
100   end
101
102   def wait_game
103     wait %r!^END Game_Summary!
104   end
105
106   def agree
107     @socket.puts "AGREE"
108   end
109
110   def wait_agree
111     wait %r!^START:!
112   end
113
114   def move(m)
115     @socket.puts m
116   end
117   def puts(m)
118     @socket.puts m
119   end
120
121   def toryo
122     @socket.puts "%TORYO"
123   end
124
125   def wait_finish
126     wait %r!^#(WIN|LOSE)!
127   end
128
129   def logout
130     @socket.puts "LOGOUT"
131     @socket.close
132   end
133
134 end
135
136 class SocketCSAPlayer < SocketPlayer
137   def initialize(game_name, name, sente)
138     super
139     @login_command = "LOGIN #{@name} dummy"
140   end
141
142   def login
143     str = @login_command
144     $stderr.puts str if $DEBUG
145     @socket.puts str
146     wait %r!^LOGIN!
147   end
148 end
149
150
151
152
153
154 class BaseClient < Test::Unit::TestCase
155   attr_accessor :game_name, :p1_name, :p2_name
156
157   def set_name
158     @game_name = self.class.name
159     @p1_name = "sente"
160     @p2_name = "gote"
161   end
162
163   def set_player
164     @p1 = SocketPlayer.new @game_name, @p1_name, true
165     @p2 = SocketPlayer.new @game_name, @p2_name, false
166   end
167
168   def setup
169     set_name
170     set_player
171     @nmoves = 0
172   end
173   attr_reader :src1, :src2
174
175   def teardown
176     @p1.close
177     @p2.close
178   end
179
180   def test_dummy
181     assert true
182   end
183
184   def login
185     sleep 0.1
186     @p1.connect
187     sleep 0.1
188     @p2.connect
189     sleep 0.1
190     @p1.login
191     sleep 0.1
192     @p2.login
193     sleep 0.5
194     @p1.game
195     sleep 0.1
196     @p2.game
197     sleep 0.1
198     @p1.wait_game
199     sleep 0.1
200     @p2.wait_game
201   end
202
203   def agree
204     sleep 0.1
205     @p1.agree
206     sleep 0.1
207     @p2.agree
208     sleep 0.1
209     @p1.wait_agree
210     sleep 0.1
211     @p2.wait_agree
212   end
213
214   def handshake
215     login
216     agree
217
218     move "+2726FU"
219     move "-3334FU"
220    
221     yield if block_given?
222
223     logout12
224     [@p1.message, @p2.message]
225   end
226
227   def move(m)
228     case m
229     when /^\+/
230       move1(m)
231     when /^\-/
232       move2(m)
233     else
234       raise "do not reach!"
235     end
236   end
237
238   def move1(m)
239     @p1.move m
240     @nmoves += 1
241     @p2.wait_nmoves @nmoves
242   end
243
244   def move2(m)
245     @p2.move m
246     @nmoves += 1
247     @p1.wait_nmoves @nmoves
248   end
249
250   def cmd(s)
251     @p1.move s
252     return @p1.message
253   end
254
255   def cmd2(s)
256     @p2.move s
257     return @p2.message
258   end
259
260   def wait_finish
261     @p1.wait_finish
262     @p2.wait_finish
263   end
264
265   def logout12
266     @p1.logout
267     @p2.logout
268   end
269
270   def logout21
271     @p2.logout
272     @p1.logout
273   end
274
275 end
276
277
278 class ReadFileClient < BaseClient
279   def filepath(csa_file_name)
280     return File.join(File.dirname(__FILE__), "csa", csa_file_name)
281   end
282
283   def handshake(csa)
284     login
285     agree
286
287     csa_io = StringIO.new(csa)
288     while line = csa_io.gets do
289       case line
290       when /^[\+\-]\d{4}\w{2}/
291         s = $&
292         $stderr.puts s if $DEBUG
293         move s
294       end
295     end
296   end
297 end # ReadFileClient
298
299
300 class CSABaseClient < BaseClient
301   ##
302   # In CSA mode, the server decides sente or gote at random; and sockets are closed
303   # just after the game ends (i.e. %TORYO is sent)
304   # 
305   def set_player
306     @p1 = SocketCSAPlayer.new @game_name, @p1_name, true
307     @p2 = SocketCSAPlayer.new @game_name, @p2_name, false
308   end
309
310   def teardown
311     @p1.stop_reader
312     @p2.stop_reader
313     super
314   end
315
316   def handshake
317     @p1.connect
318     @p2.connect
319     @p1.login
320     @p2.login
321     agree
322
323     if /Your_Turn:\+/ =~ @p1.message
324     else
325       @p1,@p2 = @p2,@p1
326     end
327
328     move "+7776FU"
329     move "-3334FU"
330     yield if block_given?
331     
332     [@p1.message, @p2.message]
333   end
334
335 end # CSABaseClient
336
337