summaryrefslogtreecommitdiff
path: root/tests/utils
diff options
context:
space:
mode:
authorFlorian Apolloner <florian@apolloner.eu>2013-02-26 09:53:47 +0100
committerFlorian Apolloner <florian@apolloner.eu>2013-02-26 14:36:57 +0100
commit89f40e36246100df6a11316c31a76712ebc6c501 (patch)
tree6e65639683ddaf2027908d1ecb1739e0e2ff853b /tests/utils
parentb3d2ccb5bfbaf6e7fe1f98843baaa48c35a70950 (diff)
Merged regressiontests and modeltests into the test root.
Diffstat (limited to 'tests/utils')
-rw-r--r--tests/utils/__init__.py0
-rw-r--r--tests/utils/archive.py72
-rw-r--r--tests/utils/archives/foobar.tarbin0 -> 10240 bytes
-rw-r--r--tests/utils/archives/foobar.tar.bz2bin0 -> 238 bytes
-rw-r--r--tests/utils/archives/foobar.tar.gzbin0 -> 249 bytes
-rw-r--r--tests/utils/archives/foobar.zipbin0 -> 1130 bytes
-rw-r--r--tests/utils/baseconv.py42
-rw-r--r--tests/utils/checksums.py29
-rw-r--r--tests/utils/crypto.py146
-rw-r--r--tests/utils/datastructures.py306
-rw-r--r--tests/utils/dateformat.py142
-rw-r--r--tests/utils/dateparse.py44
-rw-r--r--tests/utils/datetime_safe.py42
-rw-r--r--tests/utils/decorators.py108
-rw-r--r--tests/utils/eggs/test_egg.eggbin0 -> 4844 bytes
-rw-r--r--tests/utils/encoding.py17
-rw-r--r--tests/utils/feedgenerator.py117
-rw-r--r--tests/utils/functional.py66
-rw-r--r--tests/utils/html.py161
-rw-r--r--tests/utils/http.py163
-rw-r--r--tests/utils/ipv6.py53
-rw-r--r--tests/utils/jslex.py216
-rw-r--r--tests/utils/models.py1
-rw-r--r--tests/utils/module_loading.py184
-rw-r--r--tests/utils/numberformat.py47
-rw-r--r--tests/utils/os_utils.py26
-rw-r--r--tests/utils/regex_helper.py50
-rw-r--r--tests/utils/simplelazyobject.py123
-rw-r--r--tests/utils/termcolors.py157
-rw-r--r--tests/utils/test_module/__init__.py0
-rw-r--r--tests/utils/test_module/bad_module.py3
-rw-r--r--tests/utils/test_module/good_module.py1
-rw-r--r--tests/utils/test_no_submodule.py1
-rw-r--r--tests/utils/tests.py33
-rw-r--r--tests/utils/text.py108
-rw-r--r--tests/utils/timesince.py124
-rw-r--r--tests/utils/timezone.py63
-rw-r--r--tests/utils/tzinfo.py79
38 files changed, 2724 insertions, 0 deletions
diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/utils/__init__.py
diff --git a/tests/utils/archive.py b/tests/utils/archive.py
new file mode 100644
index 0000000000..8861b4a577
--- /dev/null
+++ b/tests/utils/archive.py
@@ -0,0 +1,72 @@
+import os
+import shutil
+import tempfile
+from django.utils import unittest
+
+from django.utils.archive import Archive, extract
+from django.utils._os import upath
+
+
+TEST_DIR = os.path.join(os.path.dirname(upath(__file__)), 'archives')
+
+
+class ArchiveTester(object):
+ archive = None
+
+ def setUp(self):
+ """
+ Create temporary directory for testing extraction.
+ """
+ self.old_cwd = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmpdir)
+ self.archive_path = os.path.join(TEST_DIR, self.archive)
+ # Always start off in TEST_DIR.
+ os.chdir(TEST_DIR)
+
+ def tearDown(self):
+ os.chdir(self.old_cwd)
+
+ def test_extract_method(self):
+ with Archive(self.archive) as archive:
+ archive.extract(self.tmpdir)
+ self.check_files(self.tmpdir)
+
+ def test_extract_method_no_to_path(self):
+ os.chdir(self.tmpdir)
+ with Archive(self.archive_path) as archive:
+ archive.extract()
+ self.check_files(self.tmpdir)
+
+ def test_extract_function(self):
+ extract(self.archive_path, self.tmpdir)
+ self.check_files(self.tmpdir)
+
+ def test_extract_function_no_to_path(self):
+ os.chdir(self.tmpdir)
+ extract(self.archive_path)
+ self.check_files(self.tmpdir)
+
+ def check_files(self, tmpdir):
+ self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, '1')))
+ self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, '2')))
+ self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'foo', '1')))
+ self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'foo', '2')))
+ self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'foo', 'bar', '1')))
+ self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'foo', 'bar', '2')))
+
+
+class TestZip(ArchiveTester, unittest.TestCase):
+ archive = 'foobar.zip'
+
+
+class TestTar(ArchiveTester, unittest.TestCase):
+ archive = 'foobar.tar'
+
+
+class TestGzipTar(ArchiveTester, unittest.TestCase):
+ archive = 'foobar.tar.gz'
+
+
+class TestBzip2Tar(ArchiveTester, unittest.TestCase):
+ archive = 'foobar.tar.bz2'
diff --git a/tests/utils/archives/foobar.tar b/tests/utils/archives/foobar.tar
new file mode 100644
index 0000000000..5418405070
--- /dev/null
+++ b/tests/utils/archives/foobar.tar
Binary files differ
diff --git a/tests/utils/archives/foobar.tar.bz2 b/tests/utils/archives/foobar.tar.bz2
new file mode 100644
index 0000000000..693529dcef
--- /dev/null
+++ b/tests/utils/archives/foobar.tar.bz2
Binary files differ
diff --git a/tests/utils/archives/foobar.tar.gz b/tests/utils/archives/foobar.tar.gz
new file mode 100644
index 0000000000..5b596bd58e
--- /dev/null
+++ b/tests/utils/archives/foobar.tar.gz
Binary files differ
diff --git a/tests/utils/archives/foobar.zip b/tests/utils/archives/foobar.zip
new file mode 100644
index 0000000000..dc3e2fc067
--- /dev/null
+++ b/tests/utils/archives/foobar.zip
Binary files differ
diff --git a/tests/utils/baseconv.py b/tests/utils/baseconv.py
new file mode 100644
index 0000000000..cc413b4e8e
--- /dev/null
+++ b/tests/utils/baseconv.py
@@ -0,0 +1,42 @@
+from unittest import TestCase
+from django.utils.baseconv import base2, base16, base36, base56, base62, base64, BaseConverter
+from django.utils.six.moves import xrange
+
+class TestBaseConv(TestCase):
+
+ def test_baseconv(self):
+ nums = [-10 ** 10, 10 ** 10] + list(xrange(-100, 100))
+ for converter in [base2, base16, base36, base56, base62, base64]:
+ for i in nums:
+ self.assertEqual(i, converter.decode(converter.encode(i)))
+
+ def test_base11(self):
+ base11 = BaseConverter('0123456789-', sign='$')
+ self.assertEqual(base11.encode(1234), '-22')
+ self.assertEqual(base11.decode('-22'), 1234)
+ self.assertEqual(base11.encode(-1234), '$-22')
+ self.assertEqual(base11.decode('$-22'), -1234)
+
+ def test_base20(self):
+ base20 = BaseConverter('0123456789abcdefghij')
+ self.assertEqual(base20.encode(1234), '31e')
+ self.assertEqual(base20.decode('31e'), 1234)
+ self.assertEqual(base20.encode(-1234), '-31e')
+ self.assertEqual(base20.decode('-31e'), -1234)
+
+ def test_base64(self):
+ self.assertEqual(base64.encode(1234), 'JI')
+ self.assertEqual(base64.decode('JI'), 1234)
+ self.assertEqual(base64.encode(-1234), '$JI')
+ self.assertEqual(base64.decode('$JI'), -1234)
+
+ def test_base7(self):
+ base7 = BaseConverter('cjdhel3', sign='g')
+ self.assertEqual(base7.encode(1234), 'hejd')
+ self.assertEqual(base7.decode('hejd'), 1234)
+ self.assertEqual(base7.encode(-1234), 'ghejd')
+ self.assertEqual(base7.decode('ghejd'), -1234)
+
+ def test_exception(self):
+ self.assertRaises(ValueError, BaseConverter, 'abc', sign='a')
+ self.assertTrue(isinstance(BaseConverter('abc', sign='d'), BaseConverter))
diff --git a/tests/utils/checksums.py b/tests/utils/checksums.py
new file mode 100644
index 0000000000..cee6dca2a8
--- /dev/null
+++ b/tests/utils/checksums.py
@@ -0,0 +1,29 @@
+import unittest
+
+from django.utils import checksums
+
+class TestUtilsChecksums(unittest.TestCase):
+
+ def check_output(self, function, value, output=None):
+ """
+ Check that function(value) equals output. If output is None,
+ check that function(value) equals value.
+ """
+ if output is None:
+ output = value
+ self.assertEqual(function(value), output)
+
+ def test_luhn(self):
+ f = checksums.luhn
+ items = (
+ (4111111111111111, True), ('4111111111111111', True),
+ (4222222222222, True), (378734493671000, True),
+ (5424000000000015, True), (5555555555554444, True),
+ (1008, True), ('0000001008', True), ('000000001008', True),
+ (4012888888881881, True), (1234567890123456789012345678909, True),
+ (4111111111211111, False), (42222222222224, False),
+ (100, False), ('100', False), ('0000100', False),
+ ('abc', False), (None, False), (object(), False),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
diff --git a/tests/utils/crypto.py b/tests/utils/crypto.py
new file mode 100644
index 0000000000..5cf2934ab0
--- /dev/null
+++ b/tests/utils/crypto.py
@@ -0,0 +1,146 @@
+from __future__ import unicode_literals
+
+import binascii
+import math
+import timeit
+import hashlib
+
+from django.utils import unittest
+from django.utils.crypto import constant_time_compare, pbkdf2
+
+
+class TestUtilsCryptoMisc(unittest.TestCase):
+
+ def test_constant_time_compare(self):
+ # It's hard to test for constant time, just test the result.
+ self.assertTrue(constant_time_compare(b'spam', b'spam'))
+ self.assertFalse(constant_time_compare(b'spam', b'eggs'))
+ self.assertTrue(constant_time_compare('spam', 'spam'))
+ self.assertFalse(constant_time_compare('spam', 'eggs'))
+
+
+class TestUtilsCryptoPBKDF2(unittest.TestCase):
+
+ # http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06
+ rfc_vectors = [
+ {
+ "args": {
+ "password": "password",
+ "salt": "salt",
+ "iterations": 1,
+ "dklen": 20,
+ "digest": hashlib.sha1,
+ },
+ "result": "0c60c80f961f0e71f3a9b524af6012062fe037a6",
+ },
+ {
+ "args": {
+ "password": "password",
+ "salt": "salt",
+ "iterations": 2,
+ "dklen": 20,
+ "digest": hashlib.sha1,
+ },
+ "result": "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957",
+ },
+ {
+ "args": {
+ "password": "password",
+ "salt": "salt",
+ "iterations": 4096,
+ "dklen": 20,
+ "digest": hashlib.sha1,
+ },
+ "result": "4b007901b765489abead49d926f721d065a429c1",
+ },
+ # # this takes way too long :(
+ # {
+ # "args": {
+ # "password": "password",
+ # "salt": "salt",
+ # "iterations": 16777216,
+ # "dklen": 20,
+ # "digest": hashlib.sha1,
+ # },
+ # "result": "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984",
+ # },
+ {
+ "args": {
+ "password": "passwordPASSWORDpassword",
+ "salt": "saltSALTsaltSALTsaltSALTsaltSALTsalt",
+ "iterations": 4096,
+ "dklen": 25,
+ "digest": hashlib.sha1,
+ },
+ "result": "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038",
+ },
+ {
+ "args": {
+ "password": "pass\0word",
+ "salt": "sa\0lt",
+ "iterations": 4096,
+ "dklen": 16,
+ "digest": hashlib.sha1,
+ },
+ "result": "56fa6aa75548099dcc37d7f03425e0c3",
+ },
+ ]
+
+ regression_vectors = [
+ {
+ "args": {
+ "password": "password",
+ "salt": "salt",
+ "iterations": 1,
+ "dklen": 20,
+ "digest": hashlib.sha256,
+ },
+ "result": "120fb6cffcf8b32c43e7225256c4f837a86548c9",
+ },
+ {
+ "args": {
+ "password": "password",
+ "salt": "salt",
+ "iterations": 1,
+ "dklen": 20,
+ "digest": hashlib.sha512,
+ },
+ "result": "867f70cf1ade02cff3752599a3a53dc4af34c7a6",
+ },
+ {
+ "args": {
+ "password": "password",
+ "salt": "salt",
+ "iterations": 1000,
+ "dklen": 0,
+ "digest": hashlib.sha512,
+ },
+ "result": ("afe6c5530785b6cc6b1c6453384731bd5ee432ee"
+ "549fd42fb6695779ad8a1c5bf59de69c48f774ef"
+ "c4007d5298f9033c0241d5ab69305e7b64eceeb8d"
+ "834cfec"),
+ },
+ # Check leading zeros are not stripped (#17481)
+ {
+ "args": {
+ "password": b'\xba',
+ "salt": "salt",
+ "iterations": 1,
+ "dklen": 20,
+ "digest": hashlib.sha1,
+ },
+ "result": '0053d3b91a7f1e54effebd6d68771e8a6e0b2c5b',
+ },
+ ]
+
+ def test_public_vectors(self):
+ for vector in self.rfc_vectors:
+ result = pbkdf2(**vector['args'])
+ self.assertEqual(binascii.hexlify(result).decode('ascii'),
+ vector['result'])
+
+ def test_regression_vectors(self):
+ for vector in self.regression_vectors:
+ result = pbkdf2(**vector['args'])
+ self.assertEqual(binascii.hexlify(result).decode('ascii'),
+ vector['result'])
diff --git a/tests/utils/datastructures.py b/tests/utils/datastructures.py
new file mode 100644
index 0000000000..3161c04f97
--- /dev/null
+++ b/tests/utils/datastructures.py
@@ -0,0 +1,306 @@
+"""
+Tests for stuff in django.utils.datastructures.
+"""
+
+import copy
+import pickle
+import warnings
+
+from django.test import SimpleTestCase
+from django.utils.datastructures import (DictWrapper, ImmutableList,
+ MultiValueDict, MultiValueDictKeyError, MergeDict, SortedDict)
+from django.utils import six
+
+
+class SortedDictTests(SimpleTestCase):
+ def setUp(self):
+ self.d1 = SortedDict()
+ self.d1[7] = 'seven'
+ self.d1[1] = 'one'
+ self.d1[9] = 'nine'
+
+ self.d2 = SortedDict()
+ self.d2[1] = 'one'
+ self.d2[9] = 'nine'
+ self.d2[0] = 'nil'
+ self.d2[7] = 'seven'
+
+ def test_basic_methods(self):
+ self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9])
+ self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'one', 'nine'])
+ self.assertEqual(list(six.iteritems(self.d1)), [(7, 'seven'), (1, 'one'), (9, 'nine')])
+
+ def test_overwrite_ordering(self):
+ """ Overwriting an item keeps its place. """
+ self.d1[1] = 'ONE'
+ self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'ONE', 'nine'])
+
+ def test_append_items(self):
+ """ New items go to the end. """
+ self.d1[0] = 'nil'
+ self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9, 0])
+
+ def test_delete_and_insert(self):
+ """
+ Deleting an item, then inserting the same key again will place it
+ at the end.
+ """
+ del self.d2[7]
+ self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0])
+ self.d2[7] = 'lucky number 7'
+ self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0, 7])
+
+ if not six.PY3:
+ def test_change_keys(self):
+ """
+ Changing the keys won't do anything, it's only a copy of the
+ keys dict.
+
+ This test doesn't make sense under Python 3 because keys is
+ an iterator.
+ """
+ k = self.d2.keys()
+ k.remove(9)
+ self.assertEqual(self.d2.keys(), [1, 9, 0, 7])
+
+ def test_init_keys(self):
+ """
+ Initialising a SortedDict with two keys will just take the first one.
+
+ A real dict will actually take the second value so we will too, but
+ we'll keep the ordering from the first key found.
+ """
+ tuples = ((2, 'two'), (1, 'one'), (2, 'second-two'))
+ d = SortedDict(tuples)
+
+ self.assertEqual(list(six.iterkeys(d)), [2, 1])
+
+ real_dict = dict(tuples)
+ self.assertEqual(sorted(six.itervalues(real_dict)), ['one', 'second-two'])
+
+ # Here the order of SortedDict values *is* what we are testing
+ self.assertEqual(list(six.itervalues(d)), ['second-two', 'one'])
+
+ def test_overwrite(self):
+ self.d1[1] = 'not one'
+ self.assertEqual(self.d1[1], 'not one')
+ self.assertEqual(list(six.iterkeys(self.d1)), list(six.iterkeys(self.d1.copy())))
+
+ def test_append(self):
+ self.d1[13] = 'thirteen'
+ self.assertEqual(
+ repr(self.d1),
+ "{7: 'seven', 1: 'one', 9: 'nine', 13: 'thirteen'}"
+ )
+
+ def test_pop(self):
+ self.assertEqual(self.d1.pop(1, 'missing'), 'one')
+ self.assertEqual(self.d1.pop(1, 'missing'), 'missing')
+
+ # We don't know which item will be popped in popitem(), so we'll
+ # just check that the number of keys has decreased.
+ l = len(self.d1)
+ self.d1.popitem()
+ self.assertEqual(l - len(self.d1), 1)
+
+ def test_dict_equality(self):
+ d = SortedDict((i, i) for i in range(3))
+ self.assertEqual(d, {0: 0, 1: 1, 2: 2})
+
+ def test_tuple_init(self):
+ d = SortedDict(((1, "one"), (0, "zero"), (2, "two")))
+ self.assertEqual(repr(d), "{1: 'one', 0: 'zero', 2: 'two'}")
+
+ def test_pickle(self):
+ self.assertEqual(
+ pickle.loads(pickle.dumps(self.d1, 2)),
+ {7: 'seven', 1: 'one', 9: 'nine'}
+ )
+
+ def test_copy(self):
+ orig = SortedDict(((1, "one"), (0, "zero"), (2, "two")))
+ copied = copy.copy(orig)
+ self.assertEqual(list(six.iterkeys(orig)), [1, 0, 2])
+ self.assertEqual(list(six.iterkeys(copied)), [1, 0, 2])
+
+ def test_clear(self):
+ self.d1.clear()
+ self.assertEqual(self.d1, {})
+ self.assertEqual(self.d1.keyOrder, [])
+
+ def test_reversed(self):
+ self.assertEqual(list(self.d1), [7, 1, 9])
+ self.assertEqual(list(self.d2), [1, 9, 0, 7])
+ self.assertEqual(list(reversed(self.d1)), [9, 1, 7])
+ self.assertEqual(list(reversed(self.d2)), [7, 0, 9, 1])
+
+ def test_insert(self):
+ d = SortedDict()
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+ d.insert(0, "hello", "world")
+ assert w[0].category is DeprecationWarning
+
+ def test_value_for_index(self):
+ d = SortedDict({"a": 3})
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+ self.assertEqual(d.value_for_index(0), 3)
+ assert w[0].category is DeprecationWarning
+
+
+class MergeDictTests(SimpleTestCase):
+
+ def test_simple_mergedict(self):
+ d1 = {'chris':'cool', 'camri':'cute', 'cotton':'adorable',
+ 'tulip':'snuggable', 'twoofme':'firstone'}
+
+ d2 = {'chris2':'cool2', 'camri2':'cute2', 'cotton2':'adorable2',
+ 'tulip2':'snuggable2'}
+
+ d3 = {'chris3':'cool3', 'camri3':'cute3', 'cotton3':'adorable3',
+ 'tulip3':'snuggable3'}
+
+ d4 = {'twoofme': 'secondone'}
+
+ md = MergeDict(d1, d2, d3)
+
+ self.assertEqual(md['chris'], 'cool')
+ self.assertEqual(md['camri'], 'cute')
+ self.assertEqual(md['twoofme'], 'firstone')
+
+ md2 = md.copy()
+ self.assertEqual(md2['chris'], 'cool')
+
+ def test_mergedict_merges_multivaluedict(self):
+ """ MergeDict can merge MultiValueDicts """
+
+ multi1 = MultiValueDict({'key1': ['value1'],
+ 'key2': ['value2', 'value3']})
+
+ multi2 = MultiValueDict({'key2': ['value4'],
+ 'key4': ['value5', 'value6']})
+
+ mm = MergeDict(multi1, multi2)
+
+ # Although 'key2' appears in both dictionaries,
+ # only the first value is used.
+ self.assertEqual(mm.getlist('key2'), ['value2', 'value3'])
+ self.assertEqual(mm.getlist('key4'), ['value5', 'value6'])
+ self.assertEqual(mm.getlist('undefined'), [])
+
+ self.assertEqual(sorted(six.iterkeys(mm)), ['key1', 'key2', 'key4'])
+ self.assertEqual(len(list(six.itervalues(mm))), 3)
+
+ self.assertTrue('value1' in six.itervalues(mm))
+
+ self.assertEqual(sorted(six.iteritems(mm), key=lambda k: k[0]),
+ [('key1', 'value1'), ('key2', 'value3'),
+ ('key4', 'value6')])
+
+ self.assertEqual([(k,mm.getlist(k)) for k in sorted(mm)],
+ [('key1', ['value1']),
+ ('key2', ['value2', 'value3']),
+ ('key4', ['value5', 'value6'])])
+
+class MultiValueDictTests(SimpleTestCase):
+
+ def test_multivaluedict(self):
+ d = MultiValueDict({'name': ['Adrian', 'Simon'],
+ 'position': ['Developer']})
+
+ self.assertEqual(d['name'], 'Simon')
+ self.assertEqual(d.get('name'), 'Simon')
+ self.assertEqual(d.getlist('name'), ['Adrian', 'Simon'])
+ self.assertEqual(sorted(list(six.iteritems(d))),
+ [('name', 'Simon'), ('position', 'Developer')])
+
+ self.assertEqual(sorted(list(six.iterlists(d))),
+ [('name', ['Adrian', 'Simon']),
+ ('position', ['Developer'])])
+
+ # MultiValueDictKeyError: "Key 'lastname' not found in
+ # <MultiValueDict: {'position': ['Developer'],
+ # 'name': ['Adrian', 'Simon']}>"
+ six.assertRaisesRegex(self, MultiValueDictKeyError,
+ r'"Key \'lastname\' not found in <MultiValueDict',
+ d.__getitem__, 'lastname')
+
+ self.assertEqual(d.get('lastname'), None)
+ self.assertEqual(d.get('lastname', 'nonexistent'), 'nonexistent')
+ self.assertEqual(d.getlist('lastname'), [])
+ self.assertEqual(d.getlist('doesnotexist', ['Adrian', 'Simon']),
+ ['Adrian', 'Simon'])
+
+ d.setlist('lastname', ['Holovaty', 'Willison'])
+ self.assertEqual(d.getlist('lastname'), ['Holovaty', 'Willison'])
+ self.assertEqual(sorted(list(six.itervalues(d))),
+ ['Developer', 'Simon', 'Willison'])
+
+ def test_appendlist(self):
+ d = MultiValueDict()
+ d.appendlist('name', 'Adrian')
+ d.appendlist('name', 'Simon')
+ self.assertEqual(d.getlist('name'), ['Adrian', 'Simon'])
+
+ def test_copy(self):
+ for copy_func in [copy.copy, lambda d: d.copy()]:
+ d1 = MultiValueDict({
+ "developers": ["Carl", "Fred"]
+ })
+ self.assertEqual(d1["developers"], "Fred")
+ d2 = copy_func(d1)
+ d2.update({"developers": "Groucho"})
+ self.assertEqual(d2["developers"], "Groucho")
+ self.assertEqual(d1["developers"], "Fred")
+
+ d1 = MultiValueDict({
+ "key": [[]]
+ })
+ self.assertEqual(d1["key"], [])
+ d2 = copy_func(d1)
+ d2["key"].append("Penguin")
+ self.assertEqual(d1["key"], ["Penguin"])
+ self.assertEqual(d2["key"], ["Penguin"])
+
+ def test_dict_translation(self):
+ mvd = MultiValueDict({
+ 'devs': ['Bob', 'Joe'],
+ 'pm': ['Rory'],
+ })
+ d = mvd.dict()
+ self.assertEqual(list(six.iterkeys(d)), list(six.iterkeys(mvd)))
+ for key in six.iterkeys(mvd):
+ self.assertEqual(d[key], mvd[key])
+
+ self.assertEqual({}, MultiValueDict().dict())
+
+
+class ImmutableListTests(SimpleTestCase):
+
+ def test_sort(self):
+ d = ImmutableList(range(10))
+
+ # AttributeError: ImmutableList object is immutable.
+ self.assertRaisesMessage(AttributeError,
+ 'ImmutableList object is immutable.', d.sort)
+
+ self.assertEqual(repr(d), '(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)')
+
+ def test_custom_warning(self):
+ d = ImmutableList(range(10), warning="Object is immutable!")
+
+ self.assertEqual(d[1], 1)
+
+ # AttributeError: Object is immutable!
+ self.assertRaisesMessage(AttributeError,
+ 'Object is immutable!', d.__setitem__, 1, 'test')
+
+
+class DictWrapperTests(SimpleTestCase):
+
+ def test_dictwrapper(self):
+ f = lambda x: "*%s" % x
+ d = DictWrapper({'a': 'a'}, f, 'xx_')
+ self.assertEqual("Normal: %(a)s. Modified: %(xx_a)s" % d,
+ 'Normal: a. Modified: *a')
diff --git a/tests/utils/dateformat.py b/tests/utils/dateformat.py
new file mode 100644
index 0000000000..0f4fd67f6f
--- /dev/null
+++ b/tests/utils/dateformat.py
@@ -0,0 +1,142 @@
+from __future__ import unicode_literals
+
+from datetime import datetime, date
+import os
+import time
+
+from django.utils.dateformat import format
+from django.utils import dateformat, translation, unittest
+from django.utils.timezone import utc
+from django.utils.tzinfo import FixedOffset, LocalTimezone
+
+
+class DateFormatTests(unittest.TestCase):
+ def setUp(self):
+ self.old_TZ = os.environ.get('TZ')
+ os.environ['TZ'] = 'Europe/Copenhagen'
+ translation.activate('en-us')
+
+ try:
+ # Check if a timezone has been set
+ time.tzset()
+ self.tz_tests = True
+ except AttributeError:
+ # No timezone available. Don't run the tests that require a TZ
+ self.tz_tests = False
+
+ def tearDown(self):
+ if self.old_TZ is None:
+ del os.environ['TZ']
+ else:
+ os.environ['TZ'] = self.old_TZ
+
+ # Cleanup - force re-evaluation of TZ environment variable.
+ if self.tz_tests:
+ time.tzset()
+
+ def test_date(self):
+ d = date(2009, 5, 16)
+ self.assertEqual(date.fromtimestamp(int(format(d, 'U'))), d)
+
+ def test_naive_datetime(self):
+ dt = datetime(2009, 5, 16, 5, 30, 30)
+ self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U'))), dt)
+
+ def test_datetime_with_local_tzinfo(self):
+ ltz = LocalTimezone(datetime.now())
+ dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=ltz)
+ self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), ltz), dt)
+ self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U'))), dt.replace(tzinfo=None))
+
+ def test_datetime_with_tzinfo(self):
+ tz = FixedOffset(-510)
+ ltz = LocalTimezone(datetime.now())
+ dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=tz)
+ self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), tz), dt)
+ self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), ltz), dt)
+ self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U'))), dt.astimezone(ltz).replace(tzinfo=None))
+ self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), tz).utctimetuple(), dt.utctimetuple())
+ self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), ltz).utctimetuple(), dt.utctimetuple())
+
+ def test_epoch(self):
+ udt = datetime(1970, 1, 1, tzinfo=utc)
+ self.assertEqual(format(udt, 'U'), '0')
+
+ def test_empty_format(self):
+ my_birthday = datetime(1979, 7, 8, 22, 00)
+
+ self.assertEqual(dateformat.format(my_birthday, ''), '')
+
+ def test_am_pm(self):
+ my_birthday = datetime(1979, 7, 8, 22, 00)
+
+ self.assertEqual(dateformat.format(my_birthday, 'a'), 'p.m.')
+
+ def test_microsecond(self):
+ # Regression test for #18951
+ dt = datetime(2009, 5, 16, microsecond=123)
+ self.assertEqual(dateformat.format(dt, 'u'), '000123')
+
+ def test_date_formats(self):
+ my_birthday = datetime(1979, 7, 8, 22, 00)
+ timestamp = datetime(2008, 5, 19, 11, 45, 23, 123456)
+
+ self.assertEqual(dateformat.format(my_birthday, 'A'), 'PM')
+ self.assertEqual(dateformat.format(timestamp, 'c'), '2008-05-19T11:45:23.123456')
+ self.assertEqual(dateformat.format(my_birthday, 'd'), '08')
+ self.assertEqual(dateformat.format(my_birthday, 'j'), '8')
+ self.assertEqual(dateformat.format(my_birthday, 'l'), 'Sunday')
+ self.assertEqual(dateformat.format(my_birthday, 'L'), 'False')
+ self.assertEqual(dateformat.format(my_birthday, 'm'), '07')
+ self.assertEqual(dateformat.format(my_birthday, 'M'), 'Jul')
+ self.assertEqual(dateformat.format(my_birthday, 'b'), 'jul')
+ self.assertEqual(dateformat.format(my_birthday, 'n'), '7')
+ self.assertEqual(dateformat.format(my_birthday, 'N'), 'July')
+
+ def test_time_formats(self):
+ my_birthday = datetime(1979, 7, 8, 22, 00)
+
+ self.assertEqual(dateformat.format(my_birthday, 'P'), '10 p.m.')
+ self.assertEqual(dateformat.format(my_birthday, 's'), '00')
+ self.assertEqual(dateformat.format(my_birthday, 'S'), 'th')
+ self.assertEqual(dateformat.format(my_birthday, 't'), '31')
+ self.assertEqual(dateformat.format(my_birthday, 'w'), '0')
+ self.assertEqual(dateformat.format(my_birthday, 'W'), '27')
+ self.assertEqual(dateformat.format(my_birthday, 'y'), '79')
+ self.assertEqual(dateformat.format(my_birthday, 'Y'), '1979')
+ self.assertEqual(dateformat.format(my_birthday, 'z'), '189')
+
+ def test_dateformat(self):
+ my_birthday = datetime(1979, 7, 8, 22, 00)
+
+ self.assertEqual(dateformat.format(my_birthday, r'Y z \C\E\T'), '1979 189 CET')
+
+ self.assertEqual(dateformat.format(my_birthday, r'jS \o\f F'), '8th of July')
+
+ def test_futuredates(self):
+ the_future = datetime(2100, 10, 25, 0, 00)
+ self.assertEqual(dateformat.format(the_future, r'Y'), '2100')
+
+ def test_timezones(self):
+ my_birthday = datetime(1979, 7, 8, 22, 00)
+ summertime = datetime(2005, 10, 30, 1, 00)
+ wintertime = datetime(2005, 10, 30, 4, 00)
+ timestamp = datetime(2008, 5, 19, 11, 45, 23, 123456)
+
+ if self.tz_tests:
+ self.assertEqual(dateformat.format(my_birthday, 'O'), '+0100')
+ self.assertEqual(dateformat.format(my_birthday, 'r'), 'Sun, 8 Jul 1979 22:00:00 +0100')
+ self.assertEqual(dateformat.format(my_birthday, 'T'), 'CET')
+ self.assertEqual(dateformat.format(my_birthday, 'U'), '300315600')
+ self.assertEqual(dateformat.format(timestamp, 'u'), '123456')
+ self.assertEqual(dateformat.format(my_birthday, 'Z'), '3600')
+ self.assertEqual(dateformat.format(summertime, 'I'), '1')
+ self.assertEqual(dateformat.format(summertime, 'O'), '+0200')
+ self.assertEqual(dateformat.format(wintertime, 'I'), '0')
+ self.assertEqual(dateformat.format(wintertime, 'O'), '+0100')
+
+ # Ticket #16924 -- We don't need timezone support to test this
+ # 3h30m to the west of UTC
+ tz = FixedOffset(-3*60 - 30)
+ dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=tz)
+ self.assertEqual(dateformat.format(dt, 'O'), '-0330')
diff --git a/tests/utils/dateparse.py b/tests/utils/dateparse.py
new file mode 100644
index 0000000000..1a6ca646b8
--- /dev/null
+++ b/tests/utils/dateparse.py
@@ -0,0 +1,44 @@
+from __future__ import unicode_literals
+
+from datetime import date, time, datetime
+
+from django.utils.dateparse import parse_date, parse_time, parse_datetime
+from django.utils import unittest
+from django.utils.tzinfo import FixedOffset
+
+
+class DateParseTests(unittest.TestCase):
+
+ def test_parse_date(self):
+ # Valid inputs
+ self.assertEqual(parse_date('2012-04-23'), date(2012, 4, 23))
+ self.assertEqual(parse_date('2012-4-9'), date(2012, 4, 9))
+ # Invalid inputs
+ self.assertEqual(parse_date('20120423'), None)
+ self.assertRaises(ValueError, parse_date, '2012-04-56')
+
+ def test_parse_time(self):
+ # Valid inputs
+ self.assertEqual(parse_time('09:15:00'), time(9, 15))
+ self.assertEqual(parse_time('10:10'), time(10, 10))
+ self.assertEqual(parse_time('10:20:30.400'), time(10, 20, 30, 400000))
+ self.assertEqual(parse_time('4:8:16'), time(4, 8, 16))
+ # Invalid inputs
+ self.assertEqual(parse_time('091500'), None)
+ self.assertRaises(ValueError, parse_time, '09:15:90')
+
+ def test_parse_datetime(self):
+ # Valid inputs
+ self.assertEqual(parse_datetime('2012-04-23T09:15:00'),
+ datetime(2012, 4, 23, 9, 15))
+ self.assertEqual(parse_datetime('2012-4-9 4:8:16'),
+ datetime(2012, 4, 9, 4, 8, 16))
+ self.assertEqual(parse_datetime('2012-04-23T09:15:00Z'),
+ datetime(2012, 4, 23, 9, 15, 0, 0, FixedOffset(0)))
+ self.assertEqual(parse_datetime('2012-4-9 4:8:16-0320'),
+ datetime(2012, 4, 9, 4, 8, 16, 0, FixedOffset(-200)))
+ self.assertEqual(parse_datetime('2012-04-23T10:20:30.400+02:30'),
+ datetime(2012, 4, 23, 10, 20, 30, 400000, FixedOffset(150)))
+ # Invalid inputs
+ self.assertEqual(parse_datetime('20120423091500'), None)
+ self.assertRaises(ValueError, parse_datetime, '2012-04-56T09:15:90')
diff --git a/tests/utils/datetime_safe.py b/tests/utils/datetime_safe.py
new file mode 100644
index 0000000000..c9c34dbf40
--- /dev/null
+++ b/tests/utils/datetime_safe.py
@@ -0,0 +1,42 @@
+import unittest
+
+from datetime import date as original_date, datetime as original_datetime
+from django.utils.datetime_safe import date, datetime
+
+class DatetimeTests(unittest.TestCase):
+
+ def setUp(self):
+ self.just_safe = (1900, 1, 1)
+ self.just_unsafe = (1899, 12, 31, 23, 59, 59)
+ self.really_old = (20, 1, 1)
+ self.more_recent = (2006, 1, 1)
+
+ def test_compare_datetimes(self):
+ self.assertEqual(original_datetime(*self.more_recent), datetime(*self.more_recent))
+ self.assertEqual(original_datetime(*self.really_old), datetime(*self.really_old))
+ self.assertEqual(original_date(*self.more_recent), date(*self.more_recent))
+ self.assertEqual(original_date(*self.really_old), date(*self.really_old))
+
+ self.assertEqual(original_date(*self.just_safe).strftime('%Y-%m-%d'), date(*self.just_safe).strftime('%Y-%m-%d'))
+ self.assertEqual(original_datetime(*self.just_safe).strftime('%Y-%m-%d'), datetime(*self.just_safe).strftime('%Y-%m-%d'))
+
+ def test_safe_strftime(self):
+ self.assertEqual(date(*self.just_unsafe[:3]).strftime('%Y-%m-%d (weekday %w)'), '1899-12-31 (weekday 0)')
+ self.assertEqual(date(*self.just_safe).strftime('%Y-%m-%d (weekday %w)'), '1900-01-01 (weekday 1)')
+
+ self.assertEqual(datetime(*self.just_unsafe).strftime('%Y-%m-%d %H:%M:%S (weekday %w)'), '1899-12-31 23:59:59 (weekday 0)')
+ self.assertEqual(datetime(*self.just_safe).strftime('%Y-%m-%d %H:%M:%S (weekday %w)'), '1900-01-01 00:00:00 (weekday 1)')
+
+ # %y will error before this date
+ self.assertEqual(date(*self.just_safe).strftime('%y'), '00')
+ self.assertEqual(datetime(*self.just_safe).strftime('%y'), '00')
+
+ self.assertEqual(date(1850, 8, 2).strftime("%Y/%m/%d was a %A"), '1850/08/02 was a Friday')
+
+ def test_zero_padding(self):
+ """
+ Regression for #12524
+
+ Check that pre-1000AD dates are padded with zeros if necessary
+ """
+ self.assertEqual(date(1, 1, 1).strftime("%Y/%m/%d was a %A"), '0001/01/01 was a Monday')
diff --git a/tests/utils/decorators.py b/tests/utils/decorators.py
new file mode 100644
index 0000000000..4d503df026
--- /dev/null
+++ b/tests/utils/decorators.py
@@ -0,0 +1,108 @@
+from django.http import HttpResponse
+from django.template import Template, Context
+from django.template.response import TemplateResponse
+from django.test import TestCase, RequestFactory
+from django.utils.decorators import decorator_from_middleware
+
+
+class ProcessViewMiddleware(object):
+ def process_view(self, request, view_func, view_args, view_kwargs):
+ pass
+
+process_view_dec = decorator_from_middleware(ProcessViewMiddleware)
+
+@process_view_dec
+def process_view(request):
+ return HttpResponse()
+
+
+class ClassProcessView(object):
+ def __call__(self, request):
+ return HttpResponse()
+
+class_process_view = process_view_dec(ClassProcessView())
+
+
+class FullMiddleware(object):
+ def process_request(self, request):
+ request.process_request_reached = True
+
+ def process_view(sef, request, view_func, view_args, view_kwargs):
+ request.process_view_reached = True
+
+ def process_template_response(self, request, response):
+ request.process_template_response_reached = True
+ return response
+
+ def process_response(self, request, response):
+ # This should never receive unrendered content.
+ request.process_response_content = response.content
+ request.process_response_reached = True
+ return response
+
+full_dec = decorator_from_middleware(FullMiddleware)
+
+
+class DecoratorFromMiddlewareTests(TestCase):
+ """
+ Tests for view decorators created using
+ ``django.utils.decorators.decorator_from_middleware``.
+ """
+ rf = RequestFactory()
+
+ def test_process_view_middleware(self):
+ """
+ Test a middleware that implements process_view.
+ """
+ process_view(self.rf.get('/'))
+
+ def test_callable_process_view_middleware(self):
+ """
+ Test a middleware that implements process_view, operating on a callable class.
+ """
+ class_process_view(self.rf.get('/'))
+
+ def test_full_dec_normal(self):
+ """
+ Test that all methods of middleware are called for normal HttpResponses
+ """
+
+ @full_dec
+ def normal_view(request):
+ t = Template("Hello world")
+ return HttpResponse(t.render(Context({})))
+
+ request = self.rf.get('/')
+ response = normal_view(request)
+ self.assertTrue(getattr(request, 'process_request_reached', False))
+ self.assertTrue(getattr(request, 'process_view_reached', False))
+ # process_template_response must not be called for HttpResponse
+ self.assertFalse(getattr(request, 'process_template_response_reached', False))
+ self.assertTrue(getattr(request, 'process_response_reached', False))
+
+ def test_full_dec_templateresponse(self):
+ """
+ Test that all methods of middleware are called for TemplateResponses in
+ the right sequence.
+ """
+
+ @full_dec
+ def template_response_view(request):
+ t = Template("Hello world")
+ return TemplateResponse(request, t, {})
+
+ request = self.rf.get('/')
+ response = template_response_view(request)
+ self.assertTrue(getattr(request, 'process_request_reached', False))
+ self.assertTrue(getattr(request, 'process_view_reached', False))
+ self.assertTrue(getattr(request, 'process_template_response_reached', False))
+ # response must not be rendered yet.
+ self.assertFalse(response._is_rendered)
+ # process_response must not be called until after response is rendered,
+ # otherwise some decorators like csrf_protect and gzip_page will not
+ # work correctly. See #16004
+ self.assertFalse(getattr(request, 'process_response_reached', False))
+ response.render()
+ self.assertTrue(getattr(request, 'process_response_reached', False))
+ # Check that process_response saw the rendered content
+ self.assertEqual(request.process_response_content, b"Hello world")
diff --git a/tests/utils/eggs/test_egg.egg b/tests/utils/eggs/test_egg.egg
new file mode 100644
index 0000000000..9b08cc10ef
--- /dev/null
+++ b/tests/utils/eggs/test_egg.egg
Binary files differ
diff --git a/tests/utils/encoding.py b/tests/utils/encoding.py
new file mode 100644
index 0000000000..d191845518
--- /dev/null
+++ b/tests/utils/encoding.py
@@ -0,0 +1,17 @@
+# -*- encoding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.utils import unittest
+from django.utils.encoding import force_bytes
+
+
+class TestEncodingUtils(unittest.TestCase):
+ def test_force_bytes_exception(self):
+ """
+ Test that force_bytes knows how to convert to bytes an exception
+ containing non-ASCII characters in its args.
+ """
+ error_msg = "This is an exception, voilà"
+ exc = ValueError(error_msg)
+ result = force_bytes(exc)
+ self.assertEqual(result, error_msg.encode('utf-8'))
diff --git a/tests/utils/feedgenerator.py b/tests/utils/feedgenerator.py
new file mode 100644
index 0000000000..bcd53bb2a0
--- /dev/null
+++ b/tests/utils/feedgenerator.py
@@ -0,0 +1,117 @@
+from __future__ import unicode_literals
+
+import datetime
+
+from django.utils import feedgenerator, tzinfo, unittest
+
+class FeedgeneratorTest(unittest.TestCase):
+ """
+ Tests for the low-level syndication feed framework.
+ """
+
+ def test_get_tag_uri(self):
+ """
+ Test get_tag_uri() correctly generates TagURIs.
+ """
+ self.assertEqual(
+ feedgenerator.get_tag_uri('http://example.org/foo/bar#headline', datetime.date(2004, 10, 25)),
+ 'tag:example.org,2004-10-25:/foo/bar/headline')
+
+ def test_get_tag_uri_with_port(self):
+ """
+ Test that get_tag_uri() correctly generates TagURIs from URLs with port
+ numbers.
+ """
+ self.assertEqual(
+ feedgenerator.get_tag_uri('http://www.example.org:8000/2008/11/14/django#headline', datetime.datetime(2008, 11, 14, 13, 37, 0)),
+ 'tag:www.example.org,2008-11-14:/2008/11/14/django/headline')
+
+ def test_rfc2822_date(self):
+ """
+ Test rfc2822_date() correctly formats datetime objects.
+ """
+ self.assertEqual(
+ feedgenerator.rfc2822_date(datetime.datetime(2008, 11, 14, 13, 37, 0)),
+ "Fri, 14 Nov 2008 13:37:00 -0000"
+ )
+
+ def test_rfc2822_date_with_timezone(self):
+ """
+ Test rfc2822_date() correctly formats datetime objects with tzinfo.
+ """
+ self.assertEqual(
+ feedgenerator.rfc2822_date(datetime.datetime(2008, 11, 14, 13, 37, 0, tzinfo=tzinfo.FixedOffset(datetime.timedelta(minutes=60)))),
+ "Fri, 14 Nov 2008 13:37:00 +0100"
+ )
+
+ def test_rfc2822_date_without_time(self):
+ """
+ Test rfc2822_date() correctly formats date objects.
+ """
+ self.assertEqual(
+ feedgenerator.rfc2822_date(datetime.date(2008, 11, 14)),
+ "Fri, 14 Nov 2008 00:00:00 -0000"
+ )
+
+ def test_rfc3339_date(self):
+ """
+ Test rfc3339_date() correctly formats datetime objects.
+ """
+ self.assertEqual(
+ feedgenerator.rfc3339_date(datetime.datetime(2008, 11, 14, 13, 37, 0)),
+ "2008-11-14T13:37:00Z"
+ )
+
+ def test_rfc3339_date_with_timezone(self):
+ """
+ Test rfc3339_date() correctly formats datetime objects with tzinfo.
+ """
+ self.assertEqual(
+ feedgenerator.rfc3339_date(datetime.datetime(2008, 11, 14, 13, 37, 0, tzinfo=tzinfo.FixedOffset(datetime.timedelta(minutes=120)))),
+ "2008-11-14T13:37:00+02:00"
+ )
+
+ def test_rfc3339_date_without_time(self):
+ """
+ Test rfc3339_date() correctly formats date objects.
+ """
+ self.assertEqual(
+ feedgenerator.rfc3339_date(datetime.date(2008, 11, 14)),
+ "2008-11-14T00:00:00Z"
+ )
+
+ def test_atom1_mime_type(self):
+ """
+ Test to make sure Atom MIME type has UTF8 Charset parameter set
+ """
+ atom_feed = feedgenerator.Atom1Feed("title", "link", "description")
+ self.assertEqual(
+ atom_feed.mime_type, "application/atom+xml; charset=utf-8"
+ )
+
+ def test_rss_mime_type(self):
+ """
+ Test to make sure RSS MIME type has UTF8 Charset parameter set
+ """
+ rss_feed = feedgenerator.Rss201rev2Feed("title", "link", "description")
+ self.assertEqual(
+ rss_feed.mime_type, "application/rss+xml; charset=utf-8"
+ )
+
+ # Two regression tests for #14202
+
+ def test_feed_without_feed_url_gets_rendered_without_atom_link(self):
+ feed = feedgenerator.Rss201rev2Feed('title', '/link/', 'descr')
+ self.assertEqual(feed.feed['feed_url'], None)
+ feed_content = feed.writeString('utf-8')
+ self.assertNotIn('<atom:link', feed_content)
+ self.assertNotIn('href="/feed/"', feed_content)
+ self.assertNotIn('rel="self"', feed_content)
+
+ def test_feed_with_feed_url_gets_rendered_with_atom_link(self):
+ feed = feedgenerator.Rss201rev2Feed('title', '/link/', 'descr', feed_url='/feed/')
+ self.assertEqual(feed.feed['feed_url'], '/feed/')
+ feed_content = feed.writeString('utf-8')
+ self.assertIn('<atom:link', feed_content)
+ self.assertIn('href="/feed/"', feed_content)
+ self.assertIn('rel="self"', feed_content)
diff --git a/tests/utils/functional.py b/tests/utils/functional.py
new file mode 100644
index 0000000000..3bb50007c6
--- /dev/null
+++ b/tests/utils/functional.py
@@ -0,0 +1,66 @@
+from django.utils import unittest
+from django.utils.functional import lazy, lazy_property, cached_property
+
+
+class FunctionalTestCase(unittest.TestCase):
+ def test_lazy(self):
+ t = lazy(lambda: tuple(range(3)), list, tuple)
+ for a, b in zip(t(), range(3)):
+ self.assertEqual(a, b)
+
+ def test_lazy_base_class(self):
+ """Test that lazy also finds base class methods in the proxy object"""
+
+ class Base(object):
+ def base_method(self):
+ pass
+
+ class Klazz(Base):
+ pass
+
+ t = lazy(lambda: Klazz(), Klazz)()
+ self.assertTrue('base_method' in dir(t))
+
+ def test_lazy_property(self):
+
+ class A(object):
+
+ def _get_do(self):
+ raise NotImplementedError
+ def _set_do(self, value):
+ raise NotImplementedError
+ do = lazy_property(_get_do, _set_do)
+
+ class B(A):
+ def _get_do(self):
+ return "DO IT"
+
+ self.assertRaises(NotImplementedError, lambda: A().do)
+ self.assertEqual(B().do, 'DO IT')
+
+ def test_cached_property(self):
+ """
+ Test that cached_property caches its value,
+ and that it behaves like a property
+ """
+
+ class A(object):
+
+ @cached_property
+ def value(self):
+ return 1, object()
+
+ a = A()
+
+ # check that it is cached
+ self.assertEqual(a.value, a.value)
+
+ # check that it returns the right thing
+ self.assertEqual(a.value[0], 1)
+
+ # check that state isn't shared between instances
+ a2 = A()
+ self.assertNotEqual(a.value, a2.value)
+
+ # check that it behaves like a property when there's no instance
+ self.assertIsInstance(A.value, cached_property)
diff --git a/tests/utils/html.py b/tests/utils/html.py
new file mode 100644
index 0000000000..62c7dac24a
--- /dev/null
+++ b/tests/utils/html.py
@@ -0,0 +1,161 @@
+from __future__ import unicode_literals
+
+import unittest
+
+from django.utils import html
+
+class TestUtilsHtml(unittest.TestCase):
+
+ def check_output(self, function, value, output=None):
+ """
+ Check that function(value) equals output. If output is None,
+ check that function(value) equals value.
+ """
+ if output is None:
+ output = value
+ self.assertEqual(function(value), output)
+
+ def test_escape(self):
+ f = html.escape
+ items = (
+ ('&','&amp;'),
+ ('<', '&lt;'),
+ ('>', '&gt;'),
+ ('"', '&quot;'),
+ ("'", '&#39;'),
+ )
+ # Substitution patterns for testing the above items.
+ patterns = ("%s", "asdf%sfdsa", "%s1", "1%sb")
+ for value, output in items:
+ for pattern in patterns:
+ self.check_output(f, pattern % value, pattern % output)
+ # Check repeated values.
+ self.check_output(f, value * 2, output * 2)
+ # Verify it doesn't double replace &.
+ self.check_output(f, '<&', '&lt;&amp;')
+
+ def test_format_html(self):
+ self.assertEqual(
+ html.format_html("{0} {1} {third} {fourth}",
+ "< Dangerous >",
+ html.mark_safe("<b>safe</b>"),
+ third="< dangerous again",
+ fourth=html.mark_safe("<i>safe again</i>")
+ ),
+ "&lt; Dangerous &gt; <b>safe</b> &lt; dangerous again <i>safe again</i>"
+ )
+
+ def test_linebreaks(self):
+ f = html.linebreaks
+ items = (
+ ("para1\n\npara2\r\rpara3", "<p>para1</p>\n\n<p>para2</p>\n\n<p>para3</p>"),
+ ("para1\nsub1\rsub2\n\npara2", "<p>para1<br />sub1<br />sub2</p>\n\n<p>para2</p>"),
+ ("para1\r\n\r\npara2\rsub1\r\rpara4", "<p>para1</p>\n\n<p>para2<br />sub1</p>\n\n<p>para4</p>"),
+ ("para1\tmore\n\npara2", "<p>para1\tmore</p>\n\n<p>para2</p>"),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_tags(self):
+ f = html.strip_tags
+ items = (
+ ('<adf>a', 'a'),
+ ('</adf>a', 'a'),
+ ('<asdf><asdf>e', 'e'),
+ ('<f', '<f'),
+ ('</fe', '</fe'),
+ ('<x>b<y>', 'b'),
+ ('a<p onclick="alert(\'<test>\')">b</p>c', 'abc'),
+ ('a<p a >b</p>c', 'abc'),
+ ('d<a:b c:d>e</p>f', 'def'),
+ ('<strong>foo</strong><a href="http://example.com">bar</a>', 'foobar'),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_spaces_between_tags(self):
+ f = html.strip_spaces_between_tags
+ # Strings that should come out untouched.
+ items = (' <adf>', '<adf> ', ' </adf> ', ' <f> x</f>')
+ for value in items:
+ self.check_output(f, value)
+ # Strings that have spaces to strip.
+ items = (
+ ('<d> </d>', '<d></d>'),
+ ('<p>hello </p>\n<p> world</p>', '<p>hello </p><p> world</p>'),
+ ('\n<p>\t</p>\n<p> </p>\n', '\n<p></p><p></p>\n'),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_entities(self):
+ f = html.strip_entities
+ # Strings that should come out untouched.
+ values = ("&", "&a", "&a", "a&#a")
+ for value in values:
+ self.check_output(f, value)
+ # Valid entities that should be stripped from the patterns.
+ entities = ("&#1;", "&#12;", "&a;", "&fdasdfasdfasdf;")
+ patterns = (
+ ("asdf %(entity)s ", "asdf "),
+ ("%(entity)s%(entity)s", ""),
+ ("&%(entity)s%(entity)s", "&"),
+ ("%(entity)s3", "3"),
+ )
+ for entity in entities:
+ for in_pattern, output in patterns:
+ self.check_output(f, in_pattern % {'entity': entity}, output)
+
+ def test_fix_ampersands(self):
+ f = html.fix_ampersands
+ # Strings without ampersands or with ampersands already encoded.
+ values = ("a&#1;", "b", "&a;", "&amp; &x; ", "asdf")
+ patterns = (
+ ("%s", "%s"),
+ ("&%s", "&amp;%s"),
+ ("&%s&", "&amp;%s&amp;"),
+ )
+ for value in values:
+ for in_pattern, out_pattern in patterns:
+ self.check_output(f, in_pattern % value, out_pattern % value)
+ # Strings with ampersands that need encoding.
+ items = (
+ ("&#;", "&amp;#;"),
+ ("&#875 ;", "&amp;#875 ;"),
+ ("&#4abc;", "&amp;#4abc;"),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_escapejs(self):
+ f = html.escapejs
+ items = (
+ ('"double quotes" and \'single quotes\'', '\\u0022double quotes\\u0022 and \\u0027single quotes\\u0027'),
+ (r'\ : backslashes, too', '\\u005C : backslashes, too'),
+ ('and lots of whitespace: \r\n\t\v\f\b', 'and lots of whitespace: \\u000D\\u000A\\u0009\\u000B\\u000C\\u0008'),
+ (r'<script>and this</script>', '\\u003Cscript\\u003Eand this\\u003C/script\\u003E'),
+ ('paragraph separator:\u2029and line separator:\u2028', 'paragraph separator:\\u2029and line separator:\\u2028'),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_clean_html(self):
+ f = html.clean_html
+ items = (
+ ('<p>I <i>believe</i> in <b>semantic markup</b>!</p>', '<p>I <em>believe</em> in <strong>semantic markup</strong>!</p>'),
+ ('I escape & I don\'t <a href="#" target="_blank">target</a>', 'I escape &amp; I don\'t <a href="#" >target</a>'),
+ ('<p>I kill whitespace</p><br clear="all"><p>&nbsp;</p>', '<p>I kill whitespace</p>'),
+ # also a regression test for #7267: this used to raise an UnicodeDecodeError
+ ('<p>* foo</p><p>* bar</p>', '<ul>\n<li> foo</li><li> bar</li>\n</ul>'),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_remove_tags(self):
+ f = html.remove_tags
+ items = (
+ ("<b><i>Yes</i></b>", "b i", "Yes"),
+ ("<a>x</a> <p><b>y</b></p>", "a b", "x <p>y</p>"),
+ )
+ for value, tags, output in items:
+ self.assertEqual(f(value, tags), output)
diff --git a/tests/utils/http.py b/tests/utils/http.py
new file mode 100644
index 0000000000..6d3bc025af
--- /dev/null
+++ b/tests/utils/http.py
@@ -0,0 +1,163 @@
+from datetime import datetime
+import sys
+
+from django.http import HttpResponse, utils
+from django.test import RequestFactory
+from django.utils.datastructures import MultiValueDict
+from django.utils import http
+from django.utils import six
+from django.utils import unittest
+
+
+class TestUtilsHttp(unittest.TestCase):
+
+ def test_same_origin_true(self):
+ # Identical
+ self.assertTrue(http.same_origin('http://foo.com/', 'http://foo.com/'))
+ # One with trailing slash - see #15617
+ self.assertTrue(http.same_origin('http://foo.com', 'http://foo.com/'))
+ self.assertTrue(http.same_origin('http://foo.com/', 'http://foo.com'))
+ # With port
+ self.assertTrue(http.same_origin('https://foo.com:8000', 'https://foo.com:8000/'))
+
+ def test_same_origin_false(self):
+ # Different scheme
+ self.assertFalse(http.same_origin('http://foo.com', 'https://foo.com'))
+ # Different host
+ self.assertFalse(http.same_origin('http://foo.com', 'http://goo.com'))
+ # Different host again
+ self.assertFalse(http.same_origin('http://foo.com', 'http://foo.com.evil.com'))
+ # Different port
+ self.assertFalse(http.same_origin('http://foo.com:8000', 'http://foo.com:8001'))
+
+ def test_urlencode(self):
+ # 2-tuples (the norm)
+ result = http.urlencode((('a', 1), ('b', 2), ('c', 3)))
+ self.assertEqual(result, 'a=1&b=2&c=3')
+
+ # A dictionary
+ result = http.urlencode({ 'a': 1, 'b': 2, 'c': 3})
+ acceptable_results = [
+ # Need to allow all of these as dictionaries have to be treated as
+ # unordered
+ 'a=1&b=2&c=3',
+ 'a=1&c=3&b=2',
+ 'b=2&a=1&c=3',
+ 'b=2&c=3&a=1',
+ 'c=3&a=1&b=2',
+ 'c=3&b=2&a=1'
+ ]
+ self.assertTrue(result in acceptable_results)
+ result = http.urlencode({'a': [1, 2]}, doseq=False)
+ self.assertEqual(result, 'a=%5B%271%27%2C+%272%27%5D')
+ result = http.urlencode({'a': [1, 2]}, doseq=True)
+ self.assertEqual(result, 'a=1&a=2')
+ result = http.urlencode({'a': []}, doseq=True)
+ self.assertEqual(result, '')
+
+ # A MultiValueDict
+ result = http.urlencode(MultiValueDict({
+ 'name': ['Adrian', 'Simon'],
+ 'position': ['Developer']
+ }), doseq=True)
+ acceptable_results = [
+ # MultiValueDicts are similarly unordered
+ 'name=Adrian&name=Simon&position=Developer',
+ 'position=Developer&name=Adrian&name=Simon'
+ ]
+ self.assertTrue(result in acceptable_results)
+
+ def test_fix_IE_for_vary(self):
+ """
+ Regression for #16632.
+
+ `fix_IE_for_vary` shouldn't crash when there's no Content-Type header.
+ """
+
+ # functions to generate responses
+ def response_with_unsafe_content_type():
+ r = HttpResponse(content_type="text/unsafe")
+ r['Vary'] = 'Cookie'
+ return r
+
+ def no_content_response_with_unsafe_content_type():
+ # 'Content-Type' always defaulted, so delete it
+ r = response_with_unsafe_content_type()
+ del r['Content-Type']
+ return r
+
+ # request with & without IE user agent
+ rf = RequestFactory()
+ request = rf.get('/')
+ ie_request = rf.get('/', HTTP_USER_AGENT='MSIE')
+
+ # not IE, unsafe_content_type
+ response = response_with_unsafe_content_type()
+ utils.fix_IE_for_vary(request, response)
+ self.assertTrue('Vary' in response)
+
+ # IE, unsafe_content_type
+ response = response_with_unsafe_content_type()
+ utils.fix_IE_for_vary(ie_request, response)
+ self.assertFalse('Vary' in response)
+
+ # not IE, no_content
+ response = no_content_response_with_unsafe_content_type()
+ utils.fix_IE_for_vary(request, response)
+ self.assertTrue('Vary' in response)
+
+ # IE, no_content
+ response = no_content_response_with_unsafe_content_type()
+ utils.fix_IE_for_vary(ie_request, response)
+ self.assertFalse('Vary' in response)
+
+ def test_base36(self):
+ # reciprocity works
+ for n in [0, 1, 1000, 1000000]:
+ self.assertEqual(n, http.base36_to_int(http.int_to_base36(n)))
+ if not six.PY3:
+ self.assertEqual(sys.maxint, http.base36_to_int(http.int_to_base36(sys.maxint)))
+
+ # bad input
+ self.assertRaises(ValueError, http.int_to_base36, -1)
+ if not six.PY3:
+ self.assertRaises(ValueError, http.int_to_base36, sys.maxint + 1)
+ for n in ['1', 'foo', {1: 2}, (1, 2, 3), 3.141]:
+ self.assertRaises(TypeError, http.int_to_base36, n)
+
+ for n in ['#', ' ']:
+ self.assertRaises(ValueError, http.base36_to_int, n)
+ for n in [123, {1: 2}, (1, 2, 3), 3.141]:
+ self.assertRaises(TypeError, http.base36_to_int, n)
+
+ # more explicit output testing
+ for n, b36 in [(0, '0'), (1, '1'), (42, '16'), (818469960, 'django')]:
+ self.assertEqual(http.int_to_base36(n), b36)
+ self.assertEqual(http.base36_to_int(b36), n)
+
+
+class ETagProcessingTests(unittest.TestCase):
+ def testParsing(self):
+ etags = http.parse_etags(r'"", "etag", "e\"t\"ag", "e\\tag", W/"weak"')
+ self.assertEqual(etags, ['', 'etag', 'e"t"ag', r'e\tag', 'weak'])
+
+ def testQuoting(self):
+ quoted_etag = http.quote_etag(r'e\t"ag')
+ self.assertEqual(quoted_etag, r'"e\\t\"ag"')
+
+
+class HttpDateProcessingTests(unittest.TestCase):
+ def testParsingRfc1123(self):
+ parsed = http.parse_http_date('Sun, 06 Nov 1994 08:49:37 GMT')
+ self.assertEqual(datetime.utcfromtimestamp(parsed),
+ datetime(1994, 11, 6, 8, 49, 37))
+
+ def testParsingRfc850(self):
+ parsed = http.parse_http_date('Sunday, 06-Nov-94 08:49:37 GMT')
+ self.assertEqual(datetime.utcfromtimestamp(parsed),
+ datetime(1994, 11, 6, 8, 49, 37))
+
+ def testParsingAsctime(self):
+ parsed = http.parse_http_date('Sun Nov 6 08:49:37 1994')
+ self.assertEqual(datetime.utcfromtimestamp(parsed),
+ datetime(1994, 11, 6, 8, 49, 37))
diff --git a/tests/utils/ipv6.py b/tests/utils/ipv6.py
new file mode 100644
index 0000000000..1713de82b8
--- /dev/null
+++ b/tests/utils/ipv6.py
@@ -0,0 +1,53 @@
+from __future__ import unicode_literals
+
+from django.utils import unittest
+from django.utils.ipv6 import is_valid_ipv6_address, clean_ipv6_address
+
+class TestUtilsIPv6(unittest.TestCase):
+
+ def test_validates_correct_plain_address(self):
+ self.assertTrue(is_valid_ipv6_address('fe80::223:6cff:fe8a:2e8a'))
+ self.assertTrue(is_valid_ipv6_address('2a02::223:6cff:fe8a:2e8a'))
+ self.assertTrue(is_valid_ipv6_address('1::2:3:4:5:6:7'))
+ self.assertTrue(is_valid_ipv6_address('::'))
+ self.assertTrue(is_valid_ipv6_address('::a'))
+ self.assertTrue(is_valid_ipv6_address('2::'))
+
+ def test_validates_correct_with_v4mapping(self):
+ self.assertTrue(is_valid_ipv6_address('::ffff:254.42.16.14'))
+ self.assertTrue(is_valid_ipv6_address('::ffff:0a0a:0a0a'))
+
+ def test_validates_incorrect_plain_address(self):
+ self.assertFalse(is_valid_ipv6_address('foo'))
+ self.assertFalse(is_valid_ipv6_address('127.0.0.1'))
+ self.assertFalse(is_valid_ipv6_address('12345::'))
+ self.assertFalse(is_valid_ipv6_address('1::2:3::4'))
+ self.assertFalse(is_valid_ipv6_address('1::zzz'))
+ self.assertFalse(is_valid_ipv6_address('1::2:3:4:5:6:7:8'))
+ self.assertFalse(is_valid_ipv6_address('1:2'))
+ self.assertFalse(is_valid_ipv6_address('1:::2'))
+
+ def test_validates_incorrect_with_v4mapping(self):
+ self.assertFalse(is_valid_ipv6_address('::ffff:999.42.16.14'))
+ self.assertFalse(is_valid_ipv6_address('::ffff:zzzz:0a0a'))
+ # The ::1.2.3.4 format used to be valid but was deprecated
+ # in rfc4291 section 2.5.5.1
+ self.assertTrue(is_valid_ipv6_address('::254.42.16.14'))
+ self.assertTrue(is_valid_ipv6_address('::0a0a:0a0a'))
+ self.assertFalse(is_valid_ipv6_address('::999.42.16.14'))
+ self.assertFalse(is_valid_ipv6_address('::zzzz:0a0a'))
+
+ def test_cleanes_plain_address(self):
+ self.assertEqual(clean_ipv6_address('DEAD::0:BEEF'), 'dead::beef')
+ self.assertEqual(clean_ipv6_address('2001:000:a:0000:0:fe:fe:beef'), '2001:0:a::fe:fe:beef')
+ self.assertEqual(clean_ipv6_address('2001::a:0000:0:fe:fe:beef'), '2001:0:a::fe:fe:beef')
+
+ def test_cleanes_with_v4_mapping(self):
+ self.assertEqual(clean_ipv6_address('::ffff:0a0a:0a0a'), '::ffff:10.10.10.10')
+ self.assertEqual(clean_ipv6_address('::ffff:1234:1234'), '::ffff:18.52.18.52')
+ self.assertEqual(clean_ipv6_address('::ffff:18.52.18.52'), '::ffff:18.52.18.52')
+
+ def test_unpacks_ipv4(self):
+ self.assertEqual(clean_ipv6_address('::ffff:0a0a:0a0a', unpack_ipv4=True), '10.10.10.10')
+ self.assertEqual(clean_ipv6_address('::ffff:1234:1234', unpack_ipv4=True), '18.52.18.52')
+ self.assertEqual(clean_ipv6_address('::ffff:18.52.18.52', unpack_ipv4=True), '18.52.18.52')
diff --git a/tests/utils/jslex.py b/tests/utils/jslex.py
new file mode 100644
index 0000000000..a2af7b4bcf
--- /dev/null
+++ b/tests/utils/jslex.py
@@ -0,0 +1,216 @@
+"""Tests for jslex."""
+# encoding: utf-8
+# originally from https://bitbucket.org/ned/jslex
+
+from django.test import TestCase
+from django.utils.jslex import JsLexer, prepare_js_for_gettext
+
+class JsTokensTest(TestCase):
+ LEX_CASES = [
+ # ids
+ ("a ABC $ _ a123", ["id a", "id ABC", "id $", "id _", "id a123"]),
+ (r"\u1234 abc\u0020 \u0065_\u0067", [r"id \u1234", r"id abc\u0020", r"id \u0065_\u0067"]),
+ # numbers
+ ("123 1.234 0.123e-3 0 1E+40 1e1 .123", ["dnum 123", "dnum 1.234", "dnum 0.123e-3", "dnum 0", "dnum 1E+40", "dnum 1e1", "dnum .123"]),
+ ("0x1 0xabCD 0XABcd", ["hnum 0x1", "hnum 0xabCD", "hnum 0XABcd"]),
+ ("010 0377 090", ["onum 010", "onum 0377", "dnum 0", "dnum 90"]),
+ ("0xa123ghi", ["hnum 0xa123", "id ghi"]),
+ # keywords
+ ("function Function FUNCTION", ["keyword function", "id Function", "id FUNCTION"]),
+ ("const constructor in inherits", ["keyword const", "id constructor", "keyword in", "id inherits"]),
+ ("true true_enough", ["reserved true", "id true_enough"]),
+ # strings
+ (''' 'hello' "hello" ''', ["string 'hello'", 'string "hello"']),
+ (r""" 'don\'t' "don\"t" '"' "'" '\'' "\"" """,
+ [r"""string 'don\'t'""", r'''string "don\"t"''', r"""string '"'""", r'''string "'"''', r"""string '\''""", r'''string "\""''']),
+ (r'"ƃuıxǝ⅂ ʇdıɹɔsɐʌɐſ\""', [r'string "ƃuıxǝ⅂ ʇdıɹɔsɐʌɐſ\""']),
+ # comments
+ ("a//b", ["id a", "linecomment //b"]),
+ ("/****/a/=2//hello", ["comment /****/", "id a", "punct /=", "dnum 2", "linecomment //hello"]),
+ ("/*\n * Header\n */\na=1;", ["comment /*\n * Header\n */", "id a", "punct =", "dnum 1", "punct ;"]),
+ # punctuation
+ ("a+++b", ["id a", "punct ++", "punct +", "id b"]),
+ # regex
+ (r"a=/a*/,1", ["id a", "punct =", "regex /a*/", "punct ,", "dnum 1"]),
+ (r"a=/a*[^/]+/,1", ["id a", "punct =", "regex /a*[^/]+/", "punct ,", "dnum 1"]),
+ (r"a=/a*\[^/,1", ["id a", "punct =", r"regex /a*\[^/", "punct ,", "dnum 1"]),
+ (r"a=/\//,1", ["id a", "punct =", r"regex /\//", "punct ,", "dnum 1"]),
+
+ # next two are from http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
+ ("""for (var x = a in foo && "</x>" || mot ? z:/x:3;x<5;y</g/i) {xyz(x++);}""",
+ ["keyword for", "punct (", "keyword var", "id x", "punct =", "id a", "keyword in",
+ "id foo", "punct &&", 'string "</x>"', "punct ||", "id mot", "punct ?", "id z",
+ "punct :", "regex /x:3;x<5;y</g", "punct /", "id i", "punct )", "punct {",
+ "id xyz", "punct (", "id x", "punct ++", "punct )", "punct ;", "punct }"]),
+ ("""for (var x = a in foo && "</x>" || mot ? z/x:3;x<5;y</g/i) {xyz(x++);}""",
+ ["keyword for", "punct (", "keyword var", "id x", "punct =", "id a", "keyword in",
+ "id foo", "punct &&", 'string "</x>"', "punct ||", "id mot", "punct ?", "id z",
+ "punct /", "id x", "punct :", "dnum 3", "punct ;", "id x", "punct <", "dnum 5",
+ "punct ;", "id y", "punct <", "regex /g/i", "punct )", "punct {",
+ "id xyz", "punct (", "id x", "punct ++", "punct )", "punct ;", "punct }"]),
+
+ # Various "illegal" regexes that are valid according to the std.
+ (r"""/????/, /++++/, /[----]/ """, ["regex /????/", "punct ,", "regex /++++/", "punct ,", "regex /[----]/"]),
+
+ # Stress cases from http://stackoverflow.com/questions/5533925/what-javascript-constructs-does-jslex-incorrectly-lex/5573409#5573409
+ (r"""/\[/""", [r"""regex /\[/"""]),
+ (r"""/[i]/""", [r"""regex /[i]/"""]),
+ (r"""/[\]]/""", [r"""regex /[\]]/"""]),
+ (r"""/a[\]]/""", [r"""regex /a[\]]/"""]),
+ (r"""/a[\]]b/""", [r"""regex /a[\]]b/"""]),
+ (r"""/[\]/]/gi""", [r"""regex /[\]/]/gi"""]),
+ (r"""/\[[^\]]+\]/gi""", [r"""regex /\[[^\]]+\]/gi"""]),
+ ("""
+ rexl.re = {
+ NAME: /^(?!\d)(?:\w)+|^"(?:[^"]|"")+"/,
+ UNQUOTED_LITERAL: /^@(?:(?!\d)(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/,
+ QUOTED_LITERAL: /^'(?:[^']|'')*'/,
+ NUMERIC_LITERAL: /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/,
+ SYMBOL: /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/
+ };
+ """,
+ ["id rexl", "punct .", "id re", "punct =", "punct {",
+ "id NAME", "punct :", r"""regex /^(?!\d)(?:\w)+|^"(?:[^"]|"")+"/""", "punct ,",
+ "id UNQUOTED_LITERAL", "punct :", r"""regex /^@(?:(?!\d)(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/""", "punct ,",
+ "id QUOTED_LITERAL", "punct :", r"""regex /^'(?:[^']|'')*'/""", "punct ,",
+ "id NUMERIC_LITERAL", "punct :", r"""regex /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/""", "punct ,",
+ "id SYMBOL", "punct :", r"""regex /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/""",
+ "punct }", "punct ;"
+ ]),
+
+ ("""
+ rexl.re = {
+ NAME: /^(?!\d)(?:\w)+|^"(?:[^"]|"")+"/,
+ UNQUOTED_LITERAL: /^@(?:(?!\d)(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/,
+ QUOTED_LITERAL: /^'(?:[^']|'')*'/,
+ NUMERIC_LITERAL: /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/,
+ SYMBOL: /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/
+ };
+ str = '"';
+ """,
+ ["id rexl", "punct .", "id re", "punct =", "punct {",
+ "id NAME", "punct :", r"""regex /^(?!\d)(?:\w)+|^"(?:[^"]|"")+"/""", "punct ,",
+ "id UNQUOTED_LITERAL", "punct :", r"""regex /^@(?:(?!\d)(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/""", "punct ,",
+ "id QUOTED_LITERAL", "punct :", r"""regex /^'(?:[^']|'')*'/""", "punct ,",
+ "id NUMERIC_LITERAL", "punct :", r"""regex /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/""", "punct ,",
+ "id SYMBOL", "punct :", r"""regex /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/""",
+ "punct }", "punct ;",
+ "id str", "punct =", """string '"'""", "punct ;",
+ ]),
+
+ (r""" this._js = "e.str(\"" + this.value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\")"; """,
+ ["keyword this", "punct .", "id _js", "punct =", r'''string "e.str(\""''', "punct +", "keyword this", "punct .",
+ "id value", "punct .", "id replace", "punct (", r"regex /\\/g", "punct ,", r'string "\\\\"', "punct )",
+ "punct .", "id replace", "punct (", r'regex /"/g', "punct ,", r'string "\\\""', "punct )", "punct +",
+ r'string "\")"', "punct ;"]),
+ ]
+
+def make_function(input, toks):
+ def test_func(self):
+ lexer = JsLexer()
+ result = ["%s %s" % (name, tok) for name, tok in lexer.lex(input) if name != 'ws']
+ self.assertListEqual(result, toks)
+ return test_func
+
+for i, (input, toks) in enumerate(JsTokensTest.LEX_CASES):
+ setattr(JsTokensTest, "test_case_%d" % i, make_function(input, toks))
+
+
+GETTEXT_CASES = (
+ (
+ r"""
+ a = 1; /* /[0-9]+/ */
+ b = 0x2a0b / 1; // /[0-9]+/
+ c = 3;
+ """,
+ r"""
+ a = 1; /* /[0-9]+/ */
+ b = 0x2a0b / 1; // /[0-9]+/
+ c = 3;
+ """
+ ), (
+ r"""
+ a = 1.234e-5;
+ /*
+ * /[0-9+/
+ */
+ b = .0123;
+ """,
+ r"""
+ a = 1.234e-5;
+ /*
+ * /[0-9+/
+ */
+ b = .0123;
+ """
+ ), (
+ r"""
+ x = y / z;
+ alert(gettext("hello"));
+ x /= 3;
+ """,
+ r"""
+ x = y / z;
+ alert(gettext("hello"));
+ x /= 3;
+ """
+ ), (
+ r"""
+ s = "Hello \"th/foo/ere\"";
+ s = 'He\x23llo \'th/foo/ere\'';
+ s = 'slash quote \", just quote "';
+ """,
+ r"""
+ s = "Hello \"th/foo/ere\"";
+ s = "He\x23llo \'th/foo/ere\'";
+ s = "slash quote \", just quote \"";
+ """
+ ), (
+ r"""
+ s = "Line continuation\
+ continued /hello/ still the string";/hello/;
+ """,
+ r"""
+ s = "Line continuation\
+ continued /hello/ still the string";"REGEX";
+ """
+ ), (
+ r"""
+ var regex = /pattern/;
+ var regex2 = /matter/gm;
+ var regex3 = /[*/]+/gm.foo("hey");
+ """,
+ r"""
+ var regex = "REGEX";
+ var regex2 = "REGEX";
+ var regex3 = "REGEX".foo("hey");
+ """
+ ), (
+ r"""
+ for (var x = a in foo && "</x>" || mot ? z:/x:3;x<5;y</g/i) {xyz(x++);}
+ for (var x = a in foo && "</x>" || mot ? z/x:3;x<5;y</g/i) {xyz(x++);}
+ """,
+ r"""
+ for (var x = a in foo && "</x>" || mot ? z:"REGEX"/i) {xyz(x++);}
+ for (var x = a in foo && "</x>" || mot ? z/x:3;x<5;y<"REGEX") {xyz(x++);}
+ """
+ ), (
+ r"""
+ \u1234xyz = gettext('Hello there');
+ """, r"""
+ Uu1234xyz = gettext("Hello there");
+ """
+ )
+)
+
+
+class JsToCForGettextTest(TestCase):
+ pass
+
+def make_function(js, c):
+ def test_func(self):
+ self.assertMultiLineEqual(prepare_js_for_gettext(js), c)
+ return test_func
+
+for i, pair in enumerate(GETTEXT_CASES):
+ setattr(JsToCForGettextTest, "test_case_%d" % i, make_function(*pair))
diff --git a/tests/utils/models.py b/tests/utils/models.py
new file mode 100644
index 0000000000..97a72bab14
--- /dev/null
+++ b/tests/utils/models.py
@@ -0,0 +1 @@
+# Test runner needs a models.py file.
diff --git a/tests/utils/module_loading.py b/tests/utils/module_loading.py
new file mode 100644
index 0000000000..a9e3706bb9
--- /dev/null
+++ b/tests/utils/module_loading.py
@@ -0,0 +1,184 @@
+import os
+import sys
+import imp
+from zipimport import zipimporter
+
+from django.core.exceptions import ImproperlyConfigured
+from django.utils import unittest
+from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path, module_has_submodule
+from django.utils._os import upath
+
+
+class DefaultLoader(unittest.TestCase):
+ def setUp(self):
+ sys.meta_path.insert(0, ProxyFinder())
+
+ def tearDown(self):
+ sys.meta_path.pop(0)
+
+ def test_loader(self):
+ "Normal module existence can be tested"
+ test_module = import_module('regressiontests.utils.test_module')
+ test_no_submodule = import_module(
+ 'regressiontests.utils.test_no_submodule')
+
+ # An importable child
+ self.assertTrue(module_has_submodule(test_module, 'good_module'))
+ mod = import_module('regressiontests.utils.test_module.good_module')
+ self.assertEqual(mod.content, 'Good Module')
+
+ # A child that exists, but will generate an import error if loaded
+ self.assertTrue(module_has_submodule(test_module, 'bad_module'))
+ self.assertRaises(ImportError, import_module, 'regressiontests.utils.test_module.bad_module')
+
+ # A child that doesn't exist
+ self.assertFalse(module_has_submodule(test_module, 'no_such_module'))
+ self.assertRaises(ImportError, import_module, 'regressiontests.utils.test_module.no_such_module')
+
+ # A child that doesn't exist, but is the name of a package on the path
+ self.assertFalse(module_has_submodule(test_module, 'django'))
+ self.assertRaises(ImportError, import_module, 'regressiontests.utils.test_module.django')
+
+ # Don't be confused by caching of import misses
+ import types # causes attempted import of regressiontests.utils.types
+ self.assertFalse(module_has_submodule(sys.modules['regressiontests.utils'], 'types'))
+
+ # A module which doesn't have a __path__ (so no submodules)
+ self.assertFalse(module_has_submodule(test_no_submodule, 'anything'))
+ self.assertRaises(ImportError, import_module,
+ 'regressiontests.utils.test_no_submodule.anything')
+
+class EggLoader(unittest.TestCase):
+ def setUp(self):
+ self.old_path = sys.path[:]
+ self.egg_dir = '%s/eggs' % os.path.dirname(upath(__file__))
+
+ def tearDown(self):
+ sys.path = self.old_path
+ sys.path_importer_cache.clear()
+
+ sys.modules.pop('egg_module.sub1.sub2.bad_module', None)
+ sys.modules.pop('egg_module.sub1.sub2.good_module', None)
+ sys.modules.pop('egg_module.sub1.sub2', None)
+ sys.modules.pop('egg_module.sub1', None)
+ sys.modules.pop('egg_module.bad_module', None)
+ sys.modules.pop('egg_module.good_module', None)
+ sys.modules.pop('egg_module', None)
+
+ def test_shallow_loader(self):
+ "Module existence can be tested inside eggs"
+ egg_name = '%s/test_egg.egg' % self.egg_dir
+ sys.path.append(egg_name)
+ egg_module = import_module('egg_module')
+
+ # An importable child
+ self.assertTrue(module_has_submodule(egg_module, 'good_module'))
+ mod = import_module('egg_module.good_module')
+ self.assertEqual(mod.content, 'Good Module')
+
+ # A child that exists, but will generate an import error if loaded
+ self.assertTrue(module_has_submodule(egg_module, 'bad_module'))
+ self.assertRaises(ImportError, import_module, 'egg_module.bad_module')
+
+ # A child that doesn't exist
+ self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
+ self.assertRaises(ImportError, import_module, 'egg_module.no_such_module')
+
+ def test_deep_loader(self):
+ "Modules deep inside an egg can still be tested for existence"
+ egg_name = '%s/test_egg.egg' % self.egg_dir
+ sys.path.append(egg_name)
+ egg_module = import_module('egg_module.sub1.sub2')
+
+ # An importable child
+ self.assertTrue(module_has_submodule(egg_module, 'good_module'))
+ mod = import_module('egg_module.sub1.sub2.good_module')
+ self.assertEqual(mod.content, 'Deep Good Module')
+
+ # A child that exists, but will generate an import error if loaded
+ self.assertTrue(module_has_submodule(egg_module, 'bad_module'))
+ self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.bad_module')
+
+ # A child that doesn't exist
+ self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
+ self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.no_such_module')
+
+
+class ModuleImportTestCase(unittest.TestCase):
+ def test_import_by_path(self):
+ cls = import_by_path(
+ 'django.utils.module_loading.import_by_path')
+ self.assertEqual(cls, import_by_path)
+
+ # Test exceptions raised
+ for path in ('no_dots_in_path', 'unexistent.path',
+ 'tests.regressiontests.utils.unexistent'):
+ self.assertRaises(ImproperlyConfigured, import_by_path, path)
+
+ with self.assertRaises(ImproperlyConfigured) as cm:
+ import_by_path('unexistent.module.path', error_prefix="Foo")
+ self.assertTrue(str(cm.exception).startswith('Foo'))
+
+
+class ProxyFinder(object):
+ def __init__(self):
+ self._cache = {}
+
+ def find_module(self, fullname, path=None):
+ tail = fullname.rsplit('.', 1)[-1]
+ try:
+ fd, fn, info = imp.find_module(tail, path)
+ if fullname in self._cache:
+ old_fd = self._cache[fullname][0]
+ if old_fd:
+ old_fd.close()
+ self._cache[fullname] = (fd, fn, info)
+ except ImportError:
+ return None
+ else:
+ return self # this is a loader as well
+
+ def load_module(self, fullname):
+ if fullname in sys.modules:
+ return sys.modules[fullname]
+ fd, fn, info = self._cache[fullname]
+ try:
+ return imp.load_module(fullname, fd, fn, info)
+ finally:
+ if fd:
+ fd.close()
+
+class TestFinder(object):
+ def __init__(self, *args, **kwargs):
+ self.importer = zipimporter(*args, **kwargs)
+
+ def find_module(self, path):
+ importer = self.importer.find_module(path)
+ if importer is None:
+ return
+ return TestLoader(importer)
+
+class TestLoader(object):
+ def __init__(self, importer):
+ self.importer = importer
+
+ def load_module(self, name):
+ mod = self.importer.load_module(name)
+ mod.__loader__ = self
+ return mod
+
+class CustomLoader(EggLoader):
+ """The Custom Loader test is exactly the same as the EggLoader, but
+ it uses a custom defined Loader and Finder that is intentionally
+ split into two classes. Although the EggLoader combines both functions
+ into one class, this isn't required.
+ """
+ def setUp(self):
+ super(CustomLoader, self).setUp()
+ sys.path_hooks.insert(0, TestFinder)
+ sys.path_importer_cache.clear()
+
+ def tearDown(self):
+ super(CustomLoader, self).tearDown()
+ sys.path_hooks.pop(0)
diff --git a/tests/utils/numberformat.py b/tests/utils/numberformat.py
new file mode 100644
index 0000000000..f9d9031e48
--- /dev/null
+++ b/tests/utils/numberformat.py
@@ -0,0 +1,47 @@
+from unittest import TestCase
+from django.utils.numberformat import format as nformat
+from sys import float_info
+
+
+class TestNumberFormat(TestCase):
+
+ def test_format_number(self):
+ self.assertEqual(nformat(1234, '.'), '1234')
+ self.assertEqual(nformat(1234.2, '.'), '1234.2')
+ self.assertEqual(nformat(1234, '.', decimal_pos=2), '1234.00')
+ self.assertEqual(nformat(1234, '.', grouping=2, thousand_sep=','),
+ '1234')
+ self.assertEqual(nformat(1234, '.', grouping=2, thousand_sep=',',
+ force_grouping=True), '12,34')
+ self.assertEqual(nformat(-1234.33, '.', decimal_pos=1), '-1234.3')
+
+ def test_format_string(self):
+ self.assertEqual(nformat('1234', '.'), '1234')
+ self.assertEqual(nformat('1234.2', '.'), '1234.2')
+ self.assertEqual(nformat('1234', '.', decimal_pos=2), '1234.00')
+ self.assertEqual(nformat('1234', '.', grouping=2, thousand_sep=','),
+ '1234')
+ self.assertEqual(nformat('1234', '.', grouping=2, thousand_sep=',',
+ force_grouping=True), '12,34')
+ self.assertEqual(nformat('-1234.33', '.', decimal_pos=1), '-1234.3')
+
+ def test_large_number(self):
+ most_max = ('{0}179769313486231570814527423731704356798070567525844996'
+ '598917476803157260780028538760589558632766878171540458953'
+ '514382464234321326889464182768467546703537516986049910576'
+ '551282076245490090389328944075868508455133942304583236903'
+ '222948165808559332123348274797826204144723168738177180919'
+ '29988125040402618412485836{1}')
+ most_max2 = ('{0}35953862697246314162905484746340871359614113505168999'
+ '31978349536063145215600570775211791172655337563430809179'
+ '07028764928468642653778928365536935093407075033972099821'
+ '15310256415249098018077865788815173701691026788460916647'
+ '38064458963316171186642466965495956524082894463374763543'
+ '61838599762500808052368249716736')
+ int_max = int(float_info.max)
+ self.assertEqual(nformat(int_max, '.'), most_max.format('', '8'))
+ self.assertEqual(nformat(int_max + 1, '.'), most_max.format('', '9'))
+ self.assertEqual(nformat(int_max * 2, '.'), most_max2.format(''))
+ self.assertEqual(nformat(0 - int_max, '.'), most_max.format('-', '8'))
+ self.assertEqual(nformat(-1 - int_max, '.'), most_max.format('-', '9'))
+ self.assertEqual(nformat(-2 * int_max, '.'), most_max2.format('-'))
diff --git a/tests/utils/os_utils.py b/tests/utils/os_utils.py
new file mode 100644
index 0000000000..a205d67431
--- /dev/null
+++ b/tests/utils/os_utils.py
@@ -0,0 +1,26 @@
+import os
+
+from django.utils import unittest
+from django.utils._os import safe_join
+
+
+class SafeJoinTests(unittest.TestCase):
+ def test_base_path_ends_with_sep(self):
+ drive, path = os.path.splitdrive(safe_join("/abc/", "abc"))
+ self.assertEqual(
+ path,
+ "{0}abc{0}abc".format(os.path.sep)
+ )
+
+ def test_root_path(self):
+ drive, path = os.path.splitdrive(safe_join("/", "path"))
+ self.assertEqual(
+ path,
+ "{0}path".format(os.path.sep),
+ )
+
+ drive, path = os.path.splitdrive(safe_join("/", ""))
+ self.assertEqual(
+ path,
+ os.path.sep,
+ )
diff --git a/tests/utils/regex_helper.py b/tests/utils/regex_helper.py
new file mode 100644
index 0000000000..41f4a4f85d
--- /dev/null
+++ b/tests/utils/regex_helper.py
@@ -0,0 +1,50 @@
+from __future__ import unicode_literals
+
+from django.utils import regex_helper
+from django.utils import unittest
+
+
+class NormalizeTests(unittest.TestCase):
+ def test_empty(self):
+ pattern = r""
+ expected = [('', [])]
+ result = regex_helper.normalize(pattern)
+ self.assertEqual(result, expected)
+
+ def test_escape(self):
+ pattern = r"\\\^\$\.\|\?\*\+\(\)\["
+ expected = [('\\^$.|?*+()[', [])]
+ result = regex_helper.normalize(pattern)
+ self.assertEqual(result, expected)
+
+ def test_group_positional(self):
+ pattern = r"(.*)-(.+)"
+ expected = [('%(_0)s-%(_1)s', ['_0', '_1'])]
+ result = regex_helper.normalize(pattern)
+ self.assertEqual(result, expected)
+
+ def test_group_ignored(self):
+ pattern = r"(?i)(?L)(?m)(?s)(?u)(?#)"
+ expected = [('', [])]
+ result = regex_helper.normalize(pattern)
+ self.assertEqual(result, expected)
+
+ def test_group_noncapturing(self):
+ pattern = r"(?:non-capturing)"
+ expected = [('non-capturing', [])]
+ result = regex_helper.normalize(pattern)
+ self.assertEqual(result, expected)
+
+ def test_group_named(self):
+ pattern = r"(?P<first_group_name>.*)-(?P<second_group_name>.*)"
+ expected = [('%(first_group_name)s-%(second_group_name)s',
+ ['first_group_name', 'second_group_name'])]
+ result = regex_helper.normalize(pattern)
+ self.assertEqual(result, expected)
+
+ def test_group_backreference(self):
+ pattern = r"(?P<first_group_name>.*)-(?P=first_group_name)"
+ expected = [('%(first_group_name)s-%(first_group_name)s',
+ ['first_group_name'])]
+ result = regex_helper.normalize(pattern)
+ self.assertEqual(result, expected)
diff --git a/tests/utils/simplelazyobject.py b/tests/utils/simplelazyobject.py
new file mode 100644
index 0000000000..3f81e8f608
--- /dev/null
+++ b/tests/utils/simplelazyobject.py
@@ -0,0 +1,123 @@
+from __future__ import unicode_literals
+
+import copy
+import pickle
+
+from django.test.utils import str_prefix
+from django.utils import six
+from django.utils.unittest import TestCase
+from django.utils.functional import SimpleLazyObject, empty
+
+
+class _ComplexObject(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __eq__(self, other):
+ return self.name == other.name
+
+ def __hash__(self):
+ return hash(self.name)
+
+ if six.PY3:
+ def __bytes__(self):
+ return ("I am _ComplexObject(%r)" % self.name).encode("utf-8")
+
+ def __str__(self):
+ return self.name
+
+ else:
+ def __str__(self):
+ return b"I am _ComplexObject(%r)" % str(self.name)
+
+ def __unicode__(self):
+ return self.name
+
+ def __repr__(self):
+ return "_ComplexObject(%r)" % self.name
+
+
+complex_object = lambda: _ComplexObject("joe")
+
+
+class TestUtilsSimpleLazyObject(TestCase):
+ """
+ Tests for SimpleLazyObject
+ """
+ # Note that concrete use cases for SimpleLazyObject are also found in the
+ # auth context processor tests (unless the implementation of that function
+ # is changed).
+
+ def test_equality(self):
+ self.assertEqual(complex_object(), SimpleLazyObject(complex_object))
+ self.assertEqual(SimpleLazyObject(complex_object), complex_object())
+
+ def test_hash(self):
+ # hash() equality would not be true for many objects, but it should be
+ # for _ComplexObject
+ self.assertEqual(hash(complex_object()),
+ hash(SimpleLazyObject(complex_object)))
+
+ def test_repr(self):
+ # For debugging, it will really confuse things if there is no clue that
+ # SimpleLazyObject is actually a proxy object. So we don't
+ # proxy __repr__
+ self.assertTrue("SimpleLazyObject" in repr(SimpleLazyObject(complex_object)))
+
+ def test_bytes(self):
+ self.assertEqual(b"I am _ComplexObject('joe')",
+ bytes(SimpleLazyObject(complex_object)))
+
+ def test_text(self):
+ self.assertEqual("joe", six.text_type(SimpleLazyObject(complex_object)))
+
+ def test_class(self):
+ # This is important for classes that use __class__ in things like
+ # equality tests.
+ self.assertEqual(_ComplexObject, SimpleLazyObject(complex_object).__class__)
+
+ def test_deepcopy(self):
+ # Check that we *can* do deep copy, and that it returns the right
+ # objects.
+
+ # First, for an unevaluated SimpleLazyObject
+ s = SimpleLazyObject(complex_object)
+ self.assertIs(s._wrapped, empty)
+ s2 = copy.deepcopy(s)
+ # something has gone wrong is s is evaluated
+ self.assertIs(s._wrapped, empty)
+ self.assertEqual(s2, complex_object())
+
+ # Second, for an evaluated SimpleLazyObject
+ name = s.name # evaluate
+ self.assertIsNot(s._wrapped, empty)
+ s3 = copy.deepcopy(s)
+ self.assertEqual(s3, complex_object())
+
+
+ def test_none(self):
+ i = [0]
+ def f():
+ i[0] += 1
+ return None
+
+ x = SimpleLazyObject(f)
+ self.assertEqual(str(x), "None")
+ self.assertEqual(i, [1])
+ self.assertEqual(str(x), "None")
+ self.assertEqual(i, [1])
+
+ def test_bool(self):
+ x = SimpleLazyObject(lambda: 3)
+ self.assertTrue(x)
+ x = SimpleLazyObject(lambda: 0)
+ self.assertFalse(x)
+
+ def test_pickle_complex(self):
+ # See ticket #16563
+ x = SimpleLazyObject(complex_object)
+ pickled = pickle.dumps(x)
+ unpickled = pickle.loads(pickled)
+ self.assertEqual(unpickled, x)
+ self.assertEqual(six.text_type(unpickled), six.text_type(x))
+ self.assertEqual(unpickled.name, x.name)
diff --git a/tests/utils/termcolors.py b/tests/utils/termcolors.py
new file mode 100644
index 0000000000..8cc28009cd
--- /dev/null
+++ b/tests/utils/termcolors.py
@@ -0,0 +1,157 @@
+from django.utils import unittest
+from django.utils.termcolors import (parse_color_setting, PALETTES,
+ DEFAULT_PALETTE, LIGHT_PALETTE, DARK_PALETTE, NOCOLOR_PALETTE, colorize)
+
+
+class TermColorTests(unittest.TestCase):
+
+ def test_empty_string(self):
+ self.assertEqual(parse_color_setting(''), PALETTES[DEFAULT_PALETTE])
+
+ def test_simple_palette(self):
+ self.assertEqual(parse_color_setting('light'), PALETTES[LIGHT_PALETTE])
+ self.assertEqual(parse_color_setting('dark'), PALETTES[DARK_PALETTE])
+ self.assertEqual(parse_color_setting('nocolor'), None)
+
+ def test_fg(self):
+ self.assertEqual(parse_color_setting('error=green'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+
+ def test_fg_bg(self):
+ self.assertEqual(parse_color_setting('error=green/blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue'}))
+
+ def test_fg_opts(self):
+ self.assertEqual(parse_color_setting('error=green,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+ self.assertEqual(parse_color_setting('error=green,bold,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink','bold')}))
+
+ def test_fg_bg_opts(self):
+ self.assertEqual(parse_color_setting('error=green/blue,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue', 'opts': ('blink',)}))
+ self.assertEqual(parse_color_setting('error=green/blue,bold,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue', 'opts': ('blink','bold')}))
+
+ def test_override_palette(self):
+ self.assertEqual(parse_color_setting('light;error=green'),
+ dict(PALETTES[LIGHT_PALETTE],
+ ERROR={'fg':'green'}))
+
+ def test_override_nocolor(self):
+ self.assertEqual(parse_color_setting('nocolor;error=green'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg': 'green'}))
+
+ def test_reverse_override(self):
+ self.assertEqual(parse_color_setting('error=green;light'), PALETTES[LIGHT_PALETTE])
+
+ def test_multiple_roles(self):
+ self.assertEqual(parse_color_setting('error=green;sql_field=blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'},
+ SQL_FIELD={'fg':'blue'}))
+
+ def test_override_with_multiple_roles(self):
+ self.assertEqual(parse_color_setting('light;error=green;sql_field=blue'),
+ dict(PALETTES[LIGHT_PALETTE],
+ ERROR={'fg':'green'},
+ SQL_FIELD={'fg':'blue'}))
+
+ def test_empty_definition(self):
+ self.assertEqual(parse_color_setting(';'), None)
+ self.assertEqual(parse_color_setting('light;'), PALETTES[LIGHT_PALETTE])
+ self.assertEqual(parse_color_setting(';;;'), None)
+
+ def test_empty_options(self):
+ self.assertEqual(parse_color_setting('error=green,'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEqual(parse_color_setting('error=green,,,'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEqual(parse_color_setting('error=green,,blink,,'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+
+ def test_bad_palette(self):
+ self.assertEqual(parse_color_setting('unknown'), None)
+
+ def test_bad_role(self):
+ self.assertEqual(parse_color_setting('unknown='), None)
+ self.assertEqual(parse_color_setting('unknown=green'), None)
+ self.assertEqual(parse_color_setting('unknown=green;sql_field=blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ SQL_FIELD={'fg':'blue'}))
+
+ def test_bad_color(self):
+ self.assertEqual(parse_color_setting('error='), None)
+ self.assertEqual(parse_color_setting('error=;sql_field=blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ SQL_FIELD={'fg':'blue'}))
+ self.assertEqual(parse_color_setting('error=unknown'), None)
+ self.assertEqual(parse_color_setting('error=unknown;sql_field=blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ SQL_FIELD={'fg':'blue'}))
+ self.assertEqual(parse_color_setting('error=green/unknown'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEqual(parse_color_setting('error=green/blue/something'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg': 'blue'}))
+ self.assertEqual(parse_color_setting('error=green/blue/something,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg': 'blue', 'opts': ('blink',)}))
+
+ def test_bad_option(self):
+ self.assertEqual(parse_color_setting('error=green,unknown'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEqual(parse_color_setting('error=green,unknown,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+
+ def test_role_case(self):
+ self.assertEqual(parse_color_setting('ERROR=green'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEqual(parse_color_setting('eRrOr=green'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+
+ def test_color_case(self):
+ self.assertEqual(parse_color_setting('error=GREEN'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEqual(parse_color_setting('error=GREEN/BLUE'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue'}))
+
+ self.assertEqual(parse_color_setting('error=gReEn'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEqual(parse_color_setting('error=gReEn/bLuE'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue'}))
+
+ def test_opts_case(self):
+ self.assertEqual(parse_color_setting('error=green,BLINK'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+
+ self.assertEqual(parse_color_setting('error=green,bLiNk'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+
+ def test_colorize_empty_text(self):
+ self.assertEqual(colorize(text=None), '\x1b[m\x1b[0m')
+ self.assertEqual(colorize(text=''), '\x1b[m\x1b[0m')
+
+ self.assertEqual(colorize(text=None, opts=('noreset')), '\x1b[m')
+ self.assertEqual(colorize(text='', opts=('noreset')), '\x1b[m')
diff --git a/tests/utils/test_module/__init__.py b/tests/utils/test_module/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/utils/test_module/__init__.py
diff --git a/tests/utils/test_module/bad_module.py b/tests/utils/test_module/bad_module.py
new file mode 100644
index 0000000000..cc0cd168ce
--- /dev/null
+++ b/tests/utils/test_module/bad_module.py
@@ -0,0 +1,3 @@
+import a_package_name_that_does_not_exist
+
+content = 'Bad Module' \ No newline at end of file
diff --git a/tests/utils/test_module/good_module.py b/tests/utils/test_module/good_module.py
new file mode 100644
index 0000000000..0ca689832a
--- /dev/null
+++ b/tests/utils/test_module/good_module.py
@@ -0,0 +1 @@
+content = 'Good Module' \ No newline at end of file
diff --git a/tests/utils/test_no_submodule.py b/tests/utils/test_no_submodule.py
new file mode 100644
index 0000000000..3c424ac788
--- /dev/null
+++ b/tests/utils/test_no_submodule.py
@@ -0,0 +1 @@
+# Used to test for modules which don't have submodules.
diff --git a/tests/utils/tests.py b/tests/utils/tests.py
new file mode 100644
index 0000000000..726df3d979
--- /dev/null
+++ b/tests/utils/tests.py
@@ -0,0 +1,33 @@
+"""
+Tests for django.utils.
+"""
+from __future__ import absolute_import
+
+from .archive import TestBzip2Tar, TestGzipTar, TestTar, TestZip
+from .baseconv import TestBaseConv
+from .checksums import TestUtilsChecksums
+from .crypto import TestUtilsCryptoMisc, TestUtilsCryptoPBKDF2
+from .datastructures import (DictWrapperTests, ImmutableListTests,
+ MergeDictTests, MultiValueDictTests, SortedDictTests)
+from .dateformat import DateFormatTests
+from .dateparse import DateParseTests
+from .datetime_safe import DatetimeTests
+from .decorators import DecoratorFromMiddlewareTests
+from .encoding import TestEncodingUtils
+from .feedgenerator import FeedgeneratorTest
+from .functional import FunctionalTestCase
+from .html import TestUtilsHtml
+from .http import TestUtilsHttp, ETagProcessingTests, HttpDateProcessingTests
+from .ipv6 import TestUtilsIPv6
+from .jslex import JsToCForGettextTest, JsTokensTest
+from .module_loading import (CustomLoader, DefaultLoader, EggLoader,
+ ModuleImportTestCase)
+from .numberformat import TestNumberFormat
+from .os_utils import SafeJoinTests
+from .regex_helper import NormalizeTests
+from .simplelazyobject import TestUtilsSimpleLazyObject
+from .termcolors import TermColorTests
+from .text import TestUtilsText
+from .timesince import TimesinceTests
+from .timezone import TimezoneTests
+from .tzinfo import TzinfoTests
diff --git a/tests/utils/text.py b/tests/utils/text.py
new file mode 100644
index 0000000000..c9dde6b1b3
--- /dev/null
+++ b/tests/utils/text.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import warnings
+
+from django.test import SimpleTestCase
+from django.utils import text
+
+class TestUtilsText(SimpleTestCase):
+
+ def test_truncate_chars(self):
+ truncator = text.Truncator(
+ 'The quick brown fox jumped over the lazy dog.'
+ )
+ self.assertEqual('The quick brown fox jumped over the lazy dog.',
+ truncator.chars(100)),
+ self.assertEqual('The quick brown fox ...',
+ truncator.chars(23)),
+ self.assertEqual('The quick brown fo.....',
+ truncator.chars(23, '.....')),
+
+ # Ensure that we normalize our unicode data first
+ nfc = text.Truncator('o\xfco\xfco\xfco\xfc')
+ nfd = text.Truncator('ou\u0308ou\u0308ou\u0308ou\u0308')
+ self.assertEqual('oüoüoüoü', nfc.chars(8))
+ self.assertEqual('oüoüoüoü', nfd.chars(8))
+ self.assertEqual('oü...', nfc.chars(5))
+ self.assertEqual('oü...', nfd.chars(5))
+
+ # Ensure the final length is calculated correctly when there are
+ # combining characters with no precomposed form, and that combining
+ # characters are not split up.
+ truncator = text.Truncator('-B\u030AB\u030A----8')
+ self.assertEqual('-B\u030A...', truncator.chars(5))
+ self.assertEqual('-B\u030AB\u030A-...', truncator.chars(7))
+ self.assertEqual('-B\u030AB\u030A----8', truncator.chars(8))
+
+ # Ensure the length of the end text is correctly calculated when it
+ # contains combining characters with no precomposed form.
+ truncator = text.Truncator('-----')
+ self.assertEqual('---B\u030A', truncator.chars(4, 'B\u030A'))
+ self.assertEqual('-----', truncator.chars(5, 'B\u030A'))
+
+ # Make a best effort to shorten to the desired length, but requesting
+ # a length shorter than the ellipsis shouldn't break
+ self.assertEqual('...', text.Truncator('asdf').chars(1))
+
+ def test_truncate_words(self):
+ truncator = text.Truncator('The quick brown fox jumped over the lazy '
+ 'dog.')
+ self.assertEqual('The quick brown fox jumped over the lazy dog.',
+ truncator.words(10))
+ self.assertEqual('The quick brown fox...', truncator.words(4))
+ self.assertEqual('The quick brown fox[snip]',
+ truncator.words(4, '[snip]'))
+
+ def test_truncate_html_words(self):
+ truncator = text.Truncator('<p id="par"><strong><em>The quick brown fox'
+ ' jumped over the lazy dog.</em></strong></p>')
+ self.assertEqual('<p id="par"><strong><em>The quick brown fox jumped over'
+ ' the lazy dog.</em></strong></p>', truncator.words(10, html=True))
+ self.assertEqual('<p id="par"><strong><em>The quick brown fox...</em>'
+ '</strong></p>', truncator.words(4, html=True))
+ self.assertEqual('<p id="par"><strong><em>The quick brown fox....</em>'
+ '</strong></p>', truncator.words(4, '....', html=True))
+ self.assertEqual('<p id="par"><strong><em>The quick brown fox</em>'
+ '</strong></p>', truncator.words(4, '', html=True))
+
+ # Test with new line inside tag
+ truncator = text.Truncator('<p>The quick <a href="xyz.html"\n'
+ 'id="mylink">brown fox</a> jumped over the lazy dog.</p>')
+ self.assertEqual('<p>The quick <a href="xyz.html"\n'
+ 'id="mylink">brown...</a></p>', truncator.words(3, '...', html=True))
+
+ # Test self-closing tags
+ truncator = text.Truncator('<br/>The <hr />quick brown fox jumped over'
+ ' the lazy dog.')
+ self.assertEqual('<br/>The <hr />quick brown...',
+ truncator.words(3, '...', html=True ))
+ truncator = text.Truncator('<br>The <hr/>quick <em>brown fox</em> '
+ 'jumped over the lazy dog.')
+ self.assertEqual('<br>The <hr/>quick <em>brown...</em>',
+ truncator.words(3, '...', html=True ))
+
+ def test_wrap(self):
+ digits = '1234 67 9'
+ self.assertEqual(text.wrap(digits, 100), '1234 67 9')
+ self.assertEqual(text.wrap(digits, 9), '1234 67 9')
+ self.assertEqual(text.wrap(digits, 8), '1234 67\n9')
+
+ self.assertEqual(text.wrap('short\na long line', 7),
+ 'short\na long\nline')
+
+ self.assertEqual(text.wrap('do-not-break-long-words please? ok', 8),
+ 'do-not-break-long-words\nplease?\nok')
+
+ long_word = 'l%sng' % ('o' * 20)
+ self.assertEqual(text.wrap(long_word, 20), long_word)
+ self.assertEqual(text.wrap('a %s word' % long_word, 10),
+ 'a\n%s\nword' % long_word)
+
+ def test_slugify(self):
+ items = (
+ ('Hello, World!', 'hello-world'),
+ ('spam & eggs', 'spam-eggs'),
+ )
+ for value, output in items:
+ self.assertEqual(text.slugify(value), output)
diff --git a/tests/utils/timesince.py b/tests/utils/timesince.py
new file mode 100644
index 0000000000..5e641a42c4
--- /dev/null
+++ b/tests/utils/timesince.py
@@ -0,0 +1,124 @@
+from __future__ import unicode_literals
+
+import datetime
+import unittest
+
+from django.utils.timesince import timesince, timeuntil
+from django.utils.tzinfo import LocalTimezone, FixedOffset
+
+class TimesinceTests(unittest.TestCase):
+
+ def setUp(self):
+ self.t = datetime.datetime(2007, 8, 14, 13, 46, 0)
+ self.onemicrosecond = datetime.timedelta(microseconds=1)
+ self.onesecond = datetime.timedelta(seconds=1)
+ self.oneminute = datetime.timedelta(minutes=1)
+ self.onehour = datetime.timedelta(hours=1)
+ self.oneday = datetime.timedelta(days=1)
+ self.oneweek = datetime.timedelta(days=7)
+ self.onemonth = datetime.timedelta(days=30)
+ self.oneyear = datetime.timedelta(days=365)
+
+ def test_equal_datetimes(self):
+ """ equal datetimes. """
+ self.assertEqual(timesince(self.t, self.t), '0 minutes')
+
+ def test_ignore_microseconds_and_seconds(self):
+ """ Microseconds and seconds are ignored. """
+ self.assertEqual(timesince(self.t, self.t+self.onemicrosecond),
+ '0 minutes')
+ self.assertEqual(timesince(self.t, self.t+self.onesecond),
+ '0 minutes')
+
+ def test_other_units(self):
+ """ Test other units. """
+ self.assertEqual(timesince(self.t, self.t+self.oneminute),
+ '1 minute')
+ self.assertEqual(timesince(self.t, self.t+self.onehour), '1 hour')
+ self.assertEqual(timesince(self.t, self.t+self.oneday), '1 day')
+ self.assertEqual(timesince(self.t, self.t+self.oneweek), '1 week')
+ self.assertEqual(timesince(self.t, self.t+self.onemonth),
+ '1 month')
+ self.assertEqual(timesince(self.t, self.t+self.oneyear), '1 year')
+
+ def test_multiple_units(self):
+ """ Test multiple units. """
+ self.assertEqual(timesince(self.t,
+ self.t+2*self.oneday+6*self.onehour), '2 days, 6 hours')
+ self.assertEqual(timesince(self.t,
+ self.t+2*self.oneweek+2*self.oneday), '2 weeks, 2 days')
+
+ def test_display_first_unit(self):
+ """
+ If the two differing units aren't adjacent, only the first unit is
+ displayed.
+ """
+ self.assertEqual(timesince(self.t,
+ self.t+2*self.oneweek+3*self.onehour+4*self.oneminute),
+ '2 weeks')
+
+ self.assertEqual(timesince(self.t,
+ self.t+4*self.oneday+5*self.oneminute), '4 days')
+
+ def test_display_second_before_first(self):
+ """
+ When the second date occurs before the first, we should always
+ get 0 minutes.
+ """
+ self.assertEqual(timesince(self.t, self.t-self.onemicrosecond),
+ '0 minutes')
+ self.assertEqual(timesince(self.t, self.t-self.onesecond),
+ '0 minutes')
+ self.assertEqual(timesince(self.t, self.t-self.oneminute),
+ '0 minutes')
+ self.assertEqual(timesince(self.t, self.t-self.onehour),
+ '0 minutes')
+ self.assertEqual(timesince(self.t, self.t-self.oneday),
+ '0 minutes')
+ self.assertEqual(timesince(self.t, self.t-self.oneweek),
+ '0 minutes')
+ self.assertEqual(timesince(self.t, self.t-self.onemonth),
+ '0 minutes')
+ self.assertEqual(timesince(self.t, self.t-self.oneyear),
+ '0 minutes')
+ self.assertEqual(timesince(self.t,
+ self.t-2*self.oneday-6*self.onehour), '0 minutes')
+ self.assertEqual(timesince(self.t,
+ self.t-2*self.oneweek-2*self.oneday), '0 minutes')
+ self.assertEqual(timesince(self.t,
+ self.t-2*self.oneweek-3*self.onehour-4*self.oneminute),
+ '0 minutes')
+ self.assertEqual(timesince(self.t,
+ self.t-4*self.oneday-5*self.oneminute), '0 minutes')
+
+ def test_different_timezones(self):
+ """ When using two different timezones. """
+ now = datetime.datetime.now()
+ now_tz = datetime.datetime.now(LocalTimezone(now))
+ now_tz_i = datetime.datetime.now(FixedOffset((3 * 60) + 15))
+
+ self.assertEqual(timesince(now), '0 minutes')
+ self.assertEqual(timesince(now_tz), '0 minutes')
+ self.assertEqual(timeuntil(now_tz, now_tz_i), '0 minutes')
+
+ def test_date_objects(self):
+ """ Both timesince and timeuntil should work on date objects (#17937). """
+ today = datetime.date.today()
+ self.assertEqual(timesince(today + self.oneday), '0 minutes')
+ self.assertEqual(timeuntil(today - self.oneday), '0 minutes')
+
+ def test_both_date_objects(self):
+ """ Timesince should work with both date objects (#9672) """
+ today = datetime.date.today()
+ self.assertEqual(timeuntil(today + self.oneday, today), '1 day')
+ self.assertEqual(timeuntil(today - self.oneday, today), '0 minutes')
+ self.assertEqual(timeuntil(today + self.oneweek, today), '1 week')
+
+ def test_naive_datetime_with_tzinfo_attribute(self):
+ class naive(datetime.tzinfo):
+ def utcoffset(self, dt):
+ return None
+ future = datetime.datetime(2080, 1, 1, tzinfo=naive())
+ self.assertEqual(timesince(future), '0 minutes')
+ past = datetime.datetime(1980, 1, 1, tzinfo=naive())
+ self.assertEqual(timeuntil(past), '0 minutes')
diff --git a/tests/utils/timezone.py b/tests/utils/timezone.py
new file mode 100644
index 0000000000..650198e8ff
--- /dev/null
+++ b/tests/utils/timezone.py
@@ -0,0 +1,63 @@
+import copy
+import datetime
+import pickle
+from django.test.utils import override_settings
+from django.utils import timezone
+from django.utils.tzinfo import FixedOffset
+from django.utils import unittest
+
+
+EAT = FixedOffset(180) # Africa/Nairobi
+ICT = FixedOffset(420) # Asia/Bangkok
+
+
+class TimezoneTests(unittest.TestCase):
+
+ def test_localtime(self):
+ now = datetime.datetime.utcnow().replace(tzinfo=timezone.utc)
+ local_tz = timezone.LocalTimezone()
+ local_now = timezone.localtime(now, local_tz)
+ self.assertEqual(local_now.tzinfo, local_tz)
+
+ def test_now(self):
+ with override_settings(USE_TZ=True):
+ self.assertTrue(timezone.is_aware(timezone.now()))
+ with override_settings(USE_TZ=False):
+ self.assertTrue(timezone.is_naive(timezone.now()))
+
+ def test_override(self):
+ default = timezone.get_default_timezone()
+ try:
+ timezone.activate(ICT)
+
+ with timezone.override(EAT):
+ self.assertIs(EAT, timezone.get_current_timezone())
+ self.assertIs(ICT, timezone.get_current_timezone())
+
+ with timezone.override(None):
+ self.assertIs(default, timezone.get_current_timezone())
+ self.assertIs(ICT, timezone.get_current_timezone())
+
+ timezone.deactivate()
+
+ with timezone.override(EAT):
+ self.assertIs(EAT, timezone.get_current_timezone())
+ self.assertIs(default, timezone.get_current_timezone())
+
+ with timezone.override(None):
+ self.assertIs(default, timezone.get_current_timezone())
+ self.assertIs(default, timezone.get_current_timezone())
+ finally:
+ timezone.deactivate()
+
+ def test_copy(self):
+ self.assertIsInstance(copy.copy(timezone.UTC()), timezone.UTC)
+ self.assertIsInstance(copy.copy(timezone.LocalTimezone()), timezone.LocalTimezone)
+
+ def test_deepcopy(self):
+ self.assertIsInstance(copy.deepcopy(timezone.UTC()), timezone.UTC)
+ self.assertIsInstance(copy.deepcopy(timezone.LocalTimezone()), timezone.LocalTimezone)
+
+ def test_pickling_unpickling(self):
+ self.assertIsInstance(pickle.loads(pickle.dumps(timezone.UTC())), timezone.UTC)
+ self.assertIsInstance(pickle.loads(pickle.dumps(timezone.LocalTimezone())), timezone.LocalTimezone)
diff --git a/tests/utils/tzinfo.py b/tests/utils/tzinfo.py
new file mode 100644
index 0000000000..cec92652cd
--- /dev/null
+++ b/tests/utils/tzinfo.py
@@ -0,0 +1,79 @@
+import copy
+import datetime
+import os
+import pickle
+import time
+from django.utils.tzinfo import FixedOffset, LocalTimezone
+from django.utils import unittest
+
+class TzinfoTests(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.old_TZ = os.environ.get('TZ')
+ os.environ['TZ'] = 'US/Eastern'
+
+ try:
+ # Check if a timezone has been set
+ time.tzset()
+ cls.tz_tests = True
+ except AttributeError:
+ # No timezone available. Don't run the tests that require a TZ
+ cls.tz_tests = False
+
+ @classmethod
+ def tearDownClass(cls):
+ if cls.old_TZ is None:
+ del os.environ['TZ']
+ else:
+ os.environ['TZ'] = cls.old_TZ
+
+ # Cleanup - force re-evaluation of TZ environment variable.
+ if cls.tz_tests:
+ time.tzset()
+
+ def test_fixedoffset(self):
+ self.assertEqual(repr(FixedOffset(0)), '+0000')
+ self.assertEqual(repr(FixedOffset(60)), '+0100')
+ self.assertEqual(repr(FixedOffset(-60)), '-0100')
+ self.assertEqual(repr(FixedOffset(280)), '+0440')
+ self.assertEqual(repr(FixedOffset(-280)), '-0440')
+ self.assertEqual(repr(FixedOffset(-78.4)), '-0118')
+ self.assertEqual(repr(FixedOffset(78.4)), '+0118')
+ self.assertEqual(repr(FixedOffset(-5.5*60)), '-0530')
+ self.assertEqual(repr(FixedOffset(5.5*60)), '+0530')
+ self.assertEqual(repr(FixedOffset(-.5*60)), '-0030')
+ self.assertEqual(repr(FixedOffset(.5*60)), '+0030')
+
+ def test_16899(self):
+ if not self.tz_tests:
+ return
+ ts = 1289106000
+ # Midnight at the end of DST in US/Eastern: 2010-11-07T05:00:00Z
+ dt = datetime.datetime.utcfromtimestamp(ts)
+ # US/Eastern -- we force its representation to "EST"
+ tz = LocalTimezone(dt + datetime.timedelta(days=1))
+ self.assertEqual(
+ repr(datetime.datetime.fromtimestamp(ts - 3600, tz)),
+ 'datetime.datetime(2010, 11, 7, 0, 0, tzinfo=EST)')
+ self.assertEqual(
+ repr(datetime.datetime.fromtimestamp(ts, tz)),
+ 'datetime.datetime(2010, 11, 7, 1, 0, tzinfo=EST)')
+ self.assertEqual(
+ repr(datetime.datetime.fromtimestamp(ts + 3600, tz)),
+ 'datetime.datetime(2010, 11, 7, 1, 0, tzinfo=EST)')
+
+ def test_copy(self):
+ now = datetime.datetime.now()
+ self.assertIsInstance(copy.copy(FixedOffset(90)), FixedOffset)
+ self.assertIsInstance(copy.copy(LocalTimezone(now)), LocalTimezone)
+
+ def test_deepcopy(self):
+ now = datetime.datetime.now()
+ self.assertIsInstance(copy.deepcopy(FixedOffset(90)), FixedOffset)
+ self.assertIsInstance(copy.deepcopy(LocalTimezone(now)), LocalTimezone)
+
+ def test_pickling_unpickling(self):
+ now = datetime.datetime.now()
+ self.assertIsInstance(pickle.loads(pickle.dumps(FixedOffset(90))), FixedOffset)
+ self.assertIsInstance(pickle.loads(pickle.dumps(LocalTimezone(now))), LocalTimezone)