package buffer_test import ( "code.squareroundforest.org/arpio/buffer" "errors" "io" "testing" ) func TestContent(t *testing.T) { t.Run("eof", func(t *testing.T) { c := buffer.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 := &fakePool{allocSize: 2} o := buffer.Options{Pool: p} r := buffer.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 := buffer.ContentFunc(func(w io.Writer) (int64, error) { return 0, nil }) p := &fakePool{allocSize: 2} o := buffer.Options{Pool: p} r := buffer.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 := buffer.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 := &fakePool{allocSize: 2} o := buffer.Options{Pool: p} r := buffer.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 := buffer.ContentFunc(func(w io.Writer) (int64, error) { return 0, errTest }) p := &fakePool{allocSize: 2} o := buffer.Options{Pool: p} r := buffer.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 := buffer.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 := &fakePool{ allocSize: 2, errAfter: []int{1}, } o := buffer.Options{Pool: p} r := buffer.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 := buffer.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 := &fakePool{ allocSize: 2, errAfter: []int{0}, } o := buffer.Options{Pool: p} r := buffer.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 := buffer.ContentFunc(func(w io.Writer) (int64, error) { w.Write([]byte("123")) w.Write([]byte("456")) w.Write([]byte("123")) return 0, nil }) p := &fakePool{ allocSize: 2, errAfter: []int{1}, } o := buffer.Options{Pool: p} r := buffer.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 := buffer.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 := &fakePool{allocSize: 2} o := buffer.Options{Pool: p} r := buffer.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 := buffer.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 := &fakePool{allocSize: 2} o := buffer.Options{Pool: p} r := buffer.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 := buffer.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 := &fakePool{allocSize: 3} o := buffer.Options{Pool: p} r := buffer.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 := buffer.ContentFunc(func(w io.Writer) (int64, error) { w.Write([]byte("123")) w.Write([]byte("456")) w.Write([]byte("123")) return 0, errTest2 }) p := &fakePool{ allocSize: 2, errAfter: []int{1}, } o := buffer.Options{Pool: p} r := buffer.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) } }) }