source: sasmodels/conftest.py @ 335271e

core_shell_microgelsmagnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 335271e was 335271e, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

remove restriction on pytest<4

  • Property mode set to 100644
File size: 4.2 KB
Line 
1"""
2py.test hooks for sasmodels
3
4Hooks for running sasmodels tests via py.test.
5
6*pytest_collection_modifyitems* adds the test description to the end of
7the test name.  This is needed for the generated list of tests is sasmodels,
8where each test has a description giving the name of the model.  For example
9"model_tests::[3]" becomes "model_tests::[3]::bcc_paracrystal-dll".  Need to
10leave the "::[3]" in the name since that is the way you can indicate this
11test specifically from the py.test command line. [This is perhaps because
12the modifyitems hook is only called after test selection.]
13
14*pytest_ignore_collect* skips kernelcl.py if pyopencl cannot be imported.
15"""
16from __future__ import print_function
17
18import os.path
19
20import pytest
21from _pytest.unittest import TestCaseFunction
22from _pytest.compat import is_generator
23
24def pytest_pycollect_makeitem(collector, name, obj):
25    """
26    Convert test generator into list of function tests so that pytest doesn't
27    complain about deprecated yield tests.
28
29    Note that unlike nose, the tests are generated and saved instead of run
30    immediately.  This means that any dynamic context, such as a for-loop
31    variable, must be captured by wrapping the yield result in a function call.
32
33    For example::
34
35        for value in 1, 2, 3:
36            for test in test_cases:
37                yield test, value
38
39    will need to be changed to::
40
41        def build_test(test, value):
42            return test, value
43        for value in 1, 2, 3:
44            for test in test_cases:
45                yield build_test(test, value)
46
47    This allows the context (test and value) to be captured by lexical closure
48    in build_test. See https://stackoverflow.com/a/233835/6195051.
49    """
50    if collector.istestfunction(obj, name) and is_generator(obj):
51        tests = [pytest.Function(name, parent=collector, args=yielded[1:], callobj=yielded[0])
52                 for yielded in obj()]
53        return tests
54
55
56USE_DOCSTRING_AS_DESCRIPTION = True
57def pytest_collection_modifyitems(session, config, items):
58    """
59    Add description to the test node id if item is a function and function
60    has a description attribute or __doc__ attribute.
61    """
62    for item in items:
63        #print(item.nodeid, type(item))
64        #for attr in dir(item): not attr.startswith('__') and print(attr, getattr(item, attr))
65        if isinstance(item, pytest.Function):
66            if isinstance(item, TestCaseFunction):
67                # TestCase uses item.name to find the method so skip
68                continue
69            function = item.obj
70
71            # If the test case provides a "description" attribute then use it
72            # as an extended description.  If there is no description attribute,
73            # then perhaps use the test docstring.
74            if USE_DOCSTRING_AS_DESCRIPTION:
75                description = getattr(function, 'description', function.__doc__)
76            else:
77                description = getattr(function, 'description', "")
78
79            # If description is not supplied but yield args are, then use the
80            # yield args for the description
81            if not description and getattr(item, '_args', ()):
82                description = str(item._args) if len(item._args) > 1 else str(item._args[0])
83            #print(item.nodeid, description, item._args)
84
85            if description:
86                # Strip spaces from start and end and strip dots from end
87                # pytest converts '.' to '::' on output for some reason.
88                description = description.strip().rstrip('.')
89                # Join multi-line descriptions into a single line
90                if '\n' in description:
91                    description = " ".join(line.strip() for line in description.split('\n'))
92
93            # Set the description as part of the node identifier.
94            if description:
95                #print(type(item), dir(item))
96                #print(item.nodeid, description)
97                #print(item.location, item.name, item.nodeid, item.originalname)
98                # Note: leave the current name mostly as-is since the prefix
99                # is needed to specify the nth test from a list of tests.
100                #print("updating with", description)
101                item.name += "::" + description
102            #print("=>", item.nodeid)
Note: See TracBrowser for help on using the repository browser.