Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increased test coverage of django.utils.datastructures to 100%. #13494

Merged
merged 4 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 9 additions & 13 deletions django/utils/datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,15 @@ def update(self, *args, **kwargs):
if len(args) > 1:
raise TypeError("update expected at most 1 argument, got %d" % len(args))
if args:
other_dict = args[0]
if isinstance(other_dict, MultiValueDict):
for key, value_list in other_dict.lists():
arg = args[0]
if isinstance(arg, MultiValueDict):
for key, value_list in arg.lists():
self.setlistdefault(key).extend(value_list)
else:
try:
for key, value in other_dict.items():
self.setlistdefault(key).append(value)
except TypeError:
raise ValueError("MultiValueDict.update() takes either a MultiValueDict or dictionary")
if isinstance(arg, Mapping):
arg = arg.items()
for key, value in arg:
self.setlistdefault(key).append(value)
for key, value in kwargs.items():
self.setlistdefault(key).append(value)

Expand All @@ -230,11 +229,8 @@ def __new__(cls, *args, warning='ImmutableList object is immutable.', **kwargs):
self.warning = warning
return self

def complain(self, *wargs, **kwargs):
if isinstance(self.warning, Exception):
raise self.warning
else:
raise AttributeError(self.warning)
def complain(self, *args, **kwargs):
raise AttributeError(self.warning)

# All list mutation functions complain.
__delitem__ = complain
Expand Down
157 changes: 133 additions & 24 deletions tests/utils_tests/test_datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import copy
import pickle

from django.test import SimpleTestCase
from django.utils.datastructures import (
Expand All @@ -13,6 +14,32 @@

class OrderedSetTests(SimpleTestCase):

def test_init_with_iterable(self):
s = OrderedSet([1, 2, 3])
self.assertEqual(list(s.dict.keys()), [1, 2, 3])

def test_remove(self):
s = OrderedSet()
self.assertEqual(len(s), 0)
s.add(1)
s.add(2)
s.remove(2)
self.assertEqual(len(s), 1)
self.assertNotIn(2, s)

def test_discard(self):
s = OrderedSet()
self.assertEqual(len(s), 0)
s.add(1)
s.discard(2)
self.assertEqual(len(s), 1)

def test_contains(self):
s = OrderedSet()
self.assertEqual(len(s), 0)
s.add(1)
self.assertIn(1, s)

def test_bool(self):
# Refs #23664
s = OrderedSet()
Expand All @@ -31,28 +58,34 @@ def test_len(self):

class MultiValueDictTests(SimpleTestCase):

def test_repr(self):
d = MultiValueDict({'key': 'value'})
self.assertEqual(repr(d), "<MultiValueDict: {'key': 'value'}>")

def test_multivaluedict(self):
d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer'], 'empty': []})
self.assertEqual(d['name'], 'Simon')
self.assertEqual(d.get('name'), 'Simon')
self.assertEqual(d.getlist('name'), ['Adrian', 'Simon'])
self.assertEqual(
sorted(d.items()),
[('name', 'Simon'), ('position', 'Developer')]
list(d.items()),
[('name', 'Simon'), ('position', 'Developer'), ('empty', [])]
)
self.assertEqual(
sorted(d.lists()),
[('name', ['Adrian', 'Simon']), ('position', ['Developer'])]
list(d.lists()),
[('name', ['Adrian', 'Simon']), ('position', ['Developer']), ('empty', [])]
)
with self.assertRaisesMessage(MultiValueDictKeyError, "'lastname'"):
d.__getitem__('lastname')
self.assertIsNone(d.get('empty'))
self.assertEqual(d.get('empty', 'nonexistent'), 'nonexistent')
self.assertIsNone(d.get('lastname'))
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(d.values()), ['Developer', 'Simon', 'Willison'])
self.assertEqual(list(d.values()), ['Simon', 'Developer', [], 'Willison'])

