Пример #1
0
    def setUp(self):
        self.diff = (b'diff --git a/README b/README\n'
                     b'index d6613f5..5b50866 100644\n'
                     b'--- README\n'
                     b'+++ README\n'
                     b'@ -1,1 +1,1 @@\n'
                     b'-blah blah\n'
                     b'+blah!\n')
        self.parent_diff = (b'diff --git a/README b/README\n'
                            b'index d6613f5..5b50866 100644\n'
                            b'--- README\n'
                            b'+++ README\n'
                            b'@ -1,1 +1,1 @@\n'
                            b'-blah..\n'
                            b'+blah blah\n')

        repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64='')
Пример #2
0
    def make_filediff(self, is_new=False, diffset_history=None,
                      diffset_revision=1, source_filename='file1',
                      dest_filename='file2'):
        if is_new:
            source_revision = PRE_CREATION
            dest_revision = ''
        else:
            source_revision = '1'
            dest_revision = '2'

        repository = self.create_repository()

        if not diffset_history:
            diffset_history = DiffSetHistory.objects.create(name='testhistory')

        diffset = DiffSet.objects.create(name='test',
                                         revision=diffset_revision,
                                         repository=repository,
                                         history=diffset_history)
        filediff = FileDiff(source_file=source_filename,
                            source_revision=source_revision,
                            dest_file=dest_filename,
                            dest_detail=dest_revision,
                            diffset=diffset,
                            binary=True)
        filediff.save()

        return filediff
    def setUp(self):
        super(FileDiffMigrationTests, self).setUp()

        self.repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64='')

        self.parent_diff = (
            b'diff --git a/README b/README\n'
            b'index 94bdd3e..3d2b777 100644\n'
            b'--- README\n'
            b'+++ README\n'
            b'@@ -2 +2 @@\n'
            b'-blah..\n'
            b'+blah blah\n'
        )

        repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64=b'')
Пример #4
0
    def make_filediff(self,
                      is_new=False,
                      diffset_history=None,
                      diffset_revision=1,
                      source_filename='file1',
                      dest_filename='file2'):
        """Create and return a FileDiff with the given data."""
        if is_new:
            source_revision = PRE_CREATION
            dest_revision = ''
        else:
            source_revision = '1'
            dest_revision = '2'

        repository = self.create_repository()

        if not diffset_history:
            diffset_history = DiffSetHistory.objects.create(name='testhistory')

        diffset = DiffSet.objects.create(name='test',
                                         revision=diffset_revision,
                                         repository=repository,
                                         history=diffset_history)
        filediff = FileDiff(source_file=source_filename,
                            source_revision=source_revision,
                            dest_file=dest_filename,
                            dest_detail=dest_revision,
                            diffset=diffset,
                            binary=True)
        filediff.save()

        return filediff
    def setUp(self):
        super(FileDiffMigrationTests, self).setUp()

        self.repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64=b'',
                                 parent_diff64=b'')

        self.parent_diff = (b'diff --git a/README b/README\n'
                            b'index 94bdd3e..3d2b777 100644\n'
                            b'--- README\n'
                            b'+++ README\n'
                            b'@@ -2 +2 @@\n'
                            b'-blah..\n'
                            b'+blah blah\n')

        repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64=b'',
                                 parent_diff64=b'')
Пример #6
0
    def create(self, diff_file, parent_diff_file=None, diffset_history=None):
        tool = self.repository.get_scmtool()

        # Grab the base directory if there is one.
        if not tool.get_diffs_use_absolute_paths():
            try:
                basedir = smart_unicode(self.cleaned_data['basedir'])
            except AttributeError:
                raise NoBaseDirError(_('The "Base Diff Path" field is required'))
        else:
            basedir = ''

        # Parse the diff
        files = list(self._process_files(
            diff_file, basedir, check_existance=(parent_diff_file is not None)))

        if len(files) == 0:
            raise EmptyDiffError(_("The diff file is empty"))

        # Sort the files so that header files come before implementation.
        files.sort(cmp=self._compare_files, key=lambda f: f.origFile)

        # Parse the parent diff
        parent_files = {}

        if parent_diff_file:
            # If the user supplied a base diff, we need to parse it and
            # later apply each of the files that are in the main diff
            for f in self._process_files(parent_diff_file, basedir,
                                         check_existance=True):
                parent_files[f.origFile] = f

        diffset = DiffSet(name=diff_file.name, revision=0,
                          history=diffset_history,
                          diffcompat=DEFAULT_DIFF_COMPAT_VERSION)
        diffset.repository = self.repository
        diffset.save()

        for f in files:
            if f.origFile in parent_files:
                parent_file = parent_files[f.origFile]
                parent_content = parent_file.data
                source_rev = parent_file.origInfo
            else:
                parent_content = ""
                source_rev = f.origInfo

            dest_file = os.path.join(basedir, f.newFile).replace("\\", "/")

            filediff = FileDiff(diffset=diffset,
                                source_file=f.origFile,
                                dest_file=dest_file,
                                source_revision=smart_unicode(source_rev),
                                dest_detail=f.newInfo,
                                diff=f.data,
                                parent_diff=parent_content,
                                binary=f.binary)
            filediff.save()

        return diffset
Пример #7
0
    def make_filediff(
        self, is_new=False, diffset_history=None, diffset_revision=1, source_filename="file1", dest_filename="file2"
    ):
        """Create and return a FileDiff with the given data."""
        if is_new:
            source_revision = PRE_CREATION
            dest_revision = ""
        else:
            source_revision = "1"
            dest_revision = "2"

        repository = self.create_repository()

        if not diffset_history:
            diffset_history = DiffSetHistory.objects.create(name="testhistory")

        diffset = DiffSet.objects.create(
            name="test", revision=diffset_revision, repository=repository, history=diffset_history
        )
        filediff = FileDiff(
            source_file=source_filename,
            source_revision=source_revision,
            dest_file=dest_filename,
            dest_detail=dest_revision,
            diffset=diffset,
            binary=True,
        )
        filediff.save()

        return filediff
Пример #8
0
    def create_filediff(self,
                        diffset,
                        source_file='/test-file',
                        dest_file='/test-file',
                        source_revision='123',
                        dest_detail='124',
                        status=FileDiff.MODIFIED,
                        diff=DEFAULT_FILEDIFF_DATA,
                        save=True):
        """Create a FileDiff for testing.

        The FileDiff is tied to the given DiffSet. It's populated with
        default data that can be overridden by the caller.

        Args:
            diffset (reviewboard.diffviewer.models.DiffSet):
                The parent diff set that will own this file.

            source_file (unicode, optional):
                The source filename.

            dest_file (unicode, optional):
                The destination filename, which will be the same as
                ``source_file`` unless the file was moved/renamed/copied.

            source_revision (unicode, optional):
                The source revision.

            dest_detail (unicode, optional):
                The destination revision or other detail as found in the
                parsed diff. This may be a timestamp or some other value.

            status (unicode, optional):
                The status of the file. This is the operation performed
                as indicated in the diff.

            diff (bytes, optional):
                The diff contents.

            save (bool, optional):
                Whether to automatically save the resulting object.

        Returns:
            reviewboard.diffviewer.models.FileDiff:
            The resulting FileDiff.
        """
        filediff = FileDiff(diffset=diffset,
                            source_file=source_file,
                            dest_file=dest_file,
                            source_revision=source_revision,
                            dest_detail=dest_detail,
                            status=status,
                            diff=diff)

        if save:
            filediff.save()

        return filediff
Пример #9
0
    def create_filediff(self, diffset, source_file='/test-file',
                        dest_file='/test-file', source_revision='123',
                        dest_detail='124', status=FileDiff.MODIFIED,
                        diff=DEFAULT_FILEDIFF_DATA, save=True):
        """Create a FileDiff for testing.

        The FileDiff is tied to the given DiffSet. It's populated with
        default data that can be overridden by the caller.

        Args:
            diffset (reviewboard.diffviewer.models.DiffSet):
                The parent diff set that will own this file.

            source_file (unicode, optional):
                The source filename.

            dest_file (unicode, optional):
                The destination filename, which will be the same as
                ``source_file`` unless the file was moved/renamed/copied.

            source_revision (unicode, optional):
                The source revision.

            dest_detail (unicode, optional):
                The destination revision or other detail as found in the
                parsed diff. This may be a timestamp or some other value.

            status (unicode, optional):
                The status of the file. This is the operation performed
                as indicated in the diff.

            diff (bytes, optional):
                The diff contents.

            save (bool, optional):
                Whether to automatically save the resulting object.

        Returns:
            reviewboard.diffviewer.models.FileDiff:
            The resulting FileDiff.
        """
        filediff = FileDiff(
            diffset=diffset,
            source_file=source_file,
            dest_file=dest_file,
            source_revision=source_revision,
            dest_detail=dest_detail,
            status=status,
            diff=diff)

        if save:
            filediff.save()

        return filediff
Пример #10
0
    def testLongFilenames(self):
        """Testing using long filenames (1024 characters) in FileDiff."""
        long_filename = 'x' * 1024

        repository = Repository.objects.get(pk=1)
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        filediff = FileDiff(source_file=long_filename,
                            dest_file='foo',
                            diffset=diffset)
        filediff.save()

        filediff = FileDiff.objects.get(pk=filediff.id)
        self.assertEquals(filediff.source_file, long_filename)
Пример #11
0
    def testLongFilenames(self):
        """Testing using long filenames (1024 characters) in FileDiff."""
        long_filename = 'x' * 1024

        repository = Repository.objects.get(pk=1)
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        filediff = FileDiff(source_file=long_filename,
                            dest_file='foo',
                            diffset=diffset)
        filediff.save()

        filediff = FileDiff.objects.get(pk=filediff.id)
        self.assertEquals(filediff.source_file, long_filename)
Пример #12
0
    def test_get_line_changed_regions(self):
        """Testing DiffChunkGenerator._get_line_changed_regions"""
        def deep_equal(A, B):
            typea, typeb = type(A), type(B)
            self.assertEqual(typea, typeb)
            if typea is tuple or typea is list:
                for a, b in zip_longest(A, B):
                    deep_equal(a, b)
            else:
                self.assertEqual(A, B)

        filediff = FileDiff(source_file='foo', diffset=DiffSet())
        generator = DiffChunkGenerator(None, filediff)

        deep_equal(generator._get_line_changed_regions(None, None),
                   (None, None))

        old = 'submitter = models.ForeignKey(Person, verbose_name="Submitter")'
        new = 'submitter = models.ForeignKey(User, verbose_name="Submitter")'
        regions = generator._get_line_changed_regions(old, new)
        deep_equal(regions, ([(30, 36)], [(30, 34)]))

        old = '-from reviews.models import ReviewRequest, Person, Group'
        new = '+from .reviews.models import ReviewRequest, Group'
        regions = generator._get_line_changed_regions(old, new)
        deep_equal(regions, ([(0, 1), (6, 6), (43, 51)], [(0, 1), (6, 7),
                                                          (44, 44)]))

        old = 'abcdefghijklm'
        new = 'nopqrstuvwxyz'
        regions = generator._get_line_changed_regions(old, new)
        deep_equal(regions, (None, None))
Пример #13
0
    def setUp(self):
        self.diff = (
            'diff --git a/README b/README\n'
            'index d6613f5..5b50866 100644\n'
            '--- README\n'
            '+++ README\n'
            '@ -1,1 +1,1 @@\n'
            '-blah blah\n'
            '+blah!\n')
        self.parent_diff = (
            'diff --git a/README b/README\n'
            'index d6613f5..5b50866 100644\n'
            '--- README\n'
            '+++ README\n'
            '@ -1,1 +1,1 @@\n'
            '-blah..\n'
            '+blah blah\n')

        repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64='')
Пример #14
0
    def setUp(self):
        self.diff = ('--- README  123\n'
                     '+++ README  (new)\n'
                     '@ -1,1 +1,1 @@\n'
                     '-blah blah\n'
                     '+blah!\n')
        self.parent_diff = ('--- README  123\n'
                            '+++ README  (new)\n'
                            '@ -1,1 +1,1 @@\n'
                            '-blah..\n'
                            '+blah blah\n')

        repository = Repository.objects.get(pk=1)
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64='')
Пример #15
0
    def setUp(self):
        super(FileDiffTests, self).setUp()

        diff = (b'diff --git a/README b/README\n'
                b'index 3d2b777..48272a3 100644\n'
                b'--- README\n'
                b'+++ README\n'
                b'@@ -2 +2,2 @@\n'
                b'-blah blah\n'
                b'+blah!\n'
                b'+blah!!\n')

        self.repository = self.create_repository(tool_name='Test')
        self.diffset = DiffSet.objects.create(name='test',
                                              revision=1,
                                              repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=self.diffset,
                                 diff64=diff,
                                 parent_diff64=b'')
Пример #16
0
    def test_diff_hashes(self):
        """
        Testing that uploading two of the same diff will result in only
        one database entry.
        """
        repository = self.create_repository()
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        with open(os.path.join(self.PREFIX, "diffs", "context",
                               "foo.c.diff")) as f:
            data = f.read()

        filediff1 = FileDiff(diff=data, diffset=diffset)
        filediff1.save()
        filediff2 = FileDiff(diff=data, diffset=diffset)
        filediff2.save()

        self.assertEquals(filediff1.diff_hash, filediff2.diff_hash)
Пример #17
0
    def test_make_context_with_chunk_index(self):
        """Testing DiffRenderer.make_context with chunk_index"""
        diff_file = {
            'newfile':
            True,
            'interfilediff':
            None,
            'filediff':
            FileDiff(),
            'chunks': [
                {
                    'lines': [],
                    'meta': {},
                    'change': 'insert',
                },
                {
                    # This is not how lines really look, but it's fine for
                    # current usage tests.
                    'lines': range(10),
                    'meta': {},
                    'change': 'replace',
                },
                {
                    'lines': [],
                    'meta': {},
                    'change': 'delete',
                }
            ],
        }

        renderer = DiffRenderer(diff_file, chunk_index=1)
        context = renderer.make_context()

        self.assertEqual(context['standalone'], True)
        self.assertEqual(context['file'], diff_file)
        self.assertEqual(len(diff_file['chunks']), 1)

        chunk = diff_file['chunks'][0]
        self.assertEqual(chunk['change'], 'replace')
