test.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. #!/usr/bin/env lua
  2. -- Lua CJSON tests
  3. --
  4. -- Mark Pulford <[email protected]>
  5. --
  6. -- Note: The output of this script is easier to read with "less -S"
  7. local json = require "cjson"
  8. local json_safe = require "cjson.safe"
  9. local util = require "cjson.util"
  10. local function json_encode_output_type(value)
  11. local text = json.encode(value)
  12. if string.match(text, "{.*}") then
  13. return "object"
  14. elseif string.match(text, "%[.*%]") then
  15. return "array"
  16. else
  17. return "scalar"
  18. end
  19. end
  20. local function gen_raw_octets()
  21. local chars = {}
  22. for i = 0, 255 do chars[i + 1] = string.char(i) end
  23. return table.concat(chars)
  24. end
  25. -- Generate every UTF-16 codepoint, including supplementary codes
  26. local function gen_utf16_escaped()
  27. -- Create raw table escapes
  28. local utf16_escaped = {}
  29. local count = 0
  30. local function append_escape(code)
  31. local esc = ('\\u%04X'):format(code)
  32. table.insert(utf16_escaped, esc)
  33. end
  34. table.insert(utf16_escaped, '"')
  35. for i = 0, 0xD7FF do
  36. append_escape(i)
  37. end
  38. -- Skip 0xD800 - 0xDFFF since they are used to encode supplementary
  39. -- codepoints
  40. for i = 0xE000, 0xFFFF do
  41. append_escape(i)
  42. end
  43. -- Append surrogate pair for each supplementary codepoint
  44. for high = 0xD800, 0xDBFF do
  45. for low = 0xDC00, 0xDFFF do
  46. append_escape(high)
  47. append_escape(low)
  48. end
  49. end
  50. table.insert(utf16_escaped, '"')
  51. return table.concat(utf16_escaped)
  52. end
  53. function load_testdata()
  54. local data = {}
  55. -- Data for 8bit raw <-> escaped octets tests
  56. data.octets_raw = gen_raw_octets()
  57. data.octets_escaped = util.file_load("octets-escaped.dat")
  58. -- Data for \uXXXX -> UTF-8 test
  59. data.utf16_escaped = gen_utf16_escaped()
  60. -- Load matching data for utf16_escaped
  61. local utf8_loaded
  62. utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat")
  63. if not utf8_loaded then
  64. data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl"
  65. end
  66. data.table_cycle = {}
  67. data.table_cycle[1] = data.table_cycle
  68. local big = {}
  69. for i = 1, 1100 do
  70. big = { { 10, false, true, json.null }, "string", a = big }
  71. end
  72. data.deeply_nested_data = big
  73. return data
  74. end
  75. function test_decode_cycle(filename)
  76. local obj1 = json.decode(util.file_load(filename))
  77. local obj2 = json.decode(json.encode(obj1))
  78. return util.compare_values(obj1, obj2)
  79. end
  80. -- Set up data used in tests
  81. local Inf = math.huge;
  82. local NaN = math.huge * 0;
  83. local testdata = load_testdata()
  84. local cjson_tests = {
  85. -- Test API variables
  86. { "Check module name, version",
  87. function () return json._NAME, json._VERSION end, { },
  88. true, { "cjson", "2.1devel" } },
  89. -- Test decoding simple types
  90. { "Decode string",
  91. json.decode, { '"test string"' }, true, { "test string" } },
  92. { "Decode numbers",
  93. json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' },
  94. true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } },
  95. { "Decode null",
  96. json.decode, { 'null' }, true, { json.null } },
  97. { "Decode true",
  98. json.decode, { 'true' }, true, { true } },
  99. { "Decode false",
  100. json.decode, { 'false' }, true, { false } },
  101. { "Decode object with numeric keys",
  102. json.decode, { '{ "1": "one", "3": "three" }' },
  103. true, { { ["1"] = "one", ["3"] = "three" } } },
  104. { "Decode object with string keys",
  105. json.decode, { '{ "a": "a", "b": "b" }' },
  106. true, { { a = "a", b = "b" } } },
  107. { "Decode array",
  108. json.decode, { '[ "one", null, "three" ]' },
  109. true, { { "one", json.null, "three" } } },
  110. -- Test decoding errors
  111. { "Decode UTF-16BE [throw error]",
  112. json.decode, { '\0"\0"' },
  113. false, { "JSON parser does not support UTF-16 or UTF-32" } },
  114. { "Decode UTF-16LE [throw error]",
  115. json.decode, { '"\0"\0' },
  116. false, { "JSON parser does not support UTF-16 or UTF-32" } },
  117. { "Decode UTF-32BE [throw error]",
  118. json.decode, { '\0\0\0"' },
  119. false, { "JSON parser does not support UTF-16 or UTF-32" } },
  120. { "Decode UTF-32LE [throw error]",
  121. json.decode, { '"\0\0\0' },
  122. false, { "JSON parser does not support UTF-16 or UTF-32" } },
  123. { "Decode partial JSON [throw error]",
  124. json.decode, { '{ "unexpected eof": ' },
  125. false, { "Expected value but found T_END at character 21" } },
  126. { "Decode with extra comma [throw error]",
  127. json.decode, { '{ "extra data": true }, false' },
  128. false, { "Expected the end but found T_COMMA at character 23" } },
  129. { "Decode invalid escape code [throw error]",
  130. json.decode, { [[ { "bad escape \q code" } ]] },
  131. false, { "Expected object key string but found invalid escape code at character 16" } },
  132. { "Decode invalid unicode escape [throw error]",
  133. json.decode, { [[ { "bad unicode \u0f6 escape" } ]] },
  134. false, { "Expected object key string but found invalid unicode escape code at character 17" } },
  135. { "Decode invalid keyword [throw error]",
  136. json.decode, { ' [ "bad barewood", test ] ' },
  137. false, { "Expected value but found invalid token at character 20" } },
  138. { "Decode invalid number #1 [throw error]",
  139. json.decode, { '[ -+12 ]' },
  140. false, { "Expected value but found invalid number at character 3" } },
  141. { "Decode invalid number #2 [throw error]",
  142. json.decode, { '-v' },
  143. false, { "Expected value but found invalid number at character 1" } },
  144. { "Decode invalid number exponent [throw error]",
  145. json.decode, { '[ 0.4eg10 ]' },
  146. false, { "Expected comma or array end but found invalid token at character 6" } },
  147. -- Test decoding nested arrays / objects
  148. { "Set decode_max_depth(5)",
  149. json.decode_max_depth, { 5 }, true, { 5 } },
  150. { "Decode array at nested limit",
  151. json.decode, { '[[[[[ "nested" ]]]]]' },
  152. true, { {{{{{ "nested" }}}}} } },
  153. { "Decode array over nested limit [throw error]",
  154. json.decode, { '[[[[[[ "nested" ]]]]]]' },
  155. false, { "Found too many nested data structures (6) at character 6" } },
  156. { "Decode object at nested limit",
  157. json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' },
  158. true, { {a={b={c={d={e="nested"}}}}} } },
  159. { "Decode object over nested limit [throw error]",
  160. json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' },
  161. false, { "Found too many nested data structures (6) at character 26" } },
  162. { "Set decode_max_depth(1000)",
  163. json.decode_max_depth, { 1000 }, true, { 1000 } },
  164. { "Decode deeply nested array [throw error]",
  165. json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)},
  166. false, { "Found too many nested data structures (1001) at character 1001" } },
  167. -- Test encoding nested tables
  168. { "Set encode_max_depth(5)",
  169. json.encode_max_depth, { 5 }, true, { 5 } },
  170. { "Encode nested table as array at nested limit",
  171. json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } },
  172. { "Encode nested table as array after nested limit [throw error]",
  173. json.encode, { { {{{{{"nested"}}}}} } },
  174. false, { "Cannot serialise, excessive nesting (6)" } },
  175. { "Encode nested table as object at nested limit",
  176. json.encode, { {a={b={c={d={e="nested"}}}}} },
  177. true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } },
  178. { "Encode nested table as object over nested limit [throw error]",
  179. json.encode, { {a={b={c={d={e={f="nested"}}}}}} },
  180. false, { "Cannot serialise, excessive nesting (6)" } },
  181. { "Encode table with cycle [throw error]",
  182. json.encode, { testdata.table_cycle },
  183. false, { "Cannot serialise, excessive nesting (6)" } },
  184. { "Set encode_max_depth(1000)",
  185. json.encode_max_depth, { 1000 }, true, { 1000 } },
  186. { "Encode deeply nested data [throw error]",
  187. json.encode, { testdata.deeply_nested_data },
  188. false, { "Cannot serialise, excessive nesting (1001)" } },
  189. -- Test encoding simple types
  190. { "Encode null",
  191. json.encode, { json.null }, true, { 'null' } },
  192. { "Encode true",
  193. json.encode, { true }, true, { 'true' } },
  194. { "Encode false",
  195. json.encode, { false }, true, { 'false' } },
  196. { "Encode empty object",
  197. json.encode, { { } }, true, { '{}' } },
  198. { "Encode integer",
  199. json.encode, { 10 }, true, { '10' } },
  200. { "Encode string",
  201. json.encode, { "hello" }, true, { '"hello"' } },
  202. { "Encode Lua function [throw error]",
  203. json.encode, { function () end },
  204. false, { "Cannot serialise function: type not supported" } },
  205. -- Test decoding invalid numbers
  206. { "Set decode_invalid_numbers(true)",
  207. json.decode_invalid_numbers, { true }, true, { true } },
  208. { "Decode hexadecimal",
  209. json.decode, { '0x6.ffp1' }, true, { 13.9921875 } },
  210. { "Decode numbers with leading zero",
  211. json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } },
  212. { "Decode +-Inf",
  213. json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } },
  214. { "Decode +-Infinity",
  215. json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
  216. true, { { Inf, Inf, -Inf } } },
  217. { "Decode +-NaN",
  218. json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } },
  219. { "Decode Infrared (not infinity) [throw error]",
  220. json.decode, { 'Infrared' },
  221. false, { "Expected the end but found invalid token at character 4" } },
  222. { "Decode Noodle (not NaN) [throw error]",
  223. json.decode, { 'Noodle' },
  224. false, { "Expected value but found invalid token at character 1" } },
  225. { "Set decode_invalid_numbers(false)",
  226. json.decode_invalid_numbers, { false }, true, { false } },
  227. { "Decode hexadecimal [throw error]",
  228. json.decode, { '0x6' },
  229. false, { "Expected value but found invalid number at character 1" } },
  230. { "Decode numbers with leading zero [throw error]",
  231. json.decode, { '[ 0123, 00.33 ]' },
  232. false, { "Expected value but found invalid number at character 3" } },
  233. { "Decode +-Inf [throw error]",
  234. json.decode, { '[ +Inf, Inf, -Inf ]' },
  235. false, { "Expected value but found invalid token at character 3" } },
  236. { "Decode +-Infinity [throw error]",
  237. json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
  238. false, { "Expected value but found invalid token at character 3" } },
  239. { "Decode +-NaN [throw error]",
  240. json.decode, { '[ +NaN, NaN, -NaN ]' },
  241. false, { "Expected value but found invalid token at character 3" } },
  242. { 'Set decode_invalid_numbers("on")',
  243. json.decode_invalid_numbers, { "on" }, true, { true } },
  244. -- Test encoding invalid numbers
  245. { "Set encode_invalid_numbers(false)",
  246. json.encode_invalid_numbers, { false }, true, { false } },
  247. { "Encode NaN [throw error]",
  248. json.encode, { NaN },
  249. false, { "Cannot serialise number: must not be NaN or Infinity" } },
  250. { "Encode Infinity [throw error]",
  251. json.encode, { Inf },
  252. false, { "Cannot serialise number: must not be NaN or Infinity" } },
  253. { "Set encode_invalid_numbers(\"null\")",
  254. json.encode_invalid_numbers, { "null" }, true, { "null" } },
  255. { "Encode NaN as null",
  256. json.encode, { NaN }, true, { "null" } },
  257. { "Encode Infinity as null",
  258. json.encode, { Inf }, true, { "null" } },
  259. { "Set encode_invalid_numbers(true)",
  260. json.encode_invalid_numbers, { true }, true, { true } },
  261. { "Encode NaN",
  262. json.encode, { NaN }, true, { "NaN" } },
  263. { "Encode +Infinity",
  264. json.encode, { Inf }, true, { "Infinity" } },
  265. { "Encode -Infinity",
  266. json.encode, { -Inf }, true, { "-Infinity" } },
  267. { 'Set encode_invalid_numbers("off")',
  268. json.encode_invalid_numbers, { "off" }, true, { false } },
  269. -- Test encoding tables
  270. { "Set encode_sparse_array(true, 2, 3)",
  271. json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } },
  272. { "Encode sparse table as array #1",
  273. json.encode, { { [3] = "sparse test" } },
  274. true, { '[null,null,"sparse test"]' } },
  275. { "Encode sparse table as array #2",
  276. json.encode, { { [1] = "one", [4] = "sparse test" } },
  277. true, { '["one",null,null,"sparse test"]' } },
  278. { "Encode sparse array as object",
  279. json_encode_output_type, { { [1] = "one", [5] = "sparse test" } },
  280. true, { 'object' } },
  281. { "Encode table with numeric string key as object",
  282. json.encode, { { ["2"] = "numeric string key test" } },
  283. true, { '{"2":"numeric string key test"}' } },
  284. { "Set encode_sparse_array(false)",
  285. json.encode_sparse_array, { false }, true, { false, 2, 3 } },
  286. { "Encode table with incompatible key [throw error]",
  287. json.encode, { { [false] = "wrong" } },
  288. false, { "Cannot serialise boolean: table key must be a number or string" } },
  289. -- Test escaping
  290. { "Encode all octets (8-bit clean)",
  291. json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } },
  292. { "Decode all escaped octets",
  293. json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } },
  294. { "Decode single UTF-16 escape",
  295. json.decode, { [["\uF800"]] }, true, { "\239\160\128" } },
  296. { "Decode all UTF-16 escapes (including surrogate combinations)",
  297. json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } },
  298. { "Decode swapped surrogate pair [throw error]",
  299. json.decode, { [["\uDC00\uD800"]] },
  300. false, { "Expected value but found invalid unicode escape code at character 2" } },
  301. { "Decode duplicate high surrogate [throw error]",
  302. json.decode, { [["\uDB00\uDB00"]] },
  303. false, { "Expected value but found invalid unicode escape code at character 2" } },
  304. { "Decode duplicate low surrogate [throw error]",
  305. json.decode, { [["\uDB00\uDB00"]] },
  306. false, { "Expected value but found invalid unicode escape code at character 2" } },
  307. { "Decode missing low surrogate [throw error]",
  308. json.decode, { [["\uDB00"]] },
  309. false, { "Expected value but found invalid unicode escape code at character 2" } },
  310. { "Decode invalid low surrogate [throw error]",
  311. json.decode, { [["\uDB00\uD"]] },
  312. false, { "Expected value but found invalid unicode escape code at character 2" } },
  313. -- Test locale support
  314. --
  315. -- The standard Lua interpreter is ANSI C online doesn't support locales
  316. -- by default. Force a known problematic locale to test strtod()/sprintf().
  317. { "Set locale to cs_CZ (comma separator)", function ()
  318. os.setlocale("cs_CZ")
  319. json.new()
  320. end },
  321. { "Encode number under comma locale",
  322. json.encode, { 1.5 }, true, { '1.5' } },
  323. { "Decode number in array under comma locale",
  324. json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } },
  325. { "Revert locale to POSIX", function ()
  326. os.setlocale("C")
  327. json.new()
  328. end },
  329. -- Test encode_keep_buffer() and enable_number_precision()
  330. { "Set encode_keep_buffer(false)",
  331. json.encode_keep_buffer, { false }, true, { false } },
  332. { "Set encode_number_precision(3)",
  333. json.encode_number_precision, { 3 }, true, { 3 } },
  334. { "Encode number with precision 3",
  335. json.encode, { 1/3 }, true, { "0.333" } },
  336. { "Set encode_number_precision(14)",
  337. json.encode_number_precision, { 14 }, true, { 14 } },
  338. { "Set encode_keep_buffer(true)",
  339. json.encode_keep_buffer, { true }, true, { true } },
  340. -- Test config API errors
  341. -- Function is listed as '?' due to pcall
  342. { "Set encode_number_precision(0) [throw error]",
  343. json.encode_number_precision, { 0 },
  344. false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } },
  345. { "Set encode_number_precision(\"five\") [throw error]",
  346. json.encode_number_precision, { "five" },
  347. false, { "bad argument #1 to '?' (number expected, got string)" } },
  348. { "Set encode_keep_buffer(nil, true) [throw error]",
  349. json.encode_keep_buffer, { nil, true },
  350. false, { "bad argument #2 to '?' (found too many arguments)" } },
  351. { "Set encode_max_depth(\"wrong\") [throw error]",
  352. json.encode_max_depth, { "wrong" },
  353. false, { "bad argument #1 to '?' (number expected, got string)" } },
  354. { "Set decode_max_depth(0) [throw error]",
  355. json.decode_max_depth, { "0" },
  356. false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } },
  357. { "Set encode_invalid_numbers(-2) [throw error]",
  358. json.encode_invalid_numbers, { -2 },
  359. false, { "bad argument #1 to '?' (invalid option '-2')" } },
  360. { "Set decode_invalid_numbers(true, false) [throw error]",
  361. json.decode_invalid_numbers, { true, false },
  362. false, { "bad argument #2 to '?' (found too many arguments)" } },
  363. { "Set encode_sparse_array(\"not quite on\") [throw error]",
  364. json.encode_sparse_array, { "not quite on" },
  365. false, { "bad argument #1 to '?' (invalid option 'not quite on')" } },
  366. { "Reset Lua CJSON configuration", function () json = json.new() end },
  367. -- Wrap in a function to ensure the table returned by json.new() is used
  368. { "Check encode_sparse_array()",
  369. function (...) return json.encode_sparse_array(...) end, { },
  370. true, { false, 2, 10 } },
  371. { "Encode (safe) simple value",
  372. json_safe.encode, { true },
  373. true, { "true" } },
  374. { "Encode (safe) argument validation [throw error]",
  375. json_safe.encode, { "arg1", "arg2" },
  376. false, { "bad argument #1 to '?' (expected 1 argument)" } },
  377. { "Decode (safe) error generation",
  378. json_safe.decode, { "Oops" },
  379. true, { nil, "Expected value but found invalid token at character 1" } },
  380. { "Decode (safe) error generation after new()",
  381. function(...) return json_safe.new().decode(...) end, { "Oops" },
  382. true, { nil, "Expected value but found invalid token at character 1" } },
  383. }
  384. print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION))
  385. util.run_test_group(cjson_tests)
  386. for _, filename in ipairs(arg) do
  387. util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename },
  388. true, { true })
  389. end
  390. local pass, total = util.run_test_summary()
  391. if pass == total then
  392. print("==> Summary: all tests succeeded")
  393. else
  394. print(("==> Summary: %d/%d tests failed"):format(total - pass, total))
  395. os.exit(1)
  396. end
  397. -- vi:ai et sw=4 ts=4: