local z={}local r={to=true;be=true;been=true,have=true,was=true;at=true}local f={never=true}local function H(z,r,f)r=r or"\065\115\115\101\114\116\105\111\110 \102\097\105\108\101\100\033"f=f or 1 if not z then error(r,f+1)end end local function w(z,r)return function(f,...)if f==z then return r(z,...)else return r(z,f,...)end end end local function q(z,r,f)if z then return r else return f end end function z.new(r)local f={value=r;successCondition=true;condition=false}setmetatable(f,z)f.a=w(f,f.a)f.an=f.a f.ok=w(f,f.ok)f.equal=w(f,f.equal)f.throw=w(f,f.throw)f.near=w(f,f.near)return f end function z.__index(H,w)if r[w]then return H end if f[w]then local r=z.new(H.value)r.successCondition=not H.successCondition return r end return z[w]end function z._resetModifiers(z)z.successCondition=true end function z.a(r,z)local f=(type(r.value)==z)==r.successCondition local w=q(r.successCondition,("\069\120\112\101\099\116\101\100 \118\097\108\117\101 \111\102 \116\121\112\101 \037\113\044 \103\111\116 \118\097\108\117\101 \037\113 \111\102 \116\121\112\101 \037\115"):format(z,tostring(r.value),type(r.value)),("\069\120\112\101\099\116\101\100 \118\097\108\117\101 \110\111\116 \111\102 \116\121\112\101 \037\113\044 \103\111\116 \118\097\108\117\101 \037\113 \111\102 \116\121\112\101 \037\115"):format(z,tostring(r.value),type(r.value)))H(f,w,3)r:_resetModifiers()return r end function z.ok(z)local r=(z.value~=nil)==z.successCondition local f=q(z.successCondition,("\069\120\112\101\099\116\101\100 \118\097\108\117\101 \037\113 \116\111 \098\101 \110\111\110-\110\105\108"):format(tostring(z.value)),("\069\120\112\101\099\116\101\100 \118\097\108\117\101 \037\113 \116\111 \098\101 \110\105\108"):format(tostring(z.value)))H(r,f,3)z:_resetModifiers()return z end function z.equal(r,z)local f=(r.value==z)==r.successCondition local w=q(r.successCondition,("\069\120\112\101\099\116\101\100 \118\097\108\117\101 \037\113 \040\037\115\041\044 \103\111\116 \037\113 \040\037\115\041 \105\110\115\116\101\097\100"):format(tostring(z),type(z),tostring(r.value),type(r.value)),("\069\120\112\101\099\116\101\100 \097\110\121\116\104\105\110\103 \098\117\116 \118\097\108\117\101 \037\113 \040\037\115\041"):format(tostring(z),type(z)))H(f,w,3)r:_resetModifiers()return r end function z.near(f,z,r)assert(type(f.value)=="\110\117\109\098\101\114","\069\120\112\101\099\116\097\116\105\111\110 \118\097\108\117\101 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114 \116\111 \117\115\101 \'\110\101\097\114\'")assert(type(z)=="\110\117\109\098\101\114","\111\116\104\101\114\086\097\108\117\101 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")assert(type(r)=="\110\117\109\098\101\114"or r==nil,"\108\105\109\105\116 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114 \111\114 \110\105\108")r=r or 1e-07 local w=(math.abs(f.value-z)<=r)==f.successCondition local k=q(f.successCondition,("\069\120\112\101\099\116\101\100 \118\097\108\117\101 \116\111 \098\101 \110\101\097\114 \037\102 \040\119\105\116\104\105\110 \037\102\041 \098\117\116 \103\111\116 \037\102 \105\110\115\116\101\097\100"):format(z,r,f.value),("\069\120\112\101\099\116\101\100 \118\097\108\117\101 \116\111 \110\111\116 \098\101 \110\101\097\114 \037\102 \040\119\105\116\104\105\110 \037\102\041 \098\117\116 \103\111\116 \037\102 \105\110\115\116\101\097\100"):format(z,r,f.value))H(w,k,3)f:_resetModifiers()return f end function z.throw(z)local r,f=pcall(z.value)local w=r~=z.successCondition local k=q(z.successCondition,"\069\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \116\111 \116\104\114\111\119 \097\110 \101\114\114\111\114\044 \098\117\116 \105\116 \100\105\100 \110\111\116\046",("\069\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \116\111 \115\117\099\099\101\101\100\044 \098\117\116 \105\116 \116\104\114\101\119 \097\110 \101\114\114\111\114\058 \037\115"):format(tostring(f)))H(w,k,3)z:_resetModifiers()return z end return z
--[[
	Allows creation of expectation statements designed for behavior-driven
	testing (BDD). See Chai (JS) or RSpec (Ruby) for examples of other BDD
	frameworks.

	The Expectation class is exposed to tests as a function called `expect`:

		expect(5).to.equal(5)
		expect(foo()).to.be.ok()

	Expectations can be negated using .never:

		expect(true).never.to.equal(false)

	Expectations throw errors when their conditions are not met.
]]

