diff --git a/spec/assertions_spec.lua b/spec/assertions_spec.lua index c07d8d9..0177006 100644 --- a/spec/assertions_spec.lua +++ b/spec/assertions_spec.lua @@ -77,6 +77,14 @@ describe("Test Assertions", function() assert.same(t2, t1) end) + it("Checks same() assertion handles table keys properly", function() + assert.are.same({ [{}] = 1 }, { [{}] = 1 }) + assert.are.not_same({ [{}] = 1 }, { [{}] = 2 }) + assert.are.not_same({ }, { [{}] = 2 }) + assert.are.not_same({ [{"a"}] = 1 }, { [{"b"}] = 1 }) + assert.are.not_same({ [{"a"}] = 1, [{"a"}] = 1 }, { [{"b"}] = 1, [{"a"}] = 1, [{"a"}] = 2 }) + end) + it("Checks same() assertion to handle recursive tables", function() local t1 = { k1 = 1, k2 = 2 } local t2 = { k1 = 1, k2 = 2 } @@ -89,6 +97,17 @@ describe("Test Assertions", function() assert.same(t1, t3) end) + it("Checks same() assertion handles table keys with recursion properly", function() + local a = {} + local b = {} + local t1 = { [a] = a } + local t2 = { [b] = a } + + assert.are.same(t1, t2) + assert.are.same({ [{ [{}] = 2 }] = 1 }, { [{ [{}] = 2 }] = 1 }) + assert.are.not_same({ [{ [{}] = 2 }] = 1 }, { [{ [{}] = 3 }] = 1 }) + end) + it("Checks same() assertion to handle recursive tables that don't match", function() local t1 = {} local t2 = {} @@ -168,6 +187,7 @@ describe("Test Assertions", function() assert.is_table(c1[2][3][3][3][2][3][3][3][2][3][3][3][2]) assert.is_nil(m1[2][3][3][3][2][3][3][3][2][3][3][3][2]) assert.are_not_same(c1, m1) + assert.are_not_same({ [c1] = 1 }, { [m1] = 1 }) end) it("Checks to see if tables 1 and 2 are equal", function() @@ -192,6 +212,11 @@ describe("Test Assertions", function() assert.is_not.unique(tablenotunique) end) + it("Checks to see if table1 only contains unique elements, including keys", function() + assert.is.not_unique({ [{}] = 1, [{}] = 1 }, true) + assert.is.not_unique({{ [{}] = 1 }, { [{}] = 1 }}, true) + end) + it("Checks near() assertion handles tolerances", function() assert.is.error(function() assert.near(0) end) -- minimum 3 arguments assert.is.error(function() assert.near(0, 0) end) -- minimum 3 arguments diff --git a/spec/matchers_spec.lua b/spec/matchers_spec.lua index 104904b..74ac5b2 100644 --- a/spec/matchers_spec.lua +++ b/spec/matchers_spec.lua @@ -164,6 +164,10 @@ describe("Test Matchers", function() assert.is_false(match.is_same(nil)("a string")) end) + it("Checks same() matcher to handle table keys properly", function() + assert.is_true(match.is_same({ [{}] = 1 })({ [{}] = 1})) + end) + it("Checks ref() matcher", function() local t = {} local func = function() end @@ -220,6 +224,11 @@ describe("Test Matchers", function() assert.is_true(match.is_not.unique()(tablenotunique)) end) + it("Checks to see if table1 only contains unique elements, including table keys", function() + assert.is_true(match.is_not.unique(true)({ [{}] = 1, [{}] = 1 }, true)) + assert.is_true(match.is_not.unique(true)({{ [{}] = 1 }, { [{}] = 1 }}, true)) + end) + it("Checks '_' chaining of modifiers and match", function() assert.is_true(match.is_string()("abc")) assert.is_true(match.is_true()(true)) diff --git a/src/util.lua b/src/util.lua index da2f247..2c460bd 100644 --- a/src/util.lua +++ b/src/util.lua @@ -40,23 +40,58 @@ function util.deepcompare(t1,t2,ignore_mt,cycles,thresh1,thresh2) cycles[1][t1] = cycles[1][t1] + 1 cycles[2][t2] = cycles[2][t2] + 1 + local table_keys = {{}, {}} -- keys of type 'table' that we'll handle later + for k1,v1 in next, t1 do local v2 = t2[k1] - if v2 == nil then + if v2 == nil and type(k1) == 'table' then + table.insert(table_keys[1], k1) + elseif v2 == nil then return false, {k1} + else + local same, crumbs = util.deepcompare(v1,v2,nil,cycles,thresh1,thresh2) + if not same then + crumbs = crumbs or {} + table.insert(crumbs, k1) + return false, crumbs + end + end + end + for k2,_ in next, t2 do + -- only check whether each element has a t1 counterpart, actual comparison + -- has been done in first loop above + if t1[k2] == nil then + if type(k2) == 'table' then + table.insert(table_keys[2], k2) + else + return false, {k2} + end end + end - local same, crumbs = util.deepcompare(v1,v2,nil,cycles,thresh1,thresh2) - if not same then - crumbs = crumbs or {} + -- try to match up every pair with every other pair. remove a matched pair + -- or fail if no match was found. + for _, k1 in ipairs(table_keys[1]) do + local key_same, value_same = false, false + local key_crumbs, value_crumbs = nil, nil + for j, k2 in ipairs(table_keys[2]) do + key_same, key_crumbs = util.deepcompare(k1, k2, nil, cycles, thresh1, thresh2) + value_same, value_crumbs = util.deepcompare(t1[k1], t2[k2], nil, cycles, thresh1, thresh2) + if key_same and value_same then + table.remove(table_keys[2], j) + break + end + end + if not key_same or not value_same then + local crumbs = key_crumbs or value_crumbs or {} table.insert(crumbs, k1) return false, crumbs end end - for k2,_ in next, t2 do - -- only check whether each element has a t1 counterpart, actual comparison - -- has been done in first loop above - if t1[k2] == nil then return false, {k2} end + + -- check that we've matched all keys from the second table as well + if #table_keys[2] > 0 then + return false, {table_keys[2][1]} end cycles[1][t1] = cycles[1][t1] - 1