The BaseTest Class

In PySys each test is a class which contains the methods needed to execute your test logic and then to validate the results against the expected behaviour to product a final “outcome” for the test.

All test classes inherit (directly or indirectly) from the BaseTest class, which provides everything you need to create your own tests.

Implementing your test class

Each PySys test class must implement an execute method containing the main body of the test, and (usually) a validate method containing the assertions that check for the expected result. Sometimes you may also need to provide setup and/or cleanup functionality:

setup()

Contains setup actions to be executed before the test is executed.

execute()

The method tests implement to perform the test execution steps.

validate()

The method tests implement to perform assertions that check for the expected behaviour.

addCleanupFunction(fn)

Registers a function that will be called as part of the cleanup of this object.

You may wish to override the setup method if it is necessary to do some extra setup before beginning the test’s execution (for example, starting a server or provisioning a virtual machine). However, typically this would be worth doing only if you’re implementing a custom BaseTest subclass to make the functionality available to multiple individual test classes.

Taken together, the PySys setup() -> execute() -> validate() methods correspond to the common ‘Arrange, Act, Assert’ testing paradigm.

At the end of each test, PySys automatically terminates all processes it started, in the cleanup method. If any additional custom cleanup steps are required, these can be added by calling addCleanupFunction.

NB: Do not ever override the __init__ constructor of a test class; instead use setup for any initialization, which ensures orderly cleanup if an exception occurs during the setup process.

Test configuration

The input/output directories and other essential information about this test object and its parent project are can be accessed via instance attributes on self:

  • self.input (str): Full path to the input directory of the testcase, which is where test-specific resources such as configuration files are stored. The directory is usually <testdir>/Input but the path can be customized in the testcase descriptor.

    Here is an example of copying a configuration file template from the input directory:

    self.copy(self.input+'/serverconfig.xml', self.output, mappers=[lambda line: line.replace('@PORT@', str(myport))])
    
  • self.output (str): Full path to the output directory of the testcase, which is the only location to which the test should write output files, temporary files and logs. The output directory is automatically created and cleaned of existing content before the test runs to ensure separate runs do not influence each other. There are separate output directories when a test is run for multiple cycles or in multiple modes. The directory is usually <testdir>/Output/<outdir> but the path can be customized in the testcase descriptor.

  • self.reference (str): Full path to the reference directory of the testcase, which is where reference comparison files are stored (typically for use with assertDiff). The directory is usually <testdir>/Reference but the path can be customized in the testcase descriptor.

  • self.log (logging.Logger): The Python Logger instance that should be used to record progress and status information. For example:

    self.log.info("Starting myserver on port %d", serverport)
    
  • self.mode (str): The user-defined mode this test object is running. Tests can use this to modify how the test executed based upon the mode, for example to allow the test to run against either a mock or a real database.

  • self.testCycle (int): The cycle in which this test is running. Numbering starts from 1 in a multi-cycle test run. The special value of 0 is used to indicate that this is not part of a multi-cycle run.

  • self.descriptor (pysys.xml.descriptor.TestDescriptor): The descriptor contains information about this testcase such as the test id, groups and test type, and typically comes from the test’s pysystest.xml file.

  • self.runner (pysys.baserunner.BaseRunner): A reference to the singleton runner instance that’s responsible for orchestrating all the tests in this project. The runner is where any cross-test state can be held, for example if you want to have multiple tests share use of a single server, VM, or other resource.

  • self.project (pysys.xml.project.Project): A reference to the singleton project instance containing the configuration of this PySys test project as defined by pysysproject.xml. The project can be used to access information such as the project properties which are shared across all tests (e.g. for hosts and credentials).

  • self.disableCoverage (bool): Set this to True to request that no code coverage is generated for this test (even when code coverage has been enabled for the PySys run), for example because this is a time-sensitive or performance-measuring test. Typically this would be set in an individual testcase, or in the setup method of a BaseTest subclass based on groups in the self.descriptor that indicate what kind of test this is.

Assertions and outcomes

assertGrep(file[, filedir, expr, contains, …])

Perform a validation by checking for the presence or absence of a regular expression in the specified text file.

assertLineCount(file[, filedir, expr, …])

Perform a validation assert on the count of lines in a text file matching a specific regular expression.

assertDiff(file1[, file2, filedir1, …])

Perform a validation by comparing the contents of two text files, typically a file from the output directory against a file in the <testdir>/Reference/ directory containing the expected output.

assertThat(conditionstring, …)

Performs equality/range tests or any general-purpose validation by evaluating a Python eval() expression in the context of some named values.

assertLastGrep(file[, filedir, expr, …])

Perform a validation assert on a regular expression occurring in the last line of a text file.

assertOrderedGrep(file[, filedir, exprList, …])

Perform a validation assert on a list of regular expressions occurring in specified order in a text file.

assertPathExists(path[, exists, abortOnError])

Perform a validation that the specified file or directory path exists (or does not exist).

abort(outcome, outcomeReason[, callRecord])

Raise an AbortException with the specified outcome and reason.

skipTest(outcomeReason[, callRecord])

Raise an AbortException that will set the test outcome to SKIPPED and ensure that the rest of the execute() and validate() methods do not execute.

addOutcome(outcome[, outcomeReason, …])

Add a validation outcome (and optionally a reason string) to the validation list.

getOutcome()

Get the overall outcome based on the precedence order.

getOutcomeReason()

Get the reason string for the current overall outcome (if specified).

reportPerformanceResult(value, resultKey, unit)

Reports a new performance number to the performance csv file, with an associated unique string key that identifies it for comparison purposes.

PySys has a library of assertion methods that are great for typical system testing validation tasks such as checking log messages, diff’ing output files against a reference, and for more complex cases, evaluating arbitrary Python expressions. All these methods are designed to give a really clear outcome reason if the assertion fails. Note that by default assertion failures do not abort the test, so all the validation statements will be executed even if some early ones fail.

For example:

def validate(self):
        self.assertGrep('myserver.log', expr='Successfully authenticated user ".*"')
        self.assertThat('actualNumberOfLogFiles == expected', actualNumberOfLogFiles=len(glob.glob(self.output+'/myserver*.log')), expected=3)

The available outcomes for each assertion are contained in the pysys.constants module:

pysys.constants.PASSED

Non-failure test outcome indicating successful validation steps.

pysys.constants.NOTVERIFIED

Non-failure test outcome indicating that it was not possible to positively validate correct operation.

pysys.constants.FAILED

Failure test outcome indicating validation steps with a negative outcome.

pysys.constants.TIMEDOUT

Failure test outcome indicating that the test timed out while performing execution or validation operations.

pysys.constants.DUMPEDCORE

Failure test outcome indicating that a crash occurred, and a core file was generated (UNIX only).

pysys.constants.BLOCKED

Failure test outcome indicating that something went wrong, for example an exception was raised by the testcase or a required file could not be found.

pysys.constants.SKIPPED

Non-failure test outcome indicating that the test was ignored as it is not currently required to run on this platform/mode.

There are some deprecated methods which we do not recommend using: assertEval, assertTrue, assertFalse (assertThat should be used instead of these).

Processes

startProcess(command, arguments[, environs, …])

Start a process running in the foreground or background, and return the pysys.process.commonwrapper.CommonProcessWrapper object.

startPython(arguments[, disableCoverage])

Start a Python process with the specified arguments.

getNextAvailableTCPPort([hosts, …])

Allocate a free TCP port which can be used for starting a server on this machine.

allocateUniqueStdOutErr(processKey)

Allocate unique filenames of the form processKey[.n].out/.err which can be used for the startProcess stdouterr parameter.

createEnvirons([overrides, addToLibPath, …])

Create a new customized dictionary of environment variables suitable for passing to startProcess()’s environs= argument.

getDefaultEnvirons([command])

Create a new dictionary of environment variables, suitable for passing to startProcess(), with a minimal clean set of environment variables for this platform, unaffected (as much as possible) by the environment that the tests are being run under.

signalProcess(process, signal[, abortOnError])

Send a signal to a running process.

stopProcess(process[, abortOnError])

Stops the specified process, if it is currently running.

writeProcess(process, data[, addNewLine])

Write binary data to the stdin of a process.

startProcessMonitor(process[, interval, …])

Start a background thread to monitor process statistics such as memory and CPU usage.

stopProcessMonitor(monitor)

Request a process monitor to stop.

The most important part of a test’s execute method is starting the process(es) that you are testing. Always use the startProcess method for this (or a wrapper such as startPython) rather than os.system or subprocess, to ensure that PySys can cleanup all test processes at the end of the test.

This class also provides methods to dynamically allocate a free TCP server port, and to monitor the memory/CPU usage of the processes it starts.

Waiting

PySys provides a number of methods that can be used in your execute method to wait for operations to complete, of which the most commonly used is waitForGrep.

waitForGrep(file[, expr, condition, …])

Wait for a regular expression line to be seen (one or more times) in a text file in the output directory (waitForGrep was formerly known as waitForSignal).

waitForFile(file[, filedir, timeout, …])

Wait for a file to exist on disk.

waitProcess(process, timeout[, abortOnError])

