Loic wrote on Tuesday the 8th:

> I very much doubt there will be any benefit in making an actual bot
> run in the tests. The connection pattern is specific but the rest of
> the bot actions is not.

After I sent my previous message, I took a look at what I'd been working
on before with fresh eyes.  This solution *does* work, and given that
I'd already put effort into it before sending my last message, it was
actually easier to use this setup of deferred's/reactor.callLater()'s
you see in nBotsAndOneLivePlayer() in the attached patch.

I am therefore planning to merge this bug-tests branch at r6232 into
trunk, the changset for which is shown in the attached patch.  I plan to
use this commit message:

Test to exhibit bug#14139.

This new test in test-bug-14139.py.in exhibits bug#14139 by adding bots
and user and showing that a hand does not start.  If the bug is closed,
this test should pass as written.

Note that some changes were needed in test-pokeravatar.py.in in some of
the helper functions.  This doesn't impact the functioning of those
test.  Much of the changes move some methods to base classes; the rest
make minor changes to helper functions to handle the existence of the
bots at the tables and slightly different uses of the helper functions.

Finally, a few files that should have been in .gitignore are added.
This will be meaningless currently to anyone but those using git-svn.



The attached patch is copyrighted by me and licensed under
AGPLv3-or-later.

diff --git a/.gitignore b/.gitignore
index fd53985..1b2b85e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,10 @@
 *~
 TAGS
 ###########################################################################
+# poker-network: bkuhn-specific files.  Remove this if you add files by
+# this name, bkuhn will appreciate it.
+poker-network/check.log
+###########################################################################
 # poker-network: Specific files that are generated by build
 poker-network/ABOUT-NLS
 poker-network/INSTALL
@@ -79,6 +83,12 @@ poker-network/po/remove-potcdate.sed
 poker-network/po/remove-potcdate.sin
 poker-network/po/stamp-po
 poker-network/poker-network.pc
+poker-network/pokerprizes/Makefile
+poker-network/pokerprizes/Makefile.in
+poker-network/pokerprizes/poker.prizes.xml
+poker-network/pokerprizes/run
+poker-network/pokerprizes/schema.sql
+poker-network/pokerprizes/test-prizes.py
 poker-network/pokerclient2d/.deps
 poker-network/pokerclient2d/.libs
 poker-network/pokerclient2d/Makefile
@@ -129,6 +139,7 @@ poker-network/pokernetwork/pokernetworkconfig.pyc
 poker-network/pokernetwork/pokerserver
 poker-network/pokernetwork/pokerserver.8
 poker-network/pokernetwork/version.pyc
+poker-network/pokernetwork/pokersql
 poker-network/pokerstats/Makefile
 poker-network/pokerstats/Makefile.in
 poker-network/pokerstats/poker.stats.xml
@@ -193,6 +204,14 @@ poker-network/tests/testbirthday.php
 poker-network/tests/testcurrency.php
 poker-network/tests/testpoker.php
 poker-network/tests/testwebservice.php
+poker-network/tests/test-nullfilter.py
+poker-network/tests/test-pokerrestclient.py
+poker-network/tests/test-pokerserver-run-load.py
+poker-network/tests/test-pokerservice-load.py
+poker-network/tests/test-pokersql.py
+poker-network/tests/test-proxy.py
+poker-network/tests/test-bug-14139.py
+poker-network/tests/test-sessionproxyfilter.py
 poker-network/tests/upgrade
 poker-network/tests/.coverage
 poker-network/tests/_trial_temp
diff --git a/poker-network/ChangeLog b/poker-network/ChangeLog
index 9bb61d9..fc64b1d 100644
--- a/poker-network/ChangeLog
+++ b/poker-network/ChangeLog
@@ -1,5 +1,66 @@
+2009-09-13  Bradley M. Kuhn  <[email protected]>
+
+	* tests/test-bug-14139.py.in
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.nBotsAndOneLivePlayer):
+	Abstracted test02 code into this method.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.test02_tablePicker_someBots_playShouldSucceed_1bot):
+	Wrote test.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.test03_tablePicker_someBots_playShouldSucceed_2bot):
+	Wrote test.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.test04_tablePicker_someBots_playShouldSucceed_3bot):
+	Wrote test.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.test05_tablePicker_someBots_playShouldSucceed_4bot):
+	Wrote test.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.test06_tablePicker_someBots_playShouldSucceed_5bot):
+	Wrote test.
+
+	* tests/test-pokeravatar.py.in
+	(PokerAvatarTestCaseBaseClass.readyToPlay)
+	(PokerAvatarTablePickerBaseClass.tablePickerSucceeds): Use
+	getSerial() to lookup avatar serial.
+	(settings_xml_table_picker_server): Added play money table to
+	settings.
+	(PokerAvatarTablePickerBaseClass.tablePickerSucceeds): Ignore play
+	money buy-ins.
+
+	* tests/test-bug-14139.py.in
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.test02_tablePicker_someBots_playShouldSucceed):
+	Wrote test.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.setUpBots): Wrote method.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.startHandAndReceiveCards):
+	Wrote test.
+
+2009-09-07  Bradley M. Kuhn  <[email protected]>
+
+	* tests/test-bug-14139.py.in
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.startHandAndReceiveCards):
+	Tweaked method from PokerAvatarTestCase.startHandAndReceiveCards
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.test01_tablePicker_allHuman_playSucceeds):
+	Wrote test.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.tearDown): Wrote
+	method.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.beginHandSetupOnePlayer):
+	Wrote method, mostly cut-and-paste from
+	PokerAvatarTestCaseBaseClass.beginHandSetup.
+	(PokerAvatarTablePickerDoesNotDealHandTestCase.test01_tablePicker_allHuman_playSucceeds):
+	Switched to new SetupOnePlayer, for better contrast with Bot
+	players present.
+
+	* tests/test-pokeravatar.py.in
+	(PokerAvatarTestCaseBaseClass.beginHandSetup)
+	(PokerAvatarTestCaseBaseClass.dealTable)
+	(PokerAvatarTestCaseBaseClass.doBlindPost): Moved methods into
+	base class.
+	(PokerAvatarTestCaseBaseClass.beginHandSetup): Added
+	dealerAssigned argument.
+
+	* tests/test-bug-14139.py.in: Created test file.
+
 2009-09-06  Bradley M. Kuhn  <[email protected]>
 
