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