
--[[--------------------------------------------------------------------------

    This file is part of lunit 0.5.

    For Details about lunit look at: http://www.mroth.net/lunit/

    Author: Michael Roth <mroth@nessie.de>

    Copyright (c) 2004, 2006-2009 Michael Roth <mroth@nessie.de>

    Permission is hereby granted, free of charge, to any person 
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without restriction,
    including without limitation the rights to use, copy, modify, merge,
    publish, distribute, sublicense, and/or sell copies of the Software,
    and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

    The above copyright notice and this permission notice shall be 
    included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

--]]--------------------------------------------------------------------------

-- Incluye algun que otro toqueteo hecho aca en inConcert


local orig_assert     = assert

local pairs           = pairs
local ipairs          = ipairs
local next            = next
local type            = type
local error           = error
local tostring        = tostring

local string_sub      = string.sub
local string_format   = string.format


module("luatestlauncher.asserts", package.seeall)     -- FIXME: Remove package.seeall

local lunit = _M

local __failure__    -- Type tag for failed assertions

local typenames = { "nil", "boolean", "number", "string", "table", "function", "thread", "userdata" }



-- Type check functions

for _, typename in ipairs(typenames) do
	lunit["is_"..typename] = function(x)
		return type(x) == typename
	end
end

local is_nil      = is_nil
local is_boolean  = is_boolean
local is_number   = is_number
local is_string   = is_string
local is_table    = is_table
local is_function = is_function
local is_thread   = is_thread
local is_userdata = is_userdata


local function failure(name, usermsg, defaultmsg, ...)
  local errobj = {
    type    = __failure__,
    name    = name,
    msg     = string_format(defaultmsg,...),
    usermsg = usermsg
  }
  error(errobj, 0)
end


local function format_arg(arg)
	local argtype = type(arg)
	if argtype == "string" then
		return "'"..arg.."'"
	elseif argtype == "number" or argtype == "boolean" or argtype == "nil" then
		return tostring(arg)
	else
		return "["..tostring(arg).."]"
	end
end


function fail(msg)
	--stats.assertions = stats.assertions + 1
	failure( "fail", msg, "failure" )
end


function assert(assertion, msg)
	--stats.assertions = stats.assertions + 1
	if not assertion then
		failure( "assert", msg, "assertion failed" )
	end
	return assertion
end


function assert_true(actual, msg)
	--stats.assertions = stats.assertions + 1
	local actualtype = type(actual)
	if actualtype ~= "boolean" then
		failure( "assert_true", msg, "true expected but was a "..actualtype )
	end
	if actual ~= true then
		failure( "assert_true", msg, "true expected but was false" )
	end
	return actual
end


function assert_false(actual, msg)
	--stats.assertions = stats.assertions + 1
	local actualtype = type(actual)
	if actualtype ~= "boolean" then
		failure( "assert_false", msg, "false expected but was a "..actualtype )
	end
	if actual ~= false then
		failure( "assert_false", msg, "false expected but was true" )
	end
	return actual
end


function assert_equal(expected, actual, msg)
  --stats.assertions = stats.assertions + 1
	if expected ~= actual then
		failure( "assert_equal", msg, "expected %s but was %s", format_arg(expected), format_arg(actual) )
	end
	return actual
end


function assert_not_equal(unexpected, actual, msg)
	--stats.assertions = stats.assertions + 1
	if unexpected == actual then
		failure( "assert_not_equal", msg, "%s not expected but was one", format_arg(unexpected) )
	end
	return actual
end


function assert_match(pattern, actual, msg)
	--stats.assertions = stats.assertions + 1
	local patterntype = type(pattern)
	if patterntype ~= "string" then
		failure( "assert_match", msg, "expected the pattern as a string but was a "..patterntype )
	end
	local actualtype = type(actual)
	if actualtype ~= "string" then
		failure( "assert_match", msg, "expected a string to match pattern '%s' but was a %s", pattern, actualtype )
	end
	if not string.find(actual, pattern) then
		failure( "assert_match", msg, "expected '%s' to match pattern '%s' but doesn't", actual, pattern )
	end
	return actual
end


function assert_not_match(pattern, actual, msg)
	--stats.assertions = stats.assertions + 1
	local patterntype = type(pattern)
	if patterntype ~= "string" then
		failure( "assert_not_match", msg, "expected the pattern as a string but was a "..patterntype )
	end
	local actualtype = type(actual)
	if actualtype ~= "string" then
		failure( "assert_not_match", msg, "expected a string to not match pattern '%s' but was a %s", pattern, actualtype )
	end
	if string.find(actual, pattern) then
		failure( "assert_not_match", msg, "expected '%s' to not match pattern '%s' but it does", actual, pattern )
	end
	return actual
