Jump to content

Module:IP/testcases: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
add getVersion self parameter error test
Undid revision 750954815 by Mr. Stradivarius (talk) goes against what we decided at Module talk:IP#IPCollection
Line 888: Line 888:
function suite:testIPv4CollectionGetVersion()
function suite:testIPv4CollectionGetVersion()
self:assertEquals('IPv4', IPv4Collection.new():getVersion())
self:assertEquals('IPv4', IPv4Collection.new():getVersion())
end

function suite:testIPv4CollectionGetVersionErrors()
self:assertSelfParameterError(
IPV4COLLECTION_CLASS, IPV4COLLECTION_OBJECT, 'getVersion',
function ()
IPv4Collection.new().getVersion()
end
)
self:assertSelfParameterError(
IPV4COLLECTION_CLASS, IPV4COLLECTION_OBJECT, 'getVersion',
function ()
IPv4Collection.new().getVersion(IPv4Collection.new())
end
)
end
end



Revision as of 13:44, 22 November 2016

-- Unit tests for [[Module:IP]]. Click talk page to run tests.

-- Unit test module setup
local ScribuntoUnit = require('Module:ScribuntoUnit')
local suite = ScribuntoUnit:new()

-- Target module setup
local IP = require('Module:IP')
local IPAddress = IP.IPAddress
local Subnet = IP.Subnet
local IPv4Collection = IP.IPv4Collection
local IPv6Collection = IP.IPv6Collection

-- Constants
local IP_ADDRESS_CLASS = 'IPAddress'
local IP_ADDRESS_OBJECT = 'ipAddress'
local SUBNET_CLASS = 'Subnet'
local SUBNET_OBJECT = 'subnet'
local IPV4COLLECTION_CLASS = 'IPv4Collection'
local IPV4COLLECTION_OBJECT = 'ipv4Collection'
local IPV6COLLECTION_CLASS = 'IPv6Collection'
local IPV6COLLECTION_OBJECT = 'ipv6Collection'

-------------------------------------------------------------------------------
-- Helper methods
-------------------------------------------------------------------------------

function suite:assertError(message, plain, ...)
	local success, ret = pcall(...)
	self:assertFalse(success)
	self:assertStringContains(message, ret, plain)
end

function suite:assertNotError(...)
	local success = pcall(...)
	self:assertTrue(success)
end

function suite:assertSelfParameterError(class, objName, method, ...)
	local message = string.format(
		'IP: invalid %s object. Did you call %s with a dot instead of a colon, i.e. %s.%s() instead of %s:%s()?',
		class, method, objName, method, objName, method
	)
	self:assertError(message, true, ...)
end

function suite:assertTypeError(argIdx, funcName, expected, received, ...)
	local message = string.format(
		"bad argument #%d to '%s' (%s expected, got %s)",
		argIdx, funcName, expected, received
	)
	self:assertError(message, true, ...)
end

function suite:assertObjectError(argIdx, funcName, className, ...)
	local message = string.format(
		"bad argument #%d to '%s' (not a valid %s object)",
		argIdx, funcName, className
	)
	self:assertError(message, true, ...)
end

function suite:assertIPStringError(ipStr, ...)
	local message = string.format("'%s' is an invalid IP address", ipStr)
	self:assertError(message, true, ...)
end

function suite:assertCIDRStringError(cidr, ...)
	local message = string.format("'%s' is an invalid CIDR string", cidr)
	self:assertError(message, true, ...)
end

function suite:assertMetatableProtected(obj)
	self:assertError('cannot change a protected metatable', true, function ()
		setmetatable(obj, {})
	end)
end

function suite:assertNotMetatable(val)
	self:assertFalse(type(val) == 'table' and type(val.__eq) == 'function')
end

function suite:assertObject(val, ...)
	self:assertTrue(type(val) == 'table')
	for i, method in ipairs{...} do
		self:assertTrue(type(val[method]) == 'function')
	end
end

function suite:assertIPAddressObject(val)
	suite:assertObject(val, 'getIP', 'isInSubnet')
end

function suite:assertSubnetObject(val)
	suite:assertObject(val, 'getCIDR', 'containsIP')
end

function suite:assertCollectionObject(val)
	suite:assertObject(val, 'addIP', 'addSubnet')
end

function suite:assertIPv4CollectionObject(val)
	self:assertCollectionObject(val)
	self:assertEquals('IPv4', val:getVersion())
end

function suite:assertIPv6CollectionObject(val)
	self:assertCollectionObject(val)
	self:assertEquals('IPv6', val:getVersion())
end

function suite:assertRangesEqual(expected, actual)
	self:assertTrue(#expected == #actual)
	for i = 1, #expected do
		local expectedRange = expected[i]
		local actualRange = actual[i]
		self:assertEquals(expectedRange[1], actualRange[1])
		self:assertEquals(expectedRange[2], actualRange[2])
	end
end

-------------------------------------------------------------------------------
-- IPAddress tests
-------------------------------------------------------------------------------

function suite:testIPConstructor()
	local function assertValidIP(ip)
		self:assertIPAddressObject(IPAddress.new(ip))
	end
	assertValidIP('1.2.3.4')
	assertValidIP('0.0.0.0')
	assertValidIP('255.255.255.255')
	assertValidIP('::')
	assertValidIP('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
	assertValidIP('2001::f:1234')
end

function suite:testInvalidIPs()
	local function assertInvalidIP(ip)
		self:assertError(
			string.format("'%s' is an invalid IP address", ip),
			true,
			function ()
				IPAddress.new(ip)
			end
		)
	end
	assertInvalidIP('')
	assertInvalidIP('foo')
	assertInvalidIP('01.02.03.04')
	assertInvalidIP('256.256.256.256')
	assertInvalidIP('1.2.3')
	assertInvalidIP('1.2.3.4.5')
	assertInvalidIP('-1.2.3.4')
	assertInvalidIP(':')
	-- TODO: work out what to do about the following test
	-- assertInvalidIP(':::')
	assertInvalidIP('2001::f::1234')
	assertInvalidIP('2001:g::')
end

function suite:testIPConstructorErrors()
	self:assertTypeError(
		1, 'IPAddress.new', 'string', 'boolean',
		function ()
			IPAddress.new(true)
		end
	)
	self:assertTypeError(
		1, 'IPAddress.new', 'string', 'number',
		function ()
			IPAddress.new(7)
		end
	)
	self:assertTypeError(
		1, 'IPAddress.new', 'string', 'nil',
		function ()
			IPAddress.new()
		end
	)
	self:assertError(
		"'foo' is an invalid IP address",
		true,
		function ()
			IPAddress.new('foo')
		end
	)
end

function suite:testIPEquals()
	self:assertTrue(IPAddress.new('1.2.3.4') == IPAddress.new('1.2.3.4'))
	self:assertFalse(IPAddress.new('1.2.3.5') == IPAddress.new('1.2.3.4'))
	self:assertTrue(IPAddress.new('2001:a1:b2::') == IPAddress.new('2001:a1:b2::'))
	self:assertTrue(IPAddress.new('::') == IPAddress.new('0:0:0:0:0:0:0:0'))
end

function suite:testIPLessThan()
	self:assertFalse(IPAddress.new('1.2.3.4') < IPAddress.new('1.2.3.4'))
	self:assertFalse(IPAddress.new('1.2.3.5') < IPAddress.new('1.2.3.4'))
	self:assertTrue(IPAddress.new('1.2.3.3') < IPAddress.new('1.2.3.4'))
	self:assertTrue(IPAddress.new('2.0.0.0') < IPAddress.new('10.0.0.0'))
	self:assertTrue(IPAddress.new('2001:a1:b2::') < IPAddress.new('2001:a1:b2::1'))
	self:assertTrue(IPAddress.new('2001:b::') < IPAddress.new('2001:10::'))
end

function suite:testIPLessThanOrEqualTo()
	self:assertTrue(IPAddress.new('1.2.3.4') <= IPAddress.new('1.2.3.4'))
	self:assertFalse(IPAddress.new('1.2.3.5') <= IPAddress.new('1.2.3.4'))
	self:assertTrue(IPAddress.new('1.2.3.3') <= IPAddress.new('1.2.3.4'))
end

function suite:testIPToString()
	self:assertEquals('1.2.3.4', tostring(IPAddress.new('1.2.3.4')))
	self:assertEquals('2001:a1:b2::', tostring(IPAddress.new('2001:a1:b2:0:0:0:0:0')))
end

function suite:testConcatIP()
	self:assertEquals(
		'1.2.3.45.6.7.8',
		IPAddress.new('1.2.3.4') .. IPAddress.new('5.6.7.8')
	)
	self:assertEquals('1.2.3.4foo', IPAddress.new('1.2.3.4') .. 'foo')
end

function suite:testGetIP()
	self:assertEquals('1.2.3.4', IPAddress.new('1.2.3.4'):getIP())
end

function suite:testGetIPErrors()
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getIP',
		function ()
			IPAddress.new('1.2.3.4').getIP()
		end
	)
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getIP',
		function ()
			IPAddress.new('1.2.3.4').getIP(IPAddress.new('5.6.7.8'))
		end
	)
end

function suite:testGetVersion()
	self:assertEquals('IPv4', IPAddress.new('1.2.3.4'):getVersion())
	self:assertEquals('IPv6', IPAddress.new('2001:db8::ff00:12:3456'):getVersion())
end

function suite:testGetVersionErrors()
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getVersion',
		function ()
			IPAddress.new('1.2.3.4').getVersion()
		end
	)
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getVersion',
		function ()
			IPAddress.new('1.2.3.4').getVersion(IPAddress.new('5.6.7.8'))
		end
	)
end

