Introduced a get_hashed_content function and actually enabled the tests for the CSS hash method tests.

This commit is contained in:
Jannis Leidel
2011-11-25 17:01:27 +01:00
parent 86f98db217
commit c45d8722db
6 changed files with 82 additions and 62 deletions

View File

@@ -101,6 +101,19 @@ def get_hashed_mtime(filename, length=12):
return get_hexdigest(mtime, length)
def get_hashed_content(filename, length=12):
try:
filename = os.path.realpath(filename)
except OSError:
return None
hash_file = open(filename)
try:
content = hash_file.read()
finally:
hash_file.close()
return get_hexdigest(content, length)
def cache_get(key):
packed_val = cache.get(key)
if packed_val is None:

View File

@@ -2,7 +2,8 @@ import os
import re
import posixpath
from compressor.cache import get_hexdigest, get_hashed_mtime
from compressor.cache import (get_hexdigest, get_hashed_mtime,
get_hashed_content)
from compressor.conf import settings
from compressor.filters import FilterBase, FilterError
from compressor.utils import staticfiles
@@ -61,12 +62,8 @@ class CssAbsoluteFilter(FilterBase):
if filename:
if settings.COMPRESS_CSS_HASHING_METHOD == "mtime":
suffix = get_hashed_mtime(filename)
elif settings.COMPRESS_CSS_HASHING_METHOD == "hash":
hash_file = open(filename)
try:
suffix = get_hexdigest(hash_file.read(), 12)
finally:
hash_file.close()
elif settings.COMPRESS_CSS_HASHING_METHOD in ("hash", "content"):
suffix = get_hashed_content(filename)
else:
raise FilterError('COMPRESS_CSS_HASHING_METHOD is configured '
'with an unknown method (%s).')

View File

@@ -45,6 +45,10 @@ v1.2.0
- Correctly handle offline compressing files that are found in ``{% if %}``
template blocks.
- Renamed the second option for the ``COMPRESS_CSS_HASHING_METHOD`` setting
from ``'hash'`` to ``'content'`` to better describe what it does. The old
name is also supported, as well as the default being ``'mtime'``.
v1.1.1
------

View File

@@ -97,8 +97,8 @@ A filter that normalizes the URLs used in ``url()`` CSS statements.
- ``COMPRESS_CSS_HASHING_METHOD`` -- The method to use when calculating
the hash to append to processed URLs. Either ``'mtime'`` (default) or
``'hash'``. Use the latter in case you're using multiple server to serve
your static files.
``'content'``. Use the latter in case you're using multiple server to
serve your static files.
``compressor.filters.csstidy.CSSTidyFilter``
""""""""""""""""""""""""""""""""""""""""""""

View File

@@ -1,8 +1,12 @@
from .base import CompressorTestCase, CssMediaTestCase, VerboseTestCase, CacheBackendTestCase
from .filters import CssTidyTestCase, PrecompilerTestCase, CssMinTestCase, CssAbsolutizingTestCase, CssDataUriTestCase
from .base import (CompressorTestCase, CssMediaTestCase, VerboseTestCase,
CacheBackendTestCase)
from .filters import (CssTidyTestCase, PrecompilerTestCase, CssMinTestCase,
CssAbsolutizingTestCase, CssAbsolutizingTestCaseWithHash,
CssDataUriTestCase)
from .jinja2ext import TestJinja2CompressorExtension
from .offline import OfflineGenerationTestCase
from .parsers import LxmlParserTests, Html5LibParserTests, BeautifulSoupParserTests, HtmlParserTests
from .parsers import (LxmlParserTests, Html5LibParserTests,
BeautifulSoupParserTests, HtmlParserTests)
from .signals import PostCompressSignalTestCase
from .storages import StorageTestCase
from .templatetags import TemplatetagTestCase, PrecompilerTemplatetagTestCase

View File

