mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-25 17:10:53 +00:00
INIT
This commit is contained in:
parent
e8530a146d
commit
93c073e2df
295 changed files with 86794 additions and 0 deletions
16
diplomacy/tests/__init__.py
Normal file
16
diplomacy/tests/__init__.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
1
diplomacy/tests/ai/__init__.py
Normal file
1
diplomacy/tests/ai/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Empty file to make tests a package
|
||||
73
diplomacy/tests/ai/test_lm_clients.py
Normal file
73
diplomacy/tests/ai/test_lm_clients.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import os
|
||||
import unittest
|
||||
|
||||
from lm_service_versus import (
|
||||
OpenAIClient,
|
||||
ClaudeClient,
|
||||
GeminiClient,
|
||||
DeepSeekClient
|
||||
)
|
||||
|
||||
class TestOpenAIClient(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.model_name = "gpt-4o-mini" # or "o3-mini", etc.
|
||||
self.client = OpenAIClient(self.model_name)
|
||||
|
||||
def test_openai_key_exists(self):
|
||||
self.assertIsNotNone(os.environ.get("OPENAI_API_KEY"),
|
||||
"OPENAI_API_KEY is not set in the environment.")
|
||||
|
||||
def test_openai_basic_response(self):
|
||||
"""Integration test: calls the LLM with a minimal prompt."""
|
||||
prompt = "Hello from unit test. Please respond with a short phrase."
|
||||
response = self.client.generate_response(prompt)
|
||||
self.assertTrue(len(response) > 0, "OpenAI returned an empty response.")
|
||||
|
||||
|
||||
class TestClaudeClient(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.model_name = "claude-3-5-sonnet-20241022" # or "claude-3-5-sonnet-20241022"
|
||||
self.client = ClaudeClient(self.model_name)
|
||||
|
||||
def test_claude_key_exists(self):
|
||||
self.assertIsNotNone(os.environ.get("ANTHROPIC_API_KEY"),
|
||||
"ANTHROPIC_API_KEY is not set in the environment.")
|
||||
|
||||
def test_claude_basic_response(self):
|
||||
prompt = "Hello from unit test. Please respond with a short phrase."
|
||||
response = self.client.generate_response(prompt)
|
||||
self.assertTrue(len(response) > 0, "Claude returned an empty response.")
|
||||
|
||||
|
||||
class TestGeminiClient(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.model_name = "gemini-1.5-flash"
|
||||
self.client = GeminiClient(self.model_name)
|
||||
|
||||
def test_gemini_key_exists(self):
|
||||
self.assertIsNotNone(os.environ.get("GEMINI_API_KEY"),
|
||||
"GEMINI_API_KEY is not set in the environment.")
|
||||
|
||||
def test_gemini_basic_response(self):
|
||||
prompt = "Hello from unit test. Please respond with a short phrase."
|
||||
response = self.client.generate_response(prompt)
|
||||
self.assertTrue(len(response) > 0, "Gemini returned an empty response.")
|
||||
|
||||
|
||||
class TestDeepSeekClient(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.model_name = "deepseek-reasoner"
|
||||
self.client = DeepSeekClient(self.model_name)
|
||||
|
||||
def test_deepseek_key_exists(self):
|
||||
self.assertIsNotNone(os.environ.get("DEEPSEEK_API_KEY"),
|
||||
"DEEPSEEK_API_KEY is not set in the environment.")
|
||||
|
||||
def test_deepseek_basic_response(self):
|
||||
prompt = "Hello from unit test. Please respond with a short phrase."
|
||||
response = self.client.generate_response(prompt)
|
||||
self.assertTrue(len(response) > 0, "DeepSeek returned an empty response.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
1
diplomacy/tests/network/1.json
Normal file
1
diplomacy/tests/network/1.json
Normal file
File diff suppressed because one or more lines are too long
1
diplomacy/tests/network/2.json
Normal file
1
diplomacy/tests/network/2.json
Normal file
File diff suppressed because one or more lines are too long
1
diplomacy/tests/network/3.json
Normal file
1
diplomacy/tests/network/3.json
Normal file
File diff suppressed because one or more lines are too long
16
diplomacy/tests/network/__init__.py
Normal file
16
diplomacy/tests/network/__init__.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
82
diplomacy/tests/network/run_real_game.py
Normal file
82
diplomacy/tests/network/run_real_game.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env python3
|
||||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
""" Run tests from diplomacy.tests.network.test_real_game to test games in a real environment.
|
||||
Each test run a game and checks game messages and phases against an expected game data file.
|
||||
Current tested gama data files are JSON files located into folder diplomacy/tests/network:
|
||||
|
||||
- 1.json
|
||||
- 2.json
|
||||
- 3.json
|
||||
|
||||
Need a local diplomacy server running. You must specify
|
||||
this server port using parameter ``--port=<server_port>``.
|
||||
|
||||
To run all tests: ::
|
||||
|
||||
python -m diplomacy.tests.network.run_real_game --port=<server_port>
|
||||
|
||||
To run a specific test (e.g. 2.json, or 2.json and 1.json): ::
|
||||
|
||||
python -m diplomacy.tests.network.run_real_game --cases=2 --port=<server_port>
|
||||
python -m diplomacy.tests.network.run_real_game --cases=1,2 --port=<server_port>
|
||||
|
||||
For help: ::
|
||||
|
||||
python -m diplomacy.tests.network.run_real_game --help
|
||||
|
||||
"""
|
||||
import argparse
|
||||
from tornado import gen
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
from diplomacy.tests.network import test_real_game
|
||||
|
||||
def launch_case(case_name, port, io_loop):
|
||||
""" Launch a game case. """
|
||||
case_data = test_real_game.CaseData(case_name, port=port)
|
||||
case_data.io_loop = io_loop
|
||||
return test_real_game.main(case_data)
|
||||
|
||||
def main():
|
||||
""" Main function for this module. Load and run tests.
|
||||
Each test run a game and checks game messages and phases against an expected game data file.
|
||||
Current tested gama data files are JSON files located into folder diplomacy/tests/network.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description='Run test cases against an external server to connect.')
|
||||
parser.add_argument('--port', type=int, required=True,
|
||||
help='run on the given port (required)')
|
||||
parser.add_argument('--cases', action='append',
|
||||
help="Run given cases. "
|
||||
"Each case <C> must match a test case file <C>.json located in diplomacy.tests.network. "
|
||||
"If not provided, all available cases are run.")
|
||||
args = parser.parse_args()
|
||||
io_loop = IOLoop()
|
||||
io_loop.make_current()
|
||||
|
||||
@gen.coroutine
|
||||
def run():
|
||||
""" Run all tests consecutively in one call. """
|
||||
tests = set(args.cases) if args.cases else {'1', '2', '3'}
|
||||
for test_case in list(sorted(tests)):
|
||||
yield launch_case('%s.json' % test_case, args.port, io_loop)
|
||||
|
||||
io_loop.run_sync(run)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
630
diplomacy/tests/network/test_real_game.py
Normal file
630
diplomacy/tests/network/test_real_game.py
Normal file
|
|
@ -0,0 +1,630 @@
|
|||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
""" Test server game in real environment with test data in files `{15, 20, 23}.json`. """
|
||||
# pylint: disable=unused-argument
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
from typing import Dict
|
||||
|
||||
from tornado import gen
|
||||
from tornado.concurrent import Future
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
import ujson as json
|
||||
|
||||
from diplomacy.client.connection import connect
|
||||
from diplomacy.server.server import Server
|
||||
from diplomacy.engine.game import Game
|
||||
from diplomacy.engine.map import Map
|
||||
from diplomacy.engine.message import GLOBAL, Message as EngineMessage
|
||||
from diplomacy.utils import common, constants, strings
|
||||
|
||||
LOGGER = logging.getLogger('diplomacy.tests.network.test_real_game')
|
||||
|
||||
DEFAULT_HOSTNAME = 'localhost'
|
||||
|
||||
DEFAULT_PORT = random.randint(9000, 10000)
|
||||
|
||||
class ExpectedPhase:
|
||||
""" Helper class to manage data from an expected phase. """
|
||||
__slots__ = ['name', 'state', 'orders', 'messages']
|
||||
|
||||
def __init__(self, json_phase):
|
||||
""" Initialize expected phase.
|
||||
|
||||
:param json_phase: JSON dict representing a phase. Expected fields: name, state, orders, messages.
|
||||
"""
|
||||
self.name = json_phase['name']
|
||||
self.state = json_phase['state']
|
||||
self.orders = json_phase['orders']
|
||||
self.messages = [EngineMessage(**json_message) for json_message in json_phase['messages']]
|
||||
|
||||
self.messages.sort(key=lambda msg: msg.time_sent)
|
||||
|
||||
def get_power_orders(self, power_name):
|
||||
""" Return expected orders for given power name. """
|
||||
return self.orders[power_name]
|
||||
|
||||
def get_power_related_messages(self, power_name):
|
||||
""" Return expected messages for given power name. """
|
||||
return [message for message in self.messages
|
||||
if message.sender == power_name or message.recipient in (power_name, GLOBAL)]
|
||||
|
||||
class ExpectedMessages:
|
||||
""" Expected list of messages sent and received by a power name. """
|
||||
__slots__ = ['power_name', 'messages', 'next_messages_to_send']
|
||||
|
||||
def __init__(self, power_name, messages):
|
||||
""" Initialize the expected messages.
|
||||
|
||||
:param power_name: power name which exchanges these messages
|
||||
:param messages: messages exchanged
|
||||
"""
|
||||
self.power_name = power_name
|
||||
self.messages = messages # type: [EngineMessage]
|
||||
self.next_messages_to_send = []
|
||||
|
||||
def has_messages_to_receive(self):
|
||||
""" Return True if messages list still contains messages to receive. """
|
||||
return any(message.sender != self.power_name for message in self.messages)
|
||||
|
||||
def has_messages_to_send(self):
|
||||
""" Return True if messages list still contains messages to send. """
|
||||
return any(message.sender == self.power_name for message in self.messages)
|
||||
|
||||
def move_forward(self):
|
||||
""" Move next messages to send from messages list to sending queue (self.next_messages_to_send). """
|
||||
self.next_messages_to_send.clear()
|
||||
if self.messages:
|
||||
if self.messages[0].sender != self.power_name:
|
||||
# First message in stack is a message to receive. We cannot send any message
|
||||
# until all messages to receive at top of stack were indeed received.
|
||||
return
|
||||
next_message_to_receive = len(self.messages)
|
||||
for index, message in enumerate(self.messages):
|
||||
if message.sender != self.power_name:
|
||||
next_message_to_receive = index
|
||||
break
|
||||
self.next_messages_to_send.extend(self.messages[:next_message_to_receive])
|
||||
del self.messages[:next_message_to_receive]
|
||||
|
||||
class ExpectedData:
|
||||
""" Expected data for a power in a game. """
|
||||
|
||||
__slots__ = ['messages', 'phases', '__phase_index', 'playing']
|
||||
|
||||
def __init__(self, power_name, phases, phase_index):
|
||||
""" Initialize expected data for a game power.
|
||||
|
||||
:param power_name: name of power for which those data are expected.
|
||||
:param phases: list of expected phases.
|
||||
:param phase_index: index of current expected phase in given phases.
|
||||
:type power_name: str
|
||||
:type phases: list[ExpectedPhase]
|
||||
:type phase_index: int
|
||||
"""
|
||||
self.messages = ExpectedMessages(power_name, phases[phase_index].get_power_related_messages(power_name))
|
||||
self.phases = phases
|
||||
self.__phase_index = phase_index
|
||||
self.playing = False
|
||||
|
||||
power_name = property(lambda self: self.messages.power_name)
|
||||
phase_index = property(lambda self: self.__phase_index)
|
||||
expected_phase = property(lambda self: self.phases[self.__phase_index])
|
||||
|
||||
def move_forward(self):
|
||||
""" Move to next expected phase. """
|
||||
self.__phase_index += 1
|
||||
if self.__phase_index < len(self.phases):
|
||||
self.messages = ExpectedMessages(
|
||||
self.messages.power_name, self.phases[self.__phase_index].get_power_related_messages(self.power_name))
|
||||
|
||||
class CaseData:
|
||||
""" Helper class to store test data. """
|
||||
FILE_FOLDER_NAME = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
def __init__(self, case_file_name, hostname=DEFAULT_HOSTNAME, port=DEFAULT_PORT):
|
||||
""" Initialize game test.
|
||||
|
||||
:param case_file_name: File name of JSON file containing expected game data.
|
||||
JSON file must be located in folder FILE_FOLDER_NAME.
|
||||
:param hostname: hostname to use to load server.
|
||||
:param port: port to use to load server.
|
||||
"""
|
||||
full_file_path = os.path.join(self.FILE_FOLDER_NAME, case_file_name)
|
||||
with open(full_file_path, 'rb') as file:
|
||||
data = json.load(file)
|
||||
self.case_name = case_file_name
|
||||
self.map_name = data['map']
|
||||
self.phases = [ExpectedPhase(json_phase) for json_phase in data['phases']]
|
||||
self.rules = set(data['rules'])
|
||||
self.rules.add('POWER_CHOICE')
|
||||
self.rules.add('REAL_TIME')
|
||||
|
||||
self.test_server = None
|
||||
self.io_loop = None # type: IOLoop
|
||||
self.connection = None
|
||||
self.admin_channel = None
|
||||
self.admin_game = None
|
||||
self.user_games = {}
|
||||
self.future_games_ended = {} # type: Dict[str, Future]
|
||||
|
||||
self.hostname = hostname
|
||||
self.port = port
|
||||
|
||||
def terminate_game(self, power_name):
|
||||
""" Tell Tornado that a power game is finished. """
|
||||
self.future_games_ended[power_name].set_result(None)
|
||||
|
||||
@gen.coroutine
|
||||
def on_power_phase_update(self, game, notification=None):
|
||||
""" User game notification callback for game phase updated.
|
||||
|
||||
:param game: game
|
||||
:param notification: notification
|
||||
:type game: NetworkGame
|
||||
:type notification: diplomacy.communication.notifications.GameProcessed | None
|
||||
"""
|
||||
print('We changed phase for power', game.power.name)
|
||||
expected_data = game.data # type: ExpectedData
|
||||
expected_data.move_forward()
|
||||
if expected_data.phase_index >= len(expected_data.phases):
|
||||
assert expected_data.phase_index == len(expected_data.phases)
|
||||
self.terminate_game(game.data.power_name)
|
||||
print('Game fully terminated at phase', game.phase)
|
||||
else:
|
||||
yield verify_current_phase(game)
|
||||
|
||||
@gen.coroutine
|
||||
def on_power_state_update(self, game, notification):
|
||||
""" User game notification callback for game state update.
|
||||
|
||||
:param game: game
|
||||
:param notification: notification
|
||||
:type game: NetworkGame
|
||||
:type notification: diplomacy.communication.notifications.GamePhaseUpdate
|
||||
"""
|
||||
if notification.phase_data_type == strings.PHASE:
|
||||
yield self.on_power_phase_update(game, None)
|
||||
|
||||
@gen.coroutine
|
||||
def send_messages_if_needed(game, expected_messages):
|
||||
""" Take messages to send in top of given messages list and send them.
|
||||
|
||||
:param game: a NetworkGame object.
|
||||
:param expected_messages: an instance of ExpectedMessages.
|
||||
:type game: NetworkGame
|
||||
:type expected_messages: ExpectedMessages
|
||||
"""
|
||||
power_name = game.power.name
|
||||
|
||||
if expected_messages.messages:
|
||||
expected_messages.move_forward()
|
||||
for message in expected_messages.next_messages_to_send:
|
||||
if message.recipient == GLOBAL:
|
||||
print('%s/sending global message (time %d)' % (power_name, message.time_sent))
|
||||
yield game.send_game_message(message=game.new_global_message(message.message))
|
||||
print('%s/sent global message (time %d)' % (power_name, message.time_sent))
|
||||
else:
|
||||
print('%s/sending message to %s (time %d)' % (power_name, message.recipient, message.time_sent))
|
||||
yield game.send_game_message(message=game.new_power_message(
|
||||
message.recipient, message.message))
|
||||
print('%s/sent message to %s (time %d)' % (power_name, message.recipient, message.time_sent))
|
||||
expected_messages.next_messages_to_send.clear()
|
||||
|
||||
@gen.coroutine
|
||||
def send_current_orders(game):
|
||||
""" Send expected orders for current phase.
|
||||
|
||||
:param game: a Network game object.
|
||||
:type game: NetworkGame
|
||||
"""
|
||||
expected_data = game.data # type: ExpectedData
|
||||
orders_to_send = expected_data.expected_phase.get_power_orders(expected_data.power_name)
|
||||
if orders_to_send is None:
|
||||
orders_to_send = []
|
||||
|
||||
if not orders_to_send and not game.get_orderable_locations(expected_data.power_name):
|
||||
print('%s/no need to send empty orders for unorderable power at phase %s' % (
|
||||
expected_data.power_name, expected_data.expected_phase.name))
|
||||
return
|
||||
|
||||
print('%s/sending %d orders for phase %s: %s' % (expected_data.power_name, len(orders_to_send),
|
||||
expected_data.expected_phase.name, orders_to_send))
|
||||
yield game.set_orders(orders=orders_to_send)
|
||||
print('%s/sent orders for phase %s' % (expected_data.power_name, expected_data.expected_phase.name))
|
||||
|
||||
def on_message_received(game, notification):
|
||||
""" User game notification callback for messages received.
|
||||
|
||||
:param game: a NetworkGame object,
|
||||
:param notification: a notification received by this game.
|
||||
:type game: NetworkGame
|
||||
:type notification: diplomacy.communication.notifications.GameMessageReceived
|
||||
"""
|
||||
power_name = game.power.name
|
||||
messages = game.data.messages # type: ExpectedMessages
|
||||
|
||||
if not messages.has_messages_to_receive():
|
||||
raise AssertionError('%s/should not receive more messages.' % power_name)
|
||||
|
||||
power_from = notification.message.sender
|
||||
index_found = None
|
||||
for index, expected_message in enumerate(messages.messages):
|
||||
if expected_message.recipient == power_from:
|
||||
raise AssertionError(
|
||||
'%s/there are still messages to send to %s (%d) before receiving messages from him. Received: %s'
|
||||
% (power_name, power_from, expected_message.time_sent, notification.message.message))
|
||||
if expected_message.sender == power_from:
|
||||
if notification.message.is_global():
|
||||
if not (expected_message.recipient == GLOBAL
|
||||
and expected_message.message == notification.message.message):
|
||||
raise AssertionError(
|
||||
'%s/first expected message from %s does not match received global message: %s'
|
||||
% (power_name, power_from, notification.message.message))
|
||||
else:
|
||||
if not (expected_message.recipient == notification.message.recipient
|
||||
and expected_message.message == notification.message.message):
|
||||
raise AssertionError(
|
||||
'%s/first expected message from %s does not match received power message: to %s: %s'
|
||||
% (power_name, power_from, notification.message.recipient, notification.message.message))
|
||||
index_found = index
|
||||
break
|
||||
|
||||
if index_found is None:
|
||||
raise AssertionError('%s/Received unknown message from %s to %s: %s' % (
|
||||
power_name, notification.message.sender, notification.message.recipient, notification.message.message))
|
||||
|
||||
expected_message = messages.messages.pop(index_found)
|
||||
|
||||
print('%s/checked message (time %d)' % (power_name, expected_message.time_sent))
|
||||
|
||||
def on_admin_game_phase_update(admin_game, notification=None):
|
||||
""" Admin game notification callback for game phase update.
|
||||
|
||||
:param admin_game: admin game
|
||||
:param notification: notification
|
||||
:type admin_game: NetworkGame
|
||||
:type notification: diplomacy.communication.notifications.GameProcessed | None
|
||||
"""
|
||||
assert admin_game.is_omniscient_game()
|
||||
expected_data = admin_game.data # type: ExpectedData
|
||||
expected_data.move_forward()
|
||||
print('=' * 80)
|
||||
print('We changed phase for admin game, moving from phase', expected_data.phase_index,
|
||||
'to phase', (expected_data.phase_index + 1), '/', len(expected_data.phases))
|
||||
print('=' * 80)
|
||||
|
||||
# state_history must not be empty.
|
||||
assert len(admin_game.state_history) == expected_data.phase_index, (
|
||||
len(admin_game.state_history), expected_data.phase_index)
|
||||
|
||||
# Verify previous game state.
|
||||
if admin_game.state_history:
|
||||
expected_state = expected_data.phases[expected_data.phase_index - 1].state
|
||||
expected_engine = Game(initial_state=expected_state)
|
||||
given_state = admin_game.state_history.last_value()
|
||||
given_engine = Game(initial_state=given_state)
|
||||
|
||||
print('Verifying expected previous phase', expected_engine.get_current_phase())
|
||||
print('Verifying game processing from previous phase to next phase.')
|
||||
|
||||
other_expected_engine = Game(initial_state=expected_state)
|
||||
other_expected_engine.process()
|
||||
other_given_engine = Game(initial_state=given_state)
|
||||
other_given_engine.rules.append('SOLITAIRE')
|
||||
other_given_engine.process()
|
||||
assert other_expected_engine.get_current_phase() == other_given_engine.get_current_phase(), (
|
||||
'Computed expected next phase %s, got computed given next phase %s'
|
||||
% (other_expected_engine.get_current_phase(), other_given_engine.get_current_phase())
|
||||
)
|
||||
|
||||
assert expected_engine.map_name == given_engine.map_name
|
||||
assert expected_engine.get_current_phase() == given_engine.get_current_phase()
|
||||
|
||||
expected_orders = expected_engine.get_orders()
|
||||
given_orders = given_engine.get_orders()
|
||||
assert len(expected_orders) == len(given_orders), (expected_orders, given_orders)
|
||||
for power_name in given_orders:
|
||||
assert power_name in expected_orders, power_name
|
||||
given_power_orders = list(sorted(given_orders[power_name]))
|
||||
expected_power_orders = list(sorted(expected_orders[power_name]))
|
||||
assert expected_power_orders == given_power_orders, (
|
||||
'Power orders for %s\nExpected: %s\nGiven: %s\nAll given: %s\n'
|
||||
% (power_name, expected_power_orders, given_power_orders, given_orders))
|
||||
|
||||
expected_units = expected_engine.get_units()
|
||||
given_units = expected_engine.get_units()
|
||||
assert len(expected_units) == len(given_units)
|
||||
for power_name in given_units:
|
||||
assert power_name in expected_units, (power_name, expected_units, given_units)
|
||||
expected_power_units = list(sorted(expected_units[power_name]))
|
||||
given_power_units = list(sorted(given_units[power_name]))
|
||||
assert expected_power_units == given_power_units, (
|
||||
power_name, expected_power_units, given_power_units, given_units)
|
||||
|
||||
expected_centers = expected_engine.get_centers()
|
||||
given_centers = given_engine.get_centers()
|
||||
assert len(expected_centers) == len(given_centers), (expected_centers, given_centers)
|
||||
for power_name in given_centers:
|
||||
assert power_name in expected_centers
|
||||
expected_power_centers = list(sorted(expected_centers[power_name]))
|
||||
given_power_centers = list(sorted(given_centers[power_name]))
|
||||
assert expected_power_centers == given_power_centers, (
|
||||
power_name, expected_power_centers, given_power_centers)
|
||||
|
||||
assert expected_engine.get_hash() == given_engine.get_hash(), (
|
||||
expected_engine.get_hash(), given_engine.get_hash())
|
||||
|
||||
if expected_data.phase_index >= len(expected_data.phases):
|
||||
assert expected_data.phase_index == len(expected_data.phases)
|
||||
assert admin_game.state_history.last_value()['name'] == expected_data.phases[-1].name, (
|
||||
'Wrong last phase, expected %s, got %s'
|
||||
% (admin_game.state_history.last_value()['name'], expected_data.phases[-1].name)
|
||||
)
|
||||
print('Admin game terminated.')
|
||||
|
||||
def on_admin_game_state_update(admin_game, notification):
|
||||
""" Admin game notification callback for game state update.
|
||||
|
||||
:param admin_game: admin game
|
||||
:param notification: notification
|
||||
:type admin_game: NetworkGame
|
||||
:type notification: diplomacy.communication.notifications.GamePhaseUpdate
|
||||
"""
|
||||
if notification.phase_data_type == strings.PHASE:
|
||||
on_admin_game_phase_update(admin_game, None)
|
||||
|
||||
def on_admin_powers_controllers(admin_game, notification):
|
||||
""" Admin game notification callback for powers controllers received (unexpected).
|
||||
|
||||
:param admin_game: game
|
||||
:param notification: notification
|
||||
:type admin_game: NetworkGame
|
||||
:type notification: diplomacy.communication.notifications.PowersControllers
|
||||
"""
|
||||
LOGGER.warning('%d dummy power(s).',
|
||||
len([controller for controller in notification.powers.values() if controller == strings.DUMMY]))
|
||||
|
||||
def on_admin_game_status_update(admin_game, notification):
|
||||
""" Admin game notification callback for game status update.
|
||||
|
||||
:param admin_game: admin game
|
||||
:param notification: notification
|
||||
:type admin_game: NetworkGame
|
||||
"""
|
||||
print('(admin game) game status of %s updated to %s' % (admin_game.role, admin_game.status))
|
||||
|
||||
@gen.coroutine
|
||||
def play_phase(game, expected_messages):
|
||||
""" Play a phase for a user game:
|
||||
|
||||
#. Send messages
|
||||
#. wait for messages to receive
|
||||
#. send current orders.
|
||||
|
||||
:param game: user game
|
||||
:param expected_messages: expected messages
|
||||
:type game: NetworkGame
|
||||
:type expected_messages: ExpectedMessages
|
||||
"""
|
||||
while expected_messages.has_messages_to_send():
|
||||
yield gen.sleep(10e-6)
|
||||
yield send_messages_if_needed(game, expected_messages)
|
||||
while expected_messages.has_messages_to_receive():
|
||||
yield gen.sleep(10e-6)
|
||||
yield send_current_orders(game)
|
||||
|
||||
@gen.coroutine
|
||||
def on_game_status_update(game, notification):
|
||||
""" User game notification callback for game status update.
|
||||
Used to start the game locally when game started on server.
|
||||
|
||||
:param game: game
|
||||
:param notification: notification
|
||||
:type game: NetworkGame
|
||||
:type notification: diplomacy.communication.notifications.GameStatusUpdate
|
||||
"""
|
||||
LOGGER.warning('Game status of %s updated to %s', game.role, game.status)
|
||||
expected_data = game.data # type: ExpectedData
|
||||
if not expected_data.playing and game.is_game_active:
|
||||
# Game started on server.
|
||||
expected_data.playing = True
|
||||
print('Playing.')
|
||||
yield play_phase(game, expected_data.messages)
|
||||
|
||||
@gen.coroutine
|
||||
def verify_current_phase(game):
|
||||
""" Check and play current phase.
|
||||
|
||||
:param game: a NetWork game object.
|
||||
:type game: NetworkGame
|
||||
"""
|
||||
expected_data = game.data # type: ExpectedData
|
||||
|
||||
# Verify current phase.
|
||||
expected_messages = expected_data.messages
|
||||
print('=' * 80)
|
||||
print('Checking expected phase', expected_data.expected_phase.name,
|
||||
'(%d/%d) for' % (expected_data.phase_index + 1, len(expected_data.phases)), expected_data.power_name,
|
||||
'with', len(expected_data.messages.messages), 'messages.')
|
||||
print('=' * 80)
|
||||
# Verify phase name.
|
||||
if game.current_short_phase != str(expected_data.expected_phase.name):
|
||||
raise AssertionError(str(expected_data.expected_phase.name), str(game.current_short_phase))
|
||||
|
||||
if game.is_game_active:
|
||||
yield play_phase(game, expected_messages)
|
||||
|
||||
def get_user_game_fn(case_data, power_name):
|
||||
""" Return a coroutine procedure that loads and play a user game for given power name.
|
||||
|
||||
:param case_data: case data
|
||||
:param power_name: str
|
||||
:return: a procedure.
|
||||
:type case_data: CaseData
|
||||
"""
|
||||
|
||||
@gen.coroutine
|
||||
def load_fn():
|
||||
""" Coroutine for loading power game for given power name. """
|
||||
yield load_power_game(case_data, power_name)
|
||||
|
||||
return load_fn
|
||||
|
||||
def get_future_game_done_fn(power_name):
|
||||
""" Return a callback to call when a power game is finished.
|
||||
Callback currently just prints a message to tell that power game is terminated.
|
||||
|
||||
:param power_name: power name of associated game.
|
||||
:return: a callable that receives the future done when game is finished.
|
||||
"""
|
||||
|
||||
def game_done_fn(future):
|
||||
""" Function called when related game is done. """
|
||||
print('Game ended (%s).' % power_name)
|
||||
|
||||
return game_done_fn
|
||||
|
||||
@gen.coroutine
|
||||
def load_power_game(case_data, power_name):
|
||||
""" Load and play a power game from admin game for given power name.
|
||||
|
||||
:type case_data: CaseData
|
||||
"""
|
||||
print('Loading game for power', power_name)
|
||||
|
||||
username = 'user_%s' % power_name
|
||||
password = 'password_%s' % power_name
|
||||
user_channel = yield case_data.connection.authenticate(username, password)
|
||||
print('User', username, 'connected.')
|
||||
|
||||
user_game = yield user_channel.join_game(game_id=case_data.admin_game.game_id, power_name=power_name)
|
||||
assert user_game.is_player_game()
|
||||
assert user_game.power.name == power_name
|
||||
|
||||
case_data.user_games[power_name] = user_game
|
||||
print('Game created for user %s.' % username, len(user_game.messages), len(user_game.state_history))
|
||||
|
||||
# Set notification callback for user game to manage messages received.
|
||||
user_game.add_on_game_status_update(on_game_status_update)
|
||||
user_game.add_on_game_message_received(on_message_received)
|
||||
user_game.add_on_game_processed(case_data.on_power_phase_update)
|
||||
user_game.add_on_game_phase_update(case_data.on_power_state_update)
|
||||
|
||||
# Save expected data into attribute user_game.data.
|
||||
user_game.data = ExpectedData(power_name=power_name, phases=case_data.phases, phase_index=0)
|
||||
# Start to play and test game.
|
||||
yield verify_current_phase(user_game)
|
||||
|
||||
@gen.coroutine
|
||||
def main(case_data):
|
||||
""" Test real game environment with one game and all power controlled (no dummy powers).
|
||||
This method may be called form a non-test code to run a real game case.
|
||||
|
||||
:param case_data: test data
|
||||
:type case_data: CaseData
|
||||
"""
|
||||
# ================
|
||||
# Initialize test.
|
||||
# ================
|
||||
if case_data.admin_channel is None:
|
||||
LOGGER.info('Creating connection, admin channel and admin game.')
|
||||
case_data.connection = yield connect(case_data.hostname, case_data.port)
|
||||
case_data.admin_channel = yield case_data.connection.authenticate('admin', 'password')
|
||||
# NB: For all test cases, first game state should be default game engine state when starting.
|
||||
# So, we don't need to pass game state of first expected phase when creating a server game.
|
||||
case_data.admin_game = yield case_data.admin_channel.create_game(
|
||||
map_name=case_data.map_name, rules=case_data.rules, deadline=0)
|
||||
assert case_data.admin_game.power_choice
|
||||
assert case_data.admin_game.real_time
|
||||
case_data.admin_game.data = ExpectedData(power_name='', phases=case_data.phases, phase_index=0)
|
||||
case_data.admin_game.add_on_game_status_update(on_admin_game_status_update)
|
||||
case_data.admin_game.add_on_game_processed(on_admin_game_phase_update)
|
||||
case_data.admin_game.add_on_game_phase_update(on_admin_game_state_update)
|
||||
case_data.admin_game.add_on_powers_controllers(on_admin_powers_controllers)
|
||||
|
||||
# ==========
|
||||
# Test game.
|
||||
# ==========
|
||||
|
||||
# Get available maps to retrieve map power names.
|
||||
available_maps = yield case_data.admin_channel.get_available_maps()
|
||||
print('Map: %s, powers:' % case_data.map_name,
|
||||
', '.join(power_name for power_name in sorted(available_maps[case_data.map_name])))
|
||||
# Load one game per power name.
|
||||
for power_name in available_maps[case_data.map_name]['powers']:
|
||||
case_data.future_games_ended[power_name] = Future()
|
||||
case_data.future_games_ended[power_name].add_done_callback(get_future_game_done_fn(power_name))
|
||||
case_data.io_loop.add_callback(get_user_game_fn(case_data, power_name))
|
||||
|
||||
# Wait to let power games play.
|
||||
print('Running ...')
|
||||
yield case_data.future_games_ended
|
||||
print('All game terminated. Just wait a little ...')
|
||||
yield gen.sleep(2)
|
||||
print('End running.')
|
||||
|
||||
def run(case_data, **server_kwargs):
|
||||
""" Real test function called for a given case data.
|
||||
Load a server (with optional given server kwargs),
|
||||
call function main(case_data) as client code
|
||||
and wait for main function to terminate.
|
||||
|
||||
:type case_data: CaseData
|
||||
"""
|
||||
|
||||
print()
|
||||
io_loop = IOLoop()
|
||||
io_loop.make_current()
|
||||
common.Tornado.stop_loop_on_callback_error(io_loop)
|
||||
case_data.io_loop = io_loop
|
||||
case_data.test_server = Server(**server_kwargs)
|
||||
|
||||
@gen.coroutine
|
||||
def coroutine_func():
|
||||
""" Concrete call to main function. """
|
||||
yield main(case_data)
|
||||
case_data.io_loop.stop()
|
||||
print('Finished', case_data.case_name, 'at', common.timestamp_microseconds())
|
||||
|
||||
io_loop.add_callback(coroutine_func)
|
||||
case_data.test_server.start(case_data.port, io_loop)
|
||||
case_data.io_loop.clear_current()
|
||||
case_data.io_loop.close()
|
||||
case_data.test_server.backend.http_server.stop()
|
||||
|
||||
def test_maps():
|
||||
""" Building required maps to avoid timeout on the primary test """
|
||||
for map_name in ('ancmed', 'colonial', 'empire', 'known_world_901', 'modern', 'standard',
|
||||
'standard_france_austria', 'standard_germany_italy', 'world'):
|
||||
Map(map_name)
|
||||
|
||||
def test_3():
|
||||
""" Test case 3. """
|
||||
case_data = CaseData('3.json')
|
||||
run(case_data, ping_seconds=constants.DEFAULT_PING_SECONDS)
|
||||
# We must clear server caches to allow to re-create a Server with same test case but different server attributes.
|
||||
Server.__cache__.clear()
|
||||
|
||||
def test_3_ping_1s():
|
||||
""" Test case 3 with small ping (1 second). """
|
||||
case_data = CaseData('3.json')
|
||||
run(case_data, ping_seconds=1)
|
||||
# We must clear server caches to allow to re-create a Server with same test case but different server attributes.
|
||||
Server.__cache__.clear()
|
||||
5467
diplomacy/tests/test_datc.py
Normal file
5467
diplomacy/tests/test_datc.py
Normal file
File diff suppressed because it is too large
Load diff
106
diplomacy/tests/test_datc_no_check.py
Normal file
106
diplomacy/tests/test_datc_no_check.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
""" DATC Test Cases (Using rule NO_CHECK)
|
||||
- Contains the diplomacy adjudication test cases (without order validation)
|
||||
"""
|
||||
from diplomacy.engine.game import Game
|
||||
from diplomacy.tests.test_datc import TestDATC as RootDATC
|
||||
from diplomacy.utils.order_results import OK, VOID
|
||||
|
||||
# -----------------
|
||||
# DATC TEST CASES (Without order validation)
|
||||
# -----------------
|
||||
class TestDATCNoCheck(RootDATC):
|
||||
""" DATC test cases"""
|
||||
|
||||
@staticmethod
|
||||
def create_game():
|
||||
""" Creates a game object"""
|
||||
game = Game()
|
||||
game.add_rule('NO_CHECK')
|
||||
return game
|
||||
|
||||
@staticmethod
|
||||
def check_results(game, unit, value, phase='M'):
|
||||
""" Checks adjudication results """
|
||||
# pylint: disable=too-many-return-statements
|
||||
if not game:
|
||||
return False
|
||||
|
||||
result = game.result_history.last_value()
|
||||
|
||||
# Checking if the results contain duplicate values
|
||||
unit_result = result.get(unit, [])
|
||||
if len(unit_result) != len(set(unit_result)):
|
||||
raise RuntimeError('Duplicate values detected in %s' % unit_result)
|
||||
|
||||
# Done self.processing a retreats phase
|
||||
if phase == 'R':
|
||||
if value == VOID and VOID in unit_result:
|
||||
return True
|
||||
if value == OK:
|
||||
success = unit not in game.popped and unit_result == []
|
||||
if not success:
|
||||
print('Results: %s - Expected: []' % result.get(unit, '<Not Found>'))
|
||||
return success
|
||||
|
||||
success = unit in game.popped and value in unit_result
|
||||
if not success:
|
||||
print('Results: %s - Expected: %s' % (result.get(unit, '<Not Found>'), value))
|
||||
return success
|
||||
|
||||
# Done self.processing a retreats phase
|
||||
if phase == 'A':
|
||||
if value == VOID and VOID in unit_result:
|
||||
return True
|
||||
success = value == unit_result
|
||||
if not success:
|
||||
print('Results: %s - Expected: %s' % (result.get(unit, '<Not Found>'), value))
|
||||
return success
|
||||
|
||||
order_status = game.get_order_status(unit=unit)
|
||||
|
||||
# >>>>>>>>>>>>>>>>>>>>>>>>
|
||||
# For NO_CHECK, we expect to find the unit in game.orderable_units
|
||||
# But we require that the order is marked as 'void'
|
||||
# As opposed to a regular game, where an invalid order is dropped
|
||||
# <<<<<<<<<<<<<<<<<<<<<<<<
|
||||
# Invalid order
|
||||
if value == VOID:
|
||||
if VOID in result.get(unit, []):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Invalid unit
|
||||
if unit not in game.command:
|
||||
print('Results: %s NOT FOUND - Expected: %s' % (unit, value))
|
||||
return False
|
||||
|
||||
# Expected no errors
|
||||
if value == OK:
|
||||
if order_status:
|
||||
print('Results: %s - Expected: []' % order_status)
|
||||
return False
|
||||
return True
|
||||
|
||||
# Incorrect error
|
||||
if value not in game.get_order_status(unit=unit):
|
||||
print('Results: %s - Expected: %s' % (order_status, value))
|
||||
return False
|
||||
|
||||
# Correct value
|
||||
return True
|
||||
95
diplomacy/tests/test_datc_no_expand.py
Normal file
95
diplomacy/tests/test_datc_no_expand.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
""" DATC Test Cases (No Expansion)
|
||||
- Contains the diplomacy adjudication test cases (without order expansion)
|
||||
"""
|
||||
from diplomacy.tests.test_datc import TestDATC as RootDATC
|
||||
from diplomacy.utils.order_results import OK, BOUNCE, VOID
|
||||
|
||||
# -----------------
|
||||
# DATC TEST CASES (Without order expansion)
|
||||
# -----------------
|
||||
class TestDATCNoExpand(RootDATC):
|
||||
""" DATC test cases"""
|
||||
|
||||
@staticmethod
|
||||
def set_orders(game, power_name, orders):
|
||||
""" Submit orders """
|
||||
game.set_orders(power_name, orders, expand=False)
|
||||
|
||||
def test_6_b_2(self):
|
||||
""" 6.B.2. TEST CASE, MOVING WITH UNSPECIFIED COAST WHEN COAST IS NOT NECESSARY
|
||||
There is only one coast possible in this case:
|
||||
France: F Gascony - Spain
|
||||
Since the North Coast is the only coast that can be reached, it seems logical that
|
||||
the a move is attempted to the north coast of Spain. Some adjudicators require that a coast
|
||||
is also specified in this case and will decide that the move fails or take a default coast (see 4.B.2).
|
||||
I prefer that an attempt is made to the only possible coast, the north coast of Spain.
|
||||
"""
|
||||
# Expected to failed
|
||||
|
||||
def test_6_b_9(self):
|
||||
""" 6.B.9. TEST CASE, SUPPORTING WITH WRONG COAST
|
||||
Coasts can be specified in a support, but the result depends on the house rules.
|
||||
France: F Portugal Supports F Mid-Atlantic Ocean - Spain(nc)
|
||||
France: F Mid-Atlantic Ocean - Spain(sc)
|
||||
Italy: F Gulf of Lyon Supports F Western Mediterranean - Spain(sc)
|
||||
Italy: F Western Mediterranean - Spain(sc)
|
||||
See issue 4.B.4. If it is required that the coast matches, then the support of the French fleet in the
|
||||
Mid-Atlantic Ocean fails and that the Italian fleet in the Western Mediterranean moves successfully. Some
|
||||
adjudicators ignores the coasts in support orders. In that case, the move of the Italian fleet bounces.
|
||||
I prefer that the support fails and that the Italian fleet in the Western Mediterranean moves successfully.
|
||||
"""
|
||||
game = self.create_game()
|
||||
self.clear_units(game)
|
||||
self.set_units(game, 'FRANCE', ['F POR', 'F MAO'])
|
||||
self.set_units(game, 'ITALY', ['F LYO', 'F WES'])
|
||||
self.set_orders(game, 'FRANCE', ['F POR S F MAO - SPA/NC', 'F MAO - SPA/SC'])
|
||||
self.set_orders(game, 'ITALY', ['F LYO S F WES - SPA/SC', 'F WES - SPA/SC'])
|
||||
self.process(game)
|
||||
assert self.check_results(game, 'F POR', VOID)
|
||||
assert self.check_results(game, 'F MAO', BOUNCE)
|
||||
assert self.check_results(game, 'F LYO', OK)
|
||||
assert self.check_results(game, 'F WES', OK)
|
||||
assert self.owner_name(game, 'F POR') == 'FRANCE'
|
||||
assert self.owner_name(game, 'F MAO') == 'FRANCE'
|
||||
assert self.owner_name(game, 'F SPA') == 'ITALY'
|
||||
assert self.owner_name(game, 'F SPA/NC') is None
|
||||
assert self.owner_name(game, 'F SPA/SC') == 'ITALY'
|
||||
assert self.owner_name(game, 'F LYO') == 'ITALY'
|
||||
assert self.owner_name(game, 'F WES') is None
|
||||
|
||||
def test_6_b_10(self):
|
||||
""" 6.B.10. TEST CASE, UNIT ORDERED WITH WRONG COAST
|
||||
A player might specify the wrong coast for the ordered unit.
|
||||
France has a fleet on the south coast of Spain and orders:
|
||||
France: F Spain(nc) - Gulf of Lyon
|
||||
If only perfect orders are accepted, then the move will fail, but since the coast for the ordered unit
|
||||
has no purpose, it might also be ignored (see issue 4.B.5).
|
||||
I prefer that a move will be attempted.
|
||||
"""
|
||||
# Expected to fail
|
||||
|
||||
def test_6_b_12(self):
|
||||
""" 6.B.12. TEST CASE, ARMY MOVEMENT WITH COASTAL SPECIFICATION
|
||||
For armies the coasts are irrelevant:
|
||||
France: A Gascony - Spain(nc)
|
||||
If only perfect orders are accepted, then the move will fail. But it is also possible that coasts are
|
||||
ignored in this case and a move will be attempted (see issue 4.B.6).
|
||||
I prefer that a move will be attempted.
|
||||
"""
|
||||
# Expected to fail
|
||||
669
diplomacy/tests/test_game.py
Normal file
669
diplomacy/tests/test_game.py
Normal file
|
|
@ -0,0 +1,669 @@
|
|||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
""" Test_game
|
||||
- Contains tests for the game object
|
||||
"""
|
||||
from copy import deepcopy
|
||||
from diplomacy.engine.game import Game
|
||||
from diplomacy.utils.order_results import BOUNCE
|
||||
|
||||
def test_is_game_done():
|
||||
""" Tests if the game is done """
|
||||
game = Game()
|
||||
assert not game.is_game_done
|
||||
game.phase = 'COMPLETED'
|
||||
assert game.is_game_done
|
||||
|
||||
def test_create_game():
|
||||
""" Test - Creates a game """
|
||||
game = Game()
|
||||
assert not game.error
|
||||
|
||||
def test_get_units():
|
||||
""" Tests - get units """
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('FRANCE', ['A PAR', 'A MAR'])
|
||||
game.set_units('ENGLAND', ['A PAR', 'A LON'])
|
||||
units = game.get_units()
|
||||
assert units['AUSTRIA'] == []
|
||||
assert units['ENGLAND'] == ['A PAR', 'A LON']
|
||||
assert units['FRANCE'] == ['A MAR']
|
||||
assert units['GERMANY'] == []
|
||||
assert units['ITALY'] == []
|
||||
assert units['RUSSIA'] == []
|
||||
assert units['TURKEY'] == []
|
||||
|
||||
assert game.get_units('AUSTRIA') == []
|
||||
assert game.get_units('ENGLAND') == ['A PAR', 'A LON']
|
||||
assert game.get_units('FRANCE') == ['A MAR']
|
||||
assert game.get_units('GERMANY') == []
|
||||
assert game.get_units('ITALY') == []
|
||||
assert game.get_units('RUSSIA') == []
|
||||
assert game.get_units('TURKEY') == []
|
||||
|
||||
# Making sure we got a copy, and not a direct game reference
|
||||
game.set_units('FRANCE', ['F MAR'])
|
||||
units_2 = game.get_units()
|
||||
assert units['FRANCE'] == ['A MAR']
|
||||
assert units_2['FRANCE'] == ['F MAR']
|
||||
|
||||
def test_get_centers():
|
||||
""" Test - get centers """
|
||||
game = Game()
|
||||
centers = game.get_centers()
|
||||
assert centers['AUSTRIA'] == ['BUD', 'TRI', 'VIE']
|
||||
assert centers['ENGLAND'] == ['EDI', 'LON', 'LVP']
|
||||
assert centers['FRANCE'] == ['BRE', 'MAR', 'PAR']
|
||||
assert centers['GERMANY'] == ['BER', 'KIE', 'MUN']
|
||||
assert centers['ITALY'] == ['NAP', 'ROM', 'VEN']
|
||||
assert centers['RUSSIA'] == ['MOS', 'SEV', 'STP', 'WAR']
|
||||
assert centers['TURKEY'] == ['ANK', 'CON', 'SMY']
|
||||
|
||||
assert game.get_centers('AUSTRIA') == ['BUD', 'TRI', 'VIE']
|
||||
assert game.get_centers('ENGLAND') == ['EDI', 'LON', 'LVP']
|
||||
assert game.get_centers('FRANCE') == ['BRE', 'MAR', 'PAR']
|
||||
assert game.get_centers('GERMANY') == ['BER', 'KIE', 'MUN']
|
||||
assert game.get_centers('ITALY') == ['NAP', 'ROM', 'VEN']
|
||||
assert game.get_centers('RUSSIA') == ['MOS', 'SEV', 'STP', 'WAR']
|
||||
assert game.get_centers('TURKEY') == ['ANK', 'CON', 'SMY']
|
||||
|
||||
# Making sure we got a copy, and not a direct game reference
|
||||
austria = game.get_power('AUSTRIA')
|
||||
austria.centers.remove('BUD')
|
||||
centers_2 = game.get_centers()
|
||||
assert centers['AUSTRIA'] == ['BUD', 'TRI', 'VIE']
|
||||
assert centers_2['AUSTRIA'] == ['TRI', 'VIE']
|
||||
|
||||
def test_get_orders():
|
||||
""" Test - get orders """
|
||||
check_sorted = lambda list_1, list_2: sorted(list_1) == sorted(list_2)
|
||||
game = Game()
|
||||
|
||||
# Movement phase
|
||||
game.set_orders('FRANCE', ['A PAR H', 'A MAR - BUR'])
|
||||
game.set_orders('ENGLAND', ['LON H'])
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['AUSTRIA'], [])
|
||||
assert check_sorted(orders['ENGLAND'], ['F LON H'])
|
||||
assert check_sorted(orders['FRANCE'], ['A PAR H', 'A MAR - BUR'])
|
||||
assert check_sorted(orders['GERMANY'], [])
|
||||
assert check_sorted(orders['ITALY'], [])
|
||||
assert check_sorted(orders['RUSSIA'], [])
|
||||
assert check_sorted(orders['TURKEY'], [])
|
||||
|
||||
assert check_sorted(game.get_orders('AUSTRIA'), [])
|
||||
assert check_sorted(game.get_orders('ENGLAND'), ['F LON H'])
|
||||
assert check_sorted(game.get_orders('FRANCE'), ['A PAR H', 'A MAR - BUR'])
|
||||
assert check_sorted(game.get_orders('GERMANY'), [])
|
||||
assert check_sorted(game.get_orders('ITALY'), [])
|
||||
assert check_sorted(game.get_orders('RUSSIA'), [])
|
||||
assert check_sorted(game.get_orders('TURKEY'), [])
|
||||
|
||||
# Making sure we got a copy, and not a direct game reference
|
||||
france = game.get_power('FRANCE')
|
||||
del france.orders['A PAR']
|
||||
orders_2 = game.get_orders()
|
||||
assert check_sorted(orders['FRANCE'], ['A PAR H', 'A MAR - BUR'])
|
||||
assert check_sorted(orders_2['FRANCE'], ['A MAR - BUR'])
|
||||
|
||||
# Moving to W1901A
|
||||
game.clear_units('FRANCE')
|
||||
game.set_centers('FRANCE', 'SPA')
|
||||
game.process()
|
||||
game.process()
|
||||
assert game.get_current_phase() == 'W1901A'
|
||||
|
||||
# Adjustment phase
|
||||
game.set_orders('FRANCE', ['A MAR B', 'F MAR B'])
|
||||
game.set_orders('AUSTRIA', 'A PAR H')
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['AUSTRIA'], [])
|
||||
assert check_sorted(orders['ENGLAND'], [])
|
||||
assert check_sorted(orders['FRANCE'], ['A MAR B'])
|
||||
assert check_sorted(orders['GERMANY'], [])
|
||||
assert check_sorted(orders['ITALY'], [])
|
||||
assert check_sorted(orders['RUSSIA'], [])
|
||||
assert check_sorted(orders['TURKEY'], [])
|
||||
|
||||
assert check_sorted(game.get_orders('AUSTRIA'), [])
|
||||
assert check_sorted(game.get_orders('ENGLAND'), [])
|
||||
assert check_sorted(game.get_orders('FRANCE'), ['A MAR B'])
|
||||
assert check_sorted(game.get_orders('GERMANY'), [])
|
||||
assert check_sorted(game.get_orders('ITALY'), [])
|
||||
assert check_sorted(game.get_orders('RUSSIA'), [])
|
||||
assert check_sorted(game.get_orders('TURKEY'), [])
|
||||
|
||||
def test_get_orders_no_check():
|
||||
""" Test - get orders NO_CHECK """
|
||||
check_sorted = lambda list_1, list_2: sorted(list_1) == sorted(list_2)
|
||||
game = Game()
|
||||
game.add_rule('NO_CHECK')
|
||||
|
||||
# Movement phase
|
||||
game.set_orders('FRANCE', ['A PAR H', 'A MAR - BUR'])
|
||||
game.set_orders('ENGLAND', ['LON H'])
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['AUSTRIA'], [])
|
||||
assert check_sorted(orders['ENGLAND'], ['LON H']) # Should not be fixed
|
||||
assert check_sorted(orders['FRANCE'], ['A PAR H', 'A MAR - BUR'])
|
||||
assert check_sorted(orders['GERMANY'], [])
|
||||
assert check_sorted(orders['ITALY'], [])
|
||||
assert check_sorted(orders['RUSSIA'], [])
|
||||
assert check_sorted(orders['TURKEY'], [])
|
||||
|
||||
assert check_sorted(game.get_orders('AUSTRIA'), [])
|
||||
assert check_sorted(game.get_orders('ENGLAND'), ['LON H']) # Should not be fixed
|
||||
assert check_sorted(game.get_orders('FRANCE'), ['A PAR H', 'A MAR - BUR'])
|
||||
assert check_sorted(game.get_orders('GERMANY'), [])
|
||||
assert check_sorted(game.get_orders('ITALY'), [])
|
||||
assert check_sorted(game.get_orders('RUSSIA'), [])
|
||||
assert check_sorted(game.get_orders('TURKEY'), [])
|
||||
|
||||
# Making sure we got a copy, and not a direct game reference
|
||||
france = game.get_power('FRANCE')
|
||||
france.orders = {order_ix: order_value for order_ix, order_value in france.orders.items()
|
||||
if not order_value.startswith('A PAR')}
|
||||
orders_2 = game.get_orders()
|
||||
assert check_sorted(orders['FRANCE'], ['A PAR H', 'A MAR - BUR'])
|
||||
assert check_sorted(orders_2['FRANCE'], ['A MAR - BUR'])
|
||||
|
||||
# Moving to W1901A
|
||||
game.clear_units('FRANCE')
|
||||
game.set_centers('FRANCE', 'SPA')
|
||||
game.process()
|
||||
game.process()
|
||||
assert game.get_current_phase() == 'W1901A'
|
||||
|
||||
# Adjustment phase
|
||||
game.set_orders('FRANCE', ['A MAR B', 'F MAR B'])
|
||||
game.set_orders('AUSTRIA', 'A PAR H')
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['AUSTRIA'], []) # 'A PAR H' is VOID
|
||||
assert check_sorted(orders['ENGLAND'], [])
|
||||
assert check_sorted(orders['FRANCE'], ['A MAR B']) # 'F MAR B' is VOID
|
||||
assert check_sorted(orders['GERMANY'], [])
|
||||
assert check_sorted(orders['ITALY'], [])
|
||||
assert check_sorted(orders['RUSSIA'], [])
|
||||
assert check_sorted(orders['TURKEY'], [])
|
||||
|
||||
assert check_sorted(game.get_orders('AUSTRIA'), [])
|
||||
assert check_sorted(game.get_orders('ENGLAND'), [])
|
||||
assert check_sorted(game.get_orders('FRANCE'), ['A MAR B'])
|
||||
assert check_sorted(game.get_orders('GERMANY'), [])
|
||||
assert check_sorted(game.get_orders('ITALY'), [])
|
||||
assert check_sorted(game.get_orders('RUSSIA'), [])
|
||||
assert check_sorted(game.get_orders('TURKEY'), [])
|
||||
|
||||
def test_get_order_status():
|
||||
""" Tests - get order status """
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', 'A VEN')
|
||||
game.set_units('AUSTRIA', 'A VIE')
|
||||
game.set_orders('ITALY', 'A VEN - TYR')
|
||||
game.set_orders('AUSTRIA', 'A VIE - TYR')
|
||||
game.process()
|
||||
results = game.get_order_status()
|
||||
assert BOUNCE in results['ITALY']['A VEN']
|
||||
assert BOUNCE in results['AUSTRIA']['A VIE']
|
||||
assert BOUNCE in game.get_order_status(unit='A VEN')
|
||||
assert BOUNCE in game.get_order_status(unit='A VIE')
|
||||
assert BOUNCE in game.get_order_status('ITALY')['A VEN']
|
||||
assert BOUNCE in game.get_order_status('AUSTRIA')['A VIE']
|
||||
|
||||
def test_set_units():
|
||||
""" Test - Sets units """
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('FRANCE', ['A PAR', 'A MAR', '*A GAS'], reset=False)
|
||||
game.set_units('ENGLAND', ['A PAR', 'A LON'])
|
||||
assert game.get_power('AUSTRIA').units == []
|
||||
assert game.get_power('ENGLAND').units == ['A PAR', 'A LON']
|
||||
assert game.get_power('FRANCE').units == ['A MAR']
|
||||
assert 'A GAS' in game.get_power('FRANCE').retreats
|
||||
assert game.get_power('GERMANY').units == []
|
||||
assert game.get_power('ITALY').units == []
|
||||
assert game.get_power('RUSSIA').units == []
|
||||
assert game.get_power('TURKEY').units == []
|
||||
|
||||
# Adding F PIC to England without resetting
|
||||
game.set_units('ENGLAND', ['F PIC'], reset=False)
|
||||
assert game.get_power('ENGLAND').units == ['A PAR', 'A LON', 'F PIC']
|
||||
|
||||
# Adding F PIC to England with resetting
|
||||
game.set_units('ENGLAND', ['F PIC'], reset=True)
|
||||
assert game.get_power('ENGLAND').units == ['F PIC']
|
||||
|
||||
# Adding F PAR (Illegal unit) to England without resetting
|
||||
game.set_units('ENGLAND', ['F PAR'], reset=False)
|
||||
assert game.get_power('ENGLAND').units == ['F PIC']
|
||||
|
||||
def test_set_centers():
|
||||
""" Tests - Sets centers """
|
||||
game = Game()
|
||||
game.clear_centers()
|
||||
game.set_centers('FRANCE', ['PAR', 'MAR', 'GAS']) # GAS is not a valid SC loc
|
||||
game.set_centers('ENGLAND', ['PAR', 'LON'])
|
||||
assert game.get_power('AUSTRIA').centers == []
|
||||
assert game.get_power('ENGLAND').centers == ['PAR', 'LON']
|
||||
assert game.get_power('FRANCE').centers == ['MAR']
|
||||
assert game.get_power('GERMANY').centers == []
|
||||
assert game.get_power('ITALY').centers == []
|
||||
assert game.get_power('RUSSIA').centers == []
|
||||
assert game.get_power('TURKEY').centers == []
|
||||
|
||||
# Adding BUD to England without resetting
|
||||
game.set_centers('ENGLAND', 'BUD', reset=False)
|
||||
assert game.get_power('ENGLAND').centers == ['PAR', 'LON', 'BUD']
|
||||
|
||||
# Adding BUD to England with resetting
|
||||
game.set_centers('ENGLAND', ['BUD'], reset=True)
|
||||
assert game.get_power('ENGLAND').centers == ['BUD']
|
||||
|
||||
# Adding UKR to England (illegal SC)
|
||||
game.set_centers('ENGLAND', 'UKR', reset=False)
|
||||
assert game.get_power('ENGLAND').centers == ['BUD']
|
||||
|
||||
def test_set_orders():
|
||||
""" Test - Sets orders """
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', 'A VEN')
|
||||
game.set_units('AUSTRIA', 'A VIE')
|
||||
game.set_units('FRANCE', 'A PAR')
|
||||
game.set_orders('ITALY', 'A VEN - TYR')
|
||||
game.set_orders('AUSTRIA', 'A VIE - TYR')
|
||||
|
||||
game.set_orders('FRANCE', ['', '', 'A PAR - GAS', '', '', ''])
|
||||
game.set_orders('RUSSIA', '')
|
||||
game.set_orders('GERMANY', [])
|
||||
assert game.get_orders('FRANCE') == ['A PAR - GAS']
|
||||
assert not game.get_orders('RUSSIA')
|
||||
assert not game.get_orders('GERMANY')
|
||||
|
||||
game.process()
|
||||
results = game.get_order_status()
|
||||
assert BOUNCE in results['ITALY']['A VEN']
|
||||
assert BOUNCE in results['AUSTRIA']['A VIE']
|
||||
|
||||
def test_set_orders_replace():
|
||||
""" Test - Sets orders with replace=True """
|
||||
check_sorted = lambda list_1, list_2: sorted(list_1) == sorted(list_2)
|
||||
|
||||
# Regular Movement Phase
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', ['A VEN', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A VIE')
|
||||
game.set_orders('ITALY', ['A VEN - TYR', 'A PAR H'])
|
||||
game.set_orders('AUSTRIA', 'A VIE - TYR')
|
||||
game.set_orders('ITALY', 'A PAR - GAS')
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['ITALY'], ['A VEN - TYR', 'A PAR - GAS'])
|
||||
assert check_sorted(orders['AUSTRIA'], ['A VIE - TYR'])
|
||||
|
||||
# NO_CHECK Movement Phase
|
||||
game = Game()
|
||||
game.add_rule('NO_CHECK')
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', ['A VEN', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A VIE')
|
||||
game.set_orders('ITALY', ['A VEN - TYR', 'A PAR H'])
|
||||
game.set_orders('AUSTRIA', 'A VIE - TYR')
|
||||
game.set_orders('ITALY', 'A PAR - GAS')
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['ITALY'], ['A VEN - TYR', 'A PAR - GAS'])
|
||||
assert check_sorted(orders['AUSTRIA'], ['A VIE - TYR'])
|
||||
|
||||
# Regular Retreat Phase
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', ['A BRE', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A GAS')
|
||||
game.set_orders('ITALY', ['A BRE - GAS', 'A PAR S A BRE - GAS'])
|
||||
game.set_orders('AUSTRIA', 'A GAS H')
|
||||
game.process()
|
||||
game.set_orders('AUSTRIA', 'A GAS R MAR')
|
||||
game.set_orders('AUSTRIA', 'A GAS R SPA')
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['AUSTRIA'], ['A GAS R SPA'])
|
||||
|
||||
# NO_CHECK Retreat Phase
|
||||
game = Game()
|
||||
game.add_rule('NO_CHECK')
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', ['A BRE', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A GAS')
|
||||
game.set_orders('ITALY', ['A BRE - GAS', 'A PAR S A BRE - GAS'])
|
||||
game.set_orders('AUSTRIA', 'A GAS H')
|
||||
game.process()
|
||||
game.set_orders('AUSTRIA', 'A GAS R MAR')
|
||||
game.set_orders('AUSTRIA', 'A GAS R SPA')
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['AUSTRIA'], ['A GAS R SPA'])
|
||||
|
||||
# Regular Adjustment Phase
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.clear_centers()
|
||||
game.set_units('FRANCE', ['A BRE', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A GAS')
|
||||
game.set_orders('FRANCE', 'A PAR - PIC')
|
||||
game.process()
|
||||
game.set_orders('FRANCE', ['A PIC - BEL', 'A BRE - PAR'])
|
||||
game.process()
|
||||
game.set_orders('FRANCE', 'A BRE B')
|
||||
game.set_orders('FRANCE', 'F BRE B')
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['FRANCE'], ['F BRE B'])
|
||||
|
||||
# NO_CHECK Adjustment Phase
|
||||
game = Game()
|
||||
game.add_rule('NO_CHECK')
|
||||
game.clear_units()
|
||||
game.clear_centers()
|
||||
game.set_units('FRANCE', ['A BRE', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A GAS')
|
||||
game.set_orders('FRANCE', 'A PAR - PIC')
|
||||
game.process()
|
||||
game.set_orders('FRANCE', ['A PIC - BEL', 'A BRE - PAR'])
|
||||
game.process()
|
||||
game.set_orders('FRANCE', 'A BRE B')
|
||||
game.set_orders('FRANCE', 'F BRE B')
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['FRANCE'], ['F BRE B'])
|
||||
|
||||
def test_set_orders_no_replace():
|
||||
""" Test - Sets orders with replace=False """
|
||||
check_sorted = lambda list_1, list_2: sorted(list_1) == sorted(list_2)
|
||||
|
||||
# Regular Movement Phase
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', ['A VEN', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A VIE')
|
||||
game.set_orders('ITALY', ['A VEN - TYR', 'A PAR H'], replace=False)
|
||||
game.set_orders('AUSTRIA', 'A VIE - TYR', replace=False)
|
||||
game.set_orders('ITALY', 'A PAR - GAS', replace=False)
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['ITALY'], ['A VEN - TYR', 'A PAR H'])
|
||||
assert check_sorted(orders['AUSTRIA'], ['A VIE - TYR'])
|
||||
|
||||
# NO_CHECK Movement Phase
|
||||
game = Game()
|
||||
game.add_rule('NO_CHECK')
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', ['A VEN', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A VIE')
|
||||
game.set_orders('ITALY', ['A VEN - TYR', 'A PAR H'], replace=False)
|
||||
game.set_orders('AUSTRIA', 'A VIE - TYR', replace=False)
|
||||
game.set_orders('ITALY', 'A PAR - GAS', replace=False)
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['ITALY'], ['A VEN - TYR', 'A PAR H', 'A PAR - GAS'])
|
||||
assert check_sorted(orders['AUSTRIA'], ['A VIE - TYR'])
|
||||
|
||||
# Regular Retreat Phase
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', ['A BRE', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A GAS')
|
||||
game.set_orders('ITALY', ['A BRE - GAS', 'A PAR S A BRE - GAS'], replace=False)
|
||||
game.set_orders('AUSTRIA', 'A GAS H', replace=False)
|
||||
game.process()
|
||||
game.set_orders('AUSTRIA', 'A GAS R MAR', replace=False)
|
||||
game.set_orders('AUSTRIA', 'A GAS R SPA', replace=False)
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['AUSTRIA'], ['A GAS R MAR'])
|
||||
|
||||
# NO_CHECK Retreat Phase
|
||||
game = Game()
|
||||
game.add_rule('NO_CHECK')
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', ['A BRE', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A GAS')
|
||||
game.set_orders('ITALY', ['A BRE - GAS', 'A PAR S A BRE - GAS'], replace=False)
|
||||
game.set_orders('AUSTRIA', 'A GAS H', replace=False)
|
||||
game.process()
|
||||
game.set_orders('AUSTRIA', 'A GAS R MAR', replace=False)
|
||||
game.set_orders('AUSTRIA', 'A GAS R SPA', replace=False)
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['AUSTRIA'], ['A GAS R MAR'])
|
||||
|
||||
# Regular Adjustment Phase
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.clear_centers()
|
||||
game.set_units('FRANCE', ['A BRE', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A GAS')
|
||||
game.set_orders('FRANCE', 'A PAR - PIC', replace=False)
|
||||
game.process()
|
||||
game.set_orders('FRANCE', ['A PIC - BEL', 'A BRE - PAR'], replace=False)
|
||||
game.process()
|
||||
game.set_orders('FRANCE', 'A BRE B', replace=False)
|
||||
game.set_orders('FRANCE', 'F BRE B', replace=False)
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['FRANCE'], ['A BRE B'])
|
||||
|
||||
# NO_CHECK Adjustment Phase
|
||||
game = Game()
|
||||
game.add_rule('NO_CHECK')
|
||||
game.clear_units()
|
||||
game.clear_centers()
|
||||
game.set_units('FRANCE', ['A BRE', 'A PAR'])
|
||||
game.set_units('AUSTRIA', 'A GAS')
|
||||
game.set_orders('FRANCE', 'A PAR - PIC', replace=False)
|
||||
game.process()
|
||||
game.set_orders('FRANCE', ['A PIC - BEL', 'A BRE - PAR'], replace=False)
|
||||
game.process()
|
||||
game.set_orders('FRANCE', 'A BRE B', replace=False)
|
||||
game.set_orders('FRANCE', 'F BRE B', replace=False)
|
||||
orders = game.get_orders()
|
||||
assert check_sorted(orders['FRANCE'], ['A BRE B'])
|
||||
|
||||
def test_clear_units():
|
||||
""" Tests - Clear units """
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
for power in game.powers.values():
|
||||
assert not power.units
|
||||
assert not game.error
|
||||
|
||||
def test_clear_centers():
|
||||
""" Tests - Clear centers """
|
||||
game = Game()
|
||||
game.clear_centers()
|
||||
for power in game.powers.values():
|
||||
assert not power.centers
|
||||
assert not game.error
|
||||
|
||||
def test_clear_orders():
|
||||
""" Test - Clear orders"""
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', 'A VEN')
|
||||
game.set_units('AUSTRIA', 'A VIE')
|
||||
game.set_orders('ITALY', 'A VEN - TYR')
|
||||
game.set_orders('AUSTRIA', 'A VIE - TYR')
|
||||
game.clear_orders()
|
||||
game.process()
|
||||
results = game.get_order_status()
|
||||
assert results['ITALY']['A VEN'] == []
|
||||
assert results['AUSTRIA']['A VIE'] == []
|
||||
|
||||
def test_get_current_phase():
|
||||
""" Tests - get current phase """
|
||||
game = Game()
|
||||
assert game.get_current_phase() == 'S1901M'
|
||||
|
||||
def test_set_current_phase():
|
||||
""" Tests - set current phase"""
|
||||
game = Game()
|
||||
power = game.get_power('FRANCE')
|
||||
power.units.remove('A PAR')
|
||||
game.set_current_phase('W1901A')
|
||||
game.clear_cache()
|
||||
assert game.get_current_phase() == 'W1901A'
|
||||
assert game.phase_type == 'A'
|
||||
assert 'A PAR B' in game.get_all_possible_orders()['PAR']
|
||||
|
||||
def test_process_game():
|
||||
""" Tests - Process game """
|
||||
game = Game()
|
||||
game.clear_units()
|
||||
game.set_units('ITALY', 'A VEN')
|
||||
game.set_units('AUSTRIA', 'A VIE')
|
||||
game.set_orders('ITALY', 'A VEN - TYR')
|
||||
game.set_orders('AUSTRIA', 'A VIE - TYR')
|
||||
game.process()
|
||||
results = game.get_order_status()
|
||||
assert BOUNCE in results['ITALY']['A VEN']
|
||||
assert BOUNCE in results['AUSTRIA']['A VIE']
|
||||
|
||||
def test_deepcopy():
|
||||
""" Tests - deepcopy """
|
||||
game = Game()
|
||||
game2 = deepcopy(game)
|
||||
assert game != game2
|
||||
assert game.get_hash() == game2.get_hash()
|
||||
|
||||
def test_automatic_draw():
|
||||
""" Tests - draw """
|
||||
game = Game()
|
||||
assert game.map.first_year == 1901
|
||||
|
||||
# fast forward 99 years with no winter
|
||||
for year in range(1, 100):
|
||||
game.process()
|
||||
game.process()
|
||||
assert int(game.get_current_phase()[1:5]) == game.map.first_year + year
|
||||
assert game.is_game_done is False
|
||||
|
||||
# forward 2000 year. after this year should draw
|
||||
game.process()
|
||||
game.process()
|
||||
assert game.is_game_done is True
|
||||
assert list(sorted(game.outcome)) == list(
|
||||
sorted(['W2000A', 'AUSTRIA', 'ENGLAND', 'FRANCE', 'GERMANY', 'ITALY', 'RUSSIA', 'TURKEY']))
|
||||
|
||||
def test_histories():
|
||||
""" Test order_history, state_history, message_history and messages. """
|
||||
from diplomacy.server.server_game import ServerGame
|
||||
from diplomacy.utils.sorted_dict import SortedDict
|
||||
from diplomacy.utils import strings
|
||||
game = ServerGame(status=strings.ACTIVE)
|
||||
assert game.solitaire
|
||||
assert not game.n_controls
|
||||
assert game.is_game_active
|
||||
|
||||
assert isinstance(game.messages, SortedDict)
|
||||
assert isinstance(game.message_history, SortedDict)
|
||||
assert isinstance(game.order_history, SortedDict)
|
||||
assert isinstance(game.state_history, SortedDict)
|
||||
assert not game.messages
|
||||
assert not game.message_history
|
||||
assert not game.order_history
|
||||
assert not game.state_history
|
||||
game.new_system_message('FRANCE', 'Hello France!')
|
||||
game.new_system_message('GLOBAL', 'Hello World!')
|
||||
game.set_orders('FRANCE', ['A PAR H'])
|
||||
assert len(game.messages) == 2
|
||||
previous_phase = game.get_current_phase()
|
||||
game.process()
|
||||
current_phase = game.get_current_phase()
|
||||
assert previous_phase != current_phase, (previous_phase, current_phase)
|
||||
assert not game.messages
|
||||
assert len(game.message_history) == 1
|
||||
assert len(game.order_history) == 1
|
||||
assert len(game.state_history) == 1
|
||||
game.set_orders('AUSTRIA', ['A BUD - GAL'])
|
||||
game.set_orders('FRANCE', ['A PAR H'])
|
||||
game.new_system_message('GLOBAL', 'New world.')
|
||||
assert len(game.messages) == 1
|
||||
game.process()
|
||||
assert not game.messages
|
||||
assert len(game.message_history) == 2
|
||||
assert len(game.order_history) == 2
|
||||
assert len(game.state_history) == 2
|
||||
assert all((p1 == p2 == p3) for (p1, p2, p3) in zip(game.message_history.keys(),
|
||||
game.order_history.keys(),
|
||||
game.state_history.keys()))
|
||||
|
||||
assert game.map.compare_phases(str(game.state_history.first_key()), str(game.state_history.last_key())) < 0
|
||||
|
||||
messages_phase_1 = list(game.message_history.first_value().values())
|
||||
messages_phase_2 = list(game.message_history.last_value().values())
|
||||
orders_phase_1 = game.order_history.first_value()
|
||||
orders_phase_2 = game.order_history.last_value()
|
||||
assert len(messages_phase_1) == 2
|
||||
assert len(messages_phase_2) == 1
|
||||
assert messages_phase_1[0].message == 'Hello France!'
|
||||
assert messages_phase_1[1].message == 'Hello World!'
|
||||
assert messages_phase_2[0].message == 'New world.'
|
||||
assert orders_phase_1['FRANCE'] == ['A PAR H']
|
||||
assert orders_phase_2['FRANCE'] == ['A PAR H']
|
||||
assert orders_phase_2['AUSTRIA'] == ['A BUD - GAL']
|
||||
|
||||
assert all('messages' not in state for state in game.state_history.values())
|
||||
|
||||
game_to_json = game.to_dict()
|
||||
game_copy = ServerGame.from_dict(game_to_json)
|
||||
assert list(game.state_history.keys()) == list(game_copy.state_history.keys())
|
||||
assert list(game.message_history.keys()) == list(game_copy.message_history.keys())
|
||||
assert list(game.order_history.keys()) == list(game_copy.order_history.keys())
|
||||
# Check histories in game copy.
|
||||
messages_phase_1 = list(game_copy.message_history.first_value().values())
|
||||
messages_phase_2 = list(game_copy.message_history.last_value().values())
|
||||
orders_phase_1 = game_copy.order_history.first_value()
|
||||
orders_phase_2 = game_copy.order_history.last_value()
|
||||
assert len(messages_phase_1) == 2
|
||||
assert len(messages_phase_2) == 1
|
||||
assert messages_phase_1[0].message == 'Hello France!'
|
||||
assert messages_phase_1[1].message == 'Hello World!'
|
||||
assert messages_phase_2[0].message == 'New world.'
|
||||
assert orders_phase_1['FRANCE'] == ['A PAR H']
|
||||
assert orders_phase_2['FRANCE'] == ['A PAR H']
|
||||
assert orders_phase_2['AUSTRIA'] == ['A BUD - GAL']
|
||||
|
||||
def test_result_history():
|
||||
""" Test result history. """
|
||||
short_phase_name = 'S1901M'
|
||||
game = Game()
|
||||
game.set_orders('FRANCE', ['A PAR - BUR', 'A MAR - BUR'])
|
||||
assert game.current_short_phase == short_phase_name
|
||||
game.process()
|
||||
assert game.current_short_phase == 'F1901M'
|
||||
phase_data = game.get_phase_from_history(short_phase_name)
|
||||
assert BOUNCE in phase_data.results['A PAR']
|
||||
assert BOUNCE in phase_data.results['A MAR']
|
||||
|
||||
def test_unit_owner():
|
||||
""" Test Unit Owner Resolver making sure the cached results are correct """
|
||||
game = Game()
|
||||
print(game.get_units('RUSSIA'))
|
||||
|
||||
assert game._unit_owner('F STP/SC', coast_required=1) is game.get_power('RUSSIA') # pylint: disable=protected-access
|
||||
assert game._unit_owner('F STP/SC', coast_required=0) is game.get_power('RUSSIA') # pylint: disable=protected-access
|
||||
|
||||
assert game._unit_owner('F STP', coast_required=1) is None # pylint: disable=protected-access
|
||||
assert game._unit_owner('F STP', coast_required=0) is game.get_power('RUSSIA') # pylint: disable=protected-access
|
||||
|
||||
assert game._unit_owner('A WAR', coast_required=0) is game.get_power('RUSSIA') # pylint: disable=protected-access
|
||||
assert game._unit_owner('A WAR', coast_required=1) is game.get_power('RUSSIA') # pylint: disable=protected-access
|
||||
|
||||
assert game._unit_owner('F SEV', coast_required=0) is game.get_power('RUSSIA') # pylint: disable=protected-access
|
||||
assert game._unit_owner('F SEV', coast_required=1) is game.get_power('RUSSIA') # pylint: disable=protected-access
|
||||
238
diplomacy/tests/test_map.py
Normal file
238
diplomacy/tests/test_map.py
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
""" Tests cases for Map
|
||||
- Contains the test cases for the map object
|
||||
"""
|
||||
from copy import deepcopy
|
||||
from diplomacy.engine.map import Map
|
||||
|
||||
def test_init():
|
||||
""" Creates a map"""
|
||||
Map()
|
||||
|
||||
def test_str():
|
||||
""" Tests map.__str__ """
|
||||
this_map = deepcopy(Map())
|
||||
assert str(this_map) == this_map.name
|
||||
|
||||
def test_add_homes():
|
||||
""" Tests map.add_homes """
|
||||
this_map = deepcopy(Map())
|
||||
this_map.add_homes('FRANCE', 'BRE MAR PAR'.split(), reinit=1)
|
||||
assert this_map.homes['FRANCE'] == ['BRE', 'MAR', 'PAR']
|
||||
this_map.add_homes('FRANCE', [], reinit=1)
|
||||
assert this_map.homes['FRANCE'] == []
|
||||
|
||||
def test_drop():
|
||||
""" Tests map.drop """
|
||||
this_map = deepcopy(Map())
|
||||
this_map.drop('STP')
|
||||
assert not [loc for loc in list(this_map.locs) if loc.upper().startswith('STP')]
|
||||
assert not [loc_name for (loc_name, loc) in list(this_map.loc_name.items()) if loc.startswith('STP')]
|
||||
assert not [alias for (alias, value) in list(this_map.aliases.items()) if value.startswith('STP')]
|
||||
assert not [homes for homes in list(this_map.homes.values()) if 'STP' in homes]
|
||||
assert not [units for units in list(this_map.units.values()) for unit in units if unit[2:5] == 'STP'[:3]]
|
||||
assert not [center for center in list(this_map.scs) if center.upper().startswith('STP')]
|
||||
assert not [p_name for (p_name, scs) in this_map.centers.items() for center in scs if center.startswith('STP')]
|
||||
assert not [loc for loc, abuts in list(this_map.loc_abut.items()) for there in abuts
|
||||
if loc.startswith('STP') or there.startswith('STP')]
|
||||
assert not [loc for loc in list(this_map.loc_type.keys()) if loc.startswith('STP')]
|
||||
|
||||
def test_compact():
|
||||
""" Tests map.compact """
|
||||
this_map = deepcopy(Map())
|
||||
# Power name at top of string is removed by Map.compact().
|
||||
assert this_map.compact('England: Fleet Western Mediterranean -> Tyrrhenian Sea. (*bounce*)') \
|
||||
== ['F', 'WES', 'TYS', '|']
|
||||
|
||||
def test_norm_power():
|
||||
""" Tests map.norm_power """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.norm_power('abc def. ghi/jkl!-ABC|~ (Hello)') == 'ABCDEFGHI/JKL!ABC|~(HELLO)'
|
||||
|
||||
def test_norm():
|
||||
""" Tests map.norm """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.norm('abc def. ghi/jkl!-ABC|~ (Hello)') == 'ABC DEF GHI /JKL ! ABC | ~ ( HELLO )'
|
||||
|
||||
def test_vet():
|
||||
""" Tests map.vet """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.vet(['A B']) == [('A B', 0)]
|
||||
assert this_map.vet(['SPAIN/NC']) == [('SPAIN/NC', 1)]
|
||||
assert this_map.vet(['SPANISH']) == [('SPANISH', 1)]
|
||||
assert this_map.vet(['A']) == [('A', 2)]
|
||||
assert this_map.vet(['F']) == [('F', 2)]
|
||||
assert this_map.vet(['POR']) == [('POR', 3)]
|
||||
assert this_map.vet(['SPA']) == [('SPA', 3)]
|
||||
assert this_map.vet(['SPA/NC']) == [('SPA/NC', 4)]
|
||||
assert this_map.vet(['S']) == [('S', 5)]
|
||||
assert this_map.vet(['C']) == [('C', 5)]
|
||||
assert this_map.vet(['H']) == [('H', 5)]
|
||||
assert this_map.vet(['-']) == [('-', 6)]
|
||||
assert this_map.vet(['=']) == [('=', 6)]
|
||||
assert this_map.vet(['_']) == [('_', 6)]
|
||||
assert this_map.vet(['|']) == [('|', 7)]
|
||||
assert this_map.vet(['?']) == [('?', 7)]
|
||||
assert this_map.vet(['~']) == [('~', 7)]
|
||||
assert this_map.vet(['ZZZ'], strict=0) == [('ZZZ', 3)]
|
||||
assert this_map.vet(['ZZZ'], strict=1) == [('ZZZ', -3)]
|
||||
|
||||
def test_area_type():
|
||||
""" Tests map.area_type """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.area_type('ADR') == 'WATER'
|
||||
assert this_map.area_type('ALB') == 'COAST'
|
||||
assert this_map.area_type('BUL/EC') == 'COAST'
|
||||
assert this_map.area_type('BUR') == 'LAND'
|
||||
assert this_map.area_type('SWI') == 'SHUT'
|
||||
|
||||
def test_default_coast():
|
||||
""" Tests map.default_coast """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.default_coast(['F', 'GRE', '-', 'BUL']) == ['F', 'GRE', '-', 'BUL/SC']
|
||||
assert this_map.default_coast(['F', 'MAO', '-', 'SPA']) == ['F', 'MAO', '-', 'SPA']
|
||||
assert this_map.default_coast(['F', 'FIN', '-', 'STP']) == ['F', 'FIN', '-', 'STP/SC']
|
||||
assert this_map.default_coast(['F', 'NAO', '-', 'MAO']) == ['F', 'NAO', '-', 'MAO']
|
||||
|
||||
def test_abuts():
|
||||
""" Tests map.abuts """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.abuts('A', 'POR', 'S', 'SPA/NC') == 1
|
||||
assert this_map.abuts('A', 'POR', 'C', 'SPA/NC') == 0
|
||||
assert this_map.abuts('A', 'MUN', 'S', 'SWI') == 0
|
||||
assert this_map.abuts('?', 'YOR', 'S', 'LVP') == 1
|
||||
assert this_map.abuts('F', 'YOR', 'S', 'LVP') == 0
|
||||
assert this_map.abuts('A', 'YOR', 'S', 'LVP') == 1
|
||||
assert this_map.abuts('F', 'BOT', 'S', 'STP') == 1
|
||||
assert this_map.abuts('F', 'BOT', 'S', 'MOS') == 0
|
||||
assert this_map.abuts('F', 'VEN', 'S', 'TUS') == 0
|
||||
assert this_map.abuts('A', 'POR', 'C', 'MAO') == 1
|
||||
|
||||
def test_is_valid_unit():
|
||||
""" Tests maps.is_valid_unit """
|
||||
# ADR = WATER
|
||||
# ALB = COAST
|
||||
# BUL/EC = COAST
|
||||
# BUR = LAND
|
||||
# SWI = SHUT
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.is_valid_unit('A ADR', no_coast_ok=0, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('A ALB', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('A BUL', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('A BUL/EC', no_coast_ok=0, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('A BUR', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('A SWI', no_coast_ok=0, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('F ADR', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('F ALB', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('F BUL', no_coast_ok=0, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('F BUL/EC', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('F BUR', no_coast_ok=0, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('F SWI', no_coast_ok=0, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('F ADR', no_coast_ok=1, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('F ALB', no_coast_ok=1, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('F BUL', no_coast_ok=1, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('F BUL/EC', no_coast_ok=1, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('F BUR', no_coast_ok=1, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('F SWI', no_coast_ok=1, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('? ADR', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('? ALB', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('? BUL', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('? BUL/EC', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('? BUR', no_coast_ok=0, shut_ok=0) == 1
|
||||
assert this_map.is_valid_unit('? SWI', no_coast_ok=0, shut_ok=0) == 0
|
||||
assert this_map.is_valid_unit('A SWI', no_coast_ok=0, shut_ok=1) == 1
|
||||
assert this_map.is_valid_unit('F SWI', no_coast_ok=0, shut_ok=1) == 1
|
||||
assert this_map.is_valid_unit('? SWI', no_coast_ok=0, shut_ok=1) == 1
|
||||
|
||||
def test_abut_list():
|
||||
""" Tests map.abut_list """
|
||||
this_map = deepcopy(Map())
|
||||
this_map.loc_abut['---'] = ['ABC', 'DEF', 'GHI']
|
||||
this_map.loc_abut['aaa'] = ['LOW', 'HIG', 'MAY']
|
||||
assert this_map.abut_list('---') == ['ABC', 'DEF', 'GHI']
|
||||
assert this_map.abut_list('AAA') == ['LOW', 'HIG', 'MAY']
|
||||
assert this_map.abut_list('LVP') == ['CLY', 'edi', 'IRI', 'NAO', 'WAL', 'yor']
|
||||
|
||||
def test_compare_phases():
|
||||
""" Tests map.compare_phases """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.compare_phases('FORMING', 'S1901M') == -1
|
||||
assert this_map.compare_phases('COMPLETED', 'S1901M') == 1
|
||||
assert this_map.compare_phases('S1901M', 'FORMING') == 1
|
||||
assert this_map.compare_phases('S1901M', 'COMPLETED') == -1
|
||||
assert this_map.compare_phases('FORMING', 'COMPLETED') == -1
|
||||
assert this_map.compare_phases('COMPLETED', 'FORMING') == 1
|
||||
assert this_map.compare_phases('S1901M', 'S1902M') == -1
|
||||
assert this_map.compare_phases('S1902M', 'S1901M') == 1
|
||||
assert this_map.compare_phases('S1901M', 'F1901M') == -1
|
||||
assert this_map.compare_phases('F1901M', 'S1901M') == 1
|
||||
assert this_map.compare_phases('S1901?', 'S1901R') == 0
|
||||
assert this_map.compare_phases('F1901?', 'F1901R') == 0
|
||||
assert this_map.compare_phases('W1901?', 'W1901A') == 0
|
||||
|
||||
def test_find_next_phase():
|
||||
""" Tests map.find_next_phase """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.find_next_phase('FORMING') == 'FORMING'
|
||||
assert this_map.find_next_phase('COMPLETED') == 'COMPLETED'
|
||||
assert this_map.find_next_phase('WINTER 1901 ADJUSTMENTS') == 'SPRING 1902 MOVEMENT'
|
||||
assert this_map.find_next_phase('FALL 1901 RETREATS', phase_type='M') == 'SPRING 1902 MOVEMENT'
|
||||
assert this_map.find_next_phase('SPRING 1902 RETREATS', phase_type='M', skip=1) == 'SPRING 1903 MOVEMENT'
|
||||
|
||||
def test_find_previous_phase():
|
||||
""" Tests map.find_previous_phase """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.find_previous_phase('FORMING') == 'FORMING'
|
||||
assert this_map.find_previous_phase('COMPLETED') == 'COMPLETED'
|
||||
assert this_map.find_previous_phase('SPRING 1902 MOVEMENT') == 'WINTER 1901 ADJUSTMENTS'
|
||||
assert this_map.find_previous_phase('SPRING 1902 MOVEMENT', phase_type='R') == 'FALL 1901 RETREATS'
|
||||
assert this_map.find_previous_phase('SPRING 1903 MOVEMENT', phase_type='R', skip=1) == 'SPRING 1902 RETREATS'
|
||||
|
||||
def test_phase_abbr():
|
||||
""" Tests map.phase_abbr """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.phase_abbr('SPRING 1901 MOVEMENT') == 'S1901M'
|
||||
assert this_map.phase_abbr('SPRING 1901 RETREATS') == 'S1901R'
|
||||
assert this_map.phase_abbr('FALL 1901 MOVEMENT') == 'F1901M'
|
||||
assert this_map.phase_abbr('FALL 1901 RETREATS') == 'F1901R'
|
||||
assert this_map.phase_abbr('WINTER 1901 ADJUSTMENTS') == 'W1901A'
|
||||
assert this_map.phase_abbr('spring 1901 movement') == 'S1901M'
|
||||
assert this_map.phase_abbr('spring 1901 retreats') == 'S1901R'
|
||||
assert this_map.phase_abbr('fall 1901 movement') == 'F1901M'
|
||||
assert this_map.phase_abbr('fall 1901 retreats') == 'F1901R'
|
||||
assert this_map.phase_abbr('winter 1901 adjustments') == 'W1901A'
|
||||
assert this_map.phase_abbr('COMPLETED') == 'COMPLETED'
|
||||
assert this_map.phase_abbr('FORMING') == 'FORMING'
|
||||
assert this_map.phase_abbr('Bad') == '?????'
|
||||
assert this_map.phase_abbr('Bad', default='Test') == 'Test'
|
||||
|
||||
def test_phase_long():
|
||||
""" Test map.phase_long """
|
||||
this_map = deepcopy(Map())
|
||||
assert this_map.phase_long('S1901M') == 'SPRING 1901 MOVEMENT'
|
||||
assert this_map.phase_long('S1901R') == 'SPRING 1901 RETREATS'
|
||||
assert this_map.phase_long('F1901M') == 'FALL 1901 MOVEMENT'
|
||||
assert this_map.phase_long('F1901R') == 'FALL 1901 RETREATS'
|
||||
assert this_map.phase_long('W1901A') == 'WINTER 1901 ADJUSTMENTS'
|
||||
assert this_map.phase_long('s1901m') == 'SPRING 1901 MOVEMENT'
|
||||
assert this_map.phase_long('s1901r') == 'SPRING 1901 RETREATS'
|
||||
assert this_map.phase_long('f1901m') == 'FALL 1901 MOVEMENT'
|
||||
assert this_map.phase_long('f1901r') == 'FALL 1901 RETREATS'
|
||||
assert this_map.phase_long('w1901a') == 'WINTER 1901 ADJUSTMENTS'
|
||||
assert this_map.phase_long('bad') == '?????'
|
||||
assert this_map.phase_long('bad', default='Test') == 'Test'
|
||||
23
diplomacy/tests/test_pytest.py
Normal file
23
diplomacy/tests/test_pytest.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# ==============================================================================
|
||||
# Copyright (C) 2019 - Philip Paquette
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ==============================================================================
|
||||
""" Pytest checker """
|
||||
|
||||
# Check that tests are running
|
||||
# Avoids pytests not having tests to run
|
||||
def test_pytest():
|
||||
""" Should always pass """
|
||||
assert True
|
||||
Loading…
Add table
Add a link
Reference in a new issue