github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/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  			// if line edits not specified, it is the same as edits
    99  			edits := tc.LineEdits
   100  			if edits == nil {
   101  				edits = tc.Edits
   102  			}
   103  			got, err := diff.LineEdits(tc.In, tc.Edits)
   104  			if err != nil {
   105  				t.Fatalf("LineEdits: %v", err)
   106  			}
   107  			if !reflect.DeepEqual(got, edits) {
   108  				t.Errorf("LineEdits got\n%q, want\n%q\n%#v", got, edits, tc)
   109  			}
   110  		})
   111  	}
   112  }
   113  
   114  func TestToUnified(t *testing.T) {
   115  	testenv.NeedsTool(t, "patch")
   116  	for _, tc := range difftest.TestCases {
   117  		t.Run(tc.Name, func(t *testing.T) {
   118  			unified, err := diff.ToUnified(difftest.FileA, difftest.FileB, tc.In, tc.Edits)
   119  			if err != nil {
   120  				t.Fatal(err)
   121  			}
   122  			if unified == "" {
   123  				return
   124  			}
   125  			orig := filepath.Join(t.TempDir(), "original")
   126  			err = os.WriteFile(orig, []byte(tc.In), 0644)
   127  			if err != nil {
   128  				t.Fatal(err)
   129  			}
   130  			temp := filepath.Join(t.TempDir(), "patched")
   131  			err = os.WriteFile(temp, []byte(tc.In), 0644)
   132  			if err != nil {
   133  				t.Fatal(err)
   134  			}
   135  			cmd := exec.Command("patch", "-p0", "-u", "-s", "-o", temp, orig)
   136  			cmd.Stdin = strings.NewReader(unified)
   137  			cmd.Stdout = new(bytes.Buffer)
   138  			cmd.Stderr = new(bytes.Buffer)
   139  			if err = cmd.Run(); err != nil {
   140  				t.Fatalf("%v: %q (%q) (%q)", err, cmd.String(),
   141  					cmd.Stderr, cmd.Stdout)
   142  			}
   143  			got, err := os.ReadFile(temp)
   144  			if err != nil {
   145  				t.Fatal(err)
   146  			}
   147  			if string(got) != tc.Out {
   148  				t.Errorf("applying unified failed: got\n%q, wanted\n%q unified\n%q",
   149  					got, tc.Out, unified)
   150  			}
   151  
   152  		})
   153  	}
   154  }
   155  
   156  func TestRegressionOld001(t *testing.T) {
   157  	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"
   158  
   159  	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"
   160  	diffs := diff.Strings(a, b)
   161  	got, err := diff.Apply(a, diffs)
   162  	if err != nil {
   163  		t.Fatalf("Apply failed: %v", err)
   164  	}
   165  	if got != b {
   166  		i := 0
   167  		for ; i < len(a) && i < len(b) && got[i] == b[i]; i++ {
   168  		}
   169  		t.Errorf("oops %vd\n%q\n%q", diffs, got, b)
   170  		t.Errorf("\n%q\n%q", got[i:], b[i:])
   171  	}
   172  }
   173  
   174  func TestRegressionOld002(t *testing.T) {
   175  	a := "n\"\n)\n"
   176  	b := "n\"\n\t\"golang.org/x//nnal/stack\"\n)\n"
   177  	diffs := diff.Strings(a, b)
   178  	got, err := diff.Apply(a, diffs)
   179  	if err != nil {
   180  		t.Fatalf("Apply failed: %v", err)
   181  	}
   182  	if got != b {
   183  		i := 0
   184  		for ; i < len(a) && i < len(b) && got[i] == b[i]; i++ {
   185  		}
   186  		t.Errorf("oops %vd\n%q\n%q", diffs, got, b)
   187  		t.Errorf("\n%q\n%q", got[i:], b[i:])
   188  	}
   189  }
   190  
   191  // return a random string of length n made of characters from s
   192  func randstr(s string, n int) string {
   193  	src := []rune(s)
   194  	x := make([]rune, n)
   195  	for i := 0; i < n; i++ {
   196  		x[i] = src[rand.Intn(len(src))]
   197  	}
   198  	return string(x)
   199  }