]> Sergey Matveev's repositories - btrtrc.git/blob - bencode/decode_test.go
Drop support for go 1.20
[btrtrc.git] / bencode / decode_test.go
1 package bencode
2
3 import (
4         "bytes"
5         "fmt"
6         "io"
7         "math/big"
8         "reflect"
9         "testing"
10
11         qt "github.com/frankban/quicktest"
12         "github.com/stretchr/testify/assert"
13         "github.com/stretchr/testify/require"
14 )
15
16 type random_decode_test struct {
17         data     string
18         expected interface{}
19 }
20
21 var random_decode_tests = []random_decode_test{
22         {"i57e", int64(57)},
23         {"i-9223372036854775808e", int64(-9223372036854775808)},
24         {"5:hello", "hello"},
25         {"29:unicode test проверка", "unicode test проверка"},
26         {"d1:ai5e1:b5:helloe", map[string]interface{}{"a": int64(5), "b": "hello"}},
27         {
28                 "li5ei10ei15ei20e7:bencodee",
29                 []interface{}{int64(5), int64(10), int64(15), int64(20), "bencode"},
30         },
31         {"ldedee", []interface{}{map[string]interface{}{}, map[string]interface{}{}}},
32         {"le", []interface{}{}},
33         {"i604919719469385652980544193299329427705624352086e", func() *big.Int {
34                 ret, _ := big.NewInt(-1).SetString("604919719469385652980544193299329427705624352086", 10)
35                 return ret
36         }()},
37         {"d1:rd6:\xd4/\xe2F\x00\x01i42ee1:t3:\x9a\x87\x011:v4:TR%=1:y1:re", map[string]interface{}{
38                 "r": map[string]interface{}{"\xd4/\xe2F\x00\x01": int64(42)},
39                 "t": "\x9a\x87\x01",
40                 "v": "TR%=",
41                 "y": "r",
42         }},
43         {"d0:i420ee", map[string]interface{}{"": int64(420)}},
44 }
45
46 func TestRandomDecode(t *testing.T) {
47         for _, test := range random_decode_tests {
48                 var value interface{}
49                 err := Unmarshal([]byte(test.data), &value)
50                 if err != nil {
51                         t.Error(err, test.data)
52                         continue
53                 }
54                 assert.EqualValues(t, test.expected, value)
55         }
56 }
57
58 func TestLoneE(t *testing.T) {
59         var v int
60         err := Unmarshal([]byte("e"), &v)
61         se := err.(*SyntaxError)
62         require.EqualValues(t, 0, se.Offset)
63 }
64
65 func TestDecoderConsecutive(t *testing.T) {
66         d := NewDecoder(bytes.NewReader([]byte("i1ei2e")))
67         var i int
68         err := d.Decode(&i)
69         require.NoError(t, err)
70         require.EqualValues(t, 1, i)
71         err = d.Decode(&i)
72         require.NoError(t, err)
73         require.EqualValues(t, 2, i)
74         err = d.Decode(&i)
75         require.Equal(t, io.EOF, err)
76 }
77
78 func TestDecoderConsecutiveDicts(t *testing.T) {
79         bb := bytes.NewBufferString("d4:herp4:derped3:wat1:ke17:oh baby a triple!")
80
81         d := NewDecoder(bb)
82         assert.EqualValues(t, "d4:herp4:derped3:wat1:ke17:oh baby a triple!", bb.Bytes())
83         assert.EqualValues(t, 0, d.Offset)
84
85         var m map[string]interface{}
86
87         require.NoError(t, d.Decode(&m))
88         assert.Len(t, m, 1)
89         assert.Equal(t, "derp", m["herp"])
90         assert.Equal(t, "d3:wat1:ke17:oh baby a triple!", bb.String())
91         assert.EqualValues(t, 14, d.Offset)
92
93         require.NoError(t, d.Decode(&m))
94         assert.Equal(t, "k", m["wat"])
95         assert.Equal(t, "17:oh baby a triple!", bb.String())
96         assert.EqualValues(t, 24, d.Offset)
97
98         var s string
99         require.NoError(t, d.Decode(&s))
100         assert.Equal(t, "oh baby a triple!", s)
101         assert.EqualValues(t, 44, d.Offset)
102 }
103
104 func check_error(t *testing.T, err error) {
105         if err != nil {
106                 t.Error(err)
107         }
108 }
109
110 func assert_equal(t *testing.T, x, y interface{}) {
111         if !reflect.DeepEqual(x, y) {
112                 t.Errorf("got: %v (%T), expected: %v (%T)\n", x, x, y, y)
113         }
114 }
115
116 type unmarshalerInt struct {
117         x int
118 }
119
120 func (me *unmarshalerInt) UnmarshalBencode(data []byte) error {
121         return Unmarshal(data, &me.x)
122 }
123
124 type unmarshalerString struct {
125         x string
126 }
127
128 func (me *unmarshalerString) UnmarshalBencode(data []byte) error {
129         me.x = string(data)
130         return nil
131 }
132
133 func TestUnmarshalerBencode(t *testing.T) {
134         var i unmarshalerInt
135         var ss []unmarshalerString
136         check_error(t, Unmarshal([]byte("i71e"), &i))
137         assert_equal(t, i.x, 71)
138         check_error(t, Unmarshal([]byte("l5:hello5:fruit3:waye"), &ss))
139         assert_equal(t, ss[0].x, "5:hello")
140         assert_equal(t, ss[1].x, "5:fruit")
141         assert_equal(t, ss[2].x, "3:way")
142 }
143
144 func TestIgnoreUnmarshalTypeError(t *testing.T) {
145         s := struct {
146                 Ignore int `bencode:",ignore_unmarshal_type_error"`
147                 Normal int
148         }{}
149         require.Error(t, Unmarshal([]byte("d6:Normal5:helloe"), &s))
150         assert.NoError(t, Unmarshal([]byte("d6:Ignore5:helloe"), &s))
151         qt.Assert(t, Unmarshal([]byte("d6:Ignorei42ee"), &s), qt.IsNil)
152         assert.EqualValues(t, 42, s.Ignore)
153 }
154
155 // Test unmarshalling []byte into something that has the same kind but
156 // different type.
157 func TestDecodeCustomSlice(t *testing.T) {
158         type flag byte
159         var fs3, fs2 []flag
160         // We do a longer slice then a shorter slice to see if the buffers are
161         // shared.
162         d := NewDecoder(bytes.NewBufferString("3:\x01\x10\xff2:\x04\x0f"))
163         require.NoError(t, d.Decode(&fs3))
164         require.NoError(t, d.Decode(&fs2))
165         assert.EqualValues(t, []flag{1, 16, 255}, fs3)
166         assert.EqualValues(t, []flag{4, 15}, fs2)
167 }
168
169 func TestUnmarshalUnusedBytes(t *testing.T) {
170         var i int
171         require.EqualValues(t, ErrUnusedTrailingBytes{1}, Unmarshal([]byte("i42ee"), &i))
172         assert.EqualValues(t, 42, i)
173 }
174
175 func TestUnmarshalByteArray(t *testing.T) {
176         var ba [2]byte
177         assert.NoError(t, Unmarshal([]byte("2:hi"), &ba))
178         assert.EqualValues(t, "hi", ba[:])
179 }
180
181 func TestDecodeDictIntoUnsupported(t *testing.T) {
182         // Any type that a dict shouldn't be unmarshallable into.
183         var i int
184         c := qt.New(t)
185         err := Unmarshal([]byte("d1:a1:be"), &i)
186         t.Log(err)
187         c.Check(err, qt.Not(qt.IsNil))
188 }
189
190 func TestUnmarshalDictKeyNotString(t *testing.T) {
191         // Any type that a dict shouldn't be unmarshallable into.
192         var i int
193         c := qt.New(t)
194         err := Unmarshal([]byte("di42e3:yese"), &i)
195         t.Log(err)
196         c.Check(err, qt.Not(qt.IsNil))
197 }
198
199 type arbitraryReader struct{}
200
201 func (arbitraryReader) Read(b []byte) (int, error) {
202         return len(b), nil
203 }
204
205 func decodeHugeString(t *testing.T, strLen int64, header, tail string, v interface{}, maxStrLen MaxStrLen) error {
206         r, w := io.Pipe()
207         go func() {
208                 fmt.Fprintf(w, header, strLen)
209                 io.CopyN(w, arbitraryReader{}, strLen)
210                 w.Write([]byte(tail))
211                 w.Close()
212         }()
213         d := NewDecoder(r)
214         d.MaxStrLen = maxStrLen
215         return d.Decode(v)
216 }
217
218 // Ensure that bencode strings in various places obey the Decoder.MaxStrLen field.
219 func TestDecodeMaxStrLen(t *testing.T) {
220         t.Parallel()
221         c := qt.New(t)
222         test := func(header, tail string, v interface{}, maxStrLen MaxStrLen) {
223                 strLen := maxStrLen
224                 if strLen == 0 {
225                         strLen = DefaultDecodeMaxStrLen
226                 }
227                 c.Assert(decodeHugeString(t, strLen, header, tail, v, maxStrLen), qt.IsNil)
228                 c.Assert(decodeHugeString(t, strLen+1, header, tail, v, maxStrLen), qt.IsNotNil)
229         }
230         test("d%d:", "i0ee", new(interface{}), 0)
231         test("%d:", "", new(interface{}), DefaultDecodeMaxStrLen)
232         test("%d:", "", new([]byte), 1)
233         test("d3:420%d:", "e", new(struct {
234                 Hi []byte `bencode:"420"`
235         }), 69)
236 }
237
238 // This is for the "github.com/anacrolix/torrent/metainfo".Info.Private field.
239 func TestDecodeStringIntoBoolPtr(t *testing.T) {
240         var m struct {
241                 Private *bool `bencode:"private,omitempty"`
242         }
243         c := qt.New(t)
244         check := func(msg string, expectNil, expectTrue bool) {
245                 m.Private = nil
246                 c.Check(Unmarshal([]byte(msg), &m), qt.IsNil, qt.Commentf("%q", msg))
247                 if expectNil {
248                         c.Check(m.Private, qt.IsNil)
249                 } else {
250                         if c.Check(m.Private, qt.IsNotNil, qt.Commentf("%q", msg)) {
251                                 c.Check(*m.Private, qt.Equals, expectTrue, qt.Commentf("%q", msg))
252                         }
253                 }
254         }
255         check("d7:privatei1ee", false, true)
256         check("d7:privatei0ee", false, false)
257         check("d7:privatei42ee", false, true)
258         // This is a weird case. We could not allocate the bool to indicate it was bad (maybe a bad
259         // serializer which isn't uncommon), but that requires reworking the decoder to handle
260         // automatically. I think if we cared enough we'd create a custom Unmarshaler. Also if we were
261         // worried enough about performance I'd completely rewrite this package.
262         check("d7:private0:e", false, false)
263         check("d7:private1:te", false, true)
264         check("d7:private5:falsee", false, false)
265         check("d7:private1:Fe", false, false)
266         check("d7:private11:bunnyfoofooe", false, true)
267 }