mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
Add test coverage
This commit is contained in:
parent
e1164500e6
commit
4c439c71a8
67
caret_test.go
Normal file
67
caret_test.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package peco
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCaretInitialPos(t *testing.T) {
|
||||
t.Parallel()
|
||||
var c Caret
|
||||
require.Equal(t, 0, c.Pos())
|
||||
}
|
||||
|
||||
func TestCaretSetPos(t *testing.T) {
|
||||
t.Parallel()
|
||||
var c Caret
|
||||
c.SetPos(5)
|
||||
require.Equal(t, 5, c.Pos())
|
||||
|
||||
c.SetPos(0)
|
||||
require.Equal(t, 0, c.Pos())
|
||||
|
||||
c.SetPos(100)
|
||||
require.Equal(t, 100, c.Pos())
|
||||
}
|
||||
|
||||
func TestCaretMove(t *testing.T) {
|
||||
t.Parallel()
|
||||
var c Caret
|
||||
c.SetPos(5)
|
||||
|
||||
c.Move(3)
|
||||
require.Equal(t, 8, c.Pos())
|
||||
|
||||
c.Move(-2)
|
||||
require.Equal(t, 6, c.Pos())
|
||||
|
||||
// Move to negative territory
|
||||
c.Move(-10)
|
||||
require.Equal(t, -4, c.Pos())
|
||||
}
|
||||
|
||||
func TestCaretMoveFromZero(t *testing.T) {
|
||||
t.Parallel()
|
||||
var c Caret
|
||||
|
||||
c.Move(1)
|
||||
require.Equal(t, 1, c.Pos())
|
||||
|
||||
c.Move(-1)
|
||||
require.Equal(t, 0, c.Pos())
|
||||
}
|
||||
|
||||
func TestCaretMultipleMoves(t *testing.T) {
|
||||
t.Parallel()
|
||||
var c Caret
|
||||
for i := 0; i < 10; i++ {
|
||||
c.Move(1)
|
||||
}
|
||||
require.Equal(t, 10, c.Pos())
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
c.Move(-1)
|
||||
}
|
||||
require.Equal(t, 5, c.Pos())
|
||||
}
|
||||
61
internal/buffer/line_test.go
Normal file
61
internal/buffer/line_test.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package buffer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetLineListBuf(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf := GetLineListBuf()
|
||||
require.NotNil(t, buf)
|
||||
require.Equal(t, 0, len(buf))
|
||||
require.Equal(t, DefaultFilterBufSize, cap(buf))
|
||||
}
|
||||
|
||||
func TestReleaseAndGetLineListBuf(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Get a buffer, use it, release it, get it back
|
||||
buf := GetLineListBuf()
|
||||
require.NotNil(t, buf)
|
||||
|
||||
// Release it back to the pool
|
||||
ReleaseLineListBuf(buf)
|
||||
|
||||
// Get another one — may or may not be the same one (pool behavior is not guaranteed)
|
||||
buf2 := GetLineListBuf()
|
||||
require.NotNil(t, buf2)
|
||||
require.Equal(t, 0, len(buf2))
|
||||
}
|
||||
|
||||
func TestReleaseLineListBufNil(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Should not panic
|
||||
ReleaseLineListBuf(nil)
|
||||
}
|
||||
|
||||
func TestReleaseLineListBufResetsLength(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf := GetLineListBuf()
|
||||
|
||||
// Simulate usage — we can't easily append real line.Line values
|
||||
// without importing the line package's internals, but we can verify
|
||||
// that the pool resets the slice length to 0 on release.
|
||||
// The ReleaseLineListBuf function does: l = l[0:0]
|
||||
// This is verified by the pool returning zero-length slices.
|
||||
ReleaseLineListBuf(buf)
|
||||
|
||||
buf2 := GetLineListBuf()
|
||||
require.Equal(t, 0, len(buf2))
|
||||
}
|
||||
|
||||
func TestMultipleGetReleaseCycles(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i := 0; i < 10; i++ {
|
||||
buf := GetLineListBuf()
|
||||
require.NotNil(t, buf)
|
||||
require.Equal(t, 0, len(buf))
|
||||
ReleaseLineListBuf(buf)
|
||||
}
|
||||
}
|
||||
173
internal/util/util_test.go
Normal file
173
internal/util/util_test.go
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCaseInsensitiveIndexFunc(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
r rune
|
||||
matchR rune
|
||||
matches bool
|
||||
}{
|
||||
{"same lowercase", 'a', 'a', true},
|
||||
{"upper matches lower", 'a', 'A', true},
|
||||
{"lower matches upper", 'A', 'a', true},
|
||||
{"different chars", 'a', 'b', false},
|
||||
{"unicode same", 'ä', 'Ä', true},
|
||||
{"digit matches itself", '1', '1', true},
|
||||
{"digit vs letter", '1', 'a', false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
fn := CaseInsensitiveIndexFunc(tt.r)
|
||||
require.Equal(t, tt.matches, fn(tt.matchR))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsUpper(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"all lowercase", "hello", false},
|
||||
{"all uppercase", "HELLO", true},
|
||||
{"mixed case", "hEllo", true},
|
||||
{"empty string", "", false},
|
||||
{"numbers only", "12345", false},
|
||||
{"with uppercase at end", "hellO", true},
|
||||
{"with uppercase at start", "Hello", true},
|
||||
{"unicode lowercase", "こんにちは", false},
|
||||
{"special chars", "!@#$%", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, tt.expected, ContainsUpper(tt.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripANSISequence(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"no ANSI", "hello", "hello"},
|
||||
{"empty string", "", ""},
|
||||
{"bold", "\x1B[1mhello\x1B[0m", "hello"},
|
||||
{"color red", "\x1B[31mred text\x1B[0m", "red text"},
|
||||
{"multiple sequences", "\x1B[1m\x1B[31mhello\x1B[0m", "hello"},
|
||||
{"color with semicolon", "\x1B[1;31mbold red\x1B[0m", "bold red"},
|
||||
{"mixed content", "before\x1B[32mgreen\x1B[0mafter", "beforegreenafter"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, tt.expected, StripANSISequence(tt.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Mock error types for testing error interface checks
|
||||
|
||||
type mockIgnorableError struct {
|
||||
ignorable bool
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *mockIgnorableError) Error() string { return e.msg }
|
||||
func (e *mockIgnorableError) Ignorable() bool { return e.ignorable }
|
||||
|
||||
type mockCollectResultsError struct {
|
||||
collect bool
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *mockCollectResultsError) Error() string { return e.msg }
|
||||
func (e *mockCollectResultsError) CollectResults() bool { return e.collect }
|
||||
|
||||
type mockExitStatusError struct {
|
||||
status int
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *mockExitStatusError) Error() string { return e.msg }
|
||||
func (e *mockExitStatusError) ExitStatus() int { return e.status }
|
||||
|
||||
func TestIsIgnorableError(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
expected bool
|
||||
}{
|
||||
{"ignorable true", &mockIgnorableError{ignorable: true, msg: "test"}, true},
|
||||
{"ignorable false", &mockIgnorableError{ignorable: false, msg: "test"}, false},
|
||||
{"non-ignorable error", errors.New("plain error"), false},
|
||||
{"wrapped ignorable", fmt.Errorf("wrapper: %w", &mockIgnorableError{ignorable: true, msg: "inner"}), true},
|
||||
{"wrapped non-ignorable", fmt.Errorf("wrapper: %w", errors.New("plain")), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, tt.expected, IsIgnorableError(tt.err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCollectResultsError(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
expected bool
|
||||
}{
|
||||
{"collect true", &mockCollectResultsError{collect: true, msg: "test"}, true},
|
||||
{"collect false", &mockCollectResultsError{collect: false, msg: "test"}, false},
|
||||
{"plain error", errors.New("plain error"), false},
|
||||
{"wrapped collect", fmt.Errorf("wrapper: %w", &mockCollectResultsError{collect: true, msg: "inner"}), true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, tt.expected, IsCollectResultsError(tt.err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExitStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
expectedStatus int
|
||||
expectedFound bool
|
||||
}{
|
||||
{"exit status 0", &mockExitStatusError{status: 0, msg: "test"}, 0, true},
|
||||
{"exit status 1", &mockExitStatusError{status: 1, msg: "test"}, 1, true},
|
||||
{"exit status 42", &mockExitStatusError{status: 42, msg: "test"}, 42, true},
|
||||
{"plain error", errors.New("plain"), 1, false},
|
||||
{"wrapped exit status", fmt.Errorf("wrapper: %w", &mockExitStatusError{status: 2, msg: "inner"}), 2, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
status, found := GetExitStatus(tt.err)
|
||||
require.Equal(t, tt.expectedStatus, status)
|
||||
require.Equal(t, tt.expectedFound, found)
|
||||
})
|
||||
}
|
||||
}
|
||||
197
query_test.go
Normal file
197
query_test.go
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
package peco
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestQuerySetAndString(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
}{
|
||||
{"empty string", ""},
|
||||
{"ascii", "hello"},
|
||||
{"unicode", "こんにちは"},
|
||||
{"mixed", "hello世界"},
|
||||
{"with spaces", "hello world"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
q.Set(tt.input)
|
||||
require.Equal(t, tt.input, q.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryLen(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
require.Equal(t, 0, q.Len())
|
||||
|
||||
q.Set("hello")
|
||||
require.Equal(t, 5, q.Len())
|
||||
|
||||
// Unicode: each character is one rune
|
||||
q.Set("こんにちは")
|
||||
require.Equal(t, 5, q.Len())
|
||||
}
|
||||
|
||||
func TestQueryReset(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
q.Set("hello")
|
||||
require.Equal(t, 5, q.Len())
|
||||
|
||||
q.Reset()
|
||||
require.Equal(t, 0, q.Len())
|
||||
require.Equal(t, "", q.String())
|
||||
}
|
||||
|
||||
func TestQuerySaveAndRestore(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
q.Set("original")
|
||||
|
||||
q.SaveQuery()
|
||||
// After save, query is cleared
|
||||
require.Equal(t, "", q.String())
|
||||
|
||||
// Setting new query while saved
|
||||
q.Set("temporary")
|
||||
require.Equal(t, "temporary", q.String())
|
||||
|
||||
// Restore brings back the saved query
|
||||
q.RestoreSavedQuery()
|
||||
require.Equal(t, "original", q.String())
|
||||
}
|
||||
|
||||
func TestQuerySaveAndRestoreEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
// Save an empty query
|
||||
q.SaveQuery()
|
||||
require.Equal(t, "", q.String())
|
||||
|
||||
q.Set("something")
|
||||
q.RestoreSavedQuery()
|
||||
require.Equal(t, "", q.String())
|
||||
}
|
||||
|
||||
func TestQueryDeleteRange(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
initial string
|
||||
start int
|
||||
end int
|
||||
expected string
|
||||
}{
|
||||
{"delete middle", "abcdef", 2, 4, "abef"},
|
||||
{"delete from start", "abcdef", 0, 3, "def"},
|
||||
{"delete to end", "abcdef", 3, 6, "abc"},
|
||||
{"delete all", "abcdef", 0, 6, ""},
|
||||
{"delete single char", "abcdef", 2, 3, "abdef"},
|
||||
{"start is -1 (no-op)", "abcdef", -1, 3, "abcdef"},
|
||||
{"start > end (no-op)", "abcdef", 4, 2, "abcdef"},
|
||||
{"end beyond length (clamped)", "abcdef", 4, 100, "abcd"},
|
||||
{"unicode delete", "あいうえお", 1, 3, "あえお"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
q.Set(tt.initial)
|
||||
q.DeleteRange(tt.start, tt.end)
|
||||
require.Equal(t, tt.expected, q.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryRuneSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
q.Set("hello")
|
||||
|
||||
runes := q.RuneSlice()
|
||||
require.Equal(t, []rune("hello"), runes)
|
||||
|
||||
// Verify it's a copy - modifying the returned slice shouldn't affect the query
|
||||
runes[0] = 'H'
|
||||
require.Equal(t, "hello", q.String())
|
||||
}
|
||||
|
||||
func TestQueryRuneSliceEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
runes := q.RuneSlice()
|
||||
require.Empty(t, runes)
|
||||
}
|
||||
|
||||
func TestQueryRuneAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
q.Set("hello")
|
||||
|
||||
require.Equal(t, 'h', q.RuneAt(0))
|
||||
require.Equal(t, 'e', q.RuneAt(1))
|
||||
require.Equal(t, 'o', q.RuneAt(4))
|
||||
}
|
||||
|
||||
func TestQueryRuneAtUnicode(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
q.Set("あいう")
|
||||
|
||||
require.Equal(t, 'あ', q.RuneAt(0))
|
||||
require.Equal(t, 'い', q.RuneAt(1))
|
||||
require.Equal(t, 'う', q.RuneAt(2))
|
||||
}
|
||||
|
||||
func TestQueryInsertAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
initial string
|
||||
ch rune
|
||||
where int
|
||||
expected string
|
||||
}{
|
||||
{"insert at beginning", "hello", 'X', 0, "Xhello"},
|
||||
{"insert at end (append)", "hello", 'X', 5, "helloX"},
|
||||
{"insert in middle", "hello", 'X', 2, "heXllo"},
|
||||
{"insert into empty", "", 'X', 0, "X"},
|
||||
{"insert unicode", "hello", 'あ', 2, "heあllo"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
q.Set(tt.initial)
|
||||
q.InsertAt(tt.ch, tt.where)
|
||||
require.Equal(t, tt.expected, q.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryMultipleInserts(t *testing.T) {
|
||||
t.Parallel()
|
||||
var q Query
|
||||
// Build "abc" by inserting one character at a time
|
||||
q.InsertAt('a', 0)
|
||||
q.InsertAt('b', 1)
|
||||
q.InsertAt('c', 2)
|
||||
require.Equal(t, "abc", q.String())
|
||||
|
||||
// Insert at beginning
|
||||
q.InsertAt('0', 0)
|
||||
require.Equal(t, "0abc", q.String())
|
||||
|
||||
// Insert in middle
|
||||
q.InsertAt('X', 2)
|
||||
require.Equal(t, "0aXbc", q.String())
|
||||
}
|
||||
Loading…
Reference in a new issue