Пример #18
0
    def setUp(self):
        super(FileDiffTests, self).setUp()

        diff = (
            b'diff --git a/README b/README\n'
            b'index 3d2b777..48272a3 100644\n'
            b'--- README\n'
            b'+++ README\n'
            b'@@ -2 +2,2 @@\n'
            b'-blah blah\n'
            b'+blah!\n'
            b'+blah!!\n'
        )

        self.repository = self.create_repository(tool_name='Test')
        self.diffset = DiffSet.objects.create(name='test',
                                              revision=1,
                                              repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=self.diffset,
                                 diff64=diff,
                                 parent_diff64=b'')
Пример #19
0
    def setUp(self):
        self.diff = (
            '--- README  123\n'
            '+++ README  (new)\n'
            '@ -1,1 +1,1 @@\n'
            '-blah blah\n'
            '+blah!\n')
        self.parent_diff = (
            '--- README  123\n'
            '+++ README  (new)\n'
            '@ -1,1 +1,1 @@\n'
            '-blah..\n'
            '+blah blah\n')

        repository = Repository.objects.get(pk=1)
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64='')
Пример #20
0
    def test_diff_hashes(self):
        """
        Testing that uploading two of the same diff will result in only
        one database entry.
        """
        repository = self.create_repository()
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        with open(os.path.join(self.PREFIX, "diffs", "context",
                               "foo.c.diff")) as f:
            data = f.read()

        filediff1 = FileDiff(diff=data,
                             diffset=diffset)
        filediff1.save()
        filediff2 = FileDiff(diff=data,
                             diffset=diffset)
        filediff2.save()

        self.assertEquals(filediff1.diff_hash, filediff2.diff_hash)
Пример #21
0
class FileDiffTests(TestCase):
    """Unit tests for FileDiff."""

    fixtures = ['test_scmtools']

    def setUp(self):
        super(FileDiffTests, self).setUp()

        diff = (
            b'diff --git a/README b/README\n'
            b'index 3d2b777..48272a3 100644\n'
            b'--- README\n'
            b'+++ README\n'
            b'@@ -2 +2,2 @@\n'
            b'-blah blah\n'
            b'+blah!\n'
            b'+blah!!\n'
        )

        self.repository = self.create_repository(tool_name='Test')
        self.diffset = DiffSet.objects.create(name='test',
                                              revision=1,
                                              repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=self.diffset,
                                 diff64=diff,
                                 parent_diff64=b'')

    def test_get_line_counts_with_defaults(self):
        """Testing FileDiff.get_line_counts with default values"""
        counts = self.filediff.get_line_counts()

        self.assertIn('raw_insert_count', counts)
        self.assertIn('raw_delete_count', counts)
        self.assertIn('insert_count', counts)
        self.assertIn('delete_count', counts)
        self.assertIn('replace_count', counts)
        self.assertIn('equal_count', counts)
        self.assertIn('total_line_count', counts)
        self.assertEqual(counts['raw_insert_count'], 2)
        self.assertEqual(counts['raw_delete_count'], 1)
        self.assertEqual(counts['insert_count'], 2)
        self.assertEqual(counts['delete_count'], 1)
        self.assertIsNone(counts['replace_count'])
        self.assertIsNone(counts['equal_count'])
        self.assertIsNone(counts['total_line_count'])

        diff_hash = self.filediff.diff_hash
        self.assertEqual(diff_hash.insert_count, 2)
        self.assertEqual(diff_hash.delete_count, 1)

    def test_set_line_counts(self):
        """Testing FileDiff.set_line_counts"""
        self.filediff.set_line_counts(
            raw_insert_count=1,
            raw_delete_count=2,
            insert_count=3,
            delete_count=4,
            replace_count=5,
            equal_count=6,
            total_line_count=7)

        counts = self.filediff.get_line_counts()
        self.assertEqual(counts['raw_insert_count'], 1)
        self.assertEqual(counts['raw_delete_count'], 2)
        self.assertEqual(counts['insert_count'], 3)
        self.assertEqual(counts['delete_count'], 4)
        self.assertEqual(counts['replace_count'], 5)
        self.assertEqual(counts['equal_count'], 6)
        self.assertEqual(counts['total_line_count'], 7)

        diff_hash = self.filediff.diff_hash
        self.assertEqual(diff_hash.insert_count, 1)
        self.assertEqual(diff_hash.delete_count, 2)

    def test_long_filenames(self):
        """Testing FileDiff with long filenames (1024 characters)"""
        long_filename = 'x' * 1024

        filediff = FileDiff.objects.create(source_file=long_filename,
                                           dest_file='foo',
                                           diffset=self.diffset)
        self.assertEqual(filediff.source_file, long_filename)

    def test_diff_hashes(self):
        """Testing FileDiff with multiple entries and same diff data
        deduplicates data
        """
        data = (
            b'diff -rcN orig_src/foo.c new_src/foo.c\n'
            b'*** orig_src/foo.c\t2007-01-24 02:11:31.000000000 -0800\n'
            b'--- new_src/foo.c\t2007-01-24 02:14:42.000000000 -0800\n'
            b'***************\n'
            b'*** 1,5 ****\n'
            b'  int\n'
            b'  main()\n'
            b'  {\n'
            b'! \tprintf("foo\n");\n'
            b'  }\n'
            b'--- 1,8 ----\n'
            b'+ #include <stdio.h>\n'
            b'+ \n'
            b'  int\n'
            b'  main()\n'
            b'  {\n'
            b'! \tprintf("foo bar\n");\n'
            b'! \treturn 0;\n'
            b'  }\n')

        filediff1 = FileDiff.objects.create(diff=data, diffset=self.diffset)
        filediff2 = FileDiff.objects.create(diff=data, diffset=self.diffset)

        self.assertEqual(filediff1.diff_hash, filediff2.diff_hash)

    def test_get_base_filediff(self):
        """Testing FileDiff.get_base_filediff"""
        commit1 = self.create_diffcommit(
            diffset=self.diffset,
            commit_id='r1',
            parent_id='r0',
            diff_contents=(
                b'diff --git a/ABC b/ABC\n'
                b'index 94bdd3e..197009f 100644\n'
                b'--- ABC\n'
                b'+++ ABC\n'
                b'@@ -1,1 +1,1 @@\n'
                b'-line!\n'
                b'+line..\n'
            ))
        commit2 = self.create_diffcommit(
            diffset=self.diffset,
            commit_id='r2',
            parent_id='r1',
            diff_contents=(
                b'diff --git a/README b/README\n'
                b'index 94bdd3e..197009f 100644\n'
                b'--- README\n'
                b'+++ README\n'
                b'@@ -1,1 +1,1 @@\n'
                b'-Hello, world!\n'
                b'+Hi, world!\n'
            ))
        commit3 = self.create_diffcommit(
            diffset=self.diffset,
            commit_id='r3',
            parent_id='r2',
            diff_contents=(
                b'diff --git a/FOO b/FOO\n'
                b'index 84bda3e..b975034 100644\n'
                b'--- FOO\n'
                b'+++ FOO\n'
                b'@@ -1,1 +0,0 @@\n'
                b'-Some line\n'
            ))
        commit4 = self.create_diffcommit(
            diffset=self.diffset,
            commit_id='r4',
            parent_id='r3',
            diff_contents=(
                b'diff --git a/README b/README\n'
                b'index 197009f..87abad9 100644\n'
                b'--- README\n'
                b'+++ README\n'
                b'@@ -1,1 +1,1 @@\n'
                b'-Hi, world!\n'
                b'+Yo, world.\n'
            ))

        self.diffset.finalize_commit_series(
            cumulative_diff=(
                b'diff --git a/ABC b/ABC\n'
                b'index 94bdd3e..197009f 100644\n'
                b'--- ABC\n'
                b'+++ ABC\n'
                b'@@ -1,1 +1,1 @@\n'
                b'-line!\n'
                b'+line..\n'
                b'diff --git a/FOO b/FOO\n'
                b'index 84bda3e..b975034 100644\n'
                b'--- FOO\n'
                b'+++ FOO\n'
                b'@@ -1,1 +0,0 @@\n'
                b'-Some line\n'
                b'diff --git a/README b/README\n'
                b'index 94bdd3e..87abad9 100644\n'
                b'--- README\n'
                b'+++ README\n'
                b'@@ -1,1 +1,1 @@\n'
                b'-Hello, world!\n'
                b'+Yo, world.\n'
            ),
            validation_info=None,
            validate=False,
            save=True)

        filediff1 = commit1.files.get()
        filediff2 = commit2.files.get()
        filediff3 = commit3.files.get()
        filediff4 = commit4.files.get()

        for commit in (commit1, commit2, commit3, commit4):
            self.assertIsNone(filediff1.get_base_filediff(base_commit=commit))
            self.assertIsNone(filediff2.get_base_filediff(base_commit=commit))
            self.assertIsNone(filediff3.get_base_filediff(base_commit=commit))

        self.assertIsNone(filediff4.get_base_filediff(base_commit=commit1))
        self.assertEqual(filediff4.get_base_filediff(base_commit=commit2),
                         filediff2)
        self.assertEqual(filediff4.get_base_filediff(base_commit=commit3),
                         filediff2)
        self.assertEqual(filediff4.get_base_filediff(base_commit=commit4),
                         filediff2)

    def test_get_base_filediff_without_commit(self):
        """Testing FileDiff.get_base_filediff without associated commit"""
        filediff = self.create_filediff(self.diffset)

        self.assertIsNone(filediff.get_base_filediff(base_commit=None))
Пример #22
0
class FileDiffTests(TestCase):
    """Unit tests for FileDiff."""

    fixtures = ['test_scmtools']

    def setUp(self):
        super(FileDiffTests, self).setUp()

        diff = (
            b'diff --git a/README b/README\n'
            b'index 3d2b777..48272a3 100644\n'
            b'--- README\n'
            b'+++ README\n'
            b'@@ -2 +2,2 @@\n'
            b'-blah blah\n'
            b'+blah!\n'
            b'+blah!!\n'
        )

        self.repository = self.create_repository(tool_name='Test')
        self.diffset = DiffSet.objects.create(name='test',
                                              revision=1,
                                              repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=self.diffset,
                                 diff64=diff,
                                 parent_diff64=b'')

    def test_get_line_counts_with_defaults(self):
        """Testing FileDiff.get_line_counts with default values"""
        counts = self.filediff.get_line_counts()

        self.assertIn('raw_insert_count', counts)
        self.assertIn('raw_delete_count', counts)
        self.assertIn('insert_count', counts)
        self.assertIn('delete_count', counts)
        self.assertIn('replace_count', counts)
        self.assertIn('equal_count', counts)
        self.assertIn('total_line_count', counts)
        self.assertEqual(counts['raw_insert_count'], 2)
        self.assertEqual(counts['raw_delete_count'], 1)
        self.assertEqual(counts['insert_count'], 2)
        self.assertEqual(counts['delete_count'], 1)
        self.assertIsNone(counts['replace_count'])
        self.assertIsNone(counts['equal_count'])
        self.assertIsNone(counts['total_line_count'])

        diff_hash = self.filediff.diff_hash
        self.assertEqual(diff_hash.insert_count, 2)
        self.assertEqual(diff_hash.delete_count, 1)

    def test_set_line_counts(self):
        """Testing FileDiff.set_line_counts"""
        self.filediff.set_line_counts(
            raw_insert_count=1,
            raw_delete_count=2,
            insert_count=3,
            delete_count=4,
            replace_count=5,
            equal_count=6,
            total_line_count=7)

        counts = self.filediff.get_line_counts()
        self.assertEqual(counts['raw_insert_count'], 1)
        self.assertEqual(counts['raw_delete_count'], 2)
        self.assertEqual(counts['insert_count'], 3)
        self.assertEqual(counts['delete_count'], 4)
        self.assertEqual(counts['replace_count'], 5)
        self.assertEqual(counts['equal_count'], 6)
        self.assertEqual(counts['total_line_count'], 7)

        diff_hash = self.filediff.diff_hash
        self.assertEqual(diff_hash.insert_count, 1)
        self.assertEqual(diff_hash.delete_count, 2)

    def test_long_filenames(self):
        """Testing FileDiff with long filenames (1024 characters)"""
        long_filename = 'x' * 1024

        filediff = FileDiff.objects.create(source_file=long_filename,
                                           dest_file='foo',
                                           diffset=self.diffset)
        self.assertEqual(filediff.source_file, long_filename)

    def test_diff_hashes(self):
        """Testing FileDiff with multiple entries and same diff data
        deduplicates data
        """
        data = (
            b'diff -rcN orig_src/foo.c new_src/foo.c\n'
            b'*** orig_src/foo.c\t2007-01-24 02:11:31.000000000 -0800\n'
            b'--- new_src/foo.c\t2007-01-24 02:14:42.000000000 -0800\n'
            b'***************\n'
            b'*** 1,5 ****\n'
            b'  int\n'
            b'  main()\n'
            b'  {\n'
            b'! \tprintf("foo\n");\n'
            b'  }\n'
            b'--- 1,8 ----\n'
            b'+ #include <stdio.h>\n'
            b'+ \n'
            b'  int\n'
            b'  main()\n'
            b'  {\n'
            b'! \tprintf("foo bar\n");\n'
            b'! \treturn 0;\n'
            b'  }\n')

        filediff1 = FileDiff.objects.create(diff=data, diffset=self.diffset)
        filediff2 = FileDiff.objects.create(diff=data, diffset=self.diffset)

        self.assertEqual(filediff1.diff_hash, filediff2.diff_hash)
