123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- #Copyright ReportLab Europe Ltd. 2000-2017
- #see license.txt for license details
- import reportlab
- reportlab._rl_testing=True
- del reportlab
- __version__='3.3.0'
- __doc__="""Provides support for the test suite.
- The test suite as a whole, and individual tests, need to share
- certain support functions. We have to put these in here so they
- can always be imported, and so that individual tests need to import
- nothing more than "reportlab.whatever..."
- """
- import sys, os, fnmatch, re
- try:
- from configparser import ConfigParser
- except ImportError:
- from ConfigParser import ConfigParser
- import unittest
- from reportlab.lib.utils import isCompactDistro, __rl_loader__, rl_isdir, asUnicode
- # Helper functions.
- def isWritable(D):
- try:
- fn = '00DELETE.ME'
- f = open(fn, 'w')
- f.write('test of writability - can be deleted')
- f.close()
- if os.path.isfile(fn):
- os.remove(fn)
- return 1
- except:
- return 0
- _OUTDIR = None
- RL_HOME = None
- testsFolder = None
- def setOutDir(name):
- """Is it a writable file system distro being invoked within
- test directory? If so, can write test output here. If not,
- it had better go in a temp directory. Only do this once per
- process"""
- global _OUTDIR, RL_HOME, testsFolder
- if _OUTDIR: return _OUTDIR
- D = [d[9:] for d in sys.argv if d.startswith('--outdir=')]
- if not D:
- D = os.environ.get('RL_TEST_OUTDIR','')
- if D: D=[D]
- if D:
- _OUTDIR = D[-1]
- try:
- os.makedirs(_OUTDIR)
- except:
- pass
- for d in D:
- if d in sys.argv:
- sys.argv.remove(d)
- else:
- assert name=='__main__',"setOutDir should only be called in the main script"
- scriptDir=os.path.dirname(sys.argv[0])
- if not scriptDir: scriptDir=os.getcwd()
- _OUTDIR = scriptDir
- if not isWritable(_OUTDIR):
- _OUTDIR = get_rl_tempdir('reportlab_test')
- import reportlab
- RL_HOME=reportlab.__path__[0]
- if not os.path.isabs(RL_HOME): RL_HOME=os.path.normpath(os.path.abspath(RL_HOME))
- topDir = os.path.dirname(RL_HOME)
- testsFolder = os.path.join(topDir,'tests')
- if not os.path.isdir(testsFolder):
- testsFolder = os.path.join(os.path.dirname(topDir),'tests')
- if not os.path.isdir(testsFolder):
- if name=='__main__':
- scriptDir=os.path.dirname(sys.argv[0])
- if not scriptDir: scriptDir=os.getcwd()
- testsFolder = os.path.abspath(scriptDir)
- else:
- testsFolder = None
- if testsFolder:
- sys.path.insert(0,os.path.dirname(testsFolder))
- return _OUTDIR
- _mockumap = (
- None if os.environ.get('OFFLINE_MOCK','1')!='1'
- else'http://www.reportlab.com/rsrc/encryption.gif',
- )
- def mockUrlRead(name):
- if name in _mockumap:
- with open(os.path.join(testsFolder,os.path.basename(name)),'rb') as f:
- return f.read()
- else:
- from urllib.request import urlopen
- return urlopen(name).read()
- def outputfile(fn):
- """This works out where to write test output. If running
- code in a locked down file system, this will be a
- temp directory; otherwise, the output of 'test_foo.py' will
- normally be a file called 'test_foo.pdf', next door.
- """
- D = setOutDir(__name__)
- if fn: D = os.path.join(D,fn)
- return D
- def printLocation(depth=1):
- if sys._getframe(depth).f_locals.get('__name__')=='__main__':
- outDir = outputfile('')
- if outDir!=_OUTDIR:
- print('Logs and output files written to folder "%s"' % outDir)
- def makeSuiteForClasses(*classes):
- "Return a test suite with tests loaded from provided classes."
- suite = unittest.TestSuite()
- loader = unittest.TestLoader()
- for C in classes:
- suite.addTest(loader.loadTestsFromTestCase(C))
- return suite
- def getCVSEntries(folder, files=1, folders=0):
- """Returns a list of filenames as listed in the CVS/Entries file.
- 'folder' is the folder that should contain the CVS subfolder.
- If there is no such subfolder an empty list is returned.
- 'files' is a boolean; 1 and 0 means to return files or not.
- 'folders' is a boolean; 1 and 0 means to return folders or not.
- """
- join = os.path.join
- # If CVS subfolder doesn't exist return empty list.
- try:
- f = open(join(folder, 'CVS', 'Entries'))
- except IOError:
- return []
- # Return names of files and/or folders in CVS/Entries files.
- allEntries = []
- for line in f.readlines():
- if folders and line[0] == 'D' \
- or files and line[0] != 'D':
- entry = line.split('/')[1]
- if entry:
- allEntries.append(join(folder, entry))
- return allEntries
- # Still experimental class extending ConfigParser's behaviour.
- class ExtConfigParser(ConfigParser):
- "A slightly extended version to return lists of strings."
- pat = re.compile(r'\s*\[.*\]\s*')
- def getstringlist(self, section, option):
- "Coerce option to a list of strings or return unchanged if that fails."
- value = ConfigParser.get(self, section, option)
- # This seems to allow for newlines inside values
- # of the config file, but be careful!!
- val = value.replace('\n', '')
- if self.pat.match(val):
- return eval(val,{__builtins__:None})
- else:
- return value
- # This class as suggested by /F with an additional hook
- # to be able to filter filenames.
- class GlobDirectoryWalker:
- "A forward iterator that traverses files in a directory tree."
- def __init__(self, directory, pattern='*'):
- self.index = 0
- self.pattern = pattern
- directory.replace('/',os.sep)
- if os.path.isdir(directory):
- self.stack = [directory]
- self.files = []
- else:
- if not isCompactDistro() or not __rl_loader__ or not rl_isdir(directory):
- raise ValueError('"%s" is not a directory' % directory)
- self.directory = directory[len(__rl_loader__.archive)+len(os.sep):]
- pfx = self.directory+os.sep
- n = len(pfx)
- self.files = list(map(lambda x, n=n: x[n:],list(filter(lambda x,pfx=pfx: x.startswith(pfx),list(__rl_loader__._files.keys())))))
- self.files.sort()
- self.stack = []
- def __getitem__(self, index):
- while 1:
- try:
- file = self.files[self.index]
- self.index = self.index + 1
- except IndexError:
- # pop next directory from stack
- self.directory = self.stack.pop()
- self.files = os.listdir(self.directory)
- # now call the hook
- self.files = self.filterFiles(self.directory, self.files)
- self.index = 0
- else:
- # got a filename
- fullname = os.path.join(self.directory, file)
- if os.path.isdir(fullname) and not os.path.islink(fullname):
- self.stack.append(fullname)
- if fnmatch.fnmatch(file, self.pattern):
- return fullname
- def filterFiles(self, folder, files):
- "Filter hook, overwrite in subclasses as needed."
- return files
- class RestrictedGlobDirectoryWalker(GlobDirectoryWalker):
- "An restricted directory tree iterator."
- def __init__(self, directory, pattern='*', ignore=None):
- GlobDirectoryWalker.__init__(self, directory, pattern)
- if ignore == None:
- ignore = []
- ip = [].append
- if isinstance(ignore,(tuple,list)):
- for p in ignore:
- ip(p)
- elif isinstance(ignore,str):
- ip(ignore)
- self.ignorePatterns = ([_.replace('/',os.sep) for _ in ip.__self__] if os.sep != '/'
- else ip.__self__)
- def filterFiles(self, folder, files):
- "Filters all items from files matching patterns to ignore."
- fnm = fnmatch.fnmatch
- indicesToDelete = []
- for i,f in enumerate(files):
- for p in self.ignorePatterns:
- if fnm(f, p) or fnm(os.path.join(folder,f),p):
- indicesToDelete.append(i)
- indicesToDelete.reverse()
- for i in indicesToDelete:
- del files[i]
- return files
- class CVSGlobDirectoryWalker(GlobDirectoryWalker):
- "An directory tree iterator that checks for CVS data."
- def filterFiles(self, folder, files):
- """Filters files not listed in CVS subfolder.
- This will look in the CVS subfolder of 'folder' for
- a file named 'Entries' and filter all elements from
- the 'files' list that are not listed in 'Entries'.
- """
- join = os.path.join
- cvsFiles = getCVSEntries(folder)
- if cvsFiles:
- indicesToDelete = []
- for i in range(len(files)):
- f = files[i]
- if join(folder, f) not in cvsFiles:
- indicesToDelete.append(i)
- indicesToDelete.reverse()
- for i in indicesToDelete:
- del files[i]
- return files
- # An experimental untested base class with additional 'security'.
- class SecureTestCase(unittest.TestCase):
- """Secure testing base class with additional pre- and postconditions.
- We try to ensure that each test leaves the environment it has
- found unchanged after the test is performed, successful or not.
- Currently we restore sys.path and the working directory, but more
- of this could be added easily, like removing temporary files or
- similar things.
- Use this as a base class replacing unittest.TestCase and call
- these methods in subclassed versions before doing your own
- business!
- """
- def setUp(self):
- "Remember sys.path and current working directory."
- self._initialPath = sys.path[:]
- self._initialWorkDir = os.getcwd()
- def tearDown(self):
- "Restore previous sys.path and working directory."
- sys.path = self._initialPath
- os.chdir(self._initialWorkDir)
- class NearTestCase(unittest.TestCase):
- def assertNear(a,b,accuracy=1e-5):
- if isinstance(a,(float,int)):
- if abs(a-b)>accuracy:
- raise AssertionError("%s not near %s" % (a, b))
- else:
- for ae,be in zip(a,b):
- if abs(ae-be)>accuracy:
- raise AssertionError("%s not near %s" % (a, b))
- assertNear = staticmethod(assertNear)
- class ScriptThatMakesFileTest(unittest.TestCase):
- """Runs a Python script at OS level, expecting it to produce a file.
- It CDs to the working directory to run the script."""
- def __init__(self, scriptDir, scriptName, outFileName, verbose=0):
- self.scriptDir = scriptDir
- self.scriptName = scriptName
- self.outFileName = outFileName
- self.verbose = verbose
- # normally, each instance is told which method to run)
- unittest.TestCase.__init__(self)
- def setUp(self):
- self.cwd = os.getcwd()
- global testsFolder
- scriptDir=self.scriptDir
- if not os.path.isabs(scriptDir):
- scriptDir=os.path.join(testsFolder,scriptDir)
- os.chdir(scriptDir)
- assert os.path.isfile(self.scriptName), "Script %s not found!" % self.scriptName
- if os.path.isfile(self.outFileName):
- os.remove(self.outFileName)
- def tearDown(self):
- os.chdir(self.cwd)
- def runTest(self):
- fmt = sys.platform=='win32' and '"%s" %s' or '%s %s'
- import subprocess
- out = subprocess.check_output((sys.executable,self.scriptName))
- #p = os.popen(fmt % (sys.executable,self.scriptName),'r')
- #out = p.read()
- if self.verbose:
- print(out)
- #status = p.close()
- assert os.path.isfile(self.outFileName), "File %s not created!" % self.outFileName
- def equalStrings(a,b,enc='utf8'):
- return a==b if type(a)==type(b) else asUnicode(a,enc)==asUnicode(b,enc)
- def eqCheck(r,x):
- if r!=x:
- print('Strings unequal\nexp: %s\ngot: %s' % (ascii(x),ascii(r)))
|