File: msgpack.test.lua

package info (click to toggle)
tarantool 2.6.0-1.4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 85,412 kB
  • sloc: ansic: 513,775; cpp: 69,493; sh: 25,650; python: 19,190; perl: 14,973; makefile: 4,178; yacc: 1,329; sql: 1,074; pascal: 620; ruby: 190; awk: 18; lisp: 7
file content (277 lines) | stat: -rwxr-xr-x 8,952 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#!/usr/bin/env tarantool

package.path = "lua/?.lua;"..package.path

local tap = require('tap')
local common = require('serializer_test')

local function is_map(s)
    local b = string.byte(string.sub(s, 1, 1))
    return b >= 0x80 and b <= 0x8f or b == 0xde or b == 0xdf
end

local function is_array(s)
    local b = string.byte(string.sub(s, 1, 1))
    return b >= 0x90 and b <= 0x9f or b == 0xdc or b == 0xdd
end

local function test_offsets(test, s)
    test:plan(6)
    local arr1 = {1, 2, 3}
    local arr2 = {4, 5, 6}
    local dump = s.encode(arr1)..s.encode(arr2)
    test:is(dump:len(), 8, "length of part1 + part2")

    local a
    local offset = 1
    a, offset = s.decode(dump, offset)
    test:is_deeply(a, arr1, "decoded part1")
    test:is(offset, 5, "offset of part2")

    a, offset = s.decode(dump, offset)
    test:is_deeply(a, arr2, "decoded part2")
    test:is(offset, 9, "offset of end")

    test:ok(not pcall(s.decode, dump, offset), "invalid offset")
end

local function test_misc(test, s)
    test:plan(5)
    local ffi = require('ffi')
    local buffer = require('buffer')
    local buf = ffi.cast("const char *", "\x91\x01")
    local bufcopy = ffi.cast('const char *', buf)
    local bufend, result = s.ibuf_decode(buf)
    local st,e = pcall(s.ibuf_decode, buffer.ibuf().rpos)
    test:is(buf, bufcopy, "ibuf_decode argument is constant")
    test:is(buf + 2, bufend, 'ibuf_decode position')
    test:is_deeply(result, {1}, "ibuf_decode result")
    test:ok(not st and e:match("null"), "null ibuf")
    st, e = pcall(s.decode, "\xd4\x0f\x00")
    test:ok(not st and e:match("Unsuported MsgPack extension type: 15"),
                               "unsupported extension decode")
end

