def test_restrict_mediatype():
    """Test mime type validation.
    """
    counter = image_hook_counter()
    ADDINS = [feed_image_restrict_mediatypes(('image/png', 'image/gif')), counter]

    class TestFeedImage(feedev.File):
        content = ""
        def headers(p):
            if p == 1:   return {'Content-Type': 'text/plain'}
            elif p == 2: return {'Content-Type': 'image/jpeg'}
            elif p == 3: return {'Content-Type': 'image/png; charset=ISO-8859-1'}  # charsets are correctly parsed out
            elif p == 4: return {'Content-Type': 'image/png'}

    class TestFeed(feedev.Feed):
        content = FeedWithImage % (TestFeedImage.url)

        def pass1(feed):
            assert counter.success == 0
        def pass2(feed):
            assert counter.success == 0
        def pass3(feed):
            assert counter.success == 1
        def pass4(feed):
            assert counter.success == 2

    feedev.testcaller()
def test_update_every():
    """Test timed update restriction.
    """
    counter = image_hook_counter()
    ADDINS = [feed_image_restrict_frequency(delta=20), counter]

    class TestFeedImage(feedev.File):
        pass

    class TestFeed(feedev.Feed):
        content = FeedWithImage % TestFeedImage.url

        def pass1(feed):
            assert counter.success == 1

        def pass2(feed):
            # we're not yet updating the image again...
            assert counter.success == 1
            # ...this also doesn't count as a failure (an existing images would be kept)
            assert counter.failure == 0
            # ...but let's advance the clock for the next pass
            datetime.datetime.modify(seconds=30)

        def pass3(feed):
            assert counter.success == 2

    MockDateTime.install()
    try:
        feedev.testcaller()
    finally:
        MockDateTime.uninstall()
def test_failure_reset():
    """Image fails if no valid extension (=no image).
    """

    counter = image_hook_counter()
    ADDINS = [feed_image_restrict_extensions(('png', 'gif')), counter]

    class ValidExtensionImage(feedev.File):
        url = 'http://images/image.png'
    class InvalidExtensionImage(feedev.File):
        url = 'http://images/image.jpeg'

    class TestFeed(feedev.Feed):
        def content(p):
            if p == 1:   image = ValidExtensionImage
            elif p == 2: image = InvalidExtensionImage
            return FeedWithImage % saxutils.escape(image.url)

        def pass1(feed):
            assert counter.failure == 0

        def pass2(feed):
            # invalid extension cause the image to fail completely
            assert counter.failure == 1

    feedev.testcaller()
def test_max_size_by_content_length():
    """Test validation against ``Content-Length`` header.
    """
    counter = image_hook_counter()
    ADDINS = [feed_image_restrict_size(max_size=10), counter]

    class TestFeedImage(feedev.File):
        content = ""
        def headers(p):
            if p == 1: return {'Content-Length': '15'}  # nose that those are strings, just like real headers # TODO: make the testframework ensure that header values are always strings
            else: return {'Content-Length': '5'}

    class TestFeed(feedev.Feed):
        content = FeedWithImage % (TestFeedImage.url)

        def pass1(feed):
            # at this point, the image is too large and is ignored
            assert counter.success == 0
            # also, processing failed (=no image)
            assert counter.failure == 1

        def pass2(feed):
            # it's been fixed, we meet the limit
            assert counter.success == 1

    feedev.testcaller()
def test_max_size():
    """Test live validation of file size, effective in case a
    Content-Length header is missing.
    """
    counter = image_hook_counter()
    ADDINS = [feed_image_restrict_size(max_size=10), image_reader, counter]

    class TestFeedImage(feedev.File):
        def content(p):
            if p == 1: return "b"*15
            else: return "b"*5

    class TestFeed(feedev.Feed):
        content = FeedWithImage % TestFeedImage.url

        def pass1(feed):
            # at this point, the image is to large and is ignored
            assert counter.success == 0
            # also, processing failed (=no image)
            assert counter.failure == 1

        def pass2(feed):
            # it's been fixed, we meet the limit
            assert counter.success == 1

    feedev.testcaller()
