test_docxmlrpc.py 7.88 KB
Newer Older
1
from xmlrpc.server import DocXMLRPCServer
2
import http.client
3
import sys
4
import threading
5
from test import support
6 7
import unittest

8
def make_request_and_skipIf(condition, reason):
9
    # If we skip the test, we have to make a request because
10 11 12 13 14 15 16 17 18 19 20 21
    # the server created in setUp blocks expecting one to come in.
    if not condition:
        return lambda func: func
    def decorator(func):
        def make_request_and_skip(self):
            self.client.request("GET", "/")
            self.client.getresponse()
            raise unittest.SkipTest(reason)
        return make_request_and_skip
    return decorator


22
def make_server():
Benjamin Peterson's avatar
Benjamin Peterson committed
23
    serv = DocXMLRPCServer(("localhost", 0), logRequests=False)
24

Benjamin Peterson's avatar
Benjamin Peterson committed
25
    try:
26 27 28 29
        # Add some documentation
        serv.set_server_title("DocXMLRPCServer Test Documentation")
        serv.set_server_name("DocXMLRPCServer Test Docs")
        serv.set_server_documentation(
30 31
            "This is an XML-RPC server's documentation, but the server "
            "can be used by POSTing to /RPC2. Try self.add, too.")
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

        # Create and register classes and functions
        class TestClass(object):
            def test_method(self, arg):
                """Test method's docs. This method truly does very little."""
                self.arg = arg

        serv.register_introspection_functions()
        serv.register_instance(TestClass())

        def add(x, y):
            """Add two instances together. This follows PEP008, but has nothing
            to do with RFC1952. Case should matter: pEp008 and rFC1952.  Things
            that start with http and ftp should be auto-linked, too:
            http://google.com.
            """
            return x + y

50 51 52 53 54 55 56 57
        def annotation(x: int):
            """ Use function annotations. """
            return x

        class ClassWithAnnotation:
            def method_annotation(self, x: bytes):
                return x.decode()

58 59
        serv.register_function(add)
        serv.register_function(lambda x, y: x-y)
60 61
        serv.register_function(annotation)
        serv.register_instance(ClassWithAnnotation())
62 63
        return serv
    except:
64
        serv.server_close()
65
        raise
66 67 68 69 70 71

class DocXMLRPCHTTPGETServer(unittest.TestCase):
    def setUp(self):
        # Enable server feedback
        DocXMLRPCServer._send_traceback_header = True

72 73 74
        self.serv = make_server()
        self.thread = threading.Thread(target=self.serv.serve_forever)
        self.thread.start()
75

76
        PORT = self.serv.server_address[1]
77
        self.client = http.client.HTTPConnection("localhost:%d" % PORT)
78 79 80 81 82 83

    def tearDown(self):
        self.client.close()

        # Disable server feedback
        DocXMLRPCServer._send_traceback_header = False
84 85 86
        self.serv.shutdown()
        self.thread.join()
        self.serv.server_close()
87 88 89 90 91 92 93 94

    def test_valid_get_response(self):
        self.client.request("GET", "/")
        response = self.client.getresponse()

        self.assertEqual(response.status, 200)
        self.assertEqual(response.getheader("Content-type"), "text/html")

95
        # Server raises an exception if we don't start to read the data
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
        response.read()

    def test_invalid_get_response(self):
        self.client.request("GET", "/spam")
        response = self.client.getresponse()

        self.assertEqual(response.status, 404)
        self.assertEqual(response.getheader("Content-type"), "text/plain")

        response.read()

    def test_lambda(self):
        """Test that lambda functionality stays the same.  The output produced
        currently is, I suspect invalid because of the unencoded brackets in the
        HTML, "<lambda>".

        The subtraction lambda method is tested.
        """
        self.client.request("GET", "/")
        response = self.client.getresponse()

117 118 119
        self.assertIn((b'<dl><dt><a name="-&lt;lambda&gt;"><strong>'
                       b'&lt;lambda&gt;</strong></a>(x, y)</dt></dl>'),
                      response.read())
120

121 122
    @make_request_and_skipIf(sys.flags.optimize >= 2,
                     "Docstrings are omitted with -O2 and above")
123
    def test_autolinking(self):
124 125 126
        """Test that the server correctly automatically wraps references to
        PEPS and RFCs with links, and that it linkifies text starting with
        http or ftp protocol prefixes.
127 128 129 130

        The documentation for the "add" method contains the test material.
        """
        self.client.request("GET", "/")