function suite:testIsIPv4()
	self:assertTrue(IPAddress.new('1.2.3.4'):isIPv4())
	self:assertFalse(IPAddress.new('2001:db8::ff00:12:3456'):isIPv4())
end

function suite:testIsIPv4Errors()
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'isIPv4',
		function ()
			IPAddress.new('1.2.3.4').isIPv4()
		end
	)
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'isIPv4',
		function ()
			IPAddress.new('1.2.3.4').isIPv4(IPAddress.new('5.6.7.8'))
		end
	)
end

function suite:testIsIPv6()
	self:assertTrue(IPAddress.new('2001:db8::ff00:12:3456'):isIPv6())
	self:assertFalse(IPAddress.new('1.2.3.4'):isIPv6())
end

function suite:testIsIPv6Errors()
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'isIPv6',
		function ()
			IPAddress.new('1.2.3.4').isIPv6()
		end
	)
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'isIPv6',
		function ()
			IPAddress.new('1.2.3.4').isIPv6(IPAddress.new('5.6.7.8'))
		end
	)
end

function suite:testIsInSubnet()
	self:assertTrue(IPAddress.new('1.2.3.4'):isInSubnet(Subnet.new('1.2.3.0/24')))
	self:assertFalse(IPAddress.new('1.2.3.4'):isInSubnet(Subnet.new('1.2.4.0/24')))
end

function suite:testIsInSubnetFromString()
	self:assertTrue(IPAddress.new('1.2.3.4'):isInSubnet('1.2.3.0/24'))
	self:assertFalse(IPAddress.new('1.2.3.4'):isInSubnet('1.2.4.0/24'))
end

function suite:testIsInSubnetErrors()
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'isInSubnet',
		function ()
			IPAddress.new('1.2.3.4').isInSubnet()
		end
	)
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'isInSubnet',
		function ()
			IPAddress.new('1.2.3.4').isInSubnet(IPAddress.new('5.6.7.8'))
		end
	)
	self:assertTypeError(
		1, 'isInSubnet', 'string or table', 'boolean',
		function ()
			IPAddress.new('1.2.3.4'):isInSubnet(false)
		end
	)
	self:assertTypeError(
		1, 'isInSubnet', 'string or table', 'nil',
		function ()
			IPAddress.new('1.2.3.4'):isInSubnet()
		end
	)
	self:assertCIDRStringError(
		'foo',
		function ()
			IPAddress.new('1.2.3.4'):isInSubnet('foo')
		end
	)
	self:assertObjectError(
		1, 'isInSubnet', 'Subnet',
		function ()
			IPAddress.new('1.2.3.4'):isInSubnet{foo = 'bar'}
		end
	)
end

function suite:testGetSubnet()
	self:assertEquals(
		'1.2.3.0/24',
		IPAddress.new('1.2.3.4'):getSubnet(24):getCIDR()
	)
	self:assertEquals(
		'1.2.3.128/25',
		IPAddress.new('1.2.3.130'):getSubnet(25):getCIDR()
	)
end

function suite:testGetSubnetErrors()
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getSubnet',
		function ()
			IPAddress.new('1.2.3.4').getSubnet(24)
		end
	)
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getSubnet',
		function ()
			IPAddress.new('1.2.3.4').getSubnet(IPAddress.new('5.6.7.8'), 24)
		end
	)
	self:assertTypeError(
		1, 'getSubnet', 'number', 'boolean',
		function ()
			IPAddress.new('1.2.3.4'):getSubnet(false)
		end
	)
	self:assertTypeError(
		1, 'getSubnet', 'number', 'nil',
		function ()
			IPAddress.new('1.2.3.4'):getSubnet()
		end
	)
end

function suite:testGetSubnetIPv4NumberErrors()
	local message = "bad argument #1 to 'getSubnet' (must be an integer between 0 and 32)"
	self:assertError(message, true, function ()
		IPAddress.new('1.2.3.4'):getSubnet(33)
	end)
	self:assertError(message, true, function ()
		IPAddress.new('1.2.3.4'):getSubnet(-1)
	end)
	self:assertError(message, true, function ()
		IPAddress.new('1.2.3.4'):getSubnet(24.5)
	end)
end

function suite:testGetSubnetIPv6NumberErrors()
	local message = "bad argument #1 to 'getSubnet' (must be an integer between 0 and 128)"
	self:assertError(message, true, function ()
		IPAddress.new('2001:db8::ff00:12:3456'):getSubnet(129)
	end)
	self:assertError(message, true, function ()
		IPAddress.new('2001:db8::ff00:12:3456'):getSubnet(-1)
	end)
	self:assertError(message, true, function ()
		IPAddress.new('2001:db8::ff00:12:3456'):getSubnet(112.5)
	end)
end

function suite:testGetNextIP()
	self:assertEquals('1.2.3.5', tostring(IPAddress.new('1.2.3.4'):getNextIP()))
	self:assertEquals(
		IPAddress.new('2001:db8::ff00:12:3457'),
		IPAddress.new('2001:db8::ff00:12:3456'):getNextIP()
	)
end

function suite:testGetNextIPWraparound()
	self:assertEquals(
		IPAddress.new('0.0.0.0'),
		IPAddress.new('255.255.255.255'):getNextIP()
	)
	self:assertEquals(
		'::',
		tostring(IPAddress.new('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'):getNextIP())
	)
end

function suite:testGetNextIPErrors()
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getNextIP',
		function ()
			IPAddress.new('1.2.3.4').getNextIP()
		end
	)
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getNextIP',
		function ()
			IPAddress.new('1.2.3.4').getNextIP(IPAddress.new('5.6.7.8'))
		end
	)
end

function suite:testGetPreviousIP()
	self:assertEquals(
		IPAddress.new('1.2.3.3'),
		IPAddress.new('1.2.3.4'):getPreviousIP()
	)
	self:assertEquals(
		IPAddress.new('2001:db8::ff00:12:3455'),
		IPAddress.new('2001:db8::ff00:12:3456'):getPreviousIP()
	)
end

function suite:testGetPreviousIPWraparound()
	self:assertEquals(
		IPAddress.new('255.255.255.255'),
		IPAddress.new('0.0.0.0'):getPreviousIP()
	)
	self:assertEquals(
		IPAddress.new('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'),
		IPAddress.new('::'):getPreviousIP()
	)
end

function suite:testGetPreviousIPErrors()
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getPreviousIP',
		function ()
			IPAddress.new('1.2.3.4').getPreviousIP()
		end
	)
	self:assertSelfParameterError(
		IP_ADDRESS_CLASS, IP_ADDRESS_OBJECT, 'getPreviousIP',
		function ()
			IPAddress.new('1.2.3.4').getPreviousIP(IPAddress.new('5.6.7.8'))
		end
	)
end

function suite:testGetIPMetatable()
	self:assertNotMetatable(getmetatable(IPAddress.new('1.2.3.4')))
end

function suite:testSetIPMetatable()
	self:assertMetatableProtected(IPAddress.new('1.2.3.4'))
end

-------------------------------------------------------------------------------
-- Subnet tests
-------------------------------------------------------------------------------

function suite:testValidCIDRs()
	local function assertValidCIDR(cidr)
		self:assertTrue(type(Subnet.new(cidr)) == 'table')
	end
	assertValidCIDR('1.2.3.0/24')
	assertValidCIDR(' 1.2.3.0/24 ')
	assertValidCIDR('0.0.0.0/0')
	assertValidCIDR('0.0.0.0/32')
	assertValidCIDR('255.255.255.255/32')
	assertValidCIDR('2001:db8::ff00:12:0/122')
	assertValidCIDR('2001:DB8::FF00:12:0/122')
	assertValidCIDR('::/0')
	assertValidCIDR('::/128')
end

function suite:testInvalidCIDRs()
	local function assertInvalidCIDR(cidr)
		self:assertError(
			string.format("'%s' is an invalid CIDR string", cidr),
			true,
			function ()
				Subnet.new(cidr)
			end
		)
	end
	assertInvalidCIDR('foo')
	assertInvalidCIDR('0/0')
	assertInvalidCIDR('/24')
	assertInvalidCIDR('1.2.3/24')
	assertInvalidCIDR(':/0')
	assertInvalidCIDR('1.2.3.4')
	assertInvalidCIDR('0.0.0.0/33')
	assertInvalidCIDR('0.0.0.0/-1')
	assertInvalidCIDR('256.0.0.0/24')
	assertInvalidCIDR('1.2.3.4/24')
	assertInvalidCIDR('1.2.3.0/16')
	assertInvalidCIDR('0.0.0.0/01') -- Leading zero in bit length
	assertInvalidCIDR('2001:db8::ff00:12:3456')
	assertInvalidCIDR('2001:db8::ff00:12:0/foo')
	assertInvalidCIDR('::/-1')
	assertInvalidCIDR('::/129')
	assertInvalidCIDR('gggg:db8::ff00:12:0/122')
	assertInvalidCIDR('2001:db8::ff00:12:3456/122')
	assertInvalidCIDR('2001:db8::ff00:12:0/106')
end

function suite:testSubnetConstructorErrors()
	self:assertTypeError(
		1, 'Subnet.new', 'string', 'boolean',
		function ()
			Subnet.new(true)
		end
	)
	self:assertTypeError(
		1, 'Subnet.new', 'string', 'number',
		function ()
			Subnet.new(7)
		end
	)
	self:assertTypeError(
		1, 'Subnet.new', 'string', 'nil',
		function ()
			Subnet.new()
		end
	)
end

function suite:testSubnetEquals()
	self:assertTrue(Subnet.new('1.2.3.0/24') == Subnet.new('1.2.3.0/24'))
	self:assertFalse(Subnet.new('1.2.3.0/24') == Subnet.new('1.2.0.0/16'))
end