Wait for a background process to terminate, completing on termination or expiry of the timeout.

waitForSocket(port[, host, timeout, …])

Wait until it is possible to establish a socket connection to a server running on the specified local or remote port.

wait(interval)

Wait for a specified period of time, and log a message to indicate this is happening.

Do not use wait unless there is no alternative, since it makes tests both slower and more fragile than they ought to be.

Files

The helper methods for common file I/O operations are:

copy(src, dest[, mappers, encoding])

Copy a single text or binary file, optionally tranforming the contents by filtering each line through a list of mapping functions.

mkdir(path)

Create a directory, with recursive creation of any parent directories.

deleteDir(path, **kwargs)

Recursively delete the specified directory.

getExprFromFile(path, expr[, groups, …])

Searches for a regular expression in the specified file, and returns it.

logFileContents(path[, includes, excludes, …])

Logs some or all of the lines in the specified file.

write_text(file, text[, encoding])

Writes the specified characters to a file in the output directory.

getDefaultFileEncoding(file, **xargs)

Specifies what encoding should be used to read or write the specified text file.

To get the configured input/output directories for this test see Test configuration.

Miscellaneous

compareVersions(v1, v2)

Compares two alphanumeric dotted version strings to see which is more recent.

getBoolProperty(propertyName[, default])

Get a True/False indicating whether the specified property is set on this object (typically as a result of specifying -X on the command line), or else from the project configuration.

startBackgroundThread(name, target[, …])

Start a new background thread that will invoke the specified target function.

pythonDocTest(pythonFile[, pythonPath, …])

Execute the Python doctests that exist in the specified python file; adds a FAILED outcome if any do not pass.

startManualTester(file[, filedir, state, …])

Start the manual tester, which provides a UI to guide a human through the tests needed to implement this testcase.

stopManualTester()

Stop the manual tester if running.

waitManualTester([timeout])

Wait for the manual tester to be stopped via user interaction.

All BaseTest members

class pysys.basetest.BaseTest(descriptor, outsubdir, runner)[source]

Bases: pysys.process.user.ProcessUser

BaseTest is the base class of every individual PySys test class, and contains the methods needed to execute your test logic and then to validate the results against the expected behaviour.

Apart from the addOutcome method this class is not thread-safe, so if you need to access it from multiple threads be sure to add your own locking around use of its fields and methods, including any cleanup functions.

abort(outcome, outcomeReason, callRecord=None)

Raise an AbortException with the specified outcome and reason.

See also skipTest.

Parameters
  • outcome – The outcome, which will override any existing outcomes previously recorded.

  • outcomeReason – A string summarizing the reason for the outcome.

addCleanupFunction(fn)

Registers a function that will be called as part of the cleanup of this object.

Cleanup functions should have no arguments, and are invoked in reverse order with the most recently added first (LIFO), and before the automatic termination of any remaining processes associated with this object.

e.g. self.addCleanupFunction(lambda: self.cleanlyShutdownProcessX(params))

addOutcome(outcome, outcomeReason='', printReason=True, abortOnError=False, callRecord=None, override=False)

Add a validation outcome (and optionally a reason string) to the validation list.

The method provides the ability to add a validation outcome to the internal data structure storing the list of validation outcomes. Multiple validations may be performed, the current supported validation outcomes of which are described in Assertions and outcomes.

The outcomes are considered to have a precedence order, as defined by the order of the outcomes listed above. Thus a pysys.constants.BLOCKED outcome has a higher precedence than a pysys.constants.PASSED outcome. The outcomes are defined in pysys.constants.

This method is thread-safe.

Although this method exists on all subclasses of pysys.process.user.ProcessUser, in practice only pysys.basetest.BaseTest subclasses actually do anything with the resulting outcome.

Parameters
  • outcome – The outcome to add, e.g. pysys.constants.FAILED.

  • outcomeReason – A string summarizing the reason for the outcome, for example “Grep on x.log contains ‘ERROR: server failed’”.

  • printReason – If True the specified outcomeReason will be printed

  • abortOnError – If true abort the test on any error outcome. This should usually be set to False for assertions, or the configured self.defaultAbortOnError setting (typically True) for operations that involve waiting.

  • callRecord – An array of strings indicating the call stack that lead to this outcome. This will be appended to the log output for better test triage.

  • override – Remove any existing test outcomes when adding this one, ensuring that this outcome is the one and only one reported even if an existing outcome has higher precedence.

addResource(resource)[source]

Add a resource which is owned by the test and is therefore cleaned up (deleted) when the test is cleaned up.

Deprecated

Please use addCleanupFunction instead of this function.

allocateUniqueStdOutErr(processKey)

Allocate unique filenames of the form processKey[.n].out/.err which can be used for the startProcess stdouterr parameter.

The first time this is called it will return names like ('myprocess.out', 'myprocess.err'), the second time it will return ('myprocess.1.out', 'myprocess.1.err'), then ('myprocess.2.out', 'myprocess.2.err') etc.

Parameters

processKey (str) – A user-defined identifier that will form the prefix onto which [.n].out is appended

Returns

A STDOUTERR_TUPLE named tuple of (stdout, stderr)

Return type

STDOUTERR_TUPLE

assertDiff(file1, file2=None, filedir1=None, filedir2=None, ignores=[], sort=False, replace=[], includes=[], encoding=None, abortOnError=False, assertMessage=None, stripWhitespace=None)[source]

Perform a validation by comparing the contents of two text files, typically a file from the output directory against a file in the <testdir>/Reference/ directory containing the expected output.

Differences in line ending are always ignored, and depending on the value of stripWhitespace leading and trailing whitespace may also be ignored.

The files can be pre-processed prior to the comparison to either ignore particular lines, sort their constituent lines, replace matches to regular expressions in a line with an alternate value, or to only include particular lines. However, it is often easier to instead use copy to perform the transformations (e.g. stripping out timestamps, finding lines of interest etc) and then separately call assertDiff on the file generated by copy. This makes it easier to generate a suitable reference file and to diagnose test failures. For example:

self.assertDiff(self.copy('myfile.txt', 'myfile-preprocessed.txt', ...), 'myfile-reference.txt')

Should the files after pre-processing be equivalent a PASSED outcome is added to the test outcome list, otherwise a FAILED outcome is added.

If you have a large set of test reference files which need to be updated after a behaviour or output formatting change, you can use a special command line option which makes assertDiff overwrite the reference files with a copy of the actual comparison file in cases where the diff would otherwise fail. Use this feature with caution, since it overwrites reference files with no backup. In particular, make sure you have committed all reference files to version control before running the command, and the afterwards be sure to carefully check the resulting diff to make sure the changes were as expected before committing. To use this feature, run:

pysys.py run -XautoUpdateAssertDiffReferences
Parameters
  • file1 (str) – The actual (or first) file to be compared; can be an absolute path or relative to the test output directory.

  • file2 (str) – The expected/reference (or second) file to be compared; can be an absolute path or relative to the Reference directory. The default is for file2 to be the same basename as file1.

  • filedir1 (str) – The dirname of the first file (defaults to the testcase output subdirectory)

  • filedir2 (str) – The dirname of the second file (defaults to the testcase reference directory)

  • ignores (list[str]) – A list of regular expressions used to denote lines in the files which should be ignored

  • sort (bool) – Boolean flag to indicate if the lines in the files should be sorted prior to the comparison

  • list[(regexp:str,repl:str),..]replace – List of tuples of the form (‘regexpr’, ‘replacement’). For each regular expression in the list, any occurences in the files are replaced with the replacement value prior to the comparison being carried out. This is often useful to replace timestamps in logfiles etc.

  • stripWhitespace (bool) – If True, every line has leading and trailing whitespace stripped before comparison, which means indentation differences and whether the file ends with a blank line do not affect the outcome. If the value is None, delegates to the value of the project property defaultAssertDiffStripWhitespace (which is True for old projects, but recommended to be False for new projects).

  • includes (list[str]) – A list of regular expressions used to denote lines in the files which should be used in the comparison. Only lines which match an expression in the list are used for the comparison.

  • encoding (str) – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

  • abortOnError (bool) – Set to True to make the test immediately abort if the assertion fails.

  • assertMessage (str) – Overrides the string used to describe this assertion in log messages and the outcome reason.

Returns

True if the assertion succeeds, False if a failure outcome was appended.

assertEval(evalstring, abortOnError=False, **formatparams)[source]

Perform a validation by substituting named {} placeholder values into a Python expression such as {expected}=={actual} or 4 <= {actual} <= 10.

Deprecated:

Deprecated since 1.5.1 in favour of assertThat which is now significantly more powerful and should be used for new tests.

Example use:

self.assertEval('os.path.getsize({filename}) > {origFileSize}',         
        filename=self.output+'/file.txt', origFileSize=1000)
Parameters
  • evalstring

    a string that will be formatted using .format(...) with the specified parameters, and result in failure outcome if not true.

    Parameters should be specified using {name} syntax, and quoting is not required as string values are automatically escaped using repr. e.g. ‘os.path.size({filename}) > {origFileSize}'.

    Do not use an f-string instead of explicitly passing formatparams, as with an f-string this method will not know the names of the substituted parameters which makes the intention of the assertion harder to understand from looking at the test output.

    The global environment used for evaluation includes the os.path, math, sys, re, and locale standard Python modules, as well as the pysys module and the contents of the pysys.constants module, e.g. IS_WINDOWS, and also the BaseTest’s self variable.

  • formatparams

    Named parameters for the format string, which can be of any type. Use descriptive names for the parameters to produce an assertion message that makes it really clear what is being checked.

    String parameters will be automatically passed through repr() before being formatted, so there is no need to perform additional quoting or escaping of strings.

  • abortOnError – Set to True to make the test immediately abort if the assertion fails. Unless abortOnError=True this method only throws an exception if the format string is invalid; failure to execute the eval(…) results in a BLOCKED outcome but no exception.

Returns

True if the assertion succeeds, False if a failure outcome was appended.

assertFalse(expr, abortOnError=False, assertMessage=None)[source]

Perform a validation assert on the supplied expression evaluating to false.

Deprecated

Use assertThat instead of this method, which produces clearer messages if the assertion fails.

If the supplied expression evaluates to false a PASSED outcome is added to the outcome list. Should the expression evaluate to true, a FAILED outcome is added.

Parameters
  • expr – The expression to check for the true | false value

  • abortOnError – Set to True to make the test immediately abort if the assertion fails.

  • assertMessage – Overrides the string used to describe this assertion in log messages and the outcome reason.

Returns

True if the assertion succeeds, False if a failure outcome was appended.

assertGrep(file, filedir=None, expr='', contains=True, ignores=None, literal=False, encoding=None, abortOnError=False, assertMessage=None, reFlags=0)[source]

Perform a validation by checking for the presence or absence of a regular expression in the specified text file.

This method searches through the specified text file until it finds a line matching the regular expression. When contains=True it adds a PASSED outcome if found or a FAILED outcome if not found (except when where are named groups in the expression in which case BLOCKED is used to indicate that the return value is not valid). When contains=False this is inverted so a PASSED outcome is added if not found and FAILED if found.

For example:

self.assertGrep('myserver.log', expr=' ERROR .*', contains=False)

# in Python 3+, f-Strings can be used to substitute in parameters:
self.assertGrep('myserver.log', expr=f'Successfully authenticated user "{re.escape(username)}" in .* seconds\.')

# If you need to use ``\`` regular epression escapes use a raw string to avoid double-escaping
self.assertGrep('myserver.log', expr=r'c:\Fooar\.txt')

You can get more descriptive failure messages, and also do more sophisticated checking of results, by using one or more (?P<groupName>...) named groups in the expression to extract information. A common pattern is to unpack the resulting dict using **kwargs syntax and pass to BaseTest.assertThat. For example, the grep shown above for checking that a log message contains the correct username could be rewritten as:

self.assertThat('username == expected', expected='myuser',
        **self.assertGrep('myserver.log', expr=r'Successfully authenticated user "(?P<username>[^"]*)"'))

self.assertThat('0 <= float(authSecs) < max', max=MAX_AUTH_TIME,
        **self.assertGrep('myserver.log', expr=r'Successfully authenticated user "[^"]*)" in (?P<authSecs>[^ ]+) seconds\.'))

The behaviour of the regular expression can be controlled using reFlags=. For example, to perform case-insensitive matching and to use Python’s verbose regular expression syntax which permits whitespace and comments:

self.assertGrep('myserver.log', reFlags=re.VERBOSE | re.IGNORECASE, expr=r"""
        in\   
        \d +  # the integral part
        \.    # the decimal point
        \d *  # some fractional digits
        \ seconds\. # in verbose regex mode we escape spaces with a slash
        """)

Changed in version 1.5.1: The return value and reFlags were added in 1.5.1.

Parameters
  • file – The name or relative/absolute path of the file to be searched.

  • expr (str) –

    The regular expression to check for in the file (or a string literal if literal=True), for example " ERROR .*".

    It’s sometimes helpful to use a ‘raw’ Python string so that you don’t need to double-escape slashes intended for the regular expression parser, e.g. self.assertGrep(..., expr=r'c:\Fooar\.txt').

    If you wish to do something with the text inside the match you can use the re named group syntax (?P<groupName>...) to specify a name for parts of the regular expression.

    For contains=False matches, you should end the expr with * if you wish to include just the matching text in the outcome failure reason. If contains=False and expr does not end with a * then the entire matching line will be included in the outcome failure reason.

  • contains (bool) – Boolean flag to specify if the expression should or should not be seen in the file.

  • ignores (list[str]) – Optional list of regular expressions that will be ignored when reading the file.

  • literal (bool) – By default expr is treated as a regex, but set this to True to pass in a string literal instead.

  • encoding (str) – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

  • abortOnError (bool) – Set to True to make the test immediately abort if the assertion fails.

  • assertMessage (str) – Overrides the string used to describe this assertion in log messages and the outcome reason.

  • filedir (str) – The directory of the file (defaults to the testcase output subdirectory); this is deprecated, as it’s simpler to just include the directory in the file parameter.

  • reFlags (int) –

    Zero or more flags controlling how the behaviour of regular expression matching, combined together using the | operator, for example reFlags=re.VERBOSE | re.IGNORECASE.

    For details see the re module in the Python standard library. Note that re.MULTILINE cannot be used because expressions are matched against one line at a time. Added in PySys 1.5.1.

Returns

The re.Match object, or None if there was no match (note the return value is not affected by the contains=True/False parameter).

However if the expr contains any (?P<groupName>...) named groups, then a dict is returned containing dict(groupName: str, matchValue: str or None) (or an empty {} dict if there is no match) which allows the assertGrep result to be passed to assertThat for further checking (typically unpacked using the ** operator; see example above).

assertLastGrep(file, filedir=None, expr='', contains=True, ignores=[], includes=[], encoding=None, abortOnError=False, assertMessage=None, reFlags=0)[source]

Perform a validation assert on a regular expression occurring in the last line of a text file.

When the contains input argument is set to true, this method will add a PASSED outcome to the test outcome list if the supplied regular expression is seen in the file; otherwise a FAILED outcome is added. Should contains be set to false, a PASSED outcome will only be added should the regular expression not be seen in the file.

Parameters
  • file – The basename of the file used in the grep

  • filedir – The dirname of the file (defaults to the testcase output subdirectory)

  • expr – The regular expression to check for in the last line of the file

  • contains – Boolean flag to denote if the expression should or should not be seen in the file

  • ignores – A list of regular expressions used to denote lines in the file which should be ignored

  • includes – A list of regular expressions used to denote lines in the file which should be used in the assertion.#

  • encoding – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

  • abortOnError – Set to True to make the test immediately abort if the assertion fails.

  • assertMessage – Overrides the string used to describe this assertion in log messages and the outcome reason.

  • reFlags (int) –

    Zero or more flags controlling how the behaviour of regular expression matching, combined together using the | operator, for example reFlags=re.VERBOSE | re.IGNORECASE.

    For details see the re module in the Python standard library. Note that re.MULTILINE cannot be used because expressions are matched against one line at a time. Added in PySys 1.5.1.

Returns

The re.Match object, or None if there was no match (note the return value is not affected by the contains=True/False parameter).

However if the expr contains any (?P<groupName>...) named groups, then a dict is returned containing dict(groupName: str, matchValue: str or None) (or an empty {} dict if there is no match) which allows the result to be passed to assertThat for further checking (typically unpacked using the ** operator; see assertGrep for a similar example).

assertLineCount(file, filedir=None, expr='', condition='>=1', ignores=None, encoding=None, abortOnError=False, assertMessage=None, reFlags=0)[source]

Perform a validation assert on the count of lines in a text file matching a specific regular expression.

This method will add a PASSED outcome to the outcome list if the number of lines in the input file matching the specified regular expression evaluate to true when evaluated against the supplied condition.

Parameters
  • file – The basename of the file used in the line count

  • filedir – The dirname of the file (defaults to the testcase output subdirectory)

  • expr – The regular expression string used to match a line of the input file

  • condition – The condition to be met for the number of lines matching the regular expression

  • ignores – A list of regular expressions that will cause lines to be excluded from the count

  • encoding – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

  • abortOnError – Set to True to make the test immediately abort if the assertion fails.

  • assertMessage – Overrides the string used to describe this assertion in log messages and the outcome reason.

  • reFlags (int) –

    Zero or more flags controlling how the behaviour of regular expression matching, combined together using the | operator, for example reFlags=re.VERBOSE | re.IGNORECASE.

    For details see the re module in the Python standard library. Note that re.MULTILINE cannot be used because expressions are matched against one line at a time. Added in PySys 1.5.1.

Returns

True if the assertion succeeds, False if a failure outcome was appended.

assertOrderedGrep(file, filedir=None, exprList=[], contains=True, encoding=None, abortOnError=False, assertMessage=None, reFlags=0)[source]

Perform a validation assert on a list of regular expressions occurring in specified order in a text file.

