1 package proto_test
2
3 import (
4 "bytes"
5 "encoding"
6 "fmt"
7 "net"
8 "testing"
9 "time"
10
11 . "github.com/bsm/ginkgo/v2"
12 . "github.com/bsm/gomega"
13
14 "github.com/redis/go-redis/v9/internal/proto"
15 "github.com/redis/go-redis/v9/internal/util"
16 )
17
18 type MyType struct{}
19
20 var _ encoding.BinaryMarshaler = (*MyType)(nil)
21
22 func (t *MyType) MarshalBinary() ([]byte, error) {
23 return []byte("hello"), nil
24 }
25
26 var _ = Describe("WriteBuffer", func() {
27 var buf *bytes.Buffer
28 var wr *proto.Writer
29
30 BeforeEach(func() {
31 buf = new(bytes.Buffer)
32 wr = proto.NewWriter(buf)
33 })
34
35 It("should write args", func() {
36 err := wr.WriteArgs([]interface{}{
37 "string",
38 12,
39 34.56,
40 []byte{'b', 'y', 't', 'e', 's'},
41 true,
42 nil,
43 })
44 Expect(err).NotTo(HaveOccurred())
45
46 Expect(buf.Bytes()).To(Equal([]byte("*6\r\n" +
47 "$6\r\nstring\r\n" +
48 "$2\r\n12\r\n" +
49 "$5\r\n34.56\r\n" +
50 "$5\r\nbytes\r\n" +
51 "$1\r\n1\r\n" +
52 "$0\r\n" +
53 "\r\n")))
54 })
55
56 It("should append time", func() {
57 tm := time.Date(2019, 1, 1, 9, 45, 10, 222125, time.UTC)
58 err := wr.WriteArgs([]interface{}{tm})
59 Expect(err).NotTo(HaveOccurred())
60
61 Expect(buf.Len()).To(Equal(41))
62 })
63
64 It("should append marshalable args", func() {
65 err := wr.WriteArgs([]interface{}{&MyType{}})
66 Expect(err).NotTo(HaveOccurred())
67
68 Expect(buf.Len()).To(Equal(15))
69 })
70
71 It("should append net.IP", func() {
72 ip := net.ParseIP("192.168.1.1")
73 err := wr.WriteArgs([]interface{}{ip})
74 Expect(err).NotTo(HaveOccurred())
75 Expect(buf.String()).To(Equal(fmt.Sprintf("*1\r\n$16\r\n%s\r\n", bytes.NewBuffer(ip))))
76 })
77 })
78
79 type discard struct{}
80
81 func (discard) Write(b []byte) (int, error) {
82 return len(b), nil
83 }
84
85 func (discard) WriteString(s string) (int, error) {
86 return len(s), nil
87 }
88
89 func (discard) WriteByte(c byte) error {
90 return nil
91 }
92
93 func BenchmarkWriteBuffer_Append(b *testing.B) {
94 buf := proto.NewWriter(discard{})
95 args := []interface{}{"hello", "world", "foo", "bar"}
96
97 for i := 0; i < b.N; i++ {
98 err := buf.WriteArgs(args)
99 if err != nil {
100 b.Fatal(err)
101 }
102 }
103 }
104
105 var _ = Describe("WriteArg", func() {
106 var buf *bytes.Buffer
107 var wr *proto.Writer
108
109 BeforeEach(func() {
110 buf = new(bytes.Buffer)
111 wr = proto.NewWriter(buf)
112 })
113
114 t := time.Date(2025, 2, 8, 00, 00, 00, 0, time.UTC)
115
116 args := map[any]string{
117 "hello": "$5\r\nhello\r\n",
118 util.ToPtr("hello"): "$5\r\nhello\r\n",
119 (*string)(nil): "$0\r\n\r\n",
120 int(10): "$2\r\n10\r\n",
121 util.ToPtr(int(10)): "$2\r\n10\r\n",
122 (*int)(nil): "$1\r\n0\r\n",
123 int8(10): "$2\r\n10\r\n",
124 util.ToPtr(int8(10)): "$2\r\n10\r\n",
125 (*int8)(nil): "$1\r\n0\r\n",
126 int16(10): "$2\r\n10\r\n",
127 util.ToPtr(int16(10)): "$2\r\n10\r\n",
128 (*int16)(nil): "$1\r\n0\r\n",
129 int32(10): "$2\r\n10\r\n",
130 util.ToPtr(int32(10)): "$2\r\n10\r\n",
131 (*int32)(nil): "$1\r\n0\r\n",
132 int64(10): "$2\r\n10\r\n",
133 util.ToPtr(int64(10)): "$2\r\n10\r\n",
134 (*int64)(nil): "$1\r\n0\r\n",
135 uint(10): "$2\r\n10\r\n",
136 util.ToPtr(uint(10)): "$2\r\n10\r\n",
137 (*uint)(nil): "$1\r\n0\r\n",
138 uint8(10): "$2\r\n10\r\n",
139 util.ToPtr(uint8(10)): "$2\r\n10\r\n",
140 (*uint8)(nil): "$0\r\n\r\n",
141 uint16(10): "$2\r\n10\r\n",
142 util.ToPtr(uint16(10)): "$2\r\n10\r\n",
143 (*uint16)(nil): "$1\r\n0\r\n",
144 uint32(10): "$2\r\n10\r\n",
145 util.ToPtr(uint32(10)): "$2\r\n10\r\n",
146 (*uint32)(nil): "$1\r\n0\r\n",
147 uint64(10): "$2\r\n10\r\n",
148 util.ToPtr(uint64(10)): "$2\r\n10\r\n",
149 (*uint64)(nil): "$1\r\n0\r\n",
150 float32(10.3): "$18\r\n10.300000190734863\r\n",
151 util.ToPtr(float32(10.3)): "$18\r\n10.300000190734863\r\n",
152 (*float32)(nil): "$1\r\n0\r\n",
153 float64(10.3): "$4\r\n10.3\r\n",
154 util.ToPtr(float64(10.3)): "$4\r\n10.3\r\n",
155 (*float64)(nil): "$1\r\n0\r\n",
156 bool(true): "$1\r\n1\r\n",
157 bool(false): "$1\r\n0\r\n",
158 util.ToPtr(bool(true)): "$1\r\n1\r\n",
159 util.ToPtr(bool(false)): "$1\r\n0\r\n",
160 (*bool)(nil): "$1\r\n0\r\n",
161 time.Time(t): "$20\r\n2025-02-08T00:00:00Z\r\n",
162 util.ToPtr(time.Time(t)): "$20\r\n2025-02-08T00:00:00Z\r\n",
163 (*time.Time)(nil): "$20\r\n0001-01-01T00:00:00Z\r\n",
164 time.Duration(time.Second): "$10\r\n1000000000\r\n",
165 util.ToPtr(time.Duration(time.Second)): "$10\r\n1000000000\r\n",
166 (*time.Duration)(nil): "$1\r\n0\r\n",
167 (encoding.BinaryMarshaler)(&MyType{}): "$5\r\nhello\r\n",
168 (encoding.BinaryMarshaler)(nil): "$0\r\n\r\n",
169 }
170
171 for arg, expect := range args {
172 arg, expect := arg, expect
173 It(fmt.Sprintf("should write arg of type %T", arg), func() {
174 err := wr.WriteArg(arg)
175 Expect(err).NotTo(HaveOccurred())
176 Expect(buf.String()).To(Equal(expect))
177 })
178 }
179 })
180
View as plain text