text: import os
  import unittest
  
  from django import forms, template
  from django.conf import settings
  from django.core.exceptions import ImproperlyConfigured
  from django.test import TestCase, override_settings
  from django.urls import reverse
  from mock import MagicMock
  from taggit.forms import TagField, TagWidget
  
  from wagtail.tests.testapp.models import CustomImage, CustomImageFilePath
  from wagtail.tests.utils import WagtailTestUtils
  from wagtail.images import get_image_model, get_image_model_string
  from wagtail.images.fields import WagtailImageField
  from wagtail.images.formats import Format, get_image_format, register_image_format
  from wagtail.images.forms import get_image_form
  from wagtail.images.models import Image as WagtailImage
  from wagtail.images.rect import Rect, Vector
  from wagtail.images.views.serve import ServeView, generate_signature, verify_signature
  
  from .utils import Image, get_test_image_file
  
  try:
      import sendfile  # noqa
      sendfile_mod = True
  except ImportError:
      sendfile_mod = False
  
  
  class TestImageTag(TestCase):
      def setUp(self):
          # Create an image for running tests on
          self.image = Image.objects.create(
              title="Test image",
              file=get_test_image_file(),
          )
  
      def render_image_tag(self, image, filter_spec):
          temp = template.Template('{% load wagtailimages_tags %}{% image image_obj ' + filter_spec + '%}')
          context = template.Context({'image_obj': image})
          return temp.render(context)
  
      def test_image_tag(self):
          result = self.render_image_tag(self.image, 'width-400')
  
          # Check that all the required HTML attributes are set
          self.assertTrue('width="400"' in result)
          self.assertTrue('height="300"' in result)
          self.assertTrue('alt="Test image"' in result)
  
      def test_image_tag_none(self):
          result = self.render_image_tag(None, "width-500")
          self.assertEqual(result, '')
  
      def render_image_tag_as(self, image, filter_spec):
          temp = template.Template(
              '{% load wagtailimages_tags %}{% image image_obj ' + filter_spec +
              ' as test_img %}<img {{ test_img.attrs }} />'
          )
          context = template.Context({'image_obj': image})
          return temp.render(context)
  
      def test_image_tag_attrs(self):
          result = self.render_image_tag_as(self.image, 'width-400')
  
          # Check that all the required HTML attributes are set
          self.assertTrue('width="400"' in result)
          self.assertTrue('height="300"' in result)
          self.assertTrue('alt="Test image"' in result)
  
      def render_image_tag_with_extra_attributes(self, image, title):
          temp = template.Template(
              '{% load wagtailimages_tags %}{% image image_obj width-400 \
              class="photo" title=title|lower alt="Alternate" %}'
          )
          context = template.Context({'image_obj': image, 'title': title})
          return temp.render(context)
  
      def test_image_tag_with_extra_attributes(self):
          result = self.render_image_tag_with_extra_attributes(self.image, 'My Wonderful Title')
  
          # Check that all the required HTML attributes are set
          self.assertTrue('width="400"' in result)
          self.assertTrue('height="300"' in result)
          self.assertTrue('class="photo"' in result)
          self.assertTrue('alt="Alternate"' in result)
          self.assertTrue('title="my wonderful title"' in result)
  
      def render_image_tag_with_filters(self, image):
          temp = template.Template(
              '{% load wagtailimages_tags %}{% image image_primary|default:image_alternate width-400 %}'
          )
          context = template.Context({'image_primary': None, 'image_alternate': image})
          return temp.render(context)
  
      def test_image_tag_with_filters(self):
          result = self.render_image_tag_with_filters(self.image)
          self.assertTrue('width="400"' in result)
          self.assertTrue('height="300"' in result)
  
      def test_image_tag_with_chained_filters(self):
          result = self.render_image_tag(self.image, 'fill-200x200 height-150')
          self.assertTrue('width="150"' in result)
          self.assertTrue('height="150"' in result)
  
      def test_filter_specs_must_match_allowed_pattern(self):
          with self.assertRaises(template.TemplateSyntaxError):
              self.render_image_tag(self.image, 'fill-200x200|height-150')
  
          with self.assertRaises(template.TemplateSyntaxError):
              self.render_image_tag(self.image, 'fill-800x600 alt"test"')
  
      def test_context_may_only_contain_one_argument(self):
          with self.assertRaises(template.TemplateSyntaxError):
              temp = template.Template(
                  '{% load wagtailimages_tags %}{% image image_obj fill-200x200'
                  ' as test_img this_one_should_not_be_there %}<img {{ test_img.attrs }} />'
              )
              context = template.Context({'image_obj': self.image})
              temp.render(context)
  
      def test_no_image_filter_provided(self):
          # if image template gets the image but no filters
          with self.assertRaises(template.TemplateSyntaxError):
              temp = template.Template(
                  '{% load wagtailimages_tags %}{% image image_obj %}'
              )
              context = template.Context({'image_obj': self.image})
              temp.render(context)
  
      def test_no_image_filter_provided_when_using_as(self):
          # if image template gets the image but no filters
          with self.assertRaises(template.TemplateSyntaxError):
              temp = template.Template(
                  '{% load wagtailimages_tags %}{% image image_obj as foo %}'
              )
              context = template.Context({'image_obj': self.image})
              temp.render(context)
  
      def test_no_image_filter_provided_but_attributes_provided(self):
          # if image template gets the image but no filters
          with self.assertRaises(template.TemplateSyntaxError):
              temp = template.Template(
                  '{% load wagtailimages_tags %}{% image image_obj class="cover-image"%}'
              )
              context = template.Context({'image_obj': self.image})
              temp.render(context)
  
  
  class TestMissingImage(TestCase):
      """
      Missing image files in media/original_images should be handled gracefully, to cope with
      pulling live databases to a development instance without copying the corresponding image files.
      In this case, it's acceptable to render broken images, but not to fail rendering the page outright.
      """
      fixtures = ['test.json']
  
      def test_image_tag_with_missing_image(self):
          # the page /events/christmas/ has a missing image as the feed image
          response = self.client.get('/events/christmas/')
          self.assertContains(
              response,
              '<img src="/media/not-found" width="0" height="0" alt="A missing image" class="feed-image">',
              html=True
          )
  
      def test_rich_text_with_missing_image(self):
          # the page /events/final-event/ has a missing image in the rich text body
          response = self.client.get('/events/final-event/')
          self.assertContains(
              response,
              '<img class="richtext-image full-width" src="/media/not-found" \
              width="0" height="0" alt="where did my image go?">',
              html=True
          )
  
  
  class TestFormat(TestCase):
      def setUp(self):
          # test format
          self.format = Format(
              'test name',
              'test label',
              'test classnames',
              'test filter spec'
          )
          # test image
          self.image = MagicMock()
          self.image.id = 0
  
      def test_editor_attributes(self):
          result = self.format.editor_attributes(
              self.image,
              'test alt text'
          )
          self.assertEqual(result,
                           'data-embedtype="image" data-id="0" data-format="test name" data-alt="test alt text" ')
  
      def test_image_to_editor_html(self):
          result = self.format.image_to_editor_html(
              self.image,
              'test alt text'
          )
          self.assertRegex(
              result,
              '<img data-embedtype="image" data-id="0" data-format="test name" '
              'data-alt="test alt text" class="test classnames" src="[^"]+" width="1" height="1" alt="test alt text">',
          )
  
      def test_image_to_editor_html_with_quoting(self):
          result = self.format.image_to_editor_html(
              self.image,
              'Arthur "two sheds" Jackson'
          )
          self.assertRegex(
              result,
              '<img data-embedtype="image" data-id="0" data-format="test name" '
              'data-alt="Arthur &quot;two sheds&quot; Jackson" class="test classnames" src="[^"]+" width="1" height="1" alt="Arthur &quot;two sheds&quot; Jackson">',
          )
  
      def test_image_to_html_no_classnames(self):
          self.format.classnames = None
          result = self.format.image_to_html(self.image, 'test alt text')
          self.assertRegex(
              result,
              '<img src="[^"]+" width="1" height="1" alt="test alt text">'
          )
          self.format.classnames = 'test classnames'
  
      def test_image_to_html_with_quoting(self):
          result = self.format.image_to_html(self.image, 'Arthur "two sheds" Jackson')
          self.assertRegex(
              result,
              '<img class="test classnames" src="[^"]+" width="1" height="1" alt="Arthur &quot;two sheds&quot; Jackson">'
          )
  
      def test_get_image_format(self):
          register_image_format(self.format)
          result = get_image_format('test name')
          self.assertEqual(result, self.format)
  
  
  class TestSignatureGeneration(TestCase):
      def test_signature_generation(self):
          self.assertEqual(generate_signature(100, 'fill-800x600'), 'xnZOzQyUg6pkfciqcfRJRosOrGg=')
  
      def test_signature_verification(self):
          self.assertTrue(verify_signature('xnZOzQyUg6pkfciqcfRJRosOrGg=', 100, 'fill-800x600'))
  
      def test_signature_changes_on_image_id(self):
          self.assertFalse(verify_signature('xnZOzQyUg6pkfciqcfRJRosOrGg=', 200, 'fill-800x600'))
  
      def test_signature_changes_on_filter_spec(self):
          self.assertFalse(verify_signature('xnZOzQyUg6pkfciqcfRJRosOrGg=', 100, 'fill-800x700'))
  
  
  class TestFrontendServeView(TestCase):
      def setUp(self):
          # Create an image for running tests on
          self.image = Image.objects.create(
              title="Test image",
              file=get_test_image_file(),
          )
  
      def test_get(self):
          """
          Test a valid GET request to the view
          """
          # Generate signature
          signature = generate_signature(self.image.id, 'fill-800x600')
  
          # Get the image
          response = self.client.get(reverse('wagtailimages_serve', args=(signature, self.image.id, 'fill-800x600')))
  
          # Check response
          self.assertEqual(response.status_code, 200)
          self.assertTrue(response.streaming)
          self.assertEqual(response['Content-Type'], 'image/png')
  
      def test_get_with_extra_component(self):
          """
          Test that a filename can be optionally added to the end of the URL.
          """
          # Generate signature
          signature = generate_signature(self.image.id, 'fill-800x600')
  
          # Get the image
          response = self.client.get(reverse('wagtailimages_serve', args=(signature, self.image.id, 'fill-800x600')) + 'test.png')
  
          # Check response
          self.assertEqual(response.status_code, 200)
          self.assertTrue(response.streaming)
          self.assertEqual(response['Content-Type'], 'image/png')
  
      def test_get_with_too_many_extra_components(self):
          """
          A filename can be appended to the end of the URL, but it must not contain a '/'
          """
          # Generate signature
          signature = generate_signature(self.image.id, 'fill-800x600')
  
          # Get the image
          response = self.client.get(reverse('wagtailimages_serve', args=(signature, self.image.id, 'fill-800x600')) + 'test/test.png')
  
          # URL pattern should not match
          self.assertEqual(response.status_code, 404)
  
      def test_get_with_serve_action(self):
          signature = generate_signature(self.image.id, 'fill-800x600')
          response = self.client.get(reverse('wagtailimages_serve_action_serve', args=(signature, self.image.id, 'fill-800x600')))
  
          self.assertEqual(response.status_code, 200)
          self.assertTrue(response.streaming)
          self.assertEqual(response['Content-Type'], 'image/png')
  
      def test_get_with_redirect_action(self):
          signature = generate_signature(self.image.id, 'fill-800x600')
          response = self.client.get(reverse('wagtailimages_serve_action_redirect', args=(signature, self.image.id, 'fill-800x600')))
  
          expected_redirect_url = '/media/images/{filename[0]}.2e16d0ba.fill-800x600{filename[1]}'.format(
              filename=os.path.splitext(os.path.basename(self.image.file.path))
          )
  
          self.assertRedirects(response, expected_redirect_url, status_code=301, fetch_redirect_response=False)
  
      def test_init_with_unknown_action_raises_error(self):
          with self.assertRaises(ImproperlyConfigured):
              ServeView.as_view(action='unknown')
  
      def test_get_with_custom_key(self):
          """
          Test that that the key can be changed on the view
          """
          # Generate signature
          signature = generate_signature(self.image.id, 'fill-800x600', key='custom')
  
          # Get the image
          response = self.client.get(reverse('wagtailimages_serve_custom_key', args=(signature, self.image.id, 'fill-800x600')) + 'test.png')
  
          # Check response
          self.assertEqual(response.status_code, 200)
  
      def test_get_with_custom_key_using_default_key(self):
          """
          Test that that the key can be changed on the view
  
          This tests that the default key no longer works when the key is changed on the view
          """
          # Generate signature
          signature = generate_signature(self.image.id, 'fill-800x600')
  
          # Get the image
          response = self.client.get(reverse('wagtailimages_serve_custom_key', args=(signature, self.image.id, 'fill-800x600')) + 'test.png')
  
          # Check response
          self.assertEqual(response.status_code, 403)
  
      def test_get_invalid_signature(self):
          """
          Test that an invalid signature returns a 403 response
          """
          # Generate a signature for the incorrect image id
          signature = generate_signature(self.image.id + 1, 'fill-800x600')
  
          # Get the image
          response = self.client.get(reverse('wagtailimages_serve', args=(signature, self.image.id, 'fill-800x600')))
  
          # Check response
          self.assertEqual(response.status_code, 403)
  
      def test_get_invalid_filter_spec(self):
          """
          Test that an invalid filter spec returns a 400 response
  
          This is very unlikely to happen in reality. A user would have
          to create signature for the invalid filter spec which can't be
          done with Wagtails built in URL generator. We should test it
          anyway though.
          """
          # Generate a signature with the invalid filterspec
          signature = generate_signature(self.image.id, 'bad-filter-spec')
  
          # Get the image
          response = self.client.get(reverse('wagtailimages_serve', args=(signature, self.image.id, 'bad-filter-spec')))
  
          # Check response
          self.assertEqual(response.status_code, 400)
  
      def test_get_missing_source_image_file(self):
          """
          Test that a missing image file gives a 410 response
  
          When the source image file is missing, it is presumed deleted so we
          return a 410 "Gone" response.
          """
          # Delete the image file
          os.remove(self.image.file.path)
  
          # Get the image
          signature = generate_signature(self.image.id, 'fill-800x600')
          response = self.client.get(reverse('wagtailimages_serve', args=(signature, self.image.id, 'fill-800x600')))
  
          # Check response
          self.assertEqual(response.status_code, 410)
  
  
  class TestFrontendSendfileView(TestCase):
  
      def setUp(self):
          self.image = Image.objects.create(
              title="Test image",
              file=get_test_image_file(),
          )
  
      @override_settings(SENDFILE_BACKEND='sendfile.backends.development')
      @unittest.skipIf(not sendfile_mod, 'Missing django-sendfile app.')
      def test_sendfile_nobackend(self):
          signature = generate_signature(self.image.id, 'fill-800x600')
          response = self.client.get(reverse('wagtailimages_sendfile',
                                             args=(signature, self.image.id,
                                                   'fill-800x600')))
  
          self.assertEqual(response.status_code, 200)
          self.assertEqual(response['Content-Type'], 'image/png')
  
      @override_settings(SENDFILE_BACKEND='sendfile.backends.development')
      def test_sendfile_dummy_backend(self):
          signature = generate_signature(self.image.id, 'fill-800x600')
          response = self.client.get(reverse('wagtailimages_sendfile_dummy',
                                             args=(signature, self.image.id,
                                                   'fill-800x600')))
  
          self.assertEqual(response.status_code, 200)
          self.assertTrue(response.content, 'Dummy backend response')
  
  
  class TestRect(TestCase):
      def test_init(self):
          rect = Rect(100, 150, 200, 250)
          self.assertEqual(rect.left, 100)
          self.assertEqual(rect.top, 150)
          self.assertEqual(rect.right, 200)
          self.assertEqual(rect.bottom, 250)
  
      def test_equality(self):
          self.assertEqual(Rect(100, 150, 200, 250), Rect(100, 150, 200, 250))
          self.assertNotEqual(Rect(100, 150, 200, 250), Rect(10, 15, 20, 25))
  
      def test_getitem(self):
          rect = Rect(100, 150, 200, 250)
          self.assertEqual(rect[0], 100)
          self.assertEqual(rect[1], 150)
          self.assertEqual(rect[2], 200)
          self.assertEqual(rect[3], 250)
          self.assertRaises(IndexError, rect.__getitem__, 4)
  
      def test_as_tuple(self):
          rect = Rect(100, 150, 200, 250)
          self.assertEqual(rect.as_tuple(), (100, 150, 200, 250))
  
      def test_size(self):
          rect = Rect(100, 150, 200, 350)
          self.assertIsInstance(rect.size, Vector)
          self.assertEqual(rect.size, (100, 200))
          self.assertEqual(rect.width, 100)
          self.assertEqual(rect.height, 200)
  
      def test_set_size_with_tuple(self):
          rect = Rect(100, 150, 200, 350)
          rect.size = (200, 400)
          self.assertEqual(rect, (50, 50, 250, 450))
  
      def test_set_size_with_vector(self):
          rect = Rect(100, 150, 200, 350)
          rect.size = Vector(200, 400)
          self.assertEqual(rect, (50, 50, 250, 450))
  
      def test_centroid(self):
          rect = Rect(100, 150, 200, 350)
          self.assertIsInstance(rect.centroid, Vector)
          self.assertEqual(rect.centroid, (150, 250))
          self.assertEqual(rect.x, 150)
          self.assertEqual(rect.y, 250)
          self.assertEqual(rect.centroid_x, 150)
          self.assertEqual(rect.centroid_y, 250)
  
      def test_set_centroid_with_tuple(self):
          rect = Rect(100, 150, 200, 350)
          rect.centroid = (500, 500)
          self.assertEqual(rect, (450, 400, 550, 600))
  
      def test_set_centroid_with_vector(self):
          rect = Rect(100, 150, 200, 350)
          rect.centroid = Vector(500, 500)
          self.assertEqual(rect, (450, 400, 550, 600))
  
      def test_repr(self):
          rect = Rect(100, 150, 200, 250)
          self.assertEqual(repr(rect), "Rect(left: 100, top: 150, right: 200, bottom: 250)")
  
      def test_from_point(self):
          rect = Rect.from_point(100, 200, 50, 20)
          self.assertEqual(rect, Rect(75, 190, 125, 210))
  
  
  class TestGetImageForm(TestCase, WagtailTestUtils):
      def test_fields(self):
          form = get_image_form(Image)
  
          self.assertEqual(list(form.base_fields.keys()), [
              'title',
              'file',
              'collection',
              'tags',
              'focal_point_x',
              'focal_point_y',
              'focal_point_width',
              'focal_point_height',
          ])
  
      def test_admin_form_fields_attribute(self):
          form = get_image_form(CustomImage)
  
          self.assertEqual(list(form.base_fields.keys()), [
              'title',
              'file',
              'collection',
              'tags',
              'focal_point_x',
              'focal_point_y',
              'focal_point_width',
              'focal_point_height',
              'caption',
          ])
  
      def test_file_field(self):
          form = get_image_form(WagtailImage)
  
          self.assertIsInstance(form.base_fields['file'], WagtailImageField)
          self.assertIsInstance(form.base_fields['file'].widget, forms.FileInput)
  
      def test_tags_field(self):
          form = get_image_form(WagtailImage)
  
          self.assertIsInstance(form.base_fields['tags'], TagField)
          self.assertIsInstance(form.base_fields['tags'].widget, TagWidget)
  
      def test_focal_point_fields(self):
          form = get_image_form(WagtailImage)
  
          self.assertIsInstance(form.base_fields['focal_point_x'], forms.IntegerField)
          self.assertIsInstance(form.base_fields['focal_point_y'], forms.IntegerField)
          self.assertIsInstance(form.base_fields['focal_point_width'], forms.IntegerField)
          self.assertIsInstance(form.base_fields['focal_point_height'], forms.IntegerField)
  
          self.assertIsInstance(form.base_fields['focal_point_x'].widget, forms.HiddenInput)
          self.assertIsInstance(form.base_fields['focal_point_y'].widget, forms.HiddenInput)
          self.assertIsInstance(form.base_fields['focal_point_width'].widget, forms.HiddenInput)
          self.assertIsInstance(form.base_fields['focal_point_height'].widget, forms.HiddenInput)
  
  
  class TestRenditionFilenames(TestCase):
      # Can't create image in setUp as we need a unique filename for each test.
      # This stops Django appending some rubbish to the filename which makes
      # the assertions difficult.
  
      def test_normal_filter(self):
          image = Image.objects.create(
              title="Test image",
              file=get_test_image_file(filename='test_rf1.png'),
          )
          rendition = image.get_rendition('width-100')
  
          self.assertEqual(rendition.file.name, 'images/test_rf1.width-100.png')
  
      def test_fill_filter(self):
          image = Image.objects.create(
              title="Test image",
              file=get_test_image_file(filename='test_rf2.png'),
          )
          rendition = image.get_rendition('fill-100x100')
  
          self.assertEqual(rendition.file.name, 'images/test_rf2.2e16d0ba.fill-100x100.png')
  
      def test_fill_filter_with_focal_point(self):
          image = Image.objects.create(
              title="Test image",
              file=get_test_image_file(filename='test_rf3.png'),
          )
          image.set_focal_point(Rect(100, 100, 200, 200))
          image.save()
  
          rendition = image.get_rendition('fill-100x100')
  
          self.assertEqual(rendition.file.name, 'images/test_rf3.15ee4958.fill-100x100.png')
  
      def test_filter_with_pipe_gets_dotted(self):
          image = Image.objects.create(
              title="Test image",
              file=get_test_image_file(filename='test_rf4.png'),
          )
          image.set_focal_point(Rect(100, 100, 200, 200))
          image.save()
  
          rendition = image.get_rendition('fill-200x200|height-150')
  
          self.assertEqual(rendition.file.name, 'images/test_rf4.15ee4958.fill-200x200.height-150.png')
  
  
  class TestDifferentUpload(TestCase):
      def test_upload_path(self):
          image = CustomImageFilePath.objects.create(
              title="Test image",
              file=get_test_image_file(),
          )
  
          second_image = CustomImageFilePath.objects.create(
              title="Test Image",
              file=get_test_image_file(colour='black'),
  
          )
  
          # The files should be uploaded based on it's content, not just
          # it's filename
          self.assertFalse(image.file.url == second_image.file.url)
  
  
  class TestGetImageModel(WagtailTestUtils, TestCase):
      @override_settings(WAGTAILIMAGES_IMAGE_MODEL='tests.CustomImage')
      def test_custom_get_image_model(self):
          """Test get_image_model with a custom image model"""
          self.assertIs(get_image_model(), CustomImage)
  
      @override_settings(WAGTAILIMAGES_IMAGE_MODEL='tests.CustomImage')
      def test_custom_get_image_model_string(self):
          """Test get_image_model_string with a custom image model"""
          self.assertEqual(get_image_model_string(), 'tests.CustomImage')
  
      @override_settings()
      def test_standard_get_image_model(self):
          """Test get_image_model with no WAGTAILIMAGES_IMAGE_MODEL"""
          del settings.WAGTAILIMAGES_IMAGE_MODEL
          from wagtail.images.models import Image
          self.assertIs(get_image_model(), Image)
  
      @override_settings()
      def test_standard_get_image_model_string(self):
          """Test get_image_model_STRING with no WAGTAILIMAGES_IMAGE_MODEL"""
          del settings.WAGTAILIMAGES_IMAGE_MODEL
          self.assertEqual(get_image_model_string(), 'wagtailimages.Image')
  
      @override_settings(WAGTAILIMAGES_IMAGE_MODEL='tests.UnknownModel')
      def test_unknown_get_image_model(self):
          """Test get_image_model with an unknown model"""
          with self.assertRaises(ImproperlyConfigured):
              get_image_model()
  
      @override_settings(WAGTAILIMAGES_IMAGE_MODEL='invalid-string')
      def test_invalid_get_image_model(self):
          """Test get_image_model with an invalid model string"""
          with self.assertRaises(ImproperlyConfigured):
              get_image_model()
  