When the contains input argument is set to true, this method will append a PASSED outcome to the test outcome list if the supplied regular expressions in the exprList are seen in the file in the order they appear in the list; otherwise a FAILED outcome is added. Should contains be set to false, a PASSED outcome will only be added should the regular expressions not be seen in the file in the order they appear in the list.

While this assertion method can be very convenient for checking the order of a small number of expressions, it becomes unwieldy when the number of expressions grows beyond a handful, and this is definitely not the best tool for the job if what you’re doing is really about checking that a subset of data from an output file matches expectations. For that use case, it’s better to do a filtered copy() of the file to remove prefixes (e.g. timestamps) and lines that are not important, and then use assertDiff to check the extracted text matches expectations. This approach makes it easier to write tests, and crucially makes it much easier to figure out what went wrong if they fail.

Parameters
  • file – The basename of the file used in the ordered grep

  • filedir – The dirname of the file (defaults to the testcase output subdirectory)

  • exprList – A list of regular expressions which should occur in the file in the order they appear in the list

  • contains – Boolean flag to denote if the expressions should or should not be seen in the file in the order specified

  • encoding – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

  • abortOnError – Set to True to make the test immediately abort if the assertion fails.

  • assertMessage – Overrides the string used to describe this assertion in log messages and the outcome reason.

  • reFlags (int) –

    Zero or more flags controlling how the behaviour of regular expression matching, combined together using the | operator, for example reFlags=re.VERBOSE | re.IGNORECASE.

    For details see the re module in the Python standard library. Note that re.MULTILINE cannot be used because expressions are matched against one line at a time. Added in PySys 1.5.1.

Returns

True if the assertion succeeds, False if a failure outcome was appended.

assertPathExists(path, exists=True, abortOnError=False)[source]

Perform a validation that the specified file or directory path exists (or does not exist).

Parameters
  • path – The path to be checked. This can be an absolute path or relative to the testcase output directory.

  • exists – True if the path is asserted to exist, False if it should not.

  • abortOnError – Set to True to make the test immediately abort if the assertion fails.

Returns

True if the assertion succeeds, False if a failure outcome was appended.

assertThat(conditionstring, *positional_arguments, **kwargs)[source]

Performs equality/range tests or any general-purpose validation by evaluating a Python eval() expression in the context of some named values.

For example:

# Equality comparison of an 'actual' vs 'expected' message from when our server started; 
# note the use of a descriptive name for the 'actualXXX=' keyword to produce a nice clear message if it fails
self.assertThat("actualStartupMessage == expected", expected='Started successfully', actualStartupMessage=msg)
self.assertThat('actualUser == expected', expected='myuser', actualUser=user)

# This produces the self-describing log messages like: 
#   Assert that (actualStartupMessage == expected) with expected='Started successfully', actualStartupMessage='Started unsuccessfully' ... passed

# Any valid Python expression is permitted (not only equality testing):
self.assertThat("actualStartupMessage.endswith('successfully')", actualStartupMessage=msg)
self.assertThat("re.match(expected, actualStartupMessage)", expected=".* successfully", actualStartupMessage=msg)
self.assertThat("(0 <= actualValue < max) and type(actualValue)!=float", actualValue=v, max=100)

This method is powerful enough for almost any validation that the other assert methods don’t handle, and by enforcing the discipline of naming values it generates self-describing log messages and outcome reasons to make it really obvious what is going on. For best results, make sure that your keyword= parameters have clear and unique names so it’s obvious how each assertThat() differs from the other ones, and ensure that all values you’re going to want to see are included as one of the named parameter values (rather than buried deep inside the main conditionstring).

The condition string is just a Python expression, which will be passed to eval() and can use any of the keyword= argument values passed to this method (but not the caller’s local variables). The evaluation is performed in a namespace that also includes the current BaseTest instance (self), some standard Python modules (os.path, math, sys, re, and locale), the pysys module, and the contents of the pysys.constants module, e.g. IS_WINDOWS. If necessary, symbols for additional modules can be imported dynamically using import_module(). For example:

self.assertThat("IS_WINDOWS or re.match(expected, actual)", actual="foo", expected="f.*")
self.assertThat("import_module('tarfile').is_tarfile(self.output+file) is False", file='/foo.zip')

Sometimes the differences between assertThat expressions are hard to describe in the parameter names themselves, and for these cases you can get self-describing behaviour with a parameter ending in the suffix __eval whose value is itself a Python expression to be evaluated, using any local variable in the namespace of the calling code, for example:

myDataStructure = ...
self.assertThat("actual == expected", actual__eval="myDataStructure['item1'][-1].getId()", expected="foo")
self.assertThat("actual == expected", actual__eval="myDataStructure['item2'][-1].getId()", expected="bar")
self.assertThat("actual == expected", actual__eval="myDataStructure['item3'][-1].getId()", expected="baz")

# Produces self-describing log messages like this:
#  Assert that (actual == expected) with actual (myDataStructure['item1'][-1].getId()) ='foo', expected='foo' ... passed
#  Assert that (actual == expected) with actual (myDataStructure['item2'][-1].getId()) ='bar', expected='bar' ... passed
#  Assert that (actual == expected) with actual (myDataStructure['item3'][-1].getId()) ='baZaar', expected='baz' ... failed
#       actual: 'baZaar'
#     expected: 'baz'
#                 ^

As shown above, when two named parameters are provided and the condition string is a simple equality comparison (== or is), additional lines are logged if the assertion fails to show at what point the two arguments differ (based on finding the longest common substring). So it’s a good idea to include both the actual and expected value as named parameters rather than as literals inside the condition string.

Changed in version 1.5.1: The ability to pass named keyword= parameters was added in 1.5.1 (prior to that this method was deprecated).

Parameters
  • conditionstring (str) –

    A string containing Python code that will be evaluated using eval() as a boolean expression, for example actualXXX == expected, where XXX is a brief description of what value is being tested.

    It’s best to put expected values into a separate named parameter (rather than using literals inside the conditionstring), since this will produce more informative messages if there is a failure.

    Do not be tempted to use a Python f-string here, as that would deprive PySys of the opportunity to provide a self-describing message and outcome reason.

  • **kwargs – All additional keyword arguments are treated as values which will be made available when evaluating the condition string. Any keyword ending in the special suffix __eval will be treated as a Python expression string (rather than a string literal) and will be be evaluated in a namespace containing the local variables of the calling code and (on Python 3.6+) any preceding named parameters.

  • *positional_arguments – (deprecated) Unnamed positional arguments will be substituted into the condition string using the old % format string mechanism, before it is evaluated. This feature is deprecated as it provides poor diagnostic information, requires string parameters to be explicitly escaped using repr(), and only permits stringifiable data structures to be used by the conditionstring. Instead use named keyword= in all new tests.

  • abortOnError=False – Set to True to make the test immediately abort if the assertion fails. By default this method produces a BLOCKED output but does not throw if the eval(…) cannot be executed.

  • assertMessage='' – Overrides the string used to describe this assertion in log messages and the outcome reason. We do not recommend using this as the automatically generated assertion message is usually clearer.

Returns

True if the assertion succeeds, False if a failure outcome was appended (and abortOnError=False).

assertTrue(expr, abortOnError=False, assertMessage=None)[source]

Perform a validation assert on the supplied expression evaluating to true.

Deprecated

Use assertThat instead of this method, which produces clearer messages if the assertion fails.

If the supplied expression evaluates to true a PASSED outcome is added to the outcome list. Should the expression evaluate to false, a FAILED outcome is added.

Parameters
  • expr – The expression, as a boolean, to check for the True | False value

  • abortOnError – Set to True to make the test immediately abort if the assertion fails.

  • assertMessage – Overrides the string used to describe this assertion in log messages and the outcome reason.

Returns

True if the assertion succeeds, False if a failure outcome was appended.

cleanup()[source]

Contains cleanup actions to be executed after the test’s execute and validate methods have completed.

The cleanup method automatically stops all processes that are still still running (assuming they were started with startProcess). It also stops all process monitors running in separate threads, and any instances of the manual tester user interface.

If any custom cleanup is required, use addCleanupFunction instead of overriding this method.

static compareVersions(v1, v2)

Compares two alphanumeric dotted version strings to see which is more recent.

Example usage:

if self.compareVersions(thisversion, '1.2.alpha-3') > 0:
        ... # thisversion is newer than 1.2.alpha-3 

The comparison algorithm ignores case, and normalizes separators ./-/_ so that '1.alpha2'=='1Alpha2'. Any string components are compared lexicographically with other strings, and compared to numbers strings are always considered greater.

>>> ProcessUser.compareVersions('10-alpha5.dev10', '10alpha-5-dEv_10') == 0 # normalization of case and separators
True
>>> ProcessUser.compareVersions(b'1....alpha.2', u'1Alpha2') == 0 # ascii byte and unicode strings both supported
True
>>> ProcessUser.compareVersions('1.2.0', '1.2')
0
>>> ProcessUser.compareVersions('1.02', '1.2')
0
>>> ProcessUser().compareVersions('1.2.3', '1.2') > 0
True
>>> ProcessUser.compareVersions('1.2', '1.2.3')
-1
>>> ProcessUser.compareVersions('10.2', '1.2')
1
>>> ProcessUser.compareVersions('1.2.text', '1.2.0') # letters are > numbers
1
>>> ProcessUser.compareVersions('1.2.text', '1.2') # letters are > numbers 
1
>>> ProcessUser.compareVersions('10.2alpha1', '10.2alpha')
1
>>> ProcessUser.compareVersions('10.2dev', '10.2alpha') # letters are compared lexicographically
1
>>> ProcessUser.compareVersions('', '')
0
>>> ProcessUser.compareVersions('1', '')
1
Parameters
  • v1 – A string containing a version number, with any number of components.

  • v2 – A string containing a version number, with any number of components.

Returns

an integer > 0 if v1>v2, an integer < 0 if v1<v2, or 0 if they are semantically the same.

copy(src, dest, mappers=[], encoding=None)

Copy a single text or binary file, optionally tranforming the contents by filtering each line through a list of mapping functions.

If any mappers are provided, the file is copied in text mode and each mapper is given the chance to modify or omit each line. If no mappers are provided, the file is copied in binary mode.

In addition to the file contents the mode is also copied, for example the executable permission will be retained.

This function is useful both for creating a modified version of an output file that’s more suitable for later validation steps such as diff-ing, and also for copying required files from the input to the output directory.

For example:

self.copy('output-raw.txt', 'output-processed.txt', encoding='utf-8', 
        mappers=[
                lambda line: None if ('Timestamp: ' in line) else line, 
                lambda line: line.replace('foo', 'bar'), 
        ])
Parameters
  • src – The source filename, which can be an absolute path, or a path relative to the self.output directory. Use src=self.input+'/myfile' if you wish to copy a file from the test input directory.

  • dest – The source filename, which can be an absolute path, or a path relative to the self.output directory. If this is a directory name, the file is copied to this directory with the same basename as src.

  • mappers – A list of filter functions that will be applied, in order, to each line read from the file. Each function accepts a string for the current line as input and returns either a string to write or None if the line is to be omitted.

  • encoding – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

Returns

the absolute path of the destination file.

createEnvirons(overrides=None, addToLibPath=[], addToExePath=[], command=None, **kwargs)

Create a new customized dictionary of environment variables suitable for passing to startProcess()’s environs= argument.

As a starting point, this method uses the value returned by getDefaultEnvirons() for this command. See the documentation on that method for more details. If you don’t care about minimizing the risk of your local environment affecting the test processes you start, just use environs=os.environ to allow child processes to inherit the entire parent environment instead of using this method.

Parameters
  • overrides – A dictionary of environment variables whose values will be used instead of any existing values. You can use os.getenv('VARNAME','') if you need to pass selected variables from the current process as part of the overrides list. If the value is set to None then any variable of this name will be deleted. Use unicode strings if possible (byte strings will be converted depending on the platform). A list of dictionaries can be specified, in which case the latest will override the earlier if there are any conflicts.

  • addToLibPath – A path or list of paths to be prepended to the default value for the environment variable used to load libraries (or the value specified in overrides, if any), i.e. [DY]LD_LIBRARY_PATH on Unix or PATH on Windows. This is usually more convenient than adding it directly to overrides.

  • addToExePath – A path or list of paths to be prepended to the default value for the environment variable used to locate executables (or the value specified in overrides, if any), i.e. PATH on both Unix and Windows. This is usually more convenient than adding it directly to overrides.

  • command – If known, the full path of the executable for which a default environment is being created (passed to getDefaultEnvirons).

  • kwargs – Overrides of this method should pass any additional kwargs down to the super implementation, to allow for future extensions.

Returns

A new dictionary containing the environment variables.

deleteDir(path, **kwargs)

Recursively delete the specified directory.

Does nothing if it does not exist. Raises an exception if the deletion fails.

Parameters
  • path – The path to be deleted. This can be an absolute path or relative to the testcase output directory.

  • kwargs – Any additional arguments are passed to pysys.utils.fileutils.deletedir().

execute()[source]

The method tests implement to perform the test execution steps.

Raises

NotImplementedError – If this method was not implemented yet.

getBoolProperty(propertyName, default=False)

Get a True/False indicating whether the specified property is set on this object (typically as a result of specifying -X on the command line), or else from the project configuration.

Parameters

propertyName – The name of a property set on the command line or project configuration.

getDefaultEnvirons(command=None, **kwargs)

Create a new dictionary of environment variables, suitable for passing to startProcess(), with a minimal clean set of environment variables for this platform, unaffected (as much as possible) by the environment that the tests are being run under.

This environment contains a minimal PATH/LD_LIBRARY_PATH but does not attempt to replicate the full set of default environment variables on each OS, and in particular it does not include any that identify the the current username or home area. Additional environment variables can be added as needed with createEnvirons overrides. If you don’t care about minimizing the risk of your local environment affecting the test processes you start, just use environs=os.environ to allow child processes to inherit the entire parent environment.

The createEnvirons() and startProcess() methods use this as the basis for creating a new set of default environment variables.

If needed this method can be overridden in subclasses to add common environment variables for every process invoked by startProcess, for example to enable options such as code coverage for Java/Python/etc. This is also a good place to customize behaviour for different operating systems.

Some features of this method can be configured by setting project properties:

  • defaultEnvironsDefaultLang: if set to a value such as en_US.UTF-8 the specified value is set for the LANG= variable on Unix; otherwise, the LANG variable is not set (which might result in use of the legacy POSIX/C encoding).

  • defaultEnvironsTempDir: if set the expression will be passed to Python eval() and used to set the OS-specific temp directory environment variables. A typical value is self.output.

  • defaultEnvironsLegacyMode: set to true to enable compatibility mode which keeps the behaviour the same as PySys v1.1, 1.2 and 1.3, namely using a completely empty default environment on Unix, and a copy of the entire parent environment on Windows. This is not recommended unless you have a lot of legacy tests that cannot easily be changed to only set minimal required environment variables using createEnvirons().

Parameters
  • command

    If known, the full path of the executable for which a default environment is being created (when called from startProcess this is always set). This allows default environment variables to be customized for different process types e.g. Java, Python, etc.

    When using command=sys.executable to launch another copy of the current Python executable, extra items from this process’s path environment variables are added to the returned dictionary so that it can start correctly. On Unix-based systems this includes copying all of the load library path environment variable from the parent process.

  • kwargs – Overrides of this method should pass any additional kwargs down to the super implementation, to allow for future extensions.

Returns

A new dictionary containing the environment variables.

getDefaultFileEncoding(file, **xargs)[source]

Specifies what encoding should be used to read or write the specified text file. The default implementation for BaseTest delegates to the runner, which in turn gets its defaults from the pysyproject.xml configuration.

See pysys.process.user.ProcessUser.getDefaultFileEncoding for more details.

getExprFromFile(path, expr, groups=[1], returnAll=False, returnNoneIfMissing=False, encoding=None, reFlags=0)

Searches for a regular expression in the specified file, and returns it.

If the regex contains unnamed groups, the specified group is returned. If the expression is not found, an exception is raised, unless returnAll=True or returnNoneIfMissing=True. For example:

self.getExprFromFile('test.txt', 'myKey="(.*)"') # on a file containing 'myKey="foobar"' would return "foobar"
self.getExprFromFile('test.txt', 'foo') # on a file containing 'myKey=foobar' would return "foo"

See also pysys.basetest.BaseTest.assertGrep which should be used when instead of just finding out what’s in the file you want to assert that a specific expression is matched. assertGrep also provides some additional functionality such as returning named groups which this method does not currently support.

Parameters
  • path (str) – file to search (located in the output dir unless an absolute path is specified)

  • expr (str) – the regular expression, optionally containing the regex group operator (...)

  • groups (List[int]) – which regex group numbers (as indicated by brackets in the regex) should be returned; default is [1] meaning the first group. If more than one group is specified, the result will be a tuple of group values, otherwise the result will be the value of the group at the specified index as a str.

  • returnAll (bool) – returns a list containing all matching lines if True, the first matching line otherwise.

  • returnNoneIfMissing (bool) – set this to return None instead of throwing an exception if the regex is not found in the file

  • encoding (str) – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

  • reFlags (int) –

    Zero or more flags controlling how the behaviour of regular expression matching, combined together using the | operator, for example reFlags=re.VERBOSE | re.IGNORECASE.

    For details see the re module in the Python standard library. Note that re.MULTILINE cannot be used because expressions are matched against one line at a time. Added in PySys 1.5.1.

Returns

A List[List[str]] if returnAll=True and groups contains multiple groups, a List[str] if only one of those conditions is true, or else a simple str containing just the first match found.

getInstanceCount(displayName)

(Deprecated) Return the number of processes started within the testcase matching the supplied displayName.

Deprecated

The recommended way to allocate unique names is now allocateUniqueStdOutErr

The ProcessUser class maintains a reference count of processes started within the class instance via the startProcess() method. The reference count is maintained against a logical name for the process, which is the displayName used in the method call to startProcess(), or the basename of the command if no displayName was supplied. The method returns the number of processes started with the supplied logical name, or 0 if no processes have been started.

Parameters

displayName – The process display name

Returns

The number of processes started matching the command basename

Return type

int

