package core import "testing" func TestGapBufferString(t *testing.T) { t.Run("gapBuffer.String() on empty buffer returns \"\"", func(t *testing.T) { buf := NewEmptyGapBuffer() str := buf.String() if str != "" { t.Fatalf("buf.String() expected '', got '%s'", str) } }) t.Run("gapBuffer.String() on string returns the string", func(t *testing.T) { buf := NewGapBuffer("Hello world") str := buf.String() if str != "Hello world" { t.Fatalf("buf.String() expected 'Hello world', got '%s'", str) } }) t.Run("gapBuffer.String() after moving gap returns the string", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.moveGap(6) str := buf.String() if str != "Hello world" { t.Fatalf("buf.String() expected 'Hello world', got '%s'", str) } }) t.Run("gapBuffer.String() after growing gap returns the string", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.grow(16) str := buf.String() if str != "Hello world" { t.Fatalf("buf.String() expected 'Hello world', got '%s'", str) } }) t.Run("gapBuffer.String() handles unicode characters", func(t *testing.T) { buf := NewGapBuffer("Hello δΈ–η•Œ 🌍") str := buf.String() if str != "Hello δΈ–η•Œ 🌍" { t.Fatalf("buf.String() expected 'Hello δΈ–η•Œ 🌍', got '%s'", str) } }) } func TestGapBufferLen(t *testing.T) { t.Run("empty buffer has length 0", func(t *testing.T) { buf := NewEmptyGapBuffer() if buf.Len() != 0 { t.Fatalf("expected length 0, got %d", buf.Len()) } }) t.Run("buffer with content returns correct length", func(t *testing.T) { buf := NewGapBuffer("Hello") if buf.Len() != 5 { t.Fatalf("expected length 5, got %d", buf.Len()) } }) t.Run("length with unicode characters", func(t *testing.T) { buf := NewGapBuffer("Hi 🌍") if buf.Len() != 4 { t.Fatalf("expected length 4, got %d", buf.Len()) } }) } func TestGapBufferInsert(t *testing.T) { t.Run("insert at beginning", func(t *testing.T) { buf := NewGapBuffer("world") buf.Insert(0, "Hello ") if buf.String() != "Hello world" { t.Fatalf("expected 'Hello world', got '%s'", buf.String()) } }) t.Run("insert at end", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Insert(5, " world") if buf.String() != "Hello world" { t.Fatalf("expected 'Hello world', got '%s'", buf.String()) } }) t.Run("insert in middle", func(t *testing.T) { buf := NewGapBuffer("Helloworld") buf.Insert(5, " ") if buf.String() != "Hello world" { t.Fatalf("expected 'Hello world', got '%s'", buf.String()) } }) t.Run("insert into empty buffer", func(t *testing.T) { buf := NewEmptyGapBuffer() buf.Insert(0, "Hello") if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } }) t.Run("insert empty string does nothing", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Insert(2, "") if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } }) t.Run("insert at invalid position does nothing", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Insert(-1, "X") if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } buf.Insert(100, "X") if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } }) t.Run("insert unicode characters", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Insert(5, " δΈ–η•Œ") if buf.String() != "Hello δΈ–η•Œ" { t.Fatalf("expected 'Hello δΈ–η•Œ', got '%s'", buf.String()) } }) t.Run("multiple consecutive insertions", func(t *testing.T) { buf := NewEmptyGapBuffer() buf.Insert(0, "a") buf.Insert(1, "b") buf.Insert(2, "c") if buf.String() != "abc" { t.Fatalf("expected 'abc', got '%s'", buf.String()) } }) t.Run("insert large text triggers grow", func(t *testing.T) { buf := NewGapBuffer("Hello") longText := "Lorem ipsum dolor sit amet, consectetur adipiscing elit" buf.Insert(5, longText) expected := "Hello" + longText if buf.String() != expected { t.Fatalf("expected '%s', got '%s'", expected, buf.String()) } }) } func TestGapBufferDelete(t *testing.T) { t.Run("delete from beginning", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.Delete(0, 6) if buf.String() != "world" { t.Fatalf("expected 'world', got '%s'", buf.String()) } }) t.Run("delete from end", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.Delete(5, 6) if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } }) t.Run("delete from middle", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.Delete(5, 1) if buf.String() != "Helloworld" { t.Fatalf("expected 'Helloworld', got '%s'", buf.String()) } }) t.Run("delete single character", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Delete(1, 1) if buf.String() != "Hllo" { t.Fatalf("expected 'Hllo', got '%s'", buf.String()) } }) t.Run("delete all content", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Delete(0, 5) if buf.String() != "" { t.Fatalf("expected '', got '%s'", buf.String()) } }) t.Run("delete with count exceeding length", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Delete(2, 100) if buf.String() != "He" { t.Fatalf("expected 'He', got '%s'", buf.String()) } }) t.Run("delete at invalid position does nothing", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Delete(-1, 2) if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } buf.Delete(100, 2) if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } }) t.Run("delete with zero count does nothing", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Delete(2, 0) if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } }) t.Run("delete unicode characters", func(t *testing.T) { buf := NewGapBuffer("Hello δΈ–η•Œ") buf.Delete(6, 2) if buf.String() != "Hello " { t.Fatalf("expected 'Hello ', got '%s'", buf.String()) } }) } func TestGapBufferRuneAt(t *testing.T) { t.Run("get rune at valid position", func(t *testing.T) { buf := NewGapBuffer("Hello") if buf.RuneAt(0) != 'H' { t.Fatalf("expected 'H', got '%c'", buf.RuneAt(0)) } if buf.RuneAt(4) != 'o' { t.Fatalf("expected 'o', got '%c'", buf.RuneAt(4)) } }) t.Run("get rune before gap", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.moveGap(6) if buf.RuneAt(5) != ' ' { t.Fatalf("expected ' ', got '%c'", buf.RuneAt(5)) } }) t.Run("get rune after gap", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.moveGap(6) if buf.RuneAt(6) != 'w' { t.Fatalf("expected 'w', got '%c'", buf.RuneAt(6)) } }) t.Run("get rune at invalid position returns 0", func(t *testing.T) { buf := NewGapBuffer("Hello") if buf.RuneAt(-1) != 0 { t.Fatalf("expected 0, got '%c'", buf.RuneAt(-1)) } if buf.RuneAt(100) != 0 { t.Fatalf("expected 0, got '%c'", buf.RuneAt(100)) } }) t.Run("get unicode rune", func(t *testing.T) { buf := NewGapBuffer("πŸŒδΈ–η•Œ") if buf.RuneAt(0) != '🌍' { t.Fatalf("expected '🌍', got '%c'", buf.RuneAt(0)) } if buf.RuneAt(1) != 'δΈ–' { t.Fatalf("expected 'δΈ–', got '%c'", buf.RuneAt(1)) } }) } func TestGapBufferSubstring(t *testing.T) { t.Run("substring from middle", func(t *testing.T) { buf := NewGapBuffer("Hello world") if buf.Substring(0, 5) != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.Substring(0, 5)) } if buf.Substring(6, 11) != "world" { t.Fatalf("expected 'world', got '%s'", buf.Substring(6, 11)) } }) t.Run("substring entire content", func(t *testing.T) { buf := NewGapBuffer("Hello") if buf.Substring(0, 5) != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.Substring(0, 5)) } }) t.Run("substring with start >= end returns empty", func(t *testing.T) { buf := NewGapBuffer("Hello") if buf.Substring(3, 3) != "" { t.Fatalf("expected '', got '%s'", buf.Substring(3, 3)) } if buf.Substring(3, 2) != "" { t.Fatalf("expected '', got '%s'", buf.Substring(3, 2)) } }) t.Run("substring clamps to buffer bounds", func(t *testing.T) { buf := NewGapBuffer("Hello") if buf.Substring(-5, 3) != "Hel" { t.Fatalf("expected 'Hel', got '%s'", buf.Substring(-5, 3)) } if buf.Substring(2, 100) != "llo" { t.Fatalf("expected 'llo', got '%s'", buf.Substring(2, 100)) } }) t.Run("substring with gap in middle", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.moveGap(6) if buf.Substring(0, 11) != "Hello world" { t.Fatalf("expected 'Hello world', got '%s'", buf.Substring(0, 11)) } }) t.Run("substring with unicode", func(t *testing.T) { buf := NewGapBuffer("Hi δΈ–η•Œ") if buf.Substring(3, 5) != "δΈ–η•Œ" { t.Fatalf("expected 'δΈ–η•Œ', got '%s'", buf.Substring(3, 5)) } }) } func TestGapBufferSet(t *testing.T) { t.Run("set replaces content", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Set("Goodbye") if buf.String() != "Goodbye" { t.Fatalf("expected 'Goodbye', got '%s'", buf.String()) } }) t.Run("set to empty string", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Set("") if buf.String() != "" { t.Fatalf("expected '', got '%s'", buf.String()) } }) t.Run("set on empty buffer", func(t *testing.T) { buf := NewEmptyGapBuffer() buf.Set("Hello") if buf.String() != "Hello" { t.Fatalf("expected 'Hello', got '%s'", buf.String()) } }) t.Run("set resets gap position", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.moveGap(2) buf.Set("World") if buf.String() != "World" { t.Fatalf("expected 'World', got '%s'", buf.String()) } if buf.gapStart != 5 { t.Fatalf("expected gap start at 5, got %d", buf.gapStart) } }) } func TestGapBufferClear(t *testing.T) { t.Run("clear removes all content", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.Clear() if buf.String() != "" { t.Fatalf("expected '', got '%s'", buf.String()) } }) t.Run("clear on empty buffer", func(t *testing.T) { buf := NewEmptyGapBuffer() buf.Clear() if buf.String() != "" { t.Fatalf("expected '', got '%s'", buf.String()) } }) t.Run("clear resets gap to start", func(t *testing.T) { buf := NewGapBuffer("Hello") buf.Clear() if buf.gapStart != 0 { t.Fatalf("expected gap start at 0, got %d", buf.gapStart) } if buf.gapEnd != len(buf.buffer) { t.Fatalf("expected gap end at %d, got %d", len(buf.buffer), buf.gapEnd) } }) } func TestGapBufferGapSize(t *testing.T) { t.Run("gap size on new buffer", func(t *testing.T) { buf := NewGapBuffer("Hello") if buf.GapSize() != 16 { t.Fatalf("expected gap size 16, got %d", buf.GapSize()) } }) t.Run("gap size on empty buffer", func(t *testing.T) { buf := NewEmptyGapBuffer() if buf.GapSize() != 16 { t.Fatalf("expected gap size 16, got %d", buf.GapSize()) } }) t.Run("gap size decreases after insert", func(t *testing.T) { buf := NewGapBuffer("Hello") initialGapSize := buf.GapSize() buf.Insert(5, "XX") if buf.GapSize() != initialGapSize-2 { t.Fatalf("expected gap size %d, got %d", initialGapSize-2, buf.GapSize()) } }) } func TestGapBufferComplex(t *testing.T) { t.Run("sequence of operations", func(t *testing.T) { buf := NewEmptyGapBuffer() buf.Insert(0, "Hello") buf.Insert(5, " world") buf.Delete(5, 1) buf.Insert(5, ", ") if buf.String() != "Hello, world" { t.Fatalf("expected 'Hello, world', got '%s'", buf.String()) } }) t.Run("insert and delete at different positions", func(t *testing.T) { buf := NewGapBuffer("abcdefgh") buf.Delete(2, 4) buf.Insert(2, "XX") if buf.String() != "abXXgh" { t.Fatalf("expected 'abXXgh', got '%s'", buf.String()) } }) t.Run("editing with gap movement", func(t *testing.T) { buf := NewGapBuffer("Hello world") buf.Insert(0, ">> ") buf.Insert(buf.Len(), " <<") if buf.String() != ">> Hello world <<" { t.Fatalf("expected '>> Hello world <<', got '%s'", buf.String()) } }) }