Merge branch 'feature/cleanupDocs'
[luajson.git] / tests / lunit-numbers.lua
bloba9f1e41a225a2f3340be1a8ff6593fdd5ecb4480
1 local json = require("json")
2 local lunit = require("lunit")
3 local math = require("math")
4 local testutil = require("testutil")
5 local string = require("string")
7 local encode = json.encode
8 -- DECODE NOT 'local' due to requirement for testutil to access it
9 decode = json.decode.getDecoder(false)
11 local TEST_ENV
12 if not module then
13 _ENV = lunit.module("lunit-numbers", 'seeall')
14 TEST_ENV = _ENV
15 else
16 module("lunit-numbers", lunit.testcase, package.seeall)
17 TEST_ENV = _M
18 end
20 function setup()
21 -- Ensure that the decoder is reset
22 _G["decode"] = json.decode.getDecoder(false)
23 end
25 local function is_near(expect, received)
26 local pctDiff
27 if expect == received then
28 pctDiff = 0
29 else
30 pctDiff = math.abs(1 - expect / received)
31 end
32 if pctDiff < 0.000001 then
33 return true
34 else
35 return false, ("expected '%s' but was '%s' .. '%s'%% apart"):format(expect, received, pctDiff * 100)
36 end
37 end
38 local function assert_near(expect, received)
39 assert(is_near(expect, received))
40 end
41 local function test_simple(num)
42 assert_near(num, decode(tostring(num)))
43 end
44 local function test_simple_w_encode(num)
45 assert_near(num, decode(encode(num)))
46 end
47 local function test_scientific(num)
48 assert_near(num, decode(string.format('%e', num)))
49 assert_near(num, decode(string.format('%E', num)))
50 end
51 local function test_scientific_denied(num)
52 local decode = json.decode.getDecoder({ number = { exp = false } })
53 assert_error_match("Exponent-denied error did not match", "Exponents.*denied", function()
54 decode(string.format('%e', num))
55 end)
56 assert_error_match("Exponent-denied error did not match", "Exponents.*denied", function()
57 decode(string.format('%E', num))
58 end)
59 end
60 local numbers = {
61 0, 1, -1, math.pi, -math.pi
63 math.randomseed(0xDEADBEEF)
64 local pow = math.pow or load("return function(a, b) return a ^ b end")()
65 -- Add sequence of numbers at low/high end of value-set
66 for i = -300,300,60 do
67 numbers[#numbers + 1] = math.random() * pow(10, i)
68 numbers[#numbers + 1] = -math.random() * pow(10, i)
69 end
71 local function get_number_tester(f)
72 return function ()
73 for _, v in ipairs(numbers) do
74 f(v)
75 end
76 end
77 end
79 local function test_fraction(num)
80 assert_near(num, decode(string.format("%f", num)))
81 end
82 local function test_fraction_denied(num)
83 local decode = json.decode.getDecoder({ number = { frac = false } })
84 local formatted = string.format('%f', num)
85 assert_error_match("Fraction-denied error did not match for " .. formatted, "Fractions.*denied", function()
86 decode(formatted)
87 end)
88 end
89 local function get_number_fraction_tester(f)
90 return function ()
91 for _, v in ipairs(numbers) do
92 -- Fractional portion must be present
93 local formatted = string.format("%f", v)
94 -- San check that the formatted value is near the desired value
95 if nil ~= formatted:find("%.") and is_near(v, tonumber(formatted)) then
96 f(v)
97 end
98 end
99 end
102 test_simple_numbers = get_number_tester(test_simple)
103 test_simple_numbers_w_encode = get_number_tester(test_simple_w_encode)
104 test_simple_numbers_scientific = get_number_tester(test_scientific)
105 test_simple_numbers_scientific_denied = get_number_tester(test_scientific_denied)
106 test_simple_numbers_fraction_only = get_number_fraction_tester(test_fraction)
107 test_simple_numbers_fraction_denied_only = get_number_fraction_tester(test_fraction_denied)
109 function test_infinite_nostrict()
110 assert_equal(math.huge, decode("Infinity"))
111 assert_equal(math.huge, decode("infinity"))
112 assert_equal(-math.huge, decode("-Infinity"))
113 assert_equal(-math.huge, decode("-infinity"))
116 function test_nan_nostrict()
117 local value = decode("nan")
118 assert_true(value ~= value)
119 local value = decode("NaN")
120 assert_true(value ~= value)
121 assert_equal("NaN", encode(decode("NaN")))
124 function test_expression()
125 assert_error(function()
126 decode("1 + 2")
127 end)
130 -- For strict tests, small concession must be made to allow non-array/objects as root
131 local strict = json.util.merge({}, json.decode.strict, {initialObject = false})
132 local strictDecoder = json.decode.getDecoder(strict)
134 local numberValue = {hex = true}
136 local hex = {number = numberValue}
137 local hexDecoder = json.decode.getDecoder(hex)
139 function test_hex()
140 if decode == hexDecoder then -- MUST SKIP FAIL UNTIL BETTER METHOD SETUP
141 return
143 assert_error(function()
144 decode("0x20")
145 end)
148 local hexNumbers = {
149 0xDEADBEEF,
150 0xCAFEBABE,
151 0x00000000,
152 0xFFFFFFFF,
153 0xCE,
154 0x01
157 function test_hex_only()
158 _G["decode"] = hexDecoder
159 for _, v in ipairs(hexNumbers) do
160 assert_equal(v, decode(("0x%x"):format(v)))
161 assert_equal(v, decode(("0X%X"):format(v)))
162 assert_equal(v, decode(("0x%X"):format(v)))
163 assert_equal(v, decode(("0X%x"):format(v)))
167 local decimal_hexes = {
168 "0x0.1",
169 "0x.1",
170 "0x0e+1",
171 "0x0E-1"
173 function test_no_decimal_hex_only()
174 for _, str in ipairs(decimal_hexes) do
175 assert_error(function()
176 hexDecoder(str)
177 end)
181 function test_nearly_scientific_hex_only()
182 assert_equal(0x00E1, hexDecoder("0x00e1"))
185 local function buildStrictDecoder(f)
186 return testutil.buildPatchedDecoder(f, strictDecoder)
188 local function buildFailedStrictDecoder(f)
189 return testutil.buildFailedPatchedDecoder(f, strictDecoder)
191 -- SETUP CHECKS FOR SEQUENCE OF DECODERS
192 for k, v in pairs(TEST_ENV) do
193 if k:match("^test_") and not k:match("_gen$") and not k:match("_only$") then
194 if k:match("_nostrict") then
195 TEST_ENV[k .. "_strict_gen"] = buildFailedStrictDecoder(v)
196 else
197 TEST_ENV[k .. "_strict_gen"] = buildStrictDecoder(v)
199 TEST_ENV[k .. "_hex_gen"] = testutil.buildPatchedDecoder(v, hexDecoder)