getNextAvailableTCPPort(hosts=['', 'localhost'], socketAddressFamily=<AddressFamily.AF_INET: 2>)

Allocate a free TCP port which can be used for starting a server on this machine.

The port is taken from the pool of available server (non-ephemeral) ports on this machine, and will not be available for use by any other code in the current PySys process until this object’s cleanup method is called to return it to the pool of available ports.

To allocate an IPv4 port for use only on this host:

port = self.getNextAvailableTCPPort(hosts=['localhost'])

Changed in version 1.5.1: Added hosts and socketAddressFamily parameters.

Parameters
  • hosts (list(Str)) –

    A list of the host names or IP addresses to check when establishing that a potential allocated port isn’t already in use by a process outside the PySys framework. By default we check "" (which corresponds to INADDR_ANY and depending on the OS means either one or all non-localhost IPv4 addresses) and also localhost.

    Many machines have multiple network cards each with its own host IP address, and typically you’ll only be using one of them in your test, most commonly localhost. If you do know which host/IP you’ll actually be using, just specify that directly to save time, and avoid needlessly opening remote ports on hosts your’re not using. A list of available host addresses can be found from socket.getaddrinfo('', None).

  • socketAddressFamily – The socket address family e.g. IPv4 vs IPv6. See Python’s socket module for details.

getOutcome()

Get the overall outcome based on the precedence order.

The method returns the overall outcome of the test based on the outcomes stored in the internal data structure. The pysys.constants.PRECEDENT order of the possible outcomes is used to determined the overall outcome of the test, e.g. if pysys.constants.PASSED, pysys.constants.BLOCKED and pysys.constants.FAILED were recorded during the execution of the test, the overall outcome would be pysys.constants.BLOCKED.

The method returns the integer value of the outcome as defined in pysys.constants. To convert this to a string representation use the pysys.constants.LOOKUP dictionary i.e. LOOKUP[test.getOutcome()].

Returns

The overall outcome

getOutcomeReason()

Get the reason string for the current overall outcome (if specified).

Returns

The overall test outcome reason or ‘’ if not specified

Return type

string

logFileContents(path, includes=None, excludes=None, maxLines=20, tail=False, encoding=None, logFunction=None, reFlags=0)

Logs some or all of the lines in the specified file.

If the file does not exist or cannot be opened, does nothing. The method is useful for providing key diagnostic information (e.g. error messages from tools executed by the test) directly in run.log, or to make test failures easier to triage quickly.

Changed in version 1.5.1: Added logFunction parameter.

Parameters
  • path (str) – May be an absolute, or relative to the test output directory

  • includes (list[str]) – Optional list of regex strings. If specified, only matches of these regexes will be logged

  • excludes (list[str]) – Optional list of regex strings. If specified, no line containing these will be logged

  • maxLines (int) – Upper limit on the number of lines from the file that will be logged. Set to zero for unlimited

  • tail (bool) – Prints the _last_ ‘maxLines’ in the file rather than the first ‘maxLines’

  • encoding (str) – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

  • logFunction (Callable[[line],None]) – The function that will be used to log individual lines from the file. Usually this is self.log.info(u'  %s', line, extra=BaseLogFormatter.tag(LOG_FILE_CONTENTS)) but a custom implementation can be provided, for example to provide a different color using pysys.utils.logutils.BaseLogFormatter.tag.

  • reFlags (int) –

    Zero or more flags controlling how the behaviour of regular expression matching, combined together using the | operator, for example reFlags=re.VERBOSE | re.IGNORECASE.

    For details see the re module in the Python standard library. Note that re.MULTILINE cannot be used because expressions are matched against one line at a time. Added in PySys 1.5.1.

Returns

True if anything was logged, False if not.

mkdir(path)

Create a directory, with recursive creation of any parent directories.

This function does nothing (does not raise an except) if the directory already exists.

Parameters

path – The path to be created. This can be an absolute path or relative to the testcase output directory.

Returns

the absolute path of the new directory, to facilitate fluent-style method calling.

pythonDocTest(pythonFile, pythonPath=None, output=None, environs=None, **kwargs)[source]

Execute the Python doctests that exist in the specified python file; adds a FAILED outcome if any do not pass.

Parameters
  • pythonFile – the absolute path to a python file name.

  • pythonPath – a list of directories to be added to the PYTHONPATH.

  • output – the output file; if not specified, ‘%s-doctest.txt’ is used with the basename of the python file.

  • kwargs – extra arguments are passed to startProcess/startPython.

reportPerformanceResult(value, resultKey, unit, toleranceStdDevs=None, resultDetails=None)[source]

Reports a new performance number to the performance csv file, with an associated unique string key that identifies it for comparison purposes.

Where possible it is better to report the rate at which an operation can be performed (e.g. throughput) rather than the total time taken, since this allows the number of iterations to be increased .

Parameters
  • value – The numeric value to be reported. If a str is provided, it will be converted to a float.

  • resultKey – A unique string that fully identifies what was measured, which will be used to compare results from different test runs. For example “HTTP transport message sending throughput using with 3 connections in SSL mode”. The resultKey must be unique across all test cases and modes. It should be fully self-describing (without the need to look up extra information such as the associated testId). Do not include the test id or units in the resultKey string. It must be stable across different runs, so cannot contain process identifiers, date/times or other numbers that will vary. If possible resultKeys should be written so that related results will be together when all performance results are sorted by resultKey, which usually means putting general information near the start of the string and specifics (throughput/latency, sending/receiving) towards the end of the string. It should be as concise as possible (given the above).

  • unit – Identifies the unit the the value is measured in, including whether bigger numbers are better or worse (used to determine improvement or regression). Must be an instance of pysys.utils.perfreporter.PerformanceUnit. In most cases, use pysys.utils.perfreporter.PerformanceUnit.SECONDS (e.g. for latency) or pysys.utils.perfreporter.PerformanceUnit.PER_SECOND (e.g. for throughput); the string literals ‘s’ and ‘/s’ can be used as a shorthand for those PerformanceUnit instances.

  • toleranceStdDevs – (optional) A float that indicates how many standard deviations away from the mean a result needs to be to be considered a regression.

  • resultDetails – (optional) A dictionary of detailed information about this specific result and/or test that should be recorded together with the result, for example detailed information about what mode or versions the test is measuring. Note this is separate from the global run details shared across all tests in this PySys execution, which can be customized by overriding pysys.utils.perfreporter.CSVPerformanceReporter.getRunDetails.

setup()[source]

Contains setup actions to be executed before the test is executed.

The setup method may be overridden by individual test classes, or (more commonly) in a custom BaseTest subclass that provides common functionality for multiple individual tests. If you override this method, be sure to call super(BASETEST_CLASS_HERE, self).setup() to allow the setup commands from the base test to run.

If setup throws an exception, the cleanup method will still be called, to allow for clean up resources that were already allocated.

signalProcess(process, signal, abortOnError=None)

Send a signal to a running process.

This method uses the pysys.process.commonwrapper.CommonProcessWrapper.signal to send a signal to a running process.

Should the request to send the signal to the running process fail, a BLOCKED outcome will be added to the outcome list.

Parameters
  • process – The process handle returned from the startProcess method

  • signal – The integer value of the signal to send

  • abortOnError – If True aborts the test with an exception on any error, if False just log it as a warning. (defaults to the defaultAbortOnError project setting)

skipTest(outcomeReason, callRecord=None)

Raise an AbortException that will set the test outcome to SKIPPED and ensure that the rest of the execute() and validate() methods do not execute.

This is useful when a test should not be executed in the current mode or platform.

Parameters

outcomeReason – A string summarizing the reason the test is being skipped, for example “Feature X is not supported on Windows”.

startBackgroundThread(name, target, kwargsForTarget={})[source]

Start a new background thread that will invoke the specified target function.

The target function will be invoked with the specified keyword arguments, preceded by the special keyword arguments stopping and log. The stopping argument is a Python threading.Event instance that can be used to detect when the thread has been requested to terminate. It is recommended to use this event instead of time.sleep to avoid waiting when the thread is meant to be finishing.

Example usage:

class PySysTest(BaseTest):
        def dosomething(self, stopping, log, param1, pollingInterval):
                log.debug('Message from my thread')
                while not stopping.is_set():

                        # ... do stuff here

                        # sleep for pollingInterval, waking up if requested to stop; 
                        # (hint: ensure this wait time is small to retain 
                        # responsiveness to Ctrl+C interrupts)
                        if stopping.wait(pollingInterval): return

        def execute(self):
                t = self.startBackgroundThread('DoSomething1', self.dosomething, {'param1':True, 'pollingInterval':1.0})
                ...
                t.stop() # requests thread to stop but doesn't wait for it to stop
                t.join()

Note that BaseTest is not thread-safe (apart from addOutcome, startProcess and the reading of fields like self.output that don’t change) so if you need to use its fields or methods from background threads, be sure to add your own locking to the foreground and background threads in your test, including any custom cleanup functions.

The BaseTest will stop and join all running background threads at the beginning of cleanup. If a thread doesn’t stop within the expected timeout period a constants.TIMEDOUT outcome will be appended. If a thread’s target function raises an Exception then a constants.BLOCKED outcome will be appended during cleanup or when it is joined.

