Skip to content

Commit 5c46ec5

Browse files
committed
Add examples/singleapp/iotest/errreader
1 parent 9848d0f commit 5c46ec5

File tree

3 files changed

+114
-0
lines changed

3 files changed

+114
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# https://taskfile.dev
2+
3+
version: '3'
4+
5+
tasks:
6+
default:
7+
cmds:
8+
- go test -v .
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package errreader
2+
3+
import (
4+
"errors"
5+
"strings"
6+
"syscall"
7+
"testing"
8+
"testing/iotest"
9+
)
10+
11+
func TestReadOk(t *testing.T) {
12+
var (
13+
s = "hello world"
14+
r = strings.NewReader(s)
15+
p = make([]byte, len(s))
16+
err error
17+
)
18+
19+
err = read(r, p)
20+
if err != nil {
21+
t.Fatal(err)
22+
}
23+
24+
if string(p) != s {
25+
t.Fatalf("[want] equal\t[got] not equal (%s, %s)", string(p), s)
26+
}
27+
}
28+
29+
func TestReadTooManyEAGAIN(t *testing.T) {
30+
//
31+
// iotest.ErrReader() は、指定したエラーを返す io.Reader を返してくれる。
32+
// io.Readerを使って何らかの処理を行う関数などを実装している場合のエラーテストに便利。
33+
// (例えば、ノンブロッキング処理をしている実装で、ずっとEAGAINが返ってくる場合のテストなど)
34+
//
35+
// - https://pkg.go.dev/testing/iotest@go1.23.0#ErrReader
36+
//
37+
var (
38+
s = "hello world"
39+
r = iotest.ErrReader(syscall.EAGAIN)
40+
p = make([]byte, len(s))
41+
)
42+
43+
err := read(r, p)
44+
if err == nil {
45+
t.Fatal("[want] err \t[got] nil")
46+
}
47+
48+
if !errors.Is(err, ErrTooManyEAGAIN) {
49+
t.Fatalf("[want] ErrRetryOver\t[got] %v", err)
50+
}
51+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package errreader
2+
3+
import (
4+
"errors"
5+
"io"
6+
"syscall"
7+
"time"
8+
)
9+
10+
var (
11+
Interval = 10 * time.Millisecond
12+
ErrTooManyEAGAIN = errors.New("retry over (EAGAIN)")
13+
MaxRetryCount = 5
14+
)
15+
16+
func read(r io.Reader, p []byte) error {
17+
var (
18+
buf = make([]byte, 2)
19+
numRead int
20+
offset int
21+
count int
22+
err error
23+
)
24+
25+
for count = 0; count < MaxRetryCount; {
26+
if len(p) <= offset {
27+
break
28+
}
29+
30+
clear(buf)
31+
32+
numRead, err = r.Read(buf)
33+
if err != nil {
34+
switch {
35+
case errors.Is(err, io.EOF):
36+
break
37+
case errors.Is(err, syscall.EAGAIN):
38+
time.Sleep(Interval)
39+
count++
40+
continue
41+
default:
42+
return err
43+
}
44+
}
45+
46+
copy(p[offset:offset+numRead], buf[:numRead])
47+
offset += numRead
48+
}
49+
50+
if MaxRetryCount <= count {
51+
return ErrTooManyEAGAIN
52+
}
53+
54+
return nil
55+
}

0 commit comments

Comments
 (0)