local function test_decode_array_map_header(test, s)
    local ffi = require('ffi')

    local usage_err = 'Usage: msgpack%.decode_[^_(]+_header%(ptr, size%)'
    local end_of_buffer_err = 'msgpack%.decode_[^_]+_header: unexpected end ' ..
        'of buffer'
    local non_positive_size_err = 'msgpack.decode_[^_]+_header: ' ..
        'non%-positive size'
    local wrong_type_err = "msgpack.decode_[^_]+_header: 'char %*' expected"

    local decode_cases = {
        {
            'fixarray',
            func = s.decode_array_header,
            data = ffi.cast('char *', '\x94'),
            size = 1,
            exp_len = 4,
            exp_rewind = 1,
        },
        {
            'array 16',
            func = s.decode_array_header,
            data = ffi.cast('char *', '\xdc\x00\x04'),
            size = 3,
            exp_len = 4,
            exp_rewind = 3,
        },
        {
            'array 32',
            func = s.decode_array_header,
            data = ffi.cast('char *', '\xdd\x00\x00\x00\x04'),
            size = 5,
            exp_len = 4,
            exp_rewind = 5,
        },
        {
            'truncated array 16',
            func = s.decode_array_header,
            data = ffi.cast('char *', '\xdc\x00'),
            size = 2,
            exp_err = end_of_buffer_err,
        },
        {
            'truncated array 32',
            func = s.decode_array_header,
            data = ffi.cast('char *', '\xdd\x00\x00\x00'),
            size = 4,
            exp_err = end_of_buffer_err,
        },
        {
            'fixmap',
            func = s.decode_map_header,
            data = ffi.cast('char *', '\x84'),
            size = 1,
            exp_len = 4,
            exp_rewind = 1,
        },
        {
            'map 16',
            func = s.decode_map_header,
            data = ffi.cast('char *', '\xde\x00\x04'),
            size = 3,
            exp_len = 4,
            exp_rewind = 3,
        },
        {
            'array 32',
            func = s.decode_map_header,
            data = ffi.cast('char *', '\xdf\x00\x00\x00\x04'),
            size = 5,
            exp_len = 4,
            exp_rewind = 5,
        },
        {
            'truncated map 16',
            func = s.decode_map_header,
            data = ffi.cast('char *', '\xde\x00'),
            size = 2,
            exp_err = end_of_buffer_err,
        },
        {
            'truncated map 32',
            func = s.decode_map_header,
            data = ffi.cast('char *', '\xdf\x00\x00\x00'),
            size = 4,
            exp_err = end_of_buffer_err,
        },
        -- gh-3926: Ensure that a returned pointer has the same
        -- cdata type as passed argument.
        --
        -- cdata<char *> arguments are passed in the cases above,
        -- so only cdata<const char *> argument is checked here.
        {
            'fixarray (const char *)',
            func = s.decode_array_header,
            data = ffi.cast('const char *', '\x94'),
            size = 1,
            exp_len = 4,
            exp_rewind = 1,
        },
        {
            'fixmap (const char *)',
            func = s.decode_map_header,
            data = ffi.cast('const char *', '\x84'),
            size = 1,
            exp_len = 4,
            exp_rewind = 1,
        },
    }

    local bad_api_cases = {
        {
            'wrong msgpack type',
            data = ffi.cast('char *', '\xc0'),
            size = 1,
            exp_err = 'msgpack.decode_[^_]+_header: unexpected msgpack type',
        },
        {
            'zero size buffer',
            data = ffi.cast('char *', ''),
            size = 0,
            exp_err = non_positive_size_err,
        },
        {
            'negative size buffer',
            data = ffi.cast('char *', ''),
            size = -1,
            exp_err = non_positive_size_err,
        },
        {
            'size is nil',
            data = ffi.cast('char *', ''),
            size = nil,
            exp_err = 'bad argument',
        },
        {
            'no arguments',
            args = {},
            exp_err = usage_err,
        },
        {
            'one argument',
            args = {ffi.cast('char *', '')},
            exp_err = usage_err,
        },
        {
            'data is nil',
            args = {nil, 1},
            exp_err = wrong_type_err,
        },
        {
            'data is not cdata',
            args = {1, 1},
            exp_err = wrong_type_err,
        },
        {
            'data with wrong cdata type',
            args = {box.tuple.new(), 1},
            exp_err = wrong_type_err,
        },
        {
            'size has wrong type',
            args = {ffi.cast('char *', ''), 'eee'},
            exp_err = 'bad argument',
        },
    }

    test:plan(#decode_cases + 2 * #bad_api_cases)

    -- Decode cases.
    for _, case in ipairs(decode_cases) do
        if case.exp_err ~= nil then
            local ok, err = pcall(case.func, case.data, case.size)
            local description = ('bad; %s'):format(case[1])
            test:ok(ok == false and err:match(case.exp_err), description)
        else
            local len, new_buf = case.func(case.data, case.size)
            local rewind = new_buf - case.data

            -- gh-3926: Verify cdata type of a returned buffer.
            local data_ctype = tostring(ffi.typeof(case.data))
            local new_buf_ctype = tostring(ffi.typeof(new_buf))

            local description = ('good; %s'):format(case[1])
            test:is_deeply({len, rewind, new_buf_ctype}, {case.exp_len,
                case.exp_rewind, data_ctype}, description)
        end
    end

    -- Bad api usage cases.
    for _, func_name in ipairs({'decode_array_header', 'decode_map_header'}) do
        for _, case in ipairs(bad_api_cases) do
            local ok, err
            if case.args ~= nil then
                local args_len = table.maxn(case.args)
                ok, err = pcall(s[func_name], unpack(case.args, 1, args_len))
            else
                ok, err = pcall(s[func_name], case.data, case.size)
            end
            local description = ('%s bad api usage; %s'):format(func_name,
                                                                case[1])
            test:ok(ok == false and err:match(case.exp_err), description)
        end
    end
end

tap.test("msgpack", function(test)
    local serializer = require('msgpack')
    test:plan(13)
    test:test("unsigned", common.test_unsigned, serializer)
    test:test("signed", common.test_signed, serializer)
    test:test("double", common.test_double, serializer)
    test:test("boolean", common.test_boolean, serializer)
    test:test("string", common.test_string, serializer)
    test:test("nil", common.test_nil, serializer)
    test:test("table", common.test_table, serializer, is_array, is_map)
    test:test("ucdata", common.test_ucdata, serializer)
    test:test("depth", common.test_depth, serializer)
    test:test("offsets", test_offsets, serializer)
    test:test("misc", test_misc, serializer)
    test:test("decode_array_map", test_decode_array_map_header, serializer)
    test:test("decode_buffer", common.test_decode_buffer, serializer)
end)