source: sasmodels/sasmodels/model_test.py @ d6adfbe

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since d6adfbe was d6adfbe, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

make sure test failure raises an error; support nosetests as test driver

  • Property mode set to 100644
File size: 7.6 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3Run model unit tests.
4
5Usage::
6
7     python -m sasmodels.model_test [opencl|dll|opencl_and_dll] model1 model2 ...
8
9     if model1 is 'all', then all except the remaining models will be tested
10
11Each model is tested using the default parameters at q=0.1, (qx,qy)=(0.1,0.1),
12and the ER and VR are computed.  The return values at these points are not
13considered.  The test is only to verify that the models run to completion,
14and do not produce inf or NaN.
15
16Tests are defined with the *tests* attribute in the model.py file.  *tests*
17is a list of individual tests to run, where each test consists of the
18parameter values for the test, the q-values and the expected results.  For
19the effective radius test, the q-value should be 'ER'.  For the VR test,
20the q-value should be 'VR'.  For 1-D tests, either specify the q value or
21a list of q-values, and the corresponding I(q) value, or list of I(q) values.
22
23That is::
24
25    tests = [
26        [ {parameters}, q, I(q)],
27        [ {parameters}, [q], [I(q)] ],
28        [ {parameters}, [q1, q2, ...], [I(q1), I(q2), ...]],
29
30        [ {parameters}, (qx, qy), I(qx, Iqy)],
31        [ {parameters}, [(qx1, qy1), (qx2, qy2), ...],
32                        [I(qx1,qy1), I(qx2,qy2), ...]],
33
34        [ {parameters}, 'ER', ER(pars) ],
35        [ {parameters}, 'VR', VR(pars) ],
36        ...
37    ]
38
39Parameters are *key:value* pairs, where key is one of the parameters of the
40model and value is the value to use for the test.  Any parameters not given
41in the parameter list will take on the default parameter value.
42
43Precision defaults to 5 digits (relative).
44"""
45
46import sys
47import unittest
48
49import numpy as np
50
51from .core import list_models, load_model_definition
52from .core import load_model_cl, load_model_dll
53from .core import make_kernel, call_kernel, call_ER, call_VR
54
55def annotate_exception(exc, msg):
56    """
57    Add an annotation to the current exception, which can then be forwarded
58    to the caller using a bare "raise" statement to reraise the annotated
59    exception.
60    Example::
61        >>> D = {}
62        >>> try:
63        ...    print D['hello']
64        ... except Exception,exc:
65        ...    annotate_exception(exc, "while accessing 'D'")
66        ...    raise
67        Traceback (most recent call last):
68            ...
69        KeyError: "hello while accessing 'D'"
70    """
71    args = exc.args
72    if not args:
73        exc.args = (msg,)
74    else:
75        try:
76            arg0 = " ".join((args[0],msg))
77            exc.args = tuple([arg0] + list(args[1:]))
78        except:
79            exc.args = (" ".join((str(exc),msg)),)
80   
81def suite(loaders, models):
82
83    ModelTestCase = _hide_model_case_from_nosetests()
84    suite = unittest.TestSuite()
85
86    if models[0] == 'all':
87        skip = models[1:]
88        models = list_models()
89    else:
90        skip = []
91    for model_name in models:
92        if model_name in skip: continue
93        model_definition = load_model_definition(model_name)
94
95        smoke_tests = [
96            [{},0.1,None],
97            [{},(0.1,0.1),None],
98            [{},'ER',None],
99            [{},'VR',None],
100            ]
101        tests = smoke_tests + getattr(model_definition, 'tests', [])
102       
103        if tests: # in case there are no smoke tests...
104            #print '------'
105            #print 'found tests in', model_name
106            #print '------'
107
108            # if ispy then use the dll loader to call pykernel
109            # don't try to call cl kernel since it will not be
110            # available in some environmentes.
111            ispy = callable(getattr(model_definition,'Iq', None))
112
113            # test using opencl if desired
114            if not ispy and ('opencl' in loaders and load_model_cl):
115                test_name = "Model: %s, Kernel: OpenCL"%model_name
116                test = ModelTestCase(test_name, model_definition,
117                                     load_model_cl, tests)
118                #print "defining", test_name
119                suite.addTest(test)
120
121            # test using dll if desired
122            if ispy or ('dll' in loaders and load_model_dll):
123                test_name = "Model: %s, Kernel: dll"%model_name
124                test = ModelTestCase(test_name, model_definition,
125                                     load_model_dll, tests)
126                #print "defining", test_name
127                suite.addTest(test)
128
129    return suite
130
131def _hide_model_case_from_nosetests():
132    class ModelTestCase(unittest.TestCase):
133        def __init__(self, test_name, definition, loader, tests):
134            unittest.TestCase.__init__(self)
135
136            self.test_name = test_name
137            self.definition = definition
138            self.loader = loader
139            self.tests = tests
140
141        def runTest(self):
142            try:
143                model = self.loader(self.definition)
144                for test in self.tests:
145                    pars, Q, I = test
146
147                    if not isinstance(I, list):
148                        I = [I]
149                    if not isinstance(Q, list):
150                        Q = [Q]
151
152                    self.assertEqual(len(I), len(Q))
153
154                    if Q[0] == 'ER':
155                        Iq = [call_ER(kernel, pars)]
156                    elif Q[0] == 'VR':
157                        Iq = [call_VR(kernel, pars)]
158                    elif isinstance(Q[0], tuple):
159                        Qx,Qy = zip(*Q)
160                        Q_vectors = [np.array(Qx), np.array(Qy)]
161                        kernel = make_kernel(model, Q_vectors)
162                        Iq = call_kernel(kernel, pars)
163                    else:
164                        Q_vectors = [np.array(Q)]
165                        kernel = make_kernel(model, Q_vectors)
166                        Iq = call_kernel(kernel, pars)
167
168                    self.assertGreater(len(Iq), 0)
169                    self.assertEqual(len(I), len(Iq))
170
171                    for q, i, iq in zip(Q, I, Iq):
172                        if i is None:
173                            # smoke test --- make sure it runs and produces a value
174                            self.assertTrue(np.isfinite(iq), 'q:%s; not finite; actual:%s' % (q, iq))
175                        else:
176                            err = abs(i - iq)
177                            nrm = abs(i)
178                            self.assertLess(err * 10**5, nrm, 'q:%s; expected:%s; actual:%s' % (q, i, iq))
179
180            except Exception,exc:
181                annotate_exception(exc, self.test_name)
182                raise
183
184    return ModelTestCase
185
186
187# let nosetests sniff out the tests
188def model_tests():
189    tests = suite(['opencl','dll'],['all'])
190    for test_i in tests:
191        yield test_i.runTest
192
193def main():
194    models = sys.argv[1:]
195    if models and models[0] == 'opencl':
196        if load_model_cl is None:
197            print >>sys.stderr, "opencl is not available"
198            return 1
199        loaders = ['opencl']
200        models = models[1:]
201    elif models and models[0] == 'dll':
202        # TODO: test if compiler is available?
203        loaders = ['dll']
204        models = models[1:]
205    elif models and models[0] == 'opencl_and_dll':
206        if load_model_cl is None:
207            print >>sys.stderr, "opencl is not available"
208            return 1
209        loaders = ['opencl', 'dll']
210        models = models[1:]
211    else:
212        loaders = ['opencl', 'dll']
213    if not models:
214        print >>sys.stderr, "usage: python -m sasmodels.model_test [opencl|dll|opencl_and_dll] model1 model2 ..."
215        print >>sys.stderr, "if model1 is 'all', then all except the remaining models will be tested"
216        return 1
217
218    #run_tests(loaders, models)
219    runner = unittest.TextTestRunner()
220    result = runner.run(suite(loaders, models))
221    return 1 if result.failures or result.errors else 0
222
223if __name__ == "__main__":
224    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.