+	* tests/test-pokeravatar.py.in (PokerAvatarTablePickerBaseClass):
+	Abstracted out some code from PokerAvatarTablePickerTestCase.
+
 	* Makefile.am (install-data-local, all-local): Removed 'client'
 	from list in for loop, since r6200 deleted the files it would
 	refer to.
diff --git a/poker-network/configure.ac b/poker-network/configure.ac
index 9fc07a7..9e09a6f 100644
--- a/poker-network/configure.ac
+++ b/poker-network/configure.ac
@@ -276,6 +276,7 @@ AC_CONFIG_FILES([
 	tests/test-pokerserver.py
 	tests/test-pokerserver-run-load.py
 	tests/test-pokerrestclient.py
+	tests/test-bug-14139.py
         tests/testcurrency.php
         tests/testpoker.php
         tests/testbirthday.php
diff --git a/poker-network/tests/Makefile.am b/poker-network/tests/Makefile.am
index c4674d0..7aa677c 100644
--- a/poker-network/tests/Makefile.am
+++ b/poker-network/tests/Makefile.am
@@ -93,12 +93,19 @@ TESTS = coverage-reset \
 	test-pokerserver.py \
 	test-pokerserver-run-load.py \
 	test-pokerrestclient.py \
+	test-bug-14139.py \
 	testcurrency.php \
 	testpoker.php \
 	testbirthday.php \
 	testwebservice.php \
 	coverage-report      
 
+# Regarding XFAIL_TESTS: the only items we typically list here are tests
+# designed to cover open bugs.  We expect them to fail until the bug is
+# closed.  If you close a bug, be sure to remove the test-bug-NUMBER.py
+# entry from XFAIL_TESTS below.
+
+XFAIL_TESTS = test-bug-14139.py
 
 .PHONY: coverage-reset coverage-report
 
diff --git a/poker-network/tests/test-bug-14139.py.in b/poker-network/tests/test-bug-14139.py.in
new file mode 100644
index 0000000..e68fc35
--- /dev/null
+++ b/poker-network/tests/test-bug-14139.py.in
@@ -0,0 +1,426 @@
+...@python@
+# -*- mode: python -*-
+#
+# Copyright (C) 2009 Bradley M. Kuhn  <[email protected]>
+#
+# This software's license gives you freedom; you can copy, convey,
+# propagate, redistribute and/or modify this program under the terms of
+# the GNU Affero General Public License (AGPL) as published by the Free
+# Software Foundation (FSF), either version 3 of the License, or (at your
+# option) any later version of the AGPL published by the FSF.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program in a file in the toplevel directory called
+# "AGPLv3".  If not, see <http://www.gnu.org/licenses/>.
+#
+import sys, os, tempfile, shutil, signal
+sys.path.insert(0, "@srcdir@/..")
+sys.path.insert(0, "..")
+sys.path.insert(0, ".")
+
+import locale
+import libxml2
+from types import *
+import socket
+import time
+from twisted.trial import unittest, runner, reporter
+import twisted.internet.base
+from twisted.internet import reactor, defer, error, base
+from twisted.python import failure, runtime
+from twisted.python.runtime import seconds
+
+from tests import testclock
+from tests.testmessages import restore_all_messages, silence_all_messages, search_output, clear_all_messages, get_messages
+#os.environ['VERBOSE_T'] = '0'
+verbose = int(os.environ.get('VERBOSE_T', '-1'))
+if verbose < 0: silence_all_messages()
+
+twisted.internet.base.DelayedCall.debug = True
+
+from pokernetwork import pokerservice
+from pokernetwork.pokerclientpackets import *
+from pokernetwork import pokernetworkconfig
+
+# Note that since import doesn't allow -'s in indentifiers, I have to do
+# the following trick to import another test file.
+test_pokeravatar = __import__('test-pokeravatar', globals(), locals(),
+                                             ['PokerAvatarTablePickerBaseClass'], -1)
+PokerAvatarTablePickerBaseClass = test_pokeravatar.PokerAvatarTablePickerBaseClass
+
+settings_bot_xml = """
+<settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xsi:noNamespaceSchemaLocation="poker-bot.xsd" delays="false" wait="1" reconnect="yes" watch="no" level="1" cash_in="no" ping="10" verbose="0" no_display_packets="yes" rebuy="yes" name_prefix="BOT" poker_network_version="2.0.0">
+  <delays position="0" begin_round="0" end_round="0" end_round_last="0" showdown="5" lag="15"/>
+  <name>test</name>
+  <passwd>test</passwd>
+  <servers>%(serverInfo)s</servers>
+  <muck>yes</muck>
+  <currency id="0"></currency>
+  <auto_post>yes</auto_post>
+n  <path>.. ../@srcdir@ @POKER_ENGINE_PKGSYSCONFDIR@ </path> 
+
+  <verbose>6</verbose>
+  <table name="Play Money NL HE 10-max 1/2" count="%(botCount)d"/>
+</settings>
+"""
+# -----------------------------------------------------------------------------
+class PokerAvatarTablePickerDoesNotDealHandTestCase(PokerAvatarTablePickerBaseClass):
+    # -------------------------------------------------------------------------
+    # I had serious weirdness trying to call
+    # PokerAvatarTablePickerBaseClass.tearDown(self), so I had to copy the
+    # tearDown code from the base class.  I think this problem had 
+    def tearDown(self):
+        if hasattr(self, 'tmpdir'):
+            shutil.rmtree(self.tmpdir)
+        if hasattr(self, 'botPid'):
+            os.kill(self.botPid, signal.SIGHUP)
+            os.kill(self.botPid, signal.SIGINT)
+            os.kill(self.botPid, signal.SIGTERM)
+            os.kill(self.botPid, signal.SIGKILL)
+        # Rest stupidly copied from super class, see above
+        d = self.service.stopService()
+        d.addCallback(lambda x: self.p.stopListening())
+        d.addCallback(self.destroyDb)
+        d.addCallback(self.cleanSessions)
+        return d
+    # -------------------------------------------------------------------------
+    def setUpBots(self, botCount):
+        # Next, create the bot application so that we have bots joining
+        # the any tables specificed in the settings_bot_xml above
+        self.tmpdir = tempfile.mkdtemp()
+        outFile = os.path.join(self.tmpdir, "poker.bot.xml")
+        outFH = open(outFile, "w")
+        botConfigData = settings_bot_xml % { 'serverInfo': "127.0.0.1:%d" % self.port,
+                                             'botCount' : botCount }
+        outFH.write(botConfigData)
+        outFH.close()
+        pid = os.fork()
+        if not pid:
+            # The child simply execs the bot code.
+            saveArgv = sys.argv
+            sys.argv.append(outFile)
+            newEnviorn = os.environ
+            newEnviorn['PYTHONPATH'] = ".:@srcdir@/../../"
+            os.execlp("@PYTHON@", "@srcdir@/../../pokernetwork/pokerbot", 
+                      "@srcdir@/../../pokernetwork/pokerbot", outFile)
+        else:
+            # In the parent, we continue with the test
+            self.botPid = pid
+    # -------------------------------------------------------------------------
+    # I copied the startHandAndReceiveCards from PokerAvatarTestCase
+    # rather than moving it into PokerAvatarTestCaseBaseClass because
+    # there were some changes to be made due to the fact that when
+    # TablePicker is used, a different player gets the button.  I am not
+    # sure why this is, really, and it may be part of what is going on in
+    # the bug.
+    def startHandAndReceiveCards(self, (client, packet), gameId, bigBlindAmount = 200,
+                                 smallBlindAmount = 100):
+        table = self.service.getTable(gameId)
+
+        lang2strings = { 'default' : [ "Dealer: user1 pays %(smallBlind)d blind\n",
+                                       "Dealer: user0 pays %(bigBlind)d blind\n",
+                                       "Dealer: pre-flop, %(numPlayers)d players\n" ], 
+                         'en_US' : [ "Dealer: user1 pays %(smallBlind)d blind\n",
+                                           "Dealer: user0 pays %(bigBlind)d blind\n",
+                                           "Dealer: pre-flop, %(numPlayers)d players\n" ], 
+                         'fr_FR' : [ "Dealer: user1 paye %(smallBlind)d de blind\n",
+                                           "Dealer: user0 paye %(bigBlind)d de blind\n",
+                                           "Dealer: pre-flop, %(numPlayers)d joueurs\n" ] }
+        for lang in lang2strings.keys():
+            for ii in range(0, len(lang2strings[lang])):
+                lang2strings[lang][ii] = lang2strings[lang][ii] % {'bigBlind': bigBlindAmount / 100, 'smallBlind' : smallBlindAmount / 100, 'numPlayers' : 2 }
+        avatar = []
+        avatar.append(self.service.avatars[0])
+
+        found = 0
+        ignored = 0
+        for packet in avatar[0].resetPacketsQueue():
+            if packet.type == PACKET_POKER_BLIND:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.dead, 0)
+            elif packet.type == PACKET_POKER_CHIPS_PLAYER2BET:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.cookie, "")
+                if (packet.serial == avatar[0].getSerial()):
+                    self.assertEquals(packet.chips, [smallBlindAmount, 2])
+                else:
+                    assert("unknown serial in player2bet packet: %d" 
+                           % packet.serial)
+            elif packet.type == PACKET_POKER_CHIPS:
+                found += 1
+                self.assertEquals(packet.serial, mySerial)
+                self.assertEquals(packet.game_id, gameId)
+            elif packet.type == PACKET_POKER_CHAT:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.serial, 0)
+                if (packet.message not in lang2strings[self.avatarLocales[0]]):
+                    self.fail("Unexpected and/or Wrong Language (expected %s) message: %s for avatar %d" 
+                              % (self.avatarLocales[0], packet.message, 0))
+            elif packet.type == PACKET_POKER_STATE:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.serial, 0)
+                self.assertEquals(packet.string, "pre-flop")
+            elif packet.type == PACKET_POKER_BEGIN_ROUND:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.serial, 0)
+                self.assertEquals(packet.cookie, "")
+
+            elif packet.type == PACKET_POKER_PLAYER_CARDS:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(len(packet.cards), 2)
+            elif packet.type == PACKET_POKER_BET_LIMIT:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.serial, 0)
+                self.assertEquals(packet.cookie, "")
+                self.assertEquals(packet.step, smallBlindAmount)
+                self.assertEquals(packet.min, bigBlindAmount)
+                self.assertEquals(packet.max, 180000)
+                self.assertEquals(packet.allin, packet.max)
+                self.assertEquals(packet.pot, smallBlindAmount + bigBlindAmount)
+                self.assertEquals(packet.call, 0)
+            else:
+                ignored += 1
+        self.assertEquals(found, 14, """Expected 14, but got %d.  If we
+get nothing here, it means the hand play was not started.  It is this
+failure we should see to exhibit bug#14139.  If we do not get this failure
+for test02, then something else is wrong with test02"""  % found)
+        return (client, packet)
+    # -------------------------------------------------------------------------
+    # The below was seriously hacked from
+    # PokerAvatarTestCaseBaseClass.beginHandSetup.  It might read a little
+    # weird because I did not adjust it carefully to consider only the single player.
+    def beginHandSetupOnePlayer(self, (client, packet), gameId, dealerAssigned = 1):
+        table = self.service.getTable(gameId)
+        avatars = self.service.avatar_collection.get(client.getSerial())
+        self.failUnless(len(avatars) ==  1, "Only one avatar should have this serial")
+        avatar = avatars[0]
+        avatar.queuePackets()
+        # Handle the packets that initially arrive.  I learned what to
+        # expect from "What to expect while a hand is being played?" in
+        # pokerpackets.py
+        packetList = []
+        packetList.extend(avatar.resetPacketsQueue())
+        playersExpect = [ avatar.getSerial() ]
+        playersExpect.sort()
+        if len(self.service.avatars) > 1:
+            avatar1 = self.service.avatars[1]
+            avatar1.queuePackets()
+            packetList.extend(avatar1.resetPacketsQueue())
+        found = 0
+        for packet in packetList:
+            if packet.type == PACKET_POKER_IN_GAME:
+                found += 1
+                self.assertEquals(packet.serial, 0)
+                self.assertEquals(packet.game_id, gameId)
+                self.failUnless(avatar.getSerial() in packet.players,
+                                "Should find myself in the player list")
+                self.failUnless(len(packet.players) > 1,
+                                "Should have bots or second player here")
+                packet.players.sort()
+            elif packet.type == PACKET_POKER_DEALER:
+                found += 1
+                self.assertEquals(packet.dealer, dealerAssigned)
+                self.assertEquals(packet.previous_dealer, -1)
+                self.assertEquals(packet.game_id, gameId)
+            elif packet.type == PACKET_POKER_START:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.level, 0)
+                self.assertEquals(packet.hand_serial, 1)
+                self.assertEquals(packet.serial, 0)
+                self.assertEquals(packet.hands_count, 0)
+                self.assertEquals(packet.time, 0)
+            elif packet.type == PACKET_POKER_POSITION:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+            elif packet.type == PACKET_POKER_CHIPS_POT_RESET:
+                found += 1
+                self.assertEquals(packet.cookie, "")
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.length, 11)
+                self.assertEquals(packet.serial, 0)
+            elif packet.type == PACKET_POKER_BLIND_REQUEST:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.dead, 0)
+            elif packet.type == PACKET_POKER_SELF_IN_POSITION:
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.position, -1)
+                self.assertEquals(packet.length, 8)
+            elif packet.type == PACKET_POKER_BOARD_CARDS:
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(True, packet.serial == avatar.getSerial() )
+                self.assertEquals(packet.cards, [])
+            # When I was writing this loop, I also saw a number of:
+            # POKER_PLAYER_CHIPS and also the POKER_PLAYER_ARRIVE for
+            # serial 5, but I thought it was safe to ignore them here.
+
+        self.failUnless(found >= 7, "Got %d, expected at least 7." % found)
+        avatar.queuePackets()
+        if len(self.service.avatars) > 1:
+            avatar1.queuePackets()
+        return (client, packet)
+    # -------------------------------------------------------------------------
+    def test01_tablePicker_allHuman_playSucceeds(self):
+        """test01_tablePicker_playSuceeds
+        This test should succeed even before bug 14139 is closed.  It's a
+        bit of a control group -- to make sure that when the players are
+        all human, that both using table picker suceed in having a hand
+        started.  This test is basically a "mix" of
+        PokerAvatarTestCase.test15_handPlay() and
+        PokerAvatarTablePickerTestCase.test03_tablePicker_onlyOnePossible()"""
+        self.createClients(2)
+
+        playersDeferreds = []
+        for ii in [ 0, 1 ]:
+            iiDeferred = self.preparePlayerForTablePickerSend(ii)
+            playersDeferreds.append(iiDeferred)
+            iiDeferred.addCallback(self.setMoneyForPlayer, ii, 1, "over_min_under_best", 1)
+            iiDeferred.addCallback(self.tablePickerSucceeds, ii, ii, 0,
+                                    "holdem", "100-200-no-limit", 1, "NL HE 10-max 100/200",
+                                   autoBlindAnte = (ii == 0))
+            iiDeferred.addCallback(self.readyToPlay, ii, 1)
+
+        # Next, add items to start playing to last user added.
+        iiDeferred.addCallback(self.dealTable, 1)
+        iiDeferred.addCallback(self.beginHandSetupOnePlayer, 1, dealerAssigned = 0)
+        iiDeferred.addCallback(self.doBlindPost, 1, 1)
+        iiDeferred.addCallback(self.startHandAndReceiveCards, 1,
+                               smallBlindAmount = 10000, bigBlindAmount = 20000 )
+        return defer.DeferredList(playersDeferreds)
+    # -------------------------------------------------------------------------
+    def nBotsAndOneLivePlayer(self, botCount):
+        """The code here uses a serious of reactor.callLater()'s and two
+        deferreds to be sure that the user-controlled player does not get
+        created until the bots have fully joined.  We must do this sort of
+        delay because we don't know how long the subprocess of bots will
+        take to have enough bots
+        """
+
+        self.setUpBots(botCount)
+
+        finishedDeferred = defer.Deferred()
+        
+        def setupPlayerIfBotsAtTable():
+            table = self.service.getTable(6)
+            numberJoined = len(table.avatar_collection.serial2avatars.keys())
+            
+            if (numberJoined < botCount):
+                return None
+            else:
+                self.createClients(1)
+                playerDeferred = self.preparePlayerForTablePickerSend(0)
+                playerDeferred.addCallback(self.setMoneyForPlayer, 0, 0, "over_min_under_best", 6)
+                playerDeferred.addCallback(self.tablePickerSucceeds, 0, 0, 0,
+                                   "holdem", "1-2-no-limit", 6, "Play Money NL HE 10-max 1/2",
+                                   autoBlindAnte = False)
+                playerDeferred.addCallback(self.readyToPlay, 0, 6)
+                playerDeferred.addCallback(self.dealTable, 6)
+                playerDeferred.addCallback(self.beginHandSetupOnePlayer, 6, dealerAssigned = 0)
+                playerDeferred.addCallback(self.doBlindPost, 0, 6)
+                playerDeferred.addCallback(self.startHandAndReceiveCards, 6,
+                                   smallBlindAmount = 100, bigBlindAmount = 200 )
+                playerDeferred.addCallback(lambda : finishedDeferred.callback(True))
+                return playerDeferred
+
+        def checkWaitforFinished():
+            playerDeferred = setupPlayerIfBotsAtTable()
+            if playerDeferred != None:
+                return playerDeferred
+            else:
+                reactor.callLater(5, checkWaitforFinished)
+
+        reactor.callLater(5, checkWaitforFinished)
+        return finishedDeferred
+    # -------------------------------------------------------------------------
+    def test02_tablePicker_someBots_playShouldSucceed_1bot(self):
+        """test02_tablePicker_someBots_playShouldSucceed_1bot
+        This is the actual test for bug #14139.  Similar to the previous
+        test, but instead we create a bot server.  The code in this test
+        uses a serious of reactor.callLater()'s and two deferreds to be
+        sure that the user-controlled player does not get created until
+        the bots have fully joined.  We must do this sort of delay because
+        we don't know how long the subprocess of bots will take to have enough bots 
+        """
+        return self.nBotsAndOneLivePlayer(1)
+    # -------------------------------------------------------------------------
+    def test03_tablePicker_someBots_playShouldSucceed_2bot(self):
+        """test03_tablePicker_someBots_playShouldSucceed_2bot
+        This is the actual test for bug #14139.  Similar to the previous
+        test, but instead we create a bot server.  The code in this test
+        uses a serious of reactor.callLater()'s and two deferreds to be
+        sure that the user-controlled player does not get created until
+        the bots have fully joined.  We must do this sort of delay because
+        we don't know how long the subprocess of bots will take to have enough bots 
+        """
+        return self.nBotsAndOneLivePlayer(2)
+    # -------------------------------------------------------------------------
+    def test04_tablePicker_someBots_playShouldSucceed_3bot(self):
+        """test04_tablePicker_someBots_playShouldSucceed_3bot
+        This is the actual test for bug #14139.  Similar to the previous
+        test, but instead we create a bot server.  The code in this test
+        uses a serious of reactor.callLater()'s and two deferreds to be
+        sure that the user-controlled player does not get created until
+        the bots have fully joined.  We must do this sort of delay because
+        we don't know how long the subprocess of bots will take to have enough bots 
+        """
+        return self.nBotsAndOneLivePlayer(3)
+    # -------------------------------------------------------------------------
+    def test05_tablePicker_someBots_playShouldSucceed_4bot(self):
+        """test05_tablePicker_someBots_playShouldSucceed_4bot
+        This is the actual test for bug #14139.  Similar to the previous
+        test, but instead we create a bot server.  The code in this test
+        uses a serious of reactor.callLater()'s and two deferreds to be
+        sure that the user-controlled player does not get created until
+        the bots have fully joined.  We must do this sort of delay because
+        we don't know how long the subprocess of bots will take to have enough bots 
+        """
+        return self.nBotsAndOneLivePlayer(4)
+    # -------------------------------------------------------------------------
+    def test06_tablePicker_someBots_playShouldSucceed_5bot(self):
+        """test06_tablePicker_someBots_playShouldSucceed_5bot
+        This is the actual test for bug #14139.  Similar to the previous
+        test, but instead we create a bot server.  The code in this test
+        uses a serious of reactor.callLater()'s and two deferreds to be
+        sure that the user-controlled player does not get created until
+        the bots have fully joined.  We must do this sort of delay because
+        we don't know how long the subprocess of bots will take to have enough bots 
+        """
+        return self.nBotsAndOneLivePlayer(5)
+##############################################################################
+def Run():
+    loader = runner.TestLoader()
+#    loader.methodPrefix = "test02"
+    suite = loader.suiteFactory()
+    suite.addTest(loader.loadClass(PokerAvatarTablePickerDoesNotDealHandTestCase))
+    return runner.TrialRunner(
+        reporter.VerboseTextReporter,
+#                              tracebackFormat='verbose',
+                              tracebackFormat='default',
+                              ).run(suite)
+
+# ------------------------------------------------------
+if __name__ == '__main__':
+    if Run().wasSuccessful():
+        sys.exit(0)
+    else:
+        sys.exit(1)
+# Interpreted by emacs
+# Local Variables:
+# compile-command: "( cd .. ; ./config.status tests/test-bug-14139.py tests/test-pokeravatar.py ) ; ( cd ../tests ; make VERBOSE_T=-1 COVERAGE_FILES='../pokernetwork/pokeravatar.py' TESTS='coverage-reset test-bug-14139.py coverage-report' check )"
+# End:
+
+# I have this set to cover pokerpackets because this isn't really a
+# coverage test and therefore I would rather not have errors when it fails
+# to cover some particular file.
diff --git a/poker-network/tests/test-pokeravatar.py.in b/poker-network/tests/test-pokeravatar.py.in
index 69fba7e..b791c44 100644
--- a/poker-network/tests/test-pokeravatar.py.in
+++ b/poker-network/tests/test-pokeravatar.py.in
@@ -356,7 +356,9 @@ class PokerAvatarTestCaseBaseClass(unittest.TestCase):
         return (client, packet)
     # ------------------------------------------------------------------------
     def readyToPlay(self, (client, packet), id, gameId ):
