package buffer_test import ( "bytes" "code.squareroundforest.org/arpio/buffer" "errors" "io" "testing" ) func TestReadBytes(t *testing.T) { for title, cr := range map[string]createReader{"reader": buffer.BufferedReader, "content": testContent} { t.Run(title, func(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 := cr(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 := cr(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)} r := cr(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)} r := cr(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)} r := cr(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)} r := cr(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 := cr(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 := cr(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 := cr(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)} r := cr(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)} r := cr(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)} r := cr(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)} r := cr(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 := cr(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 := cr(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("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 := cr(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("single char delimiter", func(t *testing.T) { g := &gen{ max: 1 << 15, customContentAfter: []int{12}, customContent: map[int][]byte{12: []byte("1")}, } o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := cr(g, o) b, ok, err := r.ReadBytes([]byte{'1'}, 64) if err != nil { t.Fatal(err) } if !ok { t.Fatal("delimiter") } if !bytes.Equal(b, append(generate(12), '1')) { t.Fatal("failed to generate right content") } }) t.Run("large delimiter", func(t *testing.T) { src := &gen{ max: 1 << 18, customContentAfter: []int{1<<17 - 3}, customContent: map[int][]byte{1<<17 - 3: []byte("123")}, } r := buffer.BufferedReader(src, buffer.Options{Pool: buffer.NoPool(1 << 12)}) r.ReadBytes([]byte{'1'}, 1<<17+1<<16) }) t.Run("delimiter longer than content", func(t *testing.T) { g := &gen{max: 1 << 9} o := buffer.Options{Pool: buffer.NoPool(16)} r := cr(g, o) b, ok, err := r.ReadBytes(generate(1<<10), 1<<11) if err != nil { t.Fatal(err) } if ok { t.Fatal("delimiter") } if !bytes.Equal(b, generate(1<<9)) { t.Fatal("content") } }) t.Run("find not none consumed", func(t *testing.T) { g := &gen{} o := buffer.Options{Pool: buffer.NoPool(1 << 12)} r := cr(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") } }) }) } t.Run("reader only", func(t *testing.T) { cr := buffer.BufferedReader 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)} r := cr(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)} r := cr(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)} r := cr(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 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)} r := cr(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)} r := cr(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") } }) }) }