Пример #23
0
    def create(self, diff_file, parent_diff_file=None, diffset_history=None):
        tool = self.repository.get_scmtool()

        siteconfig = SiteConfiguration.objects.get_current()
        max_diff_size = siteconfig.get('diffviewer_max_diff_size')

        if max_diff_size > 0:
            if diff_file.size > max_diff_size:
                raise DiffTooBigError(
                    _('The supplied diff file is too large'),
                    max_diff_size=max_diff_size)

            if parent_diff_file and parent_diff_file.size > max_diff_size:
                raise DiffTooBigError(
                    _('The supplied parent diff file is too large'),
                    max_diff_size=max_diff_size)

        # Grab the base directory if there is one.
        if not tool.get_diffs_use_absolute_paths():
            try:
                basedir = smart_unicode(self.cleaned_data['basedir'].strip())
            except AttributeError:
                raise NoBaseDirError(
                    _('The "Base Diff Path" field is required'))
        else:
            basedir = ''

        # Parse the diff
        files = list(self._process_files(
            diff_file, basedir, check_existance=(not parent_diff_file)))

        if len(files) == 0:
            raise EmptyDiffError(_("The diff file is empty"))

        # Sort the files so that header files come before implementation.
        files.sort(cmp=self._compare_files, key=lambda f: f.origFile)

        # Parse the parent diff
        parent_files = {}

        # This is used only for tools like Mercurial that use atomic changeset
        # IDs to identify all file versions but not individual file version
        # IDs.
        parent_changeset_id = None

        if parent_diff_file:
            # If the user supplied a base diff, we need to parse it and
            # later apply each of the files that are in the main diff
            for f in self._process_files(parent_diff_file, basedir,
                                         check_existance=True):
                parent_files[f.origFile] = f

                # Store the original changeset ID if we have it; this should
                # be the same for all files.
                if f.origChangesetId:
                    parent_changeset_id = f.origChangesetId

        diffset = DiffSet(name=diff_file.name, revision=0,
                          basedir=basedir,
                          history=diffset_history,
                          diffcompat=DEFAULT_DIFF_COMPAT_VERSION)
        diffset.repository = self.repository
        diffset.save()

        for f in files:
            if f.origFile in parent_files:
                parent_file = parent_files[f.origFile]
                parent_content = parent_file.data
                source_rev = parent_file.origInfo
            else:
                parent_content = ""

                if (tool.diff_uses_changeset_ids and
                    parent_changeset_id and
                    f.origInfo != PRE_CREATION):
                    source_rev = parent_changeset_id
                else:
                    source_rev = f.origInfo

            dest_file = os.path.join(basedir, f.newFile).replace("\\", "/")

            if f.deleted:
                status = FileDiff.DELETED
            elif f.moved:
                status = FileDiff.MOVED
            else:
                status = FileDiff.MODIFIED

            filediff = FileDiff(diffset=diffset,
                                source_file=f.origFile,
                                dest_file=dest_file,
                                source_revision=smart_unicode(source_rev),
                                dest_detail=f.newInfo,
                                diff=f.data,
                                parent_diff=parent_content,
                                binary=f.binary,
                                status=status)
            filediff.save()

        return diffset
Пример #24
0
def create_filediffs(diff_file_contents, parent_diff_file_contents,
                     repository, basedir, base_commit_id, diffset,
                     request=None, check_existence=True, get_file_exists=None,
                     diffcommit=None, validate_only=False):
    """Create FileDiffs from the given data.

    Args:
        diff_file_contents (bytes):
            The contents of the diff file.

        parent_diff_file_contents (bytes):
            The contents of the parent diff file.

        repository (reviewboard.scmtools.models.Repository):
            The repository the diff is being posted against.

        basedir (unicode):
            The base directory to prepend to all file paths in the diff.

        base_commit_id (unicode):
            The ID of the commit that the diff is based upon. This is
            needed by some SCMs or hosting services to properly look up
            files, if the diffs represent blob IDs instead of commit IDs
            and the service doesn't support those lookups.

        diffset (reviewboard.diffviewer.models.diffset.DiffSet):
            The DiffSet to attach the created FileDiffs to.

        request (django.http.HttpRequest, optional):
            The current HTTP request.

        check_existence (bool, optional):
            Whether or not existence checks should be performed against
            the upstream repository.

            This argument defaults to ``True``.

        get_file_exists (callable, optional):
            A callable that is used to determine if a file exists.

            This must be provided if ``check_existence`` is ``True``.

        diffcommit (reviewboard.diffviewer.models.diffcommit.DiffCommit,
                    optional):
            The Diffcommit to attach the created FileDiffs to.

        validate_only (bool, optional):
            Whether to just validate and not save. If ``True``, then this
            won't populate the database at all and will return ``None``
            upon success. This defaults to ``False``.

    Returns:
        list of reviewboard.diffviewer.models.filediff.FileDiff:
        The created FileDiffs.

        If ``validate_only`` is ``True``, the returned list will be empty.
    """
    from reviewboard.diffviewer.diffutils import convert_to_unicode
    from reviewboard.diffviewer.models import FileDiff

    files, parser, parent_commit_id, parent_files = _prepare_file_list(
        diff_file_contents=diff_file_contents,
        parent_diff_file_contents=parent_diff_file_contents,
        repository=repository,
        request=request,
        basedir=basedir,
        check_existence=check_existence,
        get_file_exists=get_file_exists,
        base_commit_id=base_commit_id)

    encoding_list = repository.get_encoding_list()
    filediffs = []

    for f in files:
        parent_file = None
        orig_rev = None
        parent_content = b''

        if f.origFile in parent_files:
            parent_file = parent_files[f.origFile]
            parent_content = parent_file.data
            orig_rev = parent_file.origInfo

        # If there is a parent file there is not necessarily an original
        # revision for the parent file in the case of a renamed file in
        # git.
        if not orig_rev:
            if parent_commit_id and f.origInfo != PRE_CREATION:
                orig_rev = parent_commit_id
            else:
                orig_rev = f.origInfo

        orig_file = convert_to_unicode(f.origFile, encoding_list)[1]
        dest_file = convert_to_unicode(f.newFile, encoding_list)[1]

        if f.deleted:
            status = FileDiff.DELETED
        elif f.moved:
            status = FileDiff.MOVED
        elif f.copied:
            status = FileDiff.COPIED
        else:
            status = FileDiff.MODIFIED

        filediff = FileDiff(
            diffset=diffset,
            commit=diffcommit,
            source_file=parser.normalize_diff_filename(orig_file),
            dest_file=parser.normalize_diff_filename(dest_file),
            source_revision=smart_unicode(orig_rev),
            dest_detail=f.newInfo,
            binary=f.binary,
            status=status)

        filediff.extra_data = {
            'is_symlink': f.is_symlink,
        }

        if parent_file:
            if (parent_file.insert_count == 0 and
                parent_file.delete_count == 0):
                filediff.extra_data[FileDiff._IS_PARENT_EMPTY_KEY] = True

                if parent_file.moved or parent_file.copied:
                    filediff.extra_data['parent_moved'] = True
            else:
                filediff.extra_data[FileDiff._IS_PARENT_EMPTY_KEY] = False

        if not validate_only:
            # This state all requires making modifications to the database.
            # We only want to do this if we're saving.
            filediff.diff = f.data
            filediff.parent_diff = parent_content

            filediff.set_line_counts(raw_insert_count=f.insert_count,
                                     raw_delete_count=f.delete_count)

        filediffs.append(filediff)

    if not validate_only:
        FileDiff.objects.bulk_create(filediffs)
        num_filediffs = len(filediffs)

        if diffset.file_count is None:
            diffset.reinit_file_count()
        else:
            diffset.file_count += num_filediffs
            diffset.save(update_fields=('file_count',))

        if diffcommit is not None:
            diffcommit.file_count = len(filediffs)
            diffcommit.save(update_fields=('file_count',))

    return filediffs
Пример #25
0
    def create_from_data(self,
                         repository,
                         diff_file_name,
                         diff_file_contents,
                         parent_diff_file_name,
                         parent_diff_file_contents,
                         diffset_history,
                         basedir,
                         request,
                         base_commit_id=None,
                         save=True):
        """Create a DiffSet from raw diff data.

        The diff_file_contents and parent_diff_file_contents parameters are
        strings with the actual diff contents.
        """
        from reviewboard.diffviewer.diffutils import convert_to_unicode
        from reviewboard.diffviewer.models import FileDiff

        tool = repository.get_scmtool()

        encoding, diff_text = convert_to_unicode(
            diff_file_contents, repository.get_encoding_list())
        parser = tool.get_parser(diff_text)

        files = list(
            self._process_files(
                parser,
                basedir,
                repository,
                base_commit_id,
                request,
                check_existence=(not parent_diff_file_contents)))

        # Parse the diff
        if len(files) == 0:
            raise EmptyDiffError(_("The diff file is empty"))

        # Sort the files so that header files come before implementation.
        files.sort(cmp=self._compare_files, key=lambda f: f.origFile)

        # Parse the parent diff
        parent_files = {}

        # This is used only for tools like Mercurial that use atomic changeset
        # IDs to identify all file versions but not individual file version
        # IDs.
        parent_commit_id = None

        if parent_diff_file_contents:
            diff_filenames = set([f.origFile for f in files])

            parent_parser = tool.get_parser(
                convert_to_unicode(parent_diff_file_contents, [encoding])[1])

            # If the user supplied a base diff, we need to parse it and
            # later apply each of the files that are in the main diff
            for f in self._process_files(parent_parser,
                                         basedir,
                                         repository,
                                         base_commit_id,
                                         request,
                                         check_existence=True,
                                         limit_to=diff_filenames):
                parent_files[f.origFile] = f

            # This will return a non-None value only for tools that use
            # commit IDs to identify file versions as opposed to file revision
            # IDs.
            parent_commit_id = parent_parser.get_orig_commit_id()

        diffset = super(DiffSetManager,
                        self).create(name=diff_file_name,
                                     revision=0,
                                     basedir=basedir,
                                     history=diffset_history,
                                     repository=repository,
                                     diffcompat=DiffCompatVersion.DEFAULT,
                                     base_commit_id=base_commit_id)

        if save:
            diffset.save()

        for f in files:
            if f.origFile in parent_files:
                parent_file = parent_files[f.origFile]
                parent_content = parent_file.data.encode(encoding)
                source_rev = parent_file.origInfo
            else:
                parent_content = b""

                if parent_commit_id and f.origInfo != PRE_CREATION:
                    source_rev = parent_commit_id
                else:
                    source_rev = f.origInfo

            dest_file = os.path.join(basedir, f.newFile).replace("\\", "/")

            if f.deleted:
                status = FileDiff.DELETED
            elif f.moved:
                status = FileDiff.MOVED
            elif f.copied:
                status = FileDiff.COPIED
            else:
                status = FileDiff.MODIFIED

            filediff = FileDiff(diffset=diffset,
                                source_file=f.origFile,
                                dest_file=dest_file,
                                source_revision=smart_unicode(source_rev),
                                dest_detail=f.newInfo,
                                diff=f.data.encode(encoding),
                                parent_diff=parent_content,
                                binary=f.binary,
                                status=status)
            filediff.set_line_counts(raw_insert_count=f.insert_count,
                                     raw_delete_count=f.delete_count)

            if save:
                filediff.save()

        return diffset
Пример #26
0
    def create_from_data(self, repository, diff_file_name, diff_file_contents,
                         parent_diff_file_name, parent_diff_file_contents,
                         diffset_history, basedir, request, save=True):
        """Create a DiffSet from raw diff data.

        The diff_file_contents and parent_diff_file_contents parameters are
        strings with the actual diff contents.
        """
        from reviewboard.diffviewer.models import FileDiff

        tool = repository.get_scmtool()

        files = list(self._process_files(
            diff_file_contents, basedir, repository, request,
            check_existence=(not parent_diff_file_contents)))

        # Parse the diff
        if len(files) == 0:
            raise EmptyDiffError(_("The diff file is empty"))

        # Sort the files so that header files come before implementation.
        files.sort(cmp=self._compare_files, key=lambda f: f.origFile)

        # Parse the parent diff
        parent_files = {}

        # This is used only for tools like Mercurial that use atomic changeset
        # IDs to identify all file versions but not individual file version
        # IDs.
        parent_changeset_id = None

        if parent_diff_file_contents:
            diff_filenames = set([f.origFile for f in files])

            # If the user supplied a base diff, we need to parse it and
            # later apply each of the files that are in the main diff
            for f in self._process_files(parent_diff_file_contents, basedir,
                                         repository, request,
                                         check_existence=True,
                                         limit_to=diff_filenames):
                parent_files[f.origFile] = f

                # Store the original changeset ID if we have it; this should
                # be the same for all files.
                if f.origChangesetId:
                    parent_changeset_id = f.origChangesetId

        diffset = super(DiffSetManager, self).create(
            name=diff_file_name, revision=0,
            basedir=basedir,
            history=diffset_history,
            repository=repository,
            diffcompat=DEFAULT_DIFF_COMPAT_VERSION)
        if save:
            diffset.save()

        for f in files:
            if f.origFile in parent_files:
                parent_file = parent_files[f.origFile]
                parent_content = parent_file.data
                source_rev = parent_file.origInfo
            else:
                parent_content = ""

                if (tool.diff_uses_changeset_ids and
                    parent_changeset_id and
                    f.origInfo != PRE_CREATION):
                    source_rev = parent_changeset_id
                else:
                    source_rev = f.origInfo

            dest_file = os.path.join(basedir, f.newFile).replace("\\", "/")

            if f.deleted:
                status = FileDiff.DELETED
            elif f.moved:
                status = FileDiff.MOVED
            else:
                status = FileDiff.MODIFIED

            filediff = FileDiff(diffset=diffset,
                                source_file=f.origFile,
                                dest_file=dest_file,
                                source_revision=smart_unicode(source_rev),
                                dest_detail=f.newInfo,
                                diff=f.data,
                                parent_diff=parent_content,
                                binary=f.binary,
                                status=status)
            filediff.set_line_counts(f.insert_count, f.delete_count)
            if save:
                filediff.save()

        return diffset
