github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/diff/difftest/difftest.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package difftest supplies a set of tests that will operate on any
     6  // implementation of a diff algorithm as exposed by
     7  // "golang.org/x/tools/internal/diff"
     8  package difftest
     9  
    10  // There are two kinds of tests, semantic tests, and 'golden data' tests.
    11  // The semantic tests check that the computed diffs transform the input to
    12  // the output, and that 'patch' accepts the computed unified diffs.
    13  // The other tests just check that Edits and LineEdits haven't changed
    14  // unexpectedly. These fields may need to be changed when the diff algorithm
    15  // changes.
    16  
    17  import (
    18  	"testing"
    19  
    20  	"golang.org/x/tools/internal/diff"
    21  )
    22  
    23  const (
    24  	FileA         = "from"
    25  	FileB         = "to"
    26  	UnifiedPrefix = "--- " + FileA + "\n+++ " + FileB + "\n"
    27  )
    28  
    29  var TestCases = []struct {
    30  	Name, In, Out, Unified string
    31  	Edits, LineEdits       []diff.Edit
    32  	NoDiff                 bool
    33  }{{
    34  	Name: "empty",
    35  	In:   "",
    36  	Out:  "",
    37  }, {
    38  	Name: "no_diff",
    39  	In:   "gargantuan\n",
    40  	Out:  "gargantuan\n",
    41  }, {
    42  	Name: "replace_all",
    43  	In:   "fruit\n",
    44  	Out:  "cheese\n",
    45  	Unified: UnifiedPrefix + `
    46  @@ -1 +1 @@
    47  -fruit
    48  +cheese
    49  `[1:],
    50  	Edits:     []diff.Edit{{Start: 0, End: 5, New: "cheese"}},
    51  	LineEdits: []diff.Edit{{Start: 0, End: 6, New: "cheese\n"}},
    52  }, {
    53  	Name: "insert_rune",
    54  	In:   "gord\n",
    55  	Out:  "gourd\n",
    56  	Unified: UnifiedPrefix + `
    57  @@ -1 +1 @@
    58  -gord
    59  +gourd
    60  `[1:],
    61  	Edits:     []diff.Edit{{Start: 2, End: 2, New: "u"}},
    62  	LineEdits: []diff.Edit{{Start: 0, End: 5, New: "gourd\n"}},
    63  }, {
    64  	Name: "delete_rune",
    65  	In:   "groat\n",
    66  	Out:  "goat\n",
    67  	Unified: UnifiedPrefix + `
    68  @@ -1 +1 @@
    69  -groat
    70  +goat
    71  `[1:],
    72  	Edits:     []diff.Edit{{Start: 1, End: 2, New: ""}},
    73  	LineEdits: []diff.Edit{{Start: 0, End: 6, New: "goat\n"}},
    74  }, {
    75  	Name: "replace_rune",
    76  	In:   "loud\n",
    77  	Out:  "lord\n",
    78  	Unified: UnifiedPrefix + `
    79  @@ -1 +1 @@
    80  -loud
    81  +lord
    82  `[1:],
    83  	Edits:     []diff.Edit{{Start: 2, End: 3, New: "r"}},
    84  	LineEdits: []diff.Edit{{Start: 0, End: 5, New: "lord\n"}},
    85  }, {
    86  	Name: "replace_partials",
    87  	In:   "blanket\n",
    88  	Out:  "bunker\n",
    89  	Unified: UnifiedPrefix + `
    90  @@ -1 +1 @@
    91  -blanket
    92  +bunker
    93  `[1:],
    94  	Edits: []diff.Edit{
    95  		{Start: 1, End: 3, New: "u"},
    96  		{Start: 6, End: 7, New: "r"},
    97  	},
    98  	LineEdits: []diff.Edit{{Start: 0, End: 8, New: "bunker\n"}},
    99  }, {
   100  	Name: "insert_line",
   101  	In:   "1: one\n3: three\n",
   102  	Out:  "1: one\n2: two\n3: three\n",
   103  	Unified: UnifiedPrefix + `
   104  @@ -1,2 +1,3 @@
   105   1: one
   106  +2: two
   107   3: three
   108  `[1:],
   109  	Edits: []diff.Edit{{Start: 7, End: 7, New: "2: two\n"}},
   110  }, {
   111  	Name: "replace_no_newline",
   112  	In:   "A",
   113  	Out:  "B",
   114  	Unified: UnifiedPrefix + `
   115  @@ -1 +1 @@
   116  -A
   117  \ No newline at end of file
   118  +B
   119  \ No newline at end of file
   120  `[1:],
   121  	Edits: []diff.Edit{{Start: 0, End: 1, New: "B"}},
   122  }, {
   123  	Name: "append_empty",
   124  	In:   "", // GNU diff -u special case: -0,0
   125  	Out:  "AB\nC",
   126  	Unified: UnifiedPrefix + `
   127  @@ -0,0 +1,2 @@
   128  +AB
   129  +C
   130  \ No newline at end of file
   131  `[1:],
   132  	Edits:     []diff.Edit{{Start: 0, End: 0, New: "AB\nC"}},
   133  	LineEdits: []diff.Edit{{Start: 0, End: 0, New: "AB\nC"}},
   134  },
   135  	// TODO(adonovan): fix this test: GNU diff -u prints "+1,2", Unifies prints "+1,3".
   136  	// 	{
   137  	// 		Name: "add_start",
   138  	// 		In:   "A",
   139  	// 		Out:  "B\nCA",
   140  	// 		Unified: UnifiedPrefix + `
   141  	// @@ -1 +1,2 @@
   142  	// -A
   143  	// \ No newline at end of file
   144  	// +B
   145  	// +CA
   146  	// \ No newline at end of file
   147  	// `[1:],
   148  	// 		Edits:     []diff.TextEdit{{Span: newSpan(0, 0), NewText: "B\nC"}},
   149  	// 		LineEdits: []diff.TextEdit{{Span: newSpan(0, 0), NewText: "B\nC"}},
   150  	// 	},
   151  	{
   152  		Name: "add_end",
   153  		In:   "A",
   154  		Out:  "AB",
   155  		Unified: UnifiedPrefix + `
   156  @@ -1 +1 @@
   157  -A
   158  \ No newline at end of file
   159  +AB
   160  \ No newline at end of file
   161  `[1:],
   162  		Edits:     []diff.Edit{{Start: 1, End: 1, New: "B"}},
   163  		LineEdits: []diff.Edit{{Start: 0, End: 1, New: "AB"}},
   164  	}, {
   165  		Name: "add_empty",
   166  		In:   "",
   167  		Out:  "AB\nC",
   168  		Unified: UnifiedPrefix + `
   169  @@ -0,0 +1,2 @@
   170  +AB
   171  +C
   172  \ No newline at end of file
   173  `[1:],
   174  		Edits:     []diff.Edit{{Start: 0, End: 0, New: "AB\nC"}},
   175  		LineEdits: []diff.Edit{{Start: 0, End: 0, New: "AB\nC"}},
   176  	}, {
   177  		Name: "add_newline",
   178  		In:   "A",
   179  		Out:  "A\n",
   180  		Unified: UnifiedPrefix + `
   181  @@ -1 +1 @@
   182  -A
   183  \ No newline at end of file
   184  +A
   185  `[1:],
   186  		Edits:     []diff.Edit{{Start: 1, End: 1, New: "\n"}},
   187  		LineEdits: []diff.Edit{{Start: 0, End: 1, New: "A\n"}},
   188  	}, {
   189  		Name: "delete_front",
   190  		In:   "A\nB\nC\nA\nB\nB\nA\n",
   191  		Out:  "C\nB\nA\nB\nA\nC\n",
   192  		Unified: UnifiedPrefix + `
   193  @@ -1,7 +1,6 @@
   194  -A
   195  -B
   196   C
   197  +B
   198   A
   199   B
   200  -B
   201   A
   202  +C
   203  `[1:],
   204  		NoDiff: true, // unified diff is different but valid
   205  		Edits: []diff.Edit{
   206  			{Start: 0, End: 4, New: ""},
   207  			{Start: 6, End: 6, New: "B\n"},
   208  			{Start: 10, End: 12, New: ""},
   209  			{Start: 14, End: 14, New: "C\n"},
   210  		},
   211  		LineEdits: []diff.Edit{
   212  			{Start: 0, End: 6, New: "C\n"},
   213  			{Start: 6, End: 8, New: "B\nA\n"},
   214  			{Start: 10, End: 14, New: "A\n"},
   215  			{Start: 14, End: 14, New: "C\n"},
   216  		},
   217  	}, {
   218  		Name: "replace_last_line",
   219  		In:   "A\nB\n",
   220  		Out:  "A\nC\n\n",
   221  		Unified: UnifiedPrefix + `
   222  @@ -1,2 +1,3 @@
   223   A
   224  -B
   225  +C
   226  +
   227  `[1:],
   228  		Edits:     []diff.Edit{{Start: 2, End: 3, New: "C\n"}},
   229  		LineEdits: []diff.Edit{{Start: 2, End: 4, New: "C\n\n"}},
   230  	},
   231  	{
   232  		Name: "multiple_replace",
   233  		In:   "A\nB\nC\nD\nE\nF\nG\n",
   234  		Out:  "A\nH\nI\nJ\nE\nF\nK\n",
   235  		Unified: UnifiedPrefix + `
   236  @@ -1,7 +1,7 @@
   237   A
   238  -B
   239  -C
   240  -D
   241  +H
   242  +I
   243  +J
   244   E
   245   F
   246  -G
   247  +K
   248  `[1:],
   249  		Edits: []diff.Edit{
   250  			{Start: 2, End: 8, New: "H\nI\nJ\n"},
   251  			{Start: 12, End: 14, New: "K\n"},
   252  		},
   253  		NoDiff: true, // diff algorithm produces different delete/insert pattern
   254  	},
   255  	{
   256  		Name:  "extra_newline",
   257  		In:    "\nA\n",
   258  		Out:   "A\n",
   259  		Edits: []diff.Edit{{Start: 0, End: 1, New: ""}},
   260  		Unified: UnifiedPrefix + `@@ -1,2 +1 @@
   261  -
   262   A
   263  `,
   264  	},
   265  }
   266  
   267  func DiffTest(t *testing.T, compute func(before, after string) []diff.Edit) {
   268  	for _, test := range TestCases {
   269  		t.Run(test.Name, func(t *testing.T) {
   270  			edits := compute(test.In, test.Out)
   271  			got, err := diff.Apply(test.In, edits)
   272  			if err != nil {
   273  				t.Fatalf("Apply failed: %v", err)
   274  			}
   275  			unified, err := diff.ToUnified(FileA, FileB, test.In, edits)
   276  			if err != nil {
   277  				t.Fatalf("ToUnified: %v", err)
   278  			}
   279  			if got != test.Out {
   280  				t.Errorf("Apply: got patched:\n%v\nfrom diff:\n%v\nexpected:\n%v",
   281  					got, unified, test.Out)
   282  			}
   283  			if !test.NoDiff && unified != test.Unified {
   284  				t.Errorf("Unified: got diff:\n%q\nexpected:\n%q diffs:%v",
   285  					unified, test.Unified, edits)
   286  			}
   287  		})
   288  	}
   289  }