Module:IP/testcases

-- 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'

--------------------------------------------------------------------------------
-- Sandbox run
--------------------------------------------------------------------------------

function suite.runSandbox(...)
	IP = require('Module:IP/sandbox')
	IPAddress = IP.IPAddress
	Subnet = IP.Subnet
	IPv4Collection = IP.IPv4Collection
	IPv6Collection = IP.IPv6Collection
	return suite.run(...)
end

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

function suite:assertSelfParameterError(class, objName, method, func, ...)
	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:assertThrows(func, message, nil, ...)
end

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

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

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

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

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

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

function suite:assertObject(val, ...)
	self:assertTrue(type(val) == 'table', nil, 2)
	for i, method in ipairs{...} do
		self:assertTrue(type(val[method]) == 'function', nil, 2)
	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(), nil, 2)
end

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

function suite:assertRangesEqual(expected, actual)
	self:assertTrue(#expected == #actual, nil, 1)
	for i = 1, #expected do
		local expectedRange = expected[i]
		local actualRange = actual[i]
		self:assertEquals(expectedRange[1], actualRange[1], nil, 1)
		self:assertEquals(expectedRange[2], actualRange[2], nil, 1)
	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:assertThrows(
			IPAddress.new,
			string.format("'%s' is an invalid IP address", ip), nil,
			ip
		)
	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:assertThrows(
		IPAddress.new,
		"'foo' is an invalid IP address", nil,
		'foo'
	)
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:assertThrows(function ()
		IPAddress.new('1.2.3.4'):getSubnet(33)
	end, message)
	self:assertThrows(function ()
		IPAddress.new('1.2.3.4'):getSubnet(-1)
	end, message)
	self:assertThrows(function ()
		IPAddress.new('1.2.3.4'):getSubnet(24.5)
	end, message)
end

function suite:testGetSubnetIPv6NumberErrors()
	local message = "bad argument #1 to 'getSubnet' (must be an integer between 0 and 128)"
	self:assertThrows(function ()
		IPAddress.new('2001:db8::ff00:12:3456'):getSubnet(129)
	end, message)
	self:assertThrows(function ()
		IPAddress.new('2001:db8::ff00:12:3456'):getSubnet(-1)
	end, message)
	self:assertThrows(function ()
		IPAddress.new('2001:db8::ff00:12:3456'):getSubnet(112.5)
	end, message)
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:assertThrows(
			Subnet.new,
			string.format("'%s' is an invalid CIDR string", cidr), nil,
			cidr
		)
	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:assertDoesNotThrow(function () IPv4Collection.new():addIP('1.2.3.4') end)
	self:assertDoesNotThrow(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:assertDoesNotThrow(function ()
		IPv4Collection.new()
			:addIP('1.2.3.4')
			:addIP('5.6.7.8')
	end)
end

function suite:testIPv4CollectionAddIPErrors()
	self:assertTypeError(
		1, 'addIP', 'string or table', 'boolean',
		function ()
			IPv4Collection.new():addIP(false)
		end
	)
	self:assertTypeError(
		1, 'addIP', 'string or table', 'nil',
		function ()
			IPv4Collection.new():addIP()
		end
	)
	self:assertIPStringError(
		'foo',
		function ()
			IPv4Collection.new():addIP('foo')
		end
	)
	self:assertObjectError(
		1, 'addIP', 'IPAddress',
		function ()
			IPv4Collection.new():addIP{foo = 'bar'}
		end
	)
end

function suite:testIPv4CollectionAddSubnet()
	self:assertDoesNotThrow(function () IPv4Collection.new():addSubnet('1.2.3.0/24') end)
	self:assertDoesNotThrow(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:assertDoesNotThrow(function ()
		IPv4Collection.new()
			:addSubnet('1.2.3.0/24')
			:addSubnet('5.6.7.0/24')
	end)
end

function suite:testIPv4CollectionAddSubnetErrors()
	self:assertTypeError(
		1, 'addSubnet', 'string or table', 'boolean',
		function ()
			IPv4Collection.new():addSubnet(false)
		end
	)
	self:assertTypeError(
		1, 'addSubnet', 'string or table', 'nil',
		function ()
			IPv4Collection.new():addSubnet()
		end
	)
	self:assertCIDRStringError(
		'foo',
		function ()
			IPv4Collection.new():addSubnet('foo')
		end
	)
	self:assertObjectError(
		1, 'addSubnet', 'Subnet',
		function ()
			IPv4Collection.new():addSubnet{foo = 'bar'}
		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:testIPv4CollectionContainsIPErrors()
	self:assertTypeError(
		1, 'containsIP', 'string or table', 'boolean',
		function ()
			IPv4Collection.new():containsIP(false)
		end
	)
	self:assertTypeError(
		1, 'containsIP', 'string or table', 'nil',
		function ()
			IPv4Collection.new():containsIP()
		end
	)
	self:assertIPStringError(
		'foo',
		function ()
			IPv4Collection.new():containsIP('foo')
		end
	)
	self:assertObjectError(
		1, 'containsIP', 'IPAddress',
		function ()
			IPv4Collection.new():containsIP{foo = 'bar'}
		end
	)
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:testIPv4CollectionOverlapsSubnetErrors()
	self:assertTypeError(
		1, 'overlapsSubnet', 'string or table', 'boolean',
		function ()
			IPv4Collection.new():overlapsSubnet(false)
		end
	)
	self:assertTypeError(
		1, 'overlapsSubnet', 'string or table', 'nil',
		function ()
			IPv4Collection.new():overlapsSubnet()
		end
	)
	self:assertCIDRStringError(
		'foo',
		function ()
			IPv4Collection.new():overlapsSubnet('foo')
		end
	)
	self:assertObjectError(
		1, 'overlapsSubnet', 'Subnet',
		function ()
			IPv4Collection.new():overlapsSubnet{foo = 'bar'}
		end
	)
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:assertDoesNotThrow(function ()
		IPv4Collection.new()
			:addFromString('foo 1.2.3.4')
			:addFromString('bar 5.6.7.8')
	end)
end

function suite:testIPv4CollectionAddFromStringErrors()
	self:assertTypeError(
		1, 'addFromString', 'string', 'boolean',
		function ()
			IPv4Collection.new():addFromString(false)
		end
	)
	self:assertTypeError(
		1, 'addFromString', 'string', 'nil',
		function ()
			IPv4Collection.new():addFromString()
		end
	)
	self:assertTypeError(
		1, 'addFromString', 'string', 'table',
		function ()
			IPv4Collection.new():addFromString{}
		end
	)
	self:assertTypeError(
		1, 'addFromString', 'string', 'number',
		function ()
			IPv4Collection.new():addFromString(7)
		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:assertDoesNotThrow(function () IPv6Collection.new():addIP('2001:db8::ff00:12:3456') end)
	self:assertDoesNotThrow(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:assertDoesNotThrow(function ()
		IPv6Collection.new()
			:addIP('2001:db8::ff00:0:1234')
			:addIP('2001:db8::ff00:0:5678')
	end)
end

function suite:testIPv6CollectionAddIPErrors()
	self:assertTypeError(
		1, 'addIP', 'string or table', 'boolean',
		function ()
			IPv6Collection.new():addIP(false)
		end
	)
	self:assertTypeError(
		1, 'addIP', 'string or table', 'nil',
		function ()
			IPv6Collection.new():addIP()
		end
	)
	self:assertIPStringError(
		'foo',
		function ()
			IPv6Collection.new():addIP('foo')
		end
	)
	self:assertObjectError(
		1, 'addIP', 'IPAddress',
		function ()
			IPv6Collection.new():addIP{foo = 'bar'}
		end
	)
end

function suite:testIPv6CollectionAddSubnet()
	self:assertDoesNotThrow(function () IPv6Collection.new():addSubnet('2001:db8::ff00:12:0/112') end)
	self:assertDoesNotThrow(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:assertDoesNotThrow(function ()
		IPv6Collection.new()
			:addSubnet('2001:db8::ff00:0:0/112')
			:addSubnet('2001:db8::ff00:1:0/112')
	end)
end

function suite:testIPv6CollectionAddSubnetErrors()
	self:assertTypeError(
		1, 'addSubnet', 'string or table', 'boolean',
		function ()
			IPv6Collection.new():addSubnet(false)
		end
	)
	self:assertTypeError(
		1, 'addSubnet', 'string or table', 'nil',
		function ()
			IPv6Collection.new():addSubnet()
		end
	)
	self:assertCIDRStringError(
		'foo',
		function ()
			IPv6Collection.new():addSubnet('foo')
		end
	)
	self:assertObjectError(
		1, 'addSubnet', 'Subnet',
		function ()
			IPv6Collection.new():addSubnet{foo = 'bar'}
		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:testIPv6CollectionContainsIPErrors()
	self:assertTypeError(
		1, 'containsIP', 'string or table', 'boolean',
		function ()
			IPv6Collection.new():containsIP(false)
		end
	)
	self:assertTypeError(
		1, 'containsIP', 'string or table', 'nil',
		function ()
			IPv6Collection.new():containsIP()
		end
	)
	self:assertIPStringError(
		'foo',
		function ()
			IPv6Collection.new():containsIP('foo')
		end
	)
	self:assertObjectError(
		1, 'containsIP', 'IPAddress',
		function ()
			IPv6Collection.new():containsIP{foo = 'bar'}
		end
	)
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:testIPv6CollectionOverlapsSubnetErrors()
	self:assertTypeError(
		1, 'overlapsSubnet', 'string or table', 'boolean',
		function ()
			IPv6Collection.new():overlapsSubnet(false)
		end
	)
	self:assertTypeError(
		1, 'overlapsSubnet', 'string or table', 'nil',
		function ()
			IPv6Collection.new():overlapsSubnet()
		end
	)
	self:assertCIDRStringError(
		'foo',
		function ()
			IPv6Collection.new():overlapsSubnet('foo')
		end
	)
	self:assertObjectError(
		1, 'overlapsSubnet', 'Subnet',
		function ()
			IPv6Collection.new():overlapsSubnet{foo = 'bar'}
		end
	)
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:assertDoesNotThrow(function ()
		IPv6Collection.new()
			:addFromString('foo 2001:db8::ff00:0:1234')
			:addFromString('bar 2001:db8::ff00:0:5678')
	end)
end

function suite:testIPv6CollectionAddFromStringErrors()
	self:assertTypeError(
		1, 'addFromString', 'string', 'boolean',
		function ()
			IPv6Collection.new():addFromString(false)
		end
	)
	self:assertTypeError(
		1, 'addFromString', 'string', 'nil',
		function ()
			IPv6Collection.new():addFromString()
		end
	)
	self:assertTypeError(
		1, 'addFromString', 'string', 'table',
		function ()
			IPv6Collection.new():addFromString{}
		end
	)
	self:assertTypeError(
		1, 'addFromString', 'string', 'number',
		function ()
			IPv6Collection.new():addFromString(7)
		end
	)
end

return suite

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.