Viewing file: test_options.py (10.65 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details.
""" Tests for L{twisted.application.twist._options}. """
from sys import stdout, stderr
from twisted.internet import reactor from twisted.copyright import version from twisted.python.usage import UsageError from twisted.logger import LogLevel, textFileLogObserver, jsonFileLogObserver from twisted.test.proto_helpers import MemoryReactor from ...reactors import NoSuchReactor from ...service import ServiceMaker from ...runner._exit import ExitStatus from ...runner.test.test_runner import DummyExit from ...twist import _options from .._options import TwistOptions
import twisted.trial.unittest
class OptionsTests(twisted.trial.unittest.TestCase): """ Tests for L{TwistOptions}. """
def patchExit(self): """ Patch L{_twist.exit} so we can capture usage and prevent actual exits. """ self.exit = DummyExit() self.patch(_options, "exit", self.exit)
def patchOpen(self): """ Patch L{_options.open} so we can capture usage and prevent actual opens. """ self.opened = []
def fakeOpen(name, mode=None): if name == "nocanopen": raise IOError(None, None, name)
self.opened.append((name, mode)) return NotImplemented
self.patch(_options, "openFile", fakeOpen)
def patchInstallReactor(self): """ Patch C{_options.installReactor} so we can capture usage and prevent actual installs. """ self.installedReactors = {}
def installReactor(name): if name != "fusion": raise NoSuchReactor()
reactor = MemoryReactor() self.installedReactors[name] = reactor return reactor
self.patch(_options, "installReactor", installReactor)
def test_synopsis(self): """ L{TwistOptions.getSynopsis} appends arguments. """ options = TwistOptions()
self.assertTrue( options.getSynopsis().endswith(" plugin [plugin_options]") )
def test_version(self): """ L{TwistOptions.opt_version} exits with L{ExitStatus.EX_OK} and prints the version. """ self.patchExit()
options = TwistOptions() options.opt_version()
self.assertEquals(self.exit.status, ExitStatus.EX_OK) self.assertEquals(self.exit.message, version)
def test_reactor(self): """ L{TwistOptions.installReactor} installs the chosen reactor and sets the reactor name. """ self.patchInstallReactor()
options = TwistOptions() options.opt_reactor("fusion")
self.assertEqual(set(self.installedReactors), set(["fusion"])) self.assertEquals(options["reactorName"], "fusion")
def test_installCorrectReactor(self): """ L{TwistOptions.installReactor} installs the chosen reactor after the command line options have been parsed. """ self.patchInstallReactor()
options = TwistOptions() options.subCommand = "test-subcommand" options.parseOptions(["--reactor=fusion"])
self.assertEqual(set(self.installedReactors), set(["fusion"]))
def test_installReactorBogus(self): """ L{TwistOptions.installReactor} raises UsageError if an unknown reactor is specified. """ self.patchInstallReactor()
options = TwistOptions() self.assertRaises(UsageError, options.opt_reactor, "coal")
def test_installReactorDefault(self): """ L{TwistOptions.installReactor} returns the currently installed reactor when the default reactor name is specified. """ options = TwistOptions() self.assertIdentical(reactor, options.installReactor('default'))
def test_logLevelValid(self): """ L{TwistOptions.opt_log_level} sets the corresponding log level. """ options = TwistOptions() options.opt_log_level("warn")
self.assertIdentical(options["logLevel"], LogLevel.warn)
def test_logLevelInvalid(self): """ L{TwistOptions.opt_log_level} with an invalid log level name raises UsageError. """ options = TwistOptions()
self.assertRaises(UsageError, options.opt_log_level, "cheese")
def _testLogFile(self, name, expectedStream): """ Set log file name and check the selected output stream.
@param name: The name of the file. @param expectedStream: The expected stream. """ options = TwistOptions() options.opt_log_file(name)
self.assertIdentical(options["logFile"], expectedStream)
def test_logFileStdout(self): """ L{TwistOptions.opt_log_file} given C{"-"} as a file name uses stdout. """ self._testLogFile("-", stdout)
def test_logFileStderr(self): """ L{TwistOptions.opt_log_file} given C{"+"} as a file name uses stderr. """ self._testLogFile("+", stderr)
def test_logFileNamed(self): """ L{TwistOptions.opt_log_file} opens the given file name in append mode. """ self.patchOpen()
options = TwistOptions() options.opt_log_file("mylog")
self.assertEqual([("mylog", "a")], self.opened)
def test_logFileCantOpen(self): """ L{TwistOptions.opt_log_file} exits with L{ExitStatus.EX_IOERR} if unable to open the log file due to an L{EnvironmentError}. """ self.patchExit() self.patchOpen()
options = TwistOptions() options.opt_log_file("nocanopen")
self.assertEquals(self.exit.status, ExitStatus.EX_IOERR) self.assertTrue( self.exit.message.startswith( "Unable to open log file 'nocanopen': " ) )
def _testLogFormat(self, format, expectedObserver): """ Set log file format and check the selected observer.
@param format: The format of the file. @param expectedObserver: The expected observer. """ options = TwistOptions() options.opt_log_format(format)
self.assertIdentical( options["fileLogObserverFactory"], expectedObserver ) self.assertEqual(options["logFormat"], format)
def test_logFormatText(self): """ L{TwistOptions.opt_log_format} given C{"text"} uses a L{textFileLogObserver}. """ self._testLogFormat("text", textFileLogObserver)
def test_logFormatJSON(self): """ L{TwistOptions.opt_log_format} given C{"text"} uses a L{textFileLogObserver}. """ self._testLogFormat("json", jsonFileLogObserver)
def test_logFormatInvalid(self): """ L{TwistOptions.opt_log_format} given an invalid format name raises L{UsageError}. """ options = TwistOptions()
self.assertRaises(UsageError, options.opt_log_format, "frommage")
def test_selectDefaultLogObserverNoOverride(self): """ L{TwistOptions.selectDefaultLogObserver} will not override an already selected observer. """ self.patchOpen()
options = TwistOptions() options.opt_log_format("text") # Ask for text options.opt_log_file("queso") # File, not a tty options.selectDefaultLogObserver()
# Because we didn't select a file that is a tty, the default is JSON, # but since we asked for text, we should get text. self.assertIdentical( options["fileLogObserverFactory"], textFileLogObserver ) self.assertEqual(options["logFormat"], "text")
def test_selectDefaultLogObserverDefaultWithTTY(self): """ L{TwistOptions.selectDefaultLogObserver} will not override an already selected observer. """ class TTYFile(object): def isatty(self): return True
# stdout may not be a tty, so let's make sure it thinks it is self.patch(_options, "stdout", TTYFile())
options = TwistOptions() options.opt_log_file("-") # stdout, a tty options.selectDefaultLogObserver()
self.assertIdentical( options["fileLogObserverFactory"], textFileLogObserver ) self.assertEqual(options["logFormat"], "text")
def test_selectDefaultLogObserverDefaultWithoutTTY(self): """ L{TwistOptions.selectDefaultLogObserver} will not override an already selected observer. """ self.patchOpen()
options = TwistOptions() options.opt_log_file("queso") # File, not a tty options.selectDefaultLogObserver()
self.assertIdentical( options["fileLogObserverFactory"], jsonFileLogObserver ) self.assertEqual(options["logFormat"], "json")
def test_pluginsType(self): """ L{TwistOptions.plugins} is a mapping of available plug-ins. """ options = TwistOptions() plugins = options.plugins
for name in plugins: self.assertIsInstance(name, str) self.assertIsInstance(plugins[name], ServiceMaker)
def test_pluginsIncludeWeb(self): """ L{TwistOptions.plugins} includes a C{"web"} plug-in. This is an attempt to verify that something we expect to be in the list is in there without enumerating all of the built-in plug-ins. """ options = TwistOptions()
self.assertIn("web", options.plugins)
def test_subCommandsType(self): """ L{TwistOptions.subCommands} is an iterable of tuples as expected by L{twisted.python.usage.Options}. """ options = TwistOptions()
for name, shortcut, parser, doc in options.subCommands: self.assertIsInstance(name, str) self.assertIdentical(shortcut, None) self.assertTrue(callable(parser)) self.assertIsInstance(doc, str)
def test_subCommandsIncludeWeb(self): """ L{TwistOptions.subCommands} includes a sub-command for every plug-in. """ options = TwistOptions()
plugins = set(options.plugins) subCommands = set( name for name, shortcut, parser, doc in options.subCommands )
self.assertEqual(subCommands, plugins)
def test_postOptionsNoSubCommand(self): """ L{TwistOptions.postOptions} raises L{UsageError} is it has no sub-command. """ self.patchInstallReactor()
options = TwistOptions()
self.assertRaises(UsageError, options.postOptions)
|