123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- local json = require "cjson"
- -- Various common routines used by the Lua CJSON package
- --
- -- Mark Pulford <mark@kyne.com.au>
- -- Determine with a Lua table can be treated as an array.
- -- Explicitly returns "not an array" for very sparse arrays.
- -- Returns:
- -- -1 Not an array
- -- 0 Empty table
- -- >0 Highest index in the array
- local function is_array(table)
- local max = 0
- local count = 0
- for k, v in pairs(table) do
- if type(k) == "number" then
- if k > max then max = k end
- count = count + 1
- else
- return -1
- end
- end
- if max > count * 2 then
- return -1
- end
- return max
- end
- local serialise_value
- local function serialise_table(value, indent, depth)
- local spacing, spacing2, indent2
- if indent then
- spacing = "\n" .. indent
- spacing2 = spacing .. " "
- indent2 = indent .. " "
- else
- spacing, spacing2, indent2 = " ", " ", false
- end
- depth = depth + 1
- if depth > 50 then
- return "Cannot serialise any further: too many nested tables"
- end
- local max = is_array(value)
- local comma = false
- local fragment = { "{" .. spacing2 }
- if max > 0 then
- -- Serialise array
- for i = 1, max do
- if comma then
- table.insert(fragment, "," .. spacing2)
- end
- table.insert(fragment, serialise_value(value[i], indent2, depth))
- comma = true
- end
- elseif max < 0 then
- -- Serialise table
- for k, v in pairs(value) do
- if comma then
- table.insert(fragment, "," .. spacing2)
- end
- table.insert(fragment,
- ("[%s] = %s"):format(serialise_value(k, indent2, depth),
- serialise_value(v, indent2, depth)))
- comma = true
- end
- end
- table.insert(fragment, spacing .. "}")
- return table.concat(fragment)
- end
- function serialise_value(value, indent, depth)
- if indent == nil then indent = "" end
- if depth == nil then depth = 0 end
- if value == json.null then
- return "json.null"
- elseif type(value) == "string" then
- return ("%q"):format(value)
- elseif type(value) == "nil" or type(value) == "number" or
- type(value) == "boolean" then
- return tostring(value)
- elseif type(value) == "table" then
- return serialise_table(value, indent, depth)
- else
- return "\"<" .. type(value) .. ">\""
- end
- end
- local function file_load(filename)
- local file
- if filename == nil then
- file = io.stdin
- else
- local err
- file, err = io.open(filename, "rb")
- if file == nil then
- error(("Unable to read '%s': %s"):format(filename, err))
- end
- end
- local data = file:read("*a")
- if filename ~= nil then
- file:close()
- end
- if data == nil then
- error("Failed to read " .. filename)
- end
- return data
- end
- local function file_save(filename, data)
- local file
- if filename == nil then
- file = io.stdout
- else
- local err
- file, err = io.open(filename, "wb")
- if file == nil then
- error(("Unable to write '%s': %s"):format(filename, err))
- end
- end
- file:write(data)
- if filename ~= nil then
- file:close()
- end
- end
- local function compare_values(val1, val2)
- local type1 = type(val1)
- local type2 = type(val2)
- if type1 ~= type2 then
- return false
- end
- -- Check for NaN
- if type1 == "number" and val1 ~= val1 and val2 ~= val2 then
- return true
- end
- if type1 ~= "table" then
- return val1 == val2
- end
- -- check_keys stores all the keys that must be checked in val2
- local check_keys = {}
- for k, _ in pairs(val1) do
- check_keys[k] = true
- end
- for k, v in pairs(val2) do
- if not check_keys[k] then
- return false
- end
- if not compare_values(val1[k], val2[k]) then
- return false
- end
- check_keys[k] = nil
- end
- for k, _ in pairs(check_keys) do
- -- Not the same if any keys from val1 were not found in val2
- return false
- end
- return true
- end
- local test_count_pass = 0
- local test_count_total = 0
- local function run_test_summary()
- return test_count_pass, test_count_total
- end
- local function run_test(testname, func, input, should_work, output)
- local function status_line(name, status, value)
- local statusmap = { [true] = ":success", [false] = ":error" }
- if status ~= nil then
- name = name .. statusmap[status]
- end
- print(("[%s] %s"):format(name, serialise_value(value, false)))
- end
- local result = { pcall(func, unpack(input)) }
- local success = table.remove(result, 1)
- local correct = false
- if success == should_work and compare_values(result, output) then
- correct = true
- test_count_pass = test_count_pass + 1
- end
- test_count_total = test_count_total + 1
- local teststatus = { [true] = "PASS", [false] = "FAIL" }
- print(("==> Test [%d] %s: %s"):format(test_count_total, testname,
- teststatus[correct]))
- status_line("Input", nil, input)
- if not correct then
- status_line("Expected", should_work, output)
- end
- status_line("Received", success, result)
- print()
- return correct, result
- end
- local function run_test_group(tests)
- local function run_helper(name, func, input)
- if type(name) == "string" and #name > 0 then
- print("==> " .. name)
- end
- -- Not a protected call, these functions should never generate errors.
- func(unpack(input or {}))
- print()
- end
- for _, v in ipairs(tests) do
- -- Run the helper if "should_work" is missing
- if v[4] == nil then
- run_helper(unpack(v))
- else
- run_test(unpack(v))
- end
- end
- end
- -- Run a Lua script in a separate environment
- local function run_script(script, env)
- local env = env or {}
- local func
- -- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists
- if _G.setfenv then
- func = loadstring(script)
- if func then
- setfenv(func, env)
- end
- else
- func = load(script, nil, nil, env)
- end
- if func == nil then
- error("Invalid syntax.")
- end
- func()
- return env
- end
- -- Export functions
- return {
- serialise_value = serialise_value,
- file_load = file_load,
- file_save = file_save,
- compare_values = compare_values,
- run_test_summary = run_test_summary,
- run_test = run_test,
- run_test_group = run_test_group,
- run_script = run_script
- }
- -- vi:ai et sw=4 ts=4:
|