##// END OF EJS Templates
fixed AGE funtion for future dates, testa are now cleaner with dateutil usage
marcink -
r3261:54a439ed beta
parent child Browse files
Show More
@@ -1,569 +1,573 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Some simple helper functions
7 7
8 8 :created_on: Jan 5, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import re
27 27 import time
28 28 import datetime
29 29 import webob
30 30
31 31 from pylons.i18n.translation import _, ungettext
32 32 from rhodecode.lib.vcs.utils.lazy import LazyProperty
33 33
34 34
35 35 def __get_lem():
36 36 """
37 37 Get language extension map based on what's inside pygments lexers
38 38 """
39 39 from pygments import lexers
40 40 from string import lower
41 41 from collections import defaultdict
42 42
43 43 d = defaultdict(lambda: [])
44 44
45 45 def __clean(s):
46 46 s = s.lstrip('*')
47 47 s = s.lstrip('.')
48 48
49 49 if s.find('[') != -1:
50 50 exts = []
51 51 start, stop = s.find('['), s.find(']')
52 52
53 53 for suffix in s[start + 1:stop]:
54 54 exts.append(s[:s.find('[')] + suffix)
55 55 return map(lower, exts)
56 56 else:
57 57 return map(lower, [s])
58 58
59 59 for lx, t in sorted(lexers.LEXERS.items()):
60 60 m = map(__clean, t[-2])
61 61 if m:
62 62 m = reduce(lambda x, y: x + y, m)
63 63 for ext in m:
64 64 desc = lx.replace('Lexer', '')
65 65 d[ext].append(desc)
66 66
67 67 return dict(d)
68 68
69 69
70 70 def str2bool(_str):
71 71 """
72 72 returs True/False value from given string, it tries to translate the
73 73 string into boolean
74 74
75 75 :param _str: string value to translate into boolean
76 76 :rtype: boolean
77 77 :returns: boolean from given string
78 78 """
79 79 if _str is None:
80 80 return False
81 81 if _str in (True, False):
82 82 return _str
83 83 _str = str(_str).strip().lower()
84 84 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
85 85
86 86
87 87 def aslist(obj, sep=None, strip=True):
88 88 """
89 89 Returns given string separated by sep as list
90 90
91 91 :param obj:
92 92 :param sep:
93 93 :param strip:
94 94 """
95 95 if isinstance(obj, (basestring)):
96 96 lst = obj.split(sep)
97 97 if strip:
98 98 lst = [v.strip() for v in lst]
99 99 return lst
100 100 elif isinstance(obj, (list, tuple)):
101 101 return obj
102 102 elif obj is None:
103 103 return []
104 104 else:
105 105 return [obj]
106 106
107 107
108 108 def convert_line_endings(line, mode):
109 109 """
110 110 Converts a given line "line end" accordingly to given mode
111 111
112 112 Available modes are::
113 113 0 - Unix
114 114 1 - Mac
115 115 2 - DOS
116 116
117 117 :param line: given line to convert
118 118 :param mode: mode to convert to
119 119 :rtype: str
120 120 :return: converted line according to mode
121 121 """
122 122 from string import replace
123 123
124 124 if mode == 0:
125 125 line = replace(line, '\r\n', '\n')
126 126 line = replace(line, '\r', '\n')
127 127 elif mode == 1:
128 128 line = replace(line, '\r\n', '\r')
129 129 line = replace(line, '\n', '\r')
130 130 elif mode == 2:
131 131 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
132 132 return line
133 133
134 134
135 135 def detect_mode(line, default):
136 136 """
137 137 Detects line break for given line, if line break couldn't be found
138 138 given default value is returned
139 139
140 140 :param line: str line
141 141 :param default: default
142 142 :rtype: int
143 143 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
144 144 """
145 145 if line.endswith('\r\n'):
146 146 return 2
147 147 elif line.endswith('\n'):
148 148 return 0
149 149 elif line.endswith('\r'):
150 150 return 1
151 151 else:
152 152 return default
153 153
154 154
155 155 def generate_api_key(username, salt=None):
156 156 """
157 157 Generates unique API key for given username, if salt is not given
158 158 it'll be generated from some random string
159 159
160 160 :param username: username as string
161 161 :param salt: salt to hash generate KEY
162 162 :rtype: str
163 163 :returns: sha1 hash from username+salt
164 164 """
165 165 from tempfile import _RandomNameSequence
166 166 import hashlib
167 167
168 168 if salt is None:
169 169 salt = _RandomNameSequence().next()
170 170
171 171 return hashlib.sha1(username + salt).hexdigest()
172 172
173 173
174 174 def safe_int(val, default=None):
175 175 """
176 176 Returns int() of val if val is not convertable to int use default
177 177 instead
178 178
179 179 :param val:
180 180 :param default:
181 181 """
182 182
183 183 try:
184 184 val = int(val)
185 185 except (ValueError, TypeError):
186 186 val = default
187 187
188 188 return val
189 189
190 190
191 191 def safe_unicode(str_, from_encoding=None):
192 192 """
193 193 safe unicode function. Does few trick to turn str_ into unicode
194 194
195 195 In case of UnicodeDecode error we try to return it with encoding detected
196 196 by chardet library if it fails fallback to unicode with errors replaced
197 197
198 198 :param str_: string to decode
199 199 :rtype: unicode
200 200 :returns: unicode object
201 201 """
202 202 if isinstance(str_, unicode):
203 203 return str_
204 204
205 205 if not from_encoding:
206 206 import rhodecode
207 207 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
208 208 'utf8'), sep=',')
209 209 from_encoding = DEFAULT_ENCODINGS
210 210
211 211 if not isinstance(from_encoding, (list, tuple)):
212 212 from_encoding = [from_encoding]
213 213
214 214 try:
215 215 return unicode(str_)
216 216 except UnicodeDecodeError:
217 217 pass
218 218
219 219 for enc in from_encoding:
220 220 try:
221 221 return unicode(str_, enc)
222 222 except UnicodeDecodeError:
223 223 pass
224 224
225 225 try:
226 226 import chardet
227 227 encoding = chardet.detect(str_)['encoding']
228 228 if encoding is None:
229 229 raise Exception()
230 230 return str_.decode(encoding)
231 231 except (ImportError, UnicodeDecodeError, Exception):
232 232 return unicode(str_, from_encoding[0], 'replace')
233 233
234 234
235 235 def safe_str(unicode_, to_encoding=None):
236 236 """
237 237 safe str function. Does few trick to turn unicode_ into string
238 238
239 239 In case of UnicodeEncodeError we try to return it with encoding detected
240 240 by chardet library if it fails fallback to string with errors replaced
241 241
242 242 :param unicode_: unicode to encode
243 243 :rtype: str
244 244 :returns: str object
245 245 """
246 246
247 247 # if it's not basestr cast to str
248 248 if not isinstance(unicode_, basestring):
249 249 return str(unicode_)
250 250
251 251 if isinstance(unicode_, str):
252 252 return unicode_
253 253
254 254 if not to_encoding:
255 255 import rhodecode
256 256 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
257 257 'utf8'), sep=',')
258 258 to_encoding = DEFAULT_ENCODINGS
259 259
260 260 if not isinstance(to_encoding, (list, tuple)):
261 261 to_encoding = [to_encoding]
262 262
263 263 for enc in to_encoding:
264 264 try:
265 265 return unicode_.encode(enc)
266 266 except UnicodeEncodeError:
267 267 pass
268 268
269 269 try:
270 270 import chardet
271 271 encoding = chardet.detect(unicode_)['encoding']
272 272 if encoding is None:
273 273 raise UnicodeEncodeError()
274 274
275 275 return unicode_.encode(encoding)
276 276 except (ImportError, UnicodeEncodeError):
277 277 return unicode_.encode(to_encoding[0], 'replace')
278 278
279 279 return safe_str
280 280
281 281
282 282 def remove_suffix(s, suffix):
283 283 if s.endswith(suffix):
284 284 s = s[:-1 * len(suffix)]
285 285 return s
286 286
287 287
288 288 def remove_prefix(s, prefix):
289 289 if s.startswith(prefix):
290 290 s = s[len(prefix):]
291 291 return s
292 292
293 293
294 294 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
295 295 """
296 296 Custom engine_from_config functions that makes sure we use NullPool for
297 297 file based sqlite databases. This prevents errors on sqlite. This only
298 298 applies to sqlalchemy versions < 0.7.0
299 299
300 300 """
301 301 import sqlalchemy
302 302 from sqlalchemy import engine_from_config as efc
303 303 import logging
304 304
305 305 if int(sqlalchemy.__version__.split('.')[1]) < 7:
306 306
307 307 # This solution should work for sqlalchemy < 0.7.0, and should use
308 308 # proxy=TimerProxy() for execution time profiling
309 309
310 310 from sqlalchemy.pool import NullPool
311 311 url = configuration[prefix + 'url']
312 312
313 313 if url.startswith('sqlite'):
314 314 kwargs.update({'poolclass': NullPool})
315 315 return efc(configuration, prefix, **kwargs)
316 316 else:
317 317 import time
318 318 from sqlalchemy import event
319 319 from sqlalchemy.engine import Engine
320 320
321 321 log = logging.getLogger('sqlalchemy.engine')
322 322 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
323 323 engine = efc(configuration, prefix, **kwargs)
324 324
325 325 def color_sql(sql):
326 326 COLOR_SEQ = "\033[1;%dm"
327 327 COLOR_SQL = YELLOW
328 328 normal = '\x1b[0m'
329 329 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
330 330
331 331 if configuration['debug']:
332 332 #attach events only for debug configuration
333 333
334 334 def before_cursor_execute(conn, cursor, statement,
335 335 parameters, context, executemany):
336 336 context._query_start_time = time.time()
337 337 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
338 338
339 339 def after_cursor_execute(conn, cursor, statement,
340 340 parameters, context, executemany):
341 341 total = time.time() - context._query_start_time
342 342 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
343 343
344 344 event.listen(engine, "before_cursor_execute",
345 345 before_cursor_execute)
346 346 event.listen(engine, "after_cursor_execute",
347 347 after_cursor_execute)
348 348
349 349 return engine
350 350
351 351
352 352 def age(prevdate):
353 353 """
354 354 turns a datetime into an age string.
355 355
356 356 :param prevdate: datetime object
357 357 :rtype: unicode
358 358 :returns: unicode words describing age
359 359 """
360
360 now = datetime.datetime.now()
361 now = now.replace(microsecond=0)
361 362 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
362 363 deltas = {}
363 364 future = False
364 365
365 # Get date parts deltas
366 now = datetime.datetime.now()
367 366 if prevdate > now:
368 367 now, prevdate = prevdate, now
369 368 future = True
370 369
370 # Get date parts deltas
371 371 for part in order:
372 deltas[part] = getattr(now, part) - getattr(prevdate, part)
372 if future:
373 import dateutil
374 d = dateutil.relativedelta.relativedelta(now, prevdate)
375 deltas[part] = getattr(d, part + 's')
376 else:
377 deltas[part] = getattr(now, part) - getattr(prevdate, part)
373 378
374 379 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
375 380 # not 1 hour, -59 minutes and -59 seconds)
376
377 381 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
378 382 part = order[num]
379 383 carry_part = order[num - 1]
380 384
381 385 if deltas[part] < 0:
382 386 deltas[part] += length
383 387 deltas[carry_part] -= 1
384 388
385 389 # Same thing for days except that the increment depends on the (variable)
386 390 # number of days in the month
387 391 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
388 392 if deltas['day'] < 0:
389 393 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
390 394 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
391 395 deltas['day'] += 29
392 396 else:
393 397 deltas['day'] += month_lengths[prevdate.month - 1]
394 398
395 399 deltas['month'] -= 1
396 400
397 401 if deltas['month'] < 0:
398 402 deltas['month'] += 12
399 403 deltas['year'] -= 1
400 404
401 405 # Format the result
402 406 fmt_funcs = {
403 407 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
404 408 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
405 409 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
406 410 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
407 411 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
408 412 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
409 413 }
410 414
411 415 for i, part in enumerate(order):
412 416 value = deltas[part]
413 417 if value == 0:
414 418 continue
415 419
416 420 if i < 5:
417 421 sub_part = order[i + 1]
418 422 sub_value = deltas[sub_part]
419 423 else:
420 424 sub_value = 0
421 425
422 426 if sub_value == 0:
423 427 if future:
424 428 return _(u'in %s') % fmt_funcs[part](value)
425 429 else:
426 430 return _(u'%s ago') % fmt_funcs[part](value)
427 431 if future:
428 432 return _(u'in %s and %s') % (fmt_funcs[part](value),
429 433 fmt_funcs[sub_part](sub_value))
430 434 else:
431 435 return _(u'%s and %s ago') % (fmt_funcs[part](value),
432 436 fmt_funcs[sub_part](sub_value))
433 437
434 438 return _(u'just now')
435 439
436 440
437 441 def uri_filter(uri):
438 442 """
439 443 Removes user:password from given url string
440 444
441 445 :param uri:
442 446 :rtype: unicode
443 447 :returns: filtered list of strings
444 448 """
445 449 if not uri:
446 450 return ''
447 451
448 452 proto = ''
449 453
450 454 for pat in ('https://', 'http://'):
451 455 if uri.startswith(pat):
452 456 uri = uri[len(pat):]
453 457 proto = pat
454 458 break
455 459
456 460 # remove passwords and username
457 461 uri = uri[uri.find('@') + 1:]
458 462
459 463 # get the port
460 464 cred_pos = uri.find(':')
461 465 if cred_pos == -1:
462 466 host, port = uri, None
463 467 else:
464 468 host, port = uri[:cred_pos], uri[cred_pos + 1:]
465 469
466 470 return filter(None, [proto, host, port])
467 471
468 472
469 473 def credentials_filter(uri):
470 474 """
471 475 Returns a url with removed credentials
472 476
473 477 :param uri:
474 478 """
475 479
476 480 uri = uri_filter(uri)
477 481 #check if we have port
478 482 if len(uri) > 2 and uri[2]:
479 483 uri[2] = ':' + uri[2]
480 484
481 485 return ''.join(uri)
482 486
483 487
484 488 def get_changeset_safe(repo, rev):
485 489 """
486 490 Safe version of get_changeset if this changeset doesn't exists for a
487 491 repo it returns a Dummy one instead
488 492
489 493 :param repo:
490 494 :param rev:
491 495 """
492 496 from rhodecode.lib.vcs.backends.base import BaseRepository
493 497 from rhodecode.lib.vcs.exceptions import RepositoryError
494 498 from rhodecode.lib.vcs.backends.base import EmptyChangeset
495 499 if not isinstance(repo, BaseRepository):
496 500 raise Exception('You must pass an Repository '
497 501 'object as first argument got %s', type(repo))
498 502
499 503 try:
500 504 cs = repo.get_changeset(rev)
501 505 except RepositoryError:
502 506 cs = EmptyChangeset(requested_revision=rev)
503 507 return cs
504 508
505 509
506 510 def datetime_to_time(dt):
507 511 if dt:
508 512 return time.mktime(dt.timetuple())
509 513
510 514
511 515 def time_to_datetime(tm):
512 516 if tm:
513 517 if isinstance(tm, basestring):
514 518 try:
515 519 tm = float(tm)
516 520 except ValueError:
517 521 return
518 522 return datetime.datetime.fromtimestamp(tm)
519 523
520 524 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
521 525
522 526
523 527 def extract_mentioned_users(s):
524 528 """
525 529 Returns unique usernames from given string s that have @mention
526 530
527 531 :param s: string to get mentions
528 532 """
529 533 usrs = set()
530 534 for username in re.findall(MENTIONS_REGEX, s):
531 535 usrs.add(username)
532 536
533 537 return sorted(list(usrs), key=lambda k: k.lower())
534 538
535 539
536 540 class AttributeDict(dict):
537 541 def __getattr__(self, attr):
538 542 return self.get(attr, None)
539 543 __setattr__ = dict.__setitem__
540 544 __delattr__ = dict.__delitem__
541 545
542 546
543 547 def fix_PATH(os_=None):
544 548 """
545 549 Get current active python path, and append it to PATH variable to fix issues
546 550 of subprocess calls and different python versions
547 551 """
548 552 import sys
549 553 if os_ is None:
550 554 import os
551 555 else:
552 556 os = os_
553 557
554 558 cur_path = os.path.split(sys.executable)[0]
555 559 if not os.environ['PATH'].startswith(cur_path):
556 560 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
557 561
558 562
559 563 def obfuscate_url_pw(engine):
560 564 from sqlalchemy.engine import url
561 565 url = url.make_url(engine)
562 566 if url.password:
563 567 url.password = 'XXXXX'
564 568 return str(url)
565 569
566 570
567 571 def get_server_url(environ):
568 572 req = webob.Request(environ)
569 573 return req.host_url + req.script_name
@@ -1,217 +1,214 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.tests.test_libs
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6
7 7 Package for testing various lib/helper functions in rhodecode
8 8
9 9 :created_on: Jun 9, 2011
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 from __future__ import with_statement
26 26 import unittest
27 27 import datetime
28 28 import hashlib
29 29 import mock
30 30 from rhodecode.tests import *
31 31
32 32 proto = 'http'
33 33 TEST_URLS = [
34 34 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
35 35 '%s://127.0.0.1' % proto),
36 36 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
37 37 '%s://127.0.0.1' % proto),
38 38 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
39 39 '%s://127.0.0.1' % proto),
40 40 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
41 41 '%s://127.0.0.1:8080' % proto),
42 42 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
43 43 '%s://domain.org' % proto),
44 44 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
45 45 '8080'],
46 46 '%s://domain.org:8080' % proto),
47 47 ]
48 48
49 49 proto = 'https'
50 50 TEST_URLS += [
51 51 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
52 52 '%s://127.0.0.1' % proto),
53 53 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
54 54 '%s://127.0.0.1' % proto),
55 55 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
56 56 '%s://127.0.0.1' % proto),
57 57 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
58 58 '%s://127.0.0.1:8080' % proto),
59 59 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
60 60 '%s://domain.org' % proto),
61 61 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
62 62 '8080'],
63 63 '%s://domain.org:8080' % proto),
64 64 ]
65 65
66 66
67 67 class TestLibs(unittest.TestCase):
68 68
69 69 def test_uri_filter(self):
70 70 from rhodecode.lib.utils2 import uri_filter
71 71
72 72 for url in TEST_URLS:
73 73 self.assertEqual(uri_filter(url[0]), url[1])
74 74
75 75 def test_credentials_filter(self):
76 76 from rhodecode.lib.utils2 import credentials_filter
77 77
78 78 for url in TEST_URLS:
79 79 self.assertEqual(credentials_filter(url[0]), url[2])
80 80
81 81 def test_str2bool(self):
82 82 from rhodecode.lib.utils2 import str2bool
83 83 test_cases = [
84 84 ('t', True),
85 85 ('true', True),
86 86 ('y', True),
87 87 ('yes', True),
88 88 ('on', True),
89 89 ('1', True),
90 90 ('Y', True),
91 91 ('yeS', True),
92 92 ('Y', True),
93 93 ('TRUE', True),
94 94 ('T', True),
95 95 ('False', False),
96 96 ('F', False),
97 97 ('FALSE', False),
98 98 ('0', False),
99 99 ('-1', False),
100 100 ('', False), ]
101 101
102 102 for case in test_cases:
103 103 self.assertEqual(str2bool(case[0]), case[1])
104 104
105 105 def test_mention_extractor(self):
106 106 from rhodecode.lib.utils2 import extract_mentioned_users
107 107 sample = (
108 108 "@first hi there @marcink here's my email marcin@email.com "
109 109 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
110 110 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
111 111 "@marian.user just do it @marco-polo and next extract @marco_polo "
112 112 "user.dot hej ! not-needed maril@domain.org"
113 113 )
114 114
115 115 s = sorted([
116 116 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
117 117 'marian.user', 'marco-polo', 'marco_polo'
118 118 ], key=lambda k: k.lower())
119 119 self.assertEqual(s, extract_mentioned_users(sample))
120 120
121 121 def test_age(self):
122 import calendar
123 122 from rhodecode.lib.utils2 import age
123 from dateutil import relativedelta
124 124 n = datetime.datetime.now()
125 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
126 prev_month = n.month - 1 if n.month != 1 else n.month - 2
125 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
126
127 127 self.assertEqual(age(n), u'just now')
128 self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
129 self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
130 self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
131 self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
132 self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
133 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[prev_month]))),
134 u'1 month ago')
135 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[prev_month] + 2))),
136 u'1 month and 2 days ago')
137 self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
128 self.assertEqual(age(n + delt(seconds=-1)), u'1 second ago')
129 self.assertEqual(age(n + delt(seconds=-60 * 2)), u'2 minutes ago')
130 self.assertEqual(age(n + delt(hours=-1)), u'1 hour ago')
131 self.assertEqual(age(n + delt(hours=-24)), u'1 day ago')
132 self.assertEqual(age(n + delt(hours=-24 * 5)), u'5 days ago')
133 self.assertEqual(age(n + delt(months=-1)), u'1 month ago')
134 self.assertEqual(age(n + delt(months=-1, days=-2)), u'1 month and 2 days ago')
135 self.assertEqual(age(n + delt(years=-1, months=-1)), u'1 year and 1 month ago')
138 136
139 137 def test_age_in_future(self):
140 import calendar
141 138 from rhodecode.lib.utils2 import age
139 from dateutil import relativedelta
142 140 n = datetime.datetime.now()
143 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
141 delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs)
142
144 143 self.assertEqual(age(n), u'just now')
145 144 self.assertEqual(age(n + delt(seconds=1)), u'in 1 second')
146 145 self.assertEqual(age(n + delt(seconds=60 * 2)), u'in 2 minutes')
147 146 self.assertEqual(age(n + delt(hours=1)), u'in 1 hour')
148 147 self.assertEqual(age(n + delt(hours=24)), u'in 1 day')
149 148 self.assertEqual(age(n + delt(hours=24 * 5)), u'in 5 days')
150 self.assertEqual(age(n + delt(hours=24 * (calendar.mdays[n.month]))),
151 u'in 1 month')
152 self.assertEqual(age(n + delt(hours=24 * (calendar.mdays[n.month] + 1))),
153 u'in 1 month and 1 day')
154 self.assertEqual(age(n + delt(hours=24 * 400)), u'in 1 year and 1 month')
149 self.assertEqual(age(n + delt(months=1)), u'in 1 month')
150 self.assertEqual(age(n + delt(months=1, days=1)), u'in 1 month and 1 day')
151 self.assertEqual(age(n + delt(years=1, months=1)), u'in 1 year and 1 month')
155 152
156 153 def test_tag_exctrator(self):
157 154 sample = (
158 155 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
159 156 "[requires] [stale] [see<>=>] [see => http://url.com]"
160 157 "[requires => url] [lang => python] [just a tag]"
161 158 "[,d] [ => ULR ] [obsolete] [desc]]"
162 159 )
163 160 from rhodecode.lib.helpers import desc_stylize
164 161 res = desc_stylize(sample)
165 162 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
166 163 self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
167 164 self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
168 165 self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
169 166 self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
170 167 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
171 168
172 169 def test_alternative_gravatar(self):
173 170 from rhodecode.lib.helpers import gravatar_url
174 171 _md5 = lambda s: hashlib.md5(s).hexdigest()
175 172
176 173 def fake_conf(**kwargs):
177 174 from pylons import config
178 175 config['app_conf'] = {}
179 176 config['app_conf']['use_gravatar'] = True
180 177 config['app_conf'].update(kwargs)
181 178 return config
182 179
183 180 class fake_url():
184 181 @classmethod
185 182 def current(cls, *args, **kwargs):
186 183 return 'https://server.com'
187 184
188 185 with mock.patch('pylons.url', fake_url):
189 186 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
190 187 with mock.patch('pylons.config', fake):
191 188 from pylons import url
192 189 assert url.current() == 'https://server.com'
193 190 grav = gravatar_url(email_address='test@foo.com', size=24)
194 191 assert grav == 'http://test.com/test@foo.com'
195 192
196 193 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
197 194 with mock.patch('pylons.config', fake):
198 195 grav = gravatar_url(email_address='test@foo.com', size=24)
199 196 assert grav == 'http://test.com/test@foo.com'
200 197
201 198 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}')
202 199 with mock.patch('pylons.config', fake):
203 200 em = 'test@foo.com'
204 201 grav = gravatar_url(email_address=em, size=24)
205 202 assert grav == 'http://test.com/%s' % (_md5(em))
206 203
207 204 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}')
208 205 with mock.patch('pylons.config', fake):
209 206 em = 'test@foo.com'
210 207 grav = gravatar_url(email_address=em, size=24)
211 208 assert grav == 'http://test.com/%s/%s' % (_md5(em), 24)
212 209
213 210 fake = fake_conf(alternative_gravatar_url='{scheme}://{netloc}/{md5email}/{size}')
214 211 with mock.patch('pylons.config', fake):
215 212 em = 'test@foo.com'
216 213 grav = gravatar_url(email_address=em, size=24)
217 214 assert grav == 'https://server.com/%s/%s' % (_md5(em), 24)
General Comments 0
You need to be logged in to leave comments. Login now