OSDN Git Service

Improved performance and stability of tests.
authorDaigo Moriwaki <daigo@debian.org>
Mon, 10 May 2010 12:23:47 +0000 (21:23 +0900)
committerDaigo Moriwaki <daigo@debian.org>
Mon, 10 May 2010 12:23:47 +0000 (21:23 +0900)
test/TC_before_agree.rb
test/TC_functional.rb
test/TC_jishogi_kachi.rb
test/TC_not_sennichite.rb
test/TC_oute_sennichite.rb
test/TC_uchifuzume.rb
test/baseclient.rb

index ceeebe7..8a196e7 100644 (file)
@@ -5,49 +5,39 @@ class TestBeforeAgree < BaseClient
 
   def test_gote_logout_after_sente_agree
     login
-    result  = cmd  "AGREE"
-    result2 = cmd2 "LOGOUT"
 
-    result  += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
-
-    assert(/^REJECT/ =~ result)
-    assert(/^REJECT/ =~ result2)
+    @p1.puts "AGREE"
+    @p2.puts "LOGOUT"
+    @p1.wait /^REJECT/
+    @p2.wait /^REJECT/
+    assert true
   end
 
   def test_sente_logout_after_gote_agree
     login
-    result2 = cmd2 "AGREE"
-    result  = cmd  "LOGOUT"
-
-    result  += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
 
-    assert(/^REJECT/ =~ result)
-    assert(/^REJECT/ =~ result2)
+    @p2.puts "AGREE"
+    @p1.puts "LOGOUT"
+    @p1.wait /^REJECT/
+    @p2.wait /^REJECT/
+    assert true
   end
 
   def test_gote_logout_before_sente_agree
     login
-    result  = ""
-    result2 = cmd2 "LOGOUT"
 
-    result  += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
-
-    assert(/^REJECT/ =~ result)
-    assert(/^REJECT/ =~ result2)
+    @p2.puts "LOGOUT"
+    @p1.wait /^REJECT/
+    @p2.wait /^REJECT/
+    assert true
   end
 
   def test_sente_logout_before_gote_agree
     login
-    result2 = ""
-    result  = cmd  "LOGOUT"
-
-    result  += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
 
-    assert(/^REJECT/ =~ result)
-    assert(/^REJECT/ =~ result2)
+    @p1.puts "LOGOUT"
+    @p1.wait /^REJECT/
+    @p2.wait /^REJECT/
+    assert true
   end
 end
index 5de7dd2..511598c 100644 (file)
@@ -3,17 +3,17 @@ require "kconv"
 
 class TestClientAtmark < BaseClient
   # login with trip
-  def login
-    cmd "LOGIN testsente@p1 dummy x1"
-    cmd "%%GAME testClientAtmark-1500-0 +"
-    
-    cmd2 "LOGIN testgote@p2 dummy2 x1"
-    cmd2 "%%CHALLENGE testClientAtmark-1500-0 -"
+  def set_name
+    super
+    @game_name = "atmark"
+    @p1_name = "B@p1"
+    @p2_name = "W@p2"
   end
 
   def test_toryo
     result, result2 = handshake do
