Skip to content

Commit

Permalink
Merge pull request #4 from bandaloo/master
Browse files Browse the repository at this point in the history
handle reference cycles in `is_deep_equal` to avoid stack overflow
  • Loading branch information
EvandroLG authored Feb 14, 2023
2 parents bad56d0 + e97db5b commit d34b625
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 4 deletions.
4 changes: 2 additions & 2 deletions simple_test-1.0.1-0.rockspec
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package = 'simple_test'
version = '1.0.1-0'
version = '1.0.2-0'

source = {
url = 'git://github.com/evandrolg/simple_test.git',
tag = 'v1.0.1'
tag = 'v1.0.2'
}

description = {
Expand Down
14 changes: 12 additions & 2 deletions src/simple_test/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ function is_table(obj)
return type(obj) == 'table'
end

function is_deep_equal(a, b)
local function is_deep_equal_helper(a, b, table_pairs)
if is_table(a) and is_table(b) then
if (#a ~= #b) then return false end

if table_pairs[a] == b then
return true
end

table_pairs[a] = b

for k in pairs(a) do
if not is_deep_equal(a[k], b[k]) then return false end
if not is_deep_equal_helper(a[k], b[k], table_pairs) then return false end
end

return true
Expand All @@ -16,6 +22,10 @@ function is_deep_equal(a, b)
return a == b
end

function is_deep_equal(a, b)
return is_deep_equal_helper(a, b, {})
end

return {
is_deep_equal = is_deep_equal
}
46 changes: 46 additions & 0 deletions test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,49 @@ test('utils.is_deep_equal', function(a)
a.ok(is_deep_equal({ 'a', 'b', 'c', 'd' }, { 'a', 'b', 'c', 'd' }))
a.not_ok(is_deep_equal({ 'a', 'b', 'c', 'd' }, { 'a', 'b', 'c', 'e' }))
end)

test('utils.is_deep_equal (reference cycles, different graph shape)', function(a)
-- structurally equivalent but nested table t is referenced twice in obj1,
-- but copied in obj2
local t = { d = "foo" }

local obj1 = {
a = { c = t },
b = t
}

local obj2 = {
a = { c = { d = "foo" } },
b = { d = "foo" }
}

a.ok(is_deep_equal(obj1, obj2))
end)

test("utils.is_deep_equal (reference cycles, same graph shape)", function(a)
-- tables both contain a self reference to the starting table. they are completely
-- separated graphs
local table_a1 = {}
local table_b1 = {}
table_a1.b = table_b1
table_b1.a = table_a1

local table_a2 = {}
local table_b2 = {}
table_a2.b = table_b2
table_b2.a = table_a2

a.ok(is_deep_equal(table_a1, table_a2))
end)

test("utils.is_deep_equal (reference cycles, connected graphs)", function(a)
-- tables form a simple reference cycle and are part of the same connected
-- graph. because it is a symmetrical graph, they are still structurally
-- equivalent
local table_a = {}
local table_b = {}
table_a.ref = table_b
table_b.ref = table_a

a.ok(is_deep_equal(table_a, table_b))
end)

0 comments on commit d34b625

Please sign in to comment.