test_application.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.application} and its interaction with
  5. L{twisted.persisted.sob}.
  6. """
  7. from __future__ import absolute_import, division
  8. import copy
  9. import os
  10. import pickle
  11. try:
  12. import asyncio
  13. except ImportError:
  14. asyncio = None
  15. from twisted.application import service, internet, app, reactors
  16. from twisted.internet import interfaces, defer, protocol, reactor
  17. from twisted.persisted import sob
  18. from twisted.plugins import twisted_reactors
  19. from twisted.protocols import wire, basic
  20. from twisted.python import usage
  21. from twisted.python.compat import NativeStringIO
  22. from twisted.python.runtime import platformType
  23. from twisted.python.test.modules_helpers import TwistedModulesMixin
  24. from twisted.test.proto_helpers import MemoryReactor
  25. from twisted.trial import unittest
  26. class Dummy:
  27. processName=None
  28. class ServiceTests(unittest.TestCase):
  29. def testName(self):
  30. s = service.Service()
  31. s.setName("hello")
  32. self.assertEqual(s.name, "hello")
  33. def testParent(self):
  34. s = service.Service()
  35. p = service.MultiService()
  36. s.setServiceParent(p)
  37. self.assertEqual(list(p), [s])
  38. self.assertEqual(s.parent, p)
  39. def testApplicationAsParent(self):
  40. s = service.Service()
  41. p = service.Application("")
  42. s.setServiceParent(p)
  43. self.assertEqual(list(service.IServiceCollection(p)), [s])
  44. self.assertEqual(s.parent, service.IServiceCollection(p))
  45. def testNamedChild(self):
  46. s = service.Service()
  47. p = service.MultiService()
  48. s.setName("hello")
  49. s.setServiceParent(p)
  50. self.assertEqual(list(p), [s])
  51. self.assertEqual(s.parent, p)
  52. self.assertEqual(p.getServiceNamed("hello"), s)
  53. def testDoublyNamedChild(self):
  54. s = service.Service()
  55. p = service.MultiService()
  56. s.setName("hello")
  57. s.setServiceParent(p)
  58. self.assertRaises(RuntimeError, s.setName, "lala")
  59. def testDuplicateNamedChild(self):
  60. s = service.Service()
  61. p = service.MultiService()
  62. s.setName("hello")
  63. s.setServiceParent(p)
  64. s = service.Service()
  65. s.setName("hello")
  66. self.assertRaises(RuntimeError, s.setServiceParent, p)
  67. def testDisowning(self):
  68. s = service.Service()
  69. p = service.MultiService()
  70. s.setServiceParent(p)
  71. self.assertEqual(list(p), [s])
  72. self.assertEqual(s.parent, p)
  73. s.disownServiceParent()
  74. self.assertEqual(list(p), [])
  75. self.assertIsNone(s.parent)
  76. def testRunning(self):
  77. s = service.Service()
  78. self.assertFalse(s.running)
  79. s.startService()
  80. self.assertTrue(s.running)
  81. s.stopService()
  82. self.assertFalse(s.running)
  83. def testRunningChildren1(self):
  84. s = service.Service()
  85. p = service.MultiService()
  86. s.setServiceParent(p)
  87. self.assertFalse(s.running)
  88. self.assertFalse(p.running)
  89. p.startService()
  90. self.assertTrue(s.running)
  91. self.assertTrue(p.running)
  92. p.stopService()
  93. self.assertFalse(s.running)
  94. self.assertFalse(p.running)
  95. def testRunningChildren2(self):
  96. s = service.Service()
  97. def checkRunning():
  98. self.assertTrue(s.running)
  99. t = service.Service()
  100. t.stopService = checkRunning
  101. t.startService = checkRunning
  102. p = service.MultiService()
  103. s.setServiceParent(p)
  104. t.setServiceParent(p)
  105. p.startService()
  106. p.stopService()
  107. def testAddingIntoRunning(self):
  108. p = service.MultiService()
  109. p.startService()
  110. s = service.Service()
  111. self.assertFalse(s.running)
  112. s.setServiceParent(p)
  113. self.assertTrue(s.running)
  114. s.disownServiceParent()
  115. self.assertFalse(s.running)
  116. def testPrivileged(self):
  117. s = service.Service()
  118. def pss():
  119. s.privilegedStarted = 1
  120. s.privilegedStartService = pss
  121. s1 = service.Service()
  122. p = service.MultiService()
  123. s.setServiceParent(p)
  124. s1.setServiceParent(p)
  125. p.privilegedStartService()
  126. self.assertTrue(s.privilegedStarted)
  127. def testCopying(self):
  128. s = service.Service()
  129. s.startService()
  130. s1 = copy.copy(s)
  131. self.assertFalse(s1.running)
  132. self.assertTrue(s.running)
  133. if hasattr(os, "getuid"):
  134. curuid = os.getuid()
  135. curgid = os.getgid()
  136. else:
  137. curuid = curgid = 0
  138. class ProcessTests(unittest.TestCase):
  139. def testID(self):
  140. p = service.Process(5, 6)
  141. self.assertEqual(p.uid, 5)
  142. self.assertEqual(p.gid, 6)
  143. def testDefaults(self):
  144. p = service.Process(5)
  145. self.assertEqual(p.uid, 5)
  146. self.assertIsNone(p.gid)
  147. p = service.Process(gid=5)
  148. self.assertIsNone(p.uid)
  149. self.assertEqual(p.gid, 5)
  150. p = service.Process()
  151. self.assertIsNone(p.uid)
  152. self.assertIsNone(p.gid)
  153. def testProcessName(self):
  154. p = service.Process()
  155. self.assertIsNone(p.processName)
  156. p.processName = 'hello'
  157. self.assertEqual(p.processName, 'hello')
  158. class InterfacesTests(unittest.TestCase):
  159. def testService(self):
  160. self.assertTrue(service.IService.providedBy(service.Service()))
  161. def testMultiService(self):
  162. self.assertTrue(service.IService.providedBy(service.MultiService()))
  163. self.assertTrue(service.IServiceCollection.providedBy(
  164. service.MultiService()))
  165. def testProcess(self):
  166. self.assertTrue(service.IProcess.providedBy(service.Process()))
  167. class ApplicationTests(unittest.TestCase):
  168. def testConstructor(self):
  169. service.Application("hello")
  170. service.Application("hello", 5)
  171. service.Application("hello", 5, 6)
  172. def testProcessComponent(self):
  173. a = service.Application("hello")
  174. self.assertIsNone(service.IProcess(a).uid)
  175. self.assertIsNone(service.IProcess(a).gid)
  176. a = service.Application("hello", 5)
  177. self.assertEqual(service.IProcess(a).uid, 5)
  178. self.assertIsNone(service.IProcess(a).gid)
  179. a = service.Application("hello", 5, 6)
  180. self.assertEqual(service.IProcess(a).uid, 5)
  181. self.assertEqual(service.IProcess(a).gid, 6)
  182. def testServiceComponent(self):
  183. a = service.Application("hello")
  184. self.assertIs(service.IService(a), service.IServiceCollection(a))
  185. self.assertEqual(service.IService(a).name, "hello")
  186. self.assertIsNone(service.IService(a).parent)
  187. def testPersistableComponent(self):
  188. a = service.Application("hello")
  189. p = sob.IPersistable(a)
  190. self.assertEqual(p.style, 'pickle')
  191. self.assertEqual(p.name, 'hello')
  192. self.assertIs(p.original, a)
  193. class LoadingTests(unittest.TestCase):
  194. def test_simpleStoreAndLoad(self):
  195. a = service.Application("hello")
  196. p = sob.IPersistable(a)
  197. for style in 'source pickle'.split():
  198. p.setStyle(style)
  199. p.save()
  200. a1 = service.loadApplication("hello.ta"+style[0], style)
  201. self.assertEqual(service.IService(a1).name, "hello")
  202. with open("hello.tac", 'w') as f:
  203. f.writelines([
  204. "from twisted.application import service\n",
  205. "application = service.Application('hello')\n",
  206. ])
  207. a1 = service.loadApplication("hello.tac", 'python')
  208. self.assertEqual(service.IService(a1).name, "hello")
  209. class AppSupportTests(unittest.TestCase):
  210. def testPassphrase(self):
  211. self.assertIsNone(app.getPassphrase(0))
  212. def testLoadApplication(self):
  213. """
  214. Test loading an application file in different dump format.
  215. """
  216. a = service.Application("hello")
  217. baseconfig = {'file': None, 'source': None, 'python':None}
  218. for style in 'source pickle'.split():
  219. config = baseconfig.copy()
  220. config[{'pickle': 'file'}.get(style, style)] = 'helloapplication'
  221. sob.IPersistable(a).setStyle(style)
  222. sob.IPersistable(a).save(filename='helloapplication')
  223. a1 = app.getApplication(config, None)
  224. self.assertEqual(service.IService(a1).name, "hello")
  225. config = baseconfig.copy()
  226. config['python'] = 'helloapplication'
  227. with open("helloapplication", 'w') as f:
  228. f.writelines([
  229. "from twisted.application import service\n",
  230. "application = service.Application('hello')\n",
  231. ])
  232. a1 = app.getApplication(config, None)
  233. self.assertEqual(service.IService(a1).name, "hello")
  234. def test_convertStyle(self):
  235. appl = service.Application("lala")
  236. for instyle in 'source pickle'.split():
  237. for outstyle in 'source pickle'.split():
  238. sob.IPersistable(appl).setStyle(instyle)
  239. sob.IPersistable(appl).save(filename="converttest")
  240. app.convertStyle("converttest", instyle, None,
  241. "converttest.out", outstyle, 0)
  242. appl2 = service.loadApplication("converttest.out", outstyle)
  243. self.assertEqual(service.IService(appl2).name, "lala")
  244. def test_startApplication(self):
  245. appl = service.Application("lala")
  246. app.startApplication(appl, 0)
  247. self.assertTrue(service.IService(appl).running)
  248. class Foo(basic.LineReceiver):
  249. def connectionMade(self):
  250. self.transport.write(b'lalala\r\n')
  251. def lineReceived(self, line):
  252. self.factory.line = line
  253. self.transport.loseConnection()
  254. def connectionLost(self, reason):
  255. self.factory.d.callback(self.factory.line)
  256. class DummyApp:
  257. processName = None
  258. def addService(self, service):
  259. self.services[service.name] = service
  260. def removeService(self, service):
  261. del self.services[service.name]
  262. class TimerTarget:
  263. def __init__(self):
  264. self.l = []
  265. def append(self, what):
  266. self.l.append(what)
  267. class TestEcho(wire.Echo):
  268. def connectionLost(self, reason):
  269. self.d.callback(True)
  270. class InternetTests(unittest.TestCase):
  271. def testTCP(self):
  272. s = service.MultiService()
  273. s.startService()
  274. factory = protocol.ServerFactory()
  275. factory.protocol = TestEcho
  276. TestEcho.d = defer.Deferred()
  277. t = internet.TCPServer(0, factory)
  278. t.setServiceParent(s)
  279. num = t._port.getHost().port
  280. factory = protocol.ClientFactory()
  281. factory.d = defer.Deferred()
  282. factory.protocol = Foo
  283. factory.line = None
  284. internet.TCPClient('127.0.0.1', num, factory).setServiceParent(s)
  285. factory.d.addCallback(self.assertEqual, b'lalala')
  286. factory.d.addCallback(lambda x : s.stopService())
  287. factory.d.addCallback(lambda x : TestEcho.d)
  288. return factory.d
  289. def test_UDP(self):
  290. """
  291. Test L{internet.UDPServer} with a random port: starting the service
  292. should give it valid port, and stopService should free it so that we
  293. can start a server on the same port again.
  294. """
  295. if not interfaces.IReactorUDP(reactor, None):
  296. raise unittest.SkipTest("This reactor does not support UDP sockets")
  297. p = protocol.DatagramProtocol()
  298. t = internet.UDPServer(0, p)
  299. t.startService()
  300. num = t._port.getHost().port
  301. self.assertNotEqual(num, 0)
  302. def onStop(ignored):
  303. t = internet.UDPServer(num, p)
  304. t.startService()
  305. return t.stopService()
  306. return defer.maybeDeferred(t.stopService).addCallback(onStop)
  307. def testPrivileged(self):
  308. factory = protocol.ServerFactory()
  309. factory.protocol = TestEcho
  310. TestEcho.d = defer.Deferred()
  311. t = internet.TCPServer(0, factory)
  312. t.privileged = 1
  313. t.privilegedStartService()
  314. num = t._port.getHost().port
  315. factory = protocol.ClientFactory()
  316. factory.d = defer.Deferred()
  317. factory.protocol = Foo
  318. factory.line = None
  319. c = internet.TCPClient('127.0.0.1', num, factory)
  320. c.startService()
  321. factory.d.addCallback(self.assertEqual, b'lalala')
  322. factory.d.addCallback(lambda x : c.stopService())
  323. factory.d.addCallback(lambda x : t.stopService())
  324. factory.d.addCallback(lambda x : TestEcho.d)
  325. return factory.d
  326. def testConnectionGettingRefused(self):
  327. factory = protocol.ServerFactory()
  328. factory.protocol = wire.Echo
  329. t = internet.TCPServer(0, factory)
  330. t.startService()
  331. num = t._port.getHost().port
  332. t.stopService()
  333. d = defer.Deferred()
  334. factory = protocol.ClientFactory()
  335. factory.clientConnectionFailed = lambda *args: d.callback(None)
  336. c = internet.TCPClient('127.0.0.1', num, factory)
  337. c.startService()
  338. return d
  339. def testUNIX(self):
  340. # FIXME: This test is far too dense. It needs comments.
  341. # -- spiv, 2004-11-07
  342. s = service.MultiService()
  343. s.startService()
  344. factory = protocol.ServerFactory()
  345. factory.protocol = TestEcho
  346. TestEcho.d = defer.Deferred()
  347. t = internet.UNIXServer('echo.skt', factory)
  348. t.setServiceParent(s)
  349. factory = protocol.ClientFactory()
  350. factory.protocol = Foo
  351. factory.d = defer.Deferred()
  352. factory.line = None
  353. internet.UNIXClient('echo.skt', factory).setServiceParent(s)
  354. factory.d.addCallback(self.assertEqual, b'lalala')
  355. factory.d.addCallback(lambda x : s.stopService())
  356. factory.d.addCallback(lambda x : TestEcho.d)
  357. factory.d.addCallback(self._cbTestUnix, factory, s)
  358. return factory.d
  359. def _cbTestUnix(self, ignored, factory, s):
  360. TestEcho.d = defer.Deferred()
  361. factory.line = None
  362. factory.d = defer.Deferred()
  363. s.startService()
  364. factory.d.addCallback(self.assertEqual, b'lalala')
  365. factory.d.addCallback(lambda x : s.stopService())
  366. factory.d.addCallback(lambda x : TestEcho.d)
  367. return factory.d
  368. def testVolatile(self):
  369. factory = protocol.ServerFactory()
  370. factory.protocol = wire.Echo
  371. t = internet.UNIXServer('echo.skt', factory)
  372. t.startService()
  373. self.failIfIdentical(t._port, None)
  374. t1 = copy.copy(t)
  375. self.assertIsNone(t1._port)
  376. t.stopService()
  377. self.assertIsNone(t._port)
  378. self.assertFalse(t.running)
  379. factory = protocol.ClientFactory()
  380. factory.protocol = wire.Echo
  381. t = internet.UNIXClient('echo.skt', factory)
  382. t.startService()
  383. self.failIfIdentical(t._connection, None)
  384. t1 = copy.copy(t)
  385. self.assertIsNone(t1._connection)
  386. t.stopService()
  387. self.assertIsNone(t._connection)
  388. self.assertFalse(t.running)
  389. def testStoppingServer(self):
  390. factory = protocol.ServerFactory()
  391. factory.protocol = wire.Echo
  392. t = internet.UNIXServer('echo.skt', factory)
  393. t.startService()
  394. t.stopService()
  395. self.assertFalse(t.running)
  396. factory = protocol.ClientFactory()
  397. d = defer.Deferred()
  398. factory.clientConnectionFailed = lambda *args: d.callback(None)
  399. reactor.connectUNIX('echo.skt', factory)
  400. return d
  401. if not interfaces.IReactorUNIX(reactor, None):
  402. _skipMsg = "This reactor does not support UNIX domain sockets"
  403. testUNIX.skip = _skipMsg
  404. testVolatile.skip = _skipMsg
  405. testStoppingServer.skip = _skipMsg
  406. def testPickledTimer(self):
  407. target = TimerTarget()
  408. t0 = internet.TimerService(1, target.append, "hello")
  409. t0.startService()
  410. s = pickle.dumps(t0)
  411. t0.stopService()
  412. t = pickle.loads(s)
  413. self.assertFalse(t.running)
  414. def testBrokenTimer(self):
  415. d = defer.Deferred()
  416. t = internet.TimerService(1, lambda: 1 // 0)
  417. oldFailed = t._failed
  418. def _failed(why):
  419. oldFailed(why)
  420. d.callback(None)
  421. t._failed = _failed
  422. t.startService()
  423. d.addCallback(lambda x : t.stopService)
  424. d.addCallback(lambda x : self.assertEqual(
  425. [ZeroDivisionError],
  426. [o.value.__class__ for o in self.flushLoggedErrors(ZeroDivisionError)]))
  427. return d
  428. def test_everythingThere(self):
  429. """
  430. L{twisted.application.internet} dynamically defines a set of
  431. L{service.Service} subclasses that in general have corresponding
  432. reactor.listenXXX or reactor.connectXXX calls.
  433. """
  434. trans = 'TCP UNIX SSL UDP UNIXDatagram Multicast'.split()
  435. for tran in trans[:]:
  436. if not getattr(interfaces, "IReactor" + tran)(reactor, None):
  437. trans.remove(tran)
  438. for tran in trans:
  439. for side in 'Server Client'.split():
  440. if tran == "Multicast" and side == "Client":
  441. continue
  442. if tran == "UDP" and side == "Client":
  443. continue
  444. self.assertTrue(hasattr(internet, tran + side))
  445. method = getattr(internet, tran + side).method
  446. prefix = {'Server': 'listen', 'Client': 'connect'}[side]
  447. self.assertTrue(hasattr(reactor, prefix + method) or
  448. (prefix == "connect" and method == "UDP"))
  449. o = getattr(internet, tran + side)()
  450. self.assertEqual(service.IService(o), o)
  451. def test_importAll(self):
  452. """
  453. L{twisted.application.internet} dynamically defines L{service.Service}
  454. subclasses. This test ensures that the subclasses exposed by C{__all__}
  455. are valid attributes of the module.
  456. """
  457. for cls in internet.__all__:
  458. self.assertTrue(
  459. hasattr(internet, cls),
  460. '%s not importable from twisted.application.internet' % (cls,))
  461. def test_reactorParametrizationInServer(self):
  462. """
  463. L{internet._AbstractServer} supports a C{reactor} keyword argument
  464. that can be used to parametrize the reactor used to listen for
  465. connections.
  466. """
  467. reactor = MemoryReactor()
  468. factory = object()
  469. t = internet.TCPServer(1234, factory, reactor=reactor)
  470. t.startService()
  471. self.assertEqual(reactor.tcpServers.pop()[:2], (1234, factory))
  472. def test_reactorParametrizationInClient(self):
  473. """
  474. L{internet._AbstractClient} supports a C{reactor} keyword arguments
  475. that can be used to parametrize the reactor used to create new client
  476. connections.
  477. """
  478. reactor = MemoryReactor()
  479. factory = protocol.ClientFactory()
  480. t = internet.TCPClient('127.0.0.1', 1234, factory, reactor=reactor)
  481. t.startService()
  482. self.assertEqual(
  483. reactor.tcpClients.pop()[:3], ('127.0.0.1', 1234, factory))
  484. def test_reactorParametrizationInServerMultipleStart(self):
  485. """
  486. Like L{test_reactorParametrizationInServer}, but stop and restart the
  487. service and check that the given reactor is still used.
  488. """
  489. reactor = MemoryReactor()
  490. factory = protocol.Factory()
  491. t = internet.TCPServer(1234, factory, reactor=reactor)
  492. t.startService()
  493. self.assertEqual(reactor.tcpServers.pop()[:2], (1234, factory))
  494. t.stopService()
  495. t.startService()
  496. self.assertEqual(reactor.tcpServers.pop()[:2], (1234, factory))
  497. def test_reactorParametrizationInClientMultipleStart(self):
  498. """
  499. Like L{test_reactorParametrizationInClient}, but stop and restart the
  500. service and check that the given reactor is still used.
  501. """
  502. reactor = MemoryReactor()
  503. factory = protocol.ClientFactory()
  504. t = internet.TCPClient('127.0.0.1', 1234, factory, reactor=reactor)
  505. t.startService()
  506. self.assertEqual(
  507. reactor.tcpClients.pop()[:3], ('127.0.0.1', 1234, factory))
  508. t.stopService()
  509. t.startService()
  510. self.assertEqual(
  511. reactor.tcpClients.pop()[:3], ('127.0.0.1', 1234, factory))
  512. class TimerBasicTests(unittest.TestCase):
  513. def testTimerRuns(self):
  514. d = defer.Deferred()
  515. self.t = internet.TimerService(1, d.callback, 'hello')
  516. self.t.startService()
  517. d.addCallback(self.assertEqual, 'hello')
  518. d.addCallback(lambda x : self.t.stopService())
  519. d.addCallback(lambda x : self.assertFalse(self.t.running))
  520. return d
  521. def tearDown(self):
  522. return self.t.stopService()
  523. def testTimerRestart(self):
  524. # restart the same TimerService
  525. d1 = defer.Deferred()
  526. d2 = defer.Deferred()
  527. work = [(d2, "bar"), (d1, "foo")]
  528. def trigger():
  529. d, arg = work.pop()
  530. d.callback(arg)
  531. self.t = internet.TimerService(1, trigger)
  532. self.t.startService()
  533. def onFirstResult(result):
  534. self.assertEqual(result, 'foo')
  535. return self.t.stopService()
  536. def onFirstStop(ignored):
  537. self.assertFalse(self.t.running)
  538. self.t.startService()
  539. return d2
  540. def onSecondResult(result):
  541. self.assertEqual(result, 'bar')
  542. self.t.stopService()
  543. d1.addCallback(onFirstResult)
  544. d1.addCallback(onFirstStop)
  545. d1.addCallback(onSecondResult)
  546. return d1
  547. def testTimerLoops(self):
  548. l = []
  549. def trigger(data, number, d):
  550. l.append(data)
  551. if len(l) == number:
  552. d.callback(l)
  553. d = defer.Deferred()
  554. self.t = internet.TimerService(0.01, trigger, "hello", 10, d)
  555. self.t.startService()
  556. d.addCallback(self.assertEqual, ['hello'] * 10)
  557. d.addCallback(lambda x : self.t.stopService())
  558. return d
  559. class FakeReactor(reactors.Reactor):
  560. """
  561. A fake reactor with a hooked install method.
  562. """
  563. def __init__(self, install, *args, **kwargs):
  564. """
  565. @param install: any callable that will be used as install method.
  566. @type install: C{callable}
  567. """
  568. reactors.Reactor.__init__(self, *args, **kwargs)
  569. self.install = install
  570. class PluggableReactorTests(TwistedModulesMixin, unittest.TestCase):
  571. """
  572. Tests for the reactor discovery/inspection APIs.
  573. """
  574. def setUp(self):
  575. """
  576. Override the L{reactors.getPlugins} function, normally bound to
  577. L{twisted.plugin.getPlugins}, in order to control which
  578. L{IReactorInstaller} plugins are seen as available.
  579. C{self.pluginResults} can be customized and will be used as the
  580. result of calls to C{reactors.getPlugins}.
  581. """
  582. self.pluginCalls = []
  583. self.pluginResults = []
  584. self.originalFunction = reactors.getPlugins
  585. reactors.getPlugins = self._getPlugins
  586. def tearDown(self):
  587. """
  588. Restore the original L{reactors.getPlugins}.
  589. """
  590. reactors.getPlugins = self.originalFunction
  591. def _getPlugins(self, interface, package=None):
  592. """
  593. Stand-in for the real getPlugins method which records its arguments
  594. and returns a fixed result.
  595. """
  596. self.pluginCalls.append((interface, package))
  597. return list(self.pluginResults)
  598. def test_getPluginReactorTypes(self):
  599. """
  600. Test that reactor plugins are returned from L{getReactorTypes}
  601. """
  602. name = 'fakereactortest'
  603. package = __name__ + '.fakereactor'
  604. description = 'description'
  605. self.pluginResults = [reactors.Reactor(name, package, description)]
  606. reactorTypes = reactors.getReactorTypes()
  607. self.assertEqual(
  608. self.pluginCalls,
  609. [(reactors.IReactorInstaller, None)])
  610. for r in reactorTypes:
  611. if r.shortName == name:
  612. self.assertEqual(r.description, description)
  613. break
  614. else:
  615. self.fail("Reactor plugin not present in getReactorTypes() result")
  616. def test_reactorInstallation(self):
  617. """
  618. Test that L{reactors.Reactor.install} loads the correct module and
  619. calls its install attribute.
  620. """
  621. installed = []
  622. def install():
  623. installed.append(True)
  624. fakeReactor = FakeReactor(install,
  625. 'fakereactortest', __name__, 'described')
  626. modules = {'fakereactortest': fakeReactor}
  627. self.replaceSysModules(modules)
  628. installer = reactors.Reactor('fakereactor', 'fakereactortest', 'described')
  629. installer.install()
  630. self.assertEqual(installed, [True])
  631. def test_installReactor(self):
  632. """
  633. Test that the L{reactors.installReactor} function correctly installs
  634. the specified reactor.
  635. """
  636. installed = []
  637. def install():
  638. installed.append(True)
  639. name = 'fakereactortest'
  640. package = __name__
  641. description = 'description'
  642. self.pluginResults = [FakeReactor(install, name, package, description)]
  643. reactors.installReactor(name)
  644. self.assertEqual(installed, [True])
  645. def test_installReactorReturnsReactor(self):
  646. """
  647. Test that the L{reactors.installReactor} function correctly returns
  648. the installed reactor.
  649. """
  650. reactor = object()
  651. def install():
  652. from twisted import internet
  653. self.patch(internet, 'reactor', reactor)
  654. name = 'fakereactortest'
  655. package = __name__
  656. description = 'description'
  657. self.pluginResults = [FakeReactor(install, name, package, description)]
  658. installed = reactors.installReactor(name)
  659. self.assertIs(installed, reactor)
  660. def test_installReactorMultiplePlugins(self):
  661. """
  662. Test that the L{reactors.installReactor} function correctly installs
  663. the specified reactor when there are multiple reactor plugins.
  664. """
  665. installed = []
  666. def install():
  667. installed.append(True)
  668. name = 'fakereactortest'
  669. package = __name__
  670. description = 'description'
  671. fakeReactor = FakeReactor(install, name, package, description)
  672. otherReactor = FakeReactor(lambda: None,
  673. "otherreactor", package, description)
  674. self.pluginResults = [otherReactor, fakeReactor]
  675. reactors.installReactor(name)
  676. self.assertEqual(installed, [True])
  677. def test_installNonExistentReactor(self):
  678. """
  679. Test that L{reactors.installReactor} raises L{reactors.NoSuchReactor}
  680. when asked to install a reactor which it cannot find.
  681. """
  682. self.pluginResults = []
  683. self.assertRaises(
  684. reactors.NoSuchReactor,
  685. reactors.installReactor, 'somereactor')
  686. def test_installNotAvailableReactor(self):
  687. """
  688. Test that L{reactors.installReactor} raises an exception when asked to
  689. install a reactor which doesn't work in this environment.
  690. """
  691. def install():
  692. raise ImportError("Missing foo bar")
  693. name = 'fakereactortest'
  694. package = __name__
  695. description = 'description'
  696. self.pluginResults = [FakeReactor(install, name, package, description)]
  697. self.assertRaises(ImportError, reactors.installReactor, name)
  698. def test_reactorSelectionMixin(self):
  699. """
  700. Test that the reactor selected is installed as soon as possible, ie
  701. when the option is parsed.
  702. """
  703. executed = []
  704. INSTALL_EVENT = 'reactor installed'
  705. SUBCOMMAND_EVENT = 'subcommands loaded'
  706. class ReactorSelectionOptions(usage.Options, app.ReactorSelectionMixin):
  707. def subCommands(self):
  708. executed.append(SUBCOMMAND_EVENT)
  709. return [('subcommand', None, lambda: self, 'test subcommand')]
  710. subCommands = property(subCommands)
  711. def install():
  712. executed.append(INSTALL_EVENT)
  713. self.pluginResults = [
  714. FakeReactor(install, 'fakereactortest', __name__, 'described')
  715. ]
  716. options = ReactorSelectionOptions()
  717. options.parseOptions(['--reactor', 'fakereactortest', 'subcommand'])
  718. self.assertEqual(executed[0], INSTALL_EVENT)
  719. self.assertEqual(executed.count(INSTALL_EVENT), 1)
  720. self.assertEqual(options["reactor"], "fakereactortest")
  721. def test_reactorSelectionMixinNonExistent(self):
  722. """
  723. Test that the usage mixin exits when trying to use a non existent
  724. reactor (the name not matching to any reactor), giving an error
  725. message.
  726. """
  727. class ReactorSelectionOptions(usage.Options, app.ReactorSelectionMixin):
  728. pass
  729. self.pluginResults = []
  730. options = ReactorSelectionOptions()
  731. options.messageOutput = NativeStringIO()
  732. e = self.assertRaises(usage.UsageError, options.parseOptions,
  733. ['--reactor', 'fakereactortest', 'subcommand'])
  734. self.assertIn("fakereactortest", e.args[0])
  735. self.assertIn("help-reactors", e.args[0])
  736. def test_reactorSelectionMixinNotAvailable(self):
  737. """
  738. Test that the usage mixin exits when trying to use a reactor not
  739. available (the reactor raises an error at installation), giving an
  740. error message.
  741. """
  742. class ReactorSelectionOptions(usage.Options, app.ReactorSelectionMixin):
  743. pass
  744. message = "Missing foo bar"
  745. def install():
  746. raise ImportError(message)
  747. name = 'fakereactortest'
  748. package = __name__
  749. description = 'description'
  750. self.pluginResults = [FakeReactor(install, name, package, description)]
  751. options = ReactorSelectionOptions()
  752. options.messageOutput = NativeStringIO()
  753. e = self.assertRaises(usage.UsageError, options.parseOptions,
  754. ['--reactor', 'fakereactortest', 'subcommand'])
  755. self.assertIn(message, e.args[0])
  756. self.assertIn("help-reactors", e.args[0])
  757. class HelpReactorsTests(unittest.TestCase):
  758. """
  759. --help-reactors lists the available reactors
  760. """
  761. def setUp(self):
  762. """
  763. Get the text from --help-reactors
  764. """
  765. self.options = app.ReactorSelectionMixin()
  766. self.options.messageOutput = NativeStringIO()
  767. self.assertRaises(SystemExit, self.options.opt_help_reactors)
  768. self.message = self.options.messageOutput.getvalue()
  769. def test_lacksAsyncIO(self):
  770. """
  771. --help-reactors should NOT display the asyncio reactor on Python < 3.4
  772. """
  773. self.assertIn(twisted_reactors.asyncio.description, self.message)
  774. self.assertIn("!" + twisted_reactors.asyncio.shortName, self.message)
  775. if asyncio:
  776. test_lacksAsyncIO.skip = "Not applicable, asyncio is available"
  777. def test_hasAsyncIO(self):
  778. """
  779. --help-reactors should display the asyncio reactor on Python >= 3.4
  780. """
  781. self.assertIn(twisted_reactors.asyncio.description, self.message)
  782. self.assertNotIn(
  783. "!" + twisted_reactors.asyncio.shortName, self.message)
  784. if not asyncio:
  785. test_hasAsyncIO.skip = "asyncio library not available"
  786. def test_iocpWin32(self):
  787. """
  788. --help-reactors should display the iocp reactor on Windows
  789. """
  790. self.assertIn(twisted_reactors.iocp.description, self.message)
  791. self.assertNotIn("!" + twisted_reactors.iocp.shortName, self.message)
  792. if platformType != "win32":
  793. test_iocpWin32.skip = "Test only applicable on Windows"
  794. def test_iocpNotWin32(self):
  795. """
  796. --help-reactors should NOT display the iocp reactor on Windows
  797. """
  798. self.assertIn(twisted_reactors.iocp.description, self.message)
  799. self.assertIn("!" + twisted_reactors.iocp.shortName, self.message)
  800. if platformType == "win32":
  801. test_iocpNotWin32.skip = "Test only applicable on Windows"
  802. def test_onlySupportedReactors(self):
  803. """
  804. --help-reactors with only supported reactors
  805. """
  806. def getReactorTypes():
  807. yield twisted_reactors.default
  808. options = app.ReactorSelectionMixin()
  809. options._getReactorTypes = getReactorTypes
  810. options.messageOutput = NativeStringIO()
  811. self.assertRaises(SystemExit, options.opt_help_reactors)
  812. message = options.messageOutput.getvalue()
  813. self.assertNotIn("reactors not available", message)