-      cmd  "%TORYO"
+      @p1.toryo
+      wait_finish
     end
     assert(/#LOSE/ =~ result)
     assert(/#WIN/  =~ result2)
@@ -22,7 +22,7 @@ class TestClientAtmark < BaseClient
     year  = now.strftime("%Y")
     month = now.strftime("%m")
     day   = now.strftime("%d")
-    path = File.join( File.dirname(__FILE__), "..", year, month, day, "*testClientAtmark-1500-0*")
+    path = File.join( File.dirname(__FILE__), "..", year, month, day, "*atmark-1500-0*")
     log_files = Dir.glob(path)
     assert(!log_files.empty?) 
     log_content = File.open(log_files.sort.last).read
@@ -30,8 +30,8 @@ class TestClientAtmark < BaseClient
     # "$EVENT", "$START_TIME" and "'$END_TIME" are removed since they vary dinamically.
     should_be = <<-EOF
 V2
-N+testsente@p1
-N-testgote@p2
+N+atmark_B@p1
+N-atmark_W@p2
 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
 P2 * -HI *  *  *  *  * -KA * 
 P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
@@ -42,7 +42,7 @@ P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
 P8 * +KA *  *  *  *  * +HI * 
 P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
 +
-'rating:testsente@p1+275876e34cf609db118f3d84b799a790:testgote@p2+c0c40e7a94eea7e2c238b75273087710
+'rating:atmark_B@p1+275876e34cf609db118f3d84b799a790:atmark_W@p2+275876e34cf609db118f3d84b799a790
 +2726FU
 T1
 -3334FU
@@ -58,7 +58,7 @@ T1
 'P8 * +KA *  *  *  *  * +HI * 
 'P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
 '+
-'summary:toryo:testsente@p1 lose:testgote@p2 win
+'summary:toryo:atmark_B@p1 lose:atmark_W@p2 win
 EOF
 
     log_content.gsub!(/^\$.*?\n/m, "")
@@ -71,7 +71,8 @@ end
 class TestComment < BaseClient
   def test_toryo
     result, result2 = handshake do
-      cmd  "%TORYO"
+      @p1.toryo
+      wait_finish
     end
     assert(/#LOSE/ =~ result)
     assert(/#WIN/  =~ result2)
@@ -79,9 +80,10 @@ class TestComment < BaseClient
 
   def test_inline_comment
     result, result2 = handshake do
-      cmd "+2625FU,'comment"
-      cmd2 "-2233KA"
-      cmd  "%TORYO"
+      move "+2625FU,'comment"
+      move "-2233KA"
+      @p1.toryo
+      wait_finish
     end
     assert(/#LOSE/ =~ result)
     assert(/#WIN/  =~ result2)
@@ -89,9 +91,10 @@ class TestComment < BaseClient
 
   def test_inline_comment_ja_euc
     result, result2 = handshake do
-      cmd "+2625FU,'\93ú\96{\8cêEUC"
-      cmd2 "-2233KA"
-      cmd  "%TORYO"
+      move "+2625FU,'\93ú\96{\8cêEUC"
+      move "-2233KA"
+      @p1.toryo
+      wait_finish
     end
     assert(/#LOSE/ =~ result)
     assert(/#WIN/  =~ result2)
@@ -99,9 +102,10 @@ class TestComment < BaseClient
 
   def test_inline_comment_ja_utf8
     result, result2 = handshake do
-      cmd "+2625FU,'\93ú\96{\8cêUTF8".toutf8
-      cmd2 "-2233KA"
-      cmd  "%TORYO"
+      move "+2625FU,'\93ú\96{\8cêUTF8".toutf8
+      move "-2233KA"
+      @p1.toryo
+      wait_finish
     end
     assert(/#LOSE/ =~ result)
     assert(/#WIN/  =~ result2)
@@ -112,9 +116,9 @@ end
 class TestWhiteMovesBlack < BaseClient
   def test_white_moves_black
     result, result2 = handshake do
-      cmd  "+9796FU"
-      cmd2 "+1716FU"
-      sleep 0.5
+      move "+9796FU"
+      @p2.move "+1716FU"
+      wait_finish
     end
     assert(/#ILLEGAL_MOVE/ =~ result)
     assert(/#WIN/  =~ result)
@@ -124,56 +128,23 @@ class TestWhiteMovesBlack < BaseClient
 end
 
 
-class CSABaseClient < BaseClient
-  ##
-  # In CSA mode, the server decides sente or gote at random; and sockets are closed
-  # just after the game ends (i.e. %TORYO is sent)
-  # 
-  def handshake
-    login
-
-    sleep 0.5 # wait for game matching
-
-    str  = cmd  "AGREE"
-    str2 = cmd2 "AGREE"
-
-    if /Your_Turn:\+/ =~ str
-      @sente = "cmd"
-      @sente_socket = @socket1
-      @gote  = "cmd2"
-      @gote_socket  = @socket2
-    else
-      @sente = "cmd2"
-      @sente_socket = @socket2
-      @gote  = "cmd"
-      @gote_socket  = @socket1
-    end
-
-    yield if block_given?
-    
-    result  = read_nonblock(@sente_socket)
-    result2 = read_nonblock(@gote_socket)
-    [result, result2]
-  end
-
-  def sente_cmd(str)
-    eval "#{@sente} \"#{str}\""
-  end
-
-  def gote_cmd(str)
-    eval "#{@gote} \"#{str}\""
-  end
-end
+#
+# CSA test
+#
 
 class TestLoginCSAWithoutTripGoodGamename < CSABaseClient
-  def login
-    cmd  "LOGIN wo_trip_p1 testcase-1500-0"
-    cmd2 "LOGIN wo_trip_p2 testcase-1500-0"
+  def set_name
+    super
+    @game_name = "csawotrip"
+    @p1_name   = "p1"
+    @p2_name   = "p2"
   end
 
   def test_toryo
     result, result2 = handshake do
-      sente_cmd("%TORYO")
+      @p1.toryo
+      @p1.wait_finish
+      @p2.wait_finish
     end
     assert(/#LOSE/ =~ result)
     assert(/#WIN/  =~ result2)
@@ -181,15 +152,24 @@ class TestLoginCSAWithoutTripGoodGamename < CSABaseClient
 end
 
 class TestLoginCSAWithTripGoodGamename < CSABaseClient
-  def login
-    cmd  "LOGIN w_trip_p1 testcase-1500-0,atrip"
-    cmd2 "LOGIN w_trip_p2 testcase-1500-0,anothertrip"
+  def set_name
+    super
+    @game_name = "csawtrip"
+    @p1_name   = "p1"
+    @p2_name   = "p2"
+  end
+
+  def set_player
+    super
+    @p1.login_command += ",atrip"
+    @p2.login_command += ",anothertrip"
   end
 
   def test_toryo
     result, result2 = handshake do
-      sente_cmd "%TORYO"
-      sleep 0.5
+      @p1.toryo
+      @p1.wait_finish
+      @p2.wait_finish
     end
     assert(/#LOSE/ =~ result)
     assert(/#WIN/  =~ result2)
@@ -197,64 +177,88 @@ class TestLoginCSAWithTripGoodGamename < CSABaseClient
 end
 
 class TestChallenge < CSABaseClient
-  def login
-    cmd  "LOGIN w_trip_p1 testcase-1500-0,atrip"
-    cmd2 "LOGIN w_trip_p2 testcase-1500-0,anothertrip"
+  def set_name
+    super
+    @game_name = "challenge"
+    @p1_name   = "p1"
+    @p2_name   = "p2"
+  end
+
+  def set_player
+    super
+    @p1.login_command += ",atrip"
+    @p2.login_command += ",anothertrip"
   end
 
   def test_toryo
     result, result2 = handshake do
-      sente_cmd "CHALLENGE"
-      gote_cmd  "CHALLENGE"
+      @p1.puts "CHALLENGE"
+      @p1.wait(/CHALLENGE ACCEPTED/)
+      @p2.puts "CHALLENGE"
+      @p2.wait(/CHALLENGE ACCEPTED/)
     end
-    assert_match(/CHALLENGE ACCEPTED/, result)
-    assert_match(/CHALLENGE ACCEPTED/, result2)
+    assert(true)
   end
 end
 
+#
+# Test Floodgate
+#
+
 class TestFloodgateGame < BaseClient
-  def login
-    classname = self.class.name
-    gamename  = "floodgate-900-0"
-    cmd "LOGIN sente#{classname} dummy x1"
-    cmd "%%GAME #{gamename} *"
-    
-    cmd2 "LOGIN gote#{classname} dummy2 x1"
-    cmd2 "%%GAME #{gamename} *"
+  def set_name
+    super
+    @game_name = "floodgate"
+  end
+
+  def set_player
+    @p1 = SocketPlayer.new @game_name, @p1_name, "*"
+    @p2 = SocketPlayer.new @game_name, @p2_name, "*"
   end
 
   def test_game_wait
-    login
+    @p1.connect
+    @p2.connect
+    @p1.login
+    @p2.login
+    @p1.game
+    @p2.game
     assert(true)
+    logout12
   end
 end
 
 class TestFloodgateGameWrongTebam < BaseClient
-  def login
-    classname = self.class.name
-    gamename  = "floodgate-900-0"
-    cmd "LOGIN sente#{classname} dummy x1"
-    cmd("%%GAME #{gamename} +")
+  def set_name
+    super
+    @game_name = "floodgate"
   end
 
   def test_game_wait
-    login
-    sleep 1
-    reply = read_nonblock(@socket1)
-    assert_match(/##\[ERROR\] You are not allowed/m, reply)
+    @p1.connect
+    @p2.connect
+    @p1.login
+    @p2.login
+    @p1.game
+    @p1.wait %r!##\[ERROR\] You are not allowed!
+    assert true
+    logout12
   end
 end
 
+
+
+
 class TestDuplicatedMoves < BaseClient
   def test_defer
     result, result2 = handshake do
-      cmd  "+7776FU"
-      cmd  "+8786FU" # defer
-      cmd  "+9796FU" # defer
-      cmd2 "-7374FU"
-      cmd2 "-8384FU"
-      cmd2 "%TORYO" # defer
-      sleep 1
+      @p1.puts "+7776FU"
+      @p1.puts "+8786FU" # defer
+      @p1.puts "+9796FU" # defer
+      @p2.puts "-7374FU"
+      @p2.puts "-8384FU"
+      @p2.toryo
+      wait_finish
     end
     assert(/#WIN/  =~ result)
     assert(/#LOSE/ =~ result2)
@@ -262,12 +266,12 @@ class TestDuplicatedMoves < BaseClient
 
   def test_defer2
     result, result2 = handshake do
-      cmd  "+7776FU"
-      cmd  "+8786FU" # defer
-      cmd  "%TORYO" # defer
-      cmd2 "-7374FU"
-      cmd2 "-8384FU"
-      sleep 1
+      @p1.puts "+7776FU"
+      @p1.puts "+8786FU" # defer
+      @p1.puts "%TORYO" # defer
+      @p2.puts "-7374FU"
+      @p2.puts "-8384FU"
+      wait_finish
     end
     assert(/#LOSE/  =~ result)
     assert(/#WIN/ =~ result2)
@@ -275,12 +279,12 @@ class TestDuplicatedMoves < BaseClient
 
   def test_defer3
     result, result2 = handshake do
-      cmd  "+7776FU"
-      cmd  "+8786FU" # defer
-      cmd2 "-7374FU"
-      cmd2 "-8384FU"
-      cmd  "%TORYO" # defer
-      sleep 1
+      @p1.puts "+7776FU"
+      @p1.puts "+8786FU" # defer
+      @p2.puts "-7374FU"
+      @p2.puts "-8384FU"
+      @p1.toryo
+      wait_finish
     end
     assert(/#LOSE/  =~ result)
     assert(/#WIN/ =~ result2)
@@ -289,24 +293,35 @@ end
 
 class TestFunctionalChatCommand < BaseClient
   def test_chat
-    cmd "%%CHAT Hello"
-    sleep 1
-    str = read_nonblock(@socket2)
-    puts str   
-    assert("", str)
+    result, result2 = handshake do
+      @p1.puts"%%CHAT Hello"
+      @p1.wait %r!##\[CHAT\].*Hello!
+      @p2.wait %r!##\[CHAT\].*Hello!
+    end
+    assert true
   end
 end
 
+
+
+
 class TestTwoSameMoves < CSABaseClient
+  def set_name
+    super
+    @game_name = "2moves"
+    @p1_name   = "p1"
+    @p2_name   = "p2"
+  end
+
   def test_two_same_moves
     result, result2 = handshake do
-      cmd  "+7776FU"
-      cmd2 "-3334FU"
-      cmd2 "-3334FU"
-      cmd  "+2726FU"
-      sleep 1
+      move  "+2726FU"
+      move "-8384FU"
+      @p2.puts "-8384FU" # ignored
+      move "+2625FU"
     end
     assert(/#ILLEGAL_MOVE/ !~ result)
     assert(/#ILLEGAL_MOVE/ !~ result2)
   end
 end
+
index 38f9b49..824cafc 100644 (file)
@@ -5,14 +5,10 @@ class JishogiTest < ReadFileClient
   def test_jishogi_kachi
     csa = File.open(filepath("jishogi_kachi.csa")) {|f| f.read}
     handshake(csa)
-    cmd2 "%KACHI"
-    sleep 1
-    result1 = cmd ""
-    result2 = cmd2 ""
-    result1 += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
+    @p2.puts "%KACHI"
+    @p1.wait(/#JISHOGI\n#LOSE/)
+    @p2.wait(/#JISHOGI\n#WIN/)
+    assert true
     logout12
-    assert_match(/#JISHOGI.#LOSE/m, result1)
-    assert_match(/#JISHOGI.#WIN/m, result2)
   end
 end # Client class
index 8159d1c..9a39d97 100644 (file)
@@ -5,13 +5,9 @@ class NotSennichiteTest < ReadFileClient
   def test_oute_sennichite
     csa = File.open(filepath("not_sennichite.csa")) {|f| f.read}
     handshake(csa)
-    #cmd2 "%KACHI"
-    sleep 1
-    result1 = read_nonblock(@socket1)
-    result2 = read_nonblock(@socket2)
+    assert_no_match /#DRAW/, @p1.message
+    assert_no_match /#DRAW/, @p2.message
     logout12
-    assert_no_match(/#DRAW/m, result1)
-    assert_no_match(/#DRAW/m, result2)
   end
 end # Client class
 
index fda83b4..926ef78 100644 (file)
@@ -5,43 +5,28 @@ class OuteSennichiteTest < ReadFileClient
   def test_oute_sennichite
     csa = File.open(filepath("oute_sennichite.csa")) {|f| f.read}
     handshake(csa)
-    #cmd2 "%KACHI"
-    sleep 1
-    result1 = cmd ""
-    result2 = cmd2 ""
-    result1 += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
+    @p1.wait(/#OUTE_SENNICHITE.#LOSE/m)
+    @p2.wait(/#OUTE_SENNICHITE.#WIN/m)
+    assert true
     logout12
-    assert_match(/#OUTE_SENNICHITE.#LOSE/m, result1)
-    assert_match(/#OUTE_SENNICHITE.#WIN/m, result2)
   end
 
   def test_oute_sennichite2
     csa = File.open(filepath("oute_sennichite2.csa")) {|f| f.read}
     handshake(csa)
-    #cmd2 "%KACHI"
-    sleep 1
-    result1 = cmd ""
-    result2 = cmd2 ""
-    result1 += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
+    @p1.wait(/#OUTE_SENNICHITE.#WIN/m)
+    @p2.wait(/#OUTE_SENNICHITE.#LOSE/m)
+    assert true
     logout12
-    assert_match(/#OUTE_SENNICHITE.#WIN/m, result1)
-    assert_match(/#OUTE_SENNICHITE.#LOSE/m, result2)
   end
 
   def test_oute_sennichite3
     csa = File.open(filepath("oute_sennichite3.csa")) {|f| f.read}
     handshake(csa)
-    #cmd2 "%KACHI"
-    sleep 1
-    result1 = cmd ""
-    result2 = cmd2 ""
-    result1 += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
+    @p1.wait(/#OUTE_SENNICHITE.#LOSE/m)
+    @p2.wait(/#OUTE_SENNICHITE.#WIN/m)
+    assert true
     logout12
-    assert_match(/#OUTE_SENNICHITE.#LOSE/m, result1)
-    assert_match(/#OUTE_SENNICHITE.#WIN/m, result2)
   end
 end # Client class
 
index 1798a0e..16ba282 100644 (file)
@@ -5,31 +5,23 @@ class UchifuzumeTest < ReadFileClient
   def test_uchifuzume
     csa = File.open(filepath("uchifuzume.csa")) {|f| f.read}
     handshake(csa)
-    result2 = cmd2 "-0064FU"
-    result1 = cmd  "%TORYO"
-    sleep 1
-    result1 = cmd ""
-    result2 = cmd2 ""
-    result1 += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
+    @p2.puts "-0064FU"
+    @p1.puts "%TORYO"
+    wait_finish
+    assert_match(/#ILLEGAL_MOVE.*#WIN/m, @p1.message)
+    assert_match(/#ILLEGAL_MOVE.*#LOSE/m, @p2.message)
     logout12
-    assert_match(/#ILLEGAL_MOVE.*#WIN/m, result1)
-    assert_match(/#ILLEGAL_MOVE.*#LOSE/m, result2)
   end
 
   def test_not_uchifuzume
     csa = File.open(filepath("not_uchifuzume.csa")) {|f| f.read}
     handshake(csa)
-    cmd2 "-0092FU"
-    cmd  "%TORYO"
-    sleep 1
-    result1 = cmd ""
-    result2 = cmd2 ""
-    result1 += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
+    @p2.puts "-0092FU"
+    @p1.puts "%TORYO"
+    wait_finish
+    assert_no_match(/#ILLEGAL_MOVE/, @p1.message)
+    assert_no_match(/#ILLEGAL_MOVE/, @p2.message)
     logout12
-    assert_match(/#LOSE/m, result1)
-    assert_match(/#WIN/m, result2)
   end
 end # Client class
 
index d8d487b..39ad817 100644 (file)
 require 'socket'
 require 'stringio'
+require 'thread'
 require 'test/unit'
 
+class SocketPlayer
+  def initialize(game_name, name, sente)
+    @game_name = game_name
+    @name = "%s_%s" % [game_name, name]
+    if sente == "*"
+      @turn_mark = sente
+    else
+      @turn_mark = sente ? "+" : "-"
+    end
+    @received_moves = 0
+    @socket = nil
+    @message = ""
+    @mutex = Mutex.new
+    @login_command = "LOGIN #{@name} dummy x1"
+  end
+  attr_reader :message
+  attr_accessor :login_command
+
+  def connect
+    port = 4000
+    @socket = TCPSocket.open("localhost", port)
+    @socket.sync = true
+    @message = ""
+    reader
+  end
+
+  def close
+    @socket.close if @socket && !@socket.closed?
+  end
+
+  def reader
+    @thread = Thread.new do
+      Thread.pass
+      loop do 
+#        break if @socket.closed?
+        if r = select([@socket], nil, nil, 10)
+          str = r[0].first.gets
+          break if str.nil?
+          @mutex.synchronize do
+            if %r!^[\+\-]\d{4}\w{2},T\d+$! =~ str
+                @received_moves += 1
+            end
+            @message << str
+          end
+        else
+          raise "timed out"
+        end
+      end
+    end
+  end
+
+  def stop_reader
+    @thread.kill if @thread
+  end
+
+  def wait(reg)
+    loop do 
+      @mutex.synchronize do
+        return if reg =~ @message
+      end
+      sleep 0.01
+    end
+  end
+
+  def wait_nmoves(n)
+    loop do
+      @mutex.synchronize do
+        return if @received_moves == n
+      end
+      sleep 0.01
+    end
+  end
+
+  def login
+    str = @login_command
+    $stderr.puts str if $DEBUG
+    @socket.puts str
+    wait %r!^LOGIN!
+  end
+
+  def game
+    str = "%%GAME #{@game_name}-1500-0 #{@turn_mark}"
+    $stderr.puts str if $DEBUG
+    @socket.puts str
+  end
+
+  def challenge
+    str = "%%CHALLENGE #{@game_name}-1500-0 #{@turn_mark}"
+    $stderr.puts str if $DEBUG
+    @socket.puts str
+  end
+
+  def wait_game
+    wait %r!^END Game_Summary!
+  end
+
+  def agree
+    @socket.puts "AGREE"
+  end
+
+  def wait_agree
+    wait %r!^START:!
+  end
+
+  def move(m)
+    @socket.puts m
+  end
+  def puts(m)
+    @socket.puts m
+  end
+
+  def toryo
+    @socket.puts "%TORYO"
+  end
+
+  def wait_finish
+    wait %r!^#(WIN|LOSE)!
+  end
+
+  def logout
+    stop_reader
+    @socket.puts "LOGOUT"
+  end
+
+end
+
+class SocketCSAPlayer < SocketPlayer
+  def initialize(game_name, name, sente)
+    super
+    @login_command = "LOGIN #{@name} dummy"
+  end
+
+  def login
+    str = @login_command
+    $stderr.puts str if $DEBUG
+    @socket.puts str
+    wait %r!^LOGIN!
+  end
+end
+
+
+
+
+
 class BaseClient < Test::Unit::TestCase
+  attr_accessor :game_name, :p1_name, :p2_name
+
+  def set_name
+    @game_name = self.class.name
+    @p1_name = "sente"
+    @p2_name = "gote"
+  end
+
+  def set_player
+    @p1 = SocketPlayer.new @game_name, @p1_name, true
+    @p2 = SocketPlayer.new @game_name, @p2_name, false
+  end
+
   def setup
-    port = 4000
-    params = {"Host" => "localhost", "Port" => port, "Prompt" => //}
-    @socket1 = TCPSocket.open("localhost", port)
-    @socket1.sync = true
-    @socket2 = TCPSocket.open("localhost", port)
-    @socket2.sync = true
+    set_name
+    set_player
+    @nmoves = 0
   end
+  attr_reader :src1, :src2
 
   def teardown
-    @socket1.close
-    @socket2.close
+    @p1.close
+    @p2.close
+  end
+
+  def test_dummy
+    assert true
   end
 
   def login
-    classname = self.class.name
-    cmd "LOGIN sente#{classname} dummy x1"
-    cmd "%%GAME test#{classname}-1500-0 +"
-    
-    cmd2 "LOGIN gote#{classname} dummy2 x1"
-    cmd2 "%%CHALLENGE test#{classname}-1500-0 -"
+    @p1.connect
+    @p2.connect
+    @p1.login
+    @p2.login
+    @p1.game
+    @p2.game
+    @p1.wait_game
+    @p2.wait_game
   end
 
   def agree
-    cmd  "AGREE"
-    sleep 0.5
-    cmd2 "AGREE"
+    @p1.agree
+    @p2.agree
+    @p1.wait_agree
+    @p2.wait_agree
   end
 
   def handshake
     login
-
-    sleep 2 # to wait for game matching
-
     agree
 
-    cmd  "+2726FU"
-    cmd2 "-3334FU"
+    move "+2726FU"
+    move "-3334FU"
    
     yield if block_given?
-    sleep 2
-    result  = cmd  "LOGOUT"
-    result2 = cmd2 "LOGOUT"
-    result  += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
-    [result, result2]
-  end
-
-  def read_nonblock(io)
-    sleep 0.05  
-    str = ""
-    begin
-      loop do   
-        str << io.read_nonblock(64)
-      end
-    rescue Errno::EAGAIN
-      # do nothing
-    rescue EOFError
-      # do nothing
+
+    logout12
+    [@p1.message, @p2.message]
+  end
+
+  def move(m)
+    case m
+    when /^\+/
+      move1(m)
+    when /^\-/
+      move2(m)
+    else
+      raise "do not reach!"
     end
-    str
+  end
+
+  def move1(m)
+    @p1.move m
+    @nmoves += 1
+    @p2.wait_nmoves @nmoves
+  end
+
+  def move2(m)
+    @p2.move m
+    @nmoves += 1
+    @p1.wait_nmoves @nmoves
   end
 
   def cmd(s)
-    # read the previous return
-    str = read_nonblock(@socket1)
-    @socket1.puts s if s && ! @socket1.closed?
-    str
+    @p1.move s
+    return @p1.message
   end
 
   def cmd2(s)
-    # read the previous return
-    str = read_nonblock(@socket2)
-    @socket2.puts s if s && ! @socket2.closed?
-    str
+    @p2.move s
+    return @p2.message
+  end
+
+  def wait_finish
+    @p1.wait_finish
+    @p2.wait_finish
   end
 
   def logout12
-    cmd  "LOGOUT"
-    cmd2 "LOGOUT"
-    sleep 1
+    @p1.logout
+    @p2.logout
   end
 
   def logout21
-    cmd2 "LOGOUT"
-    cmd  "LOGOUT"
-    sleep 1
+    @p2.logout
+    @p1.logout
   end
 
-  def test_dummy
-    assert true
-  end
 end
 
 
@@ -105,20 +266,56 @@ class ReadFileClient < BaseClient
 
   def handshake(csa)
     login
-    sleep 1
     agree
-    sleep 1
 
     csa_io = StringIO.new(csa)
     while line = csa_io.gets do
       case line
-      when /^\+\d{4}\w{2}/
-        cmd ""
-        cmd $&
-      when /^\-\d{4}\w{2}/
-        cmd2 ""
-        cmd2 $&
+      when /^[\+\-]\d{4}\w{2}/
+        s = $&
+        $stderr.puts s if $DEBUG
+        move s
       end
     end
   end
 end # ReadFileClient
+
+
+class CSABaseClient < BaseClient
+  ##
+  # In CSA mode, the server decides sente or gote at random; and sockets are closed
+  # just after the game ends (i.e. %TORYO is sent)
+  # 
+  def set_player
+    @p1 = SocketCSAPlayer.new @game_name, @p1_name, true
+    @p2 = SocketCSAPlayer.new @game_name, @p2_name, false
+  end
+
+  def teardown
+    @p1.stop_reader
+    @p2.stop_reader
+    super
+  end
+
+  def handshake
+    @p1.connect
+    @p2.connect
+    @p1.login
+    @p2.login
+    agree
+
+    if /Your_Turn:\+/ =~ @p1.message
+    else
+      @p1,@p2 = @p2,@p1
+    end
+
+    move "+7776FU"
+    move "-3334FU"
+    yield if block_given?
+    
+    [@p1.message, @p2.message]
+  end
+
+end # CSABaseClient
+
+