Пример #27
0
class FileDiffMigrationTests(TestCase):
    fixtures = ['test_scmtools']

    def setUp(self):
        self.diff = (b'diff --git a/README b/README\n'
                     b'index d6613f5..5b50866 100644\n'
                     b'--- README\n'
                     b'+++ README\n'
                     b'@ -1,1 +1,1 @@\n'
                     b'-blah blah\n'
                     b'+blah!\n')
        self.parent_diff = (b'diff --git a/README b/README\n'
                            b'index d6613f5..5b50866 100644\n'
                            b'--- README\n'
                            b'+++ README\n'
                            b'@ -1,1 +1,1 @@\n'
                            b'-blah..\n'
                            b'+blah blah\n')

        repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64='')

    def test_migration_by_diff(self):
        """Testing FileDiffData migration accessing FileDiff.diff"""
        self.filediff.diff64 = self.diff

        self.assertEqual(self.filediff.diff_hash, None)
        self.assertEqual(self.filediff.parent_diff_hash, None)

        # This should prompt the migration
        diff = self.filediff.diff

        self.assertEqual(self.filediff.parent_diff_hash, None)
        self.assertNotEqual(self.filediff.diff_hash, None)

        self.assertEqual(diff, self.diff)
        self.assertEqual(self.filediff.diff64, '')
        self.assertEqual(self.filediff.diff_hash.binary, self.diff)
        self.assertEqual(self.filediff.diff, diff)
        self.assertEqual(self.filediff.parent_diff, None)
        self.assertEqual(self.filediff.parent_diff_hash, None)

    def test_migration_by_parent_diff(self):
        """Testing FileDiffData migration accessing FileDiff.parent_diff"""
        self.filediff.diff64 = self.diff
        self.filediff.parent_diff64 = self.parent_diff

        self.assertEqual(self.filediff.parent_diff_hash, None)

        # This should prompt the migration
        parent_diff = self.filediff.parent_diff

        self.assertNotEqual(self.filediff.parent_diff_hash, None)

        self.assertEqual(parent_diff, self.parent_diff)
        self.assertEqual(self.filediff.parent_diff64, '')
        self.assertEqual(self.filediff.parent_diff_hash.binary,
                         self.parent_diff)
        self.assertEqual(self.filediff.parent_diff, self.parent_diff)

    def test_migration_by_delete_count(self):
        """Testing FileDiffData migration accessing FileDiff.delete_count"""
        self.filediff.diff64 = self.diff

        self.assertEqual(self.filediff.diff_hash, None)

        # This should prompt the migration
        delete_count = self.filediff.delete_count

        self.assertNotEqual(self.filediff.diff_hash, None)
        self.assertEqual(delete_count, 1)
        self.assertEqual(self.filediff.diff_hash.delete_count, 1)

    def test_migration_by_insert_count(self):
        """Testing FileDiffData migration accessing FileDiff.insert_count"""
        self.filediff.diff64 = self.diff

        self.assertEqual(self.filediff.diff_hash, None)

        # This should prompt the migration
        insert_count = self.filediff.insert_count

        self.assertNotEqual(self.filediff.diff_hash, None)
        self.assertEqual(insert_count, 1)
        self.assertEqual(self.filediff.diff_hash.insert_count, 1)

    def test_migration_by_set_line_counts(self):
        """Testing FileDiffData migration calling FileDiff.set_line_counts"""
        self.filediff.diff64 = self.diff

        self.assertEqual(self.filediff.diff_hash, None)

        # This should prompt the migration, but with our line counts.
        self.filediff.set_line_counts(10, 20)

        self.assertNotEqual(self.filediff.diff_hash, None)
        self.assertEqual(self.filediff.insert_count, 10)
        self.assertEqual(self.filediff.delete_count, 20)
        self.assertEqual(self.filediff.diff_hash.insert_count, 10)
        self.assertEqual(self.filediff.diff_hash.delete_count, 20)
Пример #28
0
    def create_from_data(self,
                         repository,
                         diff_file_name,
                         diff_file_contents,
                         parent_diff_file_name,
                         parent_diff_file_contents,
                         diffset_history,
                         basedir,
                         request,
                         base_commit_id=None,
                         save=True):
        """Create a DiffSet from raw diff data.

        The diff_file_contents and parent_diff_file_contents parameters are
        strings with the actual diff contents.
        """
        from reviewboard.diffviewer.diffutils import convert_to_unicode
        from reviewboard.diffviewer.models import FileDiff

        tool = repository.get_scmtool()

        parser = tool.get_parser(diff_file_contents)

        files = list(
            self._process_files(
                parser,
                basedir,
                repository,
                base_commit_id,
                request,
                check_existence=(not parent_diff_file_contents)))

        # Parse the diff
        if len(files) == 0:
            raise EmptyDiffError(_("The diff file is empty"))

        # Sort the files so that header files come before implementation.
        files.sort(cmp=self._compare_files, key=lambda f: f.origFile)

        # Parse the parent diff
        parent_files = {}

        # This is used only for tools like Mercurial that use atomic changeset
        # IDs to identify all file versions but not individual file version
        # IDs.
        parent_commit_id = None

        if parent_diff_file_contents:
            diff_filenames = set([f.origFile for f in files])

            parent_parser = tool.get_parser(parent_diff_file_contents)

            # If the user supplied a base diff, we need to parse it and
            # later apply each of the files that are in the main diff
            for f in self._process_files(parent_parser,
                                         basedir,
                                         repository,
                                         base_commit_id,
                                         request,
                                         check_existence=True,
                                         limit_to=diff_filenames):
                parent_files[f.newFile] = f

            # This will return a non-None value only for tools that use
            # commit IDs to identify file versions as opposed to file revision
            # IDs.
            parent_commit_id = parent_parser.get_orig_commit_id()

        diffset = self.model(name=diff_file_name,
                             revision=0,
                             basedir=basedir,
                             history=diffset_history,
                             repository=repository,
                             diffcompat=DiffCompatVersion.DEFAULT,
                             base_commit_id=base_commit_id)

        if save:
            diffset.save()

        encoding_list = repository.get_encoding_list()

        for f in files:
            parent_file = None
            orig_rev = None
            parent_content = b''

            if f.origFile in parent_files:
                parent_file = parent_files[f.origFile]
                parent_content = parent_file.data
                orig_rev = parent_file.origInfo

            # If there is a parent file there is not necessarily an original
            # revision for the parent file in the case of a renamed file in
            # git.
            if not orig_rev:
                if parent_commit_id and f.origInfo != PRE_CREATION:
                    orig_rev = parent_commit_id
                else:
                    orig_rev = f.origInfo

            enc, orig_file = convert_to_unicode(f.origFile, encoding_list)
            enc, dest_file = convert_to_unicode(f.newFile, encoding_list)

            if f.deleted:
                status = FileDiff.DELETED
            elif f.moved:
                status = FileDiff.MOVED
            elif f.copied:
                status = FileDiff.COPIED
            else:
                status = FileDiff.MODIFIED

            filediff = FileDiff(
                diffset=diffset,
                source_file=parser.normalize_diff_filename(orig_file),
                dest_file=parser.normalize_diff_filename(dest_file),
                source_revision=smart_unicode(orig_rev),
                dest_detail=f.newInfo,
                diff=f.data,
                parent_diff=parent_content,
                binary=f.binary,
                status=status)

            if (parent_file and (parent_file.moved or parent_file.copied)
                    and parent_file.insert_count == 0
                    and parent_file.delete_count == 0):
                filediff.extra_data = {'parent_moved': True}

            filediff.set_line_counts(raw_insert_count=f.insert_count,
                                     raw_delete_count=f.delete_count)

            if save:
                filediff.save()

        return diffset
Пример #29
0
    def create_from_data(self, repository, diff_file_name, diff_file_contents,
                         parent_diff_file_name, parent_diff_file_contents,
                         diffset_history, basedir, request,
                         base_commit_id=None, save=True):
        """Create a DiffSet from raw diff data.

        The diff_file_contents and parent_diff_file_contents parameters are
        strings with the actual diff contents.
        """
        from reviewboard.diffviewer.diffutils import convert_to_unicode
        from reviewboard.diffviewer.models import FileDiff

        tool = repository.get_scmtool()

        encoding, diff_text = convert_to_unicode(
            diff_file_contents, repository.get_encoding_list())
        parser = tool.get_parser(diff_text)

        files = list(self._process_files(
            parser,
            basedir,
            repository,
            base_commit_id,
            request,
            check_existence=(not parent_diff_file_contents)))

        # Parse the diff
        if len(files) == 0:
            raise EmptyDiffError(_("The diff file is empty"))

        # Sort the files so that header files come before implementation.
        files.sort(cmp=self._compare_files, key=lambda f: f.origFile)

        # Parse the parent diff
        parent_files = {}

        # This is used only for tools like Mercurial that use atomic changeset
        # IDs to identify all file versions but not individual file version
        # IDs.
        parent_commit_id = None

        if parent_diff_file_contents:
            diff_filenames = set([f.origFile for f in files])

            parent_parser = tool.get_parser(
                convert_to_unicode(parent_diff_file_contents, [encoding])[1])

            # If the user supplied a base diff, we need to parse it and
            # later apply each of the files that are in the main diff
            for f in self._process_files(parent_parser, basedir,
                                         repository, base_commit_id, request,
                                         check_existence=True,
                                         limit_to=diff_filenames):
                parent_files[f.origFile] = f

            # This will return a non-None value only for tools that use
            # commit IDs to identify file versions as opposed to file revision
            # IDs.
            parent_commit_id = parent_parser.get_orig_commit_id()

        diffset = super(DiffSetManager, self).create(
            name=diff_file_name, revision=0,
            basedir=basedir,
            history=diffset_history,
            repository=repository,
            diffcompat=DiffCompatVersion.DEFAULT,
            base_commit_id=base_commit_id)

        if save:
            diffset.save()

        for f in files:
            if f.origFile in parent_files:
                parent_file = parent_files[f.origFile]
                parent_content = parent_file.data.encode(encoding)
                source_rev = parent_file.origInfo
            else:
                parent_content = b""

                if parent_commit_id and f.origInfo != PRE_CREATION:
                    source_rev = parent_commit_id
                else:
                    source_rev = f.origInfo

            dest_file = os.path.join(basedir, f.newFile).replace("\\", "/")

            if f.deleted:
                status = FileDiff.DELETED
            elif f.moved:
                status = FileDiff.MOVED
            elif f.copied:
                status = FileDiff.COPIED
            else:
                status = FileDiff.MODIFIED

            filediff = FileDiff(
                diffset=diffset,
                source_file=parser.normalize_diff_filename(f.origFile),
                dest_file=parser.normalize_diff_filename(dest_file),
                source_revision=smart_unicode(source_rev),
                dest_detail=f.newInfo,
                diff=f.data.encode(encoding),
                parent_diff=parent_content,
                binary=f.binary,
                status=status)
            filediff.set_line_counts(raw_insert_count=f.insert_count,
                                     raw_delete_count=f.delete_count)

            if save:
                filediff.save()

        return diffset
