1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 """
31 This static class is used for comparing two objects.
32 """
33
34
35 __authors__ = [ "Marie-Francoise Incardona", "Olof Svensson", "Jérôme Kieffer" ]
36 __contact__ = "svensson@esrf.fr"
37 __license__ = "LGPLv3+"
38 __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
39
40
41 import os, tempfile, types
42 from difflib import SequenceMatcher
43 from EDVerbose import EDVerbose
47 """
48 This static class is used for comparing two objects.
49 """
50
51
52 @staticmethod
53 - def equal(_oExpected, _oObtained, _strComment="Equal", _fMaxDelta=1e-6):
54 """
55 Fail if the two objects are unequal as determined by the '==' operator.
56 Saves the two objects to the current working directory using an unique identifier
57 appended with "_obtained.txt" and "_expected.txt".
58 @param _oExpected: any python object used as reference
59 @param _oObtained: any python object to be compared with the reference
60 @param _strComment: a comment to make your assertion more understandable to the user
61 @type _strComment: python string or unicode string.
62 @param _fMaxDelta: epsilon to check equivalence for float
63 @param _fMaxDelta: float
64 """
65 bAlmostEqual = True
66
67 if (_oExpected != _oObtained):
68 if isinstance(_oExpected, float):
69 if abs(_oExpected - _oObtained) > _fMaxDelta:
70 bAlmostEqual = False
71 elif isinstance(_oExpected, dict) and isinstance(_oObtained, dict):
72 listExpKeys = list(_oExpected.keys())
73 listExpKeys.sort()
74 listObtKeys = list(_oObtained.keys())
75 listObtKeys.sort()
76 if listObtKeys == listExpKeys:
77 for key in listObtKeys:
78 oExpectedValue = _oExpected[key]
79 oObtainedValue = _oObtained[key]
80 if oExpectedValue != oObtainedValue:
81 if isinstance(oExpectedValue, float) and isinstance(oObtainedValue, float):
82 if abs(oExpectedValue - oObtainedValue) > _fMaxDelta:
83 bAlmostEqual = False
84 break
85 else:
86 bAlmostEqual = False
87 break
88 else:
89 bAlmostEqual = False
90 else:
91 bAlmostEqual = False
92 if bAlmostEqual:
93 EDVerbose.ASSERT("OK " + _strComment)
94 else:
95 strExpectedFileName = tempfile.mktemp(suffix="_expected.txt", dir=os.getcwd())
96 strUniqueIndentifier = strExpectedFileName.split("_expected.txt")[0]
97 f = open(strExpectedFileName , "w")
98 if isinstance(_oExpected, dict):
99 f.write("#Dict:" + os.linesep)
100 keys = _oExpected.keys()
101 keys.sort()
102 f.write(os.linesep.join(["%s:%s" % (i, _oExpected[i]) for i in keys ]))
103 else:
104 f. write(str(_oExpected))
105
106 f.close()
107 strObtainedFileName = strUniqueIndentifier + "_obtained.txt"
108 f = open(strObtainedFileName, "w")
109 if isinstance(_oObtained, dict):
110 f.write("#Dict:" + os.linesep)
111 keys = _oObtained.keys()
112 keys.sort()
113 print
114 f.write(os.linesep.join(["%s:%s" % (i, _oObtained[i]) for i in keys ]))
115 else:
116 f. write(str(_oObtained))
117 f.close()
118 EQUAL_ERROR_ASSERT_MESSAGE = _strComment + " FAILURE: Expected different from obtained - identifier %s" % strUniqueIndentifier
119 EDVerbose.ASSERT(EQUAL_ERROR_ASSERT_MESSAGE)
120 raise AssertionError, EQUAL_ERROR_ASSERT_MESSAGE
121
122
123 @staticmethod
124 - def strAlmostEqual(_oExpected, _oObtained, _strComment="Strings are similar", _fRelError=1e-2, _fAbsError=1e-4, _fStrSimilar=1.0, _strExcluded=None, _lstExcluded=[]):
125 """
126 Check if two strings (or XML strings) are almost equal, which means that:
127 - all pure text part are equal
128 - floats do not differ more than 1% by default or 0.0001 in absolute difference
129
130 Saves the two objects to the current working directory using an unique identifier
131 appended with "_obtained.txt" and "_expected.txt".
132 @param _oExpected: any python object used as reference, probably a string or an unicode string
133 @param _oObtained: any python object to be compared with the reference, probably a string or an unicode string
134 @param _strComment: a comment to make your assertion more understandable to the user
135 @type _strComment: python string or unicode string.
136 @param _fRelError: maximum relative error defined as a float
137 @param _fAbsError: maximum absolute error defined as a float
138 @param _strExcluded: if a "word" contains this string, it is not taken into account for the comparison
139 @type _strExcluded: string
140 @param _lstExcluded: list of words to be excluded for the comparison
141 @type _lstExcluded: list of strings
142
143 """
144 bAlmostEqual = True
145 ERROR_ASSERT_MESSAGE = _strComment + ". "
146 if _oExpected != _oObtained:
147 if _oExpected.__class__ != _oObtained.__class__:
148 EDVerbose.WARNING("Expected is type %s and Obtained is type: %s" % (_oExpected.__class__, _oObtained.__class__))
149 _oExpected = str(_oExpected)
150 _oObtained = str(_oObtained)
151 lstDataReference = _oExpected.replace(">", " ").replace("<", " ").split()
152 lstDataObtained = _oObtained.replace(">", " ").replace("<", " ").split()
153 if len(lstDataReference) == len(lstDataObtained):
154 EDVerbose.DEBUG("Checking for small numerical error...Relative:%s Absolute: %s and similarity in strings >= %s%%" % (_fRelError, _fAbsError, _fStrSimilar * 100))
155 for i in xrange(len(lstDataReference)):
156 dataReference = lstDataReference[i]
157 dataObtained = lstDataObtained[i]
158 if dataReference != dataObtained:
159 if (_strExcluded is not None) and (_strExcluded in dataReference or _strExcluded in dataObtained):
160 continue
161 if len(_lstExcluded) > 0:
162 bFound = False
163 for key in _lstExcluded:
164 if (key in dataReference) or (key in dataObtained):
165 bFound = True
166 break
167 if bFound:
168 continue
169 try:
170 fRefValue = float(dataReference)
171 fObtValue = float(dataObtained)
172 except ValueError:
173 fSimilarity = SequenceMatcher(None, dataReference, dataObtained).quick_ratio()
174 if fSimilarity < _fStrSimilar:
175 if max(len(dataReference), len(dataObtained)) < 100:
176 ERROR_ASSERT_MESSAGE += "\nMismatch on %ith word, between ref: %s, and obt: %s." % (i, dataReference, dataObtained)
177 else:
178 ERROR_ASSERT_MESSAGE += "\nMismatch on %ith word, Similarity of BIG string is %s < %s" % (i, fSimilarity, _fStrSimilar)
179 bAlmostEqual = False
180 break
181 else:
182 EDVerbose.DEBUG("Checking for similarity on %i th word: obtained %.4f%% >= %.4f%%" % (i, fSimilarity * 100.0, _fStrSimilar * 100.0))
183 continue
184 if (fObtValue != fRefValue) and \
185 (2 * abs(fRefValue - fObtValue) / (fObtValue + fRefValue) > _fRelError) and \
186 abs(fRefValue - fObtValue) > _fAbsError:
187
188 ERROR_ASSERT_MESSAGE += "\nMismatch on word %i between ref: %s, and obt: %s." % (i, fRefValue, fObtValue)
189 bAlmostEqual = False
190 break
191 else:
192 ERROR_ASSERT_MESSAGE += "\nStrings do not have the same number of words."
193 bAlmostEqual = False
194
195 if not bAlmostEqual:
196 strExpectedFileName = tempfile.mktemp(suffix="_expected.txt", dir=os.getcwd())
197 strUniqueIndentifier = strExpectedFileName.split("_expected.txt")[0]
198 f = open(strExpectedFileName , "w")
199 f. write(str(_oExpected))
200 f.close()
201 strObtainedFileName = strUniqueIndentifier + "_obtained.txt"
202 f = open(strObtainedFileName, "w")
203 f. write(str(_oObtained))
204 f.close()
205 EQUAL_ERROR_ASSERT_MESSAGE = "FAILURE: %s \nIdentifier %s" % (ERROR_ASSERT_MESSAGE, strUniqueIndentifier)
206 EDVerbose.ASSERT(EQUAL_ERROR_ASSERT_MESSAGE)
207 raise AssertionError, EQUAL_ERROR_ASSERT_MESSAGE
208 else:
209 EDVerbose.ASSERT("OK " + _strComment)
210
211
212 @staticmethod
213 - def isFile(_strFilename, _strComment=""):
214 """
215 Fail if the filename does not exist.
216 @param _strFilename: any python string representing a file that shoul exist
217 @param _strComment: a comment to make your assertion more understandable to the user
218 @type _strComment: python string or unicode string.
219 """
220 if not os.path.isfile(_strFilename):
221 EQUAL_ERROR_ASSERT_MESSAGE = "FAILURE: " + _strComment + "\n Filename does not exist " + _strFilename
222 EDVerbose.ASSERT(EQUAL_ERROR_ASSERT_MESSAGE)
223 raise AssertionError, EQUAL_ERROR_ASSERT_MESSAGE
224 else:
225 EDVerbose.ASSERT("OK " + _strComment)
226
227
228 @staticmethod
229 - def lowerThan(_fValue, _fReference=1.0, _strComment="Lower than"):
230 """
231 Fails if the _fValue is greater (or equal) than the reference.
232 @param _fValue: any python object that can be compared ...
233 @param _strComment: a comment to make your assertion more understandable to the user
234 @type _strComment: python string or unicode string.
235 """
236 if _fValue >= _fReference:
237 EQUAL_ERROR_ASSERT_MESSAGE = "FAILURE: %s\n Obtained value %s should be lower than %s !!" % (_strComment, _fValue, _fReference)
238 EDVerbose.ASSERT(EQUAL_ERROR_ASSERT_MESSAGE)
239 raise AssertionError, EQUAL_ERROR_ASSERT_MESSAGE
240 else:
241 EDVerbose.ASSERT("OK " + _strComment)
242
243
244 @staticmethod
245 - def greaterThan(_fValue, _fReference=1.0, _strComment="Greater than"):
246 """
247 Fails if the _fValue is greater (or equal) than the reference.
248 @param _fValue: any python object that can be compared ...
249 @param _strComment: a comment to make your assertion more understandable to the user
250 @type _strComment: python string or unicode string.
251 """
252 if _fValue <= _fReference:
253 EQUAL_ERROR_ASSERT_MESSAGE = "FAILURE: %s\n Obtained value %s should be greater than %s !!" % (_strComment, _fValue, _fReference)
254 EDVerbose.ASSERT(EQUAL_ERROR_ASSERT_MESSAGE)
255 raise AssertionError, EQUAL_ERROR_ASSERT_MESSAGE
256 else:
257 EDVerbose.ASSERT("OK " + _strComment)
258
259
260 @staticmethod
261 - def arraySimilar(_npaValue, _npaRef, _strComment="Arrays are similar", _fAbsMaxDelta=None, _fRelMaxDelta=None, _fRfactor=None, _fScaledMaxDelta=None):
262 """
263 Tests if two arrays are similar.
264 Two arrays (vectors, matrices, tensors, ...) if the have :
265 * same shape (always tested),
266 * max(abs(Value - Ref)) < _fAbsMaxDelta if _fAbsMaxDelta is defined
267 * max(abs(Value - Ref)/max(abs(Value),abs(Ref)) < _fRelMaxDelta if _fRelMaxDelta is defined
268 * Sigma(abs(Value - Ref)/max(abs(Value),abs(Ref)) < _fRfactor1 if _fRfactor1 is defined
269
270
271 @param _npaRef: reference array
272 @type _npaRef: Numpy like array
273 @param _npaValue: array to be compared with the reference
274 @param _strComment: a comment to make your assertion more understandable to the user
275 @type _strComment: python string or unicode string.
276 @type _fAbsMaxDelta: Float
277 @type _fRelMaxDelta: Float
278 @type _fRfactor1: Float
279 """
280 bAlmostEqual = True
281 try:
282 refShape = _npaRef.shape
283 valShape = _npaValue.shape
284 except Exception:
285 bAlmostEqual = False
286 ERROR_ASSERT_MESSAGE = "Objects passed have no shape attribute"
287
288 if bAlmostEqual and not (refShape == valShape):
289 bAlmostEqual = False
290 ERROR_ASSERT_MESSAGE = "Arrays have different shapes Ref: %s, Obt: %s" % (refShape, valShape)
291
292 if bAlmostEqual and _fAbsMaxDelta is not None:
293 fval = (abs(_npaRef - _npaValue)).max()
294 EDVerbose.DEBUG("Obtained Absolute Max Delta: %.4f" % fval)
295 if fval > _fAbsMaxDelta:
296 ERROR_ASSERT_MESSAGE = "Max delta obtained: %s larger than allowed: %s" % (fval, _fAbsMaxDelta)
297 bAlmostEqual = False
298
299 if bAlmostEqual and _fRelMaxDelta is not None:
300 fval = (abs(_npaRef - _npaValue) / abs(_npaRef)).max()
301 EDVerbose.DEBUG("Obtained Relative Max Delta: %.4f" % fval)
302 if fval > _fRelMaxDelta:
303 ERROR_ASSERT_MESSAGE = "Max relative delta obtained: %s larger than allowed: %s" % (fval, _fRelMaxDelta)
304 bAlmostEqual = False
305
306 if bAlmostEqual and _fRfactor is not None:
307 fval = (abs(_npaRef - _npaValue) / abs(_npaRef)).sum() / len(_npaRef)
308 EDVerbose.DEBUG("Obtained R-factor: %.4f" % fval)
309 if fval > _fRfactor:
310 ERROR_ASSERT_MESSAGE = "R factor obtained: %s larger than allowed: %s" % (fval, _fRfactor)
311 bAlmostEqual = False
312
313 if bAlmostEqual and _fScaledMaxDelta is not None:
314 fval = (abs(_npaRef - _npaValue).max()) / (_npaRef.max() - _npaRef.min())
315 EDVerbose.DEBUG("Obtained Scaled Max Delta: %.4f" % fval)
316 if fval > _fScaledMaxDelta:
317 ERROR_ASSERT_MESSAGE = "Scaled delta obtained: %s larger than allowed: %s" % (fval, _fScaledMaxDelta)
318 bAlmostEqual = False
319
320
321 if not bAlmostEqual:
322 EDVerbose.ASSERT("FAILURE: %s, %s " % (_strComment, ERROR_ASSERT_MESSAGE))
323 raise AssertionError, ERROR_ASSERT_MESSAGE
324 else:
325 EDVerbose.ASSERT("OK " + _strComment)
326