-        avatar = self.service.avatars[id]
+        avatars = self.service.avatar_collection.get(client.getSerial())
+        self.failUnless(len(avatars) ==  1, "Only one avatar should have this serial")
+        avatar = avatars[0]
         avatar.queuePackets()
         avatar.handlePacketLogic(PacketPokerReadyToPlay(serial = client.getSerial(),
                                                         game_id = gameId))
@@ -382,6 +384,97 @@ class PokerAvatarTestCaseBaseClass(unittest.TestCase):
                 print packet
         self.assertEquals(found, True)
         return (client, packet)
+    # ------------------------------------------------------------------------
+    def dealTable(self, (client, packet), gameId):
+        table = self.service.getTable(gameId)
+        table.beginTurn()
+        table.update()
+        return (client, packet)
+    # ------------------------------------------------------------------------
+    def beginHandSetup(self, (client, packet), gameId, dealerAssigned = 1,
+                       blindAmount = 100, blindExpected = 'small'):
+        table = self.service.getTable(gameId)
+        avatar0 = self.service.avatars[0]
+        avatar1 = self.service.avatars[1]
+        avatar0.queuePackets()
+        avatar1.queuePackets()
+        # Handle the packets that initially arrive.  I learned what to
+        # expect from "What to expect while a hand is being played?" in
+        # pokerpackets.py
+        packetList = []
+        packetList.extend(avatar0.resetPacketsQueue())
+        packetList.extend(avatar1.resetPacketsQueue())
+        playersExpect = [ avatar0.getSerial(), avatar1.getSerial() ]
+        playersExpect.sort()
+        found = 0
+        for packet in packetList:
+            if packet.type == PACKET_POKER_IN_GAME:
+                found += 1
+                self.assertEquals(packet.serial, 0)
+                self.assertEquals(packet.game_id, gameId)
+                packet.players.sort()
+                self.assertEquals(packet.players, playersExpect)
+            elif packet.type == PACKET_POKER_DEALER:
+                found += 1
+                self.assertEquals(packet.dealer, dealerAssigned)
+                self.assertEquals(packet.previous_dealer, -1)
+                self.assertEquals(packet.game_id, gameId)
+            elif packet.type == PACKET_POKER_START:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.level, 0)
+                self.assertEquals(packet.hand_serial, 1)
+                self.assertEquals(packet.serial, 0)
+                self.assertEquals(packet.hands_count, 0)
+                self.assertEquals(packet.time, 0)
+            elif packet.type == PACKET_POKER_POSITION:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(True, packet.serial == avatar0.getSerial() 
+                                        or packet.serial == avatar1.getSerial())
+            elif packet.type == PACKET_POKER_CHIPS_POT_RESET:
+                found += 1
+                self.assertEquals(packet.cookie, "")
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.length, 11)
+                self.assertEquals(packet.serial, 0)
+            elif packet.type == PACKET_POKER_BLIND_REQUEST:
+                found += 1
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.dead, 0)
+                self.assertEquals(packet.serial, avatar1.getSerial())
+                self.assertEquals(packet.amount, blindAmount)
+                self.assertEquals(packet.state, blindExpected)
+            elif packet.type == PACKET_POKER_SELF_IN_POSITION:
+                self.assertEquals(packet.serial, avatar1.getSerial())
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(packet.position, -1)
+                self.assertEquals(packet.length, 8)
+            elif packet.type == PACKET_POKER_BOARD_CARDS:
+                self.assertEquals(packet.game_id, gameId)
+                self.assertEquals(True, packet.serial == avatar0.getSerial() 
+                                        or packet.serial == avatar1.getSerial())
+                
+                self.assertEquals(packet.cards, [])
+            # When I was writing this loop, I also saw a number of:
+            # POKER_PLAYER_CHIPS and also the POKER_PLAYER_ARRIVE for
+            # serial 5, but I thought it was safe to ignore them here.
+        self.assertEquals(found, 14)
+        avatar0.queuePackets()
+        avatar1.queuePackets()
+        return (client, packet)
+    # ------------------------------------------------------------------------
+    def doBlindPost(self, (client, packet), id, gameId):
+        # By now, we should have seen as noted above, a request for the
+        # blinds for avatar1 for 100 small blind.  Here we send it.
+        avatars = self.service.avatar_collection.get(client.getSerial())
+        self.failUnless(len(avatars) ==  1, "Only one avatar should have this serial")
+        avatar = avatars[0]
+        avatar.queuePackets()
+        avatar.handlePacketLogic(PacketPokerBlind(serial= avatar.getSerial(),
+                                                   game_id = gameId, dead = 0,
+                                                   amount = 100))
+        return (client, packet)
 ##############################################################################
 class PokerAvatarTestCase(PokerAvatarTestCaseBaseClass):
     # -------------------------------------------------------------------------
