local c={}local S={to=true,be=true;been=true;have=true;was=true,at=true}local J={never=true}local function W(c,S,J)S=S or"\065\115\115\101\114\116\105\111\110 \102\097\105\108\101\100\033"J=J or 1 if not c then error(S,J+1)end end local function q(c,S)return function(J,...)if J==c then return S(c,...)else return S(c,J,...)end end end local function B(c,S,J)if c then return S else return J end end function c.new(S)local J={value=S,successCondition=true,condition=false}setmetatable(J,c)J.a=q(J,J.a)J.an=J.a J.ok=q(J,J.ok)J.equal=q(J,J.equal)J.throw=q(J,J.throw)J.near=q(J,J.near)return J end function c.__index(W,q)if S[q]then return W end if J[q]then local S=c.new(W.value)S.successCondition=not W.successCondition return S end return c[q]end function c._resetModifiers(c)c.successCondition=true end function c.a(S,c)local J=(type(S.value)==c)==S.successCondition local q=B(S.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(c,tostring(S.value),type(S.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(c,tostring(S.value),type(S.value)))W(J,q,3)S:_resetModifiers()return S end function c.ok(c)local S=(c.value~=nil)==c.successCondition local J=B(c.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(c.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(c.value)))W(S,J,3)c:_resetModifiers()return c end function c.equal(S,c)local J=(S.value==c)==S.successCondition local q=B(S.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(c),type(c),tostring(S.value),type(S.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(c),type(c)))W(J,q,3)S:_resetModifiers()return S end function c.near(J,c,S)assert(type(J.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(c)=="\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(S)=="\110\117\109\098\101\114"or S==nil,"\108\105\109\105\116 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114 \111\114 \110\105\108")S=S or 1e-07 local q=(math.abs(J.value-c)<=S)==J.successCondition local x=B(J.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(c,S,J.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(c,S,J.value))W(q,x,3)J:_resetModifiers()return J end function c.throw(c)local S,J=pcall(c.value)local q=S~=c.successCondition local x=B(c.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(J)))W(q,x,3)c:_resetModifiers()return c end return c
--[[
	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