def _testHook(self, methodName, callerName=None): """ Verify that the named hook is run with the expected arguments as specified by the arguments used to create the L{Runner}, when the specified caller is invoked. @param methodName: The name of the hook to verify. @type methodName: L{str} @param callerName: The name of the method that is expected to cause the hook to be called. If C{None}, use the L{Runner} method with the same name as the hook. @type callerName: L{str} """ if callerName is None: callerName = methodName arguments = dict(a=object(), b=object(), c=object()) argumentsSeen = [] def hook(**arguments): argumentsSeen.append(arguments) runnerArguments = { methodName: hook, "{}Arguments".format(methodName): arguments.copy(), } runner = Runner(reactor=MemoryReactor(), **runnerArguments) hookCaller = getattr(runner, callerName) hookCaller() self.assertEqual(len(argumentsSeen), 1) self.assertEqual(argumentsSeen[0], arguments)
def test_memoryReactorProvides(self): """ L{MemoryReactor} provides all of the attributes described by the interfaces it advertises. """ memoryReactor = MemoryReactor() verifyObject(IReactorTCP, memoryReactor) verifyObject(IReactorSSL, memoryReactor) verifyObject(IReactorUNIX, memoryReactor)
def test_startReactorWithReactor(self) -> None: """ L{Runner.startReactor} with the C{reactor} argument runs the given reactor. """ reactor = MemoryReactor() runner = Runner(reactor=reactor) runner.startReactor() self.assertTrue(reactor.hasRun)
def test_killNotRequested(self) -> None: """ L{Runner.killIfRequested} when C{kill} is false doesn't exit and doesn't indiscriminately murder anyone. """ runner = Runner(reactor=MemoryReactor()) runner.killIfRequested() self.assertEqual(self.kill.calls, []) self.assertFalse(self.exit.exited)
def test_killRequestedWithPIDFileNotAnInt(self) -> None: """ L{Runner.killIfRequested} when C{kill} is true and given a C{pidFile} containing a non-integer value exits with L{ExitStatus.EX_DATAERR}. """ pidFile = PIDFile(self.filePath(b"** totally not a number, dude **")) runner = Runner(reactor=MemoryReactor(), kill=True, pidFile=pidFile) runner.killIfRequested() self.assertEqual(self.exit.status, ExitStatus.EX_DATAERR) self.assertEqual(self.exit.message, "Invalid PID file.")
def test_killRequestedWithPIDFileEmpty(self) -> None: """ L{Runner.killIfRequested} when C{kill} is true and given a C{pidFile} containing no value exits with L{ExitStatus.EX_DATAERR}. """ pidFile = PIDFile(self.filePath(b"")) runner = Runner(reactor=MemoryReactor(), kill=True, pidFile=pidFile) runner.killIfRequested() self.assertEqual(self.exit.status, ExitStatus.EX_DATAERR) self.assertEqual(self.exit.message, "Invalid PID file.")
def _verify_error(self, config, expected): """ Assert that when ``DaemonizeTheRealService`` is started using the given configuration it writes the given message to stderr and stops the reactor. :param bytes config: The contents of a ``tahoe.cfg`` file to give to the service. :param bytes expected: A string to assert appears in stderr after the service starts. """ nodedir = FilePath(self.mktemp()) nodedir.makedirs() nodedir.child("tahoe.cfg").setContent(config.encode("ascii")) nodedir.child("tahoe-client.tac").touch() options = parse_options(["run", nodedir.path]) stdout = options.stdout = StringIO() stderr = options.stderr = StringIO() run_options = options.subOptions reactor = MemoryReactor() with AlternateReactor(reactor): service = DaemonizeTheRealService( "client", nodedir.path, run_options, ) service.startService() # We happen to know that the service uses reactor.callWhenRunning # to schedule all its work (though I couldn't tell you *why*). # Make sure those scheduled calls happen. waiting = reactor.whenRunningHooks[:] del reactor.whenRunningHooks[:] for f, a, k in waiting: f(*a, **k) self.assertThat( reactor.hasStopped, Equals(True), ) self.assertThat( stdout.getvalue(), Equals(""), ) self.assertThat( stderr.getvalue(), Contains(expected), )
def test_killRequestedWithPIDFile(self) -> None: """ L{Runner.killIfRequested} when C{kill} is true and given a C{pidFile} performs a targeted killing of the appropriate process. """ pidFile = PIDFile(self.filePath(self.pidFileContent)) runner = Runner(reactor=MemoryReactor(), kill=True, pidFile=pidFile) runner.killIfRequested() self.assertEqual(self.kill.calls, [(self.pid, SIGTERM)]) self.assertEqual(self.exit.status, ExitStatus.EX_OK) self.assertIdentical(self.exit.message, None)
def test_killRequestedWithoutPIDFile(self) -> None: """ L{Runner.killIfRequested} when C{kill} is true but C{pidFile} is L{nonePIDFile} exits with L{ExitStatus.EX_USAGE} and the expected message; and also doesn't indiscriminately murder anyone. """ runner = Runner(reactor=MemoryReactor(), kill=True) runner.killIfRequested() self.assertEqual(self.kill.calls, []) self.assertEqual(self.exit.status, ExitStatus.EX_USAGE) self.assertEqual(self.exit.message, "No PID file specified.")
def test_listenDefaultHost(self): """ L{MemoryReactor.listenTCP}, L{MemoryReactor.listenSSL} and L{MemoryReactor.listenUNIX} will return an L{IListeningPort} whose C{getHost} method returns an L{IAddress}; C{listenTCP} and C{listenSSL} will have a default host of C{'0.0.0.0'}, and a port that reflects the value passed, and C{listenUNIX} will have a name that reflects the path passed. """ memoryReactor = MemoryReactor() for port in [memoryReactor.listenTCP(8242, Factory()), memoryReactor.listenSSL(8242, Factory(), None)]: verifyObject(IListeningPort, port) address = port.getHost() verifyObject(IAddress, address) self.assertEqual(address.host, '0.0.0.0') self.assertEqual(address.port, 8242) port = memoryReactor.listenUNIX(b"/path/to/socket", Factory()) verifyObject(IListeningPort, port) address = port.getHost() verifyObject(IAddress, address) self.assertEqual(address.name, b"/path/to/socket")
def test_runInOrder(self): """ L{Runner.run} calls the expected methods in order. """ runner = DummyRunner(reactor=MemoryReactor()) runner.run() self.assertEqual(runner.calledMethods, [ "killIfRequested", "startLogging", "startReactor", "reactorExited", ])
def test_connectDestination(self): """ L{MemoryReactor.connectTCP}, L{MemoryReactor.connectSSL}, and L{MemoryReactor.connectUNIX} will return an L{IConnector} whose C{getDestination} method returns an L{IAddress} with attributes which reflect the values passed. """ memoryReactor = MemoryReactor() for connector in [ memoryReactor.connectTCP("test.example.com", 8321, ClientFactory()), memoryReactor.connectSSL("test.example.com", 8321, ClientFactory(), None), ]: verifyObject(IConnector, connector) address = connector.getDestination() verifyObject(IAddress, address) self.assertEqual(address.host, "test.example.com") self.assertEqual(address.port, 8321) connector = memoryReactor.connectUNIX(b"/fake/path", ClientFactory()) verifyObject(IConnector, connector) address = connector.getDestination() verifyObject(IAddress, address) self.assertEqual(address.name, b"/fake/path")
def test_writers(self): """ Adding, removing, and listing writers works. """ writer = object() reactor = MemoryReactor() reactor.addWriter(writer) reactor.addWriter(writer) self.assertEqual(reactor.getWriters(), [writer]) reactor.removeWriter(writer) self.assertEqual(reactor.getWriters(), [])
def test_readers(self): """ Adding, removing, and listing readers works. """ reader = object() reactor = MemoryReactor() reactor.addReader(reader) reactor.addReader(reader) self.assertEqual(reactor.getReaders(), [reader]) reactor.removeReader(reader) self.assertEqual(reactor.getReaders(), [])
def test_runAlreadyRunning(self) -> None: """ L{Runner.run} exits with L{ExitStatus.EX_USAGE} and the expected message if a process is already running that corresponds to the given PID file. """ pidFile = PIDFile(self.filePath(self.pidFileContent)) pidFile.isRunning = lambda: True # type: ignore[assignment] runner = Runner(reactor=MemoryReactor(), pidFile=pidFile) runner.run() self.assertEqual(self.exit.status, ExitStatus.EX_CONFIG) self.assertEqual(self.exit.message, "Already running.")
def test_runUsesPIDFile(self) -> None: """ L{Runner.run} uses the provided PID file. """ pidFile = DummyPIDFile() runner = Runner(reactor=MemoryReactor(), pidFile=pidFile) self.assertFalse(pidFile.entered) self.assertFalse(pidFile.exited) runner.run() self.assertTrue(pidFile.entered) self.assertTrue(pidFile.exited)
def test_killRequestedWithPIDFileCantRead(self) -> None: """ L{Runner.killIfRequested} when C{kill} is true and given a C{pidFile} that it can't read exits with L{ExitStatus.EX_IOERR}. """ pidFile = PIDFile(self.filePath(None)) def read() -> int: raise OSError(errno.EACCES, "Permission denied") pidFile.read = read # type: ignore[assignment] runner = Runner(reactor=MemoryReactor(), kill=True, pidFile=pidFile) runner.killIfRequested() self.assertEqual(self.exit.status, ExitStatus.EX_IOERR) self.assertEqual(self.exit.message, "Unable to read PID file.")
def setUp(self): self.make_httpbin_site_returns = Site(Resource()) self.serve_tcp_calls = [] self.serve_tcp_returns = defer.Deferred() self.serve_tls_calls = [] self.serve_tls_returns = defer.Deferred() self.output_process_description_calls = [] self.output_process_description_returns = None self.reactor = MemoryReactor() self.forever_httpbin = functools.partial( child._forever_httpbin, _make_httpbin_site=self.make_httpbin_site, _serve_tcp=self.serve_tcp, _serve_tls=self.serve_tls, _output_process_description=self.output_process_description, )
def setUp(self): self.fake_threadpool_state = FakeThreadPoolState() self.fake_threadpool = FakeThreadPool(self.fake_threadpool_state) self.reactor = MemoryReactor()
def setUp(self): self.reactor = MemoryReactor() self.site = Site(Resource)
def test_startLogging(self) -> None: """ L{Runner.startLogging} sets up a filtering observer with a log level predicate set to the given log level that contains a file observer of the given type which writes to the given file. """ logFile = StringIO() # Patch the log beginner so that we don't try to start the already # running (started by trial) logging system. class LogBeginner: observers: List[ILogObserver] = [] def beginLoggingTo(self, observers: Iterable[ILogObserver]) -> None: LogBeginner.observers = list(observers) self.patch(_runner, "globalLogBeginner", LogBeginner()) # Patch FilteringLogObserver so we can capture its arguments class MockFilteringLogObserver(FilteringLogObserver): observer: Optional[ILogObserver] = None predicates: List[LogLevelFilterPredicate] = [] def __init__( self, observer: ILogObserver, predicates: Iterable[LogLevelFilterPredicate], negativeObserver: ILogObserver = cast(ILogObserver, lambda event: None), ): MockFilteringLogObserver.observer = observer MockFilteringLogObserver.predicates = list(predicates) FilteringLogObserver.__init__(self, observer, predicates, negativeObserver) self.patch(_runner, "FilteringLogObserver", MockFilteringLogObserver) # Patch FileLogObserver so we can capture its arguments class MockFileLogObserver(FileLogObserver): outFile: Optional[TextIO] = None def __init__(self, outFile: TextIO) -> None: MockFileLogObserver.outFile = outFile FileLogObserver.__init__(self, outFile, str) # Start logging runner = Runner( reactor=MemoryReactor(), defaultLogLevel=LogLevel.critical, logFile=logFile, fileLogObserverFactory=MockFileLogObserver, ) runner.startLogging() # Check for a filtering observer self.assertEqual(len(LogBeginner.observers), 1) self.assertIsInstance(LogBeginner.observers[0], FilteringLogObserver) # Check log level predicate with the correct default log level self.assertEqual(len(MockFilteringLogObserver.predicates), 1) self.assertIsInstance(MockFilteringLogObserver.predicates[0], LogLevelFilterPredicate) self.assertIdentical( MockFilteringLogObserver.predicates[0].defaultLogLevel, LogLevel.critical) # Check for a file observer attached to the filtering observer observer = cast(MockFileLogObserver, MockFilteringLogObserver.observer) self.assertIsInstance(observer, MockFileLogObserver) # Check for the file we gave it self.assertIdentical(observer.outFile, logFile)
def __init__(self): MemoryReactor.__init__(self) self.spawnedProcesses = []