def test_restrict_extensions():
    """Test file extension validation.
    """
    counter = image_hook_counter()
    ADDINS = [feed_image_restrict_extensions(('png', 'gif')), counter]

    class JpegImage(feedev.File):
        url = 'http://images/image.jpeg'
    class NonImageFile(feedev.File):
        url = 'http://documents/stuff.xml'
    class PngImage(feedev.File):
        url = 'http://images/image.png'
    class ImageWithQueryString(feedev.File):
        url = 'http://images/image.png?q=x&r=3'
    class ImageWitoutExtension(feedev.File):
        url = 'http://images/image'
    class ImageWithExtensionInQuery(feedev.File):
        url = 'http://images/image?x=66&filename=.png'
    class ImageWithoutExtensionButValidContent(feedev.File):
        url = 'http://images/validimage'
        content = ValidPNGImage

    class TestFeed(feedev.Feed):
        def content(p):
            if p == 1:   image = JpegImage
            elif p == 2: image = NonImageFile
            elif p == 3: image = PngImage
            elif p == 4: image = ImageWithQueryString
            elif p == 5: image = ImageWitoutExtension
            elif p == 6: image = ImageWithExtensionInQuery
            elif p == 7: image = ImageWithoutExtensionButValidContent
            return FeedWithImage % saxutils.escape(image.url)

        def pass1(feed):
            assert counter.success == 0
        def pass2(feed):
            assert counter.success == 0
        def pass3(feed):
            assert counter.success == 1
        def pass4(feed):
            assert counter.success == 2
        def pass5(feed):
            # Image has no extension in URL, and extension cannot be
            # determined via content type or content (since the former
            # is missing and the latter is invalid). In this case,
            # we fail, the image is not handled.
            assert counter.success == 2
        def pass6(feed):
            # As in pass5, no extension is available. The extension
            # from the querystring is currently ignored.
            assert counter.success == 2
        def pass7(feed):
            # the image is once again lacking an extension, but this
            # time the content is valid and the extension can be
            # deferred from it.
            assert counter.success == 3

    feedev.testcaller()
def test_failure_reset():
    """Image fails if no valid extension (=no image).
    """

    counter = image_hook_counter()
    ADDINS = [feed_image_restrict_mediatypes(('image/png', 'image/gif')), counter]

    class TestFeedImage(feedev.File):
        content = ""
        def headers(p):
            if p == 1:   return {'Content-Type': 'image/png'}
            elif p == 2: return {'Content-Type': 'image/jpeg'}

    class TestFeed(feedev.Feed):
        content = FeedWithImage % TestFeedImage.url

        def pass1(feed):
            assert counter.failure == 0

        def pass2(feed):
            # invalid media types cause the image to fail completely
            assert counter.failure == 1

    feedev.testcaller()
def test_basic():
    """Test that hooks are triggered correctly, depending on whether a
    feed image is given or not.

    This is in addition to the specific tests for each hook.
    """
    counter = image_hook_counter()
    ADDINS = [handle_feed_images(), counter]

    class TestFeed(feedev.Feed):
        content = """
        <rss><channel>
            <title>test-feed</title>
            {% =2 %}<image></image>{% end %}
            {% =3 %}<image><url></url></image>{% end %}
            {% =4 %}<image><url>http://host/image.gif</url></image>{% end %}
        </channel></rss>
        """

        def pass1(feed):
            # no tag at all
            assert counter.called == 0

        def pass2(feed):
            # no url tag
            assert counter.called == 0

        def pass3(feed):
            # empty url
            assert counter.called == 0

        def pass4(feed):
            # finally, everything is ok!
            assert counter.called == 1

    feedev.testcaller()