function suite:testConcatSubnet()
	self:assertEquals(
		'1.2.3.0/244.5.6.0/24',
		Subnet.new('1.2.3.0/24') .. Subnet.new('4.5.6.0/24')
	)
	self:assertEquals('1.2.3.0/24foo', Subnet.new('1.2.3.0/24') .. 'foo')
	self:assertEquals('foo1.2.3.0/24', 'foo' .. Subnet.new('1.2.3.0/24'))
end

function suite:testSubnetToString()
	self:assertEquals('1.2.3.0/24', tostring(Subnet.new('1.2.3.0/24')))
	self:assertEquals(
		'2001:db8::ff00:12:0/122',
		tostring(Subnet.new('2001:db8::ff00:12:0/122'))
	)
end

function suite:testSubnetGetmetatable()
	self:assertNotMetatable(getmetatable(Subnet.new('1.2.3.0/24')))
end

function suite:testSubnetSetmetatable()
	self:assertMetatableProtected(Subnet.new('1.2.3.0/24'))
end

function suite:testSubnetGetPrefix()
	self:assertEquals(
		IPAddress.new('1.2.3.0'),
		Subnet.new('1.2.3.0/24'):getPrefix()
	)
end

function suite:testSubnetGetPrefixErrors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getPrefix',
		function ()
			Subnet.new('1.2.3.0/24').getPrefix()
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getPrefix',
		function ()
			Subnet.new('1.2.3.0/24').getPrefix(Subnet.new('4.5.6.0/24'))
		end
	)
end

function suite:testSubnetGetHighestIP()
	self:assertEquals(
		IPAddress.new('1.2.3.255'),
		Subnet.new('1.2.3.0/24'):getHighestIP()
	)
end

function suite:testGetHighestIPFromGetSubnet()
	self:assertEquals(
		IPAddress.new('1.2.3.255'),
		IPAddress.new('1.2.3.4'):getSubnet(24):getHighestIP()
	)
end

function suite:testSubnetGetHighestIPErrors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getHighestIP',
		function ()
			Subnet.new('1.2.3.0/24').getHighestIP()
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getHighestIP',
		function ()
			Subnet.new('1.2.3.0/24').getHighestIP(Subnet.new('4.5.6.0/24'))
		end
	)
end

function suite:testSubnetGetBitLength()
	self:assertEquals(24, Subnet.new('1.2.3.0/24'):getBitLength())
end

function suite:testSubnetGetBitLengthErrors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getBitLength',
		function ()
			Subnet.new('1.2.3.0/24').getBitLength()
		end
	)
end

function suite:testSubnetGetCIDR()
	self:assertEquals('1.2.3.0/24', Subnet.new('1.2.3.0/24'):getCIDR())
end

function suite:testGetCIDRErrors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getCIDR',
		function ()
			Subnet.new('1.2.3.0/24').getCIDR()
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getCIDR',
		function ()
			Subnet.new('1.2.3.0/24').getCIDR(Subnet.new('4.5.6.0/24'))
		end
	)
end

function suite:testSubnetGetVersion()
	self:assertEquals('IPv4', Subnet.new('1.2.3.0/24'):getVersion())
	self:assertEquals('IPv6', Subnet.new('2001:db8::ff00:0:0/96'):getVersion())
end

function suite:testSubnetGetVersionErrors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getVersion',
		function ()
			Subnet.new('1.2.3.0/24').getVersion()
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'getVersion',
		function ()
			Subnet.new('1.2.3.0/24').getVersion(Subnet.new('4.5.6.0/24'))
		end
	)
end

function suite:testSubnetIsIPv4()
	self:assertTrue(Subnet.new('1.2.3.0/24'):isIPv4())
	self:assertFalse(Subnet.new('2001:db8::ff00:0:0/96'):isIPv4())
end

function suite:testSubnetIsIPv4Errors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'isIPv4',
		function ()
			Subnet.new('1.2.3.0/24').isIPv4()
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'isIPv4',
		function ()
			Subnet.new('1.2.3.0/24').isIPv4(Subnet.new('4.5.6.0/24'))
		end
	)
end

function suite:testSubnetIsIPv6()
	self:assertTrue(Subnet.new('2001:db8::ff00:0:0/96'):isIPv6())
	self:assertFalse(Subnet.new('1.2.3.0/24'):isIPv6())
end

function suite:testSubnetIsIPv6Errors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'isIPv6',
		function ()
			Subnet.new('1.2.3.0/24').isIPv6()
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'isIPv6',
		function ()
			Subnet.new('1.2.3.0/24').isIPv6(Subnet.new('4.5.6.0/24'))
		end
	)
end

function suite:testSubnetContainsIP()
	self:assertTrue(
		Subnet.new('1.2.3.0/24'):containsIP(IPAddress.new('1.2.3.4'))
	)
	self:assertFalse(
		Subnet.new('1.2.3.0/24'):containsIP(IPAddress.new('1.2.4.4'))
	)
end

