source: sasmodels/sasmodels/model_test.py @ 1b50776

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 1b50776 was 1b50776, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Adding better test names

  • Property mode set to 100644
File size: 7.7 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 xmlrunner
48import unittest
49
50import numpy as np
51
52from .core import list_models, load_model_definition
53from .core import load_model_cl, load_model_dll
54from .core import make_kernel, call_kernel, call_ER, call_VR
55
56def annotate_exception(exc, msg):
57    """
58    Add an annotation to the current exception, which can then be forwarded
59    to the caller using a bare "raise" statement to reraise the annotated
60    exception.
61    Example::
62        >>> D = {}
63        >>> try:
64        ...    print D['hello']
65        ... except Exception,exc:
66        ...    annotate_exception(exc, "while accessing 'D'")
67        ...    raise
68        Traceback (most recent call last):
69            ...
70        KeyError: "hello while accessing 'D'"
71    """
72    args = exc.args
73    if not args:
74        exc.args = (msg,)
75    else:
76        try:
77            arg0 = " ".join((args[0],msg))
78            exc.args = tuple([arg0] + list(args[1:]))
79        except:
80            exc.args = (" ".join((str(exc),msg)),)
81
82def make_suite(loaders, models):
83
84    ModelTestCase = _hide_model_case_from_nosetests()
85    suite = unittest.TestSuite()
86
87    if models[0] == 'all':
88        skip = models[1:]
89        models = list_models()
90    else:
91        skip = []
92    for model_name in models:
93        if model_name in skip: continue
94        model_definition = load_model_definition(model_name)
95
96        smoke_tests = [
97            [{},0.1,None],
98            [{},(0.1,0.1),None],
99            [{},'ER',None],
100            [{},'VR',None],
101            ]
102        tests = smoke_tests + getattr(model_definition, 'tests', [])
103
104        if tests: # in case there are no smoke tests...
105            #print '------'
106            #print 'found tests in', model_name
107            #print '------'
108
109            # if ispy then use the dll loader to call pykernel
110            # don't try to call cl kernel since it will not be
111            # available in some environmentes.
112            ispy = callable(getattr(model_definition,'Iq', None))
113
114            # test using opencl if desired
115            if not ispy and ('opencl' in loaders and load_model_cl):
116                test_name = "Model: %s, Kernel: OpenCL"%model_name
117                test_method = "test_%s_opencl" % model_name
118                test = ModelTestCase(test_name, model_definition,
119                                     load_model_cl, tests, test_method)
120                #print "defining", test_name
121                suite.addTest(test)
122
123            # test using dll if desired
124            if ispy or ('dll' in loaders and load_model_dll):
125                test_name = "Model: %s, Kernel: dll"%model_name
126                test_method = "test_%s_dll" % model_name
127                test = ModelTestCase(test_name, model_definition,
128                                     load_model_dll, tests, test_method)
129                suite.addTest(test)
130
131    return suite
132
133def _hide_model_case_from_nosetests():
134    class ModelTestCase(unittest.TestCase):
135        def __init__(self, test_name, definition, loader, tests, test_method):
136            self.test_name = test_name
137            self.definition = definition
138            self.loader = loader
139            self.tests = tests
140
141            setattr(self, test_method, self._runTest)
142            unittest.TestCase.__init__(self, test_method)
143
144        def _runTest(self):
145            try:
146                model = self.loader(self.definition)
147                for test in self.tests:
148                    self._run_one_test(model, test)
149
150            except Exception,exc:
151                annotate_exception(exc, self.test_name)
152                raise
153
154        def _run_one_test(self, model, test):
155            pars, x, y = test
156
157            if not isinstance(y, list):
158                y = [y]
159            if not isinstance(x, list):
160                x = [x]
161
162            self.assertEqual(len(y), len(x))
163
164            if x[0] == 'ER':
165                actual = [call_ER(model.info, pars)]
166            elif x[0] == 'VR':
167                actual = [call_VR(model.info, pars)]
168            elif isinstance(x[0], tuple):
169                Qx,Qy = zip(*x)
170                q_vectors = [np.array(Qx), np.array(Qy)]
171                kernel = make_kernel(model, q_vectors)
172                actual = call_kernel(kernel, pars)
173            else:
174                q_vectors = [np.array(x)]
175                kernel = make_kernel(model, q_vectors)
176                actual = call_kernel(kernel, pars)
177
178            self.assertGreater(len(actual), 0)
179            self.assertEqual(len(y), len(actual))
180
181            for xi, yi, actual_yi in zip(x, y, actual):
182                if yi is None:
183                    # smoke test --- make sure it runs and produces a value
184                    self.assertTrue(np.isfinite(actual_yi),
185                        'invalid f(%s): %s' % (xi, actual_yi))
186                else:
187                    err = abs(yi - actual_yi)
188                    nrm = abs(yi)
189                    self.assertLess(err * 10**5, nrm,
190                        'f(%s); expected:%s; actual:%s' % (xi, yi, actual_yi))
191
192    return ModelTestCase
193
194
195
196def main():
197    models = sys.argv[1:]
198    if models and models[0] == 'opencl':
199        if load_model_cl is None:
200            print >>sys.stderr, "opencl is not available"
201            return 1
202        loaders = ['opencl']
203        models = models[1:]
204    elif models and models[0] == 'dll':
205        # TODO: test if compiler is available?
206        loaders = ['dll']
207        models = models[1:]
208    elif models and models[0] == 'opencl_and_dll':
209        if load_model_cl is None:
210            print >>sys.stderr, "opencl is not available"
211            return 1
212        loaders = ['opencl', 'dll']
213        models = models[1:]
214    else:
215        loaders = ['opencl', 'dll']
216    if not models:
217        print >>sys.stderr, "usage: python -m sasmodels.model_test [opencl|dll|opencl_and_dll] model1 model2 ..."
218        print >>sys.stderr, "if model1 is 'all', then all except the remaining models will be tested"
219        return 1
220
221    #runner = unittest.TextTestRunner()
222    runner = xmlrunner.XMLTestRunner(output='logs')
223    result = runner.run(make_suite(loaders, models))
224    return 1 if result.failures or result.errors else 0
225
226if __name__ == "__main__":
227    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.