@@ -5,14 +5,15 @@ from unittest2 import skipIf
from django.test import TestCase
from compressor.cache import get_hashed_mtime
from compressor.cache import get_hashed_mtime, get_hashed_content
from compressor.conf import settings
from compressor.css import CssCompressor
from compressor.utils import find_command
from compressor.filters.base import CompilerFilter
from compressor.filters.cssmin import CSSMinFilter
from compressor.filters.css_default import CssAbsoluteFilter
from .templatetags import render
from .base import css_tag, test_dir
from .base import test_dir
class CssTidyTestCase(TestCase):
@@ -39,7 +40,7 @@ class PrecompilerTestCase(TestCase):
self.filename = os.path.join(test_dir, 'media/css/one.css')
with open(self.filename) as f:
self.content = f.read()
self.test_precompiler = os.path.join(test_dir, 'precompiler.py')
self.test_precompiler = os.path.join(test_dir, 'precompiler.py')
def test_precompiler_infile_outfile(self):
command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler)
@@ -47,29 +48,28 @@ class PrecompilerTestCase(TestCase):
self.assertEqual(u"body { color:#990; }", compiler.input())
def test_precompiler_infile_stdout(self):
command = '%s %s -f {infile}' % (sys.executable, self.test_precompiler)
command = '%s %s -f {infile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }%s" % os.linesep, compiler.input())
def test_precompiler_stdin_outfile(self):
command = '%s %s -o {outfile}' % (sys.executable, self.test_precompiler)
command = '%s %s -o {outfile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }", compiler.input())
def test_precompiler_stdin_stdout(self):
command = '%s %s' % (sys.executable, self.test_precompiler)
command = '%s %s' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }%s" % os.linesep, compiler.input())
def test_precompiler_stdin_stdout_filename(self):
command = '%s %s' % (sys.executable, self.test_precompiler)
command = '%s %s' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command)
self.assertEqual(u"body { color:#990; }%s" % os.linesep, compiler.input())
class CssMinTestCase(TestCase):
def test_cssmin_filter(self):
from compressor.filters.cssmin import CSSMinFilter
content = """p {
@@ -78,78 +78,88 @@ class CssMinTestCase(TestCase):
}
"""
output = "p{background:#369 url('../../images/image.gif')}"
output = "p{background:#369 url('../../images/image.gif')}"
self.assertEqual(output, CSSMinFilter(content).output())
class CssAbsolutizingTestCase(TestCase):
hashing_method = 'mtime'
hashing_func = staticmethod(get_hashed_mtime)
content = "p { background: url('../../img/python.png') }"
def setUp(self):
self.old_enabled = settings.COMPRESS_ENABLED
self.old_url = settings.COMPRESS_URL
self.old_hashing_method = settings.COMPRESS_CSS_HASHING_METHOD
settings.COMPRESS_ENABLED = True
settings.COMPRESS_URL = '/media/'
settings.COMPRESS_CSS_HASHING_METHOD = 'mtime'
settings.COMPRESS_CSS_HASHING_METHOD = self.hashing_method
self.css = """
<link rel="stylesheet" href="/media/css/url/url1.css" type="text/css">
<link rel="stylesheet" href="/media/css/url/2/url2.css" type="text/css">
"""
self.css_node = CssCompressor(self.css)
def suffix_method(self, filename):
return get_hashed_mtime(filename)
def tearDown(self):
settings.COMPRESS_ENABLED = self.old_enabled
settings.COMPRESS_URL = self.old_url
settings.COMPRESS_CSS_HASHING_METHOD = self.old_hashing_method
def test_css_absolute_filter(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png')
content = "p { background: url('../../img/python.png') }"
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.suffix_method(imagefilename))
filter = CssAbsoluteFilter(content)
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
filter = CssAbsoluteFilter(self.content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'http://media.example.com/'
filter = CssAbsoluteFilter(content)
filter = CssAbsoluteFilter(self.content)
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.suffix_method(imagefilename))
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_absolute_filter_https(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png')
content = "p { background: url('../../img/python.png') }"
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.suffix_method(imagefilename))
filter = CssAbsoluteFilter(content)
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
filter = CssAbsoluteFilter(self.content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'https://media.example.com/'
filter = CssAbsoluteFilter(content)
filter = CssAbsoluteFilter(self.content)
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.suffix_method(imagefilename))
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_absolute_filter_relative_path(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.TEST_DIR, 'whatever', '..', 'media', 'whatever/../css/url/test.css')
imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png')
content = "p { background: url('../../img/python.png') }"
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.suffix_method(imagefilename))
filter = CssAbsoluteFilter(content)
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
filter = CssAbsoluteFilter(self.content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'https://media.example.com/'
filter = CssAbsoluteFilter(content)
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.suffix_method(imagefilename))
filter = CssAbsoluteFilter(self.content)
output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_hunks(self):
hash_dict = {
'hash1': self.suffix_method(os.path.join(settings.COMPRESS_ROOT, 'img/python.png')),
'hash2': self.suffix_method(os.path.join(settings.COMPRESS_ROOT, 'img/add.png')),
'hash1': self.hashing_func(os.path.join(settings.COMPRESS_ROOT, 'img/python.png')),
'hash2': self.hashing_func(os.path.join(settings.COMPRESS_ROOT, 'img/add.png')),
}
out = [u"p { background: url('/media/img/python.png?%(hash1)s'); }\np { background: url('/media/img/python.png?%(hash1)s'); }\np { background: url('/media/img/python.png?%(hash1)s'); }\np { background: url('/media/img/python.png?%(hash1)s'); }\n" % hash_dict,
u"p { background: url('/media/img/add.png?%(hash2)s'); }\np { background: url('/media/img/add.png?%(hash2)s'); }\np { background: url('/media/img/add.png?%(hash2)s'); }\np { background: url('/media/img/add.png?%(hash2)s'); }\n" % hash_dict]
hunks = [h for m, h in self.css_node.hunks()]
self.assertEqual(out, hunks)
self.assertEqual([u"""\
p { background: url('/media/img/python.png?%(hash1)s'); }
p { background: url('/media/img/python.png?%(hash1)s'); }
p { background: url('/media/img/python.png?%(hash1)s'); }
p { background: url('/media/img/python.png?%(hash1)s'); }
""" % hash_dict,
u"""\
p { background: url('/media/img/add.png?%(hash2)s'); }
p { background: url('/media/img/add.png?%(hash2)s'); }
p { background: url('/media/img/add.png?%(hash2)s'); }
p { background: url('/media/img/add.png?%(hash2)s'); }
""" % hash_dict], hunks)
def test_guess_filename(self):
import urllib
from compressor.filters.css_default import CssAbsoluteFilter
for base_url in ('/media/', 'http://media.example.com/'):
settings.COMPRESS_URL = base_url
url = '%s/img/python.png' % settings.COMPRESS_URL.rstrip('/')
@@ -158,24 +168,19 @@ class CssAbsolutizingTestCase(TestCase):
filter = CssAbsoluteFilter(content)
self.assertEqual(path, filter.guess_filename(url))
class CssAbsolutizingTestCaseWithHash(CssAbsolutizingTestCase):
hashing_method = 'content'
hashing_func = staticmethod(get_hashed_content)
def setUp(self):
settings.COMPRESS_ENABLED = True
settings.COMPRESS_URL = '/media/'
settings.COMPRESS_CSS_HASHING_METHOD = 'hash'
super(CssAbsolutizingTestCaseWithHash, self).setUp()
self.css = """
<link rel="stylesheet" href="/media/css/url/url1.css" type="text/css" charset="utf-8">
<link rel="stylesheet" href="/media/css/url/2/url2.css" type="text/css" charset="utf-8">
"""
self.css_node = CssCompressor(self.css)
def suffix_method(self, filename):
f = open(filename)
suffix = "H%s" % (get_hexdigest(f.read(), 12), )
f.close()
return suffix
class CssDataUriTestCase(TestCase):
def setUp(self):
@@ -196,6 +201,3 @@ class CssDataUriTestCase(TestCase):
out = [u'.add { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9W7YvBYOkhlkoqCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61CijSIIasOvv94VTUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI0Wgx80SBblpKtE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsmahCPdwyw75uw9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bAfWAH6RGi0HglWNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM0OKsoVwBG/1VMzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/HfFkERTzfFj8w8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8BXjWG3FgNHc9XPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3WUdNFJqLGFVPC4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH62kHOVEE+VQnjahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64TNf0mczcnnQyu/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJggg=="); }\n.python { background-image: url("/media/img/python.png?%s"); }\n.datauri { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0 vr4MkhoXe0rZigAAAABJRU5ErkJggg=="); }\n' % datauri_hash]
hunks = [h for m, h in self.css_node.hunks()]
self.assertEqual(out, hunks)