@@ -788,94 +881,6 @@ class PokerAvatarTestCase(PokerAvatarTestCaseBaseClass):
         d.addCallback(self.statsQuery, 0)
         return d
     # ------------------------------------------------------------------------
-    def dealTable(self, (client, packet), gameId):
-        table = self.service.getTable(gameId)
-        table.beginTurn()
-        table.update()
-        return (client, packet)
-    # ------------------------------------------------------------------------
-    def beginHandSetup(self, (client, packet), gameId):
-        table = self.service.getTable(gameId)
-        avatar0 = self.service.avatars[0]
-        avatar1 = self.service.avatars[1]
-        avatar0.queuePackets()
-        avatar1.queuePackets()
-        # Handle the packets that initially arrive.  I learned what to
-        # expect from "What to expect while a hand is being played?" in
-        # pokerpackets.py
-        packetList = []
-        packetList.extend(avatar0.resetPacketsQueue())
-        packetList.extend(avatar1.resetPacketsQueue())
-        playersExpect = [ avatar0.getSerial(), avatar1.getSerial() ]
-        playersExpect.sort()
-        found = 0
-        for packet in packetList:
-            if packet.type == PACKET_POKER_IN_GAME:
-                found += 1
-                self.assertEquals(packet.serial, 0)
-                self.assertEquals(packet.game_id, gameId)
-                packet.players.sort()
-                self.assertEquals(packet.players, playersExpect)
-            elif packet.type == PACKET_POKER_DEALER:
-                found += 1
-                self.assertEquals(packet.dealer, 1)
-                self.assertEquals(packet.previous_dealer, -1)
-                self.assertEquals(packet.game_id, gameId)
-            elif packet.type == PACKET_POKER_START:
-                found += 1
-                self.assertEquals(packet.game_id, gameId)
-                self.assertEquals(packet.level, 0)
-                self.assertEquals(packet.hand_serial, 1)
-                self.assertEquals(packet.serial, 0)
-                self.assertEquals(packet.hands_count, 0)
-                self.assertEquals(packet.time, 0)
-            elif packet.type == PACKET_POKER_POSITION:
-                found += 1
-                self.assertEquals(packet.game_id, gameId)
-                self.assertEquals(True, packet.serial == avatar0.getSerial() 
-                                        or packet.serial == avatar1.getSerial())
-            elif packet.type == PACKET_POKER_CHIPS_POT_RESET:
-                found += 1
-                self.assertEquals(packet.cookie, "")
-                self.assertEquals(packet.game_id, gameId)
-                self.assertEquals(packet.length, 11)
-                self.assertEquals(packet.serial, 0)
-            elif packet.type == PACKET_POKER_BLIND_REQUEST:
-                found += 1
-                self.assertEquals(packet.game_id, gameId)
-                self.assertEquals(packet.dead, 0)
-                self.assertEquals(packet.serial, avatar1.getSerial())
-                self.assertEquals(packet.amount, 100)
-                self.assertEquals(packet.state, 'small')
-            elif packet.type == PACKET_POKER_SELF_IN_POSITION:
-                self.assertEquals(packet.serial, avatar1.getSerial())
-                self.assertEquals(packet.game_id, gameId)
-                self.assertEquals(packet.position, -1)
-                self.assertEquals(packet.length, 8)
-            elif packet.type == PACKET_POKER_BOARD_CARDS:
-                self.assertEquals(packet.game_id, gameId)
-                self.assertEquals(True, packet.serial == avatar0.getSerial() 
-                                        or packet.serial == avatar1.getSerial())
-                
-                self.assertEquals(packet.cards, [])
-            # When I was writing this loop, I also saw a number of:
-            # POKER_PLAYER_CHIPS and also the POKER_PLAYER_ARRIVE for
-            # serial 5, but I thought it was safe to ignore them here.
-        self.assertEquals(found, 14)
-        avatar0.queuePackets()
-        avatar1.queuePackets()
-        return (client, packet)
-    # ------------------------------------------------------------------------
-    def doBlindPost(self, (client, packet), id, gameId):
-        # By now, we should have seen as noted above, a request for the
-        # blinds for avatar1 for 100 small blind.  Here we send it.
-        avatar = self.service.avatars[id]
-        avatar.queuePackets()
-        avatar.handlePacketLogic(PacketPokerBlind(serial= avatar.getSerial(),
-                                                   game_id = gameId, dead = 0,
-                                                   amount = 100))
-        return (client, packet)
-    # ------------------------------------------------------------------------
     def startHandAndReceiveCards(self, (client, packet), gameId):
         table = self.service.getTable(gameId)
 
