master
  1package gitdiff
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"os"
  7	"path/filepath"
  8	"slices"
  9	"testing"
 10)
 11
 12func TestFormatRoundtrip(t *testing.T) {
 13	patches := []struct {
 14		File            string
 15		SkipTextCompare bool
 16	}{
 17		{File: "copy.patch"},
 18		{File: "copy_modify.patch"},
 19		{File: "delete.patch"},
 20		{File: "mode.patch"},
 21		{File: "mode_modify.patch"},
 22		{File: "modify.patch"},
 23		{File: "new.patch"},
 24		{File: "new_empty.patch"},
 25		{File: "new_mode.patch"},
 26		{File: "rename.patch"},
 27		{File: "rename_modify.patch"},
 28
 29		// Due to differences between Go's 'encoding/zlib' package and the zlib
 30		// C library, binary patches cannot be compared directly as the patch
 31		// data is slightly different when re-encoded by Go.
 32		{File: "binary_modify.patch", SkipTextCompare: true},
 33		{File: "binary_new.patch", SkipTextCompare: true},
 34		{File: "binary_modify_nodata.patch"},
 35	}
 36
 37	for _, patch := range patches {
 38		t.Run(patch.File, func(t *testing.T) {
 39			b, err := os.ReadFile(filepath.Join("testdata", "string", patch.File))
 40			if err != nil {
 41				t.Fatalf("failed to read patch: %v", err)
 42			}
 43
 44			original := assertParseSingleFile(t, b, "patch")
 45			str := original.String()
 46
 47			if !patch.SkipTextCompare {
 48				if string(b) != str {
 49					t.Errorf("incorrect patch text\nexpected: %q\n  actual: %q\n", string(b), str)
 50				}
 51			}
 52
 53			reparsed := assertParseSingleFile(t, []byte(str), "formatted patch")
 54			assertFilesEqual(t, original, reparsed)
 55		})
 56	}
 57}
 58
 59func assertParseSingleFile(t *testing.T, b []byte, kind string) *File {
 60	files, _, err := Parse(bytes.NewReader(b))
 61	if err != nil {
 62		t.Fatalf("failed to parse %s: %v", kind, err)
 63	}
 64	if len(files) != 1 {
 65		t.Fatalf("expected %s to contain a single files, but found %d", kind, len(files))
 66	}
 67	return files[0]
 68}
 69
 70func assertFilesEqual(t *testing.T, expected, actual *File) {
 71	assertEqual(t, expected.OldName, actual.OldName, "OldName")
 72	assertEqual(t, expected.NewName, actual.NewName, "NewName")
 73
 74	assertEqual(t, expected.IsNew, actual.IsNew, "IsNew")
 75	assertEqual(t, expected.IsDelete, actual.IsDelete, "IsDelete")
 76	assertEqual(t, expected.IsCopy, actual.IsCopy, "IsCopy")
 77	assertEqual(t, expected.IsRename, actual.IsRename, "IsRename")
 78
 79	assertEqual(t, expected.OldMode, actual.OldMode, "OldMode")
 80	assertEqual(t, expected.NewMode, actual.NewMode, "NewMode")
 81
 82	assertEqual(t, expected.OldOIDPrefix, actual.OldOIDPrefix, "OldOIDPrefix")
 83	assertEqual(t, expected.NewOIDPrefix, actual.NewOIDPrefix, "NewOIDPrefix")
 84	assertEqual(t, expected.Score, actual.Score, "Score")
 85
 86	if len(expected.TextFragments) == len(actual.TextFragments) {
 87		for i := range expected.TextFragments {
 88			prefix := fmt.Sprintf("TextFragments[%d].", i)
 89			ef := expected.TextFragments[i]
 90			af := actual.TextFragments[i]
 91
 92			assertEqual(t, ef.Comment, af.Comment, prefix+"Comment")
 93
 94			assertEqual(t, ef.OldPosition, af.OldPosition, prefix+"OldPosition")
 95			assertEqual(t, ef.OldLines, af.OldLines, prefix+"OldLines")
 96
 97			assertEqual(t, ef.NewPosition, af.NewPosition, prefix+"NewPosition")
 98			assertEqual(t, ef.NewLines, af.NewLines, prefix+"NewLines")
 99
100			assertEqual(t, ef.LinesAdded, af.LinesAdded, prefix+"LinesAdded")
101			assertEqual(t, ef.LinesDeleted, af.LinesDeleted, prefix+"LinesDeleted")
102
103			assertEqual(t, ef.LeadingContext, af.LeadingContext, prefix+"LeadingContext")
104			assertEqual(t, ef.TrailingContext, af.TrailingContext, prefix+"TrailingContext")
105
106			if !slices.Equal(ef.Lines, af.Lines) {
107				t.Errorf("%sLines: expected %#v, actual %#v", prefix, ef.Lines, af.Lines)
108			}
109		}
110	} else {
111		t.Errorf("TextFragments: expected length %d, actual length %d", len(expected.TextFragments), len(actual.TextFragments))
112	}
113
114	assertEqual(t, expected.IsBinary, actual.IsBinary, "IsBinary")
115
116	if expected.BinaryFragment != nil {
117		if actual.BinaryFragment == nil {
118			t.Errorf("BinaryFragment: expected non-nil, actual is nil")
119		} else {
120			ef := expected.BinaryFragment
121			af := expected.BinaryFragment
122
123			assertEqual(t, ef.Method, af.Method, "BinaryFragment.Method")
124			assertEqual(t, ef.Size, af.Size, "BinaryFragment.Size")
125
126			if !slices.Equal(ef.Data, af.Data) {
127				t.Errorf("BinaryFragment.Data: expected %#v, actual %#v", ef.Data, af.Data)
128			}
129		}
130	} else if actual.BinaryFragment != nil {
131		t.Errorf("BinaryFragment: expected nil, actual is non-nil")
132	}
133
134	if expected.ReverseBinaryFragment != nil {
135		if actual.ReverseBinaryFragment == nil {
136			t.Errorf("ReverseBinaryFragment: expected non-nil, actual is nil")
137		} else {
138			ef := expected.ReverseBinaryFragment
139			af := expected.ReverseBinaryFragment
140
141			assertEqual(t, ef.Method, af.Method, "ReverseBinaryFragment.Method")
142			assertEqual(t, ef.Size, af.Size, "ReverseBinaryFragment.Size")
143
144			if !slices.Equal(ef.Data, af.Data) {
145				t.Errorf("ReverseBinaryFragment.Data: expected %#v, actual %#v", ef.Data, af.Data)
146			}
147		}
148	} else if actual.ReverseBinaryFragment != nil {
149		t.Errorf("ReverseBinaryFragment: expected nil, actual is non-nil")
150	}
151}
152
153func assertEqual[T comparable](t *testing.T, expected, actual T, name string) {
154	if expected != actual {
155		t.Errorf("%s: expected %#v, actual %#v", name, expected, actual)
156	}
157}