diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/README | 4 | ||||
-rwxr-xr-x | tests/bench.lua | 83 | ||||
-rw-r--r-- | tests/cjson-misc.lua | 253 | ||||
-rw-r--r-- | tests/example1.json | 22 | ||||
-rw-r--r-- | tests/example2.json | 11 | ||||
-rw-r--r-- | tests/example3.json | 26 | ||||
-rw-r--r-- | tests/example4.json | 88 | ||||
-rw-r--r-- | tests/example5.json | 27 | ||||
-rwxr-xr-x | tests/genutf8.pl | 23 | ||||
-rwxr-xr-x | tests/json2lua.lua | 14 | ||||
-rwxr-xr-x | tests/lua2json.lua | 42 | ||||
-rw-r--r-- | tests/numbers.json | 7 | ||||
-rw-r--r-- | tests/octets-escaped.dat | 1 | ||||
-rw-r--r-- | tests/rfc-example1.json | 13 | ||||
-rw-r--r-- | tests/rfc-example2.json | 22 | ||||
-rwxr-xr-x | tests/test.lua | 252 | ||||
-rw-r--r-- | tests/types.json | 1 |
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 @@ | |||
1 | These JSON examples were taken from the JSON website | ||
2 | (http://json.org/example.html) and RFC 4627. | ||
3 | |||
4 | Used 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 | |||
9 | require "socket" | ||
10 | local json = require "cjson" | ||
11 | local misc = require "cjson-misc" | ||
12 | |||
13 | function 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 | ||
53 | end | ||
54 | |||
55 | function 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) | ||
72 | end | ||
73 | |||
74 | json.encode_keep_buffer(true) | ||
75 | |||
76 | for 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 | ||
81 | end | ||
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 @@ | |||
1 | local 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 | ||
13 | local 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 | ||
29 | end | ||
30 | |||
31 | local serialise_value | ||
32 | |||
33 | local 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) | ||
76 | end | ||
77 | |||
78 | function 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 | ||
94 | end | ||
95 | |||
96 | local 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 | ||
118 | end | ||
119 | |||
120 | local 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 | ||
135 | end | ||
136 | |||
137 | local 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 | ||
175 | end | ||
176 | |||
177 | local test_count_pass = 0 | ||
178 | local test_count_total = 0 | ||
179 | |||
180 | local function run_test_summary() | ||
181 | return test_count_pass, test_count_total | ||
182 | end | ||
183 | |||
184 | local 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 | ||
215 | end | ||
216 | |||
217 | local 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 | ||
240 | end | ||
241 | |||
242 | -- Export functions | ||
243 | return { | ||
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 | |||
8 | use strict; | ||
9 | |||
10 | # 0xD800 - 0xDFFF are used to encode supplementary codepoints | ||
11 | # 0x10000 - 0x10FFFF are supplementary codepoints | ||
12 | my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF); | ||
13 | |||
14 | my $utf8 = pack("U*", @codepoints); | ||
15 | defined($utf8) or die "Unable create UTF-8 string\n"; | ||
16 | |||
17 | open(FH, ">:utf8", "utf8.dat") | ||
18 | or die "Unable to open utf8.dat: $!\n"; | ||
19 | print FH $utf8 | ||
20 | or die "Unable to write utf8.dat\n"; | ||
21 | close(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 | |||
9 | local json = require "cjson" | ||
10 | local misc = require "cjson-misc" | ||
11 | |||
12 | local json_text = misc.file_load(arg[1]) | ||
13 | local t = json.decode(json_text) | ||
14 | print(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 | |||
9 | local json = require "cjson" | ||
10 | local misc = require "cjson-misc" | ||
11 | |||
12 | function 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 | ||
37 | end | ||
38 | |||
39 | local t = get_lua_table(misc.file_load(arg[1])) | ||
40 | print(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 | |||
9 | local json = require "cjson" | ||
10 | local misc = require "cjson-misc" | ||
11 | |||
12 | local function gen_ascii() | ||
13 | local chars = {} | ||
14 | for i = 0, 255 do chars[i + 1] = string.char(i) end | ||
15 | return table.concat(chars) | ||
16 | end | ||
17 | |||
18 | -- Generate every UTF-16 codepoint, including supplementary codes | ||
19 | local 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) | ||
48 | end | ||
49 | |||
50 | function 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) | ||
54 | end | ||
55 | |||
56 | local Inf = math.huge; | ||
57 | local NaN = math.huge * 0; | ||
58 | local octets_raw = gen_ascii() | ||
59 | local octets_escaped = misc.file_load("octets-escaped.dat") | ||
60 | local utf8_loaded, utf8_raw = pcall(misc.file_load, "utf8.dat") | ||
61 | if not utf8_loaded then | ||
62 | utf8_raw = "Failed to load utf8.dat" | ||
63 | end | ||
64 | local utf16_escaped = gen_utf16_escaped() | ||
65 | local nested5 = {{{{{ "nested" }}}}} | ||
66 | local table_cycle = {} | ||
67 | local table_cycle2 = { table_cycle } | ||
68 | table_cycle[1] = table_cycle2 | ||
69 | |||
70 | local 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 | |||
82 | local 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 | |||
95 | local 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 | |||
112 | local 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 | |||
135 | local 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 | |||
160 | local json_nested = string.rep("[", 100000) .. string.rep("]", 100000) | ||
161 | |||
162 | local 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 | |||
187 | local 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(). | ||
210 | local 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 | |||
225 | print(string.format("Testing Lua CJSON version %s\n", json.version)) | ||
226 | |||
227 | misc.run_test_group("decode simple value", decode_simple_tests) | ||
228 | misc.run_test_group("encode simple value", encode_simple_tests) | ||
229 | misc.run_test_group("decode numeric", decode_numeric_tests) | ||
230 | misc.run_test_group("encode table", encode_table_tests) | ||
231 | misc.run_test_group("decode error", decode_error_tests) | ||
232 | misc.run_test_group("encode error", encode_error_tests) | ||
233 | misc.run_test_group("escape", escape_tests) | ||
234 | misc.run_test_group("locale", locale_tests) | ||
235 | |||
236 | json.refuse_invalid_numbers(false) | ||
237 | json.encode_max_depth(20) | ||
238 | for i = 1, #arg do | ||
239 | misc.run_test("decode cycle " .. arg[i], test_decode_cycle, { arg[i] }, | ||
240 | true, { true }) | ||
241 | end | ||
242 | |||
243 | local pass, total = misc.run_test_summary() | ||
244 | |||
245 | if pass == total then | ||
246 | print("==> Summary: all tests succeeded") | ||
247 | else | ||
248 | print(string.format("==> Summary: %d/%d tests failed", total - pass, total)) | ||
249 | os.exit(1) | ||
250 | end | ||
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 ] } | |||