def test_appendlist(self):
d = MultiValueDict()
Expand All @@ -62,31 +95,39 @@ def test_appendlist(self):

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"])
with self.subTest(copy_func):
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_deepcopy(self):
d1 = MultiValueDict({'a': [[123]]})
d2 = copy.copy(d1)
d3 = copy.deepcopy(d1)
self.assertIs(d1['a'], d2['a'])
self.assertIsNot(d1['a'], d3['a'])

def test_pickle(self):
x = MultiValueDict({'a': ['1', '2'], 'b': ['3']})
self.assertEqual(x, pickle.loads(pickle.dumps(x)))

def test_dict_translation(self):
mvd = MultiValueDict({
'devs': ['Bob', 'Joe'],
'pm': ['Rory'],
})
d = mvd.dict()
self.assertEqual(sorted(d), sorted(mvd))
self.assertEqual(list(d), list(mvd))
for key in mvd:
self.assertEqual(d[key], mvd[key])

Expand Down Expand Up @@ -115,6 +156,74 @@ def test_getlist_none_empty_values(self):
self.assertIsNone(x.getlist('a'))
self.assertEqual(x.getlist('b'), [])

def test_setitem(self):
x = MultiValueDict({'a': [1, 2]})
x['a'] = 3
self.assertEqual(list(x.lists()), [('a', [3])])
felixxm marked this conversation as resolved.
Show resolved Hide resolved

def test_setdefault(self):
x = MultiValueDict({'a': [1, 2]})
a = x.setdefault('a', 3)
b = x.setdefault('b', 3)
self.assertEqual(a, 2)
self.assertEqual(b, 3)
self.assertEqual(list(x.lists()), [('a', [1, 2]), ('b', [3])])

def test_update_too_many_args(self):
x = MultiValueDict({'a': []})
msg = 'update expected at most 1 argument, got 2'
with self.assertRaisesMessage(TypeError, msg):
x.update(1, 2)

def test_update_no_args(self):
x = MultiValueDict({'a': []})
x.update()
self.assertEqual(list(x.lists()), [('a', [])])

def test_update_dict_arg(self):
x = MultiValueDict({'a': [1], 'b': [2], 'c': [3]})
x.update({'a': 4, 'b': 5})
self.assertEqual(list(x.lists()), [('a', [1, 4]), ('b', [2, 5]), ('c', [3])])

def test_update_multivaluedict_arg(self):
x = MultiValueDict({'a': [1], 'b': [2], 'c': [3]})
x.update(MultiValueDict({'a': [4], 'b': [5]}))
self.assertEqual(list(x.lists()), [('a', [1, 4]), ('b', [2, 5]), ('c', [3])])

def test_update_kwargs(self):
x = MultiValueDict({'a': [1], 'b': [2], 'c': [3]})
x.update(a=4, b=5)
self.assertEqual(list(x.lists()), [('a', [1, 4]), ('b', [2, 5]), ('c', [3])])

def test_update_with_empty_iterable(self):
for value in ['', b'', (), [], set(), {}]:
d = MultiValueDict()
d.update(value)
self.assertEqual(d, MultiValueDict())

def test_update_with_iterable_of_pairs(self):
for value in [(('a', 1),), [('a', 1)], {('a', 1)}]:
d = MultiValueDict()
d.update(value)
self.assertEqual(d, MultiValueDict({'a': [1]}))

def test_update_raises_correct_exceptions(self):
# MultiValueDict.update() raises equivalent exceptions to
# dict.update().
# Non-iterable values raise TypeError.
for value in [None, True, False, 123, 123.45]:
with self.subTest(value), self.assertRaises(TypeError):
MultiValueDict().update(value)
# Iterables of objects that cannot be unpacked raise TypeError.
for value in [b'123', b'abc', (1, 2, 3), [1, 2, 3], {1, 2, 3}]:
with self.subTest(value), self.assertRaises(TypeError):
MultiValueDict().update(value)
# Iterables of unpackable objects with incorrect number of items raise
# ValueError.
for value in ['123', 'abc', ('a', 'b', 'c'), ['a', 'b', 'c'], {'a', 'b', 'c'}]:
with self.subTest(value), self.assertRaises(ValueError):
MultiValueDict().update(value)


class ImmutableListTests(SimpleTestCase):

Expand Down