class FileDiffMigrationTests(TestCase):
    """Unit tests for FileDiff migration."""

    fixtures = ['test_scmtools']

    def setUp(self):
        super(FileDiffMigrationTests, self).setUp()

        self.repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64=b'',
                                 parent_diff64=b'')

        self.parent_diff = (b'diff --git a/README b/README\n'
                            b'index 94bdd3e..3d2b777 100644\n'
                            b'--- README\n'
                            b'+++ README\n'
                            b'@@ -2 +2 @@\n'
                            b'-blah..\n'
                            b'+blah blah\n')

        repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64=b'',
                                 parent_diff64=b'')

    def test_migration_by_diff(self):
        """Testing RawFileDiffData migration accessing FileDiff.diff"""
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA_DIFF

        self.assertIsNone(self.filediff.diff_hash)
        self.assertIsNone(self.filediff.parent_diff_hash)

        # This should prompt the migration.
        diff = self.filediff.diff

        self.assertIsNone(self.filediff.parent_diff_hash)
        self.assertIsNotNone(self.filediff.diff_hash)

        self.assertEqual(diff, self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)
        self.assertEqual(self.filediff.diff64, b'')
        self.assertEqual(self.filediff.diff_hash.binary,
                         self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)
        self.assertEqual(self.filediff.diff, diff)
        self.assertIsNone(self.filediff.parent_diff)
        self.assertIsNone(self.filediff.parent_diff_hash)

    def test_migration_by_parent_diff(self):
        """Testing RawFileDiffData migration accessing FileDiff.parent_diff"""
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA_DIFF
        self.filediff.parent_diff64 = self.parent_diff

        self.assertIsNone(self.filediff.parent_diff_hash)

        # This should prompt the migration.
        parent_diff = self.filediff.parent_diff

        self.assertIsNotNone(self.filediff.parent_diff_hash)

        self.assertEqual(parent_diff, self.parent_diff)
        self.assertEqual(self.filediff.parent_diff64, b'')
        self.assertEqual(self.filediff.parent_diff_hash.binary,
                         self.parent_diff)
        self.assertEqual(self.filediff.parent_diff, self.parent_diff)

    def test_migration_by_delete_count(self):
        """Testing RawFileDiffData migration accessing FileDiff.delete_count"""
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA_DIFF

        self.assertIsNone(self.filediff.diff_hash)

        # This should prompt the migration.
        counts = self.filediff.get_line_counts()

        self.assertIsNotNone(self.filediff.diff_hash)
        self.assertEqual(counts['raw_delete_count'], 1)
        self.assertEqual(self.filediff.diff_hash.delete_count, 1)

    def test_migration_by_insert_count(self):
        """Testing RawFileDiffData migration accessing FileDiff.insert_count"""
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA_DIFF

        self.assertIsNone(self.filediff.diff_hash)

        # This should prompt the migration.
        counts = self.filediff.get_line_counts()

        self.assertIsNotNone(self.filediff.diff_hash)
        self.assertEqual(counts['raw_insert_count'], 1)
        self.assertEqual(self.filediff.diff_hash.insert_count, 1)

    def test_migration_by_set_line_counts(self):
        """Testing RawFileDiffData migration calling FileDiff.set_line_counts
        """
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA_DIFF

        self.assertIsNone(self.filediff.diff_hash)

        # This should prompt the migration, but with our line counts.
        self.filediff.set_line_counts(raw_insert_count=10, raw_delete_count=20)

        self.assertIsNotNone(self.filediff.diff_hash)

        counts = self.filediff.get_line_counts()
        self.assertEqual(counts['raw_insert_count'], 10)
        self.assertEqual(counts['raw_delete_count'], 20)
        self.assertEqual(self.filediff.diff_hash.insert_count, 10)
        self.assertEqual(self.filediff.diff_hash.delete_count, 20)

    def test_migration_by_legacy_diff_hash(self):
        """Testing RawFileDiffData migration accessing FileDiff.diff
        with associated LegacyFileDiffData
        """
        legacy = LegacyFileDiffData.objects.create(
            binary_hash='abc123',
            binary=Base64DecodedValue(self.DEFAULT_GIT_FILEDIFF_DATA_DIFF))

        self.filediff.legacy_diff_hash = legacy
        self.filediff.save()

        # This should prompt the migration.
        diff = self.filediff.diff

        self.assertIsNotNone(self.filediff.diff_hash)
        self.assertIsNone(self.filediff.parent_diff_hash)
        self.assertIsNone(self.filediff.legacy_diff_hash)
        self.assertEqual(LegacyFileDiffData.objects.count(), 0)

        self.assertEqual(diff, self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)
        self.assertEqual(self.filediff.diff64, b'')
        self.assertEqual(self.filediff.diff_hash.content,
                         self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)
        self.assertEqual(self.filediff.diff, diff)
        self.assertIsNone(self.filediff.parent_diff)
        self.assertIsNone(self.filediff.parent_diff_hash)

    def test_migration_by_shared_legacy_diff_hash(self):
        """Testing RawFileDiffData migration accessing FileDiff.diff
        with associated shared LegacyFileDiffData
        """
        legacy = LegacyFileDiffData.objects.create(
            binary_hash='abc123',
            binary=Base64DecodedValue(self.DEFAULT_GIT_FILEDIFF_DATA_DIFF))

        self.filediff.legacy_diff_hash = legacy
        self.filediff.save()

        # Create a second FileDiff using this legacy data.
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=self.repository)
        FileDiff.objects.create(source_file='README',
                                dest_file='README',
                                diffset=diffset,
                                diff64=b'',
                                parent_diff64=b'',
                                legacy_diff_hash=legacy)

        # This should prompt the migration.
        diff = self.filediff.diff

        self.assertIsNotNone(self.filediff.diff_hash)
        self.assertIsNone(self.filediff.parent_diff_hash)
        self.assertIsNone(self.filediff.legacy_diff_hash)
        self.assertEqual(LegacyFileDiffData.objects.count(), 1)

        self.assertEqual(diff, self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)
        self.assertEqual(self.filediff.diff64, b'')
        self.assertEqual(self.filediff.diff_hash.content,
                         self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)
        self.assertEqual(self.filediff.diff, diff)
        self.assertIsNone(self.filediff.parent_diff)
        self.assertIsNone(self.filediff.parent_diff_hash)

    def test_migration_by_legacy_parent_diff_hash(self):
        """Testing RawFileDiffData migration accessing FileDiff.parent_diff
        with associated LegacyFileDiffData
        """
        legacy = LegacyFileDiffData.objects.create(binary_hash='abc123',
                                                   binary=Base64DecodedValue(
                                                       self.parent_diff))

        self.filediff.legacy_parent_diff_hash = legacy
        self.filediff.save()

        # This should prompt the migration.
        parent_diff = self.filediff.parent_diff

        self.assertIsNotNone(self.filediff.parent_diff_hash)
        self.assertIsNone(self.filediff.legacy_parent_diff_hash)

        self.assertEqual(parent_diff, self.parent_diff)
        self.assertEqual(self.filediff.parent_diff64, b'')
        self.assertEqual(self.filediff.parent_diff_hash.content,
                         self.parent_diff)
        self.assertEqual(self.filediff.parent_diff, parent_diff)

    def test_migration_by_shared_legacy_parent_diff_hash(self):
        """Testing RawFileDiffData migration accessing FileDiff.parent_diff
        with associated shared LegacyFileDiffData
        """
        legacy = LegacyFileDiffData.objects.create(binary_hash='abc123',
                                                   binary=Base64DecodedValue(
                                                       self.parent_diff))

        self.filediff.legacy_parent_diff_hash = legacy
        self.filediff.save()

        # Create a second FileDiff using this legacy data.
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=self.repository)
        FileDiff.objects.create(source_file='README',
                                dest_file='README',
                                diffset=diffset,
                                diff64=b'',
                                parent_diff64=b'',
                                legacy_parent_diff_hash=legacy)

        # This should prompt the migration.
        parent_diff = self.filediff.parent_diff

        self.assertIsNotNone(self.filediff.parent_diff_hash)
        self.assertIsNone(self.filediff.legacy_parent_diff_hash)
        self.assertEqual(LegacyFileDiffData.objects.count(), 1)

        self.assertEqual(parent_diff, self.parent_diff)
        self.assertEqual(self.filediff.parent_diff64, b'')
        self.assertEqual(self.filediff.parent_diff_hash.content,
                         self.parent_diff)
        self.assertEqual(self.filediff.parent_diff, parent_diff)

    def test_migration_with_legacy_and_race_condition(self):
        """Testing RawFileDiffData migration with LegacyFileDiffData and race
        condition in migrating
        """
        legacy = LegacyFileDiffData.objects.create(
            binary_hash='abc123',
            binary=Base64DecodedValue(self.DEFAULT_GIT_FILEDIFF_DATA_DIFF))
        parent_legacy = LegacyFileDiffData.objects.create(
            binary_hash='def456', binary=Base64DecodedValue(self.parent_diff))

        filediff1 = self.filediff
        filediff1.legacy_diff_hash = legacy
        filediff1.legacy_parent_diff_hash = parent_legacy
        filediff1.save()

        filediff2 = FileDiff.objects.get(pk=filediff1.pk)

        # Make sure that we're in the expected state.
        self.assertEqual(filediff1.legacy_diff_hash_id, legacy.pk)
        self.assertEqual(filediff1.legacy_parent_diff_hash_id,
                         parent_legacy.pk)
        self.assertEqual(filediff2.legacy_diff_hash_id, legacy.pk)
        self.assertEqual(filediff2.legacy_parent_diff_hash_id,
                         parent_legacy.pk)

        # This should prompt the migration of the first instance.
        diff1 = self.filediff.diff
        parent_diff1 = filediff1.parent_diff

        # This should prompt the migration of the second instance.
        diff2 = filediff2.diff
        parent_diff2 = filediff2.parent_diff

        # At this point, we should have valid diffs, and neither call
        # above should have raised an exception due to a dangling hash ID.
        self.assertEqual(diff1, self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)
        self.assertEqual(diff1, diff2)
        self.assertEqual(parent_diff1, self.parent_diff)
        self.assertEqual(parent_diff1, parent_diff2)

        self.assertEqual(LegacyFileDiffData.objects.count(), 0)
        self.assertEqual(RawFileDiffData.objects.count(), 2)

        # Check the hash references.
        self.assertIsNotNone(filediff1.diff_hash)
        self.assertIsNotNone(filediff2.diff_hash)
        self.assertEqual(filediff1.diff_hash, filediff2.diff_hash)
        self.assertIsNotNone(filediff1.parent_diff_hash)
        self.assertIsNotNone(filediff2.parent_diff_hash)
        self.assertEqual(filediff1.parent_diff_hash,
                         filediff2.parent_diff_hash)
        self.assertIsNone(filediff1.legacy_diff_hash)
        self.assertIsNone(filediff2.legacy_diff_hash)
        self.assertIsNone(filediff1.legacy_parent_diff_hash)
        self.assertIsNone(filediff2.legacy_parent_diff_hash)

        # Check the diff content.
        self.assertEqual(filediff1.diff64, b'')
        self.assertEqual(filediff2.diff64, b'')
        self.assertEqual(filediff1.diff_hash.content,
                         self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)
        self.assertEqual(filediff2.diff_hash.content,
                         self.DEFAULT_GIT_FILEDIFF_DATA_DIFF)

        # Check the parent_diff content.
        self.assertEqual(filediff1.parent_diff64, b'')
        self.assertEqual(filediff2.parent_diff64, b'')
        self.assertEqual(filediff1.parent_diff_hash.content, self.parent_diff)
        self.assertEqual(filediff2.parent_diff_hash.content, self.parent_diff)
Пример #31
0
def create_filediffs(diff_file_contents,
                     parent_diff_file_contents,
                     repository,
                     basedir,
                     base_commit_id,
                     diffset,
                     request=None,
                     check_existence=True,
                     get_file_exists=None,
                     diffcommit=None,
                     validate_only=False):
    """Create FileDiffs from the given data.

    Args:
        diff_file_contents (bytes):
            The contents of the diff file.

        parent_diff_file_contents (bytes):
            The contents of the parent diff file.

        repository (reviewboard.scmtools.models.Repository):
            The repository the diff is being posted against.

        basedir (unicode):
            The base directory to prepend to all file paths in the diff.

        base_commit_id (unicode):
            The ID of the commit that the diff is based upon. This is
            needed by some SCMs or hosting services to properly look up
            files, if the diffs represent blob IDs instead of commit IDs
            and the service doesn't support those lookups.

        diffset (reviewboard.diffviewer.models.diffset.DiffSet):
            The DiffSet to attach the created FileDiffs to.

        request (django.http.HttpRequest, optional):
            The current HTTP request.

        check_existence (bool, optional):
            Whether or not existence checks should be performed against
            the upstream repository.

            This argument defaults to ``True``.

        get_file_exists (callable, optional):
            A callable that is used to determine if a file exists.

            This must be provided if ``check_existence`` is ``True``.

        diffcommit (reviewboard.diffviewer.models.diffcommit.DiffCommit,
                    optional):
            The DiffCommit to attach the created FileDiffs to.

        validate_only (bool, optional):
            Whether to just validate and not save. If ``True``, then this
            won't populate the database at all and will return ``None``
            upon success. This defaults to ``False``.

    Returns:
        list of reviewboard.diffviewer.models.filediff.FileDiff:
        The created FileDiffs.

        If ``validate_only`` is ``True``, the returned list will be empty.
    """
    from reviewboard.diffviewer.diffutils import convert_to_unicode
    from reviewboard.diffviewer.models import FileDiff

    diff_info = _prepare_diff_info(
        diff_file_contents=diff_file_contents,
        parent_diff_file_contents=parent_diff_file_contents,
        repository=repository,
        request=request,
        basedir=basedir,
        check_existence=check_existence,
        get_file_exists=get_file_exists,
        base_commit_id=base_commit_id)

    parent_files = diff_info['parent_files']
    parsed_diff = diff_info['parsed_diff']
    parsed_parent_diff = diff_info['parsed_parent_diff']
    parser = diff_info['parser']

    encoding_list = repository.get_encoding_list()

    # Copy over any extra_data for the DiffSet and DiffCommit, if any were
    # set by the parser.
    #
    # We'll do this even if we're validating, to ensure the data can be
    # copied over fine.
    main_extra_data = deepcopy(parsed_diff.extra_data)
    change_extra_data = deepcopy(parsed_diff.changes[0].extra_data)

    if change_extra_data:
        if diffcommit is not None:
            # We've already checked in _parse_diff that there's only a single
            # change in the diff, so we can assume that here.
            diffcommit.extra_data.update(change_extra_data)
        else:
            main_extra_data['change_extra_data'] = change_extra_data

    if main_extra_data:
        diffset.extra_data.update(main_extra_data)

    if parsed_parent_diff is not None:
        parent_extra_data = deepcopy(parsed_parent_diff.extra_data)
        parent_change_extra_data = deepcopy(
            parsed_parent_diff.changes[0].extra_data)

        if parent_change_extra_data:
            if diffcommit is not None:
                diffcommit.extra_data['parent_extra_data'] = \
                    parent_change_extra_data
            else:
                parent_extra_data['change_extra_data'] = \
                    parent_change_extra_data

        if parent_extra_data:
            diffset.extra_data['parent_extra_data'] = parent_extra_data

    # Convert the list of parsed files into FileDiffs.
    filediffs = []

    for f in diff_info['files']:
        parent_file = None
        parent_content = b''

        extra_data = f.extra_data.copy()

        if parsed_parent_diff is not None:
            parent_file = parent_files.get(f.orig_filename)

            if parent_file is not None:
                parent_content = parent_file.data

                # Store the information on the parent's filename and revision.
                # It's important we force these to text, since they may be
                # byte strings and the revision may be a Revision instance.
                parent_source_filename = parent_file.orig_filename
                parent_source_revision = parent_file.orig_file_details

                parent_is_empty = (parent_file.insert_count == 0
                                   and parent_file.delete_count == 0)

                if parent_file.moved or parent_file.copied:
                    extra_data['parent_moved'] = True

                if parent_file.extra_data:
                    extra_data['parent_extra_data'] = \
                        parent_file.extra_data.copy()
            else:
                # We don't have an entry, but we still want to record the
                # parent ID, so we have something in common for all the files
                # when looking up the source revision to fetch from the
                # repository.
                parent_is_empty = True
                parent_source_filename = f.orig_filename
                parent_source_revision = f.orig_file_details

                if (parent_source_revision != PRE_CREATION
                        and parsed_diff.uses_commit_ids_as_revisions):
                    # Since the file wasn't explicitly provided in the parent
                    # diff, but the ParsedDiff says that commit IDs are used
                    # as revisions, we can use its parent commit ID as the
                    # parent revision here.
                    parent_commit_id = \
                        parsed_parent_diff.changes[0].parent_commit_id
                    assert parent_commit_id

                    parent_source_revision = parent_commit_id

            # Store the information on the parent's filename and revision.
            # It's important we force these to text, since they may be
            # byte strings and the revision may be a Revision instance.
            #
            # Starting in Review Board 4.0.5, we store this any time there's
            # a parent diff, whether or not the file existed in the parent
            # diff.
            extra_data.update({
                FileDiff._IS_PARENT_EMPTY_KEY:
                parent_is_empty,
                'parent_source_filename':
                convert_to_unicode(parent_source_filename, encoding_list)[1],
                'parent_source_revision':
                convert_to_unicode(parent_source_revision, encoding_list)[1],
            })

        orig_file = convert_to_unicode(f.orig_filename, encoding_list)[1]
        dest_file = convert_to_unicode(f.modified_filename, encoding_list)[1]

        if f.deleted:
            status = FileDiff.DELETED
        elif f.moved:
            status = FileDiff.MOVED
        elif f.copied:
            status = FileDiff.COPIED
        else:
            status = FileDiff.MODIFIED

        filediff = FileDiff(
            diffset=diffset,
            commit=diffcommit,
            source_file=parser.normalize_diff_filename(orig_file),
            dest_file=parser.normalize_diff_filename(dest_file),
            source_revision=force_text(f.orig_file_details),
            dest_detail=force_text(f.modified_file_details),
            binary=f.binary,
            status=status,
            extra_data=extra_data)

        # Set this unconditionally, for backwards-compatibility purposes.
        # Review Board 4.0.6 introduced attribute wrappers in FileDiff and
        # introduced symlink targets. We ideally would not set this unless
        # it's True, but we don't want to risk breaking any assumptions on
        # its presence at this time.
        filediff.is_symlink = f.is_symlink

        if f.is_symlink:
            if f.old_symlink_target:
                filediff.old_symlink_target = \
                    convert_to_unicode(f.old_symlink_target, encoding_list)[1]

            if f.new_symlink_target:
                filediff.new_symlink_target = \
                    convert_to_unicode(f.new_symlink_target, encoding_list)[1]

        filediff.old_unix_mode = f.old_unix_mode
        filediff.new_unix_mode = f.new_unix_mode

        if not validate_only:
            # This state all requires making modifications to the database.
            # We only want to do this if we're saving.
            filediff.diff = f.data
            filediff.parent_diff = parent_content

            filediff.set_line_counts(raw_insert_count=f.insert_count,
                                     raw_delete_count=f.delete_count)

        filediffs.append(filediff)

    if not validate_only:
        FileDiff.objects.bulk_create(filediffs)

        if diffset.extra_data:
            diffset.save(update_fields=('extra_data', ))

        if diffcommit is not None and diffcommit.extra_data:
            diffcommit.save(update_fields=('extra_data', ))

    return filediffs
