1 local json
= require("json")
2 local lunit
= require("lunit")
3 local testutil
= require("testutil")
4 local string= require("string")
6 local encode
= json
.encode
7 -- DECODE NOT 'local' due to requirement for testutil to access it
8 decode
= json
.decode
.getDecoder(false)
13 _ENV
= lunit
.module("lunit-strings", 'seeall')
15 module("lunit-strings", lunit
.testcase
, package
.seeall
)
18 local function assert_table_equal(expect
, t
)
19 if type(expect
) ~= 'table' then
20 return assert_equal(expect
, t
)
22 for k
,v
in pairs(expect
) do
23 if type(k
) ~= 'string' and type(k
) ~= 'number' and type(k
) ~= 'boolean' then
24 error("INVALID expected table key")
28 fail(tostring(k
) .. " not found but expected")
30 assert_table_equal(v
, t
[k
])
32 for k
,v
in pairs(t
) do
33 if nil == expect
[k
] then
34 fail(tostring(k
) .. " found but not expected")
40 -- Ensure that the decoder is reset
41 _G
["decode"] = json
.decode
.getDecoder(false)
44 function test_strict_quotes()
50 assert_error(function()
51 local decoder
= json
.decode
.getDecoder(opts
)
54 opts
.strings
.strict_quotes
= false
55 assert_equal("hello", json
.decode
.getDecoder(opts
)("'hello'"))
57 assert_equal("he'\"llo'", json
.decode
.getDecoder(opts
)("'he\\'\"llo\\''"))
61 local utf16_matches
= {
63 { '"\\u0000"', string.char(0x00) },
64 { '"\\u007F"', string.char(0x7F) },
66 { '"\\u0080"', string.char(0xC2, 0x80) },
67 { '"\\u00A2"', string.char(0xC2, 0xA2) },
68 { '"\\u07FF"', string.char(0xDF, 0xBF) },
70 { '"\\u0800"', string.char(0xE0, 0xA0, 0x80) },
71 { '"\\u20AC"', string.char(0xE2, 0x82, 0xAC) },
72 { '"\\uFEFF"', string.char(0xEF, 0xBB, 0xBF) },
73 { '"\\uFFFF"', string.char(0xEF, 0xBF, 0xBF) },
74 -- 4-byte - currently not handled
75 --{ '"\\uD800\\uDC00"', string.char(0xF0, 0x90, 0x80, 0x80) },
76 --{ '"\\uDBFF\\uDFFF"', string.char(0xF4, 0x8F, 0xBF, 0xBF) }
80 function test_utf16_decode()
81 for i
, v
in ipairs(utf16_matches
) do
82 -- Test that the default \u decoder outputs UTF8
83 local num
= tostring(i
) .. ' '
84 assert_equal(num
.. v
[2], num
.. json
.decode(v
[1]))
88 local BOM
= string.char(0xEF, 0xBB, 0xBF)
89 -- BOM skipping tests - here due to relation to UTF8/16
90 local BOM_skip_tests
= {
91 { BOM
.. '"x"', "x" },
92 { BOM
.. '["\\uFFFF",true]', { string.char(0xEF, 0xBF, 0xBF), true } },
93 -- Other uses of unicode spaces
96 function test_bom_skip()
97 for i
,v
in ipairs(BOM_skip_tests
) do
98 assert_table_equal(v
[2], json
.decode(v
[1]))
102 -- Unicode whitespace codepoints gleaned from unicode.org
103 local WHITESPACES
= {
125 "\\u200B", -- addition, zero-width space
131 "\\uFEFF" -- Zero-width non-breaking space (BOM)
134 local inject_ws_values
= {
136 " %WS%'the%WS blob' %WS%",
137 "%WS%{ key: %WS%\"valueMan\",%WS% key2:%WS%4.4}",
140 function test_whitespace_ignore()
141 for _
, ws
in ipairs(WHITESPACES
) do
142 ws
= json
.decode('"' .. ws
.. '"')
143 for _
, v
in ipairs(inject_ws_values
) do
144 v
= v
:gsub("%%WS%%", ws
)
145 assert_true(nil ~= json
.decode(v
))
150 function test_u_encoding()
151 local encoder
= json
.encode
.getEncoder()
152 local decoder
= json
.decode
.getDecoder()
154 local char
= string.char(i
)
155 assert_equal(char
, decoder(encoder(char
)))
159 function test_x_encoding()
160 local encoder
= json
.encode
.getEncoder({ strings
= { xEncode
= true } })
161 local decoder
= json
.decode
.getDecoder()
163 local char
= string.char(i
)
164 assert_equal(char
, decoder(encoder(char
)))
168 local multibyte_encoding_values
= {
170 { '"\\u0080"', string.char(0xC2, 0x80) },
171 { '"\\u00A2"', string.char(0xC2, 0xA2) },
172 { '"\\u07FF"', string.char(0xDF, 0xBF) },
174 { '"\\u0800"', string.char(0xE0, 0xA0, 0x80) },
175 { '"\\u20AC"', string.char(0xE2, 0x82, 0xAC) },
176 { '"\\uFEFF"', string.char(0xEF, 0xBB, 0xBF) },
177 { '"\\uFFFF"', string.char(0xEF, 0xBF, 0xBF) },
178 -- 4-byte (surrogate pairs)
179 { '"\\uD800\\uDC00"', string.char(0xF0, 0x90, 0x80, 0x80) },
180 { '"\\uDBFF\\uDFFF"', string.char(0xF4, 0x8F, 0xBF, 0xBF) }
183 function test_custom_encoding()
184 local function processor(s
)
185 return require("utf8_processor").process(s
)
187 local encoder
= json
.encode
.getEncoder({
189 processor
= processor
192 for i
, v
in ipairs(multibyte_encoding_values
) do
193 local encoded
= encoder(v
[2])
194 assert_equal(v
[1], encoded
, "Failed to encode value using custom encoder")
198 function test_strict_decoding()
199 local encoder
= json
.encode
.getEncoder(json
.encode
.strict
)
200 local decoder
= json
.decode
.getDecoder(json
.decode
.strict
)
202 local char
= string.char(i
)
203 -- Must wrap character in array due to decoder strict-ness
204 assert_equal(char
, decoder(encoder({char
}))[1])