Christian Heimes's avatar
Christian Heimes committed
131
        response = self.client.getresponse().read()
132

133 134 135 136 137 138 139 140 141 142 143
        self.assertIn(
            (b'<dl><dt><a name="-add"><strong>add</strong></a>(x, y)</dt><dd>'
             b'<tt>Add&nbsp;two&nbsp;instances&nbsp;together.&nbsp;This&nbsp;'
             b'follows&nbsp;<a href="http://www.python.org/dev/peps/pep-0008/">'
             b'PEP008</a>,&nbsp;but&nbsp;has&nbsp;nothing<br>\nto&nbsp;do&nbsp;'
             b'with&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1952.txt">'
             b'RFC1952</a>.&nbsp;Case&nbsp;should&nbsp;matter:&nbsp;pEp008&nbsp;'
             b'and&nbsp;rFC1952.&nbsp;&nbsp;Things<br>\nthat&nbsp;start&nbsp;'
             b'with&nbsp;http&nbsp;and&nbsp;ftp&nbsp;should&nbsp;be&nbsp;'
             b'auto-linked,&nbsp;too:<br>\n<a href="http://google.com">'
             b'http://google.com</a>.</tt></dd></dl>'), response)
144

145 146
    @make_request_and_skipIf(sys.flags.optimize >= 2,
                     "Docstrings are omitted with -O2 and above")
147
    def test_system_methods(self):
148
        """Test the presence of three consecutive system.* methods.
149

150 151
        This also tests their use of parameter type recognition and the
        systems related to that process.
152 153
        """
        self.client.request("GET", "/")
154
        response = self.client.getresponse().read()
155

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
        self.assertIn(
            (b'<dl><dt><a name="-system.methodHelp"><strong>system.methodHelp'
             b'</strong></a>(method_name)</dt><dd><tt><a href="#-system.method'
             b'Help">system.methodHelp</a>(\'add\')&nbsp;=&gt;&nbsp;"Adds&nbsp;'
             b'two&nbsp;integers&nbsp;together"<br>\n&nbsp;<br>\nReturns&nbsp;a'
             b'&nbsp;string&nbsp;containing&nbsp;documentation&nbsp;for&nbsp;'
             b'the&nbsp;specified&nbsp;method.</tt></dd></dl>\n<dl><dt><a name'
             b'="-system.methodSignature"><strong>system.methodSignature</strong>'
             b'</a>(method_name)</dt><dd><tt><a href="#-system.methodSignature">'
             b'system.methodSignature</a>(\'add\')&nbsp;=&gt;&nbsp;[double,&nbsp;'
             b'int,&nbsp;int]<br>\n&nbsp;<br>\nReturns&nbsp;a&nbsp;list&nbsp;'
             b'describing&nbsp;the&nbsp;signature&nbsp;of&nbsp;the&nbsp;method.'
             b'&nbsp;In&nbsp;the<br>\nabove&nbsp;example,&nbsp;the&nbsp;add&nbsp;'
             b'method&nbsp;takes&nbsp;two&nbsp;integers&nbsp;as&nbsp;arguments'
             b'<br>\nand&nbsp;returns&nbsp;a&nbsp;double&nbsp;result.<br>\n&nbsp;'
             b'<br>\nThis&nbsp;server&nbsp;does&nbsp;NOT&nbsp;support&nbsp;system'
172
             b'.methodSignature.</tt></dd></dl>'), response)
173 174 175 176 177 178 179

    def test_autolink_dotted_methods(self):
        """Test that selfdot values are made strong automatically in the
        documentation."""
        self.client.request("GET", "/")
        response = self.client.getresponse()

180 181
        self.assertIn(b"""Try&nbsp;self.<strong>add</strong>,&nbsp;too.""",
                      response.read())
182

183 184 185 186
    def test_annotations(self):
        """ Test that annotations works as expected """
        self.client.request("GET", "/")
        response = self.client.getresponse()
187 188
        docstring = (b'' if sys.flags.optimize >= 2 else
                     b'<dd><tt>Use&nbsp;function&nbsp;annotations.</tt></dd>')
189 190
        self.assertIn(
            (b'<dl><dt><a name="-annotation"><strong>annotation</strong></a>'
191 192
             b'(x: int)</dt>' + docstring + b'</dl>\n'
             b'<dl><dt><a name="-method_annotation"><strong>'
193 194 195 196
             b'method_annotation</strong></a>(x: bytes)</dt></dl>'),
            response.read())


197
if __name__ == '__main__':
198
    unittest.main()