1#Copyright ReportLab Europe Ltd. 2000-2017 2#see license.txt for license details 3"""Tests for reportlab.lib.utils 4""" 5__version__='3.3.0' 6from reportlab.lib.testutils import setOutDir,makeSuiteForClasses, printLocation 7setOutDir(__name__) 8import os, time, sys 9import reportlab 10from reportlab import rl_config 11import unittest 12from reportlab.lib import colors 13from reportlab.lib.utils import recursiveImport, recursiveGetAttr, recursiveSetAttr, rl_isfile, \ 14 isCompactDistro, isPy3, isPyPy, TimeStamp, rl_get_module, \ 15 recursiveGetAttr, recursiveSetAttr, recursiveDelAttr, \ 16 asUnicode, asUnicodeEx, asBytes 17 18def _rel_open_and_read(fn): 19 from reportlab.lib.utils import open_and_read 20 from reportlab.lib.testutils import testsFolder 21 cwd = os.getcwd() 22 os.chdir(testsFolder) 23 try: 24 return open_and_read(fn) 25 finally: 26 os.chdir(cwd) 27 28class ImporterTestCase(unittest.TestCase): 29 "Test import utilities" 30 31 @classmethod 32 def setUpClass(cls): 33 from reportlab.lib.utils import get_rl_tempdir 34 cls._value = float(repr(time.time())) 35 s = int(cls._value) 36 cls._tempdir = get_rl_tempdir('reportlab_test','tmp_%s' % s) 37 if not os.path.isdir(cls._tempdir): 38 os.makedirs(cls._tempdir,0o700) 39 _testmodulename = os.path.join(cls._tempdir,'test_module_%s.py' % s) 40 with open(_testmodulename,'w') as f: 41 f.write('__all__=[]\nvalue=%s\n' % repr(cls._value)) 42 if sys.platform=='darwin' and isPy3: 43 time.sleep(0.3) 44 cls._testmodulename = os.path.splitext(os.path.basename(_testmodulename))[0] 45 46 @classmethod 47 def tearDownClass(cls): 48 from shutil import rmtree 49 rmtree(cls._tempdir,1) 50 51 def myAssertRaisesRegex(self,*args,**kwds): 52 return getattr(self,'assertRaisesRegex' if isPy3 else 'assertRaisesRegexp')(*args,**kwds) 53 54 def test1(self): 55 "try stuff known to be in the path" 56 m1 = recursiveImport('reportlab.pdfgen.canvas') 57 import reportlab.pdfgen.canvas 58 assert m1 == reportlab.pdfgen.canvas 59 60 def test2(self): 61 "try under a well known directory NOT on the path" 62 from reportlab.lib.testutils import testsFolder 63 D = os.path.join(testsFolder,'..','tools','pythonpoint') 64 fn = os.path.join(D,'stdparser.py') 65 if rl_isfile(fn) or rl_isfile(fn+'c') or rl_isfile(fn+'o'): 66 m1 = recursiveImport('stdparser', baseDir=D) 67 68 def test3(self): 69 "ensure CWD is on the path" 70 try: 71 cwd = os.getcwd() 72 os.chdir(self._tempdir) 73 m1 = recursiveImport(self._testmodulename) 74 finally: 75 os.chdir(cwd) 76 77 def test4(self): 78 "ensure noCWD removes current dir from path" 79 try: 80 cwd = os.getcwd() 81 os.chdir(self._tempdir) 82 import sys 83 try: 84 del sys.modules[self._testmodulename] 85 except KeyError: 86 pass 87 self.assertRaises(ImportError, 88 recursiveImport, 89 self._testmodulename, 90 noCWD=1) 91 finally: 92 os.chdir(cwd) 93 94 def test5(self): 95 "recursive attribute setting/getting on modules" 96 import reportlab.lib.units 97 inch = recursiveGetAttr(reportlab, 'lib.units.inch') 98 assert inch == 72 99 100 recursiveSetAttr(reportlab, 'lib.units.cubit', 18*inch) 101 cubit = recursiveGetAttr(reportlab, 'lib.units.cubit') 102 assert cubit == 18*inch 103 104 def test6(self): 105 "recursive attribute setting/getting on drawings" 106 from reportlab.graphics.charts.barcharts import sampleH1 107 drawing = sampleH1() 108 recursiveSetAttr(drawing, 'barchart.valueAxis.valueMax', 72) 109 theMax = recursiveGetAttr(drawing, 'barchart.valueAxis.valueMax') 110 assert theMax == 72 111 112 def test7(self): 113 "test open and read of a simple relative file" 114 b = _rel_open_and_read('../docs/images/Edit_Prefs.gif') 115 116 def test8(self): 117 "test open and read of a relative file: URL" 118 b = _rel_open_and_read('file:../docs/images/Edit_Prefs.gif') 119 120 def test9(self): 121 "test open and read of an http: URL" 122 from reportlab.lib.utils import open_and_read 123 b = open_and_read('http://www.reportlab.com/rsrc/encryption.gif') 124 125 def test10(self): 126 "test open and read of a simple relative file" 127 from reportlab.lib.utils import open_and_read, getBytesIO 128 b = getBytesIO(_rel_open_and_read('../docs/images/Edit_Prefs.gif')) 129 b = open_and_read(b) 130 131 def test11(self): 132 "test open and read of an RFC 2397 data URI with base64 encoding" 133 result = _rel_open_and_read('data:image/gif;base64,R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=') 134 self.assertEqual(result,b'GIF87a\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff\xff\xff\xff,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;') 135 136 def test12(self): 137 "test open and read of an RFC 2397 data URI without an encoding" 138 result = _rel_open_and_read('data:text/plain;,Hello%20World') 139 self.assertEqual(result,b'Hello World') 140 141 def testRecursiveImportErrors(self): 142 "check we get useful error messages" 143 try: 144 m1 = recursiveImport('reportlab.pdfgen.brush') 145 self.fail("Imported a nonexistent module") 146 except ImportError as e: 147 self.assertIn('reportlab.pdfgen.brush',str(e)) 148 149 try: 150 m1 = recursiveImport('totally.non.existent') 151 self.fail("Imported a nonexistent module") 152 except ImportError as e: 153 self.assertIn('totally',str(e)) 154 155 try: 156 #import a module in the 'tests' directory with a bug 157 m1 = recursiveImport('unimportable') 158 self.fail("Imported a buggy module") 159 except Exception as e: 160 self.assertIn(("integer division by zeroException raised while importing 'unimportable': integer division by zero" 161 if isPyPy 162 else ('division by zero' if isPy3 163 else 'integer division or modulo by zero')) 164 ,str(e)) 165 def test14(self): 166 "test the TimeStamp behaviour" 167 oinvariant = rl_config.invariant 168 sden = 'SOURCE_DATE_EPOCH' 169 if sden in os.environ: 170 sde = os.environ[sden] 171 del os.environ[sden] 172 else: 173 sde = self 174 175 try: 176 rl_config.invariant = False 177 t = time.time() 178 ts = TimeStamp() 179 self.assertTrue(abs(t-ts.t)<1) 180 ts = TimeStamp(invariant=True) 181 self.assertEqual(ts.t,946684800.0) 182 self.assertEqual(ts.YMDhms,(2000, 1, 1, 0, 0, 0)) 183 self.assertEqual(ts.tzname,'UTC') 184 os.environ[sden] = '1490003100' 185 ts = TimeStamp(invariant=True) #debian variable takes precedence here 186 self.assertEqual(ts.t,1490003100) 187 self.assertEqual(ts.YMDhms,(2017, 3, 20, 9, 45, 0)) 188 self.assertEqual(ts.tzname,'UTC') 189 rl_config.invariant = True 190 ts = TimeStamp() #still takes precedence 191 self.assertEqual(ts.t,1490003100) 192 self.assertEqual(ts.YMDhms,(2017, 3, 20, 9, 45, 0)) 193 self.assertEqual(ts.tzname,'UTC') 194 del os.environ[sden] 195 ts = TimeStamp() #now rl_config takes precedence 196 self.assertEqual(ts.t,946684800.0) 197 self.assertEqual(ts.YMDhms,(2000, 1, 1, 0, 0, 0)) 198 self.assertEqual(ts.tzname,'UTC') 199 finally: 200 if sde is not self: 201 os.environ[sden] = sde 202 rl_config.invariant = oinvariant 203 204 def test15(self): 205 m = rl_get_module(self._testmodulename,self._tempdir) 206 self.assertEqual(self._value,m.value) 207 208 def test16(self): 209 from reportlab.lib import fontfinder 210 ff = fontfinder.FontFinder(useCache=False,recur=True) 211 ff.addDirectories(rl_config.T1SearchPath + rl_config.TTFSearchPath) 212 ff.search() 213 ff.getFamilyNames() 214 215 def test17(self): 216 self.assertEqual(asUnicode(u'abc'),u'abc') 217 self.assertEqual(asUnicode(b'abc'),u'abc') 218 self.assertRaises(AttributeError,asUnicode,['abc']) 219 self.myAssertRaisesRegex(AttributeError,r"asUnicode\(.*'list' object has no attribute 'decode'", asUnicode,['abc']) 220 self.assertEqual(asUnicodeEx(u'abc'),u'abc') 221 self.assertEqual(asUnicodeEx(b'abc'),u'abc') 222 self.assertEqual(asUnicodeEx(123),u'123') 223 self.assertEqual(asBytes(u'abc'),b'abc') 224 self.assertEqual(asBytes(b'abc'),b'abc') 225 self.assertRaises(AttributeError,asBytes,['abc']) 226 self.myAssertRaisesRegex(AttributeError,"asBytes\(.*'list' object has no attribute 'encode'", asBytes,['abc']) 227 228class RaccessTest: 229 l = [1, 2] 230 a = [0, [1, 2, [3,4]]] 231 b = {'x': {'y': 'y'}, 'z': [1, 2]} 232 z = 'z' 233 234class RaccessPerson: 235 settings = { 236 'autosave': True, 237 'style': { 238 'height': 30, 239 'width': 200 240 }, 241 'themes': ['light', 'dark'] 242 } 243 def __init__(self, name, age, friends): 244 self.name = name 245 self.age = age 246 self.friends = friends 247 248class RaccessTestCase(unittest.TestCase): 249 "Test recursive access functions" 250 def test1(self): 251 def innerTest(k,v): 252 obj = RaccessTest() 253 obj.t = obj 254 obj.a.append(obj) 255 obj.b['w'] = obj 256 self.assertEqual(recursiveGetAttr(obj,k),v,"error getattr(obj,%r)==%r" % (k,v)) 257 for k,v in [ 258 ('l', RaccessTest.l), 259 ('t.t.t.t.z', 'z'), 260 ('a[0]', 0), 261 ('a[1][0]', 1), 262 ('a[1][2]', [3,4]), 263 ('b["x"]', {'y': 'y'}), 264 ('b["x"]["y"]', 'y'), 265 ('b["z"]', [1,2]), 266 ('b["z"][1]', 2), 267 ('b["w"].z', 'z'), 268 ('b["w"].t.l', [1, 2]), 269 ('a[-1].z', 'z'), 270 ('l[-1]', 2), 271 ('a[2].t.a[-1].z', 'z'), 272 ('a[2].t.b["z"][0]', 1), 273 ('a[-1].t.z', 'z'), 274 ]: 275 innerTest(k,v) 276 277 def test_person_example(self): 278 bob = RaccessPerson(name="Bob", age=31, friends=[]) 279 jill = RaccessPerson(name="Jill", age=29, friends=[bob]) 280 jack = RaccessPerson(name="Jack", age=28, friends=[bob, jill]) 281 282 # Nothing new 283 self.assertEqual(recursiveGetAttr(bob, 'age') ,31) 284 285 # Lists 286 self.assertEqual(recursiveGetAttr(jill, 'friends[0].name') ,'Bob') 287 self.assertEqual(recursiveGetAttr(jack, 'friends[-1].age') ,29) 288 289 # Dict lookups 290 self.assertEqual(recursiveGetAttr(jack, 'settings["style"]["width"]') ,200) 291 292 # Combination of lookups 293 self.assertEqual(recursiveGetAttr(jack, 'settings["themes"][-2]') ,'light') 294 self.assertEqual(recursiveGetAttr(jack, 'friends[-1].settings["themes"][1]') ,'dark') 295 296 # Setattr 297 #recursiveSetAttr(bob, 'settings["style"]["width"]', 400) 298 #self.assertEqual(recursiveGetAttr(bob, 'settings["style"]["width"]') ,400) 299 300 # Nested objects 301 recursiveSetAttr(bob, 'friends', [jack, jill]) 302 self.assertEqual(recursiveGetAttr(jack, 'friends[0].friends[0]') ,jack) 303 304 recursiveSetAttr(jill, 'friends[0].age', 32) 305 self.assertEqual(bob.age ,32) 306 307 # Deletion 308 #recursiveDelAttr(jill, 'friends[0]') 309 #self.assertEqual(len(jill.friends) ,0) 310 311 recursiveDelAttr(jill, 'age') 312 assert not hasattr(jill, 'age') 313 314 recursiveDelAttr(bob, 'friends[0].age') 315 assert not hasattr(jack, 'age') 316 317 # Unsupported 318 #with self.assertRaises(NotImplementedError) as e: 319 # recursiveGetAttr(bob, 'friends[0+1]') 320 321 # Nice try, function calls are not allowed 322 #with self.assertRaises(ValueError): 323 # recursiveGetAttr(bob, 'friends.pop(0)') 324 325 # Must be an expression 326 with self.assertRaises(SyntaxError): 327 recursiveGetAttr(bob, 'friends = []') 328 329 # Must be an expression 330 with self.assertRaises(SyntaxError): 331 recursiveGetAttr(bob, 'friends..') 332 333 # Must be an expression 334 with self.assertRaises(KeyError): 335 recursiveGetAttr(bob, 'settings["DoesNotExist"]') 336 337 # Must be an expression 338 with self.assertRaises(IndexError): 339 recursiveGetAttr(bob, 'friends[100]') 340 341 def test_empty(self): 342 obj = RaccessTest() 343 with self.assertRaises(ValueError): 344 recursiveGetAttr(obj," ") 345 346 with self.assertRaises(ValueError): 347 recursiveGetAttr(obj,"") 348 349 with self.assertRaises(TypeError): 350 recursiveGetAttr(obj, 0) 351 352 with self.assertRaises(TypeError): 353 recursiveGetAttr(obj, None) 354 355 with self.assertRaises(TypeError): 356 recursiveGetAttr(obj, obj) 357 358def makeSuite(): 359 return makeSuiteForClasses(ImporterTestCase,RaccessTestCase) 360 361if __name__ == "__main__": #noruntests 362 unittest.TextTestRunner().run(makeSuite()) 363 printLocation() 364