Parameters
  • name – A name for this thread that concisely describes its purpose. Should be unique within this test/owner instance. A prefix indicating the test/owner will be added to the provided name.

  • target – The function or instance method that will be executed on the background thread. The function must accept a keyword argument named stopping in addition to whichever keyword arguments are specified in kwargsForTarget.

  • kwargsForTarget – A dictionary of keyword arguments that will be passed to the target function.

Returns

A pysys.utils.threadutils.BackgroundThread instance wrapping the newly started thread.

Return type

pysys.utils.threadutils.BackgroundThread

startManualTester(file, filedir=None, state=11, timeout=1800)[source]

Start the manual tester, which provides a UI to guide a human through the tests needed to implement this testcase.

The manual tester user interface (UI) is used to describe a series of manual steps to be performed to execute and validate a test. Only a single instance of the UI can be running at any given time, and can be run either in the FOREGROUND (method will not return until the UI is closed or the timeout occurs) or in the BACKGROUND (method will return straight away so automated actions may be performed concurrently). Should the UI be terminated due to expiry of the timeout, a TIMEDOUT outcome will be added to the outcome list. The UI can be stopped via the stopManualTester method. An instance of the UI not explicitly stopped within a test will automatically be stopped via the cleanup method of the BaseTest.

Parameters
  • file – The name of the manual test xml input file (see pysys.xml.manual for details on the DTD)

  • filedir – The directory containing the manual test xml input file (defaults to the output subdirectory)

  • state – Start the manual tester either in the FOREGROUND or BACKGROUND (defaults to FOREGROUND)

  • timeout – The timeout period after which to termintate a manual tester running in the FOREGROUND

startProcess(command, arguments, environs=None, workingDir=None, state=None, timeout=600, stdout=None, stderr=None, displayName=None, abortOnError=None, expectedExitStatus='==0', ignoreExitStatus=None, quiet=False, stdouterr=None, background=False)

Start a process running in the foreground or background, and return the pysys.process.commonwrapper.CommonProcessWrapper object.

Typical use is:

myexecutable = self.startProcess('path_to_my_executable', 
        arguments=['myoperation', 'arg1','arg2'],
        environs=self.createEnvirons(addToLibPath=['my_ld_lib_path']), # if a customized environment is needed
        stdouterr=self.allocateUniqueStdOutErr('myoperation'), # for stdout/err files, pick a suitable logical name for what it's doing
        background=True # or remove for default behaviour of executing in foreground
        )

The method allows spawning of new processes in a platform independent way. The command, arguments, environment and working directory to run the process in can all be specified in the arguments to the method, along with the filenames used for capturing the stdout and stderr of the process. Processes may be started in the foreground, in which case the method does not return until the process has completed or a time out occurs, or in the background in which case the method returns immediately to the caller returning a handle to the process to allow manipulation at a later stage, typically with waitProcess.

All processes started in the background and not explicitly killed using the returned process object are automatically killed on completion of the test via the cleanup() destructor.

When starting a process that will listen on a server socket, use getNextAvailableTCPPort to allocate a free port before calling this method.

Parameters
  • command (str) – The path to the executable to be launched (should include the full path)

  • arguments (list[str]) – A list of arguments to pass to the command

  • environs (dict(str,str)) – A dictionary specifying the environment to run the process in. If a None or empty dictionary is passed, getDefaultEnvirons will be invoked to produce a suitable clean default environment for this command, containing a minimal set of variables. If you wish to specify a customized environment, createEnvirons() is a great way to create it.

  • workingDir (str) – The working directory for the process to run in (defaults to the testcase output subdirectory)

  • background (bool) – Set to True to start the process in the background. By default processes are started in the foreground, meaning execution of the test will continue only once the process has terminated.

  • state – Alternative way to set background=True. Run the process either in the FOREGROUND or BACKGROUND (defaults to FOREGROUND). Setting state=BACKGROUND is equivalent to setting background=True; in new tests using background=True is the preferred way to do this.

  • timeout (int) – The number of seconds after which to terminate processes running in the foreground. For processes that complete in a few seconds or less, it is best to avoid overriding this and stick with the default. However for long-running foreground processes it will be necessary to set a larger number, for example if running a soak test where the process needs to run for up to 2 hours you could set timeout=2*60*60.

  • stdouterr (str) – The filename prefix to use for the stdout and stderr of the process (out/err will be appended), or a tuple of (stdout,stderr) as returned from allocateUniqueStdOutErr. The stdouterr prefix is also used to form a default display name for the process if none is explicitly provided. The files are created relative to the test output directory. The filenames can be accessed from the returned process object using pysys.process.commonwrapper.CommonProcessWrapper.stdout and pysys.process.commonwrapper.CommonProcessWrapper.stderr.

  • stdout (str) – The filename used to capture the stdout of the process. It is usually simpler to use stdouterr instead of this.

  • stderr (str) – The filename used to capture the stderr of the process. It is usually simpler to use stdouterr instead of this.

  • displayName (str) – Logical name of the process used for display in log messages, and the str(…) representation of the returned process object (defaults to a string generated from the stdouterr and/or the command).

  • abortOnError (bool) – If true abort the test on any error outcome (defaults to the defaultAbortOnError project setting)

  • expectedExitStatus (str) – The condition string used to determine whether the exit status/code returned by the process is correct. The default is ‘==0’, as an exit code of zero usually indicates success, but if you are expecting a non-zero exit status (for example because you are testing correct handling of a failure condition) this could be set to ‘!=0’ or a specific value such as ‘==5’.

  • ignoreExitStatus (bool) –

    If False, a BLOCKED outcome is added if the process terminates with an exit code that doesn’t match expectedExitStatus (or if the command cannot be run at all). This can be set to True in cases where you do not care whether the command succeeds or fails, or wish to handle the exit status separately with more complicated logic.

    The default value of ignoreExitStatus=None means the value will be taken from the project property defaultIgnoreExitStatus, which can be configured in the project XML (the recommended default property value is defaultIgnoreExitStatus=False), or is set to True for compatibility with older PySys releases if no project property is set.

  • quiet (bool) – If True, this method will not do any INFO or WARN level logging (only DEBUG level), unless a failure outcome is appended. This parameter can be useful to avoid filling up the log where it is necessary to repeatedly execute a command check for completion of some operation until it succeeds; in such cases you should usually set ignoreExitStatus=True as well since both success and failure exit statuses are valid.

Returns

The process wrapper object.

Return type

pysys.process.commonwrapper.CommonProcessWrapper

startProcessMonitor(process, interval=5, file=None, handlers=[], **pmargs)[source]

Start a background thread to monitor process statistics such as memory and CPU usage.

All process monitors are automatically stopped on completion of the test by BaseTest.cleanup, but you may also wish to explicitly stop your process monitors using stopProcessMonitor before you begin shutting down processes at the end of a test to avoid unwanted spikes and noise in the last few samples of the data.

You can specify a file and/or a list of handlers. If you use file, a default pysys.process.monitor.ProcessMonitorTextFileHandler instance is created to produce tab-delimited lines with default columns specified by pysys.process.monitor.ProcessMonitorTextFileHandler.DEFAULT_COLUMNS. If you wish to customize this for an individual test create your own ProcessMonitorTextFileHandler instance and pass it to handlers instead. Additional default columns may be added in future releases.

Parameters
Returns

An object representing the process monitor (pysys.process.monitor.BaseProcessMonitor).

Return type

pysys.process.monitor.BaseProcessMonitor

startPython(arguments, disableCoverage=False, **kwargs)

Start a Python process with the specified arguments.

Uses the same Python process the tests are running under.

If PySys was run with the arguments -X pythonCoverage=true then startPython will add the necessary arguments to enable generation of code coverage. Note that this requried the coverage.py library to be installed. If a project property called pythonCoverageArgs exists then its value will be added as (space-delimited) arguments to the coverage tool.

Parameters
  • arguments – The arguments to pass to the Python executable. Typically the first one be either the name of a Python script to execute, or -m followed by a module name.

  • kwargs – See startProcess for detail on available arguments.

  • disableCoverage – Disables code coverage for this specific process. Coverage can also be disabled by setting self.disableCoverage==True on this test instance.

Returns

The process handle of the process.

Return type

pysys.process.commonwrapper.CommonProcessWrapper

stopManualTester()[source]

Stop the manual tester if running.

stopProcess(process, abortOnError=None)

Stops the specified process, if it is currently running.

Does nothing if the process is not running.

This is equivalent to calling pysys.process.commonwrapper.CommonProcessWrapper.stop(), except it also logs an info message when the process is stopped.

Parameters
  • process – The process handle returned from the startProcess method

  • abortOnError – If True abort the test on any error outcome (defaults to the defaultAbortOnError project setting), if False a failure to stop the process will just be logged as a warning.

stopProcessMonitor(monitor)[source]

Request a process monitor to stop.

Does not wait for it to finish stopping.

All process monitors are automatically stopped and joined during cleanup, however you may wish to explicitly stop your process monitors before you begin shutting down processes at the end of a test to avoid unwanted spikes and noise in the last few samples of the data.

