test_xmlrpc.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. import sys
  2. import unittest
  3. from supervisor.tests.base import DummySupervisor
  4. from supervisor.tests.base import DummyRequest
  5. from supervisor.tests.base import DummySupervisorRPCNamespace
  6. class GetFaultDescriptionTests(unittest.TestCase):
  7. def test_returns_description_for_known_fault(self):
  8. from supervisor import xmlrpc
  9. desc = xmlrpc.getFaultDescription(xmlrpc.Faults.SHUTDOWN_STATE)
  10. self.assertEqual(desc, 'SHUTDOWN_STATE')
  11. def test_returns_unknown_for_unknown_fault(self):
  12. from supervisor import xmlrpc
  13. desc = xmlrpc.getFaultDescription(999999)
  14. self.assertEqual(desc, 'UNKNOWN')
  15. class RPCErrorTests(unittest.TestCase):
  16. def _getTargetClass(self):
  17. from supervisor.xmlrpc import RPCError
  18. return RPCError
  19. def _makeOne(self, code, extra=None):
  20. return self._getTargetClass()(code, extra)
  21. def test_sets_text_with_fault_name_only(self):
  22. from supervisor import xmlrpc
  23. e = self._makeOne(xmlrpc.Faults.FAILED)
  24. self.assertEqual(e.text, 'FAILED')
  25. def test_sets_text_with_fault_name_and_extra(self):
  26. from supervisor import xmlrpc
  27. e = self._makeOne(xmlrpc.Faults.FAILED, 'oops')
  28. self.assertEqual(e.text, 'FAILED: oops')
  29. def test___str___shows_code_and_text(self):
  30. from supervisor import xmlrpc
  31. e = self._makeOne(xmlrpc.Faults.NO_FILE, '/nonexistent')
  32. self.assertEqual(str(e),
  33. "code=%r, text='NO_FILE: /nonexistent'" % xmlrpc.Faults.NO_FILE
  34. )
  35. class XMLRPCMarshallingTests(unittest.TestCase):
  36. def test_xmlrpc_marshal(self):
  37. import xmlrpclib
  38. from supervisor import xmlrpc
  39. data = xmlrpc.xmlrpc_marshal(1)
  40. self.assertEqual(data, xmlrpclib.dumps((1,), methodresponse=True))
  41. fault = xmlrpclib.Fault(1, 'foo')
  42. data = xmlrpc.xmlrpc_marshal(fault)
  43. self.assertEqual(data, xmlrpclib.dumps(fault))
  44. class XMLRPCHandlerTests(unittest.TestCase):
  45. def _getTargetClass(self):
  46. from supervisor.xmlrpc import supervisor_xmlrpc_handler
  47. return supervisor_xmlrpc_handler
  48. def _makeOne(self, supervisord, subinterfaces):
  49. return self._getTargetClass()(supervisord, subinterfaces)
  50. def test_ctor(self):
  51. supervisor = DummySupervisor()
  52. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  53. handler = self._makeOne(supervisor, subinterfaces)
  54. self.assertEqual(handler.supervisord, supervisor)
  55. from supervisor.xmlrpc import RootRPCInterface
  56. self.assertEqual(handler.rpcinterface.__class__, RootRPCInterface)
  57. def test_match(self):
  58. class DummyRequest2:
  59. def __init__(self, uri):
  60. self.uri = uri
  61. supervisor = DummySupervisor()
  62. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  63. handler = self._makeOne(supervisor, subinterfaces)
  64. self.assertEqual(handler.match(DummyRequest2('/RPC2')), True)
  65. self.assertEqual(handler.match(DummyRequest2('/nope')), False)
  66. def test_continue_request_nosuchmethod(self):
  67. supervisor = DummySupervisor()
  68. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  69. handler = self._makeOne(supervisor, subinterfaces)
  70. import xmlrpclib
  71. data = xmlrpclib.dumps(('a', 'b'), 'supervisor.noSuchMethod')
  72. request = DummyRequest('/what/ever', None, None, None)
  73. handler.continue_request(data, request)
  74. logdata = supervisor.options.logger.data
  75. from supervisor.xmlrpc import loads
  76. if loads:
  77. expected = 2
  78. else:
  79. expected = 3
  80. self.assertEqual(len(logdata), expected)
  81. self.assertEqual(logdata[-2],
  82. u'XML-RPC method called: supervisor.noSuchMethod()')
  83. self.assertEqual(logdata[-1],
  84. (u'XML-RPC method supervisor.noSuchMethod() returned fault: '
  85. '[1] UNKNOWN_METHOD'))
  86. self.assertEqual(len(request.producers), 1)
  87. xml_response = request.producers[0]
  88. self.assertRaises(xmlrpclib.Fault, xmlrpclib.loads, xml_response)
  89. def test_continue_request_methodsuccess(self):
  90. supervisor = DummySupervisor()
  91. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  92. handler = self._makeOne(supervisor, subinterfaces)
  93. import xmlrpclib
  94. data = xmlrpclib.dumps((), 'supervisor.getAPIVersion')
  95. request = DummyRequest('/what/ever', None, None, None)
  96. handler.continue_request(data, request)
  97. logdata = supervisor.options.logger.data
  98. from supervisor.xmlrpc import loads
  99. if loads:
  100. expected = 2
  101. else:
  102. expected = 3
  103. self.assertEqual(len(logdata), expected)
  104. self.assertEqual(logdata[-2],
  105. u'XML-RPC method called: supervisor.getAPIVersion()')
  106. self.assertEqual(logdata[-1],
  107. u'XML-RPC method supervisor.getAPIVersion() returned successfully')
  108. self.assertEqual(len(request.producers), 1)
  109. xml_response = request.producers[0]
  110. response = xmlrpclib.loads(xml_response)
  111. from supervisor.rpcinterface import API_VERSION
  112. self.assertEqual(response[0][0], API_VERSION)
  113. self.assertEqual(request._done, True)
  114. self.assertEqual(request.headers['Content-Type'], 'text/xml')
  115. self.assertEqual(request.headers['Content-Length'], len(xml_response))
  116. def test_continue_request_no_params_in_request(self):
  117. supervisor = DummySupervisor()
  118. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  119. handler = self._makeOne(supervisor, subinterfaces)
  120. data = '<?xml version="1.0" encoding="UTF-8"?>' \
  121. '<methodCall>' \
  122. '<methodName>supervisor.getAPIVersion</methodName>' \
  123. '</methodCall>'
  124. request = DummyRequest('/what/ever', None, None, None)
  125. handler.continue_request(data, request)
  126. logdata = supervisor.options.logger.data
  127. from supervisor.xmlrpc import loads
  128. if loads:
  129. expected = 2
  130. else:
  131. expected = 3
  132. self.assertEqual(len(logdata), expected)
  133. self.assertEqual(logdata[-2],
  134. u'XML-RPC method called: supervisor.getAPIVersion()')
  135. self.assertEqual(logdata[-1],
  136. u'XML-RPC method supervisor.getAPIVersion() returned successfully')
  137. self.assertEqual(len(request.producers), 1)
  138. xml_response = request.producers[0]
  139. import xmlrpclib
  140. response = xmlrpclib.loads(xml_response)
  141. from supervisor.rpcinterface import API_VERSION
  142. self.assertEqual(response[0][0], API_VERSION)
  143. self.assertEqual(request._done, True)
  144. self.assertEqual(request.headers['Content-Type'], 'text/xml')
  145. self.assertEqual(request.headers['Content-Length'], len(xml_response))
  146. def test_continue_request_400_if_method_name_is_empty(self):
  147. supervisor = DummySupervisor()
  148. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  149. handler = self._makeOne(supervisor, subinterfaces)
  150. data = '<?xml version="1.0" encoding="UTF-8"?>' \
  151. '<methodCall><methodName></methodName></methodCall>'
  152. request = DummyRequest('/what/ever', None, None, None)
  153. handler.continue_request(data, request)
  154. logdata = supervisor.options.logger.data
  155. from supervisor.xmlrpc import loads
  156. if loads:
  157. expected = 1
  158. else:
  159. expected = 2
  160. self.assertEqual(len(logdata), expected)
  161. self.assertTrue(logdata[-1].startswith('XML-RPC request data'))
  162. self.assertTrue(repr(data) in logdata[-1])
  163. self.assertTrue(logdata[-1].endswith('is invalid: no method name'))
  164. self.assertEqual(request._error, 400)
  165. def test_continue_request_400_if_loads_raises_not_xml(self):
  166. supervisor = DummySupervisor()
  167. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  168. handler = self._makeOne(supervisor, subinterfaces)
  169. data = 'this is not an xml-rpc request body'
  170. request = DummyRequest('/what/ever', None, None, None)
  171. handler.continue_request(data, request)
  172. logdata = supervisor.options.logger.data
  173. from supervisor.xmlrpc import loads
  174. if loads:
  175. expected = 1
  176. else:
  177. expected = 2
  178. self.assertEqual(len(logdata), expected)
  179. self.assertTrue(logdata[-1].startswith('XML-RPC request data'))
  180. self.assertTrue(repr(data) in logdata[-1])
  181. self.assertTrue(logdata[-1].endswith('is invalid: unmarshallable'))
  182. self.assertEqual(request._error, 400)
  183. def test_continue_request_400_if_loads_raises_weird_xml(self):
  184. supervisor = DummySupervisor()
  185. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  186. handler = self._makeOne(supervisor, subinterfaces)
  187. data = '<methodName></methodName><junk></junk>'
  188. request = DummyRequest('/what/ever', None, None, None)
  189. handler.continue_request(data, request)
  190. logdata = supervisor.options.logger.data
  191. from supervisor.xmlrpc import loads
  192. if loads:
  193. expected = 1
  194. else:
  195. expected = 2
  196. self.assertEqual(len(logdata), expected)
  197. self.assertTrue(logdata[-1].startswith('XML-RPC request data'))
  198. self.assertTrue(repr(data) in logdata[-1])
  199. self.assertTrue(logdata[-1].endswith('is invalid: unmarshallable'))
  200. self.assertEqual(request._error, 400)
  201. def test_continue_request_500_if_rpcinterface_method_call_raises(self):
  202. supervisor = DummySupervisor()
  203. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  204. handler = self._makeOne(supervisor, subinterfaces)
  205. import xmlrpclib
  206. data = xmlrpclib.dumps((), 'supervisor.raiseError')
  207. request = DummyRequest('/what/ever', None, None, None)
  208. handler.continue_request(data, request)
  209. logdata = supervisor.options.logger.data
  210. from supervisor.xmlrpc import loads
  211. if loads:
  212. expected = 2
  213. else:
  214. expected = 3
  215. self.assertEqual(len(logdata), expected)
  216. self.assertEqual(logdata[-2],
  217. u'XML-RPC method called: supervisor.raiseError()')
  218. self.assertTrue("unexpected exception" in logdata[-1])
  219. self.assertTrue(repr(data) in logdata[-1])
  220. self.assertTrue("Traceback" in logdata[-1])
  221. self.assertTrue("ValueError: error" in logdata[-1])
  222. self.assertEqual(len(request.producers), 0)
  223. self.assertEqual(request._error, 500)
  224. def test_continue_request_500_if_xmlrpc_dumps_raises(self):
  225. supervisor = DummySupervisor()
  226. subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
  227. handler = self._makeOne(supervisor, subinterfaces)
  228. import xmlrpclib
  229. data = xmlrpclib.dumps((), 'supervisor.getXmlRpcUnmarshallable')
  230. request = DummyRequest('/what/ever', None, None, None)
  231. handler.continue_request(data, request)
  232. logdata = supervisor.options.logger.data
  233. from supervisor.xmlrpc import loads
  234. if loads:
  235. expected = 3
  236. else:
  237. expected = 4
  238. self.assertEqual(len(logdata), expected)
  239. self.assertEqual(logdata[-3],
  240. 'XML-RPC method called: supervisor.getXmlRpcUnmarshallable()')
  241. self.assertEqual(logdata[-2],
  242. 'XML-RPC method supervisor.getXmlRpcUnmarshallable() '
  243. 'returned successfully')
  244. self.assertTrue("unexpected exception" in logdata[-1])
  245. self.assertTrue(repr(data) in logdata[-1])
  246. self.assertTrue("Traceback" in logdata[-1])
  247. self.assertTrue("TypeError: cannot marshal" in logdata[-1])
  248. self.assertEqual(request._error, 500)
  249. class TraverseTests(unittest.TestCase):
  250. def test_underscore(self):
  251. from supervisor import xmlrpc
  252. self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, None, '_', None)
  253. def test_notfound(self):
  254. from supervisor import xmlrpc
  255. self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, None, 'foo', None)
  256. def test_badparams(self):
  257. from supervisor import xmlrpc
  258. self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, self,
  259. 'test_badparams', (1, 2, 3))
  260. def test_success(self):
  261. from supervisor import xmlrpc
  262. L = []
  263. class Dummy:
  264. def foo(self, a):
  265. L.append(a)
  266. dummy = Dummy()
  267. xmlrpc.traverse(dummy, 'foo', [1])
  268. self.assertEqual(L, [1])
  269. class SupervisorTransportTests(unittest.TestCase):
  270. def _getTargetClass(self):
  271. from supervisor.xmlrpc import SupervisorTransport
  272. return SupervisorTransport
  273. def _makeOne(self, *arg, **kw):
  274. return self._getTargetClass()(*arg, **kw)
  275. def test_ctor_unix(self):
  276. from supervisor import xmlrpc
  277. transport = self._makeOne('user', 'pass', 'unix:///foo/bar')
  278. conn = transport._get_connection()
  279. self.assertTrue(isinstance(conn, xmlrpc.UnixStreamHTTPConnection))
  280. self.assertEqual(conn.host, 'localhost')
  281. self.assertEqual(conn.socketfile, '/foo/bar')
  282. def test__get_connection_http_9001(self):
  283. import httplib
  284. transport = self._makeOne('user', 'pass', 'http://127.0.0.1:9001/')
  285. conn = transport._get_connection()
  286. self.assertTrue(isinstance(conn, httplib.HTTPConnection))
  287. self.assertEqual(conn.host, '127.0.0.1')
  288. self.assertEqual(conn.port, 9001)
  289. def test__get_connection_http_80(self):
  290. import httplib
  291. transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
  292. conn = transport._get_connection()
  293. self.assertTrue(isinstance(conn, httplib.HTTPConnection))
  294. self.assertEqual(conn.host, '127.0.0.1')
  295. self.assertEqual(conn.port, 80)
  296. def test_request_non_200_response(self):
  297. import xmlrpclib
  298. transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
  299. dummy_conn = DummyConnection(400, '')
  300. def getconn():
  301. return dummy_conn
  302. transport._get_connection = getconn
  303. self.assertRaises(xmlrpclib.ProtocolError,
  304. transport.request, 'localhost', '/', '')
  305. self.assertEqual(transport.connection, None)
  306. self.assertEqual(dummy_conn.closed, True)
  307. def test_request_400_response(self):
  308. import xmlrpclib
  309. transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
  310. dummy_conn = DummyConnection(400, '')
  311. def getconn():
  312. return dummy_conn
  313. transport._get_connection = getconn
  314. self.assertRaises(xmlrpclib.ProtocolError,
  315. transport.request, 'localhost', '/', '')
  316. self.assertEqual(transport.connection, None)
  317. self.assertEqual(dummy_conn.closed, True)
  318. self.assertEqual(dummy_conn.requestargs[0], 'POST')
  319. self.assertEqual(dummy_conn.requestargs[1], '/')
  320. self.assertEqual(dummy_conn.requestargs[2], '')
  321. self.assertEqual(dummy_conn.requestargs[3]['Content-Length'], '0')
  322. self.assertEqual(dummy_conn.requestargs[3]['Content-Type'], 'text/xml')
  323. self.assertEqual(dummy_conn.requestargs[3]['Authorization'],
  324. 'Basic dXNlcjpwYXNz')
  325. self.assertEqual(dummy_conn.requestargs[3]['Accept'], 'text/xml')
  326. def test_request_200_response(self):
  327. transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
  328. response = """<?xml version="1.0"?>
  329. <methodResponse>
  330. <params>
  331. <param>
  332. <value><string>South Dakota</string></value>
  333. </param>
  334. </params>
  335. </methodResponse>"""
  336. dummy_conn = DummyConnection(200, response)
  337. def getconn():
  338. return dummy_conn
  339. transport._get_connection = getconn
  340. result = transport.request('localhost', '/', '')
  341. self.assertEqual(transport.connection, dummy_conn)
  342. self.assertEqual(dummy_conn.closed, False)
  343. self.assertEqual(dummy_conn.requestargs[0], 'POST')
  344. self.assertEqual(dummy_conn.requestargs[1], '/')
  345. self.assertEqual(dummy_conn.requestargs[2], '')
  346. self.assertEqual(dummy_conn.requestargs[3]['Content-Length'], '0')
  347. self.assertEqual(dummy_conn.requestargs[3]['Content-Type'], 'text/xml')
  348. self.assertEqual(dummy_conn.requestargs[3]['Authorization'],
  349. 'Basic dXNlcjpwYXNz')
  350. self.assertEqual(dummy_conn.requestargs[3]['Accept'], 'text/xml')
  351. self.assertEqual(result, ('South Dakota',))
  352. def test_works_with_py25(self):
  353. instance = self._makeOne('username', 'password', 'http://127.0.0.1')
  354. # the test is just to insure that this method can be called; failure
  355. # would be an AttributeError for _use_datetime under Python 2.5
  356. parser, unmarshaller = instance.getparser() # this uses _use_datetime
  357. class IterparseLoadsTests(unittest.TestCase):
  358. def test_iterparse_loads_methodcall(self):
  359. s = """<?xml version="1.0"?>
  360. <methodCall>
  361. <methodName>examples.getStateName</methodName>
  362. <params>
  363. <param>
  364. <value><i4>41</i4></value>
  365. </param>
  366. <param>
  367. <value><string>foo</string></value>
  368. </param>
  369. <param>
  370. <value><string></string></value>
  371. </param>
  372. <param>
  373. <!-- xml-rpc spec allows strings without <string> tag -->
  374. <value>bar</value>
  375. </param>
  376. <param>
  377. <value></value>
  378. </param>
  379. <param>
  380. <value><boolean>1</boolean></value>
  381. </param>
  382. <param>
  383. <value><double>-12.214</double></value>
  384. </param>
  385. <param>
  386. <value>
  387. <dateTime.iso8601>19980717T14:08:55</dateTime.iso8601>
  388. </value>
  389. </param>
  390. <param>
  391. <value><base64>eW91IGNhbid0IHJlYWQgdGhpcyE=</base64></value>
  392. </param>
  393. <param>
  394. <struct>
  395. <member><name>j</name><value><i4>5</i4></value></member>
  396. <member><name>k</name><value>abc</value></member>
  397. </struct>
  398. </param>
  399. <param>
  400. <array>
  401. <data>
  402. <value><i4>12</i4></value>
  403. <value><string>abc</string></value>
  404. <value>def</value>
  405. <value><i4>34</i4></value>
  406. </data>
  407. </array>
  408. </param>
  409. <param>
  410. <struct>
  411. <member>
  412. <name>k</name>
  413. <value><array><data>
  414. <value><i4>1</i4></value>
  415. <struct></struct>
  416. </data></array></value>
  417. </member>
  418. </struct>
  419. </param>
  420. </params>
  421. </methodCall>
  422. """
  423. from supervisor.xmlrpc import loads
  424. if loads is None:
  425. return # no cElementTree
  426. result = loads(s)
  427. params, method = result
  428. import datetime
  429. self.assertEqual(method, 'examples.getStateName')
  430. self.assertEqual(params[0], 41)
  431. self.assertEqual(params[1], 'foo')
  432. self.assertEqual(params[2], '')
  433. self.assertEqual(params[3], 'bar')
  434. self.assertEqual(params[4], '')
  435. self.assertEqual(params[5], True)
  436. self.assertEqual(params[6], -12.214)
  437. self.assertEqual(params[7], datetime.datetime(1998, 7, 17, 14, 8, 55))
  438. self.assertEqual(params[8], "you can't read this!")
  439. self.assertEqual(params[9], {'j': 5, 'k': 'abc'})
  440. self.assertEqual(params[10], [12, 'abc', 'def', 34])
  441. self.assertEqual(params[11], {'k': [1, {}]})
  442. class TestDeferredXMLRPCResponse(unittest.TestCase):
  443. def _getTargetClass(self):
  444. from supervisor.xmlrpc import DeferredXMLRPCResponse
  445. return DeferredXMLRPCResponse
  446. def _makeOne(self, request=None, callback=None):
  447. if request is None:
  448. request = DummyRequest(None, None, None, None, None)
  449. if callback is None:
  450. callback = Dummy()
  451. callback.delay = 1
  452. return self._getTargetClass()(request, callback)
  453. def test_ctor(self):
  454. callback = Dummy()
  455. callback.delay = 1
  456. inst = self._makeOne(request='request', callback=callback)
  457. self.assertEqual(inst.callback, callback)
  458. self.assertEqual(inst.delay, 1.0)
  459. self.assertEqual(inst.request, 'request')
  460. self.assertEqual(inst.finished, False)
  461. def test_more_finished(self):
  462. inst = self._makeOne()
  463. inst.finished = True
  464. result = inst.more()
  465. self.assertEqual(result, '')
  466. def test_more_callback_returns_not_done_yet(self):
  467. from supervisor.http import NOT_DONE_YET
  468. def callback():
  469. return NOT_DONE_YET
  470. callback.delay = 1
  471. inst = self._makeOne(callback=callback)
  472. self.assertEqual(inst.more(), NOT_DONE_YET)
  473. def test_more_callback_raises_RPCError(self):
  474. from supervisor.xmlrpc import RPCError, Faults
  475. def callback():
  476. raise RPCError(Faults.UNKNOWN_METHOD)
  477. callback.delay = 1
  478. inst = self._makeOne(callback=callback)
  479. self.assertEqual(inst.more(), None)
  480. self.assertEqual(len(inst.request.producers), 1)
  481. self.assertTrue('UNKNOWN_METHOD' in inst.request.producers[0])
  482. self.assertTrue(inst.finished)
  483. def test_more_callback_returns_value(self):
  484. def callback():
  485. return 'abc'
  486. callback.delay = 1
  487. inst = self._makeOne(callback=callback)
  488. self.assertEqual(inst.more(), None)
  489. self.assertEqual(len(inst.request.producers), 1)
  490. self.assertTrue('abc' in inst.request.producers[0])
  491. self.assertTrue(inst.finished)
  492. def test_more_callback_raises_unexpected_exception(self):
  493. def callback():
  494. raise ValueError('foo')
  495. callback.delay = 1
  496. inst = self._makeOne(callback=callback)
  497. self.assertEqual(inst.more(), None)
  498. self.assertEqual(inst.request._error, 500)
  499. self.assertTrue(inst.finished)
  500. logged = inst.request.channel.server.logger.logged
  501. self.assertEqual(len(logged), 1)
  502. src, msg = logged[0]
  503. self.assertEqual(src, 'XML-RPC response callback error')
  504. self.assertTrue("Traceback" in msg)
  505. class TestSystemNamespaceRPCInterface(unittest.TestCase):
  506. def _makeOne(self, namespaces=()):
  507. from supervisor.xmlrpc import SystemNamespaceRPCInterface
  508. return SystemNamespaceRPCInterface(namespaces)
  509. def test_listMethods_gardenpath(self):
  510. inst = self._makeOne()
  511. result = inst.listMethods()
  512. self.assertEqual(
  513. result,
  514. ['system.listMethods',
  515. 'system.methodHelp',
  516. 'system.methodSignature',
  517. 'system.multicall',
  518. ]
  519. )
  520. def test_listMethods_omits_underscore_attrs(self):
  521. class DummyNamespace(object):
  522. def foo(self): pass
  523. def _bar(self): pass
  524. ns1 = DummyNamespace()
  525. inst = self._makeOne([('ns1', ns1)])
  526. result = inst.listMethods()
  527. self.assertEqual(
  528. result,
  529. ['ns1.foo',
  530. 'system.listMethods',
  531. 'system.methodHelp',
  532. 'system.methodSignature',
  533. 'system.multicall'
  534. ]
  535. )
  536. def test_methodHelp_known_method(self):
  537. inst = self._makeOne()
  538. result = inst.methodHelp('system.listMethods')
  539. self.assertTrue('array' in result)
  540. def test_methodHelp_unknown_method(self):
  541. from supervisor.xmlrpc import RPCError
  542. inst = self._makeOne()
  543. self.assertRaises(RPCError, inst.methodHelp, 'wont.be.found')
  544. def test_methodSignature_known_method(self):
  545. inst = self._makeOne()
  546. result = inst.methodSignature('system.methodSignature')
  547. self.assertEqual(result, ['array', 'string'])
  548. def test_methodSignature_unknown_method(self):
  549. from supervisor.xmlrpc import RPCError
  550. inst = self._makeOne()
  551. self.assertRaises(RPCError, inst.methodSignature, 'wont.be.found')
  552. def test_methodSignature_with_bad_sig(self):
  553. from supervisor.xmlrpc import RPCError
  554. class DummyNamespace(object):
  555. def foo(self):
  556. """ @param string name The thing"""
  557. ns1 = DummyNamespace()
  558. inst = self._makeOne([('ns1', ns1)])
  559. self.assertRaises(RPCError, inst.methodSignature, 'ns1.foo')
  560. def test_multicall_faults_for_recursion(self):
  561. from supervisor.xmlrpc import Faults
  562. inst = self._makeOne()
  563. calls = [{'methodName':'system.multicall'}]
  564. results = inst.multicall(calls)
  565. self.assertEqual(
  566. results,
  567. [{'faultCode': Faults.INCORRECT_PARAMETERS,
  568. 'faultString': ('INCORRECT_PARAMETERS: Recursive '
  569. 'system.multicall forbidden')}]
  570. )
  571. def test_multicall_faults_for_missing_methodName(self):
  572. from supervisor.xmlrpc import Faults
  573. inst = self._makeOne()
  574. calls = [{}]
  575. results = inst.multicall(calls)
  576. self.assertEqual(
  577. results,
  578. [{'faultCode': Faults.INCORRECT_PARAMETERS,
  579. 'faultString': 'INCORRECT_PARAMETERS: No methodName'}]
  580. )
  581. def test_multicall_faults_for_methodName_bad_namespace(self):
  582. from supervisor.xmlrpc import Faults
  583. inst = self._makeOne()
  584. calls = [{'methodName': 'bad.stopProcess'}]
  585. results = inst.multicall(calls)
  586. self.assertEqual(
  587. results,
  588. [{'faultCode': Faults.UNKNOWN_METHOD,
  589. 'faultString': 'UNKNOWN_METHOD'}]
  590. )
  591. def test_multicall_faults_for_methodName_good_ns_bad_method(self):
  592. from supervisor.xmlrpc import Faults
  593. class DummyNamespace(object):
  594. pass
  595. ns1 = DummyNamespace()
  596. inst = self._makeOne([('ns1', ns1)])
  597. calls = [{'methodName': 'ns1.bad'}]
  598. results = inst.multicall(calls)
  599. self.assertEqual(
  600. results,
  601. [{'faultCode': Faults.UNKNOWN_METHOD,
  602. 'faultString': 'UNKNOWN_METHOD'}]
  603. )
  604. def test_multicall_returns_empty_results_for_empty_calls(self):
  605. inst = self._makeOne()
  606. calls = []
  607. results = inst.multicall(calls)
  608. self.assertEqual(results, [])
  609. def test_multicall_performs_noncallback_functions_serially(self):
  610. class DummyNamespace(object):
  611. def say(self, name):
  612. """ @param string name Process name"""
  613. return name
  614. ns1 = DummyNamespace()
  615. inst = self._makeOne([('ns1', ns1)])
  616. calls = [
  617. {'methodName': 'ns1.say', 'params': ['Alvin']},
  618. {'methodName': 'ns1.say', 'params': ['Simon']},
  619. {'methodName': 'ns1.say', 'params': ['Theodore']}
  620. ]
  621. results = inst.multicall(calls)
  622. self.assertEqual(results, ['Alvin', 'Simon', 'Theodore'])
  623. def test_multicall_catches_noncallback_exceptions(self):
  624. import errno
  625. from supervisor.xmlrpc import RPCError, Faults
  626. class DummyNamespace(object):
  627. def bad_name(self):
  628. raise RPCError(Faults.BAD_NAME, 'foo')
  629. def os_error(self):
  630. raise OSError(errno.ENOENT)
  631. ns1 = DummyNamespace()
  632. inst = self._makeOne([('ns1', ns1)])
  633. calls = [{'methodName': 'ns1.bad_name'}, {'methodName': 'ns1.os_error'}]
  634. results = inst.multicall(calls)
  635. bad_name = {'faultCode': Faults.BAD_NAME,
  636. 'faultString': 'BAD_NAME: foo'}
  637. os_error = {'faultCode': Faults.FAILED,
  638. 'faultString': "FAILED: %s:2" % OSError}
  639. self.assertEqual(results, [bad_name, os_error])
  640. def test_multicall_catches_callback_exceptions(self):
  641. import errno
  642. from supervisor.xmlrpc import RPCError, Faults
  643. from supervisor.http import NOT_DONE_YET
  644. class DummyNamespace(object):
  645. def bad_name(self):
  646. def inner():
  647. raise RPCError(Faults.BAD_NAME, 'foo')
  648. return inner
  649. def os_error(self):
  650. def inner():
  651. raise OSError(errno.ENOENT)
  652. return inner
  653. ns1 = DummyNamespace()
  654. inst = self._makeOne([('ns1', ns1)])
  655. calls = [{'methodName': 'ns1.bad_name'}, {'methodName': 'ns1.os_error'}]
  656. callback = inst.multicall(calls)
  657. results = NOT_DONE_YET
  658. while results is NOT_DONE_YET:
  659. results = callback()
  660. bad_name = {'faultCode': Faults.BAD_NAME,
  661. 'faultString': 'BAD_NAME: foo'}
  662. os_error = {'faultCode': Faults.FAILED,
  663. 'faultString': "FAILED: %s:2" % OSError}
  664. self.assertEqual(results, [bad_name, os_error])
  665. def test_multicall_performs_callback_functions_serially(self):
  666. from supervisor.http import NOT_DONE_YET
  667. class DummyNamespace(object):
  668. def __init__(self):
  669. self.stop_results = [NOT_DONE_YET, NOT_DONE_YET,
  670. NOT_DONE_YET, 'stop result']
  671. self.start_results = ['start result']
  672. def stopProcess(self, name):
  673. def inner():
  674. result = self.stop_results.pop(0)
  675. if result is not NOT_DONE_YET:
  676. self.stopped = True
  677. return result
  678. return inner
  679. def startProcess(self, name):
  680. def inner():
  681. if not self.stopped:
  682. raise Exception("This should not raise")
  683. return self.start_results.pop(0)
  684. return inner
  685. ns1 = DummyNamespace()
  686. inst = self._makeOne([('ns1', ns1)])
  687. calls = [{'methodName': 'ns1.stopProcess',
  688. 'params': {'name': 'foo'}},
  689. {'methodName': 'ns1.startProcess',
  690. 'params': {'name': 'foo'}}]
  691. callback = inst.multicall(calls)
  692. results = NOT_DONE_YET
  693. while results is NOT_DONE_YET:
  694. results = callback()
  695. self.assertEqual(results, ['stop result', 'start result'])
  696. class Test_gettags(unittest.TestCase):
  697. def _callFUT(self, comment):
  698. from supervisor.xmlrpc import gettags
  699. return gettags(comment)
  700. def test_one_atpart(self):
  701. lines = '@foo'
  702. result = self._callFUT(lines)
  703. self.assertEqual(
  704. result,
  705. [(0, None, None, None, ''), (0, 'foo', '', '', '')]
  706. )
  707. def test_two_atparts(self):
  708. lines = '@foo array'
  709. result = self._callFUT(lines)
  710. self.assertEqual(
  711. result,
  712. [(0, None, None, None, ''), (0, 'foo', 'array', '', '')]
  713. )
  714. def test_three_atparts(self):
  715. lines = '@foo array name'
  716. result = self._callFUT(lines)
  717. self.assertEqual(
  718. result,
  719. [(0, None, None, None, ''), (0, 'foo', 'array', 'name', '')]
  720. )
  721. def test_four_atparts(self):
  722. lines = '@foo array name text'
  723. result = self._callFUT(lines)
  724. self.assertEqual(
  725. result,
  726. [(0, None, None, None, ''), (0, 'foo', 'array', 'name', 'text')]
  727. )
  728. class Test_capped_int(unittest.TestCase):
  729. def _callFUT(self, value):
  730. from supervisor.xmlrpc import capped_int
  731. return capped_int(value)
  732. def test_converts_value_to_integer(self):
  733. self.assertEqual(self._callFUT('42'), 42)
  734. def test_caps_value_below_minint(self):
  735. import xmlrpclib
  736. self.assertEqual(self._callFUT(xmlrpclib.MININT - 1), xmlrpclib.MININT)
  737. def test_caps_value_above_maxint(self):
  738. import xmlrpclib
  739. self.assertEqual(self._callFUT(xmlrpclib.MAXINT + 1), xmlrpclib.MAXINT)
  740. class DummyResponse:
  741. def __init__(self, status=200, body='', reason='reason'):
  742. self.status = status
  743. self.body = body
  744. self.reason = reason
  745. def read(self):
  746. return self.body
  747. class Dummy(object):
  748. pass
  749. class DummyConnection:
  750. closed = False
  751. def __init__(self, status=200, body='', reason='reason'):
  752. self.response = DummyResponse(status, body, reason)
  753. def getresponse(self):
  754. return self.response
  755. def request(self, *arg, **kw):
  756. self.requestargs = arg
  757. self.requestkw = kw
  758. def close(self):
  759. self.closed = True
  760. def test_suite():
  761. return unittest.findTestCases(sys.modules[__name__])
  762. if __name__ == '__main__':
  763. unittest.main(defaultTest='test_suite')