function suite:testSubnetContainsIPErrors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'containsIP',
		function ()
			Subnet.new('1.2.3.0/24').containsIP(IPAddress.new('1.2.3.4'))
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'containsIP',
		function ()
			Subnet.new('1.2.3.0/24').containsIP(
				Subnet.new('4.5.6.0/24'),
				IPAddress.new('1.2.3.4')
			)
		end
	)
	self:assertTypeError(
		1, 'containsIP', 'string or table', 'boolean',
		function ()
			Subnet.new('1.2.3.0/24'):containsIP(false)
		end
	)
	self:assertTypeError(
		1, 'containsIP', 'string or table', 'nil',
		function ()
			Subnet.new('1.2.3.0/24'):containsIP()
		end
	)
	self:assertIPStringError(
		'foo',
		function ()
			Subnet.new('1.2.3.0/24'):containsIP('foo')
		end
	)
	self:assertObjectError(
		1, 'containsIP', 'IPAddress',
		function ()
			Subnet.new('1.2.3.0/24'):containsIP{foo = 'bar'}
		end
	)
end

function suite:testOverlapsSubnet()
	self:assertTrue(
		Subnet.new('1.2.0.0/16'):overlapsSubnet(Subnet.new('1.2.3.0/24'))
	)
	self:assertFalse(
		Subnet.new('1.2.0.0/16'):overlapsSubnet(Subnet.new('1.3.3.0/24'))
	)
end

function suite:testOverlapsSubnetErrors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'overlapsSubnet',
		function ()
			Subnet.new('1.2.3.0/24').overlapsSubnet(Subnet.new('1.2.0.0/16'))
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'overlapsSubnet',
		function ()
			Subnet.new('1.2.3.0/24').overlapsSubnet(
				Subnet.new('4.5.6.0/24'),
				Subnet.new('1.2.0.0/16')
			)
		end
	)
	self:assertTypeError(
		1, 'overlapsSubnet', 'string or table', 'boolean',
		function ()
			Subnet.new('1.2.3.0/24'):overlapsSubnet(false)
		end
	)
	self:assertTypeError(
		1, 'overlapsSubnet', 'string or table', 'nil',
		function ()
			Subnet.new('1.2.3.0/24'):overlapsSubnet()
		end
	)
	self:assertCIDRStringError(
		'foo',
		function ()
			Subnet.new('1.2.3.0/24'):overlapsSubnet('foo')
		end
	)
	self:assertObjectError(
		1, 'overlapsSubnet', 'Subnet',
		function ()
			Subnet.new('1.2.3.0/24'):overlapsSubnet{foo = 'bar'}
		end
	)
end

