Fix for SiteListTest failing on 32bit machines
[lhc/web/wiklou.git] / tests / phpunit / includes / IPTest.php
index 3655e51..c4a6c55 100644 (file)
@@ -1,19 +1,23 @@
 <?php
-/*
+/**
  * Tests for IP validity functions. Ported from /t/inc/IP.t by avar.
+ * @group IP
  */
 
 class IPTest extends MediaWikiTestCase {
-       // not sure it should be tested with boolean false. hashar 20100924
+       /**
+        *  not sure it should be tested with boolean false. hashar 20100924
+        * @covers IP::isIPAddress
+        */
        public function testisIPAddress() {
                $this->assertFalse( IP::isIPAddress( false ), 'Boolean false is not an IP' );
                $this->assertFalse( IP::isIPAddress( true  ), 'Boolean true is not an IP' );
                $this->assertFalse( IP::isIPAddress( "" ), 'Empty string is not an IP' );
                $this->assertFalse( IP::isIPAddress( 'abc' ), 'Garbage IP string' );
                $this->assertFalse( IP::isIPAddress( ':' ), 'Single ":" is not an IP' );
-               $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1'), 'IPv6 with a double :: occurence' );
-               $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::'), 'IPv6 with a double :: occurence, last at end' );
-               $this->assertFalse( IP::isIPAddress( '::2001:0DB8::5:1'), 'IPv6 with a double :: occurence, firt at beginning' );
+               $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1'), 'IPv6 with a double :: occurrence' );
+               $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::'), 'IPv6 with a double :: occurrence, last at end' );
+               $this->assertFalse( IP::isIPAddress( '::2001:0DB8::5:1'), 'IPv6 with a double :: occurrence, firt at beginning' );
                $this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
                $this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
                $this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
@@ -32,22 +36,28 @@ class IPTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * @covers IP::isIPv6
+        */
        public function testisIPv6() {
                $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
                $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
                $this->assertFalse( IP::isIPv6( 'fc:300' ), 'IPv6 with only 2 words' );
                $this->assertFalse( IP::isIPv6( 'fc:100:300' ), 'IPv6 with only 3 words' );
+
                $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
                $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
                $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
                $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
                $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
                $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
+
                $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
                $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words ending with "::"' );
 
                $this->assertFalse( IP::isIPv6( ':::' ) );
                $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
+
                $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
                $this->assertTrue( IP::isIPv6( '::0' ) );
                $this->assertTrue( IP::isIPv6( '::fc' ) );
@@ -57,24 +67,33 @@ class IPTest extends MediaWikiTestCase {
                $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
                $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
                $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
+
                $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
                $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
 
                $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
                $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending with lone ":"' );
                $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
+
                $this->assertTrue( IP::isIPv6( 'fc::100' ), 'IPv6 with "::" and 2 words' );
                $this->assertTrue( IP::isIPv6( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
                $this->assertTrue( IP::isIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' ) );
                $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
                $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ), 'IPv6 with "::" and 6 words' );
                $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
+               $this->assertTrue( IP::isIPv6( '2001::df'), 'IPv6 with "::" and 2 words' );
+               $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df'), 'IPv6 with "::" and 5 words' );
+               $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df:2'), 'IPv6 with "::" and 6 words' );
+
                $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
                $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
 
                $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
        }
 
+       /**
+        * @covers IP::isIPv4
+        */
        public function testisIPv4() {
                $this->assertFalse( IP::isIPv4( false ), 'Boolean false is not an IP' );
                $this->assertFalse( IP::isIPv4( true  ), 'Boolean true is not an IP' );
@@ -90,7 +109,9 @@ class IPTest extends MediaWikiTestCase {
                $this->assertTrue( IP::isIPv4( '74.24.52.13/20', 'IPv4 range' ) );
        }
 
-       // tests isValid()
+       /**
+        * @covers IP::isValid
+        */
        public function testValidIPs() {
                foreach ( range( 0, 255 ) as $i ) {
                        $a = sprintf( "%03d", $i );
@@ -101,7 +122,7 @@ class IPTest extends MediaWikiTestCase {
                                $this->assertTrue( IP::isValid( $ip ) , "$ip is a valid IPv4 address" );
                        }
                }
-               foreach ( range( 0x0, 0xFFFF ) as $i ) {
+               foreach ( range( 0x0, 0xFFFF, 0xF ) as $i ) {
                        $a = sprintf( "%04x", $i );
                        $b = sprintf( "%03x", $i );
                        $c = sprintf( "%02x", $i );
@@ -110,9 +131,31 @@ class IPTest extends MediaWikiTestCase {
                                $this->assertTrue( IP::isValid( $ip ) , "$ip is a valid IPv6 address" );
                        }
                }
+               // test with some abbreviations
+               $this->assertFalse( IP::isValid( ':fc:100::' ), 'IPv6 starting with lone ":"' );
+               $this->assertFalse( IP::isValid( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
+               $this->assertFalse( IP::isValid( 'fc:300' ), 'IPv6 with only 2 words' );
+               $this->assertFalse( IP::isValid( 'fc:100:300' ), 'IPv6 with only 3 words' );
+
+               $this->assertTrue( IP::isValid( 'fc:100::' ) );
+               $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e::' ) );
+               $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e:ac::' ) );
+
+               $this->assertTrue( IP::isValid( 'fc::100' ), 'IPv6 with "::" and 2 words' );
+               $this->assertTrue( IP::isValid( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
+               $this->assertTrue( IP::isValid( '2001::df'), 'IPv6 with "::" and 2 words' );
+               $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df'), 'IPv6 with "::" and 5 words' );
+               $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df:2'), 'IPv6 with "::" and 6 words' );
+               $this->assertTrue( IP::isValid( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
+               $this->assertTrue( IP::isValid( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
+
+               $this->assertFalse( IP::isValid( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
+               $this->assertFalse( IP::isValid( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words ending with "::"' );
        }
 
-       // tests isValid()
+       /**
+        * @covers IP::isValid
+        */
        public function testInvalidIPs() {
                // Out of range...
                foreach ( range( 256, 999 ) as $i ) {
@@ -160,7 +203,9 @@ class IPTest extends MediaWikiTestCase {
                }
        }
 
-       // tests isValidBlock()
+       /**
+        * @covers IP::isValidBlock
+        */
        public function testValidBlocks() {
                $valid = array(
                        '116.17.184.5/32',
@@ -181,7 +226,9 @@ class IPTest extends MediaWikiTestCase {
                }
        }
 
-       // tests isValidBlock()
+       /**
+        * @covers IP::isValidBlock
+        */
        public function testInvalidBlocks() {
                $invalid = array(
                        '116.17.184.5/33',
@@ -202,20 +249,38 @@ class IPTest extends MediaWikiTestCase {
                }
        }
 
-       // test wrapper around ip2long which might return -1 or false depending on PHP version
+       /**
+        * Improve IP::sanitizeIP() code coverage
+        * @todo Most probably incomplete
+        */
+       public function testSanitizeIP() {
+               $this->assertNull( IP::sanitizeIP('')  );
+               $this->assertNull( IP::sanitizeIP(' ') );
+       }
+
+       /**
+        * test wrapper around ip2long which might return -1 or false depending on PHP version
+        * @covers IP::toUnsigned
+        */
        public function testip2longWrapper() {
-               // fixme : add more tests ?
+               // @todo FIXME: Add more tests ?
                $this->assertEquals( pow(2,32) - 1, IP::toUnsigned( '255.255.255.255' ));
                $i = 'IN.VA.LI.D';
                $this->assertFalse( IP::toUnSigned( $i ) );
        }
 
-       // tests isPublic()
+       /**
+        * @covers IP::isPublic
+        */
        public function testPrivateIPs() {
-               $private = array( 'fc::3', 'fc::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
+               $private = array( 'fc00::3', 'fc00::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
                foreach ( $private as $p ) {
                        $this->assertFalse( IP::isPublic( $p ), "$p is not a public IP address" );
                }
+               $public = array( '2001:5c0:1000:a::133', 'fc::3', '00FC::' );
+               foreach ( $public as $p ) {
+                       $this->assertTrue( IP::isPublic( $p ), "$p is a public IP address" );
+               }
        }
 
        // Private wrapper used to test CIDR Parsing.
@@ -230,6 +295,9 @@ class IPTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
        }
 
+       /**
+        * @covers IP::hexToQuad
+        */
        public function testHexToQuad() {
                $this->assertEquals( '0.0.0.1'        , IP::hexToQuad( '00000001' ) );
                $this->assertEquals( '255.0.0.0'      , IP::hexToQuad( 'FF000000' ) );
@@ -242,6 +310,9 @@ class IPTest extends MediaWikiTestCase {
                $this->assertEquals( '0.0.255.0'   , IP::hexToQuad( 'FF00' ) );
        }
 
+       /**
+        * @covers IP::hexToOctet
+        */
        public function testHexToOctet() {
                $this->assertEquals( '0:0:0:0:0:0:0:1',
                        IP::hexToOctet( '00000000000000000000000000000001' ) );
@@ -262,9 +333,10 @@ class IPTest extends MediaWikiTestCase {
                $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
        }
 
-       /*
+       /**
         * IP::parseCIDR() returns an array containing a signed IP address
         * representing the network mask and the bit mask.
+        * @covers IP::parseCIDR
         */
        function testCIDRParsing() {
                $this->assertFalseCIDR( '192.0.2.0' , "missing mask"    );
@@ -282,7 +354,7 @@ class IPTest extends MediaWikiTestCase {
                $this->assertEquals( array( 0, 0 ), IP::parseCIDR('0.0.0.0/0') );
                $this->assertEquals( array( 0, 0 ), IP::parseCIDR('255.255.255.255/0') );
 
-               // FIXME : add more tests.
+               // @todo FIXME: Add more tests.
 
                # This part test network shifting
                $this->assertNet( '192.0.0.0'  , '192.0.0.2/24'   );
@@ -295,4 +367,176 @@ class IPTest extends MediaWikiTestCase {
                $this->assertNet( '10.128.0.0' , '10.135.0.0/9' );
                $this->assertNet( '134.0.0.0'  , '134.0.5.1/8'  );
        }
+
+
+       /**
+        * @covers IP::canonicalize
+        */
+       public function testIPCanonicalizeOnValidIp() {
+               $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
+               'Canonicalization of a valid IP returns it unchanged' );
+       }
+
+       /**
+        * @covers IP::canonicalize
+        */
+       public function testIPCanonicalizeMappedAddress() {
+               $this->assertEquals(
+                       '192.0.2.152',
+                       IP::canonicalize( '::ffff:192.0.2.152' )
+               );
+               $this->assertEquals(
+                       '192.0.2.152',
+                       IP::canonicalize( '::192.0.2.152' )
+               );
+       }
+
+       /**
+        * Issues there are most probably from IP::toHex() or IP::parseRange()
+        * @covers IP::isInRange
+        * @dataProvider provideIPsAndRanges
+        */
+       public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
+               $this->assertEquals(
+                       $expected,
+                       IP::isInRange( $addr, $range ),
+                       $message
+               );
+       }
+
+       /** Provider for testIPIsInRange() */
+       public static function provideIPsAndRanges() {
+                       # Format: (expected boolean, address, range, optional message)
+               return array(
+                       # IPv4
+                       array( true , '192.0.2.0'   , '192.0.2.0/24', 'Network address' ),
+                       array( true , '192.0.2.77'  , '192.0.2.0/24', 'Simple address' ),
+                       array( true , '192.0.2.255' , '192.0.2.0/24', 'Broadcast address' ),
+
+                       array( false, '0.0.0.0'     , '192.0.2.0/24' ),
+                       array( false, '255.255.255' , '192.0.2.0/24' ),
+
+                       # IPv6
+                       array( false, '::1'    , '2001:DB8::/32' ),
+                       array( false, '::'     , '2001:DB8::/32' ),
+                       array( false, 'FE80::1', '2001:DB8::/32' ),
+
+                       array( true , '2001:DB8::'  , '2001:DB8::/32' ),
+                       array( true , '2001:0DB8::' , '2001:DB8::/32' ),
+                       array( true , '2001:DB8::1' , '2001:DB8::/32' ),
+                       array( true , '2001:0DB8::1', '2001:DB8::/32' ),
+                       array( true , '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
+                               '2001:DB8::/32' ),
+
+                       array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
+               );
+       }
+
+       /**
+        * Test for IP::splitHostAndPort().
+        * @dataProvider provideSplitHostAndPort
+        */
+       function testSplitHostAndPort( $expected, $input, $description ) {
+               $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
+       }
+
+       /**
+        * Provider for IP::splitHostAndPort()
+        */
+       public static function provideSplitHostAndPort() {
+               return array(
+                       array( false, '[', 'Unclosed square bracket' ),
+                       array( false, '[::', 'Unclosed square bracket 2' ),
+                       array( array( '::', false ), '::', 'Bare IPv6 0' ),
+                       array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
+                       array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
+                       array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
+                       array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
+                       array( false, '::x', 'Double colon but no IPv6' ),
+                       array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
+                       array( false, 'x:x', 'Hostname and invalid port' ),
+                       array( array( 'x', false ), 'x', 'Plain hostname' )
+               );
+       }
+
+       /**
+        * Test for IP::combineHostAndPort()
+        * @dataProvider provideCombineHostAndPort
+        */
+       function testCombineHostAndPort( $expected, $input, $description ) {
+               list( $host, $port, $defaultPort ) = $input;
+               $this->assertEquals(
+                       $expected,
+                       IP::combineHostAndPort( $host, $port, $defaultPort ),
+                       $description );
+       }
+
+       /**
+        * Provider for IP::combineHostAndPort()
+        */
+       public static function provideCombineHostAndPort() {
+               return array(
+                       array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
+                       array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
+                       array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
+                       array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
+               );
+       }
+
+       /**
+        * Test for IP::sanitizeRange()
+        * @dataProvider provideIPCIDRs
+        */
+       function testSanitizeRange( $input, $expected, $description ) {
+               $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
+       }
+
+       /**
+        * Provider for IP::testSanitizeRange()
+        */
+       public static function provideIPCIDRs() {
+               return array(
+                       array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
+                       array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
+                       array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
+                       array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
+                       array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
+                       array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
+                       array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
+                       array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
+               );
+       }
+
+       /**
+        * Test for IP::prettifyIP()
+        * @dataProvider provideIPsToPrettify
+        */
+       function testPrettifyIP( $ip, $prettified ) {
+               $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
+       }
+
+       /**
+        * Provider for IP::testPrettifyIP()
+        */
+       public static function provideIPsToPrettify() {
+               return array(
+                       array( '0:0:0:0:0:0:0:0', '::' ),
+                       array( '0:0:0::0:0:0', '::' ),
+                       array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
+                       array( '0:0::f', '::f' ),
+                       array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
+                       array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
+                       array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
+                       array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
+                       array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
+                       array( '0:0:0:0:0:0:0:0/16', '::/16' ),
+                       array( '0:0:0::0:0:0/64', '::/64' ),
+                       array( '0:0::f/52', '::f/52' ),
+                       array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
+                       array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
+                       array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
+                       array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
+                       array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
+               );
+       }
 }