fork of go-gitdiff with jj support
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}