#!/bin/sh global test_description := ''Test diff-highlight'' global CURR_DIR := $[pwd] global TEST_OUTPUT_DIRECTORY := $[pwd] global TEST_DIRECTORY := ""$CURR_DIR"/../../../t" global DIFF_HIGHLIGHT := ""$CURR_DIR"/../diff-highlight" global CW := $[printf "\033[7m] # white global CR := $[printf "\033[27m] # reset source "$TEST_DIRECTORY"/test-lib.sh if ! test_have_prereq PERL { global skip_all := ''skipping diff-highlight tests; perl not available'' test_done } # dh_test is a test helper function which takes 3 file names as parameters. The # first 2 files are used to generate diff and commit output, which is then # piped through diff-highlight. The 3rd file should contain the expected output # of diff-highlight (minus the diff/commit header, ie. everything after and # including the first @@ line). proc dh_test { global a := $1, b := $2 && cat >patch.exp && do { cat $a >file && git add file && git commit -m "Add a file" && cat $b >file && git diff file >diff.raw && git commit -a -m "Update a file" && git show >commit.raw } >/dev/null && $DIFF_HIGHLIGHT diff.act && $DIFF_HIGHLIGHT commit.act && test_cmp patch.exp diff.act && test_cmp patch.exp commit.act } proc test_strip_patch_header { sed -n '/^@@/,$p' $ifsjoin(Argv) } # dh_test_setup_history generates a contrived graph such that we have at least # 1 nesting (E) and 2 nestings (F). # # A branch # / # D---E---F master # # git log --all --graph # * commit # | A # | * commit # | | F # | * commit # |/ # | E # * commit # D # proc dh_test_setup_history { echo "file1" >file1 && echo "file2" >file2 && echo "file3" >file3 && cat file1 >file && git add file && git commit -m "D" && git checkout -b branch && cat file2 >file && git commit -a -m "A" && git checkout master && cat file2 >file && git commit -a -m "E" && cat file3 >file && git commit -a -m "F" } proc left_trim { $PERL_PATH -pe 's/^\s+//' } proc trim_graph { # graphs start with * or | # followed by a space or / or \ $PERL_PATH -pe 's@^((\*|\|)( |/|\\))+@@' } test_expect_success 'diff-highlight highlights the beginning of a line' ' cat >a <<-\EOF && aaa bbb ccc EOF cat >b <<-\EOF && aaa 0bb ccc EOF dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa -${CW}b${CR}bb +${CW}0${CR}bb ccc EOF ' test_expect_success 'diff-highlight highlights the end of a line' ' cat >a <<-\EOF && aaa bbb ccc EOF cat >b <<-\EOF && aaa bb0 ccc EOF dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa -bb${CW}b${CR} +bb${CW}0${CR} ccc EOF ' test_expect_success 'diff-highlight highlights the middle of a line' ' cat >a <<-\EOF && aaa bbb ccc EOF cat >b <<-\EOF && aaa b0b ccc EOF dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa -b${CW}b${CR}b +b${CW}0${CR}b ccc EOF ' test_expect_success 'diff-highlight does not highlight whole line' ' cat >a <<-\EOF && aaa bbb ccc EOF cat >b <<-\EOF && aaa 000 ccc EOF dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa -bbb +000 ccc EOF ' test_expect_failure 'diff-highlight highlights mismatched hunk size' ' cat >a <<-\EOF && aaa bbb EOF cat >b <<-\EOF && aaa b0b ccc EOF dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa -b${CW}b${CR}b +b${CW}0${CR}b +ccc EOF ' # These two code points share the same leading byte in UTF-8 representation; # a naive byte-wise diff would highlight only the second byte. # # - U+00f3 ("o" with acute) global o_accent := $[printf '\303\263] # - U+00f8 ("o" with stroke) global o_stroke := $[printf '\303\270] test_expect_success 'diff-highlight treats multibyte utf-8 as a unit' ' echo "unic${o_accent}de" >a && echo "unic${o_stroke}de" >b && dh_test a b <<-EOF @@ -1 +1 @@ -unic${CW}${o_accent}${CR}de +unic${CW}${o_stroke}${CR}de EOF ' # Unlike the UTF-8 above, these are combining code points which are meant # to modify the character preceding them: # # - U+0301 (combining acute accent) global combine_accent := $[printf '\314\201] # - U+0302 (combining circumflex) global combine_circum := $[printf '\314\202] test_expect_failure 'diff-highlight treats combining code points as a unit' ' echo "unico${combine_accent}de" >a && echo "unico${combine_circum}de" >b && dh_test a b <<-EOF @@ -1 +1 @@ -unic${CW}o${combine_accent}${CR}de +unic${CW}o${combine_circum}${CR}de EOF ' test_expect_success 'diff-highlight works with the --graph option' ' dh_test_setup_history && # topo-order so that the order of the commits is the same as with --graph # trim graph elements so we can do a diff # trim leading space because our trim_graph is not perfect git log --branches -p --topo-order | "$DIFF_HIGHLIGHT" | left_trim >graph.exp && git log --branches -p --graph | "$DIFF_HIGHLIGHT" | trim_graph | left_trim >graph.act && test_cmp graph.exp graph.act ' # Most combined diffs won't meet diff-highlight's line-number filter. So we # create one here where one side drops a line and the other modifies it. That # should result in a diff like: # # - modified content # ++resolved content # # which naively looks like one side added "+resolved". test_expect_success 'diff-highlight ignores combined diffs' ' echo "content" >file && git add file && git commit -m base && >file && git commit -am master && git checkout -b other HEAD^ && echo "modified content" >file && git commit -am other && test_must_fail git merge master && echo "resolved content" >file && git commit -am resolved && cat >expect <<-\EOF && --- a/file +++ b/file @@@ -1,1 -1,0 +1,1 @@@ - modified content ++resolved content EOF git show -c | "$DIFF_HIGHLIGHT" >actual.raw && sed -n "/^---/,\$p" actual && test_cmp expect actual ' test_done (CommandList children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:test_description) op: Equal rhs: {(SQ <"Test diff-highlight">)} spids: [4] ) ] spids: [4] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURR_DIR) op: Equal rhs: { (CommandSubPart command_list: (CommandList children:[(C {(pwd)})]) left_token: spids: [11 13] ) } spids: [10] ) ] spids: [10] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:TEST_OUTPUT_DIRECTORY) op: Equal rhs: { (CommandSubPart command_list: (CommandList children:[(C {(pwd)})]) left_token: spids: [16 18] ) } spids: [15] ) ] spids: [15] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:TEST_DIRECTORY) op: Equal rhs: {(DQ ($ VSub_Name "$CURR_DIR")) (/../../../t)} spids: [20] ) ] spids: [20] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:DIFF_HIGHLIGHT) op: Equal rhs: {(DQ ($ VSub_Name "$CURR_DIR")) (/../diff-highlight)} spids: [26] ) ] spids: [26] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CW) op: Equal rhs: { (DQ (CommandSubPart command_list: (CommandList children: [ (C {(printf)} {(DQ (EscapedLiteralPart token:) ("33[7m"))} ) ] ) left_token: spids: [35 42] ) ) } spids: [33] ) ] spids: [33] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CR) op: Equal rhs: { (DQ (CommandSubPart command_list: (CommandList children: [ (C {(printf)} {(DQ (EscapedLiteralPart token:) ("33[27m"))} ) ] ) left_token: spids: [50 57] ) ) } spids: [48] ) ] spids: [48] ) (C {(.)} {(DQ ($ VSub_Name "$TEST_DIRECTORY")) (/test-lib.sh)}) (If arms: [ (if_arm cond: [(Pipeline children:[(C {(test_have_prereq)} {(PERL)})] negated:True)] action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:skip_all) op: Equal rhs: {(SQ <"skipping diff-highlight tests; perl not available">)} spids: [83] ) ] spids: [83] ) (C {(test_done)}) ] spids: [-1 80] ) ] spids: [-1 91] ) (FuncDef name: dh_test body: (BraceGroup children: [ (AndOr children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:a) op: Equal rhs: {(DQ ($ VSub_Number "$1"))} spids: [117] ) (assign_pair lhs: (LhsName name:b) op: Equal rhs: {(DQ ($ VSub_Number "$2"))} spids: [122] ) ] spids: [117] ) (AndOr children: [ (SimpleCommand words: [{(cat)}] redirects: [(Redir op_id:Redir_Great fd:-1 arg_word:{(patch.exp)} spids:[133])] ) (AndOr children: [ (BraceGroup children: [ (AndOr children: [ (SimpleCommand words: [{(cat)} {(DQ ($ VSub_Name "$a"))}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(file)} spids: [149] ) ] ) (AndOr children: [ (C {(git)} {(add)} {(file)}) (AndOr children: [ (C {(git)} {(commit)} {(-m)} {(DQ ("Add a file"))}) (AndOr children: [ (SimpleCommand words: [{(cat)} {(DQ ($ VSub_Name "$b"))}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(file)} spids: [184] ) ] ) (AndOr children: [ (SimpleCommand words: [{(git)} {(diff)} {(file)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(diff.raw)} spids: [196] ) ] ) (AndOr children: [ (C {(git)} {(commit)} {(-a)} {(-m)} {(DQ ("Update a file"))} ) (SimpleCommand words: [{(git)} {(show)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(commit.raw)} spids: [221] ) ] ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(/dev/null)} spids: [227] ) ] spids: [140] ) (AndOr children: [ (Pipeline children: [ (SimpleCommand words: [{(DQ ($ VSub_Name "$DIFF_HIGHLIGHT"))}] redirects: [ (Redir op_id: Redir_Less fd: -1 arg_word: {(diff.raw)} spids: [238] ) ] ) (SimpleCommand words: [{(test_strip_patch_header)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(diff.act)} spids: [245] ) ] ) ] negated: False ) (AndOr children: [ (Pipeline children: [ (SimpleCommand words: [{(DQ ($ VSub_Name "$DIFF_HIGHLIGHT"))}] redirects: [ (Redir op_id: Redir_Less fd: -1 arg_word: {(commit.raw)} spids: [255] ) ] ) (SimpleCommand words: [{(test_strip_patch_header)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(commit.act)} spids: [262] ) ] ) ] negated: False ) (AndOr children: [ (C {(test_cmp)} {(patch.exp)} {(diff.act)}) (C {(test_cmp)} {(patch.exp)} {(commit.act)}) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] spids: [114] ) spids: [109 113] ) (FuncDef name: test_strip_patch_header body: (BraceGroup children: [(C {(sed)} {(-n)} {(SQ <"/^@@/,$p">)} {($ VSub_Star "$*")})] spids: [291] ) spids: [286 290] ) (FuncDef name: dh_test_setup_history body: (BraceGroup children: [ (AndOr children: [ (SimpleCommand words: [{(echo)} {(DQ (file1))}] redirects: [(Redir op_id:Redir_Great fd:-1 arg_word:{(file1)} spids:[375])] ) (AndOr children: [ (SimpleCommand words: [{(echo)} {(DQ (file2))}] redirects: [(Redir op_id:Redir_Great fd:-1 arg_word:{(file2)} spids:[387])] ) (AndOr children: [ (SimpleCommand words: [{(echo)} {(DQ (file3))}] redirects: [(Redir op_id:Redir_Great fd:-1 arg_word:{(file3)} spids:[399])] ) (AndOr children: [ (SimpleCommand words: [{(cat)} {(file1)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(file)} spids: [410] ) ] ) (AndOr children: [ (C {(git)} {(add)} {(file)}) (AndOr children: [ (C {(git)} {(commit)} {(-m)} {(DQ (D))}) (AndOr children: [ (C {(git)} {(checkout)} {(-b)} {(branch)}) (AndOr children: [ (SimpleCommand words: [{(cat)} {(file2)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(file)} spids: [454] ) ] ) (AndOr children: [ (C {(git)} {(commit)} {(-a)} {(-m)} {(DQ (A))}) (AndOr children: [ (C {(git)} {(checkout)} {(master)}) (AndOr children: [ (SimpleCommand words: [{(cat)} {(file2)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(file)} spids: [489] ) ] ) (AndOr children: [ (C {(git)} {(commit)} {(-a)} {(-m)} {(DQ (E))}) (AndOr children: [ (SimpleCommand words: [{(cat)} {(file3)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(file)} spids: [515] ) ] ) (C {(git)} {(commit)} {(-a)} {(-m)} {(DQ (F))} ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] spids: [366] ) spids: [361 365] ) (FuncDef name: left_trim body: (BraceGroup children: [(C {(DQ ($ VSub_Name "$PERL_PATH"))} {(-pe)} {(SQ <"s/^\\s+//">)})] spids: [541] ) spids: [536 540] ) (FuncDef name: trim_graph body: (BraceGroup children: [ (C {(DQ ($ VSub_Name "$PERL_PATH"))} {(-pe)} {(SQ <"s@^((\\*|\\|)( |/|\\\\))+@@">)}) ] spids: [562] ) spids: [557 561] ) (C {(test_expect_success)} {(SQ <"diff-highlight highlights the beginning of a line">)} { (SQ <"\n"> <"\tcat >a <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\tbbb\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tcat >b <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\t0bb\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tdh_test a b <<-EOF\n"> <"\t\t@@ -1,3 +1,3 @@\n"> <"\t\t aaa\n"> <"\t\t-${CW}b${CR}bb\n"> <"\t\t+${CW}0${CR}bb\n"> <"\t\t ccc\n"> <"\tEOF\n"> ) } ) (C {(test_expect_success)} {(SQ <"diff-highlight highlights the end of a line">)} { (SQ <"\n"> <"\tcat >a <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\tbbb\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tcat >b <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\tbb0\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tdh_test a b <<-EOF\n"> <"\t\t@@ -1,3 +1,3 @@\n"> <"\t\t aaa\n"> <"\t\t-bb${CW}b${CR}\n"> <"\t\t+bb${CW}0${CR}\n"> <"\t\t ccc\n"> <"\tEOF\n"> ) } ) (C {(test_expect_success)} {(SQ <"diff-highlight highlights the middle of a line">)} { (SQ <"\n"> <"\tcat >a <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\tbbb\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tcat >b <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\tb0b\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tdh_test a b <<-EOF\n"> <"\t\t@@ -1,3 +1,3 @@\n"> <"\t\t aaa\n"> <"\t\t-b${CW}b${CR}b\n"> <"\t\t+b${CW}0${CR}b\n"> <"\t\t ccc\n"> <"\tEOF\n"> ) } ) (C {(test_expect_success)} {(SQ <"diff-highlight does not highlight whole line">)} { (SQ <"\n"> <"\tcat >a <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\tbbb\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tcat >b <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\t000\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tdh_test a b <<-EOF\n"> <"\t\t@@ -1,3 +1,3 @@\n"> <"\t\t aaa\n"> <"\t\t-bbb\n"> <"\t\t+000\n"> <"\t\t ccc\n"> <"\tEOF\n"> ) } ) (C {(test_expect_failure)} {(SQ <"diff-highlight highlights mismatched hunk size">)} { (SQ <"\n"> <"\tcat >a <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\tbbb\n"> <"\tEOF\n"> <"\n"> <"\tcat >b <<-\\EOF &&\n"> <"\t\taaa\n"> <"\t\tb0b\n"> <"\t\tccc\n"> <"\tEOF\n"> <"\n"> <"\tdh_test a b <<-EOF\n"> <"\t\t@@ -1,3 +1,3 @@\n"> <"\t\t aaa\n"> <"\t\t-b${CW}b${CR}b\n"> <"\t\t+b${CW}0${CR}b\n"> <"\t\t+ccc\n"> <"\tEOF\n"> ) } ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:o_accent) op: Equal rhs: { (CommandSubPart command_list: (CommandList children:[(C {(printf)} {(SQ <"\\303\\263">)})]) left_token: spids: [748 754] ) } spids: [747] ) ] spids: [747] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:o_stroke) op: Equal rhs: { (CommandSubPart command_list: (CommandList children:[(C {(printf)} {(SQ <"\\303\\270">)})]) left_token: spids: [760 766] ) } spids: [759] ) ] spids: [759] ) (C {(test_expect_success)} {(SQ <"diff-highlight treats multibyte utf-8 as a unit">)} { (SQ <"\n"> <"\techo \"unic${o_accent}de\" >a &&\n"> <"\techo \"unic${o_stroke}de\" >b &&\n"> <"\tdh_test a b <<-EOF\n"> <"\t\t@@ -1 +1 @@\n"> <"\t\t-unic${CW}${o_accent}${CR}de\n"> <"\t\t+unic${CW}${o_stroke}${CR}de\n"> <"\tEOF\n"> ) } ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:combine_accent) op: Equal rhs: { (CommandSubPart command_list: (CommandList children:[(C {(printf)} {(SQ <"\\314\\201">)})]) left_token: spids: [800 806] ) } spids: [799] ) ] spids: [799] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:combine_circum) op: Equal rhs: { (CommandSubPart command_list: (CommandList children:[(C {(printf)} {(SQ <"\\314\\202">)})]) left_token: spids: [812 818] ) } spids: [811] ) ] spids: [811] ) (C {(test_expect_failure)} {(SQ <"diff-highlight treats combining code points as a unit">)} { (SQ <"\n"> <"\techo \"unico${combine_accent}de\" >a &&\n"> <"\techo \"unico${combine_circum}de\" >b &&\n"> <"\tdh_test a b <<-EOF\n"> <"\t\t@@ -1 +1 @@\n"> <"\t\t-unic${CW}o${combine_accent}${CR}de\n"> <"\t\t+unic${CW}o${combine_circum}${CR}de\n"> <"\tEOF\n"> ) } ) (C {(test_expect_success)} {(SQ <"diff-highlight works with the --graph option">)} { (SQ <"\n"> <"\tdh_test_setup_history &&\n"> <"\n"> <"\t# topo-order so that the order of the commits is the same as with --graph\n"> <"\t# trim graph elements so we can do a diff\n"> <"\t# trim leading space because our trim_graph is not perfect\n"> <"\tgit log --branches -p --topo-order |\n"> <"\t\t\"$DIFF_HIGHLIGHT\" | left_trim >graph.exp &&\n"> <"\tgit log --branches -p --graph |\n"> <"\t\t\"$DIFF_HIGHLIGHT\" | trim_graph | left_trim >graph.act &&\n"> <"\ttest_cmp graph.exp graph.act\n"> ) } ) (C {(test_expect_success)} {(SQ <"diff-highlight ignores combined diffs">)} { (SQ <"\n"> <"\techo \"content\" >file &&\n"> <"\tgit add file &&\n"> <"\tgit commit -m base &&\n"> <"\n"> <"\t>file &&\n"> <"\tgit commit -am master &&\n"> <"\n"> <"\tgit checkout -b other HEAD^ &&\n"> <"\techo \"modified content\" >file &&\n"> <"\tgit commit -am other &&\n"> <"\n"> <"\ttest_must_fail git merge master &&\n"> <"\techo \"resolved content\" >file &&\n"> <"\tgit commit -am resolved &&\n"> <"\n"> <"\tcat >expect <<-\\EOF &&\n"> <"\t--- a/file\n"> <"\t+++ b/file\n"> <"\t@@@ -1,1 -1,0 +1,1 @@@\n"> <"\t- modified content\n"> <"\t++resolved content\n"> <"\tEOF\n"> <"\n"> <"\tgit show -c | \"$DIFF_HIGHLIGHT\" >actual.raw &&\n"> <"\tsed -n \"/^---/,\\$p\" actual &&\n"> <"\ttest_cmp expect actual\n"> ) } ) (C {(test_done)}) ] )