MediaWiki  REL1_24
IPTest.php
Go to the documentation of this file.
00001 <?php
00012 class IPTest extends MediaWikiTestCase {
00017     public function testisIPAddress() {
00018         $this->assertFalse( IP::isIPAddress( false ), 'Boolean false is not an IP' );
00019         $this->assertFalse( IP::isIPAddress( true ), 'Boolean true is not an IP' );
00020         $this->assertFalse( IP::isIPAddress( "" ), 'Empty string is not an IP' );
00021         $this->assertFalse( IP::isIPAddress( 'abc' ), 'Garbage IP string' );
00022         $this->assertFalse( IP::isIPAddress( ':' ), 'Single ":" is not an IP' );
00023         $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1' ), 'IPv6 with a double :: occurrence' );
00024         $this->assertFalse(
00025             IP::isIPAddress( '2001:0DB8::A:1::' ),
00026             'IPv6 with a double :: occurrence, last at end'
00027         );
00028         $this->assertFalse(
00029             IP::isIPAddress( '::2001:0DB8::5:1' ),
00030             'IPv6 with a double :: occurrence, firt at beginning'
00031         );
00032         $this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
00033         $this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
00034         $this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
00035         $this->assertFalse( IP::isIPAddress( 'fc:100:300' ), 'IPv6 with only 3 words' );
00036 
00037         $this->assertTrue( IP::isIPAddress( '::' ), 'RFC 4291 IPv6 Unspecified Address' );
00038         $this->assertTrue( IP::isIPAddress( '::1' ), 'RFC 4291 IPv6 Loopback Address' );
00039         $this->assertTrue( IP::isIPAddress( '74.24.52.13/20', 'IPv4 range' ) );
00040         $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0/24' ), 'IPv6 range' );
00041         $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac/96' ), 'IPv6 range with "::"' );
00042 
00043         $validIPs = array( 'fc:100::', 'fc:100:a:d:1:e:ac::', 'fc::100', '::fc:100:a:d:1:e:ac',
00044             '::fc', 'fc::100:a:d:1:e:ac', 'fc:100:a:d:1:e:ac:0', '124.24.52.13', '1.24.52.13' );
00045         foreach ( $validIPs as $ip ) {
00046             $this->assertTrue( IP::isIPAddress( $ip ), "$ip is a valid IP address" );
00047         }
00048     }
00049 
00053     public function testisIPv6() {
00054         $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
00055         $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
00056         $this->assertFalse( IP::isIPv6( 'fc:300' ), 'IPv6 with only 2 words' );
00057         $this->assertFalse( IP::isIPv6( 'fc:100:300' ), 'IPv6 with only 3 words' );
00058 
00059         $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
00060         $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
00061         $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
00062         $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
00063         $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
00064         $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
00065 
00066         $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
00067         $this->assertFalse(
00068             IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ),
00069             'IPv6 with 9 words ending with "::"'
00070         );
00071 
00072         $this->assertFalse( IP::isIPv6( ':::' ) );
00073         $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
00074 
00075         $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
00076         $this->assertTrue( IP::isIPv6( '::0' ) );
00077         $this->assertTrue( IP::isIPv6( '::fc' ) );
00078         $this->assertTrue( IP::isIPv6( '::fc:100' ) );
00079         $this->assertTrue( IP::isIPv6( '::fc:100:a' ) );
00080         $this->assertTrue( IP::isIPv6( '::fc:100:a:d' ) );
00081         $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
00082         $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
00083         $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
00084 
00085         $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
00086         $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
00087 
00088         $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
00089         $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending with lone ":"' );
00090         $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
00091 
00092         $this->assertTrue( IP::isIPv6( 'fc::100' ), 'IPv6 with "::" and 2 words' );
00093         $this->assertTrue( IP::isIPv6( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
00094         $this->assertTrue( IP::isIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' ) );
00095         $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
00096         $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ), 'IPv6 with "::" and 6 words' );
00097         $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
00098         $this->assertTrue( IP::isIPv6( '2001::df' ), 'IPv6 with "::" and 2 words' );
00099         $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
00100         $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
00101 
00102         $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
00103         $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
00104 
00105         $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
00106     }
00107 
00111     public function testisIPv4() {
00112         $this->assertFalse( IP::isIPv4( false ), 'Boolean false is not an IP' );
00113         $this->assertFalse( IP::isIPv4( true ), 'Boolean true is not an IP' );
00114         $this->assertFalse( IP::isIPv4( "" ), 'Empty string is not an IP' );
00115         $this->assertFalse( IP::isIPv4( 'abc' ) );
00116         $this->assertFalse( IP::isIPv4( ':' ) );
00117         $this->assertFalse( IP::isIPv4( '124.24.52' ), 'IPv4 not enough quads' );
00118         $this->assertFalse( IP::isIPv4( '24.324.52.13' ), 'IPv4 out of range' );
00119         $this->assertFalse( IP::isIPv4( '.24.52.13' ), 'IPv4 starts with period' );
00120 
00121         $this->assertTrue( IP::isIPv4( '124.24.52.13' ) );
00122         $this->assertTrue( IP::isIPv4( '1.24.52.13' ) );
00123         $this->assertTrue( IP::isIPv4( '74.24.52.13/20', 'IPv4 range' ) );
00124     }
00125 
00129     public function testValidIPs() {
00130         foreach ( range( 0, 255 ) as $i ) {
00131             $a = sprintf( "%03d", $i );
00132             $b = sprintf( "%02d", $i );
00133             $c = sprintf( "%01d", $i );
00134             foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
00135                 $ip = "$f.$f.$f.$f";
00136                 $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv4 address" );
00137             }
00138         }
00139         foreach ( range( 0x0, 0xFFFF, 0xF ) as $i ) {
00140             $a = sprintf( "%04x", $i );
00141             $b = sprintf( "%03x", $i );
00142             $c = sprintf( "%02x", $i );
00143             foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
00144                 $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
00145                 $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv6 address" );
00146             }
00147         }
00148         // test with some abbreviations
00149         $this->assertFalse( IP::isValid( ':fc:100::' ), 'IPv6 starting with lone ":"' );
00150         $this->assertFalse( IP::isValid( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
00151         $this->assertFalse( IP::isValid( 'fc:300' ), 'IPv6 with only 2 words' );
00152         $this->assertFalse( IP::isValid( 'fc:100:300' ), 'IPv6 with only 3 words' );
00153 
00154         $this->assertTrue( IP::isValid( 'fc:100::' ) );
00155         $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e::' ) );
00156         $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e:ac::' ) );
00157 
00158         $this->assertTrue( IP::isValid( 'fc::100' ), 'IPv6 with "::" and 2 words' );
00159         $this->assertTrue( IP::isValid( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
00160         $this->assertTrue( IP::isValid( '2001::df' ), 'IPv6 with "::" and 2 words' );
00161         $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
00162         $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
00163         $this->assertTrue( IP::isValid( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
00164         $this->assertTrue( IP::isValid( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
00165 
00166         $this->assertFalse(
00167             IP::isValid( 'fc:100:a:d:1:e:ac:0::' ),
00168             'IPv6 with 8 words ending with "::"'
00169         );
00170         $this->assertFalse(
00171             IP::isValid( 'fc:100:a:d:1:e:ac:0:1::' ),
00172             'IPv6 with 9 words ending with "::"'
00173         );
00174     }
00175 
00179     public function testInvalidIPs() {
00180         // Out of range...
00181         foreach ( range( 256, 999 ) as $i ) {
00182             $a = sprintf( "%03d", $i );
00183             $b = sprintf( "%02d", $i );
00184             $c = sprintf( "%01d", $i );
00185             foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
00186                 $ip = "$f.$f.$f.$f";
00187                 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv4 address" );
00188             }
00189         }
00190         foreach ( range( 'g', 'z' ) as $i ) {
00191             $a = sprintf( "%04s", $i );
00192             $b = sprintf( "%03s", $i );
00193             $c = sprintf( "%02s", $i );
00194             foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
00195                 $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
00196                 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv6 address" );
00197             }
00198         }
00199         // Have CIDR
00200         $ipCIDRs = array(
00201             '212.35.31.121/32',
00202             '212.35.31.121/18',
00203             '212.35.31.121/24',
00204             '::ff:d:321:5/96',
00205             'ff::d3:321:5/116',
00206             'c:ff:12:1:ea:d:321:5/120',
00207         );
00208         foreach ( $ipCIDRs as $i ) {
00209             $this->assertFalse( IP::isValid( $i ),
00210                 "$i is an invalid IP address because it is a block" );
00211         }
00212         // Incomplete/garbage
00213         $invalid = array(
00214             'www.xn--var-xla.net',
00215             '216.17.184.G',
00216             '216.17.184.1.',
00217             '216.17.184',
00218             '216.17.184.',
00219             '256.17.184.1'
00220         );
00221         foreach ( $invalid as $i ) {
00222             $this->assertFalse( IP::isValid( $i ), "$i is an invalid IP address" );
00223         }
00224     }
00225 
00229     public function testValidBlocks() {
00230         $valid = array(
00231             '116.17.184.5/32',
00232             '0.17.184.5/30',
00233             '16.17.184.1/24',
00234             '30.242.52.14/1',
00235             '10.232.52.13/8',
00236             '30.242.52.14/0',
00237             '::e:f:2001/96',
00238             '::c:f:2001/128',
00239             '::10:f:2001/70',
00240             '::fe:f:2001/1',
00241             '::6d:f:2001/8',
00242             '::fe:f:2001/0',
00243         );
00244         foreach ( $valid as $i ) {
00245             $this->assertTrue( IP::isValidBlock( $i ), "$i is a valid IP block" );
00246         }
00247     }
00248 
00252     public function testInvalidBlocks() {
00253         $invalid = array(
00254             '116.17.184.5/33',
00255             '0.17.184.5/130',
00256             '16.17.184.1/-1',
00257             '10.232.52.13/*',
00258             '7.232.52.13/ab',
00259             '11.232.52.13/',
00260             '::e:f:2001/129',
00261             '::c:f:2001/228',
00262             '::10:f:2001/-1',
00263             '::6d:f:2001/*',
00264             '::86:f:2001/ab',
00265             '::23:f:2001/',
00266         );
00267         foreach ( $invalid as $i ) {
00268             $this->assertFalse( IP::isValidBlock( $i ), "$i is not a valid IP block" );
00269         }
00270     }
00271 
00276     public function testSanitizeIP() {
00277         $this->assertNull( IP::sanitizeIP( '' ) );
00278         $this->assertNull( IP::sanitizeIP( ' ' ) );
00279     }
00280 
00285     public function testToHex( $expected, $input ) {
00286         $result = IP::toHex( $input );
00287         $this->assertTrue( $result === false || is_string( $result ) );
00288         $this->assertEquals( $expected, $result );
00289     }
00290 
00294     public static function provideToHex() {
00295         return array(
00296             array( '00000001', '0.0.0.1' ),
00297             array( '01020304', '1.2.3.4' ),
00298             array( '7F000001', '127.0.0.1' ),
00299             array( '80000000', '128.0.0.0' ),
00300             array( 'DEADCAFE', '222.173.202.254' ),
00301             array( 'FFFFFFFF', '255.255.255.255' ),
00302             array( false, 'IN.VA.LI.D' ),
00303             array( 'v6-00000000000000000000000000000001', '::1' ),
00304             array( 'v6-20010DB885A3000000008A2E03707334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
00305             array( 'v6-20010DB885A3000000008A2E03707334', '2001:db8:85a3::8a2e:0370:7334' ),
00306             array( false, 'IN:VA::LI:D' ),
00307             array( false, ':::1' )
00308         );
00309     }
00310 
00314     public function testPrivateIPs() {
00315         $private = array( 'fc00::3', 'fc00::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
00316         foreach ( $private as $p ) {
00317             $this->assertFalse( IP::isPublic( $p ), "$p is not a public IP address" );
00318         }
00319         $public = array( '2001:5c0:1000:a::133', 'fc::3', '00FC::' );
00320         foreach ( $public as $p ) {
00321             $this->assertTrue( IP::isPublic( $p ), "$p is a public IP address" );
00322         }
00323     }
00324 
00325     // Private wrapper used to test CIDR Parsing.
00326     private function assertFalseCIDR( $CIDR, $msg = '' ) {
00327         $ff = array( false, false );
00328         $this->assertEquals( $ff, IP::parseCIDR( $CIDR ), $msg );
00329     }
00330 
00331     // Private wrapper to test network shifting using only dot notation
00332     private function assertNet( $expected, $CIDR ) {
00333         $parse = IP::parseCIDR( $CIDR );
00334         $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
00335     }
00336 
00340     public function testHexToQuad() {
00341         $this->assertEquals( '0.0.0.1', IP::hexToQuad( '00000001' ) );
00342         $this->assertEquals( '255.0.0.0', IP::hexToQuad( 'FF000000' ) );
00343         $this->assertEquals( '255.255.255.255', IP::hexToQuad( 'FFFFFFFF' ) );
00344         $this->assertEquals( '10.188.222.255', IP::hexToQuad( '0ABCDEFF' ) );
00345         // hex not left-padded...
00346         $this->assertEquals( '0.0.0.0', IP::hexToQuad( '0' ) );
00347         $this->assertEquals( '0.0.0.1', IP::hexToQuad( '1' ) );
00348         $this->assertEquals( '0.0.0.255', IP::hexToQuad( 'FF' ) );
00349         $this->assertEquals( '0.0.255.0', IP::hexToQuad( 'FF00' ) );
00350     }
00351 
00355     public function testHexToOctet() {
00356         $this->assertEquals( '0:0:0:0:0:0:0:1',
00357             IP::hexToOctet( '00000000000000000000000000000001' ) );
00358         $this->assertEquals( '0:0:0:0:0:0:FF:3',
00359             IP::hexToOctet( '00000000000000000000000000FF0003' ) );
00360         $this->assertEquals( '0:0:0:0:0:0:FF00:6',
00361             IP::hexToOctet( '000000000000000000000000FF000006' ) );
00362         $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF',
00363             IP::hexToOctet( '000000000000000000000000FCCFFAFF' ) );
00364         $this->assertEquals( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
00365             IP::hexToOctet( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ) );
00366         // hex not left-padded...
00367         $this->assertEquals( '0:0:0:0:0:0:0:0', IP::hexToOctet( '0' ) );
00368         $this->assertEquals( '0:0:0:0:0:0:0:1', IP::hexToOctet( '1' ) );
00369         $this->assertEquals( '0:0:0:0:0:0:0:FF', IP::hexToOctet( 'FF' ) );
00370         $this->assertEquals( '0:0:0:0:0:0:0:FFD0', IP::hexToOctet( 'FFD0' ) );
00371         $this->assertEquals( '0:0:0:0:0:0:FA00:0', IP::hexToOctet( 'FA000000' ) );
00372         $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
00373     }
00374 
00380     public function testCIDRParsing() {
00381         $this->assertFalseCIDR( '192.0.2.0', "missing mask" );
00382         $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
00383 
00384         // Verify if statement
00385         $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );
00386         $this->assertFalseCIDR( '192.0.2.0/AA', "mask not numeric" );
00387         $this->assertFalseCIDR( '192.0.2.0/-1', "mask < 0" );
00388         $this->assertFalseCIDR( '192.0.2.0/33', "mask > 32" );
00389 
00390         // Check internal logic
00391         # 0 mask always result in array(0,0)
00392         $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '192.0.0.2/0' ) );
00393         $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '0.0.0.0/0' ) );
00394         $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '255.255.255.255/0' ) );
00395 
00396         // @todo FIXME: Add more tests.
00397 
00398         # This part test network shifting
00399         $this->assertNet( '192.0.0.0', '192.0.0.2/24' );
00400         $this->assertNet( '192.168.5.0', '192.168.5.13/24' );
00401         $this->assertNet( '10.0.0.160', '10.0.0.161/28' );
00402         $this->assertNet( '10.0.0.0', '10.0.0.3/28' );
00403         $this->assertNet( '10.0.0.0', '10.0.0.3/30' );
00404         $this->assertNet( '10.0.0.4', '10.0.0.4/30' );
00405         $this->assertNet( '172.17.32.0', '172.17.35.48/21' );
00406         $this->assertNet( '10.128.0.0', '10.135.0.0/9' );
00407         $this->assertNet( '134.0.0.0', '134.0.5.1/8' );
00408     }
00409 
00413     public function testIPCanonicalizeOnValidIp() {
00414         $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
00415             'Canonicalization of a valid IP returns it unchanged' );
00416     }
00417 
00421     public function testIPCanonicalizeMappedAddress() {
00422         $this->assertEquals(
00423             '192.0.2.152',
00424             IP::canonicalize( '::ffff:192.0.2.152' )
00425         );
00426         $this->assertEquals(
00427             '192.0.2.152',
00428             IP::canonicalize( '::192.0.2.152' )
00429         );
00430     }
00431 
00437     public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
00438         $this->assertEquals(
00439             $expected,
00440             IP::isInRange( $addr, $range ),
00441             $message
00442         );
00443     }
00444 
00446     public static function provideIPsAndRanges() {
00447         # Format: (expected boolean, address, range, optional message)
00448         return array(
00449             # IPv4
00450             array( true, '192.0.2.0', '192.0.2.0/24', 'Network address' ),
00451             array( true, '192.0.2.77', '192.0.2.0/24', 'Simple address' ),
00452             array( true, '192.0.2.255', '192.0.2.0/24', 'Broadcast address' ),
00453 
00454             array( false, '0.0.0.0', '192.0.2.0/24' ),
00455             array( false, '255.255.255', '192.0.2.0/24' ),
00456 
00457             # IPv6
00458             array( false, '::1', '2001:DB8::/32' ),
00459             array( false, '::', '2001:DB8::/32' ),
00460             array( false, 'FE80::1', '2001:DB8::/32' ),
00461 
00462             array( true, '2001:DB8::', '2001:DB8::/32' ),
00463             array( true, '2001:0DB8::', '2001:DB8::/32' ),
00464             array( true, '2001:DB8::1', '2001:DB8::/32' ),
00465             array( true, '2001:0DB8::1', '2001:DB8::/32' ),
00466             array( true, '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
00467                 '2001:DB8::/32' ),
00468 
00469             array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
00470         );
00471     }
00472 
00477     public function testSplitHostAndPort( $expected, $input, $description ) {
00478         $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
00479     }
00480 
00484     public static function provideSplitHostAndPort() {
00485         return array(
00486             array( false, '[', 'Unclosed square bracket' ),
00487             array( false, '[::', 'Unclosed square bracket 2' ),
00488             array( array( '::', false ), '::', 'Bare IPv6 0' ),
00489             array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
00490             array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
00491             array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
00492             array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
00493             array( false, '::x', 'Double colon but no IPv6' ),
00494             array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
00495             array( false, 'x:x', 'Hostname and invalid port' ),
00496             array( array( 'x', false ), 'x', 'Plain hostname' )
00497         );
00498     }
00499 
00504     public function testCombineHostAndPort( $expected, $input, $description ) {
00505         list( $host, $port, $defaultPort ) = $input;
00506         $this->assertEquals(
00507             $expected,
00508             IP::combineHostAndPort( $host, $port, $defaultPort ),
00509             $description );
00510     }
00511 
00515     public static function provideCombineHostAndPort() {
00516         return array(
00517             array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
00518             array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
00519             array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
00520             array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
00521         );
00522     }
00523 
00528     public function testSanitizeRange( $input, $expected, $description ) {
00529         $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
00530     }
00531 
00535     public static function provideIPCIDRs() {
00536         return array(
00537             array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
00538             array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
00539             array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
00540             array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
00541             array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
00542             array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
00543             array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
00544             array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
00545         );
00546     }
00547 
00552     public function testPrettifyIP( $ip, $prettified ) {
00553         $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
00554     }
00555 
00559     public static function provideIPsToPrettify() {
00560         return array(
00561             array( '0:0:0:0:0:0:0:0', '::' ),
00562             array( '0:0:0::0:0:0', '::' ),
00563             array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
00564             array( '0:0::f', '::f' ),
00565             array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
00566             array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
00567             array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
00568             array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
00569             array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
00570             array( '0:0:0:0:0:0:0:0/16', '::/16' ),
00571             array( '0:0:0::0:0:0/64', '::/64' ),
00572             array( '0:0::f/52', '::f/52' ),
00573             array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
00574             array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
00575             array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
00576             array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
00577             array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
00578         );
00579     }
00580 }