From 85f024c2b1d9b59d0a487f0743da8c21076064b7 Mon Sep 17 00:00:00 2001 From: Paul McMillan Date: Tue, 22 Jun 2010 02:56:25 +0000 Subject: [PATCH] [soc2010/test-refactor] updated delete modeltest to unittest git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/test-refactor@13376 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- tests/modeltests/delete/models.py | 165 ----------------------------- tests/modeltests/delete/tests.py | 169 ++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 165 deletions(-) create mode 100644 tests/modeltests/delete/tests.py diff --git a/tests/modeltests/delete/models.py b/tests/modeltests/delete/models.py index 0e063fd8f0..9c81f6b8f8 100644 --- a/tests/modeltests/delete/models.py +++ b/tests/modeltests/delete/models.py @@ -40,168 +40,3 @@ class E(DefaultRepr, models.Model): class F(DefaultRepr, models.Model): e = models.ForeignKey(E, related_name='f_rel') - -__test__ = {'API_TESTS': """ -### Tests for models A,B,C,D ### - -## First, test the CollectedObjects data structure directly - ->>> from django.db.models.query import CollectedObjects - ->>> g = CollectedObjects() ->>> g.add("key1", 1, "item1", None) -False ->>> g["key1"] -{1: 'item1'} ->>> g.add("key2", 1, "item1", "key1") -False ->>> g.add("key2", 2, "item2", "key1") -False ->>> g["key2"] -{1: 'item1', 2: 'item2'} ->>> g.add("key3", 1, "item1", "key1") -False ->>> g.add("key3", 1, "item1", "key2") -True ->>> g.ordered_keys() -['key3', 'key2', 'key1'] - ->>> g.add("key2", 1, "item1", "key3") -True ->>> g.ordered_keys() -Traceback (most recent call last): - ... -CyclicDependency: There is a cyclic dependency of items to be processed. - - -## Second, test the usage of CollectedObjects by Model.delete() - -# Due to the way that transactions work in the test harness, -# doing m.delete() here can work but fail in a real situation, -# since it may delete all objects, but not in the right order. -# So we manually check that the order of deletion is correct. - -# Also, it is possible that the order is correct 'accidentally', due -# solely to order of imports etc. To check this, we set the order -# that 'get_models()' will retrieve to a known 'nice' order, and -# then try again with a known 'tricky' order. Slightly naughty -# access to internals here :-) - -# If implementation changes, then the tests may need to be simplified: -# - remove the lines that set the .keyOrder and clear the related -# object caches -# - remove the second set of tests (with a2, b2 etc) - ->>> from django.db.models.loading import cache - ->>> def clear_rel_obj_caches(models): -... for m in models: -... if hasattr(m._meta, '_related_objects_cache'): -... del m._meta._related_objects_cache - -# Nice order ->>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd'] ->>> clear_rel_obj_caches([A, B, C, D]) - ->>> a1 = A() ->>> a1.save() ->>> b1 = B(a=a1) ->>> b1.save() ->>> c1 = C(b=b1) ->>> c1.save() ->>> d1 = D(c=c1, a=a1) ->>> d1.save() - ->>> o = CollectedObjects() ->>> a1._collect_sub_objects(o) ->>> o.keys() -[, , , ] ->>> a1.delete() - -# Same again with a known bad order ->>> cache.app_models['delete'].keyOrder = ['d', 'c', 'b', 'a'] ->>> clear_rel_obj_caches([A, B, C, D]) - ->>> a2 = A() ->>> a2.save() ->>> b2 = B(a=a2) ->>> b2.save() ->>> c2 = C(b=b2) ->>> c2.save() ->>> d2 = D(c=c2, a=a2) ->>> d2.save() - ->>> o = CollectedObjects() ->>> a2._collect_sub_objects(o) ->>> o.keys() -[, , , ] ->>> a2.delete() - -### Tests for models E,F - nullable related fields ### - -## First, test the CollectedObjects data structure directly - ->>> g = CollectedObjects() ->>> g.add("key1", 1, "item1", None) -False ->>> g.add("key2", 1, "item1", "key1", nullable=True) -False ->>> g.add("key1", 1, "item1", "key2") -True ->>> g.ordered_keys() -['key1', 'key2'] - -## Second, test the usage of CollectedObjects by Model.delete() - ->>> e1 = E() ->>> e1.save() ->>> f1 = F(e=e1) ->>> f1.save() ->>> e1.f = f1 ->>> e1.save() - -# Since E.f is nullable, we should delete F first (after nulling out -# the E.f field), then E. - ->>> o = CollectedObjects() ->>> e1._collect_sub_objects(o) ->>> o.keys() -[, ] - -# temporarily replace the UpdateQuery class to verify that E.f is actually nulled out first ->>> import django.db.models.sql ->>> class LoggingUpdateQuery(django.db.models.sql.UpdateQuery): -... def clear_related(self, related_field, pk_list, using): -... print "CLEARING FIELD",related_field.name -... return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list, using) ->>> original_class = django.db.models.sql.UpdateQuery ->>> django.db.models.sql.UpdateQuery = LoggingUpdateQuery ->>> e1.delete() -CLEARING FIELD f - ->>> e2 = E() ->>> e2.save() ->>> f2 = F(e=e2) ->>> f2.save() ->>> e2.f = f2 ->>> e2.save() - -# Same deal as before, though we are starting from the other object. - ->>> o = CollectedObjects() ->>> f2._collect_sub_objects(o) ->>> o.keys() -[, ] - ->>> f2.delete() -CLEARING FIELD f - -# Put this back to normal ->>> django.db.models.sql.UpdateQuery = original_class - -# Restore the app cache to previous condition so that all models are accounted for. ->>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd', 'e', 'f'] ->>> clear_rel_obj_caches([A, B, C, D, E, F]) - -""" -} diff --git a/tests/modeltests/delete/tests.py b/tests/modeltests/delete/tests.py new file mode 100644 index 0000000000..c274ca621c --- /dev/null +++ b/tests/modeltests/delete/tests.py @@ -0,0 +1,169 @@ +from django.test import TestCase +from django.db.models.query import CollectedObjects +from django.db.models.query_utils import CyclicDependency +from django.db.models.loading import cache +import django.db.models.sql + +from models import A, B, C, D, E, F + +test_last_cleared_field = '' + +def clear_rel_obj_caches(models): + for m in models: + if hasattr(m._meta, '_related_objects_cache'): + del m._meta._related_objects_cache + +class LoggingUpdateQuery(django.db.models.sql.UpdateQuery): + def clear_related(self, related_field, pk_list, using): + global test_last_cleared_field + test_last_cleared_field = related_field.name + return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list, using) + +class DeleteTestCase(TestCase): + ### Tests for models A,B,C,D ### + def test_collected_objects_data_structure(self): + ## Test the CollectedObjects data structure directly + + g = CollectedObjects() + self.assertFalse(g.add("key1", 1, "item1", None)) + self.assertEqual(g["key1"], {1: 'item1'}) + self.assertFalse(g.add("key2", 1, "item1", "key1")) + self.assertFalse(g.add("key2", 2, "item2", "key1")) + self.assertEqual(g["key2"], {1: 'item1', 2: 'item2'}) + self.assertFalse(g.add("key3", 1, "item1", "key1")) + self.assertTrue(g.add("key3", 1, "item1", "key2")) + self.assertEqual(g.ordered_keys(), ['key3', 'key2', 'key1']) + self.assertTrue(g.add("key2", 1, "item1", "key3")) + self.assertRaises(CyclicDependency, + g.ordered_keys) + + def test_collected_objects_by_model_delete(self): + ## Test the usage of CollectedObjects by Model.delete() + + # Due to the way that transactions work in the test harness, + # doing m.delete() here can work but fail in a real situation, + # since it may delete all objects, but not in the right order. + # So we manually check that the order of deletion is correct. + + # Also, it is possible that the order is correct 'accidentally', due + # solely to order of imports etc. To check this, we set the order + # that 'get_models()' will retrieve to a known 'nice' order, and + # then try again with a known 'tricky' order. Slightly naughty + # access to internals here :-) + + # If implementation changes, then the tests may need to be simplified: + # - remove the lines that set the .keyOrder and clear the related + # object caches + # - remove the second set of tests (with a2, b2 etc) + + # Nice order + cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd'] + clear_rel_obj_caches([A, B, C, D]) + + a1 = A() + a1.save() + b1 = B(a=a1) + b1.save() + c1 = C(b=b1) + c1.save() + d1 = D(c=c1, a=a1) + d1.save() + + o = CollectedObjects() + a1._collect_sub_objects(o) + self.assertQuerysetEqual(o.keys(), + ["", + "", + "", + ""]) + a1.delete() + + # Same again with a known bad order + cache.app_models['delete'].keyOrder = ['d', 'c', 'b', 'a'] + clear_rel_obj_caches([A, B, C, D]) + + a2 = A() + a2.save() + b2 = B(a=a2) + b2.save() + c2 = C(b=b2) + c2.save() + d2 = D(c=c2, a=a2) + d2.save() + + o = CollectedObjects() + a2._collect_sub_objects(o) + self.assertQuerysetEqual(o.keys(), + ["", + "", + "", + ""]) + a2.delete() + + ### Tests for models E,F - nullable related fields ### + def test_nullable_related_fields_collected_objects(self): + + ## First, test the CollectedObjects data structure directly + g = CollectedObjects() + self.assertFalse(g.add("key1", 1, "item1", None)) + self.assertFalse(g.add("key2", 1, "item1", "key1", nullable=True)) + self.assertTrue(g.add("key1", 1, "item1", "key2")) + self.assertEqual(g.ordered_keys(), ['key1', 'key2']) + + def test_nullable_related_fields_collected_objects_model_delete(self): + ## Second, test the usage of CollectedObjects by Model.delete() + + e1 = E() + e1.save() + f1 = F(e=e1) + f1.save() + e1.f = f1 + e1.save() + + # Since E.f is nullable, we should delete F first (after nulling out + # the E.f field), then E. + + o = CollectedObjects() + e1._collect_sub_objects(o) + self.assertQuerysetEqual(o.keys(), + ["", + ""]) + + # temporarily replace the UpdateQuery class to verify that E.f + # is actually nulled out first + + original_class = django.db.models.sql.UpdateQuery + django.db.models.sql.UpdateQuery = LoggingUpdateQuery + + # this is ugly, but it works + global test_last_cleared_field + test_last_cleared_field = '' + e1.delete() + self.assertEqual(test_last_cleared_field, 'f') + + + e2 = E() + e2.save() + f2 = F(e=e2) + f2.save() + e2.f = f2 + e2.save() + + # Same deal as before, though we are starting from the other object. + + o = CollectedObjects() + f2._collect_sub_objects(o) + o.keys() + ["", ""] + + test_last_cleared_field = '' + f2.delete() + self.assertEqual(test_last_cleared_field, 'f') + + # Put this back to normal + django.db.models.sql.UpdateQuery = original_class + + # Restore the app cache to previous condition so that all + # models are accounted for. + cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd', 'e', 'f'] + clear_rel_obj_caches([A, B, C, D, E, F])