Пример #32
0
class FileDiffMigrationTests(TestCase):
    fixtures = ['test_scmtools']

    def setUp(self):
        self.diff = (
            'diff --git a/README b/README\n'
            'index d6613f5..5b50866 100644\n'
            '--- README\n'
            '+++ README\n'
            '@ -1,1 +1,1 @@\n'
            '-blah blah\n'
            '+blah!\n')
        self.parent_diff = (
            'diff --git a/README b/README\n'
            'index d6613f5..5b50866 100644\n'
            '--- README\n'
            '+++ README\n'
            '@ -1,1 +1,1 @@\n'
            '-blah..\n'
            '+blah blah\n')

        repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64='')

    def test_migration_by_diff(self):
        """Testing FileDiffData migration accessing FileDiff.diff"""
        self.filediff.diff64 = self.diff

        self.assertEqual(self.filediff.diff_hash, None)
        self.assertEqual(self.filediff.parent_diff_hash, None)

        # This should prompt the migration
        diff = self.filediff.diff

        self.assertEqual(self.filediff.parent_diff_hash, None)
        self.assertNotEqual(self.filediff.diff_hash, None)

        self.assertEqual(diff, self.diff)
        self.assertEqual(self.filediff.diff64, '')
        self.assertEqual(self.filediff.diff_hash.binary, self.diff)
        self.assertEqual(self.filediff.diff, diff)
        self.assertEqual(self.filediff.parent_diff, None)
        self.assertEqual(self.filediff.parent_diff_hash, None)

    def test_migration_by_parent_diff(self):
        """Testing FileDiffData migration accessing FileDiff.parent_diff"""
        self.filediff.diff64 = self.diff
        self.filediff.parent_diff64 = self.parent_diff

        self.assertEqual(self.filediff.parent_diff_hash, None)

        # This should prompt the migration
        parent_diff = self.filediff.parent_diff

        self.assertNotEqual(self.filediff.parent_diff_hash, None)

        self.assertEqual(parent_diff, self.parent_diff)
        self.assertEqual(self.filediff.parent_diff64, '')
        self.assertEqual(self.filediff.parent_diff_hash.binary,
                         self.parent_diff)
        self.assertEqual(self.filediff.parent_diff, self.parent_diff)

    def test_migration_by_delete_count(self):
        """Testing FileDiffData migration accessing FileDiff.delete_count"""
        self.filediff.diff64 = self.diff

        self.assertEqual(self.filediff.diff_hash, None)

        # This should prompt the migration
        delete_count = self.filediff.delete_count

        self.assertNotEqual(self.filediff.diff_hash, None)
        self.assertEqual(delete_count, 1)
        self.assertEqual(self.filediff.diff_hash.delete_count, 1)

    def test_migration_by_insert_count(self):
        """Testing FileDiffData migration accessing FileDiff.insert_count"""
        self.filediff.diff64 = self.diff

        self.assertEqual(self.filediff.diff_hash, None)

        # This should prompt the migration
        insert_count = self.filediff.insert_count

        self.assertNotEqual(self.filediff.diff_hash, None)
        self.assertEqual(insert_count, 1)
        self.assertEqual(self.filediff.diff_hash.insert_count, 1)

    def test_migration_by_set_line_counts(self):
        """Testing FileDiffData migration calling FileDiff.set_line_counts"""
        self.filediff.diff64 = self.diff

        self.assertEqual(self.filediff.diff_hash, None)

        # This should prompt the migration, but with our line counts.
        self.filediff.set_line_counts(10, 20)

        self.assertNotEqual(self.filediff.diff_hash, None)
        self.assertEqual(self.filediff.insert_count, 10)
        self.assertEqual(self.filediff.delete_count, 20)
        self.assertEqual(self.filediff.diff_hash.insert_count, 10)
        self.assertEqual(self.filediff.diff_hash.delete_count, 20)
Пример #33
0
class FileDiffTests(TestCase):
    """Unit tests for FileDiff."""

    fixtures = ['test_scmtools']

    def setUp(self):
        super(FileDiffTests, self).setUp()

        diff = (b'diff --git a/README b/README\n'
                b'index 3d2b777..48272a3 100644\n'
                b'--- README\n'
                b'+++ README\n'
                b'@@ -2 +2,2 @@\n'
                b'-blah blah\n'
                b'+blah!\n'
                b'+blah!!\n')

        self.repository = self.create_repository(tool_name='Test')
        self.diffset = DiffSet.objects.create(name='test',
                                              revision=1,
                                              repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=self.diffset,
                                 diff64=diff,
                                 parent_diff64=b'')

    def test_get_line_counts_with_defaults(self):
        """Testing FileDiff.get_line_counts with default values"""
        counts = self.filediff.get_line_counts()

        self.assertIn('raw_insert_count', counts)
        self.assertIn('raw_delete_count', counts)
        self.assertIn('insert_count', counts)
        self.assertIn('delete_count', counts)
        self.assertIn('replace_count', counts)
        self.assertIn('equal_count', counts)
        self.assertIn('total_line_count', counts)
        self.assertEqual(counts['raw_insert_count'], 2)
        self.assertEqual(counts['raw_delete_count'], 1)
        self.assertEqual(counts['insert_count'], 2)
        self.assertEqual(counts['delete_count'], 1)
        self.assertIsNone(counts['replace_count'])
        self.assertIsNone(counts['equal_count'])
        self.assertIsNone(counts['total_line_count'])

        diff_hash = self.filediff.diff_hash
        self.assertEqual(diff_hash.insert_count, 2)
        self.assertEqual(diff_hash.delete_count, 1)

    def test_set_line_counts(self):
        """Testing FileDiff.set_line_counts"""
        self.filediff.set_line_counts(raw_insert_count=1,
                                      raw_delete_count=2,
                                      insert_count=3,
                                      delete_count=4,
                                      replace_count=5,
                                      equal_count=6,
                                      total_line_count=7)

        counts = self.filediff.get_line_counts()
        self.assertEqual(counts['raw_insert_count'], 1)
        self.assertEqual(counts['raw_delete_count'], 2)
        self.assertEqual(counts['insert_count'], 3)
        self.assertEqual(counts['delete_count'], 4)
        self.assertEqual(counts['replace_count'], 5)
        self.assertEqual(counts['equal_count'], 6)
        self.assertEqual(counts['total_line_count'], 7)

        diff_hash = self.filediff.diff_hash
        self.assertEqual(diff_hash.insert_count, 1)
        self.assertEqual(diff_hash.delete_count, 2)

    def test_long_filenames(self):
        """Testing FileDiff with long filenames (1024 characters)"""
        long_filename = 'x' * 1024

        filediff = FileDiff.objects.create(source_file=long_filename,
                                           dest_file='foo',
                                           diffset=self.diffset)
        self.assertEqual(filediff.source_file, long_filename)

    def test_diff_hashes(self):
        """Testing FileDiff with multiple entries and same diff data
        deduplicates data
        """
        data = (b'diff -rcN orig_src/foo.c new_src/foo.c\n'
                b'*** orig_src/foo.c\t2007-01-24 02:11:31.000000000 -0800\n'
                b'--- new_src/foo.c\t2007-01-24 02:14:42.000000000 -0800\n'
                b'***************\n'
                b'*** 1,5 ****\n'
                b'  int\n'
                b'  main()\n'
                b'  {\n'
                b'! \tprintf("foo\n");\n'
                b'  }\n'
                b'--- 1,8 ----\n'
                b'+ #include <stdio.h>\n'
                b'+ \n'
                b'  int\n'
                b'  main()\n'
                b'  {\n'
                b'! \tprintf("foo bar\n");\n'
                b'! \treturn 0;\n'
                b'  }\n')

        filediff1 = FileDiff.objects.create(diff=data, diffset=self.diffset)
        filediff2 = FileDiff.objects.create(diff=data, diffset=self.diffset)

        self.assertEqual(filediff1.diff_hash, filediff2.diff_hash)

    def test_get_base_filediff(self):
        """Testing FileDiff.get_base_filediff"""
        commit1 = self.create_diffcommit(
            diffset=self.diffset,
            commit_id='r1',
            parent_id='r0',
            diff_contents=(b'diff --git a/ABC b/ABC\n'
                           b'index 94bdd3e..197009f 100644\n'
                           b'--- ABC\n'
                           b'+++ ABC\n'
                           b'@@ -1,1 +1,1 @@\n'
                           b'-line!\n'
                           b'+line..\n'))
        commit2 = self.create_diffcommit(
            diffset=self.diffset,
            commit_id='r2',
            parent_id='r1',
            diff_contents=(b'diff --git a/README b/README\n'
                           b'index 94bdd3e..197009f 100644\n'
                           b'--- README\n'
                           b'+++ README\n'
                           b'@@ -1,1 +1,1 @@\n'
                           b'-Hello, world!\n'
                           b'+Hi, world!\n'))
        commit3 = self.create_diffcommit(
            diffset=self.diffset,
            commit_id='r3',
            parent_id='r2',
            diff_contents=(b'diff --git a/FOO b/FOO\n'
                           b'index 84bda3e..b975034 100644\n'
                           b'--- FOO\n'
                           b'+++ FOO\n'
                           b'@@ -1,1 +0,0 @@\n'
                           b'-Some line\n'))
        commit4 = self.create_diffcommit(
            diffset=self.diffset,
            commit_id='r4',
            parent_id='r3',
            diff_contents=(b'diff --git a/README b/README\n'
                           b'index 197009f..87abad9 100644\n'
                           b'--- README\n'
                           b'+++ README\n'
                           b'@@ -1,1 +1,1 @@\n'
                           b'-Hi, world!\n'
                           b'+Yo, world.\n'))

        self.diffset.finalize_commit_series(
            cumulative_diff=(b'diff --git a/ABC b/ABC\n'
                             b'index 94bdd3e..197009f 100644\n'
                             b'--- ABC\n'
                             b'+++ ABC\n'
                             b'@@ -1,1 +1,1 @@\n'
                             b'-line!\n'
                             b'+line..\n'
                             b'diff --git a/FOO b/FOO\n'
                             b'index 84bda3e..b975034 100644\n'
                             b'--- FOO\n'
                             b'+++ FOO\n'
                             b'@@ -1,1 +0,0 @@\n'
                             b'-Some line\n'
                             b'diff --git a/README b/README\n'
                             b'index 94bdd3e..87abad9 100644\n'
                             b'--- README\n'
                             b'+++ README\n'
                             b'@@ -1,1 +1,1 @@\n'
                             b'-Hello, world!\n'
                             b'+Yo, world.\n'),
            validation_info=None,
            validate=False,
            save=True)

        filediff1 = commit1.files.get()
        filediff2 = commit2.files.get()
        filediff3 = commit3.files.get()
        filediff4 = commit4.files.get()

        for commit in (commit1, commit2, commit3, commit4):
            self.assertIsNone(filediff1.get_base_filediff(base_commit=commit))
            self.assertIsNone(filediff2.get_base_filediff(base_commit=commit))
            self.assertIsNone(filediff3.get_base_filediff(base_commit=commit))

        self.assertIsNone(filediff4.get_base_filediff(base_commit=commit1))
        self.assertEqual(filediff4.get_base_filediff(base_commit=commit2),
                         filediff2)
        self.assertEqual(filediff4.get_base_filediff(base_commit=commit3),
                         filediff2)
        self.assertEqual(filediff4.get_base_filediff(base_commit=commit4),
                         filediff2)

    def test_get_base_filediff_without_commit(self):
        """Testing FileDiff.get_base_filediff without associated commit"""
        filediff = self.create_filediff(self.diffset)

        self.assertIsNone(filediff.get_base_filediff(base_commit=None))
