From 13857700d38ed8a221f2353e927c8cd2d2df56c9 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Mon, 23 Jul 2012 19:23:04 +0530 Subject: [PATCH] Plugin-izing the testcase runner * setup.py is the installer for marvin-nose plugin * marvin-nose drive all tests using nose with other plugins like xunit, coverage, multiprocess execution etc * -n option deprecated. All tests driven by nosetests --with-marvin --- tools/marvin/marvin/deployAndRun.py | 22 +++------ tools/marvin/marvin/marvinPlugin.py | 73 ++++++++++++++++++++++++----- tools/marvin/marvin/setup.py | 29 ++++++++++++ 3 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 tools/marvin/marvin/setup.py diff --git a/tools/marvin/marvin/deployAndRun.py b/tools/marvin/marvin/deployAndRun.py index 31faa09c389..1c82d9fcc2e 100644 --- a/tools/marvin/marvin/deployAndRun.py +++ b/tools/marvin/marvin/deployAndRun.py @@ -17,7 +17,6 @@ import deployDataCenter import TestCaseExecuteEngine -import NoseTestExecuteEngine from optparse import OptionParser import os @@ -31,7 +30,6 @@ if __name__ == "__main__": parser.add_option("-t", "--client", dest="testcaselog", help="test case log file") parser.add_option("-l", "--load", dest="load", action="store_true", help="only load config, do not deploy, it will only run testcase") parser.add_option("-f", "--file", dest="module", help="run tests in the given file") - parser.add_option("-n", "--nose", dest="nose", action="store_true", help="run tests using nose") parser.add_option("-x", "--xml", dest="xmlrunner", help="use the xml runner to generate xml reports and path to store xml files") (options, args) = parser.parse_args() @@ -59,18 +57,10 @@ if __name__ == "__main__": parser.print_usage() exit(1) else: - if options.nose: - engine = NoseTestExecuteEngine.NoseTestExecuteEngine(deploy.testClient, testCaseLogFile, testResultLogFile, format, xmlDir) - engine.runTestsFromFile(options.module) - else: - engine = TestCaseExecuteEngine.TestCaseExecuteEngine(deploy.testClient, testCaseLogFile, testResultLogFile, format, xmlDir) - engine.loadTestsFromFile(options.module) - engine.run() + engine = TestCaseExecuteEngine.TestCaseExecuteEngine(deploy.testClient, testCaseLogFile, testResultLogFile, format, xmlDir) + engine.loadTestsFromFile(options.module) + engine.run() else: - if options.nose: - engine = NoseTestExecuteEngine.NoseTestExecuteEngine(deploy.testClient, clientLog=testCaseLogFile, resultLog=testResultLogFile, workingdir=options.testCaseFolder, format=format, xmlDir=xmlDir) - engine.runTests() - else: - engine = TestCaseExecuteEngine.TestCaseExecuteEngine(deploy.testClient, testCaseLogFile, testResultLogFile, format, xmlDir) - engine.loadTestsFromDir(options.testCaseFolder) - engine.run() + engine = TestCaseExecuteEngine.TestCaseExecuteEngine(deploy.testClient, testCaseLogFile, testResultLogFile, format, xmlDir) + engine.loadTestsFromDir(options.testCaseFolder) + engine.run() diff --git a/tools/marvin/marvin/marvinPlugin.py b/tools/marvin/marvin/marvinPlugin.py index 9d523621564..b99c14fa041 100644 --- a/tools/marvin/marvin/marvinPlugin.py +++ b/tools/marvin/marvin/marvinPlugin.py @@ -1,7 +1,10 @@ -from cloudstackTestCase import cloudstackTestCase +import marvin +import logging +import nose.core +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin import deployDataCenter from nose.plugins.base import Plugin from functools import partial -import logging def testCaseLogger(message, logger=None): if logger is not None: @@ -9,21 +12,69 @@ def testCaseLogger(message, logger=None): class MarvinPlugin(Plugin): """ - Custom test loader for the cloudstackTestCase to be loaded into nose + Custom plugin for the cloudstackTestCases to be run using nose """ name = "marvin" - def configure(self, options, conf): + def configure(self, options, config): self.enabled = 1 self.enableOpt = "--with-marvin" - return Plugin.configure(self, options, conf) + self.logformat = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + + if options.debug_log: + self.logger = logging.getLogger("NoseTestExecuteEngine") + self.debug_stream = logging.FileHandler(options.debug_log) + self.debug_stream.setFormatter(self.logformat) + self.logger.addHandler(self.debug_stream) + self.logger.setLevel(logging.DEBUG) + + if options.result_log: + ch = logging.StreamHandler() + ch.setLevel(logging.ERROR) + ch.setFormatter(self.logformat) + self.logger.addHandler(ch) + self.result_stream = open(options.result_log, "w") + else: + self.result_stream = sys.stderr + + deploy = deployDataCenter.deployDataCenters(options.config) + deploy.loadCfg() if options.load else deploy.deploy() + self.setClient(deploy.testClient) + + cfg = nose.config.Config() + cfg.logStream = self.result_stream + cfg.debugLog = self.debug_stream + cfg.workingDir = options.test_dir + + self.testrunner = nose.core.TextTestRunner(stream=self.result_stream, descriptions=True, verbosity=2, config=config) def options(self, parser, env): + """ + Register command line options + """ + parser.add_option("--marvin-config", action="store", + default=env.get('MARVIN_CONFIG', './datacenter.cfg'), + dest="config", + help="Marvin's configuration file where the datacenter information is specified [MARVIN_CONFIG]") + parser.add_option("--result-log", action="store", + default=env.get('RESULT_LOG', 'result.log'), + dest="result_log", + help="The path to the results file where test summary will be written to [RESULT_LOG]") + parser.add_option("--client-log", action="store", + default=env.get('DEBUG_LOG', 'debug.log'), + dest="debug_log", + help="The path to the testcase debug logs [DEBUG_LOG]") + parser.add_option("--load", action="store_true", default=False, dest="load", + help="Only load the deployment configuration given") + Plugin.options(self, parser, env) def __init__(self): Plugin.__init__(self) + def prepareTestRunner(self, runner): + return self.testrunner + def wantClass(self, cls): if issubclass(cls, cloudstackTestCase): return True @@ -36,15 +87,10 @@ class MarvinPlugin(Plugin): if client: self.testclient = client - def setClientLog(self, clientlog): - if clientlog: - self.log = clientlog - def _injectClients(self, test): - testcaselogger = logging.getLogger("testclient.testcase.%s" % test.__class__.__name__) - fh = logging.FileHandler(self.log) - fh.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")) - testcaselogger.addHandler(fh) + testcaselogger = logging.getLogger("testclient.testcase.%s" % test.__name__) + self.debug_stream.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")) + testcaselogger.addHandler(self.debug_stream) testcaselogger.setLevel(logging.DEBUG) setattr(test, "testClient", self.testclient) @@ -52,3 +98,4 @@ class MarvinPlugin(Plugin): setattr(test, "clstestclient", self.testclient) if hasattr(test, "UserName"): self.testclient.createNewApiClient(test.UserName, test.DomainName, test.AcctType) + diff --git a/tools/marvin/marvin/setup.py b/tools/marvin/marvin/setup.py new file mode 100644 index 00000000000..83977f4b78f --- /dev/null +++ b/tools/marvin/marvin/setup.py @@ -0,0 +1,29 @@ +import os +from setuptools import setup + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read().strip() + +VERSION = '0.1.0' + +setup( + name = "marvin-nose", + version = VERSION, + author = "Prasanna Santhanam", + author_email = "Prasanna.Santhanam@citrix.com", + description = "Run tests written using CloudStack's Marvin testclient", + license = 'ASL 2.0', + classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Testing", + "Programming Language :: Python", + ], + + py_modules = ['marvinPlugin'], + zip_safe = False, + + entry_points = { + 'nose.plugins': ['marvinPlugin = marvinPlugin:MarvinPlugin'] + }, + install_requires = ['nose', 'marvin'], +)