github.com/v2fly/tools@v0.100.0/internal/lsp/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  // "github.com/v2fly/tools/internal/lsp/diff"
     8  package difftest
     9  
    10  import (
    11  	"fmt"
    12  	"testing"
    13  
    14  	"github.com/v2fly/tools/internal/lsp/diff"
    15  	"github.com/v2fly/tools/internal/span"
    16  )
    17  
    18  const (
    19  	FileA         = "from"
    20  	FileB         = "to"
    21  	UnifiedPrefix = "--- " + FileA + "\n+++ " + FileB + "\n"
    22  )
    23  
    24  var TestCases = []struct {
    25  	Name, In, Out, Unified string
    26  	Edits, LineEdits       []diff.TextEdit
    27  	NoDiff                 bool
    28  }{{
    29  	Name: "empty",
    30  	In:   "",
    31  	Out:  "",
    32  }, {
    33  	Name: "no_diff",
    34  	In:   "gargantuan\n",
    35  	Out:  "gargantuan\n",
    36  }, {
    37  	Name: "replace_all",
    38  	In:   "fruit\n",
    39  	Out:  "cheese\n",
    40  	Unified: UnifiedPrefix + `
    41  @@ -1 +1 @@
    42  -fruit
    43  +cheese
    44  `[1:],
    45  	Edits:     []diff.TextEdit{{Span: newSpan(0, 5), NewText: "cheese"}},
    46  	LineEdits: []diff.TextEdit{{Span: newSpan(0, 6), NewText: "cheese\n"}},
    47  }, {
    48  	Name: "insert_rune",
    49  	In:   "gord\n",
    50  	Out:  "gourd\n",
    51  	Unified: UnifiedPrefix + `
    52  @@ -1 +1 @@
    53  -gord
    54  +gourd
    55  `[1:],
    56  	Edits:     []diff.TextEdit{{Span: newSpan(2, 2), NewText: "u"}},
    57  	LineEdits: []diff.TextEdit{{Span: newSpan(0, 5), NewText: "gourd\n"}},
    58  }, {
    59  	Name: "delete_rune",
    60  	In:   "groat\n",
    61  	Out:  "goat\n",
    62  	Unified: UnifiedPrefix + `
    63  @@ -1 +1 @@
    64  -groat
    65  +goat
    66  `[1:],
    67  	Edits:     []diff.TextEdit{{Span: newSpan(1, 2), NewText: ""}},
    68  	LineEdits: []diff.TextEdit{{Span: newSpan(0, 6), NewText: "goat\n"}},
    69  }, {
    70  	Name: "replace_rune",
    71  	In:   "loud\n",
    72  	Out:  "lord\n",
    73  	Unified: UnifiedPrefix + `
    74  @@ -1 +1 @@
    75  -loud
    76  +lord
    77  `[1:],
    78  	Edits:     []diff.TextEdit{{Span: newSpan(2, 3), NewText: "r"}},
    79  	LineEdits: []diff.TextEdit{{Span: newSpan(0, 5), NewText: "lord\n"}},
    80  }, {
    81  	Name: "replace_partials",
    82  	In:   "blanket\n",
    83  	Out:  "bunker\n",
    84  	Unified: UnifiedPrefix + `
    85  @@ -1 +1 @@
    86  -blanket
    87  +bunker
    88  `[1:],
    89  	Edits: []diff.TextEdit{
    90  		{Span: newSpan(1, 3), NewText: "u"},
    91  		{Span: newSpan(6, 7), NewText: "r"},
    92  	},
    93  	LineEdits: []diff.TextEdit{{Span: newSpan(0, 8), NewText: "bunker\n"}},
    94  }, {
    95  	Name: "insert_line",
    96  	In:   "1: one\n3: three\n",
    97  	Out:  "1: one\n2: two\n3: three\n",
    98  	Unified: UnifiedPrefix + `
    99  @@ -1,2 +1,3 @@
   100   1: one
   101  +2: two
   102   3: three
   103  `[1:],
   104  	Edits: []diff.TextEdit{{Span: newSpan(7, 7), NewText: "2: two\n"}},
   105  }, {
   106  	Name: "replace_no_newline",
   107  	In:   "A",
   108  	Out:  "B",
   109  	Unified: UnifiedPrefix + `
   110  @@ -1 +1 @@
   111  -A
   112  \ No newline at end of file
   113  +B
   114  \ No newline at end of file
   115  `[1:],
   116  	Edits: []diff.TextEdit{{Span: newSpan(0, 1), NewText: "B"}},
   117  }, {
   118  	Name: "add_end",
   119  	In:   "A",
   120  	Out:  "AB",
   121  	Unified: UnifiedPrefix + `
   122  @@ -1 +1 @@
   123  -A
   124  \ No newline at end of file
   125  +AB
   126  \ No newline at end of file
   127  `[1:],
   128  	Edits:     []diff.TextEdit{{Span: newSpan(1, 1), NewText: "B"}},
   129  	LineEdits: []diff.TextEdit{{Span: newSpan(0, 1), NewText: "AB"}},
   130  }, {
   131  	Name: "add_newline",
   132  	In:   "A",
   133  	Out:  "A\n",
   134  	Unified: UnifiedPrefix + `
   135  @@ -1 +1 @@
   136  -A
   137  \ No newline at end of file
   138  +A
   139  `[1:],
   140  	Edits:     []diff.TextEdit{{Span: newSpan(1, 1), NewText: "\n"}},
   141  	LineEdits: []diff.TextEdit{{Span: newSpan(0, 1), NewText: "A\n"}},
   142  }, {
   143  	Name: "delete_front",
   144  	In:   "A\nB\nC\nA\nB\nB\nA\n",
   145  	Out:  "C\nB\nA\nB\nA\nC\n",
   146  	Unified: UnifiedPrefix + `
   147  @@ -1,7 +1,6 @@
   148  -A
   149  -B
   150   C
   151  +B
   152   A
   153   B
   154  -B
   155   A
   156  +C
   157  `[1:],
   158  	Edits: []diff.TextEdit{
   159  		{Span: newSpan(0, 4), NewText: ""},
   160  		{Span: newSpan(6, 6), NewText: "B\n"},
   161  		{Span: newSpan(10, 12), NewText: ""},
   162  		{Span: newSpan(14, 14), NewText: "C\n"},
   163  	},
   164  	NoDiff: true, // diff algorithm produces different delete/insert pattern
   165  },
   166  	{
   167  		Name: "replace_last_line",
   168  		In:   "A\nB\n",
   169  		Out:  "A\nC\n\n",
   170  		Unified: UnifiedPrefix + `
   171  @@ -1,2 +1,3 @@
   172   A
   173  -B
   174  +C
   175  +
   176  `[1:],
   177  		Edits:     []diff.TextEdit{{Span: newSpan(2, 3), NewText: "C\n"}},
   178  		LineEdits: []diff.TextEdit{{Span: newSpan(2, 4), NewText: "C\n\n"}},
   179  	},
   180  	{
   181  		Name: "multiple_replace",
   182  		In:   "A\nB\nC\nD\nE\nF\nG\n",
   183  		Out:  "A\nH\nI\nJ\nE\nF\nK\n",
   184  		Unified: UnifiedPrefix + `
   185  @@ -1,7 +1,7 @@
   186   A
   187  -B
   188  -C
   189  -D
   190  +H
   191  +I
   192  +J
   193   E
   194   F
   195  -G
   196  +K
   197  `[1:],
   198  		Edits: []diff.TextEdit{
   199  			{Span: newSpan(2, 8), NewText: "H\nI\nJ\n"},
   200  			{Span: newSpan(12, 14), NewText: "K\n"},
   201  		},
   202  		NoDiff: true, // diff algorithm produces different delete/insert pattern
   203  	},
   204  }
   205  
   206  func init() {
   207  	// expand all the spans to full versions
   208  	// we need them all to have their line number and column
   209  	for _, tc := range TestCases {
   210  		c := span.NewContentConverter("", []byte(tc.In))
   211  		for i := range tc.Edits {
   212  			tc.Edits[i].Span, _ = tc.Edits[i].Span.WithAll(c)
   213  		}
   214  		for i := range tc.LineEdits {
   215  			tc.LineEdits[i].Span, _ = tc.LineEdits[i].Span.WithAll(c)
   216  		}
   217  	}
   218  }
   219  
   220  func DiffTest(t *testing.T, compute diff.ComputeEdits) {
   221  	t.Helper()
   222  	for _, test := range TestCases {
   223  		t.Run(test.Name, func(t *testing.T) {
   224  			t.Helper()
   225  			edits, err := compute(span.URIFromPath("/"+test.Name), test.In, test.Out)
   226  			if err != nil {
   227  				t.Fatal(err)
   228  			}
   229  			got := diff.ApplyEdits(test.In, edits)
   230  			unified := fmt.Sprint(diff.ToUnified(FileA, FileB, test.In, edits))
   231  			if got != test.Out {
   232  				t.Errorf("got patched:\n%v\nfrom diff:\n%v\nexpected:\n%v", got, unified, test.Out)
   233  			}
   234  			if !test.NoDiff && unified != test.Unified {
   235  				t.Errorf("got diff:\n%v\nexpected:\n%v", unified, test.Unified)
   236  			}
   237  		})
   238  	}
   239  }
   240  
   241  func newSpan(start, end int) span.Span {
   242  	return span.New("", span.NewPoint(0, 0, start), span.NewPoint(0, 0, end))
   243  }