end


function assert_error(msg, func)
	--stats.assertions = stats.assertions + 1
	if func == nil then
		func, msg = msg, nil
	end
	local functype = type(func)
	if functype ~= "function" then
		failure( "assert_error", msg, "expected a function as last argument but was a "..functype )
	end
	local ok, errmsg = pcall(func)
	if ok then
		failure( "assert_error", msg, "error expected but no error occurred" )
	end
end


function assert_error_match(msg, pattern, func)
	--stats.assertions = stats.assertions + 1
	if func == nil then
		msg, pattern, func = nil, msg, pattern
	end
	local patterntype = type(pattern)
	if patterntype ~= "string" then
		failure( "assert_error_match", msg, "expected the pattern as a string but was a "..patterntype )
	end
	local functype = type(func)
	if functype ~= "function" then
		failure( "assert_error_match", msg, "expected a function as last argument but was a "..functype )
	end
	local ok, errmsg = pcall(func)
	if ok then
		failure( "assert_error_match", msg, "error expected but no error occurred" )
	end
	local errmsgtype = type(errmsg)
	if errmsgtype ~= "string" then
		failure( "assert_error_match", msg, "error as string expected but was a "..errmsgtype )
	end
	if not string.find(errmsg, pattern) then
		failure( "assert_error_match", msg, "expected error '%s' to match pattern '%s' but doesn't", errmsg, pattern )
	end
end


function assert_pass(msg, func)
	--stats.assertions = stats.assertions + 1
	if func == nil then
		func, msg = msg, nil
	end
	local functype = type(func)
	if functype ~= "function" then
		failure( "assert_pass", msg, "expected a function as last argument but was a %s", functype )
	end
	local ok, errmsg = pcall(func)
	if not ok then
		failure( "assert_pass", msg, "no error expected but error was: '%s'", errmsg )
	end
end


-- lunit.assert_typename functions

for _, typename in ipairs(typenames) do
	local assert_typename = "assert_"..typename
	lunit[assert_typename] = function(actual, msg)
		--stats.assertions = stats.assertions + 1
		local actualtype = type(actual)
		if actualtype ~= typename then
			failure( assert_typename, msg, typename.." expected but was a "..actualtype )
		end
		return actual
	end
end


-- lunit.assert_not_typename functions

for _, typename in ipairs(typenames) do
	local assert_not_typename = "assert_not_"..typename
	lunit[assert_not_typename] = function(actual, msg)
		--stats.assertions = stats.assertions + 1
		if type(actual) == typename then
			failure( assert_not_typename, msg, typename.." not expected but was one" )
		end
	end
end





-- Marks a module as a testcase.
-- Applied over a module from module("xyz", luatestlauncher.asserts.testcase).
function _M.testcase(m)
	orig_assert( is_table(m) )
	--orig_assert( m._M == m )
	orig_assert( is_string(m._NAME) )
	--orig_assert( is_string(m._PACKAGE) )

	-- Import lunit, fail, assert* and is_* function to the module/testcase
	m.lunit = lunit
	m.fail = lunit.fail
	for funcname, func in pairs(lunit) do
		if "assert" == string_sub(funcname, 1, 6) or "is_" == string_sub(funcname, 1, 3) then
			m[funcname] = func
		end
	end
end


function SetStuff(args)
	__failure__ = args.__failure__
	
	luatestlauncher.traceback_hide( failure )
	luatestlauncher.traceback_hide( fail )
	luatestlauncher.traceback_hide( assert )
	luatestlauncher.traceback_hide( assert_true )
	luatestlauncher.traceback_hide( assert_false )
	luatestlauncher.traceback_hide( assert_equal )
	luatestlauncher.traceback_hide( assert_not_equal )
	luatestlauncher.traceback_hide( assert_match )
	luatestlauncher.traceback_hide( assert_not_match )
	luatestlauncher.traceback_hide( assert_error )
	luatestlauncher.traceback_hide( assert_error_match )
	luatestlauncher.traceback_hide( assert_pass )
	for _, typename in ipairs(typenames) do
		luatestlauncher.traceback_hide( lunit["assert_"..typename] )
	end
	
	for _, typename in ipairs(typenames) do
		luatestlauncher.traceback_hide( lunit["assert_not_"..typename] )
	end
end