Source code for setuptestx.testx

# -*- coding: utf-8 -*-
"""Runs unit tests of on multiple implementations and platforms.
Provides some essential features for multi-platform development:

* easy selection of the used implementation
* detailed information of the actual called implementation for the test cases

"""
from __future__ import absolute_import
from __future__ import print_function

import os
import sys
import distutils.cmd

import yapyutils.config.capabilities

import setuplibcore

from setuptestx import SetupTestXError

from pythonids import PYVxyz, PYV27

__author__ = 'Arno-Can Uestuensoez'
__author_email__ = 'acue_sf2@sourceforge.net'
__license__ = "Artistic-License-2.0 + Forced-Fairplay-Constraints"
__copyright__ = "Copyright (C) 2019 Arno-Can Uestuensoez @Ingenieurbuero Arno-Can Uestuensoez"
__uuid__ = "1936395c-9621-42df-b5ec-9c4df4f1ff49"

__version__ = "01.01.042"

__product_family__ = "setuplib"
__product__ = "testx"
__product_component__ = "testx"


[docs]class TestX(distutils.cmd.Command): """Calls *unittest* in the subdirectory *tests*.""" description = "Calls 'unittest' in the subdirectory 'tests'" user_options = [ ('abs', 'a', "change all paths where possible to absolute"), ('prog=', 'p', "call program for tests, default 'CallCase.py'"), ('implementation=', 'i', "the Python implementation, default is 'python' - CPython," " change to e.g. 'jython' - needs eventually additional options. " "Tested values are: python, jython, ipython, ipy(IronPython), pypy"), ('name=', None, "The name of the package. " "Default: attribute of derived class self.name"), ('start=', 's', "start package, e.g. 'tests' or 'tests.setuplib'"), ('testlib=', 'm', "test library, default 'unittest discover'"), ('noexec', 'n', "print only, do not execute"), ('print-env', None, "print subprocess environment after an exec call"), ('print-ver', None, "print the version of executed Python implementation"), ('print-vinfo', None, "print information about executed implementation"), ('verbose', None, "pass verbose flag, e.g. 'jython -v' or 'python -v'"), ('quiet', 'q', "silence the default verbosity, e.g. 'jython -q' or 'python -q'"), ('exe', 'c', """call provided string, e.g. python -c "print 'hello'" """), ('coff', 'C', "JYTHON: set cache off, sets '-Dpython.cachedir.skip=true'"), ('jyjar=', 'j', "JYTHON: switch to call of 'java -jar <jython.jar>', " "requires absolute path, " "default is 'jython', " "for 'jython' the default names are 'java -jar jython.jar', " "for a valid jar-file 'java -jar /my/path/jython.jar'" "- enabled by '-i jython',"), ('jyjvm=', 'J', "JYTHON: Java JVM options for Jyhon, " "e.g. '-J-Xmx512m' - enabled by '-i jython',"), ('jyprop=', 'D', "JYTHON: properties '<prop>=<value>', " "e.g '-Dpython.path=/my/path', enabled by '-i jython'"), ] #: The provided capabilities of the builder and it's components. #: The values are loaded dynamically during bootstrap where the #: enabled packages are loaded and initialized. #: Each component is responsible for it's own data which is dynamically #: updated in place. #: Only activated components are loaded, which is dynamically defined #: by the command line parameters. #: When missing the *default* item defines the active component. #: At least one subcommand component is required. capabilities = yapyutils.config.capabilities.Capability( { "master_data": { "author": __author__, "author_email": __author_email__, "license": __license__, "copyright": __copyright__, "uuid": __uuid__, "product_family": __product_family__, "product": __product__, "product_component": __product_component__, "component_version": __version__, "product_version": __version__, }, "components": { # see also 'user_options' "default": "testx", # call when no parameters are provided "testx": "testx", # call unittest in subdirectory "tests" }, "testx": { "status": True, # enables the component "package": None, # the implementation "exit": False, # exit after list "entry": True, # List entry points. "local": True, # List local defined commands. "standard": True, # List standard commands. "long": False, # List long format, similar to shell command 'ls -l'. "format": ( # Define display format. ), } } )
[docs] def initialize_options(self): self.debug = None self.noexec = None self.quiet = None self.verbose = None self.break_on_err = None self.abs = None self.call = None self.calljy = None self.coff = None self.exe = None self.implementation = None self.jyjar = None self.jyjvm = None self.jyprop = None self.name = None self.print_env = None self.print_ver = None self.print_vinfo = None self.prog = None self.start = None self.testlib = None self.postfix = None
[docs] def finalize_options(self): # scan for any context-help request _help_request = setuplibcore.check_for_context_help(self) if _help_request: print(_help_request) sys.exit(0) # quick-and-dirty hack to resolve the inconsistency of # global and local verbose values of distutils try: # The context option is actually not set by the framework, # instead the global option is reset and intialized to # the number of occurances and passes to the initialization # of the memeber 'self.verbose'. # Thus the poll fails, while the value is already set via the framework. # See code distutils.dist.Distribution. # Anyhow...keeping it as a reminder. _v_opts = self.distribution.get_option_dict('build_docx')['verbose'][1] if _v_opts: self.verbose += 1 except: # fallback to the slightly erroneous behavior when the interface # of distutils changes pass # global and local verbose values of distutils try: # See verbose for description of the global option quiet. # See code distutils.dist.Distribution. _q_opts = self.distribution.get_option_dict('build_docx')['quiet'][1] if _q_opts: self.quiet += 1 except: # fallback to the slightly erroneous behavior when the interface # of distutils changes if self.quiet == None: self.quiet = 0 pass if self.verbose != None: if os.name == 'java': # Jython - however - re-initializes the 'None' to '1', # when missing verbose parameter if not (set(sys.argv) & set(('-v', '--verbose'))): self.verbose -= 1 if self.quiet != None: # quiet dominates self.quiet = True self.verbose = 0 # debug if self.debug == None: self.debug = 0 # if self.verbose_ext == None: # self.verbose_ext = 0 if self.abs: self.abs = True if self.print_env != None: # environment self.print_env = True if self.print_ver != None: # version self.print_ver = True if self.print_vinfo != None: # sounding info self.print_vinfo = True if self.postfix == None: self.postfix = '_cases' #FIXME: # print("4TEST:sys.implementation = " + str(self.implementation)) if not self.implementation: # default is to use current called executable self.implementation = sys.executable # this is None in case of Jython!!! if not sys.executable and os.name == 'java': self.implementation = 'jython' # # check implementation to be used for tests via subprocess call # if ( # # hard-coded for standard names for now # egg-and-chicken for the process to be started # not doing it twice :-) # self.implementation.endswith('jython') or self.implementation.endswith('jython.exe') or self.implementation.endswith('.jar') ): self.calljy = True if self.calljy: # call Jython for subprocess - enables Jython and Java specific options if self.jyjar == None: if self.abs: self.implementation = os.path.abspath('jython') else: self.implementation = 'jython' elif self.jyjar: if self.abs: self.implementation = os.path.abspath('java') + ' -jar ' + str(self.jyjar) else: self.implementation = 'java -jar ' + str(self.jyjar) else: if self.abs: self.implementation = os.path.abspath('java') + ' -jar jython.jar ' else: self.implementation = 'java -jar jython.jar' else: # prohibit Jyhton and Java specific options if( self.jyjar or self.jyjvm or self.jyprop ): # has options for Java raise SetupTestXError("Java options require '-i=jython'") if not self.testlib: self.testlib = 'unittest discover' if self.noexec != None: self.noexec = True # # name of current package - serves as default for package subdiretory etc. # if self.name == None: self.name = self.distribution.metadata.name if not self.start: if os.path.exists('tests' + os.sep + self.name): # try literally self.start = 'tests.' + self.name elif os.path.exists('tests' + os.sep + 'py' + self.name + self.postfix): # check for python specific self.start = 'tests.py' + self.name else: # provoke the sounding error message self.start = 'tests.' + self.name if not self.prog: self.prog = 'CallCase.py'
[docs] def run(self): """Run command.""" command = [] command.append(str(self.implementation)) if self.calljy: if self.jyjvm: command.append("-J" + str(self.jyjvm)) if self.jyprop: command.append("-D" + str(self.jyprop)) if self.coff: command.append("-Dpython.cachedir.skip=true") for s in range(self.verbose): # want each separate command.append("-v") if self.print_env: if (PYVxyz&PYV27) == PYV27: command.append('''-c "import os;\nfor k in sorted(os.environ.keys()):\n\tprint '%-30s = %s' % (str(k), str(os.environ[k]))"''') else: command.append('''-c "import os;\nfor k in sorted(os.environ.keys()):\n\tprint('%-30s = %s' % (str(k), str(os.environ[k])))"''') elif self.print_ver: command.append('''--version''') elif self.print_vinfo: if (PYVxyz&PYV27) == PYV27: command.append('''-c "from pythonids.pythondist import PYDIST_DATA;print PYDIST_DATA.__str__()"''') else: command.append('''-c "from pythonids.pythondist import PYDIST_DATA;print(PYDIST_DATA.__str__())"''') elif self.call: command.append('-c "' + self.call + '"') elif self.exe: command.append('-c "' + self.exe + '"') else: command.append("-m " + str(self.testlib)) command.append("-s " + str(self.start)) command.append("-p " + str(self.prog)) if self.noexec: print(' '.join(command)) exit_code = 0 else: exit_code = os.system(' '.join(command)) sys.exit(exit_code)