package buffer import ( "testing" "io" "errors" ) type testPool struct { size int failAfter []int count int } var ( errTest = errors.New("test error") errTest2 = errors.New("test error 2") ) func (p *testPool) Get() ([]byte, error) { defer func() { p.count++ }() if len(p.failAfter) > 0 && p.count == p.failAfter[0] { p.failAfter = p.failAfter[1:] return nil, errTest } return make([]byte, p.size), nil } func (p *testPool) Put([]byte) {} func TestContent(t *testing.T) { t.Run("eof", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { var n int64 for i := 0; i < 3; i++ { ni, err := w.Write([]byte("123456789")[i * 3:i * 3 + 3]) n += int64(ni) if err != nil { return n, err } } return n, nil }) p := &testPool{size: 2} o := Options{Pool: p} r := BufferedContent(c, o) b := make([]byte, 3) for i := 0; i < 3; i++ { n, err := r.Read(b) if n != 3 || err != nil { t.Fatal(n, err) } if string(b) != "123456789"[i * 3:i * 3 + 3] { t.Fatal(string(b)) } } n, err := r.Read(b) if n != 0 || !errors.Is(err, io.EOF) { t.Fatal(n, err) } }) t.Run("eof right away", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { return 0, nil }) p := &testPool{size: 2} o := Options{Pool: p} r := BufferedContent(c, o) b := make([]byte, 3) n, err := r.Read(b) if n != 0 || !errors.Is(err, io.EOF) { t.Fatal(n, err) } }) t.Run("writer error", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { var n int64 for i := 0; i < 3; i++ { ni, err := w.Write([]byte("123456789")[i * 3:i * 3 + 3]) n += int64(ni) if err != nil { return n, err } } return n, errTest }) p := &testPool{size: 2} o := Options{Pool: p} r := BufferedContent(c, o) b := make([]byte, 3) for i := 0; i < 3; i++ { n, err := r.Read(b) if n != 3 || err != nil { t.Fatal(n, err) } if string(b) != "123456789"[i * 3:i * 3 + 3] { t.Fatal(string(b)) } } n, err := r.Read(b) if n != 0 || !errors.Is(err, errTest) { t.Fatal(n, err) } }) t.Run("writer error right away", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { return 0, errTest }) p := &testPool{size: 2} o := Options{Pool: p} r := BufferedContent(c, o) b := make([]byte, 3) n, err := r.Read(b) if n != 0 || !errors.Is(err, errTest) { t.Fatal(n, err) } }) t.Run("abort", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { var n int64 for i := 0; i < 3; i++ { ni, err := w.Write([]byte("123456789")[i * 3:i * 3 + 3]) n += int64(ni) if err != nil { return n, err } } return n, nil }) p := &testPool{ size: 2, failAfter: []int{1}, } o := Options{Pool: p} r := BufferedContent(c, o) b, ok, err := r.ReadBytes([]byte("67"), 12) if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil { t.Fatal(string(b), ok, err) } b, ok, err = r.ReadBytes([]byte("67"), 12) if len(b) != 0 || ok || !errors.Is(err, errTest) { t.Fatal(string(b), ok, err) } }) t.Run("abort right away", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { var n int64 for i := 0; i < 3; i++ { ni, err := w.Write([]byte("123456789")[i * 3:i * 3 + 3]) n += int64(ni) if err != nil { return n, err } } return n, nil }) p := &testPool{ size: 2, failAfter: []int{0}, } o := Options{Pool: p} r := BufferedContent(c, o) b, ok, err := r.ReadBytes([]byte("67"), 12) if len(b) != 0 || ok || !errors.Is(err, errTest) { t.Fatal(string(b), ok, err) } }) t.Run("close when implementation ignores writer errors", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { w.Write([]byte("123")) w.Write([]byte("456")) w.Write([]byte("123")) return 0, nil }) p := &testPool{ size: 2, failAfter: []int{1}, } o := Options{Pool: p} r := BufferedContent(c, o) b, ok, err := r.ReadBytes([]byte("67"), 12) if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil { t.Fatal(string(b), ok, err) } b, ok, err = r.ReadBytes([]byte("67"), 12) if len(b) != 0 || ok || !errors.Is(err, errTest) { t.Fatal(string(b), ok, err) } }) t.Run("zero write", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { w.Write([]byte("123")) w.Write(nil) w.Write([]byte("456")) w.Write([]byte("789")) return 0, nil }) p := &testPool{size: 2} o := Options{Pool: p} r := BufferedContent(c, o) b := make([]byte, 3) for i := 0; i < 3; i++ { n, err := r.Read(b) if n != 3 || err != nil { t.Fatal(n, err) } if string(b) != "123456789"[i * 3:i * 3 + 3] { t.Fatal(string(b)) } } n, err := r.Read(b) if n != 0 || !errors.Is(err, io.EOF) { t.Fatal(n, err) } }) t.Run("zero write right away", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { w.Write(nil) w.Write([]byte("123")) w.Write([]byte("456")) w.Write([]byte("789")) return 0, nil }) p := &testPool{size: 2} o := Options{Pool: p} r := BufferedContent(c, o) b := make([]byte, 3) for i := 0; i < 3; i++ { n, err := r.Read(b) if n != 3 || err != nil { t.Fatal(n, err) } if string(b) != "123456789"[i * 3:i * 3 + 3] { t.Fatal(string(b)) } } n, err := r.Read(b) if n != 0 || !errors.Is(err, io.EOF) { t.Fatal(n, err) } }) t.Run("custom error", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { var n int64 for i := 0; i < 3; i++ { ni, err := w.Write([]byte("123456789")[i * 3:i * 3 + 3]) n += int64(ni) if err != nil { return n, err } } return n, errTest }) p := &testPool{size: 3} o := Options{Pool: p} r := BufferedContent(c, o) b := make([]byte, 3) for i := 0; i < 3; i++ { n, err := r.Read(b) if n != 3 || err != nil { t.Fatal(n, err) } if string(b) != "123456789"[i * 3:i * 3 + 3] { t.Fatal(string(b)) } } n, err := r.Read(b) if n != 0 || !errors.Is(err, errTest) { t.Fatal(n, err) } }) t.Run("custom error with pool error", func(t *testing.T) { c := ContentFunc(func(w io.Writer) (int64, error) { w.Write([]byte("123")) w.Write([]byte("456")) w.Write([]byte("123")) return 0, errTest2 }) p := &testPool{ size: 2, failAfter: []int{1}, } o := Options{Pool: p} r := BufferedContent(c, o) b, ok, err := r.ReadBytes([]byte("67"), 12) if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil { t.Fatal(string(b), ok, err) } b, ok, err = r.ReadBytes([]byte("67"), 12) if len(b) != 0 || ok || !errors.Is(err, errTest) || !errors.Is(err, errTest2) { t.Fatal(string(b), ok, err) } }) }