MediaWiki  REL1_22
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( IP::isIPAddress( '2001:0DB8::A:1::' ), 'IPv6 with a double :: occurrence, last at end' );
00025         $this->assertFalse( IP::isIPAddress( '::2001:0DB8::5:1' ), 'IPv6 with a double :: occurrence, firt at beginning' );
00026         $this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
00027         $this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
00028         $this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
00029         $this->assertFalse( IP::isIPAddress( 'fc:100:300' ), 'IPv6 with only 3 words' );
00030 
00031         $this->assertTrue( IP::isIPAddress( '::' ), 'RFC 4291 IPv6 Unspecified Address' );
00032         $this->assertTrue( IP::isIPAddress( '::1' ), 'RFC 4291 IPv6 Loopback Address' );
00033         $this->assertTrue( IP::isIPAddress( '74.24.52.13/20', 'IPv4 range' ) );
00034         $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0/24' ), 'IPv6 range' );
00035         $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac/96' ), 'IPv6 range with "::"' );
00036 
00037         $validIPs = array( 'fc:100::', 'fc:100:a:d:1:e:ac::', 'fc::100', '::fc:100:a:d:1:e:ac',
00038             '::fc', 'fc::100:a:d:1:e:ac', 'fc:100:a:d:1:e:ac:0', '124.24.52.13', '1.24.52.13' );
00039         foreach ( $validIPs as $ip ) {
00040             $this->assertTrue( IP::isIPAddress( $ip ), "$ip is a valid IP address" );
00041         }
00042     }
00043 
00047     public function testisIPv6() {
00048         $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
00049         $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
00050         $this->assertFalse( IP::isIPv6( 'fc:300' ), 'IPv6 with only 2 words' );
00051         $this->assertFalse( IP::isIPv6( 'fc:100:300' ), 'IPv6 with only 3 words' );
00052 
00053         $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
00054         $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
00055         $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
00056         $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
00057         $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
00058         $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
00059 
00060         $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
00061         $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words ending with "::"' );
00062 
00063         $this->assertFalse( IP::isIPv6( ':::' ) );
00064         $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
00065 
00066         $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
00067         $this->assertTrue( IP::isIPv6( '::0' ) );
00068         $this->assertTrue( IP::isIPv6( '::fc' ) );
00069         $this->assertTrue( IP::isIPv6( '::fc:100' ) );
00070         $this->assertTrue( IP::isIPv6( '::fc:100:a' ) );
00071         $this->assertTrue( IP::isIPv6( '::fc:100:a:d' ) );
00072         $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
00073         $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
00074         $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
00075 
00076         $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
00077         $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
00078 
00079         $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
00080         $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending with lone ":"' );
00081         $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
00082 
00083         $this->assertTrue( IP::isIPv6( 'fc::100' ), 'IPv6 with "::" and 2 words' );
00084         $this->assertTrue( IP::isIPv6( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
00085         $this->assertTrue( IP::isIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' ) );
00086         $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
00087         $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ), 'IPv6 with "::" and 6 words' );
00088         $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
00089         $this->assertTrue( IP::isIPv6( '2001::df' ), 'IPv6 with "::" and 2 words' );
00090         $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
00091         $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
00092 
00093         $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
00094         $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
00095 
00096         $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
00097     }
00098 
00102     public function testisIPv4() {
00103         $this->assertFalse( IP::isIPv4( false ), 'Boolean false is not an IP' );
00104         $this->assertFalse( IP::isIPv4( true ), 'Boolean true is not an IP' );
00105         $this->assertFalse( IP::isIPv4( "" ), 'Empty string is not an IP' );
00106         $this->assertFalse( IP::isIPv4( 'abc' ) );
00107         $this->assertFalse( IP::isIPv4( ':' ) );
00108         $this->assertFalse( IP::isIPv4( '124.24.52' ), 'IPv4 not enough quads' );
00109         $this->assertFalse( IP::isIPv4( '24.324.52.13' ), 'IPv4 out of range' );
00110         $this->assertFalse( IP::isIPv4( '.24.52.13' ), 'IPv4 starts with period' );
00111 
00112         $this->assertTrue( IP::isIPv4( '124.24.52.13' ) );
00113         $this->assertTrue( IP::isIPv4( '1.24.52.13' ) );
00114         $this->assertTrue( IP::isIPv4( '74.24.52.13/20', 'IPv4 range' ) );
00115     }
00116 
00120     public function testValidIPs() {
00121         foreach ( range( 0, 255 ) as $i ) {
00122             $a = sprintf( "%03d", $i );
00123             $b = sprintf( "%02d", $i );
00124             $c = sprintf( "%01d", $i );
00125             foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
00126                 $ip = "$f.$f.$f.$f";
00127                 $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv4 address" );
00128             }
00129         }
00130         foreach ( range( 0x0, 0xFFFF, 0xF ) as $i ) {
00131             $a = sprintf( "%04x", $i );
00132             $b = sprintf( "%03x", $i );
00133             $c = sprintf( "%02x", $i );
00134             foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
00135                 $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
00136                 $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv6 address" );
00137             }
00138         }
00139         // test with some abbreviations
00140         $this->assertFalse( IP::isValid( ':fc:100::' ), 'IPv6 starting with lone ":"' );
00141         $this->assertFalse( IP::isValid( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
00142         $this->assertFalse( IP::isValid( 'fc:300' ), 'IPv6 with only 2 words' );
00143         $this->assertFalse( IP::isValid( 'fc:100:300' ), 'IPv6 with only 3 words' );
00144 
00145         $this->assertTrue( IP::isValid( 'fc:100::' ) );
00146         $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e::' ) );
00147         $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e:ac::' ) );
00148 
00149         $this->assertTrue( IP::isValid( 'fc::100' ), 'IPv6 with "::" and 2 words' );
00150         $this->assertTrue( IP::isValid( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
00151         $this->assertTrue( IP::isValid( '2001::df' ), 'IPv6 with "::" and 2 words' );
00152         $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
00153         $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
00154         $this->assertTrue( IP::isValid( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
00155         $this->assertTrue( IP::isValid( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
00156 
00157         $this->assertFalse( IP::isValid( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
00158         $this->assertFalse( IP::isValid( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words ending with "::"' );
00159     }
00160 
00164     public function testInvalidIPs() {
00165         // Out of range...
00166         foreach ( range( 256, 999 ) as $i ) {
00167             $a = sprintf( "%03d", $i );
00168             $b = sprintf( "%02d", $i );
00169             $c = sprintf( "%01d", $i );
00170             foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
00171                 $ip = "$f.$f.$f.$f";
00172                 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv4 address" );
00173             }
00174         }
00175         foreach ( range( 'g', 'z' ) as $i ) {
00176             $a = sprintf( "%04s", $i );
00177             $b = sprintf( "%03s", $i );
00178             $c = sprintf( "%02s", $i );
00179             foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
00180                 $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
00181                 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv6 address" );
00182             }
00183         }
00184         // Have CIDR
00185         $ipCIDRs = array(
00186             '212.35.31.121/32',
00187             '212.35.31.121/18',
00188             '212.35.31.121/24',
00189             '::ff:d:321:5/96',
00190             'ff::d3:321:5/116',
00191             'c:ff:12:1:ea:d:321:5/120',
00192         );
00193         foreach ( $ipCIDRs as $i ) {
00194             $this->assertFalse( IP::isValid( $i ),
00195                 "$i is an invalid IP address because it is a block" );
00196         }
00197         // Incomplete/garbage
00198         $invalid = array(
00199             'www.xn--var-xla.net',
00200             '216.17.184.G',
00201             '216.17.184.1.',
00202             '216.17.184',
00203             '216.17.184.',
00204             '256.17.184.1'
00205         );
00206         foreach ( $invalid as $i ) {
00207             $this->assertFalse( IP::isValid( $i ), "$i is an invalid IP address" );
00208         }
00209     }
00210 
00214     public function testValidBlocks() {
00215         $valid = array(
00216             '116.17.184.5/32',
00217             '0.17.184.5/30',
00218             '16.17.184.1/24',
00219             '30.242.52.14/1',
00220             '10.232.52.13/8',
00221             '30.242.52.14/0',
00222             '::e:f:2001/96',
00223             '::c:f:2001/128',
00224             '::10:f:2001/70',
00225             '::fe:f:2001/1',
00226             '::6d:f:2001/8',
00227             '::fe:f:2001/0',
00228         );
00229         foreach ( $valid as $i ) {
00230             $this->assertTrue( IP::isValidBlock( $i ), "$i is a valid IP block" );
00231         }
00232     }
00233 
00237     public function testInvalidBlocks() {
00238         $invalid = array(
00239             '116.17.184.5/33',
00240             '0.17.184.5/130',
00241             '16.17.184.1/-1',
00242             '10.232.52.13/*',
00243             '7.232.52.13/ab',
00244             '11.232.52.13/',
00245             '::e:f:2001/129',
00246             '::c:f:2001/228',
00247             '::10:f:2001/-1',
00248             '::6d:f:2001/*',
00249             '::86:f:2001/ab',
00250             '::23:f:2001/',
00251         );
00252         foreach ( $invalid as $i ) {
00253             $this->assertFalse( IP::isValidBlock( $i ), "$i is not a valid IP block" );
00254         }
00255     }
00256 
00261     public function testSanitizeIP() {
00262         $this->assertNull( IP::sanitizeIP( '' ) );
00263         $this->assertNull( IP::sanitizeIP( ' ' ) );
00264     }
00265 
00270     public function testToUnsigned( $expected, $input ) {
00271         $result = IP::toUnsigned( $input );
00272         $this->assertTrue( $result === false || is_string( $result ) || is_int( $result ) );
00273         $this->assertEquals( $expected, $result );
00274     }
00275 
00279     public static function provideToUnsigned() {
00280         return array(
00281             array( 1, '0.0.0.1' ),
00282             array( 16909060, '1.2.3.4' ),
00283             array( 2130706433, '127.0.0.1' ),
00284             array( '2147483648', '128.0.0.0' ),
00285             array( '3735931646', '222.173.202.254' ),
00286             array( pow( 2, 32 ) - 1, '255.255.255.255' ),
00287             array( false, 'IN.VA.LI.D' ),
00288             array( 1, '::1' ),
00289             array( '42540766452641154071740215577757643572', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
00290             array( '42540766452641154071740215577757643572', '2001:db8:85a3::8a2e:0370:7334' ),
00291             array( false, 'IN:VA::LI:D' ),
00292             array( false, ':::1' )
00293         );
00294     }
00295 
00300     public function testToHex( $expected, $input ) {
00301         $result = IP::toHex( $input );
00302         $this->assertTrue( $result === false || is_string( $result ) );
00303         $this->assertEquals( $expected, $result );
00304     }
00305 
00309     public static function provideToHex() {
00310         return array(
00311             array( '00000001', '0.0.0.1' ),
00312             array( '01020304', '1.2.3.4' ),
00313             array( '7F000001', '127.0.0.1' ),
00314             array( '80000000', '128.0.0.0' ),
00315             array( 'DEADCAFE', '222.173.202.254' ),
00316             array( 'FFFFFFFF', '255.255.255.255' ),
00317             array( false, 'IN.VA.LI.D' ),
00318             array( 'v6-00000000000000000000000000000001', '::1' ),
00319             array( 'v6-20010DB885A3000000008A2E03707334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
00320             array( 'v6-20010DB885A3000000008A2E03707334', '2001:db8:85a3::8a2e:0370:7334' ),
00321             array( false, 'IN:VA::LI:D' ),
00322             array( false, ':::1' )
00323         );
00324     }
00325 
00329     public function testPrivateIPs() {
00330         $private = array( 'fc00::3', 'fc00::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
00331         foreach ( $private as $p ) {
00332             $this->assertFalse( IP::isPublic( $p ), "$p is not a public IP address" );
00333         }
00334         $public = array( '2001:5c0:1000:a::133', 'fc::3', '00FC::' );
00335         foreach ( $public as $p ) {
00336             $this->assertTrue( IP::isPublic( $p ), "$p is a public IP address" );
00337         }
00338     }
00339 
00340     // Private wrapper used to test CIDR Parsing.
00341     private function assertFalseCIDR( $CIDR, $msg = '' ) {
00342         $ff = array( false, false );
00343         $this->assertEquals( $ff, IP::parseCIDR( $CIDR ), $msg );
00344     }
00345 
00346     // Private wrapper to test network shifting using only dot notation
00347     private function assertNet( $expected, $CIDR ) {
00348         $parse = IP::parseCIDR( $CIDR );
00349         $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
00350     }
00351 
00355     public function testHexToQuad() {
00356         $this->assertEquals( '0.0.0.1', IP::hexToQuad( '00000001' ) );
00357         $this->assertEquals( '255.0.0.0', IP::hexToQuad( 'FF000000' ) );
00358         $this->assertEquals( '255.255.255.255', IP::hexToQuad( 'FFFFFFFF' ) );
00359         $this->assertEquals( '10.188.222.255', IP::hexToQuad( '0ABCDEFF' ) );
00360         // hex not left-padded...
00361         $this->assertEquals( '0.0.0.0', IP::hexToQuad( '0' ) );
00362         $this->assertEquals( '0.0.0.1', IP::hexToQuad( '1' ) );
00363         $this->assertEquals( '0.0.0.255', IP::hexToQuad( 'FF' ) );
00364         $this->assertEquals( '0.0.255.0', IP::hexToQuad( 'FF00' ) );
00365     }
00366 
00370     public function testHexToOctet() {
00371         $this->assertEquals( '0:0:0:0:0:0:0:1',
00372             IP::hexToOctet( '00000000000000000000000000000001' ) );
00373         $this->assertEquals( '0:0:0:0:0:0:FF:3',
00374             IP::hexToOctet( '00000000000000000000000000FF0003' ) );
00375         $this->assertEquals( '0:0:0:0:0:0:FF00:6',
00376             IP::hexToOctet( '000000000000000000000000FF000006' ) );
00377         $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF',
00378             IP::hexToOctet( '000000000000000000000000FCCFFAFF' ) );
00379         $this->assertEquals( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
00380             IP::hexToOctet( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ) );
00381         // hex not left-padded...
00382         $this->assertEquals( '0:0:0:0:0:0:0:0', IP::hexToOctet( '0' ) );
00383         $this->assertEquals( '0:0:0:0:0:0:0:1', IP::hexToOctet( '1' ) );
00384         $this->assertEquals( '0:0:0:0:0:0:0:FF', IP::hexToOctet( 'FF' ) );
00385         $this->assertEquals( '0:0:0:0:0:0:0:FFD0', IP::hexToOctet( 'FFD0' ) );
00386         $this->assertEquals( '0:0:0:0:0:0:FA00:0', IP::hexToOctet( 'FA000000' ) );
00387         $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
00388     }
00389 
00395     public function testCIDRParsing() {
00396         $this->assertFalseCIDR( '192.0.2.0', "missing mask" );
00397         $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
00398 
00399         // Verify if statement
00400         $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );
00401         $this->assertFalseCIDR( '192.0.2.0/AA', "mask not numeric" );
00402         $this->assertFalseCIDR( '192.0.2.0/-1', "mask < 0" );
00403         $this->assertFalseCIDR( '192.0.2.0/33', "mask > 32" );
00404 
00405         // Check internal logic
00406         # 0 mask always result in array(0,0)
00407         $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '192.0.0.2/0' ) );
00408         $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '0.0.0.0/0' ) );
00409         $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '255.255.255.255/0' ) );
00410 
00411         // @todo FIXME: Add more tests.
00412 
00413         # This part test network shifting
00414         $this->assertNet( '192.0.0.0', '192.0.0.2/24' );
00415         $this->assertNet( '192.168.5.0', '192.168.5.13/24' );
00416         $this->assertNet( '10.0.0.160', '10.0.0.161/28' );
00417         $this->assertNet( '10.0.0.0', '10.0.0.3/28' );
00418         $this->assertNet( '10.0.0.0', '10.0.0.3/30' );
00419         $this->assertNet( '10.0.0.4', '10.0.0.4/30' );
00420         $this->assertNet( '172.17.32.0', '172.17.35.48/21' );
00421         $this->assertNet( '10.128.0.0', '10.135.0.0/9' );
00422         $this->assertNet( '134.0.0.0', '134.0.5.1/8' );
00423     }
00424 
00428     public function testIPCanonicalizeOnValidIp() {
00429         $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
00430             'Canonicalization of a valid IP returns it unchanged' );
00431     }
00432 
00436     public function testIPCanonicalizeMappedAddress() {
00437         $this->assertEquals(
00438             '192.0.2.152',
00439             IP::canonicalize( '::ffff:192.0.2.152' )
00440         );
00441         $this->assertEquals(
00442             '192.0.2.152',
00443             IP::canonicalize( '::192.0.2.152' )
00444         );
00445     }
00446 
00452     public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
00453         $this->assertEquals(
00454             $expected,
00455             IP::isInRange( $addr, $range ),
00456             $message
00457         );
00458     }
00459 
00461     public static function provideIPsAndRanges() {
00462         # Format: (expected boolean, address, range, optional message)
00463         return array(
00464             # IPv4
00465             array( true, '192.0.2.0', '192.0.2.0/24', 'Network address' ),
00466             array( true, '192.0.2.77', '192.0.2.0/24', 'Simple address' ),
00467             array( true, '192.0.2.255', '192.0.2.0/24', 'Broadcast address' ),
00468 
00469             array( false, '0.0.0.0', '192.0.2.0/24' ),
00470             array( false, '255.255.255', '192.0.2.0/24' ),
00471 
00472             # IPv6
00473             array( false, '::1', '2001:DB8::/32' ),
00474             array( false, '::', '2001:DB8::/32' ),
00475             array( false, 'FE80::1', '2001:DB8::/32' ),
00476 
00477             array( true, '2001:DB8::', '2001:DB8::/32' ),
00478             array( true, '2001:0DB8::', '2001:DB8::/32' ),
00479             array( true, '2001:DB8::1', '2001:DB8::/32' ),
00480             array( true, '2001:0DB8::1', '2001:DB8::/32' ),
00481             array( true, '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
00482                 '2001:DB8::/32' ),
00483 
00484             array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
00485         );
00486     }
00487 
00492     public function testSplitHostAndPort( $expected, $input, $description ) {
00493         $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
00494     }
00495 
00499     public static function provideSplitHostAndPort() {
00500         return array(
00501             array( false, '[', 'Unclosed square bracket' ),
00502             array( false, '[::', 'Unclosed square bracket 2' ),
00503             array( array( '::', false ), '::', 'Bare IPv6 0' ),
00504             array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
00505             array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
00506             array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
00507             array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
00508             array( false, '::x', 'Double colon but no IPv6' ),
00509             array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
00510             array( false, 'x:x', 'Hostname and invalid port' ),
00511             array( array( 'x', false ), 'x', 'Plain hostname' )
00512         );
00513     }
00514 
00519     public function testCombineHostAndPort( $expected, $input, $description ) {
00520         list( $host, $port, $defaultPort ) = $input;
00521         $this->assertEquals(
00522             $expected,
00523             IP::combineHostAndPort( $host, $port, $defaultPort ),
00524             $description );
00525     }
00526 
00530     public static function provideCombineHostAndPort() {
00531         return array(
00532             array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
00533             array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
00534             array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
00535             array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
00536         );
00537     }
00538 
00543     public function testSanitizeRange( $input, $expected, $description ) {
00544         $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
00545     }
00546 
00550     public static function provideIPCIDRs() {
00551         return array(
00552             array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
00553             array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
00554             array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
00555             array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
00556             array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
00557             array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
00558             array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
00559             array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
00560         );
00561     }
00562 
00567     public function testPrettifyIP( $ip, $prettified ) {
00568         $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
00569     }
00570 
00574     public static function provideIPsToPrettify() {
00575         return array(
00576             array( '0:0:0:0:0:0:0:0', '::' ),
00577             array( '0:0:0::0:0:0', '::' ),
00578             array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
00579             array( '0:0::f', '::f' ),
00580             array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
00581             array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
00582             array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
00583             array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
00584             array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
00585             array( '0:0:0:0:0:0:0:0/16', '::/16' ),
00586             array( '0:0:0::0:0:0/64', '::/64' ),
00587             array( '0:0::f/52', '::f/52' ),
00588             array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
00589             array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
00590             array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
00591             array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
00592             array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
00593         );
00594     }
00595 }