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="-&lt;lambda&gt;"><strong>'
1264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao                      '&lt;lambda&gt;</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&nbsp;two&nbsp;instances&nbsp;together.&nbsp;This&nbsp;'
1444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             'follows&nbsp;<a href="http://www.python.org/dev/peps/pep-0008/">'
1454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             'PEP008</a>,&nbsp;but&nbsp;has&nbsp;nothing<br>\nto&nbsp;do&nbsp;'
1464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             'with&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1952.txt">'
1474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             'RFC1952</a>.&nbsp;Case&nbsp;should&nbsp;matter:&nbsp;pEp008&nbsp;'
1484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             'and&nbsp;rFC1952.&nbsp;&nbsp;Things<br>\nthat&nbsp;start&nbsp;'
1494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             'with&nbsp;http&nbsp;and&nbsp;ftp&nbsp;should&nbsp;be&nbsp;'
1504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             'auto-linked,&nbsp;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>()&nbsp;=&gt;&nbsp;[\'add\',&nbsp;\'subtract\','
1684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;\'multiple\']<br>\n&nbsp;<br>\nReturns&nbsp;a&nbsp;list'
1694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;of&nbsp;the&nbsp;methods&nbsp;supported&nbsp;by&nbsp;the'
1704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;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\')&nbsp;'
1734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '=&gt;&nbsp;"Adds&nbsp;two&nbsp;integers&nbsp;together"<br>\n&nbsp;'
1744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '<br>\nReturns&nbsp;a&nbsp;string&nbsp;containing&nbsp;documentation'
1754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;for&nbsp;the&nbsp;specified&nbsp;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\')&nbsp;'
1794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '=&gt;&nbsp;[double,&nbsp;int,&nbsp;int]<br>\n&nbsp;<br>\nReturns'
1804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;a&nbsp;list&nbsp;describing&nbsp;the&nbsp;signature&nbsp;of'
1814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;the&nbsp;method.&nbsp;In&nbsp;the<br>\nabove&nbsp;example,'
1824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;the&nbsp;add&nbsp;method&nbsp;takes&nbsp;two&nbsp;integers'
1834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;as&nbsp;arguments<br>\nand&nbsp;returns&nbsp;a&nbsp;double'
1844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             '&nbsp;result.<br>\n&nbsp;<br>\nThis&nbsp;server&nbsp;does&nbsp;'
1854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao             'NOT&nbsp;support&nbsp;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&nbsp;self.<strong>add</strong>,&nbsp;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