Пример #34
0
def create_filediffs(diff_file_contents, parent_diff_file_contents,
                     repository, basedir, base_commit_id, diffset,
                     request=None, check_existence=True, get_file_exists=None,
                     diffcommit=None, validate_only=False):
    """Create FileDiffs from the given data.

    Args:
        diff_file_contents (bytes):
            The contents of the diff file.

        parent_diff_file_contents (bytes):
            The contents of the parent diff file.

        repository (reviewboard.scmtools.models.Repository):
            The repository the diff is being posted against.

        basedir (unicode):
            The base directory to prepend to all file paths in the diff.

        base_commit_id (unicode):
            The ID of the commit that the diff is based upon. This is
            needed by some SCMs or hosting services to properly look up
            files, if the diffs represent blob IDs instead of commit IDs
            and the service doesn't support those lookups.

        diffset (reviewboard.diffviewer.models.diffset.DiffSet):
            The DiffSet to attach the created FileDiffs to.

        request (django.http.HttpRequest, optional):
            The current HTTP request.

        check_existence (bool, optional):
            Whether or not existence checks should be performed against
            the upstream repository.

            This argument defaults to ``True``.

        get_file_exists (callable, optional):
            A callable that is used to determine if a file exists.

            This must be provided if ``check_existence`` is ``True``.

        diffcommit (reviewboard.diffviewer.models.diffcommit.DiffCommit,
                    optional):
            The Diffcommit to attach the created FileDiffs to.

        validate_only (bool, optional):
            Whether to just validate and not save. If ``True``, then this
            won't populate the database at all and will return ``None``
            upon success. This defaults to ``False``.

    Returns:
        list of reviewboard.diffviewer.models.filediff.FileDiff:
        The created FileDiffs.

        If ``validate_only`` is ``True``, the returned list will be empty.
    """
    from reviewboard.diffviewer.diffutils import convert_to_unicode
    from reviewboard.diffviewer.models import FileDiff

    files, parser, parent_commit_id, parent_files = _prepare_file_list(
        diff_file_contents=diff_file_contents,
        parent_diff_file_contents=parent_diff_file_contents,
        repository=repository,
        request=request,
        basedir=basedir,
        check_existence=check_existence,
        get_file_exists=get_file_exists,
        base_commit_id=base_commit_id)

    encoding_list = repository.get_encoding_list()
    filediffs = []

    for f in files:
        parent_file = None
        orig_rev = None
        parent_content = b''

        if f.origFile in parent_files:
            parent_file = parent_files[f.origFile]
            parent_content = parent_file.data
            orig_rev = parent_file.origInfo

        # If there is a parent file there is not necessarily an original
        # revision for the parent file in the case of a renamed file in
        # git.
        if not orig_rev:
            if parent_commit_id and f.origInfo != PRE_CREATION:
                orig_rev = parent_commit_id
            else:
                orig_rev = f.origInfo

        orig_file = convert_to_unicode(f.origFile, encoding_list)[1]
        dest_file = convert_to_unicode(f.newFile, encoding_list)[1]

        if f.deleted:
            status = FileDiff.DELETED
        elif f.moved:
            status = FileDiff.MOVED
        elif f.copied:
            status = FileDiff.COPIED
        else:
            status = FileDiff.MODIFIED

        filediff = FileDiff(
            diffset=diffset,
            commit=diffcommit,
            source_file=parser.normalize_diff_filename(orig_file),
            dest_file=parser.normalize_diff_filename(dest_file),
            source_revision=force_text(orig_rev),
            dest_detail=f.newInfo,
            binary=f.binary,
            status=status)

        filediff.extra_data = {
            'is_symlink': f.is_symlink,
        }

        if parent_file:
            if (parent_file.insert_count == 0 and
                parent_file.delete_count == 0):
                filediff.extra_data[FileDiff._IS_PARENT_EMPTY_KEY] = True

                if parent_file.moved or parent_file.copied:
                    filediff.extra_data['parent_moved'] = True
            else:
                filediff.extra_data[FileDiff._IS_PARENT_EMPTY_KEY] = False

        if not validate_only:
            # This state all requires making modifications to the database.
            # We only want to do this if we're saving.
            filediff.diff = f.data
            filediff.parent_diff = parent_content

            filediff.set_line_counts(raw_insert_count=f.insert_count,
                                     raw_delete_count=f.delete_count)

        filediffs.append(filediff)

    if not validate_only:
        FileDiff.objects.bulk_create(filediffs)
        num_filediffs = len(filediffs)

    return filediffs
Пример #35
0
    def create(self, diff_file, parent_diff_file=None, diffset_history=None):
        tool = self.repository.get_scmtool()

        siteconfig = SiteConfiguration.objects.get_current()
        max_diff_size = siteconfig.get('diffviewer_max_diff_size')

        if max_diff_size > 0:
            if diff_file.size > max_diff_size:
                raise DiffTooBigError(_('The supplied diff file is too large'),
                                      max_diff_size=max_diff_size)

            if parent_diff_file and parent_diff_file.size > max_diff_size:
                raise DiffTooBigError(
                    _('The supplied parent diff file is too large'),
                    max_diff_size=max_diff_size)

        # Grab the base directory if there is one.
        if not tool.get_diffs_use_absolute_paths():
            try:
                basedir = smart_unicode(self.cleaned_data['basedir'].strip())
            except AttributeError:
                raise NoBaseDirError(
                    _('The "Base Diff Path" field is required'))
        else:
            basedir = ''

        # Parse the diff
        files = list(
            self._process_files(diff_file,
                                basedir,
                                check_existance=(not parent_diff_file)))

        if len(files) == 0:
            raise EmptyDiffError(_("The diff file is empty"))

        # Sort the files so that header files come before implementation.
        files.sort(cmp=self._compare_files, key=lambda f: f.origFile)

        # Parse the parent diff
        parent_files = {}

        # This is used only for tools like Mercurial that use atomic changeset
        # IDs to identify all file versions but not individual file version
        # IDs.
        parent_changeset_id = None

        if parent_diff_file:
            diff_filenames = set([f.origFile for f in files])

            # If the user supplied a base diff, we need to parse it and
            # later apply each of the files that are in the main diff
            for f in self._process_files(parent_diff_file,
                                         basedir,
                                         check_existance=True,
                                         limit_to=diff_filenames):
                parent_files[f.origFile] = f

                # Store the original changeset ID if we have it; this should
                # be the same for all files.
                if f.origChangesetId:
                    parent_changeset_id = f.origChangesetId

        diffset = DiffSet(name=diff_file.name,
                          revision=0,
                          basedir=basedir,
                          history=diffset_history,
                          diffcompat=DEFAULT_DIFF_COMPAT_VERSION)
        diffset.repository = self.repository
        diffset.save()

        for f in files:
            if f.origFile in parent_files:
                parent_file = parent_files[f.origFile]
                parent_content = parent_file.data
                source_rev = parent_file.origInfo
            else:
                parent_content = ""

                if (tool.diff_uses_changeset_ids and parent_changeset_id
                        and f.origInfo != PRE_CREATION):
                    source_rev = parent_changeset_id
                else:
                    source_rev = f.origInfo

            dest_file = os.path.join(basedir, f.newFile).replace("\\", "/")

            if f.deleted:
                status = FileDiff.DELETED
            elif f.moved:
                status = FileDiff.MOVED
            else:
                status = FileDiff.MODIFIED

            filediff = FileDiff(diffset=diffset,
                                source_file=f.origFile,
                                dest_file=dest_file,
                                source_revision=smart_unicode(source_rev),
                                dest_detail=f.newInfo,
                                diff=f.data,
                                parent_diff=parent_content,
                                binary=f.binary,
                                status=status)
            filediff.set_line_counts(f.insert_count, f.delete_count)
            filediff.save()

        return diffset
Пример #36
0
class FileDiffTests(TestCase):
    """Unit tests for FileDiff."""

    fixtures = ['test_scmtools']

    def setUp(self):
        super(FileDiffTests, self).setUp()

        diff = (b'diff --git a/README b/README\n'
                b'index 3d2b777..48272a3 100644\n'
                b'--- README\n'
                b'+++ README\n'
                b'@@ -2 +2,2 @@\n'
                b'-blah blah\n'
                b'+blah!\n'
                b'+blah!!\n')

        self.repository = self.create_repository(tool_name='Test')
        self.diffset = DiffSet.objects.create(name='test',
                                              revision=1,
                                              repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=self.diffset,
                                 diff64=diff,
                                 parent_diff64=b'')

    def test_get_line_counts_with_defaults(self):
        """Testing FileDiff.get_line_counts with default values"""
        counts = self.filediff.get_line_counts()

        self.assertIn('raw_insert_count', counts)
        self.assertIn('raw_delete_count', counts)
        self.assertIn('insert_count', counts)
        self.assertIn('delete_count', counts)
        self.assertIn('replace_count', counts)
        self.assertIn('equal_count', counts)
        self.assertIn('total_line_count', counts)
        self.assertEqual(counts['raw_insert_count'], 2)
        self.assertEqual(counts['raw_delete_count'], 1)
        self.assertEqual(counts['insert_count'], 2)
        self.assertEqual(counts['delete_count'], 1)
        self.assertIsNone(counts['replace_count'])
        self.assertIsNone(counts['equal_count'])
        self.assertIsNone(counts['total_line_count'])

        diff_hash = self.filediff.diff_hash
        self.assertEqual(diff_hash.insert_count, 2)
        self.assertEqual(diff_hash.delete_count, 1)

    def test_set_line_counts(self):
        """Testing FileDiff.set_line_counts"""
        self.filediff.set_line_counts(raw_insert_count=1,
                                      raw_delete_count=2,
                                      insert_count=3,
                                      delete_count=4,
                                      replace_count=5,
                                      equal_count=6,
                                      total_line_count=7)

        counts = self.filediff.get_line_counts()
        self.assertEqual(counts['raw_insert_count'], 1)
        self.assertEqual(counts['raw_delete_count'], 2)
        self.assertEqual(counts['insert_count'], 3)
        self.assertEqual(counts['delete_count'], 4)
        self.assertEqual(counts['replace_count'], 5)
        self.assertEqual(counts['equal_count'], 6)
        self.assertEqual(counts['total_line_count'], 7)

        diff_hash = self.filediff.diff_hash
        self.assertEqual(diff_hash.insert_count, 1)
        self.assertEqual(diff_hash.delete_count, 2)

    def test_long_filenames(self):
        """Testing FileDiff with long filenames (1024 characters)"""
        long_filename = 'x' * 1024

        filediff = FileDiff.objects.create(source_file=long_filename,
                                           dest_file='foo',
                                           diffset=self.diffset)
        self.assertEqual(filediff.source_file, long_filename)

    def test_diff_hashes(self):
        """Testing FileDiff with multiple entries and same diff data
        deduplicates data
        """
        data = (b'diff -rcN orig_src/foo.c new_src/foo.c\n'
                b'*** orig_src/foo.c\t2007-01-24 02:11:31.000000000 -0800\n'
                b'--- new_src/foo.c\t2007-01-24 02:14:42.000000000 -0800\n'
                b'***************\n'
                b'*** 1,5 ****\n'
                b'  int\n'
                b'  main()\n'
                b'  {\n'
                b'! \tprintf("foo\n");\n'
                b'  }\n'
                b'--- 1,8 ----\n'
                b'+ #include <stdio.h>\n'
                b'+ \n'
                b'  int\n'
                b'  main()\n'
                b'  {\n'
                b'! \tprintf("foo bar\n");\n'
                b'! \treturn 0;\n'
                b'  }\n')

        filediff1 = FileDiff.objects.create(diff=data, diffset=self.diffset)
        filediff2 = FileDiff.objects.create(diff=data, diffset=self.diffset)

        self.assertEqual(filediff1.diff_hash, filediff2.diff_hash)
Пример #37
0
    def create_from_data(self,
                         repository,
                         diff_file_name,
                         diff_file_contents,
                         parent_diff_file_name=None,
                         parent_diff_file_contents=None,
                         diffset_history=None,
                         basedir=None,
                         request=None,
                         base_commit_id=None,
                         check_existence=True,
                         validate_only=False,
                         **kwargs):
        """Create a DiffSet from raw diff data.

        This parses a diff and optional parent diff covering one or more files,
        validates, and constructs :py:class:`DiffSets
        <reviewboard.diffviewer.models.DiffSet>` and :py:class:`FileDiffs
        <reviewboard.diffviewer.models.FileDiff>` representing the diff.

        This can optionally validate the diff without saving anything to the
        database. In this case, no value will be returned. Instead, callers
        should take any result as success.

        Args:
            repository (reviewboard.scmtools.models.Repository):
                The repository the diff applies to.

            diff_file_name (unicode):
                The filename of the main diff file.

            diff_file_contents (bytes):
                The contents of the main diff file.

            parent_diff_file_name (unicode, optional):
                The filename of the parent diff, if one is provided.

            parent_diff_file_contents (bytes, optional):
                The contents of the parent diff, if one is provided.

            diffset_history (reviewboard.diffviewer.models.DiffSetHistory, optional):
                The history object to associate the DiffSet with. This is
                not required if using ``validate_only=True``.

            basedir (unicode, optional):
                The base directory to prepend to all file paths in the diff.

            request (django.http.HttpRequest, optional):
                The current HTTP request, if any. This will result in better
                logging.

            base_commit_id (unicode, optional):
                The ID of the commit that the diff is based upon. This is
                needed by some SCMs or hosting services to properly look up
                files, if the diffs represent blob IDs instead of commit IDs
                and the service doesn't support those lookups.

            check_existence (bool, optional):
                Whether to check for file existence as part of the validation
                process. This defaults to ``True``.

            validate_only (bool, optional):
                Whether to just validate and not save. If ``True``, then this
                won't populate the database at all and will return ``None``
                upon success. This defaults to ``False``.

        Returns:
            reviewboard.diffviewer.models.DiffSet:
            The resulting DiffSet stored in the database, if processing
            succeeded and ``validate_only=False``.

        Raises:
            reviewboard.diffviewer.errors.DiffParserError:
                There was an error parsing the main diff or parent diff.

            reviewboard.diffviewer.errors.EmptyDiffError:
                The provided diff file did not contain any file changes.

            reviewboard.scmtools.core.FileNotFoundError:
                A file specified in the diff could not be found in the
                repository.

            reviewboard.scmtools.core.SCMError:
                There was an error talking to the repository when validating
                the existence of a file.

            reviewboard.scmtools.git.ShortSHA1Error:
                A SHA1 specified in the diff was in the short form, which
                could not be used to look up the file. This is applicable only
                to Git.
        """
        from reviewboard.diffviewer.diffutils import convert_to_unicode
        from reviewboard.diffviewer.models import FileDiff

        if 'save' in kwargs:
            warnings.warn(
                'The save parameter to '
                'DiffSet.objects.create_from_data is deprecated. '
                'Please set validate_only instead.', DeprecationWarning)
            validate_only = not kwargs['save']

        tool = repository.get_scmtool()
        parser = tool.get_parser(diff_file_contents)

        files = list(
            self._process_files(parser,
                                basedir,
                                repository,
                                base_commit_id,
                                request,
                                check_existence=check_existence
                                and not parent_diff_file_contents))

        # Parse the diff
        if len(files) == 0:
            raise EmptyDiffError(_("The diff file is empty"))

        # Sort the files so that header files come before implementation.
        files.sort(cmp=self._compare_files, key=lambda f: f.origFile)

        # Parse the parent diff
        parent_files = {}

        # This is used only for tools like Mercurial that use atomic changeset
        # IDs to identify all file versions but not individual file version
        # IDs.
        parent_commit_id = None

        if parent_diff_file_contents:
            diff_filenames = set([f.origFile for f in files])

            parent_parser = tool.get_parser(parent_diff_file_contents)

            # If the user supplied a base diff, we need to parse it and
            # later apply each of the files that are in the main diff
            for f in self._process_files(parent_parser,
                                         basedir,
                                         repository,
                                         base_commit_id,
                                         request,
                                         check_existence=check_existence,
                                         limit_to=diff_filenames):
                parent_files[f.newFile] = f

            # This will return a non-None value only for tools that use
            # commit IDs to identify file versions as opposed to file revision
            # IDs.
            parent_commit_id = parent_parser.get_orig_commit_id()

        diffset = self.model(name=diff_file_name,
                             revision=0,
                             basedir=basedir,
                             history=diffset_history,
                             repository=repository,
                             diffcompat=DiffCompatVersion.DEFAULT,
                             base_commit_id=base_commit_id)

        if not validate_only:
            diffset.save()

        encoding_list = repository.get_encoding_list()
        filediffs = []

        for f in files:
            parent_file = None
            orig_rev = None
            parent_content = b''

            if f.origFile in parent_files:
                parent_file = parent_files[f.origFile]
                parent_content = parent_file.data
                orig_rev = parent_file.origInfo

            # If there is a parent file there is not necessarily an original
            # revision for the parent file in the case of a renamed file in
            # git.
            if not orig_rev:
                if parent_commit_id and f.origInfo != PRE_CREATION:
                    orig_rev = parent_commit_id
                else:
                    orig_rev = f.origInfo

            enc, orig_file = convert_to_unicode(f.origFile, encoding_list)
            enc, dest_file = convert_to_unicode(f.newFile, encoding_list)

            if f.deleted:
                status = FileDiff.DELETED
            elif f.moved:
                status = FileDiff.MOVED
            elif f.copied:
                status = FileDiff.COPIED
            else:
                status = FileDiff.MODIFIED

            filediff = FileDiff(
                diffset=diffset,
                source_file=parser.normalize_diff_filename(orig_file),
                dest_file=parser.normalize_diff_filename(dest_file),
                source_revision=smart_unicode(orig_rev),
                dest_detail=f.newInfo,
                binary=f.binary,
                status=status)

            filediff.extra_data = {
                'is_symlink': f.is_symlink,
            }

            if (parent_file and (parent_file.moved or parent_file.copied)
                    and parent_file.insert_count == 0
                    and parent_file.delete_count == 0):
                filediff.extra_data['parent_moved'] = True

            if not validate_only:
                # This state all requires making modifications to the database.
                # We only want to do this if we're saving.
                filediff.diff = f.data
                filediff.parent_diff = parent_content

                filediff.set_line_counts(raw_insert_count=f.insert_count,
                                         raw_delete_count=f.delete_count)

                filediffs.append(filediff)

        if validate_only:
            return None

        if filediffs:
            FileDiff.objects.bulk_create(filediffs)

        return diffset
