Files
plainleaf/client/space_lua/stdlib/format_test.lua
T

504 lines
19 KiB
Lua

local function assertEquals(actual, expected, message)
if actual ~= expected then
error('Assertion failed: ' .. message
.. '\n actual: ' .. tostring(actual)
.. '\n expected: ' .. tostring(expected), 2)
end
end
local function assertThrows(msg_substr, fn)
local ok, err = pcall(fn)
if ok then
error('Assertion failed: expected error containing "'
.. msg_substr .. '"', 2)
end
if type(err) ~= 'string' then
err = tostring(err)
end
if not string.find(err, msg_substr, 1, true) then
error('Assertion failed: expected error message to contain "'
.. msg_substr .. '", got: "' .. err .. '"', 2)
end
end
-- %%: literal percent
assertEquals(string.format("%%"), "%", "%%: literal")
assertEquals(string.format("100%%"), "100%", "%%: 100%%")
assertEquals(string.format("%%%%"), "%%", "%%: %%%%")
assertEquals(string.format("a%%b%%c"), "a%b%c", "%%: a%%b%%c")
-- %d: basic integers
assertEquals(string.format("%d", 0), "0", "%d: 0")
assertEquals(string.format("%d", 1), "1", "%d: 1")
assertEquals(string.format("%d", -1), "-1", "%d: -1")
assertEquals(string.format("%d", 42), "42", "%d: 42")
assertEquals(string.format("%d", -42), "-42", "%d: -42")
assertEquals(string.format("%d", 2147483647), "2147483647", "%d: INT_MAX")
assertEquals(string.format("%d", -2147483648), "-2147483648", "%d: INT_MIN")
-- %i: alias for %d
assertEquals(string.format("%i", 42), "42", "%i: 42")
assertEquals(string.format("%i", -7), "-7", "%i: -7")
-- %d: width
assertEquals(string.format("%5d", 42), " 42", "%d: width 5")
assertEquals(string.format("%5d", -42), " -42", "%d: width 5 neg")
assertEquals(string.format("%1d", 42), "42", "%d: width 1 no pad")
-- %d: left-justify
assertEquals(string.format("%-5d", 42), "42 ", "%d: left 42")
assertEquals(string.format("%-5d", -42), "-42 ", "%d: left -42")
-- %d: zero-pad
assertEquals(string.format("%05d", 42), "00042", "%d: zero-pad 42")
assertEquals(string.format("%05d", -42), "-0042", "%d: zero-pad -42")
assertEquals(string.format("%05d", 0), "00000", "%d: zero-pad 0")
-- %d: plus flag
assertEquals(string.format("%+d", 42), "+42", "%d: +42")
assertEquals(string.format("%+d", -42), "-42", "%d: + -42")
assertEquals(string.format("%+d", 0), "+0", "%d: +0")
-- %d: space flag
assertEquals(string.format("% d", 42), " 42", "%d: space 42")
assertEquals(string.format("% d", -42), "-42", "%d: space -42")
assertEquals(string.format("% d", 0), " 0", "%d: space 0")
-- %d: precision
assertEquals(string.format("%.5d", 42), "00042", "%d: prec5 42")
assertEquals(string.format("%.5d", -42), "-00042", "%d: prec5 -42")
assertEquals(string.format("%.0d", 0), "", "%d: prec0 0")
assertEquals(string.format("%.0d", 1), "1", "%d: prec0 1")
assertEquals(string.format("%.1d", 0), "0", "%d: prec1 0")
-- %d: width and precision
assertEquals(string.format("%8.5d", 42), " 00042", "%d: w8 p5 42")
assertEquals(string.format("%-8.5d", 42), "00042 ", "%d: left w8 p5 42")
-- %u: unsigned integers
assertEquals(string.format("%u", 0), "0", "%u: 0")
assertEquals(string.format("%u", 42), "42", "%u: 42")
assertEquals(string.format("%u", -1), "18446744073709551615", "%u: -1")
assertEquals(string.format("%u", -42), "18446744073709551574", "%u: -42")
-- %o: octal
assertEquals(string.format("%o", 0), "0", "%o: 0")
assertEquals(string.format("%o", 8), "10", "%o: 8")
assertEquals(string.format("%o", 255), "377", "%o: 255")
assertEquals(string.format("%o", -1), "1777777777777777777777", "%o: -1")
-- %o: alt flag
assertEquals(string.format("%#o", 0), "0", "%#o: 0")
assertEquals(string.format("%#o", 8), "010", "%#o: 8")
assertEquals(string.format("%#o", 255), "0377", "%#o: 255")
-- %x: hex lowercase
assertEquals(string.format("%x", 0), "0", "%x: 0")
assertEquals(string.format("%x", 255), "ff", "%x: 255")
assertEquals(string.format("%x", 4095), "fff", "%x: 4095")
assertEquals(string.format("%x", -1), "ffffffffffffffff", "%x: -1")
-- %X: hex uppercase
assertEquals(string.format("%X", 255), "FF", "%X: 255")
assertEquals(string.format("%X", -1), "FFFFFFFFFFFFFFFF", "%X: -1")
-- %x: alt flag
assertEquals(string.format("%#x", 0), "0", "%#x: 0")
assertEquals(string.format("%#x", 255), "0xff", "%#x: 255")
assertEquals(string.format("%#X", 255), "0XFF", "%#X: 255")
-- %x: width and zero-pad
assertEquals(string.format("%08x", 255), "000000ff", "%x: zero-pad 255")
assertEquals(string.format("%#08x", 255), "0x0000ff", "%#x: zero-pad 255")
-- %c: character
assertEquals(string.format("%c", 65), "A", "%c: 65")
assertEquals(string.format("%c", 97), "a", "%c: 97")
assertEquals(string.format("%c", 48), "0", "%c: 48")
-- %c: width
assertEquals(string.format("%3c", 65), " A", "%c: width 3")
assertEquals(string.format("%-3c", 65), "A ", "%c: left width 3")
-- %s: strings
assertEquals(string.format("%s", "hello"), "hello", "%s: hello")
assertEquals(string.format("%s", ""), "", "%s: empty")
assertEquals(string.format("%s %s", "hello", "world"), "hello world", "%s: two")
-- %s: width
assertEquals(string.format("%10s", "hello"), " hello", "%s: width 10")
assertEquals(string.format("%-10s", "hello"), "hello ", "%s: left width 10")
-- %s: precision truncates
assertEquals(string.format("%.3s", "hello"), "hel", "%s: prec3")
assertEquals(string.format("%.10s", "hello"), "hello", "%s: prec10")
assertEquals(string.format("%.0s", "hello"), "", "%s: prec0")
-- %s: width and precision
assertEquals(string.format("%10.3s", "hello"), " hel", "%s: w10 p3")
assertEquals(string.format("%-10.3s", "hello"), "hel ", "%s: left w10 p3")
-- %f: basic floats
assertEquals(string.format("%f", 0), "0.000000", "%f: 0")
assertEquals(string.format("%f", 1), "1.000000", "%f: 1")
assertEquals(string.format("%f", -1), "-1.000000", "%f: -1")
assertEquals(string.format("%f", 3.14), "3.140000", "%f: 3.14")
assertEquals(string.format("%f", 0.1), "0.100000", "%f: 0.1")
-- %f: precision
assertEquals(string.format("%.2f", 3.14159), "3.14", "%f: prec2")
assertEquals(string.format("%.0f", 3.14159), "3", "%f: prec0")
assertEquals(string.format("%.10f", 1.0), "1.0000000000", "%f: prec10")
-- %f: width and precision
assertEquals(string.format("%10.2f", 3.14), " 3.14", "%f: w10 p2")
assertEquals(string.format("%-10.2f", 3.14), "3.14 ", "%f: left w10 p2")
assertEquals(string.format("%010.2f", 3.14), "0000003.14", "%f: zero-pad w10 p2")
-- %f: sign flags
assertEquals(string.format("%+f", 3.14), "+3.140000", "%f: +3.14")
assertEquals(string.format("%+f", -3.14), "-3.140000", "%f: + -3.14")
assertEquals(string.format("% f", 3.14), " 3.140000", "%f: space 3.14")
assertEquals(string.format("% f", -3.14), "-3.140000", "%f: space -3.14")
-- %f: non-finite
assertEquals(string.format("%f", 1/0), "inf", "%f: inf")
assertEquals(string.format("%f", -1/0), "-inf", "%f: -inf")
assertEquals(string.format("%f", 0/0), "-nan", "%f: nan")
assertEquals(string.format("%+f", 1/0), "+inf", "%f: +inf")
-- %f: width with non-finite
assertEquals(string.format("%10f", 1/0), " inf", "%f: w10 inf")
assertEquals(string.format("%-10f", 1/0), "inf ", "%f: left w10 inf")
-- %e: basic
assertEquals(string.format("%e", 0), "0.000000e+00", "%e: 0")
assertEquals(string.format("%e", 1), "1.000000e+00", "%e: 1")
assertEquals(string.format("%e", 100), "1.000000e+02", "%e: 100")
assertEquals(string.format("%e", 0.001), "1.000000e-03", "%e: 0.001")
assertEquals(string.format("%e", -42), "-4.200000e+01", "%e: -42")
-- %E: uppercase
assertEquals(string.format("%E", 100), "1.000000E+02", "%E: 100")
-- %e: precision
assertEquals(string.format("%.2e", 100), "1.00e+02", "%e: prec2")
assertEquals(string.format("%.0e", 100), "1e+02", "%e: prec0")
assertEquals(string.format("%.14e", 1), "1.00000000000000e+00", "%e: prec14")
-- %e: non-finite
assertEquals(string.format("%e", 1/0), "inf", "%e: inf")
assertEquals(string.format("%e", -1/0), "-inf", "%e: -inf")
assertEquals(string.format("%e", 0/0), "-nan", "%e: nan")
-- %e: width and flags
assertEquals(string.format("%15e", 100), " 1.000000e+02", "%e: w15")
assertEquals(string.format("%-15e", 100), "1.000000e+02 ", "%e: left w15")
assertEquals(string.format("%+e", 100), "+1.000000e+02", "%e: +100")
-- %g: basic
assertEquals(string.format("%g", 0), "0", "%g: 0")
assertEquals(string.format("%g", 1), "1", "%g: 1")
assertEquals(string.format("%g", 100), "100", "%g: 100")
assertEquals(string.format("%g", 100000), "100000", "%g: 100000")
assertEquals(string.format("%g", 1000000), "1e+06", "%g: 1000000")
assertEquals(string.format("%g", 0.0001), "0.0001", "%g: 0.0001")
assertEquals(string.format("%g", 0.00001), "1e-05", "%g: 0.00001")
assertEquals(string.format("%g", -42), "-42", "%g: -42")
-- %G: uppercase
assertEquals(string.format("%G", 1e6), "1E+06", "%G: 1e6")
assertEquals(string.format("%G", 1e-5), "1E-05", "%G: 1e-5")
-- %g: precision
assertEquals(string.format("%.1g", 3.14), "3", "%g: prec1")
assertEquals(string.format("%.2g", 3.14), "3.1", "%g: prec2")
assertEquals(string.format("%.4g", 3.14159), "3.142", "%g: prec4")
assertEquals(string.format("%.0g", 3.14), "3", "%g: prec0")
assertEquals(string.format("%.10g", 1), "1", "%g: prec10 int")
-- %g: strips trailing zeros
assertEquals(string.format("%.6g", 1.0), "1", "%g: strip 1.0")
assertEquals(string.format("%.6g", 1.5), "1.5", "%g: strip 1.5")
assertEquals(string.format("%.6g", 1.50), "1.5", "%g: strip 1.50")
assertEquals(string.format("%.14g", 1.0), "1", "%g: strip prec14 1.0")
assertEquals(string.format("%.14g", 1.5), "1.5", "%g: strip prec14 1.5")
-- %g: alt flag keeps trailing zeros and dot
assertEquals(string.format("%#g", 1.0), "1.00000", "%#g: 1.0")
assertEquals(string.format("%#.2g", 1.0), "1.0", "%#g: prec2 1.0")
assertEquals(string.format("%#.4g", 100), "100.0", "%#g: prec4 100")
-- %g: non-finite
assertEquals(string.format("%g", 1/0), "inf", "%g: inf")
assertEquals(string.format("%g", -1/0), "-inf", "%g: -inf")
assertEquals(string.format("%g", 0/0), "-nan", "%g: nan")
assertEquals(string.format("%+g", 1/0), "+inf", "%g: +inf")
-- %g: width and flags
assertEquals(string.format("%10g", 42), " 42", "%g: w10 42")
assertEquals(string.format("%-10g", 42), "42 ", "%g: left w10 42")
assertEquals(string.format("%010g", 42), "0000000042", "%g: zero-pad w10 42")
assertEquals(string.format("%+g", 42), "+42", "%g: +42")
assertEquals(string.format("% g", 42), " 42", "%g: space 42")
-- %g: Lua 5.4 %.14g reference values
assertEquals(string.format("%.14g", 0.0), "0", "%.14g: 0.0")
assertEquals(string.format("%.14g", 1.0), "1", "%.14g: 1.0")
assertEquals(string.format("%.14g", 1/3), "0.33333333333333", "%.14g: 1/3")
assertEquals(string.format("%.14g", math.pi), "3.1415926535898", "%.14g: pi")
assertEquals(string.format("%.14g", 1e-10), "1e-10", "%.14g: 1e-10")
assertEquals(string.format("%.14g", 1e18), "1e+18", "%.14g: 1e18")
assertEquals(string.format("%.14g", 2^63), "9.2233720368548e+18", "%.14g: 2^63")
assertEquals(string.format("%.14g", 2^53), "9.007199254741e+15", "%.14g: 2^53")
assertEquals(string.format("%.14g", 1.7976931348623e+308), "1.7976931348623e+308", "%.14g: max")
assertEquals(string.format("%.14g", 5e-324), "4.9406564584125e-324", "%.14g: min subnormal")
assertEquals(string.format("%.14g", 0/0), "-nan", "%.14g: nan")
assertEquals(string.format("%.14g", 1/0), "inf", "%.14g: inf")
assertEquals(string.format("%.14g", -1/0), "-inf", "%.14g: -inf")
-- mixed: multiple specifiers
assertEquals(string.format("%d + %d = %d", 1, 2, 3), "1 + 2 = 3", "mixed: d+d=d")
assertEquals(string.format("%s is %d", "age", 42), "age is 42", "mixed: s is d")
assertEquals(string.format("[%05d] %s (%.2f)", 7, "test", 3.14), "[00007] test (3.14)", "mixed: complex")
-- mixed: no specifiers
assertEquals(string.format("hello world"), "hello world", "mixed: no spec")
assertEquals(string.format(""), "", "mixed: empty")
-- error: invalid specifier (Lua 5.4 says "invalid conversion", 5.5 may differ)
do
local ok = pcall(string.format, "%z", 1)
assertEquals(ok, false, "%z: should error")
end
-- error: trailing percent
do
local ok = pcall(string.format, "abc%")
assertEquals(ok, false, "trailing %%: should error")
end
-- %f: negative zero
assertEquals(string.format("%f", -0.0), "-0.000000", "%f: -0.0")
assertEquals(string.format("%e", -0.0), "-0.000000e+00", "%e: -0.0")
assertEquals(string.format("%g", -0.0), "-0", "%g: -0.0")
-- %f: large precision
do
local s = string.format("%.20f", 1.0)
assertEquals(string.find(s, "%.") ~= nil, true, "%f: large prec has dot")
assertEquals(#s, 22, "%f: large prec length")
end
-- zero width is no-op
assertEquals(string.format("%0d", 42), "42", "%0d: no-op")
-- %a: basic hex floats
assertEquals(string.format("%a", 0), "0x0p+0", "%a: 0")
assertEquals(string.format("%a", 1), "0x1p+0", "%a: 1")
assertEquals(string.format("%a", -1), "-0x1p+0", "%a: -1")
assertEquals(string.format("%a", 2), "0x1p+1", "%a: 2")
assertEquals(string.format("%a", 0.5), "0x1p-1", "%a: 0.5")
assertEquals(string.format("%a", 1.5), "0x1.8p+0", "%a: 1.5")
assertEquals(string.format("%a", -0.0), "-0x0p+0", "%a: -0.0")
-- %A: uppercase
assertEquals(string.format("%A", 1.5), "0X1.8P+0", "%A: 1.5")
assertEquals(string.format("%A", 255), "0X1.FEP+7", "%A: 255")
-- %a: precision
assertEquals(string.format("%.0a", 1.5), "0x2p+0", "%a: prec0 1.5")
assertEquals(string.format("%.1a", 1.5), "0x1.8p+0", "%a: prec1 1.5")
assertEquals(string.format("%.4a", 1.5), "0x1.8000p+0", "%a: prec4 1.5")
assertEquals(string.format("%.4a", 1), "0x1.0000p+0", "%a: prec4 1")
-- %a: width and flags
assertEquals(string.format("%20a", 1.5), " 0x1.8p+0", "%a: w20 1.5")
assertEquals(string.format("%-20a", 1.5), "0x1.8p+0 ", "%a: left w20 1.5")
assertEquals(string.format("%+a", 1.5), "+0x1.8p+0", "%a: +1.5")
assertEquals(string.format("% a", 1.5), " 0x1.8p+0", "%a: space 1.5")
-- %a: non-finite
assertEquals(string.format("%a", 1/0), "inf", "%a: inf")
assertEquals(string.format("%a", -1/0), "-inf", "%a: -inf")
assertEquals(string.format("%a", 0/0), "-nan", "%a: nan")
assertEquals(string.format("%A", 1/0), "INF", "%A: inf")
-- %a: Math.PI
assertEquals(string.format("%a", math.pi), "0x1.921fb54442d18p+1", "%a: pi")
-- %a: 0.1
assertEquals(string.format("%a", 0.1), "0x1.999999999999ap-4", "%a: 0.1")
-- %q: nil and booleans
assertEquals(string.format("%q", nil), "nil", "%q: nil")
assertEquals(string.format("%q", true), "true", "%q: true")
assertEquals(string.format("%q", false), "false", "%q: false")
-- %q: integers
assertEquals(string.format("%q", 0), "0", "%q: 0")
assertEquals(string.format("%q", 1), "1", "%q: 1")
assertEquals(string.format("%q", -1), "-1", "%q: -1")
assertEquals(string.format("%q", 42), "42", "%q: 42")
assertEquals(string.format("%q", -42), "-42", "%q: -42")
-- %q: floats as hex
assertEquals(string.format("%q", 1.5), "0x1.8p+0", "%q: 1.5")
assertEquals(string.format("%q", 0.1), "0x1.999999999999ap-4", "%q: 0.1")
assertEquals(string.format("%q", math.pi), "0x1.921fb54442d18p+1", "%q: pi")
assertEquals(string.format("%q", -0.5), "-0x1p-1", "%q: -0.5")
assertEquals(string.format("%q", -0.0), "-0x0p+0", "%q: -0.0")
-- %q: non-finite numbers
assertEquals(string.format("%q", 0/0), "(0/0)", "%q: nan")
assertEquals(string.format("%q", 1/0), "1e9999", "%q: inf")
assertEquals(string.format("%q", -1/0), "-1e9999", "%q: -inf")
-- %q: simple strings
assertEquals(string.format("%q", "hello"), '"hello"', "%q: hello")
assertEquals(string.format("%q", ""), '""', "%q: empty")
-- %q: strings with special characters
assertEquals(string.format("%q", 'say "hi"'), '"say \\"hi\\""', '%q: quotes')
assertEquals(string.format("%q", "back\\slash"), '"back\\\\slash"', "%q: backslash")
assertEquals(string.format("%q", "line\nbreak"), '"line\\\nbreak"', "%q: newline")
assertEquals(string.format("%q", "cr\rreturn"), '"cr\\13return"', "%q: cr")
assertEquals(string.format("%q", "tab\there"), '"tab\\9here"', "%q: tab")
-- %q: Lua reference example
do
local result = string.format("%q", 'a string with "quotes" and \n new line')
assertEquals(result, '"a string with \\"quotes\\" and \\\n new line"', "%q: reference")
end
-- %q: mixed with other specifiers
assertEquals(
string.format("name=%q age=%d", "Alice", 30),
'name="Alice" age=30',
"%q: mixed"
)
-- %p: pointer-like identity for tables
do
local t1 = {}
local t2 = {}
local s1 = string.format("%p", t1)
local s2 = string.format("%p", t2)
-- starts with 0x
assertEquals(s1:sub(1, 2), "0x", "%p: table prefix")
-- same object gives same result
assertEquals(string.format("%p", t1), s1, "%p: stable identity")
-- different objects give different results
assertEquals(s1 ~= s2, true, "%p: distinct tables differ")
end
-- %p: functions get an identity too
do
local f1 = function() end
local f2 = function() end
local s1 = string.format("%p", f1)
local s2 = string.format("%p", f2)
assertEquals(s1:sub(1, 2), "0x", "%p: function prefix")
assertEquals(s1 ~= s2, true, "%p: distinct functions differ")
end
-- %p: nil and primitives yield (null)
assertEquals(string.format("%p", nil), "(null)", "%p: nil")
assertEquals(string.format("%p", true), "(null)", "%p: boolean")
assertEquals(string.format("%p", 42), "(null)", "%p: number")
-- %p: strings get an identity (they are GC objects in Lua)
do
local s = string.format("%p", "hello")
assertEquals(s:sub(1, 2), "0x", "%p: string prefix")
end
-- %p: width
do
local t = {}
local s = string.format("%20p", t)
assertEquals(#s >= 20, true, "%p: width pads")
end
-- %p: left-justify
do
local t = {}
local s = string.format("%-20p", t)
assertEquals(#s >= 20, true, "%p: left width pads")
-- trailing spaces
assertEquals(s:sub(#s, #s), " ", "%p: left-justify trailing space")
end
-- %p: pointer-like identity for tables
do
local t1 = {}
local t2 = {}
local s1 = string.format("%p", t1)
local s2 = string.format("%p", t2)
assertEquals(s1:sub(1, 2), "0x", "%p: table prefix")
assertEquals(string.format("%p", t1), s1, "%p: table stable identity")
assertEquals(s1 ~= s2, true, "%p: distinct tables differ")
end
-- %p: functions get an identity too
do
local f1 = function() end
local f2 = function() end
local s1 = string.format("%p", f1)
local s2 = string.format("%p", f2)
assertEquals(s1:sub(1, 2), "0x", "%p: function prefix")
assertEquals(string.format("%p", f1), s1, "%p: function stable identity")
assertEquals(s1 ~= s2, true, "%p: distinct functions differ")
end
-- %p: strings get an identity (interned: same content = same id)
do
local s1 = string.format("%p", "hello")
local s2 = string.format("%p", "hello")
local s3 = string.format("%p", "world")
assertEquals(s1:sub(1, 2), "0x", "%p: string prefix")
assertEquals(s1, s2, "%p: same string content = same id")
assertEquals(s1 ~= s3, true, "%p: different string content differs")
end
-- %p: nil and primitives yield (null)
assertEquals(string.format("%p", nil), "(null)", "%p: nil")
assertEquals(string.format("%p", true), "(null)", "%p: boolean")
assertEquals(string.format("%p", 42), "(null)", "%p: number")
-- %p: repeated calls on same table are stable
do
local t = {}
local ids = {}
for i = 1, 5 do
ids[i] = string.format("%p", t)
end
for i = 2, 5 do
assertEquals(ids[i], ids[1], "%p: repeated call #" .. i .. " stable")
end
end
-- %p: table stored in another table keeps identity
do
local inner = {}
local outer = { ref = inner }
local s1 = string.format("%p", inner)
local s2 = string.format("%p", outer.ref)
assertEquals(s1, s2, "%p: table via reference stable")
end
-- %p: width
do
local t = {}
local s = string.format("%20p", t)
assertEquals(#s >= 20, true, "%p: width pads")
end
-- %p: left-justify
do
local t = {}
local s = string.format("%-20p", t)
assertEquals(#s >= 20, true, "%p: left width pads")
assertEquals(s:sub(#s, #s), " ", "%p: left-justify trailing space")
end