common.lua 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. require "cjson"
  2. -- Misc routines to assist with CJSON testing
  3. --
  4. -- Mark Pulford <[email protected]>
  5. -- Determine with a Lua table can be treated as an array.
  6. -- Explicitly returns "not an array" for very sparse arrays.
  7. -- Returns:
  8. -- -1 Not an array
  9. -- 0 Empty table
  10. -- >0 Highest index in the array
  11. function is_array(table)
  12. local max = 0
  13. local count = 0
  14. for k, v in pairs(table) do
  15. if type(k) == "number" then
  16. if k > max then max = k end
  17. count = count + 1
  18. else
  19. return -1
  20. end
  21. end
  22. if max > count * 2 then
  23. return -1
  24. end
  25. return max
  26. end
  27. function serialise_table(value, indent, depth)
  28. local spacing, spacing2, indent2
  29. if indent then
  30. spacing = "\n" .. indent
  31. spacing2 = spacing .. " "
  32. indent2 = indent .. " "
  33. else
  34. spacing, spacing2, indent2 = " ", " ", false
  35. end
  36. depth = depth + 1
  37. if depth > 50 then
  38. return "Cannot serialise any further: too many nested tables"
  39. end
  40. local max = is_array(value)
  41. local comma = false
  42. local fragment = { "{" .. spacing2 }
  43. if max > 0 then
  44. -- Serialise array
  45. for i = 1, max do
  46. if comma then
  47. table.insert(fragment, "," .. spacing2)
  48. end
  49. table.insert(fragment, serialise_value(value[i], indent2, depth))
  50. comma = true
  51. end
  52. elseif max < 0 then
  53. -- Serialise table
  54. for k, v in pairs(value) do
  55. if comma then
  56. table.insert(fragment, "," .. spacing2)
  57. end
  58. table.insert(fragment, string.format(
  59. "[%s] = %s", serialise_value(k, indent2, depth),
  60. serialise_value(v, indent2, depth))
  61. )
  62. comma = true
  63. end
  64. end
  65. table.insert(fragment, spacing .. "}")
  66. return table.concat(fragment)
  67. end
  68. function serialise_value(value, indent, depth)
  69. if indent == nil then indent = "" end
  70. if depth == nil then depth = 0 end
  71. if value == cjson.null then
  72. return "cjson.null"
  73. elseif type(value) == "string" then
  74. return string.format("%q", value)
  75. elseif type(value) == "nil" or type(value) == "number" or
  76. type(value) == "boolean" then
  77. return tostring(value)
  78. elseif type(value) == "table" then
  79. return serialise_table(value, indent, depth)
  80. else
  81. return "\"<" .. type(value) .. ">\""
  82. end
  83. end
  84. function dump_value(value)
  85. print(serialise_value(value))
  86. end
  87. function file_load(filename)
  88. local file
  89. if filename == nil then
  90. file = io.stdin
  91. else
  92. local err
  93. file, err = io.open(filename)
  94. if file == nil then
  95. error(string.format("Unable to read '%s': %s", filename, err))
  96. end
  97. end
  98. local data = file:read("*a")
  99. if filename ~= nil then
  100. file:close()
  101. end
  102. if data == nil then
  103. error("Failed to read " .. filename)
  104. end
  105. return data
  106. end
  107. function file_save(filename, data)
  108. local file
  109. if filename == nil then
  110. file = io.stdout
  111. else
  112. local err
  113. file, err = io.open(filename, "w")
  114. if file == nil then
  115. error(string.format("Unable to write '%s': %s", filename, err))
  116. end
  117. end
  118. file:write(data)
  119. if filename ~= nil then
  120. file:close()
  121. end
  122. end
  123. function compare_values(val1, val2)
  124. local type1 = type(val1)
  125. local type2 = type(val2)
  126. if type1 ~= type2 then
  127. return false
  128. end
  129. -- Check for NaN
  130. if type1 == "number" and val1 ~= val1 and val2 ~= val2 then
  131. return true
  132. end
  133. if type1 ~= "table" then
  134. return val1 == val2
  135. end
  136. -- check_keys stores all the keys that must be checked in val2
  137. local check_keys = {}
  138. for k, _ in pairs(val1) do
  139. check_keys[k] = true
  140. end
  141. for k, v in pairs(val2) do
  142. if not check_keys[k] then
  143. return false
  144. end
  145. if not compare_values(val1[k], val2[k]) then
  146. return false
  147. end
  148. check_keys[k] = nil
  149. end
  150. for k, _ in pairs(check_keys) do
  151. -- Not the same if any keys from val1 were not found in val2
  152. return false
  153. end
  154. return true
  155. end
  156. function run_test(testname, func, input, should_work, output)
  157. local function status_line(name, status, value)
  158. local statusmap = { [true] = ":success", [false] = ":error" }
  159. if status ~= nil then
  160. name = name .. statusmap[status]
  161. end
  162. print(string.format("[%s] %s", name, serialise_value(value, false)))
  163. end
  164. local result = { pcall(func, unpack(input)) }
  165. local success = table.remove(result, 1)
  166. local correct = false
  167. if success == should_work and compare_values(result, output) then
  168. correct = true
  169. end
  170. local teststatus = { [true] = "PASS", [false] = "FAIL" }
  171. print("==> Test " .. testname .. ": " .. teststatus[correct])
  172. status_line("Input", nil, input)
  173. if not correct then
  174. status_line("Expected", should_work, output)
  175. end
  176. status_line("Received", success, result)
  177. print()
  178. return correct, result
  179. end
  180. function run_test_group(testgroup, tests)
  181. local function run_config(configname, func)
  182. local success, msg = pcall(func)
  183. if msg then
  184. print(string.format("==> Config %s: %s", configname, msg))
  185. end
  186. print()
  187. end
  188. local function test_id(group, id)
  189. return string.format("%s [%d]", group, id)
  190. end
  191. for k, v in ipairs(tests) do
  192. if type(v) == "function" then
  193. -- Useful for changing configuration during a batch
  194. run_config(test_id(testgroup, k), v)
  195. elseif type(v) == "table" then
  196. run_test(test_id(testgroup, k), unpack(v))
  197. else
  198. error("Testgroup can only contain functions and tables")
  199. end
  200. end
  201. end
  202. -- vi:ai et sw=4 ts=4: