PyOGP Package Unittests

== Overview ==
Each package in the lib should have unit tests, which cover as much code as possible. The unit tests evaluate the integrity of the library code itself, in terms of apis, code paths, and dependencies. 
Packages under unit test right now:
 pyogp.lib.base
 pyogp.lib.client
Unit tests live in directories named test/ within any directory in any package.
== Python test types ==
=== unittest ===
Unit tests are written as standard python unittest implementations. For more on this, see http://docs.python.org/library/unittest.html. 
=== doctest ===
doctests are narrative sample uses of code, that may be run via various test wrappers. For more, see http://docs.python.org/library/doctest.html.
== Running tests ==
=== nose ===
We use Nose! http://somethingaboutorange.com/mrl/projects/nose/0.11.1/
Buildout grabs the necessary module. If you are an enterprising soul, please download the nose source and install to your native python or virtualenv instance.
 
 Nose Install Steps:
 # http://somethingaboutorange.com/mrl/projects/nose/nose-0.11.1.tar.gz
 # gzip -dc nose-0.11.1.tar.gz | tar xf -
 # cd nose-0.11.1
 # python setup.py install
=== in buildout ===
After running buildout, there will be a bin/unittests script available. Simply run it, passing which package's unit tests to run...
 bin/unittests --where=src/pyogp.lib.client/
 bin/unittests --where=src/pyogp.lib.base/
=== per packages test runs ===
There is a test.py in each of pyogp.lib.*.tests. It works in a buildout context by adding to path all the necessary packages and dependencies, or works outside of buildout assuming your python path has all the necessary modules sourced.
To run it, simply:
 cd pyogp/lib/base/tests
 python test.py
Pass test.py a file name, or to run all tests in the package, pass '..'.
Ensure the necessary modules are in your python instance's path:
Here's what the buildout generated nose test wrapper looks like on Enus' machine:
#!/Users/enus/svn/buildout/bin/python
import sys
sys.path[0:0] = [
 '/Users/enus/svn/buildout/eggs/uuid-1.30-py2.5.egg',
 '/Users/enus/svn/buildout/eggs/indra.base-1.0-py2.5.egg',
 '/Users/enus/svn/buildout/eggs/eventlet-0.8.14-py2.5.egg',
 '/Users/enus/svn/buildout/eggs/wsgiref-0.1.2-py2.5.egg',
 '/Users/enus/svn/buildout/src/pyogp.lib.base',
 '/Users/enus/svn/buildout/eggs/nose-0.11.1-py2.5.egg',
 '/Users/enus/svn/buildout/eggs/WebOb-0.9.6.1-py2.5.egg',
 '/Users/enus/svn/buildout/eggs/elementtree-1.2.7_20070827_preview-py2.5.egg',
 '/Users/enus/svn/buildout/eggs/setuptools-0.6c9-py2.5.egg',
 '/Users/enus/svn/buildout/eggs/pyOpenSSL-0.9-py2.5-macosx-10.5-i386.egg',
 '/Users/enus/svn/buildout/eggs/greenlet-0.2-py2.5-macosx-10.5-i386.egg',
 '/Users/enus/svn/buildout/eggs/indra.util-1.0-py2.5.egg',
 ]
import nose
if __name__ == '__main__':
 nose.main(argv=['nose']+sys.argv[1:])
