aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/README4
-rwxr-xr-xtests/bench.lua83
-rw-r--r--tests/cjson-misc.lua253
-rw-r--r--tests/example1.json22
-rw-r--r--tests/example2.json11
-rw-r--r--tests/example3.json26
-rw-r--r--tests/example4.json88
-rw-r--r--tests/example5.json27
-rwxr-xr-xtests/genutf8.pl23
-rwxr-xr-xtests/json2lua.lua14
-rwxr-xr-xtests/lua2json.lua42
-rw-r--r--tests/numbers.json7
-rw-r--r--tests/octets-escaped.dat1
-rw-r--r--tests/rfc-example1.json13
-rw-r--r--tests/rfc-example2.json22
-rwxr-xr-xtests/test.lua252
-rw-r--r--tests/types.json1
17 files changed, 889 insertions, 0 deletions
diff --git a/tests/README b/tests/README
new file mode 100644
index 0000000..39e8bd4
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,4 @@
1These JSON examples were taken from the JSON website
2(http://json.org/example.html) and RFC 4627.
3
4Used with permission.
diff --git a/tests/bench.lua b/tests/bench.lua
new file mode 100755
index 0000000..2b5177b
--- /dev/null
+++ b/tests/bench.lua
@@ -0,0 +1,83 @@
1#!/usr/bin/env lua
2
3-- Simple JSON benchmark.
4--
5-- Your Mileage May Vary.
6--
7-- Mark Pulford <mark@kyne.com.au>
8
9require "socket"
10local json = require "cjson"
11local misc = require "cjson-misc"
12
13function benchmark(tests, seconds, rep)
14 local function bench(func, iter)
15 -- collectgarbage("stop")
16 collectgarbage("collect")
17 local t = socket.gettime()
18 for i = 1, iter do
19 func(i)
20 end
21 t = socket.gettime() - t
22 -- collectgarbage("restart")
23 return (iter / t)
24 end
25
26 -- Roughly calculate the number of interations required
27 -- to obtain a particular time period.
28 local function calc_iter(func, seconds)
29 local base_iter = 10
30 local rate = (bench(func, base_iter) + bench(func, base_iter)) / 2
31 return math.ceil(seconds * rate)
32 end
33
34 local test_results = {}
35 for name, func in pairs(tests) do
36 -- k(number), v(string)
37 -- k(string), v(function)
38 -- k(number), v(function)
39 if type(func) == "string" then
40 name = func
41 func = _G[name]
42 end
43 local iter = calc_iter(func, seconds)
44 local result = {}
45 for i = 1, rep do
46 result[i] = bench(func, iter)
47 end
48 table.sort(result)
49 test_results[name] = result[rep]
50 end
51
52 return test_results
53end
54
55function bench_file(filename)
56 local data_json = misc.file_load(filename)
57 local data_obj = json.decode(data_json)
58
59 local function test_encode ()
60 json.encode(data_obj)
61 end
62 local function test_decode ()
63 json.decode(data_json)
64 end
65
66 local tests = {
67 encode = test_encode,
68 decode = test_decode
69 }
70
71 return benchmark(tests, 0.1, 5)
72end
73
74json.encode_keep_buffer(true)
75
76for i = 1, #arg do
77 local results = bench_file(arg[i])
78 for k, v in pairs(results) do
79 print(string.format("%s: %s: %d", arg[i], k, v))
80 end
81end
82
83-- vi:ai et sw=4 ts=4:
diff --git a/tests/cjson-misc.lua b/tests/cjson-misc.lua
new file mode 100644
index 0000000..d8bfe5e
--- /dev/null
+++ b/tests/cjson-misc.lua
@@ -0,0 +1,253 @@
1local json = require "cjson"
2
3-- Misc routines to assist with CJSON testing
4--
5-- Mark Pulford <mark@kyne.com.au>
6
7-- Determine with a Lua table can be treated as an array.
8-- Explicitly returns "not an array" for very sparse arrays.
9-- Returns:
10-- -1 Not an array
11-- 0 Empty table
12-- >0 Highest index in the array
13local function is_array(table)
14 local max = 0
15 local count = 0
16 for k, v in pairs(table) do
17 if type(k) == "number" then
18 if k > max then max = k end
19 count = count + 1
20 else
21 return -1
22 end
23 end
24 if max > count * 2 then
25 return -1
26 end
27
28 return max
29end
30
31local serialise_value
32
33local function serialise_table(value, indent, depth)
34 local spacing, spacing2, indent2
35 if indent then
36 spacing = "\n" .. indent
37 spacing2 = spacing .. " "
38 indent2 = indent .. " "
39 else
40 spacing, spacing2, indent2 = " ", " ", false
41 end
42 depth = depth + 1
43 if depth > 50 then
44 return "Cannot serialise any further: too many nested tables"
45 end
46
47 local max = is_array(value)
48
49 local comma = false
50 local fragment = { "{" .. spacing2 }
51 if max > 0 then
52 -- Serialise array
53 for i = 1, max do
54 if comma then
55 table.insert(fragment, "," .. spacing2)
56 end
57 table.insert(fragment, serialise_value(value[i], indent2, depth))
58 comma = true
59 end
60 elseif max < 0 then
61 -- Serialise table
62 for k, v in pairs(value) do
63 if comma then
64 table.insert(fragment, "," .. spacing2)
65 end
66 table.insert(fragment, string.format(
67 "[%s] = %s", serialise_value(k, indent2, depth),
68 serialise_value(v, indent2, depth))
69 )
70 comma = true
71 end
72 end
73 table.insert(fragment, spacing .. "}")
74
75 return table.concat(fragment)
76end
77
78function serialise_value(value, indent, depth)
79 if indent == nil then indent = "" end
80 if depth == nil then depth = 0 end
81
82 if value == json.null then
83 return "json.null"
84 elseif type(value) == "string" then
85 return string.format("%q", value)
86 elseif type(value) == "nil" or type(value) == "number" or
87 type(value) == "boolean" then
88 return tostring(value)
89 elseif type(value) == "table" then
90 return serialise_table(value, indent, depth)
91 else
92 return "\"<" .. type(value) .. ">\""
93 end
94end
95
96local function file_load(filename)
97 local file
98 if filename == nil then
99 file = io.stdin
100 else
101 local err
102 file, err = io.open(filename)
103 if file == nil then
104 error(string.format("Unable to read '%s': %s", filename, err))
105 end
106 end
107 local data = file:read("*a")
108
109 if filename ~= nil then
110 file:close()
111 end
112
113 if data == nil then
114 error("Failed to read " .. filename)
115 end
116
117 return data
118end
119
120local function file_save(filename, data)
121 local file
122 if filename == nil then
123 file = io.stdout
124 else
125 local err
126 file, err = io.open(filename, "w")
127 if file == nil then
128 error(string.format("Unable to write '%s': %s", filename, err))
129 end
130 end
131 file:write(data)
132 if filename ~= nil then
133 file:close()
134 end
135end
136
137local function compare_values(val1, val2)
138 local type1 = type(val1)
139 local type2 = type(val2)
140 if type1 ~= type2 then
141 return false
142 end
143
144 -- Check for NaN
145 if type1 == "number" and val1 ~= val1 and val2 ~= val2 then
146 return true
147 end
148
149 if type1 ~= "table" then
150 return val1 == val2
151 end
152
153 -- check_keys stores all the keys that must be checked in val2
154 local check_keys = {}
155 for k, _ in pairs(val1) do
156 check_keys[k] = true
157 end
158
159 for k, v in pairs(val2) do
160 if not check_keys[k] then
161 return false
162 end
163
164 if not compare_values(val1[k], val2[k]) then
165 return false
166 end
167
168 check_keys[k] = nil
169 end
170 for k, _ in pairs(check_keys) do
171 -- Not the same if any keys from val1 were not found in val2
172 return false
173 end
174 return true
175end
176
177local test_count_pass = 0
178local test_count_total = 0
179
180local function run_test_summary()
181 return test_count_pass, test_count_total
182end
183
184local function run_test(testname, func, input, should_work, output)
185 local function status_line(name, status, value)
186 local statusmap = { [true] = ":success", [false] = ":error" }
187 if status ~= nil then
188 name = name .. statusmap[status]
189 end
190 print(string.format("[%s] %s", name, serialise_value(value, false)))
191 end
192
193 local result = { pcall(func, unpack(input)) }
194 local success = table.remove(result, 1)
195
196 local correct = false
197 if success == should_work and compare_values(result, output) then
198 correct = true
199 test_count_pass = test_count_pass + 1
200 end
201 test_count_total = test_count_total + 1
202
203 local teststatus = { [true] = "PASS", [false] = "FAIL" }
204 print(string.format("==> Test[%d] / %s: %s",
205 test_count_total, testname, teststatus[correct]))
206
207 status_line("Input", nil, input)
208 if not correct then
209 status_line("Expected", should_work, output)
210 end
211 status_line("Received", success, result)
212 print()
213
214 return correct, result
215end
216
217local function run_test_group(testgroup, tests)
218 local function run_config(configname, func)
219 local success, msg = pcall(func)
220 if msg then
221 print(string.format("==> Config %s: %s", configname, msg))
222 end
223 print()
224 end
225
226 local function test_id(group, id)
227 return string.format("%s[%d]", group, id)
228 end
229
230 for k, v in ipairs(tests) do
231 if type(v) == "function" then
232 -- Useful for changing configuration during a batch
233 run_config(test_id(testgroup, k), v)
234 elseif type(v) == "table" then
235 run_test(test_id(testgroup, k), unpack(v))
236 else
237 error("Testgroup can only contain functions and tables")
238 end
239 end
240end
241
242-- Export functions
243return {
244 serialise_value = serialise_value,
245 file_load = file_load,
246 file_save = file_save,
247 compare_values = compare_values,
248 run_test_summary = run_test_summary,
249 run_test = run_test,
250 run_test_group = run_test_group
251}
252
253-- vi:ai et sw=4 ts=4:
diff --git a/tests/example1.json b/tests/example1.json
new file mode 100644
index 0000000..42486ce
--- /dev/null
+++ b/tests/example1.json
@@ -0,0 +1,22 @@
1{
2 "glossary": {
3 "title": "example glossary",
4 "GlossDiv": {
5 "title": "S",
6 "GlossList": {
7 "GlossEntry": {
8 "ID": "SGML",
9 "SortAs": "SGML",
10 "GlossTerm": "Standard Generalized Mark up Language",
11 "Acronym": "SGML",
12 "Abbrev": "ISO 8879:1986",
13 "GlossDef": {
14 "para": "A meta-markup language, used to create markup languages such as DocBook.",
15 "GlossSeeAlso": ["GML", "XML"]
16 },
17 "GlossSee": "markup"
18 }
19 }
20 }
21 }
22}
diff --git a/tests/example2.json b/tests/example2.json
new file mode 100644
index 0000000..5600991
--- /dev/null
+++ b/tests/example2.json
@@ -0,0 +1,11 @@
1{"menu": {
2 "id": "file",
3 "value": "File",
4 "popup": {
5 "menuitem": [
6 {"value": "New", "onclick": "CreateNewDoc()"},
7 {"value": "Open", "onclick": "OpenDoc()"},
8 {"value": "Close", "onclick": "CloseDoc()"}
9 ]
10 }
11}}
diff --git a/tests/example3.json b/tests/example3.json
new file mode 100644
index 0000000..d7237a5
--- /dev/null
+++ b/tests/example3.json
@@ -0,0 +1,26 @@
1{"widget": {
2 "debug": "on",
3 "window": {
4 "title": "Sample Konfabulator Widget",
5 "name": "main_window",
6 "width": 500,
7 "height": 500
8 },
9 "image": {
10 "src": "Images/Sun.png",
11 "name": "sun1",
12 "hOffset": 250,
13 "vOffset": 250,
14 "alignment": "center"
15 },
16 "text": {
17 "data": "Click Here",
18 "size": 36,
19 "style": "bold",
20 "name": "text1",
21 "hOffset": 250,
22 "vOffset": 100,
23 "alignment": "center",
24 "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
25 }
26}}
diff --git a/tests/example4.json b/tests/example4.json
new file mode 100644
index 0000000..d31a395
--- /dev/null
+++ b/tests/example4.json
@@ -0,0 +1,88 @@
1{"web-app": {
2 "servlet": [
3 {
4 "servlet-name": "cofaxCDS",
5 "servlet-class": "org.cofax.cds.CDSServlet",
6 "init-param": {
7 "configGlossary:installationAt": "Philadelphia, PA",
8 "configGlossary:adminEmail": "ksm@pobox.com",
9 "configGlossary:poweredBy": "Cofax",
10 "configGlossary:poweredByIcon": "/images/cofax.gif",
11 "configGlossary:staticPath": "/content/static",
12 "templateProcessorClass": "org.cofax.WysiwygTemplate",
13 "templateLoaderClass": "org.cofax.FilesTemplateLoader",
14 "templatePath": "templates",
15 "templateOverridePath": "",
16 "defaultListTemplate": "listTemplate.htm",
17 "defaultFileTemplate": "articleTemplate.htm",
18 "useJSP": false,
19 "jspListTemplate": "listTemplate.jsp",
20 "jspFileTemplate": "articleTemplate.jsp",
21 "cachePackageTagsTrack": 200,
22 "cachePackageTagsStore": 200,
23 "cachePackageTagsRefresh": 60,
24 "cacheTemplatesTrack": 100,
25 "cacheTemplatesStore": 50,
26 "cacheTemplatesRefresh": 15,
27 "cachePagesTrack": 200,
28 "cachePagesStore": 100,
29 "cachePagesRefresh": 10,
30 "cachePagesDirtyRead": 10,
31 "searchEngineListTemplate": "forSearchEnginesList.htm",
32 "searchEngineFileTemplate": "forSearchEngines.htm",
33 "searchEngineRobotsDb": "WEB-INF/robots.db",
34 "useDataStore": true,
35 "dataStoreClass": "org.cofax.SqlDataStore",
36 "redirectionClass": "org.cofax.SqlRedirection",
37 "dataStoreName": "cofax",
38 "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
39 "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
40 "dataStoreUser": "sa",
41 "dataStorePassword": "dataStoreTestQuery",
42 "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
43 "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
44 "dataStoreInitConns": 10,
45 "dataStoreMaxConns": 100,
46 "dataStoreConnUsageLimit": 100,
47 "dataStoreLogLevel": "debug",
48 "maxUrlLength": 500}},
49 {
50 "servlet-name": "cofaxEmail",
51 "servlet-class": "org.cofax.cds.EmailServlet",
52 "init-param": {
53 "mailHost": "mail1",
54 "mailHostOverride": "mail2"}},
55 {
56 "servlet-name": "cofaxAdmin",
57 "servlet-class": "org.cofax.cds.AdminServlet"},
58
59 {
60 "servlet-name": "fileServlet",
61 "servlet-class": "org.cofax.cds.FileServlet"},
62 {
63 "servlet-name": "cofaxTools",
64 "servlet-class": "org.cofax.cms.CofaxToolsServlet",
65 "init-param": {
66 "templatePath": "toolstemplates/",
67 "log": 1,
68 "logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
69 "logMaxSize": "",
70 "dataLog": 1,
71 "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
72 "dataLogMaxSize": "",
73 "removePageCache": "/content/admin/remove?cache=pages&id=",
74 "removeTemplateCache": "/content/admin/remove?cache=templates&id=",
75 "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
76 "lookInContext": 1,
77 "adminGroupID": 4,
78 "betaServer": true}}],
79 "servlet-mapping": {
80 "cofaxCDS": "/",
81 "cofaxEmail": "/cofaxutil/aemail/*",
82 "cofaxAdmin": "/admin/*",
83 "fileServlet": "/static/*",
84 "cofaxTools": "/tools/*"},
85
86 "taglib": {
87 "taglib-uri": "cofax.tld",
88 "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
diff --git a/tests/example5.json b/tests/example5.json
new file mode 100644
index 0000000..49980ca
--- /dev/null
+++ b/tests/example5.json
@@ -0,0 +1,27 @@
1{"menu": {
2 "header": "SVG Viewer",
3 "items": [
4 {"id": "Open"},
5 {"id": "OpenNew", "label": "Open New"},
6 null,
7 {"id": "ZoomIn", "label": "Zoom In"},
8 {"id": "ZoomOut", "label": "Zoom Out"},
9 {"id": "OriginalView", "label": "Original View"},
10 null,
11 {"id": "Quality"},
12 {"id": "Pause"},
13 {"id": "Mute"},
14 null,
15 {"id": "Find", "label": "Find..."},
16 {"id": "FindAgain", "label": "Find Again"},
17 {"id": "Copy"},
18 {"id": "CopyAgain", "label": "Copy Again"},
19 {"id": "CopySVG", "label": "Copy SVG"},
20 {"id": "ViewSVG", "label": "View SVG"},
21 {"id": "ViewSource", "label": "View Source"},
22 {"id": "SaveAs", "label": "Save As"},
23 null,
24 {"id": "Help"},
25 {"id": "About", "label": "About Adobe CVG Viewer..."}
26 ]
27}}
diff --git a/tests/genutf8.pl b/tests/genutf8.pl
new file mode 100755
index 0000000..db661a1
--- /dev/null
+++ b/tests/genutf8.pl
@@ -0,0 +1,23 @@
1#!/usr/bin/env perl
2
3# Create test comparison data using a different UTF-8 implementation.
4
5# The generated utf8.dat file must have the following MD5 sum:
6# cff03b039d850f370a7362f3313e5268
7
8use strict;
9
10# 0xD800 - 0xDFFF are used to encode supplementary codepoints
11# 0x10000 - 0x10FFFF are supplementary codepoints
12my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF);
13
14my $utf8 = pack("U*", @codepoints);
15defined($utf8) or die "Unable create UTF-8 string\n";
16
17open(FH, ">:utf8", "utf8.dat")
18 or die "Unable to open utf8.dat: $!\n";
19print FH $utf8
20 or die "Unable to write utf8.dat\n";
21close(FH);
22
23# vi:ai et sw=4 ts=4:
diff --git a/tests/json2lua.lua b/tests/json2lua.lua
new file mode 100755
index 0000000..050b89d
--- /dev/null
+++ b/tests/json2lua.lua
@@ -0,0 +1,14 @@
1#!/usr/bin/env lua
2
3-- usage: json2lua.lua [json_file]
4--
5-- Eg:
6-- echo '[ "testing" ]' | ./json2lua.lua
7-- ./json2lua.lua test.json
8
9local json = require "cjson"
10local misc = require "cjson-misc"
11
12local json_text = misc.file_load(arg[1])
13local t = json.decode(json_text)
14print(misc.serialise_value(t))
diff --git a/tests/lua2json.lua b/tests/lua2json.lua
new file mode 100755
index 0000000..ebe7380
--- /dev/null
+++ b/tests/lua2json.lua
@@ -0,0 +1,42 @@
1#!/usr/bin/env lua
2
3-- usage: lua2json.lua [lua_file]
4--
5-- Eg:
6-- echo '{ "testing" }' | ./lua2json.lua
7-- ./lua2json.lua test.lua
8
9local json = require "cjson"
10local misc = require "cjson-misc"
11
12function get_lua_table(s)
13 local env = {}
14 local func
15
16 env.json = {}
17 env.json.null = json.null
18 env.null = json.null
19 s = "data = " .. s
20
21 -- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists
22 if _G.setfenv then
23 func = loadstring(s)
24 if func then
25 setfenv(func, env)
26 end
27 else
28 func = load(s, nil, nil, env)
29 end
30
31 if func == nil then
32 error("Invalid syntax. Failed to parse Lua table.")
33 end
34 func()
35
36 return env.data
37end
38
39local t = get_lua_table(misc.file_load(arg[1]))
40print(json.encode(t))
41
42-- vi:ai et sw=4 ts=4:
diff --git a/tests/numbers.json b/tests/numbers.json
new file mode 100644
index 0000000..ef11a26
--- /dev/null
+++ b/tests/numbers.json
@@ -0,0 +1,7 @@
1[ 0.110001000000000000000001,
2 0.12345678910111213141516,
3 0.412454033640,
4 2.6651441426902251886502972498731,
5 2.718281828459045235360287471352,
6 3.141592653589793238462643383279,
7 2.14069263277926 ]
diff --git a/tests/octets-escaped.dat b/tests/octets-escaped.dat
new file mode 100644
index 0000000..ee99a6b
--- /dev/null
+++ b/tests/octets-escaped.dat
@@ -0,0 +1 @@
"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-.\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ" \ No newline at end of file
diff --git a/tests/rfc-example1.json b/tests/rfc-example1.json
new file mode 100644
index 0000000..73532fa
--- /dev/null
+++ b/tests/rfc-example1.json
@@ -0,0 +1,13 @@
1{
2 "Image": {
3 "Width": 800,
4 "Height": 600,
5 "Title": "View from 15th Floor",
6 "Thumbnail": {
7 "Url": "http://www.example.com/image/481989943",
8 "Height": 125,
9 "Width": "100"
10 },
11 "IDs": [116, 943, 234, 38793]
12 }
13}
diff --git a/tests/rfc-example2.json b/tests/rfc-example2.json
new file mode 100644
index 0000000..2a0cb68
--- /dev/null
+++ b/tests/rfc-example2.json
@@ -0,0 +1,22 @@
1[
2 {
3 "precision": "zip",
4 "Latitude": 37.7668,
5 "Longitude": -122.3959,
6 "Address": "",
7 "City": "SAN FRANCISCO",
8 "State": "CA",
9 "Zip": "94107",
10 "Country": "US"
11 },
12 {
13 "precision": "zip",
14 "Latitude": 37.371991,
15 "Longitude": -122.026020,
16 "Address": "",
17 "City": "SUNNYVALE",
18 "State": "CA",
19 "Zip": "94085",
20 "Country": "US"
21 }
22]
diff --git a/tests/test.lua b/tests/test.lua
new file mode 100755
index 0000000..c860878
--- /dev/null
+++ b/tests/test.lua
@@ -0,0 +1,252 @@
1#!/usr/bin/env lua
2
3-- CJSON tests
4--
5-- Mark Pulford <mark@kyne.com.au>
6--
7-- Note: The output of this script is easier to read with "less -S"
8
9local json = require "cjson"
10local misc = require "cjson-misc"
11
12local function gen_ascii()
13 local chars = {}
14 for i = 0, 255 do chars[i + 1] = string.char(i) end
15 return table.concat(chars)
16end
17
18-- Generate every UTF-16 codepoint, including supplementary codes
19local function gen_utf16_escaped()
20 -- Create raw table escapes
21 local utf16_escaped = {}
22 local count = 0
23
24 local function append_escape(code)
25 local esc = string.format('\\u%04X', code)
26 table.insert(utf16_escaped, esc)
27 end
28
29 table.insert(utf16_escaped, '"')
30 for i = 0, 0xD7FF do
31 append_escape(i)
32 end
33 -- Skip 0xD800 - 0xDFFF since they are used to encode supplementary
34 -- codepoints
35 for i = 0xE000, 0xFFFF do
36 append_escape(i)
37 end
38 -- Append surrogate pair for each supplementary codepoint
39 for high = 0xD800, 0xDBFF do
40 for low = 0xDC00, 0xDFFF do
41 append_escape(high)
42 append_escape(low)
43 end
44 end
45 table.insert(utf16_escaped, '"')
46
47 return table.concat(utf16_escaped)
48end
49
50function test_decode_cycle(filename)
51 local obj1 = json.decode(file_load(filename))
52 local obj2 = json.decode(json.encode(obj1))
53 return misc.compare_values(obj1, obj2)
54end
55
56local Inf = math.huge;
57local NaN = math.huge * 0;
58local octets_raw = gen_ascii()
59local octets_escaped = misc.file_load("octets-escaped.dat")
60local utf8_loaded, utf8_raw = pcall(misc.file_load, "utf8.dat")
61if not utf8_loaded then
62 utf8_raw = "Failed to load utf8.dat"
63end
64local utf16_escaped = gen_utf16_escaped()
65local nested5 = {{{{{ "nested" }}}}}
66local table_cycle = {}
67local table_cycle2 = { table_cycle }
68table_cycle[1] = table_cycle2
69
70local decode_simple_tests = {
71 { json.decode, { '"test string"' }, true, { "test string" } },
72 { json.decode, { '-5e3' }, true, { -5000 } },
73 { json.decode, { 'null' }, true, { json.null } },
74 { json.decode, { 'true' }, true, { true } },
75 { json.decode, { 'false' }, true, { false } },
76 { json.decode, { '{ "1": "one", "3": "three" }' },
77 true, { { ["1"] = "one", ["3"] = "three" } } },
78 { json.decode, { '[ "one", null, "three" ]' },
79 true, { { "one", json.null, "three" } } }
80}
81
82local encode_simple_tests = {
83 { json.encode, { json.null }, true, { 'null' } },
84 { json.encode, { true }, true, { 'true' } },
85 { json.encode, { false }, true, { 'false' } },
86 { json.encode, { { } }, true, { '{}' } },
87 { json.encode, { 10 }, true, { '10' } },
88 { json.encode, { NaN },
89 false, { "Cannot serialise number: must not be NaN or Inf" } },
90 { json.encode, { Inf },
91 false, { "Cannot serialise number: must not be NaN or Inf" } },
92 { json.encode, { "hello" }, true, { '"hello"' } },
93}
94
95local decode_numeric_tests = {
96 { json.decode, { '[ 0.0, -1, 0.3e-3, 1023.2 ]' },
97 true, { { 0.0, -1, 0.0003, 1023.2 } } },
98 { json.decode, { '00123' }, true, { 123 } },
99 { json.decode, { '05.2' }, true, { 5.2 } },
100 { json.decode, { '0e10' }, true, { 0 } },
101 { json.decode, { '0x6' }, true, { 6 } },
102 { json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } },
103 { json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
104 true, { { Inf, Inf, -Inf } } },
105 { json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } },
106 { json.decode, { 'Infrared' },
107 false, { "Expected the end but found invalid token at character 4" } },
108 { json.decode, { 'Noodle' },
109 false, { "Expected value but found invalid token at character 1" } },
110}
111
112local encode_table_tests = {
113 function()
114 json.encode_sparse_array(true, 2, 3)
115 json.encode_max_depth(5)
116 return "Setting sparse array (true, 2, 3) / max depth (5)"
117 end,
118 { json.encode, { { [3] = "sparse test" } },
119 true, { '[null,null,"sparse test"]' } },
120 { json.encode, { { [1] = "one", [4] = "sparse test" } },
121 true, { '["one",null,null,"sparse test"]' } },
122 { json.encode, { { [1] = "one", [5] = "sparse test" } },
123 true, { '{"1":"one","5":"sparse test"}' } },
124
125 { json.encode, { { ["2"] = "numeric string key test" } },
126 true, { '{"2":"numeric string key test"}' } },
127
128 { json.encode, { nested5 }, true, { '[[[[["nested"]]]]]' } },
129 { json.encode, { { nested5 } },
130 false, { "Cannot serialise, excessive nesting (6)" } },
131 { json.encode, { table_cycle },
132 false, { "Cannot serialise, excessive nesting (6)" } }
133}
134
135local encode_error_tests = {
136 { json.encode, { { [false] = "wrong" } },
137 false, { "Cannot serialise boolean: table key must be a number or string" } },
138 { json.encode, { function () end },
139 false, { "Cannot serialise function: type not supported" } },
140 function ()
141 json.refuse_invalid_numbers("encode")
142 return 'Setting refuse_invalid_numbers("encode")'
143 end,
144 { json.encode, { NaN },
145 false, { "Cannot serialise number: must not be NaN or Inf" } },
146 { json.encode, { Inf },
147 false, { "Cannot serialise number: must not be NaN or Inf" } },
148 function ()
149 json.refuse_invalid_numbers(false)
150 return 'Setting refuse_invalid_numbers(false).'
151 end,
152 { json.encode, { NaN }, true, { "nan" } },
153 { json.encode, { Inf }, true, { "inf" } },
154 function ()
155 json.refuse_invalid_numbers("encode")
156 return 'Setting refuse_invalid_numbers("encode")'
157 end,
158}
159
160local json_nested = string.rep("[", 100000) .. string.rep("]", 100000)
161
162local decode_error_tests = {
163 { json.decode, { '\0"\0"' },
164 false, { "JSON parser does not support UTF-16 or UTF-32" } },
165 { json.decode, { '"\0"\0' },
166 false, { "JSON parser does not support UTF-16 or UTF-32" } },
167 { json.decode, { '{ "unexpected eof": ' },
168 false, { "Expected value but found T_END at character 21" } },
169 { json.decode, { '{ "extra data": true }, false' },
170 false, { "Expected the end but found T_COMMA at character 23" } },
171 { json.decode, { ' { "bad escape \\q code" } ' },
172 false, { "Expected object key string but found invalid escape code at character 16" } },
173 { json.decode, { ' { "bad unicode \\u0f6 escape" } ' },
174 false, { "Expected object key string but found invalid unicode escape code at character 17" } },
175 { json.decode, { ' [ "bad barewood", test ] ' },
176 false, { "Expected value but found invalid token at character 20" } },
177 { json.decode, { '[ -+12 ]' },
178 false, { "Expected value but found invalid number at character 3" } },
179 { json.decode, { '-v' },
180 false, { "Expected value but found invalid number at character 1" } },
181 { json.decode, { '[ 0.4eg10 ]' },
182 false, { "Expected comma or array end but found invalid token at character 6" } },
183 { json.decode, { json_nested },
184 false, { "Too many nested data structures" } }
185}
186
187local escape_tests = {
188 -- Test 8bit clean
189 { json.encode, { octets_raw }, true, { octets_escaped } },
190 { json.decode, { octets_escaped }, true, { octets_raw } },
191 -- Ensure high bits are removed from surrogate codes
192 { json.decode, { '"\\uF800"' }, true, { "\239\160\128" } },
193 -- Test inverted surrogate pairs
194 { json.decode, { '"\\uDB00\\uD800"' },
195 false, { "Expected value but found invalid unicode escape code at character 2" } },
196 -- Test 2x high surrogate code units
197 { json.decode, { '"\\uDB00\\uDB00"' },
198 false, { "Expected value but found invalid unicode escape code at character 2" } },
199 -- Test invalid 2nd escape
200 { json.decode, { '"\\uDB00\\"' },
201 false, { "Expected value but found invalid unicode escape code at character 2" } },
202 { json.decode, { '"\\uDB00\\uD"' },
203 false, { "Expected value but found invalid unicode escape code at character 2" } },
204 -- Test decoding of all UTF-16 escapes
205 { json.decode, { utf16_escaped }, true, { utf8_raw } }
206}
207
208-- The standard Lua interpreter is ANSI C online doesn't support locales
209-- by default. Force a known problematic locale to test strtod()/sprintf().
210local locale_tests = {
211 function ()
212 os.setlocale("cs_CZ")
213 json.new()
214 return "Setting locale to cs_CZ (comma separator)"
215 end,
216 { json.encode, { 1.5 }, true, { '1.5' } },
217 { json.decode, { "[ 10, \"test\" ]" }, true, { { 10, "test" } } },
218 function ()
219 os.setlocale("C")
220 json.new()
221 return "Reverting locale to POSIX"
222 end
223}
224
225print(string.format("Testing Lua CJSON version %s\n", json.version))
226
227misc.run_test_group("decode simple value", decode_simple_tests)
228misc.run_test_group("encode simple value", encode_simple_tests)
229misc.run_test_group("decode numeric", decode_numeric_tests)
230misc.run_test_group("encode table", encode_table_tests)
231misc.run_test_group("decode error", decode_error_tests)
232misc.run_test_group("encode error", encode_error_tests)
233misc.run_test_group("escape", escape_tests)
234misc.run_test_group("locale", locale_tests)
235
236json.refuse_invalid_numbers(false)
237json.encode_max_depth(20)
238for i = 1, #arg do
239 misc.run_test("decode cycle " .. arg[i], test_decode_cycle, { arg[i] },
240 true, { true })
241end
242
243local pass, total = misc.run_test_summary()
244
245if pass == total then
246 print("==> Summary: all tests succeeded")
247else
248 print(string.format("==> Summary: %d/%d tests failed", total - pass, total))
249 os.exit(1)
250end
251
252-- vi:ai et sw=4 ts=4:
diff --git a/tests/types.json b/tests/types.json
new file mode 100644
index 0000000..c01e7d2
--- /dev/null
+++ b/tests/types.json
@@ -0,0 +1 @@
{ "array": [ 10, true, null ] }