parts/django/tests/regressiontests/model_fields/imagefield.py
changeset 69 c6bca38c1cbf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/parts/django/tests/regressiontests/model_fields/imagefield.py	Sat Jan 08 11:20:57 2011 +0530
@@ -0,0 +1,420 @@
+import os
+import shutil
+
+from django.core.files import File
+from django.core.files.base import ContentFile
+from django.core.files.images import ImageFile
+from django.test import TestCase
+
+from models import Image, Person, PersonWithHeight, PersonWithHeightAndWidth, \
+        PersonDimensionsFirst, PersonTwoImages, TestImageFieldFile
+
+
+# If PIL available, do these tests.
+if Image:
+
+    from models import temp_storage_dir
+
+
+    class ImageFieldTestMixin(object):
+        """
+        Mixin class to provide common functionality to ImageField test classes.
+        """
+
+        # Person model to use for tests.
+        PersonModel = PersonWithHeightAndWidth
+        # File class to use for file instances.
+        File = ImageFile
+
+        def setUp(self):
+            """
+            Creates a pristine temp directory (or deletes and recreates if it
+            already exists) that the model uses as its storage directory.
+
+            Sets up two ImageFile instances for use in tests.
+            """
+            if os.path.exists(temp_storage_dir):
+                shutil.rmtree(temp_storage_dir)
+            os.mkdir(temp_storage_dir)
+
+            file_path1 = os.path.join(os.path.dirname(__file__), "4x8.png")
+            self.file1 = self.File(open(file_path1, 'rb'))
+
+            file_path2 = os.path.join(os.path.dirname(__file__), "8x4.png")
+            self.file2 = self.File(open(file_path2, 'rb'))
+
+        def tearDown(self):
+            """
+            Removes temp directory and all its contents.
+            """
+            shutil.rmtree(temp_storage_dir)
+
+        def check_dimensions(self, instance, width, height,
+                             field_name='mugshot'):
+            """
+            Asserts that the given width and height values match both the
+            field's height and width attributes and the height and width fields
+            (if defined) the image field is caching to.
+
+            Note, this method will check for dimension fields named by adding
+            "_width" or "_height" to the name of the ImageField.  So, the
+            models used in these tests must have their fields named
+            accordingly.
+
+            By default, we check the field named "mugshot", but this can be
+            specified by passing the field_name parameter.
+            """
+            field = getattr(instance, field_name)
+            # Check height/width attributes of field.
+            if width is None and height is None:
+                self.assertRaises(ValueError, getattr, field, 'width')
+                self.assertRaises(ValueError, getattr, field, 'height')
+            else:
+                self.assertEqual(field.width, width)
+                self.assertEqual(field.height, height)
+
+            # Check height/width fields of model, if defined.
+            width_field_name = field_name + '_width'
+            if hasattr(instance, width_field_name):
+                self.assertEqual(getattr(instance, width_field_name), width)
+            height_field_name = field_name + '_height'
+            if hasattr(instance, height_field_name):
+                self.assertEqual(getattr(instance, height_field_name), height)
+
+
+    class ImageFieldTests(ImageFieldTestMixin, TestCase):
+        """
+        Tests for ImageField that don't need to be run with each of the
+        different test model classes.
+        """
+
+        def test_equal_notequal_hash(self):
+            """
+            Bug #9786: Ensure '==' and '!=' work correctly.
+            Bug #9508: make sure hash() works as expected (equal items must
+            hash to the same value).
+            """
+            # Create two Persons with different mugshots.
+            p1 = self.PersonModel(name="Joe")
+            p1.mugshot.save("mug", self.file1)
+            p2 = self.PersonModel(name="Bob")
+            p2.mugshot.save("mug", self.file2)
+            self.assertEqual(p1.mugshot == p2.mugshot, False)
+            self.assertEqual(p1.mugshot != p2.mugshot, True)
+
+            # Test again with an instance fetched from the db.
+            p1_db = self.PersonModel.objects.get(name="Joe")
+            self.assertEqual(p1_db.mugshot == p2.mugshot, False)
+            self.assertEqual(p1_db.mugshot != p2.mugshot, True)
+
+            # Instance from db should match the local instance.
+            self.assertEqual(p1_db.mugshot == p1.mugshot, True)
+            self.assertEqual(hash(p1_db.mugshot), hash(p1.mugshot))
+            self.assertEqual(p1_db.mugshot != p1.mugshot, False)
+
+        def test_instantiate_missing(self):
+            """
+            If the underlying file is unavailable, still create instantiate the
+            object without error.
+            """
+            p = self.PersonModel(name="Joan")
+            p.mugshot.save("shot", self.file1)
+            p = self.PersonModel.objects.get(name="Joan")
+            path = p.mugshot.path
+            shutil.move(path, path + '.moved')
+            p2 = self.PersonModel.objects.get(name="Joan")
+
+        def test_delete_when_missing(self):
+            """
+            Bug #8175: correctly delete an object where the file no longer
+            exists on the file system.
+            """
+            p = self.PersonModel(name="Fred")
+            p.mugshot.save("shot", self.file1)
+            os.remove(p.mugshot.path)
+            p.delete()
+
+        def test_size_method(self):
+            """
+            Bug #8534: FileField.size should not leave the file open.
+            """
+            p = self.PersonModel(name="Joan")
+            p.mugshot.save("shot", self.file1)
+
+            # Get a "clean" model instance
+            p = self.PersonModel.objects.get(name="Joan")
+            # It won't have an opened file.
+            self.assertEqual(p.mugshot.closed, True)
+
+            # After asking for the size, the file should still be closed.
+            _ = p.mugshot.size
+            self.assertEqual(p.mugshot.closed, True)
+
+        def test_pickle(self):
+            """
+            Tests that ImageField can be pickled, unpickled, and that the
+            image of the unpickled version is the same as the original.
+            """
+            import pickle
+
+            p = Person(name="Joe")
+            p.mugshot.save("mug", self.file1)
+            dump = pickle.dumps(p)
+
+            p2 = Person(name="Bob")
+            p2.mugshot = self.file1
+
+            loaded_p = pickle.loads(dump)
+            self.assertEqual(p.mugshot, loaded_p.mugshot)
+
+
+    class ImageFieldTwoDimensionsTests(ImageFieldTestMixin, TestCase):
+        """
+        Tests behavior of an ImageField and its dimensions fields.
+        """
+
+        def test_constructor(self):
+            """
+            Tests assigning an image field through the model's constructor.
+            """
+            p = self.PersonModel(name='Joe', mugshot=self.file1)
+            self.check_dimensions(p, 4, 8)
+            p.save()
+            self.check_dimensions(p, 4, 8)
+
+        def test_image_after_constructor(self):
+            """
+            Tests behavior when image is not passed in constructor.
+            """
+            p = self.PersonModel(name='Joe')
+            # TestImageField value will default to being an instance of its
+            # attr_class, a  TestImageFieldFile, with name == None, which will
+            # cause it to evaluate as False.
+            self.assertEqual(isinstance(p.mugshot, TestImageFieldFile), True)
+            self.assertEqual(bool(p.mugshot), False)
+
+            # Test setting a fresh created model instance.
+            p = self.PersonModel(name='Joe')
+            p.mugshot = self.file1
+            self.check_dimensions(p, 4, 8)
+
+        def test_create(self):
+            """
+            Tests assigning an image in Manager.create().
+            """
+            p = self.PersonModel.objects.create(name='Joe', mugshot=self.file1)
+            self.check_dimensions(p, 4, 8)
+
+        def test_default_value(self):
+            """
+            Tests that the default value for an ImageField is an instance of
+            the field's attr_class (TestImageFieldFile in this case) with no
+            name (name set to None).
+            """
+            p = self.PersonModel()
+            self.assertEqual(isinstance(p.mugshot, TestImageFieldFile), True)
+            self.assertEqual(bool(p.mugshot), False)
+
+        def test_assignment_to_None(self):
+            """
+            Tests that assigning ImageField to None clears dimensions.
+            """
+            p = self.PersonModel(name='Joe', mugshot=self.file1)
+            self.check_dimensions(p, 4, 8)
+
+            # If image assigned to None, dimension fields should be cleared.
+            p.mugshot = None
+            self.check_dimensions(p, None, None)
+
+            p.mugshot = self.file2
+            self.check_dimensions(p, 8, 4)
+
+        def test_field_save_and_delete_methods(self):
+            """
+            Tests assignment using the field's save method and deletion using
+            the field's delete method.
+            """
+            p = self.PersonModel(name='Joe')
+            p.mugshot.save("mug", self.file1)
+            self.check_dimensions(p, 4, 8)
+
+            # A new file should update dimensions.
+            p.mugshot.save("mug", self.file2)
+            self.check_dimensions(p, 8, 4)
+
+            # Field and dimensions should be cleared after a delete.
+            p.mugshot.delete(save=False)
+            self.assertEqual(p.mugshot, None)
+            self.check_dimensions(p, None, None)
+
+        def test_dimensions(self):
+            """
+            Checks that dimensions are updated correctly in various situations.
+            """
+            p = self.PersonModel(name='Joe')
+
+            # Dimensions should get set if file is saved.
+            p.mugshot.save("mug", self.file1)
+            self.check_dimensions(p, 4, 8)
+
+            # Test dimensions after fetching from database.
+            p = self.PersonModel.objects.get(name='Joe')
+            # Bug 11084: Dimensions should not get recalculated if file is
+            # coming from the database.  We test this by checking if the file
+            # was opened.
+            self.assertEqual(p.mugshot.was_opened, False)
+            self.check_dimensions(p, 4, 8)
+            # After checking dimensions on the image field, the file will have
+            # opened.
+            self.assertEqual(p.mugshot.was_opened, True)
+            # Dimensions should now be cached, and if we reset was_opened and
+            # check dimensions again, the file should not have opened.
+            p.mugshot.was_opened = False
+            self.check_dimensions(p, 4, 8)
+            self.assertEqual(p.mugshot.was_opened, False)
+
+            # If we assign a new image to the instance, the dimensions should
+            # update.
+            p.mugshot = self.file2
+            self.check_dimensions(p, 8, 4)
+            # Dimensions were recalculated, and hence file should have opened.
+            self.assertEqual(p.mugshot.was_opened, True)
+
+
+    class ImageFieldNoDimensionsTests(ImageFieldTwoDimensionsTests):
+        """
+        Tests behavior of an ImageField with no dimension fields.
+        """
+
+        PersonModel = Person
+
+
+    class ImageFieldOneDimensionTests(ImageFieldTwoDimensionsTests):
+        """
+        Tests behavior of an ImageField with one dimensions field.
+        """
+
+        PersonModel = PersonWithHeight
+
+
+    class ImageFieldDimensionsFirstTests(ImageFieldTwoDimensionsTests):
+        """
+        Tests behavior of an ImageField where the dimensions fields are
+        defined before the ImageField.
+        """
+
+        PersonModel = PersonDimensionsFirst
+
+
+    class ImageFieldUsingFileTests(ImageFieldTwoDimensionsTests):
+        """
+        Tests behavior of an ImageField when assigning it a File instance
+        rather than an ImageFile instance.
+        """
+
+        PersonModel = PersonDimensionsFirst
+        File = File
+
+
+    class TwoImageFieldTests(ImageFieldTestMixin, TestCase):
+        """
+        Tests a model with two ImageFields.
+        """
+
+        PersonModel = PersonTwoImages
+
+        def test_constructor(self):
+            p = self.PersonModel(mugshot=self.file1, headshot=self.file2)
+            self.check_dimensions(p, 4, 8, 'mugshot')
+            self.check_dimensions(p, 8, 4, 'headshot')
+            p.save()
+            self.check_dimensions(p, 4, 8, 'mugshot')
+            self.check_dimensions(p, 8, 4, 'headshot')
+
+        def test_create(self):
+            p = self.PersonModel.objects.create(mugshot=self.file1,
+                                                headshot=self.file2)
+            self.check_dimensions(p, 4, 8)
+            self.check_dimensions(p, 8, 4, 'headshot')
+
+        def test_assignment(self):
+            p = self.PersonModel()
+            self.check_dimensions(p, None, None, 'mugshot')
+            self.check_dimensions(p, None, None, 'headshot')
+
+            p.mugshot = self.file1
+            self.check_dimensions(p, 4, 8, 'mugshot')
+            self.check_dimensions(p, None, None, 'headshot')
+            p.headshot = self.file2
+            self.check_dimensions(p, 4, 8, 'mugshot')
+            self.check_dimensions(p, 8, 4, 'headshot')
+
+            # Clear the ImageFields one at a time.
+            p.mugshot = None
+            self.check_dimensions(p, None, None, 'mugshot')
+            self.check_dimensions(p, 8, 4, 'headshot')
+            p.headshot = None
+            self.check_dimensions(p, None, None, 'mugshot')
+            self.check_dimensions(p, None, None, 'headshot')
+
+        def test_field_save_and_delete_methods(self):
+            p = self.PersonModel(name='Joe')
+            p.mugshot.save("mug", self.file1)
+            self.check_dimensions(p, 4, 8, 'mugshot')
+            self.check_dimensions(p, None, None, 'headshot')
+            p.headshot.save("head", self.file2)
+            self.check_dimensions(p, 4, 8, 'mugshot')
+            self.check_dimensions(p, 8, 4, 'headshot')
+
+            # We can use save=True when deleting the image field with null=True
+            # dimension fields and the other field has an image.
+            p.headshot.delete(save=True)
+            self.check_dimensions(p, 4, 8, 'mugshot')
+            self.check_dimensions(p, None, None, 'headshot')
+            p.mugshot.delete(save=False)
+            self.check_dimensions(p, None, None, 'mugshot')
+            self.check_dimensions(p, None, None, 'headshot')
+
+        def test_dimensions(self):
+            """
+            Checks that dimensions are updated correctly in various situations.
+            """
+            p = self.PersonModel(name='Joe')
+
+            # Dimensions should get set for the saved file.
+            p.mugshot.save("mug", self.file1)
+            p.headshot.save("head", self.file2)
+            self.check_dimensions(p, 4, 8, 'mugshot')
+            self.check_dimensions(p, 8, 4, 'headshot')
+
+            # Test dimensions after fetching from database.
+            p = self.PersonModel.objects.get(name='Joe')
+            # Bug 11084: Dimensions should not get recalculated if file is
+            # coming from the database.  We test this by checking if the file
+            # was opened.
+            self.assertEqual(p.mugshot.was_opened, False)
+            self.assertEqual(p.headshot.was_opened, False)
+            self.check_dimensions(p, 4, 8,'mugshot')
+            self.check_dimensions(p, 8, 4, 'headshot')
+            # After checking dimensions on the image fields, the files will
+            # have been opened.
+            self.assertEqual(p.mugshot.was_opened, True)
+            self.assertEqual(p.headshot.was_opened, True)
+            # Dimensions should now be cached, and if we reset was_opened and
+            # check dimensions again, the file should not have opened.
+            p.mugshot.was_opened = False
+            p.headshot.was_opened = False
+            self.check_dimensions(p, 4, 8,'mugshot')
+            self.check_dimensions(p, 8, 4, 'headshot')
+            self.assertEqual(p.mugshot.was_opened, False)
+            self.assertEqual(p.headshot.was_opened, False)
+
+            # If we assign a new image to the instance, the dimensions should
+            # update.
+            p.mugshot = self.file2
+            p.headshot = self.file1
+            self.check_dimensions(p, 8, 4, 'mugshot')
+            self.check_dimensions(p, 4, 8, 'headshot')
+            # Dimensions were recalculated, and hence file should have opened.
+            self.assertEqual(p.mugshot.was_opened, True)
+            self.assertEqual(p.headshot.was_opened, True)