diff options
author | Mark Pulford <mark@kyne.com.au> | 2011-05-08 02:23:57 +0930 |
---|---|---|
committer | Mark Pulford <mark@kyne.com.au> | 2011-05-08 02:23:57 +0930 |
commit | 0d56e3bebecb7008f0baa7eaccf3dc9b2a39e360 (patch) | |
tree | 8c15c63f01afa79115f2b66b922bb27779df9b90 | |
parent | 3d014d14bcf3dce3e5dbb9c193d689e46d333798 (diff) | |
download | lua-cjson-0d56e3bebecb7008f0baa7eaccf3dc9b2a39e360.tar.gz lua-cjson-0d56e3bebecb7008f0baa7eaccf3dc9b2a39e360.tar.bz2 lua-cjson-0d56e3bebecb7008f0baa7eaccf3dc9b2a39e360.zip |
Implement data driven test framework
- Add run_test(): Test a function and verify its output
- Add run_test_group(): Execute a test batch
- Add serialise_value(): Serialise a Lua value into the Lua syntax
- Add file_save() helper function
- Add NaN comparison support to compare_values()
-rwxr-xr-x | tests/bench.lua | 15 | ||||
-rw-r--r-- | tests/common.lua | 168 | ||||
-rwxr-xr-x | tests/test.lua | 4 |
3 files changed, 158 insertions, 29 deletions
diff --git a/tests/bench.lua b/tests/bench.lua index b5b1a77..847365b 100755 --- a/tests/bench.lua +++ b/tests/bench.lua | |||
@@ -3,11 +3,11 @@ | |||
3 | -- Simple JSON benchmark. | 3 | -- Simple JSON benchmark. |
4 | -- | 4 | -- |
5 | -- Your Mileage May Vary. | 5 | -- Your Mileage May Vary. |
6 | -- | ||
7 | -- Mark Pulford <mark@kyne.com.au> | ||
6 | 8 | ||
7 | require "common" | 9 | require "common" |
8 | local json = require "cjson" | 10 | local json = require "cjson" |
9 | --local json = require "json" | ||
10 | --local json = require "dkjson" | ||
11 | 11 | ||
12 | function bench_file(filename) | 12 | function bench_file(filename) |
13 | local data_json = file_load(filename) | 13 | local data_json = file_load(filename) |
@@ -28,12 +28,11 @@ function bench_file(filename) | |||
28 | return benchmark(tests, 5000, 5) | 28 | return benchmark(tests, 5000, 5) |
29 | end | 29 | end |
30 | 30 | ||
31 | i = 1 | 31 | for i = 1, #arg do |
32 | while arg[i] do | 32 | local results = bench_file(arg[i]) |
33 | local results = {} | 33 | for k, v in pairs(results) do |
34 | results[arg[i]] = bench_file(arg[i]) | 34 | print(string.format("%s: %s: %d", arg[i], k, v)) |
35 | dump_value(results) | 35 | end |
36 | i = i + 1 | ||
37 | end | 36 | end |
38 | 37 | ||
39 | -- vi:ai et sw=4 ts=4: | 38 | -- vi:ai et sw=4 ts=4: |
diff --git a/tests/common.lua b/tests/common.lua index 219de3f..9a7ed19 100644 --- a/tests/common.lua +++ b/tests/common.lua | |||
@@ -1,31 +1,94 @@ | |||
1 | require "cjson" | 1 | require "cjson" |
2 | require "posix" | 2 | require "posix" |
3 | 3 | ||
4 | function dump_value(value, indent) | 4 | -- Misc routines to assist with CJSON testing |
5 | if indent == nil then | 5 | -- |
6 | indent = "" | 6 | -- Mark Pulford <mark@kyne.com.au> |
7 | |||
8 | -- Determine with a Lua table can be treated as an array. | ||
9 | -- Explicitly returns "not an array" for very sparse arrays. | ||
10 | -- Returns: | ||
11 | -- -1 Not an array | ||
12 | -- 0 Empty table | ||
13 | -- >0 Highest index in the array | ||
14 | function is_array(table) | ||
15 | local max = 0 | ||
16 | local count = 0 | ||
17 | for k, v in pairs(table) do | ||
18 | if type(k) == "number" then | ||
19 | if k > max then max = k end | ||
20 | count = count + 1 | ||
21 | else | ||
22 | return -1 | ||
23 | end | ||
24 | end | ||
25 | if max > count * 2 then | ||
26 | return -1 | ||
7 | end | 27 | end |
8 | 28 | ||
9 | if value == cjson.null then | 29 | return max |
10 | value = "<cjson.null>" | 30 | end |
31 | |||
32 | function serialise_table(value, indent) | ||
33 | local spacing, spacing2, indent2 | ||
34 | if indent then | ||
35 | spacing = "\n" .. indent | ||
36 | spacing2 = spacing .. " " | ||
37 | indent2 = indent .. " " | ||
38 | else | ||
39 | spacing, spacing2, indent2 = " ", " ", false | ||
11 | end | 40 | end |
12 | 41 | ||
13 | if type(value) == "string" or type(value) == "number" or | 42 | local max = is_array(value) |
14 | type(value) == "boolean" then | 43 | |
15 | print(indent .. tostring(value)) | 44 | local comma = false |
16 | elseif type(value) == "table" then | 45 | local fragment = { "{" .. spacing2 } |
17 | local count = 0 | 46 | if max > 0 then |
18 | for k, v in pairs(value) do | 47 | -- Serialise array |
19 | dump_value(v, indent .. k .. ": ") | 48 | for i = 1, max do |
20 | count = count + 1 | 49 | if comma then |
50 | table.insert(fragment, "," .. spacing2) | ||
51 | end | ||
52 | table.insert(fragment, serialise_value(value[i], indent2)) | ||
53 | comma = true | ||
21 | end | 54 | end |
22 | if count == 0 then | 55 | elseif max < 0 then |
23 | print(indent .. ": <empty>") | 56 | -- Serialise table |
57 | for k, v in pairs(value) do | ||
58 | if comma then | ||
59 | table.insert(fragment, "," .. spacing2) | ||
60 | end | ||
61 | table.insert(fragment, string.format( | ||
62 | "[%s] = %s", serialise_value(k, indent2), | ||
63 | serialise_value(v, indent2)) | ||
64 | ) | ||
65 | comma = true | ||
24 | end | 66 | end |
67 | end | ||
68 | table.insert(fragment, spacing .. "}") | ||
69 | |||
70 | return table.concat(fragment) | ||
71 | end | ||
72 | |||
73 | function serialise_value(value, indent) | ||
74 | if indent == nil then indent = "" end | ||
75 | |||
76 | if value == cjson.null then | ||
77 | return "cjson.null" | ||
78 | elseif type(value) == "string" then | ||
79 | return string.format("%q", value) | ||
80 | elseif type(value) == "nil" or type(value) == "number" or | ||
81 | type(value) == "boolean" then | ||
82 | return tostring(value) | ||
83 | elseif type(value) == "table" then | ||
84 | return serialise_table(value, indent) | ||
25 | else | 85 | else |
26 | print(indent .. "<" .. type(value) .. ">") | 86 | return "\"<" .. type(value) .. ">\"" |
27 | end | 87 | end |
88 | end | ||
28 | 89 | ||
90 | function dump_value(value) | ||
91 | print(serialise_value(value)) | ||
29 | end | 92 | end |
30 | 93 | ||
31 | function file_load(filename) | 94 | function file_load(filename) |
@@ -39,6 +102,15 @@ function file_load(filename) | |||
39 | return data | 102 | return data |
40 | end | 103 | end |
41 | 104 | ||
105 | function file_save(filename, data) | ||
106 | local file, err = io.open(filename, "w") | ||
107 | if file == nil then | ||
108 | error("Unable to write " .. filename) | ||
109 | end | ||
110 | file:write(data) | ||
111 | file:close() | ||
112 | end | ||
113 | |||
42 | function gettimeofday() | 114 | function gettimeofday() |
43 | local tv_sec, tv_usec = posix.gettimeofday() | 115 | local tv_sec, tv_usec = posix.gettimeofday() |
44 | 116 | ||
@@ -82,29 +154,83 @@ function compare_values(val1, val2) | |||
82 | if type1 ~= type2 then | 154 | if type1 ~= type2 then |
83 | return false | 155 | return false |
84 | end | 156 | end |
157 | |||
158 | -- Check for NaN | ||
159 | if type1 == "number" and val1 ~= val1 and val2 ~= val2 then | ||
160 | return true | ||
161 | end | ||
162 | |||
85 | if type1 ~= "table" then | 163 | if type1 ~= "table" then |
86 | return val1 == val2 | 164 | return val1 == val2 |
87 | end | 165 | end |
88 | local val1_keys = {} | 166 | |
89 | -- Note all the keys in val1 need to be checked | 167 | -- check_keys stores all the keys that must be checked in val2 |
168 | local check_keys = {} | ||
90 | for k, _ in pairs(val1) do | 169 | for k, _ in pairs(val1) do |
91 | check_keys[k] = true | 170 | check_keys[k] = true |
92 | end | 171 | end |
172 | |||
93 | for k, v in pairs(val2) do | 173 | for k, v in pairs(val2) do |
94 | if not check_keys[k] then | 174 | if not check_keys[k] then |
95 | -- Key didn't exist in val1 | ||
96 | return false | 175 | return false |
97 | end | 176 | end |
98 | if not compare_value(val1[k], val2[k]) then | 177 | |
178 | if not compare_values(val1[k], val2[k]) then | ||
99 | return false | 179 | return false |
100 | end | 180 | end |
181 | |||
101 | check_keys[k] = nil | 182 | check_keys[k] = nil |
102 | end | 183 | end |
103 | for k, _ in pairs(check_keys) do | 184 | for k, _ in pairs(check_keys) do |
104 | -- Not the same if any keys left to check | 185 | -- Not the same if any keys from val1 were not found in val2 |
105 | return false | 186 | return false |
106 | end | 187 | end |
107 | return true | 188 | return true |
108 | end | 189 | end |
109 | 190 | ||
191 | function run_test(testname, func, input, should_work, output) | ||
192 | local function status_line(name, status, value) | ||
193 | local statusmap = { [true] = ":success", [false] = ":error" } | ||
194 | if status ~= nil then | ||
195 | name = name .. statusmap[status] | ||
196 | end | ||
197 | print(string.format("[%s] %s", name, serialise_value(value, false))) | ||
198 | end | ||
199 | |||
200 | local result = { pcall(func, unpack(input)) } | ||
201 | local success = table.remove(result, 1) | ||
202 | |||
203 | local correct = false | ||
204 | if success == should_work and compare_values(result, output) then | ||
205 | correct = true | ||
206 | end | ||
207 | |||
208 | local teststatus = { [true] = "PASS", [false] = "FAIL" } | ||
209 | print("==> Test " .. testname .. ": " .. teststatus[correct]) | ||
210 | |||
211 | status_line("Input", nil, input) | ||
212 | if not correct then | ||
213 | status_line("Expected", should_work, output) | ||
214 | end | ||
215 | status_line("Received", success, result) | ||
216 | print() | ||
217 | |||
218 | return correct, result | ||
219 | end | ||
220 | |||
221 | function run_test_group(testgroup, tests) | ||
222 | for k, v in ipairs(tests) do | ||
223 | if type(v) == "function" then | ||
224 | -- Useful for changing configuration during a batch | ||
225 | msg = v() | ||
226 | print(string.format("==> Config %s [%d]: %s", testgroup, k, msg)) | ||
227 | print() | ||
228 | elseif type(v) == "table" then | ||
229 | run_test(testgroup .. " [" .. k .. "]", unpack(v)) | ||
230 | else | ||
231 | error("Testgroup can only contain functions and tables") | ||
232 | end | ||
233 | end | ||
234 | end | ||
235 | |||
110 | -- vi:ai et sw=4 ts=4: | 236 | -- vi:ai et sw=4 ts=4: |
diff --git a/tests/test.lua b/tests/test.lua index 3c8c404..51f29c4 100755 --- a/tests/test.lua +++ b/tests/test.lua | |||
@@ -1,5 +1,9 @@ | |||
1 | #!/usr/bin/env lua | 1 | #!/usr/bin/env lua |
2 | 2 | ||
3 | -- CJSON tests | ||
4 | -- | ||
5 | -- Mark Pulford <mark@kyne.com.au> | ||
6 | |||
3 | require "common" | 7 | require "common" |
4 | local json = require "cjson" | 8 | local json = require "cjson" |
5 | 9 | ||