|  | #!/bin/sh | 
|  |  | 
|  | test_description='git blame ignore fuzzy heuristic' | 
|  | . ./test-lib.sh | 
|  |  | 
|  | pick_author='s/^[0-9a-f^]* *(\([^ ]*\) .*/\1/' | 
|  |  | 
|  | # Each test is composed of 4 variables: | 
|  | # titleN - the test name | 
|  | # aN - the initial content | 
|  | # bN - the final content | 
|  | # expectedN - the line numbers from aN that we expect git blame | 
|  | #             on bN to identify, or "Final" if bN itself should | 
|  | #             be identified as the origin of that line. | 
|  |  | 
|  | # We start at test 2 because setup will show as test 1 | 
|  | title2="Regression test for partially overlapping search ranges" | 
|  | cat <<EOF >a2 | 
|  | 1 | 
|  | 2 | 
|  | 3 | 
|  | abcdef | 
|  | 5 | 
|  | 6 | 
|  | 7 | 
|  | ijkl | 
|  | 9 | 
|  | 10 | 
|  | 11 | 
|  | pqrs | 
|  | 13 | 
|  | 14 | 
|  | 15 | 
|  | wxyz | 
|  | 17 | 
|  | 18 | 
|  | 19 | 
|  | EOF | 
|  | cat <<EOF >b2 | 
|  | abcde | 
|  | ijk | 
|  | pqr | 
|  | wxy | 
|  | EOF | 
|  | cat <<EOF >expected2 | 
|  | 4 | 
|  | 8 | 
|  | 12 | 
|  | 16 | 
|  | EOF | 
|  |  | 
|  | title3="Combine 3 lines into 2" | 
|  | cat <<EOF >a3 | 
|  | if ((maxgrow==0) || | 
|  | ( single_line_field && (field->dcols < maxgrow)) || | 
|  | (!single_line_field && (field->drows < maxgrow))) | 
|  | EOF | 
|  | cat <<EOF >b3 | 
|  | if ((maxgrow == 0) || (single_line_field && (field->dcols < maxgrow)) || | 
|  | (!single_line_field && (field->drows < maxgrow))) { | 
|  | EOF | 
|  | cat <<EOF >expected3 | 
|  | 2 | 
|  | 3 | 
|  | EOF | 
|  |  | 
|  | title4="Add curly brackets" | 
|  | cat <<EOF >a4 | 
|  | if (rows) *rows = field->rows; | 
|  | if (cols) *cols = field->cols; | 
|  | if (frow) *frow = field->frow; | 
|  | if (fcol) *fcol = field->fcol; | 
|  | EOF | 
|  | cat <<EOF >b4 | 
|  | if (rows) { | 
|  | *rows = field->rows; | 
|  | } | 
|  | if (cols) { | 
|  | *cols = field->cols; | 
|  | } | 
|  | if (frow) { | 
|  | *frow = field->frow; | 
|  | } | 
|  | if (fcol) { | 
|  | *fcol = field->fcol; | 
|  | } | 
|  | EOF | 
|  | cat <<EOF >expected4 | 
|  | 1 | 
|  | 1 | 
|  | Final | 
|  | 2 | 
|  | 2 | 
|  | Final | 
|  | 3 | 
|  | 3 | 
|  | Final | 
|  | 4 | 
|  | 4 | 
|  | Final | 
|  | EOF | 
|  |  | 
|  |  | 
|  | title5="Combine many lines and change case" | 
|  | cat <<EOF >a5 | 
|  | for(row=0,pBuffer=field->buf; | 
|  | row<height; | 
|  | row++,pBuffer+=width ) | 
|  | { | 
|  | if ((len = (int)( After_End_Of_Data( pBuffer, width ) - pBuffer )) > 0) | 
|  | { | 
|  | wmove( win, row, 0 ); | 
|  | waddnstr( win, pBuffer, len ); | 
|  | EOF | 
|  | cat <<EOF >b5 | 
|  | for (Row = 0, PBuffer = field->buf; Row < Height; Row++, PBuffer += Width) { | 
|  | if ((Len = (int)(afterEndOfData(PBuffer, Width) - PBuffer)) > 0) { | 
|  | wmove(win, Row, 0); | 
|  | waddnstr(win, PBuffer, Len); | 
|  | EOF | 
|  | cat <<EOF >expected5 | 
|  | 1 | 
|  | 5 | 
|  | 7 | 
|  | 8 | 
|  | EOF | 
|  |  | 
|  | title6="Rename and combine lines" | 
|  | cat <<EOF >a6 | 
|  | bool need_visual_update = ((form != (FORM *)0)      && | 
|  | (form->status & _POSTED) && | 
|  | (form->current==field)); | 
|  |  | 
|  | if (need_visual_update) | 
|  | Synchronize_Buffer(form); | 
|  |  | 
|  | if (single_line_field) | 
|  | { | 
|  | growth = field->cols * amount; | 
|  | if (field->maxgrow) | 
|  | growth = Minimum(field->maxgrow - field->dcols,growth); | 
|  | field->dcols += growth; | 
|  | if (field->dcols == field->maxgrow) | 
|  | EOF | 
|  | cat <<EOF >b6 | 
|  | bool NeedVisualUpdate = ((Form != (FORM *)0) && (Form->status & _POSTED) && | 
|  | (Form->current == field)); | 
|  |  | 
|  | if (NeedVisualUpdate) { | 
|  | synchronizeBuffer(Form); | 
|  | } | 
|  |  | 
|  | if (SingleLineField) { | 
|  | Growth = field->cols * amount; | 
|  | if (field->maxgrow) { | 
|  | Growth = Minimum(field->maxgrow - field->dcols, Growth); | 
|  | } | 
|  | field->dcols += Growth; | 
|  | if (field->dcols == field->maxgrow) { | 
|  | EOF | 
|  | cat <<EOF >expected6 | 
|  | 1 | 
|  | 3 | 
|  | 4 | 
|  | 5 | 
|  | 6 | 
|  | Final | 
|  | 7 | 
|  | 8 | 
|  | 10 | 
|  | 11 | 
|  | 12 | 
|  | Final | 
|  | 13 | 
|  | 14 | 
|  | EOF | 
|  |  | 
|  | # Both lines match identically so position must be used to tie-break. | 
|  | title7="Same line twice" | 
|  | cat <<EOF >a7 | 
|  | abc | 
|  | abc | 
|  | EOF | 
|  | cat <<EOF >b7 | 
|  | abcd | 
|  | abcd | 
|  | EOF | 
|  | cat <<EOF >expected7 | 
|  | 1 | 
|  | 2 | 
|  | EOF | 
|  |  | 
|  | title8="Enforce line order" | 
|  | cat <<EOF >a8 | 
|  | abcdef | 
|  | ghijkl | 
|  | ab | 
|  | EOF | 
|  | cat <<EOF >b8 | 
|  | ghijk | 
|  | abcd | 
|  | EOF | 
|  | cat <<EOF >expected8 | 
|  | 2 | 
|  | 3 | 
|  | EOF | 
|  |  | 
|  | title9="Expand lines and rename variables" | 
|  | cat <<EOF >a9 | 
|  | int myFunction(int ArgumentOne, Thing *ArgTwo, Blah XuglyBug) { | 
|  | Squiggle FabulousResult = squargle(ArgumentOne, *ArgTwo, | 
|  | XuglyBug) + EwwwGlobalWithAReallyLongNameYepTooLong; | 
|  | return FabulousResult * 42; | 
|  | } | 
|  | EOF | 
|  | cat <<EOF >b9 | 
|  | int myFunction(int argument_one, Thing *arg_asdfgh, | 
|  | Blah xugly_bug) { | 
|  | Squiggle fabulous_result = squargle(argument_one, | 
|  | *arg_asdfgh, xugly_bug) | 
|  | + g_ewww_global_with_a_really_long_name_yep_too_long; | 
|  | return fabulous_result * 42; | 
|  | } | 
|  | EOF | 
|  | cat <<EOF >expected9 | 
|  | 1 | 
|  | 1 | 
|  | 2 | 
|  | 3 | 
|  | 3 | 
|  | 4 | 
|  | 5 | 
|  | EOF | 
|  |  | 
|  | title10="Two close matches versus one less close match" | 
|  | cat <<EOF >a10 | 
|  | abcdef | 
|  | abcdef | 
|  | ghijkl | 
|  | EOF | 
|  | cat <<EOF >b10 | 
|  | gh | 
|  | abcdefx | 
|  | EOF | 
|  | cat <<EOF >expected10 | 
|  | Final | 
|  | 2 | 
|  | EOF | 
|  |  | 
|  | # The first line of b matches best with the last line of a, but the overall | 
|  | # match is better if we match it with the first line of a. | 
|  | title11="Piggy in the middle" | 
|  | cat <<EOF >a11 | 
|  | abcdefg | 
|  | ijklmn | 
|  | abcdefgh | 
|  | EOF | 
|  | cat <<EOF >b11 | 
|  | abcdefghx | 
|  | ijklm | 
|  | EOF | 
|  | cat <<EOF >expected11 | 
|  | 1 | 
|  | 2 | 
|  | EOF | 
|  |  | 
|  | title12="No trailing newline" | 
|  | printf "abc\ndef" >a12 | 
|  | printf "abx\nstu" >b12 | 
|  | cat <<EOF >expected12 | 
|  | 1 | 
|  | Final | 
|  | EOF | 
|  |  | 
|  | title13="Reorder includes" | 
|  | cat <<EOF >a13 | 
|  | #include "c.h" | 
|  | #include "b.h" | 
|  | #include "a.h" | 
|  | #include "e.h" | 
|  | #include "d.h" | 
|  | EOF | 
|  | cat <<EOF >b13 | 
|  | #include "a.h" | 
|  | #include "b.h" | 
|  | #include "c.h" | 
|  | #include "d.h" | 
|  | #include "e.h" | 
|  | EOF | 
|  | cat <<EOF >expected13 | 
|  | 3 | 
|  | 2 | 
|  | 1 | 
|  | 5 | 
|  | 4 | 
|  | EOF | 
|  |  | 
|  | last_test=13 | 
|  |  | 
|  | test_expect_success setup ' | 
|  | for i in $(test_seq 2 $last_test) | 
|  | do | 
|  | # Append each line in a separate commit to make it easy to | 
|  | # check which original line the blame output relates to. | 
|  |  | 
|  | line_count=0 && | 
|  | while IFS= read line | 
|  | do | 
|  | line_count=$((line_count+1)) && | 
|  | echo "$line" >>"$i" && | 
|  | git add "$i" && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME="$line_count" git commit -m "$line_count" || return 1 | 
|  | done <"a$i" | 
|  | done && | 
|  |  | 
|  | for i in $(test_seq 2 $last_test) | 
|  | do | 
|  | # Overwrite the files with the final content. | 
|  | cp b$i $i && | 
|  | git add $i || return 1 | 
|  | done && | 
|  | test_tick && | 
|  |  | 
|  | # Commit the final content all at once so it can all be | 
|  | # referred to with the same commit ID. | 
|  | GIT_AUTHOR_NAME=Final git commit -m Final && | 
|  |  | 
|  | IGNOREME=$(git rev-parse HEAD) | 
|  | ' | 
|  |  | 
|  | for i in $(test_seq 2 $last_test); do | 
|  | eval title="\$title$i" | 
|  | test_expect_success "$title" \ | 
|  | "git blame -M9 --ignore-rev $IGNOREME $i >output && | 
|  | sed -e \"$pick_author\" output >actual && | 
|  | test_cmp expected$i actual" | 
|  | done | 
|  |  | 
|  | # This invoked a null pointer dereference when the chunk callback was called | 
|  | # with a zero length parent chunk and there were no more suspects. | 
|  | test_expect_success 'Diff chunks with no suspects' ' | 
|  | test_write_lines xy1 A B C xy1 >file && | 
|  | git add file && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=1 git commit -m 1 && | 
|  |  | 
|  | test_write_lines xy2 A B xy2 C xy2 >file && | 
|  | git add file && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=2 git commit -m 2 && | 
|  | REV_2=$(git rev-parse HEAD) && | 
|  |  | 
|  | test_write_lines xy3 A >file && | 
|  | git add file && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=3 git commit -m 3 && | 
|  | REV_3=$(git rev-parse HEAD) && | 
|  |  | 
|  | test_write_lines 1 1 >expected && | 
|  |  | 
|  | git blame --ignore-rev $REV_2 --ignore-rev $REV_3 file >output && | 
|  | sed -e "$pick_author" output >actual && | 
|  |  | 
|  | test_cmp expected actual | 
|  | ' | 
|  |  | 
|  | test_expect_success 'position matching' ' | 
|  | test_write_lines abc def >file2 && | 
|  | git add file2 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=1 git commit -m 1 && | 
|  |  | 
|  | test_write_lines abc def abc def >file2 && | 
|  | git add file2 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=2 git commit -m 2 && | 
|  |  | 
|  | test_write_lines abcx defx abcx defx >file2 && | 
|  | git add file2 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=3 git commit -m 3 && | 
|  | REV_3=$(git rev-parse HEAD) && | 
|  |  | 
|  | test_write_lines abcy defy abcx defx >file2 && | 
|  | git add file2 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=4 git commit -m 4 && | 
|  | REV_4=$(git rev-parse HEAD) && | 
|  |  | 
|  | test_write_lines 1 1 2 2 >expected && | 
|  |  | 
|  | git blame --ignore-rev $REV_3 --ignore-rev $REV_4 file2 >output && | 
|  | sed -e "$pick_author" output >actual && | 
|  |  | 
|  | test_cmp expected actual | 
|  | ' | 
|  |  | 
|  | # This fails if each blame entry is processed independently instead of | 
|  | # processing each diff change in full. | 
|  | test_expect_success 'preserve order' ' | 
|  | test_write_lines bcde >file3 && | 
|  | git add file3 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=1 git commit -m 1 && | 
|  |  | 
|  | test_write_lines bcde fghij >file3 && | 
|  | git add file3 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=2 git commit -m 2 && | 
|  |  | 
|  | test_write_lines bcde fghij abcd >file3 && | 
|  | git add file3 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=3 git commit -m 3 && | 
|  |  | 
|  | test_write_lines abcdx fghijx bcdex >file3 && | 
|  | git add file3 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=4 git commit -m 4 && | 
|  | REV_4=$(git rev-parse HEAD) && | 
|  |  | 
|  | test_write_lines abcdx fghijy bcdex >file3 && | 
|  | git add file3 && | 
|  | test_tick && | 
|  | GIT_AUTHOR_NAME=5 git commit -m 5 && | 
|  | REV_5=$(git rev-parse HEAD) && | 
|  |  | 
|  | test_write_lines 1 2 3 >expected && | 
|  |  | 
|  | git blame --ignore-rev $REV_4 --ignore-rev $REV_5 file3 >output && | 
|  | sed -e "$pick_author" output >actual && | 
|  |  | 
|  | test_cmp expected actual | 
|  | ' | 
|  |  | 
|  | test_done |