@@ -4238,7 +4243,7 @@ settings_xml_table_picker_server = """<?xml version="1.0" encoding="ISO-8859-1"?
   <table name="Limit HE 10-max 2/4" variant="holdem" betting_structure="2-4-limit" seats="10" player_timeout="60" currency_serial="2" />
   <table name="Limit HE 6-max 2/4" variant="holdem" betting_structure="2-4-limit" seats="6" player_timeout="60" currency_serial="2" />
   <table name="Stud 8-max 2/4" variant="7stud" betting_structure="2-4-limit" seats="8" player_timeout="60" currency_serial="2" />
-
+  <table name="Play Money NL HE 10-max 1/2" variant="holdem" betting_structure="1-2-no-limit" seats="10" player_timeout="60" currency_serial="0" />
   <listen tcp="19480" />
 
   <cashier acquire_timeout="5" pokerlock_queue_timeout="30" user_create="yes"/>
@@ -4250,7 +4255,7 @@ settings_xml_table_picker_server = """<?xml version="1.0" encoding="ISO-8859-1"?
 </server>
 """
 # -----------------------------------------------------------------------------
-class PokerAvatarTablePickerTestCase(PokerAvatarTestCaseBaseClass):
+class PokerAvatarTablePickerBaseClass(PokerAvatarTestCaseBaseClass):
     # Timeout needs to be higher for this test case because some of the
     # later tests add so many clients that 240 isn't enough.
     timeout = 540
@@ -4318,7 +4323,9 @@ class PokerAvatarTablePickerTestCase(PokerAvatarTestCaseBaseClass):
 
         preBuyInBalance = self.service.getMoney(client.getSerial(), table.currency_serial)
 
-        avatar = self.service.avatars[id]
+        avatars = self.service.avatar_collection.get(client.getSerial())
+        self.failUnless(len(avatars) ==  1, "Only one avatar should have this serial")
+        avatar = avatars[0]
         avatar.queuePackets()
         avatar.handlePacketLogic(
             PacketPokerTablePicker(serial = client.getSerial(),
@@ -4433,8 +4440,9 @@ class PokerAvatarTablePickerTestCase(PokerAvatarTestCaseBaseClass):
                     "best buy in amount is undefined or 0")
         self.failIf(minBuyInAmount == None or minBuyInAmount <= 0,
                     "min buy in amount is undefined or 0")
-        self.assertEquals(preBuyInBalance - amountBoughtIn, 
-                        self.service.getMoney(client.getSerial(), table.currency_serial))
+        if table.currency_serial > 0:
+            self.assertEquals(preBuyInBalance - amountBoughtIn, 
+                              self.service.getMoney(client.getSerial(), table.currency_serial))
         # Next, we should never have been allowed to buy in at a table with
         # a minimum less than we have.
         self.failUnless(preBuyInBalance > minBuyInAmount)
@@ -4487,6 +4495,7 @@ class PokerAvatarTablePickerTestCase(PokerAvatarTestCaseBaseClass):
         d.addCallback(self.login, playerNumber)
 
         return d
+class PokerAvatarTablePickerTestCase(PokerAvatarTablePickerBaseClass):
     # ------------------------------------------------------------------------
     def test00_tablePicker_failure_by_min(self):
         self.createClients(1)
@@ -4626,6 +4635,7 @@ class PokerAvatarTablePickerTestCase(PokerAvatarTestCaseBaseClass):
         playersDeferreds.append(pickerDeferred)
         return defer.DeferredList(playersDeferreds)
     # ------------------------------------------------------------------------
+
     def test09_tablePicker_noTableDueToLackofFunds(self):
         self.createClients(4)
 
@@ -4704,6 +4714,7 @@ class PokerAvatarTablePickerTestCase(PokerAvatarTestCaseBaseClass):
 ##############################################################################
 def Run():
     loader = runner.TestLoader()
+#    loader.methodPrefix = "test15"
 #    loader.methodPrefix = "test8"
     suite = loader.suiteFactory()
     suite.addTest(loader.loadClass(PokerAvatarTestCase))
-- 

   -- bkuhn
_______________________________________________
Pokersource-users mailing list
[email protected]
https://mail.gna.org/listinfo/pokersource-users

Reply via email to