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()
Diffstat (limited to '')
| -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 | ||