Пример #38
0
class FileDiffMigrationTests(TestCase):
    """Unit tests for FileDiff migration."""

    fixtures = ['test_scmtools']

    def setUp(self):
        super(FileDiffMigrationTests, self).setUp()

        self.repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=self.repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64='')

        self.parent_diff = (
            b'diff --git a/README b/README\n'
            b'index 94bdd3e..3d2b777 100644\n'
            b'--- README\n'
            b'+++ README\n'
            b'@@ -2 +2 @@\n'
            b'-blah..\n'
            b'+blah blah\n'
        )

        repository = self.create_repository(tool_name='Test')
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=repository)
        self.filediff = FileDiff(source_file='README',
                                 dest_file='README',
                                 diffset=diffset,
                                 diff64='',
                                 parent_diff64=b'')

    def test_migration_by_diff(self):
        """Testing RawFileDiffData migration accessing FileDiff.diff"""
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA

        self.assertIsNone(self.filediff.diff_hash)
        self.assertIsNone(self.filediff.parent_diff_hash)

        # This should prompt the migration.
        diff = self.filediff.diff

        self.assertIsNone(self.filediff.parent_diff_hash)
        self.assertIsNotNone(self.filediff.diff_hash)

        self.assertEqual(diff, self.DEFAULT_GIT_FILEDIFF_DATA)
        self.assertEqual(self.filediff.diff64, b'')
        self.assertEqual(self.filediff.diff_hash.binary,
                         self.DEFAULT_GIT_FILEDIFF_DATA)
        self.assertEqual(self.filediff.diff, diff)
        self.assertIsNone(self.filediff.parent_diff)
        self.assertIsNone(self.filediff.parent_diff_hash)

    def test_migration_by_parent_diff(self):
        """Testing RawFileDiffData migration accessing FileDiff.parent_diff"""
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA
        self.filediff.parent_diff64 = self.parent_diff

        self.assertIsNone(self.filediff.parent_diff_hash)

        # This should prompt the migration.
        parent_diff = self.filediff.parent_diff

        self.assertIsNotNone(self.filediff.parent_diff_hash)

        self.assertEqual(parent_diff, self.parent_diff)
        self.assertEqual(self.filediff.parent_diff64, b'')
        self.assertEqual(self.filediff.parent_diff_hash.binary,
                         self.parent_diff)
        self.assertEqual(self.filediff.parent_diff, self.parent_diff)

    def test_migration_by_delete_count(self):
        """Testing RawFileDiffData migration accessing FileDiff.delete_count"""
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA

        self.assertIsNone(self.filediff.diff_hash)

        # This should prompt the migration.
        counts = self.filediff.get_line_counts()

        self.assertIsNotNone(self.filediff.diff_hash)
        self.assertEqual(counts['raw_delete_count'], 1)
        self.assertEqual(self.filediff.diff_hash.delete_count, 1)

    def test_migration_by_insert_count(self):
        """Testing RawFileDiffData migration accessing FileDiff.insert_count"""
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA

        self.assertIsNone(self.filediff.diff_hash)

        # This should prompt the migration.
        counts = self.filediff.get_line_counts()

        self.assertIsNotNone(self.filediff.diff_hash)
        self.assertEqual(counts['raw_insert_count'], 1)
        self.assertEqual(self.filediff.diff_hash.insert_count, 1)

    def test_migration_by_set_line_counts(self):
        """Testing RawFileDiffData migration calling FileDiff.set_line_counts
        """
        self.filediff.diff64 = self.DEFAULT_GIT_FILEDIFF_DATA

        self.assertIsNone(self.filediff.diff_hash)

        # This should prompt the migration, but with our line counts.
        self.filediff.set_line_counts(raw_insert_count=10,
                                      raw_delete_count=20)

        self.assertIsNotNone(self.filediff.diff_hash)

        counts = self.filediff.get_line_counts()
        self.assertEqual(counts['raw_insert_count'], 10)
        self.assertEqual(counts['raw_delete_count'], 20)
        self.assertEqual(self.filediff.diff_hash.insert_count, 10)
        self.assertEqual(self.filediff.diff_hash.delete_count, 20)

    def test_migration_by_legacy_diff_hash(self):
        """Testing RawFileDiffData migration accessing FileDiff.diff
        with associated LegacyFileDiffData
        """
        legacy = LegacyFileDiffData.objects.create(
            binary_hash='abc123',
            binary=Base64DecodedValue(self.DEFAULT_GIT_FILEDIFF_DATA))

        self.filediff.legacy_diff_hash = legacy
        self.filediff.save()

        # This should prompt the migration.
        diff = self.filediff.diff

        self.assertIsNotNone(self.filediff.diff_hash)
        self.assertIsNone(self.filediff.parent_diff_hash)
        self.assertIsNone(self.filediff.legacy_diff_hash)
        self.assertEqual(LegacyFileDiffData.objects.count(), 0)

        self.assertEqual(diff, self.DEFAULT_GIT_FILEDIFF_DATA)
        self.assertEqual(self.filediff.diff64, '')
        self.assertEqual(self.filediff.diff_hash.content,
                         self.DEFAULT_GIT_FILEDIFF_DATA)
        self.assertEqual(self.filediff.diff, diff)
        self.assertIsNone(self.filediff.parent_diff)
        self.assertIsNone(self.filediff.parent_diff_hash)

    def test_migration_by_shared_legacy_diff_hash(self):
        """Testing RawFileDiffData migration accessing FileDiff.diff
        with associated shared LegacyFileDiffData
        """
        legacy = LegacyFileDiffData.objects.create(
            binary_hash='abc123',
            binary=Base64DecodedValue(self.DEFAULT_GIT_FILEDIFF_DATA))

        self.filediff.legacy_diff_hash = legacy
        self.filediff.save()

        # Create a second FileDiff using this legacy data.
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=self.repository)
        FileDiff.objects.create(source_file='README',
                                dest_file='README',
                                diffset=diffset,
                                diff64='',
                                parent_diff64='',
                                legacy_diff_hash=legacy)

        # This should prompt the migration.
        diff = self.filediff.diff

        self.assertIsNotNone(self.filediff.diff_hash)
        self.assertIsNone(self.filediff.parent_diff_hash)
        self.assertIsNone(self.filediff.legacy_diff_hash)
        self.assertEqual(LegacyFileDiffData.objects.count(), 1)

        self.assertEqual(diff, self.DEFAULT_GIT_FILEDIFF_DATA)
        self.assertEqual(self.filediff.diff64, '')
        self.assertEqual(self.filediff.diff_hash.content,
                         self.DEFAULT_GIT_FILEDIFF_DATA)
        self.assertEqual(self.filediff.diff, diff)
        self.assertIsNone(self.filediff.parent_diff)
        self.assertIsNone(self.filediff.parent_diff_hash)

    def test_migration_by_legacy_parent_diff_hash(self):
        """Testing RawFileDiffData migration accessing FileDiff.parent_diff
        with associated LegacyFileDiffData
        """
        legacy = LegacyFileDiffData.objects.create(
            binary_hash='abc123',
            binary=Base64DecodedValue(self.parent_diff))

        self.filediff.legacy_parent_diff_hash = legacy
        self.filediff.save()

        # This should prompt the migration.
        parent_diff = self.filediff.parent_diff

        self.assertIsNotNone(self.filediff.parent_diff_hash)
        self.assertIsNone(self.filediff.legacy_parent_diff_hash)

        self.assertEqual(parent_diff, self.parent_diff)
        self.assertEqual(self.filediff.parent_diff64, '')
        self.assertEqual(self.filediff.parent_diff_hash.content,
                         self.parent_diff)
        self.assertEqual(self.filediff.parent_diff, parent_diff)

    def test_migration_by_shared_legacy_parent_diff_hash(self):
        """Testing RawFileDiffData migration accessing FileDiff.parent_diff
        with associated shared LegacyFileDiffData
        """
        legacy = LegacyFileDiffData.objects.create(
            binary_hash='abc123',
            binary=Base64DecodedValue(self.parent_diff))

        self.filediff.legacy_parent_diff_hash = legacy
        self.filediff.save()

        # Create a second FileDiff using this legacy data.
        diffset = DiffSet.objects.create(name='test',
                                         revision=1,
                                         repository=self.repository)
        FileDiff.objects.create(source_file='README',
                                dest_file='README',
                                diffset=diffset,
                                diff64='',
                                parent_diff64='',
                                legacy_parent_diff_hash=legacy)

        # This should prompt the migration.
        parent_diff = self.filediff.parent_diff

        self.assertIsNotNone(self.filediff.parent_diff_hash)
        self.assertIsNone(self.filediff.legacy_parent_diff_hash)
        self.assertEqual(LegacyFileDiffData.objects.count(), 1)

        self.assertEqual(parent_diff, self.parent_diff)
        self.assertEqual(self.filediff.parent_diff64, '')
        self.assertEqual(self.filediff.parent_diff_hash.content,
                         self.parent_diff)
        self.assertEqual(self.filediff.parent_diff, parent_diff)

    def test_migration_with_legacy_and_race_condition(self):
        """Testing RawFileDiffData migration with LegacyFileDiffData and race
        condition in migrating
        """
        legacy = LegacyFileDiffData.objects.create(
            binary_hash='abc123',
            binary=Base64DecodedValue(self.DEFAULT_GIT_FILEDIFF_DATA))
        parent_legacy = LegacyFileDiffData.objects.create(
            binary_hash='def456',
            binary=Base64DecodedValue(self.parent_diff))

        filediff1 = self.filediff
        filediff1.legacy_diff_hash = legacy
        filediff1.legacy_parent_diff_hash = parent_legacy
        filediff1.save()

        filediff2 = FileDiff.objects.get(pk=filediff1.pk)

        # Make sure that we're in the expected state.
        self.assertEqual(filediff1.legacy_diff_hash_id, legacy.pk)
        self.assertEqual(filediff1.legacy_parent_diff_hash_id,
                         parent_legacy.pk)
        self.assertEqual(filediff2.legacy_diff_hash_id, legacy.pk)
        self.assertEqual(filediff2.legacy_parent_diff_hash_id,
                         parent_legacy.pk)

        # This should prompt the migration of the first instance.
        diff1 = self.filediff.diff
        parent_diff1 = filediff1.parent_diff

        # This should prompt the migration of the second instance.
        diff2 = filediff2.diff
        parent_diff2 = filediff2.parent_diff

        # At this point, we should have valid diffs, and neither call
        # above should have raised an exception due to a dangling hash ID.
        self.assertEqual(diff1, self.DEFAULT_GIT_FILEDIFF_DATA)
        self.assertEqual(diff1, diff2)
        self.assertEqual(parent_diff1, self.parent_diff)
        self.assertEqual(parent_diff1, parent_diff2)

        self.assertEqual(LegacyFileDiffData.objects.count(), 0)
        self.assertEqual(RawFileDiffData.objects.count(), 2)

        # Check the hash references.
        self.assertIsNotNone(filediff1.diff_hash)
        self.assertIsNotNone(filediff2.diff_hash)
        self.assertEqual(filediff1.diff_hash, filediff2.diff_hash)
        self.assertIsNotNone(filediff1.parent_diff_hash)
        self.assertIsNotNone(filediff2.parent_diff_hash)
        self.assertEqual(filediff1.parent_diff_hash,
                         filediff2.parent_diff_hash)
        self.assertIsNone(filediff1.legacy_diff_hash)
        self.assertIsNone(filediff2.legacy_diff_hash)
        self.assertIsNone(filediff1.legacy_parent_diff_hash)
        self.assertIsNone(filediff2.legacy_parent_diff_hash)

        # Check the diff content.
        self.assertEqual(filediff1.diff64, '')
        self.assertEqual(filediff2.diff64, '')
        self.assertEqual(filediff1.diff_hash.content,
                         self.DEFAULT_GIT_FILEDIFF_DATA)
        self.assertEqual(filediff2.diff_hash.content,
                         self.DEFAULT_GIT_FILEDIFF_DATA)

        # Check the parent_diff content.
        self.assertEqual(filediff1.parent_diff64, '')
        self.assertEqual(filediff2.parent_diff64, '')
        self.assertEqual(filediff1.parent_diff_hash.content, self.parent_diff)
        self.assertEqual(filediff2.parent_diff_hash.content, self.parent_diff)