Parameters

monitor – The process monitor handle returned from the startProcessMonitor method

validate()[source]

The method tests implement to perform assertions that check for the expected behaviour.

In some cases all of the required assertions (e.g. checking that a process ran without error etc) will have been performed in the execute method and so validate will be empty. However, where possible it is recommended to put assertions into the validate method for clarity, and so that the pysys run --validateOnly option can be used during test development.

wait(interval)[source]

Wait for a specified period of time, and log a message to indicate this is happening.

Tests that rely on waiting for arbitrary times usually take longer to execute than necessary, and are fragile if the timings or machine load changes, so wherever possible use a method like waitForGrep to wait for something specific instead.

Parameters

interval – The time interval in seconds to wait.

waitForFile(file, filedir=None, timeout=30, abortOnError=None)

Wait for a file to exist on disk.

This method blocks until a file is created on disk. This is useful for test timing where a component under test creates a file (e.g. for logging) indicating it has performed all initialisation actions and is ready for the test execution steps. If a file is not created on disk within the specified timeout interval, the method returns to the caller.

Parameters
  • file – The basename of the file used to wait to be created

  • filedir – The dirname of the file (defaults to the testcase output subdirectory)

  • timeout – The timeout in seconds to wait for the file to be created

  • abortOnError – If true abort the test on any failure (defaults to the defaultAbortOnError project setting)

waitForGrep(file, expr='', condition='>=1', timeout=60, poll=0.25, ignores=[], process=None, errorExpr=[], abortOnError=None, encoding=None, detailMessage='', filedir=None, reFlags=0)

Wait for a regular expression line to be seen (one or more times) in a text file in the output directory (waitForGrep was formerly known as waitForSignal).

This method provides some parameters that give helpful fail-fast behaviour with a descriptive outcome reason; use these whenever possible:

  • process= to abort if success becomes impossible due to premature termination of the process that’s generating the output

  • errorExpr= to abort if an error message/expression is written to the file

This will generate much clearer outcome reasons, which makes test failures easy to triage, and also avoids wasting time waiting for something that will never happen.

Example:

self.waitForGrep('myprocess.log', expr='INFO .*Started successfully', process=myprocess, 
        errorExpr=[' ERROR ', ' FATAL ', 'Failed to start'], encoding='utf-8')

Note that waitForGrep fails the test if the expression is not found (unless abortOnError was set to False, which isn’t recommended), so there is no need to add duplication with an assertGrep to check for the same expression in your validation logic.

The message(s) logged when there is a successful wait can be controlled with the project property verboseWaitForGrep=true/false (or equivalently, verboseWaitForSignal); for best visibility into what is happening set this property to true in your pysysproject.xml.

You can extract information from the matched expression, optionally perform assertions on it, by using one or more (?P<groupName>...) named groups in the expression. A common pattern is to unpack the resulting dict using **kwargs syntax and pass to BaseTest.assertThat. For example:

self.assertThat('username == expected', expected='myuser',
        **self.waitForGrep('myserver.log', expr=r'Successfully authenticated user "(?P<username>[^"]*)"'))

New in version 1.5.1.

Parameters
  • file (str) – The path of the file to be searched. Usually this is a name/path relative to the self.output directory, but alternatively an absolute path can be specified.

  • expr (str) – The regular expression to search for in the text file.

  • condition (str) – The condition to be met for the number of lines matching the regular expression; by default we wait until there is at least one occurrence.

  • timeout (int) – The number of seconds to wait for the regular expression before giving up and aborting the test with pysys.constants.TIMEDOUT (unless abortOnError=False in which case execution will continue).

  • process (pysys.process.commonwrapper.CommonProcessWrapper) – The process that is generating the specified file, to allow the wait to fail fast (instead of timing out) if the process dies before the expected signal appears. Can be None if the process is not known or is expected to terminate itself during this period.

  • errorExpr (list[str]) – Optional list of regular expressions, which if found in the file will cause waiting for the main expression to be aborted with a pysys.constants.BLOCKED outcome. This is useful to avoid waiting a long time for the expected expression when an ERROR is logged that means it will never happen, and also provides much clearer test failure messages in this case.

  • ignores (list[str]) – A list of regular expressions used to identify lines in the files which should be ignored when matching both expr and errorExpr.

  • poll (float) – The time in seconds between to poll the file looking for the regular expression and to check against the condition

  • abortOnError (bool) – If True abort the test on any error outcome (defaults to the defaultAbortOnError project setting, which for a modern project will be True).

  • encoding (str) – The encoding to use to open the file and convert from bytes to characters. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.

  • detailMessage (str) – An extra string to add to the message logged when waiting to provide extra information about the wait condition. e.g. detailMessage='(downstream message received)'. Added in v1.5.1.

  • reFlags (int) –

    Zero or more flags controlling how the behaviour of regular expression matching, combined together using the | operator, for example reFlags=re.VERBOSE | re.IGNORECASE.

    For details see the re module in the Python standard library. Note that re.MULTILINE cannot be used because expressions are matched against one line at a time. Added in PySys 1.5.1.

  • filedir (str) – Can be used to provide a directory name to add to the beginning of the file parameter; however usually it is clearer just to specify that directory in the file.

Return list[re.Match]

Usually this returns a list of re.Match objects found for the expr, or an empty list if there was no match.

If the expr contains any (?P<groupName>...) named groups, and assuming the condition still the default of “>=1” (i.e. not trying to find multiple matches), then a dict is returned containing dict(groupName: str, matchValue: str or None) (or an empty {} dict if there is no match) which allows the result to be passed to assertThat for further checking of the matched groups (typically unpacked using the ** operator; see example above).

waitForSignal(file, filedir=None, expr='', **waitForGrepArgs)

Old alias for waitForGrep; please use waitForGrep in new tests.

All parameters are the same, except that in waitForSignal the (rarely used) filedir argument can be specified as the 2nd positional argument (after file and before expr) whereas in waitForGrep it can only be specified as a filedir= keyword argument.

waitForSocket(port, host='localhost', timeout=60, abortOnError=None, process=None, socketAddressFamily=<AddressFamily.AF_INET: 2>)

Wait until it is possible to establish a socket connection to a server running on the specified local or remote port.

This method blocks until connection to a particular host:port pair can be established. This is useful for test timing where a component under test creates a socket for client server interaction - calling of this method ensures that on return of the method call the server process is running and a client is able to create connections to it. If a connection cannot be made within the specified timeout interval, the method returns to the caller, or aborts the test if abortOnError=True.

Changed in version 1.5.1: Added host and socketAddressFamily parameters.

Parameters
  • port – The port value in the socket host:port pair

  • host – The host value in the socket host:port pair

  • timeout – The timeout in seconds to wait for connection to the socket

  • abortOnError – If true abort the test on any failure (defaults to the defaultAbortOnError project setting)

  • process – If a handle to a process is specified, the wait will abort if the process dies before the socket becomes available. It is recommended to set this wherever possible.

  • socketAddressFamily – The socket address family e.g. IPv4 vs IPv6. See Python’s socket module for details.

waitManualTester(timeout=1800)[source]

Wait for the manual tester to be stopped via user interaction.

waitProcess(process, timeout, abortOnError=None)

Wait for a background process to terminate, completing on termination or expiry of the timeout.

Timeouts will result in an exception unless the project property defaultAbortOnError==False.

This method does not check the exit code for success, but you can manually check the return value (which is the same as process.exitStatus) using assertThat if you wish to check it succeeded.

Parameters
  • process – The process handle returned from the startProcess method

  • timeout – The timeout value in seconds to wait before returning

  • abortOnError – If True aborts the test with an exception on any error, if False just log it as a warning. (defaults to the defaultAbortOnError project setting)

Returns

The process’s exitStatus. This will be None if the process timed out and abortOnError is disabled.

writeProcess(process, data, addNewLine=True)

Write binary data to the stdin of a process.

This method uses pysys.process.commonwrapper.CommonProcessWrapper.write to write binary data to the stdin of a process. This wrapper around the write method of the process helper only adds checking of the process running status prior to the write being performed, and logging to the testcase run log to detail the write.

Parameters
  • process – The process handle returned from the startProcess() method

  • data – The data to write to the process stdin. As only binary data can be written to a process stdin, if a character string rather than a byte object is passed as the data, it will be automatically converted to a bytes object using the encoding given by locale.getpreferredencoding().

  • addNewLine – True if a new line character is to be added to the end of the data string

write_text(file, text, encoding=None)

Writes the specified characters to a file in the output directory.

Parameters
  • file – The path of the file to write, either an absolute path or relative to the self.output directory.

  • text

    The string to write to the file, with n for newlines (do not use os.linesep as the file will be opened in text mode so platform line separators will be added automatically).

    On Python 3 this must be a character string.

    On Python 2 this can be a character or byte string containing ASCII characters. If non-ASCII characters are used, it must be a unicode string if there is an encoding specified for this file/type, or else a byte string.

  • encoding – The encoding to use to open the file. The default value is None which indicates that the decision will be delegated to the getDefaultFileEncoding() method.