Skip to content

Commit 058e434

Browse files
loiccharettes
authored andcommitted
Merged the signals and signals_regress test packages.
This patch also made the tests less likely to pollute the global state in case of failure.
1 parent a0f3eec commit 058e434

File tree

5 files changed

+204
-241
lines changed

5 files changed

+204
-241
lines changed

tests/signals/models.py

+17
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,20 @@ class Car(models.Model):
2323

2424
def __str__(self):
2525
return "%s %s" % (self.make, self.model)
26+
27+
28+
@python_2_unicode_compatible
29+
class Author(models.Model):
30+
name = models.CharField(max_length=20)
31+
32+
def __str__(self):
33+
return self.name
34+
35+
36+
@python_2_unicode_compatible
37+
class Book(models.Model):
38+
name = models.CharField(max_length=20)
39+
authors = models.ManyToManyField(Author)
40+
41+
def __str__(self):
42+
return self.name

tests/signals/tests.py

+187-127
Original file line numberDiff line numberDiff line change
@@ -5,173 +5,233 @@
55
from django.test import TestCase
66
from django.utils import six
77

8-
from .models import Person, Car
9-
10-
11-
# #8285: signals can be any callable
12-
class PostDeleteHandler(object):
13-
def __init__(self, data):
14-
self.data = data
15-
16-
def __call__(self, signal, sender, instance, **kwargs):
17-
self.data.append(
18-
(instance, instance.id is None)
19-
)
20-
21-
22-
class MyReceiver(object):
23-
def __init__(self, param):
24-
self.param = param
25-
self._run = False
26-
27-
def __call__(self, signal, sender, **kwargs):
28-
self._run = True
29-
signal.disconnect(receiver=self, sender=sender)
8+
from .models import Author, Book, Car, Person
309

3110

