master
  1package gitdiff
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"io"
  7	"math/rand"
  8	"testing"
  9)
 10
 11func TestLineReaderAt(t *testing.T) {
 12	const lineTemplate = "generated test line %d\n"
 13
 14	tests := map[string]struct {
 15		InputLines int
 16		Offset     int64
 17		Count      int
 18		Err        bool
 19		EOF        bool
 20		EOFCount   int
 21	}{
 22		"readLines": {
 23			InputLines: 32,
 24			Offset:     0,
 25			Count:      4,
 26		},
 27		"readLinesOffset": {
 28			InputLines: 32,
 29			Offset:     8,
 30			Count:      4,
 31		},
 32		"readLinesLargeOffset": {
 33			InputLines: 8192,
 34			Offset:     4096,
 35			Count:      64,
 36		},
 37		"readSingleLine": {
 38			InputLines: 4,
 39			Offset:     2,
 40			Count:      1,
 41		},
 42		"readZeroLines": {
 43			InputLines: 4,
 44			Offset:     2,
 45			Count:      0,
 46		},
 47		"readAllLines": {
 48			InputLines: 64,
 49			Offset:     0,
 50			Count:      64,
 51		},
 52		"readThroughEOF": {
 53			InputLines: 16,
 54			Offset:     12,
 55			Count:      8,
 56			EOF:        true,
 57			EOFCount:   4,
 58		},
 59		"emptyInput": {
 60			InputLines: 0,
 61			Offset:     0,
 62			Count:      2,
 63			EOF:        true,
 64			EOFCount:   0,
 65		},
 66		"offsetAfterEOF": {
 67			InputLines: 8,
 68			Offset:     10,
 69			Count:      2,
 70			EOF:        true,
 71			EOFCount:   0,
 72		},
 73		"offsetNegative": {
 74			InputLines: 8,
 75			Offset:     -1,
 76			Count:      2,
 77			Err:        true,
 78		},
 79	}
 80
 81	for name, test := range tests {
 82		t.Run(name, func(t *testing.T) {
 83			var input bytes.Buffer
 84			for i := 0; i < test.InputLines; i++ {
 85				fmt.Fprintf(&input, lineTemplate, i)
 86			}
 87
 88			output := make([][]byte, test.Count)
 89			for i := 0; i < test.Count; i++ {
 90				output[i] = []byte(fmt.Sprintf(lineTemplate, test.Offset+int64(i)))
 91			}
 92
 93			r := &lineReaderAt{r: bytes.NewReader(input.Bytes())}
 94			lines := make([][]byte, test.Count)
 95
 96			n, err := r.ReadLinesAt(lines, test.Offset)
 97			if test.Err {
 98				if err == nil {
 99					t.Fatal("expected error reading lines, but got nil")
100				}
101				return
102			}
103			if err != nil && (!test.EOF || err != io.EOF) {
104				t.Fatalf("unexpected error reading lines: %v", err)
105			}
106
107			count := test.Count
108			if test.EOF {
109				count = test.EOFCount
110			}
111
112			if n != count {
113				t.Fatalf("incorrect number of lines read: expected %d, actual %d", count, n)
114			}
115			for i := 0; i < n; i++ {
116				if !bytes.Equal(output[i], lines[i]) {
117					t.Errorf("incorrect content in line %d:\nexpected: %q\nactual: %q", i, output[i], lines[i])
118				}
119			}
120		})
121	}
122
123	newlineTests := map[string]struct {
124		InputSize int
125	}{
126		"readLinesNoFinalNewline": {
127			InputSize: indexBufferSize + indexBufferSize/2,
128		},
129		"readLinesNoFinalNewlineBufferMultiple": {
130			InputSize: 4 * indexBufferSize,
131		},
132	}
133
134	for name, test := range newlineTests {
135		t.Run(name, func(t *testing.T) {
136			input := bytes.Repeat([]byte("0"), test.InputSize)
137
138			var output [][]byte
139			for i := 0; i < len(input); i++ {
140				last := i
141				i += rand.Intn(80)
142				if i < len(input)-1 { // last character of input must not be a newline
143					input[i] = '\n'
144					output = append(output, input[last:i+1])
145				} else {
146					output = append(output, input[last:])
147				}
148			}
149
150			r := &lineReaderAt{r: bytes.NewReader(input)}
151			lines := make([][]byte, len(output))
152
153			n, err := r.ReadLinesAt(lines, 0)
154			if err != nil {
155				t.Fatalf("unexpected error reading reading lines: %v", err)
156			}
157
158			if n != len(output) {
159				t.Fatalf("incorrect number of lines read: expected %d, actual %d", len(output), n)
160			}
161
162			for i, line := range lines {
163				if !bytes.Equal(output[i], line) {
164					t.Errorf("incorrect content in line %d:\nexpected: %q\nactual: %q", i, output[i], line)
165				}
166			}
167		})
168	}
169}
170
171func TestCopyFrom(t *testing.T) {
172	tests := map[string]struct {
173		Bytes  int64
174		Offset int64
175	}{
176		"copyAll": {
177			Bytes: byteBufferSize / 2,
178		},
179		"copyPartial": {
180			Bytes:  byteBufferSize / 2,
181			Offset: byteBufferSize / 4,
182		},
183		"copyLarge": {
184			Bytes: 8 * byteBufferSize,
185		},
186	}
187
188	for name, test := range tests {
189		t.Run(name, func(t *testing.T) {
190			data := make([]byte, test.Bytes)
191			rand.Read(data)
192
193			var dst bytes.Buffer
194			n, err := copyFrom(&dst, bytes.NewReader(data), test.Offset)
195			if err != nil {
196				t.Fatalf("unexpected error copying data: %v", err)
197			}
198			if n != test.Bytes-test.Offset {
199				t.Fatalf("incorrect number of bytes copied: expected %d, actual %d", test.Bytes-test.Offset, n)
200			}
201
202			expected := data[test.Offset:]
203			if !bytes.Equal(expected, dst.Bytes()) {
204				t.Fatalf("incorrect data copied:\nexpected: %v\nactual: %v", expected, dst.Bytes())
205			}
206		})
207	}
208}
209
210func TestCopyLinesFrom(t *testing.T) {
211	tests := map[string]struct {
212		Lines  int64
213		Offset int64
214	}{
215		"copyAll": {
216			Lines: lineBufferSize / 2,
217		},
218		"copyPartial": {
219			Lines:  lineBufferSize / 2,
220			Offset: lineBufferSize / 4,
221		},
222		"copyLarge": {
223			Lines: 8 * lineBufferSize,
224		},
225	}
226
227	const lineLength = 128
228
229	for name, test := range tests {
230		t.Run(name, func(t *testing.T) {
231			data := make([]byte, test.Lines*lineLength)
232			for i := range data {
233				data[i] = byte(32 + rand.Intn(95)) // ascii letters, numbers, symbols
234				if i%lineLength == lineLength-1 {
235					data[i] = '\n'
236				}
237			}
238
239			var dst bytes.Buffer
240			n, err := copyLinesFrom(&dst, &lineReaderAt{r: bytes.NewReader(data)}, test.Offset)
241			if err != nil {
242				t.Fatalf("unexpected error copying data: %v", err)
243			}
244			if n != test.Lines-test.Offset {
245				t.Fatalf("incorrect number of lines copied: expected %d, actual %d", test.Lines-test.Offset, n)
246			}
247
248			expected := data[test.Offset*lineLength:]
249			if !bytes.Equal(expected, dst.Bytes()) {
250				t.Fatalf("incorrect data copied:\nexpected: %v\nactual: %v", expected, dst.Bytes())
251			}
252		})
253	}
254}