I have a background in C and Fortran programming, however I have been trying to learn Python and object orientation. To help with some of my projects I have been trying to define some additional unit tests.
I have used the AssertAlmostEqual unit test, but I found that for large numbers it doesn't work so well, as it works to 7 decimal places (I think). When testing large exponents this becomes a bit useless. So I tried to define an assertEqualSigFig test for significant figures in stead of decimal places. This test was inspired by a stack over flow post, however I cannot find the original post I'm afraid.
This test works for integers floats and booleans however I wanted to see if it would also work with complex numbers. By splitting the numbers into the real and imaginary components and then calling itself. When this happens, no assertion Error is raised and I'm not sure why.
Here is my code:
import unittest
import math
class MyTestClass(unittest.TestCase):
"""
MyTestClass
Adds additional tests to the unit test module:
defines:
- AssertEqualSigFig
description:
- Used in place of the assertAlmostEqualTest, this tests two values
are the same to 7 significant figures (instead of decimal places)
args:
- any two integers, booleans, floats or complex number
returns:
- assertion error if not equal to defined significant figures
"""
def AssertEqualSigFig(self, expected, actual, sig_fig = 7):
if sig_fig < 1:
msg = "sig fig must be more than 1"
raise ValueError(msg)
try:
if isinstance(expected, bool):
if expected != actual:
raise AssertionError
else:
return
elif isinstance(expected, (int,float)):
pow_ex = int(math.floor(math.log(expected,10)))
pow_ac = int(math.floor(math.log(actual,10)))
tolerance = pow_ex - sig_fig + 1
tolerance = (10** tolerance)/2.0
if abs(expected - actual) > tolerance:
raise AssertionError
else:
return
elif isinstance(expected, complex):
#this part doesnt raise an error when it should
a_real = actual.real
a_imag = actual.imag
e_real = expected.real
e_imag = expected.imag
self.AssertEqualSigFig(self, a_imag, e_imag)
self.AssertEqualSigFig(self, a_real, e_real)
except AssertionError:
msg = "{0} ! = {1} to {2} sig fig".format(expected, actual, sig_fig)
raise AssertionError(msg)
This test fails when complex numbers are involved. Here are the unit tests of the unit test that it fails:
import unittest
from MyTestClass import MyTestClass
class TestMyTestClass(MyTestClass):
def test_comlex_imag_NE(self):
a = complex(10,123455)
b = complex(10,123333)
self.assertRaises(AssertionError, self.AssertEqualSigFig, a, b)
def test_complex_real_NE(self):
a = complex(2222222,10)
b = complex(1111111,10)
self.assertRaises(AssertionError, self.AssertEqualSigFig, a, b)
if __name__ == "__main__":
unittest.main()
I think it is because the self.AssertEqualSigFig call does not raise an error. I'm sure there is a silly thing I have missed, But I am still learning. Can anybody help?
I was being an idiot, I have found the solution
I should have been using
MyTestClass.assertEqualSigFig
and not
self.assertEqualSigFig
Related
I am trying to create a python code which can help to increment the version values below by 1,expected output shows the result?I am thinking of splitting the version saving each digit ,increment the last digit by 1 and reconstruct the version,is there a way to this simply in python?
version1 = 1151.1
version2 = 4.1.1
version3 = 275.1.2.99
version4 = 379
next_version1 = version1 + 1
print next_version1
next_version2 = version2 + 1
print next_version2
next_version3 = version3 + 1
print next_version3
next_version4 = version4 + 1
print next_version4
EXPECTED OUTPUT:-
1151.2
4.1.2
275.1.2.100
380
Actually not all the numbers are floats in this case.
You should treat it as strings and update the last element.
version1 = '275.1.2.3'
version2 = '279'
version3 = '275.2.3.10'
def updateVersion(version):
if '.' in version:
version = version.split('.')
version[-1] = str(int(version[-1]) + 1)
version = '.'.join(version)
else:
version = str(int(version)+1)
return version
updateVersion(version1)
updateVersion(version2)
Output:
275.1.2.4
280
275.2.3.11
First and foremost please read about Floating Point Arithmetic: Issues and Limitations
Maybe that was the reason why you ask, not clear.
However, I suggest to save each part as an integer, e.g.
main_version_number = 1151
minor_version_number = 1
sub_version_number = 0
You could maybe have a data structure with those fields (a Version class maybe?) with appropriate methods.
Do not rely on floating point arithmetic.
First off, the code you outline would most certainly give a syntax error.
A number of the form 2 is an integer; 2.2, a floating point; but a 2.2.2, meaningless.
You are looking for tuples here. For instance,
>>> version3 = (275,1,2,3)
Then you would get
>>> version3
(275, 1, 2, 3)
To dirty-update only the last bit of such a tuple, you could do
>>> version3 = version3[:-1] + (version3[-1] + 1,)
>>> version3
(275, 1, 2, 4)
The reason I call this dirty updating is that it will not take care of carrying over into the next significant bit.
Here's a relatively simple script to do just that that I could put together in a couple of minutes. Assuming you have stored your version number as a tuple object called version, attempt the following:
new_version = version
for bit in range(len(version)):
new_version = new_version[:-1-bit] + ((new_version[-1-bit] + 1) % 10,) + new_version[-bit:]
if -2-bit >=0:
new_version = new_version[-2-bit:] + (new_version[-2-bit] + (version[-2-bit] + 1) // 10,) + new_version[-1-bit:]
elif (version[-2-bit] + 1) // 10:
new_version = (1,) + new_version
Alternatively, take a look at bumpversion, a tool that lets you take care of version-numbering within your project, with git integration.
The variables 'version2' and 'version3' will result in a syntax error. This syntax error is caused by the fact that Python does not know of any (number) type that has several points in its value. In essence you are trying to use certain types in a way that they are not meant to be used. More specifically the floating point number is not suitable for your goals. As the name suggests a floating point number, only contains one point and that point can be placed anywhere between its digits (floating).
My advice would be to create your own type/class. This would enable you to store the version number in a way that allows for easy modification of its values and better separation of concerns in your code (i.e. that each part of your code is only concerned with one thing).
Example
class VersionNumber:
"""Represents a version number that can contain parts (major, minor, etc)."""
def __init__(self, *argv):
"""This is the constructor, i.e. a function that is called when you create a new VersionNumber.
The '*argv' allows the user of this class to give a variable amount of arguments. This is why
you can have a version number with only 1 number, and one with 4. The '*argv' is iterable."""
#Create a class specific field, that stores all the version number parts in a list.
self.Parts = []
#Fill it with the supplied arguments.
for part in argv:
self.Parts.append(part)
def __repr__(self):
"""This function is called when the VersionNumber needs to be displayed in the console"""
return str(self)
def __str__(self):
"""This function is called when the VersionNumber is parsed to a string"""
return '.'.join(map(str,self.Parts))
def incrementVersion(self, position, incrementAmount):
"""This function allows you to increment the version number. It does this by adjusting the list
we have set in the constructor."""
self.Parts[position] += incrementAmount
version1 = VersionNumber(1, 23, 45, 0)
print(version1)
#Position -1, takes the last (most right) version number part from the version number.
version1.incrementVersion(-1, 1)
print(version1)
version2 = VersionNumber(346)
print(version2)
version2.incrementVersion(-1, 2)
print(version2)
I would like Z3 to check whether it exists an integer t that satisfies my formula. I'm getting the following error:
Traceback (most recent call last):
File "D:/z3-4.6.0-x64-win/bin/python/Expl20180725.py", line 18, in <module>
g = ForAll(t, f1(t) == And(t>=0, t<10, user[t].rights == ["read"] ))
TypeError: list indices must be integers or slices, not ArithRef
Code:
from z3 import *
import random
from random import randrange
class Struct:
def __init__(self, **entries): self.__dict__.update(entries)
user = [Struct() for i in range(10)]
for i in range(10):
user[i].uid = i
user[i].rights = random.choice(["create","execute","read"])
s=Solver()
f1 = Function('f1', IntSort(), BoolSort())
t = Int('t')
f2 = Exists(t, f1(t))
g = ForAll(t, f1(t) == And(t>=0, t<10, user[t].rights == ["read"] ))
s.add(g)
s.add(f2)
print(s.check())
print(s.model())
You are mixing and matching Python and Z3 expressions, and while that is the whole point of Z3py, it definitely does not mean that you can mix/match them arbitrarily. In general, you should keep all the "concrete" parts in Python, and relegate the symbolic parts to "z3"; carefully coordinating the interaction in between. In your particular case, you are accessing a Python list (your user) with a symbolic z3 integer (t), and that is certainly not something that is allowed. You have to use a Z3 symbolic Array to access with a symbolic index.
The other issue is the use of strings ("create"/"read" etc.) and expecting them to have meanings in the symbolic world. That is also not how z3py is intended to be used. If you want them to mean something in the symbolic world, you'll have to model them explicitly.
I'd strongly recommend reading through http://ericpony.github.io/z3py-tutorial/guide-examples.htm which is a great introduction to z3py including many of the advanced features.
Having said all that, I'd be inclined to code your example as follows:
from z3 import *
import random
Right, (create, execute, read) = EnumSort('Right', ('create', 'execute', 'read'))
users = Array('Users', IntSort(), Right)
for i in range(10):
users = Store(users, i, random.choice([create, execute, read]))
s = Solver()
t = Int('t')
s.add(t >= 0)
s.add(t < 10)
s.add(users[t] == read)
r = s.check()
if r == sat:
print s.model()[t]
else:
print r
Note how the enumerated type Right in the symbolic land is used to model your "permissions."
When I run this program multiple times, I get:
$ python a.py
5
$ python a.py
9
$ python a.py
unsat
$ python a.py
6
Note how unsat is produced, if it happens that the "random" initialization didn't put any users with a read permission.
I am trying to partition a large string F to m blocks as follows:
import random
from largeprimes import generateRandom
def generateRandom(length):
t = random.randint(0, 2**length)
str = "{0:b}".format(t)
if (len(str) < length):
str.zfill(length)
return str
def divide_file_block (): #return bi: int
b = []
for i in range (0, m):
b_i = F[(i*blocklen) : ((i+1)* blocklen)]
temp = int(b_i, 2) % q
b.append(temp)
return b
F = generateRandom(102400)
m = 100
blocklen = len(F)/m
q = generateLargePrime(1024) # generateLargePrime is from https://langui.sh/2009/03/07/generating-very-large-primes/
print divide_file_block ()
Note: you shall copy the code from 1 to the current directory, remove the last print statement there and name it largpeprimes.py. This makes the generateLargePrime function importable.
When I test on a small example, it printed out correct result. But when I test on b_i and q having 1024 bits, it printed out the error:
temp = int(b_i, 2) % q
TypeError: unsupported operand type(s) for %: 'int' and 'str'
Could you please explain me why and give me a suggestion to show this problem. Thanks in advance.
The function generateLargePrime sometime returns string
Trying the code I run into the same problem.
Testing it in debugger I got the cause: q has a value of `'Failure after 1100.0 tries.'
This is definitely a string an causes the failures.
I would recommend modifying the generateLargePrime code to throw an exception instead of reporting the failure by returned value.
Few tips for detecting this kind of problems
print out the problematic value
This is the simplest (and probably most popular) quick solution.
something like
b_i = F[(i*blocklen) : ((i+1)* blocklen)]
print "q", q # here is all the magic
temp = int(b_i, 2) % q
would tell you the value
place assert into your code
b_i = F[(i*blocklen) : ((i+1)* blocklen)]
assert isinstance(q, int)
temp = int(b_i, 2) % q
would throw an exception as soon as q is not of type int
run the code in debugger
pdb comes with Python, I prefer ipdb which comes with IPython, both would help you.
have the failing code written as a script
Try to run it via Python interpreter
$ python failingscript.py
as it fails, you simply try once more, but instead of python use pdb or ipdb
$ ipdb failingscript.py
the debugger let you control running the code line by line. Usually I let it run by "c" (continue) command and it soon crashes at the problematic point. Then I use "l" (list) to see, what line of code we are at, and finally use "p" (print) command to print values of variables, which are making problems. This way I used "p q" and found, it is a string.
It takes a moment to learn pdb or ipdb, but it works as turbo resolver so it is definitely the skill to learn. Great tutorial is at PMotW
I build the class for geometric transformation. When I run a Unit test it fails because of rounding errors coming from the operations inside my methods.
In my test I compare the result from one of the method which should return the point (2,2,0), but because of rounding errors it returns (1.9999999999999996, 1.9999999999999996, 0.0)
Finding files... done.
Importing test modules ... done.
** DEBUG_45 from the method point=(1.9999999999999996, 1.9999999999999996, 0.0)
======================================================================
FAIL: testPointCoord (vectOper.test.TestNearestPoint.TestNearestPoint)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\src\vectOper\test\TestNearestPoint.py", line 14, in testPointCoord
self.assertEqual(pointCoord, (2,2,0), "nearest point failed")
AssertionError: nearest point failed
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
From the calculation point of view it is acceptable, but I don't want my code to fail on the simple unit test.
import unittest
from vectOper.nearestPoint import NearestPoint
class TestNearestPoint(unittest.TestCase):
def testPointCoord(self):
nearestPoint = NearestPoint()
pointCoord = nearestPoint.pointCoord(samplePoint=(2,2,2),lineStart=(0,0,0), lineVect=(1,1,0))
self.assertEqual(pointCoord, (2,2,0), "nearest point failed")
What is a correct way to resolve problem like that? Obviously I cannot round up the output numbers or convert them to integers as normally it is not the case.
Is there a way to code unit test to ignore rounding error?
Is there any other way to resolve problem?
Edit:
The question can be solved by using self.assertAlmostEqual as rightly suggested in another answer but the problem is that I need to test entrance of a tuple. After all suggestions I try to do:
def testPointCoord(self):
nearestPoint = NearestPoint()
pointCoord = nearestPoint.pointCoord(samplePoint=(2,2,2),lineStart=(0,0,0), lineVect=(1,1,0))
self.assertAlmostEqual(pointCoord[0], 2, places=7, msg="nearest point x-coodr failed")
self.assertAlmostEqual(pointCoord[1], 2, places=7, msg="nearest point y-coodr failed")
self.assertAlmostEqual(pointCoord[2], 0, places=7, msg="nearest point z-coodr failed")
but I need to automatise it somehow as later I need to test a list of tuples as the sample points' coordinates for a vector field.
The solution suggested as a duplicate is only a half measure as it would be a bit tedious write 300 more comparisons if there is 100 tuples in the list.
Why don't you use assertAlmostEqual in each dimension using map?
I don`t have access to your class, so i wrote a similar example here:
from unittest import TestCase
class Test_Tuple_Equality(TestCase):
def test_tuple_equality_True(self):
p1 = (0.00000001, 0.00000000001, 0)
p2 = (0,0,0)
map(lambda x, y: self.assertAlmostEqual(x,y), p1, p2)
def test_tuple_equality_False(self):
p1 = (0.00000001, 0.00000000001, 0)
p2 = (1,0,0)
map(lambda x, y: self.assertAlmostEqual(x,y), p1, p2)
Map will transform your a n-dimension tuple comparisson into n floats comparissons.
You can even create a compare_points function, like:
def compare_points(self, p1, p2):
map(lambda x,y: self.assertAlmostEqual(x,y), p1,p2)
And then use it in your tests
Another solution is to use numpy`s method for that:
import numpy
>>>numpy.testing.assert_almost_equal((2,2,0), (1.9999999999,2,0), decimal=7, err_msg='', verbose=True)
Numpy is a pain to install, but, if you already use it, it would be the best fit.
I am on a learning stage of using python as a tool for software QA.
I wrote the next simple test in order to find the letter 'a' in a text file number matrix.
problem is that the test fails even though the expect equals to what i got.
Why is that? Can you tell me what am I doing wrong?
test script:
fin = open("abc.txt", "r")
arr_fin = []
for line in fin:
arr_fin.append(line.split())
print arr_fin
for row in arr_fin:
arr_fin_1 = " ".join('{0:4}'.format(i or " ") for i in row)
print arr_fin_1
def find_letter(x, arr_fin_1):
"""
>>> find_letter('a', arr_fin_1)
97
"""
t=ord(x) #exchange to letter's ASCII value
for i in arr_fin_1:
if i==x:
print t
return;
def _test():
import doctest
doctest.testmod()
if __name__ == "__main__":
_test()
error message:
Expected:
97
Got:
97
**********************************************************************
1 items had failures:
1 of 1 in __main__.find_letter
***Test Failed*** 1 failures.
You've got an extra space after the 97 - if you remove it, your test should run fine.
This:
return;
Makes your function return None.
Did you mean return t?
Besides that, IMHO doctest tests are meant to be self-contained. This is something the user should see in your documentation and understand without context. In your example, you're using a module-local arr_fin_1 object which is completely opaque to the user. It's better to define it in the doctest before the find_letter call to provide a self-contained example.