/
osspkg
/
go-bb
Обзор
Документация
Войти
/
osspkg
/
go-bb
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
buffer_test.go
1 265 строк
32 KB
Mikhail Knyazhev
fix pkg name + add README.md
16 фев 2026, 04:45
Верифицирован
16 фев 2026, 04:45
c042f1b
Код
Авторство
О чём код?
/* * Copyright (c) 2026 Mikhail Knyazhev <markus621@yandex.com>. All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ package bb import ( "bytes" "errors" "io" "strings" "testing" "unicode/utf8" ) // TestNew тестирует конструктор New func TestNew(t *testing.T) { tests := []struct { name string size int expected int }{ {"Positive size", 512, 512}, {"Zero size", 0, 1024}, {"Negative size", -100, 1024}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { buf := New(tt.size) if buf == nil { t.Fatal("Buffer is nil") } if cap(buf.buf) != tt.expected { t.Errorf("Expected capacity %d, got %d", tt.expected, cap(buf.buf)) } if buf.pos != 0 { t.Errorf("Expected pos 0, got %d", buf.pos) } }) } } // TestFromBytes тестирует конструктор FromBytes func TestFromBytes(t *testing.T) { t.Run("With existing slice", func(t *testing.T) { data := []byte{1, 2, 3, 4} buf := FromBytes(data) if buf == nil { t.Fatal("Buffer is nil") } if buf.pos != 4 { t.Errorf("Expected pos 0, got %d", buf.pos) } if len(buf.buf) != len(data) { t.Errorf("Expected length %d, got %d", len(data), len(buf.buf)) } }) t.Run("With nil slice", func(t *testing.T) { buf := FromBytes(nil) if buf == nil { t.Fatal("Buffer is nil") } if buf.pos != 0 { t.Errorf("Expected pos 0, got %d", buf.pos) } if len(buf.buf) != 0 { t.Errorf("Expected empty buffer, got length %d", len(buf.buf)) } if cap(buf.buf) != 1024 { t.Errorf("Expected capacity 1024, got %d", cap(buf.buf)) } }) } // TestWrite тестирует метод Write func TestWrite(t *testing.T) { t.Run("Empty write", func(t *testing.T) { buf := New(10) n, err := buf.Write([]byte{}) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 0 { t.Errorf("Expected 0 bytes written, got %d", n) } if buf.Size() != 0 { t.Errorf("Expected size 0, got %d", buf.Size()) } }) t.Run("Simple write", func(t *testing.T) { buf := New(10) data := []byte{1, 2, 3, 4} n, err := buf.Write(data) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != len(data) { t.Errorf("Expected %d bytes written, got %d", len(data), n) } if buf.Size() != len(data) { t.Errorf("Expected size %d, got %d", len(data), buf.Size()) } if buf.pos != len(data) { t.Errorf("Expected pos %d, got %d", len(data), buf.pos) } }) t.Run("Multiple writes", func(t *testing.T) { buf := New(5) n1, _ := buf.Write([]byte{1, 2}) n2, _ := buf.Write([]byte{3, 4, 5}) if n1 != 2 { t.Errorf("Expected first write 2 bytes, got %d", n1) } if n2 != 3 { t.Errorf("Expected second write 3 bytes, got %d", n2) } if buf.Size() != 5 { t.Errorf("Expected total size 5, got %d", buf.Size()) } if !bytes.Equal(buf.buf, []byte{1, 2, 3, 4, 5}) { t.Errorf("Buffer content mismatch") } }) t.Run("Write with buffer expansion", func(t *testing.T) { buf := New(2) // Первый write заполняет буфер buf.Write([]byte{1, 2}) // Второй write требует расширения buf.Write([]byte{3, 4, 5}) if buf.Size() != 5 { t.Errorf("Expected size 5 after expansion, got %d", buf.Size()) } if !bytes.Equal(buf.buf, []byte{1, 2, 3, 4, 5}) { t.Errorf("Buffer content mismatch after expansion") } }) } // TestWriteByte тестирует метод WriteByte func TestWriteByte(t *testing.T) { buf := New(5) tests := []struct { byteVal byte wantErr bool }{ {65, false}, // 'A' {66, false}, // 'B' {67, false}, // 'C' } for i, tt := range tests { err := buf.WriteByte(tt.byteVal) if (err != nil) != tt.wantErr { t.Errorf("Test %d: got error = %v, wantErr = %v", i, err, tt.wantErr) } if buf.pos != i+1 { t.Errorf("Test %d: expected pos %d, got %d", i, i+1, buf.pos) } if buf.buf[i] != tt.byteVal { t.Errorf("Test %d: expected byte %d at position %d, got %d", i, tt.byteVal, i, buf.buf[i]) } } } // TestWriteString тестирует метод WriteString func TestWriteString(t *testing.T) { buf := New(20) t.Run("Write ASCII string", func(t *testing.T) { str := "Hello" n, err := buf.WriteString(str) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != len(str) { t.Errorf("Expected %d bytes written, got %d", len(str), n) } if buf.String() != str { t.Errorf("Expected string %q, got %q", str, buf.String()) } }) t.Run("Write Unicode string", func(t *testing.T) { buf.Reset() str := "Привет мир" n, err := buf.WriteString(str) if err != nil { t.Errorf("Unexpected error: %v", err) } // Проверяем что записано правильное количество байт (не рун!) if n != len(str) { t.Errorf("Expected %d bytes written, got %d", len(str), n) } if buf.String() != str { t.Errorf("Expected string %q, got %q", str, buf.String()) } }) } // TestWriteRune тестирует метод WriteRune func TestWriteRune(t *testing.T) { buf := New(20) tests := []struct { name string r rune want string }{ {"ASCII rune", 'A', "A"}, {"Unicode rune", '€', "€"}, {"Chinese character", '世', "世"}, {"Emoji", '🚀', "🚀"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { startSize := buf.Size() n, err := buf.WriteRune(tt.r) if err != nil { t.Errorf("Unexpected error: %v", err) } // n должно быть равно количеству байт, записанных для руны if n != len(tt.want) { t.Errorf("Expected %d bytes written, got %d", len(tt.want), n) } if buf.Size() != startSize+n { t.Errorf("Expected size %d, got %d", startSize+n, buf.Size()) } // Проверяем что последние n байт содержат корректную руну written := buf.buf[buf.Size()-n:] r, size := utf8.DecodeRune(written) if r != tt.r || size != n { t.Errorf("Written rune mismatch: expected %U (%d bytes), got %U (%d bytes)", tt.r, n, r, size) } }) } } // TestRead тестирует метод Read func TestRead(t *testing.T) { buf := New(10) data := []byte{1, 2, 3, 4, 5} buf.Write(data) buf.Seek(0, SeekStart) // Возвращаемся к началу t.Run("Read all", func(t *testing.T) { dest := make([]byte, 10) n, err := buf.Read(dest) if err != nil && !errors.Is(err, io.EOF) { t.Errorf("Unexpected error: %v", err) } if n != len(data) { t.Errorf("Expected %d bytes read, got %d", len(data), n) } if !bytes.Equal(dest[:n], data) { t.Errorf("Read data mismatch") } }) t.Run("Read in chunks", func(t *testing.T) { buf.Seek(0, SeekStart) chunk1 := make([]byte, 2) n1, err1 := buf.Read(chunk1) if err1 != nil { t.Errorf("Unexpected error on first read: %v", err1) } if n1 != 2 { t.Errorf("Expected 2 bytes read, got %d", n1) } chunk2 := make([]byte, 3) n2, err2 := buf.Read(chunk2) if err2 != nil { t.Errorf("Unexpected error on second read: %v", err2) } if n2 != 3 { t.Errorf("Expected 3 bytes read, got %d", n2) } if !bytes.Equal(append(chunk1[:n1], chunk2[:n2]...), data) { t.Errorf("Chunked read data mismatch") } }) t.Run("Read empty buffer", func(t *testing.T) { buf.Reset() dest := make([]byte, 5) _, err := buf.Read(dest) if !errors.Is(err, io.EOF) { t.Errorf("Expected EOF error, got %v", err) } }) t.Run("Read with zero buffer", func(t *testing.T) { buf.Seek(0, SeekStart) _, err := buf.Read([]byte{}) if err == nil { t.Error("Expected error for zero buffer") } }) } // TestReadByte тестирует метод ReadByte func TestReadByte(t *testing.T) { buf := New(10) buf.Write([]byte{65, 66, 67}) // A, B, C t.Run("Read sequential bytes", func(t *testing.T) { buf.Seek(0, SeekStart) b1, err1 := buf.ReadByte() if err1 != nil { t.Errorf("Unexpected error: %v", err1) } if b1 != 65 { t.Errorf("Expected byte 65, got %d", b1) } b2, err2 := buf.ReadByte() if err2 != nil { t.Errorf("Unexpected error: %v", err2) } if b2 != 66 { t.Errorf("Expected byte 66, got %d", b2) } b3, err3 := buf.ReadByte() if err3 != nil { t.Errorf("Unexpected error: %v", err3) } if b3 != 67 { t.Errorf("Expected byte 67, got %d", b3) } }) t.Run("Read at EOF", func(t *testing.T) { _, err := buf.ReadByte() if !errors.Is(err, io.EOF) { t.Errorf("Expected EOF error, got %v", err) } }) } // TestUnreadByte тестирует метод UnreadByte func TestUnreadByte(t *testing.T) { buf := New(10) buf.Write([]byte{1, 2, 3}) buf.Seek(0, SeekStart) b, _ := buf.ReadByte() if b != 1 { t.Errorf("Expected byte 1, got %d", b) } if buf.pos != 1 { t.Errorf("Expected pos 1 after read, got %d", buf.pos) } err := buf.UnreadByte() if err != nil { t.Errorf("Unexpected error: %v", err) } if buf.pos != 0 { t.Errorf("Expected pos 0 after unread, got %d", buf.pos) } // Читаем снова - должен получить тот же байт b2, _ := buf.ReadByte() if b2 != 1 { t.Errorf("Expected byte 1 after unread, got %d", b2) } t.Run("Unread at beginning", func(t *testing.T) { buf.Seek(0, SeekStart) err := buf.UnreadByte() if err == nil { t.Error("Expected error when unreading at beginning") } }) } // TestReadRune тестирует метод ReadRune func TestReadRune(t *testing.T) { buf := New(20) testString := "A€世🚀" buf.WriteString(testString) buf.Seek(0, SeekStart) expectedRunes := []rune{'A', '€', '世', '🚀'} expectedSizes := []int{1, 3, 3, 4} for i, expectedRune := range expectedRunes { r, size, err := buf.ReadRune() if err != nil { t.Errorf("Unexpected error reading rune %d: %v", i, err) } if r != expectedRune { t.Errorf("Rune %d: expected %U, got %U", i, expectedRune, r) } if size != expectedSizes[i] { t.Errorf("Rune %d: expected size %d, got %d", i, expectedSizes[i], size) } } // Чтение при EOF _, _, err := buf.ReadRune() if !errors.Is(err, io.EOF) { t.Errorf("Expected EOF, got %v", err) } } // TestUnreadRune тестирует метод UnreadRune func TestUnreadRune(t *testing.T) { buf := New(20) buf.WriteString("€世") // 3 + 3 байта buf.Seek(0, SeekStart) r1, size1, _ := buf.ReadRune() if r1 != '€' { t.Errorf("Expected rune €, got %U", r1) } if size1 != 3 { t.Errorf("Expected size 3, got %d", size1) } err := buf.UnreadRune() if err != nil { t.Errorf("Unexpected error: %v", err) } if buf.pos != 0 { t.Errorf("Expected pos 0 after unread, got %d", buf.pos) } // Читаем снова r2, size2, _ := buf.ReadRune() if r2 != '€' { t.Errorf("Expected rune € after unread, got %U", r2) } if size2 != 3 { t.Errorf("Expected size 3 after unread, got %d", size2) } } // TestSeek тестирует метод Seek func TestSeek(t *testing.T) { buf := New(10) buf.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) tests := []struct { name string offset int64 whence int expected int wantErr bool }{ {"SeekStart valid", 3, SeekStart, 3, false}, {"SeekStart negative", -1, SeekStart, 0, false}, // корректируется до 0 {"SeekStart beyond end", 15, SeekStart, 10, false}, // корректируется до размера {"SeekCurr forward", 2, SeekCurr, 10, false}, // после предыдущего seek позиция 3 {"SeekCurr backward", -2, SeekCurr, 8, false}, {"SeekEnd from end", -3, SeekEnd, 7, false}, // размер 10, 10-3=7 {"SeekEnd beyond", 5, SeekEnd, 10, false}, // корректируется {"Invalid whence", 0, 99, 0, true}, } // Первый тест устанавливает позицию в 3 for i, tt := range tests { t.Run(tt.name, func(t *testing.T) { pos, err := buf.Seek(tt.offset, tt.whence) if (err != nil) != tt.wantErr { t.Errorf("Test %d (%s): got error = %v, wantErr = %v", i, tt.name, err, tt.wantErr) } if !tt.wantErr { if int(pos) != tt.expected { t.Errorf("Test %d (%s): expected pos %d, got %d", i, tt.name, tt.expected, pos) } if buf.pos != tt.expected { t.Errorf("Test %d (%s): expected buffer.pos %d, got %d", i, tt.name, tt.expected, buf.pos) } } }) } } // TestReset тестирует метод Reset func TestReset(t *testing.T) { t.Run("Reset normal buffer", func(t *testing.T) { buf := New(100) buf.WriteString("Hello World") buf.Seek(5, SeekStart) buf.Reset() if buf.Size() != 0 { t.Errorf("Expected size 0 after reset, got %d", buf.Size()) } if buf.pos != 0 { t.Errorf("Expected pos 0 after reset, got %d", buf.pos) } if len(buf.buf) != 0 { t.Errorf("Expected empty buffer, got length %d", len(buf.buf)) } }) t.Run("Reset large buffer", func(t *testing.T) { buf := New(maxSize * 2) // Заполняем буфер data := make([]byte, maxSize*2) for i := range data { data[i] = byte(i % 256) } buf.Write(data) oldCap := cap(buf.buf) buf.Reset() // После сброса большой буфер должен урезаться до maxSize if cap(buf.buf) > maxSize { t.Errorf("Expected capacity <= %d after reset, got %d", maxSize, cap(buf.buf)) } if len(buf.buf) != 0 { t.Errorf("Expected length 0 after reset, got %d", len(buf.buf)) } // Проверяем что capacity уменьшилось if oldCap <= maxSize && cap(buf.buf) != oldCap { t.Errorf("Unexpected capacity change: was %d, now %d", oldCap, cap(buf.buf)) } }) } // TestTruncate тестирует метод Truncate func TestTruncate(t *testing.T) { buf := New(20) buf.WriteString("Hello World") tests := []struct { name string n int expected string }{ {"Truncate 0", 0, "Hello World"}, // не должно измениться {"Truncate negative", -5, "Hello World"}, // не должно измениться {"Truncate 3", 3, "Hello Wo"}, // удаляем "rld" {"Truncate more than length", 20, ""}, // очищаем полностью {"Truncate UTF-8 boundary", 1, "Hello Worl"}, // удаляем 'd' } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Восстанавливаем исходное состояние buf.Reset() buf.WriteString("Hello World") buf.Seek(7, SeekStart) // Устанавливаем позицию внутри buf.Truncate(tt.n) if buf.String() != tt.expected { t.Errorf("Truncate(%d): expected %q, got %q", tt.n, tt.expected, buf.String()) } }) } t.Run("Truncate preserves position", func(t *testing.T) { buf.Reset() buf.WriteString("Hello World") buf.Truncate(3) // удаляем "rld" // Позиция должна скорректироваться до конца буфера (8) if buf.pos != 8 { t.Errorf("Expected pos 8 after truncate, got %d", buf.pos) } // Другой случай: позиция за пределами нового размера buf.Reset() buf.WriteString("Hello World") buf.Truncate(5) // оставляем "Hello" // Позиция должна скорректироваться до конца буфера (5) if buf.pos != 6 { t.Errorf("Expected pos 6 after truncate from out of bounds, got %d", buf.pos) } }) } // TestReadBytes тестирует метод ReadBytes func TestReadBytes(t *testing.T) { buf := New(20) buf.WriteString("Hello,World,Test") buf.Seek(0, SeekStart) t.Run("Read until delimiter", func(t *testing.T) { data, err := buf.ReadBytes(',') if err != nil { t.Errorf("Unexpected error: %v", err) } if string(data) != "Hello," { t.Errorf("Expected 'Hello,', got %q", string(data)) } if buf.pos != 6 { // "Hello," = 6 байт t.Errorf("Expected pos 6, got %d", buf.pos) } }) t.Run("Read until end if no delimiter", func(t *testing.T) { // Сейчас в буфере осталось "World,Test" data, err := buf.ReadBytes(';') // разделителя нет if err != nil { t.Errorf("Unexpected error: %v", err) } if string(data) != "World,Test" { t.Errorf("Expected 'World,Test', got %q", string(data)) } if buf.pos != buf.Size() { t.Errorf("Expected pos at end (%d), got %d", buf.Size(), buf.pos) } }) t.Run("Read from empty buffer", func(t *testing.T) { buf.Reset() _, err := buf.ReadBytes(',') if !errors.Is(err, io.EOF) { t.Errorf("Expected EOF, got %v", err) } }) } // TestReadString тестирует метод ReadString func TestReadString(t *testing.T) { buf := New(20) buf.WriteString("Line1\nLine2\nLine3") buf.Seek(0, SeekStart) line1, err1 := buf.ReadString('\n') if err1 != nil { t.Errorf("Unexpected error: %v", err1) } if line1 != "Line1\n" { t.Errorf("Expected 'Line1\\n', got %q", line1) } line2, err2 := buf.ReadString('\n') if err2 != nil { t.Errorf("Unexpected error: %v", err2) } if line2 != "Line2\n" { t.Errorf("Expected 'Line2\\n', got %q", line2) } line3, err3 := buf.ReadString('\n') // разделителя нет if err3 != nil { t.Errorf("Unexpected error: %v", err3) } if line3 != "Line3" { t.Errorf("Expected 'Line3', got %q", line3) } } // TestNextField тестирует метод NextField func TestNextField(t *testing.T) { buf := New(30) buf.WriteString("field1,field2;field3") t.Run("Accurate separator", func(t *testing.T) { buf.Seek(0, SeekStart) field, err := buf.NextField(",") if err != nil { t.Errorf("Unexpected error: %v", err) } if string(field) != "field1" { t.Errorf("Expected field 'field1', got %q", string(field)) } if buf.pos != 7 { // "field1," = 7 байт t.Errorf("Expected pos 7, got %d", buf.pos) } }) t.Run("Any separator", func(t *testing.T) { buf.Seek(0, SeekStart) // Теперь в буфере "field1,field2;field3" field, err := buf.NextField(",;") if err != nil { t.Errorf("Unexpected error: %v", err) } if string(field) != "field1,field2;field3" { t.Errorf("Expected field 'field2', got %q", string(field)) } }) } // TestWriteTo тестирует метод WriteTo func TestWriteTo(t *testing.T) { buf := New(20) buf.WriteString("Test data for WriteTo") buf.Seek(5, SeekStart) // Начинаем не с начала var dest bytes.Buffer n, err := buf.WriteTo(&dest) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 16 { t.Errorf("Expected to write 16 bytes to buffer, got %d", n) } // Должно быть записано Len() байт (размер от текущей позиции) expected := int64(buf.Len()) if expected != 0 { t.Errorf("Expected %d bytes written, got %d", 0, expected) } if dest.String() != "data for WriteTo" { t.Errorf("Expected 'data for WriteTo', got %q", dest.String()) } t.Run("Write empty buffer", func(t *testing.T) { buf.Reset() var dest2 bytes.Buffer n, err := buf.WriteTo(&dest2) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 0 { t.Errorf("Expected 0 bytes written, got %d", n) } if dest2.Len() != 0 { t.Errorf("Expected empty destination, got %d bytes", dest2.Len()) } }) } // TestReadFrom тестирует метод ReadFrom func TestReadFrom(t *testing.T) { src := strings.NewReader("Data from reader") buf := New(10) n, err := buf.ReadFrom(src) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != int64(len("Data from reader")) { t.Errorf("Expected %d bytes read, got %d", len("Data from reader"), n) } if buf.String() != "Data from reader" { t.Errorf("Expected 'Data from reader', got %q", buf.String()) } if buf.pos != 0 { // ReadFrom не должен двигать позицию t.Errorf("Expected pos 0, got %d", buf.pos) } t.Run("ReadFrom with error", func(t *testing.T) { // Создаем reader, который вернет ошибку errorReader := &errorReader{err: errors.New("test error")} buf.Reset() _, err := buf.ReadFrom(errorReader) if err == nil { t.Error("Expected error from ReadFrom") } }) } // TestWriteAt тестирует метод WriteAt func TestWriteAt(t *testing.T) { buf := New(10) buf.WriteString("0123456789") // 10 байт t.Run("WriteAt middle", func(t *testing.T) { n, err := buf.WriteAt([]byte("abc"), 3) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 3 { t.Errorf("Expected 3 bytes written, got %d", n) } if buf.String() != "012abc6789" { t.Errorf("Expected '012abc6789', got %q", buf.String()) } }) t.Run("WriteAt with expansion", func(t *testing.T) { n, err := buf.WriteAt([]byte("xyz"), 15) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 3 { t.Errorf("Expected 3 bytes written, got %d", n) } // Буфер должен был расшириться if buf.Size() != 18 { // было 10, позиция 15 + 3 = 18 t.Errorf("Expected size 18, got %d", buf.Size()) } // Проверяем что байты записались в правильную позицию expected := "012abc6789\000\000\000\000\000xyz" if len(expected) != 18 { t.Errorf("Test setup error: expected length %d", len(expected)) } if buf.String() != expected { t.Errorf("Expected %q, got %q", expected, buf.String()) } }) t.Run("WriteAt negative offset", func(t *testing.T) { buf.Reset() buf.WriteString("test") n, err := buf.WriteAt([]byte("X"), -5) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 1 { t.Errorf("Expected 1 byte written, got %d", n) } // С отрицательным offset должно записаться в начало if buf.String() != "Xest" { t.Errorf("Expected 'Xest', got %q", buf.String()) } }) } // TestReadAt тестирует метод ReadAt func TestReadAt(t *testing.T) { buf := New(10) buf.WriteString("0123456789") t.Run("ReadAt valid position", func(t *testing.T) { dest := make([]byte, 3) n, err := buf.ReadAt(dest, 3) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 3 { t.Errorf("Expected 3 bytes read, got %d", n) } if string(dest) != "345" { t.Errorf("Expected '345', got %q", string(dest)) } // ReadAt не должен двигать pos if buf.pos != buf.Size() { // pos остался в конце после Write t.Errorf("ReadAt should not change pos, got %d", buf.pos) } }) t.Run("ReadAt near end", func(t *testing.T) { dest := make([]byte, 5) n, err := buf.ReadAt(dest, 8) if err != nil && !errors.Is(err, io.EOF) { t.Errorf("Unexpected error: %v", err) } if n != 2 { // только "89" доступно t.Errorf("Expected 2 bytes read, got %d", n) } if string(dest[:n]) != "89" { t.Errorf("Expected '89', got %q", string(dest[:n])) } }) t.Run("ReadAt out of bounds", func(t *testing.T) { dest := make([]byte, 3) _, err := buf.ReadAt(dest, 15) if !errors.Is(err, io.EOF) { t.Errorf("Expected EOF, got %v", err) } }) t.Run("ReadAt negative offset", func(t *testing.T) { dest := make([]byte, 3) _, err := buf.ReadAt(dest, -5) if !errors.Is(err, io.EOF) { t.Errorf("Expected EOF, got %v", err) } }) } // TestDiscardAndResume тестирует методы Discard и Resume func TestDiscardAndResume(t *testing.T) { buf := New(10) buf.WriteString("0123456789") buf.Seek(0, SeekStart) t.Run("Discard", func(t *testing.T) { discarded := buf.Discard(3) if discarded != 3 { t.Errorf("Expected 3 bytes discarded, got %d", discarded) } if buf.pos != 3 { t.Errorf("Expected pos 3, got %d", buf.pos) } // Читаем следующий байт b, _ := buf.ReadByte() if b != '3' { t.Errorf("Expected byte '3', got %c", b) } }) t.Run("Discard more than available", func(t *testing.T) { buf.Seek(8, SeekStart) // позиция на '8' discarded := buf.Discard(5) // доступно только '8', '9' if discarded != 2 { t.Errorf("Expected 2 bytes discarded, got %d", discarded) } if buf.pos != 10 { // в конце буфера t.Errorf("Expected pos 10, got %d", buf.pos) } }) t.Run("Resume", func(t *testing.T) { buf.Seek(5, SeekStart) buf.Discard(3) // теперь pos = 8 resumed := buf.Resume(2) if resumed != 2 { t.Errorf("Expected 2 bytes resumed, got %d", resumed) } if buf.pos != 6 { t.Errorf("Expected pos 6, got %d", buf.pos) } // Проверяем что можем прочитать с новой позиции b, _ := buf.ReadByte() if b != '6' { t.Errorf("Expected byte '6', got %c", b) } }) t.Run("Resume beyond start", func(t *testing.T) { buf.Seek(2, SeekStart) resumed := buf.Resume(5) // пытаемся отойти назад на 5, но доступно только 2 if resumed != 2 { t.Errorf("Expected 2 bytes resumed, got %d", resumed) } if buf.pos != 0 { t.Errorf("Expected pos 0, got %d", buf.pos) } }) } // TestNext тестирует метод Next func TestNext(t *testing.T) { buf := New(10) buf.WriteString("0123456789") buf.Seek(0, SeekStart) t.Run("Next valid", func(t *testing.T) { data := buf.Next(3) if string(data) != "012" { t.Errorf("Expected '012', got %q", string(data)) } if buf.pos != 3 { t.Errorf("Expected pos 3, got %d", buf.pos) } }) t.Run("Next more than available", func(t *testing.T) { data := buf.Next(10) // доступно только 7 байт if string(data) != "3456789" { t.Errorf("Expected '3456789', got %q", string(data)) } if buf.pos != 10 { // в конце t.Errorf("Expected pos 10, got %d", buf.pos) } }) t.Run("Next on empty buffer", func(t *testing.T) { buf.Reset() data := buf.Next(5) if data != nil { t.Errorf("Expected nil, got %q", string(data)) } }) t.Run("Next zero or negative", func(t *testing.T) { buf.WriteString("test") buf.Seek(0, SeekStart) data1 := buf.Next(0) if data1 != nil { t.Errorf("Expected nil for n=0, got %q", string(data1)) } data2 := buf.Next(-5) if data2 != nil { t.Errorf("Expected nil for negative n, got %q", string(data2)) } }) } // TestBytesAndString тестирует методы Bytes и String func TestBytesAndString(t *testing.T) { buf := New(10) data := "Hello 世界" buf.WriteString(data) // Bytes должен возвращать все содержимое буфера bytesResult := buf.Bytes() if string(bytesResult) != data { t.Errorf("Bytes(): expected %q, got %q", data, string(bytesResult)) } // String должен возвращать то же самое stringResult := buf.String() if stringResult != data { t.Errorf("String(): expected %q, got %q", data, stringResult) } // Изменение возвращенного среза должно влиять на буфер if len(bytesResult) > 0 { bytesResult[0] = 'X' if buf.buf[0] != 'X' { t.Error("Modifying returned slice modified buffer") } } } // TestLenAndSize тестирует методы Len и Size func TestLenAndSize(t *testing.T) { buf := New(10) buf.WriteString("0123456789") // Size = 10 if buf.Size() != 10 { t.Errorf("Size(): expected 10, got %d", buf.Size()) } buf.Seek(3, SeekStart) // Len = сколько байт осталось для чтения if buf.Len() != 7 { // 10 - 3 = 7 t.Errorf("Len(): expected 7, got %d", buf.Len()) } buf.Seek(10, SeekStart) if buf.Len() != 0 { t.Errorf("Len() at end: expected 0, got %d", buf.Len()) } } // TestIndex тестирует метод Index func TestIndex(t *testing.T) { buf := New(10) buf.WriteString("test") if buf.Index() != 4 { // после записи pos в конце t.Errorf("Initial Index(): expected 0, got %d", buf.Index()) } buf.Seek(2, SeekStart) if buf.Index() != 2 { t.Errorf("After Seek: expected Index()=2, got %d", buf.Index()) } buf.ReadByte() if buf.Index() != 3 { t.Errorf("After ReadByte: expected Index()=3, got %d", buf.Index()) } } // TestReadFromNAndWriteToN тестирует методы с ограничением размера пакета func TestReadFromNAndWriteToN(t *testing.T) { t.Run("ReadFromN with small packets", func(t *testing.T) { src := strings.NewReader("1234567890") buf := New(5) n, err := buf.ReadFromN(src, 3) // читаем пакетами по 3 байта if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 10 { t.Errorf("Expected 10 bytes read, got %d", n) } if buf.String() != "1234567890" { t.Errorf("Expected '1234567890', got %q", buf.String()) } }) t.Run("WriteToN with small packets", func(t *testing.T) { buf := New(10) buf.WriteString("ABCDEFGHIJ") buf.Seek(2, SeekStart) var dest bytes.Buffer n, err := buf.WriteToN(&dest, 3) // пишем пакетами по 3 байта if err != nil { t.Errorf("Unexpected error: %v", err) } if n != 8 { // от позиции 2 до конца = 8 байт t.Errorf("Expected 8 bytes written, got %d", n) } if dest.String() != "CDEFGHIJ" { t.Errorf("Expected 'CDEFGHIJ', got %q", dest.String()) } }) t.Run("WriteToN with writer error", func(t *testing.T) { buf := New(10) buf.WriteString("test") buf.Seek(0, SeekStart) errorWriter := &errorWriter{maxWrites: 2, failAfter: 1} _, err := buf.WriteToN(errorWriter, 1) // пишем по 1 байту if err == nil { t.Error("Expected error from writer") } }) } // Вспомогательные структуры для тестирования ошибок type errorReader struct { err error } func (r *errorReader) Read(p []byte) (int, error) { return 0, r.err } type errorWriter struct { maxWrites int written int failAfter int } func (w *errorWriter) Write(p []byte) (int, error) { if w.written >= w.failAfter { return 0, errors.New("write error") } n := len(p) if w.written+n > w.maxWrites { n = w.maxWrites - w.written } w.written += n return n, nil } // TestEdgeCases тестирует пограничные случаи func TestEdgeCases(t *testing.T) { t.Run("Very large writes", func(t *testing.T) { buf := New(1) largeData := make([]byte, 10000) for i := range largeData { largeData[i] = byte(i % 256) } n, err := buf.Write(largeData) if err != nil { t.Errorf("Unexpected error: %v", err) } if n != len(largeData) { t.Errorf("Expected %d bytes written, got %d", len(largeData), n) } if buf.Size() != len(largeData) { t.Errorf("Expected size %d, got %d", len(largeData), buf.Size()) } }) t.Run("UnreadRune complex UTF-8", func(t *testing.T) { buf := New(20) // Смешанные многобайтовые руны testRunes := []rune{'A', '€', 'B', '世', 'C', '🚀'} for _, r := range testRunes { buf.WriteRune(r) } buf.Seek(0, SeekStart) // Читаем и откатываем все руны for i := 0; i < len(testRunes); i++ { r1, n1, _ := buf.ReadRune() if r1 != testRunes[i] { t.Errorf("Rune %d: expected %U, got %U", i, testRunes[i], r1) } err := buf.UnreadRune() if err != nil { t.Errorf("UnreadRune %d failed: %v", i, err) } r2, n2, _ := buf.ReadRune() if r2 != testRunes[i] { t.Errorf("After UnreadRune %d: expected %U, got %U", i, testRunes[i], r2) } if n1 != n2 { t.Errorf("Byte counts differ: before=%d, after=%d", n1, n2) } } }) t.Run("Buffer methods with nil receiver", func(t *testing.T) { var buf *Buffer // Это не должно паниковать в реальном коде, но в тестах проверим // В реальности нужно было бы добавить проверки в методы // Здесь просто убедимся что можем создавать тесты if buf != nil { t.Error("Expected nil buffer") } }) } func TestBuffer_Fork(t *testing.T) { b := FromBytes([]byte("1234567890")) fs := b.Fork(0, 5) if fs.String() != "12345" { t.Errorf("Fork: expected %s, got %s", "12345", fs.String()) } if fs.Size() != 5 { t.Errorf("Fork: expected %d, got %d", 5, fs.Size()) } fs = b.Fork(0, 20) if fs.String() != "1234567890" { t.Errorf("Fork: expected %s, got %s", "1234567890", fs) } if fs.Size() != 10 { t.Errorf("Fork: expected %d, got %d", 20, fs.Size()) } } func TestBuffer_Checksum(t *testing.T) { b := FromBytes([]byte("1234567890")) if sum := b.Checksum(0, 10); sum != 869078106 { t.Errorf("Checksum: expected %d, got %d", 869078106, sum) } if sum := b.Checksum(0, 0); sum != 0 { t.Errorf("Checksum: expected %d, got %d", 0, sum) } if sum := b.Checksum(0, 2); sum != 612891891 { t.Errorf("Checksum: expected %d, got %d", 612891891, sum) } }