package buffer_test import ( "testing" "code.squareroundforest.org/arpio/buffer" "bytes" "errors" "io" ) func TestReadBytes(t *testing.T) { t.Run("find", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter") } if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("failed to generate right content") } }) t.Run("find not", func(t *testing.T) { g := &gen{max: 1 << 15} o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok || len(b) != 0 { t.Fatal("failed to not find delimiter") } }) t.Run("find across segments", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{ Pool: buffer.NoPool(14), ReadSize: 14, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("failed to find delimiter") } if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("invalid content") } }) t.Run("find across multiple segments", func(t *testing.T) { d := generateFrom([]byte("123"), 12) g := &gen{ max: 1 << 15, customContentAfter: []int{6}, customContent: map[int][]byte{6: d}, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes(d, 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("failed to find delimiter") } if !bytes.Equal(b, append(generate(6), d...)) { t.Fatal("invalid content") } }) t.Run("find not across segments", func(t *testing.T) { g := &gen{max: 1 << 15} o := buffer.Options{ Pool: buffer.NoPool(15), ReadSize: 15, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 24) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } }) t.Run("find not across multiple segments", func(t *testing.T) { g := &gen{max: 1 << 15} o := buffer.Options{ Pool: buffer.NoPool(15), ReadSize: 15, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } }) t.Run("find not due to max", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("8"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content") } }) t.Run("find partial", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("12")}, } o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content") } }) t.Run("find partial and then full", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12, 18}, customContent: map[int][]byte{ 12: []byte("12"), 18: []byte("123"), }, } o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 16) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content") } b, ok, err = r.ReadBytes([]byte("123"), 24) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter") } check := append( append(generate(12), append([]byte("12"), generate(18)[14:]...)...), []byte("123")..., ) if !bytes.Equal(b, check) { t.Fatal("invalid content", len(b), string(b), len(check), string(check)) } }) t.Run("find partial across segments", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{7}, customContent: map[int][]byte{7: []byte("12")}, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content") } }) t.Run("find partial across multiple segments", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{7}, customContent: map[int][]byte{7: []byte("1234567890")}, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("1234567890123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content") } }) t.Run("find partial across segments and then full", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{7, 15}, customContent: map[int][]byte{ 7: []byte("12"), 15: []byte("123"), }, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 10) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content") } b, ok, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter") } check := append( append(generate(7), append([]byte("12"), generate(15)[9:]...)...), []byte("123")..., ) if !bytes.Equal(b, check) { t.Fatal("invalid content", len(b), string(b), len(check), string(check)) } }) t.Run("find partial across multiple segments and then full", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{7, 22}, customContent: map[int][]byte{ 7: []byte("1234567890"), 22: []byte("1234567890123"), }, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("1234567890123"), 20) if err != nil { t.Fatal(err) } if ok { t.Fatal("invalid delimiter found") } if len(b) != 0 { t.Fatal("invalid content") } b, ok, err = r.ReadBytes([]byte("1234567890123"), 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter") } check := append( append(append(generate(7), []byte("1234567890")...), generate(22)[17:]...), []byte("1234567890123")..., ) if !bytes.Equal(b, check) { t.Fatal("invalid content", len(b), string(b), len(check), string(check)) } }) t.Run("find partial due to max", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 14) if err != nil { t.Fatal(err) } if ok { t.Fatal("delimiter") } if len(b) != 0 { t.Fatal("invalid content") } }) t.Run("find partial due to max and then full", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 14) if err != nil { t.Fatal(err) } if ok { t.Fatal("delimiter") } if len(b) != 0 { t.Fatal("invalid content") } b, ok, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter") } if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("invalid content") } }) t.Run("error before found", func(t *testing.T) { g := &gen{ max: 1 << 15, errAfter: []int{8}, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("delimiter") } if !bytes.Equal(b, generate(8)) { t.Fatal("invalid content") } b, ok, err = r.ReadBytes([]byte("123"), 64) if !errors.Is(err, errTest) { t.Fatal("failed to fail", err) } if ok { t.Fatal("delimiter") } if len(b) != 0 { t.Fatal("invalid content") } }) t.Run("error when found", func(t *testing.T) { g := &gen{ max: 1 << 15, errAfter: []int{15}, fastErr: true, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter", len(b), string(b)) } if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("invalid content") } b, ok, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("unexpected delimiter") } if !bytes.Equal(b, generate(16)[15:]) { t.Fatal("invalid content", len(b), string(b)) } b, ok, err = r.ReadBytes([]byte("123"), 64) if !errors.Is(err, errTest) { t.Fatal(err) } if ok { t.Fatal("unexpected delimiter") } if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } }) t.Run("error after found", func(t *testing.T) { g := &gen{ max: 1 << 15, errAfter: []int{18}, fastErr: true, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter", len(b), string(b)) } if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("invalid content") } b, ok, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("unexpected delimiter") } if !bytes.Equal(b, generate(24)[15:]) { t.Fatal("invalid content", len(b), string(b)) } b, ok, err = r.ReadBytes([]byte("123"), 64) if !errors.Is(err, errTest) { t.Fatal(err) } if ok { t.Fatal("unexpected delimiter") } if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } }) t.Run("null delimiter", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes(nil, 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("failed to find delimiter") } if len(b) != 0 { t.Fatal("failed to generate right content") } }) t.Run("null read", func(t *testing.T) { g := &gen{ max: 1 << 15, nullReadAfter: []int{8, 8}, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("123")}, } o := buffer.Options{ Pool: buffer.NoPool(8), ReadSize: 8, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("delimiter") } if len(b) != 0 { t.Fatal("invalid content") } b, ok, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter") } if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("failed to generate right content") } }) t.Run("find not more than max", func(t *testing.T) { g := &gen{ max: 256, fastErr: true, } o := buffer.Options{ Pool: buffer.NoPool(256), ReadSize: 256, } r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } if ok { t.Fatal("delimiter") } if !bytes.Equal(b, generate(64)) { t.Fatal("failed to generate right content") } }) t.Run("find not none consumed", func(t *testing.T) { g := &gen{} o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := buffer.ReaderFrom(g, o) b, ok, err := r.ReadBytes([]byte("123"), 64) if !errors.Is(err, io.EOF) { t.Fatal(err) } if ok { t.Fatal("failed to find delimiter") } if len(b) != 0 { t.Fatal("invalid content") } }) // conditions: // - A0: error before read // - A1: error during read // - B0: allocation error // - B1: read error // - B2: eof // - C0: error right before delimiter // - C1: error during delimiter // - C2: error right after full delimiter // - D0: error before max // - D1: error right at max // - D2: error after max // - E0: error at zero segment position // - E1: error at not first segment boundary // - E2: error in first segment // - E3: error in subsequent segment // - F0: error at zero data position // - F1: error at not zero data position // - F3: error at last data position // - G0: less than max buffered // - G1: exactly max buffered // - G2: more than max buffered // - H0: max at first segment start // - H1: max at segment boundary // - H2: max in first segment // - H3: max in subsequent segment // - I0: delimiter in newly read // - I1: delimiter in buffered // - J0: delimiter before max // - J1: delimiter right up to max // - J2: delimiter over max // - J3: delimiter right after max // - J4: delimiter after max // - K0: delimiter at zero segment position // - K1: delimiter at subsequent segment boundary // - K2: delimiter within segment // - L0: delimiter at zero data position // - L1: delimiter at within buffered data // - L2: delimiter at the end of buffered data // - L3: partial delimiter at the end of buffered data // - L4: delimiter right after buffered data // - M0: delimiter found // - M1: delimiter not found // // variations: // - A0B0... }