Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
Merge ad773a3 into d4511aa
Browse files Browse the repository at this point in the history
  • Loading branch information
danalex97 authored Sep 3, 2017
2 parents d4511aa + ad773a3 commit 2cfe3a1
Show file tree
Hide file tree
Showing 21 changed files with 862 additions and 49 deletions.
5 changes: 2 additions & 3 deletions aimmo-game-creator/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@

def main():
logging.basicConfig(level=logging.DEBUG)
WorkerManagerClass = WORKER_MANAGERS[os.environ.get('WORKER_MANAGER', 'local')]
worker_manager = WorkerManagerClass(os.environ.get('GAME_API_URL',
'http://localhost:8000/players/api/games/'))
WorkerManagerClass = WORKER_MANAGERS[os.environ['WORKER_MANAGER']]
worker_manager = WorkerManagerClass(os.environ['GAME_API_URL'])
worker_manager.run()

if __name__ == '__main__':
Expand Down
8 changes: 3 additions & 5 deletions aimmo-game-creator/tests/test_worker_manager.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
from __future__ import absolute_import

import cPickle as pickle
import unittest
from json import dumps
from json import dumps, loads

from httmock import HTTMock

from worker_manager import WorkerManager


class ConcreteWorkerManager(WorkerManager):
def __init__(self, *args, **kwargs):
self.final_workers = set()
Expand Down Expand Up @@ -40,7 +38,7 @@ def _generate_response(self, num_games):
return {
str(i): {
'name': 'Game %s' % i,
'settings': pickle.dumps({
'settings': dumps({
'test': i,
'test2': 'Settings %s' % i,
})
Expand Down Expand Up @@ -72,7 +70,7 @@ def test_workers_added(self):
for i in xrange(3):
self.assertIn(str(i), self.worker_manager.final_workers)
self.assertEqual(
pickle.loads(str(self.worker_manager.added_workers[str(i)]['settings'])),
loads(str(self.worker_manager.added_workers[str(i)]['settings'])),
{'test': i, 'test2': 'Settings %s' % i}
)
self.assertEqual(self.worker_manager.added_workers[str(i)]['name'], 'Game %s' % i)
Expand Down
2 changes: 1 addition & 1 deletion aimmo-game-creator/worker_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def update(self):
class LocalWorkerManager(WorkerManager):
"""Relies on them already being created already."""

host = '127.0.0.1'
host = '0.0.0.0'
worker_directory = os.path.join(
os.path.dirname(__file__),
'../aimmo-game/',
Expand Down
42 changes: 35 additions & 7 deletions aimmo-game/service.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/usr/bin/env python
import cPickle as pickle
import logging
import os
import sys

import eventlet
from json import loads

eventlet.sleep()
eventlet.monkey_patch()
Expand All @@ -27,20 +27,21 @@
# Every user has its own world state.
world_state_manager = {}

# socketio routes
@socketio.on('connect')
def world_init():
socketio.emit('world-init')

@socketio.on('disconnect')
def exit_game():
del world_state_manager[flask.session['id']]

@socketio.on('client-ready')
def client_ready(client_id):
flask.session['id'] = client_id
world_state = WorldState(state_provider)
world_state_manager[client_id] = world_state

@socketio.on('exit-game')
def exit_game(user_id):
del world_state_manager[user_id]

def send_world_update():
for world_state in world_state_manager.values():
socketio.emit(
Expand All @@ -49,6 +50,10 @@ def send_world_update():
broadcast=True,
)

@socketio.on('disconnect')
def on_disconnect():
del world_state_manager[flask.session['id']]

@app.route('/')
def healthcheck():
return 'HEALTHY'
Expand All @@ -62,13 +67,36 @@ def player_data(player_id):
'state': None,
})

# Plain client routes... These are easy to work with and
# they are not exposed in the kubernetes application
# as the proxy does not allow communication with them.
@app.route('/plain/<user_id>/connect')
def plain_world_init(user_id):
world_init()
return 'CONNECT'

@app.route('/plain/<user_id>/client-ready')
def plain_client_ready(user_id):
world_state = WorldState(state_provider)
world_state_manager[int(user_id)] = world_state
return 'RECEIVED USER READY ' + user_id

@app.route('/plain/<user_id>/exit-game')
def plain_exit_game(user_id):
return "EXITING GAME FOR USER " + user_id

@app.route('/plain/<user_id>/update')
def plain_update(user_id):
world_state = world_state_manager[int(user_id)]
return flask.jsonify(world_state.get_updates())

def run_game(port):
global worker_manager

print("Running game...")
settings = pickle.loads(os.environ['settings'])
settings = loads(os.environ['settings'])

api_url = os.environ.get('GAME_API_URL', 'http://localhost:8000/players/api/games/')
api_url = os.environ['GAME_API_URL']
generator = getattr(map_generator, settings['GENERATOR'])(settings)
player_manager = AvatarManager()
game_state = generator.get_game_state(player_manager)
Expand Down
69 changes: 63 additions & 6 deletions aimmo-game/tests/test_service.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,83 @@
from __future__ import absolute_import

from unittest import TestCase
from unittest import TestCase, skip

from simulation.game_state import GameState
from simulation.location import Location
from simulation.turn_manager import state_provider
from simulation.world_map import WorldMap
from simulation.world_state import WorldState
from simulation.avatar.avatar_manager import AvatarManager

import service

from .test_simulation.dummy_avatar import MoveEastDummy
from .test_simulation.maps import MockPickup
from .test_simulation.test_world_map import MockCell

class SimpleAvatarManager(object):
avatars = [MoveEastDummy(1, Location(0, -1))]
class SimpleAvatarManager(AvatarManager):
def __init__(self):
super(SimpleAvatarManager, self).__init__()

# TODO: Write test for the new API...
class TestService(TestCase):
def test_healthy(self):
avatar = MoveEastDummy(1, Location(0, -1))
self.avatars_to_create_by_id = {
1 : avatar
}

class TestServiceAPI(TestCase):
def setUp(self):
service.app.config['TESTING'] = True
self.app = service.app.test_client()

def test_healthy(self):
response = self.app.get('/')
self.assertEqual(response.data, 'HEALTHY')

class TestServiceInternals(TestCase):
def setUp(self):
self.user_id = 1

def setup_world(self):
avatar_manager = SimpleAvatarManager()
CELLS = [
[
{'pickup': MockPickup('b'), 'avatar': avatar_manager.avatars_to_create_by_id},
{},
{'generates_score': True},
],
[
{},
{'habitable': False},
{'pickup': MockPickup('a')},
],
]
grid = {Location(x, y-1): MockCell(Location(x, y-1), **CELLS[x][y])
for y in xrange(3) for x in xrange(2)}
state_provider.set_world(GameState(WorldMap(grid, {}), avatar_manager))

world_state = WorldState(state_provider)
world_state.ready_to_update = True

return world_state.get_updates()

def test_player_dict(self):
player_list = self.setup_world()['players']['create']
self.assertEqual(len(player_list), 1)
details = player_list[0]
self.assertEqual(details['id'], 1)
self.assertEqual(details['x'], 0)
self.assertEqual(details['y'], -1)
self.assertEqual(details['health'], 5)
self.assertEqual(details['score'], 0)

def test_score_locations(self):
result = self.setup_world()['map_features']['score_point']['create']
self.assertEqual(result[0]['x'], 0)
self.assertEqual(result[0]['y'], 1)

@skip("not implemented")
def test_pickup_list(self):
result = self.setup_world()['map_features']['pickup']['create']
pickup_pos_list = [(pickup['x'], pickup['y']) for pickup in result]
self.assertIn((1, 1), pickup_pos_list)
self.assertIn((0, -1), pickup_pos_list)
1 change: 1 addition & 0 deletions aimmo-game/tests/test_simulation/maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(self, location=1, habitable=True, generates_score=False,
self.name = name
self.actions = actions
self.partially_fogged = False
self.created = False

def __eq__(self, other):
return self is other
Expand Down
3 changes: 1 addition & 2 deletions all_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
import sys

BASE_DIR = os.path.abspath(os.path.dirname(__file__))
APPS = ('', 'aimmo-game/', 'aimmo-game-worker/', 'aimmo-game-creator/')

APPS = ('', 'aimmo-game/', 'aimmo-game-worker/', 'aimmo-game-creator/', 'integration-tests/')

def print_help():
print(globals()['__docstring__'])
Expand Down
18 changes: 16 additions & 2 deletions example_project/example_project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
# identified as the original program.
"""Django settings for example_project project."""
import subprocess
import socket

import os

Expand Down Expand Up @@ -106,11 +107,24 @@
'django.contrib.messages.middleware.MessageMiddleware',
]

def get_ip():
# http://stackoverflow.com/a/28950776/671626
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 0))
IP = s.getsockname()[0]
except:
IP = '127.0.0.1'
finally:
s.close()
return IP

def get_url(game):
if os.environ.get('AIMMO_MODE', '') == 'minikube':
return (os.environ['MINIKUBE_PROXY_URL'], "/game/%s/socket.io" % game)
return (os.environ['MINIKUBE_PROXY_URL'], "/game/%s/socket.io" % game)
else:
return ('http://localhost:%d' % (6001 + int(game) * 1000), '/socket.io')
return ('http://%s:%d' % (get_ip(), (6001 + int(game) * 1000)), '/socket.io')

AIMMO_GAME_SERVER_LOCATION_FUNCTION = get_url

Expand Down
19 changes: 19 additions & 0 deletions integration-tests/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from setuptools import find_packages, setup
import unittest
import os

def custom_test_suite():
return unittest.TestLoader().discover('tests', pattern='test_*.py')

setup(
name='integration-tests',
include_package_data=True,
install_requires=[
],
tests_require=[
'httmock',
'psutil'
],
test_suite="setup.custom_test_suite",
zip_safe=False,
)
Empty file.
62 changes: 62 additions & 0 deletions integration-tests/tests/misc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import signal
import requests

import os
import subprocess
import sys

import traceback
import logging
import psutil
import time
import sys
import socket

FNULL = open(os.devnull, 'w')

logging.basicConfig()
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.DEBUG)

def get_ip():
# http://stackoverflow.com/a/28950776/671626
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 0))
IP = s.getsockname()[0]
except:
IP = '127.0.0.1'
finally:
s.close()
return IP

def run_command_async(args, cwd=".", verbose=False):
if verbose:
p = subprocess.Popen(args, cwd=cwd)
else:
p = subprocess.Popen(args, cwd=cwd, stdout=FNULL, stderr=subprocess.STDOUT)
return p

def process_tree(pid):
parent = psutil.Process(pid)
to_kill = [pid]
for child in parent.children(recursive=True):
to_kill = to_kill + process_tree(child.pid)
return to_kill

def kill_process_tree(pid):
try:
LOGGER.info("Current process %s" % str(os.getpid()))
LOGGER.info("Process tree...")
to_kill = process_tree(pid)
LOGGER.info("Killing processes... %s" % str(to_kill))
for pid in to_kill:
os.kill(pid, signal.SIGKILL)
LOGGER.info("Waiting for processes to terminate...")
for pid in to_kill:
os.system("wait " + str(pid))
LOGGER.info("Process tree killed successfully...")
except Exception as e:
LOGGER.warn("An exception occured while killing the process tree...")
logging.error(traceback.format_exc())
Loading

0 comments on commit 2cfe3a1

Please sign in to comment.