-- test/test-supple.capi.lua
--
-- Supple - Tests for the capi module
--
-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
--
-- For Licence terms, see COPYING
--

-- Step one, start coverage

pcall(require, 'luacov')

local capi = require 'supple.capi'

local testnames = {}

local real_assert = assert
local total_asserts = 0
local function assert(...)
   local retval = real_assert(...)
   total_asserts = total_asserts + 1
   return retval
end

local function add_test(suite, name, value)
   rawset(suite, name, value)
   testnames[#testnames+1] = name
end

local suite = setmetatable({}, {__newindex = add_test})

function suite.capi_has_api()
   assert(capi._API, "No _API found in the CAPI")
end

function suite.capi_has_abi()
   assert(capi._ABI, "No _ABI found in the CAPI")
end

function suite.capi_explain_non_table_etc()
   local ok = pcall(capi.explain, nil, "");
   assert(ok == false, "Explained nil unexpectedly")
   ok = pcall(capi.explain, true, "");
   assert(ok == false, "Explained true unexpectedly")
   ok = pcall(capi.explain, false, "");
   assert(ok == false, "Explained false unexpectedly")
   ok = pcall(capi.explain, 123, "");
   assert(ok == false, "Explained number unexpectedly")
   ok = pcall(capi.explain, "", "");
   assert(ok == false, "Explained string unexpectedly")
   ok = pcall(capi.explain, coroutine.create(function()end), "");
   assert(ok == false, "Explained coroutine unexpectedly")
end

function suite.capi_explain_bad_tag()
   local ok = pcall(capi.explain, {}, nil)
   assert(ok == false, "Explained with nil tag unexpectedly")
   ok = pcall(capi.explain, {}, 1233)
   assert(ok == false, "Explained with number tag unexpectedly")
   ok = pcall(capi.explain, {}, true)
   assert(ok == false, "Explained with true tag unexpectedly")
   ok = pcall(capi.explain, {}, false)
   assert(ok == false, "Explained with false tag unexpectedly")
   ok = pcall(capi.explain, {}, {})
   assert(ok == false, "Explained with table tag unexpectedly")
   ok = pcall(capi.explain, {}, function()end)
   assert(ok == false, "Explained with function tag unexpectedly")
   ok = pcall(capi.explain, {}, coroutine.create(function()end))
   assert(ok == false, "Explained with coroutine tag unexpectedly")
end

function suite.capi_explain_simple_table()
   local expn = capi.explain({}, "fish")
   assert(type(expn) == "table", "Explanation wasn't a table")
   assert(type(expn.tag) == "string", "Tag not a string")
   assert(expn.tag == "fish", "Tag wasn't as passed in")
   assert(type(expn.type) == "string", "Type wasn't a string")
   assert(expn.type == "table", "Type didn't declare it is a table")
   assert(expn.methods == nil, "Methods table was present in some form")
end

function suite.capi_explain_table_with_mode()
   local tab = setmetatable({}, { __mode="k"})
   local expn = capi.explain(tab, "fish")
   assert(type(expn) == "table", "Explanation wasn't a table")
   assert(type(expn.tag) == "string", "Tag not a string")
   assert(expn.tag == "fish", "Tag wasn't as passed in")
   assert(type(expn.type) == "string", "Type wasn't a string")
   assert(expn.type == "table", "Type didn't declare it is a table")
   assert(type(expn.methods) == "table", "Methods were not present")
   assert(next(expn.methods) == nil, "Methods had something in")
end

function suite.capi_explain_table_with_index()
   local tab = setmetatable({}, { __index = {} })
   local expn = capi.explain(tab, "fish")
   assert(type(expn) == "table", "Explanation wasn't a table")
   assert(type(expn.tag) == "string", "Tag not a string")
   assert(expn.tag == "fish", "Tag wasn't as passed in")
   assert(type(expn.type) == "string", "Type wasn't a string")
   assert(expn.type == "table", "Type didn't declare it is a table")
   assert(type(expn.methods) == "table", "Methods were not present")
   assert(expn.methods[1] == "__index", "Missing __index")
   assert(expn.methods[2] == nil, "Something other than __index")
end

function suite.capi_explain_function()
   local expn = capi.explain(function()end, "fish")
   assert(type(expn) == "table", "Explanation wasn't a table")
   assert(type(expn.tag) == "string", "Tag not a string")
   assert(expn.tag == "fish", "Tag wasn't as passed in")
   assert(type(expn.type) == "string", "Type wasn't a string")
   assert(expn.type == "function", "Type didn't declare it is a table")
   assert(expn.methods == nil, "Methods table was present in some form")
end

function suite.capi_type_chains()
   assert(capi.type({}) == "table", "Plain table wasn't a table")
   local t = setmetatable({}, { __type = function() return "jeff" end })
   assert(capi.type(t) == "jeff", "Chained table wasn't jeff")
end

function suite.capi_rawtype()
   assert(capi.rawtype() == "nil", "nil was't nil")
   assert(capi.rawtype({}) == "table", "table wasn't")
   assert(capi.rawtype("") == "string", "string wasn't")
   assert(capi.rawtype(false) == "boolean", "boolean wasn't")
   assert(capi.rawtype(function()end) == "function", "function wasn't")
   assert(capi.rawtype(coroutine.create(function()end)) == "thread",
	  "coroutine wasn't")
   assert(capi.rawtype(capi.new_proxy("table")) == "userdata",
	  "userdata wasn't")
end

function suite.capi_new_proxy_fails()
   local ok = pcall(capi.new_proxy)
   assert(ok == false, "Made a new proxy with a nil type string")
   ok = pcall(capi.new_proxy, {})
   assert(ok == false, "Made a new proxy with a table type string")
end

function suite.capi_new_proxy_function()
   local proxy, mt = capi.new_proxy("function")
   local sparetype = mt.__type
   mt.__type = nil
   assert(capi.type(proxy) == "userdata", "Proxy wasn't userdata")
   mt.__type = sparetype
   assert(capi.type(proxy) == "function", "Proxy did not override type")
   assert(tostring(proxy):match("^function: "), "Proxy didn't tostring nicely")
end

function suite.capi_new_proxy_table()
   local proxy, mt = capi.new_proxy("table")
   local sparetype = mt.__type
   mt.__type = nil
   assert(type(proxy) == "userdata", "Proxy wasn't userdata")
   mt.__type = sparetype
   assert(capi.type(proxy) == "table", "Proxy did not override type")
   assert(tostring(proxy):match("^table: "), "Proxy didn't tostring nicely")
end

function suite.capi_new_proxy_table()
   local proxy, mt = capi.new_proxy("userdata")
   local sparetype = mt.__type
   mt.__type = nil
   assert(type(proxy) == "userdata", "Proxy wasn't userdata")
   mt.__type = sparetype
   assert(capi.type(proxy) == "userdata", "Proxy did not override type")
   assert(tostring(proxy):match("^userdata: "), "Proxy didn't tostring nicely")
end

function suite.capi_test_next_on_table()
   local t = { foo = "bar" }
   local k, v = capi.next(t)
   assert(k == "foo", "Key was not 'foo'")
   assert(v == "bar", "Value was not 'bar'")
   assert(capi.next(t, "foo") == nil, "Next after 'foo' was not nil")
end

function suite.capi_test_next_on_proxied_table()
   local proxied = { foo = "bar" }
   local proxy, mt = capi.new_proxy("table")
   mt.__next = function(self, key)
		  return next(proxied, key)
	       end
   local k, v = capi.next(proxy)
   assert(k == "foo", "Key was not 'foo'")
   assert(v == "bar", "Value was not 'bar'")
   assert(capi.next(proxy, "foo") == nil, "Next after 'foo' was not nil")
end

function suite.capi_test_ipairs_on_table()
   local tab = { "a", "b" }
   for i, v in capi.ipairs(tab) do
      assert(i > 0 and i < 3, "Index out of range")
      assert((i == 1 and v == "a") or
	     (i == 2 and v == "b"),
	     "Key/value pair wrong")
   end
end

function suite.capi_test_ipairs_on_proxied_table()
   local proxied = { "a", "b" }
   local tab, mt = capi.new_proxy("table")
   mt.__index = function(self, idx)
		   return proxied[idx]
		end
   for i, v in capi.ipairs(tab) do
      assert(i > 0 and i < 3, "Index out of range")
      assert((i == 1 and v == "a") or
	     (i == 2 and v == "b"),
	     "Key/value pair wrong")
   end
end

function suite.capi_test_pairs_on_table()
   local tab = { foo = "bar", baz = "meta" }
   for k, v in capi.pairs(tab) do
      assert(k == "foo" or k == "baz", "Key not right")
      assert((k == "foo" and v == "bar") or
	     (k == "baz" and v == "meta"),
	     "key/value pair wrong")
   end
end

function suite.capi_test_pairs_on_proxied_table()
   local proxied = { foo = "bar", baz = "meta" }
   local tab, mt = capi.new_proxy("table")
   mt.__next = function(self, idx)
		  return next(proxied, idx)
	       end
   for k, v in capi.pairs(tab) do
      assert(k == "foo" or k == "baz", "Key not right")
      assert((k == "foo" and v == "bar") or
	     (k == "baz" and v == "meta"),
	     "key/value pair wrong")
   end
end

local count_ok = 0
for _, testname in ipairs(testnames) do
--   print("Run: " .. testname)
   local ok, err = xpcall(suite[testname], debug.traceback)
   if not ok then
      print(err)
      print()
   else
      count_ok = count_ok + 1
   end
end

print(tostring(count_ok) .. "/" .. tostring(#testnames) .. " [" .. tostring(total_asserts) .. "] OK")

os.exit(count_ok == #testnames and 0 or 1)
