14adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaofrom DocXMLRPCServer import DocXMLRPCServer 24adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport httplib 34adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport sys 44adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaofrom test import test_support 54adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaothreading = test_support.import_module('threading') 64adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport time 74adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport socket 84adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport unittest 94adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 104adfde8bc82dd39f59e0445588c3e599ada477dJosh GaoPORT = None 114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef make_request_and_skipIf(condition, reason): 134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # If we skip the test, we have to make a request because the 144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # the server created in setUp blocks expecting one to come in. 154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not condition: 164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return lambda func: func 174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def decorator(func): 184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def make_request_and_skip(self): 194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.request("GET", "/") 204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.getresponse() 214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao raise unittest.SkipTest(reason) 224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return make_request_and_skip 234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return decorator 244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef server(evt, numrequests): 274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv = DocXMLRPCServer(("localhost", 0), logRequests=False) 284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao global PORT 314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao PORT = serv.socket.getsockname()[1] 324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # Add some documentation 344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.set_server_title("DocXMLRPCServer Test Documentation") 354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.set_server_name("DocXMLRPCServer Test Docs") 364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.set_server_documentation( 374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao "This is an XML-RPC server's documentation, but the server " 384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao "can be used by POSTing to /RPC2. Try self.add, too.") 394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # Create and register classes and functions 414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao class TestClass(object): 424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def test_method(self, arg): 434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Test method's docs. This method truly does very little.""" 444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.arg = arg 454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.register_introspection_functions() 474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.register_instance(TestClass()) 484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add(x, y): 504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Add two instances together. This follows PEP008, but has nothing 514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao to do with RFC1952. Case should matter: pEp008 and rFC1952. Things 524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao that start with http and ftp should be auto-linked, too: 534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao http://google.com. 544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return x + y 564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.register_function(add) 584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.register_function(lambda x, y: x-y) 594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao while numrequests > 0: 614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.handle_request() 624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao numrequests -= 1 634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except socket.timeout: 644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao pass 654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao finally: 664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao serv.server_close() 674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao PORT = None 684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao evt.set() 694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoclass DocXMLRPCHTTPGETServer(unittest.TestCase): 714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def setUp(self): 724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._threads = test_support.threading_setup() 734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # Enable server feedback 744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao DocXMLRPCServer._send_traceback_header = True 754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.evt = threading.Event() 774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao threading.Thread(target=server, args=(self.evt, 1)).start() 784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # wait for port to be assigned 804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao n = 1000 814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao while n > 0 and PORT is None: 824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao time.sleep(0.001) 834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao n -= 1 844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client = httplib.HTTPConnection("localhost:%d" % PORT) 864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def tearDown(self): 884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.close() 894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.evt.wait() 914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # Disable server feedback 934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao DocXMLRPCServer._send_traceback_header = False 944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao test_support.threading_cleanup(*self._threads) 954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def test_valid_get_response(self): 974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.request("GET", "/") 984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response = self.client.getresponse() 994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.assertEqual(response.status, 200) 1014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.assertEqual(response.getheader("Content-type"), "text/html") 1024adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1034adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # Server raises an exception if we don't start to read the data 1044adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response.read() 1054adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1064adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def test_invalid_get_response(self): 1074adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.request("GET", "/spam") 1084adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response = self.client.getresponse() 1094adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.assertEqual(response.status, 404) 1114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.assertEqual(response.getheader("Content-type"), "text/plain") 1124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response.read() 1144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def test_lambda(self): 1164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Test that lambda functionality stays the same. The output produced 1174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao currently is, I suspect invalid because of the unencoded brackets in the 1184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao HTML, "<lambda>". 1194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao The subtraction lambda method is tested. 1214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 1224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.request("GET", "/") 1234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response = self.client.getresponse() 1244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.assertIn('<dl><dt><a name="-<lambda>"><strong>' 1264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '<lambda></strong></a>(x, y)</dt></dl>', 1274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response.read()) 1284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao @make_request_and_skipIf(sys.flags.optimize >= 2, 1304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao "Docstrings are omitted with -O2 and above") 1314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def test_autolinking(self): 1324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Test that the server correctly automatically wraps references to 1334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao PEPS and RFCs with links, and that it linkifies text starting with 1344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao http or ftp protocol prefixes. 1354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao The documentation for the "add" method contains the test material. 1374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 1384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.request("GET", "/") 1394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response = self.client.getresponse() 1404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.assertIn( 1424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ('<dl><dt><a name="-add"><strong>add</strong></a>(x, y)</dt><dd>' 1434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '<tt>Add two instances together. This ' 1444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'follows <a href="http://www.python.org/dev/peps/pep-0008/">' 1454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'PEP008</a>, but has nothing<br>\nto do ' 1464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'with <a href="http://www.rfc-editor.org/rfc/rfc1952.txt">' 1474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'RFC1952</a>. Case should matter: pEp008 ' 1484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'and rFC1952. Things<br>\nthat start ' 1494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'with http and ftp should be ' 1504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'auto-linked, too:<br>\n<a href="http://google.com">' 1514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'http://google.com</a>.</tt></dd></dl>'), response.read()) 1524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao @make_request_and_skipIf(sys.flags.optimize >= 2, 1544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao "Docstrings are omitted with -O2 and above") 1554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def test_system_methods(self): 1564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Test the precense of three consecutive system.* methods. 1574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao This also tests their use of parameter type recognition and the 1594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao systems related to that process. 1604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 1614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.request("GET", "/") 1624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response = self.client.getresponse() 1634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.assertIn( 1654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ('<dl><dt><a name="-system.listMethods"><strong>system.listMethods' 1664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '</strong></a>()</dt><dd><tt><a href="#-system.listMethods">system' 1674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '.listMethods</a>() => [\'add\', \'subtract\',' 1684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' \'multiple\']<br>\n <br>\nReturns a list' 1694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' of the methods supported by the' 1704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' server.</tt></dd></dl>\n <dl><dt><a name="-system.methodHelp">' 1714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '<strong>system.methodHelp</strong></a>(method_name)</dt><dd><tt>' 1724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '<a href="#-system.methodHelp">system.methodHelp</a>(\'add\') ' 1734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '=> "Adds two integers together"<br>\n ' 1744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '<br>\nReturns a string containing documentation' 1754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' for the specified method.</tt></dd></dl>\n ' 1764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '<dl><dt><a name="-system.methodSignature"><strong>system.' 1774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'methodSignature</strong></a>(method_name)</dt><dd><tt><a href="#-' 1784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'system.methodSignature">system.methodSignature</a>(\'add\') ' 1794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao '=> [double, int, int]<br>\n <br>\nReturns' 1804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' a list describing the signature of' 1814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' the method. In the<br>\nabove example,' 1824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' the add method takes two integers' 1834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' as arguments<br>\nand returns a double' 1844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ' result.<br>\n <br>\nThis server does ' 1854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 'NOT support system.methodSignature.</tt></dd></dl>'), 1864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response.read()) 1874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def test_autolink_dotted_methods(self): 1894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Test that selfdot values are made strong automatically in the 1904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao documentation.""" 1914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.client.request("GET", "/") 1924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response = self.client.getresponse() 1934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.assertIn("""Try self.<strong>add</strong>, too.""", 1954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao response.read()) 1964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef test_main(): 1984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao test_support.run_unittest(DocXMLRPCHTTPGETServer) 1994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoif __name__ == '__main__': 2014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao test_main() 202