golang.org/x/tools@v0.21.0/internal/diff/diff_test.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 diff_test
     6  
     7  import (
     8  	"bytes"
     9  	"math/rand"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"reflect"
    14  	"strings"
    15  	"testing"
    16  	"unicode/utf8"
    17  
    18  	"golang.org/x/tools/internal/diff"
    19  	"golang.org/x/tools/internal/diff/difftest"
    20  	"golang.org/x/tools/internal/testenv"
    21  )
    22  
    23  func TestApply(t *testing.T) {
    24  	for _, tc := range difftest.TestCases {
    25  		t.Run(tc.Name, func(t *testing.T) {
    26  			got, err := diff.Apply(tc.In, tc.Edits)
    27  			if err != nil {
    28  				t.Fatalf("Apply(Edits) failed: %v", err)
    29  			}
    30  			if got != tc.Out {
    31  				t.Errorf("Apply(Edits): got %q, want %q", got, tc.Out)
    32  			}
    33  			if tc.LineEdits != nil {
    34  				got, err := diff.Apply(tc.In, tc.LineEdits)
    35  				if err != nil {
    36  					t.Fatalf("Apply(LineEdits) failed: %v", err)
    37  				}
    38  				if got != tc.Out {
    39  					t.Errorf("Apply(LineEdits): got %q, want %q", got, tc.Out)
    40  				}
    41  			}
    42  		})
    43  	}
    44  }
    45  
    46  func TestNEdits(t *testing.T) {
    47  	for _, tc := range difftest.TestCases {
    48  		edits := diff.Strings(tc.In, tc.Out)
    49  		got, err := diff.Apply(tc.In, edits)
    50  		if err != nil {
    51  			t.Fatalf("Apply failed: %v", err)
    52  		}
    53  		if got != tc.Out {
    54  			t.Fatalf("%s: got %q wanted %q", tc.Name, got, tc.Out)
    55  		}
    56  		if len(edits) < len(tc.Edits) { // should find subline edits
    57  			t.Errorf("got %v, expected %v for %#v", edits, tc.Edits, tc)
    58  		}
    59  	}
    60  }
    61  
    62  func TestNRandom(t *testing.T) {
    63  	rand.Seed(1)
    64  	for i := 0; i < 1000; i++ {
    65  		a := randstr("abω", 16)
    66  		b := randstr("abωc", 16)
    67  		edits := diff.Strings(a, b)
    68  		got, err := diff.Apply(a, edits)
    69  		if err != nil {
    70  			t.Fatalf("Apply failed: %v", err)
    71  		}
    72  		if got != b {
    73  			t.Fatalf("%d: got %q, wanted %q, starting with %q", i, got, b, a)
    74  		}
    75  	}
    76  }
    77  
    78  // $ go test -fuzz=FuzzRoundTrip ./internal/diff
    79  func FuzzRoundTrip(f *testing.F) {
    80  	f.Fuzz(func(t *testing.T, a, b string) {
    81  		if !utf8.ValidString(a) || !utf8.ValidString(b) {
    82  			return // inputs must be text
    83  		}
    84  		edits := diff.Strings(a, b)
    85  		got, err := diff.Apply(a, edits)
    86  		if err != nil {
    87  			t.Fatalf("Apply failed: %v", err)
    88  		}
    89  		if got != b {
    90  			t.Fatalf("applying diff(%q, %q) gives %q; edits=%v", a, b, got, edits)
    91  		}
    92  	})
    93  }
    94  
    95  func TestLineEdits(t *testing.T) {
    96  	for _, tc := range difftest.TestCases {
    97  		t.Run(tc.Name, func(t *testing.T) {
    98  			want := tc.LineEdits
    99  			if want == nil {
   100  				want = tc.Edits // already line-aligned
   101  			}
   102  			got, err := diff.LineEdits(tc.In, tc.Edits)
   103  			if err != nil {
   104  				t.Fatalf("LineEdits: %v", err)
   105  			}
   106  			if !reflect.DeepEqual(got, want) {
   107  				t.Errorf("in=<<%s>>\nout=<<%s>>\nraw  edits=%s\nline edits=%s\nwant: %s",
   108  					tc.In, tc.Out, tc.Edits, got, want)
   109  			}
   110  			// make sure that applying the edits gives the expected result
   111  			fixed, err := diff.Apply(tc.In, got)
   112  			if err != nil {
   113  				t.Error(err)
   114  			}
   115  			if fixed != tc.Out {
   116  				t.Errorf("Apply(LineEdits): got %q, want %q", fixed, tc.Out)
   117  			}
   118  		})
   119  	}
   120  }
   121  
   122  func TestToUnified(t *testing.T) {
   123  	testenv.NeedsTool(t, "patch")
   124  	for _, tc := range difftest.TestCases {
   125  		t.Run(tc.Name, func(t *testing.T) {
   126  			unified, err := diff.ToUnified(difftest.FileA, difftest.FileB, tc.In, tc.Edits, diff.DefaultContextLines)
   127  			if err != nil {
   128  				t.Fatal(err)
   129  			}
   130  			if unified == "" {
   131  				return
   132  			}
   133  			orig := filepath.Join(t.TempDir(), "original")
   134  			err = os.WriteFile(orig, []byte(tc.In), 0644)
   135  			if err != nil {
   136  				t.Fatal(err)
   137  			}
   138  			temp := filepath.Join(t.TempDir(), "patched")
   139  			err = os.WriteFile(temp, []byte(tc.In), 0644)
   140  			if err != nil {
   141  				t.Fatal(err)
   142  			}
   143  			cmd := exec.Command("patch", "-p0", "-u", "-s", "-o", temp, orig)
   144  			cmd.Stdin = strings.NewReader(unified)
   145  			cmd.Stdout = new(bytes.Buffer)
   146  			cmd.Stderr = new(bytes.Buffer)
   147  			if err = cmd.Run(); err != nil {
   148  				t.Fatalf("%v: %q (%q) (%q)", err, cmd.String(),
   149  					cmd.Stderr, cmd.Stdout)
   150  			}
   151  			got, err := os.ReadFile(temp)
   152  			if err != nil {
   153  				t.Fatal(err)
   154  			}
   155  			if string(got) != tc.Out {
   156  				t.Errorf("applying unified failed: got\n%q, wanted\n%q unified\n%q",
   157  					got, tc.Out, unified)
   158  			}
   159  
   160  		})
   161  	}
   162  }
   163  
   164  func TestRegressionOld001(t *testing.T) {
   165  	a := "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage diff_test\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"golang.org/x/tools/gopls/internal/lsp/diff\"\n\t\"golang.org/x/tools/internal/diff/difftest\"\n\t\"golang.org/x/tools/gopls/internal/span\"\n)\n"
   166  
   167  	b := "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage diff_test\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/safehtml/template\"\n\t\"golang.org/x/tools/gopls/internal/lsp/diff\"\n\t\"golang.org/x/tools/internal/diff/difftest\"\n\t\"golang.org/x/tools/gopls/internal/span\"\n)\n"
   168  	diffs := diff.Strings(a, b)
   169  	got, err := diff.Apply(a, diffs)
   170  	if err != nil {
   171  		t.Fatalf("Apply failed: %v", err)
   172  	}
   173  	if got != b {
   174  		i := 0
   175  		for ; i < len(a) && i < len(b) && got[i] == b[i]; i++ {
   176  		}
   177  		t.Errorf("oops %vd\n%q\n%q", diffs, got, b)
   178  		t.Errorf("\n%q\n%q", got[i:], b[i:])
   179  	}
   180  }
   181  
   182  func TestRegressionOld002(t *testing.T) {
   183  	a := "n\"\n)\n"
   184  	b := "n\"\n\t\"golang.org/x//nnal/stack\"\n)\n"
   185  	diffs := diff.Strings(a, b)
   186  	got, err := diff.Apply(a, diffs)
   187  	if err != nil {
   188  		t.Fatalf("Apply failed: %v", err)
   189  	}
   190  	if got != b {
   191  		i := 0
   192  		for ; i < len(a) && i < len(b) && got[i] == b[i]; i++ {
   193  		}
   194  		t.Errorf("oops %vd\n%q\n%q", diffs, got, b)
   195  		t.Errorf("\n%q\n%q", got[i:], b[i:])
   196  	}
   197  }
   198  
   199  // return a random string of length n made of characters from s
   200  func randstr(s string, n int) string {
   201  	src := []rune(s)
   202  	x := make([]rune, n)
   203  	for i := 0; i < n; i++ {
   204  		x[i] = src[rand.Intn(len(src))]
   205  	}
   206  	return string(x)
   207  }