local Expectation = {}

--[[
	These keys don't do anything except make expectations read more cleanly
]]
local SELF_KEYS = {
	to = true,
	be = true,
	been = true,
	have = true,
	was = true,
	at = true,
}

--[[
	These keys invert the condition expressed by the Expectation.
]]
local NEGATION_KEYS = {
	never = true,
}

--[[
	Extension of Lua's 'assert' that lets you specify an error level.
]]
local function assertLevel(condition, message, level)
	message = message or "Assertion failed!"
	level = level or 1

	if not condition then
		error(message, level + 1)
	end
end

--[[
	Returns a version of the given method that can be called with either . or :
]]
local function bindSelf(self, method)
	return function(firstArg, ...)
		if firstArg == self then
			return method(self, ...)
		else
			return method(self, firstArg, ...)
		end
	end
end

local function formatMessage(result, trueMessage, falseMessage)
	if result then
		return trueMessage
	else
		return falseMessage
	end
end

--[[
	Create a new expectation
]]
function Expectation.new(value)
	local self = {
		value = value,
		successCondition = true,
		condition = false
	}

	setmetatable(self, Expectation)

	self.a = bindSelf(self, self.a)
	self.an = self.a
	self.ok = bindSelf(self, self.ok)
	self.equal = bindSelf(self, self.equal)
	self.throw = bindSelf(self, self.throw)
	self.near = bindSelf(self, self.near)

	return self
end

function Expectation.__index(self, key)
	-- Keys that don't do anything except improve readability
	if SELF_KEYS[key] then
		return self
	end

	-- Invert your assertion
	if NEGATION_KEYS[key] then
		local newExpectation = Expectation.new(self.value)
		newExpectation.successCondition = not self.successCondition

		return newExpectation
	end

	-- Fall back to methods provided by Expectation
	return Expectation[key]
end

--[[
	Called by expectation terminators to reset modifiers in a statement.

	This makes chains like:

		expect(5)
			.never.to.equal(6)
			.to.equal(5)

	Work as expected.
]]
function Expectation:_resetModifiers()
	self.successCondition = true
end

--[[
	Assert that the expectation value is the given type.

	expect(5).to.be.a("number")
]]
function Expectation:a(typeName)
	local result = (type(self.value) == typeName) == self.successCondition

	local message = formatMessage(self.successCondition,
		("Expected value of type %q, got value %q of type %s"):format(
			typeName,
			tostring(self.value),
			type(self.value)
		),
		("Expected value not of type %q, got value %q of type %s"):format(
			typeName,
			tostring(self.value),
			type(self.value)
		)
	)

	assertLevel(result, message, 3)
	self:_resetModifiers()

	return self
end

--[[
	Assert that our expectation value is truthy
]]
function Expectation:ok()
	local result = (self.value ~= nil) == self.successCondition

	local message = formatMessage(self.successCondition,
		("Expected value %q to be non-nil"):format(
			tostring(self.value)
		),
		("Expected value %q to be nil"):format(
			tostring(self.value)
		)
	)

	assertLevel(result, message, 3)
	self:_resetModifiers()

	return self
end

--[[
	Assert that our expectation value is equal to another value
]]
function Expectation:equal(otherValue)
	local result = (self.value == otherValue) == self.successCondition

	local message = formatMessage(self.successCondition,
		("Expected value %q (%s), got %q (%s) instead"):format(
			tostring(otherValue),
			type(otherValue),
			tostring(self.value),
			type(self.value)
		),
		("Expected anything but value %q (%s)"):format(
			tostring(otherValue),
			type(otherValue)
		)
	)

	assertLevel(result, message, 3)
	self:_resetModifiers()

	return self
end

--[[
	Assert that our expectation value is equal to another value within some
	inclusive limit.
]]
function Expectation:near(otherValue, limit)
	assert(type(self.value) == "number", "Expectation value must be a number to use 'near'")
	assert(type(otherValue) == "number", "otherValue must be a number")
	assert(type(limit) == "number" or limit == nil, "limit must be a number or nil")

	limit = limit or 1e-7

	local result = (math.abs(self.value - otherValue) <= limit) == self.successCondition

	local message = formatMessage(self.successCondition,
		("Expected value to be near %f (within %f) but got %f instead"):format(
			otherValue,
			limit,
			self.value
		),
		("Expected value to not be near %f (within %f) but got %f instead"):format(
			otherValue,
			limit,
			self.value
		)
	)

	assertLevel(result, message, 3)
	self:_resetModifiers()

	return self
end

--[[
	Assert that our functoid expectation value throws an error when called
]]
function Expectation:throw()
	local ok, err = pcall(self.value)
	local result = ok ~= self.successCondition

	local message = formatMessage(self.successCondition,
		"Expected function to throw an error, but it did not.",
		("Expected function to succeed, but it threw an error: %s"):format(
			tostring(err)
		)
	)

	assertLevel(result, message, 3)
	self:_resetModifiers()

	return self
end

return Expectation