1. """
    
  2. Tests for stuff in django.utils.datastructures.
    
  3. """
    
  4. import collections.abc
    
  5. import copy
    
  6. import pickle
    
  7. 
    
  8. from django.test import SimpleTestCase
    
  9. from django.utils.datastructures import (
    
  10.     CaseInsensitiveMapping,
    
  11.     DictWrapper,
    
  12.     ImmutableList,
    
  13.     MultiValueDict,
    
  14.     MultiValueDictKeyError,
    
  15.     OrderedSet,
    
  16. )
    
  17. 
    
  18. 
    
  19. class OrderedSetTests(SimpleTestCase):
    
  20.     def test_init_with_iterable(self):
    
  21.         s = OrderedSet([1, 2, 3])
    
  22.         self.assertEqual(list(s.dict.keys()), [1, 2, 3])
    
  23. 
    
  24.     def test_remove(self):
    
  25.         s = OrderedSet()
    
  26.         self.assertEqual(len(s), 0)
    
  27.         s.add(1)
    
  28.         s.add(2)
    
  29.         s.remove(2)
    
  30.         self.assertEqual(len(s), 1)
    
  31.         self.assertNotIn(2, s)
    
  32. 
    
  33.     def test_discard(self):
    
  34.         s = OrderedSet()
    
  35.         self.assertEqual(len(s), 0)
    
  36.         s.add(1)
    
  37.         s.discard(2)
    
  38.         self.assertEqual(len(s), 1)
    
  39. 
    
  40.     def test_reversed(self):
    
  41.         s = reversed(OrderedSet([1, 2, 3]))
    
  42.         self.assertIsInstance(s, collections.abc.Iterator)
    
  43.         self.assertEqual(list(s), [3, 2, 1])
    
  44. 
    
  45.     def test_contains(self):
    
  46.         s = OrderedSet()
    
  47.         self.assertEqual(len(s), 0)
    
  48.         s.add(1)
    
  49.         self.assertIn(1, s)
    
  50. 
    
  51.     def test_bool(self):
    
  52.         # Refs #23664
    
  53.         s = OrderedSet()
    
  54.         self.assertFalse(s)
    
  55.         s.add(1)
    
  56.         self.assertTrue(s)
    
  57. 
    
  58.     def test_len(self):
    
  59.         s = OrderedSet()
    
  60.         self.assertEqual(len(s), 0)
    
  61.         s.add(1)
    
  62.         s.add(2)
    
  63.         s.add(2)
    
  64.         self.assertEqual(len(s), 2)
    
  65. 
    
  66.     def test_repr(self):
    
  67.         self.assertEqual(repr(OrderedSet()), "OrderedSet()")
    
  68.         self.assertEqual(repr(OrderedSet([2, 3, 2, 1])), "OrderedSet([2, 3, 1])")
    
  69. 
    
  70. 
    
  71. class MultiValueDictTests(SimpleTestCase):
    
  72.     def test_repr(self):
    
  73.         d = MultiValueDict({"key": "value"})
    
  74.         self.assertEqual(repr(d), "<MultiValueDict: {'key': 'value'}>")
    
  75. 
    
  76.     def test_multivaluedict(self):
    
  77.         d = MultiValueDict(
    
  78.             {"name": ["Adrian", "Simon"], "position": ["Developer"], "empty": []}
    
  79.         )
    
  80.         self.assertEqual(d["name"], "Simon")
    
  81.         self.assertEqual(d.get("name"), "Simon")
    
  82.         self.assertEqual(d.getlist("name"), ["Adrian", "Simon"])
    
  83.         self.assertEqual(
    
  84.             list(d.items()),
    
  85.             [("name", "Simon"), ("position", "Developer"), ("empty", [])],
    
  86.         )
    
  87.         self.assertEqual(
    
  88.             list(d.lists()),
    
  89.             [("name", ["Adrian", "Simon"]), ("position", ["Developer"]), ("empty", [])],
    
  90.         )
    
  91.         with self.assertRaisesMessage(MultiValueDictKeyError, "'lastname'"):
    
  92.             d.__getitem__("lastname")
    
  93.         self.assertIsNone(d.get("empty"))
    
  94.         self.assertEqual(d.get("empty", "nonexistent"), "nonexistent")
    
  95.         self.assertIsNone(d.get("lastname"))
    
  96.         self.assertEqual(d.get("lastname", "nonexistent"), "nonexistent")
    
  97.         self.assertEqual(d.getlist("lastname"), [])
    
  98.         self.assertEqual(
    
  99.             d.getlist("doesnotexist", ["Adrian", "Simon"]), ["Adrian", "Simon"]
    
  100.         )
    
  101.         d.setlist("lastname", ["Holovaty", "Willison"])
    
  102.         self.assertEqual(d.getlist("lastname"), ["Holovaty", "Willison"])
    
  103.         self.assertEqual(list(d.values()), ["Simon", "Developer", [], "Willison"])
    
  104. 
    
  105.     def test_appendlist(self):
    
  106.         d = MultiValueDict()
    
  107.         d.appendlist("name", "Adrian")
    
  108.         d.appendlist("name", "Simon")
    
  109.         self.assertEqual(d.getlist("name"), ["Adrian", "Simon"])
    
  110. 
    
  111.     def test_copy(self):
    
  112.         for copy_func in [copy.copy, lambda d: d.copy()]:
    
  113.             with self.subTest(copy_func):
    
  114.                 d1 = MultiValueDict({"developers": ["Carl", "Fred"]})
    
  115.                 self.assertEqual(d1["developers"], "Fred")
    
  116.                 d2 = copy_func(d1)
    
  117.                 d2.update({"developers": "Groucho"})
    
  118.                 self.assertEqual(d2["developers"], "Groucho")
    
  119.                 self.assertEqual(d1["developers"], "Fred")
    
  120. 
    
  121.                 d1 = MultiValueDict({"key": [[]]})
    
  122.                 self.assertEqual(d1["key"], [])
    
  123.                 d2 = copy_func(d1)
    
  124.                 d2["key"].append("Penguin")
    
  125.                 self.assertEqual(d1["key"], ["Penguin"])
    
  126.                 self.assertEqual(d2["key"], ["Penguin"])
    
  127. 
    
  128.     def test_deepcopy(self):
    
  129.         d1 = MultiValueDict({"a": [[123]]})
    
  130.         d2 = copy.copy(d1)
    
  131.         d3 = copy.deepcopy(d1)
    
  132.         self.assertIs(d1["a"], d2["a"])
    
  133.         self.assertIsNot(d1["a"], d3["a"])
    
  134. 
    
  135.     def test_pickle(self):
    
  136.         x = MultiValueDict({"a": ["1", "2"], "b": ["3"]})
    
  137.         self.assertEqual(x, pickle.loads(pickle.dumps(x)))
    
  138. 
    
  139.     def test_dict_translation(self):
    
  140.         mvd = MultiValueDict(
    
  141.             {
    
  142.                 "devs": ["Bob", "Joe"],
    
  143.                 "pm": ["Rory"],
    
  144.             }
    
  145.         )
    
  146.         d = mvd.dict()
    
  147.         self.assertEqual(list(d), list(mvd))
    
  148.         for key in mvd:
    
  149.             self.assertEqual(d[key], mvd[key])
    
  150. 
    
  151.         self.assertEqual({}, MultiValueDict().dict())
    
  152. 
    
  153.     def test_getlist_doesnt_mutate(self):
    
  154.         x = MultiValueDict({"a": ["1", "2"], "b": ["3"]})
    
  155.         values = x.getlist("a")
    
  156.         values += x.getlist("b")
    
  157.         self.assertEqual(x.getlist("a"), ["1", "2"])
    
  158. 
    
  159.     def test_internal_getlist_does_mutate(self):
    
  160.         x = MultiValueDict({"a": ["1", "2"], "b": ["3"]})
    
  161.         values = x._getlist("a")
    
  162.         values += x._getlist("b")
    
  163.         self.assertEqual(x._getlist("a"), ["1", "2", "3"])
    
  164. 
    
  165.     def test_getlist_default(self):
    
  166.         x = MultiValueDict({"a": [1]})
    
  167.         MISSING = object()
    
  168.         values = x.getlist("b", default=MISSING)
    
  169.         self.assertIs(values, MISSING)
    
  170. 
    
  171.     def test_getlist_none_empty_values(self):
    
  172.         x = MultiValueDict({"a": None, "b": []})
    
  173.         self.assertIsNone(x.getlist("a"))
    
  174.         self.assertEqual(x.getlist("b"), [])
    
  175. 
    
  176.     def test_setitem(self):
    
  177.         x = MultiValueDict({"a": [1, 2]})
    
  178.         x["a"] = 3
    
  179.         self.assertEqual(list(x.lists()), [("a", [3])])
    
  180. 
    
  181.     def test_setdefault(self):
    
  182.         x = MultiValueDict({"a": [1, 2]})
    
  183.         a = x.setdefault("a", 3)
    
  184.         b = x.setdefault("b", 3)
    
  185.         self.assertEqual(a, 2)
    
  186.         self.assertEqual(b, 3)
    
  187.         self.assertEqual(list(x.lists()), [("a", [1, 2]), ("b", [3])])
    
  188. 
    
  189.     def test_update_too_many_args(self):
    
  190.         x = MultiValueDict({"a": []})
    
  191.         msg = "update expected at most 1 argument, got 2"
    
  192.         with self.assertRaisesMessage(TypeError, msg):
    
  193.             x.update(1, 2)
    
  194. 
    
  195.     def test_update_no_args(self):
    
  196.         x = MultiValueDict({"a": []})
    
  197.         x.update()
    
  198.         self.assertEqual(list(x.lists()), [("a", [])])
    
  199. 
    
  200.     def test_update_dict_arg(self):
    
  201.         x = MultiValueDict({"a": [1], "b": [2], "c": [3]})
    
  202.         x.update({"a": 4, "b": 5})
    
  203.         self.assertEqual(list(x.lists()), [("a", [1, 4]), ("b", [2, 5]), ("c", [3])])
    
  204. 
    
  205.     def test_update_multivaluedict_arg(self):
    
  206.         x = MultiValueDict({"a": [1], "b": [2], "c": [3]})
    
  207.         x.update(MultiValueDict({"a": [4], "b": [5]}))
    
  208.         self.assertEqual(list(x.lists()), [("a", [1, 4]), ("b", [2, 5]), ("c", [3])])
    
  209. 
    
  210.     def test_update_kwargs(self):
    
  211.         x = MultiValueDict({"a": [1], "b": [2], "c": [3]})
    
  212.         x.update(a=4, b=5)
    
  213.         self.assertEqual(list(x.lists()), [("a", [1, 4]), ("b", [2, 5]), ("c", [3])])
    
  214. 
    
  215.     def test_update_with_empty_iterable(self):
    
  216.         for value in ["", b"", (), [], set(), {}]:
    
  217.             d = MultiValueDict()
    
  218.             d.update(value)
    
  219.             self.assertEqual(d, MultiValueDict())
    
  220. 
    
  221.     def test_update_with_iterable_of_pairs(self):
    
  222.         for value in [(("a", 1),), [("a", 1)], {("a", 1)}]:
    
  223.             d = MultiValueDict()
    
  224.             d.update(value)
    
  225.             self.assertEqual(d, MultiValueDict({"a": [1]}))
    
  226. 
    
  227.     def test_update_raises_correct_exceptions(self):
    
  228.         # MultiValueDict.update() raises equivalent exceptions to
    
  229.         # dict.update().
    
  230.         # Non-iterable values raise TypeError.
    
  231.         for value in [None, True, False, 123, 123.45]:
    
  232.             with self.subTest(value), self.assertRaises(TypeError):
    
  233.                 MultiValueDict().update(value)
    
  234.         # Iterables of objects that cannot be unpacked raise TypeError.
    
  235.         for value in [b"123", b"abc", (1, 2, 3), [1, 2, 3], {1, 2, 3}]:
    
  236.             with self.subTest(value), self.assertRaises(TypeError):
    
  237.                 MultiValueDict().update(value)
    
  238.         # Iterables of unpackable objects with incorrect number of items raise
    
  239.         # ValueError.
    
  240.         for value in ["123", "abc", ("a", "b", "c"), ["a", "b", "c"], {"a", "b", "c"}]:
    
  241.             with self.subTest(value), self.assertRaises(ValueError):
    
  242.                 MultiValueDict().update(value)
    
  243. 
    
  244. 
    
  245. class ImmutableListTests(SimpleTestCase):
    
  246.     def test_sort(self):
    
  247.         d = ImmutableList(range(10))
    
  248. 
    
  249.         # AttributeError: ImmutableList object is immutable.
    
  250.         with self.assertRaisesMessage(
    
  251.             AttributeError, "ImmutableList object is immutable."
    
  252.         ):
    
  253.             d.sort()
    
  254. 
    
  255.         self.assertEqual(repr(d), "(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)")
    
  256. 
    
  257.     def test_custom_warning(self):
    
  258.         d = ImmutableList(range(10), warning="Object is immutable!")
    
  259. 
    
  260.         self.assertEqual(d[1], 1)
    
  261. 
    
  262.         # AttributeError: Object is immutable!
    
  263.         with self.assertRaisesMessage(AttributeError, "Object is immutable!"):
    
  264.             d.__setitem__(1, "test")
    
  265. 
    
  266. 
    
  267. class DictWrapperTests(SimpleTestCase):
    
  268.     def test_dictwrapper(self):
    
  269.         def f(x):
    
  270.             return "*%s" % x
    
  271. 
    
  272.         d = DictWrapper({"a": "a"}, f, "xx_")
    
  273.         self.assertEqual(
    
  274.             "Normal: %(a)s. Modified: %(xx_a)s" % d, "Normal: a. Modified: *a"
    
  275.         )
    
  276. 
    
  277. 
    
  278. class CaseInsensitiveMappingTests(SimpleTestCase):
    
  279.     def setUp(self):
    
  280.         self.dict1 = CaseInsensitiveMapping(
    
  281.             {
    
  282.                 "Accept": "application/json",
    
  283.                 "content-type": "text/html",
    
  284.             }
    
  285.         )
    
  286. 
    
  287.     def test_create_with_invalid_values(self):
    
  288.         msg = "dictionary update sequence element #1 has length 4; 2 is required"
    
  289.         with self.assertRaisesMessage(ValueError, msg):
    
  290.             CaseInsensitiveMapping([("Key1", "Val1"), "Key2"])
    
  291. 
    
  292.     def test_create_with_invalid_key(self):
    
  293.         msg = "Element key 1 invalid, only strings are allowed"
    
  294.         with self.assertRaisesMessage(ValueError, msg):
    
  295.             CaseInsensitiveMapping([(1, "2")])
    
  296. 
    
  297.     def test_list(self):
    
  298.         self.assertEqual(list(self.dict1), ["Accept", "content-type"])
    
  299. 
    
  300.     def test_dict(self):
    
  301.         self.assertEqual(
    
  302.             dict(self.dict1),
    
  303.             {"Accept": "application/json", "content-type": "text/html"},
    
  304.         )
    
  305. 
    
  306.     def test_repr(self):
    
  307.         dict1 = CaseInsensitiveMapping({"Accept": "application/json"})
    
  308.         dict2 = CaseInsensitiveMapping({"content-type": "text/html"})
    
  309.         self.assertEqual(repr(dict1), repr({"Accept": "application/json"}))
    
  310.         self.assertEqual(repr(dict2), repr({"content-type": "text/html"}))
    
  311. 
    
  312.     def test_str(self):
    
  313.         dict1 = CaseInsensitiveMapping({"Accept": "application/json"})
    
  314.         dict2 = CaseInsensitiveMapping({"content-type": "text/html"})
    
  315.         self.assertEqual(str(dict1), str({"Accept": "application/json"}))
    
  316.         self.assertEqual(str(dict2), str({"content-type": "text/html"}))
    
  317. 
    
  318.     def test_equal(self):
    
  319.         self.assertEqual(
    
  320.             self.dict1, {"Accept": "application/json", "content-type": "text/html"}
    
  321.         )
    
  322.         self.assertNotEqual(
    
  323.             self.dict1, {"accept": "application/jso", "Content-Type": "text/html"}
    
  324.         )
    
  325.         self.assertNotEqual(self.dict1, "string")
    
  326. 
    
  327.     def test_items(self):
    
  328.         other = {"Accept": "application/json", "content-type": "text/html"}
    
  329.         self.assertEqual(sorted(self.dict1.items()), sorted(other.items()))
    
  330. 
    
  331.     def test_copy(self):
    
  332.         copy = self.dict1.copy()
    
  333.         self.assertIs(copy, self.dict1)
    
  334.         self.assertEqual(copy, self.dict1)
    
  335. 
    
  336.     def test_getitem(self):
    
  337.         self.assertEqual(self.dict1["Accept"], "application/json")
    
  338.         self.assertEqual(self.dict1["accept"], "application/json")
    
  339.         self.assertEqual(self.dict1["aCCept"], "application/json")
    
  340.         self.assertEqual(self.dict1["content-type"], "text/html")
    
  341.         self.assertEqual(self.dict1["Content-Type"], "text/html")
    
  342.         self.assertEqual(self.dict1["Content-type"], "text/html")
    
  343. 
    
  344.     def test_in(self):
    
  345.         self.assertIn("Accept", self.dict1)
    
  346.         self.assertIn("accept", self.dict1)
    
  347.         self.assertIn("aCCept", self.dict1)
    
  348.         self.assertIn("content-type", self.dict1)
    
  349.         self.assertIn("Content-Type", self.dict1)
    
  350. 
    
  351.     def test_del(self):
    
  352.         self.assertIn("Accept", self.dict1)
    
  353.         msg = "'CaseInsensitiveMapping' object does not support item deletion"
    
  354.         with self.assertRaisesMessage(TypeError, msg):
    
  355.             del self.dict1["Accept"]
    
  356.         self.assertIn("Accept", self.dict1)
    
  357. 
    
  358.     def test_set(self):
    
  359.         self.assertEqual(len(self.dict1), 2)
    
  360.         msg = "'CaseInsensitiveMapping' object does not support item assignment"
    
  361.         with self.assertRaisesMessage(TypeError, msg):
    
  362.             self.dict1["New Key"] = 1
    
  363.         self.assertEqual(len(self.dict1), 2)