3211
class SignalTests(TestCase):
33-
def test_basic(self):
12+
13+
def setUp(self):
3414
# Save up the number of connected signals so that we can check at the
3515
# end that all the signals we register get properly unregistered (#9989)
36-
pre_signals = (
16+
self.pre_signals = (
3717
len(signals.pre_save.receivers),
3818
len(signals.post_save.receivers),
3919
len(signals.pre_delete.receivers),
4020
len(signals.post_delete.receivers),
4121
)
4222

23+
def tearDown(self):
24+
# Check that all our signals got disconnected properly.
25+
post_signals = (
26+
len(signals.pre_save.receivers),
27+
len(signals.post_save.receivers),
28+
len(signals.pre_delete.receivers),
29+
len(signals.post_delete.receivers),
30+
)
31+
self.assertEqual(self.pre_signals, post_signals)
32+
33+
def test_save_signals(self):
4334
data = []
4435

45-
def pre_save_test(signal, sender, instance, **kwargs):
36+
def pre_save_handler(signal, sender, instance, **kwargs):
4637
data.append(
4738
(instance, kwargs.get("raw", False))
4839
)
49-
signals.pre_save.connect(pre_save_test)
5040

51-
def post_save_test(signal, sender, instance, **kwargs):
41+
def post_save_handler(signal, sender, instance, **kwargs):
5242
data.append(
5343
(instance, kwargs.get("created"), kwargs.get("raw", False))
5444
)
55-
signals.post_save.connect(post_save_test)
5645

57-
def pre_delete_test(signal, sender, instance, **kwargs):
46+
signals.pre_save.connect(pre_save_handler)
47+
signals.post_save.connect(post_save_handler)
48+
try:
49+
p1 = Person.objects.create(first_name="John", last_name="Smith")
50+
51+
self.assertEqual(data, [
52+
(p1, False),
53+
(p1, True, False),
54+
])
55+
data[:] = []
56+
57+
p1.first_name = "Tom"
58+
p1.save()
59+
self.assertEqual(data, [
60+
(p1, False),
61+
(p1, False, False),
62+
])
63+
data[:] = []
64+
65+
# Calling an internal method purely so that we can trigger a "raw" save.
66+
p1.save_base(raw=True)
67+
self.assertEqual(data, [
68+
(p1, True),
69+
(p1, False, True),
70+
])
71+
data[:] = []
72+
73+
p2 = Person(first_name="James", last_name="Jones")
74+
p2.id = 99999
75+
p2.save()
76+
self.assertEqual(data, [
77+
(p2, False),
78+
(p2, True, False),
79+
])
80+
data[:] = []
81+
p2.id = 99998
82+
p2.save()
83+
self.assertEqual(data, [
84+
(p2, False),
85+
(p2, True, False),
86+
])
87+
finally:
88+
signals.post_delete.disconnect(pre_save_handler)
89+
signals.pre_delete.disconnect(post_save_handler)
90+
91+
def test_delete_signals(self):
92+
data = []
93+
94+
def pre_delete_handler(signal, sender, instance, **kwargs):
5895
data.append(
5996
(instance, instance.id is None)
6097
)
61-
signals.pre_delete.connect(pre_delete_test)
6298

63-
post_delete_test = PostDeleteHandler(data)
64-
signals.post_delete.connect(post_delete_test)
99+
# #8285: signals can be any callable
100+
class PostDeleteHandler(object):
101+
def __init__(self, data):
102+
self.data = data
103+
104+
def __call__(self, signal, sender, instance, **kwargs):
105+
self.data.append(
106+
(instance, instance.id is None)
107+
)
108+
post_delete_handler = PostDeleteHandler(data)
109+
110+
signals.pre_delete.connect(pre_delete_handler)
111+
signals.post_delete.connect(post_delete_handler)
112+
try:
113+
p1 = Person.objects.create(first_name="John", last_name="Smith")
114+
p1.delete()
115+
self.assertEqual(data, [
116+
(p1, False),
117+
(p1, False),
118+
])
119+
data[:] = []
120+
121+
p2 = Person(first_name="James", last_name="Jones")
122+
p2.id = 99999
123+
p2.save()
124+
p2.id = 99998
125+
p2.save()
126+
p2.delete()
127+
self.assertEqual(data, [
128+
(p2, False),
129+
(p2, False)
130+
])
131+
data[:] = []
132+
133+
self.assertQuerysetEqual(
134+
Person.objects.all(), [
135+
"James Jones",
136+
],
137+
six.text_type
138+
)
139+
finally:
140+
signals.post_delete.disconnect(pre_delete_handler)
141+
signals.pre_delete.disconnect(post_delete_handler)
142+
143+
def test_decorators(self):
144+
data = []
65145

66-
# throw a decorator syntax receiver into the mix
67146
@receiver(signals.pre_save)
68-
def pre_save_decorator_test(signal, sender, instance, **kwargs):
147+
def decorated_handler(signal, sender, instance, **kwargs):
69148
data.append(instance)
70149

71150
@receiver(signals.pre_save, sender=Car)
72-
def pre_save_decorator_sender_test(signal, sender, instance, **kwargs):
151+
def decorated_handler_with_sender_arg(signal, sender, instance, **kwargs):
73152
data.append(instance)
74153

75-
p1 = Person(first_name="John", last_name="Smith")
76-
self.assertEqual(data, [])
77-
p1.save()
78-
self.assertEqual(data, [
79-
(p1, False),
80-
p1,
81-
(p1, True, False),
82-
])
83-
data[:] = []
84-
85-
p1.first_name = "Tom"
86-
p1.save()
87-
self.assertEqual(data, [
88-
(p1, False),
89-
p1,
90-
(p1, False, False),
91-
])
92-
data[:] = []
93-
94-
# Car signal (sender defined)
95-
c1 = Car(make="Volkswagon", model="Passat")
96-
c1.save()
97-
self.assertEqual(data, [
98-
(c1, False),
99-
c1,
100-
c1,
101-
(c1, True, False),
102-
])
103-
data[:] = []
104-
105-
# Calling an internal method purely so that we can trigger a "raw" save.
106-
p1.save_base(raw=True)
107-
self.assertEqual(data, [
108-
(p1, True),
109-
p1,
110-
(p1, False, True),
111-
])
112-
data[:] = []
113-
114-
p1.delete()
115-
self.assertEqual(data, [
116-
(p1, False),
117-
(p1, False),
118-
])
119-
data[:] = []
120-
121-
p2 = Person(first_name="James", last_name="Jones")
122-
p2.id = 99999
123-
p2.save()
124-
self.assertEqual(data, [
125-
(p2, False),
126-
p2,
127-
(p2, True, False),
128-
])
129-
data[:] = []
130-
131-
p2.id = 99998
132-
p2.save()
133-
self.assertEqual(data, [
134-
(p2, False),
135-
p2,
136-
(p2, True, False),
137-
])
138-
data[:] = []
139-
140-
p2.delete()
141-
self.assertEqual(data, [
142-
(p2, False),
143-
(p2, False)
144-
])
145-
146-
self.assertQuerysetEqual(
147-
Person.objects.all(), [
148-
"James Jones",
149-
],
150-
six.text_type
151-
)
154+
try:
155+
c1 = Car.objects.create(make="Volkswagon", model="Passat")
156+
self.assertEqual(data, [c1, c1])
157+
finally:
158+
signals.post_delete.disconnect(decorated_handler)
159+
signals.pre_delete.disconnect(decorated_handler_with_sender_arg, sender=Car)
152160

153-
signals.post_delete.disconnect(post_delete_test)
154-
signals.pre_delete.disconnect(pre_delete_test)
155-
signals.post_save.disconnect(post_save_test)
156-
signals.pre_save.disconnect(pre_save_test)
157-
signals.pre_save.disconnect(pre_save_decorator_test)
158-
signals.pre_save.disconnect(pre_save_decorator_sender_test, sender=Car)
161+
def test_save_and_delete_signals_with_m2m(self):
162+
data = []
159163

160-
# Check that all our signals got disconnected properly.
161-
post_signals = (
162-
len(signals.pre_save.receivers),
163-
len(signals.post_save.receivers),
164-
len(signals.pre_delete.receivers),
165-
len(signals.post_delete.receivers),
166-
)
167-
self.assertEqual(pre_signals, post_signals)
164+
def pre_save_handler(signal, sender, instance, **kwargs):
165+
data.append('pre_save signal, %s' % instance)
166+
if kwargs.get('raw'):
167+
data.append('Is raw')
168+
169+
def post_save_handler(signal, sender, instance, **kwargs):
170+
data.append('post_save signal, %s' % instance)
171+
if 'created' in kwargs:
172+
if kwargs['created']:
173+
data.append('Is created')
174+
else:
175+
data.append('Is updated')
176+
if kwargs.get('raw'):
177+
data.append('Is raw')
178+
179+
def pre_delete_handler(signal, sender, instance, **kwargs):
180+
data.append('pre_save signal, %s' % instance)
181+
data.append('instance.id is not None: %s' % (instance.id is not None))
182+
183+
def post_delete_handler(signal, sender, instance, **kwargs):
184+
data.append('post_delete signal, %s' % instance)
185+
data.append('instance.id is not None: %s' % (instance.id is not None))
186+
187+
signals.pre_save.connect(pre_save_handler)
188+
signals.post_save.connect(post_save_handler)
189+
signals.pre_delete.connect(pre_delete_handler)
190+
signals.post_delete.connect(post_delete_handler)
191+
try:
192+
a1 = Author.objects.create(name='Neal Stephenson')
193+
self.assertEqual(data, [
194+
"pre_save signal, Neal Stephenson",
195+
"post_save signal, Neal Stephenson",
196+
"Is created"
197+
])
198+
data[:] = []
199+
200+
b1 = Book.objects.create(name='Snow Crash')
201+
self.assertEqual(data, [
202+
"pre_save signal, Snow Crash",
203+
"post_save signal, Snow Crash",
204+
"Is created"
205+
])
206+
data[:] = []
207+
208+
# Assigning and removing to/from m2m shouldn't generate an m2m signal.
209+
b1.authors = [a1]
210+
self.assertEqual(data, [])
211+
b1.authors = []
212+
self.assertEqual(data, [])
213+
finally:
214+
signals.post_delete.disconnect(pre_save_handler)
215+
signals.pre_delete.disconnect(post_save_handler)
216+
signals.post_save.disconnect(pre_delete_handler)
217+
signals.pre_save.disconnect(post_delete_handler)
168218

169219
def test_disconnect_in_dispatch(self):
170220
"""
171221
Test that signals that disconnect when being called don't mess future
172222
dispatching.
173223
"""
174-
a, b = MyReceiver(1), MyReceiver(2)
224+
225+
class Handler(object):
226+
def __init__(self, param):
227+
self.param = param
228+
self._run = False
229+
230+
def __call__(self, signal, sender, **kwargs):
231+
self._run = True
232+
signal.disconnect(receiver=self, sender=sender)
233+
234+
a, b = Handler(1), Handler(2)
175235
signals.post_save.connect(sender=Person, receiver=a)
176236
signals.post_save.connect(sender=Person, receiver=b)
177237
Person.objects.create(first_name='John', last_name='Smith')

tests/signals_regress/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)