=== Evaluating test coverage ===
Nose has a great tie into the coverage module (http://nedbatchelder.com/code/modules/rees-coverage.html), which evaluates code coverage by our tests. Buildout automatically installs the necessary module, or, you can install it to your python instance via "easy_install coverage".
To run the unittests via a buildout instance, add the following flags (making sure package names match up):
 --with-coverage --cover-package=pyogp.lib.client
For more on the nose test wrapper, see bin/unittests -h.
== Code Coverage ==
Currently, coverage of code by unittest is OK, but, we need to improve it!
=== pyogp.lib.client ===
Current coverage:
[11:32:17] [enus@sune] buildout$ bin/unittests --where=src/pyogp.lib.client/ --with-coverage --cover-package=pyogp.lib.client
Name Stmts Exec Cover Missing
-------------------------------------------------------------
pyogp.lib.client 1 1 100% 
pyogp.lib.client.agent 513 164 31% 35-49, 71, 82, 179-180, 213, 216, 222-239, 272, 281-303, 311-331, 336-345, 350-359, 365-431, 437-446, 452-457, 477-486, 496-533, 552-570, 575-580, 586-587, 593, 600-615, 620-632, 637-644, 649, 658-674, 679-700, 705-746, 751-757, 762-787, 792-804, 818-872, 881-925, 930-953, 967-980, 986-1017, 1024-1044, 1052-1068, 1073, 1080-1086, 1090-1093, 1098-1101, 1105, 1109-1112, 1117-1119
pyogp.lib.client.appearance 151 76 50% 76-83, 89-97, 122-135, 151-162, 165, 171, 179-183, 194-199, 216-226, 234-249, 257-280, 294-310, 335, 341-347, 354-355
pyogp.lib.client.assets 125 30 24% 58, 68-123, 145-168, 174, 177, 185-192, 202-211, 215-217, 222-231, 234-261
pyogp.lib.client.event_queue 153 90 58% 31, 39, 64, 73, 104, 107-109, 118-125, 136-149, 173, 179, 184-215, 220-235, 248-273
pyogp.lib.client.event_system 89 71 79% 43, 60-67, 82-84, 173-174, 184-185, 193-200, 205, 210
pyogp.lib.client.exc 136 78 57% 36, 42, 45, 57-60, 64, 73, 76, 86-90, 94, 116, 119, 128, 131, 141-142, 145, 160-161, 164, 173-174, 177, 186-187, 190, 199-200, 203, 212-213, 216, 225-226, 229, 238-239, 242, 261, 273, 285, 302, 305, 317, 330, 333, 345, 348, 356, 359, 367, 370
pyogp.lib.client.groups 243 54 22% 65-79, 85-90, 97-111, 116-130, 135-144, 149-156, 174-205, 210-215, 221-224, 229-236, 241, 246-252, 262-273, 278-287, 292-310, 315, 320, 325-329, 333-339, 347-359, 364, 369-375, 380-382, 387-404, 424-451, 469-477, 484-488, 492, 496-520, 524-527
pyogp.lib.client.inventory 495 181 36% 73-80, 84, 88, 93-152, 157-164, 173-180, 191-193, 199-202, 227, 243, 275-277, 295-302, 312, 320-322, 332-338, 350, 362, 373, 378-384, 389-391, 399-408, 417-431, 436-468, 473-480, 522, 559-560, 567-572, 577, 582, 587, 592, 599-601, 606-630, 635-659, 664-697, 702-705, 710-742, 749, 754, 759, 764-775, 780, 784-812, 816-867, 897, 902-909, 914, 924-933, 938, 948-956, 1021-1027, 1036-1040, 1046-1063, 1075-1104, 1109-1150
pyogp.lib.client.login 205 176 85% 111, 129, 150, 171, 201, 210, 225-226, 234, 281-283, 293, 300-301, 308-313, 341, 343, 347, 361, 370, 374-377, 389, 406, 433
pyogp.lib.client.objects 612 276 45% 114, 120-132, 143, 159-190, 200, 207, 212-220, 225-227, 235-239, 244-257, 262-284, 289-294, 298-303, 329-332, 340, 354, 357, 367-376, 382-391, 425-461, 466-633, 639-663, 691-693, 729-859, 907-908, 919-967, 971-973, 977-1014, 1037-1067, 1173, 1183-1195, 1203-1205, 1213-1215, 1223-1225, 1233-1235, 1242-1244, 1251-1253, 1260, 1268-1276, 1283, 1291-1299, 1304, 1309-1323, 1328-1344, 1351, 1358-1365, 1372, 1379-1386
pyogp.lib.client.parcel 372 69 18% 99-112, 122-127, 132-185, 190-212, 219-234, 239-279, 284-299, 304-307, 311, 315, 319, 325-328, 333-338, 343-359, 364-376, 381-389, 394-412, 417-424, 429-447, 452-456, 462, 467-482, 489, 521, 554, 587, 615, 642, 670, 696-699, 704-713, 721-723, 747-750, 755-763, 768-787, 797-873, 878-882, 889, 921, 945, 978, 1006, 1030, 1051, 1083, 1107, 1139
pyogp.lib.client.permissions 23 18 78% 57-61
pyogp.lib.client.region 259 91 35% 34, 40-41, 43, 78, 85, 96, 189-195, 199, 223-229, 237-240, 245-250, 255-272, 277-285, 289-315, 321-331, 336-351, 356-361, 366-367, 373-385, 390-396, 401-407, 412-416, 433-448, 453-460, 465-472, 477-514, 519-523, 528-529, 535-565, 570, 575, 580-586, 597-620, 623
pyogp.lib.client.settings 67 55 82% 146-155, 178-179
pyogp.lib.client.visualparams 502 495 98% 42-44, 50-53
-------------------------------------------------------------
TOTAL 3946 1925 48% 
----------------------------------------------------------------------
Ran 94 tests in 14.455s
FAILED (errors=1)
=== pyogp.lib.base ===
Nice coverage!
[11:32:40] [enus@sune] buildout$ bin/unittests --where=src/pyogp.lib.base/ --with-coverage --cover-package=pyogp.lib.base
Name Stmts Exec Cover Missing
----------------------------------------------------------------------
pyogp.lib.base 1 1 100% 
pyogp.lib.base.datatypes 106 73 68% 45, 49, 53, 72, 75, 80, 88-91, 107, 111, 115, 119, 136-141, 150, 153, 158, 184-189, 208, 213, 222-225, 230-232
pyogp.lib.base.exc 136 77 56% 33, 36, 42, 45, 57-60, 64, 73, 76, 86-90, 94, 116, 119, 128, 131, 141-142, 145, 160-161, 164, 173-174, 177, 186-187, 190, 199-200, 203, 212-213, 216, 225-226, 229, 238-239, 242, 261, 273, 285, 302, 305, 317, 330, 333, 345, 348, 356, 359, 367, 370
pyogp.lib.base.message 1 1 100% 
pyogp.lib.base.message.circuit 84 69 82% 39, 78-83, 91, 98-104, 134, 149-150, 154
pyogp.lib.base.message.data 4 4 100% 
pyogp.lib.base.message.data_packer 63 48 76% 66, 69-70, 73-75, 78, 81, 84-87, 92, 99, 101
pyogp.lib.base.message.data_unpacker 65 57 87% 76-80, 83-84, 91, 94, 100
pyogp.lib.base.message.llsd_builder 39 38 97% 34
pyogp.lib.base.message.message 63 42 66% 35-36, 43-44, 109-110, 114, 119-136, 141
pyogp.lib.base.message.message_handler 44 29 65% 38, 47-49, 60, 75-78, 88-90, 93, 97, 100-102, 106
pyogp.lib.base.message.msgtypes 83 76 91% 45-47, 118, 125, 127, 143
pyogp.lib.base.message.template 133 105 78% 46, 63, 71, 84, 87-92, 98, 101, 105, 116, 119, 121, 145, 148, 151, 154, 187, 190, 196, 199, 202, 205, 208, 211, 214
pyogp.lib.base.message.template_dict 58 52 89% 49-52, 100, 106
pyogp.lib.base.message.template_parser 228 187 82% 172-173, 177, 223, 252, 266-278, 281-294, 297-305, 309-317, 320
pyogp.lib.base.message.udpdeserializer 180 139 77% 52, 76-78, 116-125, 132, 138, 145-147, 170, 175, 184, 193, 197, 220-230, 240, 262, 274-275, 296-297, 308-315, 321-323, 335
pyogp.lib.base.message.udpdispatcher 149 108 72% 80, 111, 124-130, 135, 141, 147, 164, 173, 178, 190, 192, 203-211, 220-224, 242-255, 281, 287-288, 297-301, 305
pyogp.lib.base.message.udpserializer 66 57 86% 72, 89, 110-111, 131, 138, 143-147
pyogp.lib.base.network 1 1 100% 
pyogp.lib.base.network.net 27 14 51% 31, 35-38, 41-50, 66
pyogp.lib.base.settings 67 45 67% 146-155, 167-198
pyogp.lib.base.utilities 1 1 100% 
pyogp.lib.base.utilities.events 35 17 48% 36-38, 43-51, 55-65, 69, 73-74, 78
pyogp.lib.base.utilities.helpers 107 45 42% 48, 54-58, 64-71, 77-81, 87-100, 106-119, 125-128, 136, 150, 156, 162, 187, 204, 208, 213, 267-268, 270, 275-276, 287-292, 296-305, 309
----------------------------------------------------------------------
TOTAL 1741 1286 73% 
----------------------------------------------------------------------
Ran 71 tests in 17.255s
OK
== Writing Test Cases ==
Tests can be written using standard unittest. The tests in pyogp.interop cover some ogp and a couple of legacy cases, these need to be updated to work.
Testing only the call to login.cgi is unique, we don't need to spawn the client in a coroutine, nor do we need to keep the client alive, we just need to post to the login endpoint and evaluate the response.
=== pyogp.lib.client.test.test_agent ===
This test is a standard unittest instance, which tests agent.py at some level of coverage, and also shows how to use some of the mock objects available for use in testing the libs without connecting to a real grid. The mocks should likely be reworked to be simplified and standardized, but are functional for now.
<python># standard python libs
import unittest
# pyogp
from pyogp.lib.client.agent import Agent, Home
from pyogp.lib.client.login import LegacyLoginParams, OGPLoginParams
from pyogp.lib.client.exc import LoginError
# pyogp tests
from pyogp.lib.base.tests.mock_xmlrpc import MockXMLRPC
from pyogp.lib.base.tests.base import MockXMLRPCLogin, MockAgentDomainLogin
from pyogp.lib.base.network.tests.mockup_client import MockupClient
import pyogp.lib.base.tests.config 
class TestAgent(unittest.TestCase):
 def setUp(self):
 self.legacy_loginuri = 'http://localhost:12345/cgi-bin/login.cgi'
 self.ogp_loginuri = 'http://localhost:12345/auth.cgi'
 self.firstname = 'firstname'
 self.lastname = 'lastname'
 self.password = 'secret'
 self.client = Agent()
 def tearDown(self):
 pass
 def test_agent_legacy_login_via_variables(self):
 # override the network client with the mock client pointed at the mock login handler
 self.loginhandler = MockXMLRPC(MockXMLRPCLogin(), self.legacy_loginuri) 
 self.client.login(self.legacy_loginuri, self.firstname, self.lastname, self.password, start_location = 'start', handler = self.loginhandler, connect_region = False)
 self.assertEquals(self.client.login_response, {'region_y': '256', 'region_x': '256', 'first_name': '"first"', 'secure_session_id': '00000000-0000-0000-0000-000000000000', 'sim_ip': '127.0.0.1', 'agent_access': 'M', 'circuit_code': '600000000', 'look_at': '[r0.9963859999999999939,r-0.084939700000000006863,r0]', 'session_id': '00000000-0000-0000-0000-000000000000', 'udp_blacklist': 'EnableSimulator,TeleportFinish,CrossedRegion', 'seed_capability': 'https://somesim:12043/cap/00000000-0000-0000-0000-000000000000', 'agent_id': '00000000-0000-0000-0000-000000000000', 'last_name': 'last', 'inventory_host': 'someinvhost', 'start_location': 'last', 'sim_port': '13001', 'message': 'message', 'login': 'true', 'seconds_since_epoch': '1234567890'})
 def test_agent_legacy_login_via_params(self):
 # override the network client with the mock client pointed at the mock login handler
 self.loginhandler = MockXMLRPC(MockXMLRPCLogin(), self.legacy_loginuri)
 login_params = LegacyLoginParams(self.firstname, self.lastname, self.password)
 self.client.login(self.legacy_loginuri, login_params = login_params, start_location = 'start', handler = self.loginhandler, connect_region = False)
 self.assertEquals(self.client.login_response, {'region_y': '256', 'region_x': '256', 'first_name': '"first"', 'secure_session_id': '00000000-0000-0000-0000-000000000000', 'sim_ip': '127.0.0.1', 'agent_access': 'M', 'circuit_code': '600000000', 'look_at': '[r0.9963859999999999939,r-0.084939700000000006863,r0]', 'session_id': '00000000-0000-0000-0000-000000000000', 'udp_blacklist': 'EnableSimulator,TeleportFinish,CrossedRegion', 'seed_capability': 'https://somesim:12043/cap/00000000-0000-0000-0000-000000000000', 'agent_id': '00000000-0000-0000-0000-000000000000', 'last_name': 'last', 'inventory_host': 'someinvhost', 'start_location': 'last', 'sim_port': '13001', 'message': 'message', 'login': 'true', 'seconds_since_epoch': '1234567890'})
 def test_agent_ogp_login_via_variables(self):
 # override the network client with the mock client pointed at the mock login handler
 self.loginhandler = MockupClient(MockAgentDomainLogin())
 self.client.login(self.ogp_loginuri, self.firstname, self.lastname, self.password, start_location = 'start', handler = self.loginhandler, connect_region = False)
 self.assertEquals(self.client.login_response, {'agent_seed_capability': 'http://127.0.0.1:12345/seed_cap', 'authenticated': True})
 def test_agent_ogp_login_via_params(self):
 # override the network client with the mock client pointed at the mock login handler
 self.loginhandler = MockupClient(MockAgentDomainLogin())
 login_params = OGPLoginParams(self.firstname, self.lastname, self.password)
 self.client.login(self.ogp_loginuri, self.firstname, self.lastname, self.password, start_location = 'start', handler = self.loginhandler, connect_region = False)
 self.assertEquals(self.client.login_response, {'agent_seed_capability': 'http://127.0.0.1:12345/seed_cap', 'authenticated': True})
 def test_agent_login_no_account_info(self):
 self.assertRaises(LoginError, self.client.login, self.ogp_loginuri)
 def test_legacy_get_login_params(self):
 self.client.grid_type = 'Legacy'
 params = self.client._get_login_params(self.firstname, self.lastname, self.password)
 self.assertEquals(type(params), type(LegacyLoginParams(self.firstname, self.lastname, self.password)))
 def test_ogp_get_login_params(self):
 self.client.grid_type = 'OGP'
 params = self.client._get_login_params(self.firstname, self.lastname, self.password)
 self.assertEquals(type(params), type(OGPLoginParams(self.firstname, self.lastname, self.password)))
 '''
 def test_failed_legacy_login(self):
 # ToDo: enable mne when you can get me working, it's 'correct',
 # but not raising the error properly?
 self.password = 'badpassword'
 # override the network client with the mock client pointed at the mock login handler
 self.loginhandler = MockXMLRPC(MockXMLRPCLogin(), self.legacy_loginuri) 
 self.assertRaises(LoginError, self.client.login, self.legacy_loginuri, self.firstname, self.lastname, self.password, start_location = 'start', handler = self.loginhandler)
 '''
 def test_agent_home_class(self):
 home_string = "{'region_handle':[r261120, r247040], 'position':[r171.622, r148.26, r79.3938], 'look_at':[r0, r1, r0]}"
 home = Home(home_string)
 # Note: have not yet worked out precision on floats. Kinda need to
 self.assertEquals(home.region_handle, [261120, 247040])
 self.assertEquals(home.position.X, 171.62200000000001)
 self.assertEquals(home.position.Y, 148.25999999999999)
 self.assertEquals(home.position.Z, 79.393799999999999)
 self.assertEquals(home.look_at.X, 0)
 self.assertEquals(home.look_at.Y, 1)
 self.assertEquals(home.look_at.Z, 0)
 self.assertEquals(home.global_x, 261120)
 self.assertEquals(home.global_y, 247040)
def test_suite():
 from unittest import TestSuite, makeSuite
 suite = TestSuite()
 suite.addTest(makeSuite(TestAgent))
 return suite</python>
Category: Pyogp_Client_Lib
Category: Pyogp