diff options
author | Oswaldo Ferreira <oswaldo@gitlab.com> | 2018-10-16 13:21:16 -0300 |
---|---|---|
committer | Oswaldo Ferreira <oswaldo@gitlab.com> | 2018-10-30 11:35:28 -0300 |
commit | f21e58a772d1abdbeb8be344c4261797cfffc54e (patch) | |
tree | a593083315e16ee3262d77f451ab3815ec6232e4 | |
parent | 912741cfea06ead8dad95d20f2f0adb955a55732 (diff) | |
download | gitlab-ce-f21e58a772d1abdbeb8be344c4261797cfffc54e.tar.gz |
Comment on any expanded diff line on MRs
-rw-r--r-- | app/assets/javascripts/diffs/components/diff_line_gutter_content.vue | 1 | ||||
-rw-r--r-- | app/assets/javascripts/diffs/components/diff_table_cell.vue | 1 | ||||
-rw-r--r-- | app/assets/javascripts/diffs/components/parallel_diff_table_row.vue | 1 | ||||
-rw-r--r-- | app/assets/javascripts/diffs/store/mutations.js | 9 | ||||
-rw-r--r-- | app/controllers/projects/blob_controller.rb | 2 | ||||
-rw-r--r-- | app/controllers/projects/merge_requests/diffs_controller.rb | 7 | ||||
-rw-r--r-- | app/models/diff_note.rb | 39 | ||||
-rw-r--r-- | changelogs/unreleased/osw-comment-on-any-line-on-diffs.yml | 5 | ||||
-rw-r--r-- | lib/gitlab/diff/expander.rb | 198 | ||||
-rw-r--r-- | lib/gitlab/diff/file.rb | 46 | ||||
-rw-r--r-- | lib/gitlab/diff/file_collection/merge_request_diff.rb | 3 | ||||
-rw-r--r-- | lib/gitlab/diff/line.rb | 11 | ||||
-rw-r--r-- | lib/gitlab/diff/position.rb | 8 | ||||
-rw-r--r-- | spec/controllers/projects/blob_controller_spec.rb | 4 | ||||
-rw-r--r-- | spec/features/merge_request/user_posts_diff_notes_spec.rb | 13 | ||||
-rw-r--r-- | spec/lib/gitlab/diff/expander_spec.rb | 858 |
16 files changed, 1154 insertions, 52 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue index 6eff3013dcd..9fe1903ee7c 100644 --- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue +++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue @@ -81,7 +81,6 @@ export default { this.showCommentButton && this.isHover && !this.isMatchLine && - !this.isContextLine && !this.isMetaLine && !this.hasDiscussions ); diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue index 5d9a0b123fe..5201f681e16 100644 --- a/app/assets/javascripts/diffs/components/diff_table_cell.vue +++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue @@ -91,7 +91,6 @@ export default { this.isLoggedIn && this.isHover && !this.isMatchLine && - !this.isContextLine && !this.isMetaLine, }; }, diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue index fcc3b3e9117..5419aff23d2 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -48,7 +48,6 @@ export default { }, classNameMap() { return { - [CONTEXT_LINE_CLASS_NAME]: this.isContextLine, [PARALLEL_DIFF_VIEW_TYPE]: true, }; }, diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 38a65f111a2..15619728fe6 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -65,7 +65,14 @@ export default { const { highlightedDiffLines, parallelDiffLines } = diffFile; removeMatchLine(diffFile, lineNumbers, bottom); - const lines = addLineReferences(contextLines, lineNumbers, bottom); + + const lines = addLineReferences(contextLines, lineNumbers, bottom) + .map(line => ({ + ...line, + lineCode: line.lineCode || `${fileHash}_${line.oldLine}_${line.newLine}`, + discussions: line.discussions || [], + })); + addContextLines({ inlineLines: highlightedDiffLines, parallelLines: parallelDiffLines, diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 56a884b8a2a..b35d602fb76 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -122,7 +122,7 @@ class Projects::BlobController < Projects::ApplicationController @lines.map! do |line| # These are marked as context lines but are loaded from blobs. # We also have context lines loaded from diffs in other places. - diff_line = Gitlab::Diff::Line.new(line, 'context', nil, nil, nil) + diff_line = Gitlab::Diff::Line.new(line, nil, nil, nil, nil) diff_line.rich_text = line diff_line end diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 5307cd0720a..0d9a991d21e 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -22,8 +22,13 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic def render_diffs @environment = @merge_request.environments_for(current_user).last + notes_grouped_by_old_path = @notes.group_by { |note| note.position.old_path } - @diffs.write_cache + # @diffs.write_cache + @diffs.diff_files.each do |diff_file| + notes = notes_grouped_by_old_path.fetch(diff_file.old_path, []) + notes.each { |note| diff_file.unfold_diff_lines(note.position) } + end request = { current_user: current_user, diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 95694377fe3..d1360d548f6 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -80,22 +80,29 @@ class DiffNote < Note end def fetch_diff_file - if note_diff_file - diff = Gitlab::Git::Diff.new(note_diff_file.to_hash) - Gitlab::Diff::File.new(diff, - repository: project.repository, - diff_refs: original_position.diff_refs) - elsif created_at_diff?(noteable.diff_refs) - # We're able to use the already persisted diffs (Postgres) if we're - # presenting a "current version" of the MR discussion diff. - # So no need to make an extra Gitaly diff request for it. - # As an extra benefit, the returned `diff_file` already - # has `highlighted_diff_lines` data set from Redis on - # `Diff::FileCollection::MergeRequestDiff`. - noteable.diffs(original_position.diff_options).diff_files.first - else - original_position.diff_file(self.project.repository) - end + file = + if note_diff_file + diff = Gitlab::Git::Diff.new(note_diff_file.to_hash) + Gitlab::Diff::File.new(diff, + repository: project.repository, + diff_refs: original_position.diff_refs) + elsif created_at_diff?(noteable.diff_refs) + # We're able to use the already persisted diffs (Postgres) if we're + # presenting a "current version" of the MR discussion diff. + # So no need to make an extra Gitaly diff request for it. + # As an extra benefit, the returned `diff_file` already + # has `highlighted_diff_lines` data set from Redis on + # `Diff::FileCollection::MergeRequestDiff`. + noteable.diffs(original_position.diff_options).diff_files.first + else + original_position.diff_file(self.project.repository) + end + + # Since persisted diff files already have its content "unfolded" + # there's no need to make it pass through the unfolding process. + file&.unfold_diff_lines(position) unless note_diff_file + + file end def supported? diff --git a/changelogs/unreleased/osw-comment-on-any-line-on-diffs.yml b/changelogs/unreleased/osw-comment-on-any-line-on-diffs.yml new file mode 100644 index 00000000000..de9d1ab16fe --- /dev/null +++ b/changelogs/unreleased/osw-comment-on-any-line-on-diffs.yml @@ -0,0 +1,5 @@ +--- +title: Allow commenting on any diff line in Merge Requests +merge_request: +author: +type: added diff --git a/lib/gitlab/diff/expander.rb b/lib/gitlab/diff/expander.rb new file mode 100644 index 00000000000..71c28f2e4d3 --- /dev/null +++ b/lib/gitlab/diff/expander.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +module Gitlab + module Diff + class Expander + include Gitlab::Utils::StrongMemoize + + UNFOLD_COUNT = 20 + + def initialize(diff_file, position) + @diff_file = diff_file + @blob = diff_file.old_blob + @position = position + @unfold = true + end + + # Returns merged diff lines with required blob lines for presenting. + def diff_lines + strong_memoize(:diff_lines) do + original_diff_lines = @diff_file.diff_lines + + next original_diff_lines unless should_expand? + + merged_diff_with_blob_lines(original_diff_lines) + end + end + + # Returns the extracted lines from the old blob which should be merged + # with the current diff lines. + def blob_lines + strong_memoize(:blob_lines) do + @blob.load_all_data! + + # Blob lines, unlike diffs, doesn't start with an empty line for + # unchanged line, so the parsing and highlighting step gets fuzzy. + line_prefix = ' ' + blob_as_diff_lines = @blob.data.each_line.map { |line| "#{line_prefix}#{line}" } + + lines = Gitlab::Diff::Parser.new.parse(blob_as_diff_lines, diff_file: @diff_file).to_a + + from = from_blob_line - 1 + to = to_blob_line - 1 + + lines[from..to] + end + end + + private + + def should_expand? + return false unless @diff_file.text? + return false if @blob.nil? + return false unless @position.old_line + return false if @diff_file.diff_lines.empty? + return false if @diff_file.line_for_position(@position) + return false unless expanded_line + + true + end + + def merged_diff_with_blob_lines(lines) + match_line = expanded_line + insert_index = bottom? ? -1 : match_line.index + + lines -= [match_line] unless bottom? + + lines.insert(insert_index, *blob_lines_with_new_match) + + # The inserted blob lines have invalid indexes, so we need + # to reindex them. + reindex(lines) + + lines + end + + # Returns 'unchanged' blob lines with recalculated `old_pos` and + # `new_pos` and the recalculated new match line (needed if we for instance + # we unfolded once, but there are still folded lines). + def blob_lines_with_new_match + old_pos = from_blob_line + new_pos = from_blob_line + offset + + new_blob_lines = [] + + blob_lines.each do |line| + new_blob_lines << Gitlab::Diff::Line.new(line.text, line.type, nil, old_pos, new_pos, + parent_file: @diff_file) + + old_pos += 1 + new_pos += 1 + end + + new_blob_lines.unshift(new_match_line) if new_match_line + + new_blob_lines + end + + def bottom? + @position.old_line > @diff_file.diff_lines.last.old_pos + end + + def reindex(lines) + lines.each_with_index { |line, i| line.index = i } + end + + def new_match_line + # The bottom line match addition is already handled on + # Diff::File#diff_lines_for_serializer + return if bottom? + return unless @unfold + + blob_lines_length = blob_lines.length - 1 + + # This will change for bottom scenarios + old_pos = from_blob_line + new_pos = from_blob_line + offset + + old_line_ref = [old_pos, blob_lines_length].join(',') + new_line_ref = [new_pos, blob_lines_length].join(',') + new_match_line_str = "@@ -#{old_line_ref}+#{new_line_ref} @@" + + Gitlab::Diff::Line.new(new_match_line_str, 'match', nil, old_pos, new_pos) + end + + # TODO: use similar approach used on `to_bottom_blob_line` (this will + # change) + def from_blob_line + return old_line_number + 1 if bottom? + + unfold_times = 1 + comment_position = @position.old_line + index_line = line_number_above_match + + while (from = index_line - (UNFOLD_COUNT * unfold_times)) > comment_position + index_line -= 1 + unfold_times += 1 + end + + prev_line_number = line_before_match&.old_pos || 0 + + if from <= prev_line_number + 1 + @unfold = false + from = prev_line_number + 1 + end + + from + end + + def to_blob_line + if bottom? + # Calculates unfolding times based on how many lines between the + # comment position and the bottom match line it has. + from = from_blob_line + comment_position = @position.old_line + unfold_times = ((comment_position - from_blob_line).to_f / (UNFOLD_COUNT + 1)).ceil + from + (UNFOLD_COUNT * unfold_times) + (unfold_times - 1) + else + line_number_above_match + end + end + + def line_number_above_match + old_line_number - 1 + end + + def offset + new_line_number - old_line_number + end + + def old_line_number + expanded_line.old_pos + end + + def new_line_number + expanded_line.new_pos + end + + def line_before_match + index = expanded_line&.index + + @diff_file.diff_lines[index - 1] if index > 0 + end + + # Returns the line which needed to be expanded in order to send a comment + # in `@position`. + def expanded_line + strong_memoize(:expanded_line) do + if bottom? + @diff_file.diff_lines.last + else + @diff_file.diff_lines + .find { |line| line.old_pos > @position.old_line && line.type == 'match' } + end + end + end + end + end +end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index fb117baca9e..098633327fe 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -127,6 +127,7 @@ module Gitlab new_blob || old_blob end + attr_writer :diff_lines attr_writer :highlighted_diff_lines # Array of Gitlab::Diff::Line objects @@ -135,11 +136,38 @@ module Gitlab Gitlab::Diff::Parser.new.parse(raw_diff.each_line, diff_file: self).to_a end + # Changes diff_lines according to the given position. That is, + # it checks whether the position requires blob lines into the diff + # in order to be presented. + def unfold_diff_lines(position) + return unless position + + self.diff_lines = Gitlab::Diff::Expander.new(self, position).diff_lines + end + def highlighted_diff_lines @highlighted_diff_lines ||= Gitlab::Diff::Highlight.new(self, repository: self.repository).highlight end + # This adds the bottom match line to the array if needed. It contains + # the data to load more context lines. + def diff_lines_for_serializer + lines = highlighted_diff_lines + + return if lines.empty? + return if blob.nil? + + last_line = lines.last + + if last_line.new_pos < total_blob_lines(blob) && !deleted_file? + match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos) + lines.push(match_line) + end + + lines + end + # Array[<Hash>] with right/left keys that contains Gitlab::Diff::Line objects which text is hightlighted def parallel_diff_lines @parallel_diff_lines ||= Gitlab::Diff::ParallelDiff.new(self).parallelize @@ -246,24 +274,6 @@ module Gitlab simple_viewer.is_a?(DiffViewer::Text) && (ignore_errors || simple_viewer.render_error.nil?) end - # This adds the bottom match line to the array if needed. It contains - # the data to load more context lines. - def diff_lines_for_serializer - lines = highlighted_diff_lines - - return if lines.empty? - return if blob.nil? - - last_line = lines.last - - if last_line.new_pos < total_blob_lines(blob) && !deleted_file? - match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos) - lines.push(match_line) - end - - lines - end - private def total_blob_lines(blob) diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb index 0dd073a3a8e..7d772e1a66d 100644 --- a/lib/gitlab/diff/file_collection/merge_request_diff.rb +++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb @@ -17,7 +17,8 @@ module Gitlab def diff_files diff_files = super - diff_files.each { |diff_file| cache.decorate(diff_file) } + # TODO: Fix highlight caching. + # diff_files.each { |diff_file| cache.decorate(diff_file) } diff_files end diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb index 5b67cd46c48..fcc4be42e4f 100644 --- a/lib/gitlab/diff/line.rb +++ b/lib/gitlab/diff/line.rb @@ -4,7 +4,7 @@ module Gitlab SERIALIZE_KEYS = %i(line_code rich_text text type index old_pos new_pos).freeze attr_reader :line_code, :type, :index, :old_pos, :new_pos - attr_writer :rich_text + attr_writer :rich_text, :index attr_accessor :text def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil, rich_text: nil) @@ -19,7 +19,14 @@ module Gitlab end def self.init_from_hash(hash) - new(hash[:text], hash[:type], hash[:index], hash[:old_pos], hash[:new_pos], line_code: hash[:line_code], rich_text: hash[:rich_text]) + new(hash[:text], + hash[:type], + hash[:index], + hash[:old_pos], + hash[:new_pos], + parent_file: hash[:parent_file], + line_code: hash[:line_code], + rich_text: hash[:rich_text]) end def to_hash diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb index f967494199e..89b01bf9296 100644 --- a/lib/gitlab/diff/position.rb +++ b/lib/gitlab/diff/position.rb @@ -134,7 +134,13 @@ module Gitlab return unless diff_refs.complete? return unless comparison = diff_refs.compare_in(repository.project) - comparison.diffs(diff_options).diff_files.first + file = comparison.diffs(diff_options).diff_files.first + + # We need to unfold diff lines according to the position in order + # to correctly calculate the line code and trace position changes. + file.unfold_diff_lines(self) + + file end def get_formatter_class(type) diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 64b589a6d83..f58aa25cbdd 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -157,7 +157,7 @@ describe Projects::BlobController do match_line = JSON.parse(response.body).first - expect(match_line['type']).to eq('context') + expect(match_line['type']).to be_nil end it 'adds bottom match line when "t"o is less than blob size' do @@ -177,7 +177,7 @@ describe Projects::BlobController do match_line = JSON.parse(response.body).last - expect(match_line['type']).to eq('context') + expect(match_line['type']).to be_nil end end end diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb index fa148715855..51b78d3e7d1 100644 --- a/spec/features/merge_request/user_posts_diff_notes_spec.rb +++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb @@ -85,12 +85,13 @@ describe 'Merge request > User posts diff notes', :js do # `.line_holder` will be an unfolded line. let(:line_holder) { first('#a5cc2925ca8258af241be7e5b0381edf30266302 .line_holder') } - it 'does not allow commenting on the left side' do - should_not_allow_commenting(line_holder, 'left') + it 'allows commenting on the left side' do + should_allow_commenting(line_holder, 'left') end - it 'does not allow commenting on the right side' do - should_not_allow_commenting(line_holder, 'right') + it 'allows commenting on the right side' do + # Automatically shifts comment box to left side. + should_allow_commenting(line_holder, 'right') end end end @@ -147,8 +148,8 @@ describe 'Merge request > User posts diff notes', :js do # `.line_holder` will be an unfolded line. let(:line_holder) { first('.line_holder[id="a5cc2925ca8258af241be7e5b0381edf30266302_1_1"]') } - it 'does not allow commenting' do - should_not_allow_commenting line_holder + it 'allows commenting' do + should_allow_commenting line_holder end end diff --git a/spec/lib/gitlab/diff/expander_spec.rb b/spec/lib/gitlab/diff/expander_spec.rb new file mode 100644 index 00000000000..50ab0a27b7a --- /dev/null +++ b/spec/lib/gitlab/diff/expander_spec.rb @@ -0,0 +1,858 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Diff::Expander do + include RepoHelpers + + let(:raw_diff) do + ["@@ -7,9 +7,6 @@\n", + " \"tags\": [\"devel\", \"development\", \"nightly\"],\n", + " \"desktop-file-name-prefix\": \"(Development) \",\n", + " \"finish-args\": [\n", + "- \"--share=ipc\", \"--socket=x11\",\n", + "- \"--socket=wayland\",\n", + "- \"--talk-name=org.gnome.OnlineAccounts\",\n", + " \"--talk-name=org.freedesktop.Tracker1\",\n", + " \"--filesystem=home\",\n", + " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\",\n", + "@@ -62,7 +59,7 @@\n", + " },\n", + " {\n", + " \"name\": \"gnome-desktop\",\n", + "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],\n", + "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n", + "@@ -83,11 +80,6 @@\n", + " \"buildsystem\": \"meson\",\n", + " \"builddir\": true,\n", + " \"name\": \"nautilus\",\n", + "- \"config-opts\": [\n", + "- \"-Denable-desktop=false\",\n", + "- \"-Denable-selinux=false\",\n", + "- \"--libdir=/app/lib\"\n", + "- ],\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n"].join + end + + let(:raw_old_blob) do + ["{\n", + " \"app-id\": \"org.gnome.Nautilus\",\n", + " \"runtime\": \"org.gnome.Platform\",\n", + " \"runtime-version\": \"master\",\n", + " \"sdk\": \"org.gnome.Sdk\",\n", + " \"command\": \"nautilus\",\n", + " \"tags\": [\"devel\", \"development\", \"nightly\"],\n", + " \"desktop-file-name-prefix\": \"(Development) \",\n", + " \"finish-args\": [\n", + " \"--share=ipc\", \"--socket=x11\",\n", + " \"--socket=wayland\",\n", + " \"--talk-name=org.gnome.OnlineAccounts\",\n", + " \"--talk-name=org.freedesktop.Tracker1\",\n", + " \"--filesystem=home\",\n", + " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\",\n", + " \"--filesystem=xdg-run/dconf\", \"--filesystem=~/.config/dconf:ro\",\n", + " \"--talk-name=ca.desrt.dconf\", \"--env=DCONF_USER_CONFIG_DIR=.config/dconf\"\n", + " ],\n", + " \"cleanup\": [ \"/include\", \"/share/bash-completion\" ],\n", + " \"modules\": [\n", + " {\n", + " \"name\": \"exiv2\",\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"archive\",\n", + " \"url\": \"http://exiv2.org/builds/exiv2-0.26-trunk.tar.gz\",\n", + " \"sha256\": \"c75e3c4a0811bf700d92c82319373b7a825a2331c12b8b37d41eb58e4f18eafb\"\n", + " },\n", + " {\n", + " \"type\": \"shell\",\n", + " \"commands\": [\n", + " \"cp -f /usr/share/gnu-config/config.sub ./config/\",\n", + " \"cp -f /usr/share/gnu-config/config.guess ./config/\"\n", + " ]\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"name\": \"gexiv2\",\n", + " \"config-opts\": [ \"--disable-introspection\" ],\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n", + " \"url\": \"https://git.gnome.org/browse/gexiv2\"\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"name\": \"tracker\",\n", + " \"cleanup\": [ \"/bin\", \"/etc\", \"/libexec\" ],\n", + " \"config-opts\": [ \"--disable-miner-apps\", \"--disable-static\",\n", + " \"--disable-tracker-extract\", \"--disable-tracker-needle\",\n", + " \"--disable-tracker-preferences\", \"--disable-artwork\",\n", + " \"--disable-tracker-writeback\", \"--disable-miner-user-guides\",\n", + " \"--with-bash-completion-dir=no\" ],\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n", + " \"url\": \"https://git.gnome.org/browse/tracker\"\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"name\": \"gnome-desktop\",\n", + " \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n", + " \"url\": \"https://git.gnome.org/browse/gnome-desktop\"\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"name\": \"gnome-autoar\",\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n", + " \"url\": \"https://git.gnome.org/browse/gnome-autoar\"\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"buildsystem\": \"meson\",\n", + " \"builddir\": true,\n", + " \"name\": \"nautilus\",\n", + " \"config-opts\": [\n", + " \"-Denable-desktop=false\",\n", + " \"-Denable-selinux=false\",\n", + " \"--libdir=/app/lib\"\n", + " ],\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n", + " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\"\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + "},\n", + "{\n", + " \"app-id\": \"foo\",\n", + " \"runtime\": \"foo\",\n", + " \"runtime-version\": \"foo\",\n", + " \"sdk\": \"foo\",\n", + " \"command\": \"foo\",\n", + " \"tags\": [\"foo\", \"bar\", \"kux\"],\n", + " \"desktop-file-name-prefix\": \"(Foo) \",\n", + " {\n", + " \"buildsystem\": \"meson\",\n", + " \"builddir\": true,\n", + " \"name\": \"nautilus\",\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n", + " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\"\n", + " }\n", + " ]\n", + " }\n", + "},\n", + "{\n", + " \"app-id\": \"foo\",\n", + " \"runtime\": \"foo\",\n", + " \"runtime-version\": \"foo\",\n", + " \"sdk\": \"foo\",\n", + " \"command\": \"foo\",\n", + " \"tags\": [\"foo\", \"bar\", \"kux\"],\n", + " \"desktop-file-name-prefix\": \"(Foo) \",\n", + " {\n", + " \"buildsystem\": \"meson\",\n", + " \"builddir\": true,\n", + " \"name\": \"nautilus\",\n", + " \"sources\": [\n", + " {\n", + " \"type\": \"git\",\n", + " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\"\n", + " }\n", + " ]\n", + " }\n", + "}\n"].join + end + + let(:project) { create(:project) } + + let(:old_blob) { Gitlab::Git::Blob.new(data: raw_old_blob) } + + let(:diff) do + Gitlab::Git::Diff.new(diff: raw_diff, + new_path: "build-aux/flatpak/org.gnome.Nautilus.json", + old_path: "build-aux/flatpak/org.gnome.Nautilus.json", + a_mode: "100644", + b_mode: "100644", + new_file: false, + renamed_file: false, + deleted_file: false, + too_large: false) + end + + let(:diff_file) do + Gitlab::Diff::File.new(diff, repository: project.repository) + end + + before do + allow(old_blob).to receive(:load_all_data!) + allow(diff_file).to receive(:old_blob) { old_blob } + end + + subject { described_class.new(diff_file, position) } + + context 'position requires a single middle expansion' do + let(:position) do + Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + head_sha: "1487062132228de836236c522fe52fed4980a46c", + old_path: "build-aux/flatpak/org.gnome.Nautilus.json", + new_path: "build-aux/flatpak/org.gnome.Nautilus.json", + position_type: "text", + old_line: 43, + new_line: 40) + end + + context 'blob lines' do + let(:expected_blob_lines) do + [[41, 41, " \"sources\": ["], + [42, 42, " {"], + [43, 43, " \"type\": \"git\","], + [44, 44, " \"url\": \"https://git.gnome.org/browse/gexiv2\""], + [45, 45, " }"], + [46, 46, " ]"], + [47, 47, " },"], + [48, 48, " {"], + [49, 49, " \"name\": \"tracker\","], + [50, 50, " \"cleanup\": [ \"/bin\", \"/etc\", \"/libexec\" ],"], + [51, 51, " \"config-opts\": [ \"--disable-miner-apps\", \"--disable-static\","], + [52, 52, " \"--disable-tracker-extract\", \"--disable-tracker-needle\","], + [53, 53, " \"--disable-tracker-preferences\", \"--disable-artwork\","], + [54, 54, " \"--disable-tracker-writeback\", \"--disable-miner-user-guides\","], + [55, 55, " \"--with-bash-completion-dir=no\" ],"], + [56, 56, " \"sources\": ["], + [57, 57, " {"], + [58, 58, " \"type\": \"git\","], + [59, 59, " \"url\": \"https://git.gnome.org/browse/tracker\""], + [60, 60, " }"], + [61, 61, " ]"]] + end + + it 'returns the extracted blob lines correctly' do + extracted_lines = subject.blob_lines + + expect(extracted_lines.size).to eq(21) + + extracted_lines.each_with_index do |line, i| + expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i]) + end + end + end + + context 'diff lines' do + let(:expected_diff_lines) do + [[7, 7, "@@ -7,9 +7,6 @@"], + [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"], + [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","], + [9, 9, " \"finish-args\": ["], + [10, 10, "- \"--share=ipc\", \"--socket=x11\","], + [11, 10, "- \"--socket=wayland\","], + [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","], + [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","], + [14, 11, " \"--filesystem=home\","], + [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","], + + # New match line + [41, 38, "@@ -41,20+38,20 @@"], + + # Injected blob lines + [41, 38, " \"sources\": ["], + [42, 39, " {"], + [43, 40, " \"type\": \"git\","], + [44, 41, " \"url\": \"https://git.gnome.org/browse/gexiv2\""], + [45, 42, " }"], + [46, 43, " ]"], + [47, 44, " },"], + [48, 45, " {"], + [49, 46, " \"name\": \"tracker\","], + [50, 47, " \"cleanup\": [ \"/bin\", \"/etc\", \"/libexec\" ],"], + [51, 48, " \"config-opts\": [ \"--disable-miner-apps\", \"--disable-static\","], + [52, 49, " \"--disable-tracker-extract\", \"--disable-tracker-needle\","], + [53, 50, " \"--disable-tracker-preferences\", \"--disable-artwork\","], + [54, 51, " \"--disable-tracker-writeback\", \"--disable-miner-user-guides\","], + [55, 52, " \"--with-bash-completion-dir=no\" ],"], + [56, 53, " \"sources\": ["], + [57, 54, " {"], + [58, 55, " \"type\": \"git\","], + [59, 56, " \"url\": \"https://git.gnome.org/browse/tracker\""], + [60, 57, " }"], + [61, 58, " ]"], + # end + + [62, 59, " },"], + [63, 60, " {"], + [64, 61, " \"name\": \"gnome-desktop\","], + [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"], + [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"], + [66, 63, " \"sources\": ["], + [67, 64, " {"], + [68, 65, " \"type\": \"git\","], + [83, 80, "@@ -83,11 +80,6 @@"], + [83, 80, " \"buildsystem\": \"meson\","], + [84, 81, " \"builddir\": true,"], + [85, 82, " \"name\": \"nautilus\","], + [86, 83, "- \"config-opts\": ["], + [87, 83, "- \"-Denable-desktop=false\","], + [88, 83, "- \"-Denable-selinux=false\","], + [89, 83, "- \"--libdir=/app/lib\""], + [90, 83, "- ],"], + [91, 83, " \"sources\": ["], + [92, 84, " {"], + [93, 85, " \"type\": \"git\","]] + end + + it 'return merge of blob lines with diff lines correctly' do + new_diff_lines = subject.diff_lines + + expected_diff_lines.each_with_index do |expected_line, i| + line = new_diff_lines[i] + + expect([line.old_pos, line.new_pos, line.text]) + .to eq([expected_line[0], expected_line[1], expected_line[2]]) + end + end + + it 'merged lines have correct line codes' do + new_diff_lines = subject.diff_lines + + new_diff_lines.each_with_index do |line, i| + old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1] + + unless line.type == 'match' + expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos)) + end + end + end + end + end + + context 'position requires a short top expansion' do + let(:position) do + Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + head_sha: "1487062132228de836236c522fe52fed4980a46c", + old_path: "build-aux/flatpak/org.gnome.Nautilus.json", + new_path: "build-aux/flatpak/org.gnome.Nautilus.json", + position_type: "text", + old_line: 4, + new_line: 4) + end + + context 'blob lines' do + let(:expected_blob_lines) do + [[1, 1, " {"], + [2, 2, " \"app-id\": \"org.gnome.Nautilus\","], + [3, 3, " \"runtime\": \"org.gnome.Platform\","], + [4, 4, " \"runtime-version\": \"master\","], + [5, 5, " \"sdk\": \"org.gnome.Sdk\","], + [6, 6, " \"command\": \"nautilus\","]] + end + + it 'returns the extracted blob lines correctly' do + extracted_lines = subject.blob_lines + + expect(extracted_lines.size).to eq(6) + + extracted_lines.each_with_index do |line, i| + expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i]) + end + end + end + + context 'diff lines' do + let(:expected_diff_lines) do + # Injected blob lines + [[1, 1, " {"], + [2, 2, " \"app-id\": \"org.gnome.Nautilus\","], + [3, 3, " \"runtime\": \"org.gnome.Platform\","], + [4, 4, " \"runtime-version\": \"master\","], + [5, 5, " \"sdk\": \"org.gnome.Sdk\","], + [6, 6, " \"command\": \"nautilus\","], + # + # No generated match + + [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"], + [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","], + [9, 9, " \"finish-args\": ["], + [10, 10, "- \"--share=ipc\", \"--socket=x11\","], + [11, 10, "- \"--socket=wayland\","], + [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","], + [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","], + [14, 11, " \"--filesystem=home\","], + [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","], + [62, 59, "@@ -62,7 +59,7 @@"], + [62, 59, " },"], + [63, 60, " {"], + [64, 61, " \"name\": \"gnome-desktop\","], + [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"], + [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"], + [66, 63, " \"sources\": ["], + [67, 64, " {"], + [68, 65, " \"type\": \"git\","], + [83, 80, "@@ -83,11 +80,6 @@"], + [83, 80, " \"buildsystem\": \"meson\","], + [84, 81, " \"builddir\": true,"], + [85, 82, " \"name\": \"nautilus\","], + [86, 83, "- \"config-opts\": ["], + [87, 83, "- \"-Denable-desktop=false\","], + [88, 83, "- \"-Denable-selinux=false\","], + [89, 83, "- \"--libdir=/app/lib\""], + [90, 83, "- ],"], + [91, 83, " \"sources\": ["], + [92, 84, " {"], + [93, 85, " \"type\": \"git\","]] + end + + it 'return merge of blob lines with diff lines correctly' do + new_diff_lines = subject.diff_lines + + expected_diff_lines.each_with_index do |expected_line, i| + line = new_diff_lines[i] + + expect([line.old_pos, line.new_pos, line.text]) + .to eq([expected_line[0], expected_line[1], expected_line[2]]) + end + end + + it 'merged lines have correct line codes' do + new_diff_lines = subject.diff_lines + + new_diff_lines.each_with_index do |line, i| + old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1] + + unless line.type == 'match' + expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos)) + end + end + end + end + end + + context 'position requires more than one middle expansion' do + let(:position) do + Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + head_sha: "1487062132228de836236c522fe52fed4980a46c", + old_path: "build-aux/flatpak/org.gnome.Nautilus.json", + new_path: "build-aux/flatpak/org.gnome.Nautilus.json", + position_type: "text", + old_line: 20, + new_line: 17) + end + + context 'blob lines' do + let(:expected_blob_lines) do + # Second expansion + [[20, 20, " \"modules\": ["], + [21, 21, " {"], + [22, 22, " \"name\": \"exiv2\","], + [23, 23, " \"sources\": ["], + [24, 24, " {"], + [25, 25, " \"type\": \"archive\","], + [26, 26, " \"url\": \"http://exiv2.org/builds/exiv2-0.26-trunk.tar.gz\","], + [27, 27, " \"sha256\": \"c75e3c4a0811bf700d92c82319373b7a825a2331c12b8b37d41eb58e4f18eafb\""], + [28, 28, " },"], + [29, 29, " {"], + [30, 30, " \"type\": \"shell\","], + [31, 31, " \"commands\": ["], + [32, 32, " \"cp -f /usr/share/gnu-config/config.sub ./config/\","], + [33, 33, " \"cp -f /usr/share/gnu-config/config.guess ./config/\""], + [34, 34, " ]"], + [35, 35, " }"], + [36, 36, " ]"], + [37, 37, " },"], + [38, 38, " {"], + [39, 39, " \"name\": \"gexiv2\","], + [40, 40, " \"config-opts\": [ \"--disable-introspection\" ],"], + # First expansion + [41, 41, " \"sources\": ["], + [42, 42, " {"], + [43, 43, " \"type\": \"git\","], + [44, 44, " \"url\": \"https://git.gnome.org/browse/gexiv2\""], + [45, 45, " }"], + [46, 46, " ]"], + [47, 47, " },"], + [48, 48, " {"], + [49, 49, " \"name\": \"tracker\","], + [50, 50, " \"cleanup\": [ \"/bin\", \"/etc\", \"/libexec\" ],"], + [51, 51, " \"config-opts\": [ \"--disable-miner-apps\", \"--disable-static\","], + [52, 52, " \"--disable-tracker-extract\", \"--disable-tracker-needle\","], + [53, 53, " \"--disable-tracker-preferences\", \"--disable-artwork\","], + [54, 54, " \"--disable-tracker-writeback\", \"--disable-miner-user-guides\","], + [55, 55, " \"--with-bash-completion-dir=no\" ],"], + [56, 56, " \"sources\": ["], + [57, 57, " {"], + [58, 58, " \"type\": \"git\","], + [59, 59, " \"url\": \"https://git.gnome.org/browse/tracker\""], + [60, 60, " }"], + [61, 61, " ]"]] + end + + it 'returns the extracted blob lines correctly' do + extracted_lines = subject.blob_lines + + expect(extracted_lines.size).to eq(42) + + extracted_lines.each_with_index do |line, i| + expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i]) + end + end + end + + context 'diff lines' do + let(:expected_diff_lines) do + [[7, 7, "@@ -7,9 +7,6 @@"], + [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"], + [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","], + [9, 9, " \"finish-args\": ["], + [10, 10, "- \"--share=ipc\", \"--socket=x11\","], + [11, 10, "- \"--socket=wayland\","], + [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","], + [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","], + [14, 11, " \"--filesystem=home\","], + [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","], + # Generated match line + [20, 17, "@@ -20,41+17,41 @@"], + # Second expansion + [20, 17, " \"modules\": ["], + [21, 18, " {"], + [22, 19, " \"name\": \"exiv2\","], + [23, 20, " \"sources\": ["], + [24, 21, " {"], + [25, 22, " \"type\": \"archive\","], + [26, 23, " \"url\": \"http://exiv2.org/builds/exiv2-0.26-trunk.tar.gz\","], + [27, 24, " \"sha256\": \"c75e3c4a0811bf700d92c82319373b7a825a2331c12b8b37d41eb58e4f18eafb\""], + [28, 25, " },"], + [29, 26, " {"], + [30, 27, " \"type\": \"shell\","], + [31, 28, " \"commands\": ["], + [32, 29, " \"cp -f /usr/share/gnu-config/config.sub ./config/\","], + [33, 30, " \"cp -f /usr/share/gnu-config/config.guess ./config/\""], + [34, 31, " ]"], + [35, 32, " }"], + [36, 33, " ]"], + [37, 34, " },"], + [38, 35, " {"], + [39, 36, " \"name\": \"gexiv2\","], + [40, 37, " \"config-opts\": [ \"--disable-introspection\" ],"], + # First expansion + [41, 38, " \"sources\": ["], + [42, 39, " {"], + [43, 40, " \"type\": \"git\","], + [44, 41, " \"url\": \"https://git.gnome.org/browse/gexiv2\""], + [45, 42, " }"], + [46, 43, " ]"], + [47, 44, " },"], + [48, 45, " {"], + [49, 46, " \"name\": \"tracker\","], + [50, 47, " \"cleanup\": [ \"/bin\", \"/etc\", \"/libexec\" ],"], + [51, 48, " \"config-opts\": [ \"--disable-miner-apps\", \"--disable-static\","], + [52, 49, " \"--disable-tracker-extract\", \"--disable-tracker-needle\","], + [53, 50, " \"--disable-tracker-preferences\", \"--disable-artwork\","], + [54, 51, " \"--disable-tracker-writeback\", \"--disable-miner-user-guides\","], + [55, 52, " \"--with-bash-completion-dir=no\" ],"], + [56, 53, " \"sources\": ["], + [57, 54, " {"], + [58, 55, " \"type\": \"git\","], + [59, 56, " \"url\": \"https://git.gnome.org/browse/tracker\""], + [60, 57, " }"], + [61, 58, " ]"], + # end + [62, 59, " },"], + [63, 60, " {"], + [64, 61, " \"name\": \"gnome-desktop\","], + [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"], + [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"], + [66, 63, " \"sources\": ["], + [67, 64, " {"], + [68, 65, " \"type\": \"git\","], + [83, 80, "@@ -83,11 +80,6 @@"], + [83, 80, " \"buildsystem\": \"meson\","], + [84, 81, " \"builddir\": true,"], + [85, 82, " \"name\": \"nautilus\","], + [86, 83, "- \"config-opts\": ["], + [87, 83, "- \"-Denable-desktop=false\","], + [88, 83, "- \"-Denable-selinux=false\","], + [89, 83, "- \"--libdir=/app/lib\""], + [90, 83, "- ],"], + [91, 83, " \"sources\": ["], + [92, 84, " {"], + [93, 85, " \"type\": \"git\","]] + end + + it 'return merge of blob lines with diff lines correctly' do + new_diff_lines = subject.diff_lines + + expected_diff_lines.each_with_index do |expected_line, i| + line = new_diff_lines[i] + + expect([line.old_pos, line.new_pos, line.text]) + .to eq([expected_line[0], expected_line[1], expected_line[2]]) + end + end + + it 'merged lines have correct line codes' do + new_diff_lines = subject.diff_lines + + new_diff_lines.each_with_index do |line, i| + old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1] + + unless line.type == 'match' + expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos)) + end + end + end + end + end + + context 'position sits between two match lines (no expasion needed)' do + let(:position) do + Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + head_sha: "1487062132228de836236c522fe52fed4980a46c", + old_path: "build-aux/flatpak/org.gnome.Nautilus.json", + new_path: "build-aux/flatpak/org.gnome.Nautilus.json", + position_type: "text", + old_line: 64, + new_line: 61) + end + + context 'diff lines' do + let(:expected_diff_lines) do + # No blob lines + [[7, 7, "@@ -7,9 +7,6 @@"], + [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"], + [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","], + [9, 9, " \"finish-args\": ["], + [10, 10, "- \"--share=ipc\", \"--socket=x11\","], + [11, 10, "- \"--socket=wayland\","], + [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","], + [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","], + [14, 11, " \"--filesystem=home\","], + [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","], + [62, 59, "@@ -62,7 +59,7 @@"], + [62, 59, " },"], + [63, 60, " {"], + [64, 61, " \"name\": \"gnome-desktop\","], + [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"], + [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"], + [66, 63, " \"sources\": ["], + [67, 64, " {"], + [68, 65, " \"type\": \"git\","], + [83, 80, "@@ -83,11 +80,6 @@"], + [83, 80, " \"buildsystem\": \"meson\","], + [84, 81, " \"builddir\": true,"], + [85, 82, " \"name\": \"nautilus\","], + [86, 83, "- \"config-opts\": ["], + [87, 83, "- \"-Denable-desktop=false\","], + [88, 83, "- \"-Denable-selinux=false\","], + [89, 83, "- \"--libdir=/app/lib\""], + [90, 83, "- ],"], + [91, 83, " \"sources\": ["], + [92, 84, " {"], + [93, 85, " \"type\": \"git\","]] + end + + it 'return original diff lines' do + new_diff_lines = subject.diff_lines + + expected_diff_lines.each_with_index do |expected_line, i| + line = new_diff_lines[i] + + expect([line.old_pos, line.new_pos, line.text]) + .to eq([expected_line[0], expected_line[1], expected_line[2]]) + end + end + end + end + + context 'bottom position (expansion needed)' do + let(:position) do + Gitlab::Diff::Position.new(base_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + start_sha: "1c59dfa64afbea8c721bb09a06a9d326c952ea19", + head_sha: "1487062132228de836236c522fe52fed4980a46c", + old_path: "build-aux/flatpak/org.gnome.Nautilus.json", + new_path: "build-aux/flatpak/org.gnome.Nautilus.json", + position_type: "text", + old_line: 135, + new_line: 127) + end + + context 'blob lines' do + let(:expected_blob_lines) do + [[94, 94, " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\""], + [95, 95, " }"], + [96, 96, " ]"], + [97, 97, " }"], + [98, 98, " ]"], + [99, 99, " },"], + [100, 100, " {"], + [101, 101, " \"app-id\": \"foo\","], + [102, 102, " \"runtime\": \"foo\","], + [103, 103, " \"runtime-version\": \"foo\","], + [104, 104, " \"sdk\": \"foo\","], + [105, 105, " \"command\": \"foo\","], + [106, 106, " \"tags\": [\"foo\", \"bar\", \"kux\"],"], + [107, 107, " \"desktop-file-name-prefix\": \"(Foo) \","], + [108, 108, " {"], + [109, 109, " \"buildsystem\": \"meson\","], + [110, 110, " \"builddir\": true,"], + [111, 111, " \"name\": \"nautilus\","], + [112, 112, " \"sources\": ["], + [113, 113, " {"], + [114, 114, " \"type\": \"git\","], + [115, 115, " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\""], + [116, 116, " }"], + [117, 117, " ]"], + [118, 118, " }"], + [119, 119, " },"], + [120, 120, " {"], + [121, 121, " \"app-id\": \"foo\","], + [122, 122, " \"runtime\": \"foo\","], + [123, 123, " \"runtime-version\": \"foo\","], + [124, 124, " \"sdk\": \"foo\","], + [125, 125, " \"command\": \"foo\","], + [126, 126, " \"tags\": [\"foo\", \"bar\", \"kux\"],"], + [127, 127, " \"desktop-file-name-prefix\": \"(Foo) \","], + [128, 128, " {"], + [129, 129, " \"buildsystem\": \"meson\","], + [130, 130, " \"builddir\": true,"], + [131, 131, " \"name\": \"nautilus\","], + [132, 132, " \"sources\": ["], + [133, 133, " {"], + [134, 134, " \"type\": \"git\","], + [135, 135, " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\""]] + end + + it 'returns the extracted blob lines correctly' do + extracted_lines = subject.blob_lines + + expect(extracted_lines.size).to eq(42) + + extracted_lines.each_with_index do |line, i| + expect([line.old_line, line.new_line, line.text]).to eq(expected_blob_lines[i]) + end + end + end + + context 'diff lines' do + let(:expected_diff_lines) do + [[7, 7, "@@ -7,9 +7,6 @@"], + [7, 7, " \"tags\": [\"devel\", \"development\", \"nightly\"],"], + [8, 8, " \"desktop-file-name-prefix\": \"(Development) \","], + [9, 9, " \"finish-args\": ["], + [10, 10, "- \"--share=ipc\", \"--socket=x11\","], + [11, 10, "- \"--socket=wayland\","], + [12, 10, "- \"--talk-name=org.gnome.OnlineAccounts\","], + [13, 10, " \"--talk-name=org.freedesktop.Tracker1\","], + [14, 11, " \"--filesystem=home\","], + [15, 12, " \"--talk-name=org.gtk.vfs\", \"--talk-name=org.gtk.vfs.*\","], + [62, 59, "@@ -62,7 +59,7 @@"], + [62, 59, " },"], + [63, 60, " {"], + [64, 61, " \"name\": \"gnome-desktop\","], + [65, 62, "- \"config-opts\": [\"--disable-debug-tools\", \"--disable-udev\"],"], + [66, 62, "+ \"config-opts\": [\"--disable-debug-tools\", \"--disable-\"],"], + [66, 63, " \"sources\": ["], + [67, 64, " {"], + [68, 65, " \"type\": \"git\","], + [83, 80, "@@ -83,11 +80,6 @@"], + [83, 80, " \"buildsystem\": \"meson\","], + [84, 81, " \"builddir\": true,"], + [85, 82, " \"name\": \"nautilus\","], + [86, 83, "- \"config-opts\": ["], + [87, 83, "- \"-Denable-desktop=false\","], + [88, 83, "- \"-Denable-selinux=false\","], + [89, 83, "- \"--libdir=/app/lib\""], + [90, 83, "- ],"], + [91, 83, " \"sources\": ["], + [92, 84, " {"], + [93, 85, " \"type\": \"git\","], + + # Injected blob lines + [94, 86, " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\""], + [95, 87, " }"], + [96, 88, " ]"], + [97, 89, " }"], + [98, 90, " ]"], + [99, 91, " },"], + [100, 92, " {"], + [101, 93, " \"app-id\": \"foo\","], + [102, 94, " \"runtime\": \"foo\","], + [103, 95, " \"runtime-version\": \"foo\","], + [104, 96, " \"sdk\": \"foo\","], + [105, 97, " \"command\": \"foo\","], + [106, 98, " \"tags\": [\"foo\", \"bar\", \"kux\"],"], + [107, 99, " \"desktop-file-name-prefix\": \"(Foo) \","], + [108, 100, " {"], + [109, 101, " \"buildsystem\": \"meson\","], + [110, 102, " \"builddir\": true,"], + [111, 103, " \"name\": \"nautilus\","], + [112, 104, " \"sources\": ["], + [113, 105, " {"], + [114, 106, " \"type\": \"git\","], + [115, 107, " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\""], + [116, 108, " }"], + [117, 109, " ]"], + [118, 110, " }"], + [119, 111, " },"], + [120, 112, " {"], + [121, 113, " \"app-id\": \"foo\","], + [122, 114, " \"runtime\": \"foo\","], + [123, 115, " \"runtime-version\": \"foo\","], + [124, 116, " \"sdk\": \"foo\","], + [125, 117, " \"command\": \"foo\","], + [126, 118, " \"tags\": [\"foo\", \"bar\", \"kux\"],"], + [127, 119, " \"desktop-file-name-prefix\": \"(Foo) \","], + [128, 120, " {"], + [129, 121, " \"buildsystem\": \"meson\","], + [130, 122, " \"builddir\": true,"], + [131, 123, " \"name\": \"nautilus\","], + [132, 124, " \"sources\": ["], + [133, 125, " {"], + [134, 126, " \"type\": \"git\","], + [135, 127, " \"url\": \"https://gitlab.gnome.org/GNOME/nautilus.git\""]] + end + + it 'return merge of blob lines with diff lines correctly' do + new_diff_lines = subject.diff_lines + + expected_diff_lines.each_with_index do |expected_line, i| + line = new_diff_lines[i] + + expect([line.old_pos, line.new_pos, line.text]) + .to eq([expected_line[0], expected_line[1], expected_line[2]]) + end + end + + it 'merged lines have correct line codes' do + new_diff_lines = subject.diff_lines + + new_diff_lines.each_with_index do |line, i| + old_pos, new_pos = expected_diff_lines[i][0], expected_diff_lines[i][1] + + unless line.type == 'match' + expect(line.line_code).to eq(Gitlab::Git.diff_line_code(diff_file.file_path, new_pos, old_pos)) + end + end + end + end + end +end |