• 
      

    Fix interdiff post-processing with replace chunks overflowing ranges.

    Review Request #6030 — Created June 25, 2014 and submitted

    Information

    Review Board
    release-2.0.x
    44201b8...

    Reviewers

    When post-processing interdiffs in order to filter out unrelated code,
    we had issues where replace chunks could be generated where its
    orig/modified ranges weren't equal. This would trigger an assert, which
    is probably good, because removing the assert resulted in a
    corrupted-looking diff.

    This same sort of bug could also affect equal chunks in theory, though
    in practice, they weren't in as bad a situation.

    The reason this was failing was that we attempted to cap each range to
    the chunk's boundaries, without any regard to whether or not the lengths
    needed to be consistent.

    Now, for equal and replace chunks, we get the lengths of the
    orig/modified ranges for the chunk, figure out the longest of the two,
    cap that within the valid range boundary, and use that as the end
    positions for the chunk.

    This ensures that we only show the replace/equal range for the section
    represented in the union of the two diffs. Anything else outside of that
    will turn into a filtered-equal.

    I added a new unit test that previously broke but is fixed with this
    change. However, I then noticed that another unit test broke. Turns out,
    it was already broken. It had resulting replace/equal chunks that didn't
    have consistent ranges, and chunks overflowing the bounds. After looking
    at the original chunks and throwing away the unit test's bad resulting
    chunks, I created a new set based on what they should have been. This
    ended up matching the results of the unit test.

    I added some new sanity-checking in the unit tests to make sure that any
    opcode lists we provide in the future will meet certain criteria, to
    help catch such issues in the future.

    All unit tests pass, including the fixed version of another test (which has
    results computed by hand and not copied/pasted from the results).

    Tested with a series of interdiffs I had laying around, and with some broken
    interdiffs that were identified.

    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          reviewboard/diffviewer/processors.py
          reviewboard/diffviewer/tests.py
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          reviewboard/diffviewer/processors.py
          reviewboard/diffviewer/tests.py
      
      
    2. 
        
    BC
    1. Ship It!

    2. 
        
    BC
    1. A new diff added to the existing review is now causing the following diff viewer error:

      Traceback (most recent call last):
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/views.py", line 236, in get
      renderer = self.create_renderer(context, args, kwargs)
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/reviews/views.py", line 1102, in create_renderer
      args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/views.py", line 326, in create_renderer
      self.diff_file = self._get_requested_diff_file()
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/views.py", line 367, in _get_requested_diff_file
      request=self.request)
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/diffutils.py", line 373, in populate_diff_chunks
      chunks = generator.get_chunks()
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/chunk_generator.py", line 148, in get_chunks
      large_data=True)
      File "/usr/local/lib/python2.7/dist-packages/Djblets-0.8.2-py2.7.egg/djblets/cache/backend.py", line 109, in cache_memoize
      data = lookup_callable()
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/chunk_generator.py", line 147, in <lambda>
      lambda: list(self._get_chunks_uncached()),
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/chunk_generator.py", line 253, in _get_chunks_uncached
      for tag, i1, i2, j1, j2, meta in opcodes_generator:
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/opcode_generator.py", line 70, in iter
      self._group_opcodes(opcodes)
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/opcode_generator.py", line 141, in _group_opcodes
      for group_index, group in enumerate(opcodes):
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/opcode_generator.py", line 137, in _apply_meta_processors
      for opcode in opcodes:
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/processors.py", line 214, in post_process_filtered_equals
      for tag, i1, i2, j1, j2, meta in opcodes:
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/opcode_generator.py", line 87, in _generate_opcode_meta
      for tag, i1, i2, j1, j2 in opcodes:
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/opcode_generator.py", line 83, in _apply_processors
      for opcode in opcodes:
      File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/processors.py", line 125, in filter_interdiff_opcodes
      cap_i2 = orig_range[1] + 1
      TypeError: 'NoneType' object has no attribute 'getitem'

      1. Thanks for testing this out. I'll see about getting a fix for it today or tomorrow.

        Just a tip for 2.0: You can wrap all that like:

        ```
        content here
        ```
        

        It will then format it nicely and not interpret as Markdown.

      2. I was out, but I have a change going up shortly. I would like to know more about the diff in question, though. Are either part of the diff new, deleted, or reverted files? Any major file size differences?

      3. It's failing on files that were deleted in the first diff.
        Also, it seems to get the icons in the diff summary confused with interdiffs - either leaving them blank when files have been changed, or using the wrong icons.

      4. We should have a separate bug filed for the icons.

        Were you able to test the latest patch?

      5. I've just tested the 2nd diff, and the same 'IndexError: list index out of range' exception occurs with files that were deleted in the first diff.

      6. When you say deleted, do you mean that diff revision 1 deletes a file, and diff revision 2 doesn't have that file deleted?

      7. Both diff versions delete files.

      8. Were the files different lengths in both of the diffs?

        I might need to see both of the diffs.

      9. Actually, can you provide a new backtrace? There's no way that you're getting the same exact error for this, since the original failure of trying to access orig_range when None is now wrapped in a conditional checking if it's not None first.

      10. Sorry, it is a different error:

        Traceback (most recent call last):
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/views.py", line 236, in get
            renderer = self.create_renderer(context, *args, **kwargs)
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/reviews/views.py", line 1102, in create_renderer
            *args, **kwargs)
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/views.py", line 326, in create_renderer
            self.diff_file = self._get_requested_diff_file()
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/views.py", line 367, in _get_requested_diff_file
            request=self.request)
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/diffutils.py", line 373, in populate_diff_chunks
            chunks = generator.get_chunks()
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/chunk_generator.py", line 148, in get_chunks
            large_data=True)
          File "/usr/local/lib/python2.7/dist-packages/Djblets-0.8.2-py2.7.egg/djblets/cache/backend.py", line 109, in cache_memoize
            data = lookup_callable()
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/chunk_generator.py", line 147, in <lambda>
            lambda: list(self._get_chunks_uncached()),
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/chunk_generator.py", line 283, in _get_chunks_uncached
            yield self._new_chunk(lines, 0, num_lines, False, tag, meta)
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/chunk_generator.py", line 558, in _new_chunk
            compute_chunk_last_header(lines, num_lines, meta, self._last_header)
          File "/usr/local/lib/python2.7/dist-packages/ReviewBoard-2.0.1-py2.7.egg/reviewboard/diffviewer/chunk_generator.py", line 642, in compute_chunk_last_header
            line = lines[0]
        IndexError: list index out of range
        
      11. Oh okay, so that's an entirely separate issue. If the issue for this change is fixed, we'll be able to at least get it in.

        I will need diffs and original source files for that one though.

      12. (It actually may be related, but I don't have enough information to go on right now.)

      13. Great - thanks, and apologies for the confusion.

      14. For those files, I don't actually need the content, but if you can tell me this information, I'll see if it's fallout from this change. I'll need these from both revisions of the diff for the failed file:

        1. The number of lines in the file.
        2. Each line in the diff starting with "@@" (there won't be any confidential data there, and there should probably only be one such line in each).
      15. Bruce, would you be able to send me that info? I'm hoping to figure out a solution before we do a release with this patch.

      16. Sorry for the delay - I'll get the info this afternoon. FWIW I reset the installation back to plain 2.0.1 and the "list index out of range" error still occurred, so it's not caused by this patch.

      17. If it's still useful, this is from a review that had 5 revisions and failed to display changes for some files between revisions 4 and 5.

        diff 4:
        number of lines: 43
        @@ -1,39 +0,0 @@
        diff 5:
        number of lines: 43
        @@ -1,39 +0,0 @@

        So there were apparently no changes to that file between the two revisions.

      18. Thanks Bruce. Is this for the error this review request was addressing, or the one in the most recent traceback above?

      19. That's for the most recent traceback ("list index out of range").

    2. 
        
    chipx86
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          reviewboard/diffviewer/processors.py
          reviewboard/diffviewer/tests.py
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          reviewboard/diffviewer/processors.py
          reviewboard/diffviewer/tests.py
      
      
    2. 
        
    david
    1. Ship It!

    2. 
        
    chipx86
    Review request changed
    Status:
    Completed
    Change Summary:
    Pushed to release-2.0.x (626335f)