diff --git a/djblets/util/templatetags/tests.py b/djblets/util/templatetags/tests.py
deleted file mode 100644
index b1d19469a917438f6665c9b55dcd7f71052ff5bc..0000000000000000000000000000000000000000
--- a/djblets/util/templatetags/tests.py
+++ /dev/null
@@ -1,337 +0,0 @@
-from __future__ import unicode_literals
-
-from django.forms import Form
-from django.http import HttpRequest
-from django.template import Context, Template
-from pipeline.conf import settings as pipeline_settings
-
-from djblets.testing.testcases import TestCase
-from djblets.util.templatetags.djblets_js import json_dumps
-
-
-class JSTagTests(TestCase):
-    """Unit tests for djblets_js template tags."""
-
-    def test_json_dumps_xss(self):
-        """Testing json_dumps doesn't allow XSS injection"""
-        # This is bug 3406.
-        obj = {
-            'xss': '</script><script>alert(1);</script>'
-        }
-
-        self.assertEqual(
-            json_dumps(obj),
-            '{"xss": "\\u003C/script\\u003E\\u003Cscript\\u003E'
-            'alert(1);\\u003C/script\\u003E"}')
-
-
-class UtilsTagTests(TestCase):
-    """Tests for djblets_utils template tags."""
-
-    def test_attr_with_value(self):
-        """Testing attr template tag with value"""
-        t = Template('{% load djblets_utils %}'
-                     '<span{% attr "class" %}\n'
-                     '{%  if some_bool %}truthy{% endif %}\n'
-                     '{% endattr %}>')
-
-        self.assertEqual(
-            t.render(Context({
-                'some_bool': True,
-            })),
-            '<span class="truthy">')
-
-    def test_attr_without_value(self):
-        """Testing attr template tag with no value"""
-        t = Template('{% load djblets_utils %}'
-                     '<span{% attr "class" %}\n'
-                     '{%  if some_bool %}falsy{% endif %}\n'
-                     '{% endattr %}>')
-
-        self.assertEqual(
-            t.render(Context({
-                'some_bool': False,
-            })),
-            '<span>')
-
-    def test_attr_escapes_value(self):
-        """Testing attr template tag escapes value"""
-        t = Template('{% load djblets_utils %}'
-                     '<span{% attr "data-foo" %}<hello>{% endattr %}>')
-
-        self.assertEqual(
-            t.render(Context()),
-            '<span data-foo="&lt;hello&gt;">')
-
-    def test_attr_condenses_whitespace(self):
-        """Testing attr template tag condenses/strips extra whitespace by
-        default
-        """
-        t = Template('{% load djblets_utils %}'
-                     '<span{% attr "data-foo" %}\n'
-                     'some    \n\n'
-                     'value\n'
-                     '{% endattr %}>')
-
-        self.assertEqual(
-            t.render(Context()),
-            '<span data-foo="some value">')
-
-    def test_attr_with_nocondense_preserves_whitespace(self):
-        """Testing attr template tag with "nocondense" option preserves
-        whitespace
-        """
-        t = Template('{% load djblets_utils %}'
-                     '<span{% attr "data-foo" nocondense %}\n'
-                     'some    \n\n'
-                     'value\n'
-                     '{% endattr %}>')
-
-        self.assertEqual(
-            t.render(Context()),
-            '<span data-foo="\nsome    \n\nvalue\n">')
-
-    def test_definevar(self):
-        """Testing definevar template tag"""
-        t = Template('{% load djblets_utils %}'
-                     '{% definevar "myvar" %}\n'
-                     'test{{num}}\n'
-                     '{% enddefinevar %}'
-                     '{{myvar}}')
-
-        self.assertEqual(
-            t.render(Context({
-                'num': 123,
-            })),
-            '\ntest123\n')
-
-    def test_definevar_with_strip(self):
-        """Testing definevar template tag with strip option"""
-        t = Template('{% load djblets_utils %}'
-                     '{% definevar "myvar" strip %}\n'
-                     '<span>\n'
-                     ' <strong>\n'
-                     '  test{{num}}\n'
-                     ' </strong>\n'
-                     '</span>\n'
-                     '{% enddefinevar %}'
-                     '[{{myvar}}]')
-
-        self.assertEqual(
-            t.render(Context({
-                'num': 123,
-            })),
-            '[<span>\n <strong>\n  test123\n </strong>\n</span>]')
-
-    def test_definevar_with_spaceless(self):
-        """Testing definevar template tag with spaceless option"""
-        t = Template('{% load djblets_utils %}'
-                     '{% definevar "myvar" spaceless %}\n'
-                     '<span>\n'
-                     ' <strong>\n'
-                     '  test{{num}}\n'
-                     ' </strong>\n'
-                     '</span>\n'
-                     '{% enddefinevar %}'
-                     '[{{myvar}}]')
-
-        self.assertEqual(
-            t.render(Context({
-                'num': 123,
-            })),
-            '[<span><strong>\n  test123\n </strong></span>]')
-
-    def test_definevar_with_unsafe(self):
-        """Testing definevar template tag with unsafe option"""
-        t = Template('{% load djblets_utils %}'
-                     '{% definevar "myvar" unsafe %}<hello>{% enddefinevar %}'
-                     '{{myvar}}')
-
-        self.assertEqual(t.render(Context()), '&lt;hello&gt;')
-
-    def test_include_as_string_tag(self):
-        """Testing include_as_string template tag"""
-        t = Template('{% load djblets_utils %}'
-                     '{% include_as_string template_name %}')
-
-        self.assertEqual(
-            t.render(Context({
-                'template_name': 'testing/foo.html',
-                'foo': 1,
-                'bar': 2,
-            })),
-            "'1 2\\\n'")
-
-    def test_querystring_with_tag(self):
-        """Testing querystring_with template tag"""
-        t = Template('{% load djblets_utils %}'
-                     '{% querystring_with "foo" "bar" %}')
-
-        self.assertEqual(
-            t.render(Context({
-                'request': HttpRequest()
-            })),
-            '?foo=bar')
-
-    def test_querystring_with_tag_existing_query(self):
-        """Testing querystring_with template tag with an existing query"""
-        t = Template('{% load djblets_utils %}'
-                     '{% querystring_with "foo" "bar" %}')
-
-        request = HttpRequest()
-        request.GET = {
-            'a': '1',
-            'b': '2',
-        }
-
-        self.assertEqual(
-            t.render(Context({
-                'request': request
-            })),
-            '?a=1&amp;b=2&amp;foo=bar')
-
-    def test_querystring_with_existing_query_override(self):
-        """Testing querystring_with template tag with an existing query that
-        gets overriden
-        """
-        t = Template('{% load djblets_utils %}'
-                     '{% querystring_with "foo" "bar" %}')
-
-        request = HttpRequest()
-        request.GET = {
-            'foo': 'foo',
-            'bar': 'baz',
-        }
-
-        self.assertEqual(
-            t.render(Context({
-                'request': request
-            })),
-            '?bar=baz&amp;foo=bar')
-
-
-class EmailTagTests(TestCase):
-    """Tests for djblets_email template tags."""
-
-    def test_quoted_email_tag(self):
-        """Testing quoted_email template tag"""
-        t = Template('{% load djblets_email %}'
-                     '{% quoted_email template_name %}')
-
-        self.assertEqual(
-            t.render(Context({
-                'template_name': 'testing/foo.html',
-                'foo': 'baz',
-                'bar': 'qux',
-            })),
-            '> baz qux\n'
-            '>')
-
-
-class FormsTests(TestCase):
-    """Unit tests for the djblets_forms template tags."""
-
-    def test_get_fieldsets_modern(self):
-        """Testing the get_fieldsets template filter with modern fieldsets"""
-        class MyForm(Form):
-            class Meta:
-                fieldsets = (
-                    ('Test 1', {
-                        'description': 'This is test 1',
-                        'fields': ('field_1', 'field_2'),
-                    }),
-                    (None, {
-                        'description': 'This is test 2',
-                        'fields': ('field_3', 'field_4'),
-                    }),
-                )
-
-        t = Template(
-            '{% load djblets_forms %}'
-            '{% for title, fieldset in form|get_fieldsets %}'
-            'Title: {{title}}\n'
-            'Description: {{fieldset.description}}\n'
-            'Fields: {{fieldset.fields|join:","}}\n'
-            '{% endfor %}'
-        )
-
-        self.assertEqual(
-            t.render(Context({
-                'form': MyForm(),
-            })),
-            'Title: Test 1\n'
-            'Description: This is test 1\n'
-            'Fields: field_1,field_2\n'
-            'Title: None\n'
-            'Description: This is test 2\n'
-            'Fields: field_3,field_4\n')
-
-    def test_get_fieldsets_legacy(self):
-        """Testing the get_fieldsets template filter with legacy fieldsets"""
-        class MyForm(Form):
-            class Meta:
-                fieldsets = (
-                    {
-                        'title': 'Test 1',
-                        'description': 'This is test 1',
-                        'fields': ('field_1', 'field_2'),
-                    },
-                    {
-                        'description': 'This is test 2',
-                        'fields': ('field_3', 'field_4'),
-                    }
-                )
-
-        t = Template(
-            '{% load djblets_forms %}'
-            '{% for title, fieldset in form|get_fieldsets %}'
-            'Title: {{title}}\n'
-            'Description: {{fieldset.description}}\n'
-            'Fields: {{fieldset.fields|join:","}}\n'
-            '{% endfor %}'
-        )
-
-        self.assertEqual(
-            t.render(Context({
-                'form': MyForm(),
-            })),
-            'Title: Test 1\n'
-            'Description: This is test 1\n'
-            'Fields: field_1,field_2\n'
-            'Title: None\n'
-            'Description: This is test 2\n'
-            'Fields: field_3,field_4\n')
-
-
-class CompressedTagTests(TestCase):
-    """Tests for compressed template tags."""
-
-    def test_compressed_css_tag(self):
-        """Testing compressed_css template tag"""
-        pipeline_settings.STYLESHEETS = {
-            'test': {
-                'source_filenames': [],
-                'output_filename': 'test.css',
-            }
-        }
-
-        t = Template('{% load compressed %}'
-                     '{% compressed_css "test" %}')
-
-        self.assertEqual(t.render(Context({'test': 'test'})),
-                         '/test.css\n')
-
-    def test_compressed_js_tag(self):
-        """Testing compressed_js template tag"""
-        pipeline_settings.JAVASCRIPT = {
-            'test': {
-                'source_filenames': [],
-                'output_filename': 'test.js',
-            }
-        }
-
-        t = Template('{% load compressed %}'
-                     '{% compressed_js "test" %}')
-
-        self.assertEqual(t.render(Context({'test': 'test'})),
-                         '/test.js\n')
diff --git a/djblets/util/tests.py b/djblets/util/tests.py
deleted file mode 100644
index eaab2d7e3db845e43fd5be1758484cee99b01adf..0000000000000000000000000000000000000000
--- a/djblets/util/tests.py
+++ /dev/null
@@ -1,431 +0,0 @@
-#
-# tests.py -- Unit tests for classes in djblets.util
-#
-# Copyright (c) 2007-2009  Christian Hammond
-# Copyright (c) 2007-2009  David Trowbridge
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-from __future__ import unicode_literals
-
-import datetime
-import pytz
-import unittest
-
-from django.http import HttpRequest
-from django.template import Context, Template, TemplateSyntaxError
-from django.utils.html import strip_spaces_between_tags
-
-from djblets.testing.testcases import TestCase, TagTest
-from djblets.util import dates
-from djblets.util.decorators import cached_property
-from djblets.util.http import (get_http_accept_lists,
-                               get_http_requested_mimetype,
-                               is_mimetype_a)
-from djblets.util.serializers import DjbletsJSONEncoder
-from djblets.util.templatetags import djblets_email, djblets_utils
-
-
-def normalize_html(s):
-    return strip_spaces_between_tags(s).strip()
-
-
-class BoxTest(TagTest):
-    def testPlain(self):
-        """Testing box tag"""
-        t = Template('{% load djblets_deco %}'
-                     '{% box %}content{% endbox %}')
-
-        self.assertEqual(normalize_html(t.render(Context({}))),
-                         '<div class="box-container"><div class="box">' +
-                         '<div class="box-inner">\ncontent\n  ' +
-                         '</div></div></div>')
-
-    def testClass(self):
-        """Testing box tag (with extra class)"""
-        t = Template('{% load djblets_deco %}'
-                     '{% box "class" %}content{% endbox %}')
-
-        self.assertEqual(normalize_html(t.render(Context({}))),
-                         '<div class="box-container"><div class="box class">' +
-                         '<div class="box-inner">\ncontent\n  ' +
-                         '</div></div></div>')
-
-    def testError(self):
-        """Testing box tag (invalid usage)"""
-        with self.assertRaises(TemplateSyntaxError):
-            Template('{% load djblets_deco %}'
-                     '{% box "class" "foo" %}content{% endbox %}')
-
-
-class ErrorBoxTest(TagTest):
-    def testPlain(self):
-        """Testing errorbox tag"""
-        t = Template('{% load djblets_deco %}'
-                     '{% errorbox %}content{% enderrorbox %}')
-
-        self.assertEqual(normalize_html(t.render(Context({}))),
-                         '<div class="errorbox">\ncontent\n</div>')
-
-    def testId(self):
-        """Testing errorbox tag (with id)"""
-        t = Template('{% load djblets_deco %}'
-                     '{% errorbox "id" %}content{% enderrorbox %}')
-
-        self.assertEqual(normalize_html(t.render(Context({}))),
-                         '<div class="errorbox" id="id">\ncontent\n</div>')
-
-    def testError(self):
-        """Testing errorbox tag (invalid usage)"""
-        with self.assertRaises(TemplateSyntaxError):
-            Template('{% load djblets_deco %}'
-                     '{% box "class" "foo" "foo" %}content{% endbox %}')
-
-
-class HttpTest(TestCase):
-    def setUp(self):
-        self.request = HttpRequest()
-        self.request.META['HTTP_ACCEPT'] = \
-            'application/json;q=0.5,application/xml,text/plain;q=0.0,*/*;q=0.0'
-
-    def test_http_accept_lists(self):
-        """Testing djblets.http.get_http_accept_lists"""
-
-        acceptable_mimetypes, unacceptable_mimetypes = \
-            get_http_accept_lists(self.request)
-
-        self.assertEqual(acceptable_mimetypes,
-                         ['application/xml', 'application/json'])
-        self.assertEqual(unacceptable_mimetypes, ['text/plain', '*/*'])
-
-    def test_get_requested_mimetype_with_supported_mimetype(self):
-        """Testing djblets.http.get_requested_mimetype with supported
-        mimetype
-        """
-        self.assertEqual(
-            get_http_requested_mimetype(self.request, ['foo/bar',
-                                                       'application/json']),
-            'application/json')
-        self.assertEqual(
-            get_http_requested_mimetype(self.request, ['application/xml']),
-            'application/xml')
-        self.assertEqual(
-            get_http_requested_mimetype(self.request, ['application/json',
-                                                       'application/xml']),
-            'application/xml')
-
-    def test_get_requested_mimetype_with_no_consensus(self):
-        """Testing djblets.http.get_requested_mimetype with no consensus
-        between client and server
-        """
-        self.request = HttpRequest()
-        self.request.META['HTTP_ACCEPT'] = ('text/html,application/xhtml+xml,'
-                                            'application/xml;q=0.9,*/*;q=0.8')
-
-        self.assertEqual(
-            get_http_requested_mimetype(self.request, ['application/json',
-                                                       'application/x-foo']),
-            'application/json')
-
-    def test_get_requested_mimetype_with_wildcard_supported_mimetype(self):
-        """Testing djblets.http.get_requested_mimetype with supported */*
-        mimetype
-        """
-        self.request = HttpRequest()
-        self.request.META['HTTP_ACCEPT'] = '*/*'
-        self.assertEqual(
-            get_http_requested_mimetype(self.request, ['application/json',
-                                                       'application/xml']),
-            'application/json')
-
-    def test_get_requested_mimetype_with_unsupported_mimetype(self):
-        """Testing djblets.http.get_requested_mimetype with unsupported
-        mimetype
-        """
-        self.assertEqual(
-            get_http_requested_mimetype(self.request, ['text/plain']),
-            None)
-        self.assertEqual(
-            get_http_requested_mimetype(self.request, ['foo/bar']),
-            None)
-
-    def test_is_mimetype_a(self):
-        """Testing djblets.util.http.is_mimetype_a"""
-        self.assertTrue(is_mimetype_a('application/json',
-                                      'application/json'))
-        self.assertTrue(is_mimetype_a('application/vnd.foo+json',
-                                      'application/json'))
-        self.assertFalse(is_mimetype_a('application/xml',
-                                       'application/json'))
-        self.assertFalse(is_mimetype_a('foo/vnd.bar+json',
-                                       'application/json'))
-
-
-class AgeIdTest(TagTest):
-    def setUp(self):
-        TagTest.setUp(self)
-
-        self.now = datetime.datetime.utcnow()
-
-        self.context = {
-            'now': self.now,
-            'minus1': self.now - datetime.timedelta(1),
-            'minus2': self.now - datetime.timedelta(2),
-            'minus3': self.now - datetime.timedelta(3),
-            'minus4': self.now - datetime.timedelta(4),
-        }
-
-    def testNow(self):
-        """Testing ageid tag (now)"""
-        self.assertEqual(djblets_utils.ageid(self.now), 'age1')
-
-    def testMinus1(self):
-        """Testing ageid tag (yesterday)"""
-        self.assertEqual(djblets_utils.ageid(self.now - datetime.timedelta(1)),
-                         'age2')
-
-    def testMinus2(self):
-        """Testing ageid tag (two days ago)"""
-        self.assertEqual(djblets_utils.ageid(self.now - datetime.timedelta(2)),
-                         'age3')
-
-    def testMinus3(self):
-        """Testing ageid tag (three days ago)"""
-        self.assertEqual(djblets_utils.ageid(self.now - datetime.timedelta(3)),
-                         'age4')
-
-    def testMinus4(self):
-        """Testing ageid tag (four days ago)"""
-        self.assertEqual(djblets_utils.ageid(self.now - datetime.timedelta(4)),
-                         'age5')
-
-    def testNotDateTime(self):
-        """Testing ageid tag (non-datetime object)"""
-        class Foo:
-            def __init__(self, now):
-                self.day = now.day
-                self.month = now.month
-                self.year = now.year
-
-        self.assertEqual(djblets_utils.ageid(Foo(self.now)), 'age1')
-
-
-class TestEscapeSpaces(unittest.TestCase):
-    def test(self):
-        """Testing escapespaces filter"""
-        self.assertEqual(djblets_utils.escapespaces('Hi there'),
-                         'Hi there')
-        self.assertEqual(djblets_utils.escapespaces('Hi  there'),
-                         'Hi&nbsp; there')
-        self.assertEqual(djblets_utils.escapespaces('Hi  there\n'),
-                         'Hi&nbsp; there<br />')
-
-
-class TestHumanizeList(unittest.TestCase):
-    def test0(self):
-        """Testing humanize_list filter (length 0)"""
-        self.assertEqual(djblets_utils.humanize_list([]), '')
-
-    def test1(self):
-        """Testing humanize_list filter (length 1)"""
-        self.assertEqual(djblets_utils.humanize_list(['a']), 'a')
-
-    def test2(self):
-        """Testing humanize_list filter (length 2)"""
-        self.assertEqual(djblets_utils.humanize_list(['a', 'b']), 'a and b')
-
-    def test3(self):
-        """Testing humanize_list filter (length 3)"""
-        self.assertEqual(djblets_utils.humanize_list(['a', 'b', 'c']),
-                         'a, b and c')
-
-    def test4(self):
-        """Testing humanize_list filter (length 4)"""
-        self.assertEqual(djblets_utils.humanize_list(['a', 'b', 'c', 'd']),
-                         'a, b, c, and d')
-
-
-class TestIndent(unittest.TestCase):
-    def test(self):
-        """Testing indent filter"""
-        self.assertEqual(djblets_utils.indent('foo'), '    foo')
-        self.assertEqual(djblets_utils.indent('foo', 3), '   foo')
-        self.assertEqual(djblets_utils.indent('foo\nbar'),
-                         '    foo\n    bar')
-
-
-class QuotedEmailTagTest(TagTest):
-    def testInvalid(self):
-        """Testing quoted_email tag (invalid usage)"""
-        with self.assertRaises(TemplateSyntaxError):
-            Template('{% load djblets_email %}'
-                     '{% quoted_email %}content{% end_quoted_email %}')
-
-
-class CondenseTagTest(TagTest):
-    tag_content = 'foo\nbar\n\n\n\n\n\n\nfoobar!'
-
-    def test_plain(self):
-        """Testing condense tag"""
-        t = Template('{% load djblets_email %}'
-                     '{% condense %}' +
-                     self.tag_content +
-                     '{% endcondense %}')
-
-        self.assertEqual(normalize_html(t.render(Context({}))),
-                         "foo\nbar\n\n\nfoobar!")
-
-    def test_with_max_indents(self):
-        """Testing condense tag with custom max_indents"""
-        t = Template('{% load djblets_email %}'
-                     '{% condense 1 %}' +
-                     self.tag_content +
-                     '{% endcondense %}')
-        self.assertEqual(normalize_html(t.render(Context({}))),
-                         "foo\nbar\nfoobar!")
-
-
-class QuoteTextFilterTest(unittest.TestCase):
-    def testPlain(self):
-        """Testing quote_text filter (default level)"""
-        self.assertEqual(djblets_email.quote_text('foo\nbar'),
-                         "> foo\n> bar")
-
-    def testLevel2(self):
-        """Testing quote_text filter (level 2)"""
-        self.assertEqual(djblets_email.quote_text('foo\nbar', 2),
-                         "> > foo\n> > bar")
-
-
-class DjbletsJSONEncoderTests(TestCase):
-    """Tests for djblets.util.serializers.DjbletsJSONEncoder."""
-
-    def test_object_to_json(self):
-        """Testing DjbletsJSONEncoder.encode for an object with a to_json()
-        method
-        """
-        class TestObject(object):
-            def to_json(self):
-                return {
-                    'foo': 1,
-                }
-
-        obj = TestObject()
-        encoder = DjbletsJSONEncoder()
-
-        self.assertEqual(encoder.encode(obj), '{"foo": 1}')
-
-    def test_datetime(self):
-        """Testing DjbletsJSONENcoder.encode with datetimes"""
-        encoder = DjbletsJSONEncoder()
-        self.assertEqual(
-            encoder.encode(datetime.datetime(2016, 8, 26, 3, 3, 26, 123456)),
-            '"2016-08-26T03:03:26"')
-
-    def test_datetime_with_strip_ms(self):
-        """Testing DjbletsJSONENcoder.encode with datetimes when using
-        strip_datetime_ms=False
-        """
-        encoder = DjbletsJSONEncoder(strip_datetime_ms=False)
-        self.assertEqual(
-            encoder.encode(datetime.datetime(2016, 8, 26, 3, 3, 26, 123456)),
-            '"2016-08-26T03:03:26.123"')
-
-
-class DatesTests(TestCase):
-    """Tests for djblets.util.dates."""
-
-    def test_http_date_with_datetime(self):
-        """Testing http_date with datetime"""
-        date_time = datetime.datetime(2016, 8, 26, 3, 3, 26, 123456)
-        self.assertEqual(
-            dates.http_date(date_time),
-            'Fri, 26 Aug 2016 03:03:26 GMT')
-
-    def test_http_date_with_date_string(self):
-        """Testing http_date with date string"""
-        date = '20/06/2016'
-        self.assertEqual(dates.http_date(date), date)
-
-    def test_http_date_with_unix_timestamp(self):
-        """Testing http_date with unix timestamp"""
-        unix_timestamp = '1466424000'
-        self.assertEqual(dates.http_date(unix_timestamp), unix_timestamp)
-
-    def test_get_latest_timestamp_with_empty_list(self):
-        """Testing get_latest_timestamp without any timestamps in the
-         list
-         """
-        self.assertEqual(dates.get_latest_timestamp([]), None)
-
-    def test_get_latest_timestamp_with_jumbled_list(self):
-        """Testing get_latest_timestamp with unsorted date time
-        list.
-         """
-        self.assertEqual(dates.get_latest_timestamp([
-            '1453204800',
-            '1466337600',
-            '1466424000'
-        ]), '1466424000')
-
-    def test_date_time_is_in_utc(self):
-        """Testing get_tz_aware_utcnow returns UTC time."""
-        utc_time = dates.get_tz_aware_utcnow()
-        self.assertEqual(utc_time.tzinfo, pytz.utc)
-
-
-class DecoratorTests(TestCase):
-    """Tests for djblets.util.decorators."""
-
-    def test_cached_property(self):
-        """Testing @cached_property retains attributes and docstring"""
-        class MyClass(object):
-            def expensive_method(self, state=[0]):
-                state[0] += 1
-
-                return state[0]
-
-            def my_prop1(self):
-                """This is my docstring."""
-                return self.expensive_method()
-
-            my_prop1.some_attr = 105
-            my_prop1 = cached_property(my_prop1)
-
-            @cached_property
-            def my_prop2(self):
-                """Another one!"""
-                return 'foo'
-
-        instance = MyClass()
-
-        self.assertEqual(instance.my_prop1, 1)
-        self.assertEqual(instance.my_prop1, 1)
-        self.assertEqual(instance.my_prop2, 'foo')
-
-        prop1_instance = instance.__class__.__dict__['my_prop1']
-        self.assertEqual(prop1_instance.__name__, 'my_prop1')
-        self.assertEqual(prop1_instance.__doc__, 'This is my docstring.')
-        self.assertEqual(getattr(prop1_instance, 'some_attr'), 105)
-
-        prop2_instance = instance.__class__.__dict__['my_prop2']
-        self.assertEqual(prop2_instance.__name__, 'my_prop2')
-        self.assertEqual(prop2_instance.__doc__, 'Another one!')
-        self.assertFalse(hasattr(prop2_instance, 'some_attr'))
diff --git a/djblets/util/tests/__init__.py b/djblets/util/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/djblets/util/tests/test_compressed_tags.py b/djblets/util/tests/test_compressed_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..2258bc7ddfb7dad7a5e909ca163a372813d9ff78
--- /dev/null
+++ b/djblets/util/tests/test_compressed_tags.py
@@ -0,0 +1,42 @@
+"""Unit tests for djblets.util.templatetags.djblets_forms."""
+
+from __future__ import unicode_literals
+
+from django.template import Context, Template
+from pipeline.conf import settings as pipeline_settings
+
+from djblets.testing.testcases import TestCase
+
+
+class CompressedTagsTests(TestCase):
+    """Unit tests for the {% compressed_* %} template tags."""
+
+    def test_compressed_css_tag(self):
+        """Testing {% compressed_css %}"""
+        pipeline_settings.STYLESHEETS = {
+            'test': {
+                'source_filenames': [],
+                'output_filename': 'test.css',
+            }
+        }
+
+        t = Template('{% load compressed %}'
+                     '{% compressed_css "test" %}')
+
+        self.assertEqual(t.render(Context({'test': 'test'})),
+                         '/test.css\n')
+
+    def test_compressed_js_tag(self):
+        """Testing {% compressed_js %}"""
+        pipeline_settings.JAVASCRIPT = {
+            'test': {
+                'source_filenames': [],
+                'output_filename': 'test.js',
+            }
+        }
+
+        t = Template('{% load compressed %}'
+                     '{% compressed_js "test" %}')
+
+        self.assertEqual(t.render(Context({'test': 'test'})),
+                         '/test.js\n')
diff --git a/djblets/util/tests/test_dates.py b/djblets/util/tests/test_dates.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b757a4993fce70c4828d661198b85c0cc7485d8
--- /dev/null
+++ b/djblets/util/tests/test_dates.py
@@ -0,0 +1,52 @@
+"""Unit tests for djblets.util.dates."""
+
+from __future__ import unicode_literals
+
+from datetime import datetime
+
+import pytz
+
+from djblets.testing.testcases import TestCase
+from djblets.util.dates import (get_latest_timestamp, get_tz_aware_utcnow,
+                                http_date)
+
+
+class DatesTests(TestCase):
+    """Unit tests for djblets.util.dates."""
+
+    def test_http_date_with_datetime(self):
+        """Testing http_date with datetime"""
+        date_time = datetime(2016, 8, 26, 3, 3, 26, 123456)
+        self.assertEqual(http_date(date_time),
+                         'Fri, 26 Aug 2016 03:03:26 GMT')
+
+    def test_http_date_with_date_string(self):
+        """Testing http_date with date string"""
+        date = '20/06/2016'
+        self.assertEqual(http_date(date), date)
+
+    def test_http_date_with_unix_timestamp(self):
+        """Testing http_date with unix timestamp"""
+        unix_timestamp = '1466424000'
+        self.assertEqual(http_date(unix_timestamp), unix_timestamp)
+
+    def test_get_latest_timestamp_with_empty_list(self):
+        """Testing get_latest_timestamp without any timestamps in the list"""
+        self.assertIsNone(get_latest_timestamp([]))
+
+    def test_get_latest_timestamp_with_jumbled_list(self):
+        """Testing get_latest_timestamp with unsorted date time
+        list.
+         """
+        self.assertEqual(
+            get_latest_timestamp([
+                '1453204800',
+                '1466337600',
+                '1466424000'
+            ]),
+            '1466424000')
+
+    def test_date_time_is_in_utc(self):
+        """Testing get_tz_aware_utcnow returns UTC time."""
+        utc_time = get_tz_aware_utcnow()
+        self.assertEqual(utc_time.tzinfo, pytz.utc)
diff --git a/djblets/util/tests/test_decorators.py b/djblets/util/tests/test_decorators.py
new file mode 100644
index 0000000000000000000000000000000000000000..948448b6b4557ddaf89664cb25ff354fef4fb119
--- /dev/null
+++ b/djblets/util/tests/test_decorators.py
@@ -0,0 +1,46 @@
+"""Unit tests for djblets.util.decorators."""
+
+from __future__ import unicode_literals
+
+from djblets.testing.testcases import TestCase
+from djblets.util.decorators import cached_property
+
+
+class DecoratorTests(TestCase):
+    """Unit tests for djblets.util.decorators."""
+
+    def test_cached_property(self):
+        """Testing @cached_property retains attributes and docstring"""
+        class MyClass(object):
+            def expensive_method(self, state=[0]):
+                state[0] += 1
+
+                return state[0]
+
+            def my_prop1(self):
+                """This is my docstring."""
+                return self.expensive_method()
+
+            my_prop1.some_attr = 105
+            my_prop1 = cached_property(my_prop1)
+
+            @cached_property
+            def my_prop2(self):
+                """Another one!"""
+                return 'foo'
+
+        instance = MyClass()
+
+        self.assertEqual(instance.my_prop1, 1)
+        self.assertEqual(instance.my_prop1, 1)
+        self.assertEqual(instance.my_prop2, 'foo')
+
+        prop1_instance = instance.__class__.__dict__['my_prop1']
+        self.assertEqual(prop1_instance.__name__, 'my_prop1')
+        self.assertEqual(prop1_instance.__doc__, 'This is my docstring.')
+        self.assertEqual(getattr(prop1_instance, 'some_attr'), 105)
+
+        prop2_instance = instance.__class__.__dict__['my_prop2']
+        self.assertEqual(prop2_instance.__name__, 'my_prop2')
+        self.assertEqual(prop2_instance.__doc__, 'Another one!')
+        self.assertFalse(hasattr(prop2_instance, 'some_attr'))
diff --git a/djblets/util/tests/test_djblets_deco_tags.py b/djblets/util/tests/test_djblets_deco_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d4d59b39f95b9e881621bd16e71bb6a93dddf4a
--- /dev/null
+++ b/djblets/util/tests/test_djblets_deco_tags.py
@@ -0,0 +1,67 @@
+"""Unit tests for djblets.util.templatetags.djblets_deco."""
+
+from __future__ import unicode_literals
+
+from django.template import Context, Template, TemplateSyntaxError
+
+from djblets.testing.testcases import TagTest
+
+
+class BoxTagTests(TagTest):
+    """Unit tests for the {% box %} template tag."""
+
+    def test_plain(self):
+        """Testing {% box %}"""
+        t = Template('{% load djblets_deco %}'
+                     '{% box %}content{% endbox %}')
+
+        self.assertHTMLEqual(
+            t.render(Context({})),
+            '<div class="box-container"><div class="box">'
+            '<div class="box-inner">\ncontent\n  '
+            '</div></div></div>')
+
+    def test_with_class(self):
+        """Testing {% box %} with CSS class"""
+        t = Template('{% load djblets_deco %}'
+                     '{% box "class" %}content{% endbox %}')
+
+        self.assertHTMLEqual(
+            t.render(Context({})),
+            '<div class="box-container"><div class="box class">'
+            '<div class="box-inner">\ncontent\n  '
+            '</div></div></div>')
+
+    def test_with_extra_arg_error(self):
+        """Testing {% box %} with extra argument error"""
+        with self.assertRaises(TemplateSyntaxError):
+            Template('{% load djblets_deco %}'
+                     '{% box "class" "foo" %}content{% endbox %}')
+
+
+class ErrorBoxTests(TagTest):
+    """Unit tests for the {% errorbox %} template tag."""
+
+    def test_plain(self):
+        """Testing {% errorbox %}"""
+        t = Template('{% load djblets_deco %}'
+                     '{% errorbox %}content{% enderrorbox %}')
+
+        self.assertHTMLEqual(
+            t.render(Context({})),
+            '<div class="errorbox">\ncontent\n</div>')
+
+    def test_with_id(self):
+        """Testing {% errorbox %} with element ID"""
+        t = Template('{% load djblets_deco %}'
+                     '{% errorbox "id" %}content{% enderrorbox %}')
+
+        self.assertHTMLEqual(
+            t.render(Context({})),
+            '<div class="errorbox" id="id">\ncontent\n</div>')
+
+    def test_with_extra_arg_error(self):
+        """Testing {% errorbox %} with extra argument error"""
+        with self.assertRaises(TemplateSyntaxError):
+            Template('{% load djblets_deco %}'
+                     '{% box "class" "foo" "foo" %}content{% endbox %}')
diff --git a/djblets/util/tests/test_djblets_email_tags.py b/djblets/util/tests/test_djblets_email_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb07cafb8597517ac29b8cf1093ac2437a91a4b5
--- /dev/null
+++ b/djblets/util/tests/test_djblets_email_tags.py
@@ -0,0 +1,71 @@
+"""Unit tests for djblets.util.templatetags.djblets_email."""
+
+from __future__ import unicode_literals
+
+from django.template import Context, Template, TemplateSyntaxError
+
+from djblets.testing.testcases import TagTest, TestCase
+from djblets.util.templatetags.djblets_email import quote_text
+
+
+class CondenseTagTests(TagTest):
+    """Unit tests for the {% condense %} template tag."""
+
+    tag_content = 'foo\nbar\n\n\n\n\n\n\nfoobar!'
+
+    def test_with_defaults(self):
+        """Testing {% condense %}"""
+        t = Template('{% load djblets_email %}'
+                     '{% condense %}' +
+                     self.tag_content +
+                     '{% endcondense %}')
+
+        self.assertHTMLEqual(t.render(Context({})),
+                             'foo\nbar\n\n\nfoobar!')
+
+    def test_with_max_indents(self):
+        """Testing {% condense %} with custom max_indents"""
+        t = Template('{% load djblets_email %}'
+                     '{% condense 1 %}' +
+                     self.tag_content +
+                     '{% endcondense %}')
+        self.assertHTMLEqual(t.render(Context({})),
+                             'foo\nbar\nfoobar!')
+
+
+class QuotedEmailTagTests(TagTest):
+    """Unit tests for the {% quoted_email %} template tag."""
+
+    def test_basic_usage(self):
+        """Testing {% quoted_email %}"""
+        t = Template('{% load djblets_email %}'
+                     '{% quoted_email template_name %}')
+
+        self.assertEqual(
+            t.render(Context({
+                'template_name': 'testing/foo.html',
+                'foo': 'baz',
+                'bar': 'qux',
+            })),
+            '> baz qux\n'
+            '>')
+
+    def test_with_invalid(self):
+        """Testing {% quoted_email %} with invalid usage"""
+        with self.assertRaises(TemplateSyntaxError):
+            Template('{% load djblets_email %}'
+                     '{% quoted_email %}content{% end_quoted_email %}')
+
+
+class QuoteTextFilterTests(TestCase):
+    """Unit tests for the {{...|quote_text}} template filter."""
+
+    def test_with_default_level(self):
+        """Testing {{...|quote_text}} with default quote level"""
+        self.assertEqual(quote_text('foo\nbar'),
+                         '> foo\n> bar')
+
+    def testLevel2(self):
+        """Testing {{...|quote_text}} with custom quote level"""
+        self.assertEqual(quote_text('foo\nbar', 2),
+                         '> > foo\n> > bar')
diff --git a/djblets/util/tests/test_djblets_forms_tags.py b/djblets/util/tests/test_djblets_forms_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5bb887cf59af81d592331e22963ef552537083e
--- /dev/null
+++ b/djblets/util/tests/test_djblets_forms_tags.py
@@ -0,0 +1,83 @@
+"""Unit tests for djblets.util.templatetags.djblets_forms."""
+
+from __future__ import unicode_literals
+
+from django.forms import Form
+from django.template import Context, Template
+
+from djblets.testing.testcases import TestCase
+
+
+class GetFieldsetsFilterTests(TestCase):
+    """Unit tests for the {{...|get_fieldsets}} template filter."""
+
+    def test_with_modern_fieldsets(self):
+        """Testing {{...|get_fieldsets}} with modern fieldsets"""
+        class MyForm(Form):
+            class Meta:
+                fieldsets = (
+                    ('Test 1', {
+                        'description': 'This is test 1',
+                        'fields': ('field_1', 'field_2'),
+                    }),
+                    (None, {
+                        'description': 'This is test 2',
+                        'fields': ('field_3', 'field_4'),
+                    }),
+                )
+
+        t = Template(
+            '{% load djblets_forms %}'
+            '{% for title, fieldset in form|get_fieldsets %}'
+            'Title: {{title}}\n'
+            'Description: {{fieldset.description}}\n'
+            'Fields: {{fieldset.fields|join:","}}\n'
+            '{% endfor %}'
+        )
+
+        self.assertEqual(
+            t.render(Context({
+                'form': MyForm(),
+            })),
+            'Title: Test 1\n'
+            'Description: This is test 1\n'
+            'Fields: field_1,field_2\n'
+            'Title: None\n'
+            'Description: This is test 2\n'
+            'Fields: field_3,field_4\n')
+
+    def test_with_legacy_fieldets(self):
+        """Testing {{...|get_fieldsets}} with legacy fieldsets"""
+        class MyForm(Form):
+            class Meta:
+                fieldsets = (
+                    {
+                        'title': 'Test 1',
+                        'description': 'This is test 1',
+                        'fields': ('field_1', 'field_2'),
+                    },
+                    {
+                        'description': 'This is test 2',
+                        'fields': ('field_3', 'field_4'),
+                    }
+                )
+
+        t = Template(
+            '{% load djblets_forms %}'
+            '{% for title, fieldset in form|get_fieldsets %}'
+            'Title: {{title}}\n'
+            'Description: {{fieldset.description}}\n'
+            'Fields: {{fieldset.fields|join:","}}\n'
+            '{% endfor %}'
+        )
+
+        self.assertEqual(
+            t.render(Context({
+                'form': MyForm(),
+            })),
+            'Title: Test 1\n'
+            'Description: This is test 1\n'
+            'Fields: field_1,field_2\n'
+            'Title: None\n'
+            'Description: This is test 2\n'
+            'Fields: field_3,field_4\n')
diff --git a/djblets/util/tests/test_djblets_js_tags.py b/djblets/util/tests/test_djblets_js_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b7ba474c78ae1bcd8b963365293d3981d94bd9d
--- /dev/null
+++ b/djblets/util/tests/test_djblets_js_tags.py
@@ -0,0 +1,22 @@
+"""Unit tests for djblets.util.templatetags.djblets_js."""
+
+from __future__ import unicode_literals
+
+from djblets.testing.testcases import TestCase
+from djblets.util.templatetags.djblets_js import json_dumps
+
+
+class JSONDumpsFilterTests(TestCase):
+    """Unit tests for the {{...|json_dumps}} template filter."""
+
+    def test_prevents_xss(self):
+        """Testing {{...|json_dumps}} doesn't allow XSS injection"""
+        # This is bug 3406.
+        obj = {
+            'xss': '</script><script>alert(1);</script>'
+        }
+
+        self.assertEqual(
+            json_dumps(obj),
+            '{"xss": "\\u003C/script\\u003E\\u003Cscript\\u003E'
+            'alert(1);\\u003C/script\\u003E"}')
diff --git a/djblets/util/tests/test_djblets_utils_tags.py b/djblets/util/tests/test_djblets_utils_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..df76ea8be801b3a7b1c53626d44f778b335dccf8
--- /dev/null
+++ b/djblets/util/tests/test_djblets_utils_tags.py
@@ -0,0 +1,317 @@
+"""Unit tests for djblets.util.templatetags.djblets_utils."""
+
+from __future__ import unicode_literals
+
+from datetime import datetime, timedelta
+
+from django.http import HttpRequest
+from django.template import Context, Template
+
+from djblets.testing.testcases import TagTest, TestCase
+from djblets.util.templatetags.djblets_utils import (ageid, escapespaces,
+                                                     humanize_list, indent)
+
+
+class AgeIdTagTests(TagTest):
+    """Unit tests for the {% ageid %} template tag."""
+
+    def setUp(self):
+        super(AgeIdTagTests, self).setUp()
+
+        self.now = datetime.utcnow()
+
+        self.context = {
+            'now': self.now,
+            'minus1': self.now - timedelta(1),
+            'minus2': self.now - timedelta(2),
+            'minus3': self.now - timedelta(3),
+            'minus4': self.now - timedelta(4),
+        }
+
+    def test_with_now(self):
+        """Testing {% ageid %} with now"""
+        self.assertEqual(ageid(self.now), 'age1')
+
+    def test_with_now_minus_1_day(self):
+        """Testing {% ageid %} with yesterday"""
+        self.assertEqual(ageid(self.now - timedelta(1)), 'age2')
+
+    def test_with_now_minus_2_days(self):
+        """Testing {% ageid %} with two days ago"""
+        self.assertEqual(ageid(self.now - timedelta(2)), 'age3')
+
+    def test_with_now_minus_3_days(self):
+        """Testing {% ageid %} with three days ago"""
+        self.assertEqual(ageid(self.now - timedelta(3)), 'age4')
+
+    def test_with_now_minus_4_days(self):
+        """Testing {% ageid %} with four days ago"""
+        self.assertEqual(ageid(self.now - timedelta(4)), 'age5')
+
+    def test_with_non_datetime(self):
+        """Testing {% ageid %} with non-datetime object"""
+        class Foo:
+            def __init__(self, now):
+                self.day = now.day
+                self.month = now.month
+                self.year = now.year
+
+        self.assertEqual(ageid(Foo(self.now)), 'age1')
+
+
+class AttrTagTests(TestCase):
+    """Unit tests for the {% attr %} template tag."""
+
+    def test_with_value(self):
+        """Testing {% attr %} with value"""
+        t = Template('{% load djblets_utils %}'
+                     '<span{% attr "class" %}\n'
+                     '{%  if some_bool %}truthy{% endif %}\n'
+                     '{% endattr %}>')
+
+        self.assertEqual(
+            t.render(Context({
+                'some_bool': True,
+            })),
+            '<span class="truthy">')
+
+    def test_without_value(self):
+        """Testing {% attr %} with no value"""
+        t = Template('{% load djblets_utils %}'
+                     '<span{% attr "class" %}\n'
+                     '{%  if some_bool %}falsy{% endif %}\n'
+                     '{% endattr %}>')
+
+        self.assertEqual(
+            t.render(Context({
+                'some_bool': False,
+            })),
+            '<span>')
+
+    def test_escapes_value(self):
+        """Testing {% attr %} escapes value"""
+        t = Template('{% load djblets_utils %}'
+                     '<span{% attr "data-foo" %}<hello>{% endattr %}>')
+
+        self.assertEqual(
+            t.render(Context()),
+            '<span data-foo="&lt;hello&gt;">')
+
+    def test_condenses_whitespace(self):
+        """Testing {% attr %} condenses/strips extra whitespace by default"""
+        t = Template('{% load djblets_utils %}'
+                     '<span{% attr "data-foo" %}\n'
+                     'some    \n\n'
+                     'value\n'
+                     '{% endattr %}>')
+
+        self.assertEqual(
+            t.render(Context()),
+            '<span data-foo="some value">')
+
+    def test_with_nocondense_preserves_whitespace(self):
+        """Testing {% attr %} with "nocondense" option preserves whitespace"""
+        t = Template('{% load djblets_utils %}'
+                     '<span{% attr "data-foo" nocondense %}\n'
+                     'some    \n\n'
+                     'value\n'
+                     '{% endattr %}>')
+
+        self.assertEqual(
+            t.render(Context()),
+            '<span data-foo="\nsome    \n\nvalue\n">')
+
+
+class DefineVarTagTests(TestCase):
+    """Unit tests for the {% definevar %} template tag."""
+
+    def test_basic_usage(self):
+        """Testing {% definevar %}"""
+        t = Template('{% load djblets_utils %}'
+                     '{% definevar "myvar" %}\n'
+                     'test{{num}}\n'
+                     '{% enddefinevar %}'
+                     '{{myvar}}')
+
+        self.assertEqual(
+            t.render(Context({
+                'num': 123,
+            })),
+            '\ntest123\n')
+
+    def test_with_strip(self):
+        """Testing {% definevar %} with strip option"""
+        t = Template('{% load djblets_utils %}'
+                     '{% definevar "myvar" strip %}\n'
+                     '<span>\n'
+                     ' <strong>\n'
+                     '  test{{num}}\n'
+                     ' </strong>\n'
+                     '</span>\n'
+                     '{% enddefinevar %}'
+                     '[{{myvar}}]')
+
+        self.assertEqual(
+            t.render(Context({
+                'num': 123,
+            })),
+            '[<span>\n <strong>\n  test123\n </strong>\n</span>]')
+
+    def test_with_spaceless(self):
+        """Testing {% definevar %} with spaceless option"""
+        t = Template('{% load djblets_utils %}'
+                     '{% definevar "myvar" spaceless %}\n'
+                     '<span>\n'
+                     ' <strong>\n'
+                     '  test{{num}}\n'
+                     ' </strong>\n'
+                     '</span>\n'
+                     '{% enddefinevar %}'
+                     '[{{myvar}}]')
+
+        self.assertEqual(
+            t.render(Context({
+                'num': 123,
+            })),
+            '[<span><strong>\n  test123\n </strong></span>]')
+
+    def test_with_unsafe(self):
+        """Testing {% definevar %} with unsafe option"""
+        t = Template('{% load djblets_utils %}'
+                     '{% definevar "myvar" unsafe %}<hello>{% enddefinevar %}'
+                     '{{myvar}}')
+
+        self.assertEqual(t.render(Context()), '&lt;hello&gt;')
+
+
+class EscapeSpacesFilterTests(TestCase):
+    """Unit tests for the {{...|escapespaces}} template filter."""
+
+    def test_with_single_space(self):
+        """Testing {{...|escapespaces}} with single space"""
+        self.assertEqual(escapespaces('Hi there'),
+                         'Hi there')
+
+    def test_with_multiple_spaces(self):
+        """Testing {{...|escapespaces}} with multiple consecutive spaces"""
+        self.assertEqual(escapespaces('Hi  there'),
+                         'Hi&nbsp; there')
+
+    def test_with_newline(self):
+        """Testing {{...|escapespaces}} with newline"""
+        self.assertEqual(escapespaces('Hi  there\n'),
+                         'Hi&nbsp; there<br />')
+
+
+class HumanizeListFilterTests(TestCase):
+    """Unit tests for the {{...|humanize_list}} template filter."""
+
+    def test_with_empty_list(self):
+        """Testing {{...|humanize_list}} with empty list"""
+        self.assertEqual(humanize_list([]),
+                         '')
+
+    def test_with_1_item(self):
+        """Testing {{...|humanize_list}} with 1 item"""
+        self.assertEqual(humanize_list(['a']),
+                         'a')
+
+    def test_with_2_items(self):
+        """Testing {{...|humanize_list}} with 2 items"""
+        self.assertEqual(humanize_list(['a', 'b']),
+                         'a and b')
+
+    def test_with_3_items(self):
+        """Testing {{...|humanize_list}} with 3 items"""
+        self.assertEqual(humanize_list(['a', 'b', 'c']),
+                         'a, b and c')
+
+    def test_with_4_items(self):
+        """Testing {{...|humanize_list}} with 4 items"""
+        self.assertEqual(humanize_list(['a', 'b', 'c', 'd']),
+                         'a, b, c, and d')
+
+
+class IncludeAsStringTagTests(TestCase):
+    """Unit tests for the {% include_as_string %} template tag."""
+
+    def test_basic_usage(self):
+        """Testing {% include_as_string %}"""
+        t = Template('{% load djblets_utils %}'
+                     '{% include_as_string template_name %}')
+
+        self.assertEqual(
+            t.render(Context({
+                'template_name': 'testing/foo.html',
+                'foo': 1,
+                'bar': 2,
+            })),
+            "'1 2\\\n'")
+
+
+class IndentFilterTests(TestCase):
+    """Unit tests for the {{...|indent}} template filter."""
+
+    def test_with_default_indent(self):
+        """Testing {{...|indent}} with default indentation level"""
+        self.assertEqual(indent('foo'), '    foo')
+
+    def test_with_custom_indent(self):
+        """Testing {{...|indent}} with custom indentation level"""
+        self.assertEqual(indent('foo', 3), '   foo')
+
+    def test_with_multiple_lines(self):
+        """Testing {{...|indent}} with multiple lines"""
+        self.assertEqual(indent('foo\nbar'),
+                         '    foo\n    bar')
+
+
+class QuerystringWithTagTests(TestCase):
+    """Unit tests for the {% querystring_with %} template tag."""
+
+    def test_basic_usage(self):
+        """Testing {% querystring_with %}"""
+        t = Template('{% load djblets_utils %}'
+                     '{% querystring_with "foo" "bar" %}')
+
+        self.assertEqual(
+            t.render(Context({
+                'request': HttpRequest()
+            })),
+            '?foo=bar')
+
+    def test_with_tag_existing_query(self):
+        """Testing {% querystring_with %} with an existing query"""
+        t = Template('{% load djblets_utils %}'
+                     '{% querystring_with "foo" "bar" %}')
+
+        request = HttpRequest()
+        request.GET = {
+            'a': '1',
+            'b': '2',
+        }
+
+        self.assertEqual(
+            t.render(Context({
+                'request': request
+            })),
+            '?a=1&amp;b=2&amp;foo=bar')
+
+    def test_with_existing_query_override(self):
+        """Testing {% querystring_with %} with an existing query that gets
+        overriden
+        """
+        t = Template('{% load djblets_utils %}'
+                     '{% querystring_with "foo" "bar" %}')
+
+        request = HttpRequest()
+        request.GET = {
+            'foo': 'foo',
+            'bar': 'baz',
+        }
+
+        self.assertEqual(
+            t.render(Context({
+                'request': request
+            })),
+            '?bar=baz&amp;foo=bar')
diff --git a/djblets/util/tests/test_http.py b/djblets/util/tests/test_http.py
new file mode 100644
index 0000000000000000000000000000000000000000..541cc09313b136b8f681f6b8b2bc1ab0ffa63c1b
--- /dev/null
+++ b/djblets/util/tests/test_http.py
@@ -0,0 +1,80 @@
+"""Unit tests for djblets.util.http."""
+
+from __future__ import unicode_literals
+
+from django.http import HttpRequest
+
+from djblets.testing.testcases import TestCase
+from djblets.util.http import (get_http_accept_lists,
+                               get_http_requested_mimetype,
+                               is_mimetype_a)
+
+
+class HttpTests(TestCase):
+    """Unit tests for djblets.util.http."""
+
+    def setUp(self):
+        self.request = HttpRequest()
+        self.request.META['HTTP_ACCEPT'] = \
+            'application/json;q=0.5,application/xml,text/plain;q=0.0,*/*;q=0.0'
+
+    def test_http_accept_lists(self):
+        """Testing get_http_accept_lists"""
+        acceptable_mimetypes, unacceptable_mimetypes = \
+            get_http_accept_lists(self.request)
+
+        self.assertEqual(acceptable_mimetypes,
+                         ['application/xml', 'application/json'])
+        self.assertEqual(unacceptable_mimetypes, ['text/plain', '*/*'])
+
+    def test_get_requested_mimetype_with_supported_mimetype(self):
+        """Testing get_requested_mimetype with supported mimetype"""
+        self.assertEqual(
+            get_http_requested_mimetype(self.request, ['foo/bar',
+                                                       'application/json']),
+            'application/json')
+        self.assertEqual(
+            get_http_requested_mimetype(self.request, ['application/xml']),
+            'application/xml')
+        self.assertEqual(
+            get_http_requested_mimetype(self.request, ['application/json',
+                                                       'application/xml']),
+            'application/xml')
+
+    def test_get_requested_mimetype_with_no_consensus(self):
+        """Testing get_requested_mimetype with no consensus between client and
+        server
+        """
+        self.request.META['HTTP_ACCEPT'] = ('text/html,application/xhtml+xml,'
+                                            'application/xml;q=0.9,*/*;q=0.8')
+
+        self.assertEqual(
+            get_http_requested_mimetype(self.request, ['application/json',
+                                                       'application/x-foo']),
+            'application/json')
+
+    def test_get_requested_mimetype_with_wildcard_supported_mimetype(self):
+        """Testing get_requested_mimetype with supported */* mimetype"""
+        self.request.META['HTTP_ACCEPT'] = '*/*'
+        self.assertEqual(
+            get_http_requested_mimetype(self.request, ['application/json',
+                                                       'application/xml']),
+            'application/json')
+
+    def test_get_requested_mimetype_with_unsupported_mimetype(self):
+        """Testing get_requested_mimetype with unsupported mimetype"""
+        self.assertIsNone(get_http_requested_mimetype(self.request,
+                                                      ['text/plain']))
+        self.assertIsNone(get_http_requested_mimetype(self.request,
+                                                      ['foo/bar']))
+
+    def test_is_mimetype_a(self):
+        """Testing is_mimetype_a"""
+        self.assertTrue(is_mimetype_a('application/json',
+                                      'application/json'))
+        self.assertTrue(is_mimetype_a('application/vnd.foo+json',
+                                      'application/json'))
+        self.assertFalse(is_mimetype_a('application/xml',
+                                       'application/json'))
+        self.assertFalse(is_mimetype_a('foo/vnd.bar+json',
+                                       'application/json'))
diff --git a/djblets/util/tests/test_serializers.py b/djblets/util/tests/test_serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..5940dba92eb4e3eae735039b193d91390a38dbd6
--- /dev/null
+++ b/djblets/util/tests/test_serializers.py
@@ -0,0 +1,43 @@
+"""Unit tests for djblets.util.serializers."""
+
+from __future__ import unicode_literals
+
+from datetime import datetime
+
+from djblets.testing.testcases import TestCase
+from djblets.util.serializers import DjbletsJSONEncoder
+
+
+class DjbletsJSONEncoderTests(TestCase):
+    """Unit tests for djblets.util.serializers.DjbletsJSONEncoder."""
+
+    def test_object_to_json(self):
+        """Testing DjbletsJSONEncoder.encode for an object with a to_json()
+        method
+        """
+        class TestObject(object):
+            def to_json(self):
+                return {
+                    'foo': 1,
+                }
+
+        obj = TestObject()
+        encoder = DjbletsJSONEncoder()
+
+        self.assertEqual(encoder.encode(obj), '{"foo": 1}')
+
+    def test_datetime(self):
+        """Testing DjbletsJSONENcoder.encode with datetimes"""
+        encoder = DjbletsJSONEncoder()
+        self.assertEqual(
+            encoder.encode(datetime(2016, 8, 26, 3, 3, 26, 123456)),
+            '"2016-08-26T03:03:26"')
+
+    def test_datetime_with_strip_ms(self):
+        """Testing DjbletsJSONENcoder.encode with datetimes when using
+        strip_datetime_ms=False
+        """
+        encoder = DjbletsJSONEncoder(strip_datetime_ms=False)
+        self.assertEqual(
+            encoder.encode(datetime(2016, 8, 26, 3, 3, 26, 123456)),
+            '"2016-08-26T03:03:26.123"')