function suite:testWalkSubnet()
	do
		local ips = {}
		for ip in Subnet.new('1.2.3.0/30'):walk() do
			ips[#ips + 1] = tostring(ip)
		end
		self:assertEquals(
			'1.2.3.0 1.2.3.1 1.2.3.2 1.2.3.3',
			table.concat(ips, ' ')
		)
	end
	do
		local ips = {}
		for ip in Subnet.new('2001:db8::ff00:0:0/126'):walk() do
			ips[#ips + 1] = tostring(ip)
		end
		self:assertEquals(
			'2001:db8::ff00:0:0 2001:db8::ff00:0:1 2001:db8::ff00:0:2 2001:db8::ff00:0:3',
			table.concat(ips, ' ')
		)
	end
end

function suite:testWalkSubnetErrors()
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'walk',
		function ()
			Subnet.new('1.2.3.0/24').walk()
		end
	)
	self:assertSelfParameterError(
		SUBNET_CLASS, SUBNET_OBJECT, 'walk',
		function ()
			Subnet.new('1.2.3.0/24').walk(Subnet.new('4.5.6.0/24'))
		end
	)
end

-------------------------------------------------------------------------------
-- IPv4Collection tests
-------------------------------------------------------------------------------

function suite:testIPv4CollectionConstructor()
	self:assertIPv4CollectionObject(IPv4Collection.new())
end

function suite:testIPv4CollectionGetVersion()
	self:assertEquals('IPv4', IPv4Collection.new():getVersion())
end

function suite:testIPv4CollectionAddIP()
	self:assertNotError(function () IPv4Collection.new():addIP('1.2.3.4') end)
	self:assertNotError(function () IPv4Collection.new():addIP(IPAddress.new('1.2.3.4')) end)
	suite:assertIPStringError(
		'foo',
		function ()
			IPv4Collection.new():addIP(IPAddress.new('foo'))
		end
	)
	suite:assertIPStringError(
		'1.2.3.0/24',
		function ()
			IPv4Collection.new():addIP(IPAddress.new('1.2.3.0/24'))
		end
	)
end

function suite:testIPv4CollectionAddIPChaining()
	self:assertNotError(function ()
		IPv4Collection.new()
			:addIP('1.2.3.4')
			:addIP('5.6.7.8')
	end)
end

function suite:testIPv4CollectionAddSubnet()
	self:assertNotError(function () IPv4Collection.new():addSubnet('1.2.3.0/24') end)
	self:assertNotError(function () IPv4Collection.new():addSubnet(Subnet.new('1.2.3.0/24')) end)
	suite:assertCIDRStringError(
		'foo',
		function ()
			IPv4Collection.new():addSubnet('foo')
		end
	)
	suite:assertCIDRStringError(
		'1.2.3.4',
		function ()
			IPv4Collection.new():addSubnet('1.2.3.4')
		end
	)
end

function suite:testIPv4CollectionAddSubnetChaining()
	self:assertNotError(function ()
		IPv4Collection.new()
			:addSubnet('1.2.3.0/24')
			:addSubnet('5.6.7.0/24')
	end)
end

function suite:testIPv4CollectionContainsIP()
	local collection = IPv4Collection.new()
	collection:addIP('1.2.3.4')
	self:assertEquals(true, collection:containsIP('1.2.3.4'))
	self:assertEquals(true, collection:containsIP(IPAddress.new('1.2.3.4')))
	self:assertEquals(false, collection:containsIP('1.2.3.5'))
end

function suite:testIPv4CollectionOverlapsSubnet()
	local collection = IPv4Collection.new()
	collection:addIP('1.2.3.4')
	self:assertEquals(true, collection:overlapsSubnet('1.2.3.0/24'))
	self:assertEquals(true, collection:overlapsSubnet(Subnet.new('1.2.3.0/24')))
	self:assertEquals(false, collection:overlapsSubnet('1.2.4.0/24'))
end

function suite:testIPv4CollectionGetRanges()
	local collection = IPv4Collection.new()
	collection:addSubnet('1.2.0.0/24')
	collection:addSubnet('1.2.1.0/24')
	self:assertRangesEqual(
		{{IPAddress.new('1.2.0.0'), IPAddress.new('1.2.1.255')}},
		collection:getRanges()
	)
	collection:addSubnet('1.2.10.0/24')
	self:assertRangesEqual(
		{
			{IPAddress.new('1.2.0.0'), IPAddress.new('1.2.1.255')},
			{IPAddress.new('1.2.10.0'), IPAddress.new('1.2.10.255')},
		},
		collection:getRanges()
	)
end

function suite:testIPv4CollectionOverlapsSubnet()
	local collection = IPv4Collection.new()
	self:assertEquals(false, collection:overlapsSubnet('1.2.3.0/24'))
	collection:addIP('1.2.3.4')
	self:assertEquals(true, collection:overlapsSubnet('1.2.3.0/24'))
	self:assertEquals(false, collection:overlapsSubnet('5.6.7.0/24'))
end

function suite:testIPv4CollectionOverlapsSubnetObjects()
	local collection = IPv4Collection.new()
	self:assertEquals(false, collection:overlapsSubnet(Subnet.new('1.2.3.0/24')))
	collection:addIP('1.2.3.4')
	self:assertEquals(true, collection:overlapsSubnet(Subnet.new('1.2.3.0/24')))
	self:assertEquals(false, collection:overlapsSubnet(Subnet.new('5.6.7.0/24')))
end

function suite:testIPv4CollectionAddFromString()
	local collection = IPv4Collection.new()
	collection:addFromString('foo 1.2.3.4 bar 5.6.7.0/24 baz')
	self:assertTrue(collection:containsIP('1.2.3.4'))
	self:assertTrue(collection:overlapsSubnet('5.6.7.0/24'))
end

function suite:testIPv4CollectionAddFromStringChaining()
	self:assertNotError(function ()
		IPv4Collection.new()
			:addFromString('foo 1.2.3.4')
			:addFromString('bar 5.6.7.8')
	end)
end

-------------------------------------------------------------------------------
-- IPv6Collection tests
-------------------------------------------------------------------------------

function suite:testIPv6CollectionConstructor()
	self:assertIPv6CollectionObject(IPv6Collection.new())
end

function suite:testIPv6CollectionGetVersion()
	self:assertEquals('IPv6', IPv6Collection.new():getVersion())
end

function suite:testIPv6CollectionAddIP()
	self:assertNotError(function () IPv6Collection.new():addIP('2001:db8::ff00:12:3456') end)
	self:assertNotError(function () IPv6Collection.new():addIP(IPAddress.new('2001:db8::ff00:12:3456')) end)
	suite:assertIPStringError(
		'foo',
		function ()
			IPv6Collection.new():addIP(IPAddress.new('foo'))
		end
	)
	suite:assertIPStringError(
		'2001:db8::ff00:12:0/112',
		function ()
			IPv6Collection.new():addIP(IPAddress.new('2001:db8::ff00:12:0/112'))
		end
	)
end

function suite:testIPv6CollectionAddIPChaining()
	self:assertNotError(function ()
		IPv6Collection.new()
			:addIP('2001:db8::ff00:0:1234')
			:addIP('2001:db8::ff00:0:5678')
	end)
end

function suite:testIPv6CollectionAddSubnet()
	self:assertNotError(function () IPv6Collection.new():addSubnet('2001:db8::ff00:12:0/112') end)
	self:assertNotError(function () IPv6Collection.new():addSubnet(Subnet.new('2001:db8::ff00:12:0/112')) end)
	suite:assertCIDRStringError(
		'foo',
		function ()
			IPv6Collection.new():addSubnet('foo')
		end
	)
	suite:assertCIDRStringError(
		'2001:db8::ff00:12:3456',
		function ()
			IPv6Collection.new():addSubnet('2001:db8::ff00:12:3456')
		end
	)
end

function suite:testIPv6CollectionAddSubnetChaining()
	self:assertNotError(function ()
		IPv6Collection.new()
			:addSubnet('2001:db8::ff00:0:0/112')
			:addSubnet('2001:db8::ff00:1:0/112')
	end)
end

function suite:testIPv6CollectionContainsIP()
	local collection = IPv6Collection.new()
	collection:addIP('2001:db8::ff00:12:3456')
	self:assertEquals(true, collection:containsIP('2001:db8::ff00:12:3456'))
	self:assertEquals(true, collection:containsIP(IPAddress.new('2001:db8::ff00:12:3456')))
	self:assertEquals(false, collection:containsIP('1.2.3.5'))
end

function suite:testIPv6CollectionOverlapsSubnet()
	local collection = IPv6Collection.new()
	collection:addIP('2001:db8::ff00:12:3456')
	self:assertEquals(true, collection:overlapsSubnet('2001:db8::ff00:12:0/112'))
	self:assertEquals(true, collection:overlapsSubnet(Subnet.new('2001:db8::ff00:12:0/112')))
	self:assertEquals(false, collection:overlapsSubnet('2001:db8::ff00:34:0/112'))
end

function suite:testIPv6CollectionGetRanges()
	local collection = IPv6Collection.new()
	collection:addSubnet('2001:db8::ff00:0:0/112')
	collection:addSubnet('2001:db8::ff00:1:0/112')
	self:assertRangesEqual(
		{{IPAddress.new('2001:db8::ff00:0:0'), IPAddress.new('2001:db8::ff00:1:ffff')}},
		collection:getRanges()
	)
	collection:addSubnet('2001:db8::ff00:10:0/112')
	self:assertRangesEqual(
		{
			{IPAddress.new('2001:db8::ff00:0:0'), IPAddress.new('2001:db8::ff00:1:ffff')},
			{IPAddress.new('2001:db8::ff00:10:0'), IPAddress.new('2001:db8::ff00:10:ffff')},
		},
		collection:getRanges()
	)
end

function suite:testIPv6CollectionOverlapsSubnet()
	local collection = IPv6Collection.new()
	self:assertEquals(false, collection:overlapsSubnet('2001:db8::ff00:12:0/112'))
	collection:addIP('2001:db8::ff00:12:3456')
	self:assertEquals(true, collection:overlapsSubnet('2001:db8::ff00:12:0/112'))
	self:assertEquals(false, collection:overlapsSubnet('2001:db8::ff00:34:0/112'))
end

function suite:testIPv6CollectionOverlapsSubnetObjects()
	local collection = IPv6Collection.new()
	self:assertEquals(false, collection:overlapsSubnet(Subnet.new('2001:db8::ff00:12:0/112')))
	collection:addIP('2001:db8::ff00:12:3456')
	self:assertEquals(true, collection:overlapsSubnet(Subnet.new('2001:db8::ff00:12:0/112')))
	self:assertEquals(false, collection:overlapsSubnet(Subnet.new('2001:db8::ff00:34:0/112')))
end

function suite:testIPv6CollectionAddFromString()
	local collection = IPv6Collection.new()
	collection:addFromString('foo 2001:db8::ff00:12:3456 bar 2001:db8::ff00:34:0/112 baz')
	self:assertTrue(collection:containsIP('2001:db8::ff00:12:3456'))
	self:assertTrue(collection:overlapsSubnet('2001:db8::ff00:34:0/112'))
end

function suite:testIPv6CollectionAddFromStringStartingColon()
	local collection = IPv6Collection.new()
	collection:addFromString('::12:1234 foo')
	self:assertTrue(collection:containsIP('::12:1234'))
end

function suite:testIPv6CollectionAddFromStringStartingIndent()
	local collection = IPv6Collection.new()
	collection:addFromString('::As I was saying, 2001:db8::ff00:12:3456 should be blocked. ~~~~')
	self:assertTrue(collection:containsIP('2001:db8::ff00:12:3456'))
	self:assertFalse(collection:containsIP('::'))
end

function suite:testIPv6CollectionAddFromStringChaining()
	self:assertNotError(function ()
		IPv6Collection.new()
			:addFromString('foo 2001:db8::ff00:0:1234')
			:addFromString('bar 2001:db8::ff00:0:5678')
	end)
end


return suite