1package buffer
2
3import (
4	"math/rand"
5	"strings"
6	"testing"
7
8	"github.com/stretchr/testify/assert"
9	lua "github.com/yuin/gopher-lua"
10	"github.com/zyedidia/micro/v2/internal/config"
11	ulua "github.com/zyedidia/micro/v2/internal/lua"
12	"github.com/zyedidia/micro/v2/internal/util"
13)
14
15type operation struct {
16	start Loc
17	end   Loc
18	text  []string
19}
20
21func init() {
22	ulua.L = lua.NewState()
23	config.InitGlobalSettings()
24	config.GlobalSettings["backup"] = false
25	config.GlobalSettings["fastdirty"] = true
26}
27
28func check(t *testing.T, before []string, operations []operation, after []string) {
29	assert := assert.New(t)
30
31	b := NewBufferFromString(strings.Join(before, "\n"), "", BTDefault)
32
33	assert.NotEqual("", b.GetName())
34	assert.Equal(false, b.ExternallyModified())
35	assert.Equal(false, b.Modified())
36	assert.Equal(1, b.NumCursors())
37
38	checkText := func(lines []string) {
39		assert.Equal([]byte(strings.Join(lines, "\n")), b.Bytes())
40		assert.Equal(len(lines), b.LinesNum())
41		for i, s := range lines {
42			assert.Equal(s, b.Line(i))
43			assert.Equal([]byte(s), b.LineBytes(i))
44		}
45	}
46
47	checkText(before)
48
49	var cursors []*Cursor
50
51	for _, op := range operations {
52		cursor := NewCursor(b, op.start)
53		cursor.SetSelectionStart(op.start)
54		cursor.SetSelectionEnd(op.end)
55		b.AddCursor(cursor)
56		cursors = append(cursors, cursor)
57	}
58
59	assert.Equal(1+len(operations), b.NumCursors())
60
61	for i, op := range operations {
62		cursor := cursors[i]
63		b.SetCurCursor(cursor.Num)
64		cursor.DeleteSelection()
65		b.Insert(cursor.Loc, strings.Join(op.text, "\n"))
66	}
67
68	checkText(after)
69
70	// must have exactly two events per operation (delete and insert)
71	for range operations {
72		b.UndoOneEvent()
73		b.UndoOneEvent()
74	}
75
76	checkText(before)
77
78	for i, op := range operations {
79		cursor := cursors[i]
80		if op.start == op.end {
81			assert.Equal(op.start, cursor.Loc)
82		} else {
83			assert.Equal(op.start, cursor.CurSelection[0])
84			assert.Equal(op.end, cursor.CurSelection[1])
85		}
86	}
87
88	for range operations {
89		b.RedoOneEvent()
90		b.RedoOneEvent()
91	}
92
93	checkText(after)
94
95	b.Close()
96}
97
98const maxLineLength = 200
99
100var alphabet = []rune(" abcdeäم��")
101
102func randomString(length int) string {
103	runes := make([]rune, length)
104	for i := range runes {
105		runes[i] = alphabet[rand.Intn(len(alphabet))]
106	}
107	return string(runes)
108}
109
110func randomText(nLines int) string {
111	lines := make([]string, nLines)
112	for i := range lines {
113		lines[i] = randomString(rand.Intn(maxLineLength + 1))
114	}
115	return strings.Join(lines, "\n")
116}
117
118func benchCreateAndClose(testingB *testing.B, nLines int) {
119	rand.Seed(int64(nLines))
120
121	text := randomText(nLines)
122
123	testingB.ResetTimer()
124
125	for i := 0; i < testingB.N; i++ {
126		b := NewBufferFromString(text, "", BTDefault)
127		b.Close()
128	}
129}
130
131func benchRead(testingB *testing.B, nLines int) {
132	rand.Seed(int64(nLines))
133
134	b := NewBufferFromString(randomText(nLines), "", BTDefault)
135
136	testingB.ResetTimer()
137
138	for i := 0; i < testingB.N; i++ {
139		b.Bytes()
140		for j := 0; j < b.LinesNum(); j++ {
141			b.Line(j)
142			b.LineBytes(j)
143		}
144	}
145
146	testingB.StopTimer()
147
148	b.Close()
149}
150
151func benchEdit(testingB *testing.B, nLines, nCursors int) {
152	rand.Seed(int64(nLines + nCursors))
153
154	b := NewBufferFromString(randomText(nLines), "", BTDefault)
155
156	regionSize := nLines / nCursors
157
158	operations := make([]operation, nCursors)
159	for i := range operations {
160		startLine := (i * regionSize) + rand.Intn(regionSize-5)
161		startColumn := rand.Intn(util.CharacterCountInString(b.Line(startLine)) + 1)
162		endLine := startLine + 1 + rand.Intn(5)
163		endColumn := rand.Intn(util.CharacterCountInString(b.Line(endLine)) + 1)
164
165		operations[i] = operation{
166			start: Loc{startColumn, startLine},
167			end:   Loc{endColumn, endLine},
168			text:  []string{randomText(2 + rand.Intn(4))},
169		}
170	}
171
172	testingB.ResetTimer()
173
174	for i := 0; i < testingB.N; i++ {
175		b.SetCursors([]*Cursor{})
176
177		var cursors []*Cursor
178
179		for _, op := range operations {
180			cursor := NewCursor(b, op.start)
181			cursor.SetSelectionStart(op.start)
182			cursor.SetSelectionEnd(op.end)
183			b.AddCursor(cursor)
184			cursors = append(cursors, cursor)
185		}
186
187		for j, op := range operations {
188			cursor := cursors[j]
189			b.SetCurCursor(cursor.Num)
190			cursor.DeleteSelection()
191			b.Insert(cursor.Loc, op.text[0])
192		}
193
194		for b.UndoStack.Peek() != nil {
195			b.UndoOneEvent()
196		}
197	}
198
199	testingB.StopTimer()
200
201	b.Close()
202}
203
204func BenchmarkCreateAndClose10Lines(b *testing.B) {
205	benchCreateAndClose(b, 10)
206}
207
208func BenchmarkCreateAndClose100Lines(b *testing.B) {
209	benchCreateAndClose(b, 100)
210}
211
212func BenchmarkCreateAndClose1000Lines(b *testing.B) {
213	benchCreateAndClose(b, 1000)
214}
215
216func BenchmarkCreateAndClose10000Lines(b *testing.B) {
217	benchCreateAndClose(b, 10000)
218}
219
220func BenchmarkCreateAndClose100000Lines(b *testing.B) {
221	benchCreateAndClose(b, 100000)
222}
223
224func BenchmarkCreateAndClose1000000Lines(b *testing.B) {
225	benchCreateAndClose(b, 1000000)
226}
227
228func BenchmarkRead10Lines(b *testing.B) {
229	benchRead(b, 10)
230}
231
232func BenchmarkRead100Lines(b *testing.B) {
233	benchRead(b, 100)
234}
235
236func BenchmarkRead1000Lines(b *testing.B) {
237	benchRead(b, 1000)
238}
239
240func BenchmarkRead10000Lines(b *testing.B) {
241	benchRead(b, 10000)
242}
243
244func BenchmarkRead100000Lines(b *testing.B) {
245	benchRead(b, 100000)
246}
247
248func BenchmarkRead1000000Lines(b *testing.B) {
249	benchRead(b, 1000000)
250}
251
252func BenchmarkEdit10Lines1Cursor(b *testing.B) {
253	benchEdit(b, 10, 1)
254}
255
256func BenchmarkEdit100Lines1Cursor(b *testing.B) {
257	benchEdit(b, 100, 1)
258}
259
260func BenchmarkEdit100Lines10Cursors(b *testing.B) {
261	benchEdit(b, 100, 10)
262}
263
264func BenchmarkEdit1000Lines1Cursor(b *testing.B) {
265	benchEdit(b, 1000, 1)
266}
267
268func BenchmarkEdit1000Lines10Cursors(b *testing.B) {
269	benchEdit(b, 1000, 10)
270}
271
272func BenchmarkEdit1000Lines100Cursors(b *testing.B) {
273	benchEdit(b, 1000, 100)
274}
275
276func BenchmarkEdit10000Lines1Cursor(b *testing.B) {
277	benchEdit(b, 10000, 1)
278}
279
280func BenchmarkEdit10000Lines10Cursors(b *testing.B) {
281	benchEdit(b, 10000, 10)
282}
283
284func BenchmarkEdit10000Lines100Cursors(b *testing.B) {
285	benchEdit(b, 10000, 100)
286}
287
288func BenchmarkEdit10000Lines1000Cursors(b *testing.B) {
289	benchEdit(b, 10000, 1000)
290}
291
292func BenchmarkEdit100000Lines1Cursor(b *testing.B) {
293	benchEdit(b, 100000, 1)
294}
295
296func BenchmarkEdit100000Lines10Cursors(b *testing.B) {
297	benchEdit(b, 100000, 10)
298}
299
300func BenchmarkEdit100000Lines100Cursors(b *testing.B) {
301	benchEdit(b, 100000, 100)
302}
303
304func BenchmarkEdit100000Lines1000Cursors(b *testing.B) {
305	benchEdit(b, 100000, 1000)
306}
307
308func BenchmarkEdit1000000Lines1Cursor(b *testing.B) {
309	benchEdit(b, 1000000, 1)
310}
311
312func BenchmarkEdit1000000Lines10Cursors(b *testing.B) {
313	benchEdit(b, 1000000, 10)
314}
315
316func BenchmarkEdit1000000Lines100Cursors(b *testing.B) {
317	benchEdit(b, 1000000, 100)
318}
319
320func BenchmarkEdit1000000Lines1000Cursors(b *testing.B) {
321	benchEdit(b, 1000000, 1000)
322}
323