mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
Check scanner.Err() after scan loop
This commit is contained in:
parent
77f66d5150
commit
8d5a0f672c
12
source.go
12
source.go
|
|
@ -3,6 +3,7 @@ package peco
|
|||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -109,6 +110,12 @@ func (s *Source) Setup(ctx context.Context, state *Peco) {
|
|||
}
|
||||
}()
|
||||
|
||||
// scanErr captures any I/O error from the scanner goroutine.
|
||||
// The goroutine writes to scanErr before closing the lines channel,
|
||||
// and the outer loop reads it after lines is closed, so no mutex
|
||||
// is needed.
|
||||
var scanErr error
|
||||
|
||||
lines := make(chan string)
|
||||
go func() {
|
||||
var scanned int
|
||||
|
|
@ -129,6 +136,7 @@ func (s *Source) Setup(ctx context.Context, state *Peco) {
|
|||
}
|
||||
scanned++
|
||||
}
|
||||
scanErr = scanner.Err()
|
||||
}()
|
||||
|
||||
state.Hub().SendStatusMsg(ctx, "Waiting for input...", 0)
|
||||
|
|
@ -156,6 +164,10 @@ func (s *Source) Setup(ctx context.Context, state *Peco) {
|
|||
}
|
||||
}
|
||||
|
||||
if scanErr != nil {
|
||||
state.Hub().SendStatusMsg(ctx, fmt.Sprintf("Error reading input: %s", scanErr), 0)
|
||||
}
|
||||
|
||||
if pdebug.Enabled {
|
||||
pdebug.Printf("Read all %d lines from source", readCount)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
package peco
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -77,3 +80,66 @@ func TestSource(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// errorAfterReader returns data from the underlying reader, then once
|
||||
// the underlying reader is exhausted it returns a specified error.
|
||||
type errorAfterReader struct {
|
||||
io.Reader
|
||||
err error
|
||||
hitEOF bool
|
||||
}
|
||||
|
||||
func (r *errorAfterReader) Read(p []byte) (int, error) {
|
||||
if r.hitEOF {
|
||||
return 0, r.err
|
||||
}
|
||||
n, err := r.Reader.Read(p)
|
||||
if err == io.EOF {
|
||||
r.hitEOF = true
|
||||
if n > 0 {
|
||||
return n, nil
|
||||
}
|
||||
return 0, r.err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func TestSourceScannerErr(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
ig := newIDGen()
|
||||
go ig.Run(ctx)
|
||||
|
||||
simulatedErr := errors.New("simulated I/O error")
|
||||
r := &errorAfterReader{
|
||||
Reader: strings.NewReader("line1\nline2\n"),
|
||||
err: simulatedErr,
|
||||
}
|
||||
|
||||
s := NewSource("-", r, false, ig, 0, false, false)
|
||||
p := New()
|
||||
rh := &recordingHub{}
|
||||
p.hub = rh
|
||||
s.Setup(ctx, p)
|
||||
|
||||
// Verify lines read before the error are present
|
||||
require.Equal(t, 2, s.Size(), "should have read 2 lines before the error")
|
||||
l0, err := s.LineAt(0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "line1", l0.DisplayString())
|
||||
l1, err := s.LineAt(1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "line2", l1.DisplayString())
|
||||
|
||||
// Verify the scanner error was reported via SendStatusMsg
|
||||
msgs := rh.getStatusMsgs()
|
||||
found := false
|
||||
for _, msg := range msgs {
|
||||
if strings.Contains(msg, "simulated I/O error") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
require.True(t, found, "expected scanner error in status messages, got: %v", msgs)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue