diff --git a/reviewboard/reviews/tests/test_markdown_review_ui.py b/reviewboard/reviews/tests/test_markdown_review_ui.py
new file mode 100644
index 0000000000000000000000000000000000000000..56ef21097b581f17bc27c2c7c63e47fed9c00523
--- /dev/null
+++ b/reviewboard/reviews/tests/test_markdown_review_ui.py
@@ -0,0 +1,147 @@
+"""Unit tests for reviewboard.reviews.ui.text.MarkdownReviewUI."""
+from __future__ import unicode_literals
+
+from django.test.client import RequestFactory
+
+from reviewboard.reviews.ui.markdownui import MarkdownReviewUI
+from reviewboard.testing import TestCase
+
+
+class MarkdownReviewUITests(TestCase):
+    """Unit tests for reviewboard.reviews.ui.text.MarkdownReviewUI."""
+
+    fixtures = ['test_users']
+
+    def setUp(self):
+        super(MarkdownReviewUITests, self).setUp()
+
+        self.review_request = self.create_review_request()
+        self.attachment_history = self.create_file_attachment_history(
+            self.review_request)
+        self.attachment = self.create_file_attachment(
+            self.review_request,
+            attachment_history=self.attachment_history,
+            attachment_revision=0,
+            caption='Revision 1 caption',
+            orig_filename='revision1.md',
+            mimetype='text/x-markdown',
+            file_content=b'This is revision *1*.')
+        self.review = self.create_review(self.review_request)
+
+    def test_get_extra_context(self):
+        """Testing MarkdownReviewUI.get_extra_context"""
+        new_attachment = self.create_file_attachment(
+            self.review_request,
+            attachment_history=self.attachment_history,
+            attachment_revision=1,
+            caption='Revision 2 caption',
+            orig_filename='revision2.md',
+            mimetype='text/x-markdown',
+            file_content=b'And **this** is revision 2.')
+
+        review_ui = MarkdownReviewUI(review_request=self.review_request,
+                                     obj=new_attachment)
+
+        request = RequestFactory().get('/')
+        extra_context = review_ui.get_extra_context(request)
+
+        self.assertFalse(extra_context['is_diff'])
+        self.assertFalse(extra_context['diff_type_mismatch'])
+        self.assertEqual(extra_context['filename'], 'revision2.md')
+        self.assertEqual(extra_context['revision'], 1)
+        self.assertEqual(extra_context['num_revisions'], 2)
+        self.assertNotIn('diff_caption', extra_context)
+        self.assertNotIn('diff_filename', extra_context)
+        self.assertNotIn('diff_revision', extra_context)
+        self.assertNotIn('source_chunks', extra_context)
+        self.assertNotIn('rendered_chunks', extra_context)
+        self.assertEqual(extra_context['text_lines'],
+                         ['<pre>And **this** is revision 2.</pre>'])
+        self.assertEqual(extra_context['rendered_lines'],
+                         ['<p>And <strong>this</strong> is revision 2.</p>'])
+
+    def test_get_extra_context_with_diff(self):
+        """Testing MarkdownReviewUI.get_extra_context with diff_against_obj"""
+        new_attachment = self.create_file_attachment(
+            self.review_request,
+            attachment_history=self.attachment_history,
+            attachment_revision=1,
+            caption='Revision 2 caption',
+            orig_filename='revision2.md',
+            mimetype='text/x-markdown',
+            file_content=b'And **this** is revision 2.')
+
+        review_ui = MarkdownReviewUI(review_request=self.review_request,
+                                     obj=new_attachment)
+        review_ui.set_diff_against(self.attachment)
+
+        request = RequestFactory().get('/')
+        extra_context = review_ui.get_extra_context(request)
+
+        self.assertTrue(extra_context['is_diff'])
+        self.assertFalse(extra_context['diff_type_mismatch'])
+        self.assertEqual(extra_context['filename'], 'revision2.md')
+        self.assertEqual(extra_context['revision'], 1)
+        self.assertEqual(extra_context['num_revisions'], 2)
+        self.assertEqual(extra_context['diff_caption'], 'Revision 1 caption')
+        self.assertEqual(extra_context['diff_filename'], 'revision1.md')
+        self.assertEqual(extra_context['diff_revision'], 0)
+        self.assertEqual(
+            list(extra_context['source_chunks']),
+            [
+                {
+                    'change': 'replace',
+                    'collapsable': False,
+                    'index': 0,
+                    'lines': [
+                        [
+                            1,
+                            1,
+                            'This is revision <span class="ge">*1*</span>.',
+                            [(0, 1), (4, 4), (17, 20)],
+                            1,
+                            'And <span class="gs">**this**</span> is '
+                            'revision 2.',
+                            [(0, 7), (10, 12), (25, 26)],
+                            False,
+                        ],
+                    ],
+                    'meta': {
+                        'left_headers': [],
+                        'right_headers': [],
+                        'whitespace_chunk': False,
+                        'whitespace_lines': [],
+                    },
+                    'numlines': 1,
+                },
+            ])
+        self.assertEqual(
+            list(extra_context['rendered_chunks']),
+            [
+                {
+                    'change': 'replace',
+                    'collapsable': False,
+                    'index': 0,
+                    'lines': [
+                        [
+                            1,
+                            1,
+                            '<p>This is revision <em>1</em>.</p>',
+                            [(0, 1), (17, 18)],
+                            1,
+                            '<p>And <strong>this</strong> is revision 2.</p>',
+                            [(0, 5), (21, 22)],
+                            False,
+                        ],
+                    ],
+                    'meta': {
+                        'left_headers': [],
+                        'right_headers': [],
+                        'whitespace_chunk': False,
+                        'whitespace_lines': [],
+                    },
+                    'numlines': 1,
+                },
+            ])
+        self.assertNotIn('text_lines', extra_context)
+        self.assertNotIn('rendered_lines', extra_context)
diff --git a/reviewboard/reviews/tests/test_text_based_review_ui.py b/reviewboard/reviews/tests/test_text_based_review_ui.py
new file mode 100644
index 0000000000000000000000000000000000000000..06afe955ab47082f64219ee3e6ab5b3967d0a650
--- /dev/null
+++ b/reviewboard/reviews/tests/test_text_based_review_ui.py
@@ -0,0 +1,118 @@
+"""Unit tests for reviewboard.reviews.ui.text.TextBasedReviewUI."""
+from __future__ import unicode_literals
+
+from django.test.client import RequestFactory
+
+from reviewboard.reviews.ui.text import TextBasedReviewUI
+from reviewboard.testing import TestCase
+
+
+class TextBasedReviewUITests(TestCase):
+    """Unit tests for reviewboard.reviews.ui.text.TextBasedReviewUI."""
+
+    fixtures = ['test_users']
+
+    def setUp(self):
+        super(TextBasedReviewUITests, self).setUp()
+
+        self.review_request = self.create_review_request()
+        self.attachment_history = self.create_file_attachment_history(
+            self.review_request)
+        self.attachment = self.create_file_attachment(
+            self.review_request,
+            attachment_history=self.attachment_history,
+            attachment_revision=0,
+            caption='Revision 1 caption',
+            orig_filename='revision1.txt',
+            mimetype='text/plain',
+            file_content=b'This is revision 1.')
+        self.review = self.create_review(self.review_request)
+
+    def test_get_extra_context(self):
+        """Testing TextBasedReviewUI.get_extra_context"""
+        new_attachment = self.create_file_attachment(
+            self.review_request,
+            attachment_history=self.attachment_history,
+            attachment_revision=1,
+            caption='Revision 2 caption',
+            orig_filename='revision2.txt',
+            mimetype='text/plain',
+            file_content=b'And this is revision 2.')
+
+        review_ui = TextBasedReviewUI(review_request=self.review_request,
+                                      obj=new_attachment)
+
+        request = RequestFactory().get('/')
+        extra_context = review_ui.get_extra_context(request)
+
+        self.assertFalse(extra_context['is_diff'])
+        self.assertFalse(extra_context['diff_type_mismatch'])
+        self.assertEqual(extra_context['filename'], 'revision2.txt')
+        self.assertEqual(extra_context['revision'], 1)
+        self.assertEqual(extra_context['num_revisions'], 2)
+        self.assertNotIn('diff_caption', extra_context)
+        self.assertNotIn('diff_filename', extra_context)
+        self.assertNotIn('diff_revision', extra_context)
+        self.assertNotIn('source_chunks', extra_context)
+        self.assertNotIn('rendered_chunks', extra_context)
+        self.assertEqual(extra_context['text_lines'],
+                         ['<pre>And this is revision 2.</pre>'])
+        self.assertEqual(extra_context['rendered_lines'], [])
+
+    def test_get_extra_context_with_diff(self):
+        """Testing TextBasedReviewUI.get_extra_context with diff_against_obj"""
+        new_attachment = self.create_file_attachment(
+            self.review_request,
+            attachment_history=self.attachment_history,
+            attachment_revision=1,
+            caption='Revision 2 caption',
+            orig_filename='revision2.txt',
+            mimetype='text/plain',
+            file_content=b'And this is revision 2.')
+
+        review_ui = TextBasedReviewUI(review_request=self.review_request,
+                                      obj=new_attachment)
+        review_ui.set_diff_against(self.attachment)
+
+        request = RequestFactory().get('/')
+        extra_context = review_ui.get_extra_context(request)
+
+        self.assertTrue(extra_context['is_diff'])
+        self.assertFalse(extra_context['diff_type_mismatch'])
+        self.assertEqual(extra_context['filename'], 'revision2.txt')
+        self.assertEqual(extra_context['revision'], 1)
+        self.assertEqual(extra_context['num_revisions'], 2)
+        self.assertEqual(extra_context['diff_caption'], 'Revision 1 caption')
+        self.assertEqual(extra_context['diff_filename'], 'revision1.txt')
+        self.assertEqual(extra_context['diff_revision'], 0)
+        self.assertEqual(
+            list(extra_context['source_chunks']),
+            [
+                {
+                    'change': 'replace',
+                    'collapsable': False,
+                    'index': 0,
+                    'lines': [
+                        [
+                            1,
+                            1,
+                            'This is revision 1.',
+                            [(0, 1), (17, 18)],
+                            1,
+                            'And this is revision 2.',
+                            [(0, 5), (21, 22)],
+                            False,
+                        ],
+                    ],
+                    'meta': {
+                        'left_headers': [],
+                        'right_headers': [],
+                        'whitespace_chunk': False,
+                        'whitespace_lines': [],
+                    },
+                    'numlines': 1,
+                },
+            ])
+        self.assertEqual(list(extra_context['rendered_chunks']), [])
+        self.assertNotIn('text_lines', extra_context)
+        self.assertNotIn('rendered_lines', extra_context)
diff --git a/reviewboard/reviews/ui/markdownui.py b/reviewboard/reviews/ui/markdownui.py
index 8b440c0ae7749b6e9e0c8cadacf2a961a5e99ca6..8ad76c6b1b9b580e845a8aeff10a7eb89a086c1f 100644
--- a/reviewboard/reviews/ui/markdownui.py
+++ b/reviewboard/reviews/ui/markdownui.py
@@ -8,7 +8,7 @@ from pygments.lexers import TextLexer
 
 from reviewboard.reviews.chunk_generators import MarkdownDiffChunkGenerator
 from reviewboard.reviews.ui.text import TextBasedReviewUI
-from reviewboard.reviews.markdown_utils import render_markdown_from_file
+from reviewboard.reviews.markdown_utils import render_markdown
 
 
 class MarkdownReviewUI(TextBasedReviewUI):
@@ -29,7 +29,7 @@ class MarkdownReviewUI(TextBasedReviewUI):
     def generate_render(self):
         with self.obj.file as f:
             f.open()
-            rendered = render_markdown_from_file(f)
+            rendered = render_markdown(f.read())
 
         try:
             for line in iter_markdown_lines(rendered):
diff --git a/reviewboard/reviews/ui/text.py b/reviewboard/reviews/ui/text.py
index 710c83a8420733d0ae88a8a3fc38a9143d8c3a28..f231edf8700873ed02f5e48b827f78a285d2b604 100644
--- a/reviewboard/reviews/ui/text.py
+++ b/reviewboard/reviews/ui/text.py
@@ -2,6 +2,7 @@ from __future__ import unicode_literals
 
 import logging
 
+from django.utils.encoding import force_bytes
 from django.utils.safestring import mark_safe
 from djblets.cache.backend import cache_memoize
 from djblets.util.compat.django.template.loader import render_to_string
@@ -315,25 +316,60 @@ class TextBasedReviewUI(FileAttachmentReviewUI):
 
         This is used both for displaying the file attachment and
         rendering the thumbnail.
+
+        Args:
+            chunk_generator_cls (type):
+                The chunk generator to instantiate. This should be a subclass
+                of :py:class:`~reviewboard.diffviewer.chunk_generator
+                .RawDiffChunkGenerator`.
+
+            orig (bytes or list of bytes):
+                The original file content to diff against.
+
+            modified (bytes or list of bytes):
+                The new file content.
+
+        Returns:
+            reviewboard.diffviewer.chunk_generator.RawDiffChunkGenerator:
+            The chunk generator used to diff source or rendered text.
         """
         assert self.diff_against_obj
 
         return chunk_generator_cls(
-            orig,
-            modified,
-            self.obj.filename,
-            self.diff_against_obj.filename)
+            old=orig,
+            new=modified,
+            orig_filename=self.obj.filename,
+            modified_filename=self.diff_against_obj.filename)
 
     def _get_source_diff_chunk_generator(self):
-        """Return a chunk generator for diffing source text."""
+        """Return a chunk generator for diffing source text.
+
+        Returns:
+            reviewboard.diffviewer.chunk_generator.RawDiffChunkGenerator:
+            The chunk generator used to diff source text.
+        """
         return self._get_diff_chunk_generator(
             self.source_chunk_generator_cls,
-            self.diff_against_obj.review_ui.get_text(),
-            self.get_text())
+            force_bytes(self.diff_against_obj.review_ui.get_text()),
+            force_bytes(self.get_text()))
 
     def _get_rendered_diff_chunk_generator(self):
-        """Return a chunk generator for diffing rendered text."""
+        """Return a chunk generator for diffing rendered text.
+
+        Returns:
+            reviewboard.diffviewer.chunk_generator.RawDiffChunkGenerator:
+            The chunk generator used to diff rendered text.
+        """
+        diff_against_review_ui = self.diff_against_obj.review_ui
+
         return self._get_diff_chunk_generator(
             self.rendered_chunk_generator_cls,
-            self.diff_against_obj.review_ui.get_rendered_lines(),
-            self.get_rendered_lines())
+            [
+                force_bytes(line)
+                for line in diff_against_review_ui.get_rendered_lines()
+            ],
+            [
+                force_bytes(line)
+                for line in self.get_rendered_lines()
+            ]
+        )
