Merge "Don't check namespace in SpecialWantedtemplates"
[lhc/web/wiklou.git] / tests / phpunit / includes / utils / IPTest.php
1 <?php
2 /**
3 * Tests for IP validity functions.
4 *
5 * Ported from /t/inc/IP.t by avar.
6 *
7 * @group IP
8 * @todo Test methods in this call should be split into a method and a
9 * dataprovider.
10 */
11
12 class IPTest extends PHPUnit_Framework_TestCase {
13 /**
14 * @covers IP::isIPAddress
15 * @dataProvider provideInvalidIPs
16 */
17 public function isNotIPAddress( $val, $desc ) {
18 $this->assertFalse( IP::isIPAddress( $val ), $desc );
19 }
20
21 /**
22 * Provide a list of things that aren't IP addresses
23 */
24 public function provideInvalidIPs() {
25 return array(
26 array( false, 'Boolean false is not an IP' ),
27 array( true, 'Boolean true is not an IP' ),
28 array( '', 'Empty string is not an IP' ),
29 array( 'abc', 'Garbage IP string' ),
30 array( ':', 'Single ":" is not an IP' ),
31 array( '2001:0DB8::A:1::1', 'IPv6 with a double :: occurrence' ),
32 array( '2001:0DB8::A:1::', 'IPv6 with a double :: occurrence, last at end' ),
33 array( '::2001:0DB8::5:1', 'IPv6 with a double :: occurrence, firt at beginning' ),
34 array( '124.24.52', 'IPv4 not enough quads' ),
35 array( '24.324.52.13', 'IPv4 out of range' ),
36 array( '.24.52.13', 'IPv4 starts with period' ),
37 array( 'fc:100:300', 'IPv6 with only 3 words' ),
38 );
39 }
40
41 /**
42 * @covers IP::isIPAddress
43 */
44 public function testisIPAddress() {
45 $this->assertTrue( IP::isIPAddress( '::' ), 'RFC 4291 IPv6 Unspecified Address' );
46 $this->assertTrue( IP::isIPAddress( '::1' ), 'RFC 4291 IPv6 Loopback Address' );
47 $this->assertTrue( IP::isIPAddress( '74.24.52.13/20', 'IPv4 range' ) );
48 $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0/24' ), 'IPv6 range' );
49 $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac/96' ), 'IPv6 range with "::"' );
50
51 $validIPs = array( 'fc:100::', 'fc:100:a:d:1:e:ac::', 'fc::100', '::fc:100:a:d:1:e:ac',
52 '::fc', 'fc::100:a:d:1:e:ac', 'fc:100:a:d:1:e:ac:0', '124.24.52.13', '1.24.52.13' );
53 foreach ( $validIPs as $ip ) {
54 $this->assertTrue( IP::isIPAddress( $ip ), "$ip is a valid IP address" );
55 }
56 }
57
58 /**
59 * @covers IP::isIPv6
60 */
61 public function testisIPv6() {
62 $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
63 $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
64 $this->assertFalse( IP::isIPv6( 'fc:300' ), 'IPv6 with only 2 words' );
65 $this->assertFalse( IP::isIPv6( 'fc:100:300' ), 'IPv6 with only 3 words' );
66
67 $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
68 $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
69 $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
70 $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
71 $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
72 $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
73
74 $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
75 $this->assertFalse(
76 IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ),
77 'IPv6 with 9 words ending with "::"'
78 );
79
80 $this->assertFalse( IP::isIPv6( ':::' ) );
81 $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
82
83 $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
84 $this->assertTrue( IP::isIPv6( '::0' ) );
85 $this->assertTrue( IP::isIPv6( '::fc' ) );
86 $this->assertTrue( IP::isIPv6( '::fc:100' ) );
87 $this->assertTrue( IP::isIPv6( '::fc:100:a' ) );
88 $this->assertTrue( IP::isIPv6( '::fc:100:a:d' ) );
89 $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
90 $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
91 $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
92
93 $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
94 $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
95
96 $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
97 $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending with lone ":"' );
98 $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
99
100 $this->assertTrue( IP::isIPv6( 'fc::100' ), 'IPv6 with "::" and 2 words' );
101 $this->assertTrue( IP::isIPv6( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
102 $this->assertTrue( IP::isIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' ) );
103 $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
104 $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ), 'IPv6 with "::" and 6 words' );
105 $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
106 $this->assertTrue( IP::isIPv6( '2001::df' ), 'IPv6 with "::" and 2 words' );
107 $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
108 $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
109
110 $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
111 $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
112
113 $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
114 }
115
116 /**
117 * @covers IP::isIPv4
118 * @dataProvider provideInvalidIPv4Addresses
119 */
120 public function testisNotIPv4( $bogusIP, $desc ) {
121 $this->assertFalse( IP::isIPv4( $bogusIP ), $desc );
122 }
123
124 public function provideInvalidIPv4Addresses() {
125 return array(
126 array( false, 'Boolean false is not an IP' ),
127 array( true, 'Boolean true is not an IP' ),
128 array( '', 'Empty string is not an IP' ),
129 array( 'abc', 'Letters are not an IP' ),
130 array( ':', 'A colon is not an IP' ),
131 array( '124.24.52', 'IPv4 not enough quads' ),
132 array( '24.324.52.13', 'IPv4 out of range' ),
133 array( '.24.52.13', 'IPv4 starts with period' ),
134 );
135 }
136
137 /**
138 * @covers IP::isIPv4
139 * @dataProvider provideValidIPv4Address
140 */
141 public function testIsIPv4( $ip, $desc ) {
142 $this->assertTrue( IP::isIPv4( $ip ), $desc );
143 }
144
145 /**
146 * Provide some IPv4 addresses and ranges
147 */
148 public function provideValidIPv4Address() {
149 return array(
150 array( '124.24.52.13', 'Valid IPv4 address' ),
151 array( '1.24.52.13', 'Another valid IPv4 address' ),
152 array( '74.24.52.13/20', 'An IPv4 range' ),
153 );
154 }
155
156 /**
157 * @covers IP::isValid
158 */
159 public function testValidIPs() {
160 foreach ( range( 0, 255 ) as $i ) {
161 $a = sprintf( "%03d", $i );
162 $b = sprintf( "%02d", $i );
163 $c = sprintf( "%01d", $i );
164 foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
165 $ip = "$f.$f.$f.$f";
166 $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv4 address" );
167 }
168 }
169 foreach ( range( 0x0, 0xFFFF, 0xF ) as $i ) {
170 $a = sprintf( "%04x", $i );
171 $b = sprintf( "%03x", $i );
172 $c = sprintf( "%02x", $i );
173 foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
174 $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
175 $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv6 address" );
176 }
177 }
178 // test with some abbreviations
179 $this->assertFalse( IP::isValid( ':fc:100::' ), 'IPv6 starting with lone ":"' );
180 $this->assertFalse( IP::isValid( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
181 $this->assertFalse( IP::isValid( 'fc:300' ), 'IPv6 with only 2 words' );
182 $this->assertFalse( IP::isValid( 'fc:100:300' ), 'IPv6 with only 3 words' );
183
184 $this->assertTrue( IP::isValid( 'fc:100::' ) );
185 $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e::' ) );
186 $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e:ac::' ) );
187
188 $this->assertTrue( IP::isValid( 'fc::100' ), 'IPv6 with "::" and 2 words' );
189 $this->assertTrue( IP::isValid( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
190 $this->assertTrue( IP::isValid( '2001::df' ), 'IPv6 with "::" and 2 words' );
191 $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
192 $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
193 $this->assertTrue( IP::isValid( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
194 $this->assertTrue( IP::isValid( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
195
196 $this->assertFalse(
197 IP::isValid( 'fc:100:a:d:1:e:ac:0::' ),
198 'IPv6 with 8 words ending with "::"'
199 );
200 $this->assertFalse(
201 IP::isValid( 'fc:100:a:d:1:e:ac:0:1::' ),
202 'IPv6 with 9 words ending with "::"'
203 );
204 }
205
206 /**
207 * @covers IP::isValid
208 */
209 public function testInvalidIPs() {
210 // Out of range...
211 foreach ( range( 256, 999 ) as $i ) {
212 $a = sprintf( "%03d", $i );
213 $b = sprintf( "%02d", $i );
214 $c = sprintf( "%01d", $i );
215 foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
216 $ip = "$f.$f.$f.$f";
217 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv4 address" );
218 }
219 }
220 foreach ( range( 'g', 'z' ) as $i ) {
221 $a = sprintf( "%04s", $i );
222 $b = sprintf( "%03s", $i );
223 $c = sprintf( "%02s", $i );
224 foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
225 $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
226 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv6 address" );
227 }
228 }
229 // Have CIDR
230 $ipCIDRs = array(
231 '212.35.31.121/32',
232 '212.35.31.121/18',
233 '212.35.31.121/24',
234 '::ff:d:321:5/96',
235 'ff::d3:321:5/116',
236 'c:ff:12:1:ea:d:321:5/120',
237 );
238 foreach ( $ipCIDRs as $i ) {
239 $this->assertFalse( IP::isValid( $i ),
240 "$i is an invalid IP address because it is a block" );
241 }
242 // Incomplete/garbage
243 $invalid = array(
244 'www.xn--var-xla.net',
245 '216.17.184.G',
246 '216.17.184.1.',
247 '216.17.184',
248 '216.17.184.',
249 '256.17.184.1'
250 );
251 foreach ( $invalid as $i ) {
252 $this->assertFalse( IP::isValid( $i ), "$i is an invalid IP address" );
253 }
254 }
255
256 /**
257 * Provide some valid IP blocks
258 */
259 public function provideValidBlocks() {
260 return array(
261 array( '116.17.184.5/32' ),
262 array( '0.17.184.5/30' ),
263 array( '16.17.184.1/24' ),
264 array( '30.242.52.14/1' ),
265 array( '10.232.52.13/8' ),
266 array( '30.242.52.14/0' ),
267 array( '::e:f:2001/96' ),
268 array( '::c:f:2001/128' ),
269 array( '::10:f:2001/70' ),
270 array( '::fe:f:2001/1' ),
271 array( '::6d:f:2001/8' ),
272 array( '::fe:f:2001/0' ),
273 );
274 }
275
276 /**
277 * @covers IP::isValidBlock
278 * @dataProvider provideValidBlocks
279 */
280 public function testValidBlocks( $block ) {
281 $this->assertTrue( IP::isValidBlock( $block ), "$block is a valid IP block" );
282 }
283
284 /**
285 * @covers IP::isValidBlock
286 * @dataProvider provideInvalidBlocks
287 */
288 public function testInvalidBlocks( $invalid ) {
289 $this->assertFalse( IP::isValidBlock( $invalid ), "$invalid is not a valid IP block" );
290 }
291
292 public function provideInvalidBlocks() {
293 return array(
294 array( '116.17.184.5/33' ),
295 array( '0.17.184.5/130' ),
296 array( '16.17.184.1/-1' ),
297 array( '10.232.52.13/*' ),
298 array( '7.232.52.13/ab' ),
299 array( '11.232.52.13/' ),
300 array( '::e:f:2001/129' ),
301 array( '::c:f:2001/228' ),
302 array( '::10:f:2001/-1' ),
303 array( '::6d:f:2001/*' ),
304 array( '::86:f:2001/ab' ),
305 array( '::23:f:2001/' ),
306 );
307 }
308
309 /**
310 * Improve IP::sanitizeIP() code coverage
311 * @todo Most probably incomplete
312 */
313 public function testSanitizeIP() {
314 $this->assertNull( IP::sanitizeIP( '' ) );
315 $this->assertNull( IP::sanitizeIP( ' ' ) );
316 }
317
318 /**
319 * @covers IP::toHex
320 * @dataProvider provideToHex
321 */
322 public function testToHex( $expected, $input ) {
323 $result = IP::toHex( $input );
324 $this->assertTrue( $result === false || is_string( $result ) );
325 $this->assertEquals( $expected, $result );
326 }
327
328 /**
329 * Provider for IP::testToHex()
330 */
331 public static function provideToHex() {
332 return array(
333 array( '00000001', '0.0.0.1' ),
334 array( '01020304', '1.2.3.4' ),
335 array( '7F000001', '127.0.0.1' ),
336 array( '80000000', '128.0.0.0' ),
337 array( 'DEADCAFE', '222.173.202.254' ),
338 array( 'FFFFFFFF', '255.255.255.255' ),
339 array( false, 'IN.VA.LI.D' ),
340 array( 'v6-00000000000000000000000000000001', '::1' ),
341 array( 'v6-20010DB885A3000000008A2E03707334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
342 array( 'v6-20010DB885A3000000008A2E03707334', '2001:db8:85a3::8a2e:0370:7334' ),
343 array( false, 'IN:VA::LI:D' ),
344 array( false, ':::1' )
345 );
346 }
347
348 /**
349 * @covers IP::isPublic
350 * @dataProvider provideIsPublic
351 */
352 public function testIsPublic( $expected, $input ) {
353 $result = IP::isPublic( $input );
354 $this->assertEquals( $expected, $result );
355 }
356
357 /**
358 * Provider for IP::testIsPublic()
359 */
360 public static function provideIsPublic() {
361 return array(
362 array( false, 'fc00::3' ), # RFC 4193 (local)
363 array( false, 'fc00::ff' ), # RFC 4193 (local)
364 array( false, '127.1.2.3' ), # loopback
365 array( false, '::1' ), # loopback
366 array( false, 'fe80::1' ), # link-local
367 array( false, '169.254.1.1' ), # link-local
368 array( false, '10.0.0.1' ), # RFC 1918 (private)
369 array( false, '172.16.0.1' ), # RFC 1918 (private)
370 array( false, '192.168.0.1' ), # RFC 1918 (private)
371 array( true, '2001:5c0:1000:a::133' ), # public
372 array( true, 'fc::3' ), # public
373 array( true, '00FC::' ) # public
374 );
375 }
376
377 // Private wrapper used to test CIDR Parsing.
378 private function assertFalseCIDR( $CIDR, $msg = '' ) {
379 $ff = array( false, false );
380 $this->assertEquals( $ff, IP::parseCIDR( $CIDR ), $msg );
381 }
382
383 // Private wrapper to test network shifting using only dot notation
384 private function assertNet( $expected, $CIDR ) {
385 $parse = IP::parseCIDR( $CIDR );
386 $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
387 }
388
389 /**
390 * @covers IP::hexToQuad
391 * @dataProvider provideIPsAndHexes
392 */
393 public function testHexToQuad( $ip, $hex ) {
394 $this->assertEquals( $ip, IP::hexToQuad( $hex ) );
395 }
396
397 /**
398 * Provide some IP addresses and their equivalent hex representations
399 */
400 public function provideIPsandHexes() {
401 return array(
402 array( '0.0.0.1', '00000001' ),
403 array( '255.0.0.0', 'FF000000' ),
404 array( '255.255.255.255', 'FFFFFFFF' ),
405 array( '10.188.222.255', '0ABCDEFF' ),
406 // hex not left-padded...
407 array( '0.0.0.0', '0' ),
408 array( '0.0.0.1', '1' ),
409 array( '0.0.0.255', 'FF' ),
410 array( '0.0.255.0', 'FF00' ),
411 );
412 }
413
414 /**
415 * @covers IP::hexToOctet
416 * @dataProvider provideOctetsAndHexes
417 */
418 public function testHexToOctet( $octet, $hex ) {
419 $this->assertEquals( $octet, IP::hexToOctet( $hex ) );
420 }
421
422 /**
423 * Provide some hex and octet representations of the same IPs
424 */
425 public function provideOctetsAndHexes() {
426 return array(
427 array( '0:0:0:0:0:0:0:1', '00000000000000000000000000000001' ),
428 array( '0:0:0:0:0:0:FF:3', '00000000000000000000000000FF0003' ),
429 array( '0:0:0:0:0:0:FF00:6', '000000000000000000000000FF000006' ),
430 array( '0:0:0:0:0:0:FCCF:FAFF', '000000000000000000000000FCCFFAFF' ),
431 array( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ),
432 // hex not left-padded...
433 array( '0:0:0:0:0:0:0:0', '0' ),
434 array( '0:0:0:0:0:0:0:1', '1' ),
435 array( '0:0:0:0:0:0:0:FF', 'FF' ),
436 array( '0:0:0:0:0:0:0:FFD0', 'FFD0' ),
437 array( '0:0:0:0:0:0:FA00:0', 'FA000000' ),
438 array( '0:0:0:0:0:0:FCCF:FAFF', 'FCCFFAFF' ),
439 );
440 }
441
442 /**
443 * IP::parseCIDR() returns an array containing a signed IP address
444 * representing the network mask and the bit mask.
445 * @covers IP::parseCIDR
446 */
447 public function testCIDRParsing() {
448 $this->assertFalseCIDR( '192.0.2.0', "missing mask" );
449 $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
450
451 // Verify if statement
452 $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );
453 $this->assertFalseCIDR( '192.0.2.0/AA', "mask not numeric" );
454 $this->assertFalseCIDR( '192.0.2.0/-1', "mask < 0" );
455 $this->assertFalseCIDR( '192.0.2.0/33', "mask > 32" );
456
457 // Check internal logic
458 # 0 mask always result in array(0,0)
459 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '192.0.0.2/0' ) );
460 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '0.0.0.0/0' ) );
461 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '255.255.255.255/0' ) );
462
463 // @todo FIXME: Add more tests.
464
465 # This part test network shifting
466 $this->assertNet( '192.0.0.0', '192.0.0.2/24' );
467 $this->assertNet( '192.168.5.0', '192.168.5.13/24' );
468 $this->assertNet( '10.0.0.160', '10.0.0.161/28' );
469 $this->assertNet( '10.0.0.0', '10.0.0.3/28' );
470 $this->assertNet( '10.0.0.0', '10.0.0.3/30' );
471 $this->assertNet( '10.0.0.4', '10.0.0.4/30' );
472 $this->assertNet( '172.17.32.0', '172.17.35.48/21' );
473 $this->assertNet( '10.128.0.0', '10.135.0.0/9' );
474 $this->assertNet( '134.0.0.0', '134.0.5.1/8' );
475 }
476
477 /**
478 * @covers IP::canonicalize
479 */
480 public function testIPCanonicalizeOnValidIp() {
481 $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
482 'Canonicalization of a valid IP returns it unchanged' );
483 }
484
485 /**
486 * @covers IP::canonicalize
487 */
488 public function testIPCanonicalizeMappedAddress() {
489 $this->assertEquals(
490 '192.0.2.152',
491 IP::canonicalize( '::ffff:192.0.2.152' )
492 );
493 $this->assertEquals(
494 '192.0.2.152',
495 IP::canonicalize( '::192.0.2.152' )
496 );
497 }
498
499 /**
500 * Issues there are most probably from IP::toHex() or IP::parseRange()
501 * @covers IP::isInRange
502 * @dataProvider provideIPsAndRanges
503 */
504 public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
505 $this->assertEquals(
506 $expected,
507 IP::isInRange( $addr, $range ),
508 $message
509 );
510 }
511
512 /** Provider for testIPIsInRange() */
513 public static function provideIPsAndRanges() {
514 # Format: (expected boolean, address, range, optional message)
515 return array(
516 # IPv4
517 array( true, '192.0.2.0', '192.0.2.0/24', 'Network address' ),
518 array( true, '192.0.2.77', '192.0.2.0/24', 'Simple address' ),
519 array( true, '192.0.2.255', '192.0.2.0/24', 'Broadcast address' ),
520
521 array( false, '0.0.0.0', '192.0.2.0/24' ),
522 array( false, '255.255.255', '192.0.2.0/24' ),
523
524 # IPv6
525 array( false, '::1', '2001:DB8::/32' ),
526 array( false, '::', '2001:DB8::/32' ),
527 array( false, 'FE80::1', '2001:DB8::/32' ),
528
529 array( true, '2001:DB8::', '2001:DB8::/32' ),
530 array( true, '2001:0DB8::', '2001:DB8::/32' ),
531 array( true, '2001:DB8::1', '2001:DB8::/32' ),
532 array( true, '2001:0DB8::1', '2001:DB8::/32' ),
533 array( true, '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
534 '2001:DB8::/32' ),
535
536 array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
537 );
538 }
539
540 /**
541 * Test for IP::splitHostAndPort().
542 * @dataProvider provideSplitHostAndPort
543 */
544 public function testSplitHostAndPort( $expected, $input, $description ) {
545 $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
546 }
547
548 /**
549 * Provider for IP::splitHostAndPort()
550 */
551 public static function provideSplitHostAndPort() {
552 return array(
553 array( false, '[', 'Unclosed square bracket' ),
554 array( false, '[::', 'Unclosed square bracket 2' ),
555 array( array( '::', false ), '::', 'Bare IPv6 0' ),
556 array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
557 array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
558 array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
559 array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
560 array( false, '::x', 'Double colon but no IPv6' ),
561 array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
562 array( false, 'x:x', 'Hostname and invalid port' ),
563 array( array( 'x', false ), 'x', 'Plain hostname' )
564 );
565 }
566
567 /**
568 * Test for IP::combineHostAndPort()
569 * @dataProvider provideCombineHostAndPort
570 */
571 public function testCombineHostAndPort( $expected, $input, $description ) {
572 list( $host, $port, $defaultPort ) = $input;
573 $this->assertEquals(
574 $expected,
575 IP::combineHostAndPort( $host, $port, $defaultPort ),
576 $description );
577 }
578
579 /**
580 * Provider for IP::combineHostAndPort()
581 */
582 public static function provideCombineHostAndPort() {
583 return array(
584 array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
585 array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
586 array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
587 array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
588 );
589 }
590
591 /**
592 * Test for IP::sanitizeRange()
593 * @dataProvider provideIPCIDRs
594 */
595 public function testSanitizeRange( $input, $expected, $description ) {
596 $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
597 }
598
599 /**
600 * Provider for IP::testSanitizeRange()
601 */
602 public static function provideIPCIDRs() {
603 return array(
604 array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
605 array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
606 array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
607 array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
608 array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
609 array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
610 array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
611 array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
612 );
613 }
614
615 /**
616 * Test for IP::prettifyIP()
617 * @dataProvider provideIPsToPrettify
618 */
619 public function testPrettifyIP( $ip, $prettified ) {
620 $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
621 }
622
623 /**
624 * Provider for IP::testPrettifyIP()
625 */
626 public static function provideIPsToPrettify() {
627 return array(
628 array( '0:0:0:0:0:0:0:0', '::' ),
629 array( '0:0:0::0:0:0', '::' ),
630 array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
631 array( '0:0::f', '::f' ),
632 array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
633 array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
634 array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
635 array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
636 array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
637 array( '0:0:0:0:0:0:0:0/16', '::/16' ),
638 array( '0:0:0::0:0:0/64', '::/64' ),
639 array( '0:0::f/52', '::f/52' ),
640 array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
641 array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
642 array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
643 array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
644 array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
645 );
646 }
647 }