"""
Warning:
Every unit test module should have ``Assert.coverage`` test in setUp class!
"""
from __future__ import absolute_import
import unittest
import subprocess
import difflib
from filecmp import dircmp
import os.path
import six
[docs]class Assert(unittest.TestCase):
"""Hellper class for adding functionality to unittesting.
Attributes:
maxDiff (boo): You want to see max diff of error?
"""
def __init__(self):
self.maxDiff = None
@classmethod
[docs] def getErrorMessage(cls, err, replaceChar="'", newChar=""):
"""Getting error message base on python version.
Args:
err (cls): Error class.
replaceChar (str): Replace which string in error msg.
newChar (str): Put new string in error msg.
Return:
Error message string.
"""
if six.PY2:
return err.exception.message
else:
return str(err.exception).replace(replaceChar, newChar)
[docs] def coverage(self, testClass, testClassSkiped, typeRemove, typeElements, typeSkiped):
"""Custom coverage testing for testing class.
Args:
testClass (cls): Test coverage for testing class.
testClassSkiped (arr-str): What methods don't have testing logic?
typeElements (arr-cls): What class/instance is testClass testing?
typeRemove (arr-str): Remove strings from typeElements example: '_Controller'.
typeSkiped (arr-str): What methods testClass don't want to test?
Raises:
ValueError: If typeElements is empty.
ValueError: If testClass don't containes test_ string.
ValueError: If test are missing in testClass.
"""
if not typeElements:
raise ValueError('\n\nERROR: \n' + '=' * 40 + '\n > ' + 'typeElements ==> is empty!')
# Get elements to be tested
elements = []
for typeElement in typeElements:
elements += [element for element in typeElement.__dict__.keys()]
# Get already tested elements
testElements = [element for element in testClass.__dict__.keys()]
# Remove elements in skiped
for skip in testClassSkiped:
try:
testElements.remove(skip)
except:
pass
for skip in typeSkiped:
try:
elements.remove(skip)
except:
pass
# Remove 'test' from testElements
for i in range(len(testElements)):
if 'test_' in testElements[i]:
testElements[i] = testElements[i].replace('test_', '', 1)
else:
raise ValueError('\n\nERROR: ' + testElements[i] + '\n' +
'=' * 40 + '\n > Unittest method does not contains >test_< string!')
# Replace strings
for rm in typeRemove:
for i in range(len(elements)):
elements[i] = elements[i].replace(rm, '', 1)
testMissing = []
for element in elements:
if not element in testElements:
testMissing.append(element)
if testMissing:
raise ValueError('\n\nERROR: Missing tests:\n' + '=' * 40 + '\n > ' + '\n > '.join(sorted(testMissing)))
[docs] def sameDirStructures(self, dir1, dir2):
"""Check if dirs have the same structure
Args:
dir1 (str): Path to first dir structure.
dir2 (str): Path to second -//-.
Raises:
ValueError: If dirs are diferent.
"""
compared = dircmp(dir1, dir2)
error = {}
if compared.left_only or compared.right_only or compared.diff_files or compared.funny_files:
if compared.left_only:
error['left_only'] = compared.left_only
if compared.right_only:
error['right_only'] = compared.right_only
if compared.funny_files:
error['funny_files'] = compared.funny_files
if compared.diff_files:
error['diff_files'] = compared.diff_files
for i, file in enumerate(error['diff_files']):
file1 = open(os.path.join(dir1, file))
file2 = open(os.path.join(dir2, file))
diff = difflib.ndiff(file1.readlines(), file2.readlines())
error['diff_files'][i] += '\n\n'
for line in diff:
error['diff_files'][i] += '\t\t' + line
error['diff_files'][i] += '\n'
str = '\n\nASSERT ERROR:\n' + '=' * 40
for key in error:
str += '\n\n > ' + key.upper() + ': ' + dir2 + '\n\t- ' + '\n\t- '.join(error[key])
raise ValueError(str)
for subdir in compared.common_dirs:
self.sameDirStructures(os.path.join(dir1, subdir), os.path.join(dir2, subdir))
[docs] def termCommand(self, strCommand):
"""Catching terminal stdout/stderr
Args:
strCommand (str): String command to be executed.
Return:
[ True or False, stdout, stderr ] array
will return True if no error is catched, else False.
"""
stdout = ''
stderr = ''
cmdArray = strCommand.split()
try:
process = subprocess.Popen(cmdArray, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1)
except:
return [False, '', 'ERROR : command(' + ' '.join(cmdArray) + ') could not get executed!']
for line in iter(process.stdout.readline, b''):
echoLine = line.decode("utf-8")
stdout += echoLine
for line in iter(process.stderr.readline, b''):
echoLine = line.decode("utf-8")
stderr += echoLine
process.stdout.close()
returnCode = process.wait()
if returnCode != 0 or stderr != '':
return [False, stdout, stderr]
else:
return [True, stdout, stderr]