Package trac :: Package util :: Package tests

Source Code for Package trac.util.tests

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (C) 2006-2020 Edgewall Software 
  4  # All rights reserved. 
  5  # 
  6  # This software is licensed as described in the file COPYING, which 
  7  # you should have received as part of this distribution. The terms 
  8  # are also available at https://trac.edgewall.org/wiki/TracLicense. 
  9  # 
 10  # This software consists of voluntary contributions made by many 
 11  # individuals. For the exact contribution history, see the revision 
 12  # history and logs, available at https://trac.edgewall.org/log/. 
 13   
 14  import doctest 
 15  import importlib 
 16  import os.path 
 17  import pkg_resources 
 18  import random 
 19  import re 
 20  import sys 
 21  import textwrap 
 22  import unittest 
 23   
 24  import trac 
 25  from trac import util 
 26  from trac.test import mkdtemp, rmtree 
 27  from trac.util.tests import (concurrency, datefmt, presentation, text, 
 28                               translation, html) 
29 30 31 -class AtomicFileTestCase(unittest.TestCase):
32
33 - def setUp(self):
34 self.dir = mkdtemp() 35 self.path = os.path.join(self.dir, 'trac-tempfile')
36
37 - def tearDown(self):
38 rmtree(self.dir)
39
40 - def test_non_existing(self):
41 with util.AtomicFile(self.path) as f: 42 f.write('test content') 43 self.assertTrue(f.closed) 44 self.assertEqual('test content', util.read_file(self.path))
45
46 - def test_existing(self):
47 util.create_file(self.path, 'Some content') 48 self.assertEqual('Some content', util.read_file(self.path)) 49 with util.AtomicFile(self.path) as f: 50 f.write('Some new content') 51 self.assertTrue(f.closed) 52 self.assertEqual('Some new content', util.read_file(self.path))
53 54 if os.name != 'nt': 65 66 if util.can_rename_open_file:
68 util.create_file(self.path, 'Initial file content') 69 self.assertEqual('Initial file content', util.read_file(self.path)) 70 with open(self.path) as rf: 71 with util.AtomicFile(self.path) as f: 72 f.write('Replaced content') 73 self.assertTrue(rf.closed) 74 self.assertTrue(f.closed) 75 self.assertEqual('Replaced content', util.read_file(self.path))
76 77 # FIXME: It is currently not possible to make this test pass on all 78 # platforms and with all locales. Typically, it will fail on Linux with 79 # LC_ALL=C. 80 # Python 3 adds sys.setfilesystemencoding(), which could be used here 81 # to remove the dependency on the locale. So the test is disabled until 82 # we require Python 3.
83 - def _test_unicode_path(self):
84 self.path = os.path.join(self.dir, u'träc-témpfilè') 85 with util.AtomicFile(self.path) as f: 86 f.write('test content') 87 self.assertTrue(f.closed) 88 self.assertEqual('test content', util.read_file(self.path))
89
90 91 -class PathTestCase(unittest.TestCase):
92
93 - def assert_below(self, path, parent):
94 self.assertTrue(util.is_path_below(path.replace('/', os.sep), 95 parent.replace('/', os.sep)))
96
97 - def assert_not_below(self, path, parent):
98 self.assertFalse(util.is_path_below(path.replace('/', os.sep), 99 parent.replace('/', os.sep)))
100
101 - def test_is_path_below(self):
102 self.assert_below('/svn/project1', '/svn/project1') 103 self.assert_below('/svn/project1/repos', '/svn/project1') 104 self.assert_below('/svn/project1/sub/repos', '/svn/project1') 105 self.assert_below('/svn/project1/sub/../repos', '/svn/project1') 106 self.assert_not_below('/svn/project2/repos', '/svn/project1') 107 self.assert_not_below('/svn/project2/sub/repos', '/svn/project1') 108 self.assert_not_below('/svn/project1/../project2/repos', 109 '/svn/project1') 110 self.assertTrue(util.is_path_below('repos', os.path.join(os.getcwd()))) 111 self.assertFalse(util.is_path_below('../sub/repos', 112 os.path.join(os.getcwd())))
113
114 - def test_native_path(self):
115 self.assertIsNone(util.native_path(None)) 116 if os.name == 'posix': 117 self.assertEqual('/D/Trac/x', util.native_path('D:\\Trac\\x')) 118 self.assertEqual('/D/Trac/x', util.native_path('/D/Trac/x')) 119 self.assertEqual('/D/', util.native_path('D:\\')) 120 self.assertEqual('/Trac/x', util.native_path('\\Trac\\x')) 121 self.assertEqual('Trac/x', util.native_path('Trac\\x')) 122 self.assertEqual('Trac/x', util.native_path('Trac/x')) 123 elif os.name == 'nt': 124 self.assertEqual('D:\\Trac\\x', util.native_path('/D/Trac/x')) 125 self.assertEqual('D:\\Trac\\x', util.native_path('D:/Trac/x')) 126 self.assertEqual('D:\\Trac\\x', util.native_path('D:\\Trac\\x')) 127 self.assertEqual('D:\\', util.native_path('/D/')) 128 self.assertEqual('D:', util.native_path('/D')) 129 self.assertEqual('C:\\', util.native_path('/')) 130 self.assertEqual('C:\\Trac\\x', util.native_path('/Trac/x')) 131 self.assertEqual('Trac\\x', util.native_path('Trac/x')) 132 self.assertEqual('Trac\\x', util.native_path('Trac\\x'))
133
134 -class RandomTestCase(unittest.TestCase):
135
136 - def setUp(self):
137 self.state = random.getstate()
138
139 - def tearDown(self):
140 random.setstate(self.state)
141
142 - def test_urandom(self):
143 """urandom() returns random bytes""" 144 for i in xrange(129): 145 self.assertEqual(i, len(util.urandom(i))) 146 # For a large enough sample, each value should appear at least once 147 entropy = util.urandom(65536) 148 values = {ord(c) for c in entropy} 149 self.assertEqual(256, len(values))
150
151 - def test_hex_entropy(self):
152 """hex_entropy() returns random hex digits""" 153 hex_digits = set('0123456789abcdef') 154 for i in xrange(129): 155 entropy = util.hex_entropy(i) 156 self.assertEqual(i, len(entropy)) 157 self.assertEqual(set(), set(entropy) - hex_digits)
158
160 """hex_entropy() not affected by global random generator state""" 161 random.seed(0) 162 data = util.hex_entropy(64) 163 random.seed(0) 164 self.assertNotEqual(data, util.hex_entropy(64))
165
166 167 -class ContentDispositionTestCase(unittest.TestCase):
168
169 - def test_filename(self):
170 self.assertEqual('attachment; filename=myfile.txt', 171 util.content_disposition('attachment', 'myfile.txt')) 172 self.assertEqual('attachment; filename=a%20file.txt', 173 util.content_disposition('attachment', 'a file.txt'))
174
175 - def test_no_filename(self):
176 self.assertEqual('inline', util.content_disposition('inline')) 177 self.assertEqual('attachment', util.content_disposition('attachment'))
178
179 - def test_no_type(self):
180 self.assertEqual('filename=myfile.txt', 181 util.content_disposition(filename='myfile.txt')) 182 self.assertEqual('filename=a%20file.txt', 183 util.content_disposition(filename='a file.txt'))
184
185 186 -class SafeReprTestCase(unittest.TestCase):
187 - def test_normal_repr(self):
188 for x in ([1, 2, 3], "été", u"été"): 189 self.assertEqual(repr(x), util.safe_repr(x))
190
191 - def test_buggy_repr(self):
192 class eh_ix(object): 193 def __repr__(self): 194 return 1 + "2"
195 self.assertRaises(Exception, repr, eh_ix()) 196 sr = util.safe_repr(eh_ix()) 197 sr = re.sub('[A-F0-9]{4,}', 'ADDRESS', sr) 198 sr = re.sub(r'__main__|trac\.util\.tests(\.__init__)?', 'MODULE', sr) 199 self.assertEqual("<MODULE.eh_ix object at 0xADDRESS " 200 "(repr() error: TypeError: unsupported operand " 201 "type(s) for +: 'int' and 'str')>", sr) 202
203 204 -class SetuptoolsUtilsTestCase(unittest.TestCase):
205
206 - def setUp(self):
207 self.dir = mkdtemp() 208 sys.path.append(self.dir)
209
210 - def tearDown(self):
211 sys.path.remove(self.dir) 212 rmtree(self.dir)
213
214 - def test_get_module_path(self):
215 self.assertEqual(util.get_module_path(trac), 216 util.get_module_path(util))
217
218 - def test_get_pkginfo_trac(self):
219 pkginfo = util.get_pkginfo(trac) 220 self.assertEqual(trac.__version__, pkginfo.get('version')) 221 self.assertNotEqual({}, pkginfo)
222
224 from trac import core 225 import tracopt 226 pkginfo = util.get_pkginfo(trac) 227 self.assertEqual(pkginfo, util.get_pkginfo(util)) 228 self.assertEqual(pkginfo, util.get_pkginfo(core)) 229 self.assertEqual(pkginfo, util.get_pkginfo(tracopt))
230
231 - def test_get_pkginfo_babel(self):
232 try: 233 import babel 234 import babel.core 235 dist = pkg_resources.get_distribution('Babel') 236 except: 237 pass 238 else: 239 pkginfo = util.get_pkginfo(babel) 240 self.assertNotEqual({}, pkginfo) 241 self.assertEqual(pkginfo, util.get_pkginfo(babel.core))
242
243 - def test_get_pkginfo_pymysql(self):
244 try: 245 import pymysql 246 dist = pkg_resources.get_distribution('pymysql') 247 dist.get_metadata('top_level.txt') 248 except: 249 pass 250 else: 251 pkginfo = util.get_pkginfo(pymysql) 252 self.assertNotEqual({}, pkginfo) 253 self.assertEqual(pkginfo, util.get_pkginfo(pymysql.cursors))
254
256 # python-psycopg2 deb package doesn't provide SOURCES.txt and 257 # top_level.txt 258 try: 259 import psycopg2 260 import psycopg2.extensions 261 dist = pkg_resources.get_distribution('psycopg2') 262 except: 263 pass 264 else: 265 pkginfo = util.get_pkginfo(psycopg2) 266 self.assertNotEqual({}, pkginfo) 267 self.assertEqual(pkginfo, util.get_pkginfo(psycopg2.extensions))
268
269 - def test_file_metadata(self):
270 pkgname = 'TestModule_' + util.hex_entropy(16) 271 modname = pkgname.lower() 272 with open(os.path.join(self.dir, pkgname + '-0.1.egg-info'), 'w') as f: 273 f.write('Metadata-Version: 1.1\n' 274 'Name: %(pkgname)s\n' 275 'Version: 0.1\n' 276 'Author: Joe\n' 277 'Author-email: [email protected]\n' 278 'Maintainer: Jim\n' 279 'Maintainer-email: [email protected]\n' 280 'Home-page: http://example.org/\n' 281 'Summary: summary.\n' 282 'Description: description.\n' 283 'Provides: %(modname)s\n' 284 'Provides: %(modname)s.foo\n' 285 % {'pkgname': pkgname, 'modname': modname}) 286 os.mkdir(os.path.join(self.dir, modname)) 287 for name in ('__init__.py', 'bar.py', 'foo.py'): 288 with open(os.path.join(self.dir, modname, name), 'w') as f: 289 f.write('# -*- coding: utf-8 -*-\n') 290 291 mod = importlib.import_module(modname) 292 mod.bar = importlib.import_module(modname + '.bar') 293 mod.foo = importlib.import_module(modname + '.foo') 294 pkginfo = util.get_pkginfo(mod) 295 self.assertEqual('0.1', pkginfo['version']) 296 self.assertEqual('Joe', pkginfo['author']) 297 self.assertEqual('[email protected]', pkginfo['author_email']) 298 self.assertEqual('Jim', pkginfo['maintainer']) 299 self.assertEqual('[email protected]', pkginfo['maintainer_email']) 300 self.assertEqual('http://example.org/', pkginfo['home_page']) 301 self.assertEqual('summary.', pkginfo['summary']) 302 self.assertEqual('description.', pkginfo['description']) 303 self.assertEqual(pkginfo, util.get_pkginfo(mod.bar)) 304 self.assertEqual(pkginfo, util.get_pkginfo(mod.foo))
305
306 - def _write_module(self, version, url):
307 modname = 'TestModule_' + util.hex_entropy(16) 308 modpath = os.path.join(self.dir, modname + '.py') 309 with open(modpath, 'w') as f: 310 f.write(textwrap.dedent("""\ 311 # -*- coding: utf-8 -*- 312 from trac.core import Component 313 314 version = '%s' 315 author = 'Joe' 316 author_email = '[email protected]' 317 maintainer = 'Jim' 318 maintainer_email = '[email protected]' 319 home_page = '%s' 320 license = 'BSD 3-Clause' 321 summary = 'summary.' 322 trac = 'http://my.trac.com' 323 324 class TestModule(Component): 325 pass 326 """) % (version, url)) 327 return modname
328
329 - def test_get_module_metadata(self):
330 version = '0.1' 331 home_page = 'http://example.org' 332 modname = self._write_module(version, home_page) 333 334 mod = importlib.import_module(modname) 335 info = util.get_module_metadata(mod) 336 337 self.assertEqual(version, info['version']) 338 self.assertEqual('Joe', info['author']) 339 self.assertEqual('[email protected]', info['author_email']) 340 self.assertEqual('Jim', info['maintainer']) 341 self.assertEqual('[email protected]', info['maintainer_email']) 342 self.assertEqual(home_page, info['home_page']) 343 self.assertEqual('summary.', info['summary']) 344 self.assertEqual('BSD 3-Clause', info['license']) 345 self.assertEqual('http://my.trac.com', info['trac'])
346
348 version = '10' 349 url = 'http://example.org' 350 modname = self._write_module('$Rev: %s $' % version, 351 '$URL: %s $' % url) 352 353 mod = importlib.import_module(modname) 354 info = util.get_module_metadata(mod) 355 356 self.assertEqual('r%s' % version, info['version']) 357 self.assertEqual(url, info['home_page'])
358
359 360 -class LazyClass(object):
361 @util.lazy
362 - def f(self):
363 return object()
364
365 366 -class LazyTestCase(unittest.TestCase):
367
368 - def setUp(self):
369 self.obj = LazyClass()
370
371 - def test_lazy_get(self):
372 f = self.obj.f 373 self.assertTrue(self.obj.f is f)
374
375 - def test_lazy_set(self):
376 self.obj.f = 2 377 self.assertEqual(2, self.obj.f)
378
379 - def test_lazy_del(self):
380 f = self.obj.f 381 del self.obj.f 382 self.assertFalse(self.obj.f is f)
383
384 385 -class FileTestCase(unittest.TestCase):
386
387 - def setUp(self):
388 self.dir = mkdtemp() 389 self.filename = os.path.join(self.dir, 'trac-tempfile') 390 self.data = 'Lorem\ripsum\ndolor\r\nsit\namet,\rconsectetur\r\n'
391
392 - def tearDown(self):
393 rmtree(self.dir)
394
396 util.create_file(self.filename, self.data, 'wb') 397 with open(self.filename, 'rb') as f: 398 self.assertEqual(self.data, f.read()) 399 self.assertEqual(self.data, util.read_file(self.filename, 'rb'))
400
401 - def test_touch_file(self):
402 util.create_file(self.filename, self.data, 'wb') 403 util.touch_file(self.filename) 404 with open(self.filename, 'rb') as f: 405 self.assertEqual(self.data, f.read())
406
407 - def test_missing(self):
408 util.touch_file(self.filename) 409 self.assertTrue(os.path.isfile(self.filename)) 410 self.assertEqual(0, os.path.getsize(self.filename))
411
412 -class UtilitiesTestCase(unittest.TestCase):
413
414 - def test_as_int(self):
415 self.assertEqual(1, util.as_int('1')) 416 self.assertEqual(1, util.as_int('1', None)) 417 self.assertIsNone(util.as_int('A', None)) 418 self.assertEqual(2, util.as_int('A', 2)) 419 self.assertEqual(2, util.as_int('1', None, min=2)) 420 self.assertEqual(0, util.as_int('1', None, max=0))
421
422 - def test_as_float(self):
423 self.assertEqual(1.1, util.as_float('1.1')) 424 self.assertEqual(1.1, util.as_float('1.1', None)) 425 self.assertEqual(1, util.as_float('1', None)) 426 self.assertIsNone(util.as_float('A', None)) 427 self.assertEqual(2.2, util.as_float('A', 2.2)) 428 self.assertEqual(2.2, util.as_float('1.1', None, min=2.2)) 429 self.assertEqual(0.1, util.as_float('1.1', None, max=0.1))
430
431 432 -def test_suite():
433 suite = unittest.TestSuite() 434 suite.addTest(unittest.makeSuite(AtomicFileTestCase)) 435 suite.addTest(unittest.makeSuite(PathTestCase)) 436 suite.addTest(unittest.makeSuite(RandomTestCase)) 437 suite.addTest(unittest.makeSuite(ContentDispositionTestCase)) 438 suite.addTest(unittest.makeSuite(SafeReprTestCase)) 439 suite.addTest(unittest.makeSuite(SetuptoolsUtilsTestCase)) 440 suite.addTest(unittest.makeSuite(LazyTestCase)) 441 suite.addTest(unittest.makeSuite(FileTestCase)) 442 suite.addTest(unittest.makeSuite(UtilitiesTestCase)) 443 suite.addTest(concurrency.test_suite()) 444 suite.addTest(datefmt.test_suite()) 445 suite.addTest(presentation.test_suite()) 446 suite.addTest(doctest.DocTestSuite(util)) 447 suite.addTest(text.test_suite()) 448 suite.addTest(translation.test_suite()) 449 suite.addTest(html.test_suite()) 450 suite.addTest(doctest.DocTestSuite(util.html)) 451 return suite
452 453 if __name__ == '__main__': 454 unittest.main(defaultTest='test_suite') 455