blob: d9fd9170bbab79feb4e056518adc8552232f7845 [file] [log] [blame]
Georg Brandl38eceaa2008-05-26 11:14:17 +00001from xmlrpc.server import DocXMLRPCServer
Georg Brandl24420152008-05-26 16:32:26 +00002import http.client
R. David Murray378c0cf2010-02-24 01:46:21 +00003import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +00004from test import support
Victor Stinner45df8202010-04-28 22:31:17 +00005threading = support.import_module('threading')
Christian Heimes2f1019e2007-12-10 16:18:49 +00006import time
Georg Brandl89fad142010-03-14 10:23:39 +00007import socket
Christian Heimes2f1019e2007-12-10 16:18:49 +00008import unittest
Christian Heimes2f1019e2007-12-10 16:18:49 +00009
10PORT = None
11
R. David Murray378c0cf2010-02-24 01:46:21 +000012def make_request_and_skipIf(condition, reason):
Serhiy Storchaka56a6d852014-12-01 18:28:43 +020013 # If we skip the test, we have to make a request because
R. David Murray378c0cf2010-02-24 01:46:21 +000014 # the server created in setUp blocks expecting one to come in.
15 if not condition:
16 return lambda func: func
17 def decorator(func):
18 def make_request_and_skip(self):
19 self.client.request("GET", "/")
20 self.client.getresponse()
21 raise unittest.SkipTest(reason)
22 return make_request_and_skip
23 return decorator
24
25
Christian Heimes2f1019e2007-12-10 16:18:49 +000026def server(evt, numrequests):
Benjamin Petersonf10a79a2008-10-11 00:49:57 +000027 serv = DocXMLRPCServer(("localhost", 0), logRequests=False)
Christian Heimes2f1019e2007-12-10 16:18:49 +000028
Benjamin Petersonf10a79a2008-10-11 00:49:57 +000029 try:
Christian Heimes2f1019e2007-12-10 16:18:49 +000030 global PORT
31 PORT = serv.socket.getsockname()[1]
32
33 # Add some documentation
34 serv.set_server_title("DocXMLRPCServer Test Documentation")
35 serv.set_server_name("DocXMLRPCServer Test Docs")
36 serv.set_server_documentation(
Ezio Melottib58e0bd2010-01-23 15:40:09 +000037 "This is an XML-RPC server's documentation, but the server "
38 "can be used by POSTing to /RPC2. Try self.add, too.")
Christian Heimes2f1019e2007-12-10 16:18:49 +000039
40 # Create and register classes and functions
41 class TestClass(object):
42 def test_method(self, arg):
43 """Test method's docs. This method truly does very little."""
44 self.arg = arg
45
46 serv.register_introspection_functions()
47 serv.register_instance(TestClass())
48
49 def add(x, y):
50 """Add two instances together. This follows PEP008, but has nothing
51 to do with RFC1952. Case should matter: pEp008 and rFC1952. Things
52 that start with http and ftp should be auto-linked, too:
53 http://google.com.
54 """
55 return x + y
56
R David Murrayf22b62e2013-08-10 12:01:47 -040057 def annotation(x: int):
58 """ Use function annotations. """
59 return x
60
61 class ClassWithAnnotation:
62 def method_annotation(self, x: bytes):
63 return x.decode()
64
Christian Heimes2f1019e2007-12-10 16:18:49 +000065 serv.register_function(add)
66 serv.register_function(lambda x, y: x-y)
R David Murrayf22b62e2013-08-10 12:01:47 -040067 serv.register_function(annotation)
68 serv.register_instance(ClassWithAnnotation())
Christian Heimes2f1019e2007-12-10 16:18:49 +000069
70 while numrequests > 0:
71 serv.handle_request()
72 numrequests -= 1
73 except socket.timeout:
74 pass
75 finally:
76 serv.server_close()
77 PORT = None
78 evt.set()
79
80class DocXMLRPCHTTPGETServer(unittest.TestCase):
81 def setUp(self):
Antoine Pitroud52656b2009-10-30 17:34:49 +000082 self._threads = support.threading_setup()
Christian Heimes2f1019e2007-12-10 16:18:49 +000083 # Enable server feedback
84 DocXMLRPCServer._send_traceback_header = True
85
86 self.evt = threading.Event()
87 threading.Thread(target=server, args=(self.evt, 1)).start()
88
89 # wait for port to be assigned
Victor Stinner1e48eb32014-03-18 00:39:04 +010090 deadline = time.monotonic() + 10.0
91 while PORT is None:
92 time.sleep(0.010)
93 if time.monotonic() > deadline:
94 break
Christian Heimes2f1019e2007-12-10 16:18:49 +000095
Georg Brandl24420152008-05-26 16:32:26 +000096 self.client = http.client.HTTPConnection("localhost:%d" % PORT)
Christian Heimes2f1019e2007-12-10 16:18:49 +000097
98 def tearDown(self):
99 self.client.close()
100
101 self.evt.wait()
102
103 # Disable server feedback
104 DocXMLRPCServer._send_traceback_header = False
Antoine Pitroud52656b2009-10-30 17:34:49 +0000105 support.threading_cleanup(*self._threads)
Christian Heimes2f1019e2007-12-10 16:18:49 +0000106
107 def test_valid_get_response(self):
108 self.client.request("GET", "/")
109 response = self.client.getresponse()
110
111 self.assertEqual(response.status, 200)
112 self.assertEqual(response.getheader("Content-type"), "text/html")
113
Andrew Svetlov737fb892012-12-18 21:14:22 +0200114 # Server raises an exception if we don't start to read the data
Christian Heimes2f1019e2007-12-10 16:18:49 +0000115 response.read()
116
117 def test_invalid_get_response(self):
118 self.client.request("GET", "/spam")
119 response = self.client.getresponse()
120
121 self.assertEqual(response.status, 404)
122 self.assertEqual(response.getheader("Content-type"), "text/plain")
123
124 response.read()
125
126 def test_lambda(self):
127 """Test that lambda functionality stays the same. The output produced
128 currently is, I suspect invalid because of the unencoded brackets in the
129 HTML, "<lambda>".
130
131 The subtraction lambda method is tested.
132 """
133 self.client.request("GET", "/")
134 response = self.client.getresponse()
135
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000136 self.assertIn((b'<dl><dt><a name="-&lt;lambda&gt;"><strong>'
137 b'&lt;lambda&gt;</strong></a>(x, y)</dt></dl>'),
138 response.read())
Christian Heimes2f1019e2007-12-10 16:18:49 +0000139
R. David Murray378c0cf2010-02-24 01:46:21 +0000140 @make_request_and_skipIf(sys.flags.optimize >= 2,
141 "Docstrings are omitted with -O2 and above")
Christian Heimes2f1019e2007-12-10 16:18:49 +0000142 def test_autolinking(self):
R. David Murray378c0cf2010-02-24 01:46:21 +0000143 """Test that the server correctly automatically wraps references to
144 PEPS and RFCs with links, and that it linkifies text starting with
145 http or ftp protocol prefixes.
Christian Heimes2f1019e2007-12-10 16:18:49 +0000146
147 The documentation for the "add" method contains the test material.
148 """
149 self.client.request("GET", "/")
Christian Heimes2202f872008-02-06 14:31:34 +0000150 response = self.client.getresponse().read()
Christian Heimes2f1019e2007-12-10 16:18:49 +0000151
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000152 self.assertIn(
153 (b'<dl><dt><a name="-add"><strong>add</strong></a>(x, y)</dt><dd>'
154 b'<tt>Add&nbsp;two&nbsp;instances&nbsp;together.&nbsp;This&nbsp;'
155 b'follows&nbsp;<a href="http://www.python.org/dev/peps/pep-0008/">'
156 b'PEP008</a>,&nbsp;but&nbsp;has&nbsp;nothing<br>\nto&nbsp;do&nbsp;'
157 b'with&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1952.txt">'
158 b'RFC1952</a>.&nbsp;Case&nbsp;should&nbsp;matter:&nbsp;pEp008&nbsp;'
159 b'and&nbsp;rFC1952.&nbsp;&nbsp;Things<br>\nthat&nbsp;start&nbsp;'
160 b'with&nbsp;http&nbsp;and&nbsp;ftp&nbsp;should&nbsp;be&nbsp;'
161 b'auto-linked,&nbsp;too:<br>\n<a href="http://google.com">'
162 b'http://google.com</a>.</tt></dd></dl>'), response)
Christian Heimes2f1019e2007-12-10 16:18:49 +0000163
R. David Murray378c0cf2010-02-24 01:46:21 +0000164 @make_request_and_skipIf(sys.flags.optimize >= 2,
165 "Docstrings are omitted with -O2 and above")
Christian Heimes2f1019e2007-12-10 16:18:49 +0000166 def test_system_methods(self):
167 """Test the precense of three consecutive system.* methods.
168
R. David Murray378c0cf2010-02-24 01:46:21 +0000169 This also tests their use of parameter type recognition and the
170 systems related to that process.
Christian Heimes2f1019e2007-12-10 16:18:49 +0000171 """
172 self.client.request("GET", "/")
Christian Heimesa5535f22007-12-10 20:18:07 +0000173 response = self.client.getresponse().read()
Christian Heimes2f1019e2007-12-10 16:18:49 +0000174
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000175 self.assertIn(
176 (b'<dl><dt><a name="-system.methodHelp"><strong>system.methodHelp'
177 b'</strong></a>(method_name)</dt><dd><tt><a href="#-system.method'
178 b'Help">system.methodHelp</a>(\'add\')&nbsp;=&gt;&nbsp;"Adds&nbsp;'
179 b'two&nbsp;integers&nbsp;together"<br>\n&nbsp;<br>\nReturns&nbsp;a'
180 b'&nbsp;string&nbsp;containing&nbsp;documentation&nbsp;for&nbsp;'
181 b'the&nbsp;specified&nbsp;method.</tt></dd></dl>\n<dl><dt><a name'
182 b'="-system.methodSignature"><strong>system.methodSignature</strong>'
183 b'</a>(method_name)</dt><dd><tt><a href="#-system.methodSignature">'
184 b'system.methodSignature</a>(\'add\')&nbsp;=&gt;&nbsp;[double,&nbsp;'
185 b'int,&nbsp;int]<br>\n&nbsp;<br>\nReturns&nbsp;a&nbsp;list&nbsp;'
186 b'describing&nbsp;the&nbsp;signature&nbsp;of&nbsp;the&nbsp;method.'
187 b'&nbsp;In&nbsp;the<br>\nabove&nbsp;example,&nbsp;the&nbsp;add&nbsp;'
188 b'method&nbsp;takes&nbsp;two&nbsp;integers&nbsp;as&nbsp;arguments'
189 b'<br>\nand&nbsp;returns&nbsp;a&nbsp;double&nbsp;result.<br>\n&nbsp;'
190 b'<br>\nThis&nbsp;server&nbsp;does&nbsp;NOT&nbsp;support&nbsp;system'
R David Murrayf22b62e2013-08-10 12:01:47 -0400191 b'.methodSignature.</tt></dd></dl>'), response)
Christian Heimes2f1019e2007-12-10 16:18:49 +0000192
193 def test_autolink_dotted_methods(self):
194 """Test that selfdot values are made strong automatically in the
195 documentation."""
196 self.client.request("GET", "/")
197 response = self.client.getresponse()
198
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000199 self.assertIn(b"""Try&nbsp;self.<strong>add</strong>,&nbsp;too.""",
200 response.read())
Christian Heimes2f1019e2007-12-10 16:18:49 +0000201
R David Murrayf22b62e2013-08-10 12:01:47 -0400202 def test_annotations(self):
203 """ Test that annotations works as expected """
204 self.client.request("GET", "/")
205 response = self.client.getresponse()
Serhiy Storchaka3e60a9d2013-12-08 18:14:49 +0200206 docstring = (b'' if sys.flags.optimize >= 2 else
207 b'<dd><tt>Use&nbsp;function&nbsp;annotations.</tt></dd>')
R David Murrayf22b62e2013-08-10 12:01:47 -0400208 self.assertIn(
209 (b'<dl><dt><a name="-annotation"><strong>annotation</strong></a>'
Serhiy Storchaka3e60a9d2013-12-08 18:14:49 +0200210 b'(x: int)</dt>' + docstring + b'</dl>\n'
211 b'<dl><dt><a name="-method_annotation"><strong>'
R David Murrayf22b62e2013-08-10 12:01:47 -0400212 b'method_annotation</strong></a>(x: bytes)</dt></dl>'),
213 response.read())
214
215
Christian Heimes2f1019e2007-12-10 16:18:49 +0000216def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000217 support.run_unittest(DocXMLRPCHTTPGETServer)
Christian Heimes2f1019e2007-12-10 16:18:49 +0000218
219if __name__ == '__main__':
220 test_main()