|
@@ -480,6 +480,253 @@ class TestDeferredXMLRPCResponse(unittest.TestCase):
|
|
|
self.assertEqual(src, 'XML-RPC response callback error')
|
|
|
self.assertTrue("Traceback" in msg)
|
|
|
|
|
|
+class TestSystemNamespaceRPCInterface(unittest.TestCase):
|
|
|
+ def _makeOne(self, namespaces=()):
|
|
|
+ from supervisor.xmlrpc import SystemNamespaceRPCInterface
|
|
|
+ return SystemNamespaceRPCInterface(namespaces)
|
|
|
+
|
|
|
+ def test_listMethods_gardenpath(self):
|
|
|
+ inst = self._makeOne()
|
|
|
+ result = inst.listMethods()
|
|
|
+ self.assertEqual(
|
|
|
+ result,
|
|
|
+ ['system.listMethods',
|
|
|
+ 'system.methodHelp',
|
|
|
+ 'system.methodSignature',
|
|
|
+ 'system.multicall',
|
|
|
+ ]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_listMethods_omits_underscore_attrs(self):
|
|
|
+ class DummyNamespace(object):
|
|
|
+ def foo(self): pass
|
|
|
+ def _bar(self): pass
|
|
|
+ ns1 = DummyNamespace()
|
|
|
+ inst = self._makeOne([('ns1', ns1)])
|
|
|
+ result = inst.listMethods()
|
|
|
+ self.assertEqual(
|
|
|
+ result,
|
|
|
+ ['ns1.foo',
|
|
|
+ 'system.listMethods',
|
|
|
+ 'system.methodHelp',
|
|
|
+ 'system.methodSignature',
|
|
|
+ 'system.multicall'
|
|
|
+ ]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_methodHelp_known_method(self):
|
|
|
+ inst = self._makeOne()
|
|
|
+ result = inst.methodHelp('system.listMethods')
|
|
|
+ self.assertTrue('array' in result)
|
|
|
+
|
|
|
+ def test_methodHelp_unknown_method(self):
|
|
|
+ from supervisor.xmlrpc import RPCError
|
|
|
+ inst = self._makeOne()
|
|
|
+ self.assertRaises(RPCError, inst.methodHelp, 'wont.be.found')
|
|
|
+
|
|
|
+ def test_methodSignature_known_method(self):
|
|
|
+ inst = self._makeOne()
|
|
|
+ result = inst.methodSignature('system.methodSignature')
|
|
|
+ self.assertEqual(result, ['array', 'string'])
|
|
|
+
|
|
|
+ def test_methodSignature_unknown_method(self):
|
|
|
+ from supervisor.xmlrpc import RPCError
|
|
|
+ inst = self._makeOne()
|
|
|
+ self.assertRaises(RPCError, inst.methodSignature, 'wont.be.found')
|
|
|
+
|
|
|
+ def test_methodSignature_with_bad_sig(self):
|
|
|
+ from supervisor.xmlrpc import RPCError
|
|
|
+ class DummyNamespace(object):
|
|
|
+ def foo(self):
|
|
|
+ """ @param string name The thing"""
|
|
|
+ ns1 = DummyNamespace()
|
|
|
+ inst = self._makeOne([('ns1', ns1)])
|
|
|
+ self.assertRaises(RPCError, inst.methodSignature, 'ns1.foo')
|
|
|
+
|
|
|
+ def test_multicall_faults_for_recursion(self):
|
|
|
+ from supervisor.xmlrpc import Faults
|
|
|
+ inst = self._makeOne()
|
|
|
+ calls = [{'methodName':'system.multicall'}]
|
|
|
+ results = inst.multicall(calls)
|
|
|
+ self.assertEqual(
|
|
|
+ results,
|
|
|
+ [{'faultCode': Faults.INCORRECT_PARAMETERS,
|
|
|
+ 'faultString': ('INCORRECT_PARAMETERS: Recursive '
|
|
|
+ 'system.multicall forbidden')}]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_multicall_faults_for_missing_methodName(self):
|
|
|
+ from supervisor.xmlrpc import Faults
|
|
|
+ inst = self._makeOne()
|
|
|
+ calls = [{}]
|
|
|
+ results = inst.multicall(calls)
|
|
|
+ self.assertEqual(
|
|
|
+ results,
|
|
|
+ [{'faultCode': Faults.INCORRECT_PARAMETERS,
|
|
|
+ 'faultString': 'INCORRECT_PARAMETERS: No methodName'}]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_multicall_faults_for_methodName_bad_namespace(self):
|
|
|
+ from supervisor.xmlrpc import Faults
|
|
|
+ inst = self._makeOne()
|
|
|
+ calls = [{'methodName': 'bad.stopProcess'}]
|
|
|
+ results = inst.multicall(calls)
|
|
|
+ self.assertEqual(
|
|
|
+ results,
|
|
|
+ [{'faultCode': Faults.UNKNOWN_METHOD,
|
|
|
+ 'faultString': 'UNKNOWN_METHOD'}]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_multicall_faults_for_methodName_good_ns_bad_method(self):
|
|
|
+ from supervisor.xmlrpc import Faults
|
|
|
+ class DummyNamespace(object):
|
|
|
+ pass
|
|
|
+ ns1 = DummyNamespace()
|
|
|
+ inst = self._makeOne([('ns1', ns1)])
|
|
|
+ calls = [{'methodName': 'ns1.bad'}]
|
|
|
+ results = inst.multicall(calls)
|
|
|
+ self.assertEqual(
|
|
|
+ results,
|
|
|
+ [{'faultCode': Faults.UNKNOWN_METHOD,
|
|
|
+ 'faultString': 'UNKNOWN_METHOD'}]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_multicall_returns_empty_results_for_empty_calls(self):
|
|
|
+ inst = self._makeOne()
|
|
|
+ calls = []
|
|
|
+ results = inst.multicall(calls)
|
|
|
+ self.assertEqual(results, [])
|
|
|
+
|
|
|
+ def test_multicall_performs_noncallback_functions_serially(self):
|
|
|
+ class DummyNamespace(object):
|
|
|
+ def say(self, name):
|
|
|
+ """ @param string name Process name"""
|
|
|
+ return name
|
|
|
+ ns1 = DummyNamespace()
|
|
|
+ inst = self._makeOne([('ns1', ns1)])
|
|
|
+ calls = [
|
|
|
+ {'methodName': 'ns1.say', 'params': ['Alvin']},
|
|
|
+ {'methodName': 'ns1.say', 'params': ['Simon']},
|
|
|
+ {'methodName': 'ns1.say', 'params': ['Theodore']}
|
|
|
+ ]
|
|
|
+ results = inst.multicall(calls)
|
|
|
+ self.assertEqual(results, ['Alvin', 'Simon', 'Theodore'])
|
|
|
+
|
|
|
+ def test_multicall_catches_noncallback_exceptions(self):
|
|
|
+ import errno
|
|
|
+ from supervisor.xmlrpc import RPCError, Faults
|
|
|
+ class DummyNamespace(object):
|
|
|
+ def bad_name(self):
|
|
|
+ raise RPCError(Faults.BAD_NAME, 'foo')
|
|
|
+ def os_error(self):
|
|
|
+ raise OSError(errno.ENOENT)
|
|
|
+ ns1 = DummyNamespace()
|
|
|
+ inst = self._makeOne([('ns1', ns1)])
|
|
|
+ calls = [{'methodName': 'ns1.bad_name'}, {'methodName': 'ns1.os_error'}]
|
|
|
+ results = inst.multicall(calls)
|
|
|
+
|
|
|
+ bad_name = {'faultCode': Faults.BAD_NAME,
|
|
|
+ 'faultString': 'BAD_NAME: foo'}
|
|
|
+ os_error = {'faultCode': Faults.FAILED,
|
|
|
+ 'faultString': "FAILED: %s:2" % OSError}
|
|
|
+ self.assertEqual(results, [bad_name, os_error])
|
|
|
+
|
|
|
+ def test_multicall_catches_callback_exceptions(self):
|
|
|
+ import errno
|
|
|
+ from supervisor.xmlrpc import RPCError, Faults
|
|
|
+ from supervisor.http import NOT_DONE_YET
|
|
|
+ class DummyNamespace(object):
|
|
|
+ def bad_name(self):
|
|
|
+ def inner():
|
|
|
+ raise RPCError(Faults.BAD_NAME, 'foo')
|
|
|
+ return inner
|
|
|
+ def os_error(self):
|
|
|
+ def inner():
|
|
|
+ raise OSError(errno.ENOENT)
|
|
|
+ return inner
|
|
|
+ ns1 = DummyNamespace()
|
|
|
+ inst = self._makeOne([('ns1', ns1)])
|
|
|
+ calls = [{'methodName': 'ns1.bad_name'}, {'methodName': 'ns1.os_error'}]
|
|
|
+ callback = inst.multicall(calls)
|
|
|
+ results = NOT_DONE_YET
|
|
|
+ while results is NOT_DONE_YET:
|
|
|
+ results = callback()
|
|
|
+
|
|
|
+ bad_name = {'faultCode': Faults.BAD_NAME,
|
|
|
+ 'faultString': 'BAD_NAME: foo'}
|
|
|
+ os_error = {'faultCode': Faults.FAILED,
|
|
|
+ 'faultString': "FAILED: %s:2" % OSError}
|
|
|
+ self.assertEqual(results, [bad_name, os_error])
|
|
|
+
|
|
|
+ def test_multicall_performs_callback_functions_serially(self):
|
|
|
+ from supervisor.http import NOT_DONE_YET
|
|
|
+ class DummyNamespace(object):
|
|
|
+ def __init__(self):
|
|
|
+ self.stop_results = [NOT_DONE_YET, NOT_DONE_YET,
|
|
|
+ NOT_DONE_YET, 'stop result']
|
|
|
+ self.start_results = ['start result']
|
|
|
+ def stopProcess(self, name):
|
|
|
+ def inner():
|
|
|
+ result = self.stop_results.pop(0)
|
|
|
+ if result is not NOT_DONE_YET:
|
|
|
+ self.stopped = True
|
|
|
+ return result
|
|
|
+ return inner
|
|
|
+ def startProcess(self, name):
|
|
|
+ def inner():
|
|
|
+ if not self.stopped:
|
|
|
+ raise Exception("This should not raise")
|
|
|
+ return self.start_results.pop(0)
|
|
|
+ return inner
|
|
|
+ ns1 = DummyNamespace()
|
|
|
+ inst = self._makeOne([('ns1', ns1)])
|
|
|
+ calls = [{'methodName': 'ns1.stopProcess',
|
|
|
+ 'params': {'name': 'foo'}},
|
|
|
+ {'methodName': 'ns1.startProcess',
|
|
|
+ 'params': {'name': 'foo'}}]
|
|
|
+ callback = inst.multicall(calls)
|
|
|
+ results = NOT_DONE_YET
|
|
|
+ while results is NOT_DONE_YET:
|
|
|
+ results = callback()
|
|
|
+ self.assertEqual(results, ['stop result', 'start result'])
|
|
|
+
|
|
|
+class Test_gettags(unittest.TestCase):
|
|
|
+ def _callFUT(self, comment):
|
|
|
+ from supervisor.xmlrpc import gettags
|
|
|
+ return gettags(comment)
|
|
|
+
|
|
|
+ def test_one_atpart(self):
|
|
|
+ lines = '@foo'
|
|
|
+ result = self._callFUT(lines)
|
|
|
+ self.assertEqual(
|
|
|
+ result,
|
|
|
+ [(0, None, None, None, ''), (0, 'foo', '', '', '')]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_two_atparts(self):
|
|
|
+ lines = '@foo array'
|
|
|
+ result = self._callFUT(lines)
|
|
|
+ self.assertEqual(
|
|
|
+ result,
|
|
|
+ [(0, None, None, None, ''), (0, 'foo', 'array', '', '')]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_three_atparts(self):
|
|
|
+ lines = '@foo array name'
|
|
|
+ result = self._callFUT(lines)
|
|
|
+ self.assertEqual(
|
|
|
+ result,
|
|
|
+ [(0, None, None, None, ''), (0, 'foo', 'array', 'name', '')]
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_four_atparts(self):
|
|
|
+ lines = '@foo array name text'
|
|
|
+ result = self._callFUT(lines)
|
|
|
+ self.assertEqual(
|
|
|
+ result,
|
|
|
+ [(0, None, None, None, ''), (0, 'foo', 'array', 'name', 'text')]
|
|
|
+ )
|
|
|
+
|
|
|
class DummyResponse:
|
|
|
def __init__(self, status=200, body='', reason='reason'):
|
|
|
self.status = status
|