@ -1,83 +1,127 @@
local inspect = require ' inspect '
local inspect = require ' inspect '
local unindent = require ' spec.unindent '
local unindent = require ' spec.unindent '
local is_luajit , ffi = pcall ( require , ' ffi ' )
local is_luajit , ffi = pcall ( require , ' ffi ' )
local has_rawlen = type ( _G.rawlen ) == ' function '
describe ( ' inspect ' , function ( )
describe ( ' inspect ' , function ( )
describe ( ' numbers ' , function ( )
describe ( ' numbers ' , function ( )
it ( ' works ' , function ( )
it ( ' works ' , function ( )
assert.equals ( inspect ( 1 ) , " 1 " )
assert.equals ( " 1 " , inspect ( 1 ) )
assert.equals ( inspect ( 1.5 ) , " 1.5 " )
assert.equals ( " 1.5 " , inspect ( 1.5 ) )
assert.equals ( inspect ( - 3.14 ) , " -3.14 " )
assert.equals ( " -3.14 " , inspect ( - 3.14 ) )
end )
end )
end )
end )
describe ( ' strings ' , function ( )
describe ( ' strings ' , function ( )
it ( ' puts quotes around regular strings ' , function ( )
it ( ' puts quotes around regular strings ' , function ( )
assert.equals ( inspect ( " hello " ) , ' "hello" ' )
assert.equals ( ' "hello" ' , inspect ( " hello " ) )
end )
end )
it ( ' puts apostrophes around strings with quotes ' , function ( )
it ( ' puts apostrophes around strings with quotes ' , function ( )
assert.equals ( inspect ( ' I have "quotes" ' ) , " 'I have \" quotes \" ' " )
assert.equals ( " 'I have \" quotes \" ' " , inspect ( ' I have "quotes" ' ) )
end )
end )
it ( ' uses regular quotes if the string has both quotes and apostrophes ' , function ( )
it ( ' uses regular quotes if the string has both quotes and apostrophes ' , function ( )
assert.equals ( inspect ( " I have \" quotes \" and 'apostrophes' " ) , ' "I have \\ "quotes \\ " and \' apostrophes \' " ' )
assert.equals ( ' "I have \\ "quotes \\ " and \' apostrophes \' " ' , inspect ( " I have \" quotes \" and 'apostrophes' " ) )
end )
end )
it ( ' escapes newlines properly ' , function ( )
it ( ' escapes newlines properly ' , function ( )
assert.equals ( inspect ( ' I have \n new \n lines ' ) , ' "I have \\ n new \\ n lines" ' )
assert.equals ( ' "I have \\ n new \\ n lines" ' , inspect ( ' I have \n new \n lines ' ) )
end )
end )
it ( ' escapes tabs properly ' , function ( )
it ( ' escapes tabs properly ' , function ( )
assert.equals ( inspect ( ' I have \t a tab character ' ) , ' "I have \\ t a tab character" ' )
assert.equals ( ' "I have \\ t a tab character" ' , inspect ( ' I have \t a tab character ' ) )
end )
end )
it ( ' escapes backspaces properly ' , function ( )
it ( ' escapes backspaces properly ' , function ( )
assert.equals ( inspect ( ' I have \b a back space ' ) , ' "I have \\ b a back space" ' )
assert.equals ( ' "I have \\ b a back space" ' , inspect ( ' I have \b a back space ' ) )
end )
it ( ' escapes unnamed control characters with 1 or 2 digits ' , function ( )
assert.equals ( ' "Here are some control characters: \\ 0 \\ 1 \\ 6 \\ 17 \\ 27 \\ 31" ' ,
inspect ( ' Here are some control characters: \0 \1 \6 \17 \27 \31 ' ) )
end )
it ( ' escapes unnamed control characters with 3 digits when they are followed by numbers ' , function ( )
assert.equals ( ' "Control chars followed by digits \\ 0001 \\ 0011 \\ 0061 \\ 0171 \\ 0271 \\ 0311" ' ,
inspect ( ' Control chars followed by digits \000 1 \001 1 \006 1 \017 1 \027 1 \031 1 ' ) )
end )
end )
it ( ' backslashes its backslashes ' , function ( )
it ( ' backslashes its backslashes ' , function ( )
assert.equals ( inspect ( ' I have \\ a backslash ' ) , ' "I have \\ \\ a backslash" ' )
assert.equals ( ' "I have \\ \\ a backslash" ' , inspect ( ' I have \\ a backslash ' ) )
assert.equals ( inspect ( ' I have \\ \\ two backslashes ' ) , ' "I have \\ \\ \\ \\ two backslashes" ' )
assert.equals ( ' "I have \\ \\ \\ \\ two backslashes" ' , inspect ( ' I have \\ \\ two backslashes ' ) )
assert.equals ( inspect ( ' I have \\ \n a backslash followed by a newline ' ) , ' "I have \\ \\ \\ n a backslash followed by a newline" ' )
assert.equals ( ' "I have \\ \\ \\ n a backslash followed by a newline" ' ,
inspect ( ' I have \\ \n a backslash followed by a newline ' ) )
end )
end )
end )
end )
it ( ' works with nil ' , function ( )
it ( ' works with nil ' , function ( )
assert.equals ( inspect ( nil ) , ' nil ' )
assert.equals ( ' nil ' , inspect ( nil ) )
end )
end )
it ( ' works with functions ' , function ( )
it ( ' works with functions ' , function ( )
assert.equals ( inspect ( { print , type , print } ) , ' { <function 1>, <function 2>, <function 1> } ' )
assert.equals ( ' { <function 1>, <function 2>, <function 1> } ' , inspect ( { print , type , print } ) )
end )
end )
it ( ' works with booleans ' , function ( )
it ( ' works with booleans ' , function ( )
assert.equals ( inspect ( true ) , ' true ' )
assert.equals ( ' true ' , inspect ( true ) )
assert.equals ( inspect ( false ) , ' false ' )
assert.equals ( ' false ' , inspect ( false ) )
end )
end )
if is_luajit then
if is_luajit then
describe ( ' luajit cdata ' , function ( )
it ( ' works with luajit cdata ' , function ( )
it ( ' works with luajit cdata ' , function ( )
assert.equals ( ' { cdata<int>: PTR, ctype<int>, cdata<int>: PTR } ' ,
assert.equals ( inspect ( { ffi.new ( " int " , 1 ) , ffi.typeof ( " int " ) , ffi.typeof ( " int " ) ( 1 ) } ) , ' { <cdata 1>, <cdata 2>, <cdata 3> } ' )
inspect ( { ffi.new ( " int " , 1 ) , ffi.typeof ( " int " ) , ffi.typeof ( " int " ) ( 1 ) } ) : gsub ( ' (0x%x+) ' , ' PTR ' ) )
end )
end )
end )
end
end
describe ( ' tables ' , function ( )
describe ( ' tables ' , function ( )
it ( ' works with simple array-like tables ' , function ( )
it ( ' works with simple array-like tables ' , function ( )
assert.equals ( inspect ( { 1 , 2 , 3 } ) , " { 1, 2, 3 } " )
assert.equals ( " { 1, 2, 3 } " , inspect ( { 1 , 2 , 3 } ) )
end )
end )
it ( ' works with nested arrays ' , function ( )
it ( ' works with nested arrays ' , function ( )
assert.equals ( inspect ( { ' a ' , ' b ' , ' c ' , { ' d ' , ' e ' } , ' f ' } ) , ' { "a", "b", "c", { "d", "e" }, "f" } ' )
assert.equals ( ' { "a", "b", "c", { "d", "e" }, "f" } ' , inspect ( { ' a ' , ' b ' , ' c ' , { ' d ' , ' e ' } , ' f ' } ) )
end )
end )
if has_rawlen then
it ( ' handles arrays with a __len metatable correctly (ignoring the __len metatable and using rawlen) ' , function ( )
local arr = setmetatable ( { 1 , 2 , 3 } , { __len = function ( ) return nil end } )
assert.equals ( " { 1, 2, 3, \n <metatable> = { \n __len = <function 1> \n } \n } " , inspect ( arr ) )
end )
it ( ' handles tables with a __pairs metamethod (ignoring the __pairs metamethod and using next) ' , function ( )
local t = setmetatable ( { { } , name = " yeah " } , { __pairs = function ( ) end } )
assert.equals (
unindent ( [ [ { { } ,
name = " yeah " ,
< metatable > = {
__pairs = < function 1 >
}
} ] ] ) ,
inspect ( t ) )
end )
end
it ( ' works with simple dictionary tables ' , function ( )
it ( ' works with simple dictionary tables ' , function ( )
assert.equals ( inspect ( { a = 1 , b = 2 } ) , " { \n a = 1, \n b = 2 \n } " )
assert.equals ( " { \n a = 1, \n b = 2 \n } " , inspect ( { a = 1 , b = 2 } ) )
end )
it ( ' identifies tables with no number 1 as struct-like ' , function ( )
assert.equals ( unindent ( [ [ {
[ 2 ] = 1 ,
[ 25 ] = 1 ,
id = 1
}
] ] ) , inspect ( { [ 2 ] = 1 , [ 25 ] = 1 , id = 1 } ) )
end )
it ( ' identifies numeric non-array keys as dictionary keys ' , function ( )
assert.equals ( " { 1, 2, \n [-1] = true \n } " , inspect ( { 1 , 2 , [ - 1 ] = true } ) )
assert.equals ( " { 1, 2, \n [1.5] = true \n } " , inspect ( { 1 , 2 , [ 1.5 ] = true } ) )
end )
end )
it ( ' sorts keys in dictionary tables ' , function ( )
it ( ' sorts keys in dictionary tables ' , function ( )
@ -86,7 +130,7 @@ describe( 'inspect', function()
[ coroutine.create ( function ( ) end ) ] = 1 ,
[ coroutine.create ( function ( ) end ) ] = 1 ,
[ 14 ] = 1 , [ { c = 2 } ] = 1 , [ true ] = 1
[ 14 ] = 1 , [ { c = 2 } ] = 1 , [ true ] = 1
}
}
assert.equals ( inspect ( t ) , unindent ( [ [
assert.equals ( unindent ( [ [
{ 1 , 2 , 3 ,
{ 1 , 2 , 3 ,
[ 14 ] = 1 ,
[ 14 ] = 1 ,
[ true ] = 1 ,
[ true ] = 1 ,
@ -98,30 +142,30 @@ describe( 'inspect', function()
[ < function 1 > ] = 1 ,
[ < function 1 > ] = 1 ,
[ < thread 1 > ] = 1
[ < thread 1 > ] = 1
}
}
] ] ) )
] ] ) , inspect ( t ) )
end )
end )
it ( ' works with nested dictionary tables ' , function ( )
it ( ' works with nested dictionary tables ' , function ( )
assert.equals ( inspect ( { d = 3 , b = { c = 2 } , a = 1 } ) , unindent ( [ [ {
assert.equals ( unindent ( [ [ {
a = 1 ,
a = 1 ,
b = {
b = {
c = 2
c = 2
} ,
} ,
d = 3
d = 3
} ] ] ) )
} ] ] ) , inspect ( { d = 3 , b = { c = 2 } , a = 1 } ) )
end )
end )
it ( ' works with hybrid tables ' , function ( )
it ( ' works with hybrid tables ' , function ( )
assert.equals (
assert.equals ( unindent ( [ [
inspect ( { ' a ' , { b = 1 } , 2 , c = 3 , [ ' ahoy you ' ] = 4 } ) ,
unindent ( [ [
{ " a " , {
{ " a " , {
b = 1
b = 1
} , 2 ,
} , 2 ,
[ " ahoy you " ] = 4 ,
[ " ahoy you " ] = 4 ,
c = 3
c = 3
}
}
] ] ) )
] ] ) , inspect ( { ' a ' , { b = 1 } , 2 , c = 3 , [ ' ahoy you ' ] = 4 } ) )
end )
end )
it ( ' displays <table x> instead of repeating an already existing table ' , function ( )
it ( ' displays <table x> instead of repeating an already existing table ' , function ( )
@ -130,7 +174,7 @@ describe( 'inspect', function()
a [ 4 ] = b
a [ 4 ] = b
a [ 5 ] = a
a [ 5 ] = a
a [ 6 ] = b
a [ 6 ] = b
assert.equals ( inspect ( a ) , '<1>{ 1, 2, 3, <2>{ "a", "b", "c", <table 1> }, <table 1>, <table 2> } ' )
assert.equals ( '<1>{ 1, 2, 3, <2>{ "a", "b", "c", <table 1> }, <table 1>, <table 2> } ' , inspect ( a ) )
end )
end )
describe ( ' The depth parameter ' , function ( )
describe ( ' The depth parameter ' , function ( )
@ -138,7 +182,7 @@ describe( 'inspect', function()
local keys = { [ level5 ] = true }
local keys = { [ level5 ] = true }
it ( ' has infinite depth by default ' , function ( )
it ( ' has infinite depth by default ' , function ( )
assert.equals ( inspect ( level5 ) , unindent ( [ [
assert.equals ( unindent ( [ [
{ 1 , 2 , 3 ,
{ 1 , 2 , 3 ,
a = {
a = {
b = {
b = {
@ -150,24 +194,24 @@ describe( 'inspect', function()
}
}
}
}
}
}
] ] ) )
] ] ) , inspect ( level5 ) )
end )
end )
it ( ' is modifiable by the user ' , function ( )
it ( ' is modifiable by the user ' , function ( )
assert.equals ( inspect ( level5 , { depth = 2 } ) , unindent ( [ [
assert.equals ( unindent ( [ [
{ 1 , 2 , 3 ,
{ 1 , 2 , 3 ,
a = {
a = {
b = { ... }
b = { ... }
}
}
}
}
] ] ) )
] ] ) , inspect ( level5 , { depth = 2 } ) )
assert.equals ( inspect ( level5 , { depth = 1 } ) , unindent ( [ [
assert.equals ( unindent ( [ [
{ 1 , 2 , 3 ,
{ 1 , 2 , 3 ,
a = { ... }
a = { ... }
}
}
] ] ) )
] ] ) , inspect ( level5 , { depth = 1 } ) )
assert.equals ( inspect ( level5 , { depth = 4 } ) , unindent ( [ [
assert.equals ( unindent ( [ [
{ 1 , 2 , 3 ,
{ 1 , 2 , 3 ,
a = {
a = {
b = {
b = {
@ -177,13 +221,13 @@ describe( 'inspect', function()
}
}
}
}
}
}
] ] ) )
] ] ) , inspect ( level5 , { depth = 4 } ) )
assert.equals ( inspect ( level5 , { depth = 0 } ) , " {...} " )
assert.equals ( " {...} " , inspect ( level5 , { depth = 0 } ) )
end )
end )
it ( ' respects depth on keys ' , function ( )
it ( ' respects depth on keys ' , function ( )
assert.equals ( inspect ( keys , { depth = 4 } ) , unindent ( [ [
assert.equals ( unindent ( [ [
{
{
[ { 1 , 2 , 3 ,
[ { 1 , 2 , 3 ,
a = {
a = {
@ -193,7 +237,7 @@ describe( 'inspect', function()
}
}
} ] = true
} ] = true
}
}
] ] ) )
] ] ) , inspect ( keys , { depth = 4 } ) )
end )
end )
end )
end )
@ -201,7 +245,7 @@ describe( 'inspect', function()
it ( ' changes the substring used for newlines ' , function ( )
it ( ' changes the substring used for newlines ' , function ( )
local t = { a = { b = 1 } }
local t = { a = { b = 1 } }
assert.equal ( inspect ( t , { newline = ' @ ' } ) , " {@ a = {@ b = 1@ }@} " )
assert.equal ( " {@ a = {@ b = 1@ }@} " , inspect ( t , { newline = ' @ ' } ) )
end )
end )
end )
end )
@ -209,7 +253,7 @@ describe( 'inspect', function()
it ( ' changes the substring used for indenting ' , function ( )
it ( ' changes the substring used for indenting ' , function ( )
local t = { a = { b = 1 } }
local t = { a = { b = 1 } }
assert.equal ( inspect ( t , { indent = ' >>> ' } ) , " { \n >>>a = { \n >>>>>>b = 1 \n >>>} \n } " )
assert.equal ( " { \n >>>a = { \n >>>>>>b = 1 \n >>>} \n } " , inspect ( t , { indent = ' >>> ' } ) )
end )
end )
end )
end )
@ -218,56 +262,56 @@ describe( 'inspect', function()
it ( ' removes one element ' , function ( )
it ( ' removes one element ' , function ( )
local names = { ' Andrew ' , ' Peter ' , ' Ann ' }
local names = { ' Andrew ' , ' Peter ' , ' Ann ' }
local removeAnn = function ( item ) if item ~= ' Ann ' then return item end end
local removeAnn = function ( item ) if item ~= ' Ann ' then return item end end
assert.equals ( inspect ( names , { process = removeAnn } ) , ' { "Andrew", "Peter" } ' )
assert.equals ( ' { "Andrew", "Peter" } ' , inspect ( names , { process = removeAnn } ) )
end )
end )
it ( ' uses the path ' , function ( )
it ( ' uses the path ' , function ( )
local names = { ' Andrew ' , ' Peter ' , ' Ann ' }
local names = { ' Andrew ' , ' Peter ' , ' Ann ' }
local removeThird = function ( item , path ) if path [ 1 ] ~= 3 then return item end end
local removeThird = function ( item , path ) if path [ 1 ] ~= 3 then return item end end
assert.equals ( inspect ( names , { process = removeThird } ) , ' { "Andrew", "Peter" } ' )
assert.equals ( ' { "Andrew", "Peter" } ' , inspect ( names , { process = removeThird } ) )
end )
end )
it ( ' replaces items ' , function ( )
it ( ' replaces items ' , function ( )
local names = { ' Andrew ' , ' Peter ' , ' Ann ' }
local names = { ' Andrew ' , ' Peter ' , ' Ann ' }
local filterAnn = function ( item ) return item == ' Ann ' and ' <filtered> ' or item end
local filterAnn = function ( item ) return item == ' Ann ' and ' <filtered> ' or item end
assert.equals ( inspect ( names , { process = filterAnn } ) , ' { "Andrew", "Peter", "<filtered>" } ' )
assert.equals ( ' { "Andrew", "Peter", "<filtered>" } ' , inspect ( names , { process = filterAnn } ) )
end )
end )
it ( ' nullifies metatables ' , function ( )
it ( ' nullifies metatables ' , function ( )
local mt = { ' world ' }
local mt = { ' world ' }
local t = setmetatable ( { ' hello ' } , mt )
local t = setmetatable ( { ' hello ' } , mt )
local removeMt = function ( item ) if item ~= mt then return item end end
local removeMt = function ( item ) if item ~= mt then return item end end
assert.equals ( inspect ( t , { process = removeMt } ) , ' { "hello" } ' )
assert.equals ( ' { "hello" } ' , inspect ( t , { process = removeMt } ) )
end )
end )
it ( ' nullifies metatables using their paths ' , function ( )
it ( ' nullifies metatables using their paths ' , function ( )
local mt = { ' world ' }
local mt = { ' world ' }
local t = setmetatable ( { ' hello ' } , mt )
local t = setmetatable ( { ' hello ' } , mt )
local removeMt = function ( item , path ) if path [ # path ] ~= inspect.METATABLE then return item end end
local removeMt = function ( item , path ) if path [ # path ] ~= inspect.METATABLE then return item end end
assert.equals ( inspect ( t , { process = removeMt } ) , ' { "hello" } ' )
assert.equals ( ' { "hello" } ' , inspect ( t , { process = removeMt } ) )
end )
end )
it ( ' nullifies the root object ' , function ( )
it ( ' nullifies the root object ' , function ( )
local names = { ' Andrew ' , ' Peter ' , ' Ann ' }
local names = { ' Andrew ' , ' Peter ' , ' Ann ' }
local removeNames = function ( item ) if item ~= names then return item end end
local removeNames = function ( item ) if item ~= names then return item end end
assert.equals ( inspect ( names , { process = removeNames } ) , ' nil ' )
assert.equals ( ' nil ' , inspect ( names , { process = removeNames } ) )
end )
end )
it ( ' changes keys ' , function ( )
it ( ' changes keys ' , function ( )
local dict = { a = 1 }
local dict = { a = 1 }
local changeKey = function ( item , path ) return item == ' a ' and ' x ' or item end
local changeKey = function ( item ) return item == ' a ' and ' x ' or item end
assert.equals ( inspect ( dict , { process = changeKey } ) , ' { \n x = 1 \n } ' )
assert.equals ( ' { \n x = 1 \n } ' , inspect ( dict , { process = changeKey } ) )
end )
end )
it ( ' nullifies keys ' , function ( )
it ( ' nullifies keys ' , function ( )
local dict = { a = 1 , b = 2 }
local dict = { a = 1 , b = 2 }
local removeA = function ( item , path ) return item ~= ' a ' and item or nil end
local removeA = function ( item ) return item ~= ' a ' and item or nil end
assert.equals ( inspect ( dict , { process = removeA } ) , ' { \n b = 2 \n } ' )
assert.equals ( ' { \n b = 2 \n } ' , inspect ( dict , { process = removeA } ) )
end )
end )
it ( ' prints inspect.KEY & inspect.METATABLE ' , function ( )
it ( ' prints inspect.KEY & inspect.METATABLE ' , function ( )
local t = { inspect.KEY , inspect.METATABLE }
local t = { inspect.KEY , inspect.METATABLE }
assert.equals ( inspect ( t ) , " { inspect.KEY, inspect.METATABLE } " )
assert.equals ( " { inspect.KEY, inspect.METATABLE } " , inspect ( t ) )
end )
end )
it ( ' marks key paths with inspect.KEY and metatables with inspect.METATABLE ' , function ( )
it ( ' marks key paths with inspect.KEY and metatables with inspect.METATABLE ' , function ( )
@ -281,7 +325,7 @@ describe( 'inspect', function()
inspect ( t , { process = addItem } )
inspect ( t , { process = addItem } )
assert.same ( items , {
assert.same ( {
{ item = t , path = { } } ,
{ item = t , path = { } } ,
{ item = { a = 1 } , path = { { a = 1 } , inspect.KEY } } ,
{ item = { a = 1 } , path = { { a = 1 } , inspect.KEY } } ,
{ item = ' a ' , path = { { a = 1 } , inspect.KEY , ' a ' , inspect.KEY } } ,
{ item = ' a ' , path = { { a = 1 } , inspect.KEY , ' a ' , inspect.KEY } } ,
@ -292,9 +336,15 @@ describe( 'inspect', function()
{ item = { c = 3 } , path = { { a = 1 } , inspect.METATABLE } } ,
{ item = { c = 3 } , path = { { a = 1 } , inspect.METATABLE } } ,
{ item = ' c ' , path = { { a = 1 } , inspect.METATABLE , ' c ' , inspect.KEY } } ,
{ item = ' c ' , path = { { a = 1 } , inspect.METATABLE , ' c ' , inspect.KEY } } ,
{ item = 3 , path = { { a = 1 } , inspect.METATABLE , ' c ' } }
{ item = 3 , path = { { a = 1 } , inspect.METATABLE , ' c ' } }
} )
} , items )
end )
end )
it ( ' handles recursive tables correctly ' , function ( )
local tbl = { 1 , 2 , 3 }
tbl.loop = tbl
inspect ( tbl , { process = function ( x ) return x end } )
end )
end )
end )
describe ( ' metatables ' , function ( )
describe ( ' metatables ' , function ( )
@ -302,7 +352,7 @@ describe( 'inspect', function()
it ( ' includes the metatable as an extra hash attribute ' , function ( )
it ( ' includes the metatable as an extra hash attribute ' , function ( )
local foo = { foo = 1 , __mode = ' v ' }
local foo = { foo = 1 , __mode = ' v ' }
local bar = setmetatable ( { a = 1 } , foo )
local bar = setmetatable ( { a = 1 } , foo )
assert.equals ( inspect ( bar ) , unindent ( [ [
assert.equals ( unindent ( [ [
{
{
a = 1 ,
a = 1 ,
< metatable > = {
< metatable > = {
@ -310,60 +360,101 @@ describe( 'inspect', function()
foo = 1
foo = 1
}
}
}
}
] ] ) )
] ] ) , inspect ( bar ) )
end )
end )
it ( ' includes the __tostring metamethod if it exist s' , function ( )
it ( ' can be used on the __tostring metamethod of a table without error s' , function ( )
local foo = { foo = 1 , __tostring = function ( ) return ' hello \n world ' end }
local f = function ( x ) return inspect ( x ) end
local bar = setmetatable ( { a = 1 } , foo )
local tbl = setmetatable ( { x = 1 } , { __tostring = f } )
assert.equals ( inspect ( bar ) , unindent ( [ [
assert.equals ( unindent ( [ [
{ -- hello\nworld
{
a = 1 ,
x = 1 ,
< metatable > = {
< metatable > = {
__tostring = < function 1 > ,
__tostring = < function 1 >
foo = 1
}
}
}
}
] ] ) )
] ] ) , tostring ( tbl ) )
end )
end )
it ( ' includes an error string if __tostring metamethod throws an error ' , function ( )
it ( ' does not allow collecting weak tables while they are being inspected ' , function ( )
local foo = { foo = 1 , __tostring = function ( ) error ( ' hello ' , 0 ) end }
collectgarbage ( ' stop ' )
local bar = setmetatable ( { a = 1 } , foo )
finally ( function ( ) collectgarbage ( ' restart ' ) end )
assert.equals ( inspect ( bar ) , unindent ( [ [
local shimMetatable = {
{ -- error: hello
__mode = ' v ' ,
a = 1 ,
__index = function ( ) return { } end ,
< metatable > = {
}
__tostring = < function 1 > ,
local function shim ( ) return setmetatable ( { } , shimMetatable ) end
foo = 1
local t = shim ( )
}
t.key = shim ( )
assert.equals ( unindent ( [ [
{
key = {
< metatable > = < 1 > {
__index = < function 1 > ,
__mode = " v "
}
} ,
< metatable > = < table 1 >
}
] ] ) , inspect ( t ) )
end )
it ( ' ignores metatables with __metatable field set to non-nil and non-table type ' , function ( )
local function process ( item ) return item end
local function inspector ( data ) return inspect ( data , { process = process } ) end
local foo = setmetatable ( { } , { __metatable = false } )
local bar = setmetatable ( { } , { __metatable = true } )
local baz = setmetatable ( { } , { __metatable = 10 } )
local spam = setmetatable ( { } , { __metatable = nil } )
local eggs = setmetatable ( { } , { __metatable = { } } )
assert.equals ( unindent ( ' {} ' ) , inspector ( foo ) )
assert.equals ( unindent ( ' {} ' ) , inspector ( bar ) )
assert.equals ( unindent ( ' {} ' ) , inspector ( baz ) )
assert.equals ( unindent ( [ [
{
< metatable > = { }
}
}
] ] ) )
] ] ) , inspector ( spam ) )
assert.equals ( unindent ( [ [
{
< metatable > = { }
}
] ] ) , inspector ( eggs ) )
end )
end )
describe ( ' When a table is its own metatable ' , function ( )
describe ( ' When a table is its own metatable ' , function ( )
it ( ' accepts a table that is its own metatable without stack overflowing ' , function ( )
it ( ' accepts a table that is its own metatable without stack overflowing ' , function ( )
local x = { }
local x = { }
setmetatable ( x , x )
setmetatable ( x , x )
assert.equals ( inspect ( x ) , unindent ( [ [
assert.equals ( unindent ( [ [
< 1 > {
< 1 > {
< metatable > = < table 1 >
< metatable > = < table 1 >
}
}
] ] ) )
] ] ) , inspect ( x ) )
end )
end )
it ( ' can invoke the __tostring method without stack overflowing ' , function ( )
it ( ' can invoke the __tostring method without stack overflowing ' , function ( )
local t = { }
local t = { }
t.__index = t
t.__index = t
setmetatable ( t , t )
setmetatable ( t , t )
assert.equals ( inspect ( t ) , unindent ( [ [
assert.equals ( unindent ( [ [
< 1 > {
< 1 > {
__index = < table 1 > ,
__index = < table 1 > ,
< metatable > = < table 1 >
< metatable > = < table 1 >
}
}
] ] ) )
] ] ) , inspect ( t ) )
end )
end )
end )
end )
end )
end )
end )
end )
it ( ' allows changing the global tostring ' , function ( )
local save = _G.tostring
_G.tostring = inspect
local s = tostring ( { 1 , 2 , 3 } )
_G.tostring = save
assert.equals ( " { 1, 2, 3 } " , s )
end )
end )
end )