source: sasmodels/conftest.py @ 0e7611b

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

pytest: re-enable referencing on yielded tests

  • Property mode set to 100644
File size: 4.6 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 = []
52        for number, yielded in enumerate(obj()):
53            index, call, args = split_yielded_test(yielded, number)
54            test = pytest.Function(name+index, collector, args=args, callobj=call)
55            tests.append(test)
56        return tests
57
58def split_yielded_test(obj, number):
59    if not isinstance(obj, (tuple, list)):
60        obj = (obj,)
61    if not callable(obj[0]):
62        index = "['%s']"%obj[0]
63        obj = obj[1:]
64    else:
65        index = "[%d]"%number
66    call, args = obj[0], obj[1:]
67    return index, call, args
68
69USE_DOCSTRING_AS_DESCRIPTION = True
70def pytest_collection_modifyitems(session, config, items):
71    """
72    Add description to the test node id if item is a function and function
73    has a description attribute or __doc__ attribute.
74    """
75    for item in items:
76        #print(item.nodeid, type(item))
77        #for attr in dir(item): not attr.startswith('__') and print(attr, getattr(item, attr))
78        if isinstance(item, pytest.Function):
79            if isinstance(item, TestCaseFunction):
80                # TestCase uses item.name to find the method so skip
81                continue
82            function = item.obj
83
84            # If the test case provides a "description" attribute then use it
85            # as an extended description.  If there is no description attribute,
86            # then perhaps use the test docstring.
87            if USE_DOCSTRING_AS_DESCRIPTION:
88                description = getattr(function, 'description', function.__doc__)
89            else:
90                description = getattr(function, 'description', "")
91
92            # If description is not supplied but yield args are, then use the
93            # yield args for the description
94            if not description and getattr(item, '_args', ()):
95                description = str(item._args) if len(item._args) > 1 else str(item._args[0])
96            #print(item.nodeid, description, item._args)
97
98            if description:
99                # Strip spaces from start and end and strip dots from end
100                # pytest converts '.' to '::' on output for some reason.
101                description = description.strip().rstrip('.')
102                # Join multi-line descriptions into a single line
103                if '\n' in description:
104                    description = " ".join(line.strip() for line in description.split('\n'))
105
106            # Set the description as part of the node identifier.
107            if description:
108                #print(type(item), dir(item))
109                #print(item.nodeid, description)
110                #print(item.location, item.name, item.nodeid, item.originalname)
111                # Note: leave the current name mostly as-is since the prefix
112                # is needed to specify the nth test from a list of tests.
113                #print("updating with", description)
114                item.name += "::" + description
115            #print("=>", item.